19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM * CDDL HEADER START
39484Sgarrett.damore@Sun.COM *
49484Sgarrett.damore@Sun.COM * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM *
89484Sgarrett.damore@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM * and limitations under the License.
129484Sgarrett.damore@Sun.COM *
139484Sgarrett.damore@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM *
199484Sgarrett.damore@Sun.COM * CDDL HEADER END
209484Sgarrett.damore@Sun.COM */
219484Sgarrett.damore@Sun.COM /*
22*11936Sgdamore@opensolaris.org * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
239484Sgarrett.damore@Sun.COM * Use is subject to license terms.
249484Sgarrett.damore@Sun.COM */
259484Sgarrett.damore@Sun.COM
269484Sgarrett.damore@Sun.COM
279484Sgarrett.damore@Sun.COM /*
289484Sgarrett.damore@Sun.COM * audiots Audio Driver
299484Sgarrett.damore@Sun.COM *
309484Sgarrett.damore@Sun.COM * This Audio Driver controls the T2 audio core in the ALI M1553
319484Sgarrett.damore@Sun.COM * southbridge chip. This chip supports multiple play streams, but just
329484Sgarrett.damore@Sun.COM * a single record stream. It also supports wave table synthesis and
339484Sgarrett.damore@Sun.COM * hardware MIDI and joystick ports. Unfortunately the MIDI ports are
349484Sgarrett.damore@Sun.COM * not available because their pins have been re-assigned to expose
359484Sgarrett.damore@Sun.COM * interrupts. We also aren't going to do anything with the joystick
369484Sgarrett.damore@Sun.COM * ports. The audio core controls an AC-97 V2.1 Codec.
379484Sgarrett.damore@Sun.COM *
389484Sgarrett.damore@Sun.COM * The DMA engine uses a single buffer which is large enough to hold
399484Sgarrett.damore@Sun.COM * two interrupts worth of data. When it gets to the mid point an
409484Sgarrett.damore@Sun.COM * interrupt is generated and data is either sent (for record) or
419484Sgarrett.damore@Sun.COM * requested and put in that half of the buffer (for play). When the
429484Sgarrett.damore@Sun.COM * second half is played we do the same, but the audio core loops the
439484Sgarrett.damore@Sun.COM * pointer back to the beginning.
449484Sgarrett.damore@Sun.COM *
459484Sgarrett.damore@Sun.COM * The audio core has a bug in silicon that doesn't let it read the AC-97
469484Sgarrett.damore@Sun.COM * Codec's register. T2 has provided an algorithm that attempts to read the
479484Sgarrett.damore@Sun.COM * the Codec several times. This is probably heuristic and thus isn't
489484Sgarrett.damore@Sun.COM * absolutely guaranteed to work. However we do have to place a limit on
499484Sgarrett.damore@Sun.COM * the looping, otherwise when we read a valid 0x00 we would never exit
509484Sgarrett.damore@Sun.COM * the loop. Unfortunately there is also a problem with writing the AC-97
519484Sgarrett.damore@Sun.COM * Codec's registers as well. Thus we read it back to verify the write.
529484Sgarrett.damore@Sun.COM *
539484Sgarrett.damore@Sun.COM * The AC'97 common code provides shadow state for AC'97 registers for us,
549484Sgarrett.damore@Sun.COM * so we only need to read those registers during early startup (primarily
559484Sgarrett.damore@Sun.COM * to determine codec id and capabilities.)
569484Sgarrett.damore@Sun.COM *
579484Sgarrett.damore@Sun.COM * We don't save any of the audio controller registers during normal
589484Sgarrett.damore@Sun.COM * operation. When we need to save register state we only have to save
599484Sgarrett.damore@Sun.COM * the aram and eram. The rest of the controller state is never modified
609484Sgarrett.damore@Sun.COM * from the initial programming. Thus restoring the controller state
619484Sgarrett.damore@Sun.COM * can be done from audiots_chip_init() as well.
629484Sgarrett.damore@Sun.COM *
639484Sgarrett.damore@Sun.COM *
649484Sgarrett.damore@Sun.COM * WARNING: The SME birdsnest platform uses a PCI bridge chip between the
659484Sgarrett.damore@Sun.COM * CPU and the southbridge containing the audio core. There is
669484Sgarrett.damore@Sun.COM * a bug in silicon that causes a bogus parity error. With the mixer
679484Sgarrett.damore@Sun.COM * reimplementation project, Bug 4374774, the audio driver is always
689484Sgarrett.damore@Sun.COM * set to the best precision and number of channels. Thus when turning
699484Sgarrett.damore@Sun.COM * the mixer on and off the only thing that changes is the sample rate.
709484Sgarrett.damore@Sun.COM * This change in programming doesn't trigger the silicon error.
719484Sgarrett.damore@Sun.COM * Thus the supported channels must always be 2 and the precision
729484Sgarrett.damore@Sun.COM * must always be 16-bits. This will keep any future change in the
739484Sgarrett.damore@Sun.COM * mixer from exposing this bug.
749484Sgarrett.damore@Sun.COM *
759484Sgarrett.damore@Sun.COM * Due to a hardware bug, system power management is not supported by this
769484Sgarrett.damore@Sun.COM * driver.
779484Sgarrett.damore@Sun.COM *
789484Sgarrett.damore@Sun.COM * CAUTION: If audio controller state is changed outside of aram
799484Sgarrett.damore@Sun.COM * and eram then that information must be saved and restored
809484Sgarrett.damore@Sun.COM * during power management shutdown and bringup.
819484Sgarrett.damore@Sun.COM *
829484Sgarrett.damore@Sun.COM * NOTE: The AC-97 Codec's reset pin is set to PCI reset, so we
839484Sgarrett.damore@Sun.COM * can't power down the Codec all the way.
849484Sgarrett.damore@Sun.COM *
859484Sgarrett.damore@Sun.COM * NOTE: This driver depends on the drv/audio and misc/ac97
869484Sgarrett.damore@Sun.COM * modules being loaded first.
879484Sgarrett.damore@Sun.COM *
889484Sgarrett.damore@Sun.COM * NOTE: Don't OR the ap_stop register to stop a play or record. This
899484Sgarrett.damore@Sun.COM * will just stop all active channels because a read of ap_stop
909484Sgarrett.damore@Sun.COM * returns ap_start. Just set the ap_stop register with the
919484Sgarrett.damore@Sun.COM * channels you want to stop. The same goes for ap_start.
929484Sgarrett.damore@Sun.COM *
939484Sgarrett.damore@Sun.COM * NOTE: There is a hardware problem with P2 rev motherboards. After
949484Sgarrett.damore@Sun.COM * prolonged use, reading the AC97 register will always return
959484Sgarrett.damore@Sun.COM * busy. The AC97 register is now useless. Consequently, we are no
969484Sgarrett.damore@Sun.COM * longer able to program the Codec. This work around disables
979484Sgarrett.damore@Sun.COM * audio when this state is detected. It's not great, but its
989484Sgarrett.damore@Sun.COM * better than having audio blasting out at 100% all the time.
999484Sgarrett.damore@Sun.COM *
1009484Sgarrett.damore@Sun.COM * NOTE: Power Management testing has also exposed this AC97 timeout
1019484Sgarrett.damore@Sun.COM * problem. Management has decided this is too risky for customers
1029484Sgarrett.damore@Sun.COM * and hence they want power management support removed from the
1039484Sgarrett.damore@Sun.COM * audio subsystem. All PM support is now removed.
1049484Sgarrett.damore@Sun.COM */
1059484Sgarrett.damore@Sun.COM
106*11936Sgdamore@opensolaris.org /*
107*11936Sgdamore@opensolaris.org * Synchronization notes:
108*11936Sgdamore@opensolaris.org *
109*11936Sgdamore@opensolaris.org * The audio framework guarantees that our entry points are exclusive
110*11936Sgdamore@opensolaris.org * with suspend and resume. This includes data flow and control entry
111*11936Sgdamore@opensolaris.org * points alike.
112*11936Sgdamore@opensolaris.org *
113*11936Sgdamore@opensolaris.org * The audio framework guarantees that only one control is being
114*11936Sgdamore@opensolaris.org * accessed on any given audio device at a time.
115*11936Sgdamore@opensolaris.org *
116*11936Sgdamore@opensolaris.org * The audio framework guarantees that entry points are themselves
117*11936Sgdamore@opensolaris.org * serialized for a given engine.
118*11936Sgdamore@opensolaris.org *
119*11936Sgdamore@opensolaris.org * We have no interrupt routine or other internal asynchronous routines.
120*11936Sgdamore@opensolaris.org *
121*11936Sgdamore@opensolaris.org * Our device uses completely separate registers for each engine,
122*11936Sgdamore@opensolaris.org * except for the start/stop registers, which are implemented in a
123*11936Sgdamore@opensolaris.org * manner that allows for them to be accessed concurrently safely from
124*11936Sgdamore@opensolaris.org * different threads.
125*11936Sgdamore@opensolaris.org *
126*11936Sgdamore@opensolaris.org * Hence, it turns out that we simply don't need any locking in this
127*11936Sgdamore@opensolaris.org * driver.
128*11936Sgdamore@opensolaris.org */
129*11936Sgdamore@opensolaris.org
1309484Sgarrett.damore@Sun.COM #include <sys/modctl.h>
1319484Sgarrett.damore@Sun.COM #include <sys/kmem.h>
1329484Sgarrett.damore@Sun.COM #include <sys/pci.h>
1339484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
1349484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
1359484Sgarrett.damore@Sun.COM #include <sys/debug.h>
1369484Sgarrett.damore@Sun.COM #include <sys/note.h>
1379484Sgarrett.damore@Sun.COM #include <sys/audio/audio_driver.h>
1389484Sgarrett.damore@Sun.COM #include <sys/audio/ac97.h>
1399484Sgarrett.damore@Sun.COM #include "audiots.h"
1409484Sgarrett.damore@Sun.COM
1419484Sgarrett.damore@Sun.COM /*
1429484Sgarrett.damore@Sun.COM * Module linkage routines for the kernel
1439484Sgarrett.damore@Sun.COM */
1449484Sgarrett.damore@Sun.COM static int audiots_attach(dev_info_t *, ddi_attach_cmd_t);
1459484Sgarrett.damore@Sun.COM static int audiots_detach(dev_info_t *, ddi_detach_cmd_t);
1469484Sgarrett.damore@Sun.COM static int audiots_quiesce(dev_info_t *);
1479484Sgarrett.damore@Sun.COM
1489484Sgarrett.damore@Sun.COM /*
1499484Sgarrett.damore@Sun.COM * Entry point routine prototypes
1509484Sgarrett.damore@Sun.COM */
151*11936Sgdamore@opensolaris.org static int audiots_open(void *, int, unsigned *, caddr_t *);
1529484Sgarrett.damore@Sun.COM static void audiots_close(void *);
1539484Sgarrett.damore@Sun.COM static int audiots_start(void *);
1549484Sgarrett.damore@Sun.COM static void audiots_stop(void *);
1559484Sgarrett.damore@Sun.COM static int audiots_format(void *);
1569484Sgarrett.damore@Sun.COM static int audiots_channels(void *);
1579484Sgarrett.damore@Sun.COM static int audiots_rate(void *);
1589484Sgarrett.damore@Sun.COM static void audiots_chinfo(void *, int, unsigned *, unsigned *);
1599484Sgarrett.damore@Sun.COM static uint64_t audiots_count(void *);
1609484Sgarrett.damore@Sun.COM static void audiots_sync(void *, unsigned);
1619484Sgarrett.damore@Sun.COM
1629484Sgarrett.damore@Sun.COM static audio_engine_ops_t audiots_engine_ops = {
1639484Sgarrett.damore@Sun.COM AUDIO_ENGINE_VERSION,
1649484Sgarrett.damore@Sun.COM audiots_open,
1659484Sgarrett.damore@Sun.COM audiots_close,
1669484Sgarrett.damore@Sun.COM audiots_start,
1679484Sgarrett.damore@Sun.COM audiots_stop,
1689484Sgarrett.damore@Sun.COM audiots_count,
1699484Sgarrett.damore@Sun.COM audiots_format,
1709484Sgarrett.damore@Sun.COM audiots_channels,
1719484Sgarrett.damore@Sun.COM audiots_rate,
1729484Sgarrett.damore@Sun.COM audiots_sync,
17311213Sgdamore@opensolaris.org NULL,
17411213Sgdamore@opensolaris.org audiots_chinfo,
17511213Sgdamore@opensolaris.org NULL,
1769484Sgarrett.damore@Sun.COM };
1779484Sgarrett.damore@Sun.COM
1789484Sgarrett.damore@Sun.COM /*
1799484Sgarrett.damore@Sun.COM * Local Routine Prototypes
1809484Sgarrett.damore@Sun.COM */
1819484Sgarrett.damore@Sun.COM static void audiots_power_up(audiots_state_t *);
1829484Sgarrett.damore@Sun.COM static void audiots_chip_init(audiots_state_t *);
1839484Sgarrett.damore@Sun.COM static uint16_t audiots_get_ac97(void *, uint8_t);
1849484Sgarrett.damore@Sun.COM static void audiots_set_ac97(void *, uint8_t, uint16_t);
1859484Sgarrett.damore@Sun.COM static int audiots_init_state(audiots_state_t *, dev_info_t *);
1869484Sgarrett.damore@Sun.COM static int audiots_map_regs(dev_info_t *, audiots_state_t *);
1879484Sgarrett.damore@Sun.COM static uint16_t audiots_read_ac97(audiots_state_t *, int);
1889484Sgarrett.damore@Sun.COM static void audiots_stop_everything(audiots_state_t *);
1899484Sgarrett.damore@Sun.COM static void audiots_destroy(audiots_state_t *);
1909484Sgarrett.damore@Sun.COM static int audiots_alloc_port(audiots_state_t *, int);
1919484Sgarrett.damore@Sun.COM
1929484Sgarrett.damore@Sun.COM /*
1939484Sgarrett.damore@Sun.COM * Global variables, but viewable only by this file.
1949484Sgarrett.damore@Sun.COM */
1959484Sgarrett.damore@Sun.COM
1969484Sgarrett.damore@Sun.COM /* anchor for soft state structures */
1979484Sgarrett.damore@Sun.COM static void *audiots_statep;
1989484Sgarrett.damore@Sun.COM
1999484Sgarrett.damore@Sun.COM /*
2009484Sgarrett.damore@Sun.COM * DDI Structures
2019484Sgarrett.damore@Sun.COM */
2029484Sgarrett.damore@Sun.COM
2039484Sgarrett.damore@Sun.COM /* Device operations structure */
2049484Sgarrett.damore@Sun.COM static struct dev_ops audiots_dev_ops = {
2059484Sgarrett.damore@Sun.COM DEVO_REV, /* devo_rev */
2069484Sgarrett.damore@Sun.COM 0, /* devo_refcnt */
2079484Sgarrett.damore@Sun.COM NULL, /* devo_getinfo */
2089484Sgarrett.damore@Sun.COM nulldev, /* devo_identify - obsolete */
2099484Sgarrett.damore@Sun.COM nulldev, /* devo_probe */
2109484Sgarrett.damore@Sun.COM audiots_attach, /* devo_attach */
2119484Sgarrett.damore@Sun.COM audiots_detach, /* devo_detach */
2129484Sgarrett.damore@Sun.COM nodev, /* devo_reset */
2139484Sgarrett.damore@Sun.COM NULL, /* devo_cb_ops */
2149484Sgarrett.damore@Sun.COM NULL, /* devo_bus_ops */
2159484Sgarrett.damore@Sun.COM NULL, /* devo_power */
2169484Sgarrett.damore@Sun.COM audiots_quiesce, /* devo_quiesce */
2179484Sgarrett.damore@Sun.COM };
2189484Sgarrett.damore@Sun.COM
2199484Sgarrett.damore@Sun.COM /* Linkage structure for loadable drivers */
2209484Sgarrett.damore@Sun.COM static struct modldrv audiots_modldrv = {
2219484Sgarrett.damore@Sun.COM &mod_driverops, /* drv_modops */
2229484Sgarrett.damore@Sun.COM TS_MOD_NAME, /* drv_linkinfo */
2239484Sgarrett.damore@Sun.COM &audiots_dev_ops /* drv_dev_ops */
2249484Sgarrett.damore@Sun.COM };
2259484Sgarrett.damore@Sun.COM
2269484Sgarrett.damore@Sun.COM /* Module linkage structure */
2279484Sgarrett.damore@Sun.COM static struct modlinkage audiots_modlinkage = {
2289484Sgarrett.damore@Sun.COM MODREV_1, /* ml_rev */
2299484Sgarrett.damore@Sun.COM (void *)&audiots_modldrv, /* ml_linkage */
2309484Sgarrett.damore@Sun.COM NULL /* NULL terminates the list */
2319484Sgarrett.damore@Sun.COM };
2329484Sgarrett.damore@Sun.COM
2339484Sgarrett.damore@Sun.COM
2349484Sgarrett.damore@Sun.COM /*
2359484Sgarrett.damore@Sun.COM * NOTE: Grover OBP v4.0.166 and rev G of the ALI Southbridge chip force the
2369484Sgarrett.damore@Sun.COM * audiots driver to use the upper 2 GB DMA address range. However to maintain
2379484Sgarrett.damore@Sun.COM * backwards compatibility with older systems/OBP, we're going to try the full
2389484Sgarrett.damore@Sun.COM * 4 GB DMA range.
2399484Sgarrett.damore@Sun.COM *
2409484Sgarrett.damore@Sun.COM * Eventually, this will be set back to using the proper high 2 GB DMA range.
2419484Sgarrett.damore@Sun.COM */
2429484Sgarrett.damore@Sun.COM
2439484Sgarrett.damore@Sun.COM /* Device attribute structure - full 4 gig address range */
2449484Sgarrett.damore@Sun.COM static ddi_dma_attr_t audiots_attr = {
2459484Sgarrett.damore@Sun.COM DMA_ATTR_VERSION, /* version */
2469484Sgarrett.damore@Sun.COM 0x0000000000000000LL, /* dlim_addr_lo */
2479484Sgarrett.damore@Sun.COM 0x00000000ffffffffLL, /* dlim_addr_hi */
2489484Sgarrett.damore@Sun.COM 0x0000000000003fffLL, /* DMA counter register - 16 bits */
2499484Sgarrett.damore@Sun.COM 0x0000000000000008LL, /* DMA address alignment, 64-bit */
2509484Sgarrett.damore@Sun.COM 0x0000007f, /* 1 through 64 byte burst sizes */
2519484Sgarrett.damore@Sun.COM 0x00000001, /* min effective DMA size */
2529484Sgarrett.damore@Sun.COM 0x0000000000003fffLL, /* maximum transfer size, 16k */
2539484Sgarrett.damore@Sun.COM 0x000000000000ffffLL, /* segment boundary, 64k */
2549484Sgarrett.damore@Sun.COM 0x00000001, /* s/g list length, no s/g */
2559484Sgarrett.damore@Sun.COM 0x00000001, /* granularity of device, don't care */
2569484Sgarrett.damore@Sun.COM 0 /* DMA flags */
2579484Sgarrett.damore@Sun.COM };
2589484Sgarrett.damore@Sun.COM
2599484Sgarrett.damore@Sun.COM static ddi_device_acc_attr_t ts_acc_attr = {
2609484Sgarrett.damore@Sun.COM DDI_DEVICE_ATTR_V0,
2619484Sgarrett.damore@Sun.COM DDI_NEVERSWAP_ACC,
2629484Sgarrett.damore@Sun.COM DDI_STRICTORDER_ACC
2639484Sgarrett.damore@Sun.COM };
2649484Sgarrett.damore@Sun.COM
2659484Sgarrett.damore@Sun.COM static ddi_device_acc_attr_t ts_regs_attr = {
2669484Sgarrett.damore@Sun.COM DDI_DEVICE_ATTR_V0,
2679484Sgarrett.damore@Sun.COM DDI_STRUCTURE_LE_ACC,
2689484Sgarrett.damore@Sun.COM DDI_STRICTORDER_ACC
2699484Sgarrett.damore@Sun.COM };
2709484Sgarrett.damore@Sun.COM
2719484Sgarrett.damore@Sun.COM /*
2729484Sgarrett.damore@Sun.COM * _init()
2739484Sgarrett.damore@Sun.COM *
2749484Sgarrett.damore@Sun.COM * Description:
2759484Sgarrett.damore@Sun.COM * Driver initialization, called when driver is first loaded.
2769484Sgarrett.damore@Sun.COM * This is how access is initially given to all the static structures.
2779484Sgarrett.damore@Sun.COM *
2789484Sgarrett.damore@Sun.COM * Arguments:
2799484Sgarrett.damore@Sun.COM * None
2809484Sgarrett.damore@Sun.COM *
2819484Sgarrett.damore@Sun.COM * Returns:
2829484Sgarrett.damore@Sun.COM * ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
2839484Sgarrett.damore@Sun.COM * mod_install() status, see mod_install(9f)
2849484Sgarrett.damore@Sun.COM */
2859484Sgarrett.damore@Sun.COM int
_init(void)2869484Sgarrett.damore@Sun.COM _init(void)
2879484Sgarrett.damore@Sun.COM {
2889484Sgarrett.damore@Sun.COM int error;
2899484Sgarrett.damore@Sun.COM
2909484Sgarrett.damore@Sun.COM audio_init_ops(&audiots_dev_ops, TS_NAME);
2919484Sgarrett.damore@Sun.COM
2929484Sgarrett.damore@Sun.COM /* initialize the soft state */
2939484Sgarrett.damore@Sun.COM if ((error = ddi_soft_state_init(&audiots_statep,
2949484Sgarrett.damore@Sun.COM sizeof (audiots_state_t), 1)) != 0) {
2959484Sgarrett.damore@Sun.COM audio_fini_ops(&audiots_dev_ops);
2969484Sgarrett.damore@Sun.COM return (error);
2979484Sgarrett.damore@Sun.COM }
2989484Sgarrett.damore@Sun.COM
2999484Sgarrett.damore@Sun.COM if ((error = mod_install(&audiots_modlinkage)) != 0) {
3009484Sgarrett.damore@Sun.COM audio_fini_ops(&audiots_dev_ops);
3019484Sgarrett.damore@Sun.COM ddi_soft_state_fini(&audiots_statep);
3029484Sgarrett.damore@Sun.COM }
3039484Sgarrett.damore@Sun.COM
3049484Sgarrett.damore@Sun.COM return (error);
3059484Sgarrett.damore@Sun.COM }
3069484Sgarrett.damore@Sun.COM
3079484Sgarrett.damore@Sun.COM /*
3089484Sgarrett.damore@Sun.COM * _fini()
3099484Sgarrett.damore@Sun.COM *
3109484Sgarrett.damore@Sun.COM * Description:
3119484Sgarrett.damore@Sun.COM * Module de-initialization, called when the driver is to be unloaded.
3129484Sgarrett.damore@Sun.COM *
3139484Sgarrett.damore@Sun.COM * Arguments:
3149484Sgarrett.damore@Sun.COM * None
3159484Sgarrett.damore@Sun.COM *
3169484Sgarrett.damore@Sun.COM * Returns:
3179484Sgarrett.damore@Sun.COM * mod_remove() status, see mod_remove(9f)
3189484Sgarrett.damore@Sun.COM */
3199484Sgarrett.damore@Sun.COM int
_fini(void)3209484Sgarrett.damore@Sun.COM _fini(void)
3219484Sgarrett.damore@Sun.COM {
3229484Sgarrett.damore@Sun.COM int error;
3239484Sgarrett.damore@Sun.COM
3249484Sgarrett.damore@Sun.COM if ((error = mod_remove(&audiots_modlinkage)) != 0) {
3259484Sgarrett.damore@Sun.COM return (error);
3269484Sgarrett.damore@Sun.COM }
3279484Sgarrett.damore@Sun.COM
3289484Sgarrett.damore@Sun.COM /* free the soft state internal structures */
3299484Sgarrett.damore@Sun.COM ddi_soft_state_fini(&audiots_statep);
3309484Sgarrett.damore@Sun.COM
3319484Sgarrett.damore@Sun.COM /* clean up ops */
3329484Sgarrett.damore@Sun.COM audio_fini_ops(&audiots_dev_ops);
3339484Sgarrett.damore@Sun.COM
3349484Sgarrett.damore@Sun.COM return (0);
3359484Sgarrett.damore@Sun.COM }
3369484Sgarrett.damore@Sun.COM
3379484Sgarrett.damore@Sun.COM /*
3389484Sgarrett.damore@Sun.COM * _info()
3399484Sgarrett.damore@Sun.COM *
3409484Sgarrett.damore@Sun.COM * Description:
3419484Sgarrett.damore@Sun.COM * Module information, returns infomation about the driver.
3429484Sgarrett.damore@Sun.COM *
3439484Sgarrett.damore@Sun.COM * Arguments:
3449484Sgarrett.damore@Sun.COM * modinfo *modinfop Pointer to the opaque modinfo structure
3459484Sgarrett.damore@Sun.COM *
3469484Sgarrett.damore@Sun.COM * Returns:
3479484Sgarrett.damore@Sun.COM * mod_info() status, see mod_info(9f)
3489484Sgarrett.damore@Sun.COM */
3499484Sgarrett.damore@Sun.COM int
_info(struct modinfo * modinfop)3509484Sgarrett.damore@Sun.COM _info(struct modinfo *modinfop)
3519484Sgarrett.damore@Sun.COM {
3529484Sgarrett.damore@Sun.COM int error;
3539484Sgarrett.damore@Sun.COM
3549484Sgarrett.damore@Sun.COM error = mod_info(&audiots_modlinkage, modinfop);
3559484Sgarrett.damore@Sun.COM
3569484Sgarrett.damore@Sun.COM return (error);
3579484Sgarrett.damore@Sun.COM }
3589484Sgarrett.damore@Sun.COM
3599484Sgarrett.damore@Sun.COM
3609484Sgarrett.damore@Sun.COM /*
3619484Sgarrett.damore@Sun.COM * audiots_attach()
3629484Sgarrett.damore@Sun.COM *
3639484Sgarrett.damore@Sun.COM * Description:
3649484Sgarrett.damore@Sun.COM * Attach an instance of the audiots driver. This routine does the
3659484Sgarrett.damore@Sun.COM * device dependent attach tasks.
3669484Sgarrett.damore@Sun.COM *
3679484Sgarrett.damore@Sun.COM * Arguments:
3689484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
3699484Sgarrett.damore@Sun.COM * ddi_attach_cmd_t cmd Attach command
3709484Sgarrett.damore@Sun.COM *
3719484Sgarrett.damore@Sun.COM * Returns:
3729484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was initialized properly
3739484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be initialized properly
3749484Sgarrett.damore@Sun.COM */
3759484Sgarrett.damore@Sun.COM static int
audiots_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3769484Sgarrett.damore@Sun.COM audiots_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3779484Sgarrett.damore@Sun.COM {
3789484Sgarrett.damore@Sun.COM audiots_state_t *state;
3799484Sgarrett.damore@Sun.COM int instance;
3809484Sgarrett.damore@Sun.COM
3819484Sgarrett.damore@Sun.COM instance = ddi_get_instance(dip);
3829484Sgarrett.damore@Sun.COM
3839484Sgarrett.damore@Sun.COM switch (cmd) {
3849484Sgarrett.damore@Sun.COM case DDI_ATTACH:
3859484Sgarrett.damore@Sun.COM break;
3869484Sgarrett.damore@Sun.COM case DDI_RESUME:
3879484Sgarrett.damore@Sun.COM
3889484Sgarrett.damore@Sun.COM /* we've already allocated the state structure so get ptr */
389*11936Sgdamore@opensolaris.org state = ddi_get_soft_state(audiots_statep, instance);
3909484Sgarrett.damore@Sun.COM ASSERT(dip == state->ts_dip);
3919484Sgarrett.damore@Sun.COM
3929484Sgarrett.damore@Sun.COM /* suspend/resume resets the chip, so we have no more faults */
3939484Sgarrett.damore@Sun.COM if (state->ts_flags & TS_AUDIO_READ_FAILED) {
3949484Sgarrett.damore@Sun.COM ddi_dev_report_fault(state->ts_dip,
3959484Sgarrett.damore@Sun.COM DDI_SERVICE_RESTORED,
3969484Sgarrett.damore@Sun.COM DDI_DEVICE_FAULT,
3979484Sgarrett.damore@Sun.COM "check port, gain, balance, and mute settings");
3989484Sgarrett.damore@Sun.COM /* and clear the fault state flags */
3999484Sgarrett.damore@Sun.COM state->ts_flags &=
4009484Sgarrett.damore@Sun.COM ~(TS_AUDIO_READ_FAILED|TS_READ_FAILURE_PRINTED);
4019484Sgarrett.damore@Sun.COM }
4029484Sgarrett.damore@Sun.COM
4039484Sgarrett.damore@Sun.COM audiots_power_up(state);
4049484Sgarrett.damore@Sun.COM audiots_chip_init(state);
4059484Sgarrett.damore@Sun.COM
406*11936Sgdamore@opensolaris.org ac97_reset(state->ts_ac97);
4079484Sgarrett.damore@Sun.COM
408*11936Sgdamore@opensolaris.org audio_dev_resume(state->ts_adev);
4099484Sgarrett.damore@Sun.COM
4109484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
4119484Sgarrett.damore@Sun.COM
4129484Sgarrett.damore@Sun.COM default:
4139484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
4149484Sgarrett.damore@Sun.COM }
4159484Sgarrett.damore@Sun.COM
4169484Sgarrett.damore@Sun.COM /* before we do anything make sure that we haven't had a h/w failure */
4179484Sgarrett.damore@Sun.COM if (ddi_get_devstate(dip) == DDI_DEVSTATE_DOWN) {
4189484Sgarrett.damore@Sun.COM cmn_err(CE_WARN, "%s%d: The audio hardware has "
419*11936Sgdamore@opensolaris.org "been disabled.", ddi_driver_name(dip), instance);
4209484Sgarrett.damore@Sun.COM cmn_err(CE_CONT, "Please reboot to restore audio.");
4219484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
4229484Sgarrett.damore@Sun.COM }
4239484Sgarrett.damore@Sun.COM
4249484Sgarrett.damore@Sun.COM /* allocate the state structure */
4259484Sgarrett.damore@Sun.COM if (ddi_soft_state_zalloc(audiots_statep, instance) == DDI_FAILURE) {
4269484Sgarrett.damore@Sun.COM cmn_err(CE_WARN, "!%s%d: soft state allocate failed",
427*11936Sgdamore@opensolaris.org ddi_driver_name(dip), instance);
4289484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
4299484Sgarrett.damore@Sun.COM }
4309484Sgarrett.damore@Sun.COM
4319484Sgarrett.damore@Sun.COM /*
4329484Sgarrett.damore@Sun.COM * WARNING: From here on all errors require that we free memory,
4339484Sgarrett.damore@Sun.COM * including the state structure.
4349484Sgarrett.damore@Sun.COM */
4359484Sgarrett.damore@Sun.COM
4369484Sgarrett.damore@Sun.COM /* get the state structure - cannot fail */
4379484Sgarrett.damore@Sun.COM state = ddi_get_soft_state(audiots_statep, instance);
4389484Sgarrett.damore@Sun.COM ASSERT(state != NULL);
4399484Sgarrett.damore@Sun.COM
4409484Sgarrett.damore@Sun.COM if ((state->ts_adev = audio_dev_alloc(dip, 0)) == NULL) {
4419484Sgarrett.damore@Sun.COM cmn_err(CE_WARN, "unable to allocate audio dev");
4429484Sgarrett.damore@Sun.COM goto error;
4439484Sgarrett.damore@Sun.COM }
4449484Sgarrett.damore@Sun.COM
4459484Sgarrett.damore@Sun.COM /* map in the registers, allocate DMA buffers, etc. */
4469484Sgarrett.damore@Sun.COM if (audiots_map_regs(dip, state) == DDI_FAILURE) {
4479484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev, "unable to map registers");
4489484Sgarrett.damore@Sun.COM goto error;
4499484Sgarrett.damore@Sun.COM }
4509484Sgarrett.damore@Sun.COM
4519484Sgarrett.damore@Sun.COM /* initialize the audio state structures */
4529484Sgarrett.damore@Sun.COM if (audiots_init_state(state, dip) == DDI_FAILURE) {
4539484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev, "init state structure failed");
4549484Sgarrett.damore@Sun.COM goto error;
4559484Sgarrett.damore@Sun.COM }
4569484Sgarrett.damore@Sun.COM
4579484Sgarrett.damore@Sun.COM /* power up */
4589484Sgarrett.damore@Sun.COM audiots_power_up(state);
4599484Sgarrett.damore@Sun.COM
4609484Sgarrett.damore@Sun.COM /* initialize the audio controller */
4619484Sgarrett.damore@Sun.COM audiots_chip_init(state);
4629484Sgarrett.damore@Sun.COM
4639484Sgarrett.damore@Sun.COM /* initialize the AC-97 Codec */
4649929Sgdamore@opensolaris.org if (ac97_init(state->ts_ac97, state->ts_adev) != 0) {
4659929Sgdamore@opensolaris.org goto error;
4669929Sgdamore@opensolaris.org }
4679484Sgarrett.damore@Sun.COM
4689484Sgarrett.damore@Sun.COM /* put the engine interrupts into a known state -- all off */
4699484Sgarrett.damore@Sun.COM ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
4709484Sgarrett.damore@Sun.COM TS_ALL_DMA_OFF);
4719484Sgarrett.damore@Sun.COM
4729484Sgarrett.damore@Sun.COM /* call the framework attach routine */
4739484Sgarrett.damore@Sun.COM if (audio_dev_register(state->ts_adev) != DDI_SUCCESS) {
4749484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev, "unable to register audio");
4759484Sgarrett.damore@Sun.COM goto error;
4769484Sgarrett.damore@Sun.COM }
4779484Sgarrett.damore@Sun.COM
4789484Sgarrett.damore@Sun.COM /* everything worked out, so report the device */
4799484Sgarrett.damore@Sun.COM ddi_report_dev(dip);
4809484Sgarrett.damore@Sun.COM
4819484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
4829484Sgarrett.damore@Sun.COM
4839484Sgarrett.damore@Sun.COM error:
4849484Sgarrett.damore@Sun.COM audiots_destroy(state);
4859484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
4869484Sgarrett.damore@Sun.COM }
4879484Sgarrett.damore@Sun.COM
4889484Sgarrett.damore@Sun.COM /*
4899484Sgarrett.damore@Sun.COM * audiots_detach()
4909484Sgarrett.damore@Sun.COM *
4919484Sgarrett.damore@Sun.COM * Description:
4929484Sgarrett.damore@Sun.COM * Detach an instance of the audiots driver.
4939484Sgarrett.damore@Sun.COM *
4949484Sgarrett.damore@Sun.COM * Arguments:
4959484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
4969484Sgarrett.damore@Sun.COM * ddi_detach_cmd_t cmd Detach command
4979484Sgarrett.damore@Sun.COM *
4989484Sgarrett.damore@Sun.COM * Returns:
4999484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was detached
5009484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be detached
5019484Sgarrett.damore@Sun.COM */
5029484Sgarrett.damore@Sun.COM static int
audiots_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5039484Sgarrett.damore@Sun.COM audiots_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5049484Sgarrett.damore@Sun.COM {
5059484Sgarrett.damore@Sun.COM audiots_state_t *state;
5069484Sgarrett.damore@Sun.COM int instance;
5079484Sgarrett.damore@Sun.COM
5089484Sgarrett.damore@Sun.COM instance = ddi_get_instance(dip);
5099484Sgarrett.damore@Sun.COM
5109484Sgarrett.damore@Sun.COM /* get the state structure */
5119484Sgarrett.damore@Sun.COM if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
5129484Sgarrett.damore@Sun.COM cmn_err(CE_WARN, "!%s%d: detach get soft state failed",
513*11936Sgdamore@opensolaris.org ddi_driver_name(dip), instance);
5149484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
5159484Sgarrett.damore@Sun.COM }
5169484Sgarrett.damore@Sun.COM
5179484Sgarrett.damore@Sun.COM switch (cmd) {
5189484Sgarrett.damore@Sun.COM case DDI_DETACH:
5199484Sgarrett.damore@Sun.COM break;
5209484Sgarrett.damore@Sun.COM case DDI_SUSPEND:
5219484Sgarrett.damore@Sun.COM
522*11936Sgdamore@opensolaris.org audio_dev_suspend(state->ts_adev);
5239484Sgarrett.damore@Sun.COM
5249484Sgarrett.damore@Sun.COM /* stop playing and recording */
5259484Sgarrett.damore@Sun.COM (void) audiots_stop_everything(state);
5269484Sgarrett.damore@Sun.COM
5279484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
5289484Sgarrett.damore@Sun.COM
5299484Sgarrett.damore@Sun.COM default:
5309484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
5319484Sgarrett.damore@Sun.COM }
5329484Sgarrett.damore@Sun.COM
5339484Sgarrett.damore@Sun.COM /* attempt to unregister from the framework first */
5349484Sgarrett.damore@Sun.COM if (audio_dev_unregister(state->ts_adev) != DDI_SUCCESS) {
5359484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
5369484Sgarrett.damore@Sun.COM }
5379484Sgarrett.damore@Sun.COM
5389484Sgarrett.damore@Sun.COM audiots_destroy(state);
5399484Sgarrett.damore@Sun.COM
5409484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
5419484Sgarrett.damore@Sun.COM
5429484Sgarrett.damore@Sun.COM }
5439484Sgarrett.damore@Sun.COM
5449484Sgarrett.damore@Sun.COM /*
5459484Sgarrett.damore@Sun.COM * audiots_quiesce()
5469484Sgarrett.damore@Sun.COM *
5479484Sgarrett.damore@Sun.COM * Description:
5489484Sgarrett.damore@Sun.COM * Quiesce an instance of the audiots driver. Stops all DMA and
5499484Sgarrett.damore@Sun.COM * interrupts.
5509484Sgarrett.damore@Sun.COM *
5519484Sgarrett.damore@Sun.COM * Arguments:
5529484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
5539484Sgarrett.damore@Sun.COM *
5549484Sgarrett.damore@Sun.COM * Returns:
5559484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was quiesced
5569484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was NOT quiesced
5579484Sgarrett.damore@Sun.COM */
5589484Sgarrett.damore@Sun.COM static int
audiots_quiesce(dev_info_t * dip)5599484Sgarrett.damore@Sun.COM audiots_quiesce(dev_info_t *dip)
5609484Sgarrett.damore@Sun.COM {
5619484Sgarrett.damore@Sun.COM audiots_state_t *state;
5629484Sgarrett.damore@Sun.COM int instance;
5639484Sgarrett.damore@Sun.COM
5649484Sgarrett.damore@Sun.COM instance = ddi_get_instance(dip);
5659484Sgarrett.damore@Sun.COM
5669484Sgarrett.damore@Sun.COM /* get the state structure */
5679484Sgarrett.damore@Sun.COM if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) {
5689484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
5699484Sgarrett.damore@Sun.COM }
5709484Sgarrett.damore@Sun.COM
5719484Sgarrett.damore@Sun.COM audiots_stop_everything(state);
5729484Sgarrett.damore@Sun.COM
5739484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
5749484Sgarrett.damore@Sun.COM }
5759484Sgarrett.damore@Sun.COM
5769484Sgarrett.damore@Sun.COM /*
5779484Sgarrett.damore@Sun.COM * audiots_power_up()
5789484Sgarrett.damore@Sun.COM *
5799484Sgarrett.damore@Sun.COM * Description
5809484Sgarrett.damore@Sun.COM * Ensure that the device is running in PCI power state D0.
5819484Sgarrett.damore@Sun.COM */
5829484Sgarrett.damore@Sun.COM static void
audiots_power_up(audiots_state_t * state)5839484Sgarrett.damore@Sun.COM audiots_power_up(audiots_state_t *state)
5849484Sgarrett.damore@Sun.COM {
5859484Sgarrett.damore@Sun.COM ddi_acc_handle_t pcih = state->ts_pcih;
5869484Sgarrett.damore@Sun.COM uint8_t ptr;
5879484Sgarrett.damore@Sun.COM uint16_t pmcsr;
5889484Sgarrett.damore@Sun.COM
5899484Sgarrett.damore@Sun.COM if ((pci_config_get16(pcih, PCI_CONF_STAT) & PCI_STAT_CAP) == 0) {
5909484Sgarrett.damore@Sun.COM /* does not implement PCI capabilities -- no PM */
5919484Sgarrett.damore@Sun.COM return;
5929484Sgarrett.damore@Sun.COM }
5939484Sgarrett.damore@Sun.COM
5949484Sgarrett.damore@Sun.COM ptr = pci_config_get8(pcih, PCI_CONF_CAP_PTR);
5959484Sgarrett.damore@Sun.COM for (;;) {
5969484Sgarrett.damore@Sun.COM if (ptr == PCI_CAP_NEXT_PTR_NULL) {
5979484Sgarrett.damore@Sun.COM /* PM capability not found */
5989484Sgarrett.damore@Sun.COM return;
5999484Sgarrett.damore@Sun.COM }
6009484Sgarrett.damore@Sun.COM if (pci_config_get8(pcih, ptr + PCI_CAP_ID) == PCI_CAP_ID_PM) {
6019484Sgarrett.damore@Sun.COM /* found it */
6029484Sgarrett.damore@Sun.COM break;
6039484Sgarrett.damore@Sun.COM }
6049484Sgarrett.damore@Sun.COM ptr = pci_config_get8(pcih, ptr + PCI_CAP_NEXT_PTR);
6059484Sgarrett.damore@Sun.COM }
6069484Sgarrett.damore@Sun.COM
6079484Sgarrett.damore@Sun.COM /* if we got here, then got valid PMCSR pointer */
6089484Sgarrett.damore@Sun.COM ptr += PCI_PMCSR;
6099484Sgarrett.damore@Sun.COM
6109484Sgarrett.damore@Sun.COM /* check to see if we are already in state D0 */
6119484Sgarrett.damore@Sun.COM pmcsr = pci_config_get16(pcih, ptr);
6129484Sgarrett.damore@Sun.COM if ((pmcsr & PCI_PMCSR_STATE_MASK) != PCI_PMCSR_D0) {
6139484Sgarrett.damore@Sun.COM
6149484Sgarrett.damore@Sun.COM /* D3hot (or any other state) -> D0 */
6159484Sgarrett.damore@Sun.COM pmcsr &= ~PCI_PMCSR_STATE_MASK;
6169484Sgarrett.damore@Sun.COM pmcsr |= PCI_PMCSR_D0;
6179484Sgarrett.damore@Sun.COM pci_config_put16(pcih, ptr, pmcsr);
6189484Sgarrett.damore@Sun.COM }
6199484Sgarrett.damore@Sun.COM
6209484Sgarrett.damore@Sun.COM /*
6219484Sgarrett.damore@Sun.COM * Wait for it to power up - PCI spec says 10 ms is enough.
6229484Sgarrett.damore@Sun.COM * We double it. Note that no locks are held when this routine
6239484Sgarrett.damore@Sun.COM * is called, so we can sleep (we are in attach context only).
6249484Sgarrett.damore@Sun.COM *
6259484Sgarrett.damore@Sun.COM * We do this delay even if already powerd up, just to make
6269484Sgarrett.damore@Sun.COM * sure we aren't seeing something that *just* transitioned
6279484Sgarrett.damore@Sun.COM * into D0 state.
6289484Sgarrett.damore@Sun.COM */
6299484Sgarrett.damore@Sun.COM delay(drv_usectohz(TS_20MS));
6309484Sgarrett.damore@Sun.COM
6319484Sgarrett.damore@Sun.COM /* clear PME# flag */
6329484Sgarrett.damore@Sun.COM pmcsr = pci_config_get16(pcih, ptr);
6339484Sgarrett.damore@Sun.COM pci_config_put16(pcih, ptr, pmcsr | PCI_PMCSR_PME_STAT);
6349484Sgarrett.damore@Sun.COM }
6359484Sgarrett.damore@Sun.COM
6369484Sgarrett.damore@Sun.COM /*
6379484Sgarrett.damore@Sun.COM * audiots_chip_init()
6389484Sgarrett.damore@Sun.COM *
6399484Sgarrett.damore@Sun.COM * Description:
6409484Sgarrett.damore@Sun.COM * Initialize the audio core.
6419484Sgarrett.damore@Sun.COM *
6429484Sgarrett.damore@Sun.COM * Arguments:
6439484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
6449484Sgarrett.damore@Sun.COM */
6459484Sgarrett.damore@Sun.COM static void
audiots_chip_init(audiots_state_t * state)6469484Sgarrett.damore@Sun.COM audiots_chip_init(audiots_state_t *state)
6479484Sgarrett.damore@Sun.COM {
6489484Sgarrett.damore@Sun.COM ddi_acc_handle_t handle = state->ts_acch;
6499484Sgarrett.damore@Sun.COM audiots_regs_t *regs = state->ts_regs;
6509484Sgarrett.damore@Sun.COM int str;
6519484Sgarrett.damore@Sun.COM
6529484Sgarrett.damore@Sun.COM /* start with all interrupts & dma channels disabled */
6539484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_regs.ap_stop, TS_ALL_DMA_ENGINES);
6549484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_regs.ap_ainten, TS_ALL_DMA_OFF);
6559484Sgarrett.damore@Sun.COM
6569484Sgarrett.damore@Sun.COM /* set global music and wave volume to 0dB */
6579484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_regs.ap_volume, 0x0);
6589484Sgarrett.damore@Sun.COM
6599484Sgarrett.damore@Sun.COM /* enable end interrupts for all channels. */
6609484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_regs.ap_cir_gc, AP_CIR_GC_ENDLP_IE);
6619484Sgarrett.damore@Sun.COM
6629484Sgarrett.damore@Sun.COM /* for each stream, set gain and vol settings */
6639484Sgarrett.damore@Sun.COM for (str = 0; str < TS_MAX_HW_CHANNELS; str++) {
6649484Sgarrett.damore@Sun.COM /*
6659484Sgarrett.damore@Sun.COM * Set volume to all off, 1st left and then right.
6669484Sgarrett.damore@Sun.COM * These are never changed, so we don't have to save them.
6679484Sgarrett.damore@Sun.COM */
6689484Sgarrett.damore@Sun.COM ddi_put16(handle,
6699484Sgarrett.damore@Sun.COM ®s->aud_ram[str].eram.eram_gvsel_pan_vol,
6709484Sgarrett.damore@Sun.COM (ERAM_WAVE_VOL|ERAM_PAN_LEFT|ERAM_PAN_0dB|
6719484Sgarrett.damore@Sun.COM ERAM_VOL_MAX_ATTEN));
6729484Sgarrett.damore@Sun.COM ddi_put16(handle,
6739484Sgarrett.damore@Sun.COM ®s->aud_ram[str].eram.eram_gvsel_pan_vol,
6749484Sgarrett.damore@Sun.COM (ERAM_WAVE_VOL|ERAM_PAN_RIGHT|ERAM_PAN_0dB|
6759484Sgarrett.damore@Sun.COM ERAM_VOL_MAX_ATTEN));
6769484Sgarrett.damore@Sun.COM
6779484Sgarrett.damore@Sun.COM /*
6789484Sgarrett.damore@Sun.COM * The envelope engine *MUST* remain in still mode (off).
6799484Sgarrett.damore@Sun.COM * Otherwise bad things like gain randomly disappearing might
6809484Sgarrett.damore@Sun.COM * happen. See bug #4332773.
6819484Sgarrett.damore@Sun.COM */
6829484Sgarrett.damore@Sun.COM
6839484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_ram[str].eram.eram_ebuf1,
6849484Sgarrett.damore@Sun.COM ERAM_EBUF_STILL);
6859484Sgarrett.damore@Sun.COM ddi_put32(handle, ®s->aud_ram[str].eram.eram_ebuf2,
6869484Sgarrett.damore@Sun.COM ERAM_EBUF_STILL);
6879484Sgarrett.damore@Sun.COM
6889484Sgarrett.damore@Sun.COM /* program the initial eram and aram rate */
6899484Sgarrett.damore@Sun.COM ddi_put16(handle, ®s->aud_ram[str].aram.aram_delta,
6909484Sgarrett.damore@Sun.COM 1 << TS_SRC_SHIFT);
6919484Sgarrett.damore@Sun.COM ddi_put16(handle, ®s->aud_ram[str].eram.eram_ctrl_ec,
6929484Sgarrett.damore@Sun.COM ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE |
6939484Sgarrett.damore@Sun.COM ERAM_SIGNED_PCM);
6949484Sgarrett.damore@Sun.COM }
6959484Sgarrett.damore@Sun.COM
6969484Sgarrett.damore@Sun.COM /* program channel 31 for record */
6979484Sgarrett.damore@Sun.COM OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_global_control,
6989484Sgarrett.damore@Sun.COM (AP_CLOGAL_CTRL_E_PCMIN_CH31|AP_CLOGAL_CTRL_PCM_OUT_AC97|
6999484Sgarrett.damore@Sun.COM AP_CLOGAL_CTRL_MMC_FROM_MIXER|AP_CLOGAL_CTRL_PCM_OUT_TO_AC97));
7009484Sgarrett.damore@Sun.COM
7019484Sgarrett.damore@Sun.COM /* do a warm reset, which powers up the Codec */
7029484Sgarrett.damore@Sun.COM OR_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
7039484Sgarrett.damore@Sun.COM AP_SCTRL_WRST_CODEC);
7049484Sgarrett.damore@Sun.COM drv_usecwait(2);
7059484Sgarrett.damore@Sun.COM AND_SET_WORD(handle, &state->ts_regs->aud_regs.ap_sctrl,
7069484Sgarrett.damore@Sun.COM ~AP_SCTRL_WRST_CODEC);
7079484Sgarrett.damore@Sun.COM
7089484Sgarrett.damore@Sun.COM /* do a warm reset via the Codec, yes, I'm being paranoid! */
7099484Sgarrett.damore@Sun.COM audiots_set_ac97(state, AC97_RESET_REGISTER, 0);
7109484Sgarrett.damore@Sun.COM
7119484Sgarrett.damore@Sun.COM /* Make sure the Codec is powered up. */
7129484Sgarrett.damore@Sun.COM int i = TS_WAIT_CNT;
7139484Sgarrett.damore@Sun.COM while ((audiots_get_ac97(state, AC97_POWERDOWN_CTRL_STAT_REGISTER) &
7149484Sgarrett.damore@Sun.COM PCSR_POWERD_UP) != PCSR_POWERD_UP && i--) {
7159484Sgarrett.damore@Sun.COM drv_usecwait(1);
7169484Sgarrett.damore@Sun.COM }
7179484Sgarrett.damore@Sun.COM
7189484Sgarrett.damore@Sun.COM }
7199484Sgarrett.damore@Sun.COM
7209484Sgarrett.damore@Sun.COM /*
7219484Sgarrett.damore@Sun.COM * audiots_get_ac97()
7229484Sgarrett.damore@Sun.COM *
7239484Sgarrett.damore@Sun.COM * Description:
7249484Sgarrett.damore@Sun.COM * Get the value in the specified AC-97 Codec register. There is a
7259484Sgarrett.damore@Sun.COM * bug in silicon which forces us to do multiple reads of the Codec's
7269484Sgarrett.damore@Sun.COM * register. This algorithm was provided by T2 and is heuristic in
7279484Sgarrett.damore@Sun.COM * nature. Unfortunately we have no guarantees that the real answer
7289484Sgarrett.damore@Sun.COM * isn't 0x0000, which is what we get when a read fails. So we loop
7299484Sgarrett.damore@Sun.COM * TS_LOOP_CNT times before we give up. We just have to hope this is
7309484Sgarrett.damore@Sun.COM * sufficient to give us the correct value.
7319484Sgarrett.damore@Sun.COM *
7329484Sgarrett.damore@Sun.COM * Arguments:
7339484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
7349484Sgarrett.damore@Sun.COM * int reg AC-97 register number
7359484Sgarrett.damore@Sun.COM *
7369484Sgarrett.damore@Sun.COM * Returns:
7379484Sgarrett.damore@Sun.COM * unsigned short The value in the specified register
7389484Sgarrett.damore@Sun.COM */
7399484Sgarrett.damore@Sun.COM static uint16_t
audiots_get_ac97(void * arg,uint8_t reg)7409484Sgarrett.damore@Sun.COM audiots_get_ac97(void *arg, uint8_t reg)
7419484Sgarrett.damore@Sun.COM {
7429484Sgarrett.damore@Sun.COM audiots_state_t *state = arg;
7439484Sgarrett.damore@Sun.COM ddi_acc_handle_t handle = state->ts_acch;
7449484Sgarrett.damore@Sun.COM uint16_t *data;
7459484Sgarrett.damore@Sun.COM int count;
7469484Sgarrett.damore@Sun.COM int delay;
7479484Sgarrett.damore@Sun.COM uint16_t first;
7489484Sgarrett.damore@Sun.COM uint16_t next;
7499484Sgarrett.damore@Sun.COM
7509484Sgarrett.damore@Sun.COM if (state->ts_revid == AC_REV_ID1) {
7519484Sgarrett.damore@Sun.COM data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
7529484Sgarrett.damore@Sun.COM } else {
7539484Sgarrett.damore@Sun.COM data = &state->ts_regs->aud_regs.ap_acrdwr_data;
7549484Sgarrett.damore@Sun.COM }
7559484Sgarrett.damore@Sun.COM
7569484Sgarrett.damore@Sun.COM /* make sure the register is good */
7579484Sgarrett.damore@Sun.COM reg &= AP_ACRD_INDEX_MASK;
7589484Sgarrett.damore@Sun.COM for (count = TS_LOOP_CNT; count--; ) {
7599484Sgarrett.damore@Sun.COM if ((first = audiots_read_ac97(state, reg)) != 0) {
7609484Sgarrett.damore@Sun.COM next = first;
7619484Sgarrett.damore@Sun.COM break;
7629484Sgarrett.damore@Sun.COM }
7639484Sgarrett.damore@Sun.COM
7649484Sgarrett.damore@Sun.COM delay = TS_DELAY_CNT;
7659484Sgarrett.damore@Sun.COM while (delay--) {
7669484Sgarrett.damore@Sun.COM (void) ddi_get16(handle, data);
7679484Sgarrett.damore@Sun.COM }
7689484Sgarrett.damore@Sun.COM
7699484Sgarrett.damore@Sun.COM if ((next = audiots_read_ac97(state, reg)) != 0) {
7709484Sgarrett.damore@Sun.COM break;
7719484Sgarrett.damore@Sun.COM }
7729484Sgarrett.damore@Sun.COM }
7739484Sgarrett.damore@Sun.COM
7749484Sgarrett.damore@Sun.COM /*
7759484Sgarrett.damore@Sun.COM * Arggg, if you let the next read happen too soon then it fails.
7769484Sgarrett.damore@Sun.COM * 12 usec fails, 13 usec succeeds. So set it to 20 for safety.
7779484Sgarrett.damore@Sun.COM */
7789484Sgarrett.damore@Sun.COM drv_usecwait(TS_20US);
7799484Sgarrett.damore@Sun.COM
7809484Sgarrett.damore@Sun.COM return (next);
7819484Sgarrett.damore@Sun.COM
7829484Sgarrett.damore@Sun.COM }
7839484Sgarrett.damore@Sun.COM
7849484Sgarrett.damore@Sun.COM /*
7859484Sgarrett.damore@Sun.COM * audiots_init_state()
7869484Sgarrett.damore@Sun.COM *
7879484Sgarrett.damore@Sun.COM * Description:
7889484Sgarrett.damore@Sun.COM * This routine initializes the audio driver's state structure.
7899484Sgarrett.damore@Sun.COM * This includes reading the properties.
7909484Sgarrett.damore@Sun.COM *
7919484Sgarrett.damore@Sun.COM * CAUTION: This routine cannot allocate resources, unless it frees
7929484Sgarrett.damore@Sun.COM * them before returning for an error. Also, error_destroy:
7939484Sgarrett.damore@Sun.COM * in audiots_attach() would need to be fixed as well.
7949484Sgarrett.damore@Sun.COM *
7959484Sgarrett.damore@Sun.COM * NOTE: birdsnest supports CD ROM input. We check for the cdrom
7969484Sgarrett.damore@Sun.COM * property. If there we turn it on.
7979484Sgarrett.damore@Sun.COM *
7989484Sgarrett.damore@Sun.COM * Arguments:
7999484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
8009484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
8019484Sgarrett.damore@Sun.COM *
8029484Sgarrett.damore@Sun.COM * Returns:
8039484Sgarrett.damore@Sun.COM * DDI_SUCCESS State structure initialized
8049484Sgarrett.damore@Sun.COM * DDI_FAILURE State structure not initialized
8059484Sgarrett.damore@Sun.COM */
8069484Sgarrett.damore@Sun.COM static int
audiots_init_state(audiots_state_t * state,dev_info_t * dip)8079484Sgarrett.damore@Sun.COM audiots_init_state(audiots_state_t *state, dev_info_t *dip)
8089484Sgarrett.damore@Sun.COM {
8099484Sgarrett.damore@Sun.COM state->ts_ac97 = ac97_alloc(dip, audiots_get_ac97,
8109484Sgarrett.damore@Sun.COM audiots_set_ac97, state);
8119484Sgarrett.damore@Sun.COM
8129484Sgarrett.damore@Sun.COM if (state->ts_ac97 == NULL) {
8139484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
8149484Sgarrett.damore@Sun.COM }
8159484Sgarrett.damore@Sun.COM
8169484Sgarrett.damore@Sun.COM /* save the device info pointer */
8179484Sgarrett.damore@Sun.COM state->ts_dip = dip;
8189484Sgarrett.damore@Sun.COM
8199484Sgarrett.damore@Sun.COM for (int i = 0; i < TS_NUM_PORTS; i++) {
8209484Sgarrett.damore@Sun.COM if (audiots_alloc_port(state, i) != DDI_SUCCESS) {
8219484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
8229484Sgarrett.damore@Sun.COM }
8239484Sgarrett.damore@Sun.COM }
8249484Sgarrett.damore@Sun.COM
8259484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
8269484Sgarrett.damore@Sun.COM
8279484Sgarrett.damore@Sun.COM }
8289484Sgarrett.damore@Sun.COM
8299484Sgarrett.damore@Sun.COM /*
8309484Sgarrett.damore@Sun.COM * audiots_map_regs()
8319484Sgarrett.damore@Sun.COM *
8329484Sgarrett.damore@Sun.COM * Description:
8339484Sgarrett.damore@Sun.COM * This routine maps the registers in.
8349484Sgarrett.damore@Sun.COM *
8359484Sgarrett.damore@Sun.COM * Once the config space registers are mapped in we determine if the
8369484Sgarrett.damore@Sun.COM * audio core may be power managed. It should, but if it doesn't,
8379484Sgarrett.damore@Sun.COM * then trying to may cause the core to hang.
8389484Sgarrett.damore@Sun.COM *
8399484Sgarrett.damore@Sun.COM * CAUTION: Make sure all errors call audio_dev_warn().
8409484Sgarrett.damore@Sun.COM *
8419484Sgarrett.damore@Sun.COM * Arguments:
8429484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's devinfo
8439484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
8449484Sgarrett.damore@Sun.COM * Returns:
8459484Sgarrett.damore@Sun.COM * DDI_SUCCESS Registers successfully mapped
8469484Sgarrett.damore@Sun.COM * DDI_FAILURE Registers not successfully mapped
8479484Sgarrett.damore@Sun.COM */
8489484Sgarrett.damore@Sun.COM static int
audiots_map_regs(dev_info_t * dip,audiots_state_t * state)8499484Sgarrett.damore@Sun.COM audiots_map_regs(dev_info_t *dip, audiots_state_t *state)
8509484Sgarrett.damore@Sun.COM {
8519484Sgarrett.damore@Sun.COM char rev[16];
8529484Sgarrett.damore@Sun.COM char *name;
8539484Sgarrett.damore@Sun.COM
8549484Sgarrett.damore@Sun.COM /* map in the registers, the config and memory mapped registers */
8559484Sgarrett.damore@Sun.COM if (pci_config_setup(dip, &state->ts_pcih) != DDI_SUCCESS) {
8569484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
8579484Sgarrett.damore@Sun.COM "unable to map PCI configuration space");
8589484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
8599484Sgarrett.damore@Sun.COM }
8609484Sgarrett.damore@Sun.COM
8619484Sgarrett.damore@Sun.COM /* Read the Audio Controller's vendor, device, and revision IDs */
8629484Sgarrett.damore@Sun.COM state->ts_devid =
8639484Sgarrett.damore@Sun.COM (pci_config_get16(state->ts_pcih, PCI_CONF_VENID) << 16) |
8649484Sgarrett.damore@Sun.COM pci_config_get16(state->ts_pcih, PCI_CONF_DEVID);
8659484Sgarrett.damore@Sun.COM state->ts_revid = pci_config_get8(state->ts_pcih, PCI_CONF_REVID);
8669484Sgarrett.damore@Sun.COM
8679484Sgarrett.damore@Sun.COM if (ddi_regs_map_setup(dip, TS_MEM_MAPPED_REGS,
8689484Sgarrett.damore@Sun.COM (caddr_t *)&state->ts_regs, 0, 0, &ts_regs_attr, &state->ts_acch) !=
8699484Sgarrett.damore@Sun.COM DDI_SUCCESS) {
8709484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
8719484Sgarrett.damore@Sun.COM "unable to map PCI device registers");
8729484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
8739484Sgarrett.damore@Sun.COM }
8749484Sgarrett.damore@Sun.COM
8759484Sgarrett.damore@Sun.COM switch (state->ts_devid) {
8769484Sgarrett.damore@Sun.COM case 0x10b95451:
8779484Sgarrett.damore@Sun.COM name = "ALI M5451";
8789484Sgarrett.damore@Sun.COM break;
8799484Sgarrett.damore@Sun.COM default:
8809484Sgarrett.damore@Sun.COM name = "audiots";
8819484Sgarrett.damore@Sun.COM break;
8829484Sgarrett.damore@Sun.COM }
8839484Sgarrett.damore@Sun.COM (void) snprintf(rev, sizeof (rev), "Rev %x", state->ts_revid);
8849484Sgarrett.damore@Sun.COM audio_dev_set_description(state->ts_adev, name);
8859484Sgarrett.damore@Sun.COM audio_dev_set_version(state->ts_adev, rev);
8869484Sgarrett.damore@Sun.COM
8879484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
8889484Sgarrett.damore@Sun.COM }
8899484Sgarrett.damore@Sun.COM
8909484Sgarrett.damore@Sun.COM /*
8919484Sgarrett.damore@Sun.COM * audiots_alloc_port()
8929484Sgarrett.damore@Sun.COM *
8939484Sgarrett.damore@Sun.COM * Description:
8949484Sgarrett.damore@Sun.COM * This routine allocates the DMA handles and the memory for the
8959484Sgarrett.damore@Sun.COM * DMA engines to use. It then binds each of the buffers to its
8969484Sgarrett.damore@Sun.COM * respective handle, getting a DMA cookie.
8979484Sgarrett.damore@Sun.COM *
8989484Sgarrett.damore@Sun.COM * NOTE: All of the ddi_dma_... routines sleep if they cannot get
8999484Sgarrett.damore@Sun.COM * memory. This means these calls should always succeed.
9009484Sgarrett.damore@Sun.COM *
9019484Sgarrett.damore@Sun.COM * NOTE: ddi_dma_alloc_handle() attempts to use the full 4 GB DMA address
9029484Sgarrett.damore@Sun.COM * range. This is to work around Southbridge rev E/G OBP issues.
9039484Sgarrett.damore@Sun.COM * (See Grover OBP note above)
9049484Sgarrett.damore@Sun.COM *
9059484Sgarrett.damore@Sun.COM * CAUTION: Make sure all errors call audio_dev_warn().
9069484Sgarrett.damore@Sun.COM *
9079484Sgarrett.damore@Sun.COM * Arguments:
9089484Sgarrett.damore@Sun.COM * audiots_port_t *state The port structure for a device stream
9099484Sgarrett.damore@Sun.COM * int num The port number
9109484Sgarrett.damore@Sun.COM *
9119484Sgarrett.damore@Sun.COM * Returns:
9129484Sgarrett.damore@Sun.COM * DDI_SUCCESS DMA resources mapped
9139484Sgarrett.damore@Sun.COM * DDI_FAILURE DMA resources not successfully mapped
9149484Sgarrett.damore@Sun.COM */
9159484Sgarrett.damore@Sun.COM int
audiots_alloc_port(audiots_state_t * state,int num)9169484Sgarrett.damore@Sun.COM audiots_alloc_port(audiots_state_t *state, int num)
9179484Sgarrett.damore@Sun.COM {
9189484Sgarrett.damore@Sun.COM audiots_port_t *port;
9199484Sgarrett.damore@Sun.COM dev_info_t *dip = state->ts_dip;
9209484Sgarrett.damore@Sun.COM audio_dev_t *adev = state->ts_adev;
9219484Sgarrett.damore@Sun.COM int dir;
9229484Sgarrett.damore@Sun.COM unsigned caps;
9239484Sgarrett.damore@Sun.COM ddi_dma_cookie_t cookie;
9249484Sgarrett.damore@Sun.COM unsigned count;
9259484Sgarrett.damore@Sun.COM int rc;
9269484Sgarrett.damore@Sun.COM ddi_acc_handle_t regsh = state->ts_acch;
9279484Sgarrett.damore@Sun.COM uint32_t *gcptr = &state->ts_regs->aud_regs.ap_cir_gc;
9289484Sgarrett.damore@Sun.COM
9299484Sgarrett.damore@Sun.COM port = kmem_zalloc(sizeof (*port), KM_SLEEP);
9309484Sgarrett.damore@Sun.COM state->ts_ports[num] = port;
9319484Sgarrett.damore@Sun.COM port->tp_num = num;
9329484Sgarrett.damore@Sun.COM port->tp_state = state;
933*11936Sgdamore@opensolaris.org port->tp_rate = TS_RATE;
9349484Sgarrett.damore@Sun.COM
9359484Sgarrett.damore@Sun.COM if (num == TS_INPUT_PORT) {
9369484Sgarrett.damore@Sun.COM dir = DDI_DMA_READ;
9379484Sgarrett.damore@Sun.COM caps = ENGINE_INPUT_CAP;
9389484Sgarrett.damore@Sun.COM port->tp_dma_stream = 31;
9399484Sgarrett.damore@Sun.COM port->tp_sync_dir = DDI_DMA_SYNC_FORKERNEL;
9409484Sgarrett.damore@Sun.COM } else {
9419484Sgarrett.damore@Sun.COM dir = DDI_DMA_WRITE;
9429484Sgarrett.damore@Sun.COM caps = ENGINE_OUTPUT_CAP;
9439484Sgarrett.damore@Sun.COM port->tp_dma_stream = 0;
9449484Sgarrett.damore@Sun.COM port->tp_sync_dir = DDI_DMA_SYNC_FORDEV;
9459484Sgarrett.damore@Sun.COM }
946*11936Sgdamore@opensolaris.org
9479484Sgarrett.damore@Sun.COM port->tp_dma_mask = (1U << port->tp_dma_stream);
948*11936Sgdamore@opensolaris.org port->tp_nframes = 4096;
949*11936Sgdamore@opensolaris.org port->tp_size = port->tp_nframes * TS_FRAMESZ;
9509484Sgarrett.damore@Sun.COM
9519484Sgarrett.damore@Sun.COM /* allocate dma handle */
9529484Sgarrett.damore@Sun.COM rc = ddi_dma_alloc_handle(dip, &audiots_attr, DDI_DMA_SLEEP,
9539484Sgarrett.damore@Sun.COM NULL, &port->tp_dmah);
9549484Sgarrett.damore@Sun.COM if (rc != DDI_SUCCESS) {
9559484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
9569484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9579484Sgarrett.damore@Sun.COM }
9589484Sgarrett.damore@Sun.COM /* allocate DMA buffer */
959*11936Sgdamore@opensolaris.org rc = ddi_dma_mem_alloc(port->tp_dmah, port->tp_size, &ts_acc_attr,
9609484Sgarrett.damore@Sun.COM DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->tp_kaddr,
9619484Sgarrett.damore@Sun.COM &port->tp_size, &port->tp_acch);
9629484Sgarrett.damore@Sun.COM if (rc == DDI_FAILURE) {
9639484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "dma_mem_alloc failed");
9649484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9659484Sgarrett.damore@Sun.COM }
9669484Sgarrett.damore@Sun.COM
9679484Sgarrett.damore@Sun.COM /* bind DMA buffer */
9689484Sgarrett.damore@Sun.COM rc = ddi_dma_addr_bind_handle(port->tp_dmah, NULL,
9699484Sgarrett.damore@Sun.COM port->tp_kaddr, port->tp_size, dir|DDI_DMA_CONSISTENT,
9709484Sgarrett.damore@Sun.COM DDI_DMA_SLEEP, NULL, &cookie, &count);
9719484Sgarrett.damore@Sun.COM if (rc != DDI_DMA_MAPPED) {
9729484Sgarrett.damore@Sun.COM audio_dev_warn(adev,
9739484Sgarrett.damore@Sun.COM "ddi_dma_addr_bind_handle failed: %d", rc);
9749484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9759484Sgarrett.damore@Sun.COM }
9769484Sgarrett.damore@Sun.COM ASSERT(count == 1);
9779484Sgarrett.damore@Sun.COM
9789484Sgarrett.damore@Sun.COM port->tp_paddr = cookie.dmac_address;
9799484Sgarrett.damore@Sun.COM if ((unsigned)port->tp_paddr & 0x80000000U) {
9809484Sgarrett.damore@Sun.COM ddi_put32(regsh, gcptr,
9819484Sgarrett.damore@Sun.COM ddi_get32(regsh, gcptr) | AP_CIR_GC_SYS_MEM_4G_ENABLE);
9829484Sgarrett.damore@Sun.COM } else {
9839484Sgarrett.damore@Sun.COM ddi_put32(regsh, gcptr,
9849484Sgarrett.damore@Sun.COM ddi_get32(regsh, gcptr) & ~(AP_CIR_GC_SYS_MEM_4G_ENABLE));
9859484Sgarrett.damore@Sun.COM }
9869484Sgarrett.damore@Sun.COM port->tp_engine = audio_engine_alloc(&audiots_engine_ops, caps);
9879484Sgarrett.damore@Sun.COM if (port->tp_engine == NULL) {
9889484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "audio_engine_alloc failed");
9899484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9909484Sgarrett.damore@Sun.COM }
9919484Sgarrett.damore@Sun.COM
9929484Sgarrett.damore@Sun.COM audio_engine_set_private(port->tp_engine, port);
9939484Sgarrett.damore@Sun.COM audio_dev_add_engine(adev, port->tp_engine);
9949484Sgarrett.damore@Sun.COM
9959484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
9969484Sgarrett.damore@Sun.COM }
9979484Sgarrett.damore@Sun.COM
9989484Sgarrett.damore@Sun.COM /*
9999484Sgarrett.damore@Sun.COM * audiots_read_ac97()
10009484Sgarrett.damore@Sun.COM *
10019484Sgarrett.damore@Sun.COM * Description:
10029484Sgarrett.damore@Sun.COM * This routine actually reads the AC-97 Codec's register. It may
10039484Sgarrett.damore@Sun.COM * be called several times to succeed.
10049484Sgarrett.damore@Sun.COM *
10059484Sgarrett.damore@Sun.COM * NOTE:
10069484Sgarrett.damore@Sun.COM * Revision M1535D B1-C of the ALI SouthBridge includes a workaround for
10079484Sgarrett.damore@Sun.COM * the broken busy flag. Resetting the busy flag requires a software tweak
10089484Sgarrett.damore@Sun.COM * to go with the worked around hardware. When we detect failure, we make
10099484Sgarrett.damore@Sun.COM * 10 attempts to reset the chip before we fail. This should reset the new
10109484Sgarrett.damore@Sun.COM * SB systems. On all SB systems, this will increse the read delay
10119484Sgarrett.damore@Sun.COM * slightly, but shouldn't bother it otherwise.
10129484Sgarrett.damore@Sun.COM *
10139484Sgarrett.damore@Sun.COM * Arguments:
10149484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
10159484Sgarrett.damore@Sun.COM * int reg AC-97 register number
10169484Sgarrett.damore@Sun.COM *
10179484Sgarrett.damore@Sun.COM * Returns:
10189484Sgarrett.damore@Sun.COM * unsigned short The value in the specified register
10199484Sgarrett.damore@Sun.COM */
10209484Sgarrett.damore@Sun.COM static uint16_t
audiots_read_ac97(audiots_state_t * state,int reg)10219484Sgarrett.damore@Sun.COM audiots_read_ac97(audiots_state_t *state, int reg)
10229484Sgarrett.damore@Sun.COM {
10239484Sgarrett.damore@Sun.COM ddi_acc_handle_t acch = state->ts_acch;
10249484Sgarrett.damore@Sun.COM uint16_t *addr;
10259484Sgarrett.damore@Sun.COM uint16_t *data;
10269484Sgarrett.damore@Sun.COM uint32_t *stimer = &state->ts_regs->aud_regs.ap_stimer;
10279484Sgarrett.damore@Sun.COM uint32_t chk1;
10289484Sgarrett.damore@Sun.COM uint32_t chk2;
10299484Sgarrett.damore@Sun.COM int resets = 0;
10309484Sgarrett.damore@Sun.COM int i;
10319484Sgarrett.damore@Sun.COM
10329484Sgarrett.damore@Sun.COM if (state->ts_revid == AC_REV_ID1) {
10339484Sgarrett.damore@Sun.COM addr = &state->ts_regs->aud_regs.ap_acrd_35D_reg;
10349484Sgarrett.damore@Sun.COM data = &state->ts_regs->aud_regs.ap_acrd_35D_data;
10359484Sgarrett.damore@Sun.COM } else {
10369484Sgarrett.damore@Sun.COM addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
10379484Sgarrett.damore@Sun.COM data = &state->ts_regs->aud_regs.ap_acrdwr_data;
10389484Sgarrett.damore@Sun.COM }
10399484Sgarrett.damore@Sun.COM
10409484Sgarrett.damore@Sun.COM first_read:
10419484Sgarrett.damore@Sun.COM /* wait for ready to send read request */
10429484Sgarrett.damore@Sun.COM for (i = 0; i < TS_READ_TRIES; i++) {
10439484Sgarrett.damore@Sun.COM if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
10449484Sgarrett.damore@Sun.COM break;
10459484Sgarrett.damore@Sun.COM }
10469484Sgarrett.damore@Sun.COM /* don't beat on the bus */
10479484Sgarrett.damore@Sun.COM drv_usecwait(1);
10489484Sgarrett.damore@Sun.COM }
10499484Sgarrett.damore@Sun.COM if (i >= TS_READ_TRIES) {
10509484Sgarrett.damore@Sun.COM if (resets < TS_RESET_TRIES) {
10519484Sgarrett.damore@Sun.COM /* Attempt to reset */
10529484Sgarrett.damore@Sun.COM drv_usecwait(TS_20US);
10539484Sgarrett.damore@Sun.COM ddi_put16(acch, addr, TS_SB_RESET);
10549484Sgarrett.damore@Sun.COM resets++;
10559484Sgarrett.damore@Sun.COM goto first_read;
10569484Sgarrett.damore@Sun.COM } else {
10579484Sgarrett.damore@Sun.COM state->ts_flags |= TS_AUDIO_READ_FAILED;
10589484Sgarrett.damore@Sun.COM if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
10599484Sgarrett.damore@Sun.COM ddi_dev_report_fault(state->ts_dip,
10609484Sgarrett.damore@Sun.COM DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
10619484Sgarrett.damore@Sun.COM "Unable to communicate with AC97 CODEC");
10629484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
10639484Sgarrett.damore@Sun.COM "The audio AC97 register has timed out.");
10649484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
10659484Sgarrett.damore@Sun.COM "Audio is now disabled.");
10669484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
10679484Sgarrett.damore@Sun.COM "Please reboot to restore audio.");
10689484Sgarrett.damore@Sun.COM
10699484Sgarrett.damore@Sun.COM /* Don't flood the console */
10709484Sgarrett.damore@Sun.COM state->ts_flags |= TS_READ_FAILURE_PRINTED;
10719484Sgarrett.damore@Sun.COM }
10729484Sgarrett.damore@Sun.COM }
10739484Sgarrett.damore@Sun.COM return (0);
10749484Sgarrett.damore@Sun.COM }
10759484Sgarrett.damore@Sun.COM
10769484Sgarrett.damore@Sun.COM /* program the register to read */
10779484Sgarrett.damore@Sun.COM ddi_put16(acch, addr, (reg|AP_ACRD_W_PRIMARY_CODEC|
10789484Sgarrett.damore@Sun.COM AP_ACRD_W_READ_MIXER_REG|AP_ACRD_W_AUDIO_READ_REQ&
10799484Sgarrett.damore@Sun.COM (~AP_ACWR_W_SELECT_WRITE)));
10809484Sgarrett.damore@Sun.COM
10819484Sgarrett.damore@Sun.COM /* hardware bug work around */
10829484Sgarrett.damore@Sun.COM chk1 = ddi_get32(acch, stimer);
10839484Sgarrett.damore@Sun.COM chk2 = ddi_get32(acch, stimer);
10849484Sgarrett.damore@Sun.COM i = TS_WAIT_CNT;
10859484Sgarrett.damore@Sun.COM while (chk1 == chk2 && i) {
10869484Sgarrett.damore@Sun.COM chk2 = ddi_get32(acch, stimer);
10879484Sgarrett.damore@Sun.COM i--;
10889484Sgarrett.damore@Sun.COM }
10899484Sgarrett.damore@Sun.COM OR_SET_SHORT(acch, addr, AP_ACRD_W_READ_MIXER_REG);
10909484Sgarrett.damore@Sun.COM resets = 0;
10919484Sgarrett.damore@Sun.COM
10929484Sgarrett.damore@Sun.COM second_read:
10939484Sgarrett.damore@Sun.COM /* wait again for read to send read request */
10949484Sgarrett.damore@Sun.COM for (i = 0; i < TS_READ_TRIES; i++) {
10959484Sgarrett.damore@Sun.COM if (!(ddi_get16(acch, addr) & AP_ACRD_R_READ_BUSY)) {
10969484Sgarrett.damore@Sun.COM break;
10979484Sgarrett.damore@Sun.COM }
10989484Sgarrett.damore@Sun.COM /* don't beat on the bus */
10999484Sgarrett.damore@Sun.COM drv_usecwait(1);
11009484Sgarrett.damore@Sun.COM }
11019484Sgarrett.damore@Sun.COM if (i >= TS_READ_TRIES) {
11029484Sgarrett.damore@Sun.COM if (resets < TS_RESET_TRIES) {
11039484Sgarrett.damore@Sun.COM /* Attempt to reset */
11049484Sgarrett.damore@Sun.COM drv_usecwait(TS_20US);
11059484Sgarrett.damore@Sun.COM ddi_put16(acch, addr, TS_SB_RESET);
11069484Sgarrett.damore@Sun.COM resets++;
11079484Sgarrett.damore@Sun.COM goto second_read;
11089484Sgarrett.damore@Sun.COM } else {
11099484Sgarrett.damore@Sun.COM state->ts_flags |= TS_AUDIO_READ_FAILED;
11109484Sgarrett.damore@Sun.COM if (!(state->ts_flags & TS_READ_FAILURE_PRINTED)) {
11119484Sgarrett.damore@Sun.COM ddi_dev_report_fault(state->ts_dip,
11129484Sgarrett.damore@Sun.COM DDI_SERVICE_LOST, DDI_DEVICE_FAULT,
11139484Sgarrett.damore@Sun.COM "Unable to communicate with AC97 CODEC");
11149484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
11159484Sgarrett.damore@Sun.COM "The audio AC97 register has timed out.");
11169484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
11179484Sgarrett.damore@Sun.COM "Audio is now disabled.");
11189484Sgarrett.damore@Sun.COM audio_dev_warn(state->ts_adev,
11199484Sgarrett.damore@Sun.COM "Please reboot to restore audio.");
11209484Sgarrett.damore@Sun.COM
11219484Sgarrett.damore@Sun.COM /* Don't flood the console */
11229484Sgarrett.damore@Sun.COM state->ts_flags |= TS_READ_FAILURE_PRINTED;
11239484Sgarrett.damore@Sun.COM }
11249484Sgarrett.damore@Sun.COM }
11259484Sgarrett.damore@Sun.COM return (0);
11269484Sgarrett.damore@Sun.COM }
11279484Sgarrett.damore@Sun.COM
11289484Sgarrett.damore@Sun.COM return (ddi_get16(acch, data));
11299484Sgarrett.damore@Sun.COM
11309484Sgarrett.damore@Sun.COM } /* audiots_read_ac97() */
11319484Sgarrett.damore@Sun.COM
11329484Sgarrett.damore@Sun.COM /*
11339484Sgarrett.damore@Sun.COM * audiots_set_ac97()
11349484Sgarrett.damore@Sun.COM *
11359484Sgarrett.damore@Sun.COM * Description:
11369484Sgarrett.damore@Sun.COM * Set the value in the specified AC-97 Codec register. Just like
11379484Sgarrett.damore@Sun.COM * reading the AC-97 Codec, it is possible there is a problem writing
11389484Sgarrett.damore@Sun.COM * it as well. So we loop.
11399484Sgarrett.damore@Sun.COM *
11409484Sgarrett.damore@Sun.COM * Arguments:
11419484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
11429484Sgarrett.damore@Sun.COM * int reg AC-97 register number
11439484Sgarrett.damore@Sun.COM * uint16_t value The value to write
11449484Sgarrett.damore@Sun.COM */
11459484Sgarrett.damore@Sun.COM static void
audiots_set_ac97(void * arg,uint8_t reg8,uint16_t data)11469484Sgarrett.damore@Sun.COM audiots_set_ac97(void *arg, uint8_t reg8, uint16_t data)
11479484Sgarrett.damore@Sun.COM {
11489484Sgarrett.damore@Sun.COM audiots_state_t *state = arg;
11499484Sgarrett.damore@Sun.COM ddi_acc_handle_t handle = state->ts_acch;
11509484Sgarrett.damore@Sun.COM uint16_t *data_addr = &state->ts_regs->aud_regs.ap_acrdwr_data;
11519484Sgarrett.damore@Sun.COM uint16_t *reg_addr = &state->ts_regs->aud_regs.ap_acrdwr_reg;
11529484Sgarrett.damore@Sun.COM int count;
11539484Sgarrett.damore@Sun.COM int i;
11549484Sgarrett.damore@Sun.COM uint16_t tmp_short;
11559484Sgarrett.damore@Sun.COM uint16_t reg = reg8;
11569484Sgarrett.damore@Sun.COM
11579484Sgarrett.damore@Sun.COM reg &= AP_ACWR_INDEX_MASK;
11589484Sgarrett.damore@Sun.COM
11599484Sgarrett.damore@Sun.COM /* Don't touch the reserved bits on the pre 35D+ SouthBridge */
11609484Sgarrett.damore@Sun.COM if (state->ts_revid == AC_REV_ID1) {
11619484Sgarrett.damore@Sun.COM reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG;
11629484Sgarrett.damore@Sun.COM } else {
11639484Sgarrett.damore@Sun.COM reg |= AP_ACWR_W_PRIMARY_CODEC|AP_ACWR_W_WRITE_MIXER_REG|
11649484Sgarrett.damore@Sun.COM AP_ACWR_W_SELECT_WRITE;
11659484Sgarrett.damore@Sun.COM }
11669484Sgarrett.damore@Sun.COM
11679484Sgarrett.damore@Sun.COM for (count = TS_LOOP_CNT; count--; ) {
11689484Sgarrett.damore@Sun.COM /* wait for ready to write */
11699484Sgarrett.damore@Sun.COM for (i = 0; i < TS_WAIT_CNT; i++) {
11709484Sgarrett.damore@Sun.COM if (!(ddi_get16(handle, reg_addr) &
11719484Sgarrett.damore@Sun.COM AP_ACWR_R_WRITE_BUSY)) {
11729484Sgarrett.damore@Sun.COM /* ready to write */
11739484Sgarrett.damore@Sun.COM ddi_put16(handle, reg_addr, reg);
11749484Sgarrett.damore@Sun.COM
11759484Sgarrett.damore@Sun.COM /* Write the data */
11769484Sgarrett.damore@Sun.COM ddi_put16(handle, data_addr, data);
11779484Sgarrett.damore@Sun.COM break;
11789484Sgarrett.damore@Sun.COM }
11799484Sgarrett.damore@Sun.COM }
11809484Sgarrett.damore@Sun.COM if (i >= TS_WAIT_CNT) {
11819484Sgarrett.damore@Sun.COM /* try again */
11829484Sgarrett.damore@Sun.COM continue;
11839484Sgarrett.damore@Sun.COM }
11849484Sgarrett.damore@Sun.COM
11859484Sgarrett.damore@Sun.COM /* wait for write to complete */
11869484Sgarrett.damore@Sun.COM for (i = 0; i < TS_WAIT_CNT; i++) {
11879484Sgarrett.damore@Sun.COM if (!(ddi_get16(handle, reg_addr) &
11889484Sgarrett.damore@Sun.COM AP_ACWR_R_WRITE_BUSY)) {
11899484Sgarrett.damore@Sun.COM /* done writing */
11909484Sgarrett.damore@Sun.COM break;
11919484Sgarrett.damore@Sun.COM }
11929484Sgarrett.damore@Sun.COM }
11939484Sgarrett.damore@Sun.COM
11949484Sgarrett.damore@Sun.COM /* verify the value written */
11959484Sgarrett.damore@Sun.COM tmp_short = audiots_get_ac97(state, reg8);
11969484Sgarrett.damore@Sun.COM if (data == tmp_short) {
11979484Sgarrett.damore@Sun.COM /* successfully loaded, so we can return */
11989484Sgarrett.damore@Sun.COM return;
11999484Sgarrett.damore@Sun.COM }
12009484Sgarrett.damore@Sun.COM }
12019484Sgarrett.damore@Sun.COM
12029484Sgarrett.damore@Sun.COM } /* audiots_set_ac97() */
12039484Sgarrett.damore@Sun.COM
12049484Sgarrett.damore@Sun.COM /*
12059484Sgarrett.damore@Sun.COM * audiots_open()
12069484Sgarrett.damore@Sun.COM *
12079484Sgarrett.damore@Sun.COM * Description:
12089484Sgarrett.damore@Sun.COM * Opens a DMA engine for use. Will also ensure the device is powered
12099484Sgarrett.damore@Sun.COM * up if not already done so.
12109484Sgarrett.damore@Sun.COM *
12119484Sgarrett.damore@Sun.COM * Arguments:
12129484Sgarrett.damore@Sun.COM * void *arg The DMA engine to set up
12139484Sgarrett.damore@Sun.COM * int flag Open flags
1214*11936Sgdamore@opensolaris.org * unsigned *nframesp Receives number of frames
12159484Sgarrett.damore@Sun.COM * caddr_t *bufp Receives kernel data buffer
12169484Sgarrett.damore@Sun.COM *
12179484Sgarrett.damore@Sun.COM * Returns:
12189484Sgarrett.damore@Sun.COM * 0 on success
12199484Sgarrett.damore@Sun.COM * errno on failure
12209484Sgarrett.damore@Sun.COM */
12219484Sgarrett.damore@Sun.COM static int
audiots_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)1222*11936Sgdamore@opensolaris.org audiots_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
12239484Sgarrett.damore@Sun.COM {
12249484Sgarrett.damore@Sun.COM audiots_port_t *port = arg;
12259484Sgarrett.damore@Sun.COM
12269484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(flag));
12279484Sgarrett.damore@Sun.COM
12289484Sgarrett.damore@Sun.COM port->tp_count = 0;
12299484Sgarrett.damore@Sun.COM port->tp_cso = 0;
1230*11936Sgdamore@opensolaris.org *nframesp = port->tp_nframes;
12319484Sgarrett.damore@Sun.COM *bufp = port->tp_kaddr;
12329484Sgarrett.damore@Sun.COM
12339484Sgarrett.damore@Sun.COM return (0);
12349484Sgarrett.damore@Sun.COM }
12359484Sgarrett.damore@Sun.COM
12369484Sgarrett.damore@Sun.COM /*
12379484Sgarrett.damore@Sun.COM * audiots_close()
12389484Sgarrett.damore@Sun.COM *
12399484Sgarrett.damore@Sun.COM * Description:
12409484Sgarrett.damore@Sun.COM * Closes an audio DMA engine that was previously opened. Since
1241*11936Sgdamore@opensolaris.org * nobody is using it, we could take this opportunity to possibly power
1242*11936Sgdamore@opensolaris.org * down the entire device, or at least the DMA engine.
12439484Sgarrett.damore@Sun.COM *
12449484Sgarrett.damore@Sun.COM * Arguments:
12459484Sgarrett.damore@Sun.COM * void *arg The DMA engine to shut down
12469484Sgarrett.damore@Sun.COM */
12479484Sgarrett.damore@Sun.COM static void
audiots_close(void * arg)12489484Sgarrett.damore@Sun.COM audiots_close(void *arg)
12499484Sgarrett.damore@Sun.COM {
1250*11936Sgdamore@opensolaris.org _NOTE(ARGUNUSED(arg));
12519484Sgarrett.damore@Sun.COM }
12529484Sgarrett.damore@Sun.COM
12539484Sgarrett.damore@Sun.COM /*
12549484Sgarrett.damore@Sun.COM * audiots_stop()
12559484Sgarrett.damore@Sun.COM *
12569484Sgarrett.damore@Sun.COM * Description:
12579484Sgarrett.damore@Sun.COM * This is called by the framework to stop a port that is
12589484Sgarrett.damore@Sun.COM * transferring data.
12599484Sgarrett.damore@Sun.COM *
12609484Sgarrett.damore@Sun.COM * Arguments:
12619484Sgarrett.damore@Sun.COM * void *arg The DMA engine to stop
12629484Sgarrett.damore@Sun.COM */
12639484Sgarrett.damore@Sun.COM static void
audiots_stop(void * arg)12649484Sgarrett.damore@Sun.COM audiots_stop(void *arg)
12659484Sgarrett.damore@Sun.COM {
12669484Sgarrett.damore@Sun.COM audiots_port_t *port = arg;
12679484Sgarrett.damore@Sun.COM audiots_state_t *state = port->tp_state;
12689484Sgarrett.damore@Sun.COM
1269*11936Sgdamore@opensolaris.org ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
1270*11936Sgdamore@opensolaris.org port->tp_dma_mask);
12719484Sgarrett.damore@Sun.COM }
12729484Sgarrett.damore@Sun.COM
12739484Sgarrett.damore@Sun.COM /*
12749484Sgarrett.damore@Sun.COM * audiots_start()
12759484Sgarrett.damore@Sun.COM *
12769484Sgarrett.damore@Sun.COM * Description:
12779484Sgarrett.damore@Sun.COM * This is called by the framework to start a port transferring data.
12789484Sgarrett.damore@Sun.COM *
12799484Sgarrett.damore@Sun.COM * Arguments:
12809484Sgarrett.damore@Sun.COM * void *arg The DMA engine to start
12819484Sgarrett.damore@Sun.COM *
12829484Sgarrett.damore@Sun.COM * Returns:
12839484Sgarrett.damore@Sun.COM * 0 on success (never fails, errno if it did)
12849484Sgarrett.damore@Sun.COM */
12859484Sgarrett.damore@Sun.COM static int
audiots_start(void * arg)12869484Sgarrett.damore@Sun.COM audiots_start(void *arg)
12879484Sgarrett.damore@Sun.COM {
1288*11936Sgdamore@opensolaris.org audiots_port_t *port = arg;
1289*11936Sgdamore@opensolaris.org audiots_state_t *state = port->tp_state;
1290*11936Sgdamore@opensolaris.org ddi_acc_handle_t handle = state->ts_acch;
1291*11936Sgdamore@opensolaris.org audiots_regs_t *regs = state->ts_regs;
1292*11936Sgdamore@opensolaris.org audiots_aram_t *aram;
1293*11936Sgdamore@opensolaris.org audiots_eram_t *eram;
1294*11936Sgdamore@opensolaris.org unsigned delta;
1295*11936Sgdamore@opensolaris.org uint16_t ctrl;
1296*11936Sgdamore@opensolaris.org uint16_t gvsel;
1297*11936Sgdamore@opensolaris.org uint16_t eso;
1298*11936Sgdamore@opensolaris.org
1299*11936Sgdamore@opensolaris.org aram = ®s->aud_ram[port->tp_dma_stream].aram;
1300*11936Sgdamore@opensolaris.org eram = ®s->aud_ram[port->tp_dma_stream].eram;
1301*11936Sgdamore@opensolaris.org
1302*11936Sgdamore@opensolaris.org port->tp_cso = 0;
1303*11936Sgdamore@opensolaris.org
1304*11936Sgdamore@opensolaris.org gvsel = ERAM_WAVE_VOL | ERAM_PAN_0dB | ERAM_VOL_DEFAULT;
1305*11936Sgdamore@opensolaris.org ctrl = ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE | ERAM_SIGNED_PCM;
1306*11936Sgdamore@opensolaris.org
1307*11936Sgdamore@opensolaris.org delta = (port->tp_rate << TS_SRC_SHIFT) / TS_RATE;
1308*11936Sgdamore@opensolaris.org
1309*11936Sgdamore@opensolaris.org if (port->tp_num == TS_INPUT_PORT) {
1310*11936Sgdamore@opensolaris.org delta = (TS_RATE << TS_SRC_SHIFT) / port->tp_rate;
1311*11936Sgdamore@opensolaris.org }
1312*11936Sgdamore@opensolaris.org eso = port->tp_nframes - 1;
13139484Sgarrett.damore@Sun.COM
1314*11936Sgdamore@opensolaris.org /* program the sample rate */
1315*11936Sgdamore@opensolaris.org ddi_put16(handle, &aram->aram_delta, (uint16_t)delta);
1316*11936Sgdamore@opensolaris.org
1317*11936Sgdamore@opensolaris.org /* program the precision, number of channels and loop mode */
1318*11936Sgdamore@opensolaris.org ddi_put16(handle, &eram->eram_ctrl_ec, ctrl);
1319*11936Sgdamore@opensolaris.org
1320*11936Sgdamore@opensolaris.org /* program the volume settings */
1321*11936Sgdamore@opensolaris.org ddi_put16(handle, &eram->eram_gvsel_pan_vol, gvsel);
1322*11936Sgdamore@opensolaris.org
1323*11936Sgdamore@opensolaris.org /* set ALPHA and FMS to 0 */
1324*11936Sgdamore@opensolaris.org ddi_put16(handle, &aram->aram_alpha_fms, 0x0);
1325*11936Sgdamore@opensolaris.org
1326*11936Sgdamore@opensolaris.org /* set CSO to 0 */
1327*11936Sgdamore@opensolaris.org ddi_put16(handle, &aram->aram_cso, 0x0);
1328*11936Sgdamore@opensolaris.org
1329*11936Sgdamore@opensolaris.org /* set LBA */
1330*11936Sgdamore@opensolaris.org ddi_put32(handle, &aram->aram_cptr_lba,
1331*11936Sgdamore@opensolaris.org port->tp_paddr & ARAM_LBA_MASK);
1332*11936Sgdamore@opensolaris.org
1333*11936Sgdamore@opensolaris.org /* set ESO */
1334*11936Sgdamore@opensolaris.org ddi_put16(handle, &aram->aram_eso, eso);
1335*11936Sgdamore@opensolaris.org
1336*11936Sgdamore@opensolaris.org /* stop the DMA engines */
1337*11936Sgdamore@opensolaris.org ddi_put32(handle, ®s->aud_regs.ap_stop, port->tp_dma_mask);
1338*11936Sgdamore@opensolaris.org
1339*11936Sgdamore@opensolaris.org /* now make sure it starts playing */
1340*11936Sgdamore@opensolaris.org ddi_put32(handle, ®s->aud_regs.ap_start, port->tp_dma_mask);
1341*11936Sgdamore@opensolaris.org
13429484Sgarrett.damore@Sun.COM return (0);
13439484Sgarrett.damore@Sun.COM }
13449484Sgarrett.damore@Sun.COM
13459484Sgarrett.damore@Sun.COM /*
13469484Sgarrett.damore@Sun.COM * audiots_chinfo()
13479484Sgarrett.damore@Sun.COM *
13489484Sgarrett.damore@Sun.COM * Description:
13499484Sgarrett.damore@Sun.COM * This is called by the framework to query the channel offsets
13509484Sgarrett.damore@Sun.COM * and ordering.
13519484Sgarrett.damore@Sun.COM *
13529484Sgarrett.damore@Sun.COM * Arguments:
13539484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
13549484Sgarrett.damore@Sun.COM * int chan Channel number.
13559484Sgarrett.damore@Sun.COM * unsigned *offset Starting offset of channel.
13569484Sgarrett.damore@Sun.COM * unsigned *incr Increment (in samples) between frames.
13579484Sgarrett.damore@Sun.COM *
13589484Sgarrett.damore@Sun.COM * Returns:
13599484Sgarrett.damore@Sun.COM * 0 indicating rate array is range instead of enumeration
13609484Sgarrett.damore@Sun.COM */
13619484Sgarrett.damore@Sun.COM
13629484Sgarrett.damore@Sun.COM static void
audiots_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)13639484Sgarrett.damore@Sun.COM audiots_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
13649484Sgarrett.damore@Sun.COM {
136510829SGarrett.Damore@Sun.COM _NOTE(ARGUNUSED(arg));
136610829SGarrett.Damore@Sun.COM *offset = chan;
13679484Sgarrett.damore@Sun.COM *incr = 2;
13689484Sgarrett.damore@Sun.COM }
13699484Sgarrett.damore@Sun.COM
13709484Sgarrett.damore@Sun.COM /*
13719484Sgarrett.damore@Sun.COM * audiots_format()
13729484Sgarrett.damore@Sun.COM *
13739484Sgarrett.damore@Sun.COM * Description:
13749484Sgarrett.damore@Sun.COM * Called by the framework to query the format for the device.
13759484Sgarrett.damore@Sun.COM *
13769484Sgarrett.damore@Sun.COM * Arguments:
13779484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
13789484Sgarrett.damore@Sun.COM *
13799484Sgarrett.damore@Sun.COM * Returns:
13809484Sgarrett.damore@Sun.COM * AUDIO_FORMAT_S16_LE.
13819484Sgarrett.damore@Sun.COM */
13829484Sgarrett.damore@Sun.COM static int
audiots_format(void * arg)13839484Sgarrett.damore@Sun.COM audiots_format(void *arg)
13849484Sgarrett.damore@Sun.COM {
13859484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(arg));
13869484Sgarrett.damore@Sun.COM
13879484Sgarrett.damore@Sun.COM return (AUDIO_FORMAT_S16_LE);
13889484Sgarrett.damore@Sun.COM }
13899484Sgarrett.damore@Sun.COM
13909484Sgarrett.damore@Sun.COM
13919484Sgarrett.damore@Sun.COM /*
13929484Sgarrett.damore@Sun.COM * audiots_channels()
13939484Sgarrett.damore@Sun.COM *
13949484Sgarrett.damore@Sun.COM * Description:
13959484Sgarrett.damore@Sun.COM * Called by the framework to query the channnels for the device.
13969484Sgarrett.damore@Sun.COM *
13979484Sgarrett.damore@Sun.COM * Arguments:
13989484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
13999484Sgarrett.damore@Sun.COM *
14009484Sgarrett.damore@Sun.COM * Returns:
14019484Sgarrett.damore@Sun.COM * 2 (Stereo).
14029484Sgarrett.damore@Sun.COM */
14039484Sgarrett.damore@Sun.COM static int
audiots_channels(void * arg)14049484Sgarrett.damore@Sun.COM audiots_channels(void *arg)
14059484Sgarrett.damore@Sun.COM {
14069484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(arg));
14079484Sgarrett.damore@Sun.COM
14089484Sgarrett.damore@Sun.COM return (2);
14099484Sgarrett.damore@Sun.COM }
14109484Sgarrett.damore@Sun.COM
14119484Sgarrett.damore@Sun.COM /*
14129484Sgarrett.damore@Sun.COM * audiots_rate()
14139484Sgarrett.damore@Sun.COM *
14149484Sgarrett.damore@Sun.COM * Description:
14159484Sgarrett.damore@Sun.COM * Called by the framework to query the sample rates for the device.
14169484Sgarrett.damore@Sun.COM *
14179484Sgarrett.damore@Sun.COM * Arguments:
14189484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
14199484Sgarrett.damore@Sun.COM *
14209484Sgarrett.damore@Sun.COM * Returns:
14219484Sgarrett.damore@Sun.COM * Sample rate in HZ (always 48000).
14229484Sgarrett.damore@Sun.COM */
14239484Sgarrett.damore@Sun.COM static int
audiots_rate(void * arg)14249484Sgarrett.damore@Sun.COM audiots_rate(void *arg)
14259484Sgarrett.damore@Sun.COM {
14269484Sgarrett.damore@Sun.COM audiots_port_t *port = arg;
14279484Sgarrett.damore@Sun.COM
14289484Sgarrett.damore@Sun.COM return (port->tp_rate);
14299484Sgarrett.damore@Sun.COM }
14309484Sgarrett.damore@Sun.COM
14319484Sgarrett.damore@Sun.COM /*
14329484Sgarrett.damore@Sun.COM * audiots_count()
14339484Sgarrett.damore@Sun.COM *
14349484Sgarrett.damore@Sun.COM * Description:
14359484Sgarrett.damore@Sun.COM * This is called by the framework to get the engine's frame counter
14369484Sgarrett.damore@Sun.COM *
14379484Sgarrett.damore@Sun.COM * Arguments:
14389484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
14399484Sgarrett.damore@Sun.COM *
14409484Sgarrett.damore@Sun.COM * Returns:
14419484Sgarrett.damore@Sun.COM * frame count for current engine
14429484Sgarrett.damore@Sun.COM */
14439484Sgarrett.damore@Sun.COM static uint64_t
audiots_count(void * arg)14449484Sgarrett.damore@Sun.COM audiots_count(void *arg)
14459484Sgarrett.damore@Sun.COM {
14469484Sgarrett.damore@Sun.COM audiots_port_t *port = arg;
14479484Sgarrett.damore@Sun.COM audiots_state_t *state = port->tp_state;
14489484Sgarrett.damore@Sun.COM uint64_t val;
1449*11936Sgdamore@opensolaris.org uint16_t cso;
1450*11936Sgdamore@opensolaris.org unsigned n;
14519484Sgarrett.damore@Sun.COM
14529484Sgarrett.damore@Sun.COM cso = ddi_get16(state->ts_acch,
14539484Sgarrett.damore@Sun.COM &state->ts_regs->aud_ram[port->tp_dma_stream].aram.aram_cso);
14549484Sgarrett.damore@Sun.COM
14559484Sgarrett.damore@Sun.COM n = (cso >= port->tp_cso) ?
14569484Sgarrett.damore@Sun.COM cso - port->tp_cso :
14579484Sgarrett.damore@Sun.COM cso + port->tp_nframes - port->tp_cso;
14589484Sgarrett.damore@Sun.COM
14599484Sgarrett.damore@Sun.COM port->tp_cso = cso;
14609484Sgarrett.damore@Sun.COM port->tp_count += n;
1461*11936Sgdamore@opensolaris.org val = port->tp_count;
1462*11936Sgdamore@opensolaris.org
1463*11936Sgdamore@opensolaris.org return (val);
1464*11936Sgdamore@opensolaris.org }
1465*11936Sgdamore@opensolaris.org
1466*11936Sgdamore@opensolaris.org /*
1467*11936Sgdamore@opensolaris.org * audiots_sync()
1468*11936Sgdamore@opensolaris.org *
1469*11936Sgdamore@opensolaris.org * Description:
1470*11936Sgdamore@opensolaris.org * This is called by the framework to synchronize DMA caches.
1471*11936Sgdamore@opensolaris.org *
1472*11936Sgdamore@opensolaris.org * Arguments:
1473*11936Sgdamore@opensolaris.org * void *arg The DMA engine to sync
1474*11936Sgdamore@opensolaris.org */
1475*11936Sgdamore@opensolaris.org static void
audiots_sync(void * arg,unsigned nframes)1476*11936Sgdamore@opensolaris.org audiots_sync(void *arg, unsigned nframes)
1477*11936Sgdamore@opensolaris.org {
1478*11936Sgdamore@opensolaris.org audiots_port_t *port = arg;
1479*11936Sgdamore@opensolaris.org _NOTE(ARGUNUSED(nframes));
1480*11936Sgdamore@opensolaris.org
1481*11936Sgdamore@opensolaris.org (void) ddi_dma_sync(port->tp_dmah, 0, 0, port->tp_sync_dir);
14829484Sgarrett.damore@Sun.COM }
14839484Sgarrett.damore@Sun.COM
14849484Sgarrett.damore@Sun.COM /*
14859484Sgarrett.damore@Sun.COM * audiots_stop_everything()
14869484Sgarrett.damore@Sun.COM *
14879484Sgarrett.damore@Sun.COM * Description:
14889484Sgarrett.damore@Sun.COM * This routine disables the address engine interrupt for all 32 DMA
14899484Sgarrett.damore@Sun.COM * engines. Just to be sure, it then explicitly issues a stop command to
14909484Sgarrett.damore@Sun.COM * the address engine and envelope engines for all 32 channels.
14919484Sgarrett.damore@Sun.COM *
14929484Sgarrett.damore@Sun.COM * NOTE:
14939484Sgarrett.damore@Sun.COM *
14949484Sgarrett.damore@Sun.COM * There is a hardware bug that generates a spurious interrupt
14959484Sgarrett.damore@Sun.COM * when the DMA engines are stopped. It's not consistent - it
14969484Sgarrett.damore@Sun.COM * happens every 1 out of 6 stops or so. It will show up as a
14979484Sgarrett.damore@Sun.COM * record interrupt. The problem is that once the driver is
14989484Sgarrett.damore@Sun.COM * detached or if the system goes into low power mode, nobody
14999484Sgarrett.damore@Sun.COM * will service that interrupt. The system will eventually become
15009484Sgarrett.damore@Sun.COM * unusable.
15019484Sgarrett.damore@Sun.COM *
15029484Sgarrett.damore@Sun.COM * Arguments:
15039484Sgarrett.damore@Sun.COM * audiots_state_t *state The device's state structure
15049484Sgarrett.damore@Sun.COM */
15059484Sgarrett.damore@Sun.COM static void
audiots_stop_everything(audiots_state_t * state)15069484Sgarrett.damore@Sun.COM audiots_stop_everything(audiots_state_t *state)
15079484Sgarrett.damore@Sun.COM {
15089484Sgarrett.damore@Sun.COM if (state->ts_acch == NULL)
15099484Sgarrett.damore@Sun.COM return;
15109484Sgarrett.damore@Sun.COM
15119484Sgarrett.damore@Sun.COM ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_ainten,
15129484Sgarrett.damore@Sun.COM TS_ALL_DMA_OFF);
15139484Sgarrett.damore@Sun.COM
15149484Sgarrett.damore@Sun.COM ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop,
15159484Sgarrett.damore@Sun.COM TS_ALL_DMA_ENGINES);
15169484Sgarrett.damore@Sun.COM
15179484Sgarrett.damore@Sun.COM ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_aint,
15189484Sgarrett.damore@Sun.COM TS_ALL_DMA_ENGINES);
15199484Sgarrett.damore@Sun.COM }
15209484Sgarrett.damore@Sun.COM
15219484Sgarrett.damore@Sun.COM /*
15229484Sgarrett.damore@Sun.COM * audiots_free_port()
15239484Sgarrett.damore@Sun.COM *
15249484Sgarrett.damore@Sun.COM * Description:
15259484Sgarrett.damore@Sun.COM * This routine unbinds the DMA cookies, frees the DMA buffers,
15269484Sgarrett.damore@Sun.COM * deallocates the DMA handles.
15279484Sgarrett.damore@Sun.COM *
15289484Sgarrett.damore@Sun.COM * Arguments:
15299484Sgarrett.damore@Sun.COM * audiots_port_t *port The port structure for a device stream.
15309484Sgarrett.damore@Sun.COM */
15319484Sgarrett.damore@Sun.COM void
audiots_free_port(audiots_port_t * port)15329484Sgarrett.damore@Sun.COM audiots_free_port(audiots_port_t *port)
15339484Sgarrett.damore@Sun.COM {
15349484Sgarrett.damore@Sun.COM if (port == NULL)
15359484Sgarrett.damore@Sun.COM return;
15369484Sgarrett.damore@Sun.COM
15379484Sgarrett.damore@Sun.COM if (port->tp_engine) {
15389484Sgarrett.damore@Sun.COM audio_dev_remove_engine(port->tp_state->ts_adev,
15399484Sgarrett.damore@Sun.COM port->tp_engine);
15409484Sgarrett.damore@Sun.COM audio_engine_free(port->tp_engine);
15419484Sgarrett.damore@Sun.COM }
15429484Sgarrett.damore@Sun.COM if (port->tp_paddr) {
15439484Sgarrett.damore@Sun.COM (void) ddi_dma_unbind_handle(port->tp_dmah);
15449484Sgarrett.damore@Sun.COM }
15459484Sgarrett.damore@Sun.COM if (port->tp_acch) {
15469484Sgarrett.damore@Sun.COM ddi_dma_mem_free(&port->tp_acch);
15479484Sgarrett.damore@Sun.COM }
15489484Sgarrett.damore@Sun.COM if (port->tp_dmah) {
15499484Sgarrett.damore@Sun.COM ddi_dma_free_handle(&port->tp_dmah);
15509484Sgarrett.damore@Sun.COM }
15519484Sgarrett.damore@Sun.COM kmem_free(port, sizeof (*port));
15529484Sgarrett.damore@Sun.COM }
15539484Sgarrett.damore@Sun.COM
15549484Sgarrett.damore@Sun.COM /*
15559484Sgarrett.damore@Sun.COM * audiots_destroy()
15569484Sgarrett.damore@Sun.COM *
15579484Sgarrett.damore@Sun.COM * Description:
15589484Sgarrett.damore@Sun.COM * This routine releases all resources held by the device instance,
15599484Sgarrett.damore@Sun.COM * as part of either detach or a failure in attach.
15609484Sgarrett.damore@Sun.COM *
15619484Sgarrett.damore@Sun.COM * Arguments:
15629484Sgarrett.damore@Sun.COM * audiots_state_t *state The device soft state.
15639484Sgarrett.damore@Sun.COM */
15649484Sgarrett.damore@Sun.COM void
audiots_destroy(audiots_state_t * state)15659484Sgarrett.damore@Sun.COM audiots_destroy(audiots_state_t *state)
15669484Sgarrett.damore@Sun.COM {
15679484Sgarrett.damore@Sun.COM audiots_stop_everything(state);
15689484Sgarrett.damore@Sun.COM
15699484Sgarrett.damore@Sun.COM for (int i = 0; i < TS_NUM_PORTS; i++)
15709484Sgarrett.damore@Sun.COM audiots_free_port(state->ts_ports[i]);
15719484Sgarrett.damore@Sun.COM
15729484Sgarrett.damore@Sun.COM if (state->ts_acch)
15739484Sgarrett.damore@Sun.COM ddi_regs_map_free(&state->ts_acch);
15749484Sgarrett.damore@Sun.COM
15759484Sgarrett.damore@Sun.COM if (state->ts_pcih)
15769484Sgarrett.damore@Sun.COM pci_config_teardown(&state->ts_pcih);
15779484Sgarrett.damore@Sun.COM
15789484Sgarrett.damore@Sun.COM if (state->ts_ac97)
15799484Sgarrett.damore@Sun.COM ac97_free(state->ts_ac97);
15809484Sgarrett.damore@Sun.COM
15819484Sgarrett.damore@Sun.COM if (state->ts_adev)
15829484Sgarrett.damore@Sun.COM audio_dev_free(state->ts_adev);
15839484Sgarrett.damore@Sun.COM
15849484Sgarrett.damore@Sun.COM ddi_soft_state_free(audiots_statep, ddi_get_instance(state->ts_dip));
15859484Sgarrett.damore@Sun.COM }
1586