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, ®s->aud_regs.ap_stop, TS_ALL_DMA_ENGINES);
65488447a05SGarrett D'Amore ddi_put32(handle, ®s->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, ®s->aud_regs.ap_volume, 0x0);
65888447a05SGarrett D'Amore
65988447a05SGarrett D'Amore /* enable end interrupts for all channels. */
66088447a05SGarrett D'Amore ddi_put32(handle, ®s->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 ®s->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 ®s->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, ®s->aud_ram[str].eram.eram_ebuf1,
68488447a05SGarrett D'Amore ERAM_EBUF_STILL);
68588447a05SGarrett D'Amore ddi_put32(handle, ®s->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, ®s->aud_ram[str].aram.aram_delta,
69088447a05SGarrett D'Amore 1 << TS_SRC_SHIFT);
69188447a05SGarrett D'Amore ddi_put16(handle, ®s->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 = ®s->aud_ram[port->tp_dma_stream].aram;
1300*68c47f65SGarrett D'Amore eram = ®s->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, ®s->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, ®s->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