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 /*
22d8a7fe16SGarrett 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 /*
2788447a05SGarrett D'Amore  * audioixp Audio Driver
2888447a05SGarrett D'Amore  *
2988447a05SGarrett D'Amore  * This driver supports audio hardware integrated in ATI IXP400 chipset.
3088447a05SGarrett D'Amore  *
3188447a05SGarrett D'Amore  * The IXP400 audio core is an AC'97 controller, which has independent
3288447a05SGarrett D'Amore  * channels for PCM in, PCM out. The AC'97 controller is a PCI bus master
3388447a05SGarrett D'Amore  * with scatter/gather support. Each channel has a DMA engine. Currently,
3488447a05SGarrett D'Amore  * we use only the PCM in and PCM out channels. Each DMA engine uses one
3588447a05SGarrett D'Amore  * buffer descriptor list.  Each entry contains a pointer to a data buffer,
3688447a05SGarrett D'Amore  * status, length of the buffer being pointed to and the pointer to the next
3788447a05SGarrett D'Amore  * entry. Length of the buffer is in number of bytes. Interrupt will be
3888447a05SGarrett D'Amore  * triggered each time a entry is processed by hardware.
3988447a05SGarrett D'Amore  *
4088447a05SGarrett D'Amore  * System power management is not yet supported by the driver.
4188447a05SGarrett D'Amore  *
4288447a05SGarrett D'Amore  * 	NOTE:
4388447a05SGarrett D'Amore  * 	This driver depends on the misc/ac97 and drv/audio modules being
4488447a05SGarrett D'Amore  *	loaded first.
4588447a05SGarrett D'Amore  */
4688447a05SGarrett D'Amore #include <sys/types.h>
4788447a05SGarrett D'Amore #include <sys/modctl.h>
4888447a05SGarrett D'Amore #include <sys/kmem.h>
4988447a05SGarrett D'Amore #include <sys/conf.h>
5088447a05SGarrett D'Amore #include <sys/ddi.h>
5188447a05SGarrett D'Amore #include <sys/sunddi.h>
5288447a05SGarrett D'Amore #include <sys/pci.h>
5388447a05SGarrett D'Amore #include <sys/note.h>
5488447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
5588447a05SGarrett D'Amore #include <sys/audio/ac97.h>
5688447a05SGarrett D'Amore #include "audioixp.h"
5788447a05SGarrett D'Amore 
5888447a05SGarrett D'Amore /*
5988447a05SGarrett D'Amore  * Module linkage routines for the kernel
6088447a05SGarrett D'Amore  */
6188447a05SGarrett D'Amore static int audioixp_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
6288447a05SGarrett D'Amore static int audioixp_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
6388447a05SGarrett D'Amore static int audioixp_quiesce(dev_info_t *);
6488447a05SGarrett D'Amore static int audioixp_resume(dev_info_t *);
6588447a05SGarrett D'Amore static int audioixp_suspend(dev_info_t *);
6688447a05SGarrett D'Amore 
6788447a05SGarrett D'Amore /*
6888447a05SGarrett D'Amore  * Entry point routine prototypes
6988447a05SGarrett D'Amore  */
70*68c47f65SGarrett D'Amore static int audioixp_open(void *, int, unsigned *, caddr_t *);
7188447a05SGarrett D'Amore static void audioixp_close(void *);
7288447a05SGarrett D'Amore static int audioixp_start(void *);
7388447a05SGarrett D'Amore static void audioixp_stop(void *);
7488447a05SGarrett D'Amore static int audioixp_format(void *);
7588447a05SGarrett D'Amore static int audioixp_channels(void *);
7688447a05SGarrett D'Amore static int audioixp_rate(void *);
7788447a05SGarrett D'Amore static uint64_t audioixp_count(void *);
7888447a05SGarrett D'Amore static void audioixp_sync(void *, unsigned);
7988447a05SGarrett D'Amore 
8088447a05SGarrett D'Amore static audio_engine_ops_t audioixp_engine_ops = {
8188447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
8288447a05SGarrett D'Amore 	audioixp_open,
8388447a05SGarrett D'Amore 	audioixp_close,
8488447a05SGarrett D'Amore 	audioixp_start,
8588447a05SGarrett D'Amore 	audioixp_stop,
8688447a05SGarrett D'Amore 	audioixp_count,
8788447a05SGarrett D'Amore 	audioixp_format,
8888447a05SGarrett D'Amore 	audioixp_channels,
8988447a05SGarrett D'Amore 	audioixp_rate,
9088447a05SGarrett D'Amore 	audioixp_sync,
91f9ead4a5SGarrett D'Amore 	NULL,
92f9ead4a5SGarrett D'Amore 	NULL,
93f9ead4a5SGarrett D'Amore 	NULL
9488447a05SGarrett D'Amore };
9588447a05SGarrett D'Amore 
96d8a7fe16SGarrett D'Amore /*
97d8a7fe16SGarrett D'Amore  * We drive audioixp in stereo only, so we don't want to display controls
98d8a7fe16SGarrett D'Amore  * that are used for multichannel codecs.  Note that this multichannel
99d8a7fe16SGarrett D'Amore  * configuration limitation is a problem for audioixp devices.
100d8a7fe16SGarrett D'Amore  */
101d8a7fe16SGarrett D'Amore const char *audioixp_remove_ac97[] = {
102d8a7fe16SGarrett D'Amore 	AUDIO_CTRL_ID_CENTER,
103d8a7fe16SGarrett D'Amore 	AUDIO_CTRL_ID_LFE,
104d8a7fe16SGarrett D'Amore 	AUDIO_CTRL_ID_SURROUND,
105d8a7fe16SGarrett D'Amore 	AUDIO_CTRL_ID_JACK1,
106d8a7fe16SGarrett D'Amore 	AUDIO_CTRL_ID_JACK2,
107d8a7fe16SGarrett D'Amore };
10888447a05SGarrett D'Amore 
10988447a05SGarrett D'Amore /*
11088447a05SGarrett D'Amore  * Local Routine Prototypes
11188447a05SGarrett D'Amore  */
11288447a05SGarrett D'Amore static int audioixp_attach(dev_info_t *);
11388447a05SGarrett D'Amore static int audioixp_detach(dev_info_t *);
11488447a05SGarrett D'Amore static int audioixp_alloc_port(audioixp_state_t *, int);
11588447a05SGarrett D'Amore static void audioixp_update_port(audioixp_port_t *);
11688447a05SGarrett D'Amore 
11788447a05SGarrett D'Amore static int audioixp_codec_sync(audioixp_state_t *);
11888447a05SGarrett D'Amore static void audioixp_wr97(void *, uint8_t, uint16_t);
11988447a05SGarrett D'Amore static uint16_t audioixp_rd97(void *, uint8_t);
12088447a05SGarrett D'Amore static int audioixp_reset_ac97(audioixp_state_t *);
12188447a05SGarrett D'Amore static int audioixp_map_regs(audioixp_state_t *);
12288447a05SGarrett D'Amore static void audioixp_unmap_regs(audioixp_state_t *);
12388447a05SGarrett D'Amore static int audioixp_chip_init(audioixp_state_t *);
12488447a05SGarrett D'Amore static void audioixp_destroy(audioixp_state_t *);
12588447a05SGarrett D'Amore 
12688447a05SGarrett D'Amore /*
12788447a05SGarrett D'Amore  * Global variables, but used only by this file.
12888447a05SGarrett D'Amore  */
12988447a05SGarrett D'Amore 
13088447a05SGarrett D'Amore /*
13188447a05SGarrett D'Amore  * DDI Structures
13288447a05SGarrett D'Amore  */
13388447a05SGarrett D'Amore 
13488447a05SGarrett D'Amore /* Device operations structure */
13588447a05SGarrett D'Amore static struct dev_ops audioixp_dev_ops = {
13688447a05SGarrett D'Amore 	DEVO_REV,		/* devo_rev */
13788447a05SGarrett D'Amore 	0,			/* devo_refcnt */
13888447a05SGarrett D'Amore 	NULL,			/* devo_getinfo */
13988447a05SGarrett D'Amore 	nulldev,		/* devo_identify - obsolete */
14088447a05SGarrett D'Amore 	nulldev,		/* devo_probe */
14188447a05SGarrett D'Amore 	audioixp_ddi_attach,	/* devo_attach */
14288447a05SGarrett D'Amore 	audioixp_ddi_detach,	/* devo_detach */
14388447a05SGarrett D'Amore 	nodev,			/* devo_reset */
14488447a05SGarrett D'Amore 	NULL,			/* devi_cb_ops */
14588447a05SGarrett D'Amore 	NULL,			/* devo_bus_ops */
14688447a05SGarrett D'Amore 	NULL,			/* devo_power */
14788447a05SGarrett D'Amore 	audioixp_quiesce,	/* devo_quiesce */
14888447a05SGarrett D'Amore };
14988447a05SGarrett D'Amore 
15088447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
15188447a05SGarrett D'Amore static struct modldrv audioixp_modldrv = {
15288447a05SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
15388447a05SGarrett D'Amore 	IXP_MOD_NAME,		/* drv_linkinfo */
15488447a05SGarrett D'Amore 	&audioixp_dev_ops,	/* drv_dev_ops */
15588447a05SGarrett D'Amore };
15688447a05SGarrett D'Amore 
15788447a05SGarrett D'Amore /* Module linkage structure */
15888447a05SGarrett D'Amore static struct modlinkage audioixp_modlinkage = {
15988447a05SGarrett D'Amore 	MODREV_1,			/* ml_rev */
16088447a05SGarrett D'Amore 	(void *)&audioixp_modldrv,	/* ml_linkage */
16188447a05SGarrett D'Amore 	NULL				/* NULL terminates the list */
16288447a05SGarrett D'Amore };
16388447a05SGarrett D'Amore 
16488447a05SGarrett D'Amore /*
16588447a05SGarrett D'Amore  * device access attributes for register mapping
16688447a05SGarrett D'Amore  */
16788447a05SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
16888447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
16988447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
17088447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
17188447a05SGarrett D'Amore };
17288447a05SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
17388447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
17488447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
17588447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
17688447a05SGarrett D'Amore };
17788447a05SGarrett D'Amore 
17888447a05SGarrett D'Amore /*
17988447a05SGarrett D'Amore  * DMA attributes of buffer descriptor list
18088447a05SGarrett D'Amore  */
18188447a05SGarrett D'Amore static ddi_dma_attr_t	bdlist_dma_attr = {
18288447a05SGarrett D'Amore 	DMA_ATTR_V0,	/* version */
18388447a05SGarrett D'Amore 	0,		/* addr_lo */
18488447a05SGarrett D'Amore 	0xffffffff,	/* addr_hi */
18588447a05SGarrett D'Amore 	0x0000ffff,	/* count_max */
18688447a05SGarrett D'Amore 	8,		/* align, BDL must be aligned on a 8-byte boundary */
18788447a05SGarrett D'Amore 	0x3c,		/* burstsize */
18888447a05SGarrett D'Amore 	8,		/* minxfer, set to the size of a BDlist entry */
18988447a05SGarrett D'Amore 	0x0000ffff,	/* maxxfer */
19088447a05SGarrett D'Amore 	0x00000fff,	/* seg, set to the RAM pagesize of intel platform */
19188447a05SGarrett D'Amore 	1,		/* sgllen, there's no scatter-gather list */
19288447a05SGarrett D'Amore 	8,		/* granular, set to the value of minxfer */
19388447a05SGarrett D'Amore 	0		/* flags, use virtual address */
19488447a05SGarrett D'Amore };
19588447a05SGarrett D'Amore 
19688447a05SGarrett D'Amore /*
19788447a05SGarrett D'Amore  * DMA attributes of buffers to be used to receive/send audio data
19888447a05SGarrett D'Amore  */
19988447a05SGarrett D'Amore static ddi_dma_attr_t	sample_buf_dma_attr = {
20088447a05SGarrett D'Amore 	DMA_ATTR_V0,
20188447a05SGarrett D'Amore 	0,		/* addr_lo */
20288447a05SGarrett D'Amore 	0xffffffff,	/* addr_hi */
20388447a05SGarrett D'Amore 	0x0001fffe,	/* count_max */
20488447a05SGarrett D'Amore 	4,		/* align, data buffer is aligned on a 2-byte boundary */
20588447a05SGarrett D'Amore 	0x3c,		/* burstsize */
20688447a05SGarrett D'Amore 	4,		/* minxfer, set to the size of a sample data */
20788447a05SGarrett D'Amore 	0x0001ffff,	/* maxxfer */
20888447a05SGarrett D'Amore 	0x0001ffff,	/* seg */
20988447a05SGarrett D'Amore 	1,		/* sgllen, no scatter-gather */
21088447a05SGarrett D'Amore 	4,		/* granular, set to the value of minxfer */
21188447a05SGarrett D'Amore 	0,		/* flags, use virtual address */
21288447a05SGarrett D'Amore };
21388447a05SGarrett D'Amore 
21488447a05SGarrett D'Amore /*
21588447a05SGarrett D'Amore  * _init()
21688447a05SGarrett D'Amore  *
21788447a05SGarrett D'Amore  * Description:
21888447a05SGarrett D'Amore  *	Driver initialization, called when driver is first loaded.
21988447a05SGarrett D'Amore  *	This is how access is initially given to all the static structures.
22088447a05SGarrett D'Amore  *
22188447a05SGarrett D'Amore  * Arguments:
22288447a05SGarrett D'Amore  *	None
22388447a05SGarrett D'Amore  *
22488447a05SGarrett D'Amore  * Returns:
22588447a05SGarrett D'Amore  *	ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
22688447a05SGarrett D'Amore  *	mod_install() status, see mod_install(9f)
22788447a05SGarrett D'Amore  */
22888447a05SGarrett D'Amore int
_init(void)22988447a05SGarrett D'Amore _init(void)
23088447a05SGarrett D'Amore {
23188447a05SGarrett D'Amore 	int	error;
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 	audio_init_ops(&audioixp_dev_ops, IXP_NAME);
23488447a05SGarrett D'Amore 
23588447a05SGarrett D'Amore 	if ((error = mod_install(&audioixp_modlinkage)) != 0) {
23688447a05SGarrett D'Amore 		audio_fini_ops(&audioixp_dev_ops);
23788447a05SGarrett D'Amore 	}
23888447a05SGarrett D'Amore 
23988447a05SGarrett D'Amore 	return (error);
24088447a05SGarrett D'Amore }
24188447a05SGarrett D'Amore 
24288447a05SGarrett D'Amore /*
24388447a05SGarrett D'Amore  * _fini()
24488447a05SGarrett D'Amore  *
24588447a05SGarrett D'Amore  * Description:
24688447a05SGarrett D'Amore  *	Module de-initialization, called when the driver is to be unloaded.
24788447a05SGarrett D'Amore  *
24888447a05SGarrett D'Amore  * Arguments:
24988447a05SGarrett D'Amore  *	None
25088447a05SGarrett D'Amore  *
25188447a05SGarrett D'Amore  * Returns:
25288447a05SGarrett D'Amore  *	mod_remove() status, see mod_remove(9f)
25388447a05SGarrett D'Amore  */
25488447a05SGarrett D'Amore int
_fini(void)25588447a05SGarrett D'Amore _fini(void)
25688447a05SGarrett D'Amore {
25788447a05SGarrett D'Amore 	int		error;
25888447a05SGarrett D'Amore 
25988447a05SGarrett D'Amore 	if ((error = mod_remove(&audioixp_modlinkage)) != 0) {
26088447a05SGarrett D'Amore 		return (error);
26188447a05SGarrett D'Amore 	}
26288447a05SGarrett D'Amore 
26388447a05SGarrett D'Amore 	audio_fini_ops(&audioixp_dev_ops);
26488447a05SGarrett D'Amore 
26588447a05SGarrett D'Amore 	return (0);
26688447a05SGarrett D'Amore }
26788447a05SGarrett D'Amore 
26888447a05SGarrett D'Amore /*
26988447a05SGarrett D'Amore  * _info()
27088447a05SGarrett D'Amore  *
27188447a05SGarrett D'Amore  * Description:
27288447a05SGarrett D'Amore  *	Module information, returns information about the driver.
27388447a05SGarrett D'Amore  *
27488447a05SGarrett D'Amore  * Arguments:
27588447a05SGarrett D'Amore  *	modinfo		*modinfop	Pointer to the opaque modinfo structure
27688447a05SGarrett D'Amore  *
27788447a05SGarrett D'Amore  * Returns:
27888447a05SGarrett D'Amore  *	mod_info() status, see mod_info(9f)
27988447a05SGarrett D'Amore  */
28088447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)28188447a05SGarrett D'Amore _info(struct modinfo *modinfop)
28288447a05SGarrett D'Amore {
28388447a05SGarrett D'Amore 	return (mod_info(&audioixp_modlinkage, modinfop));
28488447a05SGarrett D'Amore }
28588447a05SGarrett D'Amore 
28688447a05SGarrett D'Amore 
28788447a05SGarrett D'Amore /* ******************* Driver Entry Points ********************************* */
28888447a05SGarrett D'Amore 
28988447a05SGarrett D'Amore /*
29088447a05SGarrett D'Amore  * audioixp_ddi_attach()
29188447a05SGarrett D'Amore  *
29288447a05SGarrett D'Amore  * Description:
29388447a05SGarrett D'Amore  *	Attach an instance of the audioixp driver.
29488447a05SGarrett D'Amore  *
29588447a05SGarrett D'Amore  * Arguments:
29688447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
29788447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
29888447a05SGarrett D'Amore  *
29988447a05SGarrett D'Amore  * Returns:
30088447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
30188447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
30288447a05SGarrett D'Amore  */
30388447a05SGarrett D'Amore static int
audioixp_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30488447a05SGarrett D'Amore audioixp_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30588447a05SGarrett D'Amore {
30688447a05SGarrett D'Amore 	switch (cmd) {
30788447a05SGarrett D'Amore 	case DDI_ATTACH:
30888447a05SGarrett D'Amore 		return (audioixp_attach(dip));
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 	/*
31188447a05SGarrett D'Amore 	 * now, no suspend/resume supported. we'll do it in the future.
31288447a05SGarrett D'Amore 	 */
31388447a05SGarrett D'Amore 	case DDI_RESUME:
31488447a05SGarrett D'Amore 		return (audioixp_resume(dip));
31588447a05SGarrett D'Amore 	default:
31688447a05SGarrett D'Amore 		return (DDI_FAILURE);
31788447a05SGarrett D'Amore 	}
31888447a05SGarrett D'Amore }
31988447a05SGarrett D'Amore 
32088447a05SGarrett D'Amore /*
32188447a05SGarrett D'Amore  * audioixp_ddi_detach()
32288447a05SGarrett D'Amore  *
32388447a05SGarrett D'Amore  * Description:
32488447a05SGarrett D'Amore  *	Detach an instance of the audioixp driver.
32588447a05SGarrett D'Amore  *
32688447a05SGarrett D'Amore  * Arguments:
32788447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
32888447a05SGarrett D'Amore  *	ddi_detach_cmd_t	cmd	Detach command
32988447a05SGarrett D'Amore  *
33088447a05SGarrett D'Amore  * Returns:
33188447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
33288447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
33388447a05SGarrett D'Amore  */
33488447a05SGarrett D'Amore static int
audioixp_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)33588447a05SGarrett D'Amore audioixp_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
33688447a05SGarrett D'Amore {
33788447a05SGarrett D'Amore 	switch (cmd) {
33888447a05SGarrett D'Amore 	case DDI_DETACH:
33988447a05SGarrett D'Amore 		return (audioixp_detach(dip));
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore 	/*
34288447a05SGarrett D'Amore 	 * now, no suspend/resume supported. we'll do it in the future.
34388447a05SGarrett D'Amore 	 */
34488447a05SGarrett D'Amore 	case DDI_SUSPEND:
34588447a05SGarrett D'Amore 		return (audioixp_suspend(dip));
34688447a05SGarrett D'Amore 
34788447a05SGarrett D'Amore 	default:
34888447a05SGarrett D'Amore 		return (DDI_FAILURE);
34988447a05SGarrett D'Amore 	}
35088447a05SGarrett D'Amore }
35188447a05SGarrett D'Amore 
35288447a05SGarrett D'Amore /*
35388447a05SGarrett D'Amore  * quiesce(9E) entry point.
35488447a05SGarrett D'Amore  *
35588447a05SGarrett D'Amore  * This function is called when the system is single-threaded at high
35688447a05SGarrett D'Amore  * PIL with preemption disabled. Therefore, this function must not be blocked.
35788447a05SGarrett D'Amore  *
35888447a05SGarrett D'Amore  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
35988447a05SGarrett D'Amore  * DDI_FAILURE indicates an error condition and should almost never happen.
36088447a05SGarrett D'Amore  */
36188447a05SGarrett D'Amore static int
audioixp_quiesce(dev_info_t * dip)36288447a05SGarrett D'Amore audioixp_quiesce(dev_info_t *dip)
36388447a05SGarrett D'Amore {
36488447a05SGarrett D'Amore 	audioixp_state_t		*statep;
36588447a05SGarrett D'Amore 
36688447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
36788447a05SGarrett D'Amore 	ASSERT(statep != NULL);
36888447a05SGarrett D'Amore 
36988447a05SGarrett D'Amore 	/* stop DMA engines */
370*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
371*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
372*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
373*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
37488447a05SGarrett D'Amore 
37588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
37688447a05SGarrett D'Amore }
37788447a05SGarrett D'Amore 
37888447a05SGarrett D'Amore static int
audioixp_suspend(dev_info_t * dip)37988447a05SGarrett D'Amore audioixp_suspend(dev_info_t *dip)
38088447a05SGarrett D'Amore {
38188447a05SGarrett D'Amore 	audioixp_state_t		*statep;
38288447a05SGarrett D'Amore 
38388447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
38488447a05SGarrett D'Amore 	ASSERT(statep != NULL);
38588447a05SGarrett D'Amore 
386*68c47f65SGarrett D'Amore 	audio_dev_suspend(statep->adev);
38788447a05SGarrett D'Amore 
38888447a05SGarrett D'Amore 	return (DDI_SUCCESS);
38988447a05SGarrett D'Amore }
39088447a05SGarrett D'Amore 
39188447a05SGarrett D'Amore static int
audioixp_resume(dev_info_t * dip)39288447a05SGarrett D'Amore audioixp_resume(dev_info_t *dip)
39388447a05SGarrett D'Amore {
39488447a05SGarrett D'Amore 	audioixp_state_t		*statep;
39588447a05SGarrett D'Amore 
39688447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
39788447a05SGarrett D'Amore 	ASSERT(statep != NULL);
39888447a05SGarrett D'Amore 
39988447a05SGarrett D'Amore 	if (audioixp_chip_init(statep) != DDI_SUCCESS) {
400*68c47f65SGarrett D'Amore 		audio_dev_warn(statep->adev, "DDI_RESUME failed to init chip");
40188447a05SGarrett D'Amore 		return (DDI_SUCCESS);
40288447a05SGarrett D'Amore 	}
40388447a05SGarrett D'Amore 
404*68c47f65SGarrett D'Amore 	ac97_reset(statep->ac97);
405*68c47f65SGarrett D'Amore 	audio_dev_resume(statep->adev);
40688447a05SGarrett D'Amore 
40788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
40888447a05SGarrett D'Amore }
40988447a05SGarrett D'Amore 
41088447a05SGarrett D'Amore /*
41188447a05SGarrett D'Amore  * audioixp_open()
41288447a05SGarrett D'Amore  *
41388447a05SGarrett D'Amore  * Description:
41488447a05SGarrett D'Amore  *	Opens a DMA engine for use.
41588447a05SGarrett D'Amore  *
41688447a05SGarrett D'Amore  * Arguments:
41788447a05SGarrett D'Amore  *	void		*arg		The DMA engine to set up
41888447a05SGarrett D'Amore  *	int		flag		Open flags
419*68c47f65SGarrett D'Amore  *	unsigned	*nframesp	Receives number of frames
42088447a05SGarrett D'Amore  *	caddr_t		*bufp		Receives kernel data buffer
42188447a05SGarrett D'Amore  *
42288447a05SGarrett D'Amore  * Returns:
42388447a05SGarrett D'Amore  *	0	on success
42488447a05SGarrett D'Amore  *	errno	on failure
42588447a05SGarrett D'Amore  */
42688447a05SGarrett D'Amore static int
audioixp_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)427*68c47f65SGarrett D'Amore audioixp_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
42888447a05SGarrett D'Amore {
42988447a05SGarrett D'Amore 	audioixp_port_t	*port = arg;
43088447a05SGarrett D'Amore 
43188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
43288447a05SGarrett D'Amore 
43388447a05SGarrett D'Amore 	port->started = B_FALSE;
43488447a05SGarrett D'Amore 	port->count = 0;
43588447a05SGarrett D'Amore 	port->offset = 0;
436*68c47f65SGarrett D'Amore 	*nframesp = port->nframes;
43788447a05SGarrett D'Amore 	*bufp = port->samp_kaddr;
43888447a05SGarrett D'Amore 
43988447a05SGarrett D'Amore 	return (0);
44088447a05SGarrett D'Amore }
44188447a05SGarrett D'Amore 
44288447a05SGarrett D'Amore /*
44388447a05SGarrett D'Amore  * audioixp_close()
44488447a05SGarrett D'Amore  *
44588447a05SGarrett D'Amore  * Description:
44688447a05SGarrett D'Amore  *	Closes an audio DMA engine that was previously opened.  Since
44788447a05SGarrett D'Amore  *	nobody is using it, we take this opportunity to possibly power
44888447a05SGarrett D'Amore  *	down the entire device.
44988447a05SGarrett D'Amore  *
45088447a05SGarrett D'Amore  * Arguments:
45188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to shut down
45288447a05SGarrett D'Amore  */
45388447a05SGarrett D'Amore static void
audioixp_close(void * arg)45488447a05SGarrett D'Amore audioixp_close(void *arg)
45588447a05SGarrett D'Amore {
456*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
45788447a05SGarrett D'Amore }
45888447a05SGarrett D'Amore 
45988447a05SGarrett D'Amore /*
46088447a05SGarrett D'Amore  * audioixp_stop()
46188447a05SGarrett D'Amore  *
46288447a05SGarrett D'Amore  * Description:
46388447a05SGarrett D'Amore  *	This is called by the framework to stop a port that is
46488447a05SGarrett D'Amore  *	transferring data.
46588447a05SGarrett D'Amore  *
46688447a05SGarrett D'Amore  * Arguments:
46788447a05SGarrett D'Amore  *	void	*arg		The DMA engine to stop
46888447a05SGarrett D'Amore  */
46988447a05SGarrett D'Amore static void
audioixp_stop(void * arg)47088447a05SGarrett D'Amore audioixp_stop(void *arg)
47188447a05SGarrett D'Amore {
47288447a05SGarrett D'Amore 	audioixp_port_t		*port = arg;
47388447a05SGarrett D'Amore 	audioixp_state_t	*statep = port->statep;
47488447a05SGarrett D'Amore 
47588447a05SGarrett D'Amore 	mutex_enter(&statep->inst_lock);
476*68c47f65SGarrett D'Amore 	if (port->num == IXP_REC) {
477*68c47f65SGarrett D'Amore 		CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
478*68c47f65SGarrett D'Amore 		CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
479*68c47f65SGarrett D'Amore 	} else {
480*68c47f65SGarrett D'Amore 		CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
481*68c47f65SGarrett D'Amore 		CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
48288447a05SGarrett D'Amore 	}
48388447a05SGarrett D'Amore 	mutex_exit(&statep->inst_lock);
48488447a05SGarrett D'Amore }
48588447a05SGarrett D'Amore 
48688447a05SGarrett D'Amore /*
48788447a05SGarrett D'Amore  * audioixp_start()
48888447a05SGarrett D'Amore  *
48988447a05SGarrett D'Amore  * Description:
49088447a05SGarrett D'Amore  *	This is called by the framework to start a port transferring data.
49188447a05SGarrett D'Amore  *
49288447a05SGarrett D'Amore  * Arguments:
49388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to start
49488447a05SGarrett D'Amore  *
49588447a05SGarrett D'Amore  * Returns:
49688447a05SGarrett D'Amore  *	0 	on success (never fails, errno if it did)
49788447a05SGarrett D'Amore  */
49888447a05SGarrett D'Amore static int
audioixp_start(void * arg)49988447a05SGarrett D'Amore audioixp_start(void *arg)
50088447a05SGarrett D'Amore {
50188447a05SGarrett D'Amore 	audioixp_port_t		*port = arg;
50288447a05SGarrett D'Amore 	audioixp_state_t	*statep = port->statep;
50388447a05SGarrett D'Amore 
50488447a05SGarrett D'Amore 	mutex_enter(&statep->inst_lock);
505*68c47f65SGarrett D'Amore 
506*68c47f65SGarrett D'Amore 	port->offset = 0;
507*68c47f65SGarrett D'Amore 
508*68c47f65SGarrett D'Amore 	if (port->num == IXP_REC) {
509*68c47f65SGarrett D'Amore 		PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN);
510*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_IN);
511*68c47f65SGarrett D'Amore 
512*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
513*68c47f65SGarrett D'Amore 		PUT32(IXP_AUDIO_IN_DMA_LINK_P,
514*68c47f65SGarrett D'Amore 		    port->bdl_paddr | IXP_AUDIO_IN_DMA_LINK_P_EN);
515*68c47f65SGarrett D'Amore 
516*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
517*68c47f65SGarrett D'Amore 	} else {
518*68c47f65SGarrett D'Amore 		uint32_t slot = GET32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD);
519*68c47f65SGarrett D'Amore 		PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_OUT);
520*68c47f65SGarrett D'Amore 		/* clear all slots */
521*68c47f65SGarrett D'Amore 		slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_3 |
522*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_4 |
523*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_5 |
524*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_6 |
525*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_7 |
526*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_8 |
527*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_9 |
528*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_10 |
529*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_11 |
530*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_12);
531*68c47f65SGarrett D'Amore 		/* enable AC'97 output slots (depending on output channels) */
532*68c47f65SGarrett D'Amore 		slot |= IXP_AUDIO_OUT_DMA_SLOT_3 |
533*68c47f65SGarrett D'Amore 		    IXP_AUDIO_OUT_DMA_SLOT_4;
534*68c47f65SGarrett D'Amore 		if (port->nchan >= 4) {
535*68c47f65SGarrett D'Amore 			slot |= IXP_AUDIO_OUT_DMA_SLOT_6 |
536*68c47f65SGarrett D'Amore 			    IXP_AUDIO_OUT_DMA_SLOT_9;
537*68c47f65SGarrett D'Amore 		}
538*68c47f65SGarrett D'Amore 		if (port->nchan >= 6) {
539*68c47f65SGarrett D'Amore 			slot |= IXP_AUDIO_OUT_DMA_SLOT_7 |
540*68c47f65SGarrett D'Amore 			    IXP_AUDIO_OUT_DMA_SLOT_8;
541*68c47f65SGarrett D'Amore 		}
542*68c47f65SGarrett D'Amore 
543*68c47f65SGarrett D'Amore 		PUT32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD, slot);
544*68c47f65SGarrett D'Amore 
545*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_OUT);
546*68c47f65SGarrett D'Amore 
547*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
548*68c47f65SGarrett D'Amore 		PUT32(IXP_AUDIO_OUT_DMA_LINK_P,
549*68c47f65SGarrett D'Amore 		    port->bdl_paddr | IXP_AUDIO_OUT_DMA_LINK_P_EN);
550*68c47f65SGarrett D'Amore 
551*68c47f65SGarrett D'Amore 		SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
55288447a05SGarrett D'Amore 	}
55388447a05SGarrett D'Amore 	mutex_exit(&statep->inst_lock);
55488447a05SGarrett D'Amore 	return (0);
55588447a05SGarrett D'Amore }
55688447a05SGarrett D'Amore 
55788447a05SGarrett D'Amore /*
55888447a05SGarrett D'Amore  * audioixp_format()
55988447a05SGarrett D'Amore  *
56088447a05SGarrett D'Amore  * Description:
56188447a05SGarrett D'Amore  *	This is called by the framework to query the format for the device.
56288447a05SGarrett D'Amore  *
56388447a05SGarrett D'Amore  * Arguments:
56488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
56588447a05SGarrett D'Amore  *
56688447a05SGarrett D'Amore  * Returns:
56788447a05SGarrett D'Amore  *	AUDIO_FORMAT_S16_LE
56888447a05SGarrett D'Amore  */
56988447a05SGarrett D'Amore static int
audioixp_format(void * arg)57088447a05SGarrett D'Amore audioixp_format(void *arg)
57188447a05SGarrett D'Amore {
57288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
57388447a05SGarrett D'Amore 
57488447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
57588447a05SGarrett D'Amore }
57688447a05SGarrett D'Amore 
57788447a05SGarrett D'Amore /*
57888447a05SGarrett D'Amore  * audioixp_channels()
57988447a05SGarrett D'Amore  *
58088447a05SGarrett D'Amore  * Description:
58188447a05SGarrett D'Amore  *	This is called by the framework to query the channels for the device.
58288447a05SGarrett D'Amore  *
58388447a05SGarrett D'Amore  * Arguments:
58488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
58588447a05SGarrett D'Amore  *
58688447a05SGarrett D'Amore  * Returns:
58788447a05SGarrett D'Amore  *	Number of channels for the device.
58888447a05SGarrett D'Amore  */
58988447a05SGarrett D'Amore static int
audioixp_channels(void * arg)59088447a05SGarrett D'Amore audioixp_channels(void *arg)
59188447a05SGarrett D'Amore {
59288447a05SGarrett D'Amore 	audioixp_port_t *port = arg;
59388447a05SGarrett D'Amore 
59488447a05SGarrett D'Amore 	return (port->nchan);
59588447a05SGarrett D'Amore }
59688447a05SGarrett D'Amore 
59788447a05SGarrett D'Amore /*
59888447a05SGarrett D'Amore  * audioixp_rate()
59988447a05SGarrett D'Amore  *
60088447a05SGarrett D'Amore  * Description:
60188447a05SGarrett D'Amore  *	This is called by the framework to query the rate of the device.
60288447a05SGarrett D'Amore  *
60388447a05SGarrett D'Amore  * Arguments:
60488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
60588447a05SGarrett D'Amore  *
60688447a05SGarrett D'Amore  * Returns:
60788447a05SGarrett D'Amore  *	48000
60888447a05SGarrett D'Amore  */
60988447a05SGarrett D'Amore static int
audioixp_rate(void * arg)61088447a05SGarrett D'Amore audioixp_rate(void *arg)
61188447a05SGarrett D'Amore {
61288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
61388447a05SGarrett D'Amore 
61488447a05SGarrett D'Amore 	return (48000);
61588447a05SGarrett D'Amore }
61688447a05SGarrett D'Amore 
61788447a05SGarrett D'Amore /*
61888447a05SGarrett D'Amore  * audioixp_count()
61988447a05SGarrett D'Amore  *
62088447a05SGarrett D'Amore  * Description:
62188447a05SGarrett D'Amore  *	This is called by the framework to get the engine's frame counter
62288447a05SGarrett D'Amore  *
62388447a05SGarrett D'Amore  * Arguments:
62488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
62588447a05SGarrett D'Amore  *
62688447a05SGarrett D'Amore  * Returns:
62788447a05SGarrett D'Amore  *	frame count for current engine
62888447a05SGarrett D'Amore  */
62988447a05SGarrett D'Amore static uint64_t
audioixp_count(void * arg)63088447a05SGarrett D'Amore audioixp_count(void *arg)
63188447a05SGarrett D'Amore {
63288447a05SGarrett D'Amore 	audioixp_port_t		*port = arg;
63388447a05SGarrett D'Amore 	audioixp_state_t	*statep = port->statep;
63488447a05SGarrett D'Amore 	uint64_t		val;
63588447a05SGarrett D'Amore 
63688447a05SGarrett D'Amore 	mutex_enter(&statep->inst_lock);
63788447a05SGarrett D'Amore 	audioixp_update_port(port);
63888447a05SGarrett D'Amore 	val = port->count;
63988447a05SGarrett D'Amore 	mutex_exit(&statep->inst_lock);
64088447a05SGarrett D'Amore 
64188447a05SGarrett D'Amore 	return (val);
64288447a05SGarrett D'Amore }
64388447a05SGarrett D'Amore 
64488447a05SGarrett D'Amore /*
64588447a05SGarrett D'Amore  * audioixp_sync()
64688447a05SGarrett D'Amore  *
64788447a05SGarrett D'Amore  * Description:
64888447a05SGarrett D'Amore  *	This is called by the framework to synchronize DMA caches.
64988447a05SGarrett D'Amore  *
65088447a05SGarrett D'Amore  * Arguments:
65188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to sync
65288447a05SGarrett D'Amore  */
65388447a05SGarrett D'Amore static void
audioixp_sync(void * arg,unsigned nframes)65488447a05SGarrett D'Amore audioixp_sync(void *arg, unsigned nframes)
65588447a05SGarrett D'Amore {
65688447a05SGarrett D'Amore 	audioixp_port_t *port = arg;
65788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
65888447a05SGarrett D'Amore 
65988447a05SGarrett D'Amore 	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
66088447a05SGarrett D'Amore }
66188447a05SGarrett D'Amore 
66288447a05SGarrett D'Amore /* *********************** Local Routines *************************** */
66388447a05SGarrett D'Amore 
66488447a05SGarrett D'Amore /*
66588447a05SGarrett D'Amore  * audioixp_alloc_port()
66688447a05SGarrett D'Amore  *
66788447a05SGarrett D'Amore  * Description:
66888447a05SGarrett D'Amore  *	This routine allocates the DMA handles and the memory for the
66988447a05SGarrett D'Amore  *	DMA engines to use.  It also configures the BDL lists properly
67088447a05SGarrett D'Amore  *	for use.
67188447a05SGarrett D'Amore  *
67288447a05SGarrett D'Amore  * Arguments:
67388447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's devinfo
67488447a05SGarrett D'Amore  *
67588447a05SGarrett D'Amore  * Returns:
67688447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
67788447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
67888447a05SGarrett D'Amore  */
67988447a05SGarrett D'Amore static int
audioixp_alloc_port(audioixp_state_t * statep,int num)68088447a05SGarrett D'Amore audioixp_alloc_port(audioixp_state_t *statep, int num)
68188447a05SGarrett D'Amore {
68288447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
68388447a05SGarrett D'Amore 	uint_t			count;
68488447a05SGarrett D'Amore 	int			dir;
68588447a05SGarrett D'Amore 	unsigned		caps;
68688447a05SGarrett D'Amore 	audio_dev_t		*adev;
68788447a05SGarrett D'Amore 	audioixp_port_t		*port;
68888447a05SGarrett D'Amore 	uint32_t		paddr;
68988447a05SGarrett D'Amore 	int			rc;
69088447a05SGarrett D'Amore 	dev_info_t		*dip;
69188447a05SGarrett D'Amore 	audioixp_bd_entry_t	*bdentry;
69288447a05SGarrett D'Amore 
69388447a05SGarrett D'Amore 	adev = statep->adev;
69488447a05SGarrett D'Amore 	dip = statep->dip;
69588447a05SGarrett D'Amore 
69688447a05SGarrett D'Amore 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
69788447a05SGarrett D'Amore 	port->statep = statep;
69888447a05SGarrett D'Amore 	port->started = B_FALSE;
69988447a05SGarrett D'Amore 	port->num = num;
70088447a05SGarrett D'Amore 
70188447a05SGarrett D'Amore 	switch (num) {
70288447a05SGarrett D'Amore 	case IXP_REC:
70388447a05SGarrett D'Amore 		statep->rec_port = port;
70488447a05SGarrett D'Amore 		dir = DDI_DMA_READ;
70588447a05SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
70688447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
70788447a05SGarrett D'Amore 		port->nchan = 2;
70888447a05SGarrett D'Amore 		break;
70988447a05SGarrett D'Amore 	case IXP_PLAY:
71088447a05SGarrett D'Amore 		statep->play_port = port;
71188447a05SGarrett D'Amore 		dir = DDI_DMA_WRITE;
71288447a05SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
71388447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORDEV;
714d8a7fe16SGarrett D'Amore 		/*
715d8a7fe16SGarrett D'Amore 		 * We allow for end users to configure more channels
716d8a7fe16SGarrett D'Amore 		 * than just two, but we default to just two.  The
717d8a7fe16SGarrett D'Amore 		 * default stereo configuration works well.  On the
718d8a7fe16SGarrett D'Amore 		 * configurations we have tested, we've found that
719d8a7fe16SGarrett D'Amore 		 * more than two channels (or rather 6 channels) can
720d8a7fe16SGarrett D'Amore 		 * cause inexplicable noise.  The noise is more
721d8a7fe16SGarrett D'Amore 		 * noticeable when the system is running under load.
722d8a7fe16SGarrett D'Amore 		 * (Holding the space bar in "top" while playing an
723d8a7fe16SGarrett D'Amore 		 * MP3 is an easy way to recreate it.)  End users who
724d8a7fe16SGarrett D'Amore 		 * want to experiment, or have configurations that
725d8a7fe16SGarrett D'Amore 		 * don't suffer from this, may increase the channels
726d8a7fe16SGarrett D'Amore 		 * by setting this max-channels property.  We leave it
727d8a7fe16SGarrett D'Amore 		 * undocumented for now.
728d8a7fe16SGarrett D'Amore 		 */
729d8a7fe16SGarrett D'Amore 		port->nchan = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
730d8a7fe16SGarrett D'Amore 		    "max-channels", 2);
731d8a7fe16SGarrett D'Amore 		port->nchan = min(ac97_num_channels(statep->ac97),
732d8a7fe16SGarrett D'Amore 		    port->nchan);
733d8a7fe16SGarrett D'Amore 		port->nchan &= ~1;	/* make sure its an even number */
734d8a7fe16SGarrett D'Amore 		port->nchan = max(port->nchan, 2);
73588447a05SGarrett D'Amore 		break;
73688447a05SGarrett D'Amore 	default:
73788447a05SGarrett D'Amore 		audio_dev_warn(adev, "bad port number (%d)!", num);
73888447a05SGarrett D'Amore 		return (DDI_FAILURE);
73988447a05SGarrett D'Amore 	}
74088447a05SGarrett D'Amore 
741*68c47f65SGarrett D'Amore 	port->nframes = 4096;
742*68c47f65SGarrett D'Amore 	port->fragfr = port->nframes / IXP_BD_NUMS;
74388447a05SGarrett D'Amore 	port->fragsz = port->fragfr * port->nchan * 2;
744*68c47f65SGarrett D'Amore 	port->samp_size = port->nframes * port->nchan * 2;
74588447a05SGarrett D'Amore 
74688447a05SGarrett D'Amore 	/* allocate dma handle */
74788447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
74888447a05SGarrett D'Amore 	    NULL, &port->samp_dmah);
74988447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
75088447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
75188447a05SGarrett D'Amore 		return (DDI_FAILURE);
75288447a05SGarrett D'Amore 	}
75388447a05SGarrett D'Amore 	/* allocate DMA buffer */
75488447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
75588447a05SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
75688447a05SGarrett D'Amore 	    &port->samp_size, &port->samp_acch);
75788447a05SGarrett D'Amore 	if (rc == DDI_FAILURE) {
75888447a05SGarrett D'Amore 		audio_dev_warn(adev, "dma_mem_alloc failed");
75988447a05SGarrett D'Amore 		return (DDI_FAILURE);
76088447a05SGarrett D'Amore 	}
76188447a05SGarrett D'Amore 
76288447a05SGarrett D'Amore 	/* bind DMA buffer */
76388447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
76488447a05SGarrett D'Amore 	    port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
76588447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
76688447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
76788447a05SGarrett D'Amore 		audio_dev_warn(adev,
76888447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed: %d", rc);
76988447a05SGarrett D'Amore 		return (DDI_FAILURE);
77088447a05SGarrett D'Amore 	}
77188447a05SGarrett D'Amore 	port->samp_paddr = cookie.dmac_address;
77288447a05SGarrett D'Amore 
77388447a05SGarrett D'Amore 	/*
77488447a05SGarrett D'Amore 	 * now, from here we allocate DMA memory for buffer descriptor list.
77588447a05SGarrett D'Amore 	 * we allocate adjacent DMA memory for all DMA engines.
77688447a05SGarrett D'Amore 	 */
77788447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
77888447a05SGarrett D'Amore 	    NULL, &port->bdl_dmah);
77988447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
78088447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
78188447a05SGarrett D'Amore 		return (DDI_FAILURE);
78288447a05SGarrett D'Amore 	}
78388447a05SGarrett D'Amore 
78488447a05SGarrett D'Amore 	/*
78588447a05SGarrett D'Amore 	 * we allocate all buffer descriptors lists in continuous dma memory.
78688447a05SGarrett D'Amore 	 */
78788447a05SGarrett D'Amore 	port->bdl_size = sizeof (audioixp_bd_entry_t) * IXP_BD_NUMS;
78888447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
78988447a05SGarrett D'Amore 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
79088447a05SGarrett D'Amore 	    &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
79188447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
79288447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
79388447a05SGarrett D'Amore 		return (DDI_FAILURE);
79488447a05SGarrett D'Amore 	}
79588447a05SGarrett D'Amore 
79688447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
79788447a05SGarrett D'Amore 	    port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
79888447a05SGarrett D'Amore 	    NULL, &cookie, &count);
79988447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
80088447a05SGarrett D'Amore 		audio_dev_warn(adev, "addr_bind_handle failed");
80188447a05SGarrett D'Amore 		return (DDI_FAILURE);
80288447a05SGarrett D'Amore 	}
80388447a05SGarrett D'Amore 	port->bdl_paddr = cookie.dmac_address;
80488447a05SGarrett D'Amore 
80588447a05SGarrett D'Amore 	/*
80688447a05SGarrett D'Amore 	 * Wire up the BD list.
80788447a05SGarrett D'Amore 	 */
80888447a05SGarrett D'Amore 	paddr = port->samp_paddr;
80988447a05SGarrett D'Amore 	bdentry = (void *)port->bdl_kaddr;
81088447a05SGarrett D'Amore 
81188447a05SGarrett D'Amore 	for (int i = 0; i < IXP_BD_NUMS; i++) {
81288447a05SGarrett D'Amore 
81388447a05SGarrett D'Amore 		/* set base address of buffer */
81488447a05SGarrett D'Amore 		ddi_put32(port->bdl_acch, &bdentry->buf_base, paddr);
81588447a05SGarrett D'Amore 		ddi_put16(port->bdl_acch, &bdentry->status, 0);
81688447a05SGarrett D'Amore 		ddi_put16(port->bdl_acch, &bdentry->buf_len, port->fragsz / 4);
81788447a05SGarrett D'Amore 		ddi_put32(port->bdl_acch, &bdentry->next, port->bdl_paddr +
81888447a05SGarrett D'Amore 		    (((i + 1) % IXP_BD_NUMS) * sizeof (audioixp_bd_entry_t)));
81988447a05SGarrett D'Amore 		paddr += port->fragsz;
82088447a05SGarrett D'Amore 		bdentry++;
82188447a05SGarrett D'Amore 	}
82248f21d36SGarrett D'Amore 	(void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
82388447a05SGarrett D'Amore 
82488447a05SGarrett D'Amore 	port->engine = audio_engine_alloc(&audioixp_engine_ops, caps);
82588447a05SGarrett D'Amore 	if (port->engine == NULL) {
82688447a05SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
82788447a05SGarrett D'Amore 		return (DDI_FAILURE);
82888447a05SGarrett D'Amore 	}
82988447a05SGarrett D'Amore 
83088447a05SGarrett D'Amore 	audio_engine_set_private(port->engine, port);
83188447a05SGarrett D'Amore 	audio_dev_add_engine(adev, port->engine);
83288447a05SGarrett D'Amore 
83388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
83488447a05SGarrett D'Amore }
83588447a05SGarrett D'Amore 
83688447a05SGarrett D'Amore /*
83788447a05SGarrett D'Amore  * audioixp_free_port()
83888447a05SGarrett D'Amore  *
83988447a05SGarrett D'Amore  * Description:
84088447a05SGarrett D'Amore  *	This routine unbinds the DMA cookies, frees the DMA buffers,
84188447a05SGarrett D'Amore  *	deallocates the DMA handles.
84288447a05SGarrett D'Amore  *
84388447a05SGarrett D'Amore  * Arguments:
84488447a05SGarrett D'Amore  *	audioixp_port_t	*port	The port structure for a DMA engine.
84588447a05SGarrett D'Amore  */
84688447a05SGarrett D'Amore static void
audioixp_free_port(audioixp_port_t * port)84788447a05SGarrett D'Amore audioixp_free_port(audioixp_port_t *port)
84888447a05SGarrett D'Amore {
84988447a05SGarrett D'Amore 	if (port == NULL)
85088447a05SGarrett D'Amore 		return;
85188447a05SGarrett D'Amore 
85288447a05SGarrett D'Amore 	if (port->engine) {
85388447a05SGarrett D'Amore 		audio_dev_remove_engine(port->statep->adev, port->engine);
85488447a05SGarrett D'Amore 		audio_engine_free(port->engine);
85588447a05SGarrett D'Amore 	}
85688447a05SGarrett D'Amore 	if (port->bdl_paddr) {
85788447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->bdl_dmah);
85888447a05SGarrett D'Amore 	}
85988447a05SGarrett D'Amore 	if (port->bdl_acch) {
86088447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->bdl_acch);
86188447a05SGarrett D'Amore 	}
86288447a05SGarrett D'Amore 	if (port->bdl_dmah) {
86388447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->bdl_dmah);
86488447a05SGarrett D'Amore 	}
86588447a05SGarrett D'Amore 	if (port->samp_paddr) {
86688447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->samp_dmah);
86788447a05SGarrett D'Amore 	}
86888447a05SGarrett D'Amore 	if (port->samp_acch) {
86988447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->samp_acch);
87088447a05SGarrett D'Amore 	}
87188447a05SGarrett D'Amore 	if (port->samp_dmah) {
87288447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->samp_dmah);
87388447a05SGarrett D'Amore 	}
87488447a05SGarrett D'Amore 	kmem_free(port, sizeof (*port));
87588447a05SGarrett D'Amore }
87688447a05SGarrett D'Amore 
87788447a05SGarrett D'Amore /*
87888447a05SGarrett D'Amore  * audioixp_update_port()
87988447a05SGarrett D'Amore  *
88088447a05SGarrett D'Amore  * Description:
88188447a05SGarrett D'Amore  *	This routine updates the ports frame counter from hardware, and
88288447a05SGarrett D'Amore  *	gracefully handles wraps.
88388447a05SGarrett D'Amore  *
88488447a05SGarrett D'Amore  * Arguments:
88588447a05SGarrett D'Amore  *	audioixp_port_t	*port		The port to update.
88688447a05SGarrett D'Amore  */
88788447a05SGarrett D'Amore static void
audioixp_update_port(audioixp_port_t * port)88888447a05SGarrett D'Amore audioixp_update_port(audioixp_port_t *port)
88988447a05SGarrett D'Amore {
89088447a05SGarrett D'Amore 	audioixp_state_t	*statep = port->statep;
89188447a05SGarrett D'Amore 	unsigned		regoff;
89288447a05SGarrett D'Amore 	unsigned		n;
89388447a05SGarrett D'Amore 	int			loop;
89488447a05SGarrett D'Amore 	uint32_t		offset;
89588447a05SGarrett D'Amore 	uint32_t		paddr;
89688447a05SGarrett D'Amore 
89788447a05SGarrett D'Amore 	if (port->num == IXP_REC) {
89888447a05SGarrett D'Amore 		regoff = IXP_AUDIO_IN_DMA_DT_CUR;
89988447a05SGarrett D'Amore 	} else {
90088447a05SGarrett D'Amore 		regoff = IXP_AUDIO_OUT_DMA_DT_CUR;
90188447a05SGarrett D'Amore 	}
90288447a05SGarrett D'Amore 
90388447a05SGarrett D'Amore 	/*
90488447a05SGarrett D'Amore 	 * Apparently it may take several tries to get an update on the
90588447a05SGarrett D'Amore 	 * position.  Is this a hardware bug?
90688447a05SGarrett D'Amore 	 */
90788447a05SGarrett D'Amore 	for (loop = 100; loop; loop--) {
90888447a05SGarrett D'Amore 		paddr = GET32(regoff);
90988447a05SGarrett D'Amore 
91088447a05SGarrett D'Amore 		/* make sure address is reasonable */
91188447a05SGarrett D'Amore 		if ((paddr < port->samp_paddr) ||
91288447a05SGarrett D'Amore 		    (paddr >= (port->samp_paddr + port->samp_size))) {
91388447a05SGarrett D'Amore 			continue;
91488447a05SGarrett D'Amore 		}
91588447a05SGarrett D'Amore 
91688447a05SGarrett D'Amore 		offset = paddr - port->samp_paddr;
91788447a05SGarrett D'Amore 
91888447a05SGarrett D'Amore 		if (offset >= port->offset) {
91988447a05SGarrett D'Amore 			n = offset - port->offset;
92088447a05SGarrett D'Amore 		} else {
92188447a05SGarrett D'Amore 			n = offset + (port->samp_size - port->offset);
92288447a05SGarrett D'Amore 		}
92388447a05SGarrett D'Amore 		port->offset = offset;
92488447a05SGarrett D'Amore 		port->count += (n / (port->nchan * sizeof (uint16_t)));
92588447a05SGarrett D'Amore 		return;
92688447a05SGarrett D'Amore 	}
92788447a05SGarrett D'Amore 
92888447a05SGarrett D'Amore 	audio_dev_warn(statep->adev, "Unable to update count (h/w bug?)");
92988447a05SGarrett D'Amore }
93088447a05SGarrett D'Amore 
93188447a05SGarrett D'Amore 
93288447a05SGarrett D'Amore /*
93388447a05SGarrett D'Amore  * audioixp_map_regs()
93488447a05SGarrett D'Amore  *
93588447a05SGarrett D'Amore  * Description:
93688447a05SGarrett D'Amore  *	The registers are mapped in.
93788447a05SGarrett D'Amore  *
93888447a05SGarrett D'Amore  * Arguments:
93988447a05SGarrett D'Amore  *	audioixp_state_t	*state		  The device's state structure
94088447a05SGarrett D'Amore  *
94188447a05SGarrett D'Amore  * Returns:
94288447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
94388447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
94488447a05SGarrett D'Amore  */
94588447a05SGarrett D'Amore static int
audioixp_map_regs(audioixp_state_t * statep)94688447a05SGarrett D'Amore audioixp_map_regs(audioixp_state_t *statep)
94788447a05SGarrett D'Amore {
94888447a05SGarrett D'Amore 	dev_info_t		*dip = statep->dip;
94988447a05SGarrett D'Amore 
95088447a05SGarrett D'Amore 	/* map PCI config space */
95188447a05SGarrett D'Amore 	if (pci_config_setup(statep->dip, &statep->pcih) == DDI_FAILURE) {
95288447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "unable to map PCI config space");
95388447a05SGarrett D'Amore 		return (DDI_FAILURE);
95488447a05SGarrett D'Amore 	}
95588447a05SGarrett D'Amore 
95688447a05SGarrett D'Amore 	/* map audio mixer register */
95788447a05SGarrett D'Amore 	if ((ddi_regs_map_setup(dip, IXP_IO_AM_REGS, &statep->regsp, 0, 0,
95888447a05SGarrett D'Amore 	    &dev_attr, &statep->regsh)) != DDI_SUCCESS) {
95988447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "unable to map audio registers");
96088447a05SGarrett D'Amore 		return (DDI_FAILURE);
96188447a05SGarrett D'Amore 	}
96288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
96388447a05SGarrett D'Amore }
96488447a05SGarrett D'Amore 
96588447a05SGarrett D'Amore /*
96688447a05SGarrett D'Amore  * audioixp_unmap_regs()
96788447a05SGarrett D'Amore  *
96888447a05SGarrett D'Amore  * Description:
96988447a05SGarrett D'Amore  *	This routine unmaps control registers.
97088447a05SGarrett D'Amore  *
97188447a05SGarrett D'Amore  * Arguments:
97288447a05SGarrett D'Amore  *	audioixp_state_t	*state		The device's state structure
97388447a05SGarrett D'Amore  */
97488447a05SGarrett D'Amore static void
audioixp_unmap_regs(audioixp_state_t * statep)97588447a05SGarrett D'Amore audioixp_unmap_regs(audioixp_state_t *statep)
97688447a05SGarrett D'Amore {
97788447a05SGarrett D'Amore 	if (statep->regsh) {
97888447a05SGarrett D'Amore 		ddi_regs_map_free(&statep->regsh);
97988447a05SGarrett D'Amore 	}
98088447a05SGarrett D'Amore 
98188447a05SGarrett D'Amore 	if (statep->pcih) {
98288447a05SGarrett D'Amore 		pci_config_teardown(&statep->pcih);
98388447a05SGarrett D'Amore 	}
98488447a05SGarrett D'Amore }
98588447a05SGarrett D'Amore 
98688447a05SGarrett D'Amore /*
98788447a05SGarrett D'Amore  * audioixp_codec_ready()
98888447a05SGarrett D'Amore  *
98988447a05SGarrett D'Amore  * Description:
99088447a05SGarrett D'Amore  *	This routine checks the state of codecs.  It checks the flag to confirm
99188447a05SGarrett D'Amore  *	that primary codec is ready.
99288447a05SGarrett D'Amore  *
99388447a05SGarrett D'Amore  * Arguments:
99488447a05SGarrett D'Amore  *	audioixp_state_t	*state		The device's state structure
99588447a05SGarrett D'Amore  *
99688447a05SGarrett D'Amore  * Returns:
99788447a05SGarrett D'Amore  *	DDI_SUCCESS	 codec is ready
99888447a05SGarrett D'Amore  *	DDI_FAILURE	 codec is not ready
99988447a05SGarrett D'Amore  */
100088447a05SGarrett D'Amore static int
audioixp_codec_ready(audioixp_state_t * statep)100188447a05SGarrett D'Amore audioixp_codec_ready(audioixp_state_t *statep)
100288447a05SGarrett D'Amore {
100388447a05SGarrett D'Amore 	uint32_t	sr;
100488447a05SGarrett D'Amore 
100588447a05SGarrett D'Amore 	PUT32(IXP_AUDIO_INT, 0xffffffff);
100688447a05SGarrett D'Amore 	drv_usecwait(1000);
100788447a05SGarrett D'Amore 
100888447a05SGarrett D'Amore 	sr = GET32(IXP_AUDIO_INT);
100988447a05SGarrett D'Amore 	if (sr & IXP_AUDIO_INT_CODEC0_NOT_READY) {
101088447a05SGarrett D'Amore 		PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC0_NOT_READY);
101188447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "primary codec not ready");
101288447a05SGarrett D'Amore 
101388447a05SGarrett D'Amore 		return (DDI_FAILURE);
101488447a05SGarrett D'Amore 	}
101588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
101688447a05SGarrett D'Amore }
101788447a05SGarrett D'Amore 
101888447a05SGarrett D'Amore /*
101988447a05SGarrett D'Amore  * audioixp_codec_sync()
102088447a05SGarrett D'Amore  *
102188447a05SGarrett D'Amore  * Description:
102288447a05SGarrett D'Amore  *	Serialize access to the AC97 audio mixer registers.
102388447a05SGarrett D'Amore  *
102488447a05SGarrett D'Amore  * Arguments:
102588447a05SGarrett D'Amore  *	audioixp_state_t	*state		The device's state structure
102688447a05SGarrett D'Amore  *
102788447a05SGarrett D'Amore  * Returns:
102888447a05SGarrett D'Amore  *	DDI_SUCCESS		Ready for an I/O access to the codec
102988447a05SGarrett D'Amore  *	DDI_FAILURE		An I/O access is currently in progress, can't
103088447a05SGarrett D'Amore  *				perform another I/O access.
103188447a05SGarrett D'Amore  */
103288447a05SGarrett D'Amore static int
audioixp_codec_sync(audioixp_state_t * statep)103388447a05SGarrett D'Amore audioixp_codec_sync(audioixp_state_t *statep)
103488447a05SGarrett D'Amore {
103588447a05SGarrett D'Amore 	int 		i;
103688447a05SGarrett D'Amore 	uint32_t	cmd;
103788447a05SGarrett D'Amore 
103888447a05SGarrett D'Amore 	for (i = 0; i < 300; i++) {
103988447a05SGarrett D'Amore 		cmd = GET32(IXP_AUDIO_OUT_PHY_ADDR_DATA);
104088447a05SGarrett D'Amore 		if (!(cmd & IXP_AUDIO_OUT_PHY_EN)) {
104188447a05SGarrett D'Amore 			return (DDI_SUCCESS);
104288447a05SGarrett D'Amore 		}
104388447a05SGarrett D'Amore 		drv_usecwait(10);
104488447a05SGarrett D'Amore 	}
104588447a05SGarrett D'Amore 
104688447a05SGarrett D'Amore 	audio_dev_warn(statep->adev, "unable to synchronize codec");
104788447a05SGarrett D'Amore 	return (DDI_FAILURE);
104888447a05SGarrett D'Amore }
104988447a05SGarrett D'Amore 
105088447a05SGarrett D'Amore /*
105188447a05SGarrett D'Amore  * audioixp_rd97()
105288447a05SGarrett D'Amore  *
105388447a05SGarrett D'Amore  * Description:
105488447a05SGarrett D'Amore  *	Get the specific AC97 Codec register.
105588447a05SGarrett D'Amore  *
105688447a05SGarrett D'Amore  * Arguments:
105788447a05SGarrett D'Amore  *	void		*arg		The device's state structure
105888447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
105988447a05SGarrett D'Amore  *
106088447a05SGarrett D'Amore  * Returns:
106188447a05SGarrett D'Amore  *	Register value.
106288447a05SGarrett D'Amore  */
106388447a05SGarrett D'Amore static uint16_t
audioixp_rd97(void * arg,uint8_t reg)106488447a05SGarrett D'Amore audioixp_rd97(void *arg, uint8_t reg)
106588447a05SGarrett D'Amore {
106688447a05SGarrett D'Amore 	audioixp_state_t	*statep = arg;
106788447a05SGarrett D'Amore 	uint32_t		value;
106888447a05SGarrett D'Amore 	uint32_t		result;
106988447a05SGarrett D'Amore 
107088447a05SGarrett D'Amore 	if (audioixp_codec_sync(statep) != DDI_SUCCESS)
107188447a05SGarrett D'Amore 		return (0xffff);
107288447a05SGarrett D'Amore 
107388447a05SGarrett D'Amore 	value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC |
107488447a05SGarrett D'Amore 	    IXP_AUDIO_OUT_PHY_READ |
107588447a05SGarrett D'Amore 	    IXP_AUDIO_OUT_PHY_EN |
107688447a05SGarrett D'Amore 	    ((unsigned)reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT);
107788447a05SGarrett D'Amore 	PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value);
107888447a05SGarrett D'Amore 
107988447a05SGarrett D'Amore 	if (audioixp_codec_sync(statep) != DDI_SUCCESS)
108088447a05SGarrett D'Amore 		return (0xffff);
108188447a05SGarrett D'Amore 
108288447a05SGarrett D'Amore 	for (int i = 0; i < 300; i++) {
108388447a05SGarrett D'Amore 		result = GET32(IXP_AUDIO_IN_PHY_ADDR_DATA);
108488447a05SGarrett D'Amore 		if (result & IXP_AUDIO_IN_PHY_READY)	{
108588447a05SGarrett D'Amore 			return (result >> IXP_AUDIO_IN_PHY_DATA_SHIFT);
108688447a05SGarrett D'Amore 		}
108788447a05SGarrett D'Amore 		drv_usecwait(10);
108888447a05SGarrett D'Amore 	}
108988447a05SGarrett D'Amore 
109088447a05SGarrett D'Amore done:
109188447a05SGarrett D'Amore 	audio_dev_warn(statep->adev, "time out reading codec reg %d", reg);
109288447a05SGarrett D'Amore 	return (0xffff);
109388447a05SGarrett D'Amore }
109488447a05SGarrett D'Amore 
109588447a05SGarrett D'Amore /*
109688447a05SGarrett D'Amore  * audioixp_wr97()
109788447a05SGarrett D'Amore  *
109888447a05SGarrett D'Amore  * Description:
109988447a05SGarrett D'Amore  *	Set the specific AC97 Codec register.
110088447a05SGarrett D'Amore  *
110188447a05SGarrett D'Amore  * Arguments:
110288447a05SGarrett D'Amore  *	void		*arg		The device's state structure
110388447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
110488447a05SGarrett D'Amore  *	uint16_t	data		The data want to be set
110588447a05SGarrett D'Amore  */
110688447a05SGarrett D'Amore static void
audioixp_wr97(void * arg,uint8_t reg,uint16_t data)110788447a05SGarrett D'Amore audioixp_wr97(void *arg, uint8_t reg, uint16_t data)
110888447a05SGarrett D'Amore {
110988447a05SGarrett D'Amore 	audioixp_state_t	*statep = arg;
111088447a05SGarrett D'Amore 	uint32_t		value;
111188447a05SGarrett D'Amore 
111288447a05SGarrett D'Amore 	if (audioixp_codec_sync(statep) != DDI_SUCCESS) {
111388447a05SGarrett D'Amore 		return;
111488447a05SGarrett D'Amore 	}
111588447a05SGarrett D'Amore 
111688447a05SGarrett D'Amore 	value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC |
111788447a05SGarrett D'Amore 	    IXP_AUDIO_OUT_PHY_WRITE |
111888447a05SGarrett D'Amore 	    IXP_AUDIO_OUT_PHY_EN |
111988447a05SGarrett D'Amore 	    ((unsigned)reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT) |
112088447a05SGarrett D'Amore 	    ((unsigned)data << IXP_AUDIO_OUT_PHY_DATA_SHIFT);
112188447a05SGarrett D'Amore 	PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value);
112288447a05SGarrett D'Amore 
112388447a05SGarrett D'Amore 	(void) audioixp_rd97(statep, reg);
112488447a05SGarrett D'Amore }
112588447a05SGarrett D'Amore 
112688447a05SGarrett D'Amore /*
112788447a05SGarrett D'Amore  * audioixp_reset_ac97()
112888447a05SGarrett D'Amore  *
112988447a05SGarrett D'Amore  * Description:
113088447a05SGarrett D'Amore  *	Reset AC97 Codec register.
113188447a05SGarrett D'Amore  *
113288447a05SGarrett D'Amore  * Arguments:
113388447a05SGarrett D'Amore  *	audioixp_state_t	*state		The device's state structure
113488447a05SGarrett D'Amore  *
113588447a05SGarrett D'Amore  * Returns:
113688447a05SGarrett D'Amore  *	DDI_SUCCESS		Reset the codec successfully
113788447a05SGarrett D'Amore  *	DDI_FAILURE		Failed to reset the codec
113888447a05SGarrett D'Amore  */
113988447a05SGarrett D'Amore static int
audioixp_reset_ac97(audioixp_state_t * statep)114088447a05SGarrett D'Amore audioixp_reset_ac97(audioixp_state_t *statep)
114188447a05SGarrett D'Amore {
114288447a05SGarrett D'Amore 	uint32_t	cmd;
114388447a05SGarrett D'Amore 	int i;
114488447a05SGarrett D'Amore 
114588447a05SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_POWER_DOWN);
114688447a05SGarrett D'Amore 	drv_usecwait(10);
114788447a05SGarrett D'Amore 
114888447a05SGarrett D'Amore 	/* register reset */
114988447a05SGarrett D'Amore 	SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET);
115088447a05SGarrett D'Amore 	/* force a read to flush caches */
115188447a05SGarrett D'Amore 	(void) GET32(IXP_AUDIO_CMD);
115288447a05SGarrett D'Amore 
115388447a05SGarrett D'Amore 	drv_usecwait(10);
115488447a05SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET);
115588447a05SGarrett D'Amore 
115688447a05SGarrett D'Amore 	/* cold reset */
115788447a05SGarrett D'Amore 	for (i = 0; i < 300; i++) {
115888447a05SGarrett D'Amore 		cmd = GET32(IXP_AUDIO_CMD);
115988447a05SGarrett D'Amore 		if (cmd & IXP_AUDIO_CMD_AC_ACTIVE) {
116088447a05SGarrett D'Amore 			cmd |= IXP_AUDIO_CMD_AC_RESET | IXP_AUDIO_CMD_AC_SYNC;
116188447a05SGarrett D'Amore 			PUT32(IXP_AUDIO_CMD, cmd);
116288447a05SGarrett D'Amore 			return (DDI_SUCCESS);
116388447a05SGarrett D'Amore 		}
116488447a05SGarrett D'Amore 		cmd &= ~IXP_AUDIO_CMD_AC_RESET;
116588447a05SGarrett D'Amore 		cmd |= IXP_AUDIO_CMD_AC_SYNC;
116688447a05SGarrett D'Amore 		PUT32(IXP_AUDIO_CMD, cmd);
116788447a05SGarrett D'Amore 		(void) GET32(IXP_AUDIO_CMD);
116888447a05SGarrett D'Amore 		drv_usecwait(10);
116988447a05SGarrett D'Amore 		cmd |= IXP_AUDIO_CMD_AC_RESET;
117088447a05SGarrett D'Amore 		PUT32(IXP_AUDIO_CMD, cmd);
117188447a05SGarrett D'Amore 		drv_usecwait(10);
117288447a05SGarrett D'Amore 	}
117388447a05SGarrett D'Amore 
117488447a05SGarrett D'Amore 	audio_dev_warn(statep->adev, "AC'97 reset timed out");
117588447a05SGarrett D'Amore 	return (DDI_FAILURE);
117688447a05SGarrett D'Amore }
117788447a05SGarrett D'Amore 
117888447a05SGarrett D'Amore /*
117988447a05SGarrett D'Amore  * audioixp_chip_init()
118088447a05SGarrett D'Amore  *
118188447a05SGarrett D'Amore  * Description:
118288447a05SGarrett D'Amore  *	This routine initializes ATI IXP audio controller and the AC97
118388447a05SGarrett D'Amore  *	codec.
118488447a05SGarrett D'Amore  *
118588447a05SGarrett D'Amore  * Arguments:
118688447a05SGarrett D'Amore  *	audioixp_state_t	*state		The device's state structure
118788447a05SGarrett D'Amore  *
118888447a05SGarrett D'Amore  * Returns:
118988447a05SGarrett D'Amore  *	DDI_SUCCESS	The hardware was initialized properly
119088447a05SGarrett D'Amore  *	DDI_FAILURE	The hardware couldn't be initialized properly
119188447a05SGarrett D'Amore  */
119288447a05SGarrett D'Amore static int
audioixp_chip_init(audioixp_state_t * statep)119388447a05SGarrett D'Amore audioixp_chip_init(audioixp_state_t *statep)
119488447a05SGarrett D'Amore {
119588447a05SGarrett D'Amore 	/*
119688447a05SGarrett D'Amore 	 * put the audio controller into quiet state, everything off
119788447a05SGarrett D'Amore 	 */
119888447a05SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
119988447a05SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
120088447a05SGarrett D'Amore 
120188447a05SGarrett D'Amore 	/* AC97 reset */
120288447a05SGarrett D'Amore 	if (audioixp_reset_ac97(statep) != DDI_SUCCESS) {
120388447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "AC97 codec reset failed");
120488447a05SGarrett D'Amore 		return (DDI_FAILURE);
120588447a05SGarrett D'Amore 	}
120688447a05SGarrett D'Amore 
120788447a05SGarrett D'Amore 	if (audioixp_codec_ready(statep) != DDI_SUCCESS) {
120888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "AC97 codec not ready");
120988447a05SGarrett D'Amore 		return (DDI_FAILURE);
121088447a05SGarrett D'Amore 	}
121188447a05SGarrett D'Amore 
121288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
121388447a05SGarrett D'Amore 
121488447a05SGarrett D'Amore }	/* audioixp_chip_init() */
121588447a05SGarrett D'Amore 
121688447a05SGarrett D'Amore /*
121788447a05SGarrett D'Amore  * audioixp_attach()
121888447a05SGarrett D'Amore  *
121988447a05SGarrett D'Amore  * Description:
122088447a05SGarrett D'Amore  *	Attach an instance of the audioixp driver. This routine does
122188447a05SGarrett D'Amore  * 	the device dependent attach tasks.
122288447a05SGarrett D'Amore  *
122388447a05SGarrett D'Amore  * Arguments:
122488447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
122588447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
122688447a05SGarrett D'Amore  *
122788447a05SGarrett D'Amore  * Returns:
122888447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
122988447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
123088447a05SGarrett D'Amore  */
123188447a05SGarrett D'Amore static int
audioixp_attach(dev_info_t * dip)123288447a05SGarrett D'Amore audioixp_attach(dev_info_t *dip)
123388447a05SGarrett D'Amore {
123488447a05SGarrett D'Amore 	uint16_t		cmdeg;
123588447a05SGarrett D'Amore 	audioixp_state_t	*statep;
123688447a05SGarrett D'Amore 	audio_dev_t		*adev;
123788447a05SGarrett D'Amore 	uint32_t		devid;
123888447a05SGarrett D'Amore 	const char		*name;
123988447a05SGarrett D'Amore 	const char		*rev;
124088447a05SGarrett D'Amore 
124188447a05SGarrett D'Amore 	/* allocate the soft state structure */
124288447a05SGarrett D'Amore 	statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
124388447a05SGarrett D'Amore 	statep->dip = dip;
124488447a05SGarrett D'Amore 	ddi_set_driver_private(dip, statep);
1245*68c47f65SGarrett D'Amore 	mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, NULL);
124688447a05SGarrett D'Amore 
124788447a05SGarrett D'Amore 	/* allocate framework audio device */
124888447a05SGarrett D'Amore 	if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
124988447a05SGarrett D'Amore 		cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev",
125088447a05SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip));
125188447a05SGarrett D'Amore 		goto error;
125288447a05SGarrett D'Amore 	}
125388447a05SGarrett D'Amore 	statep->adev = adev;
125488447a05SGarrett D'Amore 
125588447a05SGarrett D'Amore 	/* map in the registers */
125688447a05SGarrett D'Amore 	if (audioixp_map_regs(statep) != DDI_SUCCESS) {
125788447a05SGarrett D'Amore 		audio_dev_warn(adev, "couldn't map registers");
125888447a05SGarrett D'Amore 		goto error;
125988447a05SGarrett D'Amore 	}
126088447a05SGarrett D'Amore 
126188447a05SGarrett D'Amore 	/* set device information -- this could be smarter */
126288447a05SGarrett D'Amore 	devid = ((pci_config_get16(statep->pcih, PCI_CONF_VENID)) << 16) |
126388447a05SGarrett D'Amore 	    pci_config_get16(statep->pcih, PCI_CONF_DEVID);
126488447a05SGarrett D'Amore 
126588447a05SGarrett D'Amore 	name = "ATI AC'97";
126688447a05SGarrett D'Amore 	switch (devid) {
126788447a05SGarrett D'Amore 	case IXP_PCI_ID_200:
126888447a05SGarrett D'Amore 		rev = "IXP150";
126988447a05SGarrett D'Amore 		break;
127088447a05SGarrett D'Amore 	case IXP_PCI_ID_300:
127188447a05SGarrett D'Amore 		rev = "SB300";
127288447a05SGarrett D'Amore 		break;
127388447a05SGarrett D'Amore 	case IXP_PCI_ID_400:
127488447a05SGarrett D'Amore 		if (pci_config_get8(statep->pcih, PCI_CONF_REVID) & 0x80) {
127588447a05SGarrett D'Amore 			rev = "SB450";
127688447a05SGarrett D'Amore 		} else {
127788447a05SGarrett D'Amore 			rev = "SB400";
127888447a05SGarrett D'Amore 		}
127988447a05SGarrett D'Amore 		break;
128088447a05SGarrett D'Amore 	case IXP_PCI_ID_SB600:
128188447a05SGarrett D'Amore 		rev = "SB600";
128288447a05SGarrett D'Amore 		break;
128388447a05SGarrett D'Amore 	default:
128488447a05SGarrett D'Amore 		rev = "Unknown";
128588447a05SGarrett D'Amore 		break;
128688447a05SGarrett D'Amore 	}
128788447a05SGarrett D'Amore 	audio_dev_set_description(adev, name);
128888447a05SGarrett D'Amore 	audio_dev_set_version(adev, rev);
128988447a05SGarrett D'Amore 
1290d8a7fe16SGarrett D'Amore 	/* set PCI command register */
1291d8a7fe16SGarrett D'Amore 	cmdeg = pci_config_get16(statep->pcih, PCI_CONF_COMM);
1292d8a7fe16SGarrett D'Amore 	pci_config_put16(statep->pcih, PCI_CONF_COMM,
1293d8a7fe16SGarrett D'Amore 	    cmdeg | PCI_COMM_IO | PCI_COMM_MAE);
129488447a05SGarrett D'Amore 
129588447a05SGarrett D'Amore 	statep->ac97 = ac97_alloc(dip, audioixp_rd97, audioixp_wr97, statep);
129688447a05SGarrett D'Amore 	if (statep->ac97 == NULL) {
129788447a05SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate ac97 handle");
129888447a05SGarrett D'Amore 		goto error;
129988447a05SGarrett D'Amore 	}
130088447a05SGarrett D'Amore 
1301d8a7fe16SGarrett D'Amore 	/* allocate port structures */
1302d8a7fe16SGarrett D'Amore 	if ((audioixp_alloc_port(statep, IXP_PLAY) != DDI_SUCCESS) ||
1303d8a7fe16SGarrett D'Amore 	    (audioixp_alloc_port(statep, IXP_REC) != DDI_SUCCESS)) {
1304d8a7fe16SGarrett D'Amore 		goto error;
1305d8a7fe16SGarrett D'Amore 	}
1306d8a7fe16SGarrett D'Amore 
1307d8a7fe16SGarrett D'Amore 	/*
1308d8a7fe16SGarrett D'Amore 	 * If we have locked in a stereo configuration, then don't expose
1309d8a7fe16SGarrett D'Amore 	 * multichannel-specific AC'97 codec controls.
1310d8a7fe16SGarrett D'Amore 	 */
1311d8a7fe16SGarrett D'Amore 	if (statep->play_port->nchan == 2) {
1312d8a7fe16SGarrett D'Amore 		int i;
1313d8a7fe16SGarrett D'Amore 		ac97_ctrl_t *ctrl;
1314d8a7fe16SGarrett D'Amore 		const char *name;
1315d8a7fe16SGarrett D'Amore 
1316d8a7fe16SGarrett D'Amore 		for (i = 0; (name = audioixp_remove_ac97[i]) != NULL; i++) {
1317d8a7fe16SGarrett D'Amore 			ctrl = ac97_control_find(statep->ac97, name);
1318d8a7fe16SGarrett D'Amore 			if (ctrl != NULL) {
1319d8a7fe16SGarrett D'Amore 				ac97_control_unregister(ctrl);
1320d8a7fe16SGarrett D'Amore 			}
1321d8a7fe16SGarrett D'Amore 		}
1322d8a7fe16SGarrett D'Amore 	}
132388447a05SGarrett D'Amore 
132488447a05SGarrett D'Amore 	if (audioixp_chip_init(statep) != DDI_SUCCESS) {
132588447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "failed to init chip");
132688447a05SGarrett D'Amore 		goto error;
132788447a05SGarrett D'Amore 	}
132888447a05SGarrett D'Amore 
132988447a05SGarrett D'Amore 	/* initialize the AC'97 part */
133088447a05SGarrett D'Amore 	if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
133188447a05SGarrett D'Amore 		audio_dev_warn(adev, "ac'97 initialization failed");
133288447a05SGarrett D'Amore 		goto error;
133388447a05SGarrett D'Amore 	}
133488447a05SGarrett D'Amore 
133588447a05SGarrett D'Amore 	if (audio_dev_register(adev) != DDI_SUCCESS) {
133688447a05SGarrett D'Amore 		audio_dev_warn(adev, "unable to register with framework");
133788447a05SGarrett D'Amore 		goto error;
133888447a05SGarrett D'Amore 	}
133988447a05SGarrett D'Amore 
134088447a05SGarrett D'Amore 	ddi_report_dev(dip);
134188447a05SGarrett D'Amore 
134288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
134388447a05SGarrett D'Amore 
134488447a05SGarrett D'Amore error:
134588447a05SGarrett D'Amore 	audioixp_destroy(statep);
134688447a05SGarrett D'Amore 	return (DDI_FAILURE);
134788447a05SGarrett D'Amore }
134888447a05SGarrett D'Amore 
134988447a05SGarrett D'Amore /*
135088447a05SGarrett D'Amore  * audioixp_detach()
135188447a05SGarrett D'Amore  *
135288447a05SGarrett D'Amore  * Description:
135388447a05SGarrett D'Amore  *	Detach an instance of the audioixp driver.
135488447a05SGarrett D'Amore  *
135588447a05SGarrett D'Amore  * Arguments:
135688447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
135788447a05SGarrett D'Amore  *
135888447a05SGarrett D'Amore  * Returns:
135988447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
136088447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
136188447a05SGarrett D'Amore  */
136288447a05SGarrett D'Amore static int
audioixp_detach(dev_info_t * dip)136388447a05SGarrett D'Amore audioixp_detach(dev_info_t *dip)
136488447a05SGarrett D'Amore {
136588447a05SGarrett D'Amore 	audioixp_state_t	*statep;
136688447a05SGarrett D'Amore 
136788447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
136888447a05SGarrett D'Amore 
136988447a05SGarrett D'Amore 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
137088447a05SGarrett D'Amore 		return (DDI_FAILURE);
137188447a05SGarrett D'Amore 	}
137288447a05SGarrett D'Amore 
137388447a05SGarrett D'Amore 	audioixp_destroy(statep);
137488447a05SGarrett D'Amore 	return (DDI_SUCCESS);
137588447a05SGarrett D'Amore }
137688447a05SGarrett D'Amore 
137788447a05SGarrett D'Amore /*
137888447a05SGarrett D'Amore  * audioixp_destroy()
137988447a05SGarrett D'Amore  *
138088447a05SGarrett D'Amore  * Description:
138188447a05SGarrett D'Amore  *	This routine releases all resources held by the device instance,
138288447a05SGarrett D'Amore  *	as part of either detach or a failure in attach.
138388447a05SGarrett D'Amore  *
138488447a05SGarrett D'Amore  * Arguments:
138588447a05SGarrett D'Amore  *	audioixp_state_t	*state	The device soft state.
138688447a05SGarrett D'Amore  */
1387*68c47f65SGarrett D'Amore static void
audioixp_destroy(audioixp_state_t * statep)138888447a05SGarrett D'Amore audioixp_destroy(audioixp_state_t *statep)
138988447a05SGarrett D'Amore {
1390*68c47f65SGarrett D'Amore 	/*
1391*68c47f65SGarrett D'Amore 	 * put the audio controller into quiet state, everything off
1392*68c47f65SGarrett D'Amore 	 */
1393*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
1394*68c47f65SGarrett D'Amore 	CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
139588447a05SGarrett D'Amore 
139688447a05SGarrett D'Amore 	audioixp_free_port(statep->play_port);
139788447a05SGarrett D'Amore 	audioixp_free_port(statep->rec_port);
139888447a05SGarrett D'Amore 
139988447a05SGarrett D'Amore 	audioixp_unmap_regs(statep);
140088447a05SGarrett D'Amore 
140188447a05SGarrett D'Amore 	if (statep->ac97) {
140288447a05SGarrett D'Amore 		ac97_free(statep->ac97);
140388447a05SGarrett D'Amore 	}
140488447a05SGarrett D'Amore 
140588447a05SGarrett D'Amore 	if (statep->adev) {
140688447a05SGarrett D'Amore 		audio_dev_free(statep->adev);
140788447a05SGarrett D'Amore 	}
140888447a05SGarrett D'Amore 
140988447a05SGarrett D'Amore 	mutex_destroy(&statep->inst_lock);
141088447a05SGarrett D'Amore 	kmem_free(statep, sizeof (*statep));
141188447a05SGarrett D'Amore }
1412