188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
22*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2388447a05SGarrett D'Amore  * Use is subject to license terms.
2488447a05SGarrett D'Amore  */
2588447a05SGarrett D'Amore 
2688447a05SGarrett D'Amore 
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * audio1575 Audio Driver
2988447a05SGarrett D'Amore  *
3088447a05SGarrett D'Amore  * The driver is primarily targeted at providing audio support for
3188447a05SGarrett D'Amore  * those systems which use the Uli M1575 audio core.
3288447a05SGarrett D'Amore  *
3388447a05SGarrett D'Amore  * The M1575 audio core, in AC'97 controller mode, has independent
3488447a05SGarrett D'Amore  * channels for PCM in, PCM out, mic in, modem in, and modem out.
3588447a05SGarrett D'Amore  *
3688447a05SGarrett D'Amore  * The AC'97 controller is a PCI bus master with scatter/gather
3788447a05SGarrett D'Amore  * support. Each channel has a DMA engine. Currently, we use only
3888447a05SGarrett D'Amore  * the PCM in and PCM out channels. Each DMA engine uses one buffer
3988447a05SGarrett D'Amore  * descriptor list. And the buffer descriptor list is an array of up
4088447a05SGarrett D'Amore  * to 32 entries, each of which describes a data buffer. Each entry
4188447a05SGarrett D'Amore  * contains a pointer to a data buffer, control bits, and the length
4288447a05SGarrett D'Amore  * of the buffer being pointed to, where the length is expressed as
4388447a05SGarrett D'Amore  * the number of samples. This, combined with the 16-bit sample size,
4488447a05SGarrett D'Amore  * gives the actual physical length of the buffer.
4588447a05SGarrett D'Amore  *
4688447a05SGarrett D'Amore  * 	NOTE:
4788447a05SGarrett D'Amore  * 	This driver depends on the drv/audio, misc/ac97
4888447a05SGarrett D'Amore  * 	modules being loaded first.
4988447a05SGarrett D'Amore  */
5088447a05SGarrett D'Amore 
5188447a05SGarrett D'Amore #include <sys/types.h>
5288447a05SGarrett D'Amore #include <sys/modctl.h>
5388447a05SGarrett D'Amore #include <sys/kmem.h>
5488447a05SGarrett D'Amore #include <sys/conf.h>
5588447a05SGarrett D'Amore #include <sys/ddi.h>
5688447a05SGarrett D'Amore #include <sys/sunddi.h>
5788447a05SGarrett D'Amore #include <sys/pci.h>
5888447a05SGarrett D'Amore #include <sys/note.h>
5988447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
6088447a05SGarrett D'Amore #include <sys/audio/ac97.h>
6188447a05SGarrett D'Amore #include "audio1575.h"
6288447a05SGarrett D'Amore 
6388447a05SGarrett D'Amore /*
6488447a05SGarrett D'Amore  * Module linkage routines for the kernel
6588447a05SGarrett D'Amore  */
6688447a05SGarrett D'Amore static int audio1575_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
6788447a05SGarrett D'Amore static int audio1575_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
680e7a77f3SGarrett D'Amore static int audio1575_ddi_quiesce(dev_info_t *);
6988447a05SGarrett D'Amore 
7088447a05SGarrett D'Amore /*
7188447a05SGarrett D'Amore  * Entry point routine prototypes
7288447a05SGarrett D'Amore  */
73*68c47f65SGarrett D'Amore static int audio1575_open(void *, int, unsigned *, caddr_t *);
7488447a05SGarrett D'Amore static void audio1575_close(void *);
7588447a05SGarrett D'Amore static int audio1575_start(void *);
7688447a05SGarrett D'Amore static void audio1575_stop(void *);
7788447a05SGarrett D'Amore static int audio1575_format(void *);
7888447a05SGarrett D'Amore static int audio1575_channels(void *);
7988447a05SGarrett D'Amore static int audio1575_rate(void *);
8088447a05SGarrett D'Amore static uint64_t audio1575_count(void *);
8188447a05SGarrett D'Amore static void audio1575_sync(void *, unsigned);
8288447a05SGarrett D'Amore 
8388447a05SGarrett D'Amore static audio_engine_ops_t audio1575_engine_ops = {
8488447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
8588447a05SGarrett D'Amore 	audio1575_open,
8688447a05SGarrett D'Amore 	audio1575_close,
8788447a05SGarrett D'Amore 	audio1575_start,
8888447a05SGarrett D'Amore 	audio1575_stop,
8988447a05SGarrett D'Amore 	audio1575_count,
9088447a05SGarrett D'Amore 	audio1575_format,
9188447a05SGarrett D'Amore 	audio1575_channels,
9288447a05SGarrett D'Amore 	audio1575_rate,
9388447a05SGarrett D'Amore 	audio1575_sync,
94f9ead4a5SGarrett D'Amore 	NULL,
95f9ead4a5SGarrett D'Amore 	NULL,
96f9ead4a5SGarrett D'Amore 	NULL
9788447a05SGarrett D'Amore };
9888447a05SGarrett D'Amore 
9988447a05SGarrett D'Amore /*
10088447a05SGarrett D'Amore  * Local Routine Prototypes
10188447a05SGarrett D'Amore  */
10288447a05SGarrett D'Amore static int audio1575_attach(dev_info_t *);
10388447a05SGarrett D'Amore static int audio1575_resume(dev_info_t *);
10488447a05SGarrett D'Amore static int audio1575_detach(dev_info_t *);
10588447a05SGarrett D'Amore static int audio1575_suspend(dev_info_t *);
10688447a05SGarrett D'Amore 
1070e7a77f3SGarrett D'Amore static int audio1575_alloc_port(audio1575_state_t *, int, uint8_t);
10888447a05SGarrett D'Amore static void audio1575_free_port(audio1575_port_t *);
10988447a05SGarrett D'Amore 
11088447a05SGarrett D'Amore static int audio1575_codec_sync(audio1575_state_t *);
11188447a05SGarrett D'Amore static void audio1575_write_ac97(void *, uint8_t, uint16_t);
11288447a05SGarrett D'Amore static uint16_t audio1575_read_ac97(void *, uint8_t);
11388447a05SGarrett D'Amore static int audio1575_chip_init(audio1575_state_t *);
11488447a05SGarrett D'Amore static int audio1575_map_regs(audio1575_state_t *);
11588447a05SGarrett D'Amore static void audio1575_unmap_regs(audio1575_state_t *);
1160e7a77f3SGarrett D'Amore static void audio1575_dma_stop(audio1575_state_t *, boolean_t);
11788447a05SGarrett D'Amore static void audio1575_pci_enable(audio1575_state_t *);
11888447a05SGarrett D'Amore static void audio1575_pci_disable(audio1575_state_t *);
11988447a05SGarrett D'Amore 
12088447a05SGarrett D'Amore static void audio1575_destroy(audio1575_state_t *);
12188447a05SGarrett D'Amore 
12288447a05SGarrett D'Amore /*
12388447a05SGarrett D'Amore  * Global variables, but used only by this file.
12488447a05SGarrett D'Amore  */
12588447a05SGarrett D'Amore 
12688447a05SGarrett D'Amore /*
12788447a05SGarrett D'Amore  * DDI Structures
12888447a05SGarrett D'Amore  */
12988447a05SGarrett D'Amore 
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore /* Device operations structure */
13288447a05SGarrett D'Amore static struct dev_ops audio1575_dev_ops = {
13388447a05SGarrett D'Amore 	DEVO_REV,		/* devo_rev */
13488447a05SGarrett D'Amore 	0,			/* devo_refcnt */
13588447a05SGarrett D'Amore 	NULL,			/* devo_getinfo */
13688447a05SGarrett D'Amore 	nulldev,		/* devo_identify - obsolete */
13788447a05SGarrett D'Amore 	nulldev,		/* devo_probe */
13888447a05SGarrett D'Amore 	audio1575_ddi_attach,	/* devo_attach */
13988447a05SGarrett D'Amore 	audio1575_ddi_detach,	/* devo_detach */
14088447a05SGarrett D'Amore 	nodev,			/* devo_reset */
14188447a05SGarrett D'Amore 	NULL,			/* devi_cb_ops */
14288447a05SGarrett D'Amore 	NULL,			/* devo_bus_ops */
14388447a05SGarrett D'Amore 	NULL,			/* devo_power */
1440e7a77f3SGarrett D'Amore 	audio1575_ddi_quiesce,	/* devo_quiesce */
14588447a05SGarrett D'Amore };
14688447a05SGarrett D'Amore 
14788447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
14888447a05SGarrett D'Amore static struct modldrv audio1575_modldrv = {
14988447a05SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
15088447a05SGarrett D'Amore 	M1575_MOD_NAME,		/* drv_linkinfo */
15188447a05SGarrett D'Amore 	&audio1575_dev_ops,	/* drv_dev_ops */
15288447a05SGarrett D'Amore };
15388447a05SGarrett D'Amore 
15488447a05SGarrett D'Amore /* Module linkage structure */
15588447a05SGarrett D'Amore static struct modlinkage audio1575_modlinkage = {
15688447a05SGarrett D'Amore 	MODREV_1,			/* ml_rev */
15788447a05SGarrett D'Amore 	(void *)&audio1575_modldrv,	/* ml_linkage */
15888447a05SGarrett D'Amore 	NULL				/* NULL terminates the list */
15988447a05SGarrett D'Amore };
16088447a05SGarrett D'Amore 
16188447a05SGarrett D'Amore 
16288447a05SGarrett D'Amore /*
16388447a05SGarrett D'Amore  * device access attributes for register mapping
16488447a05SGarrett D'Amore  */
16588447a05SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
16688447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
16788447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
16888447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
16988447a05SGarrett D'Amore };
17088447a05SGarrett D'Amore 
17188447a05SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
17288447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
17388447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
17488447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
17588447a05SGarrett D'Amore };
17688447a05SGarrett D'Amore 
17788447a05SGarrett D'Amore /*
17888447a05SGarrett D'Amore  * DMA attributes of buffer descriptor list
17988447a05SGarrett D'Amore  */
18088447a05SGarrett D'Amore static ddi_dma_attr_t bdlist_dma_attr = {
18188447a05SGarrett D'Amore 	DMA_ATTR_V0,	/* version */
18288447a05SGarrett D'Amore 	0x0000000000000000LL,		/* dlim_addr_lo */
18388447a05SGarrett D'Amore 	0x00000000ffffffffLL,		/* dlim_addr_hi */
18488447a05SGarrett D'Amore 	0x000000000000ffffLL,		/* DMA counter register - 64 bits */
18588447a05SGarrett D'Amore 	0x0000000000000008LL,		/* DMA address align must be 8-bytes */
18688447a05SGarrett D'Amore 	0x0000003c,			/* 1 through 64 byte burst sizes */
18788447a05SGarrett D'Amore 	0x00000008,			/* min xfer DMA size BDList entry */
18888447a05SGarrett D'Amore 	0x00000000000ffffLL,		/* max xfer size, 64K */
18988447a05SGarrett D'Amore 	0x000000000001fffLL,		/* seg, set to PAGESIZE */
19088447a05SGarrett D'Amore 	0x00000001,			/* s/g list length, no s/g */
19188447a05SGarrett D'Amore 	0x00000008,			/* granularity of device minxfer */
19288447a05SGarrett D'Amore 	0				/* DMA flags use virtual address */
19388447a05SGarrett D'Amore };
19488447a05SGarrett D'Amore 
19588447a05SGarrett D'Amore /*
19688447a05SGarrett D'Amore  * DMA attributes of buffers to be used to receive/send audio data
19788447a05SGarrett D'Amore  */
19888447a05SGarrett D'Amore static ddi_dma_attr_t	sample_buf_dma_attr = {
19988447a05SGarrett D'Amore 	DMA_ATTR_V0,
20088447a05SGarrett D'Amore 	0x0000000000000000LL,		/* dlim_addr_lo */
20188447a05SGarrett D'Amore 	0x00000000ffffffffLL,		/* dlim_addr_hi */
20288447a05SGarrett D'Amore 	0x000000000001fffeLL,		/* DMA counter register - 16 bits */
20388447a05SGarrett D'Amore 	0x0000000000000004LL,		/* DMA address align 2-byte boundary */
20488447a05SGarrett D'Amore 	0x0000003c,			/* 1 through 60 byte burst sizes */
20588447a05SGarrett D'Amore 	0x00000004,			/* min xfer DMA size BDList entry */
20688447a05SGarrett D'Amore 	0x000000000001ffffLL,		/* max xfer size, 64K */
20788447a05SGarrett D'Amore 	0x000000000001ffffLL,		/* seg, set to 64K */
20888447a05SGarrett D'Amore 	0x00000001,			/* s/g list length, no s/g */
20988447a05SGarrett D'Amore 	0x00000004,			/* granularity of device minxfer */
21088447a05SGarrett D'Amore 	0				/* DMA flags use virtual address */
21188447a05SGarrett D'Amore };
21288447a05SGarrett D'Amore 
21388447a05SGarrett D'Amore /*
21488447a05SGarrett D'Amore  * _init()
21588447a05SGarrett D'Amore  *
21688447a05SGarrett D'Amore  * Description:
21788447a05SGarrett D'Amore  *	Driver initialization, called when driver is first loaded.
21888447a05SGarrett D'Amore  *	This is how access is initially given to all the static structures.
21988447a05SGarrett D'Amore  *
22088447a05SGarrett D'Amore  * Arguments:
22188447a05SGarrett D'Amore  *	None
22288447a05SGarrett D'Amore  *
22388447a05SGarrett D'Amore  * Returns:
22488447a05SGarrett D'Amore  *	mod_install() status, see mod_install(9f)
22588447a05SGarrett D'Amore  */
22688447a05SGarrett D'Amore int
_init(void)22788447a05SGarrett D'Amore _init(void)
22888447a05SGarrett D'Amore {
22988447a05SGarrett D'Amore 	int	error;
23088447a05SGarrett D'Amore 
23188447a05SGarrett D'Amore 	audio_init_ops(&audio1575_dev_ops, M1575_NAME);
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 	if ((error = mod_install(&audio1575_modlinkage)) != 0) {
23488447a05SGarrett D'Amore 		audio_fini_ops(&audio1575_dev_ops);
23588447a05SGarrett D'Amore 	}
23688447a05SGarrett D'Amore 
23788447a05SGarrett D'Amore 	return (error);
23888447a05SGarrett D'Amore }
23988447a05SGarrett D'Amore 
24088447a05SGarrett D'Amore /*
24188447a05SGarrett D'Amore  * _fini()
24288447a05SGarrett D'Amore  *
24388447a05SGarrett D'Amore  * Description:
24488447a05SGarrett D'Amore  *	Module de-initialization, called when the driver is to be unloaded.
24588447a05SGarrett D'Amore  *
24688447a05SGarrett D'Amore  * Arguments:
24788447a05SGarrett D'Amore  *	None
24888447a05SGarrett D'Amore  *
24988447a05SGarrett D'Amore  * Returns:
25088447a05SGarrett D'Amore  *	mod_remove() status, see mod_remove(9f)
25188447a05SGarrett D'Amore  */
25288447a05SGarrett D'Amore int
_fini(void)25388447a05SGarrett D'Amore _fini(void)
25488447a05SGarrett D'Amore {
25588447a05SGarrett D'Amore 	int		error;
25688447a05SGarrett D'Amore 
25788447a05SGarrett D'Amore 	if ((error = mod_remove(&audio1575_modlinkage)) != 0) {
25888447a05SGarrett D'Amore 		return (error);
25988447a05SGarrett D'Amore 	}
26088447a05SGarrett D'Amore 
26188447a05SGarrett D'Amore 	/* clean up ops */
26288447a05SGarrett D'Amore 	audio_fini_ops(&audio1575_dev_ops);
26388447a05SGarrett D'Amore 
26488447a05SGarrett D'Amore 	return (0);
26588447a05SGarrett D'Amore }
26688447a05SGarrett D'Amore 
26788447a05SGarrett D'Amore /*
26888447a05SGarrett D'Amore  * _info()
26988447a05SGarrett D'Amore  *
27088447a05SGarrett D'Amore  * Description:
27188447a05SGarrett D'Amore  *	Module information, returns information about the driver.
27288447a05SGarrett D'Amore  *
27388447a05SGarrett D'Amore  * Arguments:
27488447a05SGarrett D'Amore  *	modinfo		*modinfop	Pointer to the opaque modinfo structure
27588447a05SGarrett D'Amore  *
27688447a05SGarrett D'Amore  * Returns:
27788447a05SGarrett D'Amore  *	mod_info() status, see mod_info(9f)
27888447a05SGarrett D'Amore  */
27988447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)28088447a05SGarrett D'Amore _info(struct modinfo *modinfop)
28188447a05SGarrett D'Amore {
28288447a05SGarrett D'Amore 	return (mod_info(&audio1575_modlinkage, modinfop));
28388447a05SGarrett D'Amore }
28488447a05SGarrett D'Amore 
28588447a05SGarrett D'Amore 
28688447a05SGarrett D'Amore /* ******************* Driver Entry Points ********************************* */
28788447a05SGarrett D'Amore 
28888447a05SGarrett D'Amore /*
28988447a05SGarrett D'Amore  * audio1575_ddi_attach()
29088447a05SGarrett D'Amore  *
29188447a05SGarrett D'Amore  * Description:
29288447a05SGarrett D'Amore  *	Implements the DDI attach(9e) entry point.
29388447a05SGarrett D'Amore  *
29488447a05SGarrett D'Amore  * Arguments:
29588447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
29688447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
29788447a05SGarrett D'Amore  *
29888447a05SGarrett D'Amore  * Returns:
29988447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
30088447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
30188447a05SGarrett D'Amore  */
30288447a05SGarrett D'Amore static int
audio1575_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30388447a05SGarrett D'Amore audio1575_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30488447a05SGarrett D'Amore {
30588447a05SGarrett D'Amore 	switch (cmd) {
30688447a05SGarrett D'Amore 	case DDI_ATTACH:
30788447a05SGarrett D'Amore 		return (audio1575_attach(dip));
30888447a05SGarrett D'Amore 
30988447a05SGarrett D'Amore 	case DDI_RESUME:
31088447a05SGarrett D'Amore 		return (audio1575_resume(dip));
31188447a05SGarrett D'Amore 	}
31288447a05SGarrett D'Amore 	return (DDI_FAILURE);
31388447a05SGarrett D'Amore }
31488447a05SGarrett D'Amore 
31588447a05SGarrett D'Amore /*
31688447a05SGarrett D'Amore  * audio1575_ddi_detach()
31788447a05SGarrett D'Amore  *
31888447a05SGarrett D'Amore  * Description:
31988447a05SGarrett D'Amore  *	Implements the detach(9e) entry point.
32088447a05SGarrett D'Amore  *
32188447a05SGarrett D'Amore  * Arguments:
32288447a05SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
32388447a05SGarrett D'Amore  *	ddi_detach_cmd_t	cmd	Detach command
32488447a05SGarrett D'Amore  *
32588447a05SGarrett D'Amore  * Returns:
32688447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
32788447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
32888447a05SGarrett D'Amore  */
32988447a05SGarrett D'Amore static int
audio1575_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)33088447a05SGarrett D'Amore audio1575_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
33188447a05SGarrett D'Amore {
33288447a05SGarrett D'Amore 	switch (cmd) {
33388447a05SGarrett D'Amore 	case DDI_DETACH:
33488447a05SGarrett D'Amore 		return (audio1575_detach(dip));
33588447a05SGarrett D'Amore 
33688447a05SGarrett D'Amore 	case DDI_SUSPEND:
33788447a05SGarrett D'Amore 		return (audio1575_suspend(dip));
33888447a05SGarrett D'Amore 	}
33988447a05SGarrett D'Amore 	return (DDI_FAILURE);
34088447a05SGarrett D'Amore }
34188447a05SGarrett D'Amore 
3420e7a77f3SGarrett D'Amore /*
3430e7a77f3SGarrett D'Amore  * audio1575_ddi_quiesce()
3440e7a77f3SGarrett D'Amore  *
3450e7a77f3SGarrett D'Amore  * Description:
3460e7a77f3SGarrett D'Amore  *	Implements the quiesce(9e) entry point.
3470e7a77f3SGarrett D'Amore  *
3480e7a77f3SGarrett D'Amore  * Arguments:
3490e7a77f3SGarrett D'Amore  *	dev_info_t		*dip	Pointer to the device's dev_info struct
3500e7a77f3SGarrett D'Amore  *
3510e7a77f3SGarrett D'Amore  * Returns:
3520e7a77f3SGarrett D'Amore  *	DDI_SUCCESS	The driver was quiesced
3530e7a77f3SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be quiesced
3540e7a77f3SGarrett D'Amore  */
3550e7a77f3SGarrett D'Amore static int
audio1575_ddi_quiesce(dev_info_t * dip)3560e7a77f3SGarrett D'Amore audio1575_ddi_quiesce(dev_info_t *dip)
3570e7a77f3SGarrett D'Amore {
3580e7a77f3SGarrett D'Amore 	audio1575_state_t	*statep;
3590e7a77f3SGarrett D'Amore 
3600e7a77f3SGarrett D'Amore 	if ((statep = ddi_get_driver_private(dip)) == NULL)
3610e7a77f3SGarrett D'Amore 		return (DDI_FAILURE);
3620e7a77f3SGarrett D'Amore 
3630e7a77f3SGarrett D'Amore 	audio1575_dma_stop(statep, B_TRUE);
3640e7a77f3SGarrett D'Amore 	return (DDI_SUCCESS);
3650e7a77f3SGarrett D'Amore }
3660e7a77f3SGarrett D'Amore 
36788447a05SGarrett D'Amore /*
36888447a05SGarrett D'Amore  * audio1575_open()
36988447a05SGarrett D'Amore  *
37088447a05SGarrett D'Amore  * Description:
37188447a05SGarrett D'Amore  *	Opens a DMA engine for use.
37288447a05SGarrett D'Amore  *
37388447a05SGarrett D'Amore  * Arguments:
37488447a05SGarrett D'Amore  *	void		*arg		The DMA engine to set up
37588447a05SGarrett D'Amore  *	int		flag		Open flags
376*68c47f65SGarrett D'Amore  *	unsigned	*nframesp	Receives number of frames
37788447a05SGarrett D'Amore  *	caddr_t		*bufp		Receives kernel data buffer
37888447a05SGarrett D'Amore  *
37988447a05SGarrett D'Amore  * Returns:
38088447a05SGarrett D'Amore  *	0	on success
38188447a05SGarrett D'Amore  *	errno	on failure
38288447a05SGarrett D'Amore  */
38388447a05SGarrett D'Amore static int
audio1575_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)384*68c47f65SGarrett D'Amore audio1575_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
38588447a05SGarrett D'Amore {
38688447a05SGarrett D'Amore 	audio1575_port_t	*port = arg;
38788447a05SGarrett D'Amore 
38888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
38988447a05SGarrett D'Amore 
39088447a05SGarrett D'Amore 	port->count = 0;
391*68c47f65SGarrett D'Amore 	*nframesp = port->nframes;
39288447a05SGarrett D'Amore 	*bufp = port->samp_kaddr;
39388447a05SGarrett D'Amore 
39488447a05SGarrett D'Amore 	return (0);
39588447a05SGarrett D'Amore }
39688447a05SGarrett D'Amore 
39788447a05SGarrett D'Amore 
39888447a05SGarrett D'Amore /*
39988447a05SGarrett D'Amore  * audio1575_close()
40088447a05SGarrett D'Amore  *
40188447a05SGarrett D'Amore  * Description:
40288447a05SGarrett D'Amore  *	Closes an audio DMA engine that was previously opened.  Since
40388447a05SGarrett D'Amore  *	nobody is using it, we take this opportunity to possibly power
40488447a05SGarrett D'Amore  *	down the entire device.
40588447a05SGarrett D'Amore  *
40688447a05SGarrett D'Amore  * Arguments:
40788447a05SGarrett D'Amore  *	void	*arg		The DMA engine to shut down
40888447a05SGarrett D'Amore  */
40988447a05SGarrett D'Amore static void
audio1575_close(void * arg)41088447a05SGarrett D'Amore audio1575_close(void *arg)
41188447a05SGarrett D'Amore {
412*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
41388447a05SGarrett D'Amore }
41488447a05SGarrett D'Amore 
41588447a05SGarrett D'Amore /*
41688447a05SGarrett D'Amore  * audio1575_stop()
41788447a05SGarrett D'Amore  *
41888447a05SGarrett D'Amore  * Description:
41988447a05SGarrett D'Amore  *	This is called by the framework to stop a port that is
42088447a05SGarrett D'Amore  *	transferring data.
42188447a05SGarrett D'Amore  *
42288447a05SGarrett D'Amore  * Arguments:
42388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to stop
42488447a05SGarrett D'Amore  */
42588447a05SGarrett D'Amore static void
audio1575_stop(void * arg)42688447a05SGarrett D'Amore audio1575_stop(void *arg)
42788447a05SGarrett D'Amore {
42888447a05SGarrett D'Amore 	audio1575_port_t	*port = arg;
42988447a05SGarrett D'Amore 	audio1575_state_t	*statep = port->statep;
43088447a05SGarrett D'Amore 
43188447a05SGarrett D'Amore 	mutex_enter(&statep->lock);
432*68c47f65SGarrett D'Amore 	if (port->num == M1575_REC) {
433*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE);
434*68c47f65SGarrett D'Amore 	} else {
435*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE);
43688447a05SGarrett D'Amore 	}
43788447a05SGarrett D'Amore 	mutex_exit(&statep->lock);
43888447a05SGarrett D'Amore }
43988447a05SGarrett D'Amore 
44088447a05SGarrett D'Amore /*
44188447a05SGarrett D'Amore  * audio1575_start()
44288447a05SGarrett D'Amore  *
44388447a05SGarrett D'Amore  * Description:
44488447a05SGarrett D'Amore  *	This is called by the framework to start a port transferring data.
44588447a05SGarrett D'Amore  *
44688447a05SGarrett D'Amore  * Arguments:
44788447a05SGarrett D'Amore  *	void	*arg		The DMA engine to start
44888447a05SGarrett D'Amore  *
44988447a05SGarrett D'Amore  * Returns:
45088447a05SGarrett D'Amore  *	0 	on success (never fails, errno if it did)
45188447a05SGarrett D'Amore  */
45288447a05SGarrett D'Amore static int
audio1575_start(void * arg)45388447a05SGarrett D'Amore audio1575_start(void *arg)
45488447a05SGarrett D'Amore {
45588447a05SGarrett D'Amore 	audio1575_port_t	*port = arg;
45688447a05SGarrett D'Amore 	audio1575_state_t	*statep = port->statep;
45788447a05SGarrett D'Amore 
45888447a05SGarrett D'Amore 	mutex_enter(&statep->lock);
459*68c47f65SGarrett D'Amore 
460*68c47f65SGarrett D'Amore 	port->offset = 0;
461*68c47f65SGarrett D'Amore 
462*68c47f65SGarrett D'Amore 	if (port->num == M1575_REC) {
463*68c47f65SGarrett D'Amore 		/* Uli FIFO madness ... */
464*68c47f65SGarrett D'Amore 		SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST);
465*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE);
466*68c47f65SGarrett D'Amore 
467*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMICR_REG, 0);
468*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMICR_REG, M1575_CR_RR);
469*68c47f65SGarrett D'Amore 
470*68c47f65SGarrett D'Amore 		PUT32(M1575_PCMIBDBAR_REG, port->bdl_paddr);
471*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMILVIV_REG, M1575_BD_NUMS - 1);
472*68c47f65SGarrett D'Amore 
473*68c47f65SGarrett D'Amore 		CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE);
474*68c47f65SGarrett D'Amore 
475*68c47f65SGarrett D'Amore 		/* ULi says do fifo resets here */
476*68c47f65SGarrett D'Amore 		SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST);
477*68c47f65SGarrett D'Amore 		CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE);
478*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMICR_REG, 0);
479*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMISTART);
480*68c47f65SGarrett D'Amore 
481*68c47f65SGarrett D'Amore 	} else {
482*68c47f65SGarrett D'Amore 
483*68c47f65SGarrett D'Amore 		uint32_t	scr;
484*68c47f65SGarrett D'Amore 
485*68c47f65SGarrett D'Amore 		/* Uli FIFO madness ... */
486*68c47f65SGarrett D'Amore 		SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMORST);
487*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE);
488*68c47f65SGarrett D'Amore 
489*68c47f65SGarrett D'Amore 		/* configure the number of channels properly */
490*68c47f65SGarrett D'Amore 		scr = GET32(M1575_SCR_REG);
491*68c47f65SGarrett D'Amore 		scr &= ~(M1575_SCR_6CHL_MASK | M1575_SCR_CHAMOD_MASK);
492*68c47f65SGarrett D'Amore 		scr |= M1575_SCR_6CHL_2;	/* select our proper ordering */
493*68c47f65SGarrett D'Amore 		switch (port->nchan) {
494*68c47f65SGarrett D'Amore 		case 2:
495*68c47f65SGarrett D'Amore 			scr |= M1575_SCR_CHAMOD_2;
496*68c47f65SGarrett D'Amore 			break;
497*68c47f65SGarrett D'Amore 		case 4:
498*68c47f65SGarrett D'Amore 			scr |= M1575_SCR_CHAMOD_4;
499*68c47f65SGarrett D'Amore 			break;
500*68c47f65SGarrett D'Amore 		case 6:
501*68c47f65SGarrett D'Amore 			scr |= M1575_SCR_CHAMOD_6;
502*68c47f65SGarrett D'Amore 			break;
503*68c47f65SGarrett D'Amore 		}
504*68c47f65SGarrett D'Amore 		PUT32(M1575_SCR_REG, scr);
505*68c47f65SGarrett D'Amore 
506*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMOCR_REG, 0);
507*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMOCR_REG, M1575_CR_RR);
508*68c47f65SGarrett D'Amore 
509*68c47f65SGarrett D'Amore 		PUT32(M1575_PCMOBDBAR_REG, port->bdl_paddr);
510*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMOLVIV_REG, M1575_BD_NUMS - 1);
511*68c47f65SGarrett D'Amore 
512*68c47f65SGarrett D'Amore 		CLR32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE);
513*68c47f65SGarrett D'Amore 		PUT8(M1575_PCMOCR_REG, 0);
514*68c47f65SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PCMOSTART);
51588447a05SGarrett D'Amore 	}
516*68c47f65SGarrett D'Amore 
51788447a05SGarrett D'Amore 	mutex_exit(&statep->lock);
51888447a05SGarrett D'Amore 	return (0);
51988447a05SGarrett D'Amore }
52088447a05SGarrett D'Amore 
521*68c47f65SGarrett D'Amore 
522*68c47f65SGarrett D'Amore 
52388447a05SGarrett D'Amore /*
52488447a05SGarrett D'Amore  * audio1575_format()
52588447a05SGarrett D'Amore  *
52688447a05SGarrett D'Amore  * Description:
52788447a05SGarrett D'Amore  *	Called by the framework to query the format for the device.
52888447a05SGarrett D'Amore  *
52988447a05SGarrett D'Amore  * Arguments:
53088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
53188447a05SGarrett D'Amore  *
53288447a05SGarrett D'Amore  * Returns:
53388447a05SGarrett D'Amore  *	AUDIO_FORMAT_S16_LE
53488447a05SGarrett D'Amore  */
53588447a05SGarrett D'Amore static int
audio1575_format(void * arg)53688447a05SGarrett D'Amore audio1575_format(void *arg)
53788447a05SGarrett D'Amore {
53888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
53988447a05SGarrett D'Amore 
54088447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
54188447a05SGarrett D'Amore }
54288447a05SGarrett D'Amore 
54388447a05SGarrett D'Amore /*
54488447a05SGarrett D'Amore  * audio1575_channels()
54588447a05SGarrett D'Amore  *
54688447a05SGarrett D'Amore  * Description:
54788447a05SGarrett D'Amore  *	Called by the framework to query the channels for the device.
54888447a05SGarrett D'Amore  *
54988447a05SGarrett D'Amore  * Arguments:
55088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
55188447a05SGarrett D'Amore  *
55288447a05SGarrett D'Amore  * Returns:
55388447a05SGarrett D'Amore  *	Number of channels for the device
55488447a05SGarrett D'Amore  */
55588447a05SGarrett D'Amore static int
audio1575_channels(void * arg)55688447a05SGarrett D'Amore audio1575_channels(void *arg)
55788447a05SGarrett D'Amore {
55888447a05SGarrett D'Amore 	audio1575_port_t *port = arg;
55988447a05SGarrett D'Amore 
56088447a05SGarrett D'Amore 	return (port->nchan);
56188447a05SGarrett D'Amore }
56288447a05SGarrett D'Amore 
56388447a05SGarrett D'Amore /*
56488447a05SGarrett D'Amore  * audio1575_rate()
56588447a05SGarrett D'Amore  *
56688447a05SGarrett D'Amore  * Description:
56788447a05SGarrett D'Amore  *	Called by the framework to query the sample rate for the device.
56888447a05SGarrett D'Amore  *
56988447a05SGarrett D'Amore  * Arguments:
57088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
57188447a05SGarrett D'Amore  *
57288447a05SGarrett D'Amore  * Returns:
57388447a05SGarrett D'Amore  *	48000
57488447a05SGarrett D'Amore  */
57588447a05SGarrett D'Amore static int
audio1575_rate(void * arg)57688447a05SGarrett D'Amore audio1575_rate(void *arg)
57788447a05SGarrett D'Amore {
57888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
57988447a05SGarrett D'Amore 
58088447a05SGarrett D'Amore 	return (48000);
58188447a05SGarrett D'Amore }
58288447a05SGarrett D'Amore 
58388447a05SGarrett D'Amore /*
58488447a05SGarrett D'Amore  * audio1575_count()
58588447a05SGarrett D'Amore  *
58688447a05SGarrett D'Amore  * Description:
58788447a05SGarrett D'Amore  *	This is called by the framework to get the engine's frame counter
58888447a05SGarrett D'Amore  *
58988447a05SGarrett D'Amore  * Arguments:
59088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
59188447a05SGarrett D'Amore  *
59288447a05SGarrett D'Amore  * Returns:
59388447a05SGarrett D'Amore  *	frame count for current engine
59488447a05SGarrett D'Amore  */
59588447a05SGarrett D'Amore static uint64_t
audio1575_count(void * arg)59688447a05SGarrett D'Amore audio1575_count(void *arg)
59788447a05SGarrett D'Amore {
59888447a05SGarrett D'Amore 	audio1575_port_t	*port = arg;
59988447a05SGarrett D'Amore 	audio1575_state_t	*statep = port->statep;
60088447a05SGarrett D'Amore 	uint64_t		val;
601*68c47f65SGarrett D'Amore 	uint8_t			civ;
602*68c47f65SGarrett D'Amore 	unsigned		n;
603*68c47f65SGarrett D'Amore 	int			civoff;
604*68c47f65SGarrett D'Amore 	int			lvioff;
605*68c47f65SGarrett D'Amore 	int			picoff;
60688447a05SGarrett D'Amore 
60788447a05SGarrett D'Amore 	mutex_enter(&statep->lock);
608*68c47f65SGarrett D'Amore 
609*68c47f65SGarrett D'Amore 	if (port->num == M1575_REC) {
610*68c47f65SGarrett D'Amore 		civoff = M1575_PCMICIV_REG;
611*68c47f65SGarrett D'Amore 		lvioff = M1575_PCMILVIV_REG;
612*68c47f65SGarrett D'Amore 		picoff = M1575_PCMIPICB_REG;
613*68c47f65SGarrett D'Amore 	} else {
614*68c47f65SGarrett D'Amore 		civoff = M1575_PCMOCIV_REG;
615*68c47f65SGarrett D'Amore 		lvioff = M1575_PCMOLVIV_REG;
616*68c47f65SGarrett D'Amore 		picoff = M1575_PCMOPICB_REG;
617*68c47f65SGarrett D'Amore 	}
618*68c47f65SGarrett D'Amore 
619*68c47f65SGarrett D'Amore 	/*
620*68c47f65SGarrett D'Amore 	 * Read the position counters.  We also take this opportunity
621*68c47f65SGarrett D'Amore 	 * to update the last valid index to the one just previous to
622*68c47f65SGarrett D'Amore 	 * the one we're working on (so we'll fully loop.)
623*68c47f65SGarrett D'Amore 	 */
624*68c47f65SGarrett D'Amore 	n = GET16(picoff);
625*68c47f65SGarrett D'Amore 	civ = GET8(civoff);
626*68c47f65SGarrett D'Amore 	PUT8(lvioff, (civ - 1) % M1575_BD_NUMS);
627*68c47f65SGarrett D'Amore 
628*68c47f65SGarrett D'Amore 	n = port->samp_size - (n * sizeof (int16_t));
629*68c47f65SGarrett D'Amore 	if (n < port->offset) {
630*68c47f65SGarrett D'Amore 		val = (port->samp_size - port->offset) + n;
631*68c47f65SGarrett D'Amore 	} else {
632*68c47f65SGarrett D'Amore 		val = n - port->offset;
633*68c47f65SGarrett D'Amore 	}
634*68c47f65SGarrett D'Amore 	port->offset = n;
635*68c47f65SGarrett D'Amore 	port->count += (val / (port->nchan * sizeof (int16_t)));
636*68c47f65SGarrett D'Amore 	val = port->count;
63788447a05SGarrett D'Amore 	mutex_exit(&statep->lock);
63888447a05SGarrett D'Amore 
63988447a05SGarrett D'Amore 	return (val);
64088447a05SGarrett D'Amore }
64188447a05SGarrett D'Amore 
64288447a05SGarrett D'Amore /*
64388447a05SGarrett D'Amore  * audio1575_sync()
64488447a05SGarrett D'Amore  *
64588447a05SGarrett D'Amore  * Description:
64688447a05SGarrett D'Amore  *	This is called by the framework to synchronize DMA caches.
64788447a05SGarrett D'Amore  *
64888447a05SGarrett D'Amore  * Arguments:
64988447a05SGarrett D'Amore  *	void	*arg		The DMA engine to sync
65088447a05SGarrett D'Amore  */
65188447a05SGarrett D'Amore static void
audio1575_sync(void * arg,unsigned nframes)65288447a05SGarrett D'Amore audio1575_sync(void *arg, unsigned nframes)
65388447a05SGarrett D'Amore {
65488447a05SGarrett D'Amore 	audio1575_port_t *port = arg;
65588447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
65688447a05SGarrett D'Amore 
65788447a05SGarrett D'Amore 	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
65888447a05SGarrett D'Amore }
65988447a05SGarrett D'Amore 
66088447a05SGarrett D'Amore /*
66188447a05SGarrett D'Amore  * audio1575_attach()
66288447a05SGarrett D'Amore  *
66388447a05SGarrett D'Amore  * Description:
66488447a05SGarrett D'Amore  *	Attach an instance of the audio1575 driver. This routine does the
66588447a05SGarrett D'Amore  * 	device dependent attach tasks. When it is completed, it registers
66688447a05SGarrett D'Amore  *	with the audio framework.
66788447a05SGarrett D'Amore  *
66888447a05SGarrett D'Amore  * Arguments:
66988447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
67088447a05SGarrett D'Amore  *
67188447a05SGarrett D'Amore  * Returns:
67288447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
67388447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
67488447a05SGarrett D'Amore  */
67588447a05SGarrett D'Amore static int
audio1575_attach(dev_info_t * dip)67688447a05SGarrett D'Amore audio1575_attach(dev_info_t *dip)
67788447a05SGarrett D'Amore {
67888447a05SGarrett D'Amore 	audio1575_state_t	*statep;
67988447a05SGarrett D'Amore 	audio_dev_t		*adev;
68088447a05SGarrett D'Amore 	uint32_t		devid;
68188447a05SGarrett D'Amore 	const char		*name;
68288447a05SGarrett D'Amore 	const char		*rev;
6830e7a77f3SGarrett D'Amore 	int			maxch;
68488447a05SGarrett D'Amore 
68588447a05SGarrett D'Amore 	/* allocate the soft state structure */
68688447a05SGarrett D'Amore 	statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
68788447a05SGarrett D'Amore 	ddi_set_driver_private(dip, statep);
68888447a05SGarrett D'Amore 	statep->dip = dip;
68988447a05SGarrett D'Amore 
69088447a05SGarrett D'Amore 	/*
69188447a05SGarrett D'Amore 	 * We want the micboost enabled by default as well.
69288447a05SGarrett D'Amore 	 */
693cf6aa9d8SGarrett D'Amore 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, AC97_PROP_MICBOOST, 1);
69488447a05SGarrett D'Amore 
69588447a05SGarrett D'Amore 	/* allocate common audio dev structure */
69688447a05SGarrett D'Amore 	adev = audio_dev_alloc(dip, 0);
69788447a05SGarrett D'Amore 	if (adev == NULL) {
69888447a05SGarrett D'Amore 		audio_dev_warn(NULL, "unable to allocate audio dev");
69988447a05SGarrett D'Amore 		goto error;
70088447a05SGarrett D'Amore 	}
70188447a05SGarrett D'Amore 	statep->adev = adev;
70288447a05SGarrett D'Amore 
70388447a05SGarrett D'Amore 	/* map in the audio registers */
70488447a05SGarrett D'Amore 	if (audio1575_map_regs(statep) != DDI_SUCCESS) {
70588447a05SGarrett D'Amore 		audio_dev_warn(adev, "couldn't map registers");
70688447a05SGarrett D'Amore 		goto error;
70788447a05SGarrett D'Amore 	}
70888447a05SGarrett D'Amore 
70988447a05SGarrett D'Amore 	/* Enable PCI I/O and Memory Spaces */
71088447a05SGarrett D'Amore 	audio1575_pci_enable(statep);
71188447a05SGarrett D'Amore 
71288447a05SGarrett D'Amore 	devid = (pci_config_get16(statep->pcih, PCI_CONF_VENID) << 16) |
71388447a05SGarrett D'Amore 	    pci_config_get16(statep->pcih, PCI_CONF_DEVID);
71488447a05SGarrett D'Amore 	switch (devid) {
71588447a05SGarrett D'Amore 	case 0x10b95455:
71688447a05SGarrett D'Amore 		name = "Uli M1575 AC'97";
71788447a05SGarrett D'Amore 		rev = "M5455";
71888447a05SGarrett D'Amore 		break;
71988447a05SGarrett D'Amore 	default:
72088447a05SGarrett D'Amore 		name = "Uli AC'97";
72188447a05SGarrett D'Amore 		rev = "Unknown";
72288447a05SGarrett D'Amore 		break;
72388447a05SGarrett D'Amore 	}
72488447a05SGarrett D'Amore 	/* set device information -- this should check PCI config space */
72588447a05SGarrett D'Amore 	audio_dev_set_description(adev, name);
72688447a05SGarrett D'Amore 	audio_dev_set_version(adev, rev);
72788447a05SGarrett D'Amore 
7280e7a77f3SGarrett D'Amore 	statep->ac97 = ac97_alloc(dip, audio1575_read_ac97,
7290e7a77f3SGarrett D'Amore 	    audio1575_write_ac97, statep);
7300e7a77f3SGarrett D'Amore 	ASSERT(statep->ac97 != NULL);
7310e7a77f3SGarrett D'Amore 
7320e7a77f3SGarrett D'Amore 	/*
7330e7a77f3SGarrett D'Amore 	 * Override "max-channels" property to prevent configuration
7340e7a77f3SGarrett D'Amore 	 * of 4 or 6 (or possibly even 8!) channel audio.  The default
7350e7a77f3SGarrett D'Amore 	 * is to support as many channels as the hardware can do.
7360e7a77f3SGarrett D'Amore 	 */
7370e7a77f3SGarrett D'Amore 	maxch = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7380e7a77f3SGarrett D'Amore 	    "max-channels", ac97_num_channels(statep->ac97));
7390e7a77f3SGarrett D'Amore 	if (maxch < 2) {
7400e7a77f3SGarrett D'Amore 		maxch = 2;
7410e7a77f3SGarrett D'Amore 	}
7420e7a77f3SGarrett D'Amore 
7430e7a77f3SGarrett D'Amore 	statep->maxch = min(maxch, 6) & ~1;
74488447a05SGarrett D'Amore 
74588447a05SGarrett D'Amore 	/* allocate port structures */
7460e7a77f3SGarrett D'Amore 	if ((audio1575_alloc_port(statep, M1575_PLAY, statep->maxch) !=
7470e7a77f3SGarrett D'Amore 	    DDI_SUCCESS) ||
7480e7a77f3SGarrett D'Amore 	    (audio1575_alloc_port(statep, M1575_REC, 2) != DDI_SUCCESS)) {
74988447a05SGarrett D'Amore 		goto error;
75088447a05SGarrett D'Amore 	}
75188447a05SGarrett D'Amore 
75288447a05SGarrett D'Amore 	if (audio1575_chip_init(statep) != DDI_SUCCESS) {
75388447a05SGarrett D'Amore 		audio_dev_warn(adev, "failed to init chip");
75488447a05SGarrett D'Amore 		goto error;
75588447a05SGarrett D'Amore 	}
75688447a05SGarrett D'Amore 
75788447a05SGarrett D'Amore 	if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
75888447a05SGarrett D'Amore 		audio_dev_warn(adev, "ac'97 initialization failed");
75988447a05SGarrett D'Amore 		goto error;
76088447a05SGarrett D'Amore 	}
76188447a05SGarrett D'Amore 
76288447a05SGarrett D'Amore 	/* register with the framework */
76388447a05SGarrett D'Amore 	if (audio_dev_register(adev) != DDI_SUCCESS) {
76488447a05SGarrett D'Amore 		audio_dev_warn(adev, "unable to register with framework");
76588447a05SGarrett D'Amore 		goto error;
76688447a05SGarrett D'Amore 	}
76788447a05SGarrett D'Amore 
76888447a05SGarrett D'Amore 	/* everything worked out, so report the device */
76988447a05SGarrett D'Amore 	ddi_report_dev(dip);
77088447a05SGarrett D'Amore 
77188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
77288447a05SGarrett D'Amore 
77388447a05SGarrett D'Amore error:
77488447a05SGarrett D'Amore 	audio1575_destroy(statep);
77588447a05SGarrett D'Amore 	return (DDI_FAILURE);
77688447a05SGarrett D'Amore }
77788447a05SGarrett D'Amore 
77888447a05SGarrett D'Amore /*
77988447a05SGarrett D'Amore  * audio1575_detach()
78088447a05SGarrett D'Amore  *
78188447a05SGarrett D'Amore  * Description:
78288447a05SGarrett D'Amore  *	Detach an instance of the audio1575 driver.
78388447a05SGarrett D'Amore  *
78488447a05SGarrett D'Amore  * Arguments:
78588447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
78688447a05SGarrett D'Amore  *
78788447a05SGarrett D'Amore  * Returns:
78888447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was detached
78988447a05SGarrett D'Amore  *	DDI_FAILURE	The driver couldn't be detached
79088447a05SGarrett D'Amore  */
79188447a05SGarrett D'Amore static int
audio1575_detach(dev_info_t * dip)79288447a05SGarrett D'Amore audio1575_detach(dev_info_t *dip)
79388447a05SGarrett D'Amore {
79488447a05SGarrett D'Amore 	audio1575_state_t	*statep;
79588447a05SGarrett D'Amore 
79688447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
79788447a05SGarrett D'Amore 
79888447a05SGarrett D'Amore 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
79988447a05SGarrett D'Amore 		return (DDI_FAILURE);
80088447a05SGarrett D'Amore 	}
80188447a05SGarrett D'Amore 
80288447a05SGarrett D'Amore 	audio1575_destroy(statep);
80388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
80488447a05SGarrett D'Amore }
80588447a05SGarrett D'Amore 
80688447a05SGarrett D'Amore /* *********************** Local Routines *************************** */
80788447a05SGarrett D'Amore 
80888447a05SGarrett D'Amore /*
80988447a05SGarrett D'Amore  * audio1575_alloc_port()
81088447a05SGarrett D'Amore  *
81188447a05SGarrett D'Amore  * Description:
81288447a05SGarrett D'Amore  *	This routine allocates the DMA handles and the memory for the
81388447a05SGarrett D'Amore  *	DMA engines to use.  It also configures the BDL lists properly
81488447a05SGarrett D'Amore  *	for use.
81588447a05SGarrett D'Amore  *
81688447a05SGarrett D'Amore  * Arguments:
81788447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's devinfo
8180e7a77f3SGarrett D'Amore  *	int		num	M1575_PLAY or M1575_REC
8190e7a77f3SGarrett D'Amore  *	uint8_t		nchan	Number of channels (2 = stereo, 6 = 5.1, etc.)
82088447a05SGarrett D'Amore  *
82188447a05SGarrett D'Amore  * Returns:
82288447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
82388447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
82488447a05SGarrett D'Amore  */
82588447a05SGarrett D'Amore static int
audio1575_alloc_port(audio1575_state_t * statep,int num,uint8_t nchan)8260e7a77f3SGarrett D'Amore audio1575_alloc_port(audio1575_state_t *statep, int num, uint8_t nchan)
82788447a05SGarrett D'Amore {
82888447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
82988447a05SGarrett D'Amore 	uint_t			count;
83088447a05SGarrett D'Amore 	int			dir;
83188447a05SGarrett D'Amore 	unsigned		caps;
83288447a05SGarrett D'Amore 	audio_dev_t		*adev;
83388447a05SGarrett D'Amore 	audio1575_port_t	*port;
83488447a05SGarrett D'Amore 	uint32_t		*kaddr;
83588447a05SGarrett D'Amore 	int			rc;
83688447a05SGarrett D'Amore 	dev_info_t		*dip;
83788447a05SGarrett D'Amore 
83888447a05SGarrett D'Amore 	adev = statep->adev;
83988447a05SGarrett D'Amore 	dip = statep->dip;
84088447a05SGarrett D'Amore 
84188447a05SGarrett D'Amore 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
84288447a05SGarrett D'Amore 	statep->ports[num] = port;
84388447a05SGarrett D'Amore 	port->num = num;
84488447a05SGarrett D'Amore 	port->statep = statep;
8450e7a77f3SGarrett D'Amore 	port->nchan = nchan;
84688447a05SGarrett D'Amore 
84788447a05SGarrett D'Amore 	if (num == M1575_REC) {
84888447a05SGarrett D'Amore 		dir = DDI_DMA_READ;
84988447a05SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
85088447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
85188447a05SGarrett D'Amore 	} else {
85288447a05SGarrett D'Amore 		dir = DDI_DMA_WRITE;
85388447a05SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
85488447a05SGarrett D'Amore 		port->sync_dir = DDI_DMA_SYNC_FORDEV;
85588447a05SGarrett D'Amore 	}
85688447a05SGarrett D'Amore 
85788447a05SGarrett D'Amore 	/*
858*68c47f65SGarrett D'Amore 	 * We use one big sample area.  The sample area must be larger
859*68c47f65SGarrett D'Amore 	 * than about 1.5 framework fragment sizes.  (Currently 480 *
860*68c47f65SGarrett D'Amore 	 * 1.5 = 720 frames.)  This is necessary to ensure that we
861*68c47f65SGarrett D'Amore 	 * don't have to involve an interrupt service routine on our
862*68c47f65SGarrett D'Amore 	 * own, to keep the last valid index updated reasonably.
86388447a05SGarrett D'Amore 	 */
864*68c47f65SGarrett D'Amore 	port->nframes = 2048;
865*68c47f65SGarrett D'Amore 	port->samp_size = port->nframes * port->nchan * sizeof (int16_t);
86688447a05SGarrett D'Amore 
86788447a05SGarrett D'Amore 	/* allocate dma handle */
86888447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
86988447a05SGarrett D'Amore 	    NULL, &port->samp_dmah);
87088447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
87188447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
87288447a05SGarrett D'Amore 		return (DDI_FAILURE);
87388447a05SGarrett D'Amore 	}
87488447a05SGarrett D'Amore 	/* allocate DMA buffer */
87588447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
87688447a05SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
87788447a05SGarrett D'Amore 	    &port->samp_size, &port->samp_acch);
87888447a05SGarrett D'Amore 	if (rc == DDI_FAILURE) {
87988447a05SGarrett D'Amore 		audio_dev_warn(adev, "dma_mem_alloc failed");
88088447a05SGarrett D'Amore 		return (DDI_FAILURE);
88188447a05SGarrett D'Amore 	}
88288447a05SGarrett D'Amore 
88388447a05SGarrett D'Amore 	/* bind DMA buffer */
88488447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
88588447a05SGarrett D'Amore 	    port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
88688447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
88788447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
88888447a05SGarrett D'Amore 		audio_dev_warn(adev,
88988447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed: %d", rc);
89088447a05SGarrett D'Amore 		return (DDI_FAILURE);
89188447a05SGarrett D'Amore 	}
89288447a05SGarrett D'Amore 	port->samp_paddr = cookie.dmac_address;
89388447a05SGarrett D'Amore 
89488447a05SGarrett D'Amore 	/*
89588447a05SGarrett D'Amore 	 * now, from here we allocate DMA memory for buffer descriptor list.
89688447a05SGarrett D'Amore 	 * we allocate adjacent DMA memory for all DMA engines.
89788447a05SGarrett D'Amore 	 */
89888447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
89988447a05SGarrett D'Amore 	    NULL, &port->bdl_dmah);
90088447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
90188447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
90288447a05SGarrett D'Amore 		return (DDI_FAILURE);
90388447a05SGarrett D'Amore 	}
90488447a05SGarrett D'Amore 
90588447a05SGarrett D'Amore 	/*
90688447a05SGarrett D'Amore 	 * we allocate all buffer descriptors lists in continuous dma memory.
90788447a05SGarrett D'Amore 	 */
90888447a05SGarrett D'Amore 	port->bdl_size = sizeof (m1575_bd_entry_t) * M1575_BD_NUMS;
90988447a05SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
91088447a05SGarrett D'Amore 	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
91188447a05SGarrett D'Amore 	    &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
91288447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
91388447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
91488447a05SGarrett D'Amore 		return (DDI_FAILURE);
91588447a05SGarrett D'Amore 	}
91688447a05SGarrett D'Amore 
91788447a05SGarrett D'Amore 	/*
91888447a05SGarrett D'Amore 	 * Wire up the BD list.  We do this *before* binding the BD list
91988447a05SGarrett D'Amore 	 * so that we don't have to do an extra ddi_dma_sync.
92088447a05SGarrett D'Amore 	 */
92188447a05SGarrett D'Amore 	kaddr = (void *)port->bdl_kaddr;
92288447a05SGarrett D'Amore 	for (int i = 0; i < M1575_BD_NUMS; i++) {
92388447a05SGarrett D'Amore 
92488447a05SGarrett D'Amore 		/* set base address of buffer */
925*68c47f65SGarrett D'Amore 		ddi_put32(port->bdl_acch, kaddr, port->samp_paddr);
92688447a05SGarrett D'Amore 		kaddr++;
92788447a05SGarrett D'Amore 
92888447a05SGarrett D'Amore 		/* set size in frames, and enable IOC interrupt */
92988447a05SGarrett D'Amore 		ddi_put32(port->bdl_acch, kaddr,
930*68c47f65SGarrett D'Amore 		    ((port->samp_size / sizeof (int16_t)) | (1U << 31)));
93188447a05SGarrett D'Amore 		kaddr++;
93288447a05SGarrett D'Amore 	}
93388447a05SGarrett D'Amore 
93488447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
93588447a05SGarrett D'Amore 	    port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
93688447a05SGarrett D'Amore 	    NULL, &cookie, &count);
93788447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
93888447a05SGarrett D'Amore 		audio_dev_warn(adev, "addr_bind_handle failed");
93988447a05SGarrett D'Amore 		return (DDI_FAILURE);
94088447a05SGarrett D'Amore 	}
94188447a05SGarrett D'Amore 	port->bdl_paddr = cookie.dmac_address;
94288447a05SGarrett D'Amore 
94388447a05SGarrett D'Amore 	port->engine = audio_engine_alloc(&audio1575_engine_ops, caps);
94488447a05SGarrett D'Amore 	if (port->engine == NULL) {
94588447a05SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
94688447a05SGarrett D'Amore 		return (DDI_FAILURE);
94788447a05SGarrett D'Amore 	}
94888447a05SGarrett D'Amore 
94988447a05SGarrett D'Amore 	audio_engine_set_private(port->engine, port);
95088447a05SGarrett D'Amore 	audio_dev_add_engine(adev, port->engine);
95188447a05SGarrett D'Amore 
95288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
95388447a05SGarrett D'Amore }
95488447a05SGarrett D'Amore 
95588447a05SGarrett D'Amore /*
95688447a05SGarrett D'Amore  * audio1575_free_port()
95788447a05SGarrett D'Amore  *
95888447a05SGarrett D'Amore  * Description:
95988447a05SGarrett D'Amore  *	This routine unbinds the DMA cookies, frees the DMA buffers,
96088447a05SGarrett D'Amore  *	deallocates the DMA handles.
96188447a05SGarrett D'Amore  *
96288447a05SGarrett D'Amore  * Arguments:
963*68c47f65SGarrett D'Amore  *	audio1575_port_t	*port	The port structure for a DMA engine.
96488447a05SGarrett D'Amore  */
96588447a05SGarrett D'Amore static void
audio1575_free_port(audio1575_port_t * port)96688447a05SGarrett D'Amore audio1575_free_port(audio1575_port_t *port)
96788447a05SGarrett D'Amore {
96888447a05SGarrett D'Amore 	if (port == NULL)
96988447a05SGarrett D'Amore 		return;
97088447a05SGarrett D'Amore 
97188447a05SGarrett D'Amore 	if (port->engine) {
97288447a05SGarrett D'Amore 		audio_dev_remove_engine(port->statep->adev, port->engine);
97388447a05SGarrett D'Amore 		audio_engine_free(port->engine);
97488447a05SGarrett D'Amore 	}
97588447a05SGarrett D'Amore 	if (port->bdl_paddr) {
97688447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->bdl_dmah);
97788447a05SGarrett D'Amore 	}
97888447a05SGarrett D'Amore 	if (port->bdl_acch) {
97988447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->bdl_acch);
98088447a05SGarrett D'Amore 	}
98188447a05SGarrett D'Amore 	if (port->bdl_dmah) {
98288447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->bdl_dmah);
98388447a05SGarrett D'Amore 	}
98488447a05SGarrett D'Amore 	if (port->samp_paddr) {
98588447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->samp_dmah);
98688447a05SGarrett D'Amore 	}
98788447a05SGarrett D'Amore 	if (port->samp_acch) {
98888447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->samp_acch);
98988447a05SGarrett D'Amore 	}
99088447a05SGarrett D'Amore 	if (port->samp_dmah) {
99188447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->samp_dmah);
99288447a05SGarrett D'Amore 	}
99388447a05SGarrett D'Amore 	kmem_free(port, sizeof (*port));
99488447a05SGarrett D'Amore }
99588447a05SGarrett D'Amore 
99688447a05SGarrett D'Amore /*
99788447a05SGarrett D'Amore  * audio1575_map_regs()
99888447a05SGarrett D'Amore  *
99988447a05SGarrett D'Amore  * Description:
100088447a05SGarrett D'Amore  *	The registers are mapped in.
100188447a05SGarrett D'Amore  *
100288447a05SGarrett D'Amore  * Arguments:
100388447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's devinfo
100488447a05SGarrett D'Amore  *
100588447a05SGarrett D'Amore  * Returns:
100688447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
100788447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
100888447a05SGarrett D'Amore  */
100988447a05SGarrett D'Amore static int
audio1575_map_regs(audio1575_state_t * statep)101088447a05SGarrett D'Amore audio1575_map_regs(audio1575_state_t *statep)
101188447a05SGarrett D'Amore {
101288447a05SGarrett D'Amore 	dev_info_t		*dip = statep->dip;
101388447a05SGarrett D'Amore 
101488447a05SGarrett D'Amore 	/* map the M1575 Audio PCI Cfg Space */
101588447a05SGarrett D'Amore 	if (pci_config_setup(dip, &statep->pcih) != DDI_SUCCESS) {
101688447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "PCI config map failure");
101788447a05SGarrett D'Amore 		goto error;
101888447a05SGarrett D'Amore 	}
101988447a05SGarrett D'Amore 
102088447a05SGarrett D'Amore 	/* map the M1575 Audio registers in PCI IO Space */
102188447a05SGarrett D'Amore 	if ((ddi_regs_map_setup(dip, M1575_AUDIO_IO_SPACE, &statep->regsp,
102288447a05SGarrett D'Amore 	    0, 0, &dev_attr, &statep->regsh)) != DDI_SUCCESS) {
102388447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "Audio IO mapping failure");
102488447a05SGarrett D'Amore 		goto error;
102588447a05SGarrett D'Amore 	}
102688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
102788447a05SGarrett D'Amore 
102888447a05SGarrett D'Amore error:
102988447a05SGarrett D'Amore 	audio1575_unmap_regs(statep);
103088447a05SGarrett D'Amore 
103188447a05SGarrett D'Amore 	return (DDI_FAILURE);
103288447a05SGarrett D'Amore }
103388447a05SGarrett D'Amore 
103488447a05SGarrett D'Amore /*
103588447a05SGarrett D'Amore  * audio1575_unmap_regs()
103688447a05SGarrett D'Amore  *
103788447a05SGarrett D'Amore  * Description:
103888447a05SGarrett D'Amore  *	This routine unmaps control registers.
103988447a05SGarrett D'Amore  *
104088447a05SGarrett D'Amore  * Arguments:
104188447a05SGarrett D'Amore  *	audio1575_state_t	*state	The device's state structure
104288447a05SGarrett D'Amore  */
104388447a05SGarrett D'Amore static void
audio1575_unmap_regs(audio1575_state_t * statep)104488447a05SGarrett D'Amore audio1575_unmap_regs(audio1575_state_t *statep)
104588447a05SGarrett D'Amore {
104688447a05SGarrett D'Amore 	if (statep->regsh) {
104788447a05SGarrett D'Amore 		ddi_regs_map_free(&statep->regsh);
104888447a05SGarrett D'Amore 	}
104988447a05SGarrett D'Amore 
105088447a05SGarrett D'Amore 	if (statep->pcih) {
105188447a05SGarrett D'Amore 		pci_config_teardown(&statep->pcih);
105288447a05SGarrett D'Amore 	}
105388447a05SGarrett D'Amore }
105488447a05SGarrett D'Amore 
105588447a05SGarrett D'Amore /*
105688447a05SGarrett D'Amore  * audio1575_chip_init()
105788447a05SGarrett D'Amore  *
105888447a05SGarrett D'Amore  * Description:
105988447a05SGarrett D'Amore  *	This routine initializes the M1575 AC97 audio controller and the AC97
106088447a05SGarrett D'Amore  *	codec.	The AC97 codec registers are programmed from codec_shadow[].
106188447a05SGarrett D'Amore  *	If we are not doing a restore, we initialize codec_shadow[], otherwise
106288447a05SGarrett D'Amore  *	we use the current values of shadow.	This routine expects that the
106388447a05SGarrett D'Amore  *	PCI IO and Memory spaces have been mapped and enabled already.
106488447a05SGarrett D'Amore  * Arguments:
106588447a05SGarrett D'Amore  *	audio1575_state_t	*state		The device's state structure
106688447a05SGarrett D'Amore  *						restore	from codec_shadow[]
106788447a05SGarrett D'Amore  * Returns:
106888447a05SGarrett D'Amore  *	DDI_SUCCESS	The hardware was initialized properly
106988447a05SGarrett D'Amore  *	DDI_FAILURE	The hardware couldn't be initialized properly
107088447a05SGarrett D'Amore  */
107188447a05SGarrett D'Amore static int
audio1575_chip_init(audio1575_state_t * statep)107288447a05SGarrett D'Amore audio1575_chip_init(audio1575_state_t *statep)
107388447a05SGarrett D'Amore {
107488447a05SGarrett D'Amore 	uint32_t		ssr;
107588447a05SGarrett D'Amore 	uint32_t		rtsr;
107688447a05SGarrett D'Amore 	uint32_t		intrsr;
107788447a05SGarrett D'Amore 	int 			i;
107888447a05SGarrett D'Amore 	int			j;
10790e7a77f3SGarrett D'Amore #ifdef	__sparc
108088447a05SGarrett D'Amore 	uint8_t			clk_detect;
108188447a05SGarrett D'Amore 	ddi_acc_handle_t	pcih;
10820e7a77f3SGarrett D'Amore #endif
10830e7a77f3SGarrett D'Amore 	clock_t			ticks;
108488447a05SGarrett D'Amore 
108588447a05SGarrett D'Amore 	/*
108688447a05SGarrett D'Amore 	 * clear the interrupt control and status register
108788447a05SGarrett D'Amore 	 * READ/WRITE/READ workaround required
108888447a05SGarrett D'Amore 	 * for buggy hardware
108988447a05SGarrett D'Amore 	 */
109088447a05SGarrett D'Amore 
109188447a05SGarrett D'Amore 	PUT32(M1575_INTRCR_REG, 0);
109288447a05SGarrett D'Amore 	(void) GET32(M1575_INTRCR_REG);
109388447a05SGarrett D'Amore 
109488447a05SGarrett D'Amore 	intrsr = GET32(M1575_INTRSR_REG);
109588447a05SGarrett D'Amore 	PUT32(M1575_INTRSR_REG, (intrsr & M1575_INTR_MASK));
109688447a05SGarrett D'Amore 	(void) GET32(M1575_INTRSR_REG);
109788447a05SGarrett D'Amore 
109888447a05SGarrett D'Amore 	ticks = drv_usectohz(M1575_LOOP_CTR);
109988447a05SGarrett D'Amore 
110088447a05SGarrett D'Amore 	/*
110188447a05SGarrett D'Amore 	 * SADA only supports stereo, so we set the channel bits
110288447a05SGarrett D'Amore 	 * to "00" to select 2 channels.
110388447a05SGarrett D'Amore 	 * will also set the following:
110488447a05SGarrett D'Amore 	 *
110588447a05SGarrett D'Amore 	 * Disable double rate enable
110688447a05SGarrett D'Amore 	 * no SPDIF output selected
110788447a05SGarrett D'Amore 	 * 16 bit audio record mode
110888447a05SGarrett D'Amore 	 * 16 bit pcm out mode
110988447a05SGarrett D'Amore 	 * PCM Out 6 chan mode FL FR CEN BL BR LFE
111088447a05SGarrett D'Amore 	 * PCM Out 2 channel mode (00)
111188447a05SGarrett D'Amore 	 */
111288447a05SGarrett D'Amore 	for (i = 0; i < M1575_LOOP_CTR; i++) {
111388447a05SGarrett D'Amore 		/* Reset the AC97 Codec	and default to 2 channel 16 bit mode */
111488447a05SGarrett D'Amore 		PUT32(M1575_SCR_REG, M1575_SCR_COLDRST);
111588447a05SGarrett D'Amore 		delay(ticks<<1);
111688447a05SGarrett D'Amore 
111788447a05SGarrett D'Amore 		/* Read the System Status Reg */
111888447a05SGarrett D'Amore 		ssr = GET32(M1575_SSR_REG);
111988447a05SGarrett D'Amore 
112088447a05SGarrett D'Amore 		/* make sure and release the blocked reset bit */
112188447a05SGarrett D'Amore 		if (ssr & M1575_SSR_RSTBLK) {
112288447a05SGarrett D'Amore 			SET32(M1575_INTFCR_REG, M1575_INTFCR_RSTREL);
112388447a05SGarrett D'Amore 			delay(ticks);
112488447a05SGarrett D'Amore 
112588447a05SGarrett D'Amore 			/* Read the System Status Reg */
112688447a05SGarrett D'Amore 			ssr = GET32(M1575_SSR_REG);
112788447a05SGarrett D'Amore 
112888447a05SGarrett D'Amore 			/* make sure and release the blocked reset bit */
112988447a05SGarrett D'Amore 			if (ssr & M1575_SSR_RSTBLK) {
113088447a05SGarrett D'Amore 				return (DDI_FAILURE);
113188447a05SGarrett D'Amore 			}
113288447a05SGarrett D'Amore 
113388447a05SGarrett D'Amore 			/* Reset the controller */
113488447a05SGarrett D'Amore 			PUT32(M1575_SCR_REG, M1575_SCR_COLDRST);
113588447a05SGarrett D'Amore 			delay(ticks);
113688447a05SGarrett D'Amore 		}
113788447a05SGarrett D'Amore 
113888447a05SGarrett D'Amore 		/* according AC'97 spec, wait for codec reset */
113988447a05SGarrett D'Amore 		for (j = 0; j < M1575_LOOP_CTR; j++) {
114088447a05SGarrett D'Amore 			if ((GET32(M1575_SCR_REG) & M1575_SCR_COLDRST) == 0) {
114188447a05SGarrett D'Amore 				break;
114288447a05SGarrett D'Amore 			}
114388447a05SGarrett D'Amore 			delay(ticks);
114488447a05SGarrett D'Amore 		}
114588447a05SGarrett D'Amore 
114688447a05SGarrett D'Amore 		/* codec reset failed */
114788447a05SGarrett D'Amore 		if (j >= M1575_LOOP_CTR) {
114888447a05SGarrett D'Amore 			audio_dev_warn(statep->adev,
114988447a05SGarrett D'Amore 			    "failure to reset codec");
115088447a05SGarrett D'Amore 			return (DDI_FAILURE);
115188447a05SGarrett D'Amore 		}
115288447a05SGarrett D'Amore 
115388447a05SGarrett D'Amore 		/*
115488447a05SGarrett D'Amore 		 * Wait for FACRDY First codec ready. The hardware can
115588447a05SGarrett D'Amore 		 * provide the state of
115688447a05SGarrett D'Amore 		 * codec ready bit on SDATA_IN[0] and as reflected in
115788447a05SGarrett D'Amore 		 * the Recv Tag Slot Reg.
115888447a05SGarrett D'Amore 		 */
115988447a05SGarrett D'Amore 		rtsr = GET32(M1575_RTSR_REG);
116088447a05SGarrett D'Amore 		if (rtsr & M1575_RTSR_FACRDY) {
116188447a05SGarrett D'Amore 			break;
116288447a05SGarrett D'Amore 		} else { /* reset the status and wait for new status to set */
116388447a05SGarrett D'Amore 			rtsr |= M1575_RTSR_FACRDY;
116488447a05SGarrett D'Amore 			PUT32(M1575_RTSR_REG, rtsr);
116588447a05SGarrett D'Amore 			drv_usecwait(10);
116688447a05SGarrett D'Amore 		}
116788447a05SGarrett D'Amore 	}
116888447a05SGarrett D'Amore 
116988447a05SGarrett D'Amore 	/* if we could not reset the AC97 codec then report failure */
117088447a05SGarrett D'Amore 	if (i >= M1575_LOOP_CTR) {
117188447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
117288447a05SGarrett D'Amore 		    "no codec ready signal received");
117388447a05SGarrett D'Amore 		return (DDI_FAILURE);
117488447a05SGarrett D'Amore 	}
117588447a05SGarrett D'Amore 
11760e7a77f3SGarrett D'Amore #ifdef	__sparc
117788447a05SGarrett D'Amore 	/* Magic code from ULi to Turn on the AC_LINK clock */
117888447a05SGarrett D'Amore 	pcih = statep->pcih;
117988447a05SGarrett D'Amore 	pci_config_put8(pcih, M1575_PCIACD_REG, 0);
118088447a05SGarrett D'Amore 	pci_config_put8(pcih, M1575_PCIACD_REG, 4);
118188447a05SGarrett D'Amore 	pci_config_put8(pcih, M1575_PCIACD_REG, 0);
118288447a05SGarrett D'Amore 	(void) pci_config_get8(pcih, M1575_PCIACD_REG);
118388447a05SGarrett D'Amore 	pci_config_put8(pcih, M1575_PCIACD_REG, 2);
118488447a05SGarrett D'Amore 	pci_config_put8(pcih, M1575_PCIACD_REG, 0);
118588447a05SGarrett D'Amore 	clk_detect = pci_config_get8(pcih, M1575_PCIACD_REG);
118688447a05SGarrett D'Amore 
118788447a05SGarrett D'Amore 	if (clk_detect != 1) {
118888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "No AC97 Clock Detected");
118988447a05SGarrett D'Amore 		return (DDI_FAILURE);
119088447a05SGarrett D'Amore 	}
11910e7a77f3SGarrett D'Amore #endif
119288447a05SGarrett D'Amore 
119388447a05SGarrett D'Amore 	/* Magic code from Uli to Init FIFO1 and FIFO2 */
119488447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR1_REG, 0x81818181);
119588447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR2_REG, 0x81818181);
119688447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR3_REG, 0x81818181);
119788447a05SGarrett D'Amore 
119888447a05SGarrett D'Amore 	/* Make sure that PCM in and PCM out are enabled */
119988447a05SGarrett D'Amore 	SET32(M1575_INTFCR_REG, (M1575_INTFCR_PCMIENB | M1575_INTFCR_PCMOENB));
120088447a05SGarrett D'Amore 
12010e7a77f3SGarrett D'Amore 	audio1575_dma_stop(statep, B_FALSE);
120288447a05SGarrett D'Amore 
120388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
120488447a05SGarrett D'Amore }
120588447a05SGarrett D'Amore 
120688447a05SGarrett D'Amore /*
120788447a05SGarrett D'Amore  * audio1575_dma_stop()
120888447a05SGarrett D'Amore  *
120988447a05SGarrett D'Amore  * Description:
121088447a05SGarrett D'Amore  *	This routine is used to put each DMA engine into the quiet state.
121188447a05SGarrett D'Amore  *
121288447a05SGarrett D'Amore  * Arguments:
121388447a05SGarrett D'Amore  *	audio1575_state_t *statep	The device's state structure
121488447a05SGarrett D'Amore  */
121588447a05SGarrett D'Amore static void
audio1575_dma_stop(audio1575_state_t * statep,boolean_t quiesce)12160e7a77f3SGarrett D'Amore audio1575_dma_stop(audio1575_state_t *statep, boolean_t quiesce)
121788447a05SGarrett D'Amore {
121888447a05SGarrett D'Amore 	uint32_t	intrsr;
121988447a05SGarrett D'Amore 	int		i;
122088447a05SGarrett D'Amore 
122188447a05SGarrett D'Amore 	if (statep->regsh == NULL) {
122288447a05SGarrett D'Amore 		return;
122388447a05SGarrett D'Amore 	}
122488447a05SGarrett D'Amore 
122588447a05SGarrett D'Amore 	/* pause bus master (needed for the following reset register) */
122688447a05SGarrett D'Amore 	for (i = 0; i < M1575_LOOP_CTR; i++) {
122788447a05SGarrett D'Amore 
122888447a05SGarrett D'Amore 		SET32(M1575_DMACR_REG, M1575_DMACR_PAUSE_ALL);
122988447a05SGarrett D'Amore 		if (GET32(M1575_DMACR_REG) & M1575_DMACR_PAUSE_ALL) {
123088447a05SGarrett D'Amore 			break;
123188447a05SGarrett D'Amore 		}
123288447a05SGarrett D'Amore 		drv_usecwait(10);
123388447a05SGarrett D'Amore 	}
123488447a05SGarrett D'Amore 
123588447a05SGarrett D'Amore 	if (i >= M1575_LOOP_CTR) {
12360e7a77f3SGarrett D'Amore 		if (!quiesce)
12370e7a77f3SGarrett D'Amore 			audio_dev_warn(statep->adev, "failed to stop DMA");
123888447a05SGarrett D'Amore 		return;
123988447a05SGarrett D'Amore 	}
124088447a05SGarrett D'Amore 
124188447a05SGarrett D'Amore 	/* Pause bus master (needed for the following reset register) */
124288447a05SGarrett D'Amore 	PUT8(M1575_PCMICR_REG, 0);
124388447a05SGarrett D'Amore 	PUT8(M1575_PCMOCR_REG, 0);
124488447a05SGarrett D'Amore 	PUT8(M1575_MICICR_REG, 0);
124588447a05SGarrett D'Amore 	PUT8(M1575_CSPOCR_REG, 0);
124688447a05SGarrett D'Amore 	PUT8(M1575_PCMI2CR_RR, 0);
124788447a05SGarrett D'Amore 	PUT8(M1575_MICI2CR_RR, 0);
124888447a05SGarrett D'Amore 
124988447a05SGarrett D'Amore 	/* Reset the bus master registers for all DMA engines */
125088447a05SGarrett D'Amore 	PUT8(M1575_PCMICR_REG, M1575_PCMICR_RR);
125188447a05SGarrett D'Amore 	PUT8(M1575_PCMOCR_REG, M1575_PCMOCR_RR);
125288447a05SGarrett D'Amore 	PUT8(M1575_MICICR_REG, M1575_MICICR_RR);
125388447a05SGarrett D'Amore 	PUT8(M1575_CSPOCR_REG, M1575_CSPOCR_RR);
125488447a05SGarrett D'Amore 	PUT8(M1575_PCMI2CR_REG, M1575_PCMI2CR_RR);
125588447a05SGarrett D'Amore 	PUT8(M1575_MICI2CR_REG, M1575_MICI2CR_RR);
125688447a05SGarrett D'Amore 
125788447a05SGarrett D'Amore 	/* Reset FIFOS */
125888447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR1_REG, 0x81818181);
125988447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR2_REG, 0x81818181);
126088447a05SGarrett D'Amore 	PUT32(M1575_FIFOCR3_REG, 0x81818181);
126188447a05SGarrett D'Amore 
126288447a05SGarrett D'Amore 	/* Clear Interrupts */
126388447a05SGarrett D'Amore 	SET16(M1575_PCMISR_REG, M1575_SR_CLR);
126488447a05SGarrett D'Amore 	SET16(M1575_PCMOSR_REG, M1575_SR_CLR);
126588447a05SGarrett D'Amore 	SET16(M1575_MICISR_REG, M1575_SR_CLR);
126688447a05SGarrett D'Amore 	SET16(M1575_CSPOSR_REG, M1575_SR_CLR);
126788447a05SGarrett D'Amore 	SET16(M1575_PCMI2SR_REG, M1575_SR_CLR);
126888447a05SGarrett D'Amore 	SET16(M1575_MICI2SR_REG, M1575_SR_CLR);
126988447a05SGarrett D'Amore 
127088447a05SGarrett D'Amore 	/*
127188447a05SGarrett D'Amore 	 * clear the interrupt control and status register
1272*68c47f65SGarrett D'Amore 	 * READ/WRITE/READ workaround required to flush PCI caches
127388447a05SGarrett D'Amore 	 */
127488447a05SGarrett D'Amore 
127588447a05SGarrett D'Amore 	PUT32(M1575_INTRCR_REG, 0);
127688447a05SGarrett D'Amore 	(void) GET32(M1575_INTRCR_REG);
127788447a05SGarrett D'Amore 
127888447a05SGarrett D'Amore 	intrsr = GET32(M1575_INTRSR_REG);
127988447a05SGarrett D'Amore 	PUT32(M1575_INTRSR_REG, (intrsr & M1575_INTR_MASK));
128088447a05SGarrett D'Amore 	(void) GET32(M1575_INTRSR_REG);
128188447a05SGarrett D'Amore }
128288447a05SGarrett D'Amore 
128388447a05SGarrett D'Amore /*
128488447a05SGarrett D'Amore  * audio1575_codec_sync()
128588447a05SGarrett D'Amore  *
128688447a05SGarrett D'Amore  * Description:
128788447a05SGarrett D'Amore  *	Serialize access to the AC97 audio mixer registers.
128888447a05SGarrett D'Amore  *
128988447a05SGarrett D'Amore  * Arguments:
129088447a05SGarrett D'Amore  *	audio1575_state_t	*state		The device's state structure
129188447a05SGarrett D'Amore  *
129288447a05SGarrett D'Amore  * Returns:
129388447a05SGarrett D'Amore  *	DDI_SUCCESS		Ready for an I/O access to the codec
129488447a05SGarrett D'Amore  *	DDI_FAILURE		An I/O access is currently in progress, can't
129588447a05SGarrett D'Amore  *				perform another I/O access.
129688447a05SGarrett D'Amore  */
129788447a05SGarrett D'Amore static int
audio1575_codec_sync(audio1575_state_t * statep)129888447a05SGarrett D'Amore audio1575_codec_sync(audio1575_state_t *statep)
129988447a05SGarrett D'Amore {
130088447a05SGarrett D'Amore 	/* do the Uli Shuffle ... */
130188447a05SGarrett D'Amore 	for (int i = 0; i < M1575_LOOP_CTR; i++) {
130288447a05SGarrett D'Amore 		/* Read the semaphore, and loop till we own it */
130388447a05SGarrett D'Amore 		if ((GET32(M1575_CASR_REG) & 1) == 0) {
130488447a05SGarrett D'Amore 			for (int j = 0; j < M1575_LOOP_CTR; j++) {
130588447a05SGarrett D'Amore 				/* Wait for CWRSUCC 0x8 */
130688447a05SGarrett D'Amore 				if (GET32(M1575_CSPSR_REG) &
130788447a05SGarrett D'Amore 				    M1575_CSPSR_SUCC) {
130888447a05SGarrett D'Amore 					return (DDI_SUCCESS);
130988447a05SGarrett D'Amore 				}
131088447a05SGarrett D'Amore 				drv_usecwait(1);
131188447a05SGarrett D'Amore 			}
131288447a05SGarrett D'Amore 		}
131388447a05SGarrett D'Amore 		drv_usecwait(10);
131488447a05SGarrett D'Amore 	}
131588447a05SGarrett D'Amore 
131688447a05SGarrett D'Amore 	return (DDI_FAILURE);
131788447a05SGarrett D'Amore }
131888447a05SGarrett D'Amore 
131988447a05SGarrett D'Amore /*
132088447a05SGarrett D'Amore  * audio1575_write_ac97()
132188447a05SGarrett D'Amore  *
132288447a05SGarrett D'Amore  * Description:
132388447a05SGarrett D'Amore  *	Set the specific AC97 Codec register.
132488447a05SGarrett D'Amore  *
132588447a05SGarrett D'Amore  * Arguments:
132688447a05SGarrett D'Amore  *	void		*arg		The device's state structure
132788447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
132888447a05SGarrett D'Amore  *	uint16_t	data		The data want to be set
132988447a05SGarrett D'Amore  */
133088447a05SGarrett D'Amore static void
audio1575_write_ac97(void * arg,uint8_t reg,uint16_t data)133188447a05SGarrett D'Amore audio1575_write_ac97(void *arg, uint8_t reg, uint16_t data)
133288447a05SGarrett D'Amore {
133388447a05SGarrett D'Amore 	audio1575_state_t	*statep = arg;
133488447a05SGarrett D'Amore 	int			i;
133588447a05SGarrett D'Amore 
133688447a05SGarrett D'Amore 	if (audio1575_codec_sync(statep) != DDI_SUCCESS) {
133788447a05SGarrett D'Amore 		return;
133888447a05SGarrett D'Amore 	}
133988447a05SGarrett D'Amore 
134088447a05SGarrett D'Amore 	/* write the data to WRITE to the lo word of the CPR register */
134188447a05SGarrett D'Amore 	PUT16(M1575_CPR_REG, data);
134288447a05SGarrett D'Amore 
134388447a05SGarrett D'Amore 	/* write the address to WRITE to the hi word of the CPR register */
134488447a05SGarrett D'Amore 	PUT16(M1575_CPR_REG+2, reg);
134588447a05SGarrett D'Amore 
134688447a05SGarrett D'Amore 	/* wait until command is completed sucessfully */
134788447a05SGarrett D'Amore 	for (i = 0; i < M1575_LOOP_CTR; i++) {
134888447a05SGarrett D'Amore 		/* Wait for Write Ready	0x01 */
134988447a05SGarrett D'Amore 		if (GET32(M1575_CSPSR_REG) & M1575_CSPSR_WRRDY) {
135088447a05SGarrett D'Amore 			break;
135188447a05SGarrett D'Amore 		}
135288447a05SGarrett D'Amore 		drv_usecwait(1);
135388447a05SGarrett D'Amore 	}
135488447a05SGarrett D'Amore 
135588447a05SGarrett D'Amore 	if (i < M1575_LOOP_CTR) {
135688447a05SGarrett D'Amore 		(void) audio1575_read_ac97(statep, reg);
135788447a05SGarrett D'Amore 	}
135888447a05SGarrett D'Amore }
135988447a05SGarrett D'Amore 
136088447a05SGarrett D'Amore /*
136188447a05SGarrett D'Amore  * audio1575_read_ac97()
136288447a05SGarrett D'Amore  *
136388447a05SGarrett D'Amore  * Description:
136488447a05SGarrett D'Amore  *	Get the specific AC97 Codec register. It also updates codec_shadow[]
136588447a05SGarrett D'Amore  *	with the register value.
136688447a05SGarrett D'Amore  *
136788447a05SGarrett D'Amore  * Arguments:
136888447a05SGarrett D'Amore  *	void		*arg		The device's state structure
136988447a05SGarrett D'Amore  *	uint8_t		reg		AC97 register number
137088447a05SGarrett D'Amore  *
137188447a05SGarrett D'Amore  * Returns:
137288447a05SGarrett D'Amore  *	Value of AC97 register.  (0xffff in failure situations).
137388447a05SGarrett D'Amore  */
137488447a05SGarrett D'Amore static uint16_t
audio1575_read_ac97(void * arg,uint8_t reg)137588447a05SGarrett D'Amore audio1575_read_ac97(void *arg, uint8_t reg)
137688447a05SGarrett D'Amore {
137788447a05SGarrett D'Amore 	audio1575_state_t	*statep = arg;
137888447a05SGarrett D'Amore 	uint16_t		addr = 0;
137988447a05SGarrett D'Amore 	uint16_t		data = 0xffff;
138088447a05SGarrett D'Amore 	int			i;
138188447a05SGarrett D'Amore 
138288447a05SGarrett D'Amore 	if ((audio1575_codec_sync(statep)) != DDI_SUCCESS) {
138388447a05SGarrett D'Amore 		return (data);
138488447a05SGarrett D'Amore 	}
138588447a05SGarrett D'Amore 
138688447a05SGarrett D'Amore 	/*
138788447a05SGarrett D'Amore 	 * at this point we have the CASR semaphore
138888447a05SGarrett D'Amore 	 * and the codec is r/w ready
138988447a05SGarrett D'Amore 	 * OR in the READ opcode into the address field
139088447a05SGarrett D'Amore 	 */
139188447a05SGarrett D'Amore 
139288447a05SGarrett D'Amore 	addr = (reg | M1575_CPR_READ);
139388447a05SGarrett D'Amore 
139488447a05SGarrett D'Amore 	/* write the address to READ to the hi word of the CPR register */
139588447a05SGarrett D'Amore 	PUT16(M1575_CPR_REG+2, addr);
139688447a05SGarrett D'Amore 
139788447a05SGarrett D'Amore 	/* wait until command is completed sucessfully */
139888447a05SGarrett D'Amore 	for (i = 0; i < M1575_LOOP_CTR; i++) {
139988447a05SGarrett D'Amore 		/* Wait for Read Ready	0x02 */
140088447a05SGarrett D'Amore 		if (GET32(M1575_CSPSR_REG) & M1575_CSPSR_RDRDY) {
140188447a05SGarrett D'Amore 			break;
140288447a05SGarrett D'Amore 		}
140388447a05SGarrett D'Amore 		drv_usecwait(1);
140488447a05SGarrett D'Amore 	}
140588447a05SGarrett D'Amore 
140688447a05SGarrett D'Amore 	if (i < M1575_LOOP_CTR) {
140788447a05SGarrett D'Amore 		/* read back the data and address */
140888447a05SGarrett D'Amore 		data = GET16(M1575_SPR_REG);
140988447a05SGarrett D'Amore 		addr = GET16(M1575_SPR_REG+2);
141088447a05SGarrett D'Amore 		if (addr != reg) {
141188447a05SGarrett D'Amore 			data = 0xffff;
141288447a05SGarrett D'Amore 		}
141388447a05SGarrett D'Amore 	}
141488447a05SGarrett D'Amore 
141588447a05SGarrett D'Amore 	return (data);
141688447a05SGarrett D'Amore }
141788447a05SGarrett D'Amore 
141888447a05SGarrett D'Amore /*
141988447a05SGarrett D'Amore  * audio1575_pci_enable()
142088447a05SGarrett D'Amore  *
142188447a05SGarrett D'Amore  * Description:
142288447a05SGarrett D'Amore  *	This routine Enables all PCI IO and MEMORY accesses
142388447a05SGarrett D'Amore  *
142488447a05SGarrett D'Amore  * Arguments:
142588447a05SGarrett D'Amore  *	audio1575_state_t *statep	 The device's state structure
142688447a05SGarrett D'Amore  */
142788447a05SGarrett D'Amore static void
audio1575_pci_enable(audio1575_state_t * statep)142888447a05SGarrett D'Amore audio1575_pci_enable(audio1575_state_t *statep)
142988447a05SGarrett D'Amore {
143088447a05SGarrett D'Amore 	uint16_t pcics_reg;
143188447a05SGarrett D'Amore 
143288447a05SGarrett D'Amore 	pcics_reg = pci_config_get16(statep->pcih, PCI_CONF_COMM);
143388447a05SGarrett D'Amore 	pcics_reg |= (PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
143488447a05SGarrett D'Amore 	pci_config_put16(statep->pcih, PCI_CONF_COMM, pcics_reg);
143588447a05SGarrett D'Amore }
143688447a05SGarrett D'Amore 
143788447a05SGarrett D'Amore /*
143888447a05SGarrett D'Amore  * audio1575_pci_disable()
143988447a05SGarrett D'Amore  *
144088447a05SGarrett D'Amore  * Description:
144188447a05SGarrett D'Amore  *	This routine Disables all PCI IO and MEMORY accesses
144288447a05SGarrett D'Amore  *
144388447a05SGarrett D'Amore  * Arguments:
144488447a05SGarrett D'Amore  *	audio1575_state_t *statep	The device's state structure
144588447a05SGarrett D'Amore  */
144688447a05SGarrett D'Amore static void
audio1575_pci_disable(audio1575_state_t * statep)144788447a05SGarrett D'Amore audio1575_pci_disable(audio1575_state_t *statep)
144888447a05SGarrett D'Amore {
144988447a05SGarrett D'Amore 	uint16_t pcics_reg;
145088447a05SGarrett D'Amore 
145188447a05SGarrett D'Amore 	if (statep->pcih == NULL)
145288447a05SGarrett D'Amore 		return;
145388447a05SGarrett D'Amore 	pcics_reg = pci_config_get16(statep->pcih, PCI_CONF_COMM);
145488447a05SGarrett D'Amore 	pcics_reg &= ~(PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
145588447a05SGarrett D'Amore 	pci_config_put16(statep->pcih, PCI_CONF_COMM, pcics_reg);
145688447a05SGarrett D'Amore }
145788447a05SGarrett D'Amore 
145888447a05SGarrett D'Amore /*
145988447a05SGarrett D'Amore  * audio1575_resume()
146088447a05SGarrett D'Amore  *
146188447a05SGarrett D'Amore  * Description:
146288447a05SGarrett D'Amore  *	Resume operation of the device after sleeping or hibernating.
146388447a05SGarrett D'Amore  *	Note that this should never fail, even if hardware goes wonky,
146488447a05SGarrett D'Amore  *	because the current PM framework will panic if it does.
146588447a05SGarrett D'Amore  *
146688447a05SGarrett D'Amore  * Arguments:
146788447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
146888447a05SGarrett D'Amore  *
146988447a05SGarrett D'Amore  * Returns:
147088447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was resumed
147188447a05SGarrett D'Amore  */
147288447a05SGarrett D'Amore static int
audio1575_resume(dev_info_t * dip)147388447a05SGarrett D'Amore audio1575_resume(dev_info_t *dip)
147488447a05SGarrett D'Amore {
147588447a05SGarrett D'Amore 	audio1575_state_t	*statep;
147688447a05SGarrett D'Amore 	audio_dev_t		*adev;
147788447a05SGarrett D'Amore 
147888447a05SGarrett D'Amore 	/* we've already allocated the state structure so get ptr */
147988447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
148088447a05SGarrett D'Amore 	adev = statep->adev;
148188447a05SGarrett D'Amore 	ASSERT(!mutex_owned(&statep->lock));
148288447a05SGarrett D'Amore 
148388447a05SGarrett D'Amore 	if (audio1575_chip_init(statep) != DDI_SUCCESS) {
148488447a05SGarrett D'Amore 		/*
148588447a05SGarrett D'Amore 		 * Note that PM gurus say we should return
148688447a05SGarrett D'Amore 		 * success here.  Failure of audio shouldn't
148788447a05SGarrett D'Amore 		 * be considered FATAL to the system.  The
148888447a05SGarrett D'Amore 		 * upshot is that audio will not progress.
148988447a05SGarrett D'Amore 		 */
149088447a05SGarrett D'Amore 		audio_dev_warn(adev, "DDI_RESUME failed to init chip");
149188447a05SGarrett D'Amore 		return (DDI_SUCCESS);
149288447a05SGarrett D'Amore 	}
149388447a05SGarrett D'Amore 
149488447a05SGarrett D'Amore 	/* allow ac97 operations again */
1495*68c47f65SGarrett D'Amore 	ac97_reset(statep->ac97);
149688447a05SGarrett D'Amore 
1497*68c47f65SGarrett D'Amore 	audio_dev_resume(adev);
149888447a05SGarrett D'Amore 
149988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
150088447a05SGarrett D'Amore }
150188447a05SGarrett D'Amore 
150288447a05SGarrett D'Amore /*
150388447a05SGarrett D'Amore  * audio1575_suspend()
150488447a05SGarrett D'Amore  *
150588447a05SGarrett D'Amore  * Description:
150688447a05SGarrett D'Amore  *	Suspend an instance of the audio1575 driver.
150788447a05SGarrett D'Amore  *
150888447a05SGarrett D'Amore  * Arguments:
150988447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
151088447a05SGarrett D'Amore  *
151188447a05SGarrett D'Amore  * Returns:
151288447a05SGarrett D'Amore  *	DDI_SUCCESS	The driver was suspended
151388447a05SGarrett D'Amore  */
151488447a05SGarrett D'Amore static int
audio1575_suspend(dev_info_t * dip)151588447a05SGarrett D'Amore audio1575_suspend(dev_info_t *dip)
151688447a05SGarrett D'Amore {
151788447a05SGarrett D'Amore 	audio1575_state_t	*statep;
151888447a05SGarrett D'Amore 
151988447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
152088447a05SGarrett D'Amore 
1521*68c47f65SGarrett D'Amore 	audio_dev_suspend(statep->adev);
152288447a05SGarrett D'Amore 
152388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
152488447a05SGarrett D'Amore }
152588447a05SGarrett D'Amore 
152688447a05SGarrett D'Amore /*
152788447a05SGarrett D'Amore  * audio1575_destroy()
152888447a05SGarrett D'Amore  *
152988447a05SGarrett D'Amore  * Description:
153088447a05SGarrett D'Amore  *	This routine releases all resources held by the device instance,
153188447a05SGarrett D'Amore  *	as part of either detach or a failure in attach.
153288447a05SGarrett D'Amore  *
153388447a05SGarrett D'Amore  * Arguments:
153488447a05SGarrett D'Amore  *	audio1575_state_t	*state	The device soft state.
153588447a05SGarrett D'Amore  */
153688447a05SGarrett D'Amore void
audio1575_destroy(audio1575_state_t * statep)153788447a05SGarrett D'Amore audio1575_destroy(audio1575_state_t *statep)
153888447a05SGarrett D'Amore {
153988447a05SGarrett D'Amore 	ddi_acc_handle_t	pcih;
154088447a05SGarrett D'Amore 
154188447a05SGarrett D'Amore 	/* stop DMA engines */
15420e7a77f3SGarrett D'Amore 	audio1575_dma_stop(statep, B_FALSE);
154388447a05SGarrett D'Amore 
154488447a05SGarrett D'Amore 	if (statep->regsh != NULL) {
154588447a05SGarrett D'Amore 		/* reset the codec */
154688447a05SGarrett D'Amore 		PUT32(M1575_SCR_REG, M1575_SCR_COLDRST);
154788447a05SGarrett D'Amore 	}
154888447a05SGarrett D'Amore 
154988447a05SGarrett D'Amore 	if ((pcih = statep->pcih) != NULL) {
155088447a05SGarrett D'Amore 		/* turn off the AC_LINK clock */
155188447a05SGarrett D'Amore 		pci_config_put8(pcih, M1575_PCIACD_REG, 0);
155288447a05SGarrett D'Amore 		pci_config_put8(pcih, M1575_PCIACD_REG, 4);
155388447a05SGarrett D'Amore 		pci_config_put8(pcih, M1575_PCIACD_REG, 0);
155488447a05SGarrett D'Amore 	}
155588447a05SGarrett D'Amore 
155688447a05SGarrett D'Amore 	/* Disable PCI I/O and Memory Spaces */
155788447a05SGarrett D'Amore 	audio1575_pci_disable(statep);
155888447a05SGarrett D'Amore 
155988447a05SGarrett D'Amore 	audio1575_free_port(statep->ports[M1575_PLAY]);
156088447a05SGarrett D'Amore 	audio1575_free_port(statep->ports[M1575_REC]);
156188447a05SGarrett D'Amore 
156288447a05SGarrett D'Amore 	audio1575_unmap_regs(statep);
156388447a05SGarrett D'Amore 
156488447a05SGarrett D'Amore 	if (statep->ac97 != NULL) {
156588447a05SGarrett D'Amore 		ac97_free(statep->ac97);
156688447a05SGarrett D'Amore 	}
156788447a05SGarrett D'Amore 
156888447a05SGarrett D'Amore 	if (statep->adev != NULL) {
156988447a05SGarrett D'Amore 		audio_dev_free(statep->adev);
157088447a05SGarrett D'Amore 	}
157188447a05SGarrett D'Amore 
157288447a05SGarrett D'Amore 	kmem_free(statep, sizeof (*statep));
157388447a05SGarrett D'Amore }
1574