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 
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * audio810 Audio Driver
2988447a05SGarrett D'Amore  *
3088447a05SGarrett D'Amore  * The driver is primarily targeted at providing audio support for the
31*68c47f65SGarrett D'Amore  * Intel ICHx family of AC'97 controllers and compatible parts (such
32*68c47f65SGarrett D'Amore  * as those from nVidia and AMD.)
33*68c47f65SGarrett D'Amore  *
34*68c47f65SGarrett D'Amore  * These audio parts have independent channels for PCM in, PCM out,
35*68c47f65SGarrett D'Amore  * mic in, and sometimes modem in, and modem out.  The AC'97
36*68c47f65SGarrett D'Amore  * controller is a PCI bus master with scatter/gather support. Each
37*68c47f65SGarrett D'Amore  * channel has a DMA engine. Currently, we use only the PCM in and PCM
38*68c47f65SGarrett D'Amore  * out channels. Each DMA engine uses one buffer descriptor list. And
39*68c47f65SGarrett D'Amore  * the buffer descriptor list is an array of up to 32 entries, each of
40*68c47f65SGarrett D'Amore  * which describes a data buffer.  Each entry contains a pointer to a
41*68c47f65SGarrett D'Amore  * data buffer, control bits, and the length of the buffer being
42*68c47f65SGarrett D'Amore  * pointed to, where the length is expressed as the number of
43*68c47f65SGarrett D'Amore  * samples. This, combined with the 16-bit sample size, gives the
44*68c47f65SGarrett D'Amore  * actual physical length of the buffer.
4588447a05SGarrett D'Amore  *
4688447a05SGarrett D'Amore  * A workaround for the AD1980 and AD1985 codec:
4788447a05SGarrett D'Amore  *	Most vendors connect the surr-out of the codecs to the line-out jack.
4888447a05SGarrett D'Amore  *	So far we haven't found which vendors don't do that. So we assume that
4988447a05SGarrett D'Amore  *	all vendors swap the surr-out and the line-out outputs. So we need swap
5088447a05SGarrett D'Amore  *	the two outputs. But we still internally process the
5188447a05SGarrett D'Amore  *	"ad198x-swap-output" property. If someday some vendors do not swap the
5288447a05SGarrett D'Amore  *	outputs, we would set "ad198x-swap-output = 0" in the
5388447a05SGarrett D'Amore  *	/kernel/drv/audio810.conf file, and unload and reload the audio810
5488447a05SGarrett D'Amore  *	driver (or reboot).
5588447a05SGarrett D'Amore  *
5688447a05SGarrett D'Amore  * 	NOTE:
5788447a05SGarrett D'Amore  * 	This driver depends on the drv/audio and misc/ac97
5888447a05SGarrett D'Amore  * 	modules being loaded first.
59*68c47f65SGarrett D'Amore  *
60*68c47f65SGarrett D'Amore  * The audio framework guarantees that our entry points are exclusive
61*68c47f65SGarrett D'Amore  * with suspend and resume.  This includes data flow and control entry
62*68c47f65SGarrett D'Amore  * points alike.
63*68c47f65SGarrett D'Amore  *
64*68c47f65SGarrett D'Amore  * The audio framework guarantees that only one control is being
65*68c47f65SGarrett D'Amore  * accessed on any given audio device at a time.
66*68c47f65SGarrett D'Amore  *
67*68c47f65SGarrett D'Amore  * The audio framework guarantees that entry points are themselves
68*68c47f65SGarrett D'Amore  * serialized for a given engine.
69*68c47f65SGarrett D'Amore  *
70*68c47f65SGarrett D'Amore  * We have no interrupt routine or other internal asynchronous routines.
71*68c47f65SGarrett D'Amore  *
72*68c47f65SGarrett D'Amore  * Our device uses completely separate registers for each engine,
73*68c47f65SGarrett D'Amore  * except for the start/stop registers, which are implemented in a
74*68c47f65SGarrett D'Amore  * manner that allows for them to be accessed concurrently safely from
75*68c47f65SGarrett D'Amore  * different threads.
76*68c47f65SGarrett D'Amore  *
77*68c47f65SGarrett D'Amore  * Hence, it turns out that we simply don't need any locking in this
78*68c47f65SGarrett D'Amore  * driver.
7988447a05SGarrett D'Amore  */
8088447a05SGarrett D'Amore #include <sys/types.h>
8188447a05SGarrett D'Amore #include <sys/modctl.h>
8288447a05SGarrett D'Amore #include <sys/kmem.h>
8388447a05SGarrett D'Amore #include <sys/conf.h>
8488447a05SGarrett D'Amore #include <sys/ddi.h>
8588447a05SGarrett D'Amore #include <sys/sunddi.h>
8688447a05SGarrett D'Amore #include <sys/pci.h>
8788447a05SGarrett D'Amore #include <sys/note.h>
8888447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
8988447a05SGarrett D'Amore #include <sys/audio/ac97.h>
9088447a05SGarrett D'Amore #include "audio810.h"
9188447a05SGarrett D'Amore 
9288447a05SGarrett D'Amore /*
9388447a05SGarrett D'Amore  * Module linkage routines for the kernel
9488447a05SGarrett D'Amore  */
9588447a05SGarrett D'Amore static int audio810_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
9688447a05SGarrett D'Amore static int audio810_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
9788447a05SGarrett D'Amore static int audio810_ddi_quiesce(dev_info_t *);
9888447a05SGarrett D'Amore 
9988447a05SGarrett D'Amore /*
10088447a05SGarrett D'Amore  * Entry point routine prototypes
10188447a05SGarrett D'Amore  */
102*68c47f65SGarrett D'Amore static int audio810_open(void *, int, unsigned *, caddr_t *);
10388447a05SGarrett D'Amore static void audio810_close(void *);
10488447a05SGarrett D'Amore static int audio810_start(void *);
10588447a05SGarrett D'Amore static void audio810_stop(void *);
10688447a05SGarrett D'Amore static int audio810_format(void *);
10788447a05SGarrett D'Amore static int audio810_channels(void *);
10888447a05SGarrett D'Amore static int audio810_rate(void *);
10988447a05SGarrett D'Amore static uint64_t audio810_count(void *);
11088447a05SGarrett D'Amore static void audio810_sync(void *, unsigned);
111f9ead4a5SGarrett D'Amore static unsigned audio810_playahead(void *);
11288447a05SGarrett D'Amore 
11388447a05SGarrett D'Amore static audio_engine_ops_t audio810_engine_ops = {
11488447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
11588447a05SGarrett D'Amore 	audio810_open,
11688447a05SGarrett D'Amore 	audio810_close,
11788447a05SGarrett D'Amore 	audio810_start,
11888447a05SGarrett D'Amore 	audio810_stop,
11988447a05SGarrett D'Amore 	audio810_count,
12088447a05SGarrett D'Amore 	audio810_format,
12188447a05SGarrett D'Amore 	audio810_channels,
12288447a05SGarrett D'Amore 	audio810_rate,
12388447a05SGarrett D'Amore 	audio810_sync,
124f9ead4a5SGarrett D'Amore 	NULL,
125f9ead4a5SGarrett D'Amore 	NULL,
126f9ead4a5SGarrett D'Amore 	audio810_playahead
12788447a05SGarrett D'Amore };
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore /*
13088447a05SGarrett D'Amore  * Local Routine Prototypes
13188447a05SGarrett D'Amore  */
13288447a05SGarrett D'Amore static int audio810_attach(dev_info_t *);
13388447a05SGarrett D'Amore static int audio810_resume(dev_info_t *);
13488447a05SGarrett D'Amore static int audio810_detach(dev_info_t *);
13588447a05SGarrett D'Amore static int audio810_suspend(dev_info_t *);
13688447a05SGarrett D'Amore 
13788447a05SGarrett D'Amore static int audio810_alloc_port(audio810_state_t *, int, uint8_t);
13888447a05SGarrett D'Amore static int audio810_codec_sync(audio810_state_t *);
13988447a05SGarrett D'Amore static void audio810_write_ac97(void *, uint8_t, uint16_t);
14088447a05SGarrett D'Amore static uint16_t audio810_read_ac97(void *, uint8_t);
14188447a05SGarrett D'Amore static int audio810_map_regs(dev_info_t *, audio810_state_t *);
14288447a05SGarrett D'Amore static void audio810_unmap_regs(audio810_state_t *);
14388447a05SGarrett D'Amore static void audio810_stop_dma(audio810_state_t *);
14488447a05SGarrett D'Amore static int audio810_chip_init(audio810_state_t *);
145*68c47f65SGarrett D'Amore static void audio810_set_channels(audio810_state_t *);
14688447a05SGarrett D'Amore static void audio810_destroy(audio810_state_t *);
14788447a05SGarrett D'Amore 
14888447a05SGarrett D'Amore /*
14988447a05SGarrett D'Amore  * Global variables, but used only by this file.
15088447a05SGarrett D'Amore  */
15188447a05SGarrett D'Amore 
15288447a05SGarrett D'Amore /*
15388447a05SGarrett D'Amore  * DDI Structures
15488447a05SGarrett D'Amore  */
15588447a05SGarrett D'Amore 
15688447a05SGarrett D'Amore /* Device operations structure */
15788447a05SGarrett D'Amore static struct dev_ops audio810_dev_ops = {
15888447a05SGarrett D'Amore 	DEVO_REV,		/* devo_rev */
15988447a05SGarrett D'Amore 	0,			/* devo_refcnt */
16088447a05SGarrett D'Amore 	NULL,			/* devo_getinfo */
16188447a05SGarrett D'Amore 	nulldev,		/* devo_identify - obsolete */
16288447a05SGarrett D'Amore 	nulldev,		/* devo_probe */
16388447a05SGarrett D'Amore 	audio810_ddi_attach,	/* devo_attach */
16488447a05SGarrett D'Amore 	audio810_ddi_detach,	/* devo_detach */
16588447a05SGarrett D'Amore 	nodev,			/* devo_reset */
16688447a05SGarrett D'Amore 	NULL,			/* devi_cb_ops */
16788447a05SGarrett D'Amore 	NULL,			/* devo_bus_ops */
16888447a05SGarrett D'Amore 	NULL,			/* devo_power */
16988447a05SGarrett D'Amore 	audio810_ddi_quiesce,	/* devo_quiesce */
17088447a05SGarrett D'Amore };
17188447a05SGarrett D'Amore 
17288447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
17388447a05SGarrett D'Amore static struct modldrv audio810_modldrv = {
17488447a05SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
17588447a05SGarrett D'Amore 	I810_MOD_NAME,		/* drv_linkinfo */
17688447a05SGarrett D'Amore 	&audio810_dev_ops,	/* drv_dev_ops */
17788447a05SGarrett D'Amore };
17888447a05SGarrett D'Amore 
17988447a05SGarrett D'Amore /* Module linkage structure */
18088447a05SGarrett D'Amore static struct modlinkage audio810_modlinkage = {
18188447a05SGarrett D'Amore 	MODREV_1,			/* ml_rev */
18288447a05SGarrett D'Amore 	(void *)&audio810_modldrv,	/* ml_linkage */
18388447a05SGarrett D'Amore 	NULL				/* NULL terminates the list */
18488447a05SGarrett D'Amore };
18588447a05SGarrett D'Amore 
18688447a05SGarrett D'Amore /*
18788447a05SGarrett D'Amore  * device access attributes for register mapping
18888447a05SGarrett D'Amore  */
18988447a05SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
19088447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
19188447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
19288447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
19388447a05SGarrett D'Amore };
19488447a05SGarrett D'Amore 
19588447a05SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
19688447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
19788447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
19888447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
19988447a05SGarrett D'Amore };
20088447a05SGarrett D'Amore 
20188447a05SGarrett D'Amore /*
20288447a05SGarrett D'Amore  * DMA attributes of buffer descriptor list
20388447a05SGarrett D'Amore  */
20488447a05SGarrett D'Amore static ddi_dma_attr_t	bdlist_dma_attr = {
20588447a05SGarrett D'Amore 	DMA_ATTR_V0,	/* version */
20688447a05SGarrett D'Amore 	0,		/* addr_lo */
20788447a05SGarrett D'Amore 	0xffffffff,	/* addr_hi */
20888447a05SGarrett D'Amore 	0x0000ffff,	/* count_max */
20988447a05SGarrett D'Amore 	8,		/* align, BDL must be aligned on a 8-byte boundary */
21088447a05SGarrett D'Amore 	0x3c,		/* burstsize */
21188447a05SGarrett D'Amore 	8,		/* minxfer, set to the size of a BDlist entry */
21288447a05SGarrett D'Amore 	0x0000ffff,	/* maxxfer */
21388447a05SGarrett D'Amore 	0x00000fff,	/* seg, set to the RAM pagesize of intel platform */
21488447a05SGarrett D'Amore 	1,		/* sgllen, there's no scatter-gather list */
21588447a05SGarrett D'Amore 	8,		/* granular, set to the value of minxfer */
21688447a05SGarrett D'Amore 	0		/* flags, use virtual address */
21788447a05SGarrett D'Amore };
21888447a05SGarrett D'Amore 
21988447a05SGarrett D'Amore /*
22088447a05SGarrett D'Amore  * DMA attributes of buffers to be used to receive/send audio data
22188447a05SGarrett D'Amore  */
22288447a05SGarrett D'Amore static ddi_dma_attr_t	sample_buf_dma_attr = {
22388447a05SGarrett D'Amore 	DMA_ATTR_V0,
22488447a05SGarrett D'Amore 	0,		/* addr_lo */
22588447a05SGarrett D'Amore 	0xffffffff,	/* addr_hi */
22688447a05SGarrett D'Amore 	0x0001ffff,	/* count_max */
22788447a05SGarrett D'Amore 	4,		/* align, data buffer is aligned on a 4-byte boundary */
22888447a05SGarrett D'Amore 	0x3c,		/* burstsize */
22988447a05SGarrett D'Amore 	4,		/* minxfer, set to the size of a sample data */
23088447a05SGarrett D'Amore 	0x0001ffff,	/* maxxfer */
23188447a05SGarrett D'Amore 	0x0001ffff,	/* seg */
23288447a05SGarrett D'Amore 	1,		/* sgllen, no scatter-gather */
23388447a05SGarrett D'Amore 	4,		/* granular, set to the value of minxfer */
23488447a05SGarrett D'Amore 	0,		/* flags, use virtual address */
23588447a05SGarrett D'Amore };
23688447a05SGarrett D'Amore 
23788447a05SGarrett D'Amore /*
23888447a05SGarrett D'Amore  * _init()
23988447a05SGarrett D'Amore  *
24088447a05SGarrett D'Amore  * Description:
24188447a05SGarrett D'Amore  *	Driver initialization, called when driver is first loaded.
24288447a05SGarrett D'Amore  *	This is how access is initially given to all the static structures.
24388447a05SGarrett D'Amore  *
24488447a05SGarrett D'Amore  * Arguments:
24588447a05SGarrett D'Amore  *	None
24688447a05SGarrett D'Amore  *
24788447a05SGarrett D'Amore  * Returns:
24888447a05SGarrett D'Amore  *	mod_install() status, see mod_install(9f)
24988447a05SGarrett D'Amore  */
25088447a05SGarrett D'Amore int
_init(void)25188447a05SGarrett D'Amore _init(void)
25288447a05SGarrett D'Amore {
25388447a05SGarrett D'Amore 	int	error;
25488447a05SGarrett D'Amore 
25588447a05SGarrett D'Amore 	audio_init_ops(&audio810_dev_ops, I810_NAME);
25688447a05SGarrett D'Amore 
25788447a05SGarrett D'Amore 	if ((error = mod_install(&audio810_modlinkage)) != 0) {
25888447a05SGarrett D'Amore 		audio_fini_ops(&audio810_dev_ops);
25988447a05SGarrett D'Amore 	}
26088447a05SGarrett D'Amore 
26188447a05SGarrett D'Amore 	return (error);
26288447a05SGarrett D'Amore }
26388447a05SGarrett D'Amore 
26488447a05SGarrett D'Amore /*
26588447a05SGarrett D'Amore  * _fini()
26688447a05SGarrett D'Amore  *
26788447a05SGarrett D'Amore  * Description:
26888447a05SGarrett D'Amore  *	Module de-initialization, called when the driver is to be unloaded.
26988447a05SGarrett D'Amore  *
27088447a05SGarrett D'Amore  * Arguments:
27188447a05SGarrett D'Amore  *	None
27288447a05SGarrett D'Amore  *
27388447a05SGarrett D'Amore  * Returns:
27488447a05SGarrett D'Amore  *	mod_remove() status, see mod_remove(9f)
27588447a05SGarrett D'Amore  */
27688447a05SGarrett D'Amore int
_fini(void)27788447a05SGarrett D'Amore _fini(void)
27888447a05SGarrett D'Amore {
27988447a05SGarrett D'Amore 	int		error;
28088447a05SGarrett D'Amore 
28188447a05SGarrett D'Amore 	if ((error = mod_remove(&audio810_modlinkage)) != 0) {
28288447a05SGarrett D'Amore 		return (error);
28388447a05SGarrett D'Amore 	}
28488447a05SGarrett D'Amore 
28588447a05SGarrett D'Amore 	/* clean up ops */
28688447a05SGarrett D'Amore 	audio_fini_ops(&audio810_dev_ops);
28788447a05SGarrett D'Amore 
28888447a05SGarrett D'Amore 	return (0);
28988447a05SGarrett D'Amore }
29088447a05SGarrett D'Amore 
29188447a05SGarrett D'Amore /*
29288447a05SGarrett D'Amore  * _info()
29388447a05SGarrett D'Amore  *
29488447a05SGarrett D'Amore  * Description:
29588447a05SGarrett D'Amore  *	Module information, returns information about the driver.
29688447a05SGarrett D'Amore  *
29788447a05SGarrett D'Amore  * Arguments:
29888447a05SGarrett D'Amore  *	modinfo		*modinfop	Pointer to the opaque modinfo structure
29988447a05SGarrett D'Amore  *
30088447a05SGarrett D'Amore  * Returns:
30188447a05SGarrett D'Amore  *	mod_info() status, see mod_info(9f)
30288447a05SGarrett D'Amore  */
30388447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)30488447a05SGarrett D'Amore _info(struct modinfo *modinfop)
30588447a05SGarrett D'Amore {
30688447a05SGarrett D'Amore 	return (mod_info(&audio810_modlinkage, modinfop));
30788447a05SGarrett D'Amore }
30888447a05SGarrett D'Amore 
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore /* ******************* Driver Entry Points ********************************* */
31188447a05SGarrett D'Amore 
31288447a05SGarrett D'Amore /*
31388447a05SGarrett D'Amore  * audio810_ddi_attach()
31488447a05SGarrett D'Amore  *
31588447a05SGarrett D'Amore  * Description:
31688447a05SGarrett D'Amore  *	Implements the DDI attach(9e) entry point.
31788447a05SGarrett D'Amore  *
31888447a05SGarrett D'Amore  * Arguments:
31988447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
32088447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
32188447a05SGarrett D'Amore  *
32288447a05SGarrett D'Amore  * Returns:
32388447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
32488447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
32588447a05SGarrett D'Amore  */
32688447a05SGarrett D'Amore static int
audio810_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)32788447a05SGarrett D'Amore audio810_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
32888447a05SGarrett D'Amore {
32988447a05SGarrett D'Amore 	switch (cmd) {
33088447a05SGarrett D'Amore 	case DDI_ATTACH:
33188447a05SGarrett D'Amore 		return (audio810_attach(dip));
33288447a05SGarrett D'Amore 
33388447a05SGarrett D'Amore 	case DDI_RESUME:
33488447a05SGarrett D'Amore 		return (audio810_resume(dip));
33588447a05SGarrett D'Amore 	}
33688447a05SGarrett D'Amore 	return (DDI_FAILURE);
33788447a05SGarrett D'Amore }
33888447a05SGarrett D'Amore 
33988447a05SGarrett D'Amore /*
34088447a05SGarrett D'Amore  * audio810_ddi_detach()
34188447a05SGarrett D'Amore  *
34288447a05SGarrett D'Amore  * Description:
34388447a05SGarrett D'Amore  *	Implements the detach(9e) entry point.
34488447a05SGarrett D'Amore  *
34588447a05SGarrett D'Amore  * Arguments:
34688447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
34788447a05SGarrett D'Amore  *	ddi_detach_cmd_t	cmd	Detach command
34888447a05SGarrett D'Amore  *
34988447a05SGarrett D'Amore  * Returns:
35088447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
35188447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
35288447a05SGarrett D'Amore  */
35388447a05SGarrett D'Amore static int
audio810_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)35488447a05SGarrett D'Amore audio810_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
35588447a05SGarrett D'Amore {
35688447a05SGarrett D'Amore 	switch (cmd) {
35788447a05SGarrett D'Amore 	case DDI_DETACH:
35888447a05SGarrett D'Amore 		return (audio810_detach(dip));
35988447a05SGarrett D'Amore 
36088447a05SGarrett D'Amore 	case DDI_SUSPEND:
36188447a05SGarrett D'Amore 		return (audio810_suspend(dip));
36288447a05SGarrett D'Amore 	}
36388447a05SGarrett D'Amore 	return (DDI_FAILURE);
36488447a05SGarrett D'Amore }
36588447a05SGarrett D'Amore 
36688447a05SGarrett D'Amore /*
36788447a05SGarrett D'Amore  * audio810_ddi_quiesce()
36888447a05SGarrett D'Amore  *
36988447a05SGarrett D'Amore  * Description:
37088447a05SGarrett D'Amore  *	Implements the quiesce(9e) entry point.
37188447a05SGarrett D'Amore  *
37288447a05SGarrett D'Amore  * Arguments:
37388447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
37488447a05SGarrett D'Amore  *
37588447a05SGarrett D'Amore  * Returns:
37688447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was quiesced
37788447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be quiesced
37888447a05SGarrett D'Amore  */
37988447a05SGarrett D'Amore static int
audio810_ddi_quiesce(dev_info_t * dip)38088447a05SGarrett D'Amore audio810_ddi_quiesce(dev_info_t *dip)
38188447a05SGarrett D'Amore {
38288447a05SGarrett D'Amore 	audio810_state_t	*statep;
38388447a05SGarrett D'Amore 
38488447a05SGarrett D'Amore 	if ((statep = ddi_get_driver_private(dip)) == NULL)
38588447a05SGarrett D'Amore 		return (DDI_FAILURE);
38688447a05SGarrett D'Amore 
38788447a05SGarrett D'Amore 	audio810_stop_dma(statep);
38888447a05SGarrett D'Amore 	return (DDI_SUCCESS);
38988447a05SGarrett D'Amore }
39088447a05SGarrett D'Amore 
39188447a05SGarrett D'Amore /*
39288447a05SGarrett D'Amore  * audio810_open()
39388447a05SGarrett D'Amore  *
39488447a05SGarrett D'Amore  * Description:
39588447a05SGarrett D'Amore  *	Opens a DMA engine for use.
39688447a05SGarrett D'Amore  *
39788447a05SGarrett D'Amore  * Arguments:
39888447a05SGarrett D'Amore  *	void		*arg		The DMA engine to set up
39988447a05SGarrett D'Amore  *	int		flag		Open flags
400*68c47f65SGarrett D'Amore  *	unsigned	*nframes	Receives total number of frames
40188447a05SGarrett D'Amore  *	caddr_t		*bufp		Receives kernel data buffer
40288447a05SGarrett D'Amore  *
40388447a05SGarrett D'Amore  * Returns:
40488447a05SGarrett D'Amore  *	0	on success
40588447a05SGarrett D'Amore  *	errno	on failure
40688447a05SGarrett D'Amore  */
40788447a05SGarrett D'Amore static int
audio810_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)408*68c47f65SGarrett D'Amore audio810_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
40988447a05SGarrett D'Amore {
41088447a05SGarrett D'Amore 	audio810_port_t	*port = arg;
41188447a05SGarrett D'Amore 
41288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
41388447a05SGarrett D'Amore 
41488447a05SGarrett D'Amore 	port->count = 0;
415*68c47f65SGarrett D'Amore 	*nframes = port->samp_frames;
41688447a05SGarrett D'Amore 	*bufp = port->samp_kaddr;
41788447a05SGarrett D'Amore 
41888447a05SGarrett D'Amore 	return (0);
41988447a05SGarrett D'Amore }
42088447a05SGarrett D'Amore 
42188447a05SGarrett D'Amore /*
42288447a05SGarrett D'Amore  * audio810_close()
42388447a05SGarrett D'Amore  *
42488447a05SGarrett D'Amore  * Description:
42588447a05SGarrett D'Amore  *	Closes an audio DMA engine that was previously opened.  Since
42688447a05SGarrett D'Amore  *	nobody is using it, we take this opportunity to possibly power
42788447a05SGarrett D'Amore  *	down the entire device.
42888447a05SGarrett D'Amore  *
42988447a05SGarrett D'Amore  * Arguments:
43088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to shut down
43188447a05SGarrett D'Amore  */
43288447a05SGarrett D'Amore static void
audio810_close(void * arg)43388447a05SGarrett D'Amore audio810_close(void *arg)
43488447a05SGarrett D'Amore {
435*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
43688447a05SGarrett D'Amore }
43788447a05SGarrett D'Amore 
43888447a05SGarrett D'Amore /*
43988447a05SGarrett D'Amore  * audio810_stop()
44088447a05SGarrett D'Amore  *
44188447a05SGarrett D'Amore  * Description:
44288447a05SGarrett D'Amore  *	This is called by the framework to stop a port that is
44388447a05SGarrett D'Amore  *	transferring data.
44488447a05SGarrett D'Amore  *
44588447a05SGarrett D'Amore  * Arguments:
44688447a05SGarrett D'Amore  *	void	*arg		The DMA engine to stop
44788447a05SGarrett D'Amore  */
44888447a05SGarrett D'Amore static void
audio810_stop(void * arg)44988447a05SGarrett D'Amore audio810_stop(void *arg)
45088447a05SGarrett D'Amore {
45188447a05SGarrett D'Amore 	audio810_port_t		*port = arg;
45288447a05SGarrett D'Amore 	audio810_state_t	*statep = port->statep;
453*68c47f65SGarrett D'Amore 	uint8_t			cr;
45488447a05SGarrett D'Amore 
455*68c47f65SGarrett D'Amore 	cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR);
456*68c47f65SGarrett D'Amore 	cr &= ~I810_BM_CR_RUN;
457*68c47f65SGarrett D'Amore 	I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr);
45888447a05SGarrett D'Amore }
45988447a05SGarrett D'Amore 
46088447a05SGarrett D'Amore /*
46188447a05SGarrett D'Amore  * audio810_start()
46288447a05SGarrett D'Amore  *
46388447a05SGarrett D'Amore  * Description:
46488447a05SGarrett D'Amore  *	This is called by the framework to start a port transferring data.
46588447a05SGarrett D'Amore  *
46688447a05SGarrett D'Amore  * Arguments:
46788447a05SGarrett D'Amore  *	void	*arg		The DMA engine to start
46888447a05SGarrett D'Amore  *
46988447a05SGarrett D'Amore  * Returns:
47088447a05SGarrett D'Amore  *	0 	on success (never fails, errno if it did)
47188447a05SGarrett D'Amore  */
47288447a05SGarrett D'Amore static int
audio810_start(void * arg)47388447a05SGarrett D'Amore audio810_start(void *arg)
47488447a05SGarrett D'Amore {
47588447a05SGarrett D'Amore 	audio810_port_t		*port = arg;
47688447a05SGarrett D'Amore 	audio810_state_t	*statep = port->statep;
477*68c47f65SGarrett D'Amore 	uint8_t			regoff, cr;
47888447a05SGarrett D'Amore 
479*68c47f65SGarrett D'Amore 	regoff = port->regoff;
480*68c47f65SGarrett D'Amore 	port->offset = 0;
481*68c47f65SGarrett D'Amore 
482*68c47f65SGarrett D'Amore 	/* program multiple channel settings */
483*68c47f65SGarrett D'Amore 	if (port->num == I810_PCM_OUT) {
484*68c47f65SGarrett D'Amore 		audio810_set_channels(statep);
485*68c47f65SGarrett D'Amore 
486*68c47f65SGarrett D'Amore 		if (statep->quirk == QUIRK_SIS7012) {
487*68c47f65SGarrett D'Amore 			/*
488*68c47f65SGarrett D'Amore 			 * SiS 7012 has special unmute bit.
489*68c47f65SGarrett D'Amore 			 */
490*68c47f65SGarrett D'Amore 			I810_BM_PUT8(I810_REG_SISCTL, I810_SISCTL_UNMUTE);
491*68c47f65SGarrett D'Amore 		}
49288447a05SGarrett D'Amore 	}
493*68c47f65SGarrett D'Amore 
494*68c47f65SGarrett D'Amore 	/*
495*68c47f65SGarrett D'Amore 	 * Perform full reset of the engine, but leave it turned off.
496*68c47f65SGarrett D'Amore 	 */
497*68c47f65SGarrett D'Amore 	I810_BM_PUT8(regoff + I810_OFFSET_CR, 0);
498*68c47f65SGarrett D'Amore 	I810_BM_PUT8(regoff + I810_OFFSET_CR, I810_BM_CR_RST);
499*68c47f65SGarrett D'Amore 
500*68c47f65SGarrett D'Amore 	/* program the offset of the BD list */
501*68c47f65SGarrett D'Amore 	I810_BM_PUT32(regoff + I810_OFFSET_BD_BASE, port->bdl_paddr);
502*68c47f65SGarrett D'Amore 
503*68c47f65SGarrett D'Amore 	/* we set the last index to the full count -- all buffers are valid */
504*68c47f65SGarrett D'Amore 	I810_BM_PUT8(regoff + I810_OFFSET_LVI, I810_BD_NUMS - 1);
505*68c47f65SGarrett D'Amore 
506*68c47f65SGarrett D'Amore 	cr = I810_BM_GET8(regoff + I810_OFFSET_CR);
507*68c47f65SGarrett D'Amore 	cr |= I810_BM_CR_RUN;
508*68c47f65SGarrett D'Amore 	I810_BM_PUT8(regoff + I810_OFFSET_CR, cr);
509*68c47f65SGarrett D'Amore 
510*68c47f65SGarrett D'Amore 	(void) I810_BM_GET8(regoff + I810_OFFSET_CR);
511*68c47f65SGarrett D'Amore 
51288447a05SGarrett D'Amore 	return (0);
51388447a05SGarrett D'Amore }
51488447a05SGarrett D'Amore 
51588447a05SGarrett D'Amore /*
51688447a05SGarrett D'Amore  * audio810_format()
51788447a05SGarrett D'Amore  *
51888447a05SGarrett D'Amore  * Description:
51988447a05SGarrett D'Amore  *	This is called by the framework to query the format of the device.
52088447a05SGarrett D'Amore  *
52188447a05SGarrett D'Amore  * Arguments:
52288447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
52388447a05SGarrett D'Amore  *
52488447a05SGarrett D'Amore  * Returns:
52588447a05SGarrett D'Amore  *	Format of the device (fixed at AUDIO_FORMAT_S16_LE)
52688447a05SGarrett D'Amore  */
52788447a05SGarrett D'Amore static int
audio810_format(void * arg)52888447a05SGarrett D'Amore audio810_format(void *arg)
52988447a05SGarrett D'Amore {
53088447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
53188447a05SGarrett D'Amore 
53288447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
53388447a05SGarrett D'Amore }
53488447a05SGarrett D'Amore 
53588447a05SGarrett D'Amore /*
53688447a05SGarrett D'Amore  * audio810_channels()
53788447a05SGarrett D'Amore  *
53888447a05SGarrett D'Amore  * Description:
53988447a05SGarrett D'Amore  *	This is called by the framework to query the num channels of
54088447a05SGarrett D'Amore  *	the device.
54188447a05SGarrett D'Amore  *
54288447a05SGarrett D'Amore  * Arguments:
54388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
54488447a05SGarrett D'Amore  *
54588447a05SGarrett D'Amore  * Returns:
54688447a05SGarrett D'Amore  *	0 number of channels for device
54788447a05SGarrett D'Amore  */
54888447a05SGarrett D'Amore static int
audio810_channels(void * arg)54988447a05SGarrett D'Amore audio810_channels(void *arg)
55088447a05SGarrett D'Amore {
55188447a05SGarrett D'Amore 	audio810_port_t	*port = arg;
55288447a05SGarrett D'Amore 
55388447a05SGarrett D'Amore 	return (port->nchan);
55488447a05SGarrett D'Amore }
55588447a05SGarrett D'Amore 
55688447a05SGarrett D'Amore /*
55788447a05SGarrett D'Amore  * audio810_rate()
55888447a05SGarrett D'Amore  *
55988447a05SGarrett D'Amore  * Description:
56088447a05SGarrett D'Amore  *	This is called by the framework to query the rate of the device.
56188447a05SGarrett D'Amore  *
56288447a05SGarrett D'Amore  * Arguments:
56388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
56488447a05SGarrett D'Amore  *
56588447a05SGarrett D'Amore  * Returns:
56688447a05SGarrett D'Amore  *	Rate of device (fixed at 48000 Hz)
56788447a05SGarrett D'Amore  */
56888447a05SGarrett D'Amore static int
audio810_rate(void * arg)56988447a05SGarrett D'Amore audio810_rate(void *arg)
57088447a05SGarrett D'Amore {
57188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
57288447a05SGarrett D'Amore 
57388447a05SGarrett D'Amore 	return (48000);
57488447a05SGarrett D'Amore }
57588447a05SGarrett D'Amore 
57688447a05SGarrett D'Amore /*
57788447a05SGarrett D'Amore  * audio810_count()
57888447a05SGarrett D'Amore  *
57988447a05SGarrett D'Amore  * Description:
58088447a05SGarrett D'Amore  *	This is called by the framework to get the engine's frame counter
58188447a05SGarrett D'Amore  *
58288447a05SGarrett D'Amore  * Arguments:
58388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
58488447a05SGarrett D'Amore  *
58588447a05SGarrett D'Amore  * Returns:
58688447a05SGarrett D'Amore  *	frame count for current engine
58788447a05SGarrett D'Amore  */
58888447a05SGarrett D'Amore static uint64_t
audio810_count(void * arg)58988447a05SGarrett D'Amore audio810_count(void *arg)
59088447a05SGarrett D'Amore {
59188447a05SGarrett D'Amore 	audio810_port_t		*port = arg;
59288447a05SGarrett D'Amore 	audio810_state_t	*statep = port->statep;
593*68c47f65SGarrett D'Amore 	uint8_t			regoff = port->regoff;
59488447a05SGarrett D'Amore 	uint64_t		val;
595*68c47f65SGarrett D'Amore 	uint32_t		offset;
596*68c47f65SGarrett D'Amore 	uint8_t			civ;
597*68c47f65SGarrett D'Amore 
598*68c47f65SGarrett D'Amore 	/*
599*68c47f65SGarrett D'Amore 	 * Read the position counters.  We also take this opportunity
600*68c47f65SGarrett D'Amore 	 * to update the last valid index to the one just previous to
601*68c47f65SGarrett D'Amore 	 * the one we're working on (so we'll fully loop.)
602*68c47f65SGarrett D'Amore 	 */
603*68c47f65SGarrett D'Amore 	offset = I810_BM_GET16(port->picboff);
604*68c47f65SGarrett D'Amore 	civ = I810_BM_GET8(regoff + I810_OFFSET_CIV);
605*68c47f65SGarrett D'Amore 	I810_BM_PUT8(port->regoff + I810_OFFSET_LVI, (civ - 1) % I810_BD_NUMS);
60688447a05SGarrett D'Amore 
607*68c47f65SGarrett D'Amore 	/* SiS counts in bytes, all others in words. */
608*68c47f65SGarrett D'Amore 	if (statep->quirk != QUIRK_SIS7012)
609*68c47f65SGarrett D'Amore 		offset *= 2;
61088447a05SGarrett D'Amore 
611*68c47f65SGarrett D'Amore 	/* counter is reversed */
612*68c47f65SGarrett D'Amore 	offset = port->samp_size - offset;
613*68c47f65SGarrett D'Amore 
614*68c47f65SGarrett D'Amore 	if (offset < port->offset) {
615*68c47f65SGarrett D'Amore 		val = (port->samp_size - port->offset) + offset;
61688447a05SGarrett D'Amore 	} else {
617*68c47f65SGarrett D'Amore 		val = offset - port->offset;
61888447a05SGarrett D'Amore 	}
619*68c47f65SGarrett D'Amore 	port->offset = offset;
620*68c47f65SGarrett D'Amore 	port->count += (val / (port->nchan * 2));
621*68c47f65SGarrett D'Amore 	val = port->count;
62288447a05SGarrett D'Amore 
62388447a05SGarrett D'Amore 	return (val);
62488447a05SGarrett D'Amore }
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore /*
62788447a05SGarrett D'Amore  * audio810_sync()
62888447a05SGarrett D'Amore  *
62988447a05SGarrett D'Amore  * Description:
63088447a05SGarrett D'Amore  *	This is called by the framework to synchronize DMA caches.
63188447a05SGarrett D'Amore  *
63288447a05SGarrett D'Amore  * Arguments:
63388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to sync
63488447a05SGarrett D'Amore  */
63588447a05SGarrett D'Amore static void
audio810_sync(void * arg,unsigned nframes)63688447a05SGarrett D'Amore audio810_sync(void *arg, unsigned nframes)
63788447a05SGarrett D'Amore {
63888447a05SGarrett D'Amore 	audio810_port_t *port = arg;
63988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
64088447a05SGarrett D'Amore 
64188447a05SGarrett D'Amore 	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
64288447a05SGarrett D'Amore }
64388447a05SGarrett D'Amore 
64488447a05SGarrett D'Amore /*
645f9ead4a5SGarrett D'Amore  * audio810_playahead()
64688447a05SGarrett D'Amore  *
64788447a05SGarrett D'Amore  * Description:
648f9ead4a5SGarrett D'Amore  *	This is called by the framework to determine how much data it
649f9ead4a5SGarrett D'Amore  *	should queue up.  We desire a deeper playahead than most to
650f9ead4a5SGarrett D'Amore  *	allow for virtualized devices which have less "regular"
651f9ead4a5SGarrett D'Amore  *	interrupt scheduling.
65288447a05SGarrett D'Amore  *
65388447a05SGarrett D'Amore  * Arguments:
65488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
65588447a05SGarrett D'Amore  *
65688447a05SGarrett D'Amore  * Returns:
657*68c47f65SGarrett D'Amore  *	Play ahead in frames.
65888447a05SGarrett D'Amore  */
659f9ead4a5SGarrett D'Amore static unsigned
audio810_playahead(void * arg)660f9ead4a5SGarrett D'Amore audio810_playahead(void *arg)
66188447a05SGarrett D'Amore {
662f9ead4a5SGarrett D'Amore 	audio810_port_t *port = arg;
66388447a05SGarrett D'Amore 	audio810_state_t	*statep = port->statep;
66488447a05SGarrett D'Amore 
665*68c47f65SGarrett D'Amore 	/* Older ICH is likely to be emulated, deeper (40 ms) playahead */
666*68c47f65SGarrett D'Amore 	return (statep->quirk == QUIRK_OLDICH ? 1920 : 0);
66788447a05SGarrett D'Amore }
66888447a05SGarrett D'Amore 
66988447a05SGarrett D'Amore 
67088447a05SGarrett D'Amore 
671*68c47f65SGarrett D'Amore /* *********************** Local Routines *************************** */
67288447a05SGarrett D'Amore 
67388447a05SGarrett D'Amore /*
67488447a05SGarrett D'Amore  * audio810_attach()
67588447a05SGarrett D'Amore  *
67688447a05SGarrett D'Amore  * Description:
67788447a05SGarrett D'Amore  *	Attach an instance of the audio810 driver. This routine does the
67888447a05SGarrett D'Amore  * 	device dependent attach tasks, and registers with the audio framework.
67988447a05SGarrett D'Amore  *
68088447a05SGarrett D'Amore  * Arguments:
68188447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
68288447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
68388447a05SGarrett D'Amore  *
68488447a05SGarrett D'Amore  * Returns:
68588447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
68688447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
68788447a05SGarrett D'Amore  */
68888447a05SGarrett D'Amore static int
audio810_attach(dev_info_t * dip)68988447a05SGarrett D'Amore audio810_attach(dev_info_t *dip)
69088447a05SGarrett D'Amore {
69188447a05SGarrett D'Amore 	uint16_t		cmdreg;
69288447a05SGarrett D'Amore 	audio810_state_t	*statep;
69388447a05SGarrett D'Amore 	audio_dev_t		*adev;
69488447a05SGarrett D'Amore 	ddi_acc_handle_t	pcih;
69588447a05SGarrett D'Amore 	uint32_t		devid;
69688447a05SGarrett D'Amore 	uint32_t		gsr;
69788447a05SGarrett D'Amore 	const char		*name;
69888447a05SGarrett D'Amore 	const char		*vers;
69988447a05SGarrett D'Amore 	uint8_t			nch;
70088447a05SGarrett D'Amore 	int			maxch;
70188447a05SGarrett D'Amore 
70288447a05SGarrett D'Amore 	/* allocate the soft state structure */
70388447a05SGarrett D'Amore 	statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
70488447a05SGarrett D'Amore 	ddi_set_driver_private(dip, statep);
70588447a05SGarrett D'Amore 
70688447a05SGarrett D'Amore 	if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
707*68c47f65SGarrett D'Amore 		cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev",
708*68c47f65SGarrett D'Amore 		    ddi_driver_name(dip), ddi_get_instance(dip));
70988447a05SGarrett D'Amore 		goto error;
71088447a05SGarrett D'Amore 	}
71188447a05SGarrett D'Amore 	statep->adev = adev;
71288447a05SGarrett D'Amore 	statep->dip = dip;
71388447a05SGarrett D'Amore 
71488447a05SGarrett D'Amore 	/* map in the registers, allocate DMA buffers, etc. */
71588447a05SGarrett D'Amore 	if (audio810_map_regs(dip, statep) != DDI_SUCCESS) {
71688447a05SGarrett D'Amore 		audio_dev_warn(adev, "couldn't map registers");
71788447a05SGarrett D'Amore 		goto error;
71888447a05SGarrett D'Amore 	}
71988447a05SGarrett D'Amore 
72088447a05SGarrett D'Amore 	/* set PCI command register */
72188447a05SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
72288447a05SGarrett D'Amore 		audio_dev_warn(adev, "pci conf mapping failed");
72388447a05SGarrett D'Amore 		goto error;
72488447a05SGarrett D'Amore 	}
72588447a05SGarrett D'Amore 	cmdreg = pci_config_get16(pcih, PCI_CONF_COMM);
72688447a05SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM,
72788447a05SGarrett D'Amore 	    cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
72888447a05SGarrett D'Amore 	devid = pci_config_get16(pcih, PCI_CONF_VENID);
72988447a05SGarrett D'Amore 	devid <<= 16;
73088447a05SGarrett D'Amore 	devid |= pci_config_get16(pcih, PCI_CONF_DEVID);
73188447a05SGarrett D'Amore 	pci_config_teardown(&pcih);
73288447a05SGarrett D'Amore 
73388447a05SGarrett D'Amore 	name = "Unknown AC'97";
73488447a05SGarrett D'Amore 	vers = "";
73588447a05SGarrett D'Amore 
73688447a05SGarrett D'Amore 	statep->quirk = QUIRK_NONE;
73788447a05SGarrett D'Amore 	switch (devid) {
73888447a05SGarrett D'Amore 	case 0x80862415:
73988447a05SGarrett D'Amore 		name = "Intel AC'97";
74088447a05SGarrett D'Amore 		vers = "ICH";
741*68c47f65SGarrett D'Amore 		statep->quirk = QUIRK_OLDICH;
74288447a05SGarrett D'Amore 		break;
74388447a05SGarrett D'Amore 	case 0x80862425:
74488447a05SGarrett D'Amore 		name = "Intel AC'97";
74588447a05SGarrett D'Amore 		vers = "ICH0";
74688447a05SGarrett D'Amore 		break;
74788447a05SGarrett D'Amore 	case 0x80867195:
74888447a05SGarrett D'Amore 		name = "Intel AC'97";
74988447a05SGarrett D'Amore 		vers = "440MX";
75088447a05SGarrett D'Amore 		break;
75188447a05SGarrett D'Amore 	case 0x80862445:
75288447a05SGarrett D'Amore 		name = "Intel AC'97";
75388447a05SGarrett D'Amore 		vers = "ICH2";
75488447a05SGarrett D'Amore 		break;
75588447a05SGarrett D'Amore 	case 0x80862485:
75688447a05SGarrett D'Amore 		name = "Intel AC'97";
75788447a05SGarrett D'Amore 		vers = "ICH3";
75888447a05SGarrett D'Amore 		break;
75988447a05SGarrett D'Amore 	case 0x808624C5:
76088447a05SGarrett D'Amore 		name = "Intel AC'97";
76188447a05SGarrett D'Amore 		vers = "ICH4";
76288447a05SGarrett D'Amore 		break;
76388447a05SGarrett D'Amore 	case 0x808624D5:
76488447a05SGarrett D'Amore 		name = "Intel AC'97";
76588447a05SGarrett D'Amore 		vers = "ICH5";
76688447a05SGarrett D'Amore 		break;
76788447a05SGarrett D'Amore 	case 0x8086266E:
76888447a05SGarrett D'Amore 		name = "Intel AC'97";
76988447a05SGarrett D'Amore 		vers = "ICH6";
77088447a05SGarrett D'Amore 		break;
77188447a05SGarrett D'Amore 	case 0x808627DE:
77288447a05SGarrett D'Amore 		name = "Intel AC'97";
77388447a05SGarrett D'Amore 		vers = "ICH7";
77488447a05SGarrett D'Amore 		break;
77588447a05SGarrett D'Amore 	case 0x808625A6:
77688447a05SGarrett D'Amore 		name = "Intel AC'97";
77788447a05SGarrett D'Amore 		vers = "6300ESB";
77888447a05SGarrett D'Amore 		break;
77988447a05SGarrett D'Amore 	case 0x80862698:
78088447a05SGarrett D'Amore 		name = "Intel AC'97";
78188447a05SGarrett D'Amore 		vers = "ESB2";
78288447a05SGarrett D'Amore 		break;
78388447a05SGarrett D'Amore 	case 0x10397012:
78488447a05SGarrett D'Amore 		name = "SiS AC'97";
78588447a05SGarrett D'Amore 		vers = "7012";
78688447a05SGarrett D'Amore 		statep->quirk = QUIRK_SIS7012;
78788447a05SGarrett D'Amore 		break;
78888447a05SGarrett D'Amore 	case 0x10de01b1:	/* nForce */
78988447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
79088447a05SGarrett D'Amore 		vers = "MCP1";
79188447a05SGarrett D'Amore 		break;
79288447a05SGarrett D'Amore 	case 0x10de006a:	/* nForce 2 */
79388447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
79488447a05SGarrett D'Amore 		vers = "MCP2";
79588447a05SGarrett D'Amore 		break;
79688447a05SGarrett D'Amore 	case 0x10de00da:	/* nForce 3 */
79788447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
79888447a05SGarrett D'Amore 		vers = "MCP3";
79988447a05SGarrett D'Amore 		break;
80088447a05SGarrett D'Amore 	case 0x10de00ea:
80188447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
80288447a05SGarrett D'Amore 		vers = "CK8S";
80388447a05SGarrett D'Amore 		break;
80488447a05SGarrett D'Amore 	case 0x10de0059:
80588447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
80688447a05SGarrett D'Amore 		vers = "CK804";
80788447a05SGarrett D'Amore 		break;
80888447a05SGarrett D'Amore 	case 0x10de008a:
80988447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
81088447a05SGarrett D'Amore 		vers = "CK8";
81188447a05SGarrett D'Amore 		break;
81288447a05SGarrett D'Amore 	case 0x10de003a:	/* nForce 4 */
81388447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
81488447a05SGarrett D'Amore 		vers = "MCP4";
81588447a05SGarrett D'Amore 		break;
81688447a05SGarrett D'Amore 	case 0x10de026b:
81788447a05SGarrett D'Amore 		name = "NVIDIA AC'97";
81888447a05SGarrett D'Amore 		vers = "MCP51";
81988447a05SGarrett D'Amore 		break;
82088447a05SGarrett D'Amore 	case 0x1022746d:
82188447a05SGarrett D'Amore 		name = "AMD AC'97";
82288447a05SGarrett D'Amore 		vers = "8111";
82388447a05SGarrett D'Amore 		break;
82488447a05SGarrett D'Amore 	case 0x10227445:
82588447a05SGarrett D'Amore 		name = "AMD AC'97";
82688447a05SGarrett D'Amore 		vers = "AMD768";
82788447a05SGarrett D'Amore 		break;
82888447a05SGarrett D'Amore 	}
82988447a05SGarrett D'Amore 	/* set device information */
83088447a05SGarrett D'Amore 	audio_dev_set_description(adev, name);
83188447a05SGarrett D'Amore 	audio_dev_set_version(adev, vers);
83288447a05SGarrett D'Amore 
8330e7a77f3SGarrett D'Amore 	/* initialize audio controller and AC97 codec */
8340e7a77f3SGarrett D'Amore 	if (audio810_chip_init(statep) != DDI_SUCCESS) {
8350e7a77f3SGarrett D'Amore 		audio_dev_warn(adev, "failed to init chip");
83688447a05SGarrett D'Amore 		goto error;
83788447a05SGarrett D'Amore 	}
83888447a05SGarrett D'Amore 
83988447a05SGarrett D'Amore 	/* allocate ac97 handle */
84088447a05SGarrett D'Amore 	statep->ac97 = ac97_alloc(dip, audio810_read_ac97, audio810_write_ac97,
84188447a05SGarrett D'Amore 	    statep);
84288447a05SGarrett D'Amore 	if (statep->ac97 == NULL) {
84388447a05SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate ac97 handle");
84488447a05SGarrett D'Amore 		goto error;
84588447a05SGarrett D'Amore 	}
84688447a05SGarrett D'Amore 
84788447a05SGarrett D'Amore 	/* initialize the AC'97 part */
84888447a05SGarrett D'Amore 	if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
84988447a05SGarrett D'Amore 		audio_dev_warn(adev, "ac'97 initialization failed");
85088447a05SGarrett D'Amore 		goto error;
85188447a05SGarrett D'Amore 	}
85288447a05SGarrett D'Amore 
8530e7a77f3SGarrett D'Amore 	/*
8540e7a77f3SGarrett D'Amore 	 * Override "max-channels" property to prevent configuration
8550e7a77f3SGarrett D'Amore 	 * of 4 or 6 (or possibly even 8!) channel audio.  The default
8560e7a77f3SGarrett D'Amore 	 * is to support as many channels as the hardware can do.
8570e7a77f3SGarrett D'Amore 	 *
8580e7a77f3SGarrett D'Amore 	 * (Hmmm... perhaps this should be driven in the common
8590e7a77f3SGarrett D'Amore 	 * framework.  The framework could even offer simplistic upmix
8600e7a77f3SGarrett D'Amore 	 * and downmix for various standard configs.)
8610e7a77f3SGarrett D'Amore 	 */
8620e7a77f3SGarrett D'Amore 	maxch = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8630e7a77f3SGarrett D'Amore 	    "max-channels", ac97_num_channels(statep->ac97));
8640e7a77f3SGarrett D'Amore 	if (maxch < 2) {
8650e7a77f3SGarrett D'Amore 		maxch = 2;
8660e7a77f3SGarrett D'Amore 	}
8670e7a77f3SGarrett D'Amore 
8680e7a77f3SGarrett D'Amore 	gsr = I810_BM_GET32(I810_REG_GSR);
8690e7a77f3SGarrett D'Amore 	if (gsr & I810_GSR_CAP6CH) {
8700e7a77f3SGarrett D'Amore 		nch = 6;
8710e7a77f3SGarrett D'Amore 	} else if (gsr & I810_GSR_CAP4CH) {
8720e7a77f3SGarrett D'Amore 		nch = 4;
8730e7a77f3SGarrett D'Amore 	} else {
8740e7a77f3SGarrett D'Amore 		nch = 2;
8750e7a77f3SGarrett D'Amore 	}
8760e7a77f3SGarrett D'Amore 
8770e7a77f3SGarrett D'Amore 	statep->maxch = (uint8_t)min(nch, maxch);
8780e7a77f3SGarrett D'Amore 	statep->maxch &= ~1;
8790e7a77f3SGarrett D'Amore 
8800e7a77f3SGarrett D'Amore 	/* allocate port structures */
8810e7a77f3SGarrett D'Amore 	if ((audio810_alloc_port(statep, I810_PCM_OUT, statep->maxch) !=
8820e7a77f3SGarrett D'Amore 	    DDI_SUCCESS) ||
8830e7a77f3SGarrett D'Amore 	    (audio810_alloc_port(statep, I810_PCM_IN, 2) != DDI_SUCCESS)) {
8840e7a77f3SGarrett D'Amore 		goto error;
8850e7a77f3SGarrett D'Amore 	}
8860e7a77f3SGarrett D'Amore 
88788447a05SGarrett D'Amore 	if (audio_dev_register(adev) != DDI_SUCCESS) {
88888447a05SGarrett D'Amore 		audio_dev_warn(adev, "unable to register with framework");
88988447a05SGarrett D'Amore 		goto error;
89088447a05SGarrett D'Amore 	}
89188447a05SGarrett D'Amore 
89288447a05SGarrett D'Amore 	ddi_report_dev(dip);
89388447a05SGarrett D'Amore 
89488447a05SGarrett D'Amore 	return (DDI_SUCCESS);
89588447a05SGarrett D'Amore 
89688447a05SGarrett D'Amore error:
89788447a05SGarrett D'Amore 	audio810_destroy(statep);
89888447a05SGarrett D'Amore 
89988447a05SGarrett D'Amore 	return (DDI_FAILURE);
90088447a05SGarrett D'Amore }
90188447a05SGarrett D'Amore 
90288447a05SGarrett D'Amore 
90388447a05SGarrett D'Amore /*
90488447a05SGarrett D'Amore  * audio810_resume()
90588447a05SGarrett D'Amore  *
90688447a05SGarrett D'Amore  * Description:
90788447a05SGarrett D'Amore  *	Resume operation of the device after sleeping or hibernating.
90888447a05SGarrett D'Amore  *	Note that this should never fail, even if hardware goes wonky,
90988447a05SGarrett D'Amore  *	because the current PM framework will panic if it does.
91088447a05SGarrett D'Amore  *
91188447a05SGarrett D'Amore  * Arguments:
91288447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
91388447a05SGarrett D'Amore  *
91488447a05SGarrett D'Amore  * Returns:
91588447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was resumed.
91688447a05SGarrett D'Amore  */
91788447a05SGarrett D'Amore static int
audio810_resume(dev_info_t * dip)91888447a05SGarrett D'Amore audio810_resume(dev_info_t *dip)
91988447a05SGarrett D'Amore {
92088447a05SGarrett D'Amore 	audio810_state_t	*statep;
92188447a05SGarrett D'Amore 	audio_dev_t		*adev;
92288447a05SGarrett D'Amore 
92388447a05SGarrett D'Amore 	/* this should always be valid */
92488447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
92588447a05SGarrett D'Amore 	adev = statep->adev;
92688447a05SGarrett D'Amore 
92788447a05SGarrett D'Amore 	ASSERT(statep != NULL);
92888447a05SGarrett D'Amore 	ASSERT(dip == statep->dip);
92988447a05SGarrett D'Amore 
93088447a05SGarrett D'Amore 	/* Restore the audio810 chip's state */
93188447a05SGarrett D'Amore 	if (audio810_chip_init(statep) != DDI_SUCCESS) {
93288447a05SGarrett D'Amore 		/*
933*68c47f65SGarrett D'Amore 		 * Note that PM gurus say we should return success
934*68c47f65SGarrett D'Amore 		 * here.  Failure of audio shouldn't be considered
935*68c47f65SGarrett D'Amore 		 * FATAL to the system.
936*68c47f65SGarrett D'Amore 		 *
937*68c47f65SGarrett D'Amore 		 * It turns out that the only way that the
938*68c47f65SGarrett D'Amore 		 * audio810_chip_init fails is that the codec won't
939*68c47f65SGarrett D'Amore 		 * re-initialize.  Audio streams may or may not make
940*68c47f65SGarrett D'Amore 		 * progress; setting changes may or may not have the
941*68c47f65SGarrett D'Amore 		 * desired effect.  What we'd really to do at this
942*68c47f65SGarrett D'Amore 		 * point is use FMA to offline the part.  In the
943*68c47f65SGarrett D'Amore 		 * meantime, we just muddle on logging the error.
944*68c47f65SGarrett D'Amore 		 *
945*68c47f65SGarrett D'Amore 		 * Note that returning from this routine without
946*68c47f65SGarrett D'Amore 		 * allowing the audio_dev_resume() to take place can
947*68c47f65SGarrett D'Amore 		 * have bad effects, as the framework does not know
948*68c47f65SGarrett D'Amore 		 * what to do in the event of a failure of this
949*68c47f65SGarrett D'Amore 		 * nature.  (It may be unsafe to call ENG_CLOSE(), for
950*68c47f65SGarrett D'Amore 		 * example.)
95188447a05SGarrett D'Amore 		 */
952*68c47f65SGarrett D'Amore 		audio_dev_warn(adev, "failure to resume codec");
95388447a05SGarrett D'Amore 	}
95488447a05SGarrett D'Amore 
955*68c47f65SGarrett D'Amore 	/* Reset the AC'97 codec. */
956*68c47f65SGarrett D'Amore 	ac97_reset(statep->ac97);
95788447a05SGarrett D'Amore 
958*68c47f65SGarrett D'Amore 	/* And let the framework know we're ready for business again. */
959*68c47f65SGarrett D'Amore 	audio_dev_resume(statep->adev);
96088447a05SGarrett D'Amore 
96188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
96288447a05SGarrett D'Amore }
96388447a05SGarrett D'Amore 
96488447a05SGarrett D'Amore /*
96588447a05SGarrett D'Amore  * audio810_detach()
96688447a05SGarrett D'Amore  *
96788447a05SGarrett D'Amore  * Description:
96888447a05SGarrett D'Amore  *	Detach an instance of the audio810 driver.
96988447a05SGarrett D'Amore  *
97088447a05SGarrett D'Amore  * Arguments:
97188447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
97288447a05SGarrett D'Amore  *
97388447a05SGarrett D'Amore  * Returns:
97488447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
97588447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
97688447a05SGarrett D'Amore  */
97788447a05SGarrett D'Amore static int
audio810_detach(dev_info_t * dip)97888447a05SGarrett D'Amore audio810_detach(dev_info_t *dip)
97988447a05SGarrett D'Amore {
98088447a05SGarrett D'Amore 	audio810_state_t	*statep;
98188447a05SGarrett D'Amore 
98288447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
98388447a05SGarrett D'Amore 	ASSERT(statep != NULL);
98488447a05SGarrett D'Amore 
98588447a05SGarrett D'Amore 	/* don't detach us if we are still in use */
98688447a05SGarrett D'Amore 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
98788447a05SGarrett D'Amore 		return (DDI_FAILURE);
98888447a05SGarrett D'Amore 	}
98988447a05SGarrett D'Amore 
99088447a05SGarrett D'Amore 	audio810_destroy(statep);
99188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
99288447a05SGarrett D'Amore }
99388447a05SGarrett D'Amore 
99488447a05SGarrett D'Amore /*
99588447a05SGarrett D'Amore  * audio810_suspend()
99688447a05SGarrett D'Amore  *
99788447a05SGarrett D'Amore  * Description:
99888447a05SGarrett D'Amore  *	Suspend an instance of the audio810 driver, in preparation for
99988447a05SGarrett D'Amore  *	sleep or hibernation.
100088447a05SGarrett D'Amore  *
100188447a05SGarrett D'Amore  * Arguments:
100288447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
100388447a05SGarrett D'Amore  *
100488447a05SGarrett D'Amore  * Returns:
100588447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was suspended
100688447a05SGarrett D'Amore  */
100788447a05SGarrett D'Amore static int
audio810_suspend(dev_info_t * dip)100888447a05SGarrett D'Amore audio810_suspend(dev_info_t *dip)
100988447a05SGarrett D'Amore {
101088447a05SGarrett D'Amore 	audio810_state_t	*statep;
101188447a05SGarrett D'Amore 
101288447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
101388447a05SGarrett D'Amore 	ASSERT(statep != NULL);
101488447a05SGarrett D'Amore 
1015*68c47f65SGarrett D'Amore 	audio_dev_suspend(statep->adev);
101688447a05SGarrett D'Amore 
1017*68c47f65SGarrett D'Amore 	/* stop DMA engines - should be redundant (paranoia) */
101888447a05SGarrett D'Amore 	audio810_stop_dma(statep);
101988447a05SGarrett D'Amore 
102088447a05SGarrett D'Amore 	return (DDI_SUCCESS);
102188447a05SGarrett D'Amore }
102288447a05SGarrett D'Amore 
102388447a05SGarrett D'Amore /*
102488447a05SGarrett D'Amore  * audio810_alloc_port()
102588447a05SGarrett D'Amore  *
102688447a05SGarrett D'Amore  * Description:
102788447a05SGarrett D'Amore  *	This routine allocates the DMA handles and the memory for the
102888447a05SGarrett D'Amore  *	DMA engines to use.  It also configures the BDL lists properly
102988447a05SGarrett D'Amore  *	for use.
103088447a05SGarrett D'Amore  *
103188447a05SGarrett D'Amore  * Arguments:
103288447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's devinfo
103388447a05SGarrett D'Amore  *
103488447a05SGarrett D'Amore  * Returns:
103588447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
103688447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
103788447a05SGarrett D'Amore  */
103888447a05SGarrett D'Amore static int
audio810_alloc_port(audio810_state_t * statep,int num,uint8_t nchan)103988447a05SGarrett D'Amore audio810_alloc_port(audio810_state_t *statep, int num, uint8_t nchan)
104088447a05SGarrett D'Amore {
104188447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
104288447a05SGarrett D'Amore 	uint_t			count;
104388447a05SGarrett D'Amore 	int			dir;
104488447a05SGarrett D'Amore 	unsigned		caps;
104588447a05SGarrett D'Amore 	audio_dev_t		*adev;
104688447a05SGarrett D'Amore 	audio810_port_t		*port;
104788447a05SGarrett D'Amore 	int			rc;
104888447a05SGarrett D'Amore 	dev_info_t		*dip;
104988447a05SGarrett D'Amore 	i810_bd_entry_t		*bdentry;
105088447a05SGarrett D'Amore 
105188447a05SGarrett D'Amore 	adev = statep->adev;
105288447a05SGarrett D'Amore 	dip = statep->dip;
105388447a05SGarrett D'Amore 
105488447a05SGarrett D'Amore 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
105588447a05SGarrett D'Amore 	statep->ports[num] = port;
105688447a05SGarrett D'Amore 	port->statep = statep;
105788447a05SGarrett D'Amore 	port->nchan = nchan;
105888447a05SGarrett D'Amore 	port->num = num;
105988447a05SGarrett D'Amore 
106088447a05SGarrett D'Amore 	switch (num) {
106188447a05SGarrett D'Amore 	case I810_PCM_IN:
106288447a05SGarrett D'Amore 		dir = DDI_DMA_READ;
106388447a05SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
106488447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
106588447a05SGarrett D'Amore 		port->regoff = I810_BASE_PCM_IN;
106688447a05SGarrett D'Amore 		break;
106788447a05SGarrett D'Amore 	case I810_PCM_OUT:
106888447a05SGarrett D'Amore 		dir = DDI_DMA_WRITE;
106988447a05SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
107088447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORDEV;
107188447a05SGarrett D'Amore 		port->regoff = I810_BASE_PCM_OUT;
107288447a05SGarrett D'Amore 		break;
107388447a05SGarrett D'Amore 	default:
107488447a05SGarrett D'Amore 		audio_dev_warn(adev, "bad port number (%d)!", num);
107588447a05SGarrett D'Amore 		return (DDI_FAILURE);
107688447a05SGarrett D'Amore 	}
107788447a05SGarrett D'Amore 
107888447a05SGarrett D'Amore 	/*
107988447a05SGarrett D'Amore 	 * SiS 7012 swaps status and picb registers.
108088447a05SGarrett D'Amore 	 */
108188447a05SGarrett D'Amore 	if (statep->quirk == QUIRK_SIS7012) {
108288447a05SGarrett D'Amore 		port->stsoff = port->regoff + I810_OFFSET_PICB;
108388447a05SGarrett D'Amore 		port->picboff = port->regoff + I810_OFFSET_SR;
108488447a05SGarrett D'Amore 	} else {
108588447a05SGarrett D'Amore 		port->stsoff = port->regoff + I810_OFFSET_SR;
108688447a05SGarrett D'Amore 		port->picboff = port->regoff + I810_OFFSET_PICB;
108788447a05SGarrett D'Amore 	}
108888447a05SGarrett D'Amore 
108988447a05SGarrett D'Amore 	/*
1090*68c47f65SGarrett D'Amore 	 * We use one big sample area.  The sample area must be larger
1091*68c47f65SGarrett D'Amore 	 * than about 1.5 framework fragment sizes.  (Currently 480 *
1092*68c47f65SGarrett D'Amore 	 * 1.5 = 720 frames.)  This is necessary to ensure that we
1093*68c47f65SGarrett D'Amore 	 * don't have to involve an interrupt service routine on our
1094*68c47f65SGarrett D'Amore 	 * own, to keep the last valid index updated reasonably.
109588447a05SGarrett D'Amore 	 */
1096*68c47f65SGarrett D'Amore 	port->samp_frames = 4096;
1097*68c47f65SGarrett D'Amore 	port->samp_size = port->samp_frames * port->nchan * sizeof (int16_t);
109888447a05SGarrett D'Amore 
109988447a05SGarrett D'Amore 	/* allocate dma handle */
110088447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
110188447a05SGarrett D'Amore 	    NULL, &port->samp_dmah);
110288447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
110388447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
110488447a05SGarrett D'Amore 		return (DDI_FAILURE);
110588447a05SGarrett D'Amore 	}
110688447a05SGarrett D'Amore 	/* allocate DMA buffer */
110788447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
110888447a05SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
110988447a05SGarrett D'Amore 	    &port->samp_size, &port->samp_acch);
111088447a05SGarrett D'Amore 	if (rc == DDI_FAILURE) {
111188447a05SGarrett D'Amore 		audio_dev_warn(adev, "dma_mem_alloc (%d) failed: %d",
111288447a05SGarrett D'Amore 		    port->samp_size, rc);
111388447a05SGarrett D'Amore 		return (DDI_FAILURE);
111488447a05SGarrett D'Amore 	}
111588447a05SGarrett D'Amore 
111688447a05SGarrett D'Amore 	/* bind DMA buffer */
111788447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
111888447a05SGarrett D'Amore 	    port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
111988447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
112088447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
112188447a05SGarrett D'Amore 		audio_dev_warn(adev,
112288447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed: %d", rc);
112388447a05SGarrett D'Amore 		return (DDI_FAILURE);
112488447a05SGarrett D'Amore 	}
112588447a05SGarrett D'Amore 	port->samp_paddr = cookie.dmac_address;
112688447a05SGarrett D'Amore 
112788447a05SGarrett D'Amore 	/*
112888447a05SGarrett D'Amore 	 * now, from here we allocate DMA memory for buffer descriptor list.
112988447a05SGarrett D'Amore 	 * we allocate adjacent DMA memory for all DMA engines.
113088447a05SGarrett D'Amore 	 */
113188447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
113288447a05SGarrett D'Amore 	    NULL, &port->bdl_dmah);
113388447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
113488447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
113588447a05SGarrett D'Amore 		return (DDI_FAILURE);
113688447a05SGarrett D'Amore 	}
113788447a05SGarrett D'Amore 
113888447a05SGarrett D'Amore 	/*
113988447a05SGarrett D'Amore 	 * we allocate all buffer descriptors lists in continuous dma memory.
114088447a05SGarrett D'Amore 	 */
114188447a05SGarrett D'Amore 	port->bdl_size = sizeof (i810_bd_entry_t) * I810_BD_NUMS;
114288447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
114388447a05SGarrett D'Amore 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
114488447a05SGarrett D'Amore 	    &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
114588447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
114688447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
114788447a05SGarrett D'Amore 		return (DDI_FAILURE);
114888447a05SGarrett D'Amore 	}
114988447a05SGarrett D'Amore 
115088447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
115188447a05SGarrett D'Amore 	    port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
115288447a05SGarrett D'Amore 	    NULL, &cookie, &count);
115388447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
115488447a05SGarrett D'Amore 		audio_dev_warn(adev, "addr_bind_handle failed");
115588447a05SGarrett D'Amore 		return (DDI_FAILURE);
115688447a05SGarrett D'Amore 	}
115788447a05SGarrett D'Amore 	port->bdl_paddr = cookie.dmac_address;
115888447a05SGarrett D'Amore 
115988447a05SGarrett D'Amore 	/*
116088447a05SGarrett D'Amore 	 * Wire up the BD list.
116188447a05SGarrett D'Amore 	 */
116288447a05SGarrett D'Amore 	bdentry = (void *)port->bdl_kaddr;
116388447a05SGarrett D'Amore 	for (int i = 0; i < I810_BD_NUMS; i++) {
116488447a05SGarrett D'Amore 
116588447a05SGarrett D'Amore 		/* set base address of buffer */
1166*68c47f65SGarrett D'Amore 		ddi_put32(port->bdl_acch, &bdentry->buf_base,
1167*68c47f65SGarrett D'Amore 		    port->samp_paddr);
1168*68c47f65SGarrett D'Amore 		/* SiS 7012 counts in bytes, all others in words */
116988447a05SGarrett D'Amore 		ddi_put16(port->bdl_acch, &bdentry->buf_len,
1170*68c47f65SGarrett D'Amore 		    statep->quirk == QUIRK_SIS7012 ? port->samp_size :
1171*68c47f65SGarrett D'Amore 		    port->samp_size / 2);
1172*68c47f65SGarrett D'Amore 		ddi_put16(port->bdl_acch, &bdentry->buf_cmd, BUF_CMD_BUP);
1173*68c47f65SGarrett D'Amore 
117488447a05SGarrett D'Amore 		bdentry++;
117588447a05SGarrett D'Amore 	}
117688447a05SGarrett D'Amore 	(void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
117788447a05SGarrett D'Amore 
117888447a05SGarrett D'Amore 	port->engine = audio_engine_alloc(&audio810_engine_ops, caps);
117988447a05SGarrett D'Amore 	if (port->engine == NULL) {
118088447a05SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
118188447a05SGarrett D'Amore 		return (DDI_FAILURE);
118288447a05SGarrett D'Amore 	}
118388447a05SGarrett D'Amore 
118488447a05SGarrett D'Amore 	audio_engine_set_private(port->engine, port);
118588447a05SGarrett D'Amore 	audio_dev_add_engine(adev, port->engine);
118688447a05SGarrett D'Amore 
118788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
118888447a05SGarrett D'Amore }
118988447a05SGarrett D'Amore 
119088447a05SGarrett D'Amore /*
119188447a05SGarrett D'Amore  * audio810_free_port()
119288447a05SGarrett D'Amore  *
119388447a05SGarrett D'Amore  * Description:
119488447a05SGarrett D'Amore  *	This routine unbinds the DMA cookies, frees the DMA buffers,
119588447a05SGarrett D'Amore  *	deallocates the DMA handles.
119688447a05SGarrett D'Amore  *
119788447a05SGarrett D'Amore  * Arguments:
119888447a05SGarrett D'Amore  *	audio810_port_t	*port	The port structure for a DMA engine.
119988447a05SGarrett D'Amore  */
120088447a05SGarrett D'Amore static void
audio810_free_port(audio810_port_t * port)120188447a05SGarrett D'Amore audio810_free_port(audio810_port_t *port)
120288447a05SGarrett D'Amore {
120388447a05SGarrett D'Amore 	if (port == NULL)
120488447a05SGarrett D'Amore 		return;
120588447a05SGarrett D'Amore 
120688447a05SGarrett D'Amore 	if (port->engine) {
120788447a05SGarrett D'Amore 		audio_dev_remove_engine(port->statep->adev, port->engine);
120888447a05SGarrett D'Amore 		audio_engine_free(port->engine);
120988447a05SGarrett D'Amore 	}
121088447a05SGarrett D'Amore 	if (port->bdl_paddr) {
121188447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->bdl_dmah);
121288447a05SGarrett D'Amore 	}
121388447a05SGarrett D'Amore 	if (port->bdl_acch) {
121488447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->bdl_acch);
121588447a05SGarrett D'Amore 	}
121688447a05SGarrett D'Amore 	if (port->bdl_dmah) {
121788447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->bdl_dmah);
121888447a05SGarrett D'Amore 	}
121988447a05SGarrett D'Amore 	if (port->samp_paddr) {
122088447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->samp_dmah);
122188447a05SGarrett D'Amore 	}
122288447a05SGarrett D'Amore 	if (port->samp_acch) {
122388447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->samp_acch);
122488447a05SGarrett D'Amore 	}
122588447a05SGarrett D'Amore 	if (port->samp_dmah) {
122688447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->samp_dmah);
122788447a05SGarrett D'Amore 	}
122888447a05SGarrett D'Amore 	kmem_free(port, sizeof (*port));
122988447a05SGarrett D'Amore }
123088447a05SGarrett D'Amore 
123188447a05SGarrett D'Amore /*
123288447a05SGarrett D'Amore  * audio810_map_regs()
123388447a05SGarrett D'Amore  *
123488447a05SGarrett D'Amore  * Description:
123588447a05SGarrett D'Amore  *	The registers are mapped in.
123688447a05SGarrett D'Amore  *
123788447a05SGarrett D'Amore  * Arguments:
123888447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's devinfo
123988447a05SGarrett D'Amore  *
124088447a05SGarrett D'Amore  * Returns:
124188447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
124288447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
124388447a05SGarrett D'Amore  */
124488447a05SGarrett D'Amore static int
audio810_map_regs(dev_info_t * dip,audio810_state_t * statep)124588447a05SGarrett D'Amore audio810_map_regs(dev_info_t *dip, audio810_state_t *statep)
124688447a05SGarrett D'Amore {
124788447a05SGarrett D'Amore 	uint_t			nregs = 0;
124888447a05SGarrett D'Amore 	int			*regs_list;
124988447a05SGarrett D'Amore 	int			i;
125088447a05SGarrett D'Amore 	int			pciBar1 = 0;
125188447a05SGarrett D'Amore 	int			pciBar2 = 0;
125288447a05SGarrett D'Amore 	int			pciBar3 = 0;
125388447a05SGarrett D'Amore 	int			pciBar4 = 0;
125488447a05SGarrett D'Amore 
125588447a05SGarrett D'Amore 	/* check the "reg" property to get the length of memory-mapped I/O */
125688447a05SGarrett D'Amore 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
125788447a05SGarrett D'Amore 	    "reg", (int **)&regs_list, &nregs) != DDI_PROP_SUCCESS) {
125888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "inquire regs property failed");
125988447a05SGarrett D'Amore 		goto error;
126088447a05SGarrett D'Amore 	}
126188447a05SGarrett D'Amore 	/*
126288447a05SGarrett D'Amore 	 * Some hardwares, such as Intel ICH0/ICH and AMD 8111, use PCI 0x10
126388447a05SGarrett D'Amore 	 * and 0x14 BAR separately for native audio mixer BAR and native bus
126488447a05SGarrett D'Amore 	 * mastering BAR. More advanced hardwares, such as Intel ICH4 and ICH5,
126588447a05SGarrett D'Amore 	 * support PCI memory BAR, via PCI 0x18 and 0x1C BAR, that allows for
126688447a05SGarrett D'Amore 	 * higher performance access to the controller register. All features
126788447a05SGarrett D'Amore 	 * can be accessed via this BAR making the I/O BAR (PCI 0x10 and 0x14
126888447a05SGarrett D'Amore 	 * BAR) capabilities obsolete. However, these controller maintain the
126988447a05SGarrett D'Amore 	 * I/O BAR capability to allow for the reuse of legacy code maintaining
127088447a05SGarrett D'Amore 	 * backward compatibility. The I/O BAR is disabled unless system BIOS
127188447a05SGarrett D'Amore 	 * enables the simultaneous backward compatible capability on the 0x41
127288447a05SGarrett D'Amore 	 * register.
127388447a05SGarrett D'Amore 	 *
127488447a05SGarrett D'Amore 	 * When I/O BAR is enabled, the value of "reg" property should be like
127588447a05SGarrett D'Amore 	 * this,
127688447a05SGarrett D'Amore 	 *	phys_hi   phys_mid  phys_lo   size_hi   size_lo
127788447a05SGarrett D'Amore 	 * --------------------------------------------------------
127888447a05SGarrett D'Amore 	 *	0000fd00  00000000  00000000  00000000  00000000
127988447a05SGarrett D'Amore 	 *	0100fd10  00000000  00000000  00000000  00000100
128088447a05SGarrett D'Amore 	 *	0100fd14  00000000  00000000  00000000  00000040
128188447a05SGarrett D'Amore 	 *	0200fd18  00000000  00000000  00000000  00000200
128288447a05SGarrett D'Amore 	 *	0200fd1c  00000000  00000000  00000000  00000100
128388447a05SGarrett D'Amore 	 *
128488447a05SGarrett D'Amore 	 * When I/O BAR is disabled, the "reg" property of the device node does
128588447a05SGarrett D'Amore 	 * not consist of the description for the I/O BAR. The following example
128688447a05SGarrett D'Amore 	 * illustrates the vaule of "reg" property,
128788447a05SGarrett D'Amore 	 *
128888447a05SGarrett D'Amore 	 *	phys_hi   phys_mid  phys_lo   size_hi   size_lo
128988447a05SGarrett D'Amore 	 * --------------------------------------------------------
129088447a05SGarrett D'Amore 	 *	0000fd00  00000000  00000000  00000000  00000000
129188447a05SGarrett D'Amore 	 *	0200fd18  00000000  00000000  00000000  00000200
129288447a05SGarrett D'Amore 	 *	0200fd1c  00000000  00000000  00000000  00000100
129388447a05SGarrett D'Amore 	 *
129488447a05SGarrett D'Amore 	 * If the hardware has memory-mapped I/O access, first try to use
129588447a05SGarrett D'Amore 	 * this facility, otherwise we will try I/O access.
129688447a05SGarrett D'Amore 	 */
129788447a05SGarrett D'Amore 	for (i = 1; i < nregs/I810_INTS_PER_REG_PROP; i++) {
129888447a05SGarrett D'Amore 		switch (regs_list[I810_INTS_PER_REG_PROP * i] & 0x000000ff) {
129988447a05SGarrett D'Amore 			case 0x10:
130088447a05SGarrett D'Amore 				pciBar1 = i;
130188447a05SGarrett D'Amore 				break;
130288447a05SGarrett D'Amore 			case 0x14:
130388447a05SGarrett D'Amore 				pciBar2 = i;
130488447a05SGarrett D'Amore 				break;
130588447a05SGarrett D'Amore 			case 0x18:
130688447a05SGarrett D'Amore 				pciBar3 = i;
130788447a05SGarrett D'Amore 				break;
130888447a05SGarrett D'Amore 			case 0x1c:
130988447a05SGarrett D'Amore 				pciBar4 = i;
131088447a05SGarrett D'Amore 				break;
131188447a05SGarrett D'Amore 			default:	/* we don't care others */
131288447a05SGarrett D'Amore 				break;
131388447a05SGarrett D'Amore 		}
131488447a05SGarrett D'Amore 	}
131588447a05SGarrett D'Amore 
131688447a05SGarrett D'Amore 	if ((pciBar3 != 0) && (pciBar4 != 0)) {
131788447a05SGarrett D'Amore 		/* map audio mixer registers */
131888447a05SGarrett D'Amore 		if ((ddi_regs_map_setup(dip, pciBar3, &statep->am_regs_base, 0,
131988447a05SGarrett D'Amore 		    0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
132088447a05SGarrett D'Amore 			audio_dev_warn(statep->adev,
132188447a05SGarrett D'Amore 			    "memory am mapping failed");
132288447a05SGarrett D'Amore 			goto error;
132388447a05SGarrett D'Amore 		}
132488447a05SGarrett D'Amore 
132588447a05SGarrett D'Amore 		/* map bus master register */
132688447a05SGarrett D'Amore 		if ((ddi_regs_map_setup(dip, pciBar4, &statep->bm_regs_base, 0,
132788447a05SGarrett D'Amore 		    0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
132888447a05SGarrett D'Amore 			audio_dev_warn(statep->adev,
132988447a05SGarrett D'Amore 			    "memory bm mapping failed");
133088447a05SGarrett D'Amore 			goto error;
133188447a05SGarrett D'Amore 		}
133288447a05SGarrett D'Amore 
133388447a05SGarrett D'Amore 	} else if ((pciBar1 != 0) && (pciBar2 != 0)) {
133488447a05SGarrett D'Amore 		/* map audio mixer registers */
133588447a05SGarrett D'Amore 		if ((ddi_regs_map_setup(dip, pciBar1, &statep->am_regs_base, 0,
133688447a05SGarrett D'Amore 		    0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
133788447a05SGarrett D'Amore 			audio_dev_warn(statep->adev, "I/O am mapping failed");
133888447a05SGarrett D'Amore 			goto error;
133988447a05SGarrett D'Amore 		}
134088447a05SGarrett D'Amore 
134188447a05SGarrett D'Amore 		/* map bus master register */
134288447a05SGarrett D'Amore 		if ((ddi_regs_map_setup(dip, pciBar2, &statep->bm_regs_base, 0,
134388447a05SGarrett D'Amore 		    0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
134488447a05SGarrett D'Amore 			audio_dev_warn(statep->adev, "I/O bm mapping failed");
134588447a05SGarrett D'Amore 			goto error;
134688447a05SGarrett D'Amore 		}
134788447a05SGarrett D'Amore 	} else {
134888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "map_regs() pci BAR error");
134988447a05SGarrett D'Amore 		goto error;
135088447a05SGarrett D'Amore 	}
135188447a05SGarrett D'Amore 
135288447a05SGarrett D'Amore 	ddi_prop_free(regs_list);
135388447a05SGarrett D'Amore 
135488447a05SGarrett D'Amore 	return (DDI_SUCCESS);
135588447a05SGarrett D'Amore 
135688447a05SGarrett D'Amore error:
135788447a05SGarrett D'Amore 	if (nregs > 0) {
135888447a05SGarrett D'Amore 		ddi_prop_free(regs_list);
135988447a05SGarrett D'Amore 	}
136088447a05SGarrett D'Amore 	audio810_unmap_regs(statep);
136188447a05SGarrett D'Amore 
136288447a05SGarrett D'Amore 	return (DDI_FAILURE);
136388447a05SGarrett D'Amore }
136488447a05SGarrett D'Amore 
136588447a05SGarrett D'Amore /*
136688447a05SGarrett D'Amore  * audio810_unmap_regs()
136788447a05SGarrett D'Amore  *
136888447a05SGarrett D'Amore  * Description:
136988447a05SGarrett D'Amore  *	This routine unmaps control registers.
137088447a05SGarrett D'Amore  *
137188447a05SGarrett D'Amore  * Arguments:
137288447a05SGarrett D'Amore  *	audio810_state_t	*state	The device's state structure
137388447a05SGarrett D'Amore  */
137488447a05SGarrett D'Amore static void
audio810_unmap_regs(audio810_state_t * statep)137588447a05SGarrett D'Amore audio810_unmap_regs(audio810_state_t *statep)
137688447a05SGarrett D'Amore {
137788447a05SGarrett D'Amore 	if (statep->bm_regs_handle) {
137888447a05SGarrett D'Amore 		ddi_regs_map_free(&statep->bm_regs_handle);
137988447a05SGarrett D'Amore 	}
138088447a05SGarrett D'Amore 
138188447a05SGarrett D'Amore 	if (statep->am_regs_handle) {
138288447a05SGarrett D'Amore 		ddi_regs_map_free(&statep->am_regs_handle);
138388447a05SGarrett D'Amore 	}
138488447a05SGarrett D'Amore }
138588447a05SGarrett D'Amore 
138688447a05SGarrett D'Amore /*
138788447a05SGarrett D'Amore  * audio810_chip_init()
138888447a05SGarrett D'Amore  *
138988447a05SGarrett D'Amore  * Description:
1390*68c47f65SGarrett D'Amore  *	This routine initializes the audio controller.
139188447a05SGarrett D'Amore  *
139288447a05SGarrett D'Amore  * Arguments:
139388447a05SGarrett D'Amore  *	audio810_state_t	*state		The device's state structure
139488447a05SGarrett D'Amore  *
139588447a05SGarrett D'Amore  * Returns:
139688447a05SGarrett D'Amore  *	DDI_SUCCESS	The hardware was initialized properly
139788447a05SGarrett D'Amore  *	DDI_FAILURE	The hardware couldn't be initialized properly
139888447a05SGarrett D'Amore  */
139988447a05SGarrett D'Amore static int
audio810_chip_init(audio810_state_t * statep)140088447a05SGarrett D'Amore audio810_chip_init(audio810_state_t *statep)
140188447a05SGarrett D'Amore {
140288447a05SGarrett D'Amore 	uint32_t	gcr;
140388447a05SGarrett D'Amore 	uint32_t	gsr;
140488447a05SGarrett D'Amore 	uint32_t	codec_ready;
140588447a05SGarrett D'Amore 	int 		loop;
140688447a05SGarrett D'Amore 	clock_t		ticks;
140788447a05SGarrett D'Amore 
140888447a05SGarrett D'Amore 	gcr = I810_BM_GET32(I810_REG_GCR);
140988447a05SGarrett D'Amore 	ticks = drv_usectohz(100);
141088447a05SGarrett D'Amore 
141188447a05SGarrett D'Amore 	/*
141288447a05SGarrett D'Amore 	 * Clear the channels bits for now.  We'll set them later in
141388447a05SGarrett D'Amore 	 * reset port.
141488447a05SGarrett D'Amore 	 */
141588447a05SGarrett D'Amore 	if (statep->quirk == QUIRK_SIS7012) {
141688447a05SGarrett D'Amore 		gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_SIS_CHANNELS_MASK);
141788447a05SGarrett D'Amore 	} else {
141888447a05SGarrett D'Amore 		gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_CHANNELS_MASK);
141988447a05SGarrett D'Amore 	}
142088447a05SGarrett D'Amore 
142188447a05SGarrett D'Amore 	/*
142288447a05SGarrett D'Amore 	 * Datasheet(ICH5, document number of Intel: 252751-001):
142388447a05SGarrett D'Amore 	 * 3.6.5.5(page 37)
142488447a05SGarrett D'Amore 	 * 	if reset bit(bit1) is "0", driver must set it
142588447a05SGarrett D'Amore 	 * 	to "1" to de-assert the AC_RESET# signal in AC
142688447a05SGarrett D'Amore 	 * 	link, thus completing a cold reset. But if the
142788447a05SGarrett D'Amore 	 * 	bit is "1", then a warm reset is required.
142888447a05SGarrett D'Amore 	 */
142988447a05SGarrett D'Amore 	gcr |= (gcr & I810_GCR_COLD_RST) == 0 ?
143088447a05SGarrett D'Amore 	    I810_GCR_COLD_RST:I810_GCR_WARM_RST;
143188447a05SGarrett D'Amore 	I810_BM_PUT32(I810_REG_GCR, gcr);
143288447a05SGarrett D'Amore 
143388447a05SGarrett D'Amore 	/* according AC'97 spec, wait for codec reset */
143488447a05SGarrett D'Amore 	for (loop = 6000; --loop >= 0; ) {
143588447a05SGarrett D'Amore 		delay(ticks);
143688447a05SGarrett D'Amore 		gcr = I810_BM_GET32(I810_REG_GCR);
143788447a05SGarrett D'Amore 		if ((gcr & I810_GCR_WARM_RST) == 0) {
143888447a05SGarrett D'Amore 			break;
143988447a05SGarrett D'Amore 		}
144088447a05SGarrett D'Amore 	}
144188447a05SGarrett D'Amore 
144288447a05SGarrett D'Amore 	/* codec reset failed */
144388447a05SGarrett D'Amore 	if (loop < 0) {
144488447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "Failed to reset codec");
144588447a05SGarrett D'Amore 		return (DDI_FAILURE);
144688447a05SGarrett D'Amore 	}
144788447a05SGarrett D'Amore 
144888447a05SGarrett D'Amore 	/*
144988447a05SGarrett D'Amore 	 * Wait for codec ready. The hardware can provide the state of
145088447a05SGarrett D'Amore 	 * codec ready bit on SDATA_IN[0], SDATA_IN[1] or SDATA_IN[2]
145188447a05SGarrett D'Amore 	 */
145288447a05SGarrett D'Amore 	codec_ready =
145388447a05SGarrett D'Amore 	    I810_GSR_PRI_READY | I810_GSR_SEC_READY | I810_GSR_TRI_READY;
145488447a05SGarrett D'Amore 	for (loop = 7000; --loop >= 0; ) {
145588447a05SGarrett D'Amore 		delay(ticks);
145688447a05SGarrett D'Amore 		gsr = I810_BM_GET32(I810_REG_GSR);
145788447a05SGarrett D'Amore 		if ((gsr & codec_ready) != 0) {
145888447a05SGarrett D'Amore 			break;
145988447a05SGarrett D'Amore 		}
146088447a05SGarrett D'Amore 	}
146188447a05SGarrett D'Amore 	if (loop < 0) {
146288447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "No codec ready signal received");
146388447a05SGarrett D'Amore 		return (DDI_FAILURE);
146488447a05SGarrett D'Amore 	}
146588447a05SGarrett D'Amore 
146688447a05SGarrett D'Amore 	/*
146788447a05SGarrett D'Amore 	 * put the audio controller into quiet state, everything off
146888447a05SGarrett D'Amore 	 */
146988447a05SGarrett D'Amore 	audio810_stop_dma(statep);
147088447a05SGarrett D'Amore 
147188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
147288447a05SGarrett D'Amore }
147388447a05SGarrett D'Amore 
1474*68c47f65SGarrett D'Amore /*
1475*68c47f65SGarrett D'Amore  * audio810_set_channels()
1476*68c47f65SGarrett D'Amore  *
1477*68c47f65SGarrett D'Amore  * Description:
1478*68c47f65SGarrett D'Amore  *	This routine initializes the multichannel configuration.
1479*68c47f65SGarrett D'Amore  *
1480*68c47f65SGarrett D'Amore  * Arguments:
1481*68c47f65SGarrett D'Amore  *	audio810_state_t	*state		The device's state structure
1482*68c47f65SGarrett D'Amore  */
1483*68c47f65SGarrett D'Amore static void
audio810_set_channels(audio810_state_t * statep)1484*68c47f65SGarrett D'Amore audio810_set_channels(audio810_state_t *statep)
1485*68c47f65SGarrett D'Amore {
1486*68c47f65SGarrett D'Amore 	uint32_t	gcr;
1487*68c47f65SGarrett D'Amore 
1488*68c47f65SGarrett D'Amore 	/*
1489*68c47f65SGarrett D'Amore 	 * Configure multi-channel.
1490*68c47f65SGarrett D'Amore 	 */
1491*68c47f65SGarrett D'Amore 	if (statep->quirk == QUIRK_SIS7012) {
1492*68c47f65SGarrett D'Amore 		/*
1493*68c47f65SGarrett D'Amore 		 * SiS 7012 needs its own special multichannel config.
1494*68c47f65SGarrett D'Amore 		 */
1495*68c47f65SGarrett D'Amore 		gcr = I810_BM_GET32(I810_REG_GCR);
1496*68c47f65SGarrett D'Amore 		gcr &= ~I810_GCR_SIS_CHANNELS_MASK;
1497*68c47f65SGarrett D'Amore 		I810_BM_PUT32(I810_REG_GCR, gcr);
1498*68c47f65SGarrett D'Amore 		delay(drv_usectohz(50000));	/* 50 msec */
1499*68c47f65SGarrett D'Amore 
1500*68c47f65SGarrett D'Amore 		switch (statep->maxch) {
1501*68c47f65SGarrett D'Amore 		case 2:
1502*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_SIS_2_CHANNELS;
1503*68c47f65SGarrett D'Amore 			break;
1504*68c47f65SGarrett D'Amore 		case 4:
1505*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_SIS_4_CHANNELS;
1506*68c47f65SGarrett D'Amore 			break;
1507*68c47f65SGarrett D'Amore 		case 6:
1508*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_SIS_6_CHANNELS;
1509*68c47f65SGarrett D'Amore 			break;
1510*68c47f65SGarrett D'Amore 		}
1511*68c47f65SGarrett D'Amore 		I810_BM_PUT32(I810_REG_GCR, gcr);
1512*68c47f65SGarrett D'Amore 		delay(drv_usectohz(50000));	/* 50 msec */
1513*68c47f65SGarrett D'Amore 	} else {
1514*68c47f65SGarrett D'Amore 
1515*68c47f65SGarrett D'Amore 		/*
1516*68c47f65SGarrett D'Amore 		 * All other devices work the same.
1517*68c47f65SGarrett D'Amore 		 */
1518*68c47f65SGarrett D'Amore 		gcr = I810_BM_GET32(I810_REG_GCR);
1519*68c47f65SGarrett D'Amore 		gcr &= ~I810_GCR_CHANNELS_MASK;
1520*68c47f65SGarrett D'Amore 
1521*68c47f65SGarrett D'Amore 		I810_BM_PUT32(I810_REG_GCR, gcr);
1522*68c47f65SGarrett D'Amore 		delay(drv_usectohz(50000));	/* 50 msec */
1523*68c47f65SGarrett D'Amore 
1524*68c47f65SGarrett D'Amore 		switch (statep->maxch) {
1525*68c47f65SGarrett D'Amore 		case 2:
1526*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_2_CHANNELS;
1527*68c47f65SGarrett D'Amore 			break;
1528*68c47f65SGarrett D'Amore 		case 4:
1529*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_4_CHANNELS;
1530*68c47f65SGarrett D'Amore 			break;
1531*68c47f65SGarrett D'Amore 		case 6:
1532*68c47f65SGarrett D'Amore 			gcr |= I810_GCR_6_CHANNELS;
1533*68c47f65SGarrett D'Amore 			break;
1534*68c47f65SGarrett D'Amore 		}
1535*68c47f65SGarrett D'Amore 		I810_BM_PUT32(I810_REG_GCR, gcr);
1536*68c47f65SGarrett D'Amore 		delay(drv_usectohz(50000));	/* 50 msec */
1537*68c47f65SGarrett D'Amore 	}
1538*68c47f65SGarrett D'Amore }
1539*68c47f65SGarrett D'Amore 
154088447a05SGarrett D'Amore /*
154188447a05SGarrett D'Amore  * audio810_stop_dma()
154288447a05SGarrett D'Amore  *
154388447a05SGarrett D'Amore  * Description:
154488447a05SGarrett D'Amore  *	This routine is used to put each DMA engine into the quiet state.
154588447a05SGarrett D'Amore  *
154688447a05SGarrett D'Amore  * Arguments:
154788447a05SGarrett D'Amore  *	audio810_state_t	*state		The device's state structure
154888447a05SGarrett D'Amore  */
154988447a05SGarrett D'Amore static void
audio810_stop_dma(audio810_state_t * statep)155088447a05SGarrett D'Amore audio810_stop_dma(audio810_state_t *statep)
155188447a05SGarrett D'Amore {
155288447a05SGarrett D'Amore 	if (statep->bm_regs_handle == NULL) {
155388447a05SGarrett D'Amore 		return;
155488447a05SGarrett D'Amore 	}
155588447a05SGarrett D'Amore 	/* pause bus master (needed for the following reset register) */
155688447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, 0x0);
155788447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, 0x0);
155888447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, 0x0);
155988447a05SGarrett D'Amore 
156088447a05SGarrett D'Amore 	/* and then reset the bus master registers for a three DMA engines */
156188447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, I810_BM_CR_RST);
156288447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, I810_BM_CR_RST);
156388447a05SGarrett D'Amore 	I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, I810_BM_CR_RST);
156488447a05SGarrett D'Amore }
156588447a05SGarrett D'Amore 
156688447a05SGarrett D'Amore 
156788447a05SGarrett D'Amore /*
156888447a05SGarrett D'Amore  * audio810_codec_sync()
156988447a05SGarrett D'Amore  *
157088447a05SGarrett D'Amore  * Description:
157188447a05SGarrett D'Amore  *	Serialize access to the AC97 audio mixer registers.
157288447a05SGarrett D'Amore  *
157388447a05SGarrett D'Amore  * Arguments:
157488447a05SGarrett D'Amore  *	audio810_state_t	*state		The device's state structure
157588447a05SGarrett D'Amore  *
157688447a05SGarrett D'Amore  * Returns:
157788447a05SGarrett D'Amore  *	DDI_SUCCESS		Ready for an I/O access to the codec
157888447a05SGarrett D'Amore  *	DDI_FAILURE		An I/O access is currently in progress, can't
157988447a05SGarrett D'Amore  *				perform another I/O access.
158088447a05SGarrett D'Amore  */
158188447a05SGarrett D'Amore static int
audio810_codec_sync(audio810_state_t * statep)158288447a05SGarrett D'Amore audio810_codec_sync(audio810_state_t *statep)
158388447a05SGarrett D'Amore {
158488447a05SGarrett D'Amore 	int 		i;
158588447a05SGarrett D'Amore 	uint16_t	casr;
158688447a05SGarrett D'Amore 
158788447a05SGarrett D'Amore 	for (i = 0; i < 300; i++) {
158888447a05SGarrett D'Amore 		casr = I810_BM_GET8(I810_REG_CASR);
158988447a05SGarrett D'Amore 		if ((casr & 1) == 0) {
159088447a05SGarrett D'Amore 			return (DDI_SUCCESS);
159188447a05SGarrett D'Amore 		}
159288447a05SGarrett D'Amore 		drv_usecwait(10);
159388447a05SGarrett D'Amore 	}
159488447a05SGarrett D'Amore 
159588447a05SGarrett D'Amore 	return (DDI_FAILURE);
159688447a05SGarrett D'Amore }
159788447a05SGarrett D'Amore 
159888447a05SGarrett D'Amore /*
159988447a05SGarrett D'Amore  * audio810_write_ac97()
160088447a05SGarrett D'Amore  *
160188447a05SGarrett D'Amore  * Description:
160288447a05SGarrett D'Amore  *	Set the specific AC97 Codec register.
160388447a05SGarrett D'Amore  *
160488447a05SGarrett D'Amore  * Arguments:
160588447a05SGarrett D'Amore  *	void		*arg		The device's state structure
160688447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
160788447a05SGarrett D'Amore  *	uint16_t	data		The data want to be set
160888447a05SGarrett D'Amore  */
160988447a05SGarrett D'Amore static void
audio810_write_ac97(void * arg,uint8_t reg,uint16_t data)161088447a05SGarrett D'Amore audio810_write_ac97(void *arg, uint8_t reg, uint16_t data)
161188447a05SGarrett D'Amore {
161288447a05SGarrett D'Amore 	audio810_state_t	*statep = arg;
161388447a05SGarrett D'Amore 
161488447a05SGarrett D'Amore 	if (audio810_codec_sync(statep) == DDI_SUCCESS) {
161588447a05SGarrett D'Amore 		I810_AM_PUT16(reg, data);
161688447a05SGarrett D'Amore 	}
161788447a05SGarrett D'Amore 
161888447a05SGarrett D'Amore 	(void) audio810_read_ac97(statep, reg);
161988447a05SGarrett D'Amore }
162088447a05SGarrett D'Amore 
162188447a05SGarrett D'Amore /*
162288447a05SGarrett D'Amore  * audio810_read_ac97()
162388447a05SGarrett D'Amore  *
162488447a05SGarrett D'Amore  * Description:
162588447a05SGarrett D'Amore  *	Get the specific AC97 Codec register.
162688447a05SGarrett D'Amore  *
162788447a05SGarrett D'Amore  * Arguments:
162888447a05SGarrett D'Amore  *	void		*arg		The device's state structure
162988447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
163088447a05SGarrett D'Amore  *
163188447a05SGarrett D'Amore  * Returns:
163288447a05SGarrett D'Amore  *	The register value.
163388447a05SGarrett D'Amore  */
163488447a05SGarrett D'Amore static uint16_t
audio810_read_ac97(void * arg,uint8_t reg)163588447a05SGarrett D'Amore audio810_read_ac97(void *arg, uint8_t reg)
163688447a05SGarrett D'Amore {
163788447a05SGarrett D'Amore 	audio810_state_t	*statep = arg;
163888447a05SGarrett D'Amore 	uint16_t		val = 0xffff;
163988447a05SGarrett D'Amore 
164088447a05SGarrett D'Amore 	if (audio810_codec_sync(statep) == DDI_SUCCESS) {
164188447a05SGarrett D'Amore 		val = I810_AM_GET16(reg);
164288447a05SGarrett D'Amore 	}
164388447a05SGarrett D'Amore 	return (val);
164488447a05SGarrett D'Amore }
164588447a05SGarrett D'Amore 
164688447a05SGarrett D'Amore /*
164788447a05SGarrett D'Amore  * audio810_destroy()
164888447a05SGarrett D'Amore  *
164988447a05SGarrett D'Amore  * Description:
165088447a05SGarrett D'Amore  *	This routine releases all resources held by the device instance,
165188447a05SGarrett D'Amore  *	as part of either detach or a failure in attach.
165288447a05SGarrett D'Amore  *
165388447a05SGarrett D'Amore  * Arguments:
165488447a05SGarrett D'Amore  *	audio810_state_t	*state	The device soft state.
165588447a05SGarrett D'Amore  */
165688447a05SGarrett D'Amore void
audio810_destroy(audio810_state_t * statep)165788447a05SGarrett D'Amore audio810_destroy(audio810_state_t *statep)
165888447a05SGarrett D'Amore {
165988447a05SGarrett D'Amore 	/* stop DMA engines */
166088447a05SGarrett D'Amore 	audio810_stop_dma(statep);
166188447a05SGarrett D'Amore 
166288447a05SGarrett D'Amore 	for (int i = 0; i < I810_NUM_PORTS; i++) {
166388447a05SGarrett D'Amore 		audio810_free_port(statep->ports[i]);
166488447a05SGarrett D'Amore 	}
166588447a05SGarrett D'Amore 
166688447a05SGarrett D'Amore 	audio810_unmap_regs(statep);
166788447a05SGarrett D'Amore 
166888447a05SGarrett D'Amore 	if (statep->ac97)
166988447a05SGarrett D'Amore 		ac97_free(statep->ac97);
167088447a05SGarrett D'Amore 
167188447a05SGarrett D'Amore 	if (statep->adev)
167288447a05SGarrett D'Amore 		audio_dev_free(statep->adev);
167388447a05SGarrett D'Amore 
167488447a05SGarrett D'Amore 	kmem_free(statep, sizeof (*statep));
167588447a05SGarrett D'Amore }
1676