/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * audiots Audio Driver
 *
 * This Audio Driver controls the T2 audio core in the ALI M1553
 * southbridge chip. This chip supports multiple play streams, but just
 * a single record stream. It also supports wave table synthesis and
 * hardware MIDI and joystick ports. Unfortunately the MIDI ports are
 * not available because their pins have been re-assigned to expose
 * interrupts. We also aren't going to do anything with the joystick
 * ports. The audio core controls an AC-97 V2.1 Codec.
 *
 * The DMA engine uses a single buffer which is large enough to hold
 * two interrupts worth of data. When it gets to the mid point an
 * interrupt is generated and data is either sent (for record) or
 * requested and put in that half of the buffer (for play). When the
 * second half is played we do the same, but the audio core loops the
 * pointer back to the beginning.
 *
 * The audio core has a bug in silicon that doesn't let it read the AC-97
 * Codec's register. T2 has provided an algorithm that attempts to read the
 * the Codec several times. This is probably heuristic and thus isn't
 * absolutely guaranteed to work. However we do have to place a limit on
 * the looping, otherwise when we read a valid 0x00 we would never exit
 * the loop. Unfortunately there is also a problem with writing the AC-97
 * Codec's registers as well. Thus we read it back to verify the write.
 *
 * The AC'97 common code provides shadow state for AC'97 registers for us,
 * so we only need to read those registers during early startup (primarily
 * to determine codec id and capabilities.)
 *
 * We don't save any of the audio controller registers during normal
 * operation. When we need to save register state we only have to save
 * the aram and eram. The rest of the controller state is never modified
 * from the initial programming. Thus restoring the controller state
 * can be done from audiots_chip_init() as well.
 *
 *
 * WARNING: The SME birdsnest platform uses a PCI bridge chip between the
 *	CPU and the southbridge containing the audio core. There is
 *	a bug in silicon that causes a bogus parity error. With the mixer
 *	reimplementation project, Bug 4374774, the audio driver is always
 *	set to the best precision and number of channels. Thus when turning
 *	the mixer on and off the only thing that changes is the sample rate.
 *	This change in programming doesn't trigger the silicon error.
 *	Thus the supported channels must always be 2 and the precision
 *	must always be 16-bits. This will keep any future change in the
 *	mixer from exposing this bug.
 *
 * Due to a hardware bug, system power management is not supported by this
 * driver.
 *
 *	CAUTION: If audio controller state is changed outside of aram
 *		and eram then that information must be saved and restored
 *		during power management shutdown and bringup.
 *
 *	NOTE: The AC-97 Codec's reset pin is set to PCI reset, so we
 *		can't power down the Codec all the way.
 *
 *	NOTE: This driver depends on the drv/audio and misc/ac97
 *		modules being loaded first.
 *
 *	NOTE: Don't OR the ap_stop register to stop a play or record. This
 *		will just stop all active channels because a read of ap_stop
 *		returns ap_start. Just set the ap_stop register with the
 *		channels you want to stop. The same goes for ap_start.
 *
 *	NOTE: There is a hardware problem with P2 rev motherboards. After
 *		prolonged use, reading the AC97 register will always return
 *		busy. The AC97 register is now useless. Consequently, we are no
 *		longer able to program the Codec. This work around disables
 *		audio when this state is detected. It's not great, but its
 *		better than having audio blasting out at 100% all the time.
 *
 *	NOTE: Power Management testing has also exposed this AC97 timeout
 *		problem. Management has decided this is too risky for customers
 *		and hence they want power management support removed from the
 *		audio subsystem. All PM support is now removed.
 */

/*
 * Synchronization notes:
 *
 * The audio framework guarantees that our entry points are exclusive
 * with suspend and resume.  This includes data flow and control entry
 * points alike.
 *
 * The audio framework guarantees that only one control is being
 * accessed on any given audio device at a time.
 *
 * The audio framework guarantees that entry points are themselves
 * serialized for a given engine.
 *
 * We have no interrupt routine or other internal asynchronous routines.
 *
 * Our device uses completely separate registers for each engine,
 * except for the start/stop registers, which are implemented in a
 * manner that allows for them to be accessed concurrently safely from
 * different threads.
 *
 * Hence, it turns out that we simply don't need any locking in this
 * driver.
 */

#include <sys/modctl.h>
#include <sys/kmem.h>
#include <sys/pci.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/debug.h>
#include <sys/note.h>
#include <sys/audio/audio_driver.h>
#include <sys/audio/ac97.h>
#include "audiots.h"

/*
 * Module linkage routines for the kernel
 */
static int audiots_attach(dev_info_t *, ddi_attach_cmd_t);
static int audiots_detach(dev_info_t *, ddi_detach_cmd_t);
static int audiots_quiesce(dev_info_t *);

/*
 * Entry point routine prototypes
 */
static int audiots_open(void *, int, unsigned *, caddr_t *);
static void audiots_close(void *);
static int audiots_start(void *);
static void audiots_stop(void *);
static int audiots_format(void *);
static int audiots_channels(void *);
static int audiots_rate(void *);
static void audiots_chinfo(void *, int, unsigned *, unsigned *);
static uint64_t audiots_count(void *);
static void audiots_sync(void *, unsigned);

static audio_engine_ops_t	audiots_engine_ops = {
	AUDIO_ENGINE_VERSION,
	audiots_open,
	audiots_close,
	audiots_start,
	audiots_stop,
	audiots_count,
	audiots_format,
	audiots_channels,
	audiots_rate,
	audiots_sync,
	NULL,
	audiots_chinfo,
	NULL,
};

/*
 * Local Routine Prototypes
 */
static void audiots_power_up(audiots_state_t *);
static void audiots_chip_init(audiots_state_t *);
static uint16_t audiots_get_ac97(void *, uint8_t);
static void audiots_set_ac97(void *, uint8_t, uint16_t);
static int audiots_init_state(audiots_state_t *, dev_info_t *);
static int audiots_map_regs(dev_info_t *, audiots_state_t *);
static uint16_t audiots_read_ac97(audiots_state_t *, int);
static void audiots_stop_everything(audiots_state_t *);
static void audiots_destroy(audiots_state_t *);
static int audiots_alloc_port(audiots_state_t *, int);

/*
 * Global variables, but viewable only by this file.
 */

/* anchor for soft state structures */
static void *audiots_statep;

/*
 * DDI Structures
 */

/* Device operations structure */
static struct dev_ops audiots_dev_ops = {
	DEVO_REV,		/* devo_rev */
	0,			/* devo_refcnt */
	NULL,			/* devo_getinfo */
	nulldev,		/* devo_identify - obsolete */
	nulldev,		/* devo_probe */
	audiots_attach,		/* devo_attach */
	audiots_detach,		/* devo_detach */
	nodev,			/* devo_reset */
	NULL,			/* devo_cb_ops */
	NULL,			/* devo_bus_ops */
	NULL,			/* devo_power */
	audiots_quiesce,	/* devo_quiesce */
};

/* Linkage structure for loadable drivers */
static struct modldrv audiots_modldrv = {
	&mod_driverops,		/* drv_modops */
	TS_MOD_NAME,		/* drv_linkinfo */
	&audiots_dev_ops	/* drv_dev_ops */
};

/* Module linkage structure */
static struct modlinkage audiots_modlinkage = {
	MODREV_1,			/* ml_rev */
	(void *)&audiots_modldrv,	/* ml_linkage */
	NULL				/* NULL terminates the list */
};


/*
 * NOTE: Grover OBP v4.0.166 and rev G of the ALI Southbridge chip force the
 * audiots driver to use the upper 2 GB DMA address range. However to maintain
 * backwards compatibility with older systems/OBP, we're going to try the full
 * 4 GB DMA range.
 *
 * Eventually, this will be set back to using the proper high 2 GB DMA range.
 */

/* Device attribute structure - full 4 gig address range */
static ddi_dma_attr_t audiots_attr = {
	DMA_ATTR_VERSION,		/* version */
	0x0000000000000000LL,		/* dlim_addr_lo */
	0x00000000ffffffffLL,		/* dlim_addr_hi */
	0x0000000000003fffLL,		/* DMA counter register - 16 bits */
	0x0000000000000008LL,		/* DMA address alignment, 64-bit */
	0x0000007f,			/* 1 through 64 byte burst sizes */
	0x00000001,			/* min effective DMA size */
	0x0000000000003fffLL,		/* maximum transfer size, 16k */
	0x000000000000ffffLL,		/* segment boundary, 64k */
	0x00000001,			/* s/g list length, no s/g */
	0x00000001,			/* granularity of device, don't care */
	0				/* DMA flags */
};

static ddi_device_acc_attr_t ts_acc_attr = {
	DDI_DEVICE_ATTR_V0,
	DDI_NEVERSWAP_ACC,
	DDI_STRICTORDER_ACC
};

static ddi_device_acc_attr_t ts_regs_attr = {
	DDI_DEVICE_ATTR_V0,
	DDI_STRUCTURE_LE_ACC,
	DDI_STRICTORDER_ACC
};

/*
 * _init()
 *
 * Description:
 *	Driver initialization, called when driver is first loaded.
 *	This is how access is initially given to all the static structures.
 *
 * Arguments:
 *	None
 *
 * Returns:
 *	ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
 *	mod_install() status, see mod_install(9f)
 */
int
_init(void)
{
	int		error;

	audio_init_ops(&audiots_dev_ops, TS_NAME);

	/* initialize the soft state */
	if ((error = ddi_soft_state_init(&audiots_statep,
	    sizeof (audiots_state_t), 1)) != 0) {
		audio_fini_ops(&audiots_dev_ops);
		return (error);
	}

	if ((error = mod_install(&audiots_modlinkage)) != 0) {
		audio_fini_ops(&audiots_dev_ops);
		ddi_soft_state_fini(&audiots_statep);
	}

	return (error);
}

/*
 * _fini()
 *
 * Description:
 *	Module de-initialization, called when the driver is to be unloaded.
 *
 * Arguments:
 *	None
 *
 * Returns:
 *	mod_remove() status, see mod_remove(9f)
 */
int
_fini(void)
{
	int		error;

	if ((error = mod_remove(&audiots_modlinkage)) != 0) {
		return (error);
	}

	/* free the soft state internal structures */
	ddi_soft_state_fini(&audiots_statep);

	/* clean up ops */
	audio_fini_ops(&audiots_dev_ops);

	return (0);
}

/*
 * _info()
 *
 * Description:
 *	Module information, returns infomation about the driver.
 *
 * Arguments:
 *	modinfo *modinfop	Pointer to the opaque modinfo structure
 *
 * Returns:
 *	mod_info() status, see mod_info(9f)
 */
int
_info(struct modinfo *modinfop)
{
	int		error;

	error = mod_info(&audiots_modlinkage, modinfop);

	return (error);
}


/*
 * audiots_attach()
 *
 * Description:
 *	Attach an instance of the audiots driver. This routine does the
 *	device dependent attach tasks.
 *
 * Arguments:
 *	dev_info_t	*dip	Pointer to the device's dev_info struct
 *	ddi_attach_cmd_t cmd	Attach command
 *
 * Returns:
 *	DDI_SUCCESS		The driver was initialized properly
 *	DDI_FAILURE		The driver couldn't be initialized properly
 */
static int
audiots_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	audiots_state_t		*state;
	int			instance;

	instance = ddi_get_instance(dip);

	switch (cmd) {
	case DDI_ATTACH:
		break;
	case DDI_RESUME:

		/* we've already allocated the state structure so get ptr */
		state = ddi_get_soft_state(audiots_statep, instance);
		ASSERT(dip == state->ts_dip);

		/* suspend/resume resets the chip, so we have no more faults */
		if (state->ts_flags & TS_AUDIO_READ_FAILED) {
			ddi_dev_report_fault(state->ts_dip,
			    DDI_SERVICE_RESTORED,
			    DDI_DEVICE_FAULT,
			    "check port, gain, balance, and mute settings");
			/* and clear the fault state flags */
			state->ts_flags &=
			    ~(TS_AUDIO_READ_FAILED|TS_READ_FAILURE_PRINTED);
		}

		audiots_power_up(state);
		audiots_chip_init(state);

		ac97_reset(state->ts_ac97);

		audio_dev_resume(state->ts_adev);

		return (DDI_SUCCESS);

	default:
		return (DDI_FAILURE);
	}

	/* before we do anything make sure that we haven't had a h/w failure */
	if (ddi_get_devstate(dip) == DDI_DEVSTATE_DOWN) {
		cmn_err(CE_WARN, "%s%d: The audio hardware has "
		    "been disabled.", ddi_driver_name(dip), instance);
		cmn_err(CE_CONT, "Please reboot to restore audio.");
		return (DDI_FAILURE);
	}

	/* allocate the state structure */
	if (ddi_soft_state_zalloc(audiots_statep, instance) == DDI_FAILURE) {
		cmn_err(CE_WARN, "!%s%d: soft state allocate failed",
		    ddi_driver_name(dip), instance);
		return (DDI_FAILURE);
	}

	/*
	 * WARNING: From here on all errors require that we free memory,
	 *	including the state structure.
	 */

	/* get the state structure - cannot fail */
	state = ddi_get_soft_state(audiots_statep, instance);
	ASSERT(state != NULL);

	if ((state->ts_adev = audio_dev_alloc(dip, 0)) == NULL) {
		cmn_err(CE_WARN, "unable to allocate audio dev");
		goto error;
	}

	/* map in the registers, allocate DMA buffers, etc. */
	if (audiots_map_regs(dip, state) == DDI_FAILURE) {
		audio_dev_warn(state->ts_adev, "unable to map registers");
		goto error;
	}

	/* initialize the audio state structures */
	if (audiots_init_state(state, dip) == DDI_FAILURE) {
		audio_dev_warn(state->ts_adev, "init state structure failed");
		goto error;
	}

	/* power up */
	audiots_power_up(state);

	/* initialize the audio controller */
	audiots_chip_init(state);

	/* initialize the AC-97 Codec */
	if (ac97_init(state->ts_ac97, state->ts_adev) != 0) {
		goto error;
	}

	/* put the engine interrupts into a known state -- all off */
	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
	    TS_ALL_DMA_OFF);

	/* call the framework attach routine */
	if (audio_dev_register(state->ts_adev) != DDI_SUCCESS) {
		audio_dev_warn(state->ts_adev, "unable to register audio");
		goto error;
	}

	/* everything worked out, so report the device */
	ddi_report_dev(dip);

	return (DDI_SUCCESS);

error:
	audiots_destroy(state);
	return (DDI_FAILURE);
}

/*
 * audiots_detach()
 *
 * Description:
 *	Detach an instance of the audiots driver.
 *
 * Arguments:
 *	dev_info_t	*dip	Pointer to the device's dev_info struct
 *	ddi_detach_cmd_t cmd	Detach command
 *
 * Returns:
 *	DDI_SUCCESS		The driver was detached
 *	DDI_FAILURE		The driver couldn't be detached
 */
static int
audiots_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	audiots_state_t		*state;
	int			instance;

	instance = ddi_get_instance(dip);

	/* get the state structure */
	if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
		cmn_err(CE_WARN, "!%s%d: detach get soft state failed",
		    ddi_driver_name(dip), instance);
		return (DDI_FAILURE);
	}

	switch (cmd) {
	case DDI_DETACH:
		break;
	case DDI_SUSPEND:

		audio_dev_suspend(state->ts_adev);

		/* stop playing and recording */
		(void) audiots_stop_everything(state);

		return (DDI_SUCCESS);

	default:
		return (DDI_FAILURE);
	}

	/* attempt to unregister from the framework first */
	if (audio_dev_unregister(state->ts_adev) != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	audiots_destroy(state);

	return (DDI_SUCCESS);

}

/*
 * audiots_quiesce()
 *
 * Description:
 *	Quiesce an instance of the audiots driver. Stops all DMA and
 *	interrupts.
 *
 * Arguments:
 *	dev_info_t	*dip	Pointer to the device's dev_info struct
 *
 * Returns:
 *	DDI_SUCCESS		The driver was quiesced
 *	DDI_SUCCESS		The driver was NOT quiesced
 */
static int
audiots_quiesce(dev_info_t *dip)
{
	audiots_state_t		*state;
	int			instance;

	instance = ddi_get_instance(dip);

	/* get the state structure */
	if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
		return (DDI_FAILURE);
	}

	audiots_stop_everything(state);

	return (DDI_SUCCESS);
}

/*
 * audiots_power_up()
 *
 * Description
 *	Ensure that the device is running in PCI power state D0.
 */
static void
audiots_power_up(audiots_state_t *state)
{
	ddi_acc_handle_t	pcih = state->ts_pcih;
	uint8_t			ptr;
	uint16_t		pmcsr;

	if ((pci_config_get16(pcih, PCI_CONF_STAT) & PCI_STAT_CAP) == 0) {
		/* does not implement PCI capabilities -- no PM */
		return;
	}

	ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
	for (;;) {
		if (ptr == PCI_CAP_NEXT_PTR_NULL) {
			/* PM capability not found */
			return;
		}
		if (pci_config_get8(pcih, ptr + PCI_CAP_ID) == PCI_CAP_ID_PM) {
			/* found it */
			break;
		}
		ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR);
	}

	/* if we got here, then got valid PMCSR pointer */
	ptr += PCI_PMCSR;

	/* check to see if we are already in state D0 */
	pmcsr = pci_config_get16(pcih, ptr);
	if ((pmcsr & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_D0) {

		/* D3hot (or any other state) -> D0 */
		pmcsr &= ~PCI_PMCSR_STATE_MASK;
		pmcsr |= PCI_PMCSR_D0;
		pci_config_put16(pcih, ptr, pmcsr);
	}

	/*
	 * Wait for it to power up - PCI spec says 10 ms is enough.
	 * We double it.  Note that no locks are held when this routine
	 * is called, so we can sleep (we are in attach context only).
	 *
	 * We do this delay even if already powerd up, just to make
	 * sure we aren't seeing something that *just* transitioned
	 * into D0 state.
	 */
	delay(drv_usectohz(TS_20MS));

	/* clear PME# flag */
	pmcsr = pci_config_get16(pcih, ptr);
	pci_config_put16(pcih, ptr, pmcsr | PCI_PMCSR_PME_STAT);
}

/*
 * audiots_chip_init()
 *
 * Description:
 *	Initialize the audio core.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 */
static void
audiots_chip_init(audiots_state_t *state)
{
	ddi_acc_handle_t	handle = state->ts_acch;
	audiots_regs_t		*regs = state->ts_regs;
	int			str;

	/* start with all interrupts & dma channels disabled */
	ddi_put32(handle, &regs->aud_regs.ap_stop, TS_ALL_DMA_ENGINES);
	ddi_put32(handle, &regs->aud_regs.ap_ainten, TS_ALL_DMA_OFF);

	/* set global music and wave volume to 0dB */
	ddi_put32(handle, &regs->aud_regs.ap_volume, 0x0);

	/* enable end interrupts for all channels. */
	ddi_put32(handle, &regs->aud_regs.ap_cir_gc, AP_CIR_GC_ENDLP_IE);

	/* for each stream, set gain and vol settings */
	for (str = 0; str < TS_MAX_HW_CHANNELS; str++) {
		/*
		 * Set volume to all off, 1st left and then right.
		 * These are never changed, so we don't have to save them.
		 */
		ddi_put16(handle,
		    &regs->aud_ram[str].eram.eram_gvsel_pan_vol,
		    (ERAM_WAVE_VOL|ERAM_PAN_LEFT|ERAM_PAN_0dB|
		    ERAM_VOL_MAX_ATTEN));
		ddi_put16(handle,
		    &regs->aud_ram[str].eram.eram_gvsel_pan_vol,
		    (ERAM_WAVE_VOL|ERAM_PAN_RIGHT|ERAM_PAN_0dB|
		    ERAM_VOL_MAX_ATTEN));

		/*
		 * The envelope engine *MUST* remain in still mode (off).
		 * Otherwise bad things like gain randomly disappearing might
		 * happen. See bug #4332773.
		 */

		ddi_put32(handle, &regs->aud_ram[str].eram.eram_ebuf1,
		    ERAM_EBUF_STILL);
		ddi_put32(handle, &regs->aud_ram[str].eram.eram_ebuf2,
		    ERAM_EBUF_STILL);

		/* program the initial eram and aram rate */
		ddi_put16(handle, &regs->aud_ram[str].aram.aram_delta,
		    1 << TS_SRC_SHIFT);
		ddi_put16(handle, &regs->aud_ram[str].eram.eram_ctrl_ec,
		    ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE |
		    ERAM_SIGNED_PCM);
	}

	/* program channel 31 for record */
	OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_global_control,
	    (AP_CLOGAL_CTRL_E_PCMIN_CH31|AP_CLOGAL_CTRL_PCM_OUT_AC97|
	    AP_CLOGAL_CTRL_MMC_FROM_MIXER|AP_CLOGAL_CTRL_PCM_OUT_TO_AC97));

	/* do a warm reset, which powers up the Codec */
	OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
	    AP_SCTRL_WRST_CODEC);
	drv_usecwait(2);
	AND_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
	    ~AP_SCTRL_WRST_CODEC);

	/* do a warm reset via the Codec, yes, I'm being paranoid! */
	audiots_set_ac97(state, AC97_RESET_REGISTER, 0);

	/* Make sure the Codec is powered up. */
	int i = TS_WAIT_CNT;
	while ((audiots_get_ac97(state, AC97_POWERDOWN_CTRL_STAT_REGISTER) &
	    PCSR_POWERD_UP) != PCSR_POWERD_UP && i--) {
		drv_usecwait(1);
	}

}

/*
 * audiots_get_ac97()
 *
 * Description:
 *	Get the value in the specified AC-97 Codec register. There is a
 *	bug in silicon which forces us to do multiple reads of the Codec's
 *	register. This algorithm was provided by T2 and is heuristic in
 *	nature. Unfortunately we have no guarantees that the real answer
 *	isn't 0x0000, which is what we get when a read fails. So we loop
 *	TS_LOOP_CNT times before we give up. We just have to hope this is
 *	sufficient to give us the correct value.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 *	int		reg		AC-97 register number
 *
 * Returns:
 *	unsigned short		The value in the specified register
 */
static uint16_t
audiots_get_ac97(void *arg, uint8_t reg)
{
	audiots_state_t		*state = arg;
	ddi_acc_handle_t	handle = state->ts_acch;
	uint16_t		*data;
	int			count;
	int			delay;
	uint16_t		first;
	uint16_t		next;

	if (state->ts_revid == AC_REV_ID1) {
		data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
	} else {
		data = &state->ts_regs->aud_regs.ap_acrdwr_data;
	}

	/* make sure the register is good */
	reg &= AP_ACRD_INDEX_MASK;
	for (count = TS_LOOP_CNT; count--; ) {
		if ((first = audiots_read_ac97(state, reg)) != 0) {
			next = first;
			break;
		}

		delay = TS_DELAY_CNT;
		while (delay--) {
			(void) ddi_get16(handle, data);
		}

		if ((next = audiots_read_ac97(state, reg)) != 0) {
			break;
		}
	}

	/*
	 * Arggg, if you let the next read happen too soon then it fails.
	 * 12 usec fails, 13 usec succeeds. So set it to 20 for safety.
	 */
	drv_usecwait(TS_20US);

	return (next);

}

/*
 * audiots_init_state()
 *
 * Description:
 *	This routine initializes the audio driver's state structure.
 *	This includes reading the properties.
 *
 *	CAUTION: This routine cannot allocate resources, unless it frees
 *		them before returning for an error. Also, error_destroy:
 *		in audiots_attach() would need to be fixed as well.
 *
 *	NOTE: birdsnest supports CD ROM input. We check for the cdrom
 *		property. If there we turn it on.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 *	dev_info_t	*dip		Pointer to the device's dev_info struct
 *
 * Returns:
 *	DDI_SUCCESS			State structure initialized
 *	DDI_FAILURE			State structure not initialized
 */
static int
audiots_init_state(audiots_state_t *state, dev_info_t *dip)
{
	state->ts_ac97 = ac97_alloc(dip, audiots_get_ac97,
	    audiots_set_ac97, state);

	if (state->ts_ac97 == NULL) {
		return (DDI_FAILURE);
	}

	/* save the device info pointer */
	state->ts_dip = dip;

	for (int i = 0; i < TS_NUM_PORTS; i++) {
		if (audiots_alloc_port(state, i) != DDI_SUCCESS) {
			return (DDI_FAILURE);
		}
	}

	return (DDI_SUCCESS);

}

/*
 * audiots_map_regs()
 *
 * Description:
 *	This routine maps the registers in.
 *
 *	Once the config space registers are mapped in we determine if the
 *	audio core may be power managed. It should, but if it doesn't,
 *	then trying to may cause the core to hang.
 *
 *	CAUTION: Make sure all errors call audio_dev_warn().
 *
 * Arguments:
 *	dev_info_t	*dip            Pointer to the device's devinfo
 *	audiots_state_t	*state          The device's state structure
 * Returns:
 *	DDI_SUCCESS		Registers successfully mapped
 *	DDI_FAILURE		Registers not successfully mapped
 */
static int
audiots_map_regs(dev_info_t *dip, audiots_state_t *state)
{
	char	rev[16];
	char	*name;

	/* map in the registers, the config and memory mapped registers */
	if (pci_config_setup(dip, &state->ts_pcih) != DDI_SUCCESS) {
		audio_dev_warn(state->ts_adev,
		    "unable to map PCI configuration space");
		return (DDI_FAILURE);
	}

	/* Read the Audio Controller's vendor, device, and revision IDs */
	state->ts_devid =
	    (pci_config_get16(state->ts_pcih, PCI_CONF_VENID) << 16) |
	    pci_config_get16(state->ts_pcih, PCI_CONF_DEVID);
	state->ts_revid = pci_config_get8(state->ts_pcih, PCI_CONF_REVID);

	if (ddi_regs_map_setup(dip, TS_MEM_MAPPED_REGS,
	    (caddr_t *)&state->ts_regs, 0, 0, &ts_regs_attr, &state->ts_acch) !=
	    DDI_SUCCESS) {
		audio_dev_warn(state->ts_adev,
		    "unable to map PCI device registers");
		return (DDI_FAILURE);
	}

	switch (state->ts_devid) {
	case 0x10b95451:
		name = "ALI M5451";
		break;
	default:
		name = "audiots";
		break;
	}
	(void) snprintf(rev, sizeof (rev), "Rev %x", state->ts_revid);
	audio_dev_set_description(state->ts_adev, name);
	audio_dev_set_version(state->ts_adev, rev);

	return (DDI_SUCCESS);
}

/*
 * audiots_alloc_port()
 *
 * Description:
 *	This routine allocates the DMA handles and the memory for the
 *	DMA engines to use. It then binds each of the buffers to its
 *	respective handle, getting a DMA cookie.
 *
 *	NOTE: All of the ddi_dma_... routines sleep if they cannot get
 *		memory. This means these calls should always succeed.
 *
 *	NOTE: ddi_dma_alloc_handle() attempts to use the full 4 GB DMA address
 *		range. This is to work around Southbridge rev E/G OBP issues.
 *		(See Grover OBP note above)
 *
 *	CAUTION: Make sure all errors call audio_dev_warn().
 *
 * Arguments:
 *	audiots_port_t	*state          The port structure for a device stream
 *	int		num		The port number
 *
 * Returns:
 *	DDI_SUCCESS		DMA resources mapped
 *	DDI_FAILURE		DMA resources not successfully mapped
 */
int
audiots_alloc_port(audiots_state_t *state, int num)
{
	audiots_port_t		*port;
	dev_info_t		*dip = state->ts_dip;
	audio_dev_t		*adev = state->ts_adev;
	int			dir;
	unsigned		caps;
	ddi_dma_cookie_t	cookie;
	unsigned		count;
	int			rc;
	ddi_acc_handle_t	regsh = state->ts_acch;
	uint32_t		*gcptr = &state->ts_regs->aud_regs.ap_cir_gc;

	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
	state->ts_ports[num] = port;
	port->tp_num = num;
	port->tp_state = state;
	port->tp_rate = TS_RATE;

	if (num == TS_INPUT_PORT) {
		dir = DDI_DMA_READ;
		caps = ENGINE_INPUT_CAP;
		port->tp_dma_stream = 31;
		port->tp_sync_dir = DDI_DMA_SYNC_FORKERNEL;
	} else {
		dir = DDI_DMA_WRITE;
		caps = ENGINE_OUTPUT_CAP;
		port->tp_dma_stream = 0;
		port->tp_sync_dir = DDI_DMA_SYNC_FORDEV;
	}

	port->tp_dma_mask = (1U << port->tp_dma_stream);
	port->tp_nframes = 4096;
	port->tp_size = port->tp_nframes * TS_FRAMESZ;

	/* allocate dma handle */
	rc = ddi_dma_alloc_handle(dip, &audiots_attr, DDI_DMA_SLEEP,
	    NULL, &port->tp_dmah);
	if (rc != DDI_SUCCESS) {
		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
		return (DDI_FAILURE);
	}
	/* allocate DMA buffer */
	rc = ddi_dma_mem_alloc(port->tp_dmah, port->tp_size, &ts_acc_attr,
	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->tp_kaddr,
	    &port->tp_size, &port->tp_acch);
	if (rc == DDI_FAILURE) {
		audio_dev_warn(adev, "dma_mem_alloc failed");
		return (DDI_FAILURE);
	}

	/* bind DMA buffer */
	rc = ddi_dma_addr_bind_handle(port->tp_dmah, NULL,
	    port->tp_kaddr, port->tp_size, dir|DDI_DMA_CONSISTENT,
	    DDI_DMA_SLEEP, NULL, &cookie, &count);
	if (rc != DDI_DMA_MAPPED) {
		audio_dev_warn(adev,
		    "ddi_dma_addr_bind_handle failed: %d", rc);
		return (DDI_FAILURE);
	}
	ASSERT(count == 1);

	port->tp_paddr = cookie.dmac_address;
	if ((unsigned)port->tp_paddr & 0x80000000U) {
		ddi_put32(regsh, gcptr,
		    ddi_get32(regsh, gcptr) | AP_CIR_GC_SYS_MEM_4G_ENABLE);
	} else {
		ddi_put32(regsh, gcptr,
		    ddi_get32(regsh, gcptr) & ~(AP_CIR_GC_SYS_MEM_4G_ENABLE));
	}
	port->tp_engine = audio_engine_alloc(&audiots_engine_ops, caps);
	if (port->tp_engine == NULL) {
		audio_dev_warn(adev, "audio_engine_alloc failed");
		return (DDI_FAILURE);
	}

	audio_engine_set_private(port->tp_engine, port);
	audio_dev_add_engine(adev, port->tp_engine);

	return (DDI_SUCCESS);
}

/*
 * audiots_read_ac97()
 *
 * Description:
 *	This routine actually reads the AC-97 Codec's register. It may
 *	be called several times to succeed.
 *
 * NOTE:
 * 	Revision M1535D B1-C of the ALI SouthBridge includes a workaround for
 *	the broken busy flag. Resetting the busy flag requires a software tweak
 *	to go with the worked around hardware. When we detect failure, we make
 *	10 attempts to reset the chip before we fail. This should reset the new
 *	SB systems. On all SB systems, this will increse the read delay
 *	slightly, but shouldn't bother it otherwise.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 *	int		reg		AC-97 register number
 *
 * Returns:
 *	unsigned short		The value in the specified register
 */
static uint16_t
audiots_read_ac97(audiots_state_t *state, int reg)
{
	ddi_acc_handle_t	acch = state->ts_acch;
	uint16_t		*addr;
	uint16_t		*data;
	uint32_t		*stimer = &state->ts_regs->aud_regs.ap_stimer;
	uint32_t		chk1;
	uint32_t		chk2;
	int			resets = 0;
	int			i;

	if (state->ts_revid == AC_REV_ID1) {
		addr = &state->ts_regs->aud_regs.ap_acrd_35D_reg;
		data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
	} else {
		addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
		data = &state->ts_regs->aud_regs.ap_acrdwr_data;
	}

first_read:
	/* wait for ready to send read request */
	for (i = 0; i < TS_READ_TRIES; i++) {
		if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
			break;
		}
		/* don't beat on the bus */
		drv_usecwait(1);
	}
	if (i >= TS_READ_TRIES) {
		if (resets < TS_RESET_TRIES) {
			/* Attempt to reset */
			drv_usecwait(TS_20US);
			ddi_put16(acch, addr, TS_SB_RESET);
			resets++;
			goto first_read;
		} else {
			state->ts_flags |= TS_AUDIO_READ_FAILED;
			if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
				ddi_dev_report_fault(state->ts_dip,
				    DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
				    "Unable to communicate with AC97 CODEC");
				audio_dev_warn(state->ts_adev,
				    "The audio AC97 register has timed out.");
				audio_dev_warn(state->ts_adev,
				    "Audio is now disabled.");
				audio_dev_warn(state->ts_adev,
				    "Please reboot to restore audio.");

				/* Don't flood the console */
				state->ts_flags |= TS_READ_FAILURE_PRINTED;
			}
		}
		return (0);
	}

	/* program the register to read */
	ddi_put16(acch, addr, (reg|AP_ACRD_W_PRIMARY_CODEC|
	    AP_ACRD_W_READ_MIXER_REG|AP_ACRD_W_AUDIO_READ_REQ&
	    (~AP_ACWR_W_SELECT_WRITE)));

	/* hardware bug work around */
	chk1 = ddi_get32(acch, stimer);
	chk2 = ddi_get32(acch, stimer);
	i = TS_WAIT_CNT;
	while (chk1 == chk2 && i) {
		chk2 = ddi_get32(acch, stimer);
		i--;
	}
	OR_SET_SHORT(acch, addr, AP_ACRD_W_READ_MIXER_REG);
	resets = 0;

second_read:
	/* wait again for read to send read request */
	for (i = 0; i < TS_READ_TRIES; i++) {
		if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
			break;
		}
		/* don't beat on the bus */
		drv_usecwait(1);
	}
	if (i >= TS_READ_TRIES) {
		if (resets < TS_RESET_TRIES) {
			/* Attempt to reset */
			drv_usecwait(TS_20US);
			ddi_put16(acch, addr, TS_SB_RESET);
			resets++;
			goto second_read;
		} else {
			state->ts_flags |= TS_AUDIO_READ_FAILED;
			if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
				ddi_dev_report_fault(state->ts_dip,
				    DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
				    "Unable to communicate with AC97 CODEC");
				audio_dev_warn(state->ts_adev,
				    "The audio AC97 register has timed out.");
				audio_dev_warn(state->ts_adev,
				    "Audio is now disabled.");
				audio_dev_warn(state->ts_adev,
				    "Please reboot to restore audio.");

				/* Don't flood the console */
				state->ts_flags |= TS_READ_FAILURE_PRINTED;
			}
		}
		return (0);
	}

	return (ddi_get16(acch, data));

}	/* audiots_read_ac97() */

/*
 * audiots_set_ac97()
 *
 * Description:
 *	Set the value in the specified AC-97 Codec register. Just like
 *	reading the AC-97 Codec, it is possible there is a problem writing
 *	it as well. So we loop.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 *	int		reg		AC-97 register number
 *	uint16_t	value		The value to write
 */
static void
audiots_set_ac97(void *arg, uint8_t reg8, uint16_t data)
{
	audiots_state_t	*state = arg;
	ddi_acc_handle_t handle = state->ts_acch;
	uint16_t	*data_addr = &state->ts_regs->aud_regs.ap_acrdwr_data;
	uint16_t	*reg_addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
	int		count;
	int		i;
	uint16_t	tmp_short;
	uint16_t	reg = reg8;

	reg &= AP_ACWR_INDEX_MASK;

	/* Don't touch the reserved bits on the pre 35D+ SouthBridge */
	if (state->ts_revid == AC_REV_ID1) {
		reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG;
	} else {
		reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG|
		    AP_ACWR_W_SELECT_WRITE;
	}

	for (count = TS_LOOP_CNT; count--; ) {
		/* wait for ready to write */
		for (i = 0; i < TS_WAIT_CNT; i++) {
			if (!(ddi_get16(handle, reg_addr) &
			    AP_ACWR_R_WRITE_BUSY)) {
				/* ready to write */
				ddi_put16(handle, reg_addr, reg);

				/* Write the data */
				ddi_put16(handle, data_addr, data);
				break;
			}
		}
		if (i >= TS_WAIT_CNT) {
			/* try again */
			continue;
		}

		/* wait for write to complete */
		for (i = 0; i < TS_WAIT_CNT; i++) {
			if (!(ddi_get16(handle, reg_addr) &
			    AP_ACWR_R_WRITE_BUSY)) {
				/* done writing */
				break;
			}
		}

		/* verify the value written */
		tmp_short = audiots_get_ac97(state, reg8);
		if (data == tmp_short) {
			/* successfully loaded, so we can return */
			return;
		}
	}

}	/* audiots_set_ac97() */

/*
 * audiots_open()
 *
 * Description:
 *	Opens a DMA engine for use.  Will also ensure the device is powered
 *	up if not already done so.
 *
 * Arguments:
 *	void		*arg		The DMA engine to set up
 *	int		flag		Open flags
 *	unsigned	*nframesp	Receives number of frames
 *	caddr_t		*bufp		Receives kernel data buffer
 *
 * Returns:
 *	0	on success
 *	errno	on failure
 */
static int
audiots_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
{
	audiots_port_t	*port = arg;

	_NOTE(ARGUNUSED(flag));

	port->tp_count = 0;
	port->tp_cso = 0;
	*nframesp = port->tp_nframes;
	*bufp = port->tp_kaddr;

	return (0);
}

/*
 * audiots_close()
 *
 * Description:
 *	Closes an audio DMA engine that was previously opened.  Since
 *	nobody is using it, we could take this opportunity to possibly power
 *	down the entire device, or at least the DMA engine.
 *
 * Arguments:
 *	void	*arg		The DMA engine to shut down
 */
static void
audiots_close(void *arg)
{
	_NOTE(ARGUNUSED(arg));
}

/*
 * audiots_stop()
 *
 * Description:
 *	This is called by the framework to stop a port that is
 *	transferring data.
 *
 * Arguments:
 *	void	*arg		The DMA engine to stop
 */
static void
audiots_stop(void *arg)
{
	audiots_port_t	*port = arg;
	audiots_state_t	*state = port->tp_state;

	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
	    port->tp_dma_mask);
}

/*
 * audiots_start()
 *
 * Description:
 *	This is called by the framework to start a port transferring data.
 *
 * Arguments:
 *	void	*arg		The DMA engine to start
 *
 * Returns:
 *	0 	on success (never fails, errno if it did)
 */
static int
audiots_start(void *arg)
{
	audiots_port_t		*port = arg;
	audiots_state_t		*state = port->tp_state;
	ddi_acc_handle_t	handle = state->ts_acch;
	audiots_regs_t		*regs = state->ts_regs;
	audiots_aram_t		*aram;
	audiots_eram_t		*eram;
	unsigned		delta;
	uint16_t		ctrl;
	uint16_t		gvsel;
	uint16_t		eso;

	aram = &regs->aud_ram[port->tp_dma_stream].aram;
	eram = &regs->aud_ram[port->tp_dma_stream].eram;

	port->tp_cso = 0;

	gvsel = ERAM_WAVE_VOL | ERAM_PAN_0dB | ERAM_VOL_DEFAULT;
	ctrl = ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE | ERAM_SIGNED_PCM;

	delta = (port->tp_rate << TS_SRC_SHIFT) / TS_RATE;

	if (port->tp_num == TS_INPUT_PORT) {
		delta = (TS_RATE << TS_SRC_SHIFT) / port->tp_rate;
	}
	eso = port->tp_nframes - 1;

	/* program the sample rate */
	ddi_put16(handle, &aram->aram_delta, (uint16_t)delta);

	/* program the precision, number of channels and loop mode */
	ddi_put16(handle, &eram->eram_ctrl_ec, ctrl);

	/* program the volume settings */
	ddi_put16(handle, &eram->eram_gvsel_pan_vol, gvsel);

	/* set ALPHA and FMS to 0 */
	ddi_put16(handle, &aram->aram_alpha_fms, 0x0);

	/* set CSO to 0 */
	ddi_put16(handle, &aram->aram_cso, 0x0);

	/* set LBA */
	ddi_put32(handle, &aram->aram_cptr_lba,
	    port->tp_paddr & ARAM_LBA_MASK);

	/* set ESO */
	ddi_put16(handle, &aram->aram_eso, eso);

	/* stop the DMA engines */
	ddi_put32(handle, &regs->aud_regs.ap_stop, port->tp_dma_mask);

	/* now make sure it starts playing */
	ddi_put32(handle, &regs->aud_regs.ap_start, port->tp_dma_mask);

	return (0);
}

/*
 * audiots_chinfo()
 *
 * Description:
 *	This is called by the framework to query the channel offsets
 *	and ordering.
 *
 * Arguments:
 *	void	*arg		The DMA engine to query
 *	int	chan		Channel number.
 *	unsigned *offset	Starting offset of channel.
 *	unsigned *incr		Increment (in samples) between frames.
 *
 * Returns:
 *	0 indicating rate array is range instead of enumeration
 */

static void
audiots_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
{
	_NOTE(ARGUNUSED(arg));
	*offset = chan;
	*incr = 2;
}

/*
 * audiots_format()
 *
 * Description:
 *	Called by the framework to query the format for the device.
 *
 * Arguments:
 *	void	*arg		The DMA engine to query
 *
 * Returns:
 *	AUDIO_FORMAT_S16_LE.
 */
static int
audiots_format(void *arg)
{
	_NOTE(ARGUNUSED(arg));

	return (AUDIO_FORMAT_S16_LE);
}


/*
 * audiots_channels()
 *
 * Description:
 *	Called by the framework to query the channnels for the device.
 *
 * Arguments:
 *	void	*arg		The DMA engine to query
 *
 * Returns:
 *	2 (Stereo).
 */
static int
audiots_channels(void *arg)
{
	_NOTE(ARGUNUSED(arg));

	return (2);
}

/*
 * audiots_rate()
 *
 * Description:
 *	Called by the framework to query the sample rates for the device.
 *
 * Arguments:
 *	void	*arg		The DMA engine to query
 *
 * Returns:
 *	Sample rate in HZ (always 48000).
 */
static int
audiots_rate(void *arg)
{
	audiots_port_t *port = arg;

	return (port->tp_rate);
}

/*
 * audiots_count()
 *
 * Description:
 *	This is called by the framework to get the engine's frame counter
 *
 * Arguments:
 *	void	*arg		The DMA engine to query
 *
 * Returns:
 *	frame count for current engine
 */
static uint64_t
audiots_count(void *arg)
{
	audiots_port_t	*port = arg;
	audiots_state_t	*state = port->tp_state;
	uint64_t	val;
	uint16_t	cso;
	unsigned	n;

	cso = ddi_get16(state->ts_acch,
	    &state->ts_regs->aud_ram[port->tp_dma_stream].aram.aram_cso);

	n = (cso >= port->tp_cso) ?
	    cso - port->tp_cso :
	    cso + port->tp_nframes - port->tp_cso;

	port->tp_cso = cso;
	port->tp_count += n;
	val = port->tp_count;

	return (val);
}

/*
 * audiots_sync()
 *
 * Description:
 *	This is called by the framework to synchronize DMA caches.
 *
 * Arguments:
 *	void	*arg		The DMA engine to sync
 */
static void
audiots_sync(void *arg, unsigned nframes)
{
	audiots_port_t *port = arg;
	_NOTE(ARGUNUSED(nframes));

	(void) ddi_dma_sync(port->tp_dmah, 0, 0, port->tp_sync_dir);
}

/*
 * audiots_stop_everything()
 *
 * Description:
 *	This routine disables the address engine interrupt for all 32 DMA
 *	engines. Just to be sure, it then explicitly issues a stop command to
 *	the address engine and envelope engines for all 32 channels.
 *
 * NOTE:
 *
 * 	There is a hardware bug that generates a spurious interrupt
 *	when the DMA engines are stopped. It's not consistent - it
 *	happens every 1 out of 6 stops or so. It will show up as a
 *	record interrupt. The problem is that once the driver is
 *	detached or if the system goes into low power mode, nobody
 *	will service that interrupt. The system will eventually become
 *	unusable.
 *
 * Arguments:
 *	audiots_state_t	*state		The device's state structure
 */
static void
audiots_stop_everything(audiots_state_t *state)
{
	if (state->ts_acch == NULL)
		return;

	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
	    TS_ALL_DMA_OFF);

	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
	    TS_ALL_DMA_ENGINES);

	ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_aint,
	    TS_ALL_DMA_ENGINES);
}

/*
 * audiots_free_port()
 *
 * Description:
 *	This routine unbinds the DMA cookies, frees the DMA buffers,
 *	deallocates the DMA handles.
 *
 * Arguments:
 *	audiots_port_t	*port	The port structure for a device stream.
 */
void
audiots_free_port(audiots_port_t *port)
{
	if (port == NULL)
		return;

	if (port->tp_engine) {
		audio_dev_remove_engine(port->tp_state->ts_adev,
		    port->tp_engine);
		audio_engine_free(port->tp_engine);
	}
	if (port->tp_paddr) {
		(void) ddi_dma_unbind_handle(port->tp_dmah);
	}
	if (port->tp_acch) {
		ddi_dma_mem_free(&port->tp_acch);
	}
	if (port->tp_dmah) {
		ddi_dma_free_handle(&port->tp_dmah);
	}
	kmem_free(port, sizeof (*port));
}

/*
 * audiots_destroy()
 *
 * Description:
 *	This routine releases all resources held by the device instance,
 *	as part of either detach or a failure in attach.
 *
 * Arguments:
 *	audiots_state_t	*state	The device soft state.
 */
void
audiots_destroy(audiots_state_t *state)
{
	audiots_stop_everything(state);

	for (int i = 0; i < TS_NUM_PORTS; i++)
		audiots_free_port(state->ts_ports[i]);

	if (state->ts_acch)
		ddi_regs_map_free(&state->ts_acch);

	if (state->ts_pcih)
		pci_config_teardown(&state->ts_pcih);

	if (state->ts_ac97)
		ac97_free(state->ts_ac97);

	if (state->ts_adev)
		audio_dev_free(state->ts_adev);

	ddi_soft_state_free(audiots_statep, ddi_get_instance(state->ts_dip));
}