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 * audio810 Audio Driver
299484Sgarrett.damore@Sun.COM *
309484Sgarrett.damore@Sun.COM * The driver is primarily targeted at providing audio support for the
31*11936Sgdamore@opensolaris.org * Intel ICHx family of AC'97 controllers and compatible parts (such
32*11936Sgdamore@opensolaris.org * as those from nVidia and AMD.)
339484Sgarrett.damore@Sun.COM *
34*11936Sgdamore@opensolaris.org * These audio parts have independent channels for PCM in, PCM out,
35*11936Sgdamore@opensolaris.org * mic in, and sometimes modem in, and modem out. The AC'97
36*11936Sgdamore@opensolaris.org * controller is a PCI bus master with scatter/gather support. Each
37*11936Sgdamore@opensolaris.org * channel has a DMA engine. Currently, we use only the PCM in and PCM
38*11936Sgdamore@opensolaris.org * out channels. Each DMA engine uses one buffer descriptor list. And
39*11936Sgdamore@opensolaris.org * the buffer descriptor list is an array of up to 32 entries, each of
40*11936Sgdamore@opensolaris.org * which describes a data buffer. Each entry contains a pointer to a
41*11936Sgdamore@opensolaris.org * data buffer, control bits, and the length of the buffer being
42*11936Sgdamore@opensolaris.org * pointed to, where the length is expressed as the number of
43*11936Sgdamore@opensolaris.org * samples. This, combined with the 16-bit sample size, gives the
44*11936Sgdamore@opensolaris.org * actual physical length of the buffer.
459484Sgarrett.damore@Sun.COM *
469484Sgarrett.damore@Sun.COM * A workaround for the AD1980 and AD1985 codec:
479484Sgarrett.damore@Sun.COM * Most vendors connect the surr-out of the codecs to the line-out jack.
489484Sgarrett.damore@Sun.COM * So far we haven't found which vendors don't do that. So we assume that
499484Sgarrett.damore@Sun.COM * all vendors swap the surr-out and the line-out outputs. So we need swap
509484Sgarrett.damore@Sun.COM * the two outputs. But we still internally process the
519484Sgarrett.damore@Sun.COM * "ad198x-swap-output" property. If someday some vendors do not swap the
529484Sgarrett.damore@Sun.COM * outputs, we would set "ad198x-swap-output = 0" in the
539484Sgarrett.damore@Sun.COM * /kernel/drv/audio810.conf file, and unload and reload the audio810
549484Sgarrett.damore@Sun.COM * driver (or reboot).
559484Sgarrett.damore@Sun.COM *
569484Sgarrett.damore@Sun.COM * NOTE:
579484Sgarrett.damore@Sun.COM * This driver depends on the drv/audio and misc/ac97
589484Sgarrett.damore@Sun.COM * modules being loaded first.
59*11936Sgdamore@opensolaris.org *
60*11936Sgdamore@opensolaris.org * The audio framework guarantees that our entry points are exclusive
61*11936Sgdamore@opensolaris.org * with suspend and resume. This includes data flow and control entry
62*11936Sgdamore@opensolaris.org * points alike.
63*11936Sgdamore@opensolaris.org *
64*11936Sgdamore@opensolaris.org * The audio framework guarantees that only one control is being
65*11936Sgdamore@opensolaris.org * accessed on any given audio device at a time.
66*11936Sgdamore@opensolaris.org *
67*11936Sgdamore@opensolaris.org * The audio framework guarantees that entry points are themselves
68*11936Sgdamore@opensolaris.org * serialized for a given engine.
69*11936Sgdamore@opensolaris.org *
70*11936Sgdamore@opensolaris.org * We have no interrupt routine or other internal asynchronous routines.
71*11936Sgdamore@opensolaris.org *
72*11936Sgdamore@opensolaris.org * Our device uses completely separate registers for each engine,
73*11936Sgdamore@opensolaris.org * except for the start/stop registers, which are implemented in a
74*11936Sgdamore@opensolaris.org * manner that allows for them to be accessed concurrently safely from
75*11936Sgdamore@opensolaris.org * different threads.
76*11936Sgdamore@opensolaris.org *
77*11936Sgdamore@opensolaris.org * Hence, it turns out that we simply don't need any locking in this
78*11936Sgdamore@opensolaris.org * driver.
799484Sgarrett.damore@Sun.COM */
809484Sgarrett.damore@Sun.COM #include <sys/types.h>
819484Sgarrett.damore@Sun.COM #include <sys/modctl.h>
829484Sgarrett.damore@Sun.COM #include <sys/kmem.h>
839484Sgarrett.damore@Sun.COM #include <sys/conf.h>
849484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
859484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
869484Sgarrett.damore@Sun.COM #include <sys/pci.h>
879484Sgarrett.damore@Sun.COM #include <sys/note.h>
889484Sgarrett.damore@Sun.COM #include <sys/audio/audio_driver.h>
899484Sgarrett.damore@Sun.COM #include <sys/audio/ac97.h>
909484Sgarrett.damore@Sun.COM #include "audio810.h"
919484Sgarrett.damore@Sun.COM
929484Sgarrett.damore@Sun.COM /*
939484Sgarrett.damore@Sun.COM * Module linkage routines for the kernel
949484Sgarrett.damore@Sun.COM */
959484Sgarrett.damore@Sun.COM static int audio810_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
969484Sgarrett.damore@Sun.COM static int audio810_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
979484Sgarrett.damore@Sun.COM static int audio810_ddi_quiesce(dev_info_t *);
989484Sgarrett.damore@Sun.COM
999484Sgarrett.damore@Sun.COM /*
1009484Sgarrett.damore@Sun.COM * Entry point routine prototypes
1019484Sgarrett.damore@Sun.COM */
102*11936Sgdamore@opensolaris.org static int audio810_open(void *, int, unsigned *, caddr_t *);
1039484Sgarrett.damore@Sun.COM static void audio810_close(void *);
1049484Sgarrett.damore@Sun.COM static int audio810_start(void *);
1059484Sgarrett.damore@Sun.COM static void audio810_stop(void *);
1069484Sgarrett.damore@Sun.COM static int audio810_format(void *);
1079484Sgarrett.damore@Sun.COM static int audio810_channels(void *);
1089484Sgarrett.damore@Sun.COM static int audio810_rate(void *);
1099484Sgarrett.damore@Sun.COM static uint64_t audio810_count(void *);
1109484Sgarrett.damore@Sun.COM static void audio810_sync(void *, unsigned);
11111213Sgdamore@opensolaris.org static unsigned audio810_playahead(void *);
1129484Sgarrett.damore@Sun.COM
1139484Sgarrett.damore@Sun.COM static audio_engine_ops_t audio810_engine_ops = {
1149484Sgarrett.damore@Sun.COM AUDIO_ENGINE_VERSION,
1159484Sgarrett.damore@Sun.COM audio810_open,
1169484Sgarrett.damore@Sun.COM audio810_close,
1179484Sgarrett.damore@Sun.COM audio810_start,
1189484Sgarrett.damore@Sun.COM audio810_stop,
1199484Sgarrett.damore@Sun.COM audio810_count,
1209484Sgarrett.damore@Sun.COM audio810_format,
1219484Sgarrett.damore@Sun.COM audio810_channels,
1229484Sgarrett.damore@Sun.COM audio810_rate,
1239484Sgarrett.damore@Sun.COM audio810_sync,
12411213Sgdamore@opensolaris.org NULL,
12511213Sgdamore@opensolaris.org NULL,
12611213Sgdamore@opensolaris.org audio810_playahead
1279484Sgarrett.damore@Sun.COM };
1289484Sgarrett.damore@Sun.COM
1299484Sgarrett.damore@Sun.COM /*
1309484Sgarrett.damore@Sun.COM * Local Routine Prototypes
1319484Sgarrett.damore@Sun.COM */
1329484Sgarrett.damore@Sun.COM static int audio810_attach(dev_info_t *);
1339484Sgarrett.damore@Sun.COM static int audio810_resume(dev_info_t *);
1349484Sgarrett.damore@Sun.COM static int audio810_detach(dev_info_t *);
1359484Sgarrett.damore@Sun.COM static int audio810_suspend(dev_info_t *);
1369484Sgarrett.damore@Sun.COM
1379484Sgarrett.damore@Sun.COM static int audio810_alloc_port(audio810_state_t *, int, uint8_t);
1389484Sgarrett.damore@Sun.COM static int audio810_codec_sync(audio810_state_t *);
1399484Sgarrett.damore@Sun.COM static void audio810_write_ac97(void *, uint8_t, uint16_t);
1409484Sgarrett.damore@Sun.COM static uint16_t audio810_read_ac97(void *, uint8_t);
1419484Sgarrett.damore@Sun.COM static int audio810_map_regs(dev_info_t *, audio810_state_t *);
1429484Sgarrett.damore@Sun.COM static void audio810_unmap_regs(audio810_state_t *);
1439484Sgarrett.damore@Sun.COM static void audio810_stop_dma(audio810_state_t *);
1449484Sgarrett.damore@Sun.COM static int audio810_chip_init(audio810_state_t *);
145*11936Sgdamore@opensolaris.org static void audio810_set_channels(audio810_state_t *);
1469484Sgarrett.damore@Sun.COM static void audio810_destroy(audio810_state_t *);
1479484Sgarrett.damore@Sun.COM
1489484Sgarrett.damore@Sun.COM /*
1499484Sgarrett.damore@Sun.COM * Global variables, but used only by this file.
1509484Sgarrett.damore@Sun.COM */
1519484Sgarrett.damore@Sun.COM
1529484Sgarrett.damore@Sun.COM /*
1539484Sgarrett.damore@Sun.COM * DDI Structures
1549484Sgarrett.damore@Sun.COM */
1559484Sgarrett.damore@Sun.COM
1569484Sgarrett.damore@Sun.COM /* Device operations structure */
1579484Sgarrett.damore@Sun.COM static struct dev_ops audio810_dev_ops = {
1589484Sgarrett.damore@Sun.COM DEVO_REV, /* devo_rev */
1599484Sgarrett.damore@Sun.COM 0, /* devo_refcnt */
1609484Sgarrett.damore@Sun.COM NULL, /* devo_getinfo */
1619484Sgarrett.damore@Sun.COM nulldev, /* devo_identify - obsolete */
1629484Sgarrett.damore@Sun.COM nulldev, /* devo_probe */
1639484Sgarrett.damore@Sun.COM audio810_ddi_attach, /* devo_attach */
1649484Sgarrett.damore@Sun.COM audio810_ddi_detach, /* devo_detach */
1659484Sgarrett.damore@Sun.COM nodev, /* devo_reset */
1669484Sgarrett.damore@Sun.COM NULL, /* devi_cb_ops */
1679484Sgarrett.damore@Sun.COM NULL, /* devo_bus_ops */
1689484Sgarrett.damore@Sun.COM NULL, /* devo_power */
1699484Sgarrett.damore@Sun.COM audio810_ddi_quiesce, /* devo_quiesce */
1709484Sgarrett.damore@Sun.COM };
1719484Sgarrett.damore@Sun.COM
1729484Sgarrett.damore@Sun.COM /* Linkage structure for loadable drivers */
1739484Sgarrett.damore@Sun.COM static struct modldrv audio810_modldrv = {
1749484Sgarrett.damore@Sun.COM &mod_driverops, /* drv_modops */
1759484Sgarrett.damore@Sun.COM I810_MOD_NAME, /* drv_linkinfo */
1769484Sgarrett.damore@Sun.COM &audio810_dev_ops, /* drv_dev_ops */
1779484Sgarrett.damore@Sun.COM };
1789484Sgarrett.damore@Sun.COM
1799484Sgarrett.damore@Sun.COM /* Module linkage structure */
1809484Sgarrett.damore@Sun.COM static struct modlinkage audio810_modlinkage = {
1819484Sgarrett.damore@Sun.COM MODREV_1, /* ml_rev */
1829484Sgarrett.damore@Sun.COM (void *)&audio810_modldrv, /* ml_linkage */
1839484Sgarrett.damore@Sun.COM NULL /* NULL terminates the list */
1849484Sgarrett.damore@Sun.COM };
1859484Sgarrett.damore@Sun.COM
1869484Sgarrett.damore@Sun.COM /*
1879484Sgarrett.damore@Sun.COM * device access attributes for register mapping
1889484Sgarrett.damore@Sun.COM */
1899484Sgarrett.damore@Sun.COM static struct ddi_device_acc_attr dev_attr = {
1909484Sgarrett.damore@Sun.COM DDI_DEVICE_ATTR_V0,
1919484Sgarrett.damore@Sun.COM DDI_STRUCTURE_LE_ACC,
1929484Sgarrett.damore@Sun.COM DDI_STRICTORDER_ACC
1939484Sgarrett.damore@Sun.COM };
1949484Sgarrett.damore@Sun.COM
1959484Sgarrett.damore@Sun.COM static struct ddi_device_acc_attr buf_attr = {
1969484Sgarrett.damore@Sun.COM DDI_DEVICE_ATTR_V0,
1979484Sgarrett.damore@Sun.COM DDI_STRUCTURE_LE_ACC,
1989484Sgarrett.damore@Sun.COM DDI_STRICTORDER_ACC
1999484Sgarrett.damore@Sun.COM };
2009484Sgarrett.damore@Sun.COM
2019484Sgarrett.damore@Sun.COM /*
2029484Sgarrett.damore@Sun.COM * DMA attributes of buffer descriptor list
2039484Sgarrett.damore@Sun.COM */
2049484Sgarrett.damore@Sun.COM static ddi_dma_attr_t bdlist_dma_attr = {
2059484Sgarrett.damore@Sun.COM DMA_ATTR_V0, /* version */
2069484Sgarrett.damore@Sun.COM 0, /* addr_lo */
2079484Sgarrett.damore@Sun.COM 0xffffffff, /* addr_hi */
2089484Sgarrett.damore@Sun.COM 0x0000ffff, /* count_max */
2099484Sgarrett.damore@Sun.COM 8, /* align, BDL must be aligned on a 8-byte boundary */
2109484Sgarrett.damore@Sun.COM 0x3c, /* burstsize */
2119484Sgarrett.damore@Sun.COM 8, /* minxfer, set to the size of a BDlist entry */
2129484Sgarrett.damore@Sun.COM 0x0000ffff, /* maxxfer */
2139484Sgarrett.damore@Sun.COM 0x00000fff, /* seg, set to the RAM pagesize of intel platform */
2149484Sgarrett.damore@Sun.COM 1, /* sgllen, there's no scatter-gather list */
2159484Sgarrett.damore@Sun.COM 8, /* granular, set to the value of minxfer */
2169484Sgarrett.damore@Sun.COM 0 /* flags, use virtual address */
2179484Sgarrett.damore@Sun.COM };
2189484Sgarrett.damore@Sun.COM
2199484Sgarrett.damore@Sun.COM /*
2209484Sgarrett.damore@Sun.COM * DMA attributes of buffers to be used to receive/send audio data
2219484Sgarrett.damore@Sun.COM */
2229484Sgarrett.damore@Sun.COM static ddi_dma_attr_t sample_buf_dma_attr = {
2239484Sgarrett.damore@Sun.COM DMA_ATTR_V0,
2249484Sgarrett.damore@Sun.COM 0, /* addr_lo */
2259484Sgarrett.damore@Sun.COM 0xffffffff, /* addr_hi */
2269484Sgarrett.damore@Sun.COM 0x0001ffff, /* count_max */
2279484Sgarrett.damore@Sun.COM 4, /* align, data buffer is aligned on a 4-byte boundary */
2289484Sgarrett.damore@Sun.COM 0x3c, /* burstsize */
2299484Sgarrett.damore@Sun.COM 4, /* minxfer, set to the size of a sample data */
2309484Sgarrett.damore@Sun.COM 0x0001ffff, /* maxxfer */
2319484Sgarrett.damore@Sun.COM 0x0001ffff, /* seg */
2329484Sgarrett.damore@Sun.COM 1, /* sgllen, no scatter-gather */
2339484Sgarrett.damore@Sun.COM 4, /* granular, set to the value of minxfer */
2349484Sgarrett.damore@Sun.COM 0, /* flags, use virtual address */
2359484Sgarrett.damore@Sun.COM };
2369484Sgarrett.damore@Sun.COM
2379484Sgarrett.damore@Sun.COM /*
2389484Sgarrett.damore@Sun.COM * _init()
2399484Sgarrett.damore@Sun.COM *
2409484Sgarrett.damore@Sun.COM * Description:
2419484Sgarrett.damore@Sun.COM * Driver initialization, called when driver is first loaded.
2429484Sgarrett.damore@Sun.COM * This is how access is initially given to all the static structures.
2439484Sgarrett.damore@Sun.COM *
2449484Sgarrett.damore@Sun.COM * Arguments:
2459484Sgarrett.damore@Sun.COM * None
2469484Sgarrett.damore@Sun.COM *
2479484Sgarrett.damore@Sun.COM * Returns:
2489484Sgarrett.damore@Sun.COM * mod_install() status, see mod_install(9f)
2499484Sgarrett.damore@Sun.COM */
2509484Sgarrett.damore@Sun.COM int
_init(void)2519484Sgarrett.damore@Sun.COM _init(void)
2529484Sgarrett.damore@Sun.COM {
2539484Sgarrett.damore@Sun.COM int error;
2549484Sgarrett.damore@Sun.COM
2559484Sgarrett.damore@Sun.COM audio_init_ops(&audio810_dev_ops, I810_NAME);
2569484Sgarrett.damore@Sun.COM
2579484Sgarrett.damore@Sun.COM if ((error = mod_install(&audio810_modlinkage)) != 0) {
2589484Sgarrett.damore@Sun.COM audio_fini_ops(&audio810_dev_ops);
2599484Sgarrett.damore@Sun.COM }
2609484Sgarrett.damore@Sun.COM
2619484Sgarrett.damore@Sun.COM return (error);
2629484Sgarrett.damore@Sun.COM }
2639484Sgarrett.damore@Sun.COM
2649484Sgarrett.damore@Sun.COM /*
2659484Sgarrett.damore@Sun.COM * _fini()
2669484Sgarrett.damore@Sun.COM *
2679484Sgarrett.damore@Sun.COM * Description:
2689484Sgarrett.damore@Sun.COM * Module de-initialization, called when the driver is to be unloaded.
2699484Sgarrett.damore@Sun.COM *
2709484Sgarrett.damore@Sun.COM * Arguments:
2719484Sgarrett.damore@Sun.COM * None
2729484Sgarrett.damore@Sun.COM *
2739484Sgarrett.damore@Sun.COM * Returns:
2749484Sgarrett.damore@Sun.COM * mod_remove() status, see mod_remove(9f)
2759484Sgarrett.damore@Sun.COM */
2769484Sgarrett.damore@Sun.COM int
_fini(void)2779484Sgarrett.damore@Sun.COM _fini(void)
2789484Sgarrett.damore@Sun.COM {
2799484Sgarrett.damore@Sun.COM int error;
2809484Sgarrett.damore@Sun.COM
2819484Sgarrett.damore@Sun.COM if ((error = mod_remove(&audio810_modlinkage)) != 0) {
2829484Sgarrett.damore@Sun.COM return (error);
2839484Sgarrett.damore@Sun.COM }
2849484Sgarrett.damore@Sun.COM
2859484Sgarrett.damore@Sun.COM /* clean up ops */
2869484Sgarrett.damore@Sun.COM audio_fini_ops(&audio810_dev_ops);
2879484Sgarrett.damore@Sun.COM
2889484Sgarrett.damore@Sun.COM return (0);
2899484Sgarrett.damore@Sun.COM }
2909484Sgarrett.damore@Sun.COM
2919484Sgarrett.damore@Sun.COM /*
2929484Sgarrett.damore@Sun.COM * _info()
2939484Sgarrett.damore@Sun.COM *
2949484Sgarrett.damore@Sun.COM * Description:
2959484Sgarrett.damore@Sun.COM * Module information, returns information about the driver.
2969484Sgarrett.damore@Sun.COM *
2979484Sgarrett.damore@Sun.COM * Arguments:
2989484Sgarrett.damore@Sun.COM * modinfo *modinfop Pointer to the opaque modinfo structure
2999484Sgarrett.damore@Sun.COM *
3009484Sgarrett.damore@Sun.COM * Returns:
3019484Sgarrett.damore@Sun.COM * mod_info() status, see mod_info(9f)
3029484Sgarrett.damore@Sun.COM */
3039484Sgarrett.damore@Sun.COM int
_info(struct modinfo * modinfop)3049484Sgarrett.damore@Sun.COM _info(struct modinfo *modinfop)
3059484Sgarrett.damore@Sun.COM {
3069484Sgarrett.damore@Sun.COM return (mod_info(&audio810_modlinkage, modinfop));
3079484Sgarrett.damore@Sun.COM }
3089484Sgarrett.damore@Sun.COM
3099484Sgarrett.damore@Sun.COM
3109484Sgarrett.damore@Sun.COM /* ******************* Driver Entry Points ********************************* */
3119484Sgarrett.damore@Sun.COM
3129484Sgarrett.damore@Sun.COM /*
3139484Sgarrett.damore@Sun.COM * audio810_ddi_attach()
3149484Sgarrett.damore@Sun.COM *
3159484Sgarrett.damore@Sun.COM * Description:
3169484Sgarrett.damore@Sun.COM * Implements the DDI attach(9e) entry point.
3179484Sgarrett.damore@Sun.COM *
3189484Sgarrett.damore@Sun.COM * Arguments:
3199484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
3209484Sgarrett.damore@Sun.COM * ddi_attach_cmd_t cmd Attach command
3219484Sgarrett.damore@Sun.COM *
3229484Sgarrett.damore@Sun.COM * Returns:
3239484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was initialized properly
3249484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be initialized properly
3259484Sgarrett.damore@Sun.COM */
3269484Sgarrett.damore@Sun.COM static int
audio810_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3279484Sgarrett.damore@Sun.COM audio810_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3289484Sgarrett.damore@Sun.COM {
3299484Sgarrett.damore@Sun.COM switch (cmd) {
3309484Sgarrett.damore@Sun.COM case DDI_ATTACH:
3319484Sgarrett.damore@Sun.COM return (audio810_attach(dip));
3329484Sgarrett.damore@Sun.COM
3339484Sgarrett.damore@Sun.COM case DDI_RESUME:
3349484Sgarrett.damore@Sun.COM return (audio810_resume(dip));
3359484Sgarrett.damore@Sun.COM }
3369484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
3379484Sgarrett.damore@Sun.COM }
3389484Sgarrett.damore@Sun.COM
3399484Sgarrett.damore@Sun.COM /*
3409484Sgarrett.damore@Sun.COM * audio810_ddi_detach()
3419484Sgarrett.damore@Sun.COM *
3429484Sgarrett.damore@Sun.COM * Description:
3439484Sgarrett.damore@Sun.COM * Implements the detach(9e) entry point.
3449484Sgarrett.damore@Sun.COM *
3459484Sgarrett.damore@Sun.COM * Arguments:
3469484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
3479484Sgarrett.damore@Sun.COM * ddi_detach_cmd_t cmd Detach command
3489484Sgarrett.damore@Sun.COM *
3499484Sgarrett.damore@Sun.COM * Returns:
3509484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was detached
3519484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be detached
3529484Sgarrett.damore@Sun.COM */
3539484Sgarrett.damore@Sun.COM static int
audio810_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3549484Sgarrett.damore@Sun.COM audio810_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3559484Sgarrett.damore@Sun.COM {
3569484Sgarrett.damore@Sun.COM switch (cmd) {
3579484Sgarrett.damore@Sun.COM case DDI_DETACH:
3589484Sgarrett.damore@Sun.COM return (audio810_detach(dip));
3599484Sgarrett.damore@Sun.COM
3609484Sgarrett.damore@Sun.COM case DDI_SUSPEND:
3619484Sgarrett.damore@Sun.COM return (audio810_suspend(dip));
3629484Sgarrett.damore@Sun.COM }
3639484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
3649484Sgarrett.damore@Sun.COM }
3659484Sgarrett.damore@Sun.COM
3669484Sgarrett.damore@Sun.COM /*
3679484Sgarrett.damore@Sun.COM * audio810_ddi_quiesce()
3689484Sgarrett.damore@Sun.COM *
3699484Sgarrett.damore@Sun.COM * Description:
3709484Sgarrett.damore@Sun.COM * Implements the quiesce(9e) entry point.
3719484Sgarrett.damore@Sun.COM *
3729484Sgarrett.damore@Sun.COM * Arguments:
3739484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
3749484Sgarrett.damore@Sun.COM *
3759484Sgarrett.damore@Sun.COM * Returns:
3769484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was quiesced
3779484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be quiesced
3789484Sgarrett.damore@Sun.COM */
3799484Sgarrett.damore@Sun.COM static int
audio810_ddi_quiesce(dev_info_t * dip)3809484Sgarrett.damore@Sun.COM audio810_ddi_quiesce(dev_info_t *dip)
3819484Sgarrett.damore@Sun.COM {
3829484Sgarrett.damore@Sun.COM audio810_state_t *statep;
3839484Sgarrett.damore@Sun.COM
3849484Sgarrett.damore@Sun.COM if ((statep = ddi_get_driver_private(dip)) == NULL)
3859484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
3869484Sgarrett.damore@Sun.COM
3879484Sgarrett.damore@Sun.COM audio810_stop_dma(statep);
3889484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
3899484Sgarrett.damore@Sun.COM }
3909484Sgarrett.damore@Sun.COM
3919484Sgarrett.damore@Sun.COM /*
3929484Sgarrett.damore@Sun.COM * audio810_open()
3939484Sgarrett.damore@Sun.COM *
3949484Sgarrett.damore@Sun.COM * Description:
3959484Sgarrett.damore@Sun.COM * Opens a DMA engine for use.
3969484Sgarrett.damore@Sun.COM *
3979484Sgarrett.damore@Sun.COM * Arguments:
3989484Sgarrett.damore@Sun.COM * void *arg The DMA engine to set up
3999484Sgarrett.damore@Sun.COM * int flag Open flags
400*11936Sgdamore@opensolaris.org * unsigned *nframes Receives total number of frames
4019484Sgarrett.damore@Sun.COM * caddr_t *bufp Receives kernel data buffer
4029484Sgarrett.damore@Sun.COM *
4039484Sgarrett.damore@Sun.COM * Returns:
4049484Sgarrett.damore@Sun.COM * 0 on success
4059484Sgarrett.damore@Sun.COM * errno on failure
4069484Sgarrett.damore@Sun.COM */
4079484Sgarrett.damore@Sun.COM static int
audio810_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)408*11936Sgdamore@opensolaris.org audio810_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
4099484Sgarrett.damore@Sun.COM {
4109484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
4119484Sgarrett.damore@Sun.COM
4129484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(flag));
4139484Sgarrett.damore@Sun.COM
4149484Sgarrett.damore@Sun.COM port->count = 0;
415*11936Sgdamore@opensolaris.org *nframes = port->samp_frames;
4169484Sgarrett.damore@Sun.COM *bufp = port->samp_kaddr;
4179484Sgarrett.damore@Sun.COM
4189484Sgarrett.damore@Sun.COM return (0);
4199484Sgarrett.damore@Sun.COM }
4209484Sgarrett.damore@Sun.COM
4219484Sgarrett.damore@Sun.COM /*
4229484Sgarrett.damore@Sun.COM * audio810_close()
4239484Sgarrett.damore@Sun.COM *
4249484Sgarrett.damore@Sun.COM * Description:
4259484Sgarrett.damore@Sun.COM * Closes an audio DMA engine that was previously opened. Since
4269484Sgarrett.damore@Sun.COM * nobody is using it, we take this opportunity to possibly power
4279484Sgarrett.damore@Sun.COM * down the entire device.
4289484Sgarrett.damore@Sun.COM *
4299484Sgarrett.damore@Sun.COM * Arguments:
4309484Sgarrett.damore@Sun.COM * void *arg The DMA engine to shut down
4319484Sgarrett.damore@Sun.COM */
4329484Sgarrett.damore@Sun.COM static void
audio810_close(void * arg)4339484Sgarrett.damore@Sun.COM audio810_close(void *arg)
4349484Sgarrett.damore@Sun.COM {
435*11936Sgdamore@opensolaris.org _NOTE(ARGUNUSED(arg));
4369484Sgarrett.damore@Sun.COM }
4379484Sgarrett.damore@Sun.COM
4389484Sgarrett.damore@Sun.COM /*
4399484Sgarrett.damore@Sun.COM * audio810_stop()
4409484Sgarrett.damore@Sun.COM *
4419484Sgarrett.damore@Sun.COM * Description:
4429484Sgarrett.damore@Sun.COM * This is called by the framework to stop a port that is
4439484Sgarrett.damore@Sun.COM * transferring data.
4449484Sgarrett.damore@Sun.COM *
4459484Sgarrett.damore@Sun.COM * Arguments:
4469484Sgarrett.damore@Sun.COM * void *arg The DMA engine to stop
4479484Sgarrett.damore@Sun.COM */
4489484Sgarrett.damore@Sun.COM static void
audio810_stop(void * arg)4499484Sgarrett.damore@Sun.COM audio810_stop(void *arg)
4509484Sgarrett.damore@Sun.COM {
4519484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
4529484Sgarrett.damore@Sun.COM audio810_state_t *statep = port->statep;
453*11936Sgdamore@opensolaris.org uint8_t cr;
4549484Sgarrett.damore@Sun.COM
455*11936Sgdamore@opensolaris.org cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR);
456*11936Sgdamore@opensolaris.org cr &= ~I810_BM_CR_RUN;
457*11936Sgdamore@opensolaris.org I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr);
4589484Sgarrett.damore@Sun.COM }
4599484Sgarrett.damore@Sun.COM
4609484Sgarrett.damore@Sun.COM /*
4619484Sgarrett.damore@Sun.COM * audio810_start()
4629484Sgarrett.damore@Sun.COM *
4639484Sgarrett.damore@Sun.COM * Description:
4649484Sgarrett.damore@Sun.COM * This is called by the framework to start a port transferring data.
4659484Sgarrett.damore@Sun.COM *
4669484Sgarrett.damore@Sun.COM * Arguments:
4679484Sgarrett.damore@Sun.COM * void *arg The DMA engine to start
4689484Sgarrett.damore@Sun.COM *
4699484Sgarrett.damore@Sun.COM * Returns:
4709484Sgarrett.damore@Sun.COM * 0 on success (never fails, errno if it did)
4719484Sgarrett.damore@Sun.COM */
4729484Sgarrett.damore@Sun.COM static int
audio810_start(void * arg)4739484Sgarrett.damore@Sun.COM audio810_start(void *arg)
4749484Sgarrett.damore@Sun.COM {
4759484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
4769484Sgarrett.damore@Sun.COM audio810_state_t *statep = port->statep;
477*11936Sgdamore@opensolaris.org uint8_t regoff, cr;
4789484Sgarrett.damore@Sun.COM
479*11936Sgdamore@opensolaris.org regoff = port->regoff;
480*11936Sgdamore@opensolaris.org port->offset = 0;
481*11936Sgdamore@opensolaris.org
482*11936Sgdamore@opensolaris.org /* program multiple channel settings */
483*11936Sgdamore@opensolaris.org if (port->num == I810_PCM_OUT) {
484*11936Sgdamore@opensolaris.org audio810_set_channels(statep);
485*11936Sgdamore@opensolaris.org
486*11936Sgdamore@opensolaris.org if (statep->quirk == QUIRK_SIS7012) {
487*11936Sgdamore@opensolaris.org /*
488*11936Sgdamore@opensolaris.org * SiS 7012 has special unmute bit.
489*11936Sgdamore@opensolaris.org */
490*11936Sgdamore@opensolaris.org I810_BM_PUT8(I810_REG_SISCTL, I810_SISCTL_UNMUTE);
491*11936Sgdamore@opensolaris.org }
4929484Sgarrett.damore@Sun.COM }
493*11936Sgdamore@opensolaris.org
494*11936Sgdamore@opensolaris.org /*
495*11936Sgdamore@opensolaris.org * Perform full reset of the engine, but leave it turned off.
496*11936Sgdamore@opensolaris.org */
497*11936Sgdamore@opensolaris.org I810_BM_PUT8(regoff + I810_OFFSET_CR, 0);
498*11936Sgdamore@opensolaris.org I810_BM_PUT8(regoff + I810_OFFSET_CR, I810_BM_CR_RST);
499*11936Sgdamore@opensolaris.org
500*11936Sgdamore@opensolaris.org /* program the offset of the BD list */
501*11936Sgdamore@opensolaris.org I810_BM_PUT32(regoff + I810_OFFSET_BD_BASE, port->bdl_paddr);
502*11936Sgdamore@opensolaris.org
503*11936Sgdamore@opensolaris.org /* we set the last index to the full count -- all buffers are valid */
504*11936Sgdamore@opensolaris.org I810_BM_PUT8(regoff + I810_OFFSET_LVI, I810_BD_NUMS - 1);
505*11936Sgdamore@opensolaris.org
506*11936Sgdamore@opensolaris.org cr = I810_BM_GET8(regoff + I810_OFFSET_CR);
507*11936Sgdamore@opensolaris.org cr |= I810_BM_CR_RUN;
508*11936Sgdamore@opensolaris.org I810_BM_PUT8(regoff + I810_OFFSET_CR, cr);
509*11936Sgdamore@opensolaris.org
510*11936Sgdamore@opensolaris.org (void) I810_BM_GET8(regoff + I810_OFFSET_CR);
511*11936Sgdamore@opensolaris.org
5129484Sgarrett.damore@Sun.COM return (0);
5139484Sgarrett.damore@Sun.COM }
5149484Sgarrett.damore@Sun.COM
5159484Sgarrett.damore@Sun.COM /*
5169484Sgarrett.damore@Sun.COM * audio810_format()
5179484Sgarrett.damore@Sun.COM *
5189484Sgarrett.damore@Sun.COM * Description:
5199484Sgarrett.damore@Sun.COM * This is called by the framework to query the format of the device.
5209484Sgarrett.damore@Sun.COM *
5219484Sgarrett.damore@Sun.COM * Arguments:
5229484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
5239484Sgarrett.damore@Sun.COM *
5249484Sgarrett.damore@Sun.COM * Returns:
5259484Sgarrett.damore@Sun.COM * Format of the device (fixed at AUDIO_FORMAT_S16_LE)
5269484Sgarrett.damore@Sun.COM */
5279484Sgarrett.damore@Sun.COM static int
audio810_format(void * arg)5289484Sgarrett.damore@Sun.COM audio810_format(void *arg)
5299484Sgarrett.damore@Sun.COM {
5309484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(arg));
5319484Sgarrett.damore@Sun.COM
5329484Sgarrett.damore@Sun.COM return (AUDIO_FORMAT_S16_LE);
5339484Sgarrett.damore@Sun.COM }
5349484Sgarrett.damore@Sun.COM
5359484Sgarrett.damore@Sun.COM /*
5369484Sgarrett.damore@Sun.COM * audio810_channels()
5379484Sgarrett.damore@Sun.COM *
5389484Sgarrett.damore@Sun.COM * Description:
5399484Sgarrett.damore@Sun.COM * This is called by the framework to query the num channels of
5409484Sgarrett.damore@Sun.COM * the device.
5419484Sgarrett.damore@Sun.COM *
5429484Sgarrett.damore@Sun.COM * Arguments:
5439484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
5449484Sgarrett.damore@Sun.COM *
5459484Sgarrett.damore@Sun.COM * Returns:
5469484Sgarrett.damore@Sun.COM * 0 number of channels for device
5479484Sgarrett.damore@Sun.COM */
5489484Sgarrett.damore@Sun.COM static int
audio810_channels(void * arg)5499484Sgarrett.damore@Sun.COM audio810_channels(void *arg)
5509484Sgarrett.damore@Sun.COM {
5519484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
5529484Sgarrett.damore@Sun.COM
5539484Sgarrett.damore@Sun.COM return (port->nchan);
5549484Sgarrett.damore@Sun.COM }
5559484Sgarrett.damore@Sun.COM
5569484Sgarrett.damore@Sun.COM /*
5579484Sgarrett.damore@Sun.COM * audio810_rate()
5589484Sgarrett.damore@Sun.COM *
5599484Sgarrett.damore@Sun.COM * Description:
5609484Sgarrett.damore@Sun.COM * This is called by the framework to query the rate of the device.
5619484Sgarrett.damore@Sun.COM *
5629484Sgarrett.damore@Sun.COM * Arguments:
5639484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
5649484Sgarrett.damore@Sun.COM *
5659484Sgarrett.damore@Sun.COM * Returns:
5669484Sgarrett.damore@Sun.COM * Rate of device (fixed at 48000 Hz)
5679484Sgarrett.damore@Sun.COM */
5689484Sgarrett.damore@Sun.COM static int
audio810_rate(void * arg)5699484Sgarrett.damore@Sun.COM audio810_rate(void *arg)
5709484Sgarrett.damore@Sun.COM {
5719484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(arg));
5729484Sgarrett.damore@Sun.COM
5739484Sgarrett.damore@Sun.COM return (48000);
5749484Sgarrett.damore@Sun.COM }
5759484Sgarrett.damore@Sun.COM
5769484Sgarrett.damore@Sun.COM /*
5779484Sgarrett.damore@Sun.COM * audio810_count()
5789484Sgarrett.damore@Sun.COM *
5799484Sgarrett.damore@Sun.COM * Description:
5809484Sgarrett.damore@Sun.COM * This is called by the framework to get the engine's frame counter
5819484Sgarrett.damore@Sun.COM *
5829484Sgarrett.damore@Sun.COM * Arguments:
5839484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
5849484Sgarrett.damore@Sun.COM *
5859484Sgarrett.damore@Sun.COM * Returns:
5869484Sgarrett.damore@Sun.COM * frame count for current engine
5879484Sgarrett.damore@Sun.COM */
5889484Sgarrett.damore@Sun.COM static uint64_t
audio810_count(void * arg)5899484Sgarrett.damore@Sun.COM audio810_count(void *arg)
5909484Sgarrett.damore@Sun.COM {
5919484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
5929484Sgarrett.damore@Sun.COM audio810_state_t *statep = port->statep;
593*11936Sgdamore@opensolaris.org uint8_t regoff = port->regoff;
5949484Sgarrett.damore@Sun.COM uint64_t val;
595*11936Sgdamore@opensolaris.org uint32_t offset;
596*11936Sgdamore@opensolaris.org uint8_t civ;
597*11936Sgdamore@opensolaris.org
598*11936Sgdamore@opensolaris.org /*
599*11936Sgdamore@opensolaris.org * Read the position counters. We also take this opportunity
600*11936Sgdamore@opensolaris.org * to update the last valid index to the one just previous to
601*11936Sgdamore@opensolaris.org * the one we're working on (so we'll fully loop.)
602*11936Sgdamore@opensolaris.org */
603*11936Sgdamore@opensolaris.org offset = I810_BM_GET16(port->picboff);
604*11936Sgdamore@opensolaris.org civ = I810_BM_GET8(regoff + I810_OFFSET_CIV);
605*11936Sgdamore@opensolaris.org I810_BM_PUT8(port->regoff + I810_OFFSET_LVI, (civ - 1) % I810_BD_NUMS);
6069484Sgarrett.damore@Sun.COM
607*11936Sgdamore@opensolaris.org /* SiS counts in bytes, all others in words. */
608*11936Sgdamore@opensolaris.org if (statep->quirk != QUIRK_SIS7012)
609*11936Sgdamore@opensolaris.org offset *= 2;
610*11936Sgdamore@opensolaris.org
611*11936Sgdamore@opensolaris.org /* counter is reversed */
612*11936Sgdamore@opensolaris.org offset = port->samp_size - offset;
6139484Sgarrett.damore@Sun.COM
614*11936Sgdamore@opensolaris.org if (offset < port->offset) {
615*11936Sgdamore@opensolaris.org val = (port->samp_size - port->offset) + offset;
6169484Sgarrett.damore@Sun.COM } else {
617*11936Sgdamore@opensolaris.org val = offset - port->offset;
6189484Sgarrett.damore@Sun.COM }
619*11936Sgdamore@opensolaris.org port->offset = offset;
620*11936Sgdamore@opensolaris.org port->count += (val / (port->nchan * 2));
621*11936Sgdamore@opensolaris.org val = port->count;
6229484Sgarrett.damore@Sun.COM
6239484Sgarrett.damore@Sun.COM return (val);
6249484Sgarrett.damore@Sun.COM }
6259484Sgarrett.damore@Sun.COM
6269484Sgarrett.damore@Sun.COM /*
6279484Sgarrett.damore@Sun.COM * audio810_sync()
6289484Sgarrett.damore@Sun.COM *
6299484Sgarrett.damore@Sun.COM * Description:
6309484Sgarrett.damore@Sun.COM * This is called by the framework to synchronize DMA caches.
6319484Sgarrett.damore@Sun.COM *
6329484Sgarrett.damore@Sun.COM * Arguments:
6339484Sgarrett.damore@Sun.COM * void *arg The DMA engine to sync
6349484Sgarrett.damore@Sun.COM */
6359484Sgarrett.damore@Sun.COM static void
audio810_sync(void * arg,unsigned nframes)6369484Sgarrett.damore@Sun.COM audio810_sync(void *arg, unsigned nframes)
6379484Sgarrett.damore@Sun.COM {
6389484Sgarrett.damore@Sun.COM audio810_port_t *port = arg;
6399484Sgarrett.damore@Sun.COM _NOTE(ARGUNUSED(nframes));
6409484Sgarrett.damore@Sun.COM
6419484Sgarrett.damore@Sun.COM (void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
6429484Sgarrett.damore@Sun.COM }
6439484Sgarrett.damore@Sun.COM
6449484Sgarrett.damore@Sun.COM /*
64511213Sgdamore@opensolaris.org * audio810_playahead()
6469484Sgarrett.damore@Sun.COM *
6479484Sgarrett.damore@Sun.COM * Description:
64811213Sgdamore@opensolaris.org * This is called by the framework to determine how much data it
64911213Sgdamore@opensolaris.org * should queue up. We desire a deeper playahead than most to
65011213Sgdamore@opensolaris.org * allow for virtualized devices which have less "regular"
65111213Sgdamore@opensolaris.org * interrupt scheduling.
6529484Sgarrett.damore@Sun.COM *
6539484Sgarrett.damore@Sun.COM * Arguments:
6549484Sgarrett.damore@Sun.COM * void *arg The DMA engine to query
6559484Sgarrett.damore@Sun.COM *
6569484Sgarrett.damore@Sun.COM * Returns:
657*11936Sgdamore@opensolaris.org * Play ahead in frames.
6589484Sgarrett.damore@Sun.COM */
65911213Sgdamore@opensolaris.org static unsigned
audio810_playahead(void * arg)66011213Sgdamore@opensolaris.org audio810_playahead(void *arg)
6619484Sgarrett.damore@Sun.COM {
66211213Sgdamore@opensolaris.org audio810_port_t *port = arg;
663*11936Sgdamore@opensolaris.org audio810_state_t *statep = port->statep;
66411213Sgdamore@opensolaris.org
665*11936Sgdamore@opensolaris.org /* Older ICH is likely to be emulated, deeper (40 ms) playahead */
666*11936Sgdamore@opensolaris.org return (statep->quirk == QUIRK_OLDICH ? 1920 : 0);
6679484Sgarrett.damore@Sun.COM }
6689484Sgarrett.damore@Sun.COM
66911213Sgdamore@opensolaris.org
67011213Sgdamore@opensolaris.org
6719484Sgarrett.damore@Sun.COM /* *********************** Local Routines *************************** */
6729484Sgarrett.damore@Sun.COM
6739484Sgarrett.damore@Sun.COM /*
6749484Sgarrett.damore@Sun.COM * audio810_attach()
6759484Sgarrett.damore@Sun.COM *
6769484Sgarrett.damore@Sun.COM * Description:
6779484Sgarrett.damore@Sun.COM * Attach an instance of the audio810 driver. This routine does the
6789484Sgarrett.damore@Sun.COM * device dependent attach tasks, and registers with the audio framework.
6799484Sgarrett.damore@Sun.COM *
6809484Sgarrett.damore@Sun.COM * Arguments:
6819484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
6829484Sgarrett.damore@Sun.COM * ddi_attach_cmd_t cmd Attach command
6839484Sgarrett.damore@Sun.COM *
6849484Sgarrett.damore@Sun.COM * Returns:
6859484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was initialized properly
6869484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be initialized properly
6879484Sgarrett.damore@Sun.COM */
6889484Sgarrett.damore@Sun.COM static int
audio810_attach(dev_info_t * dip)6899484Sgarrett.damore@Sun.COM audio810_attach(dev_info_t *dip)
6909484Sgarrett.damore@Sun.COM {
6919484Sgarrett.damore@Sun.COM uint16_t cmdreg;
6929484Sgarrett.damore@Sun.COM audio810_state_t *statep;
6939484Sgarrett.damore@Sun.COM audio_dev_t *adev;
6949484Sgarrett.damore@Sun.COM ddi_acc_handle_t pcih;
6959484Sgarrett.damore@Sun.COM uint32_t devid;
6969484Sgarrett.damore@Sun.COM uint32_t gsr;
6979484Sgarrett.damore@Sun.COM const char *name;
6989484Sgarrett.damore@Sun.COM const char *vers;
6999484Sgarrett.damore@Sun.COM uint8_t nch;
7009484Sgarrett.damore@Sun.COM int maxch;
7019484Sgarrett.damore@Sun.COM
7029484Sgarrett.damore@Sun.COM /* allocate the soft state structure */
7039484Sgarrett.damore@Sun.COM statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
7049484Sgarrett.damore@Sun.COM ddi_set_driver_private(dip, statep);
7059484Sgarrett.damore@Sun.COM
7069484Sgarrett.damore@Sun.COM if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
707*11936Sgdamore@opensolaris.org cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev",
708*11936Sgdamore@opensolaris.org ddi_driver_name(dip), ddi_get_instance(dip));
7099484Sgarrett.damore@Sun.COM goto error;
7109484Sgarrett.damore@Sun.COM }
7119484Sgarrett.damore@Sun.COM statep->adev = adev;
7129484Sgarrett.damore@Sun.COM statep->dip = dip;
7139484Sgarrett.damore@Sun.COM
7149484Sgarrett.damore@Sun.COM /* map in the registers, allocate DMA buffers, etc. */
7159484Sgarrett.damore@Sun.COM if (audio810_map_regs(dip, statep) != DDI_SUCCESS) {
7169484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "couldn't map registers");
7179484Sgarrett.damore@Sun.COM goto error;
7189484Sgarrett.damore@Sun.COM }
7199484Sgarrett.damore@Sun.COM
7209484Sgarrett.damore@Sun.COM /* set PCI command register */
7219484Sgarrett.damore@Sun.COM if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
7229484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "pci conf mapping failed");
7239484Sgarrett.damore@Sun.COM goto error;
7249484Sgarrett.damore@Sun.COM }
7259484Sgarrett.damore@Sun.COM cmdreg = pci_config_get16(pcih, PCI_CONF_COMM);
7269484Sgarrett.damore@Sun.COM pci_config_put16(pcih, PCI_CONF_COMM,
7279484Sgarrett.damore@Sun.COM cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
7289484Sgarrett.damore@Sun.COM devid = pci_config_get16(pcih, PCI_CONF_VENID);
7299484Sgarrett.damore@Sun.COM devid <<= 16;
7309484Sgarrett.damore@Sun.COM devid |= pci_config_get16(pcih, PCI_CONF_DEVID);
7319484Sgarrett.damore@Sun.COM pci_config_teardown(&pcih);
7329484Sgarrett.damore@Sun.COM
7339484Sgarrett.damore@Sun.COM name = "Unknown AC'97";
7349484Sgarrett.damore@Sun.COM vers = "";
7359484Sgarrett.damore@Sun.COM
7369484Sgarrett.damore@Sun.COM statep->quirk = QUIRK_NONE;
7379484Sgarrett.damore@Sun.COM switch (devid) {
7389484Sgarrett.damore@Sun.COM case 0x80862415:
7399484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7409484Sgarrett.damore@Sun.COM vers = "ICH";
741*11936Sgdamore@opensolaris.org statep->quirk = QUIRK_OLDICH;
7429484Sgarrett.damore@Sun.COM break;
7439484Sgarrett.damore@Sun.COM case 0x80862425:
7449484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7459484Sgarrett.damore@Sun.COM vers = "ICH0";
7469484Sgarrett.damore@Sun.COM break;
7479484Sgarrett.damore@Sun.COM case 0x80867195:
7489484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7499484Sgarrett.damore@Sun.COM vers = "440MX";
7509484Sgarrett.damore@Sun.COM break;
7519484Sgarrett.damore@Sun.COM case 0x80862445:
7529484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7539484Sgarrett.damore@Sun.COM vers = "ICH2";
7549484Sgarrett.damore@Sun.COM break;
7559484Sgarrett.damore@Sun.COM case 0x80862485:
7569484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7579484Sgarrett.damore@Sun.COM vers = "ICH3";
7589484Sgarrett.damore@Sun.COM break;
7599484Sgarrett.damore@Sun.COM case 0x808624C5:
7609484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7619484Sgarrett.damore@Sun.COM vers = "ICH4";
7629484Sgarrett.damore@Sun.COM break;
7639484Sgarrett.damore@Sun.COM case 0x808624D5:
7649484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7659484Sgarrett.damore@Sun.COM vers = "ICH5";
7669484Sgarrett.damore@Sun.COM break;
7679484Sgarrett.damore@Sun.COM case 0x8086266E:
7689484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7699484Sgarrett.damore@Sun.COM vers = "ICH6";
7709484Sgarrett.damore@Sun.COM break;
7719484Sgarrett.damore@Sun.COM case 0x808627DE:
7729484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7739484Sgarrett.damore@Sun.COM vers = "ICH7";
7749484Sgarrett.damore@Sun.COM break;
7759484Sgarrett.damore@Sun.COM case 0x808625A6:
7769484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7779484Sgarrett.damore@Sun.COM vers = "6300ESB";
7789484Sgarrett.damore@Sun.COM break;
7799484Sgarrett.damore@Sun.COM case 0x80862698:
7809484Sgarrett.damore@Sun.COM name = "Intel AC'97";
7819484Sgarrett.damore@Sun.COM vers = "ESB2";
7829484Sgarrett.damore@Sun.COM break;
7839484Sgarrett.damore@Sun.COM case 0x10397012:
7849484Sgarrett.damore@Sun.COM name = "SiS AC'97";
7859484Sgarrett.damore@Sun.COM vers = "7012";
7869484Sgarrett.damore@Sun.COM statep->quirk = QUIRK_SIS7012;
7879484Sgarrett.damore@Sun.COM break;
7889484Sgarrett.damore@Sun.COM case 0x10de01b1: /* nForce */
7899484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
7909484Sgarrett.damore@Sun.COM vers = "MCP1";
7919484Sgarrett.damore@Sun.COM break;
7929484Sgarrett.damore@Sun.COM case 0x10de006a: /* nForce 2 */
7939484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
7949484Sgarrett.damore@Sun.COM vers = "MCP2";
7959484Sgarrett.damore@Sun.COM break;
7969484Sgarrett.damore@Sun.COM case 0x10de00da: /* nForce 3 */
7979484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
7989484Sgarrett.damore@Sun.COM vers = "MCP3";
7999484Sgarrett.damore@Sun.COM break;
8009484Sgarrett.damore@Sun.COM case 0x10de00ea:
8019484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
8029484Sgarrett.damore@Sun.COM vers = "CK8S";
8039484Sgarrett.damore@Sun.COM break;
8049484Sgarrett.damore@Sun.COM case 0x10de0059:
8059484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
8069484Sgarrett.damore@Sun.COM vers = "CK804";
8079484Sgarrett.damore@Sun.COM break;
8089484Sgarrett.damore@Sun.COM case 0x10de008a:
8099484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
8109484Sgarrett.damore@Sun.COM vers = "CK8";
8119484Sgarrett.damore@Sun.COM break;
8129484Sgarrett.damore@Sun.COM case 0x10de003a: /* nForce 4 */
8139484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
8149484Sgarrett.damore@Sun.COM vers = "MCP4";
8159484Sgarrett.damore@Sun.COM break;
8169484Sgarrett.damore@Sun.COM case 0x10de026b:
8179484Sgarrett.damore@Sun.COM name = "NVIDIA AC'97";
8189484Sgarrett.damore@Sun.COM vers = "MCP51";
8199484Sgarrett.damore@Sun.COM break;
8209484Sgarrett.damore@Sun.COM case 0x1022746d:
8219484Sgarrett.damore@Sun.COM name = "AMD AC'97";
8229484Sgarrett.damore@Sun.COM vers = "8111";
8239484Sgarrett.damore@Sun.COM break;
8249484Sgarrett.damore@Sun.COM case 0x10227445:
8259484Sgarrett.damore@Sun.COM name = "AMD AC'97";
8269484Sgarrett.damore@Sun.COM vers = "AMD768";
8279484Sgarrett.damore@Sun.COM break;
8289484Sgarrett.damore@Sun.COM }
8299484Sgarrett.damore@Sun.COM /* set device information */
8309484Sgarrett.damore@Sun.COM audio_dev_set_description(adev, name);
8319484Sgarrett.damore@Sun.COM audio_dev_set_version(adev, vers);
8329484Sgarrett.damore@Sun.COM
8339602Sgdamore@opensolaris.org /* initialize audio controller and AC97 codec */
8349602Sgdamore@opensolaris.org if (audio810_chip_init(statep) != DDI_SUCCESS) {
8359602Sgdamore@opensolaris.org audio_dev_warn(adev, "failed to init chip");
8369484Sgarrett.damore@Sun.COM goto error;
8379484Sgarrett.damore@Sun.COM }
8389484Sgarrett.damore@Sun.COM
8399484Sgarrett.damore@Sun.COM /* allocate ac97 handle */
8409484Sgarrett.damore@Sun.COM statep->ac97 = ac97_alloc(dip, audio810_read_ac97, audio810_write_ac97,
8419484Sgarrett.damore@Sun.COM statep);
8429484Sgarrett.damore@Sun.COM if (statep->ac97 == NULL) {
8439484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "failed to allocate ac97 handle");
8449484Sgarrett.damore@Sun.COM goto error;
8459484Sgarrett.damore@Sun.COM }
8469484Sgarrett.damore@Sun.COM
8479602Sgdamore@opensolaris.org /* initialize the AC'97 part */
8489602Sgdamore@opensolaris.org if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
8499602Sgdamore@opensolaris.org audio_dev_warn(adev, "ac'97 initialization failed");
8509484Sgarrett.damore@Sun.COM goto error;
8519484Sgarrett.damore@Sun.COM }
8529484Sgarrett.damore@Sun.COM
8539602Sgdamore@opensolaris.org /*
8549602Sgdamore@opensolaris.org * Override "max-channels" property to prevent configuration
8559602Sgdamore@opensolaris.org * of 4 or 6 (or possibly even 8!) channel audio. The default
8569602Sgdamore@opensolaris.org * is to support as many channels as the hardware can do.
8579602Sgdamore@opensolaris.org *
8589602Sgdamore@opensolaris.org * (Hmmm... perhaps this should be driven in the common
8599602Sgdamore@opensolaris.org * framework. The framework could even offer simplistic upmix
8609602Sgdamore@opensolaris.org * and downmix for various standard configs.)
8619602Sgdamore@opensolaris.org */
8629602Sgdamore@opensolaris.org maxch = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8639602Sgdamore@opensolaris.org "max-channels", ac97_num_channels(statep->ac97));
8649602Sgdamore@opensolaris.org if (maxch < 2) {
8659602Sgdamore@opensolaris.org maxch = 2;
8669602Sgdamore@opensolaris.org }
8679602Sgdamore@opensolaris.org
8689602Sgdamore@opensolaris.org gsr = I810_BM_GET32(I810_REG_GSR);
8699602Sgdamore@opensolaris.org if (gsr & I810_GSR_CAP6CH) {
8709602Sgdamore@opensolaris.org nch = 6;
8719602Sgdamore@opensolaris.org } else if (gsr & I810_GSR_CAP4CH) {
8729602Sgdamore@opensolaris.org nch = 4;
8739602Sgdamore@opensolaris.org } else {
8749602Sgdamore@opensolaris.org nch = 2;
8759602Sgdamore@opensolaris.org }
8769602Sgdamore@opensolaris.org
8779602Sgdamore@opensolaris.org statep->maxch = (uint8_t)min(nch, maxch);
8789602Sgdamore@opensolaris.org statep->maxch &= ~1;
8799602Sgdamore@opensolaris.org
8809602Sgdamore@opensolaris.org /* allocate port structures */
8819602Sgdamore@opensolaris.org if ((audio810_alloc_port(statep, I810_PCM_OUT, statep->maxch) !=
8829602Sgdamore@opensolaris.org DDI_SUCCESS) ||
8839602Sgdamore@opensolaris.org (audio810_alloc_port(statep, I810_PCM_IN, 2) != DDI_SUCCESS)) {
8849484Sgarrett.damore@Sun.COM goto error;
8859484Sgarrett.damore@Sun.COM }
8869484Sgarrett.damore@Sun.COM
8879484Sgarrett.damore@Sun.COM if (audio_dev_register(adev) != DDI_SUCCESS) {
8889484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "unable to register with framework");
8899484Sgarrett.damore@Sun.COM goto error;
8909484Sgarrett.damore@Sun.COM }
8919484Sgarrett.damore@Sun.COM
8929484Sgarrett.damore@Sun.COM ddi_report_dev(dip);
8939484Sgarrett.damore@Sun.COM
8949484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
8959484Sgarrett.damore@Sun.COM
8969484Sgarrett.damore@Sun.COM error:
8979484Sgarrett.damore@Sun.COM audio810_destroy(statep);
8989484Sgarrett.damore@Sun.COM
8999484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9009484Sgarrett.damore@Sun.COM }
9019484Sgarrett.damore@Sun.COM
9029484Sgarrett.damore@Sun.COM
9039484Sgarrett.damore@Sun.COM /*
9049484Sgarrett.damore@Sun.COM * audio810_resume()
9059484Sgarrett.damore@Sun.COM *
9069484Sgarrett.damore@Sun.COM * Description:
9079484Sgarrett.damore@Sun.COM * Resume operation of the device after sleeping or hibernating.
9089484Sgarrett.damore@Sun.COM * Note that this should never fail, even if hardware goes wonky,
9099484Sgarrett.damore@Sun.COM * because the current PM framework will panic if it does.
9109484Sgarrett.damore@Sun.COM *
9119484Sgarrett.damore@Sun.COM * Arguments:
9129484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
9139484Sgarrett.damore@Sun.COM *
9149484Sgarrett.damore@Sun.COM * Returns:
9159484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was resumed.
9169484Sgarrett.damore@Sun.COM */
9179484Sgarrett.damore@Sun.COM static int
audio810_resume(dev_info_t * dip)9189484Sgarrett.damore@Sun.COM audio810_resume(dev_info_t *dip)
9199484Sgarrett.damore@Sun.COM {
9209484Sgarrett.damore@Sun.COM audio810_state_t *statep;
9219484Sgarrett.damore@Sun.COM audio_dev_t *adev;
9229484Sgarrett.damore@Sun.COM
9239484Sgarrett.damore@Sun.COM /* this should always be valid */
9249484Sgarrett.damore@Sun.COM statep = ddi_get_driver_private(dip);
9259484Sgarrett.damore@Sun.COM adev = statep->adev;
9269484Sgarrett.damore@Sun.COM
9279484Sgarrett.damore@Sun.COM ASSERT(statep != NULL);
9289484Sgarrett.damore@Sun.COM ASSERT(dip == statep->dip);
9299484Sgarrett.damore@Sun.COM
9309484Sgarrett.damore@Sun.COM /* Restore the audio810 chip's state */
9319484Sgarrett.damore@Sun.COM if (audio810_chip_init(statep) != DDI_SUCCESS) {
9329484Sgarrett.damore@Sun.COM /*
933*11936Sgdamore@opensolaris.org * Note that PM gurus say we should return success
934*11936Sgdamore@opensolaris.org * here. Failure of audio shouldn't be considered
935*11936Sgdamore@opensolaris.org * FATAL to the system.
936*11936Sgdamore@opensolaris.org *
937*11936Sgdamore@opensolaris.org * It turns out that the only way that the
938*11936Sgdamore@opensolaris.org * audio810_chip_init fails is that the codec won't
939*11936Sgdamore@opensolaris.org * re-initialize. Audio streams may or may not make
940*11936Sgdamore@opensolaris.org * progress; setting changes may or may not have the
941*11936Sgdamore@opensolaris.org * desired effect. What we'd really to do at this
942*11936Sgdamore@opensolaris.org * point is use FMA to offline the part. In the
943*11936Sgdamore@opensolaris.org * meantime, we just muddle on logging the error.
944*11936Sgdamore@opensolaris.org *
945*11936Sgdamore@opensolaris.org * Note that returning from this routine without
946*11936Sgdamore@opensolaris.org * allowing the audio_dev_resume() to take place can
947*11936Sgdamore@opensolaris.org * have bad effects, as the framework does not know
948*11936Sgdamore@opensolaris.org * what to do in the event of a failure of this
949*11936Sgdamore@opensolaris.org * nature. (It may be unsafe to call ENG_CLOSE(), for
950*11936Sgdamore@opensolaris.org * example.)
9519484Sgarrett.damore@Sun.COM */
952*11936Sgdamore@opensolaris.org audio_dev_warn(adev, "failure to resume codec");
9539484Sgarrett.damore@Sun.COM }
9549484Sgarrett.damore@Sun.COM
955*11936Sgdamore@opensolaris.org /* Reset the AC'97 codec. */
956*11936Sgdamore@opensolaris.org ac97_reset(statep->ac97);
9579484Sgarrett.damore@Sun.COM
958*11936Sgdamore@opensolaris.org /* And let the framework know we're ready for business again. */
959*11936Sgdamore@opensolaris.org audio_dev_resume(statep->adev);
9609484Sgarrett.damore@Sun.COM
9619484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
9629484Sgarrett.damore@Sun.COM }
9639484Sgarrett.damore@Sun.COM
9649484Sgarrett.damore@Sun.COM /*
9659484Sgarrett.damore@Sun.COM * audio810_detach()
9669484Sgarrett.damore@Sun.COM *
9679484Sgarrett.damore@Sun.COM * Description:
9689484Sgarrett.damore@Sun.COM * Detach an instance of the audio810 driver.
9699484Sgarrett.damore@Sun.COM *
9709484Sgarrett.damore@Sun.COM * Arguments:
9719484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
9729484Sgarrett.damore@Sun.COM *
9739484Sgarrett.damore@Sun.COM * Returns:
9749484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was detached
9759484Sgarrett.damore@Sun.COM * DDI_FAILURE The driver couldn't be detached
9769484Sgarrett.damore@Sun.COM */
9779484Sgarrett.damore@Sun.COM static int
audio810_detach(dev_info_t * dip)9789484Sgarrett.damore@Sun.COM audio810_detach(dev_info_t *dip)
9799484Sgarrett.damore@Sun.COM {
9809484Sgarrett.damore@Sun.COM audio810_state_t *statep;
9819484Sgarrett.damore@Sun.COM
9829484Sgarrett.damore@Sun.COM statep = ddi_get_driver_private(dip);
9839484Sgarrett.damore@Sun.COM ASSERT(statep != NULL);
9849484Sgarrett.damore@Sun.COM
9859484Sgarrett.damore@Sun.COM /* don't detach us if we are still in use */
9869484Sgarrett.damore@Sun.COM if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
9879484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
9889484Sgarrett.damore@Sun.COM }
9899484Sgarrett.damore@Sun.COM
9909484Sgarrett.damore@Sun.COM audio810_destroy(statep);
9919484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
9929484Sgarrett.damore@Sun.COM }
9939484Sgarrett.damore@Sun.COM
9949484Sgarrett.damore@Sun.COM /*
9959484Sgarrett.damore@Sun.COM * audio810_suspend()
9969484Sgarrett.damore@Sun.COM *
9979484Sgarrett.damore@Sun.COM * Description:
9989484Sgarrett.damore@Sun.COM * Suspend an instance of the audio810 driver, in preparation for
9999484Sgarrett.damore@Sun.COM * sleep or hibernation.
10009484Sgarrett.damore@Sun.COM *
10019484Sgarrett.damore@Sun.COM * Arguments:
10029484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's dev_info struct
10039484Sgarrett.damore@Sun.COM *
10049484Sgarrett.damore@Sun.COM * Returns:
10059484Sgarrett.damore@Sun.COM * DDI_SUCCESS The driver was suspended
10069484Sgarrett.damore@Sun.COM */
10079484Sgarrett.damore@Sun.COM static int
audio810_suspend(dev_info_t * dip)10089484Sgarrett.damore@Sun.COM audio810_suspend(dev_info_t *dip)
10099484Sgarrett.damore@Sun.COM {
10109484Sgarrett.damore@Sun.COM audio810_state_t *statep;
10119484Sgarrett.damore@Sun.COM
10129484Sgarrett.damore@Sun.COM statep = ddi_get_driver_private(dip);
10139484Sgarrett.damore@Sun.COM ASSERT(statep != NULL);
10149484Sgarrett.damore@Sun.COM
1015*11936Sgdamore@opensolaris.org audio_dev_suspend(statep->adev);
10169484Sgarrett.damore@Sun.COM
1017*11936Sgdamore@opensolaris.org /* stop DMA engines - should be redundant (paranoia) */
10189484Sgarrett.damore@Sun.COM audio810_stop_dma(statep);
10199484Sgarrett.damore@Sun.COM
10209484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
10219484Sgarrett.damore@Sun.COM }
10229484Sgarrett.damore@Sun.COM
10239484Sgarrett.damore@Sun.COM /*
10249484Sgarrett.damore@Sun.COM * audio810_alloc_port()
10259484Sgarrett.damore@Sun.COM *
10269484Sgarrett.damore@Sun.COM * Description:
10279484Sgarrett.damore@Sun.COM * This routine allocates the DMA handles and the memory for the
10289484Sgarrett.damore@Sun.COM * DMA engines to use. It also configures the BDL lists properly
10299484Sgarrett.damore@Sun.COM * for use.
10309484Sgarrett.damore@Sun.COM *
10319484Sgarrett.damore@Sun.COM * Arguments:
10329484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's devinfo
10339484Sgarrett.damore@Sun.COM *
10349484Sgarrett.damore@Sun.COM * Returns:
10359484Sgarrett.damore@Sun.COM * DDI_SUCCESS Registers successfully mapped
10369484Sgarrett.damore@Sun.COM * DDI_FAILURE Registers not successfully mapped
10379484Sgarrett.damore@Sun.COM */
10389484Sgarrett.damore@Sun.COM static int
audio810_alloc_port(audio810_state_t * statep,int num,uint8_t nchan)10399484Sgarrett.damore@Sun.COM audio810_alloc_port(audio810_state_t *statep, int num, uint8_t nchan)
10409484Sgarrett.damore@Sun.COM {
10419484Sgarrett.damore@Sun.COM ddi_dma_cookie_t cookie;
10429484Sgarrett.damore@Sun.COM uint_t count;
10439484Sgarrett.damore@Sun.COM int dir;
10449484Sgarrett.damore@Sun.COM unsigned caps;
10459484Sgarrett.damore@Sun.COM audio_dev_t *adev;
10469484Sgarrett.damore@Sun.COM audio810_port_t *port;
10479484Sgarrett.damore@Sun.COM int rc;
10489484Sgarrett.damore@Sun.COM dev_info_t *dip;
10499484Sgarrett.damore@Sun.COM i810_bd_entry_t *bdentry;
10509484Sgarrett.damore@Sun.COM
10519484Sgarrett.damore@Sun.COM adev = statep->adev;
10529484Sgarrett.damore@Sun.COM dip = statep->dip;
10539484Sgarrett.damore@Sun.COM
10549484Sgarrett.damore@Sun.COM port = kmem_zalloc(sizeof (*port), KM_SLEEP);
10559484Sgarrett.damore@Sun.COM statep->ports[num] = port;
10569484Sgarrett.damore@Sun.COM port->statep = statep;
10579484Sgarrett.damore@Sun.COM port->nchan = nchan;
10589484Sgarrett.damore@Sun.COM port->num = num;
10599484Sgarrett.damore@Sun.COM
10609484Sgarrett.damore@Sun.COM switch (num) {
10619484Sgarrett.damore@Sun.COM case I810_PCM_IN:
10629484Sgarrett.damore@Sun.COM dir = DDI_DMA_READ;
10639484Sgarrett.damore@Sun.COM caps = ENGINE_INPUT_CAP;
10649484Sgarrett.damore@Sun.COM port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
10659484Sgarrett.damore@Sun.COM port->regoff = I810_BASE_PCM_IN;
10669484Sgarrett.damore@Sun.COM break;
10679484Sgarrett.damore@Sun.COM case I810_PCM_OUT:
10689484Sgarrett.damore@Sun.COM dir = DDI_DMA_WRITE;
10699484Sgarrett.damore@Sun.COM caps = ENGINE_OUTPUT_CAP;
10709484Sgarrett.damore@Sun.COM port->sync_dir = DDI_DMA_SYNC_FORDEV;
10719484Sgarrett.damore@Sun.COM port->regoff = I810_BASE_PCM_OUT;
10729484Sgarrett.damore@Sun.COM break;
10739484Sgarrett.damore@Sun.COM default:
10749484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "bad port number (%d)!", num);
10759484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
10769484Sgarrett.damore@Sun.COM }
10779484Sgarrett.damore@Sun.COM
10789484Sgarrett.damore@Sun.COM /*
10799484Sgarrett.damore@Sun.COM * SiS 7012 swaps status and picb registers.
10809484Sgarrett.damore@Sun.COM */
10819484Sgarrett.damore@Sun.COM if (statep->quirk == QUIRK_SIS7012) {
10829484Sgarrett.damore@Sun.COM port->stsoff = port->regoff + I810_OFFSET_PICB;
10839484Sgarrett.damore@Sun.COM port->picboff = port->regoff + I810_OFFSET_SR;
10849484Sgarrett.damore@Sun.COM } else {
10859484Sgarrett.damore@Sun.COM port->stsoff = port->regoff + I810_OFFSET_SR;
10869484Sgarrett.damore@Sun.COM port->picboff = port->regoff + I810_OFFSET_PICB;
10879484Sgarrett.damore@Sun.COM }
10889484Sgarrett.damore@Sun.COM
10899484Sgarrett.damore@Sun.COM /*
1090*11936Sgdamore@opensolaris.org * We use one big sample area. The sample area must be larger
1091*11936Sgdamore@opensolaris.org * than about 1.5 framework fragment sizes. (Currently 480 *
1092*11936Sgdamore@opensolaris.org * 1.5 = 720 frames.) This is necessary to ensure that we
1093*11936Sgdamore@opensolaris.org * don't have to involve an interrupt service routine on our
1094*11936Sgdamore@opensolaris.org * own, to keep the last valid index updated reasonably.
10959484Sgarrett.damore@Sun.COM */
1096*11936Sgdamore@opensolaris.org port->samp_frames = 4096;
1097*11936Sgdamore@opensolaris.org port->samp_size = port->samp_frames * port->nchan * sizeof (int16_t);
10989484Sgarrett.damore@Sun.COM
10999484Sgarrett.damore@Sun.COM /* allocate dma handle */
11009484Sgarrett.damore@Sun.COM rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
11019484Sgarrett.damore@Sun.COM NULL, &port->samp_dmah);
11029484Sgarrett.damore@Sun.COM if (rc != DDI_SUCCESS) {
11039484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
11049484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11059484Sgarrett.damore@Sun.COM }
11069484Sgarrett.damore@Sun.COM /* allocate DMA buffer */
11079484Sgarrett.damore@Sun.COM rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
11089484Sgarrett.damore@Sun.COM DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
11099484Sgarrett.damore@Sun.COM &port->samp_size, &port->samp_acch);
11109484Sgarrett.damore@Sun.COM if (rc == DDI_FAILURE) {
11119484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "dma_mem_alloc (%d) failed: %d",
11129484Sgarrett.damore@Sun.COM port->samp_size, rc);
11139484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11149484Sgarrett.damore@Sun.COM }
11159484Sgarrett.damore@Sun.COM
11169484Sgarrett.damore@Sun.COM /* bind DMA buffer */
11179484Sgarrett.damore@Sun.COM rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
11189484Sgarrett.damore@Sun.COM port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
11199484Sgarrett.damore@Sun.COM DDI_DMA_SLEEP, NULL, &cookie, &count);
11209484Sgarrett.damore@Sun.COM if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
11219484Sgarrett.damore@Sun.COM audio_dev_warn(adev,
11229484Sgarrett.damore@Sun.COM "ddi_dma_addr_bind_handle failed: %d", rc);
11239484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11249484Sgarrett.damore@Sun.COM }
11259484Sgarrett.damore@Sun.COM port->samp_paddr = cookie.dmac_address;
11269484Sgarrett.damore@Sun.COM
11279484Sgarrett.damore@Sun.COM /*
11289484Sgarrett.damore@Sun.COM * now, from here we allocate DMA memory for buffer descriptor list.
11299484Sgarrett.damore@Sun.COM * we allocate adjacent DMA memory for all DMA engines.
11309484Sgarrett.damore@Sun.COM */
11319484Sgarrett.damore@Sun.COM rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
11329484Sgarrett.damore@Sun.COM NULL, &port->bdl_dmah);
11339484Sgarrett.damore@Sun.COM if (rc != DDI_SUCCESS) {
11349484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
11359484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11369484Sgarrett.damore@Sun.COM }
11379484Sgarrett.damore@Sun.COM
11389484Sgarrett.damore@Sun.COM /*
11399484Sgarrett.damore@Sun.COM * we allocate all buffer descriptors lists in continuous dma memory.
11409484Sgarrett.damore@Sun.COM */
11419484Sgarrett.damore@Sun.COM port->bdl_size = sizeof (i810_bd_entry_t) * I810_BD_NUMS;
11429484Sgarrett.damore@Sun.COM rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
11439484Sgarrett.damore@Sun.COM &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
11449484Sgarrett.damore@Sun.COM &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
11459484Sgarrett.damore@Sun.COM if (rc != DDI_SUCCESS) {
11469484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
11479484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11489484Sgarrett.damore@Sun.COM }
11499484Sgarrett.damore@Sun.COM
11509484Sgarrett.damore@Sun.COM rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
11519484Sgarrett.damore@Sun.COM port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
11529484Sgarrett.damore@Sun.COM NULL, &cookie, &count);
11539484Sgarrett.damore@Sun.COM if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
11549484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "addr_bind_handle failed");
11559484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11569484Sgarrett.damore@Sun.COM }
11579484Sgarrett.damore@Sun.COM port->bdl_paddr = cookie.dmac_address;
11589484Sgarrett.damore@Sun.COM
11599484Sgarrett.damore@Sun.COM /*
11609484Sgarrett.damore@Sun.COM * Wire up the BD list.
11619484Sgarrett.damore@Sun.COM */
11629484Sgarrett.damore@Sun.COM bdentry = (void *)port->bdl_kaddr;
11639484Sgarrett.damore@Sun.COM for (int i = 0; i < I810_BD_NUMS; i++) {
11649484Sgarrett.damore@Sun.COM
11659484Sgarrett.damore@Sun.COM /* set base address of buffer */
1166*11936Sgdamore@opensolaris.org ddi_put32(port->bdl_acch, &bdentry->buf_base,
1167*11936Sgdamore@opensolaris.org port->samp_paddr);
1168*11936Sgdamore@opensolaris.org /* SiS 7012 counts in bytes, all others in words */
11699484Sgarrett.damore@Sun.COM ddi_put16(port->bdl_acch, &bdentry->buf_len,
1170*11936Sgdamore@opensolaris.org statep->quirk == QUIRK_SIS7012 ? port->samp_size :
1171*11936Sgdamore@opensolaris.org port->samp_size / 2);
1172*11936Sgdamore@opensolaris.org ddi_put16(port->bdl_acch, &bdentry->buf_cmd, BUF_CMD_BUP);
1173*11936Sgdamore@opensolaris.org
11749484Sgarrett.damore@Sun.COM bdentry++;
11759484Sgarrett.damore@Sun.COM }
11769484Sgarrett.damore@Sun.COM (void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
11779484Sgarrett.damore@Sun.COM
11789484Sgarrett.damore@Sun.COM port->engine = audio_engine_alloc(&audio810_engine_ops, caps);
11799484Sgarrett.damore@Sun.COM if (port->engine == NULL) {
11809484Sgarrett.damore@Sun.COM audio_dev_warn(adev, "audio_engine_alloc failed");
11819484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
11829484Sgarrett.damore@Sun.COM }
11839484Sgarrett.damore@Sun.COM
11849484Sgarrett.damore@Sun.COM audio_engine_set_private(port->engine, port);
11859484Sgarrett.damore@Sun.COM audio_dev_add_engine(adev, port->engine);
11869484Sgarrett.damore@Sun.COM
11879484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
11889484Sgarrett.damore@Sun.COM }
11899484Sgarrett.damore@Sun.COM
11909484Sgarrett.damore@Sun.COM /*
11919484Sgarrett.damore@Sun.COM * audio810_free_port()
11929484Sgarrett.damore@Sun.COM *
11939484Sgarrett.damore@Sun.COM * Description:
11949484Sgarrett.damore@Sun.COM * This routine unbinds the DMA cookies, frees the DMA buffers,
11959484Sgarrett.damore@Sun.COM * deallocates the DMA handles.
11969484Sgarrett.damore@Sun.COM *
11979484Sgarrett.damore@Sun.COM * Arguments:
11989484Sgarrett.damore@Sun.COM * audio810_port_t *port The port structure for a DMA engine.
11999484Sgarrett.damore@Sun.COM */
12009484Sgarrett.damore@Sun.COM static void
audio810_free_port(audio810_port_t * port)12019484Sgarrett.damore@Sun.COM audio810_free_port(audio810_port_t *port)
12029484Sgarrett.damore@Sun.COM {
12039484Sgarrett.damore@Sun.COM if (port == NULL)
12049484Sgarrett.damore@Sun.COM return;
12059484Sgarrett.damore@Sun.COM
12069484Sgarrett.damore@Sun.COM if (port->engine) {
12079484Sgarrett.damore@Sun.COM audio_dev_remove_engine(port->statep->adev, port->engine);
12089484Sgarrett.damore@Sun.COM audio_engine_free(port->engine);
12099484Sgarrett.damore@Sun.COM }
12109484Sgarrett.damore@Sun.COM if (port->bdl_paddr) {
12119484Sgarrett.damore@Sun.COM (void) ddi_dma_unbind_handle(port->bdl_dmah);
12129484Sgarrett.damore@Sun.COM }
12139484Sgarrett.damore@Sun.COM if (port->bdl_acch) {
12149484Sgarrett.damore@Sun.COM ddi_dma_mem_free(&port->bdl_acch);
12159484Sgarrett.damore@Sun.COM }
12169484Sgarrett.damore@Sun.COM if (port->bdl_dmah) {
12179484Sgarrett.damore@Sun.COM ddi_dma_free_handle(&port->bdl_dmah);
12189484Sgarrett.damore@Sun.COM }
12199484Sgarrett.damore@Sun.COM if (port->samp_paddr) {
12209484Sgarrett.damore@Sun.COM (void) ddi_dma_unbind_handle(port->samp_dmah);
12219484Sgarrett.damore@Sun.COM }
12229484Sgarrett.damore@Sun.COM if (port->samp_acch) {
12239484Sgarrett.damore@Sun.COM ddi_dma_mem_free(&port->samp_acch);
12249484Sgarrett.damore@Sun.COM }
12259484Sgarrett.damore@Sun.COM if (port->samp_dmah) {
12269484Sgarrett.damore@Sun.COM ddi_dma_free_handle(&port->samp_dmah);
12279484Sgarrett.damore@Sun.COM }
12289484Sgarrett.damore@Sun.COM kmem_free(port, sizeof (*port));
12299484Sgarrett.damore@Sun.COM }
12309484Sgarrett.damore@Sun.COM
12319484Sgarrett.damore@Sun.COM /*
12329484Sgarrett.damore@Sun.COM * audio810_map_regs()
12339484Sgarrett.damore@Sun.COM *
12349484Sgarrett.damore@Sun.COM * Description:
12359484Sgarrett.damore@Sun.COM * The registers are mapped in.
12369484Sgarrett.damore@Sun.COM *
12379484Sgarrett.damore@Sun.COM * Arguments:
12389484Sgarrett.damore@Sun.COM * dev_info_t *dip Pointer to the device's devinfo
12399484Sgarrett.damore@Sun.COM *
12409484Sgarrett.damore@Sun.COM * Returns:
12419484Sgarrett.damore@Sun.COM * DDI_SUCCESS Registers successfully mapped
12429484Sgarrett.damore@Sun.COM * DDI_FAILURE Registers not successfully mapped
12439484Sgarrett.damore@Sun.COM */
12449484Sgarrett.damore@Sun.COM static int
audio810_map_regs(dev_info_t * dip,audio810_state_t * statep)12459484Sgarrett.damore@Sun.COM audio810_map_regs(dev_info_t *dip, audio810_state_t *statep)
12469484Sgarrett.damore@Sun.COM {
12479484Sgarrett.damore@Sun.COM uint_t nregs = 0;
12489484Sgarrett.damore@Sun.COM int *regs_list;
12499484Sgarrett.damore@Sun.COM int i;
12509484Sgarrett.damore@Sun.COM int pciBar1 = 0;
12519484Sgarrett.damore@Sun.COM int pciBar2 = 0;
12529484Sgarrett.damore@Sun.COM int pciBar3 = 0;
12539484Sgarrett.damore@Sun.COM int pciBar4 = 0;
12549484Sgarrett.damore@Sun.COM
12559484Sgarrett.damore@Sun.COM /* check the "reg" property to get the length of memory-mapped I/O */
12569484Sgarrett.damore@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
12579484Sgarrett.damore@Sun.COM "reg", (int **)®s_list, &nregs) != DDI_PROP_SUCCESS) {
12589484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "inquire regs property failed");
12599484Sgarrett.damore@Sun.COM goto error;
12609484Sgarrett.damore@Sun.COM }
12619484Sgarrett.damore@Sun.COM /*
12629484Sgarrett.damore@Sun.COM * Some hardwares, such as Intel ICH0/ICH and AMD 8111, use PCI 0x10
12639484Sgarrett.damore@Sun.COM * and 0x14 BAR separately for native audio mixer BAR and native bus
12649484Sgarrett.damore@Sun.COM * mastering BAR. More advanced hardwares, such as Intel ICH4 and ICH5,
12659484Sgarrett.damore@Sun.COM * support PCI memory BAR, via PCI 0x18 and 0x1C BAR, that allows for
12669484Sgarrett.damore@Sun.COM * higher performance access to the controller register. All features
12679484Sgarrett.damore@Sun.COM * can be accessed via this BAR making the I/O BAR (PCI 0x10 and 0x14
12689484Sgarrett.damore@Sun.COM * BAR) capabilities obsolete. However, these controller maintain the
12699484Sgarrett.damore@Sun.COM * I/O BAR capability to allow for the reuse of legacy code maintaining
12709484Sgarrett.damore@Sun.COM * backward compatibility. The I/O BAR is disabled unless system BIOS
12719484Sgarrett.damore@Sun.COM * enables the simultaneous backward compatible capability on the 0x41
12729484Sgarrett.damore@Sun.COM * register.
12739484Sgarrett.damore@Sun.COM *
12749484Sgarrett.damore@Sun.COM * When I/O BAR is enabled, the value of "reg" property should be like
12759484Sgarrett.damore@Sun.COM * this,
12769484Sgarrett.damore@Sun.COM * phys_hi phys_mid phys_lo size_hi size_lo
12779484Sgarrett.damore@Sun.COM * --------------------------------------------------------
12789484Sgarrett.damore@Sun.COM * 0000fd00 00000000 00000000 00000000 00000000
12799484Sgarrett.damore@Sun.COM * 0100fd10 00000000 00000000 00000000 00000100
12809484Sgarrett.damore@Sun.COM * 0100fd14 00000000 00000000 00000000 00000040
12819484Sgarrett.damore@Sun.COM * 0200fd18 00000000 00000000 00000000 00000200
12829484Sgarrett.damore@Sun.COM * 0200fd1c 00000000 00000000 00000000 00000100
12839484Sgarrett.damore@Sun.COM *
12849484Sgarrett.damore@Sun.COM * When I/O BAR is disabled, the "reg" property of the device node does
12859484Sgarrett.damore@Sun.COM * not consist of the description for the I/O BAR. The following example
12869484Sgarrett.damore@Sun.COM * illustrates the vaule of "reg" property,
12879484Sgarrett.damore@Sun.COM *
12889484Sgarrett.damore@Sun.COM * phys_hi phys_mid phys_lo size_hi size_lo
12899484Sgarrett.damore@Sun.COM * --------------------------------------------------------
12909484Sgarrett.damore@Sun.COM * 0000fd00 00000000 00000000 00000000 00000000
12919484Sgarrett.damore@Sun.COM * 0200fd18 00000000 00000000 00000000 00000200
12929484Sgarrett.damore@Sun.COM * 0200fd1c 00000000 00000000 00000000 00000100
12939484Sgarrett.damore@Sun.COM *
12949484Sgarrett.damore@Sun.COM * If the hardware has memory-mapped I/O access, first try to use
12959484Sgarrett.damore@Sun.COM * this facility, otherwise we will try I/O access.
12969484Sgarrett.damore@Sun.COM */
12979484Sgarrett.damore@Sun.COM for (i = 1; i < nregs/I810_INTS_PER_REG_PROP; i++) {
12989484Sgarrett.damore@Sun.COM switch (regs_list[I810_INTS_PER_REG_PROP * i] & 0x000000ff) {
12999484Sgarrett.damore@Sun.COM case 0x10:
13009484Sgarrett.damore@Sun.COM pciBar1 = i;
13019484Sgarrett.damore@Sun.COM break;
13029484Sgarrett.damore@Sun.COM case 0x14:
13039484Sgarrett.damore@Sun.COM pciBar2 = i;
13049484Sgarrett.damore@Sun.COM break;
13059484Sgarrett.damore@Sun.COM case 0x18:
13069484Sgarrett.damore@Sun.COM pciBar3 = i;
13079484Sgarrett.damore@Sun.COM break;
13089484Sgarrett.damore@Sun.COM case 0x1c:
13099484Sgarrett.damore@Sun.COM pciBar4 = i;
13109484Sgarrett.damore@Sun.COM break;
13119484Sgarrett.damore@Sun.COM default: /* we don't care others */
13129484Sgarrett.damore@Sun.COM break;
13139484Sgarrett.damore@Sun.COM }
13149484Sgarrett.damore@Sun.COM }
13159484Sgarrett.damore@Sun.COM
13169484Sgarrett.damore@Sun.COM if ((pciBar3 != 0) && (pciBar4 != 0)) {
13179484Sgarrett.damore@Sun.COM /* map audio mixer registers */
13189484Sgarrett.damore@Sun.COM if ((ddi_regs_map_setup(dip, pciBar3, &statep->am_regs_base, 0,
13199484Sgarrett.damore@Sun.COM 0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
13209484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev,
13219484Sgarrett.damore@Sun.COM "memory am mapping failed");
13229484Sgarrett.damore@Sun.COM goto error;
13239484Sgarrett.damore@Sun.COM }
13249484Sgarrett.damore@Sun.COM
13259484Sgarrett.damore@Sun.COM /* map bus master register */
13269484Sgarrett.damore@Sun.COM if ((ddi_regs_map_setup(dip, pciBar4, &statep->bm_regs_base, 0,
13279484Sgarrett.damore@Sun.COM 0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
13289484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev,
13299484Sgarrett.damore@Sun.COM "memory bm mapping failed");
13309484Sgarrett.damore@Sun.COM goto error;
13319484Sgarrett.damore@Sun.COM }
13329484Sgarrett.damore@Sun.COM
13339484Sgarrett.damore@Sun.COM } else if ((pciBar1 != 0) && (pciBar2 != 0)) {
13349484Sgarrett.damore@Sun.COM /* map audio mixer registers */
13359484Sgarrett.damore@Sun.COM if ((ddi_regs_map_setup(dip, pciBar1, &statep->am_regs_base, 0,
13369484Sgarrett.damore@Sun.COM 0, &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) {
13379484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "I/O am mapping failed");
13389484Sgarrett.damore@Sun.COM goto error;
13399484Sgarrett.damore@Sun.COM }
13409484Sgarrett.damore@Sun.COM
13419484Sgarrett.damore@Sun.COM /* map bus master register */
13429484Sgarrett.damore@Sun.COM if ((ddi_regs_map_setup(dip, pciBar2, &statep->bm_regs_base, 0,
13439484Sgarrett.damore@Sun.COM 0, &dev_attr, &statep->bm_regs_handle)) != DDI_SUCCESS) {
13449484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "I/O bm mapping failed");
13459484Sgarrett.damore@Sun.COM goto error;
13469484Sgarrett.damore@Sun.COM }
13479484Sgarrett.damore@Sun.COM } else {
13489484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "map_regs() pci BAR error");
13499484Sgarrett.damore@Sun.COM goto error;
13509484Sgarrett.damore@Sun.COM }
13519484Sgarrett.damore@Sun.COM
13529484Sgarrett.damore@Sun.COM ddi_prop_free(regs_list);
13539484Sgarrett.damore@Sun.COM
13549484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
13559484Sgarrett.damore@Sun.COM
13569484Sgarrett.damore@Sun.COM error:
13579484Sgarrett.damore@Sun.COM if (nregs > 0) {
13589484Sgarrett.damore@Sun.COM ddi_prop_free(regs_list);
13599484Sgarrett.damore@Sun.COM }
13609484Sgarrett.damore@Sun.COM audio810_unmap_regs(statep);
13619484Sgarrett.damore@Sun.COM
13629484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
13639484Sgarrett.damore@Sun.COM }
13649484Sgarrett.damore@Sun.COM
13659484Sgarrett.damore@Sun.COM /*
13669484Sgarrett.damore@Sun.COM * audio810_unmap_regs()
13679484Sgarrett.damore@Sun.COM *
13689484Sgarrett.damore@Sun.COM * Description:
13699484Sgarrett.damore@Sun.COM * This routine unmaps control registers.
13709484Sgarrett.damore@Sun.COM *
13719484Sgarrett.damore@Sun.COM * Arguments:
13729484Sgarrett.damore@Sun.COM * audio810_state_t *state The device's state structure
13739484Sgarrett.damore@Sun.COM */
13749484Sgarrett.damore@Sun.COM static void
audio810_unmap_regs(audio810_state_t * statep)13759484Sgarrett.damore@Sun.COM audio810_unmap_regs(audio810_state_t *statep)
13769484Sgarrett.damore@Sun.COM {
13779484Sgarrett.damore@Sun.COM if (statep->bm_regs_handle) {
13789484Sgarrett.damore@Sun.COM ddi_regs_map_free(&statep->bm_regs_handle);
13799484Sgarrett.damore@Sun.COM }
13809484Sgarrett.damore@Sun.COM
13819484Sgarrett.damore@Sun.COM if (statep->am_regs_handle) {
13829484Sgarrett.damore@Sun.COM ddi_regs_map_free(&statep->am_regs_handle);
13839484Sgarrett.damore@Sun.COM }
13849484Sgarrett.damore@Sun.COM }
13859484Sgarrett.damore@Sun.COM
13869484Sgarrett.damore@Sun.COM /*
13879484Sgarrett.damore@Sun.COM * audio810_chip_init()
13889484Sgarrett.damore@Sun.COM *
13899484Sgarrett.damore@Sun.COM * Description:
1390*11936Sgdamore@opensolaris.org * This routine initializes the audio controller.
13919484Sgarrett.damore@Sun.COM *
13929484Sgarrett.damore@Sun.COM * Arguments:
13939484Sgarrett.damore@Sun.COM * audio810_state_t *state The device's state structure
13949484Sgarrett.damore@Sun.COM *
13959484Sgarrett.damore@Sun.COM * Returns:
13969484Sgarrett.damore@Sun.COM * DDI_SUCCESS The hardware was initialized properly
13979484Sgarrett.damore@Sun.COM * DDI_FAILURE The hardware couldn't be initialized properly
13989484Sgarrett.damore@Sun.COM */
13999484Sgarrett.damore@Sun.COM static int
audio810_chip_init(audio810_state_t * statep)14009484Sgarrett.damore@Sun.COM audio810_chip_init(audio810_state_t *statep)
14019484Sgarrett.damore@Sun.COM {
14029484Sgarrett.damore@Sun.COM uint32_t gcr;
14039484Sgarrett.damore@Sun.COM uint32_t gsr;
14049484Sgarrett.damore@Sun.COM uint32_t codec_ready;
14059484Sgarrett.damore@Sun.COM int loop;
14069484Sgarrett.damore@Sun.COM clock_t ticks;
14079484Sgarrett.damore@Sun.COM
14089484Sgarrett.damore@Sun.COM gcr = I810_BM_GET32(I810_REG_GCR);
14099484Sgarrett.damore@Sun.COM ticks = drv_usectohz(100);
14109484Sgarrett.damore@Sun.COM
14119484Sgarrett.damore@Sun.COM /*
14129484Sgarrett.damore@Sun.COM * Clear the channels bits for now. We'll set them later in
14139484Sgarrett.damore@Sun.COM * reset port.
14149484Sgarrett.damore@Sun.COM */
14159484Sgarrett.damore@Sun.COM if (statep->quirk == QUIRK_SIS7012) {
14169484Sgarrett.damore@Sun.COM gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_SIS_CHANNELS_MASK);
14179484Sgarrett.damore@Sun.COM } else {
14189484Sgarrett.damore@Sun.COM gcr &= ~(I810_GCR_ACLINK_OFF | I810_GCR_CHANNELS_MASK);
14199484Sgarrett.damore@Sun.COM }
14209484Sgarrett.damore@Sun.COM
14219484Sgarrett.damore@Sun.COM /*
14229484Sgarrett.damore@Sun.COM * Datasheet(ICH5, document number of Intel: 252751-001):
14239484Sgarrett.damore@Sun.COM * 3.6.5.5(page 37)
14249484Sgarrett.damore@Sun.COM * if reset bit(bit1) is "0", driver must set it
14259484Sgarrett.damore@Sun.COM * to "1" to de-assert the AC_RESET# signal in AC
14269484Sgarrett.damore@Sun.COM * link, thus completing a cold reset. But if the
14279484Sgarrett.damore@Sun.COM * bit is "1", then a warm reset is required.
14289484Sgarrett.damore@Sun.COM */
14299484Sgarrett.damore@Sun.COM gcr |= (gcr & I810_GCR_COLD_RST) == 0 ?
14309484Sgarrett.damore@Sun.COM I810_GCR_COLD_RST:I810_GCR_WARM_RST;
14319484Sgarrett.damore@Sun.COM I810_BM_PUT32(I810_REG_GCR, gcr);
14329484Sgarrett.damore@Sun.COM
14339484Sgarrett.damore@Sun.COM /* according AC'97 spec, wait for codec reset */
14349484Sgarrett.damore@Sun.COM for (loop = 6000; --loop >= 0; ) {
14359484Sgarrett.damore@Sun.COM delay(ticks);
14369484Sgarrett.damore@Sun.COM gcr = I810_BM_GET32(I810_REG_GCR);
14379484Sgarrett.damore@Sun.COM if ((gcr & I810_GCR_WARM_RST) == 0) {
14389484Sgarrett.damore@Sun.COM break;
14399484Sgarrett.damore@Sun.COM }
14409484Sgarrett.damore@Sun.COM }
14419484Sgarrett.damore@Sun.COM
14429484Sgarrett.damore@Sun.COM /* codec reset failed */
14439484Sgarrett.damore@Sun.COM if (loop < 0) {
14449484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "Failed to reset codec");
14459484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
14469484Sgarrett.damore@Sun.COM }
14479484Sgarrett.damore@Sun.COM
14489484Sgarrett.damore@Sun.COM /*
14499484Sgarrett.damore@Sun.COM * Wait for codec ready. The hardware can provide the state of
14509484Sgarrett.damore@Sun.COM * codec ready bit on SDATA_IN[0], SDATA_IN[1] or SDATA_IN[2]
14519484Sgarrett.damore@Sun.COM */
14529484Sgarrett.damore@Sun.COM codec_ready =
14539484Sgarrett.damore@Sun.COM I810_GSR_PRI_READY | I810_GSR_SEC_READY | I810_GSR_TRI_READY;
14549484Sgarrett.damore@Sun.COM for (loop = 7000; --loop >= 0; ) {
14559484Sgarrett.damore@Sun.COM delay(ticks);
14569484Sgarrett.damore@Sun.COM gsr = I810_BM_GET32(I810_REG_GSR);
14579484Sgarrett.damore@Sun.COM if ((gsr & codec_ready) != 0) {
14589484Sgarrett.damore@Sun.COM break;
14599484Sgarrett.damore@Sun.COM }
14609484Sgarrett.damore@Sun.COM }
14619484Sgarrett.damore@Sun.COM if (loop < 0) {
14629484Sgarrett.damore@Sun.COM audio_dev_warn(statep->adev, "No codec ready signal received");
14639484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
14649484Sgarrett.damore@Sun.COM }
14659484Sgarrett.damore@Sun.COM
14669484Sgarrett.damore@Sun.COM /*
14679484Sgarrett.damore@Sun.COM * put the audio controller into quiet state, everything off
14689484Sgarrett.damore@Sun.COM */
14699484Sgarrett.damore@Sun.COM audio810_stop_dma(statep);
14709484Sgarrett.damore@Sun.COM
14719484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
14729484Sgarrett.damore@Sun.COM }
14739484Sgarrett.damore@Sun.COM
14749484Sgarrett.damore@Sun.COM /*
1475*11936Sgdamore@opensolaris.org * audio810_set_channels()
1476*11936Sgdamore@opensolaris.org *
1477*11936Sgdamore@opensolaris.org * Description:
1478*11936Sgdamore@opensolaris.org * This routine initializes the multichannel configuration.
1479*11936Sgdamore@opensolaris.org *
1480*11936Sgdamore@opensolaris.org * Arguments:
1481*11936Sgdamore@opensolaris.org * audio810_state_t *state The device's state structure
1482*11936Sgdamore@opensolaris.org */
1483*11936Sgdamore@opensolaris.org static void
audio810_set_channels(audio810_state_t * statep)1484*11936Sgdamore@opensolaris.org audio810_set_channels(audio810_state_t *statep)
1485*11936Sgdamore@opensolaris.org {
1486*11936Sgdamore@opensolaris.org uint32_t gcr;
1487*11936Sgdamore@opensolaris.org
1488*11936Sgdamore@opensolaris.org /*
1489*11936Sgdamore@opensolaris.org * Configure multi-channel.
1490*11936Sgdamore@opensolaris.org */
1491*11936Sgdamore@opensolaris.org if (statep->quirk == QUIRK_SIS7012) {
1492*11936Sgdamore@opensolaris.org /*
1493*11936Sgdamore@opensolaris.org * SiS 7012 needs its own special multichannel config.
1494*11936Sgdamore@opensolaris.org */
1495*11936Sgdamore@opensolaris.org gcr = I810_BM_GET32(I810_REG_GCR);
1496*11936Sgdamore@opensolaris.org gcr &= ~I810_GCR_SIS_CHANNELS_MASK;
1497*11936Sgdamore@opensolaris.org I810_BM_PUT32(I810_REG_GCR, gcr);
1498*11936Sgdamore@opensolaris.org delay(drv_usectohz(50000)); /* 50 msec */
1499*11936Sgdamore@opensolaris.org
1500*11936Sgdamore@opensolaris.org switch (statep->maxch) {
1501*11936Sgdamore@opensolaris.org case 2:
1502*11936Sgdamore@opensolaris.org gcr |= I810_GCR_SIS_2_CHANNELS;
1503*11936Sgdamore@opensolaris.org break;
1504*11936Sgdamore@opensolaris.org case 4:
1505*11936Sgdamore@opensolaris.org gcr |= I810_GCR_SIS_4_CHANNELS;
1506*11936Sgdamore@opensolaris.org break;
1507*11936Sgdamore@opensolaris.org case 6:
1508*11936Sgdamore@opensolaris.org gcr |= I810_GCR_SIS_6_CHANNELS;
1509*11936Sgdamore@opensolaris.org break;
1510*11936Sgdamore@opensolaris.org }
1511*11936Sgdamore@opensolaris.org I810_BM_PUT32(I810_REG_GCR, gcr);
1512*11936Sgdamore@opensolaris.org delay(drv_usectohz(50000)); /* 50 msec */
1513*11936Sgdamore@opensolaris.org } else {
1514*11936Sgdamore@opensolaris.org
1515*11936Sgdamore@opensolaris.org /*
1516*11936Sgdamore@opensolaris.org * All other devices work the same.
1517*11936Sgdamore@opensolaris.org */
1518*11936Sgdamore@opensolaris.org gcr = I810_BM_GET32(I810_REG_GCR);
1519*11936Sgdamore@opensolaris.org gcr &= ~I810_GCR_CHANNELS_MASK;
1520*11936Sgdamore@opensolaris.org
1521*11936Sgdamore@opensolaris.org I810_BM_PUT32(I810_REG_GCR, gcr);
1522*11936Sgdamore@opensolaris.org delay(drv_usectohz(50000)); /* 50 msec */
1523*11936Sgdamore@opensolaris.org
1524*11936Sgdamore@opensolaris.org switch (statep->maxch) {
1525*11936Sgdamore@opensolaris.org case 2:
1526*11936Sgdamore@opensolaris.org gcr |= I810_GCR_2_CHANNELS;
1527*11936Sgdamore@opensolaris.org break;
1528*11936Sgdamore@opensolaris.org case 4:
1529*11936Sgdamore@opensolaris.org gcr |= I810_GCR_4_CHANNELS;
1530*11936Sgdamore@opensolaris.org break;
1531*11936Sgdamore@opensolaris.org case 6:
1532*11936Sgdamore@opensolaris.org gcr |= I810_GCR_6_CHANNELS;
1533*11936Sgdamore@opensolaris.org break;
1534*11936Sgdamore@opensolaris.org }
1535*11936Sgdamore@opensolaris.org I810_BM_PUT32(I810_REG_GCR, gcr);
1536*11936Sgdamore@opensolaris.org delay(drv_usectohz(50000)); /* 50 msec */
1537*11936Sgdamore@opensolaris.org }
1538*11936Sgdamore@opensolaris.org }
1539*11936Sgdamore@opensolaris.org
1540*11936Sgdamore@opensolaris.org /*
15419484Sgarrett.damore@Sun.COM * audio810_stop_dma()
15429484Sgarrett.damore@Sun.COM *
15439484Sgarrett.damore@Sun.COM * Description:
15449484Sgarrett.damore@Sun.COM * This routine is used to put each DMA engine into the quiet state.
15459484Sgarrett.damore@Sun.COM *
15469484Sgarrett.damore@Sun.COM * Arguments:
15479484Sgarrett.damore@Sun.COM * audio810_state_t *state The device's state structure
15489484Sgarrett.damore@Sun.COM */
15499484Sgarrett.damore@Sun.COM static void
audio810_stop_dma(audio810_state_t * statep)15509484Sgarrett.damore@Sun.COM audio810_stop_dma(audio810_state_t *statep)
15519484Sgarrett.damore@Sun.COM {
15529484Sgarrett.damore@Sun.COM if (statep->bm_regs_handle == NULL) {
15539484Sgarrett.damore@Sun.COM return;
15549484Sgarrett.damore@Sun.COM }
15559484Sgarrett.damore@Sun.COM /* pause bus master (needed for the following reset register) */
15569484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, 0x0);
15579484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, 0x0);
15589484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, 0x0);
15599484Sgarrett.damore@Sun.COM
15609484Sgarrett.damore@Sun.COM /* and then reset the bus master registers for a three DMA engines */
15619484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_PCM_IN + I810_OFFSET_CR, I810_BM_CR_RST);
15629484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_PCM_OUT + I810_OFFSET_CR, I810_BM_CR_RST);
15639484Sgarrett.damore@Sun.COM I810_BM_PUT8(I810_BASE_MIC + I810_OFFSET_CR, I810_BM_CR_RST);
15649484Sgarrett.damore@Sun.COM }
15659484Sgarrett.damore@Sun.COM
15669484Sgarrett.damore@Sun.COM
15679484Sgarrett.damore@Sun.COM /*
15689484Sgarrett.damore@Sun.COM * audio810_codec_sync()
15699484Sgarrett.damore@Sun.COM *
15709484Sgarrett.damore@Sun.COM * Description:
15719484Sgarrett.damore@Sun.COM * Serialize access to the AC97 audio mixer registers.
15729484Sgarrett.damore@Sun.COM *
15739484Sgarrett.damore@Sun.COM * Arguments:
15749484Sgarrett.damore@Sun.COM * audio810_state_t *state The device's state structure
15759484Sgarrett.damore@Sun.COM *
15769484Sgarrett.damore@Sun.COM * Returns:
15779484Sgarrett.damore@Sun.COM * DDI_SUCCESS Ready for an I/O access to the codec
15789484Sgarrett.damore@Sun.COM * DDI_FAILURE An I/O access is currently in progress, can't
15799484Sgarrett.damore@Sun.COM * perform another I/O access.
15809484Sgarrett.damore@Sun.COM */
15819484Sgarrett.damore@Sun.COM static int
audio810_codec_sync(audio810_state_t * statep)15829484Sgarrett.damore@Sun.COM audio810_codec_sync(audio810_state_t *statep)
15839484Sgarrett.damore@Sun.COM {
15849484Sgarrett.damore@Sun.COM int i;
15859484Sgarrett.damore@Sun.COM uint16_t casr;
15869484Sgarrett.damore@Sun.COM
15879484Sgarrett.damore@Sun.COM for (i = 0; i < 300; i++) {
15889484Sgarrett.damore@Sun.COM casr = I810_BM_GET8(I810_REG_CASR);
15899484Sgarrett.damore@Sun.COM if ((casr & 1) == 0) {
15909484Sgarrett.damore@Sun.COM return (DDI_SUCCESS);
15919484Sgarrett.damore@Sun.COM }
15929484Sgarrett.damore@Sun.COM drv_usecwait(10);
15939484Sgarrett.damore@Sun.COM }
15949484Sgarrett.damore@Sun.COM
15959484Sgarrett.damore@Sun.COM return (DDI_FAILURE);
15969484Sgarrett.damore@Sun.COM }
15979484Sgarrett.damore@Sun.COM
15989484Sgarrett.damore@Sun.COM /*
15999484Sgarrett.damore@Sun.COM * audio810_write_ac97()
16009484Sgarrett.damore@Sun.COM *
16019484Sgarrett.damore@Sun.COM * Description:
16029484Sgarrett.damore@Sun.COM * Set the specific AC97 Codec register.
16039484Sgarrett.damore@Sun.COM *
16049484Sgarrett.damore@Sun.COM * Arguments:
16059484Sgarrett.damore@Sun.COM * void *arg The device's state structure
16069484Sgarrett.damore@Sun.COM * uint8_t reg AC97 register number
16079484Sgarrett.damore@Sun.COM * uint16_t data The data want to be set
16089484Sgarrett.damore@Sun.COM */
16099484Sgarrett.damore@Sun.COM static void
audio810_write_ac97(void * arg,uint8_t reg,uint16_t data)16109484Sgarrett.damore@Sun.COM audio810_write_ac97(void *arg, uint8_t reg, uint16_t data)
16119484Sgarrett.damore@Sun.COM {
16129484Sgarrett.damore@Sun.COM audio810_state_t *statep = arg;
16139484Sgarrett.damore@Sun.COM
16149484Sgarrett.damore@Sun.COM if (audio810_codec_sync(statep) == DDI_SUCCESS) {
16159484Sgarrett.damore@Sun.COM I810_AM_PUT16(reg, data);
16169484Sgarrett.damore@Sun.COM }
16179484Sgarrett.damore@Sun.COM
16189484Sgarrett.damore@Sun.COM (void) audio810_read_ac97(statep, reg);
16199484Sgarrett.damore@Sun.COM }
16209484Sgarrett.damore@Sun.COM
16219484Sgarrett.damore@Sun.COM /*
16229484Sgarrett.damore@Sun.COM * audio810_read_ac97()
16239484Sgarrett.damore@Sun.COM *
16249484Sgarrett.damore@Sun.COM * Description:
16259484Sgarrett.damore@Sun.COM * Get the specific AC97 Codec register.
16269484Sgarrett.damore@Sun.COM *
16279484Sgarrett.damore@Sun.COM * Arguments:
16289484Sgarrett.damore@Sun.COM * void *arg The device's state structure
16299484Sgarrett.damore@Sun.COM * uint8_t reg AC97 register number
16309484Sgarrett.damore@Sun.COM *
16319484Sgarrett.damore@Sun.COM * Returns:
16329484Sgarrett.damore@Sun.COM * The register value.
16339484Sgarrett.damore@Sun.COM */
16349484Sgarrett.damore@Sun.COM static uint16_t
audio810_read_ac97(void * arg,uint8_t reg)16359484Sgarrett.damore@Sun.COM audio810_read_ac97(void *arg, uint8_t reg)
16369484Sgarrett.damore@Sun.COM {
16379484Sgarrett.damore@Sun.COM audio810_state_t *statep = arg;
16389484Sgarrett.damore@Sun.COM uint16_t val = 0xffff;
16399484Sgarrett.damore@Sun.COM
16409484Sgarrett.damore@Sun.COM if (audio810_codec_sync(statep) == DDI_SUCCESS) {
16419484Sgarrett.damore@Sun.COM val = I810_AM_GET16(reg);
16429484Sgarrett.damore@Sun.COM }
16439484Sgarrett.damore@Sun.COM return (val);
16449484Sgarrett.damore@Sun.COM }
16459484Sgarrett.damore@Sun.COM
16469484Sgarrett.damore@Sun.COM /*
16479484Sgarrett.damore@Sun.COM * audio810_destroy()
16489484Sgarrett.damore@Sun.COM *
16499484Sgarrett.damore@Sun.COM * Description:
16509484Sgarrett.damore@Sun.COM * This routine releases all resources held by the device instance,
16519484Sgarrett.damore@Sun.COM * as part of either detach or a failure in attach.
16529484Sgarrett.damore@Sun.COM *
16539484Sgarrett.damore@Sun.COM * Arguments:
16549484Sgarrett.damore@Sun.COM * audio810_state_t *state The device soft state.
16559484Sgarrett.damore@Sun.COM */
16569484Sgarrett.damore@Sun.COM void
audio810_destroy(audio810_state_t * statep)16579484Sgarrett.damore@Sun.COM audio810_destroy(audio810_state_t *statep)
16589484Sgarrett.damore@Sun.COM {
16599484Sgarrett.damore@Sun.COM /* stop DMA engines */
16609484Sgarrett.damore@Sun.COM audio810_stop_dma(statep);
16619484Sgarrett.damore@Sun.COM
16629484Sgarrett.damore@Sun.COM for (int i = 0; i < I810_NUM_PORTS; i++) {
16639484Sgarrett.damore@Sun.COM audio810_free_port(statep->ports[i]);
16649484Sgarrett.damore@Sun.COM }
16659484Sgarrett.damore@Sun.COM
16669484Sgarrett.damore@Sun.COM audio810_unmap_regs(statep);
16679484Sgarrett.damore@Sun.COM
16689484Sgarrett.damore@Sun.COM if (statep->ac97)
16699484Sgarrett.damore@Sun.COM ac97_free(statep->ac97);
16709484Sgarrett.damore@Sun.COM
16719484Sgarrett.damore@Sun.COM if (statep->adev)
16729484Sgarrett.damore@Sun.COM audio_dev_free(statep->adev);
16739484Sgarrett.damore@Sun.COM
16749484Sgarrett.damore@Sun.COM kmem_free(statep, sizeof (*statep));
16759484Sgarrett.damore@Sun.COM }
1676