188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore * CDDL HEADER START
388447a05SGarrett D'Amore *
488447a05SGarrett D'Amore * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore *
888447a05SGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore * and limitations under the License.
1288447a05SGarrett D'Amore *
1388447a05SGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore *
1988447a05SGarrett D'Amore * CDDL HEADER END
2088447a05SGarrett D'Amore */
2188447a05SGarrett D'Amore /*
22d8a7fe16SGarrett D'Amore * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2388447a05SGarrett D'Amore * Use is subject to license terms.
2488447a05SGarrett D'Amore */
2588447a05SGarrett D'Amore
2688447a05SGarrett D'Amore /*
2788447a05SGarrett D'Amore * audioixp Audio Driver
2888447a05SGarrett D'Amore *
2988447a05SGarrett D'Amore * This driver supports audio hardware integrated in ATI IXP400 chipset.
3088447a05SGarrett D'Amore *
3188447a05SGarrett D'Amore * The IXP400 audio core is an AC'97 controller, which has independent
3288447a05SGarrett D'Amore * channels for PCM in, PCM out. The AC'97 controller is a PCI bus master
3388447a05SGarrett D'Amore * with scatter/gather support. Each channel has a DMA engine. Currently,
3488447a05SGarrett D'Amore * we use only the PCM in and PCM out channels. Each DMA engine uses one
3588447a05SGarrett D'Amore * buffer descriptor list. Each entry contains a pointer to a data buffer,
3688447a05SGarrett D'Amore * status, length of the buffer being pointed to and the pointer to the next
3788447a05SGarrett D'Amore * entry. Length of the buffer is in number of bytes. Interrupt will be
3888447a05SGarrett D'Amore * triggered each time a entry is processed by hardware.
3988447a05SGarrett D'Amore *
4088447a05SGarrett D'Amore * System power management is not yet supported by the driver.
4188447a05SGarrett D'Amore *
4288447a05SGarrett D'Amore * NOTE:
4388447a05SGarrett D'Amore * This driver depends on the misc/ac97 and drv/audio modules being
4488447a05SGarrett D'Amore * loaded first.
4588447a05SGarrett D'Amore */
4688447a05SGarrett D'Amore #include <sys/types.h>
4788447a05SGarrett D'Amore #include <sys/modctl.h>
4888447a05SGarrett D'Amore #include <sys/kmem.h>
4988447a05SGarrett D'Amore #include <sys/conf.h>
5088447a05SGarrett D'Amore #include <sys/ddi.h>
5188447a05SGarrett D'Amore #include <sys/sunddi.h>
5288447a05SGarrett D'Amore #include <sys/pci.h>
5388447a05SGarrett D'Amore #include <sys/note.h>
5488447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
5588447a05SGarrett D'Amore #include <sys/audio/ac97.h>
5688447a05SGarrett D'Amore #include "audioixp.h"
5788447a05SGarrett D'Amore
5888447a05SGarrett D'Amore /*
5988447a05SGarrett D'Amore * Module linkage routines for the kernel
6088447a05SGarrett D'Amore */
6188447a05SGarrett D'Amore static int audioixp_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
6288447a05SGarrett D'Amore static int audioixp_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
6388447a05SGarrett D'Amore static int audioixp_quiesce(dev_info_t *);
6488447a05SGarrett D'Amore static int audioixp_resume(dev_info_t *);
6588447a05SGarrett D'Amore static int audioixp_suspend(dev_info_t *);
6688447a05SGarrett D'Amore
6788447a05SGarrett D'Amore /*
6888447a05SGarrett D'Amore * Entry point routine prototypes
6988447a05SGarrett D'Amore */
70*68c47f65SGarrett D'Amore static int audioixp_open(void *, int, unsigned *, caddr_t *);
7188447a05SGarrett D'Amore static void audioixp_close(void *);
7288447a05SGarrett D'Amore static int audioixp_start(void *);
7388447a05SGarrett D'Amore static void audioixp_stop(void *);
7488447a05SGarrett D'Amore static int audioixp_format(void *);
7588447a05SGarrett D'Amore static int audioixp_channels(void *);
7688447a05SGarrett D'Amore static int audioixp_rate(void *);
7788447a05SGarrett D'Amore static uint64_t audioixp_count(void *);
7888447a05SGarrett D'Amore static void audioixp_sync(void *, unsigned);
7988447a05SGarrett D'Amore
8088447a05SGarrett D'Amore static audio_engine_ops_t audioixp_engine_ops = {
8188447a05SGarrett D'Amore AUDIO_ENGINE_VERSION,
8288447a05SGarrett D'Amore audioixp_open,
8388447a05SGarrett D'Amore audioixp_close,
8488447a05SGarrett D'Amore audioixp_start,
8588447a05SGarrett D'Amore audioixp_stop,
8688447a05SGarrett D'Amore audioixp_count,
8788447a05SGarrett D'Amore audioixp_format,
8888447a05SGarrett D'Amore audioixp_channels,
8988447a05SGarrett D'Amore audioixp_rate,
9088447a05SGarrett D'Amore audioixp_sync,
91f9ead4a5SGarrett D'Amore NULL,
92f9ead4a5SGarrett D'Amore NULL,
93f9ead4a5SGarrett D'Amore NULL
9488447a05SGarrett D'Amore };
9588447a05SGarrett D'Amore
96d8a7fe16SGarrett D'Amore /*
97d8a7fe16SGarrett D'Amore * We drive audioixp in stereo only, so we don't want to display controls
98d8a7fe16SGarrett D'Amore * that are used for multichannel codecs. Note that this multichannel
99d8a7fe16SGarrett D'Amore * configuration limitation is a problem for audioixp devices.
100d8a7fe16SGarrett D'Amore */
101d8a7fe16SGarrett D'Amore const char *audioixp_remove_ac97[] = {
102d8a7fe16SGarrett D'Amore AUDIO_CTRL_ID_CENTER,
103d8a7fe16SGarrett D'Amore AUDIO_CTRL_ID_LFE,
104d8a7fe16SGarrett D'Amore AUDIO_CTRL_ID_SURROUND,
105d8a7fe16SGarrett D'Amore AUDIO_CTRL_ID_JACK1,
106d8a7fe16SGarrett D'Amore AUDIO_CTRL_ID_JACK2,
107d8a7fe16SGarrett D'Amore };
10888447a05SGarrett D'Amore
10988447a05SGarrett D'Amore /*
11088447a05SGarrett D'Amore * Local Routine Prototypes
11188447a05SGarrett D'Amore */
11288447a05SGarrett D'Amore static int audioixp_attach(dev_info_t *);
11388447a05SGarrett D'Amore static int audioixp_detach(dev_info_t *);
11488447a05SGarrett D'Amore static int audioixp_alloc_port(audioixp_state_t *, int);
11588447a05SGarrett D'Amore static void audioixp_update_port(audioixp_port_t *);
11688447a05SGarrett D'Amore
11788447a05SGarrett D'Amore static int audioixp_codec_sync(audioixp_state_t *);
11888447a05SGarrett D'Amore static void audioixp_wr97(void *, uint8_t, uint16_t);
11988447a05SGarrett D'Amore static uint16_t audioixp_rd97(void *, uint8_t);
12088447a05SGarrett D'Amore static int audioixp_reset_ac97(audioixp_state_t *);
12188447a05SGarrett D'Amore static int audioixp_map_regs(audioixp_state_t *);
12288447a05SGarrett D'Amore static void audioixp_unmap_regs(audioixp_state_t *);
12388447a05SGarrett D'Amore static int audioixp_chip_init(audioixp_state_t *);
12488447a05SGarrett D'Amore static void audioixp_destroy(audioixp_state_t *);
12588447a05SGarrett D'Amore
12688447a05SGarrett D'Amore /*
12788447a05SGarrett D'Amore * Global variables, but used only by this file.
12888447a05SGarrett D'Amore */
12988447a05SGarrett D'Amore
13088447a05SGarrett D'Amore /*
13188447a05SGarrett D'Amore * DDI Structures
13288447a05SGarrett D'Amore */
13388447a05SGarrett D'Amore
13488447a05SGarrett D'Amore /* Device operations structure */
13588447a05SGarrett D'Amore static struct dev_ops audioixp_dev_ops = {
13688447a05SGarrett D'Amore DEVO_REV, /* devo_rev */
13788447a05SGarrett D'Amore 0, /* devo_refcnt */
13888447a05SGarrett D'Amore NULL, /* devo_getinfo */
13988447a05SGarrett D'Amore nulldev, /* devo_identify - obsolete */
14088447a05SGarrett D'Amore nulldev, /* devo_probe */
14188447a05SGarrett D'Amore audioixp_ddi_attach, /* devo_attach */
14288447a05SGarrett D'Amore audioixp_ddi_detach, /* devo_detach */
14388447a05SGarrett D'Amore nodev, /* devo_reset */
14488447a05SGarrett D'Amore NULL, /* devi_cb_ops */
14588447a05SGarrett D'Amore NULL, /* devo_bus_ops */
14688447a05SGarrett D'Amore NULL, /* devo_power */
14788447a05SGarrett D'Amore audioixp_quiesce, /* devo_quiesce */
14888447a05SGarrett D'Amore };
14988447a05SGarrett D'Amore
15088447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
15188447a05SGarrett D'Amore static struct modldrv audioixp_modldrv = {
15288447a05SGarrett D'Amore &mod_driverops, /* drv_modops */
15388447a05SGarrett D'Amore IXP_MOD_NAME, /* drv_linkinfo */
15488447a05SGarrett D'Amore &audioixp_dev_ops, /* drv_dev_ops */
15588447a05SGarrett D'Amore };
15688447a05SGarrett D'Amore
15788447a05SGarrett D'Amore /* Module linkage structure */
15888447a05SGarrett D'Amore static struct modlinkage audioixp_modlinkage = {
15988447a05SGarrett D'Amore MODREV_1, /* ml_rev */
16088447a05SGarrett D'Amore (void *)&audioixp_modldrv, /* ml_linkage */
16188447a05SGarrett D'Amore NULL /* NULL terminates the list */
16288447a05SGarrett D'Amore };
16388447a05SGarrett D'Amore
16488447a05SGarrett D'Amore /*
16588447a05SGarrett D'Amore * device access attributes for register mapping
16688447a05SGarrett D'Amore */
16788447a05SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
16888447a05SGarrett D'Amore DDI_DEVICE_ATTR_V0,
16988447a05SGarrett D'Amore DDI_STRUCTURE_LE_ACC,
17088447a05SGarrett D'Amore DDI_STRICTORDER_ACC
17188447a05SGarrett D'Amore };
17288447a05SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
17388447a05SGarrett D'Amore DDI_DEVICE_ATTR_V0,
17488447a05SGarrett D'Amore DDI_NEVERSWAP_ACC,
17588447a05SGarrett D'Amore DDI_STRICTORDER_ACC
17688447a05SGarrett D'Amore };
17788447a05SGarrett D'Amore
17888447a05SGarrett D'Amore /*
17988447a05SGarrett D'Amore * DMA attributes of buffer descriptor list
18088447a05SGarrett D'Amore */
18188447a05SGarrett D'Amore static ddi_dma_attr_t bdlist_dma_attr = {
18288447a05SGarrett D'Amore DMA_ATTR_V0, /* version */
18388447a05SGarrett D'Amore 0, /* addr_lo */
18488447a05SGarrett D'Amore 0xffffffff, /* addr_hi */
18588447a05SGarrett D'Amore 0x0000ffff, /* count_max */
18688447a05SGarrett D'Amore 8, /* align, BDL must be aligned on a 8-byte boundary */
18788447a05SGarrett D'Amore 0x3c, /* burstsize */
18888447a05SGarrett D'Amore 8, /* minxfer, set to the size of a BDlist entry */
18988447a05SGarrett D'Amore 0x0000ffff, /* maxxfer */
19088447a05SGarrett D'Amore 0x00000fff, /* seg, set to the RAM pagesize of intel platform */
19188447a05SGarrett D'Amore 1, /* sgllen, there's no scatter-gather list */
19288447a05SGarrett D'Amore 8, /* granular, set to the value of minxfer */
19388447a05SGarrett D'Amore 0 /* flags, use virtual address */
19488447a05SGarrett D'Amore };
19588447a05SGarrett D'Amore
19688447a05SGarrett D'Amore /*
19788447a05SGarrett D'Amore * DMA attributes of buffers to be used to receive/send audio data
19888447a05SGarrett D'Amore */
19988447a05SGarrett D'Amore static ddi_dma_attr_t sample_buf_dma_attr = {
20088447a05SGarrett D'Amore DMA_ATTR_V0,
20188447a05SGarrett D'Amore 0, /* addr_lo */
20288447a05SGarrett D'Amore 0xffffffff, /* addr_hi */
20388447a05SGarrett D'Amore 0x0001fffe, /* count_max */
20488447a05SGarrett D'Amore 4, /* align, data buffer is aligned on a 2-byte boundary */
20588447a05SGarrett D'Amore 0x3c, /* burstsize */
20688447a05SGarrett D'Amore 4, /* minxfer, set to the size of a sample data */
20788447a05SGarrett D'Amore 0x0001ffff, /* maxxfer */
20888447a05SGarrett D'Amore 0x0001ffff, /* seg */
20988447a05SGarrett D'Amore 1, /* sgllen, no scatter-gather */
21088447a05SGarrett D'Amore 4, /* granular, set to the value of minxfer */
21188447a05SGarrett D'Amore 0, /* flags, use virtual address */
21288447a05SGarrett D'Amore };
21388447a05SGarrett D'Amore
21488447a05SGarrett D'Amore /*
21588447a05SGarrett D'Amore * _init()
21688447a05SGarrett D'Amore *
21788447a05SGarrett D'Amore * Description:
21888447a05SGarrett D'Amore * Driver initialization, called when driver is first loaded.
21988447a05SGarrett D'Amore * This is how access is initially given to all the static structures.
22088447a05SGarrett D'Amore *
22188447a05SGarrett D'Amore * Arguments:
22288447a05SGarrett D'Amore * None
22388447a05SGarrett D'Amore *
22488447a05SGarrett D'Amore * Returns:
22588447a05SGarrett D'Amore * ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
22688447a05SGarrett D'Amore * mod_install() status, see mod_install(9f)
22788447a05SGarrett D'Amore */
22888447a05SGarrett D'Amore int
_init(void)22988447a05SGarrett D'Amore _init(void)
23088447a05SGarrett D'Amore {
23188447a05SGarrett D'Amore int error;
23288447a05SGarrett D'Amore
23388447a05SGarrett D'Amore audio_init_ops(&audioixp_dev_ops, IXP_NAME);
23488447a05SGarrett D'Amore
23588447a05SGarrett D'Amore if ((error = mod_install(&audioixp_modlinkage)) != 0) {
23688447a05SGarrett D'Amore audio_fini_ops(&audioixp_dev_ops);
23788447a05SGarrett D'Amore }
23888447a05SGarrett D'Amore
23988447a05SGarrett D'Amore return (error);
24088447a05SGarrett D'Amore }
24188447a05SGarrett D'Amore
24288447a05SGarrett D'Amore /*
24388447a05SGarrett D'Amore * _fini()
24488447a05SGarrett D'Amore *
24588447a05SGarrett D'Amore * Description:
24688447a05SGarrett D'Amore * Module de-initialization, called when the driver is to be unloaded.
24788447a05SGarrett D'Amore *
24888447a05SGarrett D'Amore * Arguments:
24988447a05SGarrett D'Amore * None
25088447a05SGarrett D'Amore *
25188447a05SGarrett D'Amore * Returns:
25288447a05SGarrett D'Amore * mod_remove() status, see mod_remove(9f)
25388447a05SGarrett D'Amore */
25488447a05SGarrett D'Amore int
_fini(void)25588447a05SGarrett D'Amore _fini(void)
25688447a05SGarrett D'Amore {
25788447a05SGarrett D'Amore int error;
25888447a05SGarrett D'Amore
25988447a05SGarrett D'Amore if ((error = mod_remove(&audioixp_modlinkage)) != 0) {
26088447a05SGarrett D'Amore return (error);
26188447a05SGarrett D'Amore }
26288447a05SGarrett D'Amore
26388447a05SGarrett D'Amore audio_fini_ops(&audioixp_dev_ops);
26488447a05SGarrett D'Amore
26588447a05SGarrett D'Amore return (0);
26688447a05SGarrett D'Amore }
26788447a05SGarrett D'Amore
26888447a05SGarrett D'Amore /*
26988447a05SGarrett D'Amore * _info()
27088447a05SGarrett D'Amore *
27188447a05SGarrett D'Amore * Description:
27288447a05SGarrett D'Amore * Module information, returns information about the driver.
27388447a05SGarrett D'Amore *
27488447a05SGarrett D'Amore * Arguments:
27588447a05SGarrett D'Amore * modinfo *modinfop Pointer to the opaque modinfo structure
27688447a05SGarrett D'Amore *
27788447a05SGarrett D'Amore * Returns:
27888447a05SGarrett D'Amore * mod_info() status, see mod_info(9f)
27988447a05SGarrett D'Amore */
28088447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)28188447a05SGarrett D'Amore _info(struct modinfo *modinfop)
28288447a05SGarrett D'Amore {
28388447a05SGarrett D'Amore return (mod_info(&audioixp_modlinkage, modinfop));
28488447a05SGarrett D'Amore }
28588447a05SGarrett D'Amore
28688447a05SGarrett D'Amore
28788447a05SGarrett D'Amore /* ******************* Driver Entry Points ********************************* */
28888447a05SGarrett D'Amore
28988447a05SGarrett D'Amore /*
29088447a05SGarrett D'Amore * audioixp_ddi_attach()
29188447a05SGarrett D'Amore *
29288447a05SGarrett D'Amore * Description:
29388447a05SGarrett D'Amore * Attach an instance of the audioixp driver.
29488447a05SGarrett D'Amore *
29588447a05SGarrett D'Amore * Arguments:
29688447a05SGarrett D'Amore * dev_info_t *dip Pointer to the device's dev_info struct
29788447a05SGarrett D'Amore * ddi_attach_cmd_t cmd Attach command
29888447a05SGarrett D'Amore *
29988447a05SGarrett D'Amore * Returns:
30088447a05SGarrett D'Amore * DDI_SUCCESS The driver was initialized properly
30188447a05SGarrett D'Amore * DDI_FAILURE The driver couldn't be initialized properly
30288447a05SGarrett D'Amore */
30388447a05SGarrett D'Amore static int
audioixp_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30488447a05SGarrett D'Amore audioixp_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30588447a05SGarrett D'Amore {
30688447a05SGarrett D'Amore switch (cmd) {
30788447a05SGarrett D'Amore case DDI_ATTACH:
30888447a05SGarrett D'Amore return (audioixp_attach(dip));
30988447a05SGarrett D'Amore
31088447a05SGarrett D'Amore /*
31188447a05SGarrett D'Amore * now, no suspend/resume supported. we'll do it in the future.
31288447a05SGarrett D'Amore */
31388447a05SGarrett D'Amore case DDI_RESUME:
31488447a05SGarrett D'Amore return (audioixp_resume(dip));
31588447a05SGarrett D'Amore default:
31688447a05SGarrett D'Amore return (DDI_FAILURE);
31788447a05SGarrett D'Amore }
31888447a05SGarrett D'Amore }
31988447a05SGarrett D'Amore
32088447a05SGarrett D'Amore /*
32188447a05SGarrett D'Amore * audioixp_ddi_detach()
32288447a05SGarrett D'Amore *
32388447a05SGarrett D'Amore * Description:
32488447a05SGarrett D'Amore * Detach an instance of the audioixp driver.
32588447a05SGarrett D'Amore *
32688447a05SGarrett D'Amore * Arguments:
32788447a05SGarrett D'Amore * dev_info_t *dip Pointer to the device's dev_info struct
32888447a05SGarrett D'Amore * ddi_detach_cmd_t cmd Detach command
32988447a05SGarrett D'Amore *
33088447a05SGarrett D'Amore * Returns:
33188447a05SGarrett D'Amore * DDI_SUCCESS The driver was detached
33288447a05SGarrett D'Amore * DDI_FAILURE The driver couldn't be detached
33388447a05SGarrett D'Amore */
33488447a05SGarrett D'Amore static int
audioixp_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)33588447a05SGarrett D'Amore audioixp_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
33688447a05SGarrett D'Amore {
33788447a05SGarrett D'Amore switch (cmd) {
33888447a05SGarrett D'Amore case DDI_DETACH:
33988447a05SGarrett D'Amore return (audioixp_detach(dip));
34088447a05SGarrett D'Amore
34188447a05SGarrett D'Amore /*
34288447a05SGarrett D'Amore * now, no suspend/resume supported. we'll do it in the future.
34388447a05SGarrett D'Amore */
34488447a05SGarrett D'Amore case DDI_SUSPEND:
34588447a05SGarrett D'Amore return (audioixp_suspend(dip));
34688447a05SGarrett D'Amore
34788447a05SGarrett D'Amore default:
34888447a05SGarrett D'Amore return (DDI_FAILURE);
34988447a05SGarrett D'Amore }
35088447a05SGarrett D'Amore }
35188447a05SGarrett D'Amore
35288447a05SGarrett D'Amore /*
35388447a05SGarrett D'Amore * quiesce(9E) entry point.
35488447a05SGarrett D'Amore *
35588447a05SGarrett D'Amore * This function is called when the system is single-threaded at high
35688447a05SGarrett D'Amore * PIL with preemption disabled. Therefore, this function must not be blocked.
35788447a05SGarrett D'Amore *
35888447a05SGarrett D'Amore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
35988447a05SGarrett D'Amore * DDI_FAILURE indicates an error condition and should almost never happen.
36088447a05SGarrett D'Amore */
36188447a05SGarrett D'Amore static int
audioixp_quiesce(dev_info_t * dip)36288447a05SGarrett D'Amore audioixp_quiesce(dev_info_t *dip)
36388447a05SGarrett D'Amore {
36488447a05SGarrett D'Amore audioixp_state_t *statep;
36588447a05SGarrett D'Amore
36688447a05SGarrett D'Amore statep = ddi_get_driver_private(dip);
36788447a05SGarrett D'Amore ASSERT(statep != NULL);
36888447a05SGarrett D'Amore
36988447a05SGarrett D'Amore /* stop DMA engines */
370*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
371*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
372*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
373*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
37488447a05SGarrett D'Amore
37588447a05SGarrett D'Amore return (DDI_SUCCESS);
37688447a05SGarrett D'Amore }
37788447a05SGarrett D'Amore
37888447a05SGarrett D'Amore static int
audioixp_suspend(dev_info_t * dip)37988447a05SGarrett D'Amore audioixp_suspend(dev_info_t *dip)
38088447a05SGarrett D'Amore {
38188447a05SGarrett D'Amore audioixp_state_t *statep;
38288447a05SGarrett D'Amore
38388447a05SGarrett D'Amore statep = ddi_get_driver_private(dip);
38488447a05SGarrett D'Amore ASSERT(statep != NULL);
38588447a05SGarrett D'Amore
386*68c47f65SGarrett D'Amore audio_dev_suspend(statep->adev);
38788447a05SGarrett D'Amore
38888447a05SGarrett D'Amore return (DDI_SUCCESS);
38988447a05SGarrett D'Amore }
39088447a05SGarrett D'Amore
39188447a05SGarrett D'Amore static int
audioixp_resume(dev_info_t * dip)39288447a05SGarrett D'Amore audioixp_resume(dev_info_t *dip)
39388447a05SGarrett D'Amore {
39488447a05SGarrett D'Amore audioixp_state_t *statep;
39588447a05SGarrett D'Amore
39688447a05SGarrett D'Amore statep = ddi_get_driver_private(dip);
39788447a05SGarrett D'Amore ASSERT(statep != NULL);
39888447a05SGarrett D'Amore
39988447a05SGarrett D'Amore if (audioixp_chip_init(statep) != DDI_SUCCESS) {
400*68c47f65SGarrett D'Amore audio_dev_warn(statep->adev, "DDI_RESUME failed to init chip");
40188447a05SGarrett D'Amore return (DDI_SUCCESS);
40288447a05SGarrett D'Amore }
40388447a05SGarrett D'Amore
404*68c47f65SGarrett D'Amore ac97_reset(statep->ac97);
405*68c47f65SGarrett D'Amore audio_dev_resume(statep->adev);
40688447a05SGarrett D'Amore
40788447a05SGarrett D'Amore return (DDI_SUCCESS);
40888447a05SGarrett D'Amore }
40988447a05SGarrett D'Amore
41088447a05SGarrett D'Amore /*
41188447a05SGarrett D'Amore * audioixp_open()
41288447a05SGarrett D'Amore *
41388447a05SGarrett D'Amore * Description:
41488447a05SGarrett D'Amore * Opens a DMA engine for use.
41588447a05SGarrett D'Amore *
41688447a05SGarrett D'Amore * Arguments:
41788447a05SGarrett D'Amore * void *arg The DMA engine to set up
41888447a05SGarrett D'Amore * int flag Open flags
419*68c47f65SGarrett D'Amore * unsigned *nframesp Receives number of frames
42088447a05SGarrett D'Amore * caddr_t *bufp Receives kernel data buffer
42188447a05SGarrett D'Amore *
42288447a05SGarrett D'Amore * Returns:
42388447a05SGarrett D'Amore * 0 on success
42488447a05SGarrett D'Amore * errno on failure
42588447a05SGarrett D'Amore */
42688447a05SGarrett D'Amore static int
audioixp_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)427*68c47f65SGarrett D'Amore audioixp_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
42888447a05SGarrett D'Amore {
42988447a05SGarrett D'Amore audioixp_port_t *port = arg;
43088447a05SGarrett D'Amore
43188447a05SGarrett D'Amore _NOTE(ARGUNUSED(flag));
43288447a05SGarrett D'Amore
43388447a05SGarrett D'Amore port->started = B_FALSE;
43488447a05SGarrett D'Amore port->count = 0;
43588447a05SGarrett D'Amore port->offset = 0;
436*68c47f65SGarrett D'Amore *nframesp = port->nframes;
43788447a05SGarrett D'Amore *bufp = port->samp_kaddr;
43888447a05SGarrett D'Amore
43988447a05SGarrett D'Amore return (0);
44088447a05SGarrett D'Amore }
44188447a05SGarrett D'Amore
44288447a05SGarrett D'Amore /*
44388447a05SGarrett D'Amore * audioixp_close()
44488447a05SGarrett D'Amore *
44588447a05SGarrett D'Amore * Description:
44688447a05SGarrett D'Amore * Closes an audio DMA engine that was previously opened. Since
44788447a05SGarrett D'Amore * nobody is using it, we take this opportunity to possibly power
44888447a05SGarrett D'Amore * down the entire device.
44988447a05SGarrett D'Amore *
45088447a05SGarrett D'Amore * Arguments:
45188447a05SGarrett D'Amore * void *arg The DMA engine to shut down
45288447a05SGarrett D'Amore */
45388447a05SGarrett D'Amore static void
audioixp_close(void * arg)45488447a05SGarrett D'Amore audioixp_close(void *arg)
45588447a05SGarrett D'Amore {
456*68c47f65SGarrett D'Amore _NOTE(ARGUNUSED(arg));
45788447a05SGarrett D'Amore }
45888447a05SGarrett D'Amore
45988447a05SGarrett D'Amore /*
46088447a05SGarrett D'Amore * audioixp_stop()
46188447a05SGarrett D'Amore *
46288447a05SGarrett D'Amore * Description:
46388447a05SGarrett D'Amore * This is called by the framework to stop a port that is
46488447a05SGarrett D'Amore * transferring data.
46588447a05SGarrett D'Amore *
46688447a05SGarrett D'Amore * Arguments:
46788447a05SGarrett D'Amore * void *arg The DMA engine to stop
46888447a05SGarrett D'Amore */
46988447a05SGarrett D'Amore static void
audioixp_stop(void * arg)47088447a05SGarrett D'Amore audioixp_stop(void *arg)
47188447a05SGarrett D'Amore {
47288447a05SGarrett D'Amore audioixp_port_t *port = arg;
47388447a05SGarrett D'Amore audioixp_state_t *statep = port->statep;
47488447a05SGarrett D'Amore
47588447a05SGarrett D'Amore mutex_enter(&statep->inst_lock);
476*68c47f65SGarrett D'Amore if (port->num == IXP_REC) {
477*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
478*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
479*68c47f65SGarrett D'Amore } else {
480*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
481*68c47f65SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
48288447a05SGarrett D'Amore }
48388447a05SGarrett D'Amore mutex_exit(&statep->inst_lock);
48488447a05SGarrett D'Amore }
48588447a05SGarrett D'Amore
48688447a05SGarrett D'Amore /*
48788447a05SGarrett D'Amore * audioixp_start()
48888447a05SGarrett D'Amore *
48988447a05SGarrett D'Amore * Description:
49088447a05SGarrett D'Amore * This is called by the framework to start a port transferring data.
49188447a05SGarrett D'Amore *
49288447a05SGarrett D'Amore * Arguments:
49388447a05SGarrett D'Amore * void *arg The DMA engine to start
49488447a05SGarrett D'Amore *
49588447a05SGarrett D'Amore * Returns:
49688447a05SGarrett D'Amore * 0 on success (never fails, errno if it did)
49788447a05SGarrett D'Amore */
49888447a05SGarrett D'Amore static int
audioixp_start(void * arg)49988447a05SGarrett D'Amore audioixp_start(void *arg)
50088447a05SGarrett D'Amore {
50188447a05SGarrett D'Amore audioixp_port_t *port = arg;
50288447a05SGarrett D'Amore audioixp_state_t *statep = port->statep;
50388447a05SGarrett D'Amore
50488447a05SGarrett D'Amore mutex_enter(&statep->inst_lock);
505*68c47f65SGarrett D'Amore
506*68c47f65SGarrett D'Amore port->offset = 0;
507*68c47f65SGarrett D'Amore
508*68c47f65SGarrett D'Amore if (port->num == IXP_REC) {
509*68c47f65SGarrett D'Amore PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN);
510*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_IN);
511*68c47f65SGarrett D'Amore
512*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
513*68c47f65SGarrett D'Amore PUT32(IXP_AUDIO_IN_DMA_LINK_P,
514*68c47f65SGarrett D'Amore port->bdl_paddr | IXP_AUDIO_IN_DMA_LINK_P_EN);
515*68c47f65SGarrett D'Amore
516*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN);
517*68c47f65SGarrett D'Amore } else {
518*68c47f65SGarrett D'Amore uint32_t slot = GET32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD);
519*68c47f65SGarrett D'Amore PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_OUT);
520*68c47f65SGarrett D'Amore /* clear all slots */
521*68c47f65SGarrett D'Amore slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_3 |
522*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_4 |
523*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_5 |
524*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_6 |
525*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_7 |
526*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_8 |
527*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_9 |
528*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_10 |
529*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_11 |
530*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_12);
531*68c47f65SGarrett D'Amore /* enable AC'97 output slots (depending on output channels) */
532*68c47f65SGarrett D'Amore slot |= IXP_AUDIO_OUT_DMA_SLOT_3 |
533*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_4;
534*68c47f65SGarrett D'Amore if (port->nchan >= 4) {
535*68c47f65SGarrett D'Amore slot |= IXP_AUDIO_OUT_DMA_SLOT_6 |
536*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_9;
537*68c47f65SGarrett D'Amore }
538*68c47f65SGarrett D'Amore if (port->nchan >= 6) {
539*68c47f65SGarrett D'Amore slot |= IXP_AUDIO_OUT_DMA_SLOT_7 |
540*68c47f65SGarrett D'Amore IXP_AUDIO_OUT_DMA_SLOT_8;
541*68c47f65SGarrett D'Amore }
542*68c47f65SGarrett D'Amore
543*68c47f65SGarrett D'Amore PUT32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD, slot);
544*68c47f65SGarrett D'Amore
545*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_OUT);
546*68c47f65SGarrett D'Amore
547*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
548*68c47f65SGarrett D'Amore PUT32(IXP_AUDIO_OUT_DMA_LINK_P,
549*68c47f65SGarrett D'Amore port->bdl_paddr | IXP_AUDIO_OUT_DMA_LINK_P_EN);
550*68c47f65SGarrett D'Amore
551*68c47f65SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT);
55288447a05SGarrett D'Amore }
55388447a05SGarrett D'Amore mutex_exit(&statep->inst_lock);
55488447a05SGarrett D'Amore return (0);
55588447a05SGarrett D'Amore }
55688447a05SGarrett D'Amore
55788447a05SGarrett D'Amore /*
55888447a05SGarrett D'Amore * audioixp_format()
55988447a05SGarrett D'Amore *
56088447a05SGarrett D'Amore * Description:
56188447a05SGarrett D'Amore * This is called by the framework to query the format for the device.
56288447a05SGarrett D'Amore *
56388447a05SGarrett D'Amore * Arguments:
56488447a05SGarrett D'Amore * void *arg The DMA engine to query
56588447a05SGarrett D'Amore *
56688447a05SGarrett D'Amore * Returns:
56788447a05SGarrett D'Amore * AUDIO_FORMAT_S16_LE
56888447a05SGarrett D'Amore */
56988447a05SGarrett D'Amore static int
audioixp_format(void * arg)57088447a05SGarrett D'Amore audioixp_format(void *arg)
57188447a05SGarrett D'Amore {
57288447a05SGarrett D'Amore _NOTE(ARGUNUSED(arg));
57388447a05SGarrett D'Amore
57488447a05SGarrett D'Amore return (AUDIO_FORMAT_S16_LE);
57588447a05SGarrett D'Amore }
57688447a05SGarrett D'Amore
57788447a05SGarrett D'Amore /*
57888447a05SGarrett D'Amore * audioixp_channels()
57988447a05SGarrett D'Amore *
58088447a05SGarrett D'Amore * Description:
58188447a05SGarrett D'Amore * This is called by the framework to query the channels for the device.
58288447a05SGarrett D'Amore *
58388447a05SGarrett D'Amore * Arguments:
58488447a05SGarrett D'Amore * void *arg The DMA engine to query
58588447a05SGarrett D'Amore *
58688447a05SGarrett D'Amore * Returns:
58788447a05SGarrett D'Amore * Number of channels for the device.
58888447a05SGarrett D'Amore */
58988447a05SGarrett D'Amore static int
audioixp_channels(void * arg)59088447a05SGarrett D'Amore audioixp_channels(void *arg)
59188447a05SGarrett D'Amore {
59288447a05SGarrett D'Amore audioixp_port_t *port = arg;
59388447a05SGarrett D'Amore
59488447a05SGarrett D'Amore return (port->nchan);
59588447a05SGarrett D'Amore }
59688447a05SGarrett D'Amore
59788447a05SGarrett D'Amore /*
59888447a05SGarrett D'Amore * audioixp_rate()
59988447a05SGarrett D'Amore *
60088447a05SGarrett D'Amore * Description:
60188447a05SGarrett D'Amore * This is called by the framework to query the rate of the device.
60288447a05SGarrett D'Amore *
60388447a05SGarrett D'Amore * Arguments:
60488447a05SGarrett D'Amore * void *arg The DMA engine to query
60588447a05SGarrett D'Amore *
60688447a05SGarrett D'Amore * Returns:
60788447a05SGarrett D'Amore * 48000
60888447a05SGarrett D'Amore */
60988447a05SGarrett D'Amore static int
audioixp_rate(void * arg)61088447a05SGarrett D'Amore audioixp_rate(void *arg)
61188447a05SGarrett D'Amore {
61288447a05SGarrett D'Amore _NOTE(ARGUNUSED(arg));
61388447a05SGarrett D'Amore
61488447a05SGarrett D'Amore return (48000);
61588447a05SGarrett D'Amore }
61688447a05SGarrett D'Amore
61788447a05SGarrett D'Amore /*
61888447a05SGarrett D'Amore * audioixp_count()
61988447a05SGarrett D'Amore *
62088447a05SGarrett D'Amore * Description:
62188447a05SGarrett D'Amore * This is called by the framework to get the engine's frame counter
62288447a05SGarrett D'Amore *
62388447a05SGarrett D'Amore * Arguments:
62488447a05SGarrett D'Amore * void *arg The DMA engine to query
62588447a05SGarrett D'Amore *
62688447a05SGarrett D'Amore * Returns:
62788447a05SGarrett D'Amore * frame count for current engine
62888447a05SGarrett D'Amore */
62988447a05SGarrett D'Amore static uint64_t
audioixp_count(void * arg)63088447a05SGarrett D'Amore audioixp_count(void *arg)
63188447a05SGarrett D'Amore {
63288447a05SGarrett D'Amore audioixp_port_t *port = arg;
63388447a05SGarrett D'Amore audioixp_state_t *statep = port->statep;
63488447a05SGarrett D'Amore uint64_t val;
63588447a05SGarrett D'Amore
63688447a05SGarrett D'Amore mutex_enter(&statep->inst_lock);
63788447a05SGarrett D'Amore audioixp_update_port(port);
63888447a05SGarrett D'Amore val = port->count;
63988447a05SGarrett D'Amore mutex_exit(&statep->inst_lock);
64088447a05SGarrett D'Amore
64188447a05SGarrett D'Amore return (val);
64288447a05SGarrett D'Amore }
64388447a05SGarrett D'Amore
64488447a05SGarrett D'Amore /*
64588447a05SGarrett D'Amore * audioixp_sync()
64688447a05SGarrett D'Amore *
64788447a05SGarrett D'Amore * Description:
64888447a05SGarrett D'Amore * This is called by the framework to synchronize DMA caches.
64988447a05SGarrett D'Amore *
65088447a05SGarrett D'Amore * Arguments:
65188447a05SGarrett D'Amore * void *arg The DMA engine to sync
65288447a05SGarrett D'Amore */
65388447a05SGarrett D'Amore static void
audioixp_sync(void * arg,unsigned nframes)65488447a05SGarrett D'Amore audioixp_sync(void *arg, unsigned nframes)
65588447a05SGarrett D'Amore {
65688447a05SGarrett D'Amore audioixp_port_t *port = arg;
65788447a05SGarrett D'Amore _NOTE(ARGUNUSED(nframes));
65888447a05SGarrett D'Amore
65988447a05SGarrett D'Amore (void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
66088447a05SGarrett D'Amore }
66188447a05SGarrett D'Amore
66288447a05SGarrett D'Amore /* *********************** Local Routines *************************** */
66388447a05SGarrett D'Amore
66488447a05SGarrett D'Amore /*
66588447a05SGarrett D'Amore * audioixp_alloc_port()
66688447a05SGarrett D'Amore *
66788447a05SGarrett D'Amore * Description:
66888447a05SGarrett D'Amore * This routine allocates the DMA handles and the memory for the
66988447a05SGarrett D'Amore * DMA engines to use. It also configures the BDL lists properly
67088447a05SGarrett D'Amore * for use.
67188447a05SGarrett D'Amore *
67288447a05SGarrett D'Amore * Arguments:
67388447a05SGarrett D'Amore * dev_info_t *dip Pointer to the device's devinfo
67488447a05SGarrett D'Amore *
67588447a05SGarrett D'Amore * Returns:
67688447a05SGarrett D'Amore * DDI_SUCCESS Registers successfully mapped
67788447a05SGarrett D'Amore * DDI_FAILURE Registers not successfully mapped
67888447a05SGarrett D'Amore */
67988447a05SGarrett D'Amore static int
audioixp_alloc_port(audioixp_state_t * statep,int num)68088447a05SGarrett D'Amore audioixp_alloc_port(audioixp_state_t *statep, int num)
68188447a05SGarrett D'Amore {
68288447a05SGarrett D'Amore ddi_dma_cookie_t cookie;
68388447a05SGarrett D'Amore uint_t count;
68488447a05SGarrett D'Amore int dir;
68588447a05SGarrett D'Amore unsigned caps;
68688447a05SGarrett D'Amore audio_dev_t *adev;
68788447a05SGarrett D'Amore audioixp_port_t *port;
68888447a05SGarrett D'Amore uint32_t paddr;
68988447a05SGarrett D'Amore int rc;
69088447a05SGarrett D'Amore dev_info_t *dip;
69188447a05SGarrett D'Amore audioixp_bd_entry_t *bdentry;
69288447a05SGarrett D'Amore
69388447a05SGarrett D'Amore adev = statep->adev;
69488447a05SGarrett D'Amore dip = statep->dip;
69588447a05SGarrett D'Amore
69688447a05SGarrett D'Amore port = kmem_zalloc(sizeof (*port), KM_SLEEP);
69788447a05SGarrett D'Amore port->statep = statep;
69888447a05SGarrett D'Amore port->started = B_FALSE;
69988447a05SGarrett D'Amore port->num = num;
70088447a05SGarrett D'Amore
70188447a05SGarrett D'Amore switch (num) {
70288447a05SGarrett D'Amore case IXP_REC:
70388447a05SGarrett D'Amore statep->rec_port = port;
70488447a05SGarrett D'Amore dir = DDI_DMA_READ;
70588447a05SGarrett D'Amore caps = ENGINE_INPUT_CAP;
70688447a05SGarrett D'Amore port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
70788447a05SGarrett D'Amore port->nchan = 2;
70888447a05SGarrett D'Amore break;
70988447a05SGarrett D'Amore case IXP_PLAY:
71088447a05SGarrett D'Amore statep->play_port = port;
71188447a05SGarrett D'Amore dir = DDI_DMA_WRITE;
71288447a05SGarrett D'Amore caps = ENGINE_OUTPUT_CAP;
71388447a05SGarrett D'Amore port->sync_dir = DDI_DMA_SYNC_FORDEV;
714d8a7fe16SGarrett D'Amore /*
715d8a7fe16SGarrett D'Amore * We allow for end users to configure more channels
716d8a7fe16SGarrett D'Amore * than just two, but we default to just two. The
717d8a7fe16SGarrett D'Amore * default stereo configuration works well. On the
718d8a7fe16SGarrett D'Amore * configurations we have tested, we've found that
719d8a7fe16SGarrett D'Amore * more than two channels (or rather 6 channels) can
720d8a7fe16SGarrett D'Amore * cause inexplicable noise. The noise is more
721d8a7fe16SGarrett D'Amore * noticeable when the system is running under load.
722d8a7fe16SGarrett D'Amore * (Holding the space bar in "top" while playing an
723d8a7fe16SGarrett D'Amore * MP3 is an easy way to recreate it.) End users who
724d8a7fe16SGarrett D'Amore * want to experiment, or have configurations that
725d8a7fe16SGarrett D'Amore * don't suffer from this, may increase the channels
726d8a7fe16SGarrett D'Amore * by setting this max-channels property. We leave it
727d8a7fe16SGarrett D'Amore * undocumented for now.
728d8a7fe16SGarrett D'Amore */
729d8a7fe16SGarrett D'Amore port->nchan = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
730d8a7fe16SGarrett D'Amore "max-channels", 2);
731d8a7fe16SGarrett D'Amore port->nchan = min(ac97_num_channels(statep->ac97),
732d8a7fe16SGarrett D'Amore port->nchan);
733d8a7fe16SGarrett D'Amore port->nchan &= ~1; /* make sure its an even number */
734d8a7fe16SGarrett D'Amore port->nchan = max(port->nchan, 2);
73588447a05SGarrett D'Amore break;
73688447a05SGarrett D'Amore default:
73788447a05SGarrett D'Amore audio_dev_warn(adev, "bad port number (%d)!", num);
73888447a05SGarrett D'Amore return (DDI_FAILURE);
73988447a05SGarrett D'Amore }
74088447a05SGarrett D'Amore
741*68c47f65SGarrett D'Amore port->nframes = 4096;
742*68c47f65SGarrett D'Amore port->fragfr = port->nframes / IXP_BD_NUMS;
74388447a05SGarrett D'Amore port->fragsz = port->fragfr * port->nchan * 2;
744*68c47f65SGarrett D'Amore port->samp_size = port->nframes * port->nchan * 2;
74588447a05SGarrett D'Amore
74688447a05SGarrett D'Amore /* allocate dma handle */
74788447a05SGarrett D'Amore rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP,
74888447a05SGarrett D'Amore NULL, &port->samp_dmah);
74988447a05SGarrett D'Amore if (rc != DDI_SUCCESS) {
75088447a05SGarrett D'Amore audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
75188447a05SGarrett D'Amore return (DDI_FAILURE);
75288447a05SGarrett D'Amore }
75388447a05SGarrett D'Amore /* allocate DMA buffer */
75488447a05SGarrett D'Amore rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr,
75588447a05SGarrett D'Amore DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
75688447a05SGarrett D'Amore &port->samp_size, &port->samp_acch);
75788447a05SGarrett D'Amore if (rc == DDI_FAILURE) {
75888447a05SGarrett D'Amore audio_dev_warn(adev, "dma_mem_alloc failed");
75988447a05SGarrett D'Amore return (DDI_FAILURE);
76088447a05SGarrett D'Amore }
76188447a05SGarrett D'Amore
76288447a05SGarrett D'Amore /* bind DMA buffer */
76388447a05SGarrett D'Amore rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
76488447a05SGarrett D'Amore port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT,
76588447a05SGarrett D'Amore DDI_DMA_SLEEP, NULL, &cookie, &count);
76688447a05SGarrett D'Amore if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
76788447a05SGarrett D'Amore audio_dev_warn(adev,
76888447a05SGarrett D'Amore "ddi_dma_addr_bind_handle failed: %d", rc);
76988447a05SGarrett D'Amore return (DDI_FAILURE);
77088447a05SGarrett D'Amore }
77188447a05SGarrett D'Amore port->samp_paddr = cookie.dmac_address;
77288447a05SGarrett D'Amore
77388447a05SGarrett D'Amore /*
77488447a05SGarrett D'Amore * now, from here we allocate DMA memory for buffer descriptor list.
77588447a05SGarrett D'Amore * we allocate adjacent DMA memory for all DMA engines.
77688447a05SGarrett D'Amore */
77788447a05SGarrett D'Amore rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP,
77888447a05SGarrett D'Amore NULL, &port->bdl_dmah);
77988447a05SGarrett D'Amore if (rc != DDI_SUCCESS) {
78088447a05SGarrett D'Amore audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed");
78188447a05SGarrett D'Amore return (DDI_FAILURE);
78288447a05SGarrett D'Amore }
78388447a05SGarrett D'Amore
78488447a05SGarrett D'Amore /*
78588447a05SGarrett D'Amore * we allocate all buffer descriptors lists in continuous dma memory.
78688447a05SGarrett D'Amore */
78788447a05SGarrett D'Amore port->bdl_size = sizeof (audioixp_bd_entry_t) * IXP_BD_NUMS;
78888447a05SGarrett D'Amore rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
78988447a05SGarrett D'Amore &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
79088447a05SGarrett D'Amore &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch);
79188447a05SGarrett D'Amore if (rc != DDI_SUCCESS) {
79288447a05SGarrett D'Amore audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed");
79388447a05SGarrett D'Amore return (DDI_FAILURE);
79488447a05SGarrett D'Amore }
79588447a05SGarrett D'Amore
79688447a05SGarrett D'Amore rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr,
79788447a05SGarrett D'Amore port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
79888447a05SGarrett D'Amore NULL, &cookie, &count);
79988447a05SGarrett D'Amore if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
80088447a05SGarrett D'Amore audio_dev_warn(adev, "addr_bind_handle failed");
80188447a05SGarrett D'Amore return (DDI_FAILURE);
80288447a05SGarrett D'Amore }
80388447a05SGarrett D'Amore port->bdl_paddr = cookie.dmac_address;
80488447a05SGarrett D'Amore
80588447a05SGarrett D'Amore /*
80688447a05SGarrett D'Amore * Wire up the BD list.
80788447a05SGarrett D'Amore */
80888447a05SGarrett D'Amore paddr = port->samp_paddr;
80988447a05SGarrett D'Amore bdentry = (void *)port->bdl_kaddr;
81088447a05SGarrett D'Amore
81188447a05SGarrett D'Amore for (int i = 0; i < IXP_BD_NUMS; i++) {
81288447a05SGarrett D'Amore
81388447a05SGarrett D'Amore /* set base address of buffer */
81488447a05SGarrett D'Amore ddi_put32(port->bdl_acch, &bdentry->buf_base, paddr);
81588447a05SGarrett D'Amore ddi_put16(port->bdl_acch, &bdentry->status, 0);
81688447a05SGarrett D'Amore ddi_put16(port->bdl_acch, &bdentry->buf_len, port->fragsz / 4);
81788447a05SGarrett D'Amore ddi_put32(port->bdl_acch, &bdentry->next, port->bdl_paddr +
81888447a05SGarrett D'Amore (((i + 1) % IXP_BD_NUMS) * sizeof (audioixp_bd_entry_t)));
81988447a05SGarrett D'Amore paddr += port->fragsz;
82088447a05SGarrett D'Amore bdentry++;
82188447a05SGarrett D'Amore }
82248f21d36SGarrett D'Amore (void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
82388447a05SGarrett D'Amore
82488447a05SGarrett D'Amore port->engine = audio_engine_alloc(&audioixp_engine_ops, caps);
82588447a05SGarrett D'Amore if (port->engine == NULL) {
82688447a05SGarrett D'Amore audio_dev_warn(adev, "audio_engine_alloc failed");
82788447a05SGarrett D'Amore return (DDI_FAILURE);
82888447a05SGarrett D'Amore }
82988447a05SGarrett D'Amore
83088447a05SGarrett D'Amore audio_engine_set_private(port->engine, port);
83188447a05SGarrett D'Amore audio_dev_add_engine(adev, port->engine);
83288447a05SGarrett D'Amore
83388447a05SGarrett D'Amore return (DDI_SUCCESS);
83488447a05SGarrett D'Amore }
83588447a05SGarrett D'Amore
83688447a05SGarrett D'Amore /*
83788447a05SGarrett D'Amore * audioixp_free_port()
83888447a05SGarrett D'Amore *
83988447a05SGarrett D'Amore * Description:
84088447a05SGarrett D'Amore * This routine unbinds the DMA cookies, frees the DMA buffers,
84188447a05SGarrett D'Amore * deallocates the DMA handles.
84288447a05SGarrett D'Amore *
84388447a05SGarrett D'Amore * Arguments:
84488447a05SGarrett D'Amore * audioixp_port_t *port The port structure for a DMA engine.
84588447a05SGarrett D'Amore */
84688447a05SGarrett D'Amore static void
audioixp_free_port(audioixp_port_t * port)84788447a05SGarrett D'Amore audioixp_free_port(audioixp_port_t *port)
84888447a05SGarrett D'Amore {
84988447a05SGarrett D'Amore if (port == NULL)
85088447a05SGarrett D'Amore return;
85188447a05SGarrett D'Amore
85288447a05SGarrett D'Amore if (port->engine) {
85388447a05SGarrett D'Amore audio_dev_remove_engine(port->statep->adev, port->engine);
85488447a05SGarrett D'Amore audio_engine_free(port->engine);
85588447a05SGarrett D'Amore }
85688447a05SGarrett D'Amore if (port->bdl_paddr) {
85788447a05SGarrett D'Amore (void) ddi_dma_unbind_handle(port->bdl_dmah);
85888447a05SGarrett D'Amore }
85988447a05SGarrett D'Amore if (port->bdl_acch) {
86088447a05SGarrett D'Amore ddi_dma_mem_free(&port->bdl_acch);
86188447a05SGarrett D'Amore }
86288447a05SGarrett D'Amore if (port->bdl_dmah) {
86388447a05SGarrett D'Amore ddi_dma_free_handle(&port->bdl_dmah);
86488447a05SGarrett D'Amore }
86588447a05SGarrett D'Amore if (port->samp_paddr) {
86688447a05SGarrett D'Amore (void) ddi_dma_unbind_handle(port->samp_dmah);
86788447a05SGarrett D'Amore }
86888447a05SGarrett D'Amore if (port->samp_acch) {
86988447a05SGarrett D'Amore ddi_dma_mem_free(&port->samp_acch);
87088447a05SGarrett D'Amore }
87188447a05SGarrett D'Amore if (port->samp_dmah) {
87288447a05SGarrett D'Amore ddi_dma_free_handle(&port->samp_dmah);
87388447a05SGarrett D'Amore }
87488447a05SGarrett D'Amore kmem_free(port, sizeof (*port));
87588447a05SGarrett D'Amore }
87688447a05SGarrett D'Amore
87788447a05SGarrett D'Amore /*
87888447a05SGarrett D'Amore * audioixp_update_port()
87988447a05SGarrett D'Amore *
88088447a05SGarrett D'Amore * Description:
88188447a05SGarrett D'Amore * This routine updates the ports frame counter from hardware, and
88288447a05SGarrett D'Amore * gracefully handles wraps.
88388447a05SGarrett D'Amore *
88488447a05SGarrett D'Amore * Arguments:
88588447a05SGarrett D'Amore * audioixp_port_t *port The port to update.
88688447a05SGarrett D'Amore */
88788447a05SGarrett D'Amore static void
audioixp_update_port(audioixp_port_t * port)88888447a05SGarrett D'Amore audioixp_update_port(audioixp_port_t *port)
88988447a05SGarrett D'Amore {
89088447a05SGarrett D'Amore audioixp_state_t *statep = port->statep;
89188447a05SGarrett D'Amore unsigned regoff;
89288447a05SGarrett D'Amore unsigned n;
89388447a05SGarrett D'Amore int loop;
89488447a05SGarrett D'Amore uint32_t offset;
89588447a05SGarrett D'Amore uint32_t paddr;
89688447a05SGarrett D'Amore
89788447a05SGarrett D'Amore if (port->num == IXP_REC) {
89888447a05SGarrett D'Amore regoff = IXP_AUDIO_IN_DMA_DT_CUR;
89988447a05SGarrett D'Amore } else {
90088447a05SGarrett D'Amore regoff = IXP_AUDIO_OUT_DMA_DT_CUR;
90188447a05SGarrett D'Amore }
90288447a05SGarrett D'Amore
90388447a05SGarrett D'Amore /*
90488447a05SGarrett D'Amore * Apparently it may take several tries to get an update on the
90588447a05SGarrett D'Amore * position. Is this a hardware bug?
90688447a05SGarrett D'Amore */
90788447a05SGarrett D'Amore for (loop = 100; loop; loop--) {
90888447a05SGarrett D'Amore paddr = GET32(regoff);
90988447a05SGarrett D'Amore
91088447a05SGarrett D'Amore /* make sure address is reasonable */
91188447a05SGarrett D'Amore if ((paddr < port->samp_paddr) ||
91288447a05SGarrett D'Amore (paddr >= (port->samp_paddr + port->samp_size))) {
91388447a05SGarrett D'Amore continue;
91488447a05SGarrett D'Amore }
91588447a05SGarrett D'Amore
91688447a05SGarrett D'Amore offset = paddr - port->samp_paddr;
91788447a05SGarrett D'Amore
91888447a05SGarrett D'Amore if (offset >= port->offset) {
91988447a05SGarrett D'Amore n = offset - port->offset;
92088447a05SGarrett D'Amore } else {
92188447a05SGarrett D'Amore n = offset + (port->samp_size - port->offset);
92288447a05SGarrett D'Amore }
92388447a05SGarrett D'Amore port->offset = offset;
92488447a05SGarrett D'Amore port->count += (n / (port->nchan * sizeof (uint16_t)));
92588447a05SGarrett D'Amore return;
92688447a05SGarrett D'Amore }
92788447a05SGarrett D'Amore
92888447a05SGarrett D'Amore audio_dev_warn(statep->adev, "Unable to update count (h/w bug?)");
92988447a05SGarrett D'Amore }
93088447a05SGarrett D'Amore
93188447a05SGarrett D'Amore
93288447a05SGarrett D'Amore /*
93388447a05SGarrett D'Amore * audioixp_map_regs()
93488447a05SGarrett D'Amore *
93588447a05SGarrett D'Amore * Description:
93688447a05SGarrett D'Amore * The registers are mapped in.
93788447a05SGarrett D'Amore *
93888447a05SGarrett D'Amore * Arguments:
93988447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
94088447a05SGarrett D'Amore *
94188447a05SGarrett D'Amore * Returns:
94288447a05SGarrett D'Amore * DDI_SUCCESS Registers successfully mapped
94388447a05SGarrett D'Amore * DDI_FAILURE Registers not successfully mapped
94488447a05SGarrett D'Amore */
94588447a05SGarrett D'Amore static int
audioixp_map_regs(audioixp_state_t * statep)94688447a05SGarrett D'Amore audioixp_map_regs(audioixp_state_t *statep)
94788447a05SGarrett D'Amore {
94888447a05SGarrett D'Amore dev_info_t *dip = statep->dip;
94988447a05SGarrett D'Amore
95088447a05SGarrett D'Amore /* map PCI config space */
95188447a05SGarrett D'Amore if (pci_config_setup(statep->dip, &statep->pcih) == DDI_FAILURE) {
95288447a05SGarrett D'Amore audio_dev_warn(statep->adev, "unable to map PCI config space");
95388447a05SGarrett D'Amore return (DDI_FAILURE);
95488447a05SGarrett D'Amore }
95588447a05SGarrett D'Amore
95688447a05SGarrett D'Amore /* map audio mixer register */
95788447a05SGarrett D'Amore if ((ddi_regs_map_setup(dip, IXP_IO_AM_REGS, &statep->regsp, 0, 0,
95888447a05SGarrett D'Amore &dev_attr, &statep->regsh)) != DDI_SUCCESS) {
95988447a05SGarrett D'Amore audio_dev_warn(statep->adev, "unable to map audio registers");
96088447a05SGarrett D'Amore return (DDI_FAILURE);
96188447a05SGarrett D'Amore }
96288447a05SGarrett D'Amore return (DDI_SUCCESS);
96388447a05SGarrett D'Amore }
96488447a05SGarrett D'Amore
96588447a05SGarrett D'Amore /*
96688447a05SGarrett D'Amore * audioixp_unmap_regs()
96788447a05SGarrett D'Amore *
96888447a05SGarrett D'Amore * Description:
96988447a05SGarrett D'Amore * This routine unmaps control registers.
97088447a05SGarrett D'Amore *
97188447a05SGarrett D'Amore * Arguments:
97288447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
97388447a05SGarrett D'Amore */
97488447a05SGarrett D'Amore static void
audioixp_unmap_regs(audioixp_state_t * statep)97588447a05SGarrett D'Amore audioixp_unmap_regs(audioixp_state_t *statep)
97688447a05SGarrett D'Amore {
97788447a05SGarrett D'Amore if (statep->regsh) {
97888447a05SGarrett D'Amore ddi_regs_map_free(&statep->regsh);
97988447a05SGarrett D'Amore }
98088447a05SGarrett D'Amore
98188447a05SGarrett D'Amore if (statep->pcih) {
98288447a05SGarrett D'Amore pci_config_teardown(&statep->pcih);
98388447a05SGarrett D'Amore }
98488447a05SGarrett D'Amore }
98588447a05SGarrett D'Amore
98688447a05SGarrett D'Amore /*
98788447a05SGarrett D'Amore * audioixp_codec_ready()
98888447a05SGarrett D'Amore *
98988447a05SGarrett D'Amore * Description:
99088447a05SGarrett D'Amore * This routine checks the state of codecs. It checks the flag to confirm
99188447a05SGarrett D'Amore * that primary codec is ready.
99288447a05SGarrett D'Amore *
99388447a05SGarrett D'Amore * Arguments:
99488447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
99588447a05SGarrett D'Amore *
99688447a05SGarrett D'Amore * Returns:
99788447a05SGarrett D'Amore * DDI_SUCCESS codec is ready
99888447a05SGarrett D'Amore * DDI_FAILURE codec is not ready
99988447a05SGarrett D'Amore */
100088447a05SGarrett D'Amore static int
audioixp_codec_ready(audioixp_state_t * statep)100188447a05SGarrett D'Amore audioixp_codec_ready(audioixp_state_t *statep)
100288447a05SGarrett D'Amore {
100388447a05SGarrett D'Amore uint32_t sr;
100488447a05SGarrett D'Amore
100588447a05SGarrett D'Amore PUT32(IXP_AUDIO_INT, 0xffffffff);
100688447a05SGarrett D'Amore drv_usecwait(1000);
100788447a05SGarrett D'Amore
100888447a05SGarrett D'Amore sr = GET32(IXP_AUDIO_INT);
100988447a05SGarrett D'Amore if (sr & IXP_AUDIO_INT_CODEC0_NOT_READY) {
101088447a05SGarrett D'Amore PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC0_NOT_READY);
101188447a05SGarrett D'Amore audio_dev_warn(statep->adev, "primary codec not ready");
101288447a05SGarrett D'Amore
101388447a05SGarrett D'Amore return (DDI_FAILURE);
101488447a05SGarrett D'Amore }
101588447a05SGarrett D'Amore return (DDI_SUCCESS);
101688447a05SGarrett D'Amore }
101788447a05SGarrett D'Amore
101888447a05SGarrett D'Amore /*
101988447a05SGarrett D'Amore * audioixp_codec_sync()
102088447a05SGarrett D'Amore *
102188447a05SGarrett D'Amore * Description:
102288447a05SGarrett D'Amore * Serialize access to the AC97 audio mixer registers.
102388447a05SGarrett D'Amore *
102488447a05SGarrett D'Amore * Arguments:
102588447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
102688447a05SGarrett D'Amore *
102788447a05SGarrett D'Amore * Returns:
102888447a05SGarrett D'Amore * DDI_SUCCESS Ready for an I/O access to the codec
102988447a05SGarrett D'Amore * DDI_FAILURE An I/O access is currently in progress, can't
103088447a05SGarrett D'Amore * perform another I/O access.
103188447a05SGarrett D'Amore */
103288447a05SGarrett D'Amore static int
audioixp_codec_sync(audioixp_state_t * statep)103388447a05SGarrett D'Amore audioixp_codec_sync(audioixp_state_t *statep)
103488447a05SGarrett D'Amore {
103588447a05SGarrett D'Amore int i;
103688447a05SGarrett D'Amore uint32_t cmd;
103788447a05SGarrett D'Amore
103888447a05SGarrett D'Amore for (i = 0; i < 300; i++) {
103988447a05SGarrett D'Amore cmd = GET32(IXP_AUDIO_OUT_PHY_ADDR_DATA);
104088447a05SGarrett D'Amore if (!(cmd & IXP_AUDIO_OUT_PHY_EN)) {
104188447a05SGarrett D'Amore return (DDI_SUCCESS);
104288447a05SGarrett D'Amore }
104388447a05SGarrett D'Amore drv_usecwait(10);
104488447a05SGarrett D'Amore }
104588447a05SGarrett D'Amore
104688447a05SGarrett D'Amore audio_dev_warn(statep->adev, "unable to synchronize codec");
104788447a05SGarrett D'Amore return (DDI_FAILURE);
104888447a05SGarrett D'Amore }
104988447a05SGarrett D'Amore
105088447a05SGarrett D'Amore /*
105188447a05SGarrett D'Amore * audioixp_rd97()
105288447a05SGarrett D'Amore *
105388447a05SGarrett D'Amore * Description:
105488447a05SGarrett D'Amore * Get the specific AC97 Codec register.
105588447a05SGarrett D'Amore *
105688447a05SGarrett D'Amore * Arguments:
105788447a05SGarrett D'Amore * void *arg The device's state structure
105888447a05SGarrett D'Amore * uint8_t reg AC97 register number
105988447a05SGarrett D'Amore *
106088447a05SGarrett D'Amore * Returns:
106188447a05SGarrett D'Amore * Register value.
106288447a05SGarrett D'Amore */
106388447a05SGarrett D'Amore static uint16_t
audioixp_rd97(void * arg,uint8_t reg)106488447a05SGarrett D'Amore audioixp_rd97(void *arg, uint8_t reg)
106588447a05SGarrett D'Amore {
106688447a05SGarrett D'Amore audioixp_state_t *statep = arg;
106788447a05SGarrett D'Amore uint32_t value;
106888447a05SGarrett D'Amore uint32_t result;
106988447a05SGarrett D'Amore
107088447a05SGarrett D'Amore if (audioixp_codec_sync(statep) != DDI_SUCCESS)
107188447a05SGarrett D'Amore return (0xffff);
107288447a05SGarrett D'Amore
107388447a05SGarrett D'Amore value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC |
107488447a05SGarrett D'Amore IXP_AUDIO_OUT_PHY_READ |
107588447a05SGarrett D'Amore IXP_AUDIO_OUT_PHY_EN |
107688447a05SGarrett D'Amore ((unsigned)reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT);
107788447a05SGarrett D'Amore PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value);
107888447a05SGarrett D'Amore
107988447a05SGarrett D'Amore if (audioixp_codec_sync(statep) != DDI_SUCCESS)
108088447a05SGarrett D'Amore return (0xffff);
108188447a05SGarrett D'Amore
108288447a05SGarrett D'Amore for (int i = 0; i < 300; i++) {
108388447a05SGarrett D'Amore result = GET32(IXP_AUDIO_IN_PHY_ADDR_DATA);
108488447a05SGarrett D'Amore if (result & IXP_AUDIO_IN_PHY_READY) {
108588447a05SGarrett D'Amore return (result >> IXP_AUDIO_IN_PHY_DATA_SHIFT);
108688447a05SGarrett D'Amore }
108788447a05SGarrett D'Amore drv_usecwait(10);
108888447a05SGarrett D'Amore }
108988447a05SGarrett D'Amore
109088447a05SGarrett D'Amore done:
109188447a05SGarrett D'Amore audio_dev_warn(statep->adev, "time out reading codec reg %d", reg);
109288447a05SGarrett D'Amore return (0xffff);
109388447a05SGarrett D'Amore }
109488447a05SGarrett D'Amore
109588447a05SGarrett D'Amore /*
109688447a05SGarrett D'Amore * audioixp_wr97()
109788447a05SGarrett D'Amore *
109888447a05SGarrett D'Amore * Description:
109988447a05SGarrett D'Amore * Set the specific AC97 Codec register.
110088447a05SGarrett D'Amore *
110188447a05SGarrett D'Amore * Arguments:
110288447a05SGarrett D'Amore * void *arg The device's state structure
110388447a05SGarrett D'Amore * uint8_t reg AC97 register number
110488447a05SGarrett D'Amore * uint16_t data The data want to be set
110588447a05SGarrett D'Amore */
110688447a05SGarrett D'Amore static void
audioixp_wr97(void * arg,uint8_t reg,uint16_t data)110788447a05SGarrett D'Amore audioixp_wr97(void *arg, uint8_t reg, uint16_t data)
110888447a05SGarrett D'Amore {
110988447a05SGarrett D'Amore audioixp_state_t *statep = arg;
111088447a05SGarrett D'Amore uint32_t value;
111188447a05SGarrett D'Amore
111288447a05SGarrett D'Amore if (audioixp_codec_sync(statep) != DDI_SUCCESS) {
111388447a05SGarrett D'Amore return;
111488447a05SGarrett D'Amore }
111588447a05SGarrett D'Amore
111688447a05SGarrett D'Amore value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC |
111788447a05SGarrett D'Amore IXP_AUDIO_OUT_PHY_WRITE |
111888447a05SGarrett D'Amore IXP_AUDIO_OUT_PHY_EN |
111988447a05SGarrett D'Amore ((unsigned)reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT) |
112088447a05SGarrett D'Amore ((unsigned)data << IXP_AUDIO_OUT_PHY_DATA_SHIFT);
112188447a05SGarrett D'Amore PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value);
112288447a05SGarrett D'Amore
112388447a05SGarrett D'Amore (void) audioixp_rd97(statep, reg);
112488447a05SGarrett D'Amore }
112588447a05SGarrett D'Amore
112688447a05SGarrett D'Amore /*
112788447a05SGarrett D'Amore * audioixp_reset_ac97()
112888447a05SGarrett D'Amore *
112988447a05SGarrett D'Amore * Description:
113088447a05SGarrett D'Amore * Reset AC97 Codec register.
113188447a05SGarrett D'Amore *
113288447a05SGarrett D'Amore * Arguments:
113388447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
113488447a05SGarrett D'Amore *
113588447a05SGarrett D'Amore * Returns:
113688447a05SGarrett D'Amore * DDI_SUCCESS Reset the codec successfully
113788447a05SGarrett D'Amore * DDI_FAILURE Failed to reset the codec
113888447a05SGarrett D'Amore */
113988447a05SGarrett D'Amore static int
audioixp_reset_ac97(audioixp_state_t * statep)114088447a05SGarrett D'Amore audioixp_reset_ac97(audioixp_state_t *statep)
114188447a05SGarrett D'Amore {
114288447a05SGarrett D'Amore uint32_t cmd;
114388447a05SGarrett D'Amore int i;
114488447a05SGarrett D'Amore
114588447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_POWER_DOWN);
114688447a05SGarrett D'Amore drv_usecwait(10);
114788447a05SGarrett D'Amore
114888447a05SGarrett D'Amore /* register reset */
114988447a05SGarrett D'Amore SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET);
115088447a05SGarrett D'Amore /* force a read to flush caches */
115188447a05SGarrett D'Amore (void) GET32(IXP_AUDIO_CMD);
115288447a05SGarrett D'Amore
115388447a05SGarrett D'Amore drv_usecwait(10);
115488447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET);
115588447a05SGarrett D'Amore
115688447a05SGarrett D'Amore /* cold reset */
115788447a05SGarrett D'Amore for (i = 0; i < 300; i++) {
115888447a05SGarrett D'Amore cmd = GET32(IXP_AUDIO_CMD);
115988447a05SGarrett D'Amore if (cmd & IXP_AUDIO_CMD_AC_ACTIVE) {
116088447a05SGarrett D'Amore cmd |= IXP_AUDIO_CMD_AC_RESET | IXP_AUDIO_CMD_AC_SYNC;
116188447a05SGarrett D'Amore PUT32(IXP_AUDIO_CMD, cmd);
116288447a05SGarrett D'Amore return (DDI_SUCCESS);
116388447a05SGarrett D'Amore }
116488447a05SGarrett D'Amore cmd &= ~IXP_AUDIO_CMD_AC_RESET;
116588447a05SGarrett D'Amore cmd |= IXP_AUDIO_CMD_AC_SYNC;
116688447a05SGarrett D'Amore PUT32(IXP_AUDIO_CMD, cmd);
116788447a05SGarrett D'Amore (void) GET32(IXP_AUDIO_CMD);
116888447a05SGarrett D'Amore drv_usecwait(10);
116988447a05SGarrett D'Amore cmd |= IXP_AUDIO_CMD_AC_RESET;
117088447a05SGarrett D'Amore PUT32(IXP_AUDIO_CMD, cmd);
117188447a05SGarrett D'Amore drv_usecwait(10);
117288447a05SGarrett D'Amore }
117388447a05SGarrett D'Amore
117488447a05SGarrett D'Amore audio_dev_warn(statep->adev, "AC'97 reset timed out");
117588447a05SGarrett D'Amore return (DDI_FAILURE);
117688447a05SGarrett D'Amore }
117788447a05SGarrett D'Amore
117888447a05SGarrett D'Amore /*
117988447a05SGarrett D'Amore * audioixp_chip_init()
118088447a05SGarrett D'Amore *
118188447a05SGarrett D'Amore * Description:
118288447a05SGarrett D'Amore * This routine initializes ATI IXP audio controller and the AC97
118388447a05SGarrett D'Amore * codec.
118488447a05SGarrett D'Amore *
118588447a05SGarrett D'Amore * Arguments:
118688447a05SGarrett D'Amore * audioixp_state_t *state The device's state structure
118788447a05SGarrett D'Amore *
118888447a05SGarrett D'Amore * Returns:
118988447a05SGarrett D'Amore * DDI_SUCCESS The hardware was initialized properly
119088447a05SGarrett D'Amore * DDI_FAILURE The hardware couldn't be initialized properly
119188447a05SGarrett D'Amore */
119288447a05SGarrett D'Amore static int
audioixp_chip_init(audioixp_state_t * statep)119388447a05SGarrett D'Amore audioixp_chip_init(audioixp_state_t *statep)
119488447a05SGarrett D'Amore {
119588447a05SGarrett D'Amore /*
119688447a05SGarrett D'Amore * put the audio controller into quiet state, everything off
119788447a05SGarrett D'Amore */
119888447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
119988447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
120088447a05SGarrett D'Amore
120188447a05SGarrett D'Amore /* AC97 reset */
120288447a05SGarrett D'Amore if (audioixp_reset_ac97(statep) != DDI_SUCCESS) {
120388447a05SGarrett D'Amore audio_dev_warn(statep->adev, "AC97 codec reset failed");
120488447a05SGarrett D'Amore return (DDI_FAILURE);
120588447a05SGarrett D'Amore }
120688447a05SGarrett D'Amore
120788447a05SGarrett D'Amore if (audioixp_codec_ready(statep) != DDI_SUCCESS) {
120888447a05SGarrett D'Amore audio_dev_warn(statep->adev, "AC97 codec not ready");
120988447a05SGarrett D'Amore return (DDI_FAILURE);
121088447a05SGarrett D'Amore }
121188447a05SGarrett D'Amore
121288447a05SGarrett D'Amore return (DDI_SUCCESS);
121388447a05SGarrett D'Amore
121488447a05SGarrett D'Amore } /* audioixp_chip_init() */
121588447a05SGarrett D'Amore
121688447a05SGarrett D'Amore /*
121788447a05SGarrett D'Amore * audioixp_attach()
121888447a05SGarrett D'Amore *
121988447a05SGarrett D'Amore * Description:
122088447a05SGarrett D'Amore * Attach an instance of the audioixp driver. This routine does
122188447a05SGarrett D'Amore * the device dependent attach tasks.
122288447a05SGarrett D'Amore *
122388447a05SGarrett D'Amore * Arguments:
122488447a05SGarrett D'Amore * dev_info_t *dip Pointer to the device's dev_info struct
122588447a05SGarrett D'Amore * ddi_attach_cmd_t cmd Attach command
122688447a05SGarrett D'Amore *
122788447a05SGarrett D'Amore * Returns:
122888447a05SGarrett D'Amore * DDI_SUCCESS The driver was initialized properly
122988447a05SGarrett D'Amore * DDI_FAILURE The driver couldn't be initialized properly
123088447a05SGarrett D'Amore */
123188447a05SGarrett D'Amore static int
audioixp_attach(dev_info_t * dip)123288447a05SGarrett D'Amore audioixp_attach(dev_info_t *dip)
123388447a05SGarrett D'Amore {
123488447a05SGarrett D'Amore uint16_t cmdeg;
123588447a05SGarrett D'Amore audioixp_state_t *statep;
123688447a05SGarrett D'Amore audio_dev_t *adev;
123788447a05SGarrett D'Amore uint32_t devid;
123888447a05SGarrett D'Amore const char *name;
123988447a05SGarrett D'Amore const char *rev;
124088447a05SGarrett D'Amore
124188447a05SGarrett D'Amore /* allocate the soft state structure */
124288447a05SGarrett D'Amore statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
124388447a05SGarrett D'Amore statep->dip = dip;
124488447a05SGarrett D'Amore ddi_set_driver_private(dip, statep);
1245*68c47f65SGarrett D'Amore mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, NULL);
124688447a05SGarrett D'Amore
124788447a05SGarrett D'Amore /* allocate framework audio device */
124888447a05SGarrett D'Amore if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
124988447a05SGarrett D'Amore cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev",
125088447a05SGarrett D'Amore ddi_driver_name(dip), ddi_get_instance(dip));
125188447a05SGarrett D'Amore goto error;
125288447a05SGarrett D'Amore }
125388447a05SGarrett D'Amore statep->adev = adev;
125488447a05SGarrett D'Amore
125588447a05SGarrett D'Amore /* map in the registers */
125688447a05SGarrett D'Amore if (audioixp_map_regs(statep) != DDI_SUCCESS) {
125788447a05SGarrett D'Amore audio_dev_warn(adev, "couldn't map registers");
125888447a05SGarrett D'Amore goto error;
125988447a05SGarrett D'Amore }
126088447a05SGarrett D'Amore
126188447a05SGarrett D'Amore /* set device information -- this could be smarter */
126288447a05SGarrett D'Amore devid = ((pci_config_get16(statep->pcih, PCI_CONF_VENID)) << 16) |
126388447a05SGarrett D'Amore pci_config_get16(statep->pcih, PCI_CONF_DEVID);
126488447a05SGarrett D'Amore
126588447a05SGarrett D'Amore name = "ATI AC'97";
126688447a05SGarrett D'Amore switch (devid) {
126788447a05SGarrett D'Amore case IXP_PCI_ID_200:
126888447a05SGarrett D'Amore rev = "IXP150";
126988447a05SGarrett D'Amore break;
127088447a05SGarrett D'Amore case IXP_PCI_ID_300:
127188447a05SGarrett D'Amore rev = "SB300";
127288447a05SGarrett D'Amore break;
127388447a05SGarrett D'Amore case IXP_PCI_ID_400:
127488447a05SGarrett D'Amore if (pci_config_get8(statep->pcih, PCI_CONF_REVID) & 0x80) {
127588447a05SGarrett D'Amore rev = "SB450";
127688447a05SGarrett D'Amore } else {
127788447a05SGarrett D'Amore rev = "SB400";
127888447a05SGarrett D'Amore }
127988447a05SGarrett D'Amore break;
128088447a05SGarrett D'Amore case IXP_PCI_ID_SB600:
128188447a05SGarrett D'Amore rev = "SB600";
128288447a05SGarrett D'Amore break;
128388447a05SGarrett D'Amore default:
128488447a05SGarrett D'Amore rev = "Unknown";
128588447a05SGarrett D'Amore break;
128688447a05SGarrett D'Amore }
128788447a05SGarrett D'Amore audio_dev_set_description(adev, name);
128888447a05SGarrett D'Amore audio_dev_set_version(adev, rev);
128988447a05SGarrett D'Amore
1290d8a7fe16SGarrett D'Amore /* set PCI command register */
1291d8a7fe16SGarrett D'Amore cmdeg = pci_config_get16(statep->pcih, PCI_CONF_COMM);
1292d8a7fe16SGarrett D'Amore pci_config_put16(statep->pcih, PCI_CONF_COMM,
1293d8a7fe16SGarrett D'Amore cmdeg | PCI_COMM_IO | PCI_COMM_MAE);
129488447a05SGarrett D'Amore
129588447a05SGarrett D'Amore statep->ac97 = ac97_alloc(dip, audioixp_rd97, audioixp_wr97, statep);
129688447a05SGarrett D'Amore if (statep->ac97 == NULL) {
129788447a05SGarrett D'Amore audio_dev_warn(adev, "failed to allocate ac97 handle");
129888447a05SGarrett D'Amore goto error;
129988447a05SGarrett D'Amore }
130088447a05SGarrett D'Amore
1301d8a7fe16SGarrett D'Amore /* allocate port structures */
1302d8a7fe16SGarrett D'Amore if ((audioixp_alloc_port(statep, IXP_PLAY) != DDI_SUCCESS) ||
1303d8a7fe16SGarrett D'Amore (audioixp_alloc_port(statep, IXP_REC) != DDI_SUCCESS)) {
1304d8a7fe16SGarrett D'Amore goto error;
1305d8a7fe16SGarrett D'Amore }
1306d8a7fe16SGarrett D'Amore
1307d8a7fe16SGarrett D'Amore /*
1308d8a7fe16SGarrett D'Amore * If we have locked in a stereo configuration, then don't expose
1309d8a7fe16SGarrett D'Amore * multichannel-specific AC'97 codec controls.
1310d8a7fe16SGarrett D'Amore */
1311d8a7fe16SGarrett D'Amore if (statep->play_port->nchan == 2) {
1312d8a7fe16SGarrett D'Amore int i;
1313d8a7fe16SGarrett D'Amore ac97_ctrl_t *ctrl;
1314d8a7fe16SGarrett D'Amore const char *name;
1315d8a7fe16SGarrett D'Amore
1316d8a7fe16SGarrett D'Amore for (i = 0; (name = audioixp_remove_ac97[i]) != NULL; i++) {
1317d8a7fe16SGarrett D'Amore ctrl = ac97_control_find(statep->ac97, name);
1318d8a7fe16SGarrett D'Amore if (ctrl != NULL) {
1319d8a7fe16SGarrett D'Amore ac97_control_unregister(ctrl);
1320d8a7fe16SGarrett D'Amore }
1321d8a7fe16SGarrett D'Amore }
1322d8a7fe16SGarrett D'Amore }
132388447a05SGarrett D'Amore
132488447a05SGarrett D'Amore if (audioixp_chip_init(statep) != DDI_SUCCESS) {
132588447a05SGarrett D'Amore audio_dev_warn(statep->adev, "failed to init chip");
132688447a05SGarrett D'Amore goto error;
132788447a05SGarrett D'Amore }
132888447a05SGarrett D'Amore
132988447a05SGarrett D'Amore /* initialize the AC'97 part */
133088447a05SGarrett D'Amore if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) {
133188447a05SGarrett D'Amore audio_dev_warn(adev, "ac'97 initialization failed");
133288447a05SGarrett D'Amore goto error;
133388447a05SGarrett D'Amore }
133488447a05SGarrett D'Amore
133588447a05SGarrett D'Amore if (audio_dev_register(adev) != DDI_SUCCESS) {
133688447a05SGarrett D'Amore audio_dev_warn(adev, "unable to register with framework");
133788447a05SGarrett D'Amore goto error;
133888447a05SGarrett D'Amore }
133988447a05SGarrett D'Amore
134088447a05SGarrett D'Amore ddi_report_dev(dip);
134188447a05SGarrett D'Amore
134288447a05SGarrett D'Amore return (DDI_SUCCESS);
134388447a05SGarrett D'Amore
134488447a05SGarrett D'Amore error:
134588447a05SGarrett D'Amore audioixp_destroy(statep);
134688447a05SGarrett D'Amore return (DDI_FAILURE);
134788447a05SGarrett D'Amore }
134888447a05SGarrett D'Amore
134988447a05SGarrett D'Amore /*
135088447a05SGarrett D'Amore * audioixp_detach()
135188447a05SGarrett D'Amore *
135288447a05SGarrett D'Amore * Description:
135388447a05SGarrett D'Amore * Detach an instance of the audioixp driver.
135488447a05SGarrett D'Amore *
135588447a05SGarrett D'Amore * Arguments:
135688447a05SGarrett D'Amore * dev_info_t *dip Pointer to the device's dev_info struct
135788447a05SGarrett D'Amore *
135888447a05SGarrett D'Amore * Returns:
135988447a05SGarrett D'Amore * DDI_SUCCESS The driver was detached
136088447a05SGarrett D'Amore * DDI_FAILURE The driver couldn't be detached
136188447a05SGarrett D'Amore */
136288447a05SGarrett D'Amore static int
audioixp_detach(dev_info_t * dip)136388447a05SGarrett D'Amore audioixp_detach(dev_info_t *dip)
136488447a05SGarrett D'Amore {
136588447a05SGarrett D'Amore audioixp_state_t *statep;
136688447a05SGarrett D'Amore
136788447a05SGarrett D'Amore statep = ddi_get_driver_private(dip);
136888447a05SGarrett D'Amore
136988447a05SGarrett D'Amore if (audio_dev_unregister(statep->adev) != DDI_SUCCESS) {
137088447a05SGarrett D'Amore return (DDI_FAILURE);
137188447a05SGarrett D'Amore }
137288447a05SGarrett D'Amore
137388447a05SGarrett D'Amore audioixp_destroy(statep);
137488447a05SGarrett D'Amore return (DDI_SUCCESS);
137588447a05SGarrett D'Amore }
137688447a05SGarrett D'Amore
137788447a05SGarrett D'Amore /*
137888447a05SGarrett D'Amore * audioixp_destroy()
137988447a05SGarrett D'Amore *
138088447a05SGarrett D'Amore * Description:
138188447a05SGarrett D'Amore * This routine releases all resources held by the device instance,
138288447a05SGarrett D'Amore * as part of either detach or a failure in attach.
138388447a05SGarrett D'Amore *
138488447a05SGarrett D'Amore * Arguments:
138588447a05SGarrett D'Amore * audioixp_state_t *state The device soft state.
138688447a05SGarrett D'Amore */
1387*68c47f65SGarrett D'Amore static void
audioixp_destroy(audioixp_state_t * statep)138888447a05SGarrett D'Amore audioixp_destroy(audioixp_state_t *statep)
138988447a05SGarrett D'Amore {
139088447a05SGarrett D'Amore /*
139188447a05SGarrett D'Amore * put the audio controller into quiet state, everything off
139288447a05SGarrett D'Amore */
139388447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA);
139488447a05SGarrett D'Amore CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA);
139588447a05SGarrett D'Amore
139688447a05SGarrett D'Amore audioixp_free_port(statep->play_port);
139788447a05SGarrett D'Amore audioixp_free_port(statep->rec_port);
139888447a05SGarrett D'Amore
139988447a05SGarrett D'Amore audioixp_unmap_regs(statep);
140088447a05SGarrett D'Amore
140188447a05SGarrett D'Amore if (statep->ac97) {
140288447a05SGarrett D'Amore ac97_free(statep->ac97);
140388447a05SGarrett D'Amore }
140488447a05SGarrett D'Amore
140588447a05SGarrett D'Amore if (statep->adev) {
140688447a05SGarrett D'Amore audio_dev_free(statep->adev);
140788447a05SGarrett D'Amore }
140888447a05SGarrett D'Amore
140988447a05SGarrett D'Amore mutex_destroy(&statep->inst_lock);
141088447a05SGarrett D'Amore kmem_free(statep, sizeof (*statep));
141188447a05SGarrett D'Amore }
1412