xref: /onnv-gate/usr/src/uts/common/io/audio/drv/audio810/audio810.c (revision 11936:54dc8a89ba0d)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
22*11936Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
239484Sgarrett.damore@Sun.COM  * Use is subject to license terms.
249484Sgarrett.damore@Sun.COM  */
259484Sgarrett.damore@Sun.COM 
269484Sgarrett.damore@Sun.COM 
279484Sgarrett.damore@Sun.COM /*
289484Sgarrett.damore@Sun.COM  * audio810 Audio Driver
299484Sgarrett.damore@Sun.COM  *
309484Sgarrett.damore@Sun.COM  * The driver is primarily targeted at providing audio support for the
31*11936Sgdamore@opensolaris.org  * Intel ICHx family of AC'97 controllers and compatible parts (such
32*11936Sgdamore@opensolaris.org  * as those from nVidia and AMD.)
339484Sgarrett.damore@Sun.COM  *
34*11936Sgdamore@opensolaris.org  * These audio parts have independent channels for PCM in, PCM out,
35*11936Sgdamore@opensolaris.org  * mic in, and sometimes modem in, and modem out.  The AC'97
36*11936Sgdamore@opensolaris.org  * controller is a PCI bus master with scatter/gather support. Each
37*11936Sgdamore@opensolaris.org  * channel has a DMA engine. Currently, we use only the PCM in and PCM
38*11936Sgdamore@opensolaris.org  * out channels. Each DMA engine uses one buffer descriptor list. And
39*11936Sgdamore@opensolaris.org  * the buffer descriptor list is an array of up to 32 entries, each of
40*11936Sgdamore@opensolaris.org  * which describes a data buffer.  Each entry contains a pointer to a
41*11936Sgdamore@opensolaris.org  * data buffer, control bits, and the length of the buffer being
42*11936Sgdamore@opensolaris.org  * pointed to, where the length is expressed as the number of
43*11936Sgdamore@opensolaris.org  * samples. This, combined with the 16-bit sample size, gives the
44*11936Sgdamore@opensolaris.org  * actual physical length of the buffer.
459484Sgarrett.damore@Sun.COM  *
469484Sgarrett.damore@Sun.COM  * A workaround for the AD1980 and AD1985 codec:
479484Sgarrett.damore@Sun.COM  *	Most vendors connect the surr-out of the codecs to the line-out jack.
489484Sgarrett.damore@Sun.COM  *	So far we haven't found which vendors don't do that. So we assume that
499484Sgarrett.damore@Sun.COM  *	all vendors swap the surr-out and the line-out outputs. So we need swap
509484Sgarrett.damore@Sun.COM  *	the two outputs. But we still internally process the
519484Sgarrett.damore@Sun.COM  *	"ad198x-swap-output" property. If someday some vendors do not swap the
529484Sgarrett.damore@Sun.COM  *	outputs, we would set "ad198x-swap-output = 0" in the
539484Sgarrett.damore@Sun.COM  *	/kernel/drv/audio810.conf file, and unload and reload the audio810
549484Sgarrett.damore@Sun.COM  *	driver (or reboot).
559484Sgarrett.damore@Sun.COM  *
569484Sgarrett.damore@Sun.COM  * 	NOTE:
579484Sgarrett.damore@Sun.COM  * 	This driver depends on the drv/audio and misc/ac97
589484Sgarrett.damore@Sun.COM  * 	modules being loaded first.
59*11936Sgdamore@opensolaris.org  *
60*11936Sgdamore@opensolaris.org  * The audio framework guarantees that our entry points are exclusive
61*11936Sgdamore@opensolaris.org  * with suspend and resume.  This includes data flow and control entry
62*11936Sgdamore@opensolaris.org  * points alike.
63*11936Sgdamore@opensolaris.org  *
64*11936Sgdamore@opensolaris.org  * The audio framework guarantees that only one control is being
65*11936Sgdamore@opensolaris.org  * accessed on any given audio device at a time.
66*11936Sgdamore@opensolaris.org  *
67*11936Sgdamore@opensolaris.org  * The audio framework guarantees that entry points are themselves
68*11936Sgdamore@opensolaris.org  * serialized for a given engine.
69*11936Sgdamore@opensolaris.org  *
70*11936Sgdamore@opensolaris.org  * We have no interrupt routine or other internal asynchronous routines.
71*11936Sgdamore@opensolaris.org  *
72*11936Sgdamore@opensolaris.org  * Our device uses completely separate registers for each engine,
73*11936Sgdamore@opensolaris.org  * except for the start/stop registers, which are implemented in a
74*11936Sgdamore@opensolaris.org  * manner that allows for them to be accessed concurrently safely from
75*11936Sgdamore@opensolaris.org  * different threads.
76*11936Sgdamore@opensolaris.org  *
77*11936Sgdamore@opensolaris.org  * Hence, it turns out that we simply don't need any locking in this
78*11936Sgdamore@opensolaris.org  * driver.
799484Sgarrett.damore@Sun.COM  */
809484Sgarrett.damore@Sun.COM #include <sys/types.h>
819484Sgarrett.damore@Sun.COM #include <sys/modctl.h>
829484Sgarrett.damore@Sun.COM #include <sys/kmem.h>
839484Sgarrett.damore@Sun.COM #include <sys/conf.h>
849484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
859484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
869484Sgarrett.damore@Sun.COM #include <sys/pci.h>
879484Sgarrett.damore@Sun.COM #include <sys/note.h>
889484Sgarrett.damore@Sun.COM #include <sys/audio/audio_driver.h>
899484Sgarrett.damore@Sun.COM #include <sys/audio/ac97.h>
909484Sgarrett.damore@Sun.COM #include "audio810.h"
919484Sgarrett.damore@Sun.COM 
929484Sgarrett.damore@Sun.COM /*
939484Sgarrett.damore@Sun.COM  * Module linkage routines for the kernel
949484Sgarrett.damore@Sun.COM  */
959484Sgarrett.damore@Sun.COM static int audio810_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
969484Sgarrett.damore@Sun.COM static int audio810_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
979484Sgarrett.damore@Sun.COM static int audio810_ddi_quiesce(dev_info_t *);
989484Sgarrett.damore@Sun.COM 
999484Sgarrett.damore@Sun.COM /*
1009484Sgarrett.damore@Sun.COM  * Entry point routine prototypes
1019484Sgarrett.damore@Sun.COM  */
102*11936Sgdamore@opensolaris.org static int audio810_open(void *, int, unsigned *, caddr_t *);
1039484Sgarrett.damore@Sun.COM static void audio810_close(void *);
1049484Sgarrett.damore@Sun.COM static int audio810_start(void *);
1059484Sgarrett.damore@Sun.COM static void audio810_stop(void *);
1069484Sgarrett.damore@Sun.COM static int audio810_format(void *);
1079484Sgarrett.damore@Sun.COM static int audio810_channels(void *);
1089484Sgarrett.damore@Sun.COM static int audio810_rate(void *);
1099484Sgarrett.damore@Sun.COM static uint64_t audio810_count(void *);
1109484Sgarrett.damore@Sun.COM static void audio810_sync(void *, unsigned);
11111213Sgdamore@opensolaris.org static unsigned audio810_playahead(void *);
1129484Sgarrett.damore@Sun.COM 
1139484Sgarrett.damore@Sun.COM static audio_engine_ops_t audio810_engine_ops = {
1149484Sgarrett.damore@Sun.COM 	AUDIO_ENGINE_VERSION,
1159484Sgarrett.damore@Sun.COM 	audio810_open,
1169484Sgarrett.damore@Sun.COM 	audio810_close,
1179484Sgarrett.damore@Sun.COM 	audio810_start,
1189484Sgarrett.damore@Sun.COM 	audio810_stop,
1199484Sgarrett.damore@Sun.COM 	audio810_count,
1209484Sgarrett.damore@Sun.COM 	audio810_format,
1219484Sgarrett.damore@Sun.COM 	audio810_channels,
1229484Sgarrett.damore@Sun.COM 	audio810_rate,
1239484Sgarrett.damore@Sun.COM 	audio810_sync,
12411213Sgdamore@opensolaris.org 	NULL,
12511213Sgdamore@opensolaris.org 	NULL,
12611213Sgdamore@opensolaris.org 	audio810_playahead
1279484Sgarrett.damore@Sun.COM };
1289484Sgarrett.damore@Sun.COM 
1299484Sgarrett.damore@Sun.COM /*
1309484Sgarrett.damore@Sun.COM  * Local Routine Prototypes
1319484Sgarrett.damore@Sun.COM  */
1329484Sgarrett.damore@Sun.COM static int audio810_attach(dev_info_t *);
1339484Sgarrett.damore@Sun.COM static int audio810_resume(dev_info_t *);
1349484Sgarrett.damore@Sun.COM static int audio810_detach(dev_info_t *);
1359484Sgarrett.damore@Sun.COM static int audio810_suspend(dev_info_t *);
1369484Sgarrett.damore@Sun.COM 
1379484Sgarrett.damore@Sun.COM static int audio810_alloc_port(audio810_state_t *, int, uint8_t);
1389484Sgarrett.damore@Sun.COM static int audio810_codec_sync(audio810_state_t *);
1399484Sgarrett.damore@Sun.COM static void audio810_write_ac97(void *, uint8_t, uint16_t);
1409484Sgarrett.damore@Sun.COM static uint16_t audio810_read_ac97(void *, uint8_t);
1419484Sgarrett.damore@Sun.COM static int audio810_map_regs(dev_info_t *, audio810_state_t *);
1429484Sgarrett.damore@Sun.COM static void audio810_unmap_regs(audio810_state_t *);
1439484Sgarrett.damore@Sun.COM static void audio810_stop_dma(audio810_state_t *);
1449484Sgarrett.damore@Sun.COM static int audio810_chip_init(audio810_state_t *);
145*11936Sgdamore@opensolaris.org static void audio810_set_channels(audio810_state_t *);
1469484Sgarrett.damore@Sun.COM static void audio810_destroy(audio810_state_t *);
1479484Sgarrett.damore@Sun.COM 
1489484Sgarrett.damore@Sun.COM /*
1499484Sgarrett.damore@Sun.COM  * Global variables, but used only by this file.
1509484Sgarrett.damore@Sun.COM  */
1519484Sgarrett.damore@Sun.COM 
1529484Sgarrett.damore@Sun.COM /*
1539484Sgarrett.damore@Sun.COM  * DDI Structures
1549484Sgarrett.damore@Sun.COM  */
1559484Sgarrett.damore@Sun.COM 
1569484Sgarrett.damore@Sun.COM /* Device operations structure */
1579484Sgarrett.damore@Sun.COM static struct dev_ops audio810_dev_ops = {
1589484Sgarrett.damore@Sun.COM 	DEVO_REV,		/* devo_rev */
1599484Sgarrett.damore@Sun.COM 	0,			/* devo_refcnt */
1609484Sgarrett.damore@Sun.COM 	NULL,			/* devo_getinfo */
1619484Sgarrett.damore@Sun.COM 	nulldev,		/* devo_identify - obsolete */
1629484Sgarrett.damore@Sun.COM 	nulldev,		/* devo_probe */
1639484Sgarrett.damore@Sun.COM 	audio810_ddi_attach,	/* devo_attach */
1649484Sgarrett.damore@Sun.COM 	audio810_ddi_detach,	/* devo_detach */
1659484Sgarrett.damore@Sun.COM 	nodev,			/* devo_reset */
1669484Sgarrett.damore@Sun.COM 	NULL,			/* devi_cb_ops */
1679484Sgarrett.damore@Sun.COM 	NULL,			/* devo_bus_ops */
1689484Sgarrett.damore@Sun.COM 	NULL,			/* devo_power */
1699484Sgarrett.damore@Sun.COM 	audio810_ddi_quiesce,	/* devo_quiesce */
1709484Sgarrett.damore@Sun.COM };
1719484Sgarrett.damore@Sun.COM 
1729484Sgarrett.damore@Sun.COM /* Linkage structure for loadable drivers */
1739484Sgarrett.damore@Sun.COM static struct modldrv audio810_modldrv = {
1749484Sgarrett.damore@Sun.COM 	&mod_driverops,		/* drv_modops */
1759484Sgarrett.damore@Sun.COM 	I810_MOD_NAME,		/* drv_linkinfo */
1769484Sgarrett.damore@Sun.COM 	&audio810_dev_ops,	/* drv_dev_ops */
1779484Sgarrett.damore@Sun.COM };
1789484Sgarrett.damore@Sun.COM 
1799484Sgarrett.damore@Sun.COM /* Module linkage structure */
1809484Sgarrett.damore@Sun.COM static struct modlinkage audio810_modlinkage = {
1819484Sgarrett.damore@Sun.COM 	MODREV_1,			/* ml_rev */
1829484Sgarrett.damore@Sun.COM 	(void *)&audio810_modldrv,	/* ml_linkage */
1839484Sgarrett.damore@Sun.COM 	NULL				/* NULL terminates the list */
1849484Sgarrett.damore@Sun.COM };
1859484Sgarrett.damore@Sun.COM 
1869484Sgarrett.damore@Sun.COM /*
1879484Sgarrett.damore@Sun.COM  * device access attributes for register mapping
1889484Sgarrett.damore@Sun.COM  */
1899484Sgarrett.damore@Sun.COM static struct ddi_device_acc_attr dev_attr = {
1909484Sgarrett.damore@Sun.COM 	DDI_DEVICE_ATTR_V0,
1919484Sgarrett.damore@Sun.COM 	DDI_STRUCTURE_LE_ACC,
1929484Sgarrett.damore@Sun.COM 	DDI_STRICTORDER_ACC
1939484Sgarrett.damore@Sun.COM };
1949484Sgarrett.damore@Sun.COM 
1959484Sgarrett.damore@Sun.COM static struct ddi_device_acc_attr buf_attr = {
1969484Sgarrett.damore@Sun.COM 	DDI_DEVICE_ATTR_V0,
1979484Sgarrett.damore@Sun.COM 	DDI_STRUCTURE_LE_ACC,
1989484Sgarrett.damore@Sun.COM 	DDI_STRICTORDER_ACC
1999484Sgarrett.damore@Sun.COM };
2009484Sgarrett.damore@Sun.COM 
2019484Sgarrett.damore@Sun.COM /*
2029484Sgarrett.damore@Sun.COM  * DMA attributes of buffer descriptor list
2039484Sgarrett.damore@Sun.COM  */
2049484Sgarrett.damore@Sun.COM static ddi_dma_attr_t	bdlist_dma_attr = {
2059484Sgarrett.damore@Sun.COM 	DMA_ATTR_V0,	/* version */
2069484Sgarrett.damore@Sun.COM 	0,		/* addr_lo */
2079484Sgarrett.damore@Sun.COM 	0xffffffff,	/* addr_hi */
2089484Sgarrett.damore@Sun.COM 	0x0000ffff,	/* count_max */
2099484Sgarrett.damore@Sun.COM 	8,		/* align, BDL must be aligned on a 8-byte boundary */
2109484Sgarrett.damore@Sun.COM 	0x3c,		/* burstsize */
2119484Sgarrett.damore@Sun.COM 	8,		/* minxfer, set to the size of a BDlist entry */
2129484Sgarrett.damore@Sun.COM 	0x0000ffff,	/* maxxfer */
2139484Sgarrett.damore@Sun.COM 	0x00000fff,	/* seg, set to the RAM pagesize of intel platform */
2149484Sgarrett.damore@Sun.COM 	1,		/* sgllen, there's no scatter-gather list */
2159484Sgarrett.damore@Sun.COM 	8,		/* granular, set to the value of minxfer */
2169484Sgarrett.damore@Sun.COM 	0		/* flags, use virtual address */
2179484Sgarrett.damore@Sun.COM };
2189484Sgarrett.damore@Sun.COM 
2199484Sgarrett.damore@Sun.COM /*
2209484Sgarrett.damore@Sun.COM  * DMA attributes of buffers to be used to receive/send audio data
2219484Sgarrett.damore@Sun.COM  */
2229484Sgarrett.damore@Sun.COM static ddi_dma_attr_t	sample_buf_dma_attr = {
2239484Sgarrett.damore@Sun.COM 	DMA_ATTR_V0,
2249484Sgarrett.damore@Sun.COM 	0,		/* addr_lo */
2259484Sgarrett.damore@Sun.COM 	0xffffffff,	/* addr_hi */
2269484Sgarrett.damore@Sun.COM 	0x0001ffff,	/* count_max */
2279484Sgarrett.damore@Sun.COM 	4,		/* align, data buffer is aligned on a 4-byte boundary */
2289484Sgarrett.damore@Sun.COM 	0x3c,		/* burstsize */
2299484Sgarrett.damore@Sun.COM 	4,		/* minxfer, set to the size of a sample data */
2309484Sgarrett.damore@Sun.COM 	0x0001ffff,	/* maxxfer */
2319484Sgarrett.damore@Sun.COM 	0x0001ffff,	/* seg */
2329484Sgarrett.damore@Sun.COM 	1,		/* sgllen, no scatter-gather */
2339484Sgarrett.damore@Sun.COM 	4,		/* granular, set to the value of minxfer */
2349484Sgarrett.damore@Sun.COM 	0,		/* flags, use virtual address */
2359484Sgarrett.damore@Sun.COM };
2369484Sgarrett.damore@Sun.COM 
2379484Sgarrett.damore@Sun.COM /*
2389484Sgarrett.damore@Sun.COM  * _init()
2399484Sgarrett.damore@Sun.COM  *
2409484Sgarrett.damore@Sun.COM  * Description:
2419484Sgarrett.damore@Sun.COM  *	Driver initialization, called when driver is first loaded.
2429484Sgarrett.damore@Sun.COM  *	This is how access is initially given to all the static structures.
2439484Sgarrett.damore@Sun.COM  *
2449484Sgarrett.damore@Sun.COM  * Arguments:
2459484Sgarrett.damore@Sun.COM  *	None
2469484Sgarrett.damore@Sun.COM  *
2479484Sgarrett.damore@Sun.COM  * Returns:
2489484Sgarrett.damore@Sun.COM  *	mod_install() status, see mod_install(9f)
2499484Sgarrett.damore@Sun.COM  */
2509484Sgarrett.damore@Sun.COM int
_init(void)2519484Sgarrett.damore@Sun.COM _init(void)
2529484Sgarrett.damore@Sun.COM {
2539484Sgarrett.damore@Sun.COM 	int	error;
2549484Sgarrett.damore@Sun.COM 
2559484Sgarrett.damore@Sun.COM 	audio_init_ops(&audio810_dev_ops, I810_NAME);
2569484Sgarrett.damore@Sun.COM 
2579484Sgarrett.damore@Sun.COM 	if ((error = mod_install(&audio810_modlinkage)) != 0) {
2589484Sgarrett.damore@Sun.COM 		audio_fini_ops(&audio810_dev_ops);
2599484Sgarrett.damore@Sun.COM 	}
2609484Sgarrett.damore@Sun.COM 
2619484Sgarrett.damore@Sun.COM 	return (error);
2629484Sgarrett.damore@Sun.COM }
2639484Sgarrett.damore@Sun.COM 
2649484Sgarrett.damore@Sun.COM /*
2659484Sgarrett.damore@Sun.COM  * _fini()
2669484Sgarrett.damore@Sun.COM  *
2679484Sgarrett.damore@Sun.COM  * Description:
2689484Sgarrett.damore@Sun.COM  *	Module de-initialization, called when the driver is to be unloaded.
2699484Sgarrett.damore@Sun.COM  *
2709484Sgarrett.damore@Sun.COM  * Arguments:
2719484Sgarrett.damore@Sun.COM  *	None
2729484Sgarrett.damore@Sun.COM  *
2739484Sgarrett.damore@Sun.COM  * Returns:
2749484Sgarrett.damore@Sun.COM  *	mod_remove() status, see mod_remove(9f)
2759484Sgarrett.damore@Sun.COM  */
2769484Sgarrett.damore@Sun.COM int
_fini(void)2779484Sgarrett.damore@Sun.COM _fini(void)
2789484Sgarrett.damore@Sun.COM {
2799484Sgarrett.damore@Sun.COM 	int		error;
2809484Sgarrett.damore@Sun.COM 
2819484Sgarrett.damore@Sun.COM 	if ((error = mod_remove(&audio810_modlinkage)) != 0) {
2829484Sgarrett.damore@Sun.COM 		return (error);
2839484Sgarrett.damore@Sun.COM 	}
2849484Sgarrett.damore@Sun.COM 
2859484Sgarrett.damore@Sun.COM 	/* clean up ops */
2869484Sgarrett.damore@Sun.COM 	audio_fini_ops(&audio810_dev_ops);
2879484Sgarrett.damore@Sun.COM 
2889484Sgarrett.damore@Sun.COM 	return (0);
2899484Sgarrett.damore@Sun.COM }
2909484Sgarrett.damore@Sun.COM 
2919484Sgarrett.damore@Sun.COM /*
2929484Sgarrett.damore@Sun.COM  * _info()
2939484Sgarrett.damore@Sun.COM  *
2949484Sgarrett.damore@Sun.COM  * Description:
2959484Sgarrett.damore@Sun.COM  *	Module information, returns information about the driver.
2969484Sgarrett.damore@Sun.COM  *
2979484Sgarrett.damore@Sun.COM  * Arguments:
2989484Sgarrett.damore@Sun.COM  *	modinfo		*modinfop	Pointer to the opaque modinfo structure
2999484Sgarrett.damore@Sun.COM  *
3009484Sgarrett.damore@Sun.COM  * Returns:
3019484Sgarrett.damore@Sun.COM  *	mod_info() status, see mod_info(9f)
3029484Sgarrett.damore@Sun.COM  */
3039484Sgarrett.damore@Sun.COM int
_info(struct modinfo * modinfop)3049484Sgarrett.damore@Sun.COM _info(struct modinfo *modinfop)
3059484Sgarrett.damore@Sun.COM {
3069484Sgarrett.damore@Sun.COM 	return (mod_info(&audio810_modlinkage, modinfop));
3079484Sgarrett.damore@Sun.COM }
3089484Sgarrett.damore@Sun.COM 
3099484Sgarrett.damore@Sun.COM 
3109484Sgarrett.damore@Sun.COM /* ******************* Driver Entry Points ********************************* */
3119484Sgarrett.damore@Sun.COM 
3129484Sgarrett.damore@Sun.COM /*
3139484Sgarrett.damore@Sun.COM  * audio810_ddi_attach()
3149484Sgarrett.damore@Sun.COM  *
3159484Sgarrett.damore@Sun.COM  * Description:
3169484Sgarrett.damore@Sun.COM  *	Implements the DDI attach(9e) entry point.
3179484Sgarrett.damore@Sun.COM  *
3189484Sgarrett.damore@Sun.COM  * Arguments:
3199484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
3209484Sgarrett.damore@Sun.COM  *	ddi_attach_cmd_t cmd	Attach command
3219484Sgarrett.damore@Sun.COM  *
3229484Sgarrett.damore@Sun.COM  * Returns:
3239484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was initialized properly
3249484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be initialized properly
3259484Sgarrett.damore@Sun.COM  */
3269484Sgarrett.damore@Sun.COM static int
audio810_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3279484Sgarrett.damore@Sun.COM audio810_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3289484Sgarrett.damore@Sun.COM {
3299484Sgarrett.damore@Sun.COM 	switch (cmd) {
3309484Sgarrett.damore@Sun.COM 	case DDI_ATTACH:
3319484Sgarrett.damore@Sun.COM 		return (audio810_attach(dip));
3329484Sgarrett.damore@Sun.COM 
3339484Sgarrett.damore@Sun.COM 	case DDI_RESUME:
3349484Sgarrett.damore@Sun.COM 		return (audio810_resume(dip));
3359484Sgarrett.damore@Sun.COM 	}
3369484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
3379484Sgarrett.damore@Sun.COM }
3389484Sgarrett.damore@Sun.COM 
3399484Sgarrett.damore@Sun.COM /*
3409484Sgarrett.damore@Sun.COM  * audio810_ddi_detach()
3419484Sgarrett.damore@Sun.COM  *
3429484Sgarrett.damore@Sun.COM  * Description:
3439484Sgarrett.damore@Sun.COM  *	Implements the detach(9e) entry point.
3449484Sgarrett.damore@Sun.COM  *
3459484Sgarrett.damore@Sun.COM  * Arguments:
3469484Sgarrett.damore@Sun.COM  *	dev_info_t		*dip	Pointer to the device's dev_info struct
3479484Sgarrett.damore@Sun.COM  *	ddi_detach_cmd_t	cmd	Detach command
3489484Sgarrett.damore@Sun.COM  *
3499484Sgarrett.damore@Sun.COM  * Returns:
3509484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	The driver was detached
3519484Sgarrett.damore@Sun.COM  *	DDI_FAILURE	The driver couldn't be detached
3529484Sgarrett.damore@Sun.COM  */
3539484Sgarrett.damore@Sun.COM static int
audio810_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3549484Sgarrett.damore@Sun.COM audio810_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3559484Sgarrett.damore@Sun.COM {
3569484Sgarrett.damore@Sun.COM 	switch (cmd) {
3579484Sgarrett.damore@Sun.COM 	case DDI_DETACH:
3589484Sgarrett.damore@Sun.COM 		return (audio810_detach(dip));
3599484Sgarrett.damore@Sun.COM 
3609484Sgarrett.damore@Sun.COM 	case DDI_SUSPEND:
3619484Sgarrett.damore@Sun.COM 		return (audio810_suspend(dip));
3629484Sgarrett.damore@Sun.COM 	}
3639484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
3649484Sgarrett.damore@Sun.COM }
3659484Sgarrett.damore@Sun.COM 
3669484Sgarrett.damore@Sun.COM /*
3679484Sgarrett.damore@Sun.COM  * audio810_ddi_quiesce()
3689484Sgarrett.damore@Sun.COM  *
3699484Sgarrett.damore@Sun.COM  * Description:
3709484Sgarrett.damore@Sun.COM  *	Implements the quiesce(9e) entry point.
3719484Sgarrett.damore@Sun.COM  *
3729484Sgarrett.damore@Sun.COM  * Arguments:
3739484Sgarrett.damore@Sun.COM  *	dev_info_t		*dip	Pointer to the device's dev_info struct
3749484Sgarrett.damore@Sun.COM  *
3759484Sgarrett.damore@Sun.COM  * Returns:
3769484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	The driver was quiesced
3779484Sgarrett.damore@Sun.COM  *	DDI_FAILURE	The driver couldn't be quiesced
3789484Sgarrett.damore@Sun.COM  */
3799484Sgarrett.damore@Sun.COM static int
audio810_ddi_quiesce(dev_info_t * dip)3809484Sgarrett.damore@Sun.COM audio810_ddi_quiesce(dev_info_t *dip)
3819484Sgarrett.damore@Sun.COM {
3829484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep;
3839484Sgarrett.damore@Sun.COM 
3849484Sgarrett.damore@Sun.COM 	if ((statep = ddi_get_driver_private(dip)) == NULL)
3859484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
3869484Sgarrett.damore@Sun.COM 
3879484Sgarrett.damore@Sun.COM 	audio810_stop_dma(statep);
3889484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
3899484Sgarrett.damore@Sun.COM }
3909484Sgarrett.damore@Sun.COM 
3919484Sgarrett.damore@Sun.COM /*
3929484Sgarrett.damore@Sun.COM  * audio810_open()
3939484Sgarrett.damore@Sun.COM  *
3949484Sgarrett.damore@Sun.COM  * Description:
3959484Sgarrett.damore@Sun.COM  *	Opens a DMA engine for use.
3969484Sgarrett.damore@Sun.COM  *
3979484Sgarrett.damore@Sun.COM  * Arguments:
3989484Sgarrett.damore@Sun.COM  *	void		*arg		The DMA engine to set up
3999484Sgarrett.damore@Sun.COM  *	int		flag		Open flags
400*11936Sgdamore@opensolaris.org  *	unsigned	*nframes	Receives total number of frames
4019484Sgarrett.damore@Sun.COM  *	caddr_t		*bufp		Receives kernel data buffer
4029484Sgarrett.damore@Sun.COM  *
4039484Sgarrett.damore@Sun.COM  * Returns:
4049484Sgarrett.damore@Sun.COM  *	0	on success
4059484Sgarrett.damore@Sun.COM  *	errno	on failure
4069484Sgarrett.damore@Sun.COM  */
4079484Sgarrett.damore@Sun.COM static int
audio810_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)408*11936Sgdamore@opensolaris.org audio810_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
4099484Sgarrett.damore@Sun.COM {
4109484Sgarrett.damore@Sun.COM 	audio810_port_t	*port = arg;
4119484Sgarrett.damore@Sun.COM 
4129484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(flag));
4139484Sgarrett.damore@Sun.COM 
4149484Sgarrett.damore@Sun.COM 	port->count = 0;
415*11936Sgdamore@opensolaris.org 	*nframes = port->samp_frames;
4169484Sgarrett.damore@Sun.COM 	*bufp = port->samp_kaddr;
4179484Sgarrett.damore@Sun.COM 
4189484Sgarrett.damore@Sun.COM 	return (0);
4199484Sgarrett.damore@Sun.COM }
4209484Sgarrett.damore@Sun.COM 
4219484Sgarrett.damore@Sun.COM /*
4229484Sgarrett.damore@Sun.COM  * audio810_close()
4239484Sgarrett.damore@Sun.COM  *
4249484Sgarrett.damore@Sun.COM  * Description:
4259484Sgarrett.damore@Sun.COM  *	Closes an audio DMA engine that was previously opened.  Since
4269484Sgarrett.damore@Sun.COM  *	nobody is using it, we take this opportunity to possibly power
4279484Sgarrett.damore@Sun.COM  *	down the entire device.
4289484Sgarrett.damore@Sun.COM  *
4299484Sgarrett.damore@Sun.COM  * Arguments:
4309484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to shut down
4319484Sgarrett.damore@Sun.COM  */
4329484Sgarrett.damore@Sun.COM static void
audio810_close(void * arg)4339484Sgarrett.damore@Sun.COM audio810_close(void *arg)
4349484Sgarrett.damore@Sun.COM {
435*11936Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(arg));
4369484Sgarrett.damore@Sun.COM }
4379484Sgarrett.damore@Sun.COM 
4389484Sgarrett.damore@Sun.COM /*
4399484Sgarrett.damore@Sun.COM  * audio810_stop()
4409484Sgarrett.damore@Sun.COM  *
4419484Sgarrett.damore@Sun.COM  * Description:
4429484Sgarrett.damore@Sun.COM  *	This is called by the framework to stop a port that is
4439484Sgarrett.damore@Sun.COM  *	transferring data.
4449484Sgarrett.damore@Sun.COM  *
4459484Sgarrett.damore@Sun.COM  * Arguments:
4469484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to stop
4479484Sgarrett.damore@Sun.COM  */
4489484Sgarrett.damore@Sun.COM static void
audio810_stop(void * arg)4499484Sgarrett.damore@Sun.COM audio810_stop(void *arg)
4509484Sgarrett.damore@Sun.COM {
4519484Sgarrett.damore@Sun.COM 	audio810_port_t		*port = arg;
4529484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep = port->statep;
453*11936Sgdamore@opensolaris.org 	uint8_t			cr;
4549484Sgarrett.damore@Sun.COM 
455*11936Sgdamore@opensolaris.org 	cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR);
456*11936Sgdamore@opensolaris.org 	cr &= ~I810_BM_CR_RUN;
457*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr);
4589484Sgarrett.damore@Sun.COM }
4599484Sgarrett.damore@Sun.COM 
4609484Sgarrett.damore@Sun.COM /*
4619484Sgarrett.damore@Sun.COM  * audio810_start()
4629484Sgarrett.damore@Sun.COM  *
4639484Sgarrett.damore@Sun.COM  * Description:
4649484Sgarrett.damore@Sun.COM  *	This is called by the framework to start a port transferring data.
4659484Sgarrett.damore@Sun.COM  *
4669484Sgarrett.damore@Sun.COM  * Arguments:
4679484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to start
4689484Sgarrett.damore@Sun.COM  *
4699484Sgarrett.damore@Sun.COM  * Returns:
4709484Sgarrett.damore@Sun.COM  *	0 	on success (never fails, errno if it did)
4719484Sgarrett.damore@Sun.COM  */
4729484Sgarrett.damore@Sun.COM static int
audio810_start(void * arg)4739484Sgarrett.damore@Sun.COM audio810_start(void *arg)
4749484Sgarrett.damore@Sun.COM {
4759484Sgarrett.damore@Sun.COM 	audio810_port_t		*port = arg;
4769484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep = port->statep;
477*11936Sgdamore@opensolaris.org 	uint8_t			regoff, cr;
4789484Sgarrett.damore@Sun.COM 
479*11936Sgdamore@opensolaris.org 	regoff = port->regoff;
480*11936Sgdamore@opensolaris.org 	port->offset = 0;
481*11936Sgdamore@opensolaris.org 
482*11936Sgdamore@opensolaris.org 	/* program multiple channel settings */
483*11936Sgdamore@opensolaris.org 	if (port->num == I810_PCM_OUT) {
484*11936Sgdamore@opensolaris.org 		audio810_set_channels(statep);
485*11936Sgdamore@opensolaris.org 
486*11936Sgdamore@opensolaris.org 		if (statep->quirk == QUIRK_SIS7012) {
487*11936Sgdamore@opensolaris.org 			/*
488*11936Sgdamore@opensolaris.org 			 * SiS 7012 has special unmute bit.
489*11936Sgdamore@opensolaris.org 			 */
490*11936Sgdamore@opensolaris.org 			I810_BM_PUT8(I810_REG_SISCTL, I810_SISCTL_UNMUTE);
491*11936Sgdamore@opensolaris.org 		}
4929484Sgarrett.damore@Sun.COM 	}
493*11936Sgdamore@opensolaris.org 
494*11936Sgdamore@opensolaris.org 	/*
495*11936Sgdamore@opensolaris.org 	 * Perform full reset of the engine, but leave it turned off.
496*11936Sgdamore@opensolaris.org 	 */
497*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(regoff + I810_OFFSET_CR, 0);
498*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(regoff + I810_OFFSET_CR, I810_BM_CR_RST);
499*11936Sgdamore@opensolaris.org 
500*11936Sgdamore@opensolaris.org 	/* program the offset of the BD list */
501*11936Sgdamore@opensolaris.org 	I810_BM_PUT32(regoff + I810_OFFSET_BD_BASE, port->bdl_paddr);
502*11936Sgdamore@opensolaris.org 
503*11936Sgdamore@opensolaris.org 	/* we set the last index to the full count -- all buffers are valid */
504*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(regoff + I810_OFFSET_LVI, I810_BD_NUMS - 1);
505*11936Sgdamore@opensolaris.org 
506*11936Sgdamore@opensolaris.org 	cr = I810_BM_GET8(regoff + I810_OFFSET_CR);
507*11936Sgdamore@opensolaris.org 	cr |= I810_BM_CR_RUN;
508*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(regoff + I810_OFFSET_CR, cr);
509*11936Sgdamore@opensolaris.org 
510*11936Sgdamore@opensolaris.org 	(void) I810_BM_GET8(regoff + I810_OFFSET_CR);
511*11936Sgdamore@opensolaris.org 
5129484Sgarrett.damore@Sun.COM 	return (0);
5139484Sgarrett.damore@Sun.COM }
5149484Sgarrett.damore@Sun.COM 
5159484Sgarrett.damore@Sun.COM /*
5169484Sgarrett.damore@Sun.COM  * audio810_format()
5179484Sgarrett.damore@Sun.COM  *
5189484Sgarrett.damore@Sun.COM  * Description:
5199484Sgarrett.damore@Sun.COM  *	This is called by the framework to query the format of the device.
5209484Sgarrett.damore@Sun.COM  *
5219484Sgarrett.damore@Sun.COM  * Arguments:
5229484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
5239484Sgarrett.damore@Sun.COM  *
5249484Sgarrett.damore@Sun.COM  * Returns:
5259484Sgarrett.damore@Sun.COM  *	Format of the device (fixed at AUDIO_FORMAT_S16_LE)
5269484Sgarrett.damore@Sun.COM  */
5279484Sgarrett.damore@Sun.COM static int
audio810_format(void * arg)5289484Sgarrett.damore@Sun.COM audio810_format(void *arg)
5299484Sgarrett.damore@Sun.COM {
5309484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
5319484Sgarrett.damore@Sun.COM 
5329484Sgarrett.damore@Sun.COM 	return (AUDIO_FORMAT_S16_LE);
5339484Sgarrett.damore@Sun.COM }
5349484Sgarrett.damore@Sun.COM 
5359484Sgarrett.damore@Sun.COM /*
5369484Sgarrett.damore@Sun.COM  * audio810_channels()
5379484Sgarrett.damore@Sun.COM  *
5389484Sgarrett.damore@Sun.COM  * Description:
5399484Sgarrett.damore@Sun.COM  *	This is called by the framework to query the num channels of
5409484Sgarrett.damore@Sun.COM  *	the device.
5419484Sgarrett.damore@Sun.COM  *
5429484Sgarrett.damore@Sun.COM  * Arguments:
5439484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
5449484Sgarrett.damore@Sun.COM  *
5459484Sgarrett.damore@Sun.COM  * Returns:
5469484Sgarrett.damore@Sun.COM  *	0 number of channels for device
5479484Sgarrett.damore@Sun.COM  */
5489484Sgarrett.damore@Sun.COM static int
audio810_channels(void * arg)5499484Sgarrett.damore@Sun.COM audio810_channels(void *arg)
5509484Sgarrett.damore@Sun.COM {
5519484Sgarrett.damore@Sun.COM 	audio810_port_t	*port = arg;
5529484Sgarrett.damore@Sun.COM 
5539484Sgarrett.damore@Sun.COM 	return (port->nchan);
5549484Sgarrett.damore@Sun.COM }
5559484Sgarrett.damore@Sun.COM 
5569484Sgarrett.damore@Sun.COM /*
5579484Sgarrett.damore@Sun.COM  * audio810_rate()
5589484Sgarrett.damore@Sun.COM  *
5599484Sgarrett.damore@Sun.COM  * Description:
5609484Sgarrett.damore@Sun.COM  *	This is called by the framework to query the rate of the device.
5619484Sgarrett.damore@Sun.COM  *
5629484Sgarrett.damore@Sun.COM  * Arguments:
5639484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
5649484Sgarrett.damore@Sun.COM  *
5659484Sgarrett.damore@Sun.COM  * Returns:
5669484Sgarrett.damore@Sun.COM  *	Rate of device (fixed at 48000 Hz)
5679484Sgarrett.damore@Sun.COM  */
5689484Sgarrett.damore@Sun.COM static int
audio810_rate(void * arg)5699484Sgarrett.damore@Sun.COM audio810_rate(void *arg)
5709484Sgarrett.damore@Sun.COM {
5719484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
5729484Sgarrett.damore@Sun.COM 
5739484Sgarrett.damore@Sun.COM 	return (48000);
5749484Sgarrett.damore@Sun.COM }
5759484Sgarrett.damore@Sun.COM 
5769484Sgarrett.damore@Sun.COM /*
5779484Sgarrett.damore@Sun.COM  * audio810_count()
5789484Sgarrett.damore@Sun.COM  *
5799484Sgarrett.damore@Sun.COM  * Description:
5809484Sgarrett.damore@Sun.COM  *	This is called by the framework to get the engine's frame counter
5819484Sgarrett.damore@Sun.COM  *
5829484Sgarrett.damore@Sun.COM  * Arguments:
5839484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
5849484Sgarrett.damore@Sun.COM  *
5859484Sgarrett.damore@Sun.COM  * Returns:
5869484Sgarrett.damore@Sun.COM  *	frame count for current engine
5879484Sgarrett.damore@Sun.COM  */
5889484Sgarrett.damore@Sun.COM static uint64_t
audio810_count(void * arg)5899484Sgarrett.damore@Sun.COM audio810_count(void *arg)
5909484Sgarrett.damore@Sun.COM {
5919484Sgarrett.damore@Sun.COM 	audio810_port_t		*port = arg;
5929484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep = port->statep;
593*11936Sgdamore@opensolaris.org 	uint8_t			regoff = port->regoff;
5949484Sgarrett.damore@Sun.COM 	uint64_t		val;
595*11936Sgdamore@opensolaris.org 	uint32_t		offset;
596*11936Sgdamore@opensolaris.org 	uint8_t			civ;
597*11936Sgdamore@opensolaris.org 
598*11936Sgdamore@opensolaris.org 	/*
599*11936Sgdamore@opensolaris.org 	 * Read the position counters.  We also take this opportunity
600*11936Sgdamore@opensolaris.org 	 * to update the last valid index to the one just previous to
601*11936Sgdamore@opensolaris.org 	 * the one we're working on (so we'll fully loop.)
602*11936Sgdamore@opensolaris.org 	 */
603*11936Sgdamore@opensolaris.org 	offset = I810_BM_GET16(port->picboff);
604*11936Sgdamore@opensolaris.org 	civ = I810_BM_GET8(regoff + I810_OFFSET_CIV);
605*11936Sgdamore@opensolaris.org 	I810_BM_PUT8(port->regoff + I810_OFFSET_LVI, (civ - 1) % I810_BD_NUMS);
6069484Sgarrett.damore@Sun.COM 
607*11936Sgdamore@opensolaris.org 	/* SiS counts in bytes, all others in words. */
608*11936Sgdamore@opensolaris.org 	if (statep->quirk != QUIRK_SIS7012)
609*11936Sgdamore@opensolaris.org 		offset *= 2;
610*11936Sgdamore@opensolaris.org 
611*11936Sgdamore@opensolaris.org 	/* counter is reversed */
612*11936Sgdamore@opensolaris.org 	offset = port->samp_size - offset;
6139484Sgarrett.damore@Sun.COM 
614*11936Sgdamore@opensolaris.org 	if (offset < port->offset) {
615*11936Sgdamore@opensolaris.org 		val = (port->samp_size - port->offset) + offset;
6169484Sgarrett.damore@Sun.COM 	} else {
617*11936Sgdamore@opensolaris.org 		val = offset - port->offset;
6189484Sgarrett.damore@Sun.COM 	}
619*11936Sgdamore@opensolaris.org 	port->offset = offset;
620*11936Sgdamore@opensolaris.org 	port->count += (val / (port->nchan * 2));
621*11936Sgdamore@opensolaris.org 	val = port->count;
6229484Sgarrett.damore@Sun.COM 
6239484Sgarrett.damore@Sun.COM 	return (val);
6249484Sgarrett.damore@Sun.COM }
6259484Sgarrett.damore@Sun.COM 
6269484Sgarrett.damore@Sun.COM /*
6279484Sgarrett.damore@Sun.COM  * audio810_sync()
6289484Sgarrett.damore@Sun.COM  *
6299484Sgarrett.damore@Sun.COM  * Description:
6309484Sgarrett.damore@Sun.COM  *	This is called by the framework to synchronize DMA caches.
6319484Sgarrett.damore@Sun.COM  *
6329484Sgarrett.damore@Sun.COM  * Arguments:
6339484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to sync
6349484Sgarrett.damore@Sun.COM  */
6359484Sgarrett.damore@Sun.COM static void
audio810_sync(void * arg,unsigned nframes)6369484Sgarrett.damore@Sun.COM audio810_sync(void *arg, unsigned nframes)
6379484Sgarrett.damore@Sun.COM {
6389484Sgarrett.damore@Sun.COM 	audio810_port_t *port = arg;
6399484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(nframes));
6409484Sgarrett.damore@Sun.COM 
6419484Sgarrett.damore@Sun.COM 	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
6429484Sgarrett.damore@Sun.COM }
6439484Sgarrett.damore@Sun.COM 
6449484Sgarrett.damore@Sun.COM /*
64511213Sgdamore@opensolaris.org  * audio810_playahead()
6469484Sgarrett.damore@Sun.COM  *
6479484Sgarrett.damore@Sun.COM  * Description:
64811213Sgdamore@opensolaris.org  *	This is called by the framework to determine how much data it
64911213Sgdamore@opensolaris.org  *	should queue up.  We desire a deeper playahead than most to
65011213Sgdamore@opensolaris.org  *	allow for virtualized devices which have less "regular"
65111213Sgdamore@opensolaris.org  *	interrupt scheduling.
6529484Sgarrett.damore@Sun.COM  *
6539484Sgarrett.damore@Sun.COM  * Arguments:
6549484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
6559484Sgarrett.damore@Sun.COM  *
6569484Sgarrett.damore@Sun.COM  * Returns:
657*11936Sgdamore@opensolaris.org  *	Play ahead in frames.
6589484Sgarrett.damore@Sun.COM  */
65911213Sgdamore@opensolaris.org static unsigned
audio810_playahead(void * arg)66011213Sgdamore@opensolaris.org audio810_playahead(void *arg)
6619484Sgarrett.damore@Sun.COM {
66211213Sgdamore@opensolaris.org 	audio810_port_t *port = arg;
663*11936Sgdamore@opensolaris.org 	audio810_state_t	*statep = port->statep;
66411213Sgdamore@opensolaris.org 
665*11936Sgdamore@opensolaris.org 	/* Older ICH is likely to be emulated, deeper (40 ms) playahead */
666*11936Sgdamore@opensolaris.org 	return (statep->quirk == QUIRK_OLDICH ? 1920 : 0);
6679484Sgarrett.damore@Sun.COM }
6689484Sgarrett.damore@Sun.COM 
66911213Sgdamore@opensolaris.org 
67011213Sgdamore@opensolaris.org 
6719484Sgarrett.damore@Sun.COM /* *********************** Local Routines *************************** */
6729484Sgarrett.damore@Sun.COM 
6739484Sgarrett.damore@Sun.COM /*
6749484Sgarrett.damore@Sun.COM  * audio810_attach()
6759484Sgarrett.damore@Sun.COM  *
6769484Sgarrett.damore@Sun.COM  * Description:
6779484Sgarrett.damore@Sun.COM  *	Attach an instance of the audio810 driver. This routine does the
6789484Sgarrett.damore@Sun.COM  * 	device dependent attach tasks, and registers with the audio framework.
6799484Sgarrett.damore@Sun.COM  *
6809484Sgarrett.damore@Sun.COM  * Arguments:
6819484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
6829484Sgarrett.damore@Sun.COM  *	ddi_attach_cmd_t cmd	Attach command
6839484Sgarrett.damore@Sun.COM  *
6849484Sgarrett.damore@Sun.COM  * Returns:
6859484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was initialized properly
6869484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be initialized properly
6879484Sgarrett.damore@Sun.COM  */
6889484Sgarrett.damore@Sun.COM static int
audio810_attach(dev_info_t * dip)6899484Sgarrett.damore@Sun.COM audio810_attach(dev_info_t *dip)
6909484Sgarrett.damore@Sun.COM {
6919484Sgarrett.damore@Sun.COM 	uint16_t		cmdreg;
6929484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep;
6939484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
6949484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	pcih;
6959484Sgarrett.damore@Sun.COM 	uint32_t		devid;
6969484Sgarrett.damore@Sun.COM 	uint32_t		gsr;
6979484Sgarrett.damore@Sun.COM 	const char		*name;
6989484Sgarrett.damore@Sun.COM 	const char		*vers;
6999484Sgarrett.damore@Sun.COM 	uint8_t			nch;
7009484Sgarrett.damore@Sun.COM 	int			maxch;
7019484Sgarrett.damore@Sun.COM 
7029484Sgarrett.damore@Sun.COM 	/* allocate the soft state structure */
7039484Sgarrett.damore@Sun.COM 	statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
7049484Sgarrett.damore@Sun.COM 	ddi_set_driver_private(dip, statep);
7059484Sgarrett.damore@Sun.COM 
7069484Sgarrett.damore@Sun.COM 	if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
707*11936Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev",
708*11936Sgdamore@opensolaris.org 		    ddi_driver_name(dip), ddi_get_instance(dip));
7099484Sgarrett.damore@Sun.COM 		goto error;
7109484Sgarrett.damore@Sun.COM 	}
7119484Sgarrett.damore@Sun.COM 	statep->adev = adev;
7129484Sgarrett.damore@Sun.COM 	statep->dip = dip;
7139484Sgarrett.damore@Sun.COM 
7149484Sgarrett.damore@Sun.COM 	/* map in the registers, allocate DMA buffers, etc. */
7159484Sgarrett.damore@Sun.COM 	if (audio810_map_regs(dip, statep) != DDI_SUCCESS) {
7169484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "couldn't map registers");
7179484Sgarrett.damore@Sun.COM 		goto error;
7189484Sgarrett.damore@Sun.COM 	}
7199484Sgarrett.damore@Sun.COM 
7209484Sgarrett.damore@Sun.COM 	/* set PCI command register */
7219484Sgarrett.damore@Sun.COM 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
7229484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "pci conf mapping failed");
7239484Sgarrett.damore@Sun.COM 		goto error;
7249484Sgarrett.damore@Sun.COM 	}
7259484Sgarrett.damore@Sun.COM 	cmdreg = pci_config_get16(pcih, PCI_CONF_COMM);
7269484Sgarrett.damore@Sun.COM 	pci_config_put16(pcih, PCI_CONF_COMM,
7279484Sgarrett.damore@Sun.COM 	    cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
7289484Sgarrett.damore@Sun.COM 	devid = pci_config_get16(pcih, PCI_CONF_VENID);
7299484Sgarrett.damore@Sun.COM 	devid <<= 16;
7309484Sgarrett.damore@Sun.COM 	devid |= pci_config_get16(pcih, PCI_CONF_DEVID);
7319484Sgarrett.damore@Sun.COM 	pci_config_teardown(&pcih);
7329484Sgarrett.damore@Sun.COM 
7339484Sgarrett.damore@Sun.COM 	name = "Unknown AC'97";
7349484Sgarrett.damore@Sun.COM 	vers = "";
7359484Sgarrett.damore@Sun.COM 
7369484Sgarrett.damore@Sun.COM 	statep->quirk = QUIRK_NONE;
7379484Sgarrett.damore@Sun.COM 	switch (devid) {
7389484Sgarrett.damore@Sun.COM 	case 0x80862415:
7399484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7409484Sgarrett.damore@Sun.COM 		vers = "ICH";
741*11936Sgdamore@opensolaris.org 		statep->quirk = QUIRK_OLDICH;
7429484Sgarrett.damore@Sun.COM 		break;
7439484Sgarrett.damore@Sun.COM 	case 0x80862425:
7449484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7459484Sgarrett.damore@Sun.COM 		vers = "ICH0";
7469484Sgarrett.damore@Sun.COM 		break;
7479484Sgarrett.damore@Sun.COM 	case 0x80867195:
7489484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7499484Sgarrett.damore@Sun.COM 		vers = "440MX";
7509484Sgarrett.damore@Sun.COM 		break;
7519484Sgarrett.damore@Sun.COM 	case 0x80862445:
7529484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7539484Sgarrett.damore@Sun.COM 		vers = "ICH2";
7549484Sgarrett.damore@Sun.COM 		break;
7559484Sgarrett.damore@Sun.COM 	case 0x80862485:
7569484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7579484Sgarrett.damore@Sun.COM 		vers = "ICH3";
7589484Sgarrett.damore@Sun.COM 		break;
7599484Sgarrett.damore@Sun.COM 	case 0x808624C5:
7609484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7619484Sgarrett.damore@Sun.COM 		vers = "ICH4";
7629484Sgarrett.damore@Sun.COM 		break;
7639484Sgarrett.damore@Sun.COM 	case 0x808624D5:
7649484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7659484Sgarrett.damore@Sun.COM 		vers = "ICH5";
7669484Sgarrett.damore@Sun.COM 		break;
7679484Sgarrett.damore@Sun.COM 	case 0x8086266E:
7689484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7699484Sgarrett.damore@Sun.COM 		vers = "ICH6";
7709484Sgarrett.damore@Sun.COM 		break;
7719484Sgarrett.damore@Sun.COM 	case 0x808627DE:
7729484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7739484Sgarrett.damore@Sun.COM 		vers = "ICH7";
7749484Sgarrett.damore@Sun.COM 		break;
7759484Sgarrett.damore@Sun.COM 	case 0x808625A6:
7769484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7779484Sgarrett.damore@Sun.COM 		vers = "6300ESB";
7789484Sgarrett.damore@Sun.COM 		break;
7799484Sgarrett.damore@Sun.COM 	case 0x80862698:
7809484Sgarrett.damore@Sun.COM 		name = "Intel AC'97";
7819484Sgarrett.damore@Sun.COM 		vers = "ESB2";
7829484Sgarrett.damore@Sun.COM 		break;
7839484Sgarrett.damore@Sun.COM 	case 0x10397012:
7849484Sgarrett.damore@Sun.COM 		name = "SiS AC'97";
7859484Sgarrett.damore@Sun.COM 		vers = "7012";
7869484Sgarrett.damore@Sun.COM 		statep->quirk = QUIRK_SIS7012;
7879484Sgarrett.damore@Sun.COM 		break;
7889484Sgarrett.damore@Sun.COM 	case 0x10de01b1:	/* nForce */
7899484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
7909484Sgarrett.damore@Sun.COM 		vers = "MCP1";
7919484Sgarrett.damore@Sun.COM 		break;
7929484Sgarrett.damore@Sun.COM 	case 0x10de006a:	/* nForce 2 */
7939484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
7949484Sgarrett.damore@Sun.COM 		vers = "MCP2";
7959484Sgarrett.damore@Sun.COM 		break;
7969484Sgarrett.damore@Sun.COM 	case 0x10de00da:	/* nForce 3 */
7979484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
7989484Sgarrett.damore@Sun.COM 		vers = "MCP3";
7999484Sgarrett.damore@Sun.COM 		break;
8009484Sgarrett.damore@Sun.COM 	case 0x10de00ea:
8019484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
8029484Sgarrett.damore@Sun.COM 		vers = "CK8S";
8039484Sgarrett.damore@Sun.COM 		break;
8049484Sgarrett.damore@Sun.COM 	case 0x10de0059:
8059484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
8069484Sgarrett.damore@Sun.COM 		vers = "CK804";
8079484Sgarrett.damore@Sun.COM 		break;
8089484Sgarrett.damore@Sun.COM 	case 0x10de008a:
8099484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
8109484Sgarrett.damore@Sun.COM 		vers = "CK8";
8119484Sgarrett.damore@Sun.COM 		break;
8129484Sgarrett.damore@Sun.COM 	case 0x10de003a:	/* nForce 4 */
8139484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
8149484Sgarrett.damore@Sun.COM 		vers = "MCP4";
8159484Sgarrett.damore@Sun.COM 		break;
8169484Sgarrett.damore@Sun.COM 	case 0x10de026b:
8179484Sgarrett.damore@Sun.COM 		name = "NVIDIA AC'97";
8189484Sgarrett.damore@Sun.COM 		vers = "MCP51";
8199484Sgarrett.damore@Sun.COM 		break;
8209484Sgarrett.damore@Sun.COM 	case 0x1022746d:
8219484Sgarrett.damore@Sun.COM 		name = "AMD AC'97";
8229484Sgarrett.damore@Sun.COM 		vers = "8111";
8239484Sgarrett.damore@Sun.COM 		break;
8249484Sgarrett.damore@Sun.COM 	case 0x10227445:
8259484Sgarrett.damore@Sun.COM 		name = "AMD AC'97";
8269484Sgarrett.damore@Sun.COM 		vers = "AMD768";
8279484Sgarrett.damore@Sun.COM 		break;
8289484Sgarrett.damore@Sun.COM 	}
8299484Sgarrett.damore@Sun.COM 	/* set device information */
8309484Sgarrett.damore@Sun.COM 	audio_dev_set_description(adev, name);
8319484Sgarrett.damore@Sun.COM 	audio_dev_set_version(adev, vers);
8329484Sgarrett.damore@Sun.COM 
8339602Sgdamore@opensolaris.org 	/* initialize audio controller and AC97 codec */
8349602Sgdamore@opensolaris.org 	if (audio810_chip_init(statep) != DDI_SUCCESS) {
8359602Sgdamore@opensolaris.org 		audio_dev_warn(adev, "failed to init chip");
8369484Sgarrett.damore@Sun.COM 		goto error;
8379484Sgarrett.damore@Sun.COM 	}
8389484Sgarrett.damore@Sun.COM 
8399484Sgarrett.damore@Sun.COM 	/* allocate ac97 handle */
8409484Sgarrett.damore@Sun.COM 	statep->ac97 = ac97_alloc(dip, audio810_read_ac97, audio810_write_ac97,
8419484Sgarrett.damore@Sun.COM 	    statep);
8429484Sgarrett.damore@Sun.COM 	if (statep->ac97 == NULL) {
8439484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "failed to allocate ac97 handle");
8449484Sgarrett.damore@Sun.COM 		goto error;
8459484Sgarrett.damore@Sun.COM 	}
8469484Sgarrett.damore@Sun.COM 
8479602Sgdamore@opensolaris.org 	/* initialize the AC'97 part */
8489602Sgdamore@opensolaris.org 	if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
8499602Sgdamore@opensolaris.org 		audio_dev_warn(adev, "ac'97 initialization failed");
8509484Sgarrett.damore@Sun.COM 		goto error;
8519484Sgarrett.damore@Sun.COM 	}
8529484Sgarrett.damore@Sun.COM 
8539602Sgdamore@opensolaris.org 	/*
8549602Sgdamore@opensolaris.org 	 * Override "max-channels" property to prevent configuration
8559602Sgdamore@opensolaris.org 	 * of 4 or 6 (or possibly even 8!) channel audio.  The default
8569602Sgdamore@opensolaris.org 	 * is to support as many channels as the hardware can do.
8579602Sgdamore@opensolaris.org 	 *
8589602Sgdamore@opensolaris.org 	 * (Hmmm... perhaps this should be driven in the common
8599602Sgdamore@opensolaris.org 	 * framework.  The framework could even offer simplistic upmix
8609602Sgdamore@opensolaris.org 	 * and downmix for various standard configs.)
8619602Sgdamore@opensolaris.org 	 */
8629602Sgdamore@opensolaris.org 	maxch = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8639602Sgdamore@opensolaris.org 	    "max-channels", ac97_num_channels(statep->ac97));
8649602Sgdamore@opensolaris.org 	if (maxch < 2) {
8659602Sgdamore@opensolaris.org 		maxch = 2;
8669602Sgdamore@opensolaris.org 	}
8679602Sgdamore@opensolaris.org 
8689602Sgdamore@opensolaris.org 	gsr = I810_BM_GET32(I810_REG_GSR);
8699602Sgdamore@opensolaris.org 	if (gsr & I810_GSR_CAP6CH) {
8709602Sgdamore@opensolaris.org 		nch = 6;
8719602Sgdamore@opensolaris.org 	} else if (gsr & I810_GSR_CAP4CH) {
8729602Sgdamore@opensolaris.org 		nch = 4;
8739602Sgdamore@opensolaris.org 	} else {
8749602Sgdamore@opensolaris.org 		nch = 2;
8759602Sgdamore@opensolaris.org 	}
8769602Sgdamore@opensolaris.org 
8779602Sgdamore@opensolaris.org 	statep->maxch = (uint8_t)min(nch, maxch);
8789602Sgdamore@opensolaris.org 	statep->maxch &= ~1;
8799602Sgdamore@opensolaris.org 
8809602Sgdamore@opensolaris.org 	/* allocate port structures */
8819602Sgdamore@opensolaris.org 	if ((audio810_alloc_port(statep, I810_PCM_OUT, statep->maxch) !=
8829602Sgdamore@opensolaris.org 	    DDI_SUCCESS) ||
8839602Sgdamore@opensolaris.org 	    (audio810_alloc_port(statep, I810_PCM_IN, 2) != DDI_SUCCESS)) {
8849484Sgarrett.damore@Sun.COM 		goto error;
8859484Sgarrett.damore@Sun.COM 	}
8869484Sgarrett.damore@Sun.COM 
8879484Sgarrett.damore@Sun.COM 	if (audio_dev_register(adev) != DDI_SUCCESS) {
8889484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "unable to register with framework");
8899484Sgarrett.damore@Sun.COM 		goto error;
8909484Sgarrett.damore@Sun.COM 	}
8919484Sgarrett.damore@Sun.COM 
8929484Sgarrett.damore@Sun.COM 	ddi_report_dev(dip);
8939484Sgarrett.damore@Sun.COM 
8949484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
8959484Sgarrett.damore@Sun.COM 
8969484Sgarrett.damore@Sun.COM error:
8979484Sgarrett.damore@Sun.COM 	audio810_destroy(statep);
8989484Sgarrett.damore@Sun.COM 
8999484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
9009484Sgarrett.damore@Sun.COM }
9019484Sgarrett.damore@Sun.COM 
9029484Sgarrett.damore@Sun.COM 
9039484Sgarrett.damore@Sun.COM /*
9049484Sgarrett.damore@Sun.COM  * audio810_resume()
9059484Sgarrett.damore@Sun.COM  *
9069484Sgarrett.damore@Sun.COM  * Description:
9079484Sgarrett.damore@Sun.COM  *	Resume operation of the device after sleeping or hibernating.
9089484Sgarrett.damore@Sun.COM  *	Note that this should never fail, even if hardware goes wonky,
9099484Sgarrett.damore@Sun.COM  *	because the current PM framework will panic if it does.
9109484Sgarrett.damore@Sun.COM  *
9119484Sgarrett.damore@Sun.COM  * Arguments:
9129484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
9139484Sgarrett.damore@Sun.COM  *
9149484Sgarrett.damore@Sun.COM  * Returns:
9159484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was resumed.
9169484Sgarrett.damore@Sun.COM  */
9179484Sgarrett.damore@Sun.COM static int
audio810_resume(dev_info_t * dip)9189484Sgarrett.damore@Sun.COM audio810_resume(dev_info_t *dip)
9199484Sgarrett.damore@Sun.COM {
9209484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep;
9219484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
9229484Sgarrett.damore@Sun.COM 
9239484Sgarrett.damore@Sun.COM 	/* this should always be valid */
9249484Sgarrett.damore@Sun.COM 	statep = ddi_get_driver_private(dip);
9259484Sgarrett.damore@Sun.COM 	adev = statep->adev;
9269484Sgarrett.damore@Sun.COM 
9279484Sgarrett.damore@Sun.COM 	ASSERT(statep != NULL);
9289484Sgarrett.damore@Sun.COM 	ASSERT(dip == statep->dip);
9299484Sgarrett.damore@Sun.COM 
9309484Sgarrett.damore@Sun.COM 	/* Restore the audio810 chip's state */
9319484Sgarrett.damore@Sun.COM 	if (audio810_chip_init(statep) != DDI_SUCCESS) {
9329484Sgarrett.damore@Sun.COM 		/*
933*11936Sgdamore@opensolaris.org 		 * Note that PM gurus say we should return success
934*11936Sgdamore@opensolaris.org 		 * here.  Failure of audio shouldn't be considered
935*11936Sgdamore@opensolaris.org 		 * FATAL to the system.
936*11936Sgdamore@opensolaris.org 		 *
937*11936Sgdamore@opensolaris.org 		 * It turns out that the only way that the
938*11936Sgdamore@opensolaris.org 		 * audio810_chip_init fails is that the codec won't
939*11936Sgdamore@opensolaris.org 		 * re-initialize.  Audio streams may or may not make
940*11936Sgdamore@opensolaris.org 		 * progress; setting changes may or may not have the
941*11936Sgdamore@opensolaris.org 		 * desired effect.  What we'd really to do at this
942*11936Sgdamore@opensolaris.org 		 * point is use FMA to offline the part.  In the
943*11936Sgdamore@opensolaris.org 		 * meantime, we just muddle on logging the error.
944*11936Sgdamore@opensolaris.org 		 *
945*11936Sgdamore@opensolaris.org 		 * Note that returning from this routine without
946*11936Sgdamore@opensolaris.org 		 * allowing the audio_dev_resume() to take place can
947*11936Sgdamore@opensolaris.org 		 * have bad effects, as the framework does not know
948*11936Sgdamore@opensolaris.org 		 * what to do in the event of a failure of this
949*11936Sgdamore@opensolaris.org 		 * nature.  (It may be unsafe to call ENG_CLOSE(), for
950*11936Sgdamore@opensolaris.org 		 * example.)
9519484Sgarrett.damore@Sun.COM 		 */
952*11936Sgdamore@opensolaris.org 		audio_dev_warn(adev, "failure to resume codec");
9539484Sgarrett.damore@Sun.COM 	}
9549484Sgarrett.damore@Sun.COM 
955*11936Sgdamore@opensolaris.org 	/* Reset the AC'97 codec. */
956*11936Sgdamore@opensolaris.org 	ac97_reset(statep->ac97);
9579484Sgarrett.damore@Sun.COM 
958*11936Sgdamore@opensolaris.org 	/* And let the framework know we're ready for business again. */
959*11936Sgdamore@opensolaris.org 	audio_dev_resume(statep->adev);
9609484Sgarrett.damore@Sun.COM 
9619484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
9629484Sgarrett.damore@Sun.COM }
9639484Sgarrett.damore@Sun.COM 
9649484Sgarrett.damore@Sun.COM /*
9659484Sgarrett.damore@Sun.COM  * audio810_detach()
9669484Sgarrett.damore@Sun.COM  *
9679484Sgarrett.damore@Sun.COM  * Description:
9689484Sgarrett.damore@Sun.COM  *	Detach an instance of the audio810 driver.
9699484Sgarrett.damore@Sun.COM  *
9709484Sgarrett.damore@Sun.COM  * Arguments:
9719484Sgarrett.damore@Sun.COM  *	dev_info_t		*dip	Pointer to the device's dev_info struct
9729484Sgarrett.damore@Sun.COM  *
9739484Sgarrett.damore@Sun.COM  * Returns:
9749484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	The driver was detached
9759484Sgarrett.damore@Sun.COM  *	DDI_FAILURE	The driver couldn't be detached
9769484Sgarrett.damore@Sun.COM  */
9779484Sgarrett.damore@Sun.COM static int
audio810_detach(dev_info_t * dip)9789484Sgarrett.damore@Sun.COM audio810_detach(dev_info_t *dip)
9799484Sgarrett.damore@Sun.COM {
9809484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep;
9819484Sgarrett.damore@Sun.COM 
9829484Sgarrett.damore@Sun.COM 	statep = ddi_get_driver_private(dip);
9839484Sgarrett.damore@Sun.COM 	ASSERT(statep != NULL);
9849484Sgarrett.damore@Sun.COM 
9859484Sgarrett.damore@Sun.COM 	/* don't detach us if we are still in use */
9869484Sgarrett.damore@Sun.COM 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
9879484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
9889484Sgarrett.damore@Sun.COM 	}
9899484Sgarrett.damore@Sun.COM 
9909484Sgarrett.damore@Sun.COM 	audio810_destroy(statep);
9919484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
9929484Sgarrett.damore@Sun.COM }
9939484Sgarrett.damore@Sun.COM 
9949484Sgarrett.damore@Sun.COM /*
9959484Sgarrett.damore@Sun.COM  * audio810_suspend()
9969484Sgarrett.damore@Sun.COM  *
9979484Sgarrett.damore@Sun.COM  * Description:
9989484Sgarrett.damore@Sun.COM  *	Suspend an instance of the audio810 driver, in preparation for
9999484Sgarrett.damore@Sun.COM  *	sleep or hibernation.
10009484Sgarrett.damore@Sun.COM  *
10019484Sgarrett.damore@Sun.COM  * Arguments:
10029484Sgarrett.damore@Sun.COM  *	dev_info_t		*dip	Pointer to the device's dev_info struct
10039484Sgarrett.damore@Sun.COM  *
10049484Sgarrett.damore@Sun.COM  * Returns:
10059484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	The driver was suspended
10069484Sgarrett.damore@Sun.COM  */
10079484Sgarrett.damore@Sun.COM static int
audio810_suspend(dev_info_t * dip)10089484Sgarrett.damore@Sun.COM audio810_suspend(dev_info_t *dip)
10099484Sgarrett.damore@Sun.COM {
10109484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep;
10119484Sgarrett.damore@Sun.COM 
10129484Sgarrett.damore@Sun.COM 	statep = ddi_get_driver_private(dip);
10139484Sgarrett.damore@Sun.COM 	ASSERT(statep != NULL);
10149484Sgarrett.damore@Sun.COM 
1015*11936Sgdamore@opensolaris.org 	audio_dev_suspend(statep->adev);
10169484Sgarrett.damore@Sun.COM 
1017*11936Sgdamore@opensolaris.org 	/* stop DMA engines - should be redundant (paranoia) */
10189484Sgarrett.damore@Sun.COM 	audio810_stop_dma(statep);
10199484Sgarrett.damore@Sun.COM 
10209484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
10219484Sgarrett.damore@Sun.COM }
10229484Sgarrett.damore@Sun.COM 
10239484Sgarrett.damore@Sun.COM /*
10249484Sgarrett.damore@Sun.COM  * audio810_alloc_port()
10259484Sgarrett.damore@Sun.COM  *
10269484Sgarrett.damore@Sun.COM  * Description:
10279484Sgarrett.damore@Sun.COM  *	This routine allocates the DMA handles and the memory for the
10289484Sgarrett.damore@Sun.COM  *	DMA engines to use.  It also configures the BDL lists properly
10299484Sgarrett.damore@Sun.COM  *	for use.
10309484Sgarrett.damore@Sun.COM  *
10319484Sgarrett.damore@Sun.COM  * Arguments:
10329484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's devinfo
10339484Sgarrett.damore@Sun.COM  *
10349484Sgarrett.damore@Sun.COM  * Returns:
10359484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Registers successfully mapped
10369484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		Registers not successfully mapped
10379484Sgarrett.damore@Sun.COM  */
10389484Sgarrett.damore@Sun.COM static int
audio810_alloc_port(audio810_state_t * statep,int num,uint8_t nchan)10399484Sgarrett.damore@Sun.COM audio810_alloc_port(audio810_state_t *statep, int num, uint8_t nchan)
10409484Sgarrett.damore@Sun.COM {
10419484Sgarrett.damore@Sun.COM 	ddi_dma_cookie_t	cookie;
10429484Sgarrett.damore@Sun.COM 	uint_t			count;
10439484Sgarrett.damore@Sun.COM 	int			dir;
10449484Sgarrett.damore@Sun.COM 	unsigned		caps;
10459484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
10469484Sgarrett.damore@Sun.COM 	audio810_port_t		*port;
10479484Sgarrett.damore@Sun.COM 	int			rc;
10489484Sgarrett.damore@Sun.COM 	dev_info_t		*dip;
10499484Sgarrett.damore@Sun.COM 	i810_bd_entry_t		*bdentry;
10509484Sgarrett.damore@Sun.COM 
10519484Sgarrett.damore@Sun.COM 	adev = statep->adev;
10529484Sgarrett.damore@Sun.COM 	dip = statep->dip;
10539484Sgarrett.damore@Sun.COM 
10549484Sgarrett.damore@Sun.COM 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
10559484Sgarrett.damore@Sun.COM 	statep->ports[num] = port;
10569484Sgarrett.damore@Sun.COM 	port->statep = statep;
10579484Sgarrett.damore@Sun.COM 	port->nchan = nchan;
10589484Sgarrett.damore@Sun.COM 	port->num = num;
10599484Sgarrett.damore@Sun.COM 
10609484Sgarrett.damore@Sun.COM 	switch (num) {
10619484Sgarrett.damore@Sun.COM 	case I810_PCM_IN:
10629484Sgarrett.damore@Sun.COM 		dir = DDI_DMA_READ;
10639484Sgarrett.damore@Sun.COM 		caps = ENGINE_INPUT_CAP;
10649484Sgarrett.damore@Sun.COM 		port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
10659484Sgarrett.damore@Sun.COM 		port->regoff = I810_BASE_PCM_IN;
10669484Sgarrett.damore@Sun.COM 		break;
10679484Sgarrett.damore@Sun.COM 	case I810_PCM_OUT:
10689484Sgarrett.damore@Sun.COM 		dir = DDI_DMA_WRITE;
10699484Sgarrett.damore@Sun.COM 		caps = ENGINE_OUTPUT_CAP;
10709484Sgarrett.damore@Sun.COM 		port->sync_dir = DDI_DMA_SYNC_FORDEV;
10719484Sgarrett.damore@Sun.COM 		port->regoff = I810_BASE_PCM_OUT;
10729484Sgarrett.damore@Sun.COM 		break;
10739484Sgarrett.damore@Sun.COM 	default:
10749484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "bad port number (%d)!", num);
10759484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
10769484Sgarrett.damore@Sun.COM 	}
10779484Sgarrett.damore@Sun.COM 
10789484Sgarrett.damore@Sun.COM 	/*
10799484Sgarrett.damore@Sun.COM 	 * SiS 7012 swaps status and picb registers.
10809484Sgarrett.damore@Sun.COM 	 */
10819484Sgarrett.damore@Sun.COM 	if (statep->quirk == QUIRK_SIS7012) {
10829484Sgarrett.damore@Sun.COM 		port->stsoff = port->regoff + I810_OFFSET_PICB;
10839484Sgarrett.damore@Sun.COM 		port->picboff = port->regoff + I810_OFFSET_SR;
10849484Sgarrett.damore@Sun.COM 	} else {
10859484Sgarrett.damore@Sun.COM 		port->stsoff = port->regoff + I810_OFFSET_SR;
10869484Sgarrett.damore@Sun.COM 		port->picboff = port->regoff + I810_OFFSET_PICB;
10879484Sgarrett.damore@Sun.COM 	}
10889484Sgarrett.damore@Sun.COM 
10899484Sgarrett.damore@Sun.COM 	/*
1090*11936Sgdamore@opensolaris.org 	 * We use one big sample area.  The sample area must be larger
1091*11936Sgdamore@opensolaris.org 	 * than about 1.5 framework fragment sizes.  (Currently 480 *
1092*11936Sgdamore@opensolaris.org 	 * 1.5 = 720 frames.)  This is necessary to ensure that we
1093*11936Sgdamore@opensolaris.org 	 * don't have to involve an interrupt service routine on our
1094*11936Sgdamore@opensolaris.org 	 * own, to keep the last valid index updated reasonably.
10959484Sgarrett.damore@Sun.COM 	 */
1096*11936Sgdamore@opensolaris.org 	port->samp_frames = 4096;
1097*11936Sgdamore@opensolaris.org 	port->samp_size = port->samp_frames * port->nchan * sizeof (int16_t);
10989484Sgarrett.damore@Sun.COM 
10999484Sgarrett.damore@Sun.COM 	/* allocate dma handle */
11009484Sgarrett.damore@Sun.COM 	rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
11019484Sgarrett.damore@Sun.COM 	    NULL, &port->samp_dmah);
11029484Sgarrett.damore@Sun.COM 	if (rc != DDI_SUCCESS) {
11039484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
11049484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11059484Sgarrett.damore@Sun.COM 	}
11069484Sgarrett.damore@Sun.COM 	/* allocate DMA buffer */
11079484Sgarrett.damore@Sun.COM 	rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
11089484Sgarrett.damore@Sun.COM 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
11099484Sgarrett.damore@Sun.COM 	    &port->samp_size, &port->samp_acch);
11109484Sgarrett.damore@Sun.COM 	if (rc == DDI_FAILURE) {
11119484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "dma_mem_alloc (%d) failed: %d",
11129484Sgarrett.damore@Sun.COM 		    port->samp_size, rc);
11139484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11149484Sgarrett.damore@Sun.COM 	}
11159484Sgarrett.damore@Sun.COM 
11169484Sgarrett.damore@Sun.COM 	/* bind DMA buffer */
11179484Sgarrett.damore@Sun.COM 	rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
11189484Sgarrett.damore@Sun.COM 	    port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
11199484Sgarrett.damore@Sun.COM 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
11209484Sgarrett.damore@Sun.COM 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
11219484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev,
11229484Sgarrett.damore@Sun.COM 		    "ddi_dma_addr_bind_handle failed: %d", rc);
11239484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11249484Sgarrett.damore@Sun.COM 	}
11259484Sgarrett.damore@Sun.COM 	port->samp_paddr = cookie.dmac_address;
11269484Sgarrett.damore@Sun.COM 
11279484Sgarrett.damore@Sun.COM 	/*
11289484Sgarrett.damore@Sun.COM 	 * now, from here we allocate DMA memory for buffer descriptor list.
11299484Sgarrett.damore@Sun.COM 	 * we allocate adjacent DMA memory for all DMA engines.
11309484Sgarrett.damore@Sun.COM 	 */
11319484Sgarrett.damore@Sun.COM 	rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
11329484Sgarrett.damore@Sun.COM 	    NULL, &port->bdl_dmah);
11339484Sgarrett.damore@Sun.COM 	if (rc != DDI_SUCCESS) {
11349484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
11359484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11369484Sgarrett.damore@Sun.COM 	}
11379484Sgarrett.damore@Sun.COM 
11389484Sgarrett.damore@Sun.COM 	/*
11399484Sgarrett.damore@Sun.COM 	 * we allocate all buffer descriptors lists in continuous dma memory.
11409484Sgarrett.damore@Sun.COM 	 */
11419484Sgarrett.damore@Sun.COM 	port->bdl_size = sizeof (i810_bd_entry_t) * I810_BD_NUMS;
11429484Sgarrett.damore@Sun.COM 	rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
11439484Sgarrett.damore@Sun.COM 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
11449484Sgarrett.damore@Sun.COM 	    &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
11459484Sgarrett.damore@Sun.COM 	if (rc != DDI_SUCCESS) {
11469484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
11479484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11489484Sgarrett.damore@Sun.COM 	}
11499484Sgarrett.damore@Sun.COM 
11509484Sgarrett.damore@Sun.COM 	rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
11519484Sgarrett.damore@Sun.COM 	    port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
11529484Sgarrett.damore@Sun.COM 	    NULL, &cookie, &count);
11539484Sgarrett.damore@Sun.COM 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
11549484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "addr_bind_handle failed");
11559484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11569484Sgarrett.damore@Sun.COM 	}
11579484Sgarrett.damore@Sun.COM 	port->bdl_paddr = cookie.dmac_address;
11589484Sgarrett.damore@Sun.COM 
11599484Sgarrett.damore@Sun.COM 	/*
11609484Sgarrett.damore@Sun.COM 	 * Wire up the BD list.
11619484Sgarrett.damore@Sun.COM 	 */
11629484Sgarrett.damore@Sun.COM 	bdentry = (void *)port->bdl_kaddr;
11639484Sgarrett.damore@Sun.COM 	for (int i = 0; i < I810_BD_NUMS; i++) {
11649484Sgarrett.damore@Sun.COM 
11659484Sgarrett.damore@Sun.COM 		/* set base address of buffer */
1166*11936Sgdamore@opensolaris.org 		ddi_put32(port->bdl_acch, &bdentry->buf_base,
1167*11936Sgdamore@opensolaris.org 		    port->samp_paddr);
1168*11936Sgdamore@opensolaris.org 		/* SiS 7012 counts in bytes, all others in words */
11699484Sgarrett.damore@Sun.COM 		ddi_put16(port->bdl_acch, &bdentry->buf_len,
1170*11936Sgdamore@opensolaris.org 		    statep->quirk == QUIRK_SIS7012 ? port->samp_size :
1171*11936Sgdamore@opensolaris.org 		    port->samp_size / 2);
1172*11936Sgdamore@opensolaris.org 		ddi_put16(port->bdl_acch, &bdentry->buf_cmd, BUF_CMD_BUP);
1173*11936Sgdamore@opensolaris.org 
11749484Sgarrett.damore@Sun.COM 		bdentry++;
11759484Sgarrett.damore@Sun.COM 	}
11769484Sgarrett.damore@Sun.COM 	(void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
11779484Sgarrett.damore@Sun.COM 
11789484Sgarrett.damore@Sun.COM 	port->engine = audio_engine_alloc(&audio810_engine_ops, caps);
11799484Sgarrett.damore@Sun.COM 	if (port->engine == NULL) {
11809484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "audio_engine_alloc failed");
11819484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
11829484Sgarrett.damore@Sun.COM 	}
11839484Sgarrett.damore@Sun.COM 
11849484Sgarrett.damore@Sun.COM 	audio_engine_set_private(port->engine, port);
11859484Sgarrett.damore@Sun.COM 	audio_dev_add_engine(adev, port->engine);
11869484Sgarrett.damore@Sun.COM 
11879484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
11889484Sgarrett.damore@Sun.COM }
11899484Sgarrett.damore@Sun.COM 
11909484Sgarrett.damore@Sun.COM /*
11919484Sgarrett.damore@Sun.COM  * audio810_free_port()
11929484Sgarrett.damore@Sun.COM  *
11939484Sgarrett.damore@Sun.COM  * Description:
11949484Sgarrett.damore@Sun.COM  *	This routine unbinds the DMA cookies, frees the DMA buffers,
11959484Sgarrett.damore@Sun.COM  *	deallocates the DMA handles.
11969484Sgarrett.damore@Sun.COM  *
11979484Sgarrett.damore@Sun.COM  * Arguments:
11989484Sgarrett.damore@Sun.COM  *	audio810_port_t	*port	The port structure for a DMA engine.
11999484Sgarrett.damore@Sun.COM  */
12009484Sgarrett.damore@Sun.COM static void
audio810_free_port(audio810_port_t * port)12019484Sgarrett.damore@Sun.COM audio810_free_port(audio810_port_t *port)
12029484Sgarrett.damore@Sun.COM {
12039484Sgarrett.damore@Sun.COM 	if (port == NULL)
12049484Sgarrett.damore@Sun.COM 		return;
12059484Sgarrett.damore@Sun.COM 
12069484Sgarrett.damore@Sun.COM 	if (port->engine) {
12079484Sgarrett.damore@Sun.COM 		audio_dev_remove_engine(port->statep->adev, port->engine);
12089484Sgarrett.damore@Sun.COM 		audio_engine_free(port->engine);
12099484Sgarrett.damore@Sun.COM 	}
12109484Sgarrett.damore@Sun.COM 	if (port->bdl_paddr) {
12119484Sgarrett.damore@Sun.COM 		(void) ddi_dma_unbind_handle(port->bdl_dmah);
12129484Sgarrett.damore@Sun.COM 	}
12139484Sgarrett.damore@Sun.COM 	if (port->bdl_acch) {
12149484Sgarrett.damore@Sun.COM 		ddi_dma_mem_free(&port->bdl_acch);
12159484Sgarrett.damore@Sun.COM 	}
12169484Sgarrett.damore@Sun.COM 	if (port->bdl_dmah) {
12179484Sgarrett.damore@Sun.COM 		ddi_dma_free_handle(&port->bdl_dmah);
12189484Sgarrett.damore@Sun.COM 	}
12199484Sgarrett.damore@Sun.COM 	if (port->samp_paddr) {
12209484Sgarrett.damore@Sun.COM 		(void) ddi_dma_unbind_handle(port->samp_dmah);
12219484Sgarrett.damore@Sun.COM 	}
12229484Sgarrett.damore@Sun.COM 	if (port->samp_acch) {
12239484Sgarrett.damore@Sun.COM 		ddi_dma_mem_free(&port->samp_acch);
12249484Sgarrett.damore@Sun.COM 	}
12259484Sgarrett.damore@Sun.COM 	if (port->samp_dmah) {
12269484Sgarrett.damore@Sun.COM 		ddi_dma_free_handle(&port->samp_dmah);
12279484Sgarrett.damore@Sun.COM 	}
12289484Sgarrett.damore@Sun.COM 	kmem_free(port, sizeof (*port));
12299484Sgarrett.damore@Sun.COM }
12309484Sgarrett.damore@Sun.COM 
12319484Sgarrett.damore@Sun.COM /*
12329484Sgarrett.damore@Sun.COM  * audio810_map_regs()
12339484Sgarrett.damore@Sun.COM  *
12349484Sgarrett.damore@Sun.COM  * Description:
12359484Sgarrett.damore@Sun.COM  *	The registers are mapped in.
12369484Sgarrett.damore@Sun.COM  *
12379484Sgarrett.damore@Sun.COM  * Arguments:
12389484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's devinfo
12399484Sgarrett.damore@Sun.COM  *
12409484Sgarrett.damore@Sun.COM  * Returns:
12419484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Registers successfully mapped
12429484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		Registers not successfully mapped
12439484Sgarrett.damore@Sun.COM  */
12449484Sgarrett.damore@Sun.COM static int
audio810_map_regs(dev_info_t * dip,audio810_state_t * statep)12459484Sgarrett.damore@Sun.COM audio810_map_regs(dev_info_t *dip, audio810_state_t *statep)
12469484Sgarrett.damore@Sun.COM {
12479484Sgarrett.damore@Sun.COM 	uint_t			nregs = 0;
12489484Sgarrett.damore@Sun.COM 	int			*regs_list;
12499484Sgarrett.damore@Sun.COM 	int			i;
12509484Sgarrett.damore@Sun.COM 	int			pciBar1 = 0;
12519484Sgarrett.damore@Sun.COM 	int			pciBar2 = 0;
12529484Sgarrett.damore@Sun.COM 	int			pciBar3 = 0;
12539484Sgarrett.damore@Sun.COM 	int			pciBar4 = 0;
12549484Sgarrett.damore@Sun.COM 
12559484Sgarrett.damore@Sun.COM 	/* check the "reg" property to get the length of memory-mapped I/O */
12569484Sgarrett.damore@Sun.COM 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
12579484Sgarrett.damore@Sun.COM 	    "reg", (int **)&regs_list, &nregs) != DDI_PROP_SUCCESS) {
12589484Sgarrett.damore@Sun.COM 		audio_dev_warn(statep->adev, "inquire regs property failed");
12599484Sgarrett.damore@Sun.COM 		goto error;
12609484Sgarrett.damore@Sun.COM 	}
12619484Sgarrett.damore@Sun.COM 	/*
12629484Sgarrett.damore@Sun.COM 	 * Some hardwares, such as Intel ICH0/ICH and AMD 8111, use PCI 0x10
12639484Sgarrett.damore@Sun.COM 	 * and 0x14 BAR separately for native audio mixer BAR and native bus
12649484Sgarrett.damore@Sun.COM 	 * mastering BAR. More advanced hardwares, such as Intel ICH4 and ICH5,
12659484Sgarrett.damore@Sun.COM 	 * support PCI memory BAR, via PCI 0x18 and 0x1C BAR, that allows for
12669484Sgarrett.damore@Sun.COM 	 * higher performance access to the controller register. All features
12679484Sgarrett.damore@Sun.COM 	 * can be accessed via this BAR making the I/O BAR (PCI 0x10 and 0x14
12689484Sgarrett.damore@Sun.COM 	 * BAR) capabilities obsolete. However, these controller maintain the
12699484Sgarrett.damore@Sun.COM 	 * I/O BAR capability to allow for the reuse of legacy code maintaining
12709484Sgarrett.damore@Sun.COM 	 * backward compatibility. The I/O BAR is disabled unless system BIOS
12719484Sgarrett.damore@Sun.COM 	 * enables the simultaneous backward compatible capability on the 0x41
12729484Sgarrett.damore@Sun.COM 	 * register.
12739484Sgarrett.damore@Sun.COM 	 *
12749484Sgarrett.damore@Sun.COM 	 * When I/O BAR is enabled, the value of "reg" property should be like
12759484Sgarrett.damore@Sun.COM 	 * this,
12769484Sgarrett.damore@Sun.COM 	 *	phys_hi   phys_mid  phys_lo   size_hi   size_lo
12779484Sgarrett.damore@Sun.COM 	 * --------------------------------------------------------
12789484Sgarrett.damore@Sun.COM 	 *	0000fd00  00000000  00000000  00000000  00000000
12799484Sgarrett.damore@Sun.COM 	 *	0100fd10  00000000  00000000  00000000  00000100
12809484Sgarrett.damore@Sun.COM 	 *	0100fd14  00000000  00000000  00000000  00000040
12819484Sgarrett.damore@Sun.COM 	 *	0200fd18  00000000  00000000  00000000  00000200
12829484Sgarrett.damore@Sun.COM 	 *	0200fd1c  00000000  00000000  00000000  00000100
12839484Sgarrett.damore@Sun.COM 	 *
12849484Sgarrett.damore@Sun.COM 	 * When I/O BAR is disabled, the "reg" property of the device node does
12859484Sgarrett.damore@Sun.COM 	 * not consist of the description for the I/O BAR. The following example
12869484Sgarrett.damore@Sun.COM 	 * illustrates the vaule of "reg" property,
12879484Sgarrett.damore@Sun.COM 	 *
12889484Sgarrett.damore@Sun.COM 	 *	phys_hi   phys_mid  phys_lo   size_hi   size_lo
12899484Sgarrett.damore@Sun.COM 	 * --------------------------------------------------------
12909484Sgarrett.damore@Sun.COM 	 *	0000fd00  00000000  00000000  00000000  00000000
12919484Sgarrett.damore@Sun.COM 	 *	0200fd18  00000000  00000000  00000000  00000200
12929484Sgarrett.damore@Sun.COM 	 *	0200fd1c  00000000  00000000  00000000  00000100
12939484Sgarrett.damore@Sun.COM 	 *
12949484Sgarrett.damore@Sun.COM 	 * If the hardware has memory-mapped I/O access, first try to use
12959484Sgarrett.damore@Sun.COM 	 * this facility, otherwise we will try I/O access.
12969484Sgarrett.damore@Sun.COM 	 */
12979484Sgarrett.damore@Sun.COM 	for (i = 1; i < nregs/I810_INTS_PER_REG_PROP; i++) {
12989484Sgarrett.damore@Sun.COM 		switch (regs_list[I810_INTS_PER_REG_PROP * i] & 0x000000ff) {
12999484Sgarrett.damore@Sun.COM 			case 0x10:
13009484Sgarrett.damore@Sun.COM 				pciBar1 = i;
13019484Sgarrett.damore@Sun.COM 				break;
13029484Sgarrett.damore@Sun.COM 			case 0x14:
13039484Sgarrett.damore@Sun.COM 				pciBar2 = i;
13049484Sgarrett.damore@Sun.COM 				break;
13059484Sgarrett.damore@Sun.COM 			case 0x18:
13069484Sgarrett.damore@Sun.COM 				pciBar3 = i;
13079484Sgarrett.damore@Sun.COM 				break;
13089484Sgarrett.damore@Sun.COM 			case 0x1c:
13099484Sgarrett.damore@Sun.COM 				pciBar4 = i;
13109484Sgarrett.damore@Sun.COM 				break;
13119484Sgarrett.damore@Sun.COM 			default:	/* we don't care others */
13129484Sgarrett.damore@Sun.COM 				break;
13139484Sgarrett.damore@Sun.COM 		}
13149484Sgarrett.damore@Sun.COM 	}
13159484Sgarrett.damore@Sun.COM 
13169484Sgarrett.damore@Sun.COM 	if ((pciBar3 != 0) && (pciBar4 != 0)) {
13179484Sgarrett.damore@Sun.COM 		/* map audio mixer registers */
13189484Sgarrett.damore@Sun.COM 		if ((ddi_regs_map_setup(dip, pciBar3, &statep->am_regs_base, 0,
13199484Sgarrett.damore@Sun.COM 		    0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
13209484Sgarrett.damore@Sun.COM 			audio_dev_warn(statep->adev,
13219484Sgarrett.damore@Sun.COM 			    "memory am mapping failed");
13229484Sgarrett.damore@Sun.COM 			goto error;
13239484Sgarrett.damore@Sun.COM 		}
13249484Sgarrett.damore@Sun.COM 
13259484Sgarrett.damore@Sun.COM 		/* map bus master register */
13269484Sgarrett.damore@Sun.COM 		if ((ddi_regs_map_setup(dip, pciBar4, &statep->bm_regs_base, 0,
13279484Sgarrett.damore@Sun.COM 		    0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
13289484Sgarrett.damore@Sun.COM 			audio_dev_warn(statep->adev,
13299484Sgarrett.damore@Sun.COM 			    "memory bm mapping failed");
13309484Sgarrett.damore@Sun.COM 			goto error;
13319484Sgarrett.damore@Sun.COM 		}
13329484Sgarrett.damore@Sun.COM 
13339484Sgarrett.damore@Sun.COM 	} else if ((pciBar1 != 0) && (pciBar2 != 0)) {
13349484Sgarrett.damore@Sun.COM 		/* map audio mixer registers */
13359484Sgarrett.damore@Sun.COM 		if ((ddi_regs_map_setup(dip, pciBar1, &statep->am_regs_base, 0,
13369484Sgarrett.damore@Sun.COM 		    0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
13379484Sgarrett.damore@Sun.COM 			audio_dev_warn(statep->adev, "I/O am mapping failed");
13389484Sgarrett.damore@Sun.COM 			goto error;
13399484Sgarrett.damore@Sun.COM 		}
13409484Sgarrett.damore@Sun.COM 
13419484Sgarrett.damore@Sun.COM 		/* map bus master register */
13429484Sgarrett.damore@Sun.COM 		if ((ddi_regs_map_setup(dip, pciBar2, &statep->bm_regs_base, 0,
13439484Sgarrett.damore@Sun.COM 		    0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
13449484Sgarrett.damore@Sun.COM 			audio_dev_warn(statep->adev, "I/O bm mapping failed");
13459484Sgarrett.damore@Sun.COM 			goto error;
13469484Sgarrett.damore@Sun.COM 		}
13479484Sgarrett.damore@Sun.COM 	} else {
13489484Sgarrett.damore@Sun.COM 		audio_dev_warn(statep->adev, "map_regs() pci BAR error");
13499484Sgarrett.damore@Sun.COM 		goto error;
13509484Sgarrett.damore@Sun.COM 	}
13519484Sgarrett.damore@Sun.COM 
13529484Sgarrett.damore@Sun.COM 	ddi_prop_free(regs_list);
13539484Sgarrett.damore@Sun.COM 
13549484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
13559484Sgarrett.damore@Sun.COM 
13569484Sgarrett.damore@Sun.COM error:
13579484Sgarrett.damore@Sun.COM 	if (nregs > 0) {
13589484Sgarrett.damore@Sun.COM 		ddi_prop_free(regs_list);
13599484Sgarrett.damore@Sun.COM 	}
13609484Sgarrett.damore@Sun.COM 	audio810_unmap_regs(statep);
13619484Sgarrett.damore@Sun.COM 
13629484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
13639484Sgarrett.damore@Sun.COM }
13649484Sgarrett.damore@Sun.COM 
13659484Sgarrett.damore@Sun.COM /*
13669484Sgarrett.damore@Sun.COM  * audio810_unmap_regs()
13679484Sgarrett.damore@Sun.COM  *
13689484Sgarrett.damore@Sun.COM  * Description:
13699484Sgarrett.damore@Sun.COM  *	This routine unmaps control registers.
13709484Sgarrett.damore@Sun.COM  *
13719484Sgarrett.damore@Sun.COM  * Arguments:
13729484Sgarrett.damore@Sun.COM  *	audio810_state_t	*state	The device's state structure
13739484Sgarrett.damore@Sun.COM  */
13749484Sgarrett.damore@Sun.COM static void
audio810_unmap_regs(audio810_state_t * statep)13759484Sgarrett.damore@Sun.COM audio810_unmap_regs(audio810_state_t *statep)
13769484Sgarrett.damore@Sun.COM {
13779484Sgarrett.damore@Sun.COM 	if (statep->bm_regs_handle) {
13789484Sgarrett.damore@Sun.COM 		ddi_regs_map_free(&statep->bm_regs_handle);
13799484Sgarrett.damore@Sun.COM 	}
13809484Sgarrett.damore@Sun.COM 
13819484Sgarrett.damore@Sun.COM 	if (statep->am_regs_handle) {
13829484Sgarrett.damore@Sun.COM 		ddi_regs_map_free(&statep->am_regs_handle);
13839484Sgarrett.damore@Sun.COM 	}
13849484Sgarrett.damore@Sun.COM }
13859484Sgarrett.damore@Sun.COM 
13869484Sgarrett.damore@Sun.COM /*
13879484Sgarrett.damore@Sun.COM  * audio810_chip_init()
13889484Sgarrett.damore@Sun.COM  *
13899484Sgarrett.damore@Sun.COM  * Description:
1390*11936Sgdamore@opensolaris.org  *	This routine initializes the audio controller.
13919484Sgarrett.damore@Sun.COM  *
13929484Sgarrett.damore@Sun.COM  * Arguments:
13939484Sgarrett.damore@Sun.COM  *	audio810_state_t	*state		The device's state structure
13949484Sgarrett.damore@Sun.COM  *
13959484Sgarrett.damore@Sun.COM  * Returns:
13969484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	The hardware was initialized properly
13979484Sgarrett.damore@Sun.COM  *	DDI_FAILURE	The hardware couldn't be initialized properly
13989484Sgarrett.damore@Sun.COM  */
13999484Sgarrett.damore@Sun.COM static int
audio810_chip_init(audio810_state_t * statep)14009484Sgarrett.damore@Sun.COM audio810_chip_init(audio810_state_t *statep)
14019484Sgarrett.damore@Sun.COM {
14029484Sgarrett.damore@Sun.COM 	uint32_t	gcr;
14039484Sgarrett.damore@Sun.COM 	uint32_t	gsr;
14049484Sgarrett.damore@Sun.COM 	uint32_t	codec_ready;
14059484Sgarrett.damore@Sun.COM 	int 		loop;
14069484Sgarrett.damore@Sun.COM 	clock_t		ticks;
14079484Sgarrett.damore@Sun.COM 
14089484Sgarrett.damore@Sun.COM 	gcr = I810_BM_GET32(I810_REG_GCR);
14099484Sgarrett.damore@Sun.COM 	ticks = drv_usectohz(100);
14109484Sgarrett.damore@Sun.COM 
14119484Sgarrett.damore@Sun.COM 	/*
14129484Sgarrett.damore@Sun.COM 	 * Clear the channels bits for now.  We'll set them later in
14139484Sgarrett.damore@Sun.COM 	 * reset port.
14149484Sgarrett.damore@Sun.COM 	 */
14159484Sgarrett.damore@Sun.COM 	if (statep->quirk == QUIRK_SIS7012) {
14169484Sgarrett.damore@Sun.COM 		gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_SIS_CHANNELS_MASK);
14179484Sgarrett.damore@Sun.COM 	} else {
14189484Sgarrett.damore@Sun.COM 		gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_CHANNELS_MASK);
14199484Sgarrett.damore@Sun.COM 	}
14209484Sgarrett.damore@Sun.COM 
14219484Sgarrett.damore@Sun.COM 	/*
14229484Sgarrett.damore@Sun.COM 	 * Datasheet(ICH5, document number of Intel: 252751-001):
14239484Sgarrett.damore@Sun.COM 	 * 3.6.5.5(page 37)
14249484Sgarrett.damore@Sun.COM 	 * 	if reset bit(bit1) is "0", driver must set it
14259484Sgarrett.damore@Sun.COM 	 * 	to "1" to de-assert the AC_RESET# signal in AC
14269484Sgarrett.damore@Sun.COM 	 * 	link, thus completing a cold reset. But if the
14279484Sgarrett.damore@Sun.COM 	 * 	bit is "1", then a warm reset is required.
14289484Sgarrett.damore@Sun.COM 	 */
14299484Sgarrett.damore@Sun.COM 	gcr |= (gcr & I810_GCR_COLD_RST) == 0 ?
14309484Sgarrett.damore@Sun.COM 	    I810_GCR_COLD_RST:I810_GCR_WARM_RST;
14319484Sgarrett.damore@Sun.COM 	I810_BM_PUT32(I810_REG_GCR, gcr);
14329484Sgarrett.damore@Sun.COM 
14339484Sgarrett.damore@Sun.COM 	/* according AC'97 spec, wait for codec reset */
14349484Sgarrett.damore@Sun.COM 	for (loop = 6000; --loop >= 0; ) {
14359484Sgarrett.damore@Sun.COM 		delay(ticks);
14369484Sgarrett.damore@Sun.COM 		gcr = I810_BM_GET32(I810_REG_GCR);
14379484Sgarrett.damore@Sun.COM 		if ((gcr & I810_GCR_WARM_RST) == 0) {
14389484Sgarrett.damore@Sun.COM 			break;
14399484Sgarrett.damore@Sun.COM 		}
14409484Sgarrett.damore@Sun.COM 	}
14419484Sgarrett.damore@Sun.COM 
14429484Sgarrett.damore@Sun.COM 	/* codec reset failed */
14439484Sgarrett.damore@Sun.COM 	if (loop < 0) {
14449484Sgarrett.damore@Sun.COM 		audio_dev_warn(statep->adev, "Failed to reset codec");
14459484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
14469484Sgarrett.damore@Sun.COM 	}
14479484Sgarrett.damore@Sun.COM 
14489484Sgarrett.damore@Sun.COM 	/*
14499484Sgarrett.damore@Sun.COM 	 * Wait for codec ready. The hardware can provide the state of
14509484Sgarrett.damore@Sun.COM 	 * codec ready bit on SDATA_IN[0], SDATA_IN[1] or SDATA_IN[2]
14519484Sgarrett.damore@Sun.COM 	 */
14529484Sgarrett.damore@Sun.COM 	codec_ready =
14539484Sgarrett.damore@Sun.COM 	    I810_GSR_PRI_READY | I810_GSR_SEC_READY | I810_GSR_TRI_READY;
14549484Sgarrett.damore@Sun.COM 	for (loop = 7000; --loop >= 0; ) {
14559484Sgarrett.damore@Sun.COM 		delay(ticks);
14569484Sgarrett.damore@Sun.COM 		gsr = I810_BM_GET32(I810_REG_GSR);
14579484Sgarrett.damore@Sun.COM 		if ((gsr & codec_ready) != 0) {
14589484Sgarrett.damore@Sun.COM 			break;
14599484Sgarrett.damore@Sun.COM 		}
14609484Sgarrett.damore@Sun.COM 	}
14619484Sgarrett.damore@Sun.COM 	if (loop < 0) {
14629484Sgarrett.damore@Sun.COM 		audio_dev_warn(statep->adev, "No codec ready signal received");
14639484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
14649484Sgarrett.damore@Sun.COM 	}
14659484Sgarrett.damore@Sun.COM 
14669484Sgarrett.damore@Sun.COM 	/*
14679484Sgarrett.damore@Sun.COM 	 * put the audio controller into quiet state, everything off
14689484Sgarrett.damore@Sun.COM 	 */
14699484Sgarrett.damore@Sun.COM 	audio810_stop_dma(statep);
14709484Sgarrett.damore@Sun.COM 
14719484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
14729484Sgarrett.damore@Sun.COM }
14739484Sgarrett.damore@Sun.COM 
14749484Sgarrett.damore@Sun.COM /*
1475*11936Sgdamore@opensolaris.org  * audio810_set_channels()
1476*11936Sgdamore@opensolaris.org  *
1477*11936Sgdamore@opensolaris.org  * Description:
1478*11936Sgdamore@opensolaris.org  *	This routine initializes the multichannel configuration.
1479*11936Sgdamore@opensolaris.org  *
1480*11936Sgdamore@opensolaris.org  * Arguments:
1481*11936Sgdamore@opensolaris.org  *	audio810_state_t	*state		The device's state structure
1482*11936Sgdamore@opensolaris.org  */
1483*11936Sgdamore@opensolaris.org static void
audio810_set_channels(audio810_state_t * statep)1484*11936Sgdamore@opensolaris.org audio810_set_channels(audio810_state_t *statep)
1485*11936Sgdamore@opensolaris.org {
1486*11936Sgdamore@opensolaris.org 	uint32_t	gcr;
1487*11936Sgdamore@opensolaris.org 
1488*11936Sgdamore@opensolaris.org 	/*
1489*11936Sgdamore@opensolaris.org 	 * Configure multi-channel.
1490*11936Sgdamore@opensolaris.org 	 */
1491*11936Sgdamore@opensolaris.org 	if (statep->quirk == QUIRK_SIS7012) {
1492*11936Sgdamore@opensolaris.org 		/*
1493*11936Sgdamore@opensolaris.org 		 * SiS 7012 needs its own special multichannel config.
1494*11936Sgdamore@opensolaris.org 		 */
1495*11936Sgdamore@opensolaris.org 		gcr = I810_BM_GET32(I810_REG_GCR);
1496*11936Sgdamore@opensolaris.org 		gcr &= ~I810_GCR_SIS_CHANNELS_MASK;
1497*11936Sgdamore@opensolaris.org 		I810_BM_PUT32(I810_REG_GCR, gcr);
1498*11936Sgdamore@opensolaris.org 		delay(drv_usectohz(50000));	/* 50 msec */
1499*11936Sgdamore@opensolaris.org 
1500*11936Sgdamore@opensolaris.org 		switch (statep->maxch) {
1501*11936Sgdamore@opensolaris.org 		case 2:
1502*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_SIS_2_CHANNELS;
1503*11936Sgdamore@opensolaris.org 			break;
1504*11936Sgdamore@opensolaris.org 		case 4:
1505*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_SIS_4_CHANNELS;
1506*11936Sgdamore@opensolaris.org 			break;
1507*11936Sgdamore@opensolaris.org 		case 6:
1508*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_SIS_6_CHANNELS;
1509*11936Sgdamore@opensolaris.org 			break;
1510*11936Sgdamore@opensolaris.org 		}
1511*11936Sgdamore@opensolaris.org 		I810_BM_PUT32(I810_REG_GCR, gcr);
1512*11936Sgdamore@opensolaris.org 		delay(drv_usectohz(50000));	/* 50 msec */
1513*11936Sgdamore@opensolaris.org 	} else {
1514*11936Sgdamore@opensolaris.org 
1515*11936Sgdamore@opensolaris.org 		/*
1516*11936Sgdamore@opensolaris.org 		 * All other devices work the same.
1517*11936Sgdamore@opensolaris.org 		 */
1518*11936Sgdamore@opensolaris.org 		gcr = I810_BM_GET32(I810_REG_GCR);
1519*11936Sgdamore@opensolaris.org 		gcr &= ~I810_GCR_CHANNELS_MASK;
1520*11936Sgdamore@opensolaris.org 
1521*11936Sgdamore@opensolaris.org 		I810_BM_PUT32(I810_REG_GCR, gcr);
1522*11936Sgdamore@opensolaris.org 		delay(drv_usectohz(50000));	/* 50 msec */
1523*11936Sgdamore@opensolaris.org 
1524*11936Sgdamore@opensolaris.org 		switch (statep->maxch) {
1525*11936Sgdamore@opensolaris.org 		case 2:
1526*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_2_CHANNELS;
1527*11936Sgdamore@opensolaris.org 			break;
1528*11936Sgdamore@opensolaris.org 		case 4:
1529*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_4_CHANNELS;
1530*11936Sgdamore@opensolaris.org 			break;
1531*11936Sgdamore@opensolaris.org 		case 6:
1532*11936Sgdamore@opensolaris.org 			gcr |= I810_GCR_6_CHANNELS;
1533*11936Sgdamore@opensolaris.org 			break;
1534*11936Sgdamore@opensolaris.org 		}
1535*11936Sgdamore@opensolaris.org 		I810_BM_PUT32(I810_REG_GCR, gcr);
1536*11936Sgdamore@opensolaris.org 		delay(drv_usectohz(50000));	/* 50 msec */
1537*11936Sgdamore@opensolaris.org 	}
1538*11936Sgdamore@opensolaris.org }
1539*11936Sgdamore@opensolaris.org 
1540*11936Sgdamore@opensolaris.org /*
15419484Sgarrett.damore@Sun.COM  * audio810_stop_dma()
15429484Sgarrett.damore@Sun.COM  *
15439484Sgarrett.damore@Sun.COM  * Description:
15449484Sgarrett.damore@Sun.COM  *	This routine is used to put each DMA engine into the quiet state.
15459484Sgarrett.damore@Sun.COM  *
15469484Sgarrett.damore@Sun.COM  * Arguments:
15479484Sgarrett.damore@Sun.COM  *	audio810_state_t	*state		The device's state structure
15489484Sgarrett.damore@Sun.COM  */
15499484Sgarrett.damore@Sun.COM static void
audio810_stop_dma(audio810_state_t * statep)15509484Sgarrett.damore@Sun.COM audio810_stop_dma(audio810_state_t *statep)
15519484Sgarrett.damore@Sun.COM {
15529484Sgarrett.damore@Sun.COM 	if (statep->bm_regs_handle == NULL) {
15539484Sgarrett.damore@Sun.COM 		return;
15549484Sgarrett.damore@Sun.COM 	}
15559484Sgarrett.damore@Sun.COM 	/* pause bus master (needed for the following reset register) */
15569484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, 0x0);
15579484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, 0x0);
15589484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, 0x0);
15599484Sgarrett.damore@Sun.COM 
15609484Sgarrett.damore@Sun.COM 	/* and then reset the bus master registers for a three DMA engines */
15619484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, I810_BM_CR_RST);
15629484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, I810_BM_CR_RST);
15639484Sgarrett.damore@Sun.COM 	I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, I810_BM_CR_RST);
15649484Sgarrett.damore@Sun.COM }
15659484Sgarrett.damore@Sun.COM 
15669484Sgarrett.damore@Sun.COM 
15679484Sgarrett.damore@Sun.COM /*
15689484Sgarrett.damore@Sun.COM  * audio810_codec_sync()
15699484Sgarrett.damore@Sun.COM  *
15709484Sgarrett.damore@Sun.COM  * Description:
15719484Sgarrett.damore@Sun.COM  *	Serialize access to the AC97 audio mixer registers.
15729484Sgarrett.damore@Sun.COM  *
15739484Sgarrett.damore@Sun.COM  * Arguments:
15749484Sgarrett.damore@Sun.COM  *	audio810_state_t	*state		The device's state structure
15759484Sgarrett.damore@Sun.COM  *
15769484Sgarrett.damore@Sun.COM  * Returns:
15779484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Ready for an I/O access to the codec
15789484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		An I/O access is currently in progress, can't
15799484Sgarrett.damore@Sun.COM  *				perform another I/O access.
15809484Sgarrett.damore@Sun.COM  */
15819484Sgarrett.damore@Sun.COM static int
audio810_codec_sync(audio810_state_t * statep)15829484Sgarrett.damore@Sun.COM audio810_codec_sync(audio810_state_t *statep)
15839484Sgarrett.damore@Sun.COM {
15849484Sgarrett.damore@Sun.COM 	int 		i;
15859484Sgarrett.damore@Sun.COM 	uint16_t	casr;
15869484Sgarrett.damore@Sun.COM 
15879484Sgarrett.damore@Sun.COM 	for (i = 0; i < 300; i++) {
15889484Sgarrett.damore@Sun.COM 		casr = I810_BM_GET8(I810_REG_CASR);
15899484Sgarrett.damore@Sun.COM 		if ((casr & 1) == 0) {
15909484Sgarrett.damore@Sun.COM 			return (DDI_SUCCESS);
15919484Sgarrett.damore@Sun.COM 		}
15929484Sgarrett.damore@Sun.COM 		drv_usecwait(10);
15939484Sgarrett.damore@Sun.COM 	}
15949484Sgarrett.damore@Sun.COM 
15959484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
15969484Sgarrett.damore@Sun.COM }
15979484Sgarrett.damore@Sun.COM 
15989484Sgarrett.damore@Sun.COM /*
15999484Sgarrett.damore@Sun.COM  * audio810_write_ac97()
16009484Sgarrett.damore@Sun.COM  *
16019484Sgarrett.damore@Sun.COM  * Description:
16029484Sgarrett.damore@Sun.COM  *	Set the specific AC97 Codec register.
16039484Sgarrett.damore@Sun.COM  *
16049484Sgarrett.damore@Sun.COM  * Arguments:
16059484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
16069484Sgarrett.damore@Sun.COM  *	uint8_t		reg		AC97 register number
16079484Sgarrett.damore@Sun.COM  *	uint16_t	data		The data want to be set
16089484Sgarrett.damore@Sun.COM  */
16099484Sgarrett.damore@Sun.COM static void
audio810_write_ac97(void * arg,uint8_t reg,uint16_t data)16109484Sgarrett.damore@Sun.COM audio810_write_ac97(void *arg, uint8_t reg, uint16_t data)
16119484Sgarrett.damore@Sun.COM {
16129484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep = arg;
16139484Sgarrett.damore@Sun.COM 
16149484Sgarrett.damore@Sun.COM 	if (audio810_codec_sync(statep) == DDI_SUCCESS) {
16159484Sgarrett.damore@Sun.COM 		I810_AM_PUT16(reg, data);
16169484Sgarrett.damore@Sun.COM 	}
16179484Sgarrett.damore@Sun.COM 
16189484Sgarrett.damore@Sun.COM 	(void) audio810_read_ac97(statep, reg);
16199484Sgarrett.damore@Sun.COM }
16209484Sgarrett.damore@Sun.COM 
16219484Sgarrett.damore@Sun.COM /*
16229484Sgarrett.damore@Sun.COM  * audio810_read_ac97()
16239484Sgarrett.damore@Sun.COM  *
16249484Sgarrett.damore@Sun.COM  * Description:
16259484Sgarrett.damore@Sun.COM  *	Get the specific AC97 Codec register.
16269484Sgarrett.damore@Sun.COM  *
16279484Sgarrett.damore@Sun.COM  * Arguments:
16289484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
16299484Sgarrett.damore@Sun.COM  *	uint8_t		reg		AC97 register number
16309484Sgarrett.damore@Sun.COM  *
16319484Sgarrett.damore@Sun.COM  * Returns:
16329484Sgarrett.damore@Sun.COM  *	The register value.
16339484Sgarrett.damore@Sun.COM  */
16349484Sgarrett.damore@Sun.COM static uint16_t
audio810_read_ac97(void * arg,uint8_t reg)16359484Sgarrett.damore@Sun.COM audio810_read_ac97(void *arg, uint8_t reg)
16369484Sgarrett.damore@Sun.COM {
16379484Sgarrett.damore@Sun.COM 	audio810_state_t	*statep = arg;
16389484Sgarrett.damore@Sun.COM 	uint16_t		val = 0xffff;
16399484Sgarrett.damore@Sun.COM 
16409484Sgarrett.damore@Sun.COM 	if (audio810_codec_sync(statep) == DDI_SUCCESS) {
16419484Sgarrett.damore@Sun.COM 		val = I810_AM_GET16(reg);
16429484Sgarrett.damore@Sun.COM 	}
16439484Sgarrett.damore@Sun.COM 	return (val);
16449484Sgarrett.damore@Sun.COM }
16459484Sgarrett.damore@Sun.COM 
16469484Sgarrett.damore@Sun.COM /*
16479484Sgarrett.damore@Sun.COM  * audio810_destroy()
16489484Sgarrett.damore@Sun.COM  *
16499484Sgarrett.damore@Sun.COM  * Description:
16509484Sgarrett.damore@Sun.COM  *	This routine releases all resources held by the device instance,
16519484Sgarrett.damore@Sun.COM  *	as part of either detach or a failure in attach.
16529484Sgarrett.damore@Sun.COM  *
16539484Sgarrett.damore@Sun.COM  * Arguments:
16549484Sgarrett.damore@Sun.COM  *	audio810_state_t	*state	The device soft state.
16559484Sgarrett.damore@Sun.COM  */
16569484Sgarrett.damore@Sun.COM void
audio810_destroy(audio810_state_t * statep)16579484Sgarrett.damore@Sun.COM audio810_destroy(audio810_state_t *statep)
16589484Sgarrett.damore@Sun.COM {
16599484Sgarrett.damore@Sun.COM 	/* stop DMA engines */
16609484Sgarrett.damore@Sun.COM 	audio810_stop_dma(statep);
16619484Sgarrett.damore@Sun.COM 
16629484Sgarrett.damore@Sun.COM 	for (int i = 0; i < I810_NUM_PORTS; i++) {
16639484Sgarrett.damore@Sun.COM 		audio810_free_port(statep->ports[i]);
16649484Sgarrett.damore@Sun.COM 	}
16659484Sgarrett.damore@Sun.COM 
16669484Sgarrett.damore@Sun.COM 	audio810_unmap_regs(statep);
16679484Sgarrett.damore@Sun.COM 
16689484Sgarrett.damore@Sun.COM 	if (statep->ac97)
16699484Sgarrett.damore@Sun.COM 		ac97_free(statep->ac97);
16709484Sgarrett.damore@Sun.COM 
16719484Sgarrett.damore@Sun.COM 	if (statep->adev)
16729484Sgarrett.damore@Sun.COM 		audio_dev_free(statep->adev);
16739484Sgarrett.damore@Sun.COM 
16749484Sgarrett.damore@Sun.COM 	kmem_free(statep, sizeof (*statep));
16759484Sgarrett.damore@Sun.COM }
1676