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  * audiots Audio Driver
2988447a05SGarrett D'Amore  *
3088447a05SGarrett D'Amore  * This Audio Driver controls the T2 audio core in the ALI M1553
3188447a05SGarrett D'Amore  * southbridge chip. This chip supports multiple play streams, but just
3288447a05SGarrett D'Amore  * a single record stream. It also supports wave table synthesis and
3388447a05SGarrett D'Amore  * hardware MIDI and joystick ports. Unfortunately the MIDI ports are
3488447a05SGarrett D'Amore  * not available because their pins have been re-assigned to expose
3588447a05SGarrett D'Amore  * interrupts. We also aren't going to do anything with the joystick
3688447a05SGarrett D'Amore  * ports. The audio core controls an AC-97 V2.1 Codec.
3788447a05SGarrett D'Amore  *
3888447a05SGarrett D'Amore  * The DMA engine uses a single buffer which is large enough to hold
3988447a05SGarrett D'Amore  * two interrupts worth of data. When it gets to the mid point an
4088447a05SGarrett D'Amore  * interrupt is generated and data is either sent (for record) or
4188447a05SGarrett D'Amore  * requested and put in that half of the buffer (for play). When the
4288447a05SGarrett D'Amore  * second half is played we do the same, but the audio core loops the
4388447a05SGarrett D'Amore  * pointer back to the beginning.
4488447a05SGarrett D'Amore  *
4588447a05SGarrett D'Amore  * The audio core has a bug in silicon that doesn't let it read the AC-97
4688447a05SGarrett D'Amore  * Codec's register. T2 has provided an algorithm that attempts to read the
4788447a05SGarrett D'Amore  * the Codec several times. This is probably heuristic and thus isn't
4888447a05SGarrett D'Amore  * absolutely guaranteed to work. However we do have to place a limit on
4988447a05SGarrett D'Amore  * the looping, otherwise when we read a valid 0x00 we would never exit
5088447a05SGarrett D'Amore  * the loop. Unfortunately there is also a problem with writing the AC-97
5188447a05SGarrett D'Amore  * Codec's registers as well. Thus we read it back to verify the write.
5288447a05SGarrett D'Amore  *
5388447a05SGarrett D'Amore  * The AC'97 common code provides shadow state for AC'97 registers for us,
5488447a05SGarrett D'Amore  * so we only need to read those registers during early startup (primarily
5588447a05SGarrett D'Amore  * to determine codec id and capabilities.)
5688447a05SGarrett D'Amore  *
5788447a05SGarrett D'Amore  * We don't save any of the audio controller registers during normal
5888447a05SGarrett D'Amore  * operation. When we need to save register state we only have to save
5988447a05SGarrett D'Amore  * the aram and eram. The rest of the controller state is never modified
6088447a05SGarrett D'Amore  * from the initial programming. Thus restoring the controller state
6188447a05SGarrett D'Amore  * can be done from audiots_chip_init() as well.
6288447a05SGarrett D'Amore  *
6388447a05SGarrett D'Amore  *
6488447a05SGarrett D'Amore  * WARNING: The SME birdsnest platform uses a PCI bridge chip between the
6588447a05SGarrett D'Amore  *	CPU and the southbridge containing the audio core. There is
6688447a05SGarrett D'Amore  *	a bug in silicon that causes a bogus parity error. With the mixer
6788447a05SGarrett D'Amore  *	reimplementation project, Bug 4374774, the audio driver is always
6888447a05SGarrett D'Amore  *	set to the best precision and number of channels. Thus when turning
6988447a05SGarrett D'Amore  *	the mixer on and off the only thing that changes is the sample rate.
7088447a05SGarrett D'Amore  *	This change in programming doesn't trigger the silicon error.
7188447a05SGarrett D'Amore  *	Thus the supported channels must always be 2 and the precision
7288447a05SGarrett D'Amore  *	must always be 16-bits. This will keep any future change in the
7388447a05SGarrett D'Amore  *	mixer from exposing this bug.
7488447a05SGarrett D'Amore  *
7588447a05SGarrett D'Amore  * Due to a hardware bug, system power management is not supported by this
7688447a05SGarrett D'Amore  * driver.
7788447a05SGarrett D'Amore  *
7888447a05SGarrett D'Amore  *	CAUTION: If audio controller state is changed outside of aram
7988447a05SGarrett D'Amore  *		and eram then that information must be saved and restored
8088447a05SGarrett D'Amore  *		during power management shutdown and bringup.
8188447a05SGarrett D'Amore  *
8288447a05SGarrett D'Amore  *	NOTE: The AC-97 Codec's reset pin is set to PCI reset, so we
8388447a05SGarrett D'Amore  *		can't power down the Codec all the way.
8488447a05SGarrett D'Amore  *
8588447a05SGarrett D'Amore  *	NOTE: This driver depends on the drv/audio and misc/ac97
8688447a05SGarrett D'Amore  *		modules being loaded first.
8788447a05SGarrett D'Amore  *
8888447a05SGarrett D'Amore  *	NOTE: Don't OR the ap_stop register to stop a play or record. This
8988447a05SGarrett D'Amore  *		will just stop all active channels because a read of ap_stop
9088447a05SGarrett D'Amore  *		returns ap_start. Just set the ap_stop register with the
9188447a05SGarrett D'Amore  *		channels you want to stop. The same goes for ap_start.
9288447a05SGarrett D'Amore  *
9388447a05SGarrett D'Amore  *	NOTE: There is a hardware problem with P2 rev motherboards. After
9488447a05SGarrett D'Amore  *		prolonged use, reading the AC97 register will always return
9588447a05SGarrett D'Amore  *		busy. The AC97 register is now useless. Consequently, we are no
9688447a05SGarrett D'Amore  *		longer able to program the Codec. This work around disables
9788447a05SGarrett D'Amore  *		audio when this state is detected. It's not great, but its
9888447a05SGarrett D'Amore  *		better than having audio blasting out at 100% all the time.
9988447a05SGarrett D'Amore  *
10088447a05SGarrett D'Amore  *	NOTE: Power Management testing has also exposed this AC97 timeout
10188447a05SGarrett D'Amore  *		problem. Management has decided this is too risky for customers
10288447a05SGarrett D'Amore  *		and hence they want power management support removed from the
10388447a05SGarrett D'Amore  *		audio subsystem. All PM support is now removed.
10488447a05SGarrett D'Amore  */
10588447a05SGarrett D'Amore 
106*68c47f65SGarrett D'Amore /*
107*68c47f65SGarrett D'Amore  * Synchronization notes:
108*68c47f65SGarrett D'Amore  *
109*68c47f65SGarrett D'Amore  * The audio framework guarantees that our entry points are exclusive
110*68c47f65SGarrett D'Amore  * with suspend and resume.  This includes data flow and control entry
111*68c47f65SGarrett D'Amore  * points alike.
112*68c47f65SGarrett D'Amore  *
113*68c47f65SGarrett D'Amore  * The audio framework guarantees that only one control is being
114*68c47f65SGarrett D'Amore  * accessed on any given audio device at a time.
115*68c47f65SGarrett D'Amore  *
116*68c47f65SGarrett D'Amore  * The audio framework guarantees that entry points are themselves
117*68c47f65SGarrett D'Amore  * serialized for a given engine.
118*68c47f65SGarrett D'Amore  *
119*68c47f65SGarrett D'Amore  * We have no interrupt routine or other internal asynchronous routines.
120*68c47f65SGarrett D'Amore  *
121*68c47f65SGarrett D'Amore  * Our device uses completely separate registers for each engine,
122*68c47f65SGarrett D'Amore  * except for the start/stop registers, which are implemented in a
123*68c47f65SGarrett D'Amore  * manner that allows for them to be accessed concurrently safely from
124*68c47f65SGarrett D'Amore  * different threads.
125*68c47f65SGarrett D'Amore  *
126*68c47f65SGarrett D'Amore  * Hence, it turns out that we simply don't need any locking in this
127*68c47f65SGarrett D'Amore  * driver.
128*68c47f65SGarrett D'Amore  */
129*68c47f65SGarrett D'Amore 
13088447a05SGarrett D'Amore #include <sys/modctl.h>
13188447a05SGarrett D'Amore #include <sys/kmem.h>
13288447a05SGarrett D'Amore #include <sys/pci.h>
13388447a05SGarrett D'Amore #include <sys/ddi.h>
13488447a05SGarrett D'Amore #include <sys/sunddi.h>
13588447a05SGarrett D'Amore #include <sys/debug.h>
13688447a05SGarrett D'Amore #include <sys/note.h>
13788447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
13888447a05SGarrett D'Amore #include <sys/audio/ac97.h>
13988447a05SGarrett D'Amore #include "audiots.h"
14088447a05SGarrett D'Amore 
14188447a05SGarrett D'Amore /*
14288447a05SGarrett D'Amore  * Module linkage routines for the kernel
14388447a05SGarrett D'Amore  */
14488447a05SGarrett D'Amore static int audiots_attach(dev_info_t *, ddi_attach_cmd_t);
14588447a05SGarrett D'Amore static int audiots_detach(dev_info_t *, ddi_detach_cmd_t);
14688447a05SGarrett D'Amore static int audiots_quiesce(dev_info_t *);
14788447a05SGarrett D'Amore 
14888447a05SGarrett D'Amore /*
14988447a05SGarrett D'Amore  * Entry point routine prototypes
15088447a05SGarrett D'Amore  */
151*68c47f65SGarrett D'Amore static int audiots_open(void *, int, unsigned *, caddr_t *);
15288447a05SGarrett D'Amore static void audiots_close(void *);
15388447a05SGarrett D'Amore static int audiots_start(void *);
15488447a05SGarrett D'Amore static void audiots_stop(void *);
15588447a05SGarrett D'Amore static int audiots_format(void *);
15688447a05SGarrett D'Amore static int audiots_channels(void *);
15788447a05SGarrett D'Amore static int audiots_rate(void *);
15888447a05SGarrett D'Amore static void audiots_chinfo(void *, int, unsigned *, unsigned *);
15988447a05SGarrett D'Amore static uint64_t audiots_count(void *);
16088447a05SGarrett D'Amore static void audiots_sync(void *, unsigned);
16188447a05SGarrett D'Amore 
16288447a05SGarrett D'Amore static audio_engine_ops_t	audiots_engine_ops = {
16388447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
16488447a05SGarrett D'Amore 	audiots_open,
16588447a05SGarrett D'Amore 	audiots_close,
16688447a05SGarrett D'Amore 	audiots_start,
16788447a05SGarrett D'Amore 	audiots_stop,
16888447a05SGarrett D'Amore 	audiots_count,
16988447a05SGarrett D'Amore 	audiots_format,
17088447a05SGarrett D'Amore 	audiots_channels,
17188447a05SGarrett D'Amore 	audiots_rate,
17288447a05SGarrett D'Amore 	audiots_sync,
173f9ead4a5SGarrett D'Amore 	NULL,
174f9ead4a5SGarrett D'Amore 	audiots_chinfo,
175f9ead4a5SGarrett D'Amore 	NULL,
17688447a05SGarrett D'Amore };
17788447a05SGarrett D'Amore 
17888447a05SGarrett D'Amore /*
17988447a05SGarrett D'Amore  * Local Routine Prototypes
18088447a05SGarrett D'Amore  */
18188447a05SGarrett D'Amore static void audiots_power_up(audiots_state_t *);
18288447a05SGarrett D'Amore static void audiots_chip_init(audiots_state_t *);
18388447a05SGarrett D'Amore static uint16_t audiots_get_ac97(void *, uint8_t);
18488447a05SGarrett D'Amore static void audiots_set_ac97(void *, uint8_t, uint16_t);
18588447a05SGarrett D'Amore static int audiots_init_state(audiots_state_t *, dev_info_t *);
18688447a05SGarrett D'Amore static int audiots_map_regs(dev_info_t *, audiots_state_t *);
18788447a05SGarrett D'Amore static uint16_t audiots_read_ac97(audiots_state_t *, int);
18888447a05SGarrett D'Amore static void audiots_stop_everything(audiots_state_t *);
18988447a05SGarrett D'Amore static void audiots_destroy(audiots_state_t *);
19088447a05SGarrett D'Amore static int audiots_alloc_port(audiots_state_t *, int);
19188447a05SGarrett D'Amore 
19288447a05SGarrett D'Amore /*
19388447a05SGarrett D'Amore  * Global variables, but viewable only by this file.
19488447a05SGarrett D'Amore  */
19588447a05SGarrett D'Amore 
19688447a05SGarrett D'Amore /* anchor for soft state structures */
19788447a05SGarrett D'Amore static void *audiots_statep;
19888447a05SGarrett D'Amore 
19988447a05SGarrett D'Amore /*
20088447a05SGarrett D'Amore  * DDI Structures
20188447a05SGarrett D'Amore  */
20288447a05SGarrett D'Amore 
20388447a05SGarrett D'Amore /* Device operations structure */
20488447a05SGarrett D'Amore static struct dev_ops audiots_dev_ops = {
20588447a05SGarrett D'Amore 	DEVO_REV,		/* devo_rev */
20688447a05SGarrett D'Amore 	0,			/* devo_refcnt */
20788447a05SGarrett D'Amore 	NULL,			/* devo_getinfo */
20888447a05SGarrett D'Amore 	nulldev,		/* devo_identify - obsolete */
20988447a05SGarrett D'Amore 	nulldev,		/* devo_probe */
21088447a05SGarrett D'Amore 	audiots_attach,		/* devo_attach */
21188447a05SGarrett D'Amore 	audiots_detach,		/* devo_detach */
21288447a05SGarrett D'Amore 	nodev,			/* devo_reset */
21388447a05SGarrett D'Amore 	NULL,			/* devo_cb_ops */
21488447a05SGarrett D'Amore 	NULL,			/* devo_bus_ops */
21588447a05SGarrett D'Amore 	NULL,			/* devo_power */
21688447a05SGarrett D'Amore 	audiots_quiesce,	/* devo_quiesce */
21788447a05SGarrett D'Amore };
21888447a05SGarrett D'Amore 
21988447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
22088447a05SGarrett D'Amore static struct modldrv audiots_modldrv = {
22188447a05SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
22288447a05SGarrett D'Amore 	TS_MOD_NAME,		/* drv_linkinfo */
22388447a05SGarrett D'Amore 	&audiots_dev_ops	/* drv_dev_ops */
22488447a05SGarrett D'Amore };
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore /* Module linkage structure */
22788447a05SGarrett D'Amore static struct modlinkage audiots_modlinkage = {
22888447a05SGarrett D'Amore 	MODREV_1,			/* ml_rev */
22988447a05SGarrett D'Amore 	(void *)&audiots_modldrv,	/* ml_linkage */
23088447a05SGarrett D'Amore 	NULL				/* NULL terminates the list */
23188447a05SGarrett D'Amore };
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 
23488447a05SGarrett D'Amore /*
23588447a05SGarrett D'Amore  * NOTE: Grover OBP v4.0.166 and rev G of the ALI Southbridge chip force the
23688447a05SGarrett D'Amore  * audiots driver to use the upper 2 GB DMA address range. However to maintain
23788447a05SGarrett D'Amore  * backwards compatibility with older systems/OBP, we're going to try the full
23888447a05SGarrett D'Amore  * 4 GB DMA range.
23988447a05SGarrett D'Amore  *
24088447a05SGarrett D'Amore  * Eventually, this will be set back to using the proper high 2 GB DMA range.
24188447a05SGarrett D'Amore  */
24288447a05SGarrett D'Amore 
24388447a05SGarrett D'Amore /* Device attribute structure - full 4 gig address range */
24488447a05SGarrett D'Amore static ddi_dma_attr_t audiots_attr = {
24588447a05SGarrett D'Amore 	DMA_ATTR_VERSION,		/* version */
24688447a05SGarrett D'Amore 	0x0000000000000000LL,		/* dlim_addr_lo */
24788447a05SGarrett D'Amore 	0x00000000ffffffffLL,		/* dlim_addr_hi */
24888447a05SGarrett D'Amore 	0x0000000000003fffLL,		/* DMA counter register - 16 bits */
24988447a05SGarrett D'Amore 	0x0000000000000008LL,		/* DMA address alignment, 64-bit */
25088447a05SGarrett D'Amore 	0x0000007f,			/* 1 through 64 byte burst sizes */
25188447a05SGarrett D'Amore 	0x00000001,			/* min effective DMA size */
25288447a05SGarrett D'Amore 	0x0000000000003fffLL,		/* maximum transfer size, 16k */
25388447a05SGarrett D'Amore 	0x000000000000ffffLL,		/* segment boundary, 64k */
25488447a05SGarrett D'Amore 	0x00000001,			/* s/g list length, no s/g */
25588447a05SGarrett D'Amore 	0x00000001,			/* granularity of device, don't care */
25688447a05SGarrett D'Amore 	0				/* DMA flags */
25788447a05SGarrett D'Amore };
25888447a05SGarrett D'Amore 
25988447a05SGarrett D'Amore static ddi_device_acc_attr_t ts_acc_attr = {
26088447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
26188447a05SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
26288447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
26388447a05SGarrett D'Amore };
26488447a05SGarrett D'Amore 
26588447a05SGarrett D'Amore static ddi_device_acc_attr_t ts_regs_attr = {
26688447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
26788447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
26888447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
26988447a05SGarrett D'Amore };
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore /*
27288447a05SGarrett D'Amore  * _init()
27388447a05SGarrett D'Amore  *
27488447a05SGarrett D'Amore  * Description:
27588447a05SGarrett D'Amore  *	Driver initialization, called when driver is first loaded.
27688447a05SGarrett D'Amore  *	This is how access is initially given to all the static structures.
27788447a05SGarrett D'Amore  *
27888447a05SGarrett D'Amore  * Arguments:
27988447a05SGarrett D'Amore  *	None
28088447a05SGarrett D'Amore  *
28188447a05SGarrett D'Amore  * Returns:
28288447a05SGarrett D'Amore  *	ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
28388447a05SGarrett D'Amore  *	mod_install() status, see mod_install(9f)
28488447a05SGarrett D'Amore  */
28588447a05SGarrett D'Amore int
_init(void)28688447a05SGarrett D'Amore _init(void)
28788447a05SGarrett D'Amore {
28888447a05SGarrett D'Amore 	int		error;
28988447a05SGarrett D'Amore 
29088447a05SGarrett D'Amore 	audio_init_ops(&audiots_dev_ops, TS_NAME);
29188447a05SGarrett D'Amore 
29288447a05SGarrett D'Amore 	/* initialize the soft state */
29388447a05SGarrett D'Amore 	if ((error = ddi_soft_state_init(&audiots_statep,
29488447a05SGarrett D'Amore 	    sizeof (audiots_state_t), 1)) != 0) {
29588447a05SGarrett D'Amore 		audio_fini_ops(&audiots_dev_ops);
29688447a05SGarrett D'Amore 		return (error);
29788447a05SGarrett D'Amore 	}
29888447a05SGarrett D'Amore 
29988447a05SGarrett D'Amore 	if ((error = mod_install(&audiots_modlinkage)) != 0) {
30088447a05SGarrett D'Amore 		audio_fini_ops(&audiots_dev_ops);
30188447a05SGarrett D'Amore 		ddi_soft_state_fini(&audiots_statep);
30288447a05SGarrett D'Amore 	}
30388447a05SGarrett D'Amore 
30488447a05SGarrett D'Amore 	return (error);
30588447a05SGarrett D'Amore }
30688447a05SGarrett D'Amore 
30788447a05SGarrett D'Amore /*
30888447a05SGarrett D'Amore  * _fini()
30988447a05SGarrett D'Amore  *
31088447a05SGarrett D'Amore  * Description:
31188447a05SGarrett D'Amore  *	Module de-initialization, called when the driver is to be unloaded.
31288447a05SGarrett D'Amore  *
31388447a05SGarrett D'Amore  * Arguments:
31488447a05SGarrett D'Amore  *	None
31588447a05SGarrett D'Amore  *
31688447a05SGarrett D'Amore  * Returns:
31788447a05SGarrett D'Amore  *	mod_remove() status, see mod_remove(9f)
31888447a05SGarrett D'Amore  */
31988447a05SGarrett D'Amore int
_fini(void)32088447a05SGarrett D'Amore _fini(void)
32188447a05SGarrett D'Amore {
32288447a05SGarrett D'Amore 	int		error;
32388447a05SGarrett D'Amore 
32488447a05SGarrett D'Amore 	if ((error = mod_remove(&audiots_modlinkage)) != 0) {
32588447a05SGarrett D'Amore 		return (error);
32688447a05SGarrett D'Amore 	}
32788447a05SGarrett D'Amore 
32888447a05SGarrett D'Amore 	/* free the soft state internal structures */
32988447a05SGarrett D'Amore 	ddi_soft_state_fini(&audiots_statep);
33088447a05SGarrett D'Amore 
33188447a05SGarrett D'Amore 	/* clean up ops */
33288447a05SGarrett D'Amore 	audio_fini_ops(&audiots_dev_ops);
33388447a05SGarrett D'Amore 
33488447a05SGarrett D'Amore 	return (0);
33588447a05SGarrett D'Amore }
33688447a05SGarrett D'Amore 
33788447a05SGarrett D'Amore /*
33888447a05SGarrett D'Amore  * _info()
33988447a05SGarrett D'Amore  *
34088447a05SGarrett D'Amore  * Description:
34188447a05SGarrett D'Amore  *	Module information, returns infomation about the driver.
34288447a05SGarrett D'Amore  *
34388447a05SGarrett D'Amore  * Arguments:
34488447a05SGarrett D'Amore  *	modinfo *modinfop	Pointer to the opaque modinfo structure
34588447a05SGarrett D'Amore  *
34688447a05SGarrett D'Amore  * Returns:
34788447a05SGarrett D'Amore  *	mod_info() status, see mod_info(9f)
34888447a05SGarrett D'Amore  */
34988447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)35088447a05SGarrett D'Amore _info(struct modinfo *modinfop)
35188447a05SGarrett D'Amore {
35288447a05SGarrett D'Amore 	int		error;
35388447a05SGarrett D'Amore 
35488447a05SGarrett D'Amore 	error = mod_info(&audiots_modlinkage, modinfop);
35588447a05SGarrett D'Amore 
35688447a05SGarrett D'Amore 	return (error);
35788447a05SGarrett D'Amore }
35888447a05SGarrett D'Amore 
35988447a05SGarrett D'Amore 
36088447a05SGarrett D'Amore /*
36188447a05SGarrett D'Amore  * audiots_attach()
36288447a05SGarrett D'Amore  *
36388447a05SGarrett D'Amore  * Description:
36488447a05SGarrett D'Amore  *	Attach an instance of the audiots driver. This routine does the
36588447a05SGarrett D'Amore  *	device dependent attach tasks.
36688447a05SGarrett D'Amore  *
36788447a05SGarrett D'Amore  * Arguments:
36888447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
36988447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
37088447a05SGarrett D'Amore  *
37188447a05SGarrett D'Amore  * Returns:
37288447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
37388447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
37488447a05SGarrett D'Amore  */
37588447a05SGarrett D'Amore static int
audiots_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)37688447a05SGarrett D'Amore audiots_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
37788447a05SGarrett D'Amore {
37888447a05SGarrett D'Amore 	audiots_state_t		*state;
37988447a05SGarrett D'Amore 	int			instance;
38088447a05SGarrett D'Amore 
38188447a05SGarrett D'Amore 	instance = ddi_get_instance(dip);
38288447a05SGarrett D'Amore 
38388447a05SGarrett D'Amore 	switch (cmd) {
38488447a05SGarrett D'Amore 	case DDI_ATTACH:
38588447a05SGarrett D'Amore 		break;
38688447a05SGarrett D'Amore 	case DDI_RESUME:
38788447a05SGarrett D'Amore 
38888447a05SGarrett D'Amore 		/* we've already allocated the state structure so get ptr */
389*68c47f65SGarrett D'Amore 		state = ddi_get_soft_state(audiots_statep, instance);
39088447a05SGarrett D'Amore 		ASSERT(dip == state->ts_dip);
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore 		/* suspend/resume resets the chip, so we have no more faults */
39388447a05SGarrett D'Amore 		if (state->ts_flags & TS_AUDIO_READ_FAILED) {
39488447a05SGarrett D'Amore 			ddi_dev_report_fault(state->ts_dip,
39588447a05SGarrett D'Amore 			    DDI_SERVICE_RESTORED,
39688447a05SGarrett D'Amore 			    DDI_DEVICE_FAULT,
39788447a05SGarrett D'Amore 			    "check port, gain, balance, and mute settings");
39888447a05SGarrett D'Amore 			/* and clear the fault state flags */
39988447a05SGarrett D'Amore 			state->ts_flags &=
40088447a05SGarrett D'Amore 			    ~(TS_AUDIO_READ_FAILED|TS_READ_FAILURE_PRINTED);
40188447a05SGarrett D'Amore 		}
40288447a05SGarrett D'Amore 
40388447a05SGarrett D'Amore 		audiots_power_up(state);
40488447a05SGarrett D'Amore 		audiots_chip_init(state);
40588447a05SGarrett D'Amore 
406*68c47f65SGarrett D'Amore 		ac97_reset(state->ts_ac97);
407*68c47f65SGarrett D'Amore 
408*68c47f65SGarrett D'Amore 		audio_dev_resume(state->ts_adev);
40988447a05SGarrett D'Amore 
41088447a05SGarrett D'Amore 		return (DDI_SUCCESS);
41188447a05SGarrett D'Amore 
41288447a05SGarrett D'Amore 	default:
41388447a05SGarrett D'Amore 		return (DDI_FAILURE);
41488447a05SGarrett D'Amore 	}
41588447a05SGarrett D'Amore 
41688447a05SGarrett D'Amore 	/* before we do anything make sure that we haven't had a h/w failure */
41788447a05SGarrett D'Amore 	if (ddi_get_devstate(dip) == DDI_DEVSTATE_DOWN) {
41888447a05SGarrett D'Amore 		cmn_err(CE_WARN, "%s%d: The audio hardware has "
419*68c47f65SGarrett D'Amore 		    "been disabled.", ddi_driver_name(dip), instance);
42088447a05SGarrett D'Amore 		cmn_err(CE_CONT, "Please reboot to restore audio.");
42188447a05SGarrett D'Amore 		return (DDI_FAILURE);
42288447a05SGarrett D'Amore 	}
42388447a05SGarrett D'Amore 
42488447a05SGarrett D'Amore 	/* allocate the state structure */
42588447a05SGarrett D'Amore 	if (ddi_soft_state_zalloc(audiots_statep, instance) == DDI_FAILURE) {
42688447a05SGarrett D'Amore 		cmn_err(CE_WARN, "!%s%d: soft state allocate failed",
427*68c47f65SGarrett D'Amore 		    ddi_driver_name(dip), instance);
42888447a05SGarrett D'Amore 		return (DDI_FAILURE);
42988447a05SGarrett D'Amore 	}
43088447a05SGarrett D'Amore 
43188447a05SGarrett D'Amore 	/*
43288447a05SGarrett D'Amore 	 * WARNING: From here on all errors require that we free memory,
43388447a05SGarrett D'Amore 	 *	including the state structure.
43488447a05SGarrett D'Amore 	 */
43588447a05SGarrett D'Amore 
43688447a05SGarrett D'Amore 	/* get the state structure - cannot fail */
43788447a05SGarrett D'Amore 	state = ddi_get_soft_state(audiots_statep, instance);
43888447a05SGarrett D'Amore 	ASSERT(state != NULL);
43988447a05SGarrett D'Amore 
44088447a05SGarrett D'Amore 	if ((state->ts_adev = audio_dev_alloc(dip, 0)) == NULL) {
44188447a05SGarrett D'Amore 		cmn_err(CE_WARN, "unable to allocate audio dev");
44288447a05SGarrett D'Amore 		goto error;
44388447a05SGarrett D'Amore 	}
44488447a05SGarrett D'Amore 
44588447a05SGarrett D'Amore 	/* map in the registers, allocate DMA buffers, etc. */
44688447a05SGarrett D'Amore 	if (audiots_map_regs(dip, state) == DDI_FAILURE) {
44788447a05SGarrett D'Amore 		audio_dev_warn(state->ts_adev, "unable to map registers");
44888447a05SGarrett D'Amore 		goto error;
44988447a05SGarrett D'Amore 	}
45088447a05SGarrett D'Amore 
45188447a05SGarrett D'Amore 	/* initialize the audio state structures */
45288447a05SGarrett D'Amore 	if (audiots_init_state(state, dip) == DDI_FAILURE) {
45388447a05SGarrett D'Amore 		audio_dev_warn(state->ts_adev, "init state structure failed");
45488447a05SGarrett D'Amore 		goto error;
45588447a05SGarrett D'Amore 	}
45688447a05SGarrett D'Amore 
45788447a05SGarrett D'Amore 	/* power up */
45888447a05SGarrett D'Amore 	audiots_power_up(state);
45988447a05SGarrett D'Amore 
46088447a05SGarrett D'Amore 	/* initialize the audio controller */
46188447a05SGarrett D'Amore 	audiots_chip_init(state);
46288447a05SGarrett D'Amore 
46388447a05SGarrett D'Amore 	/* initialize the AC-97 Codec */
464505c7a69SGarrett D'Amore 	if (ac97_init(state->ts_ac97, state->ts_adev) != 0) {
465505c7a69SGarrett D'Amore 		goto error;
466505c7a69SGarrett D'Amore 	}
46788447a05SGarrett D'Amore 
46888447a05SGarrett D'Amore 	/* put the engine interrupts into a known state -- all off */
46988447a05SGarrett D'Amore 	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
47088447a05SGarrett D'Amore 	    TS_ALL_DMA_OFF);
47188447a05SGarrett D'Amore 
47288447a05SGarrett D'Amore 	/* call the framework attach routine */
47388447a05SGarrett D'Amore 	if (audio_dev_register(state->ts_adev) != DDI_SUCCESS) {
47488447a05SGarrett D'Amore 		audio_dev_warn(state->ts_adev, "unable to register audio");
47588447a05SGarrett D'Amore 		goto error;
47688447a05SGarrett D'Amore 	}
47788447a05SGarrett D'Amore 
47888447a05SGarrett D'Amore 	/* everything worked out, so report the device */
47988447a05SGarrett D'Amore 	ddi_report_dev(dip);
48088447a05SGarrett D'Amore 
48188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
48288447a05SGarrett D'Amore 
48388447a05SGarrett D'Amore error:
48488447a05SGarrett D'Amore 	audiots_destroy(state);
48588447a05SGarrett D'Amore 	return (DDI_FAILURE);
48688447a05SGarrett D'Amore }
48788447a05SGarrett D'Amore 
48888447a05SGarrett D'Amore /*
48988447a05SGarrett D'Amore  * audiots_detach()
49088447a05SGarrett D'Amore  *
49188447a05SGarrett D'Amore  * Description:
49288447a05SGarrett D'Amore  *	Detach an instance of the audiots driver.
49388447a05SGarrett D'Amore  *
49488447a05SGarrett D'Amore  * Arguments:
49588447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
49688447a05SGarrett D'Amore  *	ddi_detach_cmd_t cmd	Detach command
49788447a05SGarrett D'Amore  *
49888447a05SGarrett D'Amore  * Returns:
49988447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was detached
50088447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be detached
50188447a05SGarrett D'Amore  */
50288447a05SGarrett D'Amore static int
audiots_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)50388447a05SGarrett D'Amore audiots_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
50488447a05SGarrett D'Amore {
50588447a05SGarrett D'Amore 	audiots_state_t		*state;
50688447a05SGarrett D'Amore 	int			instance;
50788447a05SGarrett D'Amore 
50888447a05SGarrett D'Amore 	instance = ddi_get_instance(dip);
50988447a05SGarrett D'Amore 
51088447a05SGarrett D'Amore 	/* get the state structure */
51188447a05SGarrett D'Amore 	if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
51288447a05SGarrett D'Amore 		cmn_err(CE_WARN, "!%s%d: detach get soft state failed",
513*68c47f65SGarrett D'Amore 		    ddi_driver_name(dip), instance);
51488447a05SGarrett D'Amore 		return (DDI_FAILURE);
51588447a05SGarrett D'Amore 	}
51688447a05SGarrett D'Amore 
51788447a05SGarrett D'Amore 	switch (cmd) {
51888447a05SGarrett D'Amore 	case DDI_DETACH:
51988447a05SGarrett D'Amore 		break;
52088447a05SGarrett D'Amore 	case DDI_SUSPEND:
52188447a05SGarrett D'Amore 
522*68c47f65SGarrett D'Amore 		audio_dev_suspend(state->ts_adev);
52388447a05SGarrett D'Amore 
52488447a05SGarrett D'Amore 		/* stop playing and recording */
52588447a05SGarrett D'Amore 		(void) audiots_stop_everything(state);
52688447a05SGarrett D'Amore 
52788447a05SGarrett D'Amore 		return (DDI_SUCCESS);
52888447a05SGarrett D'Amore 
52988447a05SGarrett D'Amore 	default:
53088447a05SGarrett D'Amore 		return (DDI_FAILURE);
53188447a05SGarrett D'Amore 	}
53288447a05SGarrett D'Amore 
53388447a05SGarrett D'Amore 	/* attempt to unregister from the framework first */
53488447a05SGarrett D'Amore 	if (audio_dev_unregister(state->ts_adev) != DDI_SUCCESS) {
53588447a05SGarrett D'Amore 		return (DDI_FAILURE);
53688447a05SGarrett D'Amore 	}
53788447a05SGarrett D'Amore 
53888447a05SGarrett D'Amore 	audiots_destroy(state);
53988447a05SGarrett D'Amore 
54088447a05SGarrett D'Amore 	return (DDI_SUCCESS);
54188447a05SGarrett D'Amore 
54288447a05SGarrett D'Amore }
54388447a05SGarrett D'Amore 
54488447a05SGarrett D'Amore /*
54588447a05SGarrett D'Amore  * audiots_quiesce()
54688447a05SGarrett D'Amore  *
54788447a05SGarrett D'Amore  * Description:
54888447a05SGarrett D'Amore  *	Quiesce an instance of the audiots driver. Stops all DMA and
54988447a05SGarrett D'Amore  *	interrupts.
55088447a05SGarrett D'Amore  *
55188447a05SGarrett D'Amore  * Arguments:
55288447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
55388447a05SGarrett D'Amore  *
55488447a05SGarrett D'Amore  * Returns:
55588447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was quiesced
55688447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was NOT quiesced
55788447a05SGarrett D'Amore  */
55888447a05SGarrett D'Amore static int
audiots_quiesce(dev_info_t * dip)55988447a05SGarrett D'Amore audiots_quiesce(dev_info_t *dip)
56088447a05SGarrett D'Amore {
56188447a05SGarrett D'Amore 	audiots_state_t		*state;
56288447a05SGarrett D'Amore 	int			instance;
56388447a05SGarrett D'Amore 
56488447a05SGarrett D'Amore 	instance = ddi_get_instance(dip);
56588447a05SGarrett D'Amore 
56688447a05SGarrett D'Amore 	/* get the state structure */
56788447a05SGarrett D'Amore 	if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
56888447a05SGarrett D'Amore 		return (DDI_FAILURE);
56988447a05SGarrett D'Amore 	}
57088447a05SGarrett D'Amore 
57188447a05SGarrett D'Amore 	audiots_stop_everything(state);
57288447a05SGarrett D'Amore 
57388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
57488447a05SGarrett D'Amore }
57588447a05SGarrett D'Amore 
57688447a05SGarrett D'Amore /*
57788447a05SGarrett D'Amore  * audiots_power_up()
57888447a05SGarrett D'Amore  *
57988447a05SGarrett D'Amore  * Description
58088447a05SGarrett D'Amore  *	Ensure that the device is running in PCI power state D0.
58188447a05SGarrett D'Amore  */
58288447a05SGarrett D'Amore static void
audiots_power_up(audiots_state_t * state)58388447a05SGarrett D'Amore audiots_power_up(audiots_state_t *state)
58488447a05SGarrett D'Amore {
58588447a05SGarrett D'Amore 	ddi_acc_handle_t	pcih = state->ts_pcih;
58688447a05SGarrett D'Amore 	uint8_t			ptr;
58788447a05SGarrett D'Amore 	uint16_t		pmcsr;
58888447a05SGarrett D'Amore 
58988447a05SGarrett D'Amore 	if ((pci_config_get16(pcih, PCI_CONF_STAT) & PCI_STAT_CAP) == 0) {
59088447a05SGarrett D'Amore 		/* does not implement PCI capabilities -- no PM */
59188447a05SGarrett D'Amore 		return;
59288447a05SGarrett D'Amore 	}
59388447a05SGarrett D'Amore 
59488447a05SGarrett D'Amore 	ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
59588447a05SGarrett D'Amore 	for (;;) {
59688447a05SGarrett D'Amore 		if (ptr == PCI_CAP_NEXT_PTR_NULL) {
59788447a05SGarrett D'Amore 			/* PM capability not found */
59888447a05SGarrett D'Amore 			return;
59988447a05SGarrett D'Amore 		}
60088447a05SGarrett D'Amore 		if (pci_config_get8(pcih, ptr + PCI_CAP_ID) == PCI_CAP_ID_PM) {
60188447a05SGarrett D'Amore 			/* found it */
60288447a05SGarrett D'Amore 			break;
60388447a05SGarrett D'Amore 		}
60488447a05SGarrett D'Amore 		ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR);
60588447a05SGarrett D'Amore 	}
60688447a05SGarrett D'Amore 
60788447a05SGarrett D'Amore 	/* if we got here, then got valid PMCSR pointer */
60888447a05SGarrett D'Amore 	ptr += PCI_PMCSR;
60988447a05SGarrett D'Amore 
61088447a05SGarrett D'Amore 	/* check to see if we are already in state D0 */
61188447a05SGarrett D'Amore 	pmcsr = pci_config_get16(pcih, ptr);
61288447a05SGarrett D'Amore 	if ((pmcsr & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_D0) {
61388447a05SGarrett D'Amore 
61488447a05SGarrett D'Amore 		/* D3hot (or any other state) -> D0 */
61588447a05SGarrett D'Amore 		pmcsr &= ~PCI_PMCSR_STATE_MASK;
61688447a05SGarrett D'Amore 		pmcsr |= PCI_PMCSR_D0;
61788447a05SGarrett D'Amore 		pci_config_put16(pcih, ptr, pmcsr);
61888447a05SGarrett D'Amore 	}
61988447a05SGarrett D'Amore 
62088447a05SGarrett D'Amore 	/*
62188447a05SGarrett D'Amore 	 * Wait for it to power up - PCI spec says 10 ms is enough.
62288447a05SGarrett D'Amore 	 * We double it.  Note that no locks are held when this routine
62388447a05SGarrett D'Amore 	 * is called, so we can sleep (we are in attach context only).
62488447a05SGarrett D'Amore 	 *
62588447a05SGarrett D'Amore 	 * We do this delay even if already powerd up, just to make
62688447a05SGarrett D'Amore 	 * sure we aren't seeing something that *just* transitioned
62788447a05SGarrett D'Amore 	 * into D0 state.
62888447a05SGarrett D'Amore 	 */
62988447a05SGarrett D'Amore 	delay(drv_usectohz(TS_20MS));
63088447a05SGarrett D'Amore 
63188447a05SGarrett D'Amore 	/* clear PME# flag */
63288447a05SGarrett D'Amore 	pmcsr = pci_config_get16(pcih, ptr);
63388447a05SGarrett D'Amore 	pci_config_put16(pcih, ptr, pmcsr | PCI_PMCSR_PME_STAT);
63488447a05SGarrett D'Amore }
63588447a05SGarrett D'Amore 
63688447a05SGarrett D'Amore /*
63788447a05SGarrett D'Amore  * audiots_chip_init()
63888447a05SGarrett D'Amore  *
63988447a05SGarrett D'Amore  * Description:
64088447a05SGarrett D'Amore  *	Initialize the audio core.
64188447a05SGarrett D'Amore  *
64288447a05SGarrett D'Amore  * Arguments:
64388447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
64488447a05SGarrett D'Amore  */
64588447a05SGarrett D'Amore static void
audiots_chip_init(audiots_state_t * state)64688447a05SGarrett D'Amore audiots_chip_init(audiots_state_t *state)
64788447a05SGarrett D'Amore {
64888447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = state->ts_acch;
64988447a05SGarrett D'Amore 	audiots_regs_t		*regs = state->ts_regs;
65088447a05SGarrett D'Amore 	int			str;
65188447a05SGarrett D'Amore 
65288447a05SGarrett D'Amore 	/* start with all interrupts & dma channels disabled */
65388447a05SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_stop, TS_ALL_DMA_ENGINES);
65488447a05SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_ainten, TS_ALL_DMA_OFF);
65588447a05SGarrett D'Amore 
65688447a05SGarrett D'Amore 	/* set global music and wave volume to 0dB */
65788447a05SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_volume, 0x0);
65888447a05SGarrett D'Amore 
65988447a05SGarrett D'Amore 	/* enable end interrupts for all channels. */
66088447a05SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_cir_gc, AP_CIR_GC_ENDLP_IE);
66188447a05SGarrett D'Amore 
66288447a05SGarrett D'Amore 	/* for each stream, set gain and vol settings */
66388447a05SGarrett D'Amore 	for (str = 0; str < TS_MAX_HW_CHANNELS; str++) {
66488447a05SGarrett D'Amore 		/*
66588447a05SGarrett D'Amore 		 * Set volume to all off, 1st left and then right.
66688447a05SGarrett D'Amore 		 * These are never changed, so we don't have to save them.
66788447a05SGarrett D'Amore 		 */
66888447a05SGarrett D'Amore 		ddi_put16(handle,
66988447a05SGarrett D'Amore 		    &regs->aud_ram[str].eram.eram_gvsel_pan_vol,
67088447a05SGarrett D'Amore 		    (ERAM_WAVE_VOL|ERAM_PAN_LEFT|ERAM_PAN_0dB|
67188447a05SGarrett D'Amore 		    ERAM_VOL_MAX_ATTEN));
67288447a05SGarrett D'Amore 		ddi_put16(handle,
67388447a05SGarrett D'Amore 		    &regs->aud_ram[str].eram.eram_gvsel_pan_vol,
67488447a05SGarrett D'Amore 		    (ERAM_WAVE_VOL|ERAM_PAN_RIGHT|ERAM_PAN_0dB|
67588447a05SGarrett D'Amore 		    ERAM_VOL_MAX_ATTEN));
67688447a05SGarrett D'Amore 
67788447a05SGarrett D'Amore 		/*
67888447a05SGarrett D'Amore 		 * The envelope engine *MUST* remain in still mode (off).
67988447a05SGarrett D'Amore 		 * Otherwise bad things like gain randomly disappearing might
68088447a05SGarrett D'Amore 		 * happen. See bug #4332773.
68188447a05SGarrett D'Amore 		 */
68288447a05SGarrett D'Amore 
68388447a05SGarrett D'Amore 		ddi_put32(handle, &regs->aud_ram[str].eram.eram_ebuf1,
68488447a05SGarrett D'Amore 		    ERAM_EBUF_STILL);
68588447a05SGarrett D'Amore 		ddi_put32(handle, &regs->aud_ram[str].eram.eram_ebuf2,
68688447a05SGarrett D'Amore 		    ERAM_EBUF_STILL);
68788447a05SGarrett D'Amore 
68888447a05SGarrett D'Amore 		/* program the initial eram and aram rate */
68988447a05SGarrett D'Amore 		ddi_put16(handle, &regs->aud_ram[str].aram.aram_delta,
69088447a05SGarrett D'Amore 		    1 << TS_SRC_SHIFT);
69188447a05SGarrett D'Amore 		ddi_put16(handle, &regs->aud_ram[str].eram.eram_ctrl_ec,
69288447a05SGarrett D'Amore 		    ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE |
69388447a05SGarrett D'Amore 		    ERAM_SIGNED_PCM);
69488447a05SGarrett D'Amore 	}
69588447a05SGarrett D'Amore 
69688447a05SGarrett D'Amore 	/* program channel 31 for record */
69788447a05SGarrett D'Amore 	OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_global_control,
69888447a05SGarrett D'Amore 	    (AP_CLOGAL_CTRL_E_PCMIN_CH31|AP_CLOGAL_CTRL_PCM_OUT_AC97|
69988447a05SGarrett D'Amore 	    AP_CLOGAL_CTRL_MMC_FROM_MIXER|AP_CLOGAL_CTRL_PCM_OUT_TO_AC97));
70088447a05SGarrett D'Amore 
70188447a05SGarrett D'Amore 	/* do a warm reset, which powers up the Codec */
70288447a05SGarrett D'Amore 	OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
70388447a05SGarrett D'Amore 	    AP_SCTRL_WRST_CODEC);
70488447a05SGarrett D'Amore 	drv_usecwait(2);
70588447a05SGarrett D'Amore 	AND_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
70688447a05SGarrett D'Amore 	    ~AP_SCTRL_WRST_CODEC);
70788447a05SGarrett D'Amore 
70888447a05SGarrett D'Amore 	/* do a warm reset via the Codec, yes, I'm being paranoid! */
70988447a05SGarrett D'Amore 	audiots_set_ac97(state, AC97_RESET_REGISTER, 0);
71088447a05SGarrett D'Amore 
71188447a05SGarrett D'Amore 	/* Make sure the Codec is powered up. */
71288447a05SGarrett D'Amore 	int i = TS_WAIT_CNT;
71388447a05SGarrett D'Amore 	while ((audiots_get_ac97(state, AC97_POWERDOWN_CTRL_STAT_REGISTER) &
71488447a05SGarrett D'Amore 	    PCSR_POWERD_UP) != PCSR_POWERD_UP && i--) {
71588447a05SGarrett D'Amore 		drv_usecwait(1);
71688447a05SGarrett D'Amore 	}
71788447a05SGarrett D'Amore 
71888447a05SGarrett D'Amore }
71988447a05SGarrett D'Amore 
72088447a05SGarrett D'Amore /*
72188447a05SGarrett D'Amore  * audiots_get_ac97()
72288447a05SGarrett D'Amore  *
72388447a05SGarrett D'Amore  * Description:
72488447a05SGarrett D'Amore  *	Get the value in the specified AC-97 Codec register. There is a
72588447a05SGarrett D'Amore  *	bug in silicon which forces us to do multiple reads of the Codec's
72688447a05SGarrett D'Amore  *	register. This algorithm was provided by T2 and is heuristic in
72788447a05SGarrett D'Amore  *	nature. Unfortunately we have no guarantees that the real answer
72888447a05SGarrett D'Amore  *	isn't 0x0000, which is what we get when a read fails. So we loop
72988447a05SGarrett D'Amore  *	TS_LOOP_CNT times before we give up. We just have to hope this is
73088447a05SGarrett D'Amore  *	sufficient to give us the correct value.
73188447a05SGarrett D'Amore  *
73288447a05SGarrett D'Amore  * Arguments:
73388447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
73488447a05SGarrett D'Amore  *	int		reg		AC-97 register number
73588447a05SGarrett D'Amore  *
73688447a05SGarrett D'Amore  * Returns:
73788447a05SGarrett D'Amore  *	unsigned short		The value in the specified register
73888447a05SGarrett D'Amore  */
73988447a05SGarrett D'Amore static uint16_t
audiots_get_ac97(void * arg,uint8_t reg)74088447a05SGarrett D'Amore audiots_get_ac97(void *arg, uint8_t reg)
74188447a05SGarrett D'Amore {
74288447a05SGarrett D'Amore 	audiots_state_t		*state = arg;
74388447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = state->ts_acch;
74488447a05SGarrett D'Amore 	uint16_t		*data;
74588447a05SGarrett D'Amore 	int			count;
74688447a05SGarrett D'Amore 	int			delay;
74788447a05SGarrett D'Amore 	uint16_t		first;
74888447a05SGarrett D'Amore 	uint16_t		next;
74988447a05SGarrett D'Amore 
75088447a05SGarrett D'Amore 	if (state->ts_revid == AC_REV_ID1) {
75188447a05SGarrett D'Amore 		data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
75288447a05SGarrett D'Amore 	} else {
75388447a05SGarrett D'Amore 		data = &state->ts_regs->aud_regs.ap_acrdwr_data;
75488447a05SGarrett D'Amore 	}
75588447a05SGarrett D'Amore 
75688447a05SGarrett D'Amore 	/* make sure the register is good */
75788447a05SGarrett D'Amore 	reg &= AP_ACRD_INDEX_MASK;
75888447a05SGarrett D'Amore 	for (count = TS_LOOP_CNT; count--; ) {
75988447a05SGarrett D'Amore 		if ((first = audiots_read_ac97(state, reg)) != 0) {
76088447a05SGarrett D'Amore 			next = first;
76188447a05SGarrett D'Amore 			break;
76288447a05SGarrett D'Amore 		}
76388447a05SGarrett D'Amore 
76488447a05SGarrett D'Amore 		delay = TS_DELAY_CNT;
76588447a05SGarrett D'Amore 		while (delay--) {
76688447a05SGarrett D'Amore 			(void) ddi_get16(handle, data);
76788447a05SGarrett D'Amore 		}
76888447a05SGarrett D'Amore 
76988447a05SGarrett D'Amore 		if ((next = audiots_read_ac97(state, reg)) != 0) {
77088447a05SGarrett D'Amore 			break;
77188447a05SGarrett D'Amore 		}
77288447a05SGarrett D'Amore 	}
77388447a05SGarrett D'Amore 
77488447a05SGarrett D'Amore 	/*
77588447a05SGarrett D'Amore 	 * Arggg, if you let the next read happen too soon then it fails.
77688447a05SGarrett D'Amore 	 * 12 usec fails, 13 usec succeeds. So set it to 20 for safety.
77788447a05SGarrett D'Amore 	 */
77888447a05SGarrett D'Amore 	drv_usecwait(TS_20US);
77988447a05SGarrett D'Amore 
78088447a05SGarrett D'Amore 	return (next);
78188447a05SGarrett D'Amore 
78288447a05SGarrett D'Amore }
78388447a05SGarrett D'Amore 
78488447a05SGarrett D'Amore /*
78588447a05SGarrett D'Amore  * audiots_init_state()
78688447a05SGarrett D'Amore  *
78788447a05SGarrett D'Amore  * Description:
78888447a05SGarrett D'Amore  *	This routine initializes the audio driver's state structure.
78988447a05SGarrett D'Amore  *	This includes reading the properties.
79088447a05SGarrett D'Amore  *
79188447a05SGarrett D'Amore  *	CAUTION: This routine cannot allocate resources, unless it frees
79288447a05SGarrett D'Amore  *		them before returning for an error. Also, error_destroy:
79388447a05SGarrett D'Amore  *		in audiots_attach() would need to be fixed as well.
79488447a05SGarrett D'Amore  *
79588447a05SGarrett D'Amore  *	NOTE: birdsnest supports CD ROM input. We check for the cdrom
79688447a05SGarrett D'Amore  *		property. If there we turn it on.
79788447a05SGarrett D'Amore  *
79888447a05SGarrett D'Amore  * Arguments:
79988447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
80088447a05SGarrett D'Amore  *	dev_info_t	*dip		Pointer to the device's dev_info struct
80188447a05SGarrett D'Amore  *
80288447a05SGarrett D'Amore  * Returns:
80388447a05SGarrett D'Amore  *	DDI_SUCCESS			State structure initialized
80488447a05SGarrett D'Amore  *	DDI_FAILURE			State structure not initialized
80588447a05SGarrett D'Amore  */
80688447a05SGarrett D'Amore static int
audiots_init_state(audiots_state_t * state,dev_info_t * dip)80788447a05SGarrett D'Amore audiots_init_state(audiots_state_t *state, dev_info_t *dip)
80888447a05SGarrett D'Amore {
80988447a05SGarrett D'Amore 	state->ts_ac97 = ac97_alloc(dip, audiots_get_ac97,
81088447a05SGarrett D'Amore 	    audiots_set_ac97, state);
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 	if (state->ts_ac97 == NULL) {
81388447a05SGarrett D'Amore 		return (DDI_FAILURE);
81488447a05SGarrett D'Amore 	}
81588447a05SGarrett D'Amore 
81688447a05SGarrett D'Amore 	/* save the device info pointer */
81788447a05SGarrett D'Amore 	state->ts_dip = dip;
81888447a05SGarrett D'Amore 
81988447a05SGarrett D'Amore 	for (int i = 0; i < TS_NUM_PORTS; i++) {
82088447a05SGarrett D'Amore 		if (audiots_alloc_port(state, i) != DDI_SUCCESS) {
82188447a05SGarrett D'Amore 			return (DDI_FAILURE);
82288447a05SGarrett D'Amore 		}
82388447a05SGarrett D'Amore 	}
82488447a05SGarrett D'Amore 
82588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
82688447a05SGarrett D'Amore 
82788447a05SGarrett D'Amore }
82888447a05SGarrett D'Amore 
82988447a05SGarrett D'Amore /*
83088447a05SGarrett D'Amore  * audiots_map_regs()
83188447a05SGarrett D'Amore  *
83288447a05SGarrett D'Amore  * Description:
83388447a05SGarrett D'Amore  *	This routine maps the registers in.
83488447a05SGarrett D'Amore  *
83588447a05SGarrett D'Amore  *	Once the config space registers are mapped in we determine if the
83688447a05SGarrett D'Amore  *	audio core may be power managed. It should, but if it doesn't,
83788447a05SGarrett D'Amore  *	then trying to may cause the core to hang.
83888447a05SGarrett D'Amore  *
83988447a05SGarrett D'Amore  *	CAUTION: Make sure all errors call audio_dev_warn().
84088447a05SGarrett D'Amore  *
84188447a05SGarrett D'Amore  * Arguments:
84288447a05SGarrett D'Amore  *	dev_info_t	*dip            Pointer to the device's devinfo
84388447a05SGarrett D'Amore  *	audiots_state_t	*state          The device's state structure
84488447a05SGarrett D'Amore  * Returns:
84588447a05SGarrett D'Amore  *	DDI_SUCCESS		Registers successfully mapped
84688447a05SGarrett D'Amore  *	DDI_FAILURE		Registers not successfully mapped
84788447a05SGarrett D'Amore  */
84888447a05SGarrett D'Amore static int
audiots_map_regs(dev_info_t * dip,audiots_state_t * state)84988447a05SGarrett D'Amore audiots_map_regs(dev_info_t *dip, audiots_state_t *state)
85088447a05SGarrett D'Amore {
85188447a05SGarrett D'Amore 	char	rev[16];
85288447a05SGarrett D'Amore 	char	*name;
85388447a05SGarrett D'Amore 
85488447a05SGarrett D'Amore 	/* map in the registers, the config and memory mapped registers */
85588447a05SGarrett D'Amore 	if (pci_config_setup(dip, &state->ts_pcih) != DDI_SUCCESS) {
85688447a05SGarrett D'Amore 		audio_dev_warn(state->ts_adev,
85788447a05SGarrett D'Amore 		    "unable to map PCI configuration space");
85888447a05SGarrett D'Amore 		return (DDI_FAILURE);
85988447a05SGarrett D'Amore 	}
86088447a05SGarrett D'Amore 
86188447a05SGarrett D'Amore 	/* Read the Audio Controller's vendor, device, and revision IDs */
86288447a05SGarrett D'Amore 	state->ts_devid =
86388447a05SGarrett D'Amore 	    (pci_config_get16(state->ts_pcih, PCI_CONF_VENID) << 16) |
86488447a05SGarrett D'Amore 	    pci_config_get16(state->ts_pcih, PCI_CONF_DEVID);
86588447a05SGarrett D'Amore 	state->ts_revid = pci_config_get8(state->ts_pcih, PCI_CONF_REVID);
86688447a05SGarrett D'Amore 
86788447a05SGarrett D'Amore 	if (ddi_regs_map_setup(dip, TS_MEM_MAPPED_REGS,
86888447a05SGarrett D'Amore 	    (caddr_t *)&state->ts_regs, 0, 0, &ts_regs_attr, &state->ts_acch) !=
86988447a05SGarrett D'Amore 	    DDI_SUCCESS) {
87088447a05SGarrett D'Amore 		audio_dev_warn(state->ts_adev,
87188447a05SGarrett D'Amore 		    "unable to map PCI device registers");
87288447a05SGarrett D'Amore 		return (DDI_FAILURE);
87388447a05SGarrett D'Amore 	}
87488447a05SGarrett D'Amore 
87588447a05SGarrett D'Amore 	switch (state->ts_devid) {
87688447a05SGarrett D'Amore 	case 0x10b95451:
87788447a05SGarrett D'Amore 		name = "ALI M5451";
87888447a05SGarrett D'Amore 		break;
87988447a05SGarrett D'Amore 	default:
88088447a05SGarrett D'Amore 		name = "audiots";
88188447a05SGarrett D'Amore 		break;
88288447a05SGarrett D'Amore 	}
88388447a05SGarrett D'Amore 	(void) snprintf(rev, sizeof (rev), "Rev %x", state->ts_revid);
88488447a05SGarrett D'Amore 	audio_dev_set_description(state->ts_adev, name);
88588447a05SGarrett D'Amore 	audio_dev_set_version(state->ts_adev, rev);
88688447a05SGarrett D'Amore 
88788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
88888447a05SGarrett D'Amore }
88988447a05SGarrett D'Amore 
89088447a05SGarrett D'Amore /*
89188447a05SGarrett D'Amore  * audiots_alloc_port()
89288447a05SGarrett D'Amore  *
89388447a05SGarrett D'Amore  * Description:
89488447a05SGarrett D'Amore  *	This routine allocates the DMA handles and the memory for the
89588447a05SGarrett D'Amore  *	DMA engines to use. It then binds each of the buffers to its
89688447a05SGarrett D'Amore  *	respective handle, getting a DMA cookie.
89788447a05SGarrett D'Amore  *
89888447a05SGarrett D'Amore  *	NOTE: All of the ddi_dma_... routines sleep if they cannot get
89988447a05SGarrett D'Amore  *		memory. This means these calls should always succeed.
90088447a05SGarrett D'Amore  *
90188447a05SGarrett D'Amore  *	NOTE: ddi_dma_alloc_handle() attempts to use the full 4 GB DMA address
90288447a05SGarrett D'Amore  *		range. This is to work around Southbridge rev E/G OBP issues.
90388447a05SGarrett D'Amore  *		(See Grover OBP note above)
90488447a05SGarrett D'Amore  *
90588447a05SGarrett D'Amore  *	CAUTION: Make sure all errors call audio_dev_warn().
90688447a05SGarrett D'Amore  *
90788447a05SGarrett D'Amore  * Arguments:
90888447a05SGarrett D'Amore  *	audiots_port_t	*state          The port structure for a device stream
90988447a05SGarrett D'Amore  *	int		num		The port number
91088447a05SGarrett D'Amore  *
91188447a05SGarrett D'Amore  * Returns:
91288447a05SGarrett D'Amore  *	DDI_SUCCESS		DMA resources mapped
91388447a05SGarrett D'Amore  *	DDI_FAILURE		DMA resources not successfully mapped
91488447a05SGarrett D'Amore  */
91588447a05SGarrett D'Amore int
audiots_alloc_port(audiots_state_t * state,int num)91688447a05SGarrett D'Amore audiots_alloc_port(audiots_state_t *state, int num)
91788447a05SGarrett D'Amore {
91888447a05SGarrett D'Amore 	audiots_port_t		*port;
91988447a05SGarrett D'Amore 	dev_info_t		*dip = state->ts_dip;
92088447a05SGarrett D'Amore 	audio_dev_t		*adev = state->ts_adev;
92188447a05SGarrett D'Amore 	int			dir;
92288447a05SGarrett D'Amore 	unsigned		caps;
92388447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
92488447a05SGarrett D'Amore 	unsigned		count;
92588447a05SGarrett D'Amore 	int			rc;
92688447a05SGarrett D'Amore 	ddi_acc_handle_t	regsh = state->ts_acch;
92788447a05SGarrett D'Amore 	uint32_t		*gcptr = &state->ts_regs->aud_regs.ap_cir_gc;
92888447a05SGarrett D'Amore 
92988447a05SGarrett D'Amore 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
93088447a05SGarrett D'Amore 	state->ts_ports[num] = port;
93188447a05SGarrett D'Amore 	port->tp_num = num;
93288447a05SGarrett D'Amore 	port->tp_state = state;
933*68c47f65SGarrett D'Amore 	port->tp_rate = TS_RATE;
93488447a05SGarrett D'Amore 
93588447a05SGarrett D'Amore 	if (num == TS_INPUT_PORT) {
93688447a05SGarrett D'Amore 		dir = DDI_DMA_READ;
93788447a05SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
93888447a05SGarrett D'Amore 		port->tp_dma_stream = 31;
93988447a05SGarrett D'Amore 		port->tp_sync_dir = DDI_DMA_SYNC_FORKERNEL;
94088447a05SGarrett D'Amore 	} else {
94188447a05SGarrett D'Amore 		dir = DDI_DMA_WRITE;
94288447a05SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
94388447a05SGarrett D'Amore 		port->tp_dma_stream = 0;
94488447a05SGarrett D'Amore 		port->tp_sync_dir = DDI_DMA_SYNC_FORDEV;
94588447a05SGarrett D'Amore 	}
94688447a05SGarrett D'Amore 
947*68c47f65SGarrett D'Amore 	port->tp_dma_mask = (1U << port->tp_dma_stream);
948*68c47f65SGarrett D'Amore 	port->tp_nframes = 4096;
949*68c47f65SGarrett D'Amore 	port->tp_size = port->tp_nframes * TS_FRAMESZ;
95088447a05SGarrett D'Amore 
95188447a05SGarrett D'Amore 	/* allocate dma handle */
95288447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, &audiots_attr, DDI_DMA_SLEEP,
95388447a05SGarrett D'Amore 	    NULL, &port->tp_dmah);
95488447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
95588447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
95688447a05SGarrett D'Amore 		return (DDI_FAILURE);
95788447a05SGarrett D'Amore 	}
95888447a05SGarrett D'Amore 	/* allocate DMA buffer */
959*68c47f65SGarrett D'Amore 	rc = ddi_dma_mem_alloc(port->tp_dmah, port->tp_size, &ts_acc_attr,
96088447a05SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->tp_kaddr,
96188447a05SGarrett D'Amore 	    &port->tp_size, &port->tp_acch);
96288447a05SGarrett D'Amore 	if (rc == DDI_FAILURE) {
96388447a05SGarrett D'Amore 		audio_dev_warn(adev, "dma_mem_alloc failed");
96488447a05SGarrett D'Amore 		return (DDI_FAILURE);
96588447a05SGarrett D'Amore 	}
96688447a05SGarrett D'Amore 
96788447a05SGarrett D'Amore 	/* bind DMA buffer */
96888447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(port->tp_dmah, NULL,
96988447a05SGarrett D'Amore 	    port->tp_kaddr, port->tp_size, dir|DDI_DMA_CONSISTENT,
97088447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count);
97188447a05SGarrett D'Amore 	if (rc != DDI_DMA_MAPPED) {
97288447a05SGarrett D'Amore 		audio_dev_warn(adev,
97388447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed: %d", rc);
97488447a05SGarrett D'Amore 		return (DDI_FAILURE);
97588447a05SGarrett D'Amore 	}
97688447a05SGarrett D'Amore 	ASSERT(count == 1);
97788447a05SGarrett D'Amore 
97888447a05SGarrett D'Amore 	port->tp_paddr = cookie.dmac_address;
97988447a05SGarrett D'Amore 	if ((unsigned)port->tp_paddr & 0x80000000U) {
98088447a05SGarrett D'Amore 		ddi_put32(regsh, gcptr,
98188447a05SGarrett D'Amore 		    ddi_get32(regsh, gcptr) | AP_CIR_GC_SYS_MEM_4G_ENABLE);
98288447a05SGarrett D'Amore 	} else {
98388447a05SGarrett D'Amore 		ddi_put32(regsh, gcptr,
98488447a05SGarrett D'Amore 		    ddi_get32(regsh, gcptr) & ~(AP_CIR_GC_SYS_MEM_4G_ENABLE));
98588447a05SGarrett D'Amore 	}
98688447a05SGarrett D'Amore 	port->tp_engine = audio_engine_alloc(&audiots_engine_ops, caps);
98788447a05SGarrett D'Amore 	if (port->tp_engine == NULL) {
98888447a05SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
98988447a05SGarrett D'Amore 		return (DDI_FAILURE);
99088447a05SGarrett D'Amore 	}
99188447a05SGarrett D'Amore 
99288447a05SGarrett D'Amore 	audio_engine_set_private(port->tp_engine, port);
99388447a05SGarrett D'Amore 	audio_dev_add_engine(adev, port->tp_engine);
99488447a05SGarrett D'Amore 
99588447a05SGarrett D'Amore 	return (DDI_SUCCESS);
99688447a05SGarrett D'Amore }
99788447a05SGarrett D'Amore 
99888447a05SGarrett D'Amore /*
99988447a05SGarrett D'Amore  * audiots_read_ac97()
100088447a05SGarrett D'Amore  *
100188447a05SGarrett D'Amore  * Description:
100288447a05SGarrett D'Amore  *	This routine actually reads the AC-97 Codec's register. It may
100388447a05SGarrett D'Amore  *	be called several times to succeed.
100488447a05SGarrett D'Amore  *
100588447a05SGarrett D'Amore  * NOTE:
100688447a05SGarrett D'Amore  * 	Revision M1535D B1-C of the ALI SouthBridge includes a workaround for
100788447a05SGarrett D'Amore  *	the broken busy flag. Resetting the busy flag requires a software tweak
100888447a05SGarrett D'Amore  *	to go with the worked around hardware. When we detect failure, we make
100988447a05SGarrett D'Amore  *	10 attempts to reset the chip before we fail. This should reset the new
101088447a05SGarrett D'Amore  *	SB systems. On all SB systems, this will increse the read delay
101188447a05SGarrett D'Amore  *	slightly, but shouldn't bother it otherwise.
101288447a05SGarrett D'Amore  *
101388447a05SGarrett D'Amore  * Arguments:
101488447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
101588447a05SGarrett D'Amore  *	int		reg		AC-97 register number
101688447a05SGarrett D'Amore  *
101788447a05SGarrett D'Amore  * Returns:
101888447a05SGarrett D'Amore  *	unsigned short		The value in the specified register
101988447a05SGarrett D'Amore  */
102088447a05SGarrett D'Amore static uint16_t
audiots_read_ac97(audiots_state_t * state,int reg)102188447a05SGarrett D'Amore audiots_read_ac97(audiots_state_t *state, int reg)
102288447a05SGarrett D'Amore {
102388447a05SGarrett D'Amore 	ddi_acc_handle_t	acch = state->ts_acch;
102488447a05SGarrett D'Amore 	uint16_t		*addr;
102588447a05SGarrett D'Amore 	uint16_t		*data;
102688447a05SGarrett D'Amore 	uint32_t		*stimer = &state->ts_regs->aud_regs.ap_stimer;
102788447a05SGarrett D'Amore 	uint32_t		chk1;
102888447a05SGarrett D'Amore 	uint32_t		chk2;
102988447a05SGarrett D'Amore 	int			resets = 0;
103088447a05SGarrett D'Amore 	int			i;
103188447a05SGarrett D'Amore 
103288447a05SGarrett D'Amore 	if (state->ts_revid == AC_REV_ID1) {
103388447a05SGarrett D'Amore 		addr = &state->ts_regs->aud_regs.ap_acrd_35D_reg;
103488447a05SGarrett D'Amore 		data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
103588447a05SGarrett D'Amore 	} else {
103688447a05SGarrett D'Amore 		addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
103788447a05SGarrett D'Amore 		data = &state->ts_regs->aud_regs.ap_acrdwr_data;
103888447a05SGarrett D'Amore 	}
103988447a05SGarrett D'Amore 
104088447a05SGarrett D'Amore first_read:
104188447a05SGarrett D'Amore 	/* wait for ready to send read request */
104288447a05SGarrett D'Amore 	for (i = 0; i < TS_READ_TRIES; i++) {
104388447a05SGarrett D'Amore 		if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
104488447a05SGarrett D'Amore 			break;
104588447a05SGarrett D'Amore 		}
104688447a05SGarrett D'Amore 		/* don't beat on the bus */
104788447a05SGarrett D'Amore 		drv_usecwait(1);
104888447a05SGarrett D'Amore 	}
104988447a05SGarrett D'Amore 	if (i >= TS_READ_TRIES) {
105088447a05SGarrett D'Amore 		if (resets < TS_RESET_TRIES) {
105188447a05SGarrett D'Amore 			/* Attempt to reset */
105288447a05SGarrett D'Amore 			drv_usecwait(TS_20US);
105388447a05SGarrett D'Amore 			ddi_put16(acch, addr, TS_SB_RESET);
105488447a05SGarrett D'Amore 			resets++;
105588447a05SGarrett D'Amore 			goto first_read;
105688447a05SGarrett D'Amore 		} else {
105788447a05SGarrett D'Amore 			state->ts_flags |= TS_AUDIO_READ_FAILED;
105888447a05SGarrett D'Amore 			if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
105988447a05SGarrett D'Amore 				ddi_dev_report_fault(state->ts_dip,
106088447a05SGarrett D'Amore 				    DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
106188447a05SGarrett D'Amore 				    "Unable to communicate with AC97 CODEC");
106288447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
106388447a05SGarrett D'Amore 				    "The audio AC97 register has timed out.");
106488447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
106588447a05SGarrett D'Amore 				    "Audio is now disabled.");
106688447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
106788447a05SGarrett D'Amore 				    "Please reboot to restore audio.");
106888447a05SGarrett D'Amore 
106988447a05SGarrett D'Amore 				/* Don't flood the console */
107088447a05SGarrett D'Amore 				state->ts_flags |= TS_READ_FAILURE_PRINTED;
107188447a05SGarrett D'Amore 			}
107288447a05SGarrett D'Amore 		}
107388447a05SGarrett D'Amore 		return (0);
107488447a05SGarrett D'Amore 	}
107588447a05SGarrett D'Amore 
107688447a05SGarrett D'Amore 	/* program the register to read */
107788447a05SGarrett D'Amore 	ddi_put16(acch, addr, (reg|AP_ACRD_W_PRIMARY_CODEC|
107888447a05SGarrett D'Amore 	    AP_ACRD_W_READ_MIXER_REG|AP_ACRD_W_AUDIO_READ_REQ&
107988447a05SGarrett D'Amore 	    (~AP_ACWR_W_SELECT_WRITE)));
108088447a05SGarrett D'Amore 
108188447a05SGarrett D'Amore 	/* hardware bug work around */
108288447a05SGarrett D'Amore 	chk1 = ddi_get32(acch, stimer);
108388447a05SGarrett D'Amore 	chk2 = ddi_get32(acch, stimer);
108488447a05SGarrett D'Amore 	i = TS_WAIT_CNT;
108588447a05SGarrett D'Amore 	while (chk1 == chk2 && i) {
108688447a05SGarrett D'Amore 		chk2 = ddi_get32(acch, stimer);
108788447a05SGarrett D'Amore 		i--;
108888447a05SGarrett D'Amore 	}
108988447a05SGarrett D'Amore 	OR_SET_SHORT(acch, addr, AP_ACRD_W_READ_MIXER_REG);
109088447a05SGarrett D'Amore 	resets = 0;
109188447a05SGarrett D'Amore 
109288447a05SGarrett D'Amore second_read:
109388447a05SGarrett D'Amore 	/* wait again for read to send read request */
109488447a05SGarrett D'Amore 	for (i = 0; i < TS_READ_TRIES; i++) {
109588447a05SGarrett D'Amore 		if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
109688447a05SGarrett D'Amore 			break;
109788447a05SGarrett D'Amore 		}
109888447a05SGarrett D'Amore 		/* don't beat on the bus */
109988447a05SGarrett D'Amore 		drv_usecwait(1);
110088447a05SGarrett D'Amore 	}
110188447a05SGarrett D'Amore 	if (i >= TS_READ_TRIES) {
110288447a05SGarrett D'Amore 		if (resets < TS_RESET_TRIES) {
110388447a05SGarrett D'Amore 			/* Attempt to reset */
110488447a05SGarrett D'Amore 			drv_usecwait(TS_20US);
110588447a05SGarrett D'Amore 			ddi_put16(acch, addr, TS_SB_RESET);
110688447a05SGarrett D'Amore 			resets++;
110788447a05SGarrett D'Amore 			goto second_read;
110888447a05SGarrett D'Amore 		} else {
110988447a05SGarrett D'Amore 			state->ts_flags |= TS_AUDIO_READ_FAILED;
111088447a05SGarrett D'Amore 			if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
111188447a05SGarrett D'Amore 				ddi_dev_report_fault(state->ts_dip,
111288447a05SGarrett D'Amore 				    DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
111388447a05SGarrett D'Amore 				    "Unable to communicate with AC97 CODEC");
111488447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
111588447a05SGarrett D'Amore 				    "The audio AC97 register has timed out.");
111688447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
111788447a05SGarrett D'Amore 				    "Audio is now disabled.");
111888447a05SGarrett D'Amore 				audio_dev_warn(state->ts_adev,
111988447a05SGarrett D'Amore 				    "Please reboot to restore audio.");
112088447a05SGarrett D'Amore 
112188447a05SGarrett D'Amore 				/* Don't flood the console */
112288447a05SGarrett D'Amore 				state->ts_flags |= TS_READ_FAILURE_PRINTED;
112388447a05SGarrett D'Amore 			}
112488447a05SGarrett D'Amore 		}
112588447a05SGarrett D'Amore 		return (0);
112688447a05SGarrett D'Amore 	}
112788447a05SGarrett D'Amore 
112888447a05SGarrett D'Amore 	return (ddi_get16(acch, data));
112988447a05SGarrett D'Amore 
113088447a05SGarrett D'Amore }	/* audiots_read_ac97() */
113188447a05SGarrett D'Amore 
113288447a05SGarrett D'Amore /*
113388447a05SGarrett D'Amore  * audiots_set_ac97()
113488447a05SGarrett D'Amore  *
113588447a05SGarrett D'Amore  * Description:
113688447a05SGarrett D'Amore  *	Set the value in the specified AC-97 Codec register. Just like
113788447a05SGarrett D'Amore  *	reading the AC-97 Codec, it is possible there is a problem writing
113888447a05SGarrett D'Amore  *	it as well. So we loop.
113988447a05SGarrett D'Amore  *
114088447a05SGarrett D'Amore  * Arguments:
114188447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
114288447a05SGarrett D'Amore  *	int		reg		AC-97 register number
114388447a05SGarrett D'Amore  *	uint16_t	value		The value to write
114488447a05SGarrett D'Amore  */
114588447a05SGarrett D'Amore static void
audiots_set_ac97(void * arg,uint8_t reg8,uint16_t data)114688447a05SGarrett D'Amore audiots_set_ac97(void *arg, uint8_t reg8, uint16_t data)
114788447a05SGarrett D'Amore {
114888447a05SGarrett D'Amore 	audiots_state_t	*state = arg;
114988447a05SGarrett D'Amore 	ddi_acc_handle_t handle = state->ts_acch;
115088447a05SGarrett D'Amore 	uint16_t	*data_addr = &state->ts_regs->aud_regs.ap_acrdwr_data;
115188447a05SGarrett D'Amore 	uint16_t	*reg_addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
115288447a05SGarrett D'Amore 	int		count;
115388447a05SGarrett D'Amore 	int		i;
115488447a05SGarrett D'Amore 	uint16_t	tmp_short;
115588447a05SGarrett D'Amore 	uint16_t	reg = reg8;
115688447a05SGarrett D'Amore 
115788447a05SGarrett D'Amore 	reg &= AP_ACWR_INDEX_MASK;
115888447a05SGarrett D'Amore 
115988447a05SGarrett D'Amore 	/* Don't touch the reserved bits on the pre 35D+ SouthBridge */
116088447a05SGarrett D'Amore 	if (state->ts_revid == AC_REV_ID1) {
116188447a05SGarrett D'Amore 		reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG;
116288447a05SGarrett D'Amore 	} else {
116388447a05SGarrett D'Amore 		reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG|
116488447a05SGarrett D'Amore 		    AP_ACWR_W_SELECT_WRITE;
116588447a05SGarrett D'Amore 	}
116688447a05SGarrett D'Amore 
116788447a05SGarrett D'Amore 	for (count = TS_LOOP_CNT; count--; ) {
116888447a05SGarrett D'Amore 		/* wait for ready to write */
116988447a05SGarrett D'Amore 		for (i = 0; i < TS_WAIT_CNT; i++) {
117088447a05SGarrett D'Amore 			if (!(ddi_get16(handle, reg_addr) &
117188447a05SGarrett D'Amore 			    AP_ACWR_R_WRITE_BUSY)) {
117288447a05SGarrett D'Amore 				/* ready to write */
117388447a05SGarrett D'Amore 				ddi_put16(handle, reg_addr, reg);
117488447a05SGarrett D'Amore 
117588447a05SGarrett D'Amore 				/* Write the data */
117688447a05SGarrett D'Amore 				ddi_put16(handle, data_addr, data);
117788447a05SGarrett D'Amore 				break;
117888447a05SGarrett D'Amore 			}
117988447a05SGarrett D'Amore 		}
118088447a05SGarrett D'Amore 		if (i >= TS_WAIT_CNT) {
118188447a05SGarrett D'Amore 			/* try again */
118288447a05SGarrett D'Amore 			continue;
118388447a05SGarrett D'Amore 		}
118488447a05SGarrett D'Amore 
118588447a05SGarrett D'Amore 		/* wait for write to complete */
118688447a05SGarrett D'Amore 		for (i = 0; i < TS_WAIT_CNT; i++) {
118788447a05SGarrett D'Amore 			if (!(ddi_get16(handle, reg_addr) &
118888447a05SGarrett D'Amore 			    AP_ACWR_R_WRITE_BUSY)) {
118988447a05SGarrett D'Amore 				/* done writing */
119088447a05SGarrett D'Amore 				break;
119188447a05SGarrett D'Amore 			}
119288447a05SGarrett D'Amore 		}
119388447a05SGarrett D'Amore 
119488447a05SGarrett D'Amore 		/* verify the value written */
119588447a05SGarrett D'Amore 		tmp_short = audiots_get_ac97(state, reg8);
119688447a05SGarrett D'Amore 		if (data == tmp_short) {
119788447a05SGarrett D'Amore 			/* successfully loaded, so we can return */
119888447a05SGarrett D'Amore 			return;
119988447a05SGarrett D'Amore 		}
120088447a05SGarrett D'Amore 	}
120188447a05SGarrett D'Amore 
120288447a05SGarrett D'Amore }	/* audiots_set_ac97() */
120388447a05SGarrett D'Amore 
120488447a05SGarrett D'Amore /*
120588447a05SGarrett D'Amore  * audiots_open()
120688447a05SGarrett D'Amore  *
120788447a05SGarrett D'Amore  * Description:
120888447a05SGarrett D'Amore  *	Opens a DMA engine for use.  Will also ensure the device is powered
120988447a05SGarrett D'Amore  *	up if not already done so.
121088447a05SGarrett D'Amore  *
121188447a05SGarrett D'Amore  * Arguments:
121288447a05SGarrett D'Amore  *	void		*arg		The DMA engine to set up
121388447a05SGarrett D'Amore  *	int		flag		Open flags
1214*68c47f65SGarrett D'Amore  *	unsigned	*nframesp	Receives number of frames
121588447a05SGarrett D'Amore  *	caddr_t		*bufp		Receives kernel data buffer
121688447a05SGarrett D'Amore  *
121788447a05SGarrett D'Amore  * Returns:
121888447a05SGarrett D'Amore  *	0	on success
121988447a05SGarrett D'Amore  *	errno	on failure
122088447a05SGarrett D'Amore  */
122188447a05SGarrett D'Amore static int
audiots_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)1222*68c47f65SGarrett D'Amore audiots_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
122388447a05SGarrett D'Amore {
122488447a05SGarrett D'Amore 	audiots_port_t	*port = arg;
122588447a05SGarrett D'Amore 
122688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
122788447a05SGarrett D'Amore 
122888447a05SGarrett D'Amore 	port->tp_count = 0;
122988447a05SGarrett D'Amore 	port->tp_cso = 0;
1230*68c47f65SGarrett D'Amore 	*nframesp = port->tp_nframes;
123188447a05SGarrett D'Amore 	*bufp = port->tp_kaddr;
123288447a05SGarrett D'Amore 
123388447a05SGarrett D'Amore 	return (0);
123488447a05SGarrett D'Amore }
123588447a05SGarrett D'Amore 
123688447a05SGarrett D'Amore /*
123788447a05SGarrett D'Amore  * audiots_close()
123888447a05SGarrett D'Amore  *
123988447a05SGarrett D'Amore  * Description:
124088447a05SGarrett D'Amore  *	Closes an audio DMA engine that was previously opened.  Since
1241*68c47f65SGarrett D'Amore  *	nobody is using it, we could take this opportunity to possibly power
1242*68c47f65SGarrett D'Amore  *	down the entire device, or at least the DMA engine.
124388447a05SGarrett D'Amore  *
124488447a05SGarrett D'Amore  * Arguments:
124588447a05SGarrett D'Amore  *	void	*arg		The DMA engine to shut down
124688447a05SGarrett D'Amore  */
124788447a05SGarrett D'Amore static void
audiots_close(void * arg)124888447a05SGarrett D'Amore audiots_close(void *arg)
124988447a05SGarrett D'Amore {
1250*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
125188447a05SGarrett D'Amore }
125288447a05SGarrett D'Amore 
125388447a05SGarrett D'Amore /*
125488447a05SGarrett D'Amore  * audiots_stop()
125588447a05SGarrett D'Amore  *
125688447a05SGarrett D'Amore  * Description:
125788447a05SGarrett D'Amore  *	This is called by the framework to stop a port that is
125888447a05SGarrett D'Amore  *	transferring data.
125988447a05SGarrett D'Amore  *
126088447a05SGarrett D'Amore  * Arguments:
126188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to stop
126288447a05SGarrett D'Amore  */
126388447a05SGarrett D'Amore static void
audiots_stop(void * arg)126488447a05SGarrett D'Amore audiots_stop(void *arg)
126588447a05SGarrett D'Amore {
126688447a05SGarrett D'Amore 	audiots_port_t	*port = arg;
126788447a05SGarrett D'Amore 	audiots_state_t	*state = port->tp_state;
126888447a05SGarrett D'Amore 
1269*68c47f65SGarrett D'Amore 	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
1270*68c47f65SGarrett D'Amore 	    port->tp_dma_mask);
127188447a05SGarrett D'Amore }
127288447a05SGarrett D'Amore 
127388447a05SGarrett D'Amore /*
127488447a05SGarrett D'Amore  * audiots_start()
127588447a05SGarrett D'Amore  *
127688447a05SGarrett D'Amore  * Description:
127788447a05SGarrett D'Amore  *	This is called by the framework to start a port transferring data.
127888447a05SGarrett D'Amore  *
127988447a05SGarrett D'Amore  * Arguments:
128088447a05SGarrett D'Amore  *	void	*arg		The DMA engine to start
128188447a05SGarrett D'Amore  *
128288447a05SGarrett D'Amore  * Returns:
128388447a05SGarrett D'Amore  *	0 	on success (never fails, errno if it did)
128488447a05SGarrett D'Amore  */
128588447a05SGarrett D'Amore static int
audiots_start(void * arg)128688447a05SGarrett D'Amore audiots_start(void *arg)
128788447a05SGarrett D'Amore {
1288*68c47f65SGarrett D'Amore 	audiots_port_t		*port = arg;
1289*68c47f65SGarrett D'Amore 	audiots_state_t		*state = port->tp_state;
1290*68c47f65SGarrett D'Amore 	ddi_acc_handle_t	handle = state->ts_acch;
1291*68c47f65SGarrett D'Amore 	audiots_regs_t		*regs = state->ts_regs;
1292*68c47f65SGarrett D'Amore 	audiots_aram_t		*aram;
1293*68c47f65SGarrett D'Amore 	audiots_eram_t		*eram;
1294*68c47f65SGarrett D'Amore 	unsigned		delta;
1295*68c47f65SGarrett D'Amore 	uint16_t		ctrl;
1296*68c47f65SGarrett D'Amore 	uint16_t		gvsel;
1297*68c47f65SGarrett D'Amore 	uint16_t		eso;
1298*68c47f65SGarrett D'Amore 
1299*68c47f65SGarrett D'Amore 	aram = &regs->aud_ram[port->tp_dma_stream].aram;
1300*68c47f65SGarrett D'Amore 	eram = &regs->aud_ram[port->tp_dma_stream].eram;
1301*68c47f65SGarrett D'Amore 
1302*68c47f65SGarrett D'Amore 	port->tp_cso = 0;
1303*68c47f65SGarrett D'Amore 
1304*68c47f65SGarrett D'Amore 	gvsel = ERAM_WAVE_VOL | ERAM_PAN_0dB | ERAM_VOL_DEFAULT;
1305*68c47f65SGarrett D'Amore 	ctrl = ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE | ERAM_SIGNED_PCM;
130688447a05SGarrett D'Amore 
1307*68c47f65SGarrett D'Amore 	delta = (port->tp_rate << TS_SRC_SHIFT) / TS_RATE;
1308*68c47f65SGarrett D'Amore 
1309*68c47f65SGarrett D'Amore 	if (port->tp_num == TS_INPUT_PORT) {
1310*68c47f65SGarrett D'Amore 		delta = (TS_RATE << TS_SRC_SHIFT) / port->tp_rate;
131188447a05SGarrett D'Amore 	}
1312*68c47f65SGarrett D'Amore 	eso = port->tp_nframes - 1;
1313*68c47f65SGarrett D'Amore 
1314*68c47f65SGarrett D'Amore 	/* program the sample rate */
1315*68c47f65SGarrett D'Amore 	ddi_put16(handle, &aram->aram_delta, (uint16_t)delta);
1316*68c47f65SGarrett D'Amore 
1317*68c47f65SGarrett D'Amore 	/* program the precision, number of channels and loop mode */
1318*68c47f65SGarrett D'Amore 	ddi_put16(handle, &eram->eram_ctrl_ec, ctrl);
1319*68c47f65SGarrett D'Amore 
1320*68c47f65SGarrett D'Amore 	/* program the volume settings */
1321*68c47f65SGarrett D'Amore 	ddi_put16(handle, &eram->eram_gvsel_pan_vol, gvsel);
1322*68c47f65SGarrett D'Amore 
1323*68c47f65SGarrett D'Amore 	/* set ALPHA and FMS to 0 */
1324*68c47f65SGarrett D'Amore 	ddi_put16(handle, &aram->aram_alpha_fms, 0x0);
1325*68c47f65SGarrett D'Amore 
1326*68c47f65SGarrett D'Amore 	/* set CSO to 0 */
1327*68c47f65SGarrett D'Amore 	ddi_put16(handle, &aram->aram_cso, 0x0);
1328*68c47f65SGarrett D'Amore 
1329*68c47f65SGarrett D'Amore 	/* set LBA */
1330*68c47f65SGarrett D'Amore 	ddi_put32(handle, &aram->aram_cptr_lba,
1331*68c47f65SGarrett D'Amore 	    port->tp_paddr & ARAM_LBA_MASK);
1332*68c47f65SGarrett D'Amore 
1333*68c47f65SGarrett D'Amore 	/* set ESO */
1334*68c47f65SGarrett D'Amore 	ddi_put16(handle, &aram->aram_eso, eso);
1335*68c47f65SGarrett D'Amore 
1336*68c47f65SGarrett D'Amore 	/* stop the DMA engines */
1337*68c47f65SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_stop, port->tp_dma_mask);
1338*68c47f65SGarrett D'Amore 
1339*68c47f65SGarrett D'Amore 	/* now make sure it starts playing */
1340*68c47f65SGarrett D'Amore 	ddi_put32(handle, &regs->aud_regs.ap_start, port->tp_dma_mask);
1341*68c47f65SGarrett D'Amore 
134288447a05SGarrett D'Amore 	return (0);
134388447a05SGarrett D'Amore }
134488447a05SGarrett D'Amore 
134588447a05SGarrett D'Amore /*
134688447a05SGarrett D'Amore  * audiots_chinfo()
134788447a05SGarrett D'Amore  *
134888447a05SGarrett D'Amore  * Description:
134988447a05SGarrett D'Amore  *	This is called by the framework to query the channel offsets
135088447a05SGarrett D'Amore  *	and ordering.
135188447a05SGarrett D'Amore  *
135288447a05SGarrett D'Amore  * Arguments:
135388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
135488447a05SGarrett D'Amore  *	int	chan		Channel number.
135588447a05SGarrett D'Amore  *	unsigned *offset	Starting offset of channel.
135688447a05SGarrett D'Amore  *	unsigned *incr		Increment (in samples) between frames.
135788447a05SGarrett D'Amore  *
135888447a05SGarrett D'Amore  * Returns:
135988447a05SGarrett D'Amore  *	0 indicating rate array is range instead of enumeration
136088447a05SGarrett D'Amore  */
136188447a05SGarrett D'Amore 
136288447a05SGarrett D'Amore static void
audiots_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)136388447a05SGarrett D'Amore audiots_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
136488447a05SGarrett D'Amore {
1365807e8f10SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
1366807e8f10SGarrett D'Amore 	*offset = chan;
136788447a05SGarrett D'Amore 	*incr = 2;
136888447a05SGarrett D'Amore }
136988447a05SGarrett D'Amore 
137088447a05SGarrett D'Amore /*
137188447a05SGarrett D'Amore  * audiots_format()
137288447a05SGarrett D'Amore  *
137388447a05SGarrett D'Amore  * Description:
137488447a05SGarrett D'Amore  *	Called by the framework to query the format for the device.
137588447a05SGarrett D'Amore  *
137688447a05SGarrett D'Amore  * Arguments:
137788447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
137888447a05SGarrett D'Amore  *
137988447a05SGarrett D'Amore  * Returns:
138088447a05SGarrett D'Amore  *	AUDIO_FORMAT_S16_LE.
138188447a05SGarrett D'Amore  */
138288447a05SGarrett D'Amore static int
audiots_format(void * arg)138388447a05SGarrett D'Amore audiots_format(void *arg)
138488447a05SGarrett D'Amore {
138588447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
138688447a05SGarrett D'Amore 
138788447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
138888447a05SGarrett D'Amore }
138988447a05SGarrett D'Amore 
139088447a05SGarrett D'Amore 
139188447a05SGarrett D'Amore /*
139288447a05SGarrett D'Amore  * audiots_channels()
139388447a05SGarrett D'Amore  *
139488447a05SGarrett D'Amore  * Description:
139588447a05SGarrett D'Amore  *	Called by the framework to query the channnels for the device.
139688447a05SGarrett D'Amore  *
139788447a05SGarrett D'Amore  * Arguments:
139888447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
139988447a05SGarrett D'Amore  *
140088447a05SGarrett D'Amore  * Returns:
140188447a05SGarrett D'Amore  *	2 (Stereo).
140288447a05SGarrett D'Amore  */
140388447a05SGarrett D'Amore static int
audiots_channels(void * arg)140488447a05SGarrett D'Amore audiots_channels(void *arg)
140588447a05SGarrett D'Amore {
140688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
140788447a05SGarrett D'Amore 
140888447a05SGarrett D'Amore 	return (2);
140988447a05SGarrett D'Amore }
141088447a05SGarrett D'Amore 
141188447a05SGarrett D'Amore /*
141288447a05SGarrett D'Amore  * audiots_rate()
141388447a05SGarrett D'Amore  *
141488447a05SGarrett D'Amore  * Description:
141588447a05SGarrett D'Amore  *	Called by the framework to query the sample rates for the device.
141688447a05SGarrett D'Amore  *
141788447a05SGarrett D'Amore  * Arguments:
141888447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
141988447a05SGarrett D'Amore  *
142088447a05SGarrett D'Amore  * Returns:
142188447a05SGarrett D'Amore  *	Sample rate in HZ (always 48000).
142288447a05SGarrett D'Amore  */
142388447a05SGarrett D'Amore static int
audiots_rate(void * arg)142488447a05SGarrett D'Amore audiots_rate(void *arg)
142588447a05SGarrett D'Amore {
142688447a05SGarrett D'Amore 	audiots_port_t *port = arg;
142788447a05SGarrett D'Amore 
142888447a05SGarrett D'Amore 	return (port->tp_rate);
142988447a05SGarrett D'Amore }
143088447a05SGarrett D'Amore 
143188447a05SGarrett D'Amore /*
143288447a05SGarrett D'Amore  * audiots_count()
143388447a05SGarrett D'Amore  *
143488447a05SGarrett D'Amore  * Description:
143588447a05SGarrett D'Amore  *	This is called by the framework to get the engine's frame counter
143688447a05SGarrett D'Amore  *
143788447a05SGarrett D'Amore  * Arguments:
143888447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
143988447a05SGarrett D'Amore  *
144088447a05SGarrett D'Amore  * Returns:
144188447a05SGarrett D'Amore  *	frame count for current engine
144288447a05SGarrett D'Amore  */
144388447a05SGarrett D'Amore static uint64_t
audiots_count(void * arg)144488447a05SGarrett D'Amore audiots_count(void *arg)
144588447a05SGarrett D'Amore {
144688447a05SGarrett D'Amore 	audiots_port_t	*port = arg;
144788447a05SGarrett D'Amore 	audiots_state_t	*state = port->tp_state;
144888447a05SGarrett D'Amore 	uint64_t	val;
1449*68c47f65SGarrett D'Amore 	uint16_t	cso;
1450*68c47f65SGarrett D'Amore 	unsigned	n;
1451*68c47f65SGarrett D'Amore 
1452*68c47f65SGarrett D'Amore 	cso = ddi_get16(state->ts_acch,
1453*68c47f65SGarrett D'Amore 	    &state->ts_regs->aud_ram[port->tp_dma_stream].aram.aram_cso);
145488447a05SGarrett D'Amore 
1455*68c47f65SGarrett D'Amore 	n = (cso >= port->tp_cso) ?
1456*68c47f65SGarrett D'Amore 	    cso - port->tp_cso :
1457*68c47f65SGarrett D'Amore 	    cso + port->tp_nframes - port->tp_cso;
145888447a05SGarrett D'Amore 
1459*68c47f65SGarrett D'Amore 	port->tp_cso = cso;
1460*68c47f65SGarrett D'Amore 	port->tp_count += n;
146188447a05SGarrett D'Amore 	val = port->tp_count;
1462*68c47f65SGarrett D'Amore 
146388447a05SGarrett D'Amore 	return (val);
146488447a05SGarrett D'Amore }
146588447a05SGarrett D'Amore 
146688447a05SGarrett D'Amore /*
146788447a05SGarrett D'Amore  * audiots_sync()
146888447a05SGarrett D'Amore  *
146988447a05SGarrett D'Amore  * Description:
147088447a05SGarrett D'Amore  *	This is called by the framework to synchronize DMA caches.
147188447a05SGarrett D'Amore  *
147288447a05SGarrett D'Amore  * Arguments:
147388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to sync
147488447a05SGarrett D'Amore  */
147588447a05SGarrett D'Amore static void
audiots_sync(void * arg,unsigned nframes)147688447a05SGarrett D'Amore audiots_sync(void *arg, unsigned nframes)
147788447a05SGarrett D'Amore {
147888447a05SGarrett D'Amore 	audiots_port_t *port = arg;
147988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
148088447a05SGarrett D'Amore 
148188447a05SGarrett D'Amore 	(void) ddi_dma_sync(port->tp_dmah, 0, 0, port->tp_sync_dir);
148288447a05SGarrett D'Amore }
148388447a05SGarrett D'Amore 
148488447a05SGarrett D'Amore /*
148588447a05SGarrett D'Amore  * audiots_stop_everything()
148688447a05SGarrett D'Amore  *
148788447a05SGarrett D'Amore  * Description:
148888447a05SGarrett D'Amore  *	This routine disables the address engine interrupt for all 32 DMA
148988447a05SGarrett D'Amore  *	engines. Just to be sure, it then explicitly issues a stop command to
149088447a05SGarrett D'Amore  *	the address engine and envelope engines for all 32 channels.
149188447a05SGarrett D'Amore  *
149288447a05SGarrett D'Amore  * NOTE:
149388447a05SGarrett D'Amore  *
149488447a05SGarrett D'Amore  * 	There is a hardware bug that generates a spurious interrupt
149588447a05SGarrett D'Amore  *	when the DMA engines are stopped. It's not consistent - it
149688447a05SGarrett D'Amore  *	happens every 1 out of 6 stops or so. It will show up as a
149788447a05SGarrett D'Amore  *	record interrupt. The problem is that once the driver is
149888447a05SGarrett D'Amore  *	detached or if the system goes into low power mode, nobody
149988447a05SGarrett D'Amore  *	will service that interrupt. The system will eventually become
150088447a05SGarrett D'Amore  *	unusable.
150188447a05SGarrett D'Amore  *
150288447a05SGarrett D'Amore  * Arguments:
150388447a05SGarrett D'Amore  *	audiots_state_t	*state		The device's state structure
150488447a05SGarrett D'Amore  */
150588447a05SGarrett D'Amore static void
audiots_stop_everything(audiots_state_t * state)150688447a05SGarrett D'Amore audiots_stop_everything(audiots_state_t *state)
150788447a05SGarrett D'Amore {
150888447a05SGarrett D'Amore 	if (state->ts_acch == NULL)
150988447a05SGarrett D'Amore 		return;
151088447a05SGarrett D'Amore 
151188447a05SGarrett D'Amore 	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
151288447a05SGarrett D'Amore 	    TS_ALL_DMA_OFF);
151388447a05SGarrett D'Amore 
151488447a05SGarrett D'Amore 	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
151588447a05SGarrett D'Amore 	    TS_ALL_DMA_ENGINES);
151688447a05SGarrett D'Amore 
151788447a05SGarrett D'Amore 	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_aint,
151888447a05SGarrett D'Amore 	    TS_ALL_DMA_ENGINES);
151988447a05SGarrett D'Amore }
152088447a05SGarrett D'Amore 
152188447a05SGarrett D'Amore /*
152288447a05SGarrett D'Amore  * audiots_free_port()
152388447a05SGarrett D'Amore  *
152488447a05SGarrett D'Amore  * Description:
152588447a05SGarrett D'Amore  *	This routine unbinds the DMA cookies, frees the DMA buffers,
152688447a05SGarrett D'Amore  *	deallocates the DMA handles.
152788447a05SGarrett D'Amore  *
152888447a05SGarrett D'Amore  * Arguments:
152988447a05SGarrett D'Amore  *	audiots_port_t	*port	The port structure for a device stream.
153088447a05SGarrett D'Amore  */
153188447a05SGarrett D'Amore void
audiots_free_port(audiots_port_t * port)153288447a05SGarrett D'Amore audiots_free_port(audiots_port_t *port)
153388447a05SGarrett D'Amore {
153488447a05SGarrett D'Amore 	if (port == NULL)
153588447a05SGarrett D'Amore 		return;
153688447a05SGarrett D'Amore 
153788447a05SGarrett D'Amore 	if (port->tp_engine) {
153888447a05SGarrett D'Amore 		audio_dev_remove_engine(port->tp_state->ts_adev,
153988447a05SGarrett D'Amore 		    port->tp_engine);
154088447a05SGarrett D'Amore 		audio_engine_free(port->tp_engine);
154188447a05SGarrett D'Amore 	}
154288447a05SGarrett D'Amore 	if (port->tp_paddr) {
154388447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(port->tp_dmah);
154488447a05SGarrett D'Amore 	}
154588447a05SGarrett D'Amore 	if (port->tp_acch) {
154688447a05SGarrett D'Amore 		ddi_dma_mem_free(&port->tp_acch);
154788447a05SGarrett D'Amore 	}
154888447a05SGarrett D'Amore 	if (port->tp_dmah) {
154988447a05SGarrett D'Amore 		ddi_dma_free_handle(&port->tp_dmah);
155088447a05SGarrett D'Amore 	}
155188447a05SGarrett D'Amore 	kmem_free(port, sizeof (*port));
155288447a05SGarrett D'Amore }
155388447a05SGarrett D'Amore 
155488447a05SGarrett D'Amore /*
155588447a05SGarrett D'Amore  * audiots_destroy()
155688447a05SGarrett D'Amore  *
155788447a05SGarrett D'Amore  * Description:
155888447a05SGarrett D'Amore  *	This routine releases all resources held by the device instance,
155988447a05SGarrett D'Amore  *	as part of either detach or a failure in attach.
156088447a05SGarrett D'Amore  *
156188447a05SGarrett D'Amore  * Arguments:
156288447a05SGarrett D'Amore  *	audiots_state_t	*state	The device soft state.
156388447a05SGarrett D'Amore  */
156488447a05SGarrett D'Amore void
audiots_destroy(audiots_state_t * state)156588447a05SGarrett D'Amore audiots_destroy(audiots_state_t *state)
156688447a05SGarrett D'Amore {
156788447a05SGarrett D'Amore 	audiots_stop_everything(state);
156888447a05SGarrett D'Amore 
156988447a05SGarrett D'Amore 	for (int i = 0; i < TS_NUM_PORTS; i++)
157088447a05SGarrett D'Amore 		audiots_free_port(state->ts_ports[i]);
157188447a05SGarrett D'Amore 
157288447a05SGarrett D'Amore 	if (state->ts_acch)
157388447a05SGarrett D'Amore 		ddi_regs_map_free(&state->ts_acch);
157488447a05SGarrett D'Amore 
157588447a05SGarrett D'Amore 	if (state->ts_pcih)
157688447a05SGarrett D'Amore 		pci_config_teardown(&state->ts_pcih);
157788447a05SGarrett D'Amore 
157888447a05SGarrett D'Amore 	if (state->ts_ac97)
157988447a05SGarrett D'Amore 		ac97_free(state->ts_ac97);
158088447a05SGarrett D'Amore 
158188447a05SGarrett D'Amore 	if (state->ts_adev)
158288447a05SGarrett D'Amore 		audio_dev_free(state->ts_adev);
158388447a05SGarrett D'Amore 
158488447a05SGarrett D'Amore 	ddi_soft_state_free(audiots_statep, ddi_get_instance(state->ts_dip));
158588447a05SGarrett D'Amore }
1586