xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c (revision 453b56c79396ac22cee97f816f0cefa8494eeefc)
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 /*
222c30fa45SGarrett D'Amore  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2388447a05SGarrett D'Amore  */
2488447a05SGarrett D'Amore 
2588447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
2688447a05SGarrett D'Amore #include <sys/note.h>
2742c41cf8Slipeng sang - Sun Microsystems - Beijing China #include <sys/beep.h>
2888447a05SGarrett D'Amore #include <sys/pci.h>
2988447a05SGarrett D'Amore #include "audiohd.h"
3088447a05SGarrett D'Amore 
3188447a05SGarrett D'Amore #define	DRVNAME			"audiohd"
3268c47f65SGarrett D'Amore 
3388447a05SGarrett D'Amore /*
3488447a05SGarrett D'Amore  * Module linkage routines for the kernel
3588447a05SGarrett D'Amore  */
3688447a05SGarrett D'Amore static int audiohd_attach(dev_info_t *, ddi_attach_cmd_t);
3788447a05SGarrett D'Amore static int audiohd_detach(dev_info_t *, ddi_detach_cmd_t);
3888447a05SGarrett D'Amore static int audiohd_quiesce(dev_info_t *);
3988447a05SGarrett D'Amore static int audiohd_resume(audiohd_state_t *);
4088447a05SGarrett D'Amore static int audiohd_suspend(audiohd_state_t *);
4188447a05SGarrett D'Amore 
4288447a05SGarrett D'Amore /*
4388447a05SGarrett D'Amore  * Local routines
4488447a05SGarrett D'Amore  */
4588447a05SGarrett D'Amore static int audiohd_init_state(audiohd_state_t *, dev_info_t *);
4688447a05SGarrett D'Amore static int audiohd_init_pci(audiohd_state_t *, ddi_device_acc_attr_t *);
4788447a05SGarrett D'Amore static void audiohd_fini_pci(audiohd_state_t *);
4888447a05SGarrett D'Amore static int audiohd_reset_controller(audiohd_state_t *);
4988447a05SGarrett D'Amore static int audiohd_init_controller(audiohd_state_t *);
5088447a05SGarrett D'Amore static void audiohd_fini_controller(audiohd_state_t *);
5188447a05SGarrett D'Amore static void audiohd_stop_dma(audiohd_state_t *);
5288447a05SGarrett D'Amore static void audiohd_disable_intr(audiohd_state_t *);
5388447a05SGarrett D'Amore static int audiohd_create_codec(audiohd_state_t *);
5488447a05SGarrett D'Amore static void audiohd_build_path(audiohd_state_t *);
5588447a05SGarrett D'Amore static void audiohd_destroy_codec(audiohd_state_t *);
5688447a05SGarrett D'Amore static int audiohd_alloc_dma_mem(audiohd_state_t *, audiohd_dma_t *,
5788447a05SGarrett D'Amore     size_t, ddi_dma_attr_t *, uint_t);
585ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_finish_output_path(hda_codec_t *);
5988447a05SGarrett D'Amore static uint32_t audioha_codec_verb_get(void *, uint8_t,
6088447a05SGarrett D'Amore     uint8_t, uint16_t, uint8_t);
6188447a05SGarrett D'Amore static uint32_t audioha_codec_4bit_verb_get(void *, uint8_t,
6288447a05SGarrett D'Amore     uint8_t, uint16_t, uint16_t);
6388447a05SGarrett D'Amore static int audiohd_reinit_hda(audiohd_state_t *);
645ec2209cSZhao Edgar Liu - Sun Microsystems static int audiohd_response_from_codec(audiohd_state_t *,
655ec2209cSZhao Edgar Liu - Sun Microsystems     uint32_t *, uint32_t *);
665ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_restore_codec_gpio(audiohd_state_t *);
675ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_change_speaker_state(audiohd_state_t *, int);
685ec2209cSZhao Edgar Liu - Sun Microsystems static int audiohd_allocate_port(audiohd_state_t *);
695ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_free_port(audiohd_state_t *);
705ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_restore_path(audiohd_state_t *);
71c1cfefcdSZhao Edgar Liu - Sun Microsystems static void audiohd_create_controls(audiohd_state_t *);
725ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_get_channels(audiohd_state_t *);
735ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_init_path(audiohd_state_t *);
745ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_del_controls(audiohd_state_t *);
755ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_destroy(audiohd_state_t *);
765ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_beep_on(void *);
775ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_beep_off(void *);
785ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_beep_freq(void *, int);
795ec2209cSZhao Edgar Liu - Sun Microsystems static wid_t audiohd_find_beep(hda_codec_t *, wid_t, int);
805ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_build_beep_path(hda_codec_t *);
815ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_build_beep_amp(hda_codec_t *);
825ec2209cSZhao Edgar Liu - Sun Microsystems static void  audiohd_finish_beep_path(hda_codec_t *);
835ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_do_set_beep_volume(audiohd_state_t *,
845ec2209cSZhao Edgar Liu - Sun Microsystems     audiohd_path_t *, uint64_t);
855ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_set_beep_volume(audiohd_state_t *);
865ec2209cSZhao Edgar Liu - Sun Microsystems static int audiohd_set_beep(void *, uint64_t);
87989b958fSZhao Edgar Liu - Sun Microsystems static void audiohd_pin_sense(audiohd_state_t *, uint32_t, uint32_t);
8842c41cf8Slipeng sang - Sun Microsystems - Beijing China 
8942c41cf8Slipeng sang - Sun Microsystems - Beijing China static	int	audiohd_beep;
9042c41cf8Slipeng sang - Sun Microsystems - Beijing China static	int	audiohd_beep_divider;
9142c41cf8Slipeng sang - Sun Microsystems - Beijing China static	int	audiohd_beep_vol = 1;
9288447a05SGarrett D'Amore 
93ea463888SZhao Edgar Liu - Sun Microsystems /* Warlock annotation */
94ea463888SZhao Edgar Liu - Sun Microsystems _NOTE(SCHEME_PROTECTS_DATA("unshared data", audiohd_beep))
95ea463888SZhao Edgar Liu - Sun Microsystems _NOTE(SCHEME_PROTECTS_DATA("unshared data", audiohd_beep_divider))
96ea463888SZhao Edgar Liu - Sun Microsystems _NOTE(SCHEME_PROTECTS_DATA("unshared data", audiohd_beep_vol))
97ea463888SZhao Edgar Liu - Sun Microsystems 
9888447a05SGarrett D'Amore static ddi_device_acc_attr_t hda_dev_accattr = {
9988447a05SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
10088447a05SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
10188447a05SGarrett D'Amore 	DDI_STRICTORDER_ACC
10288447a05SGarrett D'Amore };
10388447a05SGarrett D'Amore 
10488447a05SGarrett D'Amore static const char *audiohd_dtypes[] = {
10588447a05SGarrett D'Amore 	AUDIO_PORT_LINEOUT,
10688447a05SGarrett D'Amore 	AUDIO_PORT_SPEAKER,
10788447a05SGarrett D'Amore 	AUDIO_PORT_HEADPHONES,
10888447a05SGarrett D'Amore 	AUDIO_PORT_CD,
10988447a05SGarrett D'Amore 	AUDIO_PORT_SPDIFOUT,
11088447a05SGarrett D'Amore 	AUDIO_PORT_DIGOUT,
11188447a05SGarrett D'Amore 	AUDIO_PORT_MODEM,
11288447a05SGarrett D'Amore 	AUDIO_PORT_HANDSET,
11388447a05SGarrett D'Amore 	AUDIO_PORT_LINEIN,
11488447a05SGarrett D'Amore 	AUDIO_PORT_AUX1IN,
11588447a05SGarrett D'Amore 	AUDIO_PORT_MIC,
11688447a05SGarrett D'Amore 	AUDIO_PORT_PHONE,
11788447a05SGarrett D'Amore 	AUDIO_PORT_SPDIFIN,
11888447a05SGarrett D'Amore 	AUDIO_PORT_DIGIN,
119c1cfefcdSZhao Edgar Liu - Sun Microsystems 	AUDIO_PORT_STEREOMIX,
12088447a05SGarrett D'Amore 	AUDIO_PORT_NONE,	/* reserved port, don't use */
12188447a05SGarrett D'Amore 	AUDIO_PORT_OTHER,
12288447a05SGarrett D'Amore 	NULL,
12388447a05SGarrett D'Amore };
12488447a05SGarrett D'Amore 
125cbe6566fSZhao Edgar Liu - Sun Microsystems static audiohd_codec_info_t audiohd_codecs[] = {
126cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x1002aa01, "ATI R600 HDMI", 0x0},
127cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10134206, "Cirrus CS4206", 0x0},
128cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10de0002, "nVidia MCP78 HDMI", 0x0},
1295ec2209cSZhao Edgar Liu - Sun Microsystems 	{0x10de0003, "nVidia MCP78 HDMI", 0x0},
1305ec2209cSZhao Edgar Liu - Sun Microsystems 	{0x10de0006, "nVidia MCP78 HDMI", 0x0},
131cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10de0007, "nVidia MCP7A HDMI", 0x0},
132cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0260, "Realtek ALC260", (NO_GPIO)},
133ee97b734SZhao Edgar Liu - Sun Microsystems 	{0x10ec0262, "Realtek ALC262", (NO_GPIO | EN_PIN_BEEP)},
134cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0268, "Realtek ALC268", 0x0},
135cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0272, "Realtek ALC272", 0x0},
136cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0662, "Realtek ALC662", 0x0},
137cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0663, "Realtek ALC663", 0x0},
138cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0861, "Realtek ALC861", 0x0},
139cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0862, "Realtek ALC862", 0x0},
140cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0880, "Realtek ALC880", 0x0},
141cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0882, "Realtek ALC882", 0x0},
142cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0883, "Realtek ALC883", 0x0},
143cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0885, "Realtek ALC885", 0x0},
144cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x10ec0888, "Realtek ALC888", (NO_SPDIF)},
145*453b56c7SYuri Pankov 	{0x111d7603, "Integrated Devices 92HD75B3X5", (NO_MIXER)},
146cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x111d7608, "Integrated Devices 92HD75B2X5", (NO_MIXER)},
147cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x111d76b2, "Integrated Devices 92HD71B7X", (NO_MIXER)},
148cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d4194a, "Analog Devices AD1984A", 0x0},
149cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d41981, "Analog Devices AD1981", (NO_MIXER)},
150cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d41983, "Analog Devices AD1983", 0x0},
151cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d41984, "Analog Devices AD1984", 0x0},
152cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d41986, "Analog Devices AD1986A", 0x0},
153cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d41988, "Analog Devices AD1988A", 0x0},
154cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x11d4198b, "Analog Devices AD1988B", 0x0},
155cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x13f69880, "CMedia CMI19880", 0x0},
156cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x14f15045, "Conexant CX20549", (NO_MIXER)},
157cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x14f15051, "Conexant CX20561", 0x0},
158cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x434d4980, "CMedia CMI19880", 0x0},
159cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x80862802, "Intel HDMI", 0x0},
160cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847610, "Sigmatel STAC9230XN", 0x0},
161cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847611, "Sigmatel STAC9230DN", 0x0},
162cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847612, "Sigmatel STAC9230XT", 0x0},
163cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847613, "Sigmatel STAC9230DT", 0x0},
164cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847614, "Sigmatel STAC9229X", 0x0},
165cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847615, "Sigmatel STAC9229D", 0x0},
166cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847616, "Sigmatel STAC9228X", 0x0},
167cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847617, "Sigmatel STAC9228D", 0x0},
168cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847618, "Sigmatel STAC9227X", 0x0},
169cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847619, "Sigmatel STAC9227D", 0x0},
170cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847620, "Sigmatel STAC9274", 0x0},
171cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847621, "Sigmatel STAC9274D", 0x0},
172cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847622, "Sigmatel STAC9273X", 0x0},
173cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847623, "Sigmatel STAC9273D", 0x0},
174cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847624, "Sigmatel STAC9272X", 0x0},
175cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847625, "Sigmatel STAC9272D", 0x0},
176cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847626, "Sigmatel STAC9271X", 0x0},
177cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847627, "Sigmatel STAC9271D", 0x0},
178cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847628, "Sigmatel STAC9274X5NH", 0x0},
179cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847629, "Sigmatel STAC9274D5NH", 0x0},
180cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847662, "Sigmatel STAC9872AK", 0x0},
181cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847664, "Sigmatel STAC9872K", 0x0},
182cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847680, "Sigmatel STAC9221A1", 0x0},
183cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847680, "Sigmatel STAC9221A1", 0x0},
184cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847681, "Sigmatel STAC9220D", 0x0},
185cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847682, "Sigmatel STAC9221", 0x0},
186cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847683, "Sigmatel STAC9221D", 0x0},
187cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847690, "Sigmatel STAC9200", 0x0},
188cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a0, "Sigmatel STAC9205", 0x0},
189cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a1, "Sigmatel STAC9205D", 0x0},
190cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a2, "Sigmatel STAC9204", 0x0},
191cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a3, "Sigmatel STAC9204D", 0x0},
192cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a4, "Sigmatel STAC9255", 0x0},
193cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a5, "Sigmatel STAC9255D", 0x0},
194cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a6, "Sigmatel STAC9254", 0x0},
195cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x838476a7, "Sigmatel STAC9254D", 0x0},
196cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847880, "Sigmatel STAC9220A1", 0x0},
197cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x83847882, "Sigmatel STAC9220A2", 0x0},
198cbe6566fSZhao Edgar Liu - Sun Microsystems 	{0x0, "Unknown 0x00000000", 0x0},
199cbe6566fSZhao Edgar Liu - Sun Microsystems };
200cbe6566fSZhao Edgar Liu - Sun Microsystems 
20188447a05SGarrett D'Amore static void
audiohd_set_chipset_info(audiohd_state_t * statep)20288447a05SGarrett D'Amore audiohd_set_chipset_info(audiohd_state_t *statep)
20388447a05SGarrett D'Amore {
20488447a05SGarrett D'Amore 	uint32_t		devid;
20588447a05SGarrett D'Amore 	const char		*name;
20688447a05SGarrett D'Amore 	const char		*vers;
20788447a05SGarrett D'Amore 
20888447a05SGarrett D'Amore 	devid = pci_config_get16(statep->hda_pci_handle, PCI_CONF_VENID);
20988447a05SGarrett D'Amore 	devid <<= 16;
21088447a05SGarrett D'Amore 	devid |= pci_config_get16(statep->hda_pci_handle, PCI_CONF_DEVID);
21189e1f902SZhao Edgar Liu - Sun Microsystems 	statep->devid = devid;
21288447a05SGarrett D'Amore 
21388447a05SGarrett D'Amore 	name = AUDIOHD_DEV_CONFIG;
21488447a05SGarrett D'Amore 	vers = AUDIOHD_DEV_VERSION;
21588447a05SGarrett D'Amore 
21688447a05SGarrett D'Amore 	switch (devid) {
21726ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x1002437b:
21826ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "ATI HD Audio";
21926ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "SB450";
22088447a05SGarrett D'Amore 		break;
22126ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x10024383:
22226ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "ATI HD Audio";
22326ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "SB600";
22488447a05SGarrett D'Amore 		break;
22570feb41cSZhao Edgar Liu - Sun Microsystems 	case 0x10029442:
22670feb41cSZhao Edgar Liu - Sun Microsystems 		name = "ATI HD Audio";
22770feb41cSZhao Edgar Liu - Sun Microsystems 		vers = "Radeon HD 4850";
22870feb41cSZhao Edgar Liu - Sun Microsystems 		break;
229ee97b734SZhao Edgar Liu - Sun Microsystems 	case 0x1002aa30:
230ee97b734SZhao Edgar Liu - Sun Microsystems 		name = "ATI HD Audio";
231ee97b734SZhao Edgar Liu - Sun Microsystems 		vers = "HD 48x0";
232ee97b734SZhao Edgar Liu - Sun Microsystems 		break;
23326ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x1002aa38:
23426ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "ATI HD Audio";
23526ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "Radeon HD 4670";
23688447a05SGarrett D'Amore 		break;
23788447a05SGarrett D'Amore 	case 0x10de026c:
23888447a05SGarrett D'Amore 		name = "NVIDIA HD Audio";
23989e1f902SZhao Edgar Liu - Sun Microsystems 		vers = "MCP51";
24088447a05SGarrett D'Amore 		break;
2410c240c64SZhao Edgar Liu - Sun Microsystems 	case 0x10de0371:
2420c240c64SZhao Edgar Liu - Sun Microsystems 		name = "NVIDIA HD Audio";
2430c240c64SZhao Edgar Liu - Sun Microsystems 		vers = "MCP55";
2440c240c64SZhao Edgar Liu - Sun Microsystems 		break;
24588447a05SGarrett D'Amore 	case 0x10de03e4:
24688447a05SGarrett D'Amore 		name = "NVIDIA HD Audio";
24788447a05SGarrett D'Amore 		vers = "MCP61";
24888447a05SGarrett D'Amore 		break;
2490c240c64SZhao Edgar Liu - Sun Microsystems 	case 0x10de03f0:
2500c240c64SZhao Edgar Liu - Sun Microsystems 		name = "NVIDIA HD Audio";
2510c240c64SZhao Edgar Liu - Sun Microsystems 		vers = "MCP61A";
2520c240c64SZhao Edgar Liu - Sun Microsystems 		break;
25388447a05SGarrett D'Amore 	case 0x10de044a:
25488447a05SGarrett D'Amore 		name = "NVIDIA HD Audio";
25588447a05SGarrett D'Amore 		vers = "MCP65";
25688447a05SGarrett D'Amore 		break;
25788447a05SGarrett D'Amore 	case 0x10de055c:
25888447a05SGarrett D'Amore 		name = "NVIDIA HD Audio";
25988447a05SGarrett D'Amore 		vers = "MCP67";
26088447a05SGarrett D'Amore 		break;
2610c240c64SZhao Edgar Liu - Sun Microsystems 	case 0x10de0774:
2620c240c64SZhao Edgar Liu - Sun Microsystems 		name = "NVIDIA HD Audio";
2630c240c64SZhao Edgar Liu - Sun Microsystems 		vers = "MCP78S";
2640c240c64SZhao Edgar Liu - Sun Microsystems 		break;
26589e1f902SZhao Edgar Liu - Sun Microsystems 	case 0x10de0ac0:
26689e1f902SZhao Edgar Liu - Sun Microsystems 		name = "NVIDIA HD Audio";
26789e1f902SZhao Edgar Liu - Sun Microsystems 		vers = "MCP79";
26889e1f902SZhao Edgar Liu - Sun Microsystems 		break;
26988447a05SGarrett D'Amore 	case 0x11063288:
27088447a05SGarrett D'Amore 		name = "VIA HD Audio";
27188447a05SGarrett D'Amore 		vers = "HDA";
27288447a05SGarrett D'Amore 		break;
27326ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x80862668:
27426ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "Intel HD Audio";
27526ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "ICH6";
27626ae4a35SZhao Edgar Liu - Sun Microsystems 		break;
27726ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x808627d8:
27826ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "Intel HD Audio";
27926ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "ICH7";
28026ae4a35SZhao Edgar Liu - Sun Microsystems 		break;
28126ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x8086284b:
28226ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "Intel HD Audio";
28326ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "ICH8";
28426ae4a35SZhao Edgar Liu - Sun Microsystems 		break;
28526ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x8086293e:
28626ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "Intel HD Audio";
28726ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "ICH9";
28826ae4a35SZhao Edgar Liu - Sun Microsystems 		break;
28926ae4a35SZhao Edgar Liu - Sun Microsystems 	case 0x80863a3e:
29026ae4a35SZhao Edgar Liu - Sun Microsystems 		name = "Intel HD Audio";
29126ae4a35SZhao Edgar Liu - Sun Microsystems 		vers = "ICH10";
29226ae4a35SZhao Edgar Liu - Sun Microsystems 		break;
293*453b56c7SYuri Pankov 	case 0x80863b56:
294*453b56c7SYuri Pankov 		name = "Intel HD Audio";
295*453b56c7SYuri Pankov 		vers = "PCH";
296*453b56c7SYuri Pankov 		break;
29788447a05SGarrett D'Amore 	}
29888447a05SGarrett D'Amore 	/* set device information */
29988447a05SGarrett D'Amore 	audio_dev_set_description(statep->adev, name);
30088447a05SGarrett D'Amore 	audio_dev_set_version(statep->adev, vers);
30188447a05SGarrett D'Amore }
30288447a05SGarrett D'Amore 
30388447a05SGarrett D'Amore static int
audiohd_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30488447a05SGarrett D'Amore audiohd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30588447a05SGarrett D'Amore {
30688447a05SGarrett D'Amore 	audiohd_state_t		*statep;
30788447a05SGarrett D'Amore 	int			instance;
30888447a05SGarrett D'Amore 
30988447a05SGarrett D'Amore 	instance = ddi_get_instance(dip);
31088447a05SGarrett D'Amore 	switch (cmd) {
31188447a05SGarrett D'Amore 	case DDI_ATTACH:
31288447a05SGarrett D'Amore 		break;
31388447a05SGarrett D'Amore 
31488447a05SGarrett D'Amore 	case DDI_RESUME:
31588447a05SGarrett D'Amore 		statep = ddi_get_driver_private(dip);
31688447a05SGarrett D'Amore 		ASSERT(statep != NULL);
31788447a05SGarrett D'Amore 		return (audiohd_resume(statep));
31888447a05SGarrett D'Amore 
31988447a05SGarrett D'Amore 	default:
32088447a05SGarrett D'Amore 		return (DDI_FAILURE);
32188447a05SGarrett D'Amore 	}
32288447a05SGarrett D'Amore 
32388447a05SGarrett D'Amore 	/* allocate the soft state structure */
32488447a05SGarrett D'Amore 	statep = kmem_zalloc(sizeof (*statep), KM_SLEEP);
32588447a05SGarrett D'Amore 	ddi_set_driver_private(dip, statep);
32688447a05SGarrett D'Amore 
327ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_init(&statep->hda_mutex, NULL, MUTEX_DRIVER, 0);
328ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
329ea463888SZhao Edgar Liu - Sun Microsystems 
33088447a05SGarrett D'Amore 	/* interrupt cookie and initialize mutex */
331c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_init_state(statep, dip) != DDI_SUCCESS) {
332e7236f70SZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(NULL, "audiohd_init_state failed");
333c6e681c0SYang-Rong Jerry Zhou 		goto error;
33488447a05SGarrett D'Amore 	}
33588447a05SGarrett D'Amore 
33688447a05SGarrett D'Amore 	/* Set PCI command register to enable bus master and memeory I/O */
337c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_init_pci(statep, &hda_dev_accattr) != DDI_SUCCESS) {
33888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
33988447a05SGarrett D'Amore 		    "couldn't init pci regs");
340c6e681c0SYang-Rong Jerry Zhou 		goto error;
34188447a05SGarrett D'Amore 	}
34288447a05SGarrett D'Amore 
34388447a05SGarrett D'Amore 	audiohd_set_chipset_info(statep);
34488447a05SGarrett D'Amore 
345c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_init_controller(statep) != DDI_SUCCESS) {
34688447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
34788447a05SGarrett D'Amore 		    "couldn't init controller");
348c6e681c0SYang-Rong Jerry Zhou 		goto error;
34988447a05SGarrett D'Amore 	}
35088447a05SGarrett D'Amore 
351c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_create_codec(statep) != DDI_SUCCESS) {
35288447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
35388447a05SGarrett D'Amore 		    "couldn't create codec");
354c6e681c0SYang-Rong Jerry Zhou 		goto error;
35588447a05SGarrett D'Amore 	}
35688447a05SGarrett D'Amore 
35788447a05SGarrett D'Amore 	audiohd_build_path(statep);
35888447a05SGarrett D'Amore 
35988447a05SGarrett D'Amore 	audiohd_get_channels(statep);
36088447a05SGarrett D'Amore 	if (audiohd_allocate_port(statep) != DDI_SUCCESS) {
36188447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "allocate port failure");
362c6e681c0SYang-Rong Jerry Zhou 		goto error;
36388447a05SGarrett D'Amore 	}
36488447a05SGarrett D'Amore 	audiohd_init_path(statep);
36588447a05SGarrett D'Amore 	/* set up kernel statistics */
36688447a05SGarrett D'Amore 	if ((statep->hda_ksp = kstat_create(DRVNAME, instance,
36788447a05SGarrett D'Amore 	    DRVNAME, "controller", KSTAT_TYPE_INTR, 1,
36888447a05SGarrett D'Amore 	    KSTAT_FLAG_PERSISTENT)) != NULL) {
36988447a05SGarrett D'Amore 		kstat_install(statep->hda_ksp);
37088447a05SGarrett D'Amore 	}
37188447a05SGarrett D'Amore 
37288447a05SGarrett D'Amore 	/* disable interrupts and clear interrupt status */
37388447a05SGarrett D'Amore 	audiohd_disable_intr(statep);
37488447a05SGarrett D'Amore 
37588447a05SGarrett D'Amore 	/*
37688447a05SGarrett D'Amore 	 * Register audio controls.
37788447a05SGarrett D'Amore 	 */
378c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_create_controls(statep);
379c1cfefcdSZhao Edgar Liu - Sun Microsystems 
38088447a05SGarrett D'Amore 	if (audio_dev_register(statep->adev) != DDI_SUCCESS) {
38188447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
38288447a05SGarrett D'Amore 		    "unable to register with framework");
383c6e681c0SYang-Rong Jerry Zhou 		goto error;
38488447a05SGarrett D'Amore 	}
38588447a05SGarrett D'Amore 	ddi_report_dev(dip);
38688447a05SGarrett D'Amore 
387ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
38888447a05SGarrett D'Amore 	return (DDI_SUCCESS);
389c6e681c0SYang-Rong Jerry Zhou error:
390ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
391c6e681c0SYang-Rong Jerry Zhou 	audiohd_destroy(statep);
39288447a05SGarrett D'Amore 	return (DDI_FAILURE);
39388447a05SGarrett D'Amore }
39488447a05SGarrett D'Amore 
39588447a05SGarrett D'Amore static int
audiohd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)39688447a05SGarrett D'Amore audiohd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
39788447a05SGarrett D'Amore {
39888447a05SGarrett D'Amore 	audiohd_state_t		*statep;
39988447a05SGarrett D'Amore 
40088447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
40188447a05SGarrett D'Amore 	ASSERT(statep != NULL);
40288447a05SGarrett D'Amore 
40388447a05SGarrett D'Amore 	switch (cmd) {
40488447a05SGarrett D'Amore 	case DDI_DETACH:
40588447a05SGarrett D'Amore 		break;
40688447a05SGarrett D'Amore 
40788447a05SGarrett D'Amore 	case DDI_SUSPEND:
40888447a05SGarrett D'Amore 		return (audiohd_suspend(statep));
40988447a05SGarrett D'Amore 
41088447a05SGarrett D'Amore 	default:
41188447a05SGarrett D'Amore 		return (DDI_FAILURE);
41288447a05SGarrett D'Amore 	}
41388447a05SGarrett D'Amore 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS)
41488447a05SGarrett D'Amore 		return (DDI_FAILURE);
41588447a05SGarrett D'Amore 
41642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (audiohd_beep)
41742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		(void) beep_fini();
418c6e681c0SYang-Rong Jerry Zhou 	audiohd_destroy(statep);
41988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
42088447a05SGarrett D'Amore }
42188447a05SGarrett D'Amore 
42288447a05SGarrett D'Amore static struct dev_ops audiohd_dev_ops = {
42388447a05SGarrett D'Amore 	DEVO_REV,		/* rev */
42488447a05SGarrett D'Amore 	0,			/* refcnt */
42588447a05SGarrett D'Amore 	NULL,			/* getinfo */
42688447a05SGarrett D'Amore 	nulldev,		/* identify */
42788447a05SGarrett D'Amore 	nulldev,		/* probe */
42888447a05SGarrett D'Amore 	audiohd_attach,		/* attach */
42988447a05SGarrett D'Amore 	audiohd_detach,		/* detach */
43088447a05SGarrett D'Amore 	nodev,			/* reset */
43188447a05SGarrett D'Amore 	NULL,			/* cb_ops */
43288447a05SGarrett D'Amore 	NULL,			/* bus_ops */
43388447a05SGarrett D'Amore 	NULL,			/* power */
43488447a05SGarrett D'Amore 	audiohd_quiesce,	/* quiesce */
43588447a05SGarrett D'Amore };
43688447a05SGarrett D'Amore 
43788447a05SGarrett D'Amore static struct modldrv audiohd_modldrv = {
43888447a05SGarrett D'Amore 	&mod_driverops,			/* drv_modops */
43988447a05SGarrett D'Amore 	"AudioHD",			/* linkinfo */
44088447a05SGarrett D'Amore 	&audiohd_dev_ops,		/* dev_ops */
44188447a05SGarrett D'Amore };
44288447a05SGarrett D'Amore 
44388447a05SGarrett D'Amore static struct modlinkage modlinkage = {
44488447a05SGarrett D'Amore 	MODREV_1,
44588447a05SGarrett D'Amore 	{ &audiohd_modldrv, NULL }
44688447a05SGarrett D'Amore };
44788447a05SGarrett D'Amore 
44888447a05SGarrett D'Amore int
_init(void)44988447a05SGarrett D'Amore _init(void)
45088447a05SGarrett D'Amore {
45188447a05SGarrett D'Amore 	int	rv;
45288447a05SGarrett D'Amore 
45388447a05SGarrett D'Amore 	audio_init_ops(&audiohd_dev_ops, DRVNAME);
45488447a05SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
45588447a05SGarrett D'Amore 		audio_fini_ops(&audiohd_dev_ops);
45688447a05SGarrett D'Amore 	}
45788447a05SGarrett D'Amore 	return (rv);
45888447a05SGarrett D'Amore }
45988447a05SGarrett D'Amore 
46088447a05SGarrett D'Amore int
_fini(void)46188447a05SGarrett D'Amore _fini(void)
46288447a05SGarrett D'Amore {
46388447a05SGarrett D'Amore 	int	rv;
46488447a05SGarrett D'Amore 
46588447a05SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
46688447a05SGarrett D'Amore 		audio_fini_ops(&audiohd_dev_ops);
46788447a05SGarrett D'Amore 	}
46888447a05SGarrett D'Amore 	return (rv);
46988447a05SGarrett D'Amore }
47088447a05SGarrett D'Amore 
47188447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)47288447a05SGarrett D'Amore _info(struct modinfo *modinfop)
47388447a05SGarrett D'Amore {
47488447a05SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
47588447a05SGarrett D'Amore }
47688447a05SGarrett D'Amore 
47788447a05SGarrett D'Amore /*
47888447a05SGarrett D'Amore  * Audio routines
47988447a05SGarrett D'Amore  */
48088447a05SGarrett D'Amore 
48188447a05SGarrett D'Amore static int
audiohd_engine_format(void * arg)48288447a05SGarrett D'Amore audiohd_engine_format(void *arg)
48388447a05SGarrett D'Amore {
484a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_port_t *port = arg;
485a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_state_t *statep = port->statep;
48688447a05SGarrett D'Amore 
487a33ad26eSZhao Edgar Liu - Sun Microsystems 	switch (statep->sample_bit_depth) {
488a33ad26eSZhao Edgar Liu - Sun Microsystems 	case AUDIOHD_BIT_DEPTH24:
489a33ad26eSZhao Edgar Liu - Sun Microsystems 		return (AUDIO_FORMAT_S32_LE);
490a33ad26eSZhao Edgar Liu - Sun Microsystems 	case AUDIOHD_BIT_DEPTH16:
491a33ad26eSZhao Edgar Liu - Sun Microsystems 	default:
49288447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S16_LE);
49388447a05SGarrett D'Amore 	}
494a33ad26eSZhao Edgar Liu - Sun Microsystems }
49588447a05SGarrett D'Amore 
49688447a05SGarrett D'Amore static int
audiohd_engine_channels(void * arg)49788447a05SGarrett D'Amore audiohd_engine_channels(void *arg)
49888447a05SGarrett D'Amore {
49988447a05SGarrett D'Amore 	audiohd_port_t *port = arg;
50088447a05SGarrett D'Amore 
50188447a05SGarrett D'Amore 	return (port->nchan);
50288447a05SGarrett D'Amore }
50388447a05SGarrett D'Amore 
50488447a05SGarrett D'Amore static int
audiohd_engine_rate(void * arg)50588447a05SGarrett D'Amore audiohd_engine_rate(void *arg)
50688447a05SGarrett D'Amore {
507a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_port_t *port = arg;
508a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_state_t *statep = port->statep;
50988447a05SGarrett D'Amore 
510a33ad26eSZhao Edgar Liu - Sun Microsystems 	return (statep->sample_rate);
51188447a05SGarrett D'Amore }
512c6e681c0SYang-Rong Jerry Zhou static void
audiohd_free_path(audiohd_state_t * statep)513c6e681c0SYang-Rong Jerry Zhou audiohd_free_path(audiohd_state_t *statep)
514c6e681c0SYang-Rong Jerry Zhou {
515c6e681c0SYang-Rong Jerry Zhou 	audiohd_path_t		*path;
516c6e681c0SYang-Rong Jerry Zhou 	int			i;
51788447a05SGarrett D'Amore 
518c6e681c0SYang-Rong Jerry Zhou 	for (i = 0; i < statep->pathnum; i++) {
519c6e681c0SYang-Rong Jerry Zhou 		if (statep->path[i]) {
520c6e681c0SYang-Rong Jerry Zhou 			path = statep->path[i];
521c6e681c0SYang-Rong Jerry Zhou 			kmem_free(path, sizeof (audiohd_path_t));
522c6e681c0SYang-Rong Jerry Zhou 		}
523c6e681c0SYang-Rong Jerry Zhou 	}
524c6e681c0SYang-Rong Jerry Zhou }
525c6e681c0SYang-Rong Jerry Zhou static void
audiohd_destroy(audiohd_state_t * statep)526c6e681c0SYang-Rong Jerry Zhou audiohd_destroy(audiohd_state_t *statep)
527c6e681c0SYang-Rong Jerry Zhou {
528ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
529c6e681c0SYang-Rong Jerry Zhou 	audiohd_stop_dma(statep);
530c6e681c0SYang-Rong Jerry Zhou 	if (statep->hda_ksp)
531c6e681c0SYang-Rong Jerry Zhou 		kstat_delete(statep->hda_ksp);
532c6e681c0SYang-Rong Jerry Zhou 	audiohd_free_port(statep);
533c6e681c0SYang-Rong Jerry Zhou 	audiohd_free_path(statep);
534c6e681c0SYang-Rong Jerry Zhou 	audiohd_destroy_codec(statep);
535c6e681c0SYang-Rong Jerry Zhou 	audiohd_del_controls(statep);
536c6e681c0SYang-Rong Jerry Zhou 	audiohd_fini_controller(statep);
537c6e681c0SYang-Rong Jerry Zhou 	audiohd_fini_pci(statep);
538ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
539c6e681c0SYang-Rong Jerry Zhou 	mutex_destroy(&statep->hda_mutex);
540c6e681c0SYang-Rong Jerry Zhou 	if (statep->adev)
541c6e681c0SYang-Rong Jerry Zhou 		audio_dev_free(statep->adev);
542c6e681c0SYang-Rong Jerry Zhou 	kmem_free(statep, sizeof (*statep));
543c6e681c0SYang-Rong Jerry Zhou }
54488447a05SGarrett D'Amore /*
54588447a05SGarrett D'Amore  * get the max channels the hardware supported
54688447a05SGarrett D'Amore  */
54788447a05SGarrett D'Amore static void
audiohd_get_channels(audiohd_state_t * statep)54888447a05SGarrett D'Amore audiohd_get_channels(audiohd_state_t *statep)
54988447a05SGarrett D'Amore {
55088447a05SGarrett D'Amore 	int		i;
55188447a05SGarrett D'Amore 	uint8_t		maxp, assoc;
55288447a05SGarrett D'Amore 
55388447a05SGarrett D'Amore 	maxp = 2;
55488447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_MAX_ASSOC; i++) {
55588447a05SGarrett D'Amore 		if (maxp < statep->chann[i]) {
55688447a05SGarrett D'Amore 			maxp = statep->chann[i];
55788447a05SGarrett D'Amore 			assoc = i;
55888447a05SGarrett D'Amore 		}
55988447a05SGarrett D'Amore 	}
56088447a05SGarrett D'Amore 	statep->pchan = maxp;
56188447a05SGarrett D'Amore 	statep->assoc = assoc;
56288447a05SGarrett D'Amore 	/* for record, support stereo so far */
56388447a05SGarrett D'Amore 	statep->rchan = 2;
56488447a05SGarrett D'Amore }
56588447a05SGarrett D'Amore static void
audiohd_init_play_path(audiohd_path_t * path)56688447a05SGarrett D'Amore audiohd_init_play_path(audiohd_path_t *path)
56788447a05SGarrett D'Amore {
56888447a05SGarrett D'Amore 	int				i;
56988447a05SGarrett D'Amore 	uint32_t			ctrl;
57088447a05SGarrett D'Amore 	uint8_t				ctrl8;
57188447a05SGarrett D'Amore 	uint8_t				nchann;
57288447a05SGarrett D'Amore 	audiohd_widget_t		*widget;
57388447a05SGarrett D'Amore 	audiohd_pin_t			*pin;
57488447a05SGarrett D'Amore 	wid_t				wid;
57588447a05SGarrett D'Amore 	audiohd_pin_color_t		color;
57688447a05SGarrett D'Amore 
57788447a05SGarrett D'Amore 	audiohd_state_t		*statep = path->statep;
57888447a05SGarrett D'Amore 	hda_codec_t		*codec = path->codec;
57988447a05SGarrett D'Amore 
58088447a05SGarrett D'Amore 	/* enable SPDIF output */
58188447a05SGarrett D'Amore 	for (i = 0; i < path->pin_nums; i++) {
58288447a05SGarrett D'Amore 		wid = path->pin_wid[i];
58388447a05SGarrett D'Amore 		widget = codec->widget[wid];
58488447a05SGarrett D'Amore 		pin = (audiohd_pin_t *)widget->priv;
58588447a05SGarrett D'Amore 		if (pin->device == DTYPE_SPDIF_OUT) {
58688447a05SGarrett D'Amore 			ctrl = audioha_codec_verb_get(
58788447a05SGarrett D'Amore 			    statep,
58888447a05SGarrett D'Amore 			    codec->index,
58988447a05SGarrett D'Amore 			    path->adda_wid,
59088447a05SGarrett D'Amore 			    AUDIOHDC_VERB_GET_SPDIF_CTL,
59188447a05SGarrett D'Amore 			    0);
59288447a05SGarrett D'Amore 			ctrl |= AUDIOHD_SPDIF_ON;
59388447a05SGarrett D'Amore 			ctrl8 = ctrl &
59488447a05SGarrett D'Amore 			    AUDIOHD_SPDIF_MASK;
59588447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(
59688447a05SGarrett D'Amore 			    statep,
59788447a05SGarrett D'Amore 			    codec->index,
59888447a05SGarrett D'Amore 			    path->adda_wid,
59988447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_SPDIF_LCL,
60088447a05SGarrett D'Amore 			    ctrl8);
60165a41de7SYang-Rong Jerry Zhou 			/*
60265a41de7SYang-Rong Jerry Zhou 			 * We find that on intel ICH10 chipset with codec
60365a41de7SYang-Rong Jerry Zhou 			 * ALC888, audio is scratchy if we set the tag on the
60465a41de7SYang-Rong Jerry Zhou 			 * SPDIF path. So we just return here without setting
60565a41de7SYang-Rong Jerry Zhou 			 * the tag for the path as a workaround.
60665a41de7SYang-Rong Jerry Zhou 			 */
6075ec2209cSZhao Edgar Liu - Sun Microsystems 			if (codec->codec_info->flags & NO_SPDIF)
60865a41de7SYang-Rong Jerry Zhou 				return;
60965a41de7SYang-Rong Jerry Zhou 		}
61088447a05SGarrett D'Amore 	}
61188447a05SGarrett D'Amore 	wid = path->pin_wid[0];
61288447a05SGarrett D'Amore 	widget = codec->widget[wid];
61388447a05SGarrett D'Amore 	pin = (audiohd_pin_t *)widget->priv;
61488447a05SGarrett D'Amore 
61588447a05SGarrett D'Amore 	/* two channels supported */
61688447a05SGarrett D'Amore 	if (pin->device == DTYPE_SPEAKER ||
617c1aa074aSYang-Rong Jerry Zhou 	    pin->device == DTYPE_HP_OUT ||
61888447a05SGarrett D'Amore 	    pin->assoc != statep->assoc) {
61988447a05SGarrett D'Amore 		(void) audioha_codec_verb_get(
62088447a05SGarrett D'Amore 		    statep,
62188447a05SGarrett D'Amore 		    codec->index,
62288447a05SGarrett D'Amore 		    path->adda_wid,
62388447a05SGarrett D'Amore 		    AUDIOHDC_VERB_SET_STREAM_CHANN,
62488447a05SGarrett D'Amore 		    statep->port[PORT_DAC]->index <<
62588447a05SGarrett D'Amore 		    AUDIOHD_PLAY_TAG_OFF);
62688447a05SGarrett D'Amore 		(void) audioha_codec_4bit_verb_get(
62788447a05SGarrett D'Amore 		    statep,
62888447a05SGarrett D'Amore 		    codec->index,
62988447a05SGarrett D'Amore 		    path->adda_wid,
63088447a05SGarrett D'Amore 		    AUDIOHDC_VERB_SET_CONV_FMT,
631a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->port[PORT_DAC]->format << 4 |
63288447a05SGarrett D'Amore 		    statep->pchan - 1);
63388447a05SGarrett D'Amore 	/* multichannel supported */
63488447a05SGarrett D'Amore 	} else {
63588447a05SGarrett D'Amore 		color = (pin->config >> AUDIOHD_PIN_CLR_OFF) &
63688447a05SGarrett D'Amore 		    AUDIOHD_PIN_CLR_MASK;
63788447a05SGarrett D'Amore 		switch (color) {
63888447a05SGarrett D'Amore 		case AUDIOHD_PIN_BLACK:
63988447a05SGarrett D'Amore 			nchann = statep->pchan - 2;
64088447a05SGarrett D'Amore 			break;
64188447a05SGarrett D'Amore 		case AUDIOHD_PIN_ORANGE:
64288447a05SGarrett D'Amore 			nchann = 2;
64388447a05SGarrett D'Amore 			break;
64488447a05SGarrett D'Amore 		case AUDIOHD_PIN_GREY:
64588447a05SGarrett D'Amore 			nchann = 4;
64688447a05SGarrett D'Amore 			break;
64788447a05SGarrett D'Amore 		case AUDIOHD_PIN_GREEN:
64888447a05SGarrett D'Amore 			nchann = 0;
64988447a05SGarrett D'Amore 			break;
65088447a05SGarrett D'Amore 		default:
65188447a05SGarrett D'Amore 			nchann = 0;
65288447a05SGarrett D'Amore 			break;
65388447a05SGarrett D'Amore 		}
65488447a05SGarrett D'Amore 		(void) audioha_codec_verb_get(statep,
65588447a05SGarrett D'Amore 		    codec->index,
65688447a05SGarrett D'Amore 		    path->adda_wid,
65788447a05SGarrett D'Amore 		    AUDIOHDC_VERB_SET_STREAM_CHANN,
65888447a05SGarrett D'Amore 		    statep->port[PORT_DAC]->index <<
65988447a05SGarrett D'Amore 		    AUDIOHD_PLAY_TAG_OFF |
66088447a05SGarrett D'Amore 		    nchann);
66188447a05SGarrett D'Amore 		(void) audioha_codec_4bit_verb_get(
66288447a05SGarrett D'Amore 		    statep,
66388447a05SGarrett D'Amore 		    codec->index,
66488447a05SGarrett D'Amore 		    path->adda_wid,
66588447a05SGarrett D'Amore 		    AUDIOHDC_VERB_SET_CONV_FMT,
666a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->port[PORT_DAC]->format << 4 |
66788447a05SGarrett D'Amore 		    statep->pchan - 1);
66888447a05SGarrett D'Amore 	}
66988447a05SGarrett D'Amore }
67088447a05SGarrett D'Amore static void
audiohd_init_record_path(audiohd_path_t * path)67188447a05SGarrett D'Amore audiohd_init_record_path(audiohd_path_t *path)
67288447a05SGarrett D'Amore {
67388447a05SGarrett D'Amore 	audiohd_state_t		*statep = path->statep;
67488447a05SGarrett D'Amore 	hda_codec_t		*codec = path->codec;
67588447a05SGarrett D'Amore 	int			i;
67688447a05SGarrett D'Amore 	wid_t			wid;
67788447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
67888447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
67988447a05SGarrett D'Amore 
68088447a05SGarrett D'Amore 	for (i = 0; i < path->pin_nums; i++) {
68188447a05SGarrett D'Amore 		wid = path->pin_wid[i];
68288447a05SGarrett D'Amore 		widget = codec->widget[wid];
68388447a05SGarrett D'Amore 		pin = (audiohd_pin_t *)widget->priv;
68488447a05SGarrett D'Amore 	/*
68588447a05SGarrett D'Amore 	 * Since there is no SPDIF input device available for test,
68688447a05SGarrett D'Amore 	 * we will use this code in the future to support SPDIF input
68788447a05SGarrett D'Amore 	 */
68888447a05SGarrett D'Amore #if 0
68988447a05SGarrett D'Amore 		if (pin->device == DTYPE_SPDIF_IN) {
69088447a05SGarrett D'Amore 			ctrl = audioha_codec_verb_get(
69188447a05SGarrett D'Amore 			    statep,
69288447a05SGarrett D'Amore 			    codec->index,
69388447a05SGarrett D'Amore 			    path->adda_wid,
69488447a05SGarrett D'Amore 			    AUDIOHDC_VERB_GET_SPDIF_CTL,
69588447a05SGarrett D'Amore 			    0);
69688447a05SGarrett D'Amore 			ctrl |= AUDIOHD_SPDIF_ON;
69788447a05SGarrett D'Amore 			ctrl8 = ctrl &
69888447a05SGarrett D'Amore 			    AUDIOHD_SPDIF_MASK;
69988447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(
70088447a05SGarrett D'Amore 			    statep,
70188447a05SGarrett D'Amore 			    codec->index,
70288447a05SGarrett D'Amore 			    path->adda_wid,
70388447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_SPDIF_LCL,
70488447a05SGarrett D'Amore 			    ctrl8);
70588447a05SGarrett D'Amore 			statep->inmask |= (1U << DTYPE_SPDIF_IN);
70688447a05SGarrett D'Amore 		}
70788447a05SGarrett D'Amore #endif
70888447a05SGarrett D'Amore 		if (pin->device == DTYPE_MIC_IN) {
70988447a05SGarrett D'Amore 			if (((pin->config >>
71088447a05SGarrett D'Amore 			    AUDIOHD_PIN_CONTP_OFF) &
71188447a05SGarrett D'Amore 			    AUDIOHD_PIN_CONTP_MASK) ==
71288447a05SGarrett D'Amore 			    AUDIOHD_PIN_CON_FIXED)
71388447a05SGarrett D'Amore 				statep->port[PORT_ADC]->index = path->tag;
71488447a05SGarrett D'Amore 		}
71588447a05SGarrett D'Amore 		if ((pin->device == DTYPE_LINE_IN) ||
71688447a05SGarrett D'Amore 		    (pin->device == DTYPE_CD) ||
71788447a05SGarrett D'Amore 		    (pin->device == DTYPE_MIC_IN)) {
71888447a05SGarrett D'Amore 			statep->inmask |= (1U << pin->device);
71988447a05SGarrett D'Amore 		}
72088447a05SGarrett D'Amore 	}
72188447a05SGarrett D'Amore 	(void) audioha_codec_verb_get(statep,
72288447a05SGarrett D'Amore 	    codec->index,
72388447a05SGarrett D'Amore 	    path->adda_wid,
72488447a05SGarrett D'Amore 	    AUDIOHDC_VERB_SET_STREAM_CHANN,
72588447a05SGarrett D'Amore 	    path->tag <<
72688447a05SGarrett D'Amore 	    AUDIOHD_REC_TAG_OFF);
72788447a05SGarrett D'Amore 	(void) audioha_codec_4bit_verb_get(statep,
72888447a05SGarrett D'Amore 	    codec->index,
72988447a05SGarrett D'Amore 	    path->adda_wid,
73088447a05SGarrett D'Amore 	    AUDIOHDC_VERB_SET_CONV_FMT,
731a33ad26eSZhao Edgar Liu - Sun Microsystems 	    statep->port[PORT_ADC]->format << 4 | statep->rchan - 1);
73288447a05SGarrett D'Amore }
733c1cfefcdSZhao Edgar Liu - Sun Microsystems 
73488447a05SGarrett D'Amore static void
audiohd_init_path(audiohd_state_t * statep)73588447a05SGarrett D'Amore audiohd_init_path(audiohd_state_t *statep)
73688447a05SGarrett D'Amore {
73788447a05SGarrett D'Amore 	int				i;
73888447a05SGarrett D'Amore 	audiohd_path_t			*path;
73988447a05SGarrett D'Amore 
74088447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
74188447a05SGarrett D'Amore 		path = statep->path[i];
74288447a05SGarrett D'Amore 		if (!path)
74388447a05SGarrett D'Amore 			continue;
74488447a05SGarrett D'Amore 		switch (path->path_type) {
74588447a05SGarrett D'Amore 		case PLAY:
74688447a05SGarrett D'Amore 			audiohd_init_play_path(path);
74788447a05SGarrett D'Amore 			break;
74888447a05SGarrett D'Amore 		case RECORD:
74988447a05SGarrett D'Amore 			audiohd_init_record_path(path);
75088447a05SGarrett D'Amore 			break;
75188447a05SGarrett D'Amore 		default:
75288447a05SGarrett D'Amore 			break;
75388447a05SGarrett D'Amore 		}
75488447a05SGarrett D'Amore 	}
75588447a05SGarrett D'Amore 	statep->in_port = 0;
75688447a05SGarrett D'Amore }
75788447a05SGarrett D'Amore 
75888447a05SGarrett D'Amore static int
audiohd_reset_port(audiohd_port_t * port)75988447a05SGarrett D'Amore audiohd_reset_port(audiohd_port_t *port)
76088447a05SGarrett D'Amore {
76188447a05SGarrett D'Amore 	uint16_t		regbase;
76288447a05SGarrett D'Amore 	audiohd_state_t		*statep;
76388447a05SGarrett D'Amore 	uint8_t			bTmp;
76488447a05SGarrett D'Amore 	int			i;
76588447a05SGarrett D'Amore 
76688447a05SGarrett D'Amore 	regbase = port->regoff;
76788447a05SGarrett D'Amore 	statep = port->statep;
76888447a05SGarrett D'Amore 
76988447a05SGarrett D'Amore 	bTmp = AUDIOHD_REG_GET8(regbase + AUDIOHD_SDREG_OFFSET_CTL);
77088447a05SGarrett D'Amore 	/* stop stream */
77188447a05SGarrett D'Amore 	bTmp &= ~AUDIOHD_REG_RIRBSIZE;
77288447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_CTL, bTmp);
77388447a05SGarrett D'Amore 
77488447a05SGarrett D'Amore 	/* wait 40us for stream to stop as HD spec */
77588447a05SGarrett D'Amore 	drv_usecwait(40);
77688447a05SGarrett D'Amore 
77788447a05SGarrett D'Amore 	/* reset stream */
77888447a05SGarrett D'Amore 	bTmp |= AUDIOHDR_SD_CTL_SRST;
77988447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_CTL, bTmp);
78088447a05SGarrett D'Amore 
78188447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
78288447a05SGarrett D'Amore 		/* Empirical testing time, which works well */
78388447a05SGarrett D'Amore 		drv_usecwait(50);
78488447a05SGarrett D'Amore 		bTmp = AUDIOHD_REG_GET8(regbase + AUDIOHD_SDREG_OFFSET_CTL);
78588447a05SGarrett D'Amore 		bTmp &= AUDIOHDR_SD_CTL_SRST;
78688447a05SGarrett D'Amore 		if (bTmp)
78788447a05SGarrett D'Amore 			break;
78888447a05SGarrett D'Amore 	}
78988447a05SGarrett D'Amore 
79088447a05SGarrett D'Amore 	if (!bTmp) {
79188447a05SGarrett D'Amore 		audio_dev_warn(statep->adev, "Failed to reset stream %d",
79288447a05SGarrett D'Amore 		    port->index);
79368c47f65SGarrett D'Amore 		return (EIO);
79488447a05SGarrett D'Amore 	}
79588447a05SGarrett D'Amore 
79688447a05SGarrett D'Amore 	/* Empirical testing time, which works well */
79788447a05SGarrett D'Amore 	drv_usecwait(300);
79888447a05SGarrett D'Amore 
79988447a05SGarrett D'Amore 	/* exit reset stream */
80088447a05SGarrett D'Amore 	bTmp &= ~AUDIOHDR_SD_CTL_SRST;
80188447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_CTL, bTmp);
80288447a05SGarrett D'Amore 
80388447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
80488447a05SGarrett D'Amore 		/* Empircal testing time */
80588447a05SGarrett D'Amore 		drv_usecwait(50);
80688447a05SGarrett D'Amore 		bTmp = AUDIOHD_REG_GET8(regbase + AUDIOHD_SDREG_OFFSET_CTL);
80788447a05SGarrett D'Amore 		bTmp &= AUDIOHDR_SD_CTL_SRST;
80888447a05SGarrett D'Amore 		if (!bTmp)
80988447a05SGarrett D'Amore 			break;
81088447a05SGarrett D'Amore 	}
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 	if (bTmp) {
81388447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
81488447a05SGarrett D'Amore 		    "Failed to exit reset state for"
81588447a05SGarrett D'Amore 		    " stream %d, bTmp=0x%02x", port->index, bTmp);
81668c47f65SGarrett D'Amore 		return (EIO);
81788447a05SGarrett D'Amore 	}
81888447a05SGarrett D'Amore 
81988447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_BDLPL,
82088447a05SGarrett D'Amore 	    (uint32_t)port->bdl_paddr);
82188447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_BDLPU,
82288447a05SGarrett D'Amore 	    (uint32_t)(port->bdl_paddr >> 32));
82388447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_LVI,
82488447a05SGarrett D'Amore 	    AUDIOHD_BDLE_NUMS - 1);
82568c47f65SGarrett D'Amore 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_CBL, port->bufsize);
82688447a05SGarrett D'Amore 
82788447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_FORMAT,
82888447a05SGarrett D'Amore 	    port->format << 4 | port->nchan - 1);
82988447a05SGarrett D'Amore 
83088447a05SGarrett D'Amore 	/* clear status */
83188447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_STS,
83288447a05SGarrett D'Amore 	    AUDIOHDR_SD_STS_BCIS | AUDIOHDR_SD_STS_FIFOE |
83388447a05SGarrett D'Amore 	    AUDIOHDR_SD_STS_DESE);
83488447a05SGarrett D'Amore 
83588447a05SGarrett D'Amore 	/* set stream tag */
83688447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_CTL +
83788447a05SGarrett D'Amore 	    AUDIOHD_PLAY_CTL_OFF,
83888447a05SGarrett D'Amore 	    (port->index) << AUDIOHD_PLAY_TAG_OFF);
83988447a05SGarrett D'Amore 
84088447a05SGarrett D'Amore 	return (0);
84188447a05SGarrett D'Amore }
84288447a05SGarrett D'Amore 
84368c47f65SGarrett D'Amore static int
audiohd_engine_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)84468c47f65SGarrett D'Amore audiohd_engine_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
84588447a05SGarrett D'Amore {
84668c47f65SGarrett D'Amore 	audiohd_port_t	*port = arg;
847ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t	*statep = port->statep;
84888447a05SGarrett D'Amore 
84968c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
85088447a05SGarrett D'Amore 
851ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
85268c47f65SGarrett D'Amore 	port->count = 0;
85368c47f65SGarrett D'Amore 	port->curpos = 0;
85468c47f65SGarrett D'Amore 	*nframes = port->nframes;
85568c47f65SGarrett D'Amore 	*bufp = port->samp_kaddr;
856ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
85788447a05SGarrett D'Amore 
85868c47f65SGarrett D'Amore 	return (0);
85988447a05SGarrett D'Amore }
86088447a05SGarrett D'Amore 
86188447a05SGarrett D'Amore static int
audiohd_engine_start(void * arg)86288447a05SGarrett D'Amore audiohd_engine_start(void *arg)
86388447a05SGarrett D'Amore {
86488447a05SGarrett D'Amore 	audiohd_port_t		*port = arg;
86588447a05SGarrett D'Amore 	audiohd_state_t		*statep = port->statep;
86668c47f65SGarrett D'Amore 	int			rv;
86788447a05SGarrett D'Amore 
86888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
86968c47f65SGarrett D'Amore 
87068c47f65SGarrett D'Amore 	if ((rv = audiohd_reset_port(port)) != 0) {
871ea463888SZhao Edgar Liu - Sun Microsystems 		mutex_exit(&statep->hda_mutex);
87268c47f65SGarrett D'Amore 		return (rv);
87388447a05SGarrett D'Amore 	}
87468c47f65SGarrett D'Amore 	/* Start DMA */
87568c47f65SGarrett D'Amore 	AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL,
87668c47f65SGarrett D'Amore 	    AUDIOHDR_SD_CTL_SRUN);
87768c47f65SGarrett D'Amore 
87888447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
87988447a05SGarrett D'Amore 	return (0);
88088447a05SGarrett D'Amore }
88188447a05SGarrett D'Amore 
88288447a05SGarrett D'Amore static void
audiohd_engine_stop(void * arg)88388447a05SGarrett D'Amore audiohd_engine_stop(void *arg)
88488447a05SGarrett D'Amore {
88588447a05SGarrett D'Amore 	audiohd_port_t		*port = arg;
88688447a05SGarrett D'Amore 	audiohd_state_t		*statep = port->statep;
88788447a05SGarrett D'Amore 
88888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
88968c47f65SGarrett D'Amore 	AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, 0);
89088447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
89188447a05SGarrett D'Amore }
89288447a05SGarrett D'Amore 
89388447a05SGarrett D'Amore static void
audiohd_update_port(audiohd_port_t * port)89488447a05SGarrett D'Amore audiohd_update_port(audiohd_port_t *port)
89588447a05SGarrett D'Amore {
896989b958fSZhao Edgar Liu - Sun Microsystems 	uint32_t		pos, len;
89788447a05SGarrett D'Amore 	audiohd_state_t		*statep = port->statep;
898989b958fSZhao Edgar Liu - Sun Microsystems 	int			i, ret;
899989b958fSZhao Edgar Liu - Sun Microsystems 	uint32_t		status, resp = 0, respex = 0;
900989b958fSZhao Edgar Liu - Sun Microsystems 	uint8_t			rirbsts;
90188447a05SGarrett D'Amore 
90288447a05SGarrett D'Amore 	pos = AUDIOHD_REG_GET32(port->regoff + AUDIOHD_SDREG_OFFSET_LPIB);
9030c240c64SZhao Edgar Liu - Sun Microsystems 	/* Convert the position into a frame count */
904a33ad26eSZhao Edgar Liu - Sun Microsystems 	pos /= (port->nchan * statep->sample_packed_bytes);
9050c240c64SZhao Edgar Liu - Sun Microsystems 
90668c47f65SGarrett D'Amore 	ASSERT(pos <= port->nframes);
90768c47f65SGarrett D'Amore 	if (pos >= port->curpos) {
9080c240c64SZhao Edgar Liu - Sun Microsystems 		len = (pos - port->curpos);
90968c47f65SGarrett D'Amore 	} else {
9100c240c64SZhao Edgar Liu - Sun Microsystems 		len = pos + port->nframes - port->curpos;
91188447a05SGarrett D'Amore 	}
91288447a05SGarrett D'Amore 
9130c240c64SZhao Edgar Liu - Sun Microsystems 	ASSERT(len <= port->nframes);
9140c240c64SZhao Edgar Liu - Sun Microsystems 	port->curpos = pos;
9150c240c64SZhao Edgar Liu - Sun Microsystems 	port->count += len;
916989b958fSZhao Edgar Liu - Sun Microsystems 
917989b958fSZhao Edgar Liu - Sun Microsystems 	/*
918989b958fSZhao Edgar Liu - Sun Microsystems 	 * Check unsolicited response from pins, maybe something plugged in or
919989b958fSZhao Edgar Liu - Sun Microsystems 	 * out of the jack.
920989b958fSZhao Edgar Liu - Sun Microsystems 	 */
921989b958fSZhao Edgar Liu - Sun Microsystems 	status = AUDIOHD_REG_GET32(AUDIOHD_REG_INTSTS);
922989b958fSZhao Edgar Liu - Sun Microsystems 	if (status == 0) {
923989b958fSZhao Edgar Liu - Sun Microsystems 		/* No pending interrupt we should take care */
924989b958fSZhao Edgar Liu - Sun Microsystems 		return;
925989b958fSZhao Edgar Liu - Sun Microsystems 	}
926989b958fSZhao Edgar Liu - Sun Microsystems 
927989b958fSZhao Edgar Liu - Sun Microsystems 	if (status & AUDIOHD_CIS_MASK) {
928989b958fSZhao Edgar Liu - Sun Microsystems 		/* Clear the unsolicited response interrupt */
929989b958fSZhao Edgar Liu - Sun Microsystems 		rirbsts = AUDIOHD_REG_GET8(AUDIOHD_REG_RIRBSTS);
930989b958fSZhao Edgar Liu - Sun Microsystems 		AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSTS, rirbsts);
931989b958fSZhao Edgar Liu - Sun Microsystems 
932989b958fSZhao Edgar Liu - Sun Microsystems 		/*
933989b958fSZhao Edgar Liu - Sun Microsystems 		 * We have to wait and try several times to make sure the
934989b958fSZhao Edgar Liu - Sun Microsystems 		 * unsolicited response is generated by our pins.
935989b958fSZhao Edgar Liu - Sun Microsystems 		 * we need to make it work for audiohd spec 0.9, which is
936989b958fSZhao Edgar Liu - Sun Microsystems 		 * just a draft version and requires more time to wait.
937989b958fSZhao Edgar Liu - Sun Microsystems 		 */
938989b958fSZhao Edgar Liu - Sun Microsystems 		for (i = 0; i < AUDIOHD_TEST_TIMES; i++) {
939989b958fSZhao Edgar Liu - Sun Microsystems 			ret = audiohd_response_from_codec(statep, &resp,
940989b958fSZhao Edgar Liu - Sun Microsystems 			    &respex);
941989b958fSZhao Edgar Liu - Sun Microsystems 			if ((ret == DDI_SUCCESS) &&
942989b958fSZhao Edgar Liu - Sun Microsystems 			    (respex & AUDIOHD_RIRB_UR_MASK)) {
943989b958fSZhao Edgar Liu - Sun Microsystems 				/*
944989b958fSZhao Edgar Liu - Sun Microsystems 				 * A pin may generate more than one ur rirb,
945989b958fSZhao Edgar Liu - Sun Microsystems 				 * we only need handle one of them, and clear
946989b958fSZhao Edgar Liu - Sun Microsystems 				 * the other ones
947989b958fSZhao Edgar Liu - Sun Microsystems 				 */
948989b958fSZhao Edgar Liu - Sun Microsystems 				statep->hda_rirb_rp =
949989b958fSZhao Edgar Liu - Sun Microsystems 				    AUDIOHD_REG_GET16(AUDIOHD_REG_RIRBWP) &
950989b958fSZhao Edgar Liu - Sun Microsystems 				    AUDIOHD_RIRB_WPMASK;
951989b958fSZhao Edgar Liu - Sun Microsystems 				audiohd_pin_sense(statep, resp, respex);
952989b958fSZhao Edgar Liu - Sun Microsystems 				break;
953989b958fSZhao Edgar Liu - Sun Microsystems 			}
954989b958fSZhao Edgar Liu - Sun Microsystems 		}
955989b958fSZhao Edgar Liu - Sun Microsystems 	}
95688447a05SGarrett D'Amore }
95788447a05SGarrett D'Amore 
95888447a05SGarrett D'Amore static uint64_t
audiohd_engine_count(void * arg)95988447a05SGarrett D'Amore audiohd_engine_count(void *arg)
96088447a05SGarrett D'Amore {
96188447a05SGarrett D'Amore 	audiohd_port_t	*port = arg;
96288447a05SGarrett D'Amore 	audiohd_state_t	*statep = port->statep;
96388447a05SGarrett D'Amore 	uint64_t	val;
96488447a05SGarrett D'Amore 
96588447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
96688447a05SGarrett D'Amore 	audiohd_update_port(port);
96788447a05SGarrett D'Amore 	val = port->count;
96888447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
96988447a05SGarrett D'Amore 	return (val);
97088447a05SGarrett D'Amore }
97188447a05SGarrett D'Amore 
97288447a05SGarrett D'Amore static void
audiohd_engine_close(void * arg)97388447a05SGarrett D'Amore audiohd_engine_close(void *arg)
97488447a05SGarrett D'Amore {
97568c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
97688447a05SGarrett D'Amore }
97788447a05SGarrett D'Amore 
97888447a05SGarrett D'Amore static void
audiohd_engine_sync(void * arg,unsigned nframes)97988447a05SGarrett D'Amore audiohd_engine_sync(void *arg, unsigned nframes)
98088447a05SGarrett D'Amore {
98188447a05SGarrett D'Amore 	audiohd_port_t *port = arg;
98288447a05SGarrett D'Amore 
98388447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
98488447a05SGarrett D'Amore 
98568c47f65SGarrett D'Amore 	(void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir);
98688447a05SGarrett D'Amore 
98788447a05SGarrett D'Amore }
98888447a05SGarrett D'Amore 
98988447a05SGarrett D'Amore audio_engine_ops_t audiohd_engine_ops = {
99088447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,		/* version number */
99188447a05SGarrett D'Amore 	audiohd_engine_open,
99288447a05SGarrett D'Amore 	audiohd_engine_close,
99388447a05SGarrett D'Amore 	audiohd_engine_start,
99488447a05SGarrett D'Amore 	audiohd_engine_stop,
99588447a05SGarrett D'Amore 	audiohd_engine_count,
99688447a05SGarrett D'Amore 	audiohd_engine_format,
99788447a05SGarrett D'Amore 	audiohd_engine_channels,
99888447a05SGarrett D'Amore 	audiohd_engine_rate,
99988447a05SGarrett D'Amore 	audiohd_engine_sync,
1000f9ead4a5SGarrett D'Amore 	NULL,
1001f9ead4a5SGarrett D'Amore 	NULL,
1002f9ead4a5SGarrett D'Amore 	NULL
100388447a05SGarrett D'Amore };
100488447a05SGarrett D'Amore 
100588447a05SGarrett D'Amore static int
audiohd_get_control(void * arg,uint64_t * val)1006c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_get_control(void *arg, uint64_t *val)
100788447a05SGarrett D'Amore {
1008c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t	*ac = arg;
1009ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t	*statep = ac->statep;
101088447a05SGarrett D'Amore 
1011ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
1012c1cfefcdSZhao Edgar Liu - Sun Microsystems 	*val = ac->val;
1013ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
1014ea463888SZhao Edgar Liu - Sun Microsystems 
101588447a05SGarrett D'Amore 	return (0);
101688447a05SGarrett D'Amore }
101788447a05SGarrett D'Amore 
101888447a05SGarrett D'Amore static void
audiohd_do_set_pin_volume(audiohd_state_t * statep,audiohd_path_t * path,uint64_t val)101913084339SYang-Rong Jerry Zhou audiohd_do_set_pin_volume(audiohd_state_t *statep, audiohd_path_t *path,
1020c6e681c0SYang-Rong Jerry Zhou     uint64_t val)
102113084339SYang-Rong Jerry Zhou {
102213084339SYang-Rong Jerry Zhou 	uint8_t				l, r;
102313084339SYang-Rong Jerry Zhou 	uint_t				tmp;
102413084339SYang-Rong Jerry Zhou 	int				gain;
102513084339SYang-Rong Jerry Zhou 
1026211ec5c5SYang-Rong Jerry Zhou 	if (path->mute_wid && val == 0) {
102713084339SYang-Rong Jerry Zhou 		(void) audioha_codec_4bit_verb_get(
102813084339SYang-Rong Jerry Zhou 		    statep,
102913084339SYang-Rong Jerry Zhou 		    path->codec->index,
1030c6e681c0SYang-Rong Jerry Zhou 		    path->mute_wid,
103113084339SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_SET_AMP_MUTE,
1032c6e681c0SYang-Rong Jerry Zhou 		    path->mute_dir |
103313084339SYang-Rong Jerry Zhou 		    AUDIOHDC_AMP_SET_LNR |
103413084339SYang-Rong Jerry Zhou 		    AUDIOHDC_AMP_SET_MUTE);
103513084339SYang-Rong Jerry Zhou 		return;
103613084339SYang-Rong Jerry Zhou 	}
103713084339SYang-Rong Jerry Zhou 
103813084339SYang-Rong Jerry Zhou 	l = (val & 0xff00) >> 8;
103913084339SYang-Rong Jerry Zhou 	r = (val & 0xff);
1040c6e681c0SYang-Rong Jerry Zhou 	tmp = l * path->gain_bits / 100;
104113084339SYang-Rong Jerry Zhou 	(void) audioha_codec_4bit_verb_get(statep,
104213084339SYang-Rong Jerry Zhou 	    path->codec->index,
1043c6e681c0SYang-Rong Jerry Zhou 	    path->gain_wid,
104413084339SYang-Rong Jerry Zhou 	    AUDIOHDC_VERB_SET_AMP_MUTE,
1045c6e681c0SYang-Rong Jerry Zhou 	    AUDIOHDC_AMP_SET_LEFT | path->gain_dir |
104688447a05SGarrett D'Amore 	    tmp);
1047c6e681c0SYang-Rong Jerry Zhou 	tmp = r * path->gain_bits / 100;
104813084339SYang-Rong Jerry Zhou 	(void) audioha_codec_4bit_verb_get(statep,
104913084339SYang-Rong Jerry Zhou 	    path->codec->index,
1050c6e681c0SYang-Rong Jerry Zhou 	    path->gain_wid,
105113084339SYang-Rong Jerry Zhou 	    AUDIOHDC_VERB_SET_AMP_MUTE,
1052c6e681c0SYang-Rong Jerry Zhou 	    AUDIOHDC_AMP_SET_RIGHT | path->gain_dir |
105313084339SYang-Rong Jerry Zhou 	    tmp);
10545ec2209cSZhao Edgar Liu - Sun Microsystems 
1055211ec5c5SYang-Rong Jerry Zhou 	if (path->mute_wid && path->mute_wid != path->gain_wid) {
105613084339SYang-Rong Jerry Zhou 		gain = AUDIOHDC_GAIN_MAX;
105713084339SYang-Rong Jerry Zhou 		(void) audioha_codec_4bit_verb_get(
105813084339SYang-Rong Jerry Zhou 		    statep,
105913084339SYang-Rong Jerry Zhou 		    path->codec->index,
1060c6e681c0SYang-Rong Jerry Zhou 		    path->mute_wid,
106113084339SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_SET_AMP_MUTE,
1062c6e681c0SYang-Rong Jerry Zhou 		    path->mute_dir |
106313084339SYang-Rong Jerry Zhou 		    AUDIOHDC_AMP_SET_LEFT |
106413084339SYang-Rong Jerry Zhou 		    gain);
106513084339SYang-Rong Jerry Zhou 		(void) audioha_codec_4bit_verb_get(
106613084339SYang-Rong Jerry Zhou 		    statep,
106713084339SYang-Rong Jerry Zhou 		    path->codec->index,
1068c6e681c0SYang-Rong Jerry Zhou 		    path->mute_wid,
106913084339SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_SET_AMP_MUTE,
1070c6e681c0SYang-Rong Jerry Zhou 		    path->mute_dir |
107113084339SYang-Rong Jerry Zhou 		    AUDIOHDC_AMP_SET_RIGHT |
107213084339SYang-Rong Jerry Zhou 		    gain);
107388447a05SGarrett D'Amore 	}
107488447a05SGarrett D'Amore }
107588447a05SGarrett D'Amore 
107688447a05SGarrett D'Amore static void
audiohd_set_pin_volume(audiohd_state_t * statep,audiohda_device_type_t type)107788447a05SGarrett D'Amore audiohd_set_pin_volume(audiohd_state_t *statep, audiohda_device_type_t type)
107888447a05SGarrett D'Amore {
107988447a05SGarrett D'Amore 	int				i, j;
108088447a05SGarrett D'Amore 	audiohd_path_t			*path;
108188447a05SGarrett D'Amore 	audiohd_widget_t		*widget;
108288447a05SGarrett D'Amore 	wid_t				wid;
108388447a05SGarrett D'Amore 	audiohd_pin_t			*pin;
108488447a05SGarrett D'Amore 	hda_codec_t			*codec;
108588447a05SGarrett D'Amore 	uint64_t			val;
1086c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t			control;
108788447a05SGarrett D'Amore 
108888447a05SGarrett D'Amore 	switch (type) {
108988447a05SGarrett D'Amore 		case DTYPE_SPEAKER:
1090c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_SPEAKER];
1091901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1092901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1093c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
109488447a05SGarrett D'Amore 			break;
109588447a05SGarrett D'Amore 		case DTYPE_HP_OUT:
1096c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_HEADPHONE];
1097901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1098901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1099c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
110088447a05SGarrett D'Amore 			break;
1101211ec5c5SYang-Rong Jerry Zhou 		case DTYPE_LINEOUT:
1102c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_FRONT];
1103901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1104901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1105c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
1106211ec5c5SYang-Rong Jerry Zhou 			break;
110788447a05SGarrett D'Amore 		case DTYPE_CD:
1108c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_CD];
1109901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1110901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1111c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
111288447a05SGarrett D'Amore 			break;
111388447a05SGarrett D'Amore 		case DTYPE_LINE_IN:
1114c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_LINEIN];
1115901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1116901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1117c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
111888447a05SGarrett D'Amore 			break;
111988447a05SGarrett D'Amore 		case DTYPE_MIC_IN:
1120c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_MIC];
1121901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1122901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1123c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
112488447a05SGarrett D'Amore 			break;
112588447a05SGarrett D'Amore 	}
112688447a05SGarrett D'Amore 
112788447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
1128e7236f70SZhao Edgar Liu - Sun Microsystems 		if ((path = statep->path[i]) == NULL)
112988447a05SGarrett D'Amore 			continue;
1130e7236f70SZhao Edgar Liu - Sun Microsystems 
113188447a05SGarrett D'Amore 		codec = path->codec;
113288447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
113388447a05SGarrett D'Amore 			wid = path->pin_wid[j];
113488447a05SGarrett D'Amore 			widget = codec->widget[wid];
113588447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)widget->priv;
1136c6e681c0SYang-Rong Jerry Zhou 			if ((pin->device == type) && path->gain_wid) {
1137c6e681c0SYang-Rong Jerry Zhou 				audiohd_do_set_pin_volume(statep, path, val);
113888447a05SGarrett D'Amore 			}
113988447a05SGarrett D'Amore 		}
114088447a05SGarrett D'Amore 	}
114188447a05SGarrett D'Amore }
114288447a05SGarrett D'Amore 
114388447a05SGarrett D'Amore 
114488447a05SGarrett D'Amore static void
audiohd_set_pin_volume_by_color(audiohd_state_t * statep,audiohd_pin_color_t color)114588447a05SGarrett D'Amore audiohd_set_pin_volume_by_color(audiohd_state_t *statep,
114688447a05SGarrett D'Amore     audiohd_pin_color_t color)
114788447a05SGarrett D'Amore {
114888447a05SGarrett D'Amore 	int			i, j;
114988447a05SGarrett D'Amore 	audiohd_path_t		*path;
115088447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
115188447a05SGarrett D'Amore 	wid_t			wid;
115288447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
115388447a05SGarrett D'Amore 	hda_codec_t		*codec;
115488447a05SGarrett D'Amore 	uint8_t			l, r;
115588447a05SGarrett D'Amore 	uint64_t		val;
115688447a05SGarrett D'Amore 	audiohd_pin_color_t	clr;
1157c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		control;
115888447a05SGarrett D'Amore 
115988447a05SGarrett D'Amore 	switch (color) {
116065a41de7SYang-Rong Jerry Zhou 		case AUDIOHD_PIN_GREEN:
1161c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_FRONT];
1162901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1163901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1164c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
116565a41de7SYang-Rong Jerry Zhou 			break;
116688447a05SGarrett D'Amore 		case AUDIOHD_PIN_BLACK:
1167c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_REAR];
1168901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1169901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1170c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
117188447a05SGarrett D'Amore 			break;
117288447a05SGarrett D'Amore 		case AUDIOHD_PIN_ORANGE:
1173c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_CENTER];
1174901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1175901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1176c1cfefcdSZhao Edgar Liu - Sun Microsystems 			l = control.val;
1177c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_LFE];
1178901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1179901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1180c1cfefcdSZhao Edgar Liu - Sun Microsystems 			r = control.val;
118113084339SYang-Rong Jerry Zhou 			val = (l << 8) | r;
118288447a05SGarrett D'Amore 			break;
118388447a05SGarrett D'Amore 		case AUDIOHD_PIN_GREY:
1184c1cfefcdSZhao Edgar Liu - Sun Microsystems 			control = statep->ctrls[CTL_SURROUND];
1185901f2979SZhao Edgar Liu - Sun Microsystems 			if (control.ctrl == NULL)
1186901f2979SZhao Edgar Liu - Sun Microsystems 				return;
1187c1cfefcdSZhao Edgar Liu - Sun Microsystems 			val = control.val;
118888447a05SGarrett D'Amore 			break;
118988447a05SGarrett D'Amore 	}
119088447a05SGarrett D'Amore 
119188447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
119288447a05SGarrett D'Amore 		path = statep->path[i];
119388447a05SGarrett D'Amore 		if (!path)
119488447a05SGarrett D'Amore 			continue;
119588447a05SGarrett D'Amore 		codec = path->codec;
119688447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
119788447a05SGarrett D'Amore 			wid = path->pin_wid[j];
119888447a05SGarrett D'Amore 			widget = codec->widget[wid];
119988447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)widget->priv;
120088447a05SGarrett D'Amore 			clr = (pin->config >> AUDIOHD_PIN_CLR_OFF) &
120188447a05SGarrett D'Amore 			    AUDIOHD_PIN_CLR_MASK;
1202c6e681c0SYang-Rong Jerry Zhou 			if ((clr == color) && path->gain_wid) {
1203c6e681c0SYang-Rong Jerry Zhou 				audiohd_do_set_pin_volume(statep, path, val);
120488447a05SGarrett D'Amore 			}
120588447a05SGarrett D'Amore 		}
120688447a05SGarrett D'Amore 	}
120788447a05SGarrett D'Amore }
120888447a05SGarrett D'Amore 
120988447a05SGarrett D'Amore static int
audiohd_set_input_pin(audiohd_state_t * statep)121088447a05SGarrett D'Amore audiohd_set_input_pin(audiohd_state_t *statep)
121188447a05SGarrett D'Amore {
121288447a05SGarrett D'Amore 	uint64_t		val;
1213a4c3d128SYang-Rong Jerry Zhou 	hda_codec_t		*codec;
121488447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
121588447a05SGarrett D'Amore 	audiohd_path_t		*path;
1216a4c3d128SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget, *w;
121788447a05SGarrett D'Amore 	int			i, j;
1218a4c3d128SYang-Rong Jerry Zhou 	wid_t			wid, pin_wid = 0;
1219c1cfefcdSZhao Edgar Liu - Sun Microsystems 	uint32_t		set_val;
122088447a05SGarrett D'Amore 
1221c1cfefcdSZhao Edgar Liu - Sun Microsystems 	val = statep->ctrls[CTL_RECSRC].val;
1222c1cfefcdSZhao Edgar Liu - Sun Microsystems 	set_val = ddi_ffs(val & 0xffff) - 1;
122388447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
1224e7236f70SZhao Edgar Liu - Sun Microsystems 		if ((path = statep->path[i]) == NULL ||
1225e7236f70SZhao Edgar Liu - Sun Microsystems 		    path->path_type != RECORD)
122688447a05SGarrett D'Amore 			continue;
1227c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1228c1cfefcdSZhao Edgar Liu - Sun Microsystems 		switch (set_val) {
122988447a05SGarrett D'Amore 		case DTYPE_LINE_IN:
123088447a05SGarrett D'Amore 		case DTYPE_MIC_IN:
123188447a05SGarrett D'Amore 		case DTYPE_CD:
123288447a05SGarrett D'Amore 			for (j = 0; j < path->pin_nums; j++) {
123388447a05SGarrett D'Amore 				wid = path->pin_wid[j];
123488447a05SGarrett D'Amore 				widget = path->codec->widget[wid];
123588447a05SGarrett D'Amore 				pin = (audiohd_pin_t *)widget->priv;
1236e7236f70SZhao Edgar Liu - Sun Microsystems 
123788447a05SGarrett D'Amore 				if ((1U << pin->device) == val) {
123888447a05SGarrett D'Amore 					AUDIOHD_ENABLE_PIN_IN(statep,
1239e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->codec->index, pin->wid);
1240a4c3d128SYang-Rong Jerry Zhou 					pin_wid = pin->wid;
1241a4c3d128SYang-Rong Jerry Zhou 					codec = path->codec;
124288447a05SGarrett D'Amore 					statep->in_port = pin->device;
124388447a05SGarrett D'Amore 				} else if (statep->in_port == pin->device) {
124488447a05SGarrett D'Amore 					AUDIOHD_DISABLE_PIN_IN(statep,
1245e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->codec->index, pin->wid);
124688447a05SGarrett D'Amore 				}
124788447a05SGarrett D'Amore 			}
124888447a05SGarrett D'Amore 			break;
124988447a05SGarrett D'Amore 		default:
125088447a05SGarrett D'Amore 			break;
125188447a05SGarrett D'Amore 		}
125288447a05SGarrett D'Amore 	}
1253e7236f70SZhao Edgar Liu - Sun Microsystems 
1254a4c3d128SYang-Rong Jerry Zhou 	if (pin_wid == 0)
1255a4c3d128SYang-Rong Jerry Zhou 		return (DDI_SUCCESS);
1256e7236f70SZhao Edgar Liu - Sun Microsystems 
1257a4c3d128SYang-Rong Jerry Zhou 	w = codec->widget[pin_wid];
1258a4c3d128SYang-Rong Jerry Zhou 	pin = (audiohd_pin_t *)w->priv;
1259e7236f70SZhao Edgar Liu - Sun Microsystems 	w = codec->widget[pin->adc_wid];
1260a4c3d128SYang-Rong Jerry Zhou 	path = (audiohd_path_t *)w->priv;
1261e7236f70SZhao Edgar Liu - Sun Microsystems 
1262a4c3d128SYang-Rong Jerry Zhou 	/*
1263a4c3d128SYang-Rong Jerry Zhou 	 * If there is a real selector in this input path,
1264a4c3d128SYang-Rong Jerry Zhou 	 * we select the right one input for the selector.
1265a4c3d128SYang-Rong Jerry Zhou 	 */
1266a4c3d128SYang-Rong Jerry Zhou 	if (path->sum_wid) {
1267a4c3d128SYang-Rong Jerry Zhou 		w = codec->widget[path->sum_wid];
1268a4c3d128SYang-Rong Jerry Zhou 		if (w->type == WTYPE_AUDIO_SEL) {
1269e7236f70SZhao Edgar Liu - Sun Microsystems 			for (i = 0; i < path->pin_nums; i++) {
1270e7236f70SZhao Edgar Liu - Sun Microsystems 				if (path->pin_wid[i] == pin->wid) {
1271a4c3d128SYang-Rong Jerry Zhou 					(void) audioha_codec_verb_get(
1272a4c3d128SYang-Rong Jerry Zhou 					    statep, codec->index, path->sum_wid,
1273a4c3d128SYang-Rong Jerry Zhou 					    AUDIOHDC_VERB_SET_CONN_SEL,
1274a4c3d128SYang-Rong Jerry Zhou 					    path->sum_selconn[i]);
1275e7236f70SZhao Edgar Liu - Sun Microsystems 					break;
1276a4c3d128SYang-Rong Jerry Zhou 				}
1277a4c3d128SYang-Rong Jerry Zhou 			}
1278e7236f70SZhao Edgar Liu - Sun Microsystems 		}
1279e7236f70SZhao Edgar Liu - Sun Microsystems 	}
1280e7236f70SZhao Edgar Liu - Sun Microsystems 
128188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
128288447a05SGarrett D'Amore }
128388447a05SGarrett D'Amore 
128488447a05SGarrett D'Amore static void
audiohd_set_pin_monitor_gain(hda_codec_t * codec,audiohd_state_t * statep,uint_t caddr,audiohd_pin_t * pin,uint64_t gain)128588447a05SGarrett D'Amore audiohd_set_pin_monitor_gain(hda_codec_t *codec, audiohd_state_t *statep,
128688447a05SGarrett D'Amore     uint_t caddr, audiohd_pin_t *pin, uint64_t gain)
128788447a05SGarrett D'Amore {
128888447a05SGarrett D'Amore 	int 			i, k;
128988447a05SGarrett D'Amore 	uint_t			ltmp, rtmp;
129088447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
129188447a05SGarrett D'Amore 	uint8_t		l, r;
129288447a05SGarrett D'Amore 
129388447a05SGarrett D'Amore 	l = (gain & 0xff00) >> 8;
129488447a05SGarrett D'Amore 	r = (gain & 0xff);
129588447a05SGarrett D'Amore 
129688447a05SGarrett D'Amore 	for (k = 0; k < pin->num; k++) {
129788447a05SGarrett D'Amore 		ltmp = l * pin->mg_gain[k] / 100;
129888447a05SGarrett D'Amore 		rtmp = r * pin->mg_gain[k] / 100;
129988447a05SGarrett D'Amore 		widget = codec->widget[pin->mg_wid[k]];
130088447a05SGarrett D'Amore 		if (pin->mg_dir[k] == AUDIOHDC_AMP_SET_OUTPUT) {
130188447a05SGarrett D'Amore 			(void) audioha_codec_4bit_verb_get(
130288447a05SGarrett D'Amore 			    statep,
130388447a05SGarrett D'Amore 			    caddr,
130488447a05SGarrett D'Amore 			    pin->mg_wid[k],
130588447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_AMP_MUTE,
130688447a05SGarrett D'Amore 			    AUDIOHDC_AMP_SET_LEFT|
130788447a05SGarrett D'Amore 			    pin->mg_dir[k] | ltmp);
130888447a05SGarrett D'Amore 			(void) audioha_codec_4bit_verb_get(
130988447a05SGarrett D'Amore 			    statep,
131088447a05SGarrett D'Amore 			    caddr,
131188447a05SGarrett D'Amore 			    pin->mg_wid[k],
131288447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_AMP_MUTE,
131388447a05SGarrett D'Amore 			    AUDIOHDC_AMP_SET_RIGHT|
131488447a05SGarrett D'Amore 			    pin->mg_dir[k] | rtmp);
131588447a05SGarrett D'Amore 		} else if (pin->mg_dir[k] == AUDIOHDC_AMP_SET_INPUT) {
131688447a05SGarrett D'Amore 			for (i = 0; i < widget->used; i++) {
131788447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(
131888447a05SGarrett D'Amore 				    statep,
131988447a05SGarrett D'Amore 				    caddr,
132088447a05SGarrett D'Amore 				    pin->mg_wid[k],
132188447a05SGarrett D'Amore 				    AUDIOHDC_VERB_SET_AMP_MUTE,
132288447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_RIGHT|
1323b96a6eceSZhao Edgar Liu - Sun Microsystems 				    widget->monitor_path_next[i]<<
132488447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_INDEX_OFFSET |
132588447a05SGarrett D'Amore 				    pin->mg_dir[k] | rtmp);
132688447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(
132788447a05SGarrett D'Amore 				    statep,
132888447a05SGarrett D'Amore 				    caddr,
132988447a05SGarrett D'Amore 				    pin->mg_wid[k],
133088447a05SGarrett D'Amore 				    AUDIOHDC_VERB_SET_AMP_MUTE,
133188447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_LEFT|
1332b96a6eceSZhao Edgar Liu - Sun Microsystems 				    widget->monitor_path_next[i]<<
133388447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_INDEX_OFFSET |
133488447a05SGarrett D'Amore 				    pin->mg_dir[k] | ltmp);
133588447a05SGarrett D'Amore 			}
133688447a05SGarrett D'Amore 		}
133788447a05SGarrett D'Amore 	}
133888447a05SGarrett D'Amore }
133988447a05SGarrett D'Amore 
134088447a05SGarrett D'Amore static void
audiohd_set_monitor_gain(audiohd_state_t * statep)134188447a05SGarrett D'Amore audiohd_set_monitor_gain(audiohd_state_t *statep)
134288447a05SGarrett D'Amore {
134388447a05SGarrett D'Amore 	int			i, j;
134488447a05SGarrett D'Amore 	audiohd_path_t		*path;
134588447a05SGarrett D'Amore 	uint_t			caddr;
134688447a05SGarrett D'Amore 	audiohd_widget_t	*w;
134788447a05SGarrett D'Amore 	wid_t			wid;
134888447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
1349c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		ctrl;
135088447a05SGarrett D'Amore 	uint64_t		val;
135188447a05SGarrett D'Amore 
1352c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ctrl = statep->ctrls[CTL_MONGAIN];
1353c1cfefcdSZhao Edgar Liu - Sun Microsystems 	val = ctrl.val;
135488447a05SGarrett D'Amore 
135588447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
135688447a05SGarrett D'Amore 		path = statep->path[i];
135788447a05SGarrett D'Amore 		if (path == NULL || path->path_type != PLAY)
135888447a05SGarrett D'Amore 			continue;
135988447a05SGarrett D'Amore 		caddr = path->codec->index;
136088447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
136188447a05SGarrett D'Amore 			wid = path->pin_wid[j];
136288447a05SGarrett D'Amore 			w = path->codec->widget[wid];
136388447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)w->priv;
136488447a05SGarrett D'Amore 			audiohd_set_pin_monitor_gain(path->codec, statep,
136588447a05SGarrett D'Amore 			    caddr, pin, val);
136688447a05SGarrett D'Amore 		}
136788447a05SGarrett D'Amore 	}
136888447a05SGarrett D'Amore 
136988447a05SGarrett D'Amore }
137088447a05SGarrett D'Amore 
137188447a05SGarrett D'Amore static void
audiohd_set_beep_volume(audiohd_state_t * statep)137242c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_set_beep_volume(audiohd_state_t *statep)
137342c41cf8Slipeng sang - Sun Microsystems - Beijing China {
137442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int			i;
137542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_path_t		*path;
137642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	hda_codec_t		*codec;
137742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint64_t		val;
137842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint_t			tmp;
1379c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		control;
138042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint32_t		vid;
138142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
1382c1cfefcdSZhao Edgar Liu - Sun Microsystems 	control = statep->ctrls[CTL_BEEP];
1383c1cfefcdSZhao Edgar Liu - Sun Microsystems 	val = control.val;
138442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	for (i = 0; i < statep->pathnum; i++) {
138542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		path = statep->path[i];
138642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if (!path || path->path_type != BEEP)
138742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
138842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		codec = path->codec;
138942c41cf8Slipeng sang - Sun Microsystems - Beijing China 		vid = codec->vid;
139042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		vid = vid >> 16;
139142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
139242c41cf8Slipeng sang - Sun Microsystems - Beijing China 		switch (vid) {
139342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		case  AUDIOHD_VID_SIGMATEL:
139442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			/*
139542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * Sigmatel HD codec specific operation.
139642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * There is a workaround,
139742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * Due to Sigmatel HD codec hardware problem,
139842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * which it can't mute beep when volume is 0.
139942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * So add global value audiohd_beep_vol,
140042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * Set freq to 0 when volume is 0.
140142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 */
140242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			tmp = val * path->gain_bits / 100;
140342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			if (tmp == 0) {
140442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				audiohd_beep_vol = 0;
140542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			} else {
140642c41cf8Slipeng sang - Sun Microsystems - Beijing China 				audiohd_beep_vol = tmp;
140742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				(void) audioha_codec_verb_get(
140842c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    statep,
140942c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    codec->index,
141042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    path->beep_wid,
141142c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    AUDIOHDC_VERB_SET_BEEP_VOL,
141242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    tmp);
141342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
141442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			break;
141542c41cf8Slipeng sang - Sun Microsystems - Beijing China 
141642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		default:
141742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			/* Common operation based on audiohd spec */
141842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			audiohd_do_set_beep_volume(statep, path, val);
141942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			break;
142042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
142142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
142242c41cf8Slipeng sang - Sun Microsystems - Beijing China }
142342c41cf8Slipeng sang - Sun Microsystems - Beijing China 
142442c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_do_set_beep_volume(audiohd_state_t * statep,audiohd_path_t * path,uint64_t val)142542c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_do_set_beep_volume(audiohd_state_t *statep, audiohd_path_t *path,
142642c41cf8Slipeng sang - Sun Microsystems - Beijing China     uint64_t val)
142742c41cf8Slipeng sang - Sun Microsystems - Beijing China {
142842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint8_t		l, r;
142942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint_t		tmp;
143042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int		gain;
143142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
143242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (val == 0) {
143342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		(void) audioha_codec_4bit_verb_get(
143442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    statep,
143542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->codec->index,
143642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_wid,
143742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_VERB_SET_AMP_MUTE,
143842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_dir |
143942c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_AMP_SET_LNR |
144042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_AMP_SET_MUTE);
144142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		return;
144242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
144342c41cf8Slipeng sang - Sun Microsystems - Beijing China 
144442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	r = (val & 0xff);
144542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	l = r;
144642c41cf8Slipeng sang - Sun Microsystems - Beijing China 
144742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	tmp = l * path->gain_bits / 100;
144842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	(void) audioha_codec_4bit_verb_get(statep,
144942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    path->codec->index,
145042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    path->gain_wid,
145142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_VERB_SET_AMP_MUTE,
145242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_AMP_SET_LEFT | path->gain_dir |
145342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    tmp);
145442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	tmp = r * path->gain_bits / 100;
145542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	(void) audioha_codec_4bit_verb_get(statep,
145642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    path->codec->index,
145742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    path->gain_wid,
145842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_VERB_SET_AMP_MUTE,
145942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_AMP_SET_RIGHT | path->gain_dir |
146042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    tmp);
146142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (path->mute_wid != path->gain_wid) {
146242c41cf8Slipeng sang - Sun Microsystems - Beijing China 		gain = AUDIOHDC_GAIN_MAX;
146342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		(void) audioha_codec_4bit_verb_get(
146442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    statep,
146542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->codec->index,
146642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_wid,
146742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_VERB_SET_AMP_MUTE,
146842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_dir |
146942c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_AMP_SET_LEFT |
147042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    gain);
147142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		(void) audioha_codec_4bit_verb_get(
147242c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    statep,
147342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->codec->index,
147442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_wid,
147542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_VERB_SET_AMP_MUTE,
147642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->mute_dir |
147742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHDC_AMP_SET_RIGHT |
147842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    gain);
147942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
148042c41cf8Slipeng sang - Sun Microsystems - Beijing China }
148142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
148242c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_configure_output(audiohd_state_t * statep)1483211ec5c5SYang-Rong Jerry Zhou audiohd_configure_output(audiohd_state_t *statep)
148488447a05SGarrett D'Amore {
148588447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_LINEOUT);
148688447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_SPEAKER);
148788447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_HP_OUT);
148888447a05SGarrett D'Amore 
1489211ec5c5SYang-Rong Jerry Zhou 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_GREEN);
149088447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_BLACK);
149188447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_GREY);
149288447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_ORANGE);
149388447a05SGarrett D'Amore }
1494c7b817cfSZhao Edgar Liu - Sun Microsystems 
149588447a05SGarrett D'Amore static void
audiohd_configure_input(audiohd_state_t * statep)149688447a05SGarrett D'Amore audiohd_configure_input(audiohd_state_t *statep)
149788447a05SGarrett D'Amore {
149888447a05SGarrett D'Amore 	(void) audiohd_set_input_pin(statep);
149988447a05SGarrett D'Amore 	audiohd_set_monitor_gain(statep);
150088447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_LINE_IN);
150188447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_CD);
150288447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_MIC_IN);
150388447a05SGarrett D'Amore }
1504c7b817cfSZhao Edgar Liu - Sun Microsystems 
150588447a05SGarrett D'Amore static int
audiohd_set_recsrc(void * arg,uint64_t val)150688447a05SGarrett D'Amore audiohd_set_recsrc(void *arg, uint64_t val)
150788447a05SGarrett D'Amore {
150888447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
150988447a05SGarrett D'Amore 	audiohd_state_t *statep = pc->statep;
151088447a05SGarrett D'Amore 
151188447a05SGarrett D'Amore 	if (val & ~(statep->inmask))
151288447a05SGarrett D'Amore 		return (EINVAL);
151388447a05SGarrett D'Amore 
151488447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
151588447a05SGarrett D'Amore 	pc->val = val;
151688447a05SGarrett D'Amore 	audiohd_configure_input(statep);
151788447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
151888447a05SGarrett D'Amore 	return (0);
151988447a05SGarrett D'Amore }
152088447a05SGarrett D'Amore 
152188447a05SGarrett D'Amore static int
audiohd_set_rear(void * arg,uint64_t val)152288447a05SGarrett D'Amore audiohd_set_rear(void *arg, uint64_t val)
152388447a05SGarrett D'Amore {
152488447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
152588447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1526c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
152788447a05SGarrett D'Amore 
152888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
152988447a05SGarrett D'Amore 	pc->val = val;
153088447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_BLACK);
153188447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
153288447a05SGarrett D'Amore 
153388447a05SGarrett D'Amore 	return (0);
153488447a05SGarrett D'Amore }
153588447a05SGarrett D'Amore 
153688447a05SGarrett D'Amore static int
audiohd_set_center(void * arg,uint64_t val)153788447a05SGarrett D'Amore audiohd_set_center(void *arg, uint64_t val)
153888447a05SGarrett D'Amore {
153988447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
154088447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1541c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_CHANNEL_VOLUME(val);
154288447a05SGarrett D'Amore 
154388447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
154488447a05SGarrett D'Amore 	pc->val = val;
154588447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_ORANGE);
154688447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
154788447a05SGarrett D'Amore 
154888447a05SGarrett D'Amore 	return (0);
154988447a05SGarrett D'Amore }
155088447a05SGarrett D'Amore 
155188447a05SGarrett D'Amore static int
audiohd_set_surround(void * arg,uint64_t val)155288447a05SGarrett D'Amore audiohd_set_surround(void *arg, uint64_t val)
155388447a05SGarrett D'Amore {
155488447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
155588447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1556c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
155788447a05SGarrett D'Amore 
155888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
155988447a05SGarrett D'Amore 	pc->val = val;
156088447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_GREY);
156188447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
156288447a05SGarrett D'Amore 
156388447a05SGarrett D'Amore 	return (0);
156488447a05SGarrett D'Amore }
156588447a05SGarrett D'Amore 
156688447a05SGarrett D'Amore static int
audiohd_set_lfe(void * arg,uint64_t val)156788447a05SGarrett D'Amore audiohd_set_lfe(void *arg, uint64_t val)
156888447a05SGarrett D'Amore {
156988447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
157088447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1571c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_CHANNEL_VOLUME(val);
157288447a05SGarrett D'Amore 
157388447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
157488447a05SGarrett D'Amore 	pc->val = val;
157588447a05SGarrett D'Amore 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_ORANGE);
157688447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
157788447a05SGarrett D'Amore 
157888447a05SGarrett D'Amore 	return (0);
157988447a05SGarrett D'Amore }
158088447a05SGarrett D'Amore static int
audiohd_set_speaker(void * arg,uint64_t val)158188447a05SGarrett D'Amore audiohd_set_speaker(void *arg, uint64_t val)
158288447a05SGarrett D'Amore {
158388447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
158488447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1585c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
158688447a05SGarrett D'Amore 
158788447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
158888447a05SGarrett D'Amore 	pc->val = val;
158988447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_SPEAKER);
159088447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
159188447a05SGarrett D'Amore 
159288447a05SGarrett D'Amore 	return (0);
159388447a05SGarrett D'Amore }
159488447a05SGarrett D'Amore static int
audiohd_set_front(void * arg,uint64_t val)159588447a05SGarrett D'Amore audiohd_set_front(void *arg, uint64_t val)
159688447a05SGarrett D'Amore {
159788447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
159888447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1599c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
160088447a05SGarrett D'Amore 
160188447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
160288447a05SGarrett D'Amore 	pc->val = val;
160365a41de7SYang-Rong Jerry Zhou 	audiohd_set_pin_volume_by_color(statep, AUDIOHD_PIN_GREEN);
160488447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
160588447a05SGarrett D'Amore 
160688447a05SGarrett D'Amore 	return (0);
160788447a05SGarrett D'Amore }
1608e7236f70SZhao Edgar Liu - Sun Microsystems 
160988447a05SGarrett D'Amore static int
audiohd_set_headphone(void * arg,uint64_t val)161088447a05SGarrett D'Amore audiohd_set_headphone(void *arg, uint64_t val)
161188447a05SGarrett D'Amore {
161288447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
161388447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1614c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
161588447a05SGarrett D'Amore 
161688447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
161788447a05SGarrett D'Amore 	pc->val = val;
161888447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_HP_OUT);
161988447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
162088447a05SGarrett D'Amore 
162188447a05SGarrett D'Amore 	return (0);
162288447a05SGarrett D'Amore }
1623e7236f70SZhao Edgar Liu - Sun Microsystems 
162488447a05SGarrett D'Amore static int
audiohd_set_linein(void * arg,uint64_t val)162588447a05SGarrett D'Amore audiohd_set_linein(void *arg, uint64_t val)
162688447a05SGarrett D'Amore {
162788447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
162888447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1629c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
163088447a05SGarrett D'Amore 
163188447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
163288447a05SGarrett D'Amore 	pc->val = val;
163388447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_LINE_IN);
163488447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
163588447a05SGarrett D'Amore 
163688447a05SGarrett D'Amore 	return (0);
163788447a05SGarrett D'Amore }
163888447a05SGarrett D'Amore 
163988447a05SGarrett D'Amore static int
audiohd_set_loopback(void * arg,uint64_t val)1640e7236f70SZhao Edgar Liu - Sun Microsystems audiohd_set_loopback(void *arg, uint64_t val)
1641e7236f70SZhao Edgar Liu - Sun Microsystems {
1642e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		*pc = arg;
1643e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = pc->statep;
1644e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_path_t		*path = NULL;
1645e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget = NULL;
1646e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_pin_t		*pin = NULL;
1647e7236f70SZhao Edgar Liu - Sun Microsystems 	wid_t			wid;
1648e7236f70SZhao Edgar Liu - Sun Microsystems 	uint32_t		pinctrl;
1649e7236f70SZhao Edgar Liu - Sun Microsystems 	int			i, j;
1650e7236f70SZhao Edgar Liu - Sun Microsystems 
1651e7236f70SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
1652e7236f70SZhao Edgar Liu - Sun Microsystems 	pc->val = val;
1653e7236f70SZhao Edgar Liu - Sun Microsystems 
1654e7236f70SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < statep->pathnum; i++) {
1655e7236f70SZhao Edgar Liu - Sun Microsystems 		path = statep->path[i];
1656e7236f70SZhao Edgar Liu - Sun Microsystems 		if (path == NULL || path->path_type != LOOPBACK)
1657e7236f70SZhao Edgar Liu - Sun Microsystems 			continue;
1658e7236f70SZhao Edgar Liu - Sun Microsystems 
1659e7236f70SZhao Edgar Liu - Sun Microsystems 		for (j = 0; j < path->pin_nums; j++) {
1660e7236f70SZhao Edgar Liu - Sun Microsystems 			wid = path->pin_wid[j];
1661e7236f70SZhao Edgar Liu - Sun Microsystems 			widget = path->codec->widget[wid];
1662e7236f70SZhao Edgar Liu - Sun Microsystems 			pin = (audiohd_pin_t *)widget->priv;
1663e7236f70SZhao Edgar Liu - Sun Microsystems 
1664e7236f70SZhao Edgar Liu - Sun Microsystems 			if (val == 1) {
1665e7236f70SZhao Edgar Liu - Sun Microsystems 				/* Turn on loopback recording */
1666e7236f70SZhao Edgar Liu - Sun Microsystems 				pinctrl = audioha_codec_verb_get(statep,
1667e7236f70SZhao Edgar Liu - Sun Microsystems 				    path->codec->index, wid,
1668e7236f70SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
1669e7236f70SZhao Edgar Liu - Sun Microsystems 				(void) audioha_codec_verb_get(statep,
1670e7236f70SZhao Edgar Liu - Sun Microsystems 				    path->codec->index, wid,
1671e7236f70SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_VERB_SET_PIN_CTRL,
1672e7236f70SZhao Edgar Liu - Sun Microsystems 				    pinctrl | AUDIOHD_PIN_OUT_ENABLE);
1673e7236f70SZhao Edgar Liu - Sun Microsystems 
1674e7236f70SZhao Edgar Liu - Sun Microsystems 				if (pin->cap & AUDIOHD_EXT_AMP_MASK) {
1675e7236f70SZhao Edgar Liu - Sun Microsystems 					(void) audioha_codec_verb_get(statep,
1676e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->codec->index,
1677e7236f70SZhao Edgar Liu - Sun Microsystems 					    wid, AUDIOHDC_VERB_SET_EAPD,
1678e7236f70SZhao Edgar Liu - Sun Microsystems 					    AUDIOHD_EXT_AMP_ENABLE);
1679e7236f70SZhao Edgar Liu - Sun Microsystems 				}
1680e7236f70SZhao Edgar Liu - Sun Microsystems 
1681e7236f70SZhao Edgar Liu - Sun Microsystems 			} else {
1682e7236f70SZhao Edgar Liu - Sun Microsystems 				/* Turn off loopback recording */
1683e7236f70SZhao Edgar Liu - Sun Microsystems 				if (pin->device == DTYPE_LINE_IN) {
1684e7236f70SZhao Edgar Liu - Sun Microsystems 					pinctrl = audioha_codec_verb_get(statep,
1685e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->codec->index, wid,
1686e7236f70SZhao Edgar Liu - Sun Microsystems 					    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
1687e7236f70SZhao Edgar Liu - Sun Microsystems 					(void) audioha_codec_verb_get(statep,
1688e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->codec->index, wid,
1689e7236f70SZhao Edgar Liu - Sun Microsystems 					    AUDIOHDC_VERB_SET_PIN_CTRL,
1690e7236f70SZhao Edgar Liu - Sun Microsystems 					    pinctrl & ~AUDIOHD_PIN_OUT_ENABLE);
1691e7236f70SZhao Edgar Liu - Sun Microsystems 				}
1692e7236f70SZhao Edgar Liu - Sun Microsystems 			}
1693e7236f70SZhao Edgar Liu - Sun Microsystems 
1694e7236f70SZhao Edgar Liu - Sun Microsystems 		}
1695e7236f70SZhao Edgar Liu - Sun Microsystems 	}
1696e7236f70SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
1697e7236f70SZhao Edgar Liu - Sun Microsystems 
1698e7236f70SZhao Edgar Liu - Sun Microsystems 	return (0);
1699e7236f70SZhao Edgar Liu - Sun Microsystems }
1700e7236f70SZhao Edgar Liu - Sun Microsystems 
1701e7236f70SZhao Edgar Liu - Sun Microsystems static int
audiohd_set_mic(void * arg,uint64_t val)170288447a05SGarrett D'Amore audiohd_set_mic(void *arg, uint64_t val)
170388447a05SGarrett D'Amore {
170488447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
170588447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1706c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
170788447a05SGarrett D'Amore 
170888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
170988447a05SGarrett D'Amore 	pc->val = val;
171088447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_MIC_IN);
171188447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
171288447a05SGarrett D'Amore 
171388447a05SGarrett D'Amore 	return (0);
171488447a05SGarrett D'Amore }
171588447a05SGarrett D'Amore 
171688447a05SGarrett D'Amore static int
audiohd_set_cd(void * arg,uint64_t val)171788447a05SGarrett D'Amore audiohd_set_cd(void *arg, uint64_t val)
171888447a05SGarrett D'Amore {
171988447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
172088447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1721c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
172288447a05SGarrett D'Amore 
172388447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
172488447a05SGarrett D'Amore 	pc->val = val;
172588447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_CD);
172688447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
172788447a05SGarrett D'Amore 
172888447a05SGarrett D'Amore 	return (0);
172988447a05SGarrett D'Amore }
173088447a05SGarrett D'Amore 
173188447a05SGarrett D'Amore static int
audiohd_set_mongain(void * arg,uint64_t val)173288447a05SGarrett D'Amore audiohd_set_mongain(void *arg, uint64_t val)
173388447a05SGarrett D'Amore {
173488447a05SGarrett D'Amore 	audiohd_ctrl_t	*pc = arg;
173588447a05SGarrett D'Amore 	audiohd_state_t	*statep = pc->statep;
1736c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_2CHANNELS_VOLUME(val);
173788447a05SGarrett D'Amore 
173888447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
173988447a05SGarrett D'Amore 	pc->val = val;
174070feb41cSZhao Edgar Liu - Sun Microsystems 	audiohd_set_monitor_gain(statep);
174188447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
174288447a05SGarrett D'Amore 
174388447a05SGarrett D'Amore 	return (0);
174488447a05SGarrett D'Amore }
174588447a05SGarrett D'Amore 
174642c41cf8Slipeng sang - Sun Microsystems - Beijing China static int
audiohd_set_beep(void * arg,uint64_t val)174742c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_set_beep(void *arg, uint64_t val)
174842c41cf8Slipeng sang - Sun Microsystems - Beijing China {
174942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_ctrl_t  *pc = arg;
175042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_state_t *statep = pc->statep;
1751c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_CHECK_CHANNEL_VOLUME(val);
175242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
175342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	mutex_enter(&statep->hda_mutex);
175442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	pc->val = val;
175542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_set_beep_volume(statep);
175642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	mutex_exit(&statep->hda_mutex);
175742c41cf8Slipeng sang - Sun Microsystems - Beijing China 
175842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	return (0);
175942c41cf8Slipeng sang - Sun Microsystems - Beijing China }
176042c41cf8Slipeng sang - Sun Microsystems - Beijing China 
176188447a05SGarrett D'Amore #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
176288447a05SGarrett D'Amore #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
176388447a05SGarrett D'Amore #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
176488447a05SGarrett D'Amore #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
176588447a05SGarrett D'Amore #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
176688447a05SGarrett D'Amore #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
176788447a05SGarrett D'Amore #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
176888447a05SGarrett D'Amore 
176988447a05SGarrett D'Amore static void
audiohd_del_controls(audiohd_state_t * statep)177088447a05SGarrett D'Amore audiohd_del_controls(audiohd_state_t *statep)
177188447a05SGarrett D'Amore {
177288447a05SGarrett D'Amore 	int		i;
1773c1cfefcdSZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < CTL_MAX; i++) {
1774c1cfefcdSZhao Edgar Liu - Sun Microsystems 		audiohd_ctrl_t *ac = &statep->ctrls[i];
1775c1cfefcdSZhao Edgar Liu - Sun Microsystems 		if (ac->ctrl != NULL) {
1776c1cfefcdSZhao Edgar Liu - Sun Microsystems 			audio_dev_del_control(ac->ctrl);
1777c1cfefcdSZhao Edgar Liu - Sun Microsystems 			ac->ctrl = NULL;
1778c1cfefcdSZhao Edgar Liu - Sun Microsystems 		}
177988447a05SGarrett D'Amore 	}
178088447a05SGarrett D'Amore }
178188447a05SGarrett D'Amore 
1782c1cfefcdSZhao Edgar Liu - Sun Microsystems static void
audiohd_create_mono(audiohd_state_t * statep,int ctl,const char * id,int flags,int defval,audio_ctrl_wr_t fn)1783c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_create_mono(audiohd_state_t *statep, int ctl,
1784c1cfefcdSZhao Edgar Liu - Sun Microsystems     const char *id, int flags, int defval, audio_ctrl_wr_t fn)
178588447a05SGarrett D'Amore {
1786c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		*ac;
1787c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audio_ctrl_desc_t	desc;
178888447a05SGarrett D'Amore 
1789c1cfefcdSZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
1790c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1791c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac = &statep->ctrls[ctl];
1792c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->statep = statep;
1793c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->num = ctl;
1794c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1795c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_name = id;
1796c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_type = AUDIO_CTRL_TYPE_MONO;
1797c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_minvalue = 0;
1798c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_maxvalue = 100;
1799c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_flags = flags;
1800c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1801c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->val = defval;
1802c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->ctrl = audio_dev_add_control(statep->adev, &desc,
1803c1cfefcdSZhao Edgar Liu - Sun Microsystems 	    audiohd_get_control, fn, ac);
180488447a05SGarrett D'Amore }
180588447a05SGarrett D'Amore 
1806c1cfefcdSZhao Edgar Liu - Sun Microsystems static void
audiohd_create_stereo(audiohd_state_t * statep,int ctl,const char * id,int flags,int defval,audio_ctrl_wr_t fn)1807c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_create_stereo(audiohd_state_t *statep, int ctl,
1808c1cfefcdSZhao Edgar Liu - Sun Microsystems     const char *id, int flags, int defval, audio_ctrl_wr_t fn)
1809c1cfefcdSZhao Edgar Liu - Sun Microsystems {
1810c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		*ac;
1811c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audio_ctrl_desc_t	desc;
1812c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1813c1cfefcdSZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
1814c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1815c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac = &statep->ctrls[ctl];
1816c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->statep = statep;
1817c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->num = ctl;
1818c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1819c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_name = id;
1820c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
1821c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_minvalue = 0;
1822c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_maxvalue = 100;
1823c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_flags = flags;
1824c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1825c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->val = (defval << 8) | defval;
1826c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->ctrl = audio_dev_add_control(statep->adev, &desc,
1827c1cfefcdSZhao Edgar Liu - Sun Microsystems 	    audiohd_get_control, fn, ac);
1828c1cfefcdSZhao Edgar Liu - Sun Microsystems }
1829c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1830c1cfefcdSZhao Edgar Liu - Sun Microsystems static void
audiohd_create_bool(audiohd_state_t * statep,int ctl,const char * id,int defval,audio_ctrl_wr_t fn)1831e7236f70SZhao Edgar Liu - Sun Microsystems audiohd_create_bool(audiohd_state_t *statep, int ctl,
1832e7236f70SZhao Edgar Liu - Sun Microsystems     const char *id, int defval, audio_ctrl_wr_t fn)
1833e7236f70SZhao Edgar Liu - Sun Microsystems {
1834e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		*ac;
1835e7236f70SZhao Edgar Liu - Sun Microsystems 	audio_ctrl_desc_t	desc;
1836e7236f70SZhao Edgar Liu - Sun Microsystems 
1837e7236f70SZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
1838e7236f70SZhao Edgar Liu - Sun Microsystems 
1839e7236f70SZhao Edgar Liu - Sun Microsystems 	ac = &statep->ctrls[ctl];
1840e7236f70SZhao Edgar Liu - Sun Microsystems 	ac->statep = statep;
1841e7236f70SZhao Edgar Liu - Sun Microsystems 	ac->num = ctl;
1842e7236f70SZhao Edgar Liu - Sun Microsystems 
1843e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_name = id;
1844e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
1845e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_minvalue = 0;
1846e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_maxvalue = 1;
1847e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_flags = RECCTL;
1848e7236f70SZhao Edgar Liu - Sun Microsystems 
1849e7236f70SZhao Edgar Liu - Sun Microsystems 	ac->val = defval;
1850e7236f70SZhao Edgar Liu - Sun Microsystems 	ac->ctrl = audio_dev_add_control(statep->adev, &desc,
1851e7236f70SZhao Edgar Liu - Sun Microsystems 	    audiohd_get_control, fn, ac);
1852e7236f70SZhao Edgar Liu - Sun Microsystems }
1853e7236f70SZhao Edgar Liu - Sun Microsystems 
1854e7236f70SZhao Edgar Liu - Sun Microsystems static void
audiohd_create_recsrc(audiohd_state_t * statep)1855c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_create_recsrc(audiohd_state_t *statep)
1856c1cfefcdSZhao Edgar Liu - Sun Microsystems {
1857c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t *ac;
1858c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audio_ctrl_desc_t desc;
1859c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1860c1cfefcdSZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
1861c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1862c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac = &statep->ctrls[CTL_RECSRC];
1863c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->statep = statep;
1864c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->num = CTL_RECSRC;
1865c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1866c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_name = AUDIO_CTRL_ID_RECSRC;
1867c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
1868e7236f70SZhao Edgar Liu - Sun Microsystems 	desc.acd_flags = RECVOL;
1869c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_minvalue = statep->inmask;
1870c1cfefcdSZhao Edgar Liu - Sun Microsystems 	desc.acd_maxvalue = statep->inmask;
1871c1cfefcdSZhao Edgar Liu - Sun Microsystems 	for (int i = 0; audiohd_dtypes[i]; i++) {
1872c1cfefcdSZhao Edgar Liu - Sun Microsystems 		desc.acd_enum[i] = audiohd_dtypes[i];
1873c1cfefcdSZhao Edgar Liu - Sun Microsystems 	}
1874c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1875c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->val = (1U << DTYPE_MIC_IN);
1876c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ac->ctrl = audio_dev_add_control(statep->adev, &desc,
1877c1cfefcdSZhao Edgar Liu - Sun Microsystems 	    audiohd_get_control, audiohd_set_recsrc, ac);
1878c1cfefcdSZhao Edgar Liu - Sun Microsystems }
1879c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1880c1cfefcdSZhao Edgar Liu - Sun Microsystems static void
audiohd_create_controls(audiohd_state_t * statep)1881c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_create_controls(audiohd_state_t *statep)
1882c1cfefcdSZhao Edgar Liu - Sun Microsystems {
1883c1cfefcdSZhao Edgar Liu - Sun Microsystems 	wid_t			wid;
1884c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget;
1885c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_path_t		*path;
1886c1cfefcdSZhao Edgar Liu - Sun Microsystems 	hda_codec_t		*codec;
1887c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_pin_t		*pin;
1888c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_pin_color_t	color;
1889c1cfefcdSZhao Edgar Liu - Sun Microsystems 	int			i, j;
1890c1cfefcdSZhao Edgar Liu - Sun Microsystems 
189113084339SYang-Rong Jerry Zhou 	/*
1892901f2979SZhao Edgar Liu - Sun Microsystems 	 * We always use soft volume control to adjust PCM volume.
189313084339SYang-Rong Jerry Zhou 	 */
18942c30fa45SGarrett D'Amore 	audio_dev_add_soft_volume(statep->adev);
1895c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1896c1cfefcdSZhao Edgar Liu - Sun Microsystems 	/* Allocate other controls */
189788447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
189888447a05SGarrett D'Amore 		path = statep->path[i];
1899c1cfefcdSZhao Edgar Liu - Sun Microsystems 		if (path == NULL)
190088447a05SGarrett D'Amore 			continue;
190188447a05SGarrett D'Amore 		codec = path->codec;
190242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
190388447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
190488447a05SGarrett D'Amore 			wid = path->pin_wid[j];
190588447a05SGarrett D'Amore 			widget = codec->widget[wid];
190688447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)widget->priv;
1907c1cfefcdSZhao Edgar Liu - Sun Microsystems 			color = (pin->config >> AUDIOHD_PIN_CLR_OFF) &
190888447a05SGarrett D'Amore 			    AUDIOHD_PIN_CLR_MASK;
1909c1cfefcdSZhao Edgar Liu - Sun Microsystems 			if (color == AUDIOHD_PIN_GREEN) {
1910c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_FRONT,
1911c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_FRONT, MAINVOL, 75,
1912c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_front);
1913c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (color == AUDIOHD_PIN_BLACK &&
191488447a05SGarrett D'Amore 			    pin->device != DTYPE_HP_OUT &&
191588447a05SGarrett D'Amore 			    pin->device != DTYPE_MIC_IN) {
1916c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_REAR,
1917c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_REAR, MAINVOL, 75,
1918c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_rear);
1919c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (color == AUDIOHD_PIN_ORANGE) {
1920c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_mono(statep, CTL_CENTER,
1921c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_CENTER, MAINVOL, 75,
1922c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_center);
1923c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_mono(statep, CTL_LFE,
1924c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_LFE, MAINVOL, 75,
1925c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_lfe);
1926c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (color == AUDIOHD_PIN_GREY) {
1927c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_SURROUND,
1928c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_SURROUND, MAINVOL, 75,
1929c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_surround);
1930c1cfefcdSZhao Edgar Liu - Sun Microsystems 			}
1931c1cfefcdSZhao Edgar Liu - Sun Microsystems 			if (pin->device == DTYPE_SPEAKER) {
1932c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_SPEAKER,
1933c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_SPEAKER, MAINVOL, 75,
1934c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_speaker);
1935c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (pin->device == DTYPE_HP_OUT) {
1936c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_HEADPHONE,
1937c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_HEADPHONE, MAINVOL, 75,
1938c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_headphone);
1939c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (pin->device == DTYPE_LINE_IN) {
1940c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_LINEIN,
1941c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_LINEIN, RECVOL, 50,
1942c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_linein);
1943c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (pin->device == DTYPE_MIC_IN) {
1944c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_MIC,
1945c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_MIC, RECVOL, 50,
1946c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_mic);
1947c1cfefcdSZhao Edgar Liu - Sun Microsystems 			} else if (pin->device == DTYPE_CD) {
1948c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_stereo(statep, CTL_CD,
1949c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_CD, RECVOL, 50,
1950c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_cd);
1951c1cfefcdSZhao Edgar Liu - Sun Microsystems 			}
1952c1cfefcdSZhao Edgar Liu - Sun Microsystems 		}
1953c1cfefcdSZhao Edgar Liu - Sun Microsystems 
1954c1cfefcdSZhao Edgar Liu - Sun Microsystems 		if (path->path_type == BEEP) {
1955c1cfefcdSZhao Edgar Liu - Sun Microsystems 			widget = codec->widget[path->beep_wid];
1956c1cfefcdSZhao Edgar Liu - Sun Microsystems 			if (widget->type == WTYPE_BEEP &&
1957c1cfefcdSZhao Edgar Liu - Sun Microsystems 			    path->gain_wid != 0) {
1958c1cfefcdSZhao Edgar Liu - Sun Microsystems 				audiohd_create_mono(statep, CTL_BEEP,
1959e7236f70SZhao Edgar Liu - Sun Microsystems 				    AUDIO_CTRL_ID_BEEP, AUDIO_CTRL_FLAG_RW, 75,
1960c1cfefcdSZhao Edgar Liu - Sun Microsystems 				    audiohd_set_beep);
1961c1cfefcdSZhao Edgar Liu - Sun Microsystems 				continue;
196288447a05SGarrett D'Amore 			}
196388447a05SGarrett D'Amore 		}
196488447a05SGarrett D'Amore 	}
196588447a05SGarrett D'Amore 
1966e7236f70SZhao Edgar Liu - Sun Microsystems 	if (statep->monitor_supported) {
1967c1cfefcdSZhao Edgar Liu - Sun Microsystems 		audiohd_create_stereo(statep, CTL_MONGAIN,
1968c1cfefcdSZhao Edgar Liu - Sun Microsystems 		    AUDIO_CTRL_ID_MONGAIN, MONVOL, 0,
1969c1cfefcdSZhao Edgar Liu - Sun Microsystems 		    audiohd_set_mongain);
197088447a05SGarrett D'Amore 	}
197188447a05SGarrett D'Amore 
1972e7236f70SZhao Edgar Liu - Sun Microsystems 	if (statep->loopback_supported) {
1973e7236f70SZhao Edgar Liu - Sun Microsystems 		audiohd_create_bool(statep, CTL_LOOP, AUDIO_CTRL_ID_LOOPBACK,
1974e7236f70SZhao Edgar Liu - Sun Microsystems 		    0, audiohd_set_loopback);
1975e7236f70SZhao Edgar Liu - Sun Microsystems 	}
1976e7236f70SZhao Edgar Liu - Sun Microsystems 
1977c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_create_recsrc(statep);
197888447a05SGarrett D'Amore 	audiohd_configure_output(statep);
197988447a05SGarrett D'Amore 	audiohd_configure_input(statep);
198088447a05SGarrett D'Amore }
198188447a05SGarrett D'Amore 
198288447a05SGarrett D'Amore /*
198388447a05SGarrett D'Amore  * quiesce(9E) entry point.
198488447a05SGarrett D'Amore  *
198588447a05SGarrett D'Amore  * This function is called when the system is single-threaded at high
198688447a05SGarrett D'Amore  * PIL with preemption disabled. Therefore, this function must not be
198788447a05SGarrett D'Amore  * blocked.
198888447a05SGarrett D'Amore  *
198988447a05SGarrett D'Amore  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
199088447a05SGarrett D'Amore  * DDI_FAILURE indicates an error condition and should almost never happen.
199188447a05SGarrett D'Amore  */
199288447a05SGarrett D'Amore static int
audiohd_quiesce(dev_info_t * dip)199388447a05SGarrett D'Amore audiohd_quiesce(dev_info_t *dip)
199488447a05SGarrett D'Amore {
199588447a05SGarrett D'Amore 	audiohd_state_t		*statep;
199688447a05SGarrett D'Amore 
199788447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
199888447a05SGarrett D'Amore 
1999ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
200088447a05SGarrett D'Amore 	audiohd_stop_dma(statep);
2001ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
200288447a05SGarrett D'Amore 
200388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
200488447a05SGarrett D'Amore }
200542c41cf8Slipeng sang - Sun Microsystems - Beijing China 
200642c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_beep_on(void * arg)200742c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_beep_on(void *arg)
200842c41cf8Slipeng sang - Sun Microsystems - Beijing China {
200942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	hda_codec_t *codec = ((audiohd_widget_t *)arg)->codec;
2010ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t *statep = codec->statep;
201142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int caddr = codec->index;
201242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	wid_t wid = ((audiohd_widget_t *)arg)->wid_wid;
201342c41cf8Slipeng sang - Sun Microsystems - Beijing China 
2014ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
201542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	(void) audioha_codec_verb_get(statep, caddr, wid,
201642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_VERB_SET_BEEP_GEN, audiohd_beep_divider);
2017ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
201842c41cf8Slipeng sang - Sun Microsystems - Beijing China }
201942c41cf8Slipeng sang - Sun Microsystems - Beijing China 
202042c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_beep_off(void * arg)202142c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_beep_off(void *arg)
202242c41cf8Slipeng sang - Sun Microsystems - Beijing China {
202342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	hda_codec_t *codec = ((audiohd_widget_t *)arg)->codec;
2024ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t *statep = codec->statep;
202542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int caddr = codec->index;
202642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	wid_t wid = ((audiohd_widget_t *)arg)->wid_wid;
202742c41cf8Slipeng sang - Sun Microsystems - Beijing China 
2028ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
202942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	(void) audioha_codec_verb_get(statep, caddr, wid,
203042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	    AUDIOHDC_VERB_SET_BEEP_GEN, AUDIOHDC_MUTE_BEEP_GEN);
2031ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
203242c41cf8Slipeng sang - Sun Microsystems - Beijing China }
203342c41cf8Slipeng sang - Sun Microsystems - Beijing China 
203442c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_beep_freq(void * arg,int freq)203542c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_beep_freq(void *arg, int freq)
203642c41cf8Slipeng sang - Sun Microsystems - Beijing China {
20370c240c64SZhao Edgar Liu - Sun Microsystems 	hda_codec_t 	*codec = ((audiohd_widget_t *)arg)->codec;
2038ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t	*statep = codec->statep;
20390c240c64SZhao Edgar Liu - Sun Microsystems 	uint32_t	vid = codec->vid >> 16;
2040ea463888SZhao Edgar Liu - Sun Microsystems 	int		divider;
20410c240c64SZhao Edgar Liu - Sun Microsystems 
204242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	_NOTE(ARGUNUSED(arg));
204342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (freq == 0) {
2044ea463888SZhao Edgar Liu - Sun Microsystems 		divider = 0;
204542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	} else {
204642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if (freq > AUDIOHDC_MAX_BEEP_GEN)
204742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			freq = AUDIOHDC_MAX_BEEP_GEN;
204842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		else if (freq < AUDIOHDC_MIX_BEEP_GEN)
204942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			freq = AUDIOHDC_MIX_BEEP_GEN;
20500c240c64SZhao Edgar Liu - Sun Microsystems 
20510c240c64SZhao Edgar Liu - Sun Microsystems 		switch (vid) {
20520c240c64SZhao Edgar Liu - Sun Microsystems 		case AUDIOHD_VID_SIGMATEL:
20530c240c64SZhao Edgar Liu - Sun Microsystems 			/*
20540c240c64SZhao Edgar Liu - Sun Microsystems 			 * Sigmatel HD codec specification:
20550c240c64SZhao Edgar Liu - Sun Microsystems 			 * frequency = 48000 * (257 - Divider) / 1024
20560c240c64SZhao Edgar Liu - Sun Microsystems 			 */
2057ea463888SZhao Edgar Liu - Sun Microsystems 			divider = 257 - freq * 1024 / AUDIOHDC_SAMPR48000;
20580c240c64SZhao Edgar Liu - Sun Microsystems 			break;
20590c240c64SZhao Edgar Liu - Sun Microsystems 		default:
2060ea463888SZhao Edgar Liu - Sun Microsystems 			divider = AUDIOHDC_SAMPR48000 / freq;
20610c240c64SZhao Edgar Liu - Sun Microsystems 			break;
20620c240c64SZhao Edgar Liu - Sun Microsystems 		}
206342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
206442c41cf8Slipeng sang - Sun Microsystems - Beijing China 
206542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (audiohd_beep_vol == 0)
2066ea463888SZhao Edgar Liu - Sun Microsystems 		divider = 0;
2067ea463888SZhao Edgar Liu - Sun Microsystems 
2068ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
2069ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_beep_divider = divider;
2070ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
207142c41cf8Slipeng sang - Sun Microsystems - Beijing China }
207242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
207388447a05SGarrett D'Amore /*
207488447a05SGarrett D'Amore  * audiohd_init_state()
207588447a05SGarrett D'Amore  *
207688447a05SGarrett D'Amore  * Description
207788447a05SGarrett D'Amore  *	This routine initailizes soft state of driver instance,
207888447a05SGarrett D'Amore  *	also, it requests an interrupt cookie and initializes
207988447a05SGarrett D'Amore  *	mutex for soft state.
208088447a05SGarrett D'Amore  */
208188447a05SGarrett D'Amore /*ARGSUSED*/
208288447a05SGarrett D'Amore static int
audiohd_init_state(audiohd_state_t * statep,dev_info_t * dip)208388447a05SGarrett D'Amore audiohd_init_state(audiohd_state_t *statep, dev_info_t *dip)
208488447a05SGarrett D'Amore {
208588447a05SGarrett D'Amore 	audio_dev_t	*adev;
208688447a05SGarrett D'Amore 
208788447a05SGarrett D'Amore 	statep->hda_dip = dip;
2088c7b817cfSZhao Edgar Liu - Sun Microsystems 	statep->hda_rirb_rp = 0;
208988447a05SGarrett D'Amore 
209088447a05SGarrett D'Amore 	if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
2091e7236f70SZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(NULL, "unable to allocate audio dev");
2092c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
209388447a05SGarrett D'Amore 	}
209488447a05SGarrett D'Amore 	statep->adev = adev;
209588447a05SGarrett D'Amore 
209688447a05SGarrett D'Amore 	/* set device information */
209788447a05SGarrett D'Amore 	audio_dev_set_description(adev, AUDIOHD_DEV_CONFIG);
209888447a05SGarrett D'Amore 	audio_dev_set_version(adev, AUDIOHD_DEV_VERSION);
209988447a05SGarrett D'Amore 
2100c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
210188447a05SGarrett D'Amore }	/* audiohd_init_state() */
210288447a05SGarrett D'Amore 
210388447a05SGarrett D'Amore /*
210488447a05SGarrett D'Amore  * audiohd_init_pci()
210588447a05SGarrett D'Amore  *
210688447a05SGarrett D'Amore  * Description
210788447a05SGarrett D'Amore  *	enable driver to access PCI configure space and memory
210888447a05SGarrett D'Amore  *	I/O space.
210988447a05SGarrett D'Amore  */
211088447a05SGarrett D'Amore static int
audiohd_init_pci(audiohd_state_t * statep,ddi_device_acc_attr_t * acc_attr)211188447a05SGarrett D'Amore audiohd_init_pci(audiohd_state_t *statep, ddi_device_acc_attr_t *acc_attr)
211288447a05SGarrett D'Amore {
211388447a05SGarrett D'Amore 	uint16_t	cmdreg;
211488447a05SGarrett D'Amore 	uint16_t	vid;
211588447a05SGarrett D'Amore 	uint8_t		cTmp;
211688447a05SGarrett D'Amore 	dev_info_t	*dip = statep->hda_dip;
2117c7b817cfSZhao Edgar Liu - Sun Microsystems 	audio_dev_t	*adev = statep->adev;
211888447a05SGarrett D'Amore 
211988447a05SGarrett D'Amore 	if (pci_config_setup(dip, &statep->hda_pci_handle) == DDI_FAILURE) {
2120c7b817cfSZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(adev,
212188447a05SGarrett D'Amore 		    "pci config mapping failed");
2122c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
212388447a05SGarrett D'Amore 	}
212488447a05SGarrett D'Amore 
212588447a05SGarrett D'Amore 	if (ddi_regs_map_setup(dip, 1, &statep->hda_reg_base, 0,
212688447a05SGarrett D'Amore 	    0, acc_attr, &statep->hda_reg_handle) != DDI_SUCCESS) {
2127c7b817cfSZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(adev,
212888447a05SGarrett D'Amore 		    "memory I/O mapping failed");
2129c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
213088447a05SGarrett D'Amore 	}
213188447a05SGarrett D'Amore 
213288447a05SGarrett D'Amore 	/*
213388447a05SGarrett D'Amore 	 * HD audio control uses memory I/O only, enable it here.
213488447a05SGarrett D'Amore 	 */
213588447a05SGarrett D'Amore 	cmdreg = pci_config_get16(statep->hda_pci_handle, PCI_CONF_COMM);
213688447a05SGarrett D'Amore 	pci_config_put16(statep->hda_pci_handle, PCI_CONF_COMM,
213788447a05SGarrett D'Amore 	    cmdreg | PCI_COMM_MAE | PCI_COMM_ME);
213888447a05SGarrett D'Amore 
213988447a05SGarrett D'Amore 	vid = pci_config_get16(statep->hda_pci_handle, PCI_CONF_VENID);
214088447a05SGarrett D'Amore 	switch (vid) {
214188447a05SGarrett D'Amore 	case AUDIOHD_VID_INTEL:
214288447a05SGarrett D'Amore 		/*
214388447a05SGarrett D'Amore 		 * Currently, Intel (G)MCH and ICHx chipsets support PCI
214488447a05SGarrett D'Amore 		 * Express QoS. It implemenets two VCs(virtual channels)
214588447a05SGarrett D'Amore 		 * and allows OS software to map 8 traffic classes to the
214688447a05SGarrett D'Amore 		 * two VCs. Some BIOSes initialize HD audio hardware to
214788447a05SGarrett D'Amore 		 * use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
214888447a05SGarrett D'Amore 		 * recommended. However, solaris doesn't support PCI express
214988447a05SGarrett D'Amore 		 * QoS yet. As a result, this driver can not work for those
215088447a05SGarrett D'Amore 		 * hardware without touching PCI express control registers.
215188447a05SGarrett D'Amore 		 * Here, we set TCSEL to 0 so as to use TC0/VC0 (VC0 is
215288447a05SGarrett D'Amore 		 * always enabled and TC0 is always mapped to VC0) for all
215388447a05SGarrett D'Amore 		 * Intel HD audio controllers.
215488447a05SGarrett D'Amore 		 */
215588447a05SGarrett D'Amore 		cTmp = pci_config_get8(statep->hda_pci_handle,
215688447a05SGarrett D'Amore 		    AUDIOHD_INTEL_PCI_TCSEL);
215788447a05SGarrett D'Amore 		pci_config_put8(statep->hda_pci_handle,
215888447a05SGarrett D'Amore 		    AUDIOHD_INTEL_PCI_TCSEL, (cTmp & AUDIOHD_INTEL_TCS_MASK));
215988447a05SGarrett D'Amore 		break;
216088447a05SGarrett D'Amore 	case AUDIOHD_VID_ATI:
216188447a05SGarrett D'Amore 		/*
216288447a05SGarrett D'Amore 		 * Refer to ATI SB450 datesheet. We set snoop for SB450
216388447a05SGarrett D'Amore 		 * like hardware.
216488447a05SGarrett D'Amore 		 */
216588447a05SGarrett D'Amore 		cTmp = pci_config_get8(statep->hda_pci_handle,
216688447a05SGarrett D'Amore 		    AUDIOHD_ATI_PCI_MISC2);
216788447a05SGarrett D'Amore 		pci_config_put8(statep->hda_pci_handle, AUDIOHD_ATI_PCI_MISC2,
216888447a05SGarrett D'Amore 		    (cTmp & AUDIOHD_ATI_MISC2_MASK) | AUDIOHD_ATI_MISC2_SNOOP);
216988447a05SGarrett D'Amore 		break;
2170989b958fSZhao Edgar Liu - Sun Microsystems 	case AUDIOHD_VID_NVIDIA:
217188447a05SGarrett D'Amore 		/*
217288447a05SGarrett D'Amore 		 * Refer to the datasheet, we set snoop for NVIDIA
217388447a05SGarrett D'Amore 		 * like hardware
217488447a05SGarrett D'Amore 		 */
217588447a05SGarrett D'Amore 		cTmp = pci_config_get8(statep->hda_pci_handle,
217688447a05SGarrett D'Amore 		    AUDIOHD_CORB_SIZE_OFF);
217788447a05SGarrett D'Amore 		pci_config_put8(statep->hda_pci_handle, AUDIOHD_CORB_SIZE_OFF,
217888447a05SGarrett D'Amore 		    cTmp | AUDIOHD_NVIDIA_SNOOP);
217988447a05SGarrett D'Amore 		break;
218088447a05SGarrett D'Amore 	default:
218188447a05SGarrett D'Amore 		break;
218288447a05SGarrett D'Amore 	}
218388447a05SGarrett D'Amore 
2184c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
218588447a05SGarrett D'Amore }	/* audiohd_init_pci() */
218688447a05SGarrett D'Amore 
218788447a05SGarrett D'Amore 
218888447a05SGarrett D'Amore /*
218988447a05SGarrett D'Amore  * audiohd_fini_pci()
219088447a05SGarrett D'Amore  *
219188447a05SGarrett D'Amore  * Description
219288447a05SGarrett D'Amore  *	Release mapping for PCI configure space.
219388447a05SGarrett D'Amore  */
219488447a05SGarrett D'Amore static void
audiohd_fini_pci(audiohd_state_t * statep)219588447a05SGarrett D'Amore audiohd_fini_pci(audiohd_state_t *statep)
219688447a05SGarrett D'Amore {
219788447a05SGarrett D'Amore 	if (statep->hda_reg_handle != NULL) {
219888447a05SGarrett D'Amore 		ddi_regs_map_free(&statep->hda_reg_handle);
219988447a05SGarrett D'Amore 	}
220088447a05SGarrett D'Amore 
220188447a05SGarrett D'Amore 	if (statep->hda_pci_handle != NULL) {
220288447a05SGarrett D'Amore 		pci_config_teardown(&statep->hda_pci_handle);
220388447a05SGarrett D'Amore 	}
220488447a05SGarrett D'Amore 
220588447a05SGarrett D'Amore }	/* audiohd_fini_pci() */
220688447a05SGarrett D'Amore 
220788447a05SGarrett D'Amore /*
220888447a05SGarrett D'Amore  * audiohd_stop_dma()
220988447a05SGarrett D'Amore  *
221088447a05SGarrett D'Amore  * Description
221188447a05SGarrett D'Amore  *	Stop all DMA behaviors of controllers, for command I/O
221288447a05SGarrett D'Amore  *	and each audio stream.
221388447a05SGarrett D'Amore  */
221488447a05SGarrett D'Amore static void
audiohd_stop_dma(audiohd_state_t * statep)221588447a05SGarrett D'Amore audiohd_stop_dma(audiohd_state_t *statep)
221688447a05SGarrett D'Amore {
221788447a05SGarrett D'Amore 	int	i;
221888447a05SGarrett D'Amore 	uint_t	base;
221988447a05SGarrett D'Amore 	uint8_t	bTmp;
222088447a05SGarrett D'Amore 
222188447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, 0);
222288447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, 0);
222388447a05SGarrett D'Amore 
222488447a05SGarrett D'Amore 	base = AUDIOHD_REG_SD_BASE;
222588447a05SGarrett D'Amore 	for (i = 0; i < statep->hda_streams_nums; i++) {
222688447a05SGarrett D'Amore 		bTmp = AUDIOHD_REG_GET8(base + AUDIOHD_SDREG_OFFSET_CTL);
222788447a05SGarrett D'Amore 
222888447a05SGarrett D'Amore 		/* for input/output stream, it is the same */
222988447a05SGarrett D'Amore 		bTmp &= ~AUDIOHDR_RIRBCTL_DMARUN;
223088447a05SGarrett D'Amore 
223188447a05SGarrett D'Amore 		AUDIOHD_REG_SET8(base + AUDIOHD_SDREG_OFFSET_CTL, bTmp);
223288447a05SGarrett D'Amore 		base += AUDIOHD_REG_SD_LEN;
223388447a05SGarrett D'Amore 	}
223488447a05SGarrett D'Amore 
223588447a05SGarrett D'Amore 	/* wait 40us for stream DMA to stop */
223688447a05SGarrett D'Amore 	drv_usecwait(40);
223788447a05SGarrett D'Amore 
223888447a05SGarrett D'Amore }	/* audiohd_stop_dma() */
223988447a05SGarrett D'Amore 
224088447a05SGarrett D'Amore /*
224188447a05SGarrett D'Amore  * audiohd_reset_controller()
224288447a05SGarrett D'Amore  *
224388447a05SGarrett D'Amore  * Description:
224488447a05SGarrett D'Amore  *	This routine is just used to reset controller and
224588447a05SGarrett D'Amore  *	CODEC as well by HW reset bit in global control
224688447a05SGarrett D'Amore  *	register of HD controller.
224788447a05SGarrett D'Amore  */
224888447a05SGarrett D'Amore static int
audiohd_reset_controller(audiohd_state_t * statep)224988447a05SGarrett D'Amore audiohd_reset_controller(audiohd_state_t *statep)
225088447a05SGarrett D'Amore {
225188447a05SGarrett D'Amore 	int		i;
225288447a05SGarrett D'Amore 	uint16_t	sTmp;
225388447a05SGarrett D'Amore 	uint32_t	gctl;
225488447a05SGarrett D'Amore 
225588447a05SGarrett D'Amore 	/* Reset Status register but preserve the first bit */
225688447a05SGarrett D'Amore 	sTmp = AUDIOHD_REG_GET16(AUDIOHD_REG_STATESTS);
225788447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_STATESTS, sTmp & 0x8000);
225888447a05SGarrett D'Amore 
225988447a05SGarrett D'Amore 	/* reset controller */
226088447a05SGarrett D'Amore 	gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
226188447a05SGarrett D'Amore 	gctl &= ~AUDIOHDR_GCTL_CRST;
226288447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL, gctl);  /* entering reset state */
226388447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
226488447a05SGarrett D'Amore 		/* Empirical testing time: 150 */
226588447a05SGarrett D'Amore 		drv_usecwait(150);
226688447a05SGarrett D'Amore 		gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
226788447a05SGarrett D'Amore 		if ((gctl & AUDIOHDR_GCTL_CRST) == 0)
226888447a05SGarrett D'Amore 			break;
226988447a05SGarrett D'Amore 	}
227088447a05SGarrett D'Amore 
227188447a05SGarrett D'Amore 	if ((gctl & AUDIOHDR_GCTL_CRST) != 0) {
227288447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
227388447a05SGarrett D'Amore 		    "failed to enter reset state");
2274c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
227588447a05SGarrett D'Amore 	}
227688447a05SGarrett D'Amore 
227788447a05SGarrett D'Amore 	/* Empirical testing time:300 */
227888447a05SGarrett D'Amore 	drv_usecwait(300);
227988447a05SGarrett D'Amore 
228088447a05SGarrett D'Amore 	/* exit reset state */
228188447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL, gctl | AUDIOHDR_GCTL_CRST);
228288447a05SGarrett D'Amore 
228388447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
228488447a05SGarrett D'Amore 		/* Empirical testing time: 150, which works well */
228588447a05SGarrett D'Amore 		drv_usecwait(150);
228688447a05SGarrett D'Amore 		gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
228788447a05SGarrett D'Amore 		if (gctl & AUDIOHDR_GCTL_CRST)
228888447a05SGarrett D'Amore 			break;
228988447a05SGarrett D'Amore 	}
229088447a05SGarrett D'Amore 
229188447a05SGarrett D'Amore 	if ((gctl & AUDIOHDR_GCTL_CRST) == 0) {
229288447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
229388447a05SGarrett D'Amore 		    "failed to exit reset state");
2294c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
229588447a05SGarrett D'Amore 	}
229688447a05SGarrett D'Amore 
229788447a05SGarrett D'Amore 	/* HD spec requires to wait 250us at least. we use 500us */
229888447a05SGarrett D'Amore 	drv_usecwait(500);
229988447a05SGarrett D'Amore 
230088447a05SGarrett D'Amore 	/* enable unsolicited response */
230188447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL,
230288447a05SGarrett D'Amore 	    gctl |  AUDIOHDR_GCTL_URESPE);
230388447a05SGarrett D'Amore 
2304c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
230588447a05SGarrett D'Amore 
230688447a05SGarrett D'Amore }	/* audiohd_reset_controller() */
230788447a05SGarrett D'Amore 
230888447a05SGarrett D'Amore /*
230988447a05SGarrett D'Amore  * audiohd_alloc_dma_mem()
231088447a05SGarrett D'Amore  *
231188447a05SGarrett D'Amore  * Description:
231288447a05SGarrett D'Amore  *	This is an utility routine. It is used to allocate DMA
231388447a05SGarrett D'Amore  *	memory.
231488447a05SGarrett D'Amore  */
231588447a05SGarrett D'Amore static int
audiohd_alloc_dma_mem(audiohd_state_t * statep,audiohd_dma_t * pdma,size_t memsize,ddi_dma_attr_t * dma_attr_p,uint_t dma_flags)231688447a05SGarrett D'Amore audiohd_alloc_dma_mem(audiohd_state_t *statep, audiohd_dma_t *pdma,
231788447a05SGarrett D'Amore     size_t memsize, ddi_dma_attr_t *dma_attr_p, uint_t dma_flags)
231888447a05SGarrett D'Amore {
231988447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
232088447a05SGarrett D'Amore 	uint_t			count;
232188447a05SGarrett D'Amore 	dev_info_t		*dip = statep->hda_dip;
232288447a05SGarrett D'Amore 	audio_dev_t		*ahandle = statep->adev;
232388447a05SGarrett D'Amore 
232488447a05SGarrett D'Amore 	if (ddi_dma_alloc_handle(dip, dma_attr_p, DDI_DMA_SLEEP,
232588447a05SGarrett D'Amore 	    NULL, &pdma->ad_dmahdl) != DDI_SUCCESS) {
232688447a05SGarrett D'Amore 		audio_dev_warn(ahandle,
232788447a05SGarrett D'Amore 		    "ddi_dma_alloc_handle failed");
2328c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
232988447a05SGarrett D'Amore 	}
233088447a05SGarrett D'Amore 
233188447a05SGarrett D'Amore 	if (ddi_dma_mem_alloc(pdma->ad_dmahdl, memsize, &hda_dev_accattr,
233288447a05SGarrett D'Amore 	    dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING),
233388447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL,
233488447a05SGarrett D'Amore 	    (caddr_t *)&pdma->ad_vaddr, &pdma->ad_real_sz,
233588447a05SGarrett D'Amore 	    &pdma->ad_acchdl) != DDI_SUCCESS) {
233688447a05SGarrett D'Amore 		audio_dev_warn(ahandle,
233788447a05SGarrett D'Amore 		    "ddi_dma_mem_alloc failed");
2338c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
233988447a05SGarrett D'Amore 	}
234088447a05SGarrett D'Amore 
234188447a05SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(pdma->ad_dmahdl, NULL,
234288447a05SGarrett D'Amore 	    (caddr_t)pdma->ad_vaddr, pdma->ad_real_sz, dma_flags,
234388447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_DMA_MAPPED) {
234488447a05SGarrett D'Amore 		audio_dev_warn(ahandle,
234588447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed");
2346c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
234788447a05SGarrett D'Amore 	}
234888447a05SGarrett D'Amore 
234988447a05SGarrett D'Amore 	pdma->ad_paddr = (uint64_t)(cookie.dmac_laddress);
235088447a05SGarrett D'Amore 	pdma->ad_req_sz = memsize;
235188447a05SGarrett D'Amore 
2352c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
235388447a05SGarrett D'Amore }	/* audiohd_alloc_dma_mem() */
235488447a05SGarrett D'Amore 
235588447a05SGarrett D'Amore /*
235688447a05SGarrett D'Amore  * audiohd_release_dma_mem()
235788447a05SGarrett D'Amore  *
235888447a05SGarrett D'Amore  * Description:
235988447a05SGarrett D'Amore  *	Release DMA memory.
236088447a05SGarrett D'Amore  */
236188447a05SGarrett D'Amore 
236288447a05SGarrett D'Amore static void
audiohd_release_dma_mem(audiohd_dma_t * pdma)236388447a05SGarrett D'Amore audiohd_release_dma_mem(audiohd_dma_t *pdma)
236488447a05SGarrett D'Amore {
236588447a05SGarrett D'Amore 	if (pdma->ad_dmahdl != NULL) {
236688447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(pdma->ad_dmahdl);
236788447a05SGarrett D'Amore 	}
236888447a05SGarrett D'Amore 
236988447a05SGarrett D'Amore 	if (pdma->ad_acchdl != NULL) {
237088447a05SGarrett D'Amore 		ddi_dma_mem_free(&pdma->ad_acchdl);
237188447a05SGarrett D'Amore 		pdma->ad_acchdl = NULL;
237288447a05SGarrett D'Amore 	}
237388447a05SGarrett D'Amore 
237488447a05SGarrett D'Amore 	if (pdma->ad_dmahdl != NULL) {
237588447a05SGarrett D'Amore 		ddi_dma_free_handle(&pdma->ad_dmahdl);
237688447a05SGarrett D'Amore 		pdma->ad_dmahdl = NULL;
237788447a05SGarrett D'Amore 	}
237888447a05SGarrett D'Amore 
237988447a05SGarrett D'Amore }	/* audiohd_release_dma_mem() */
238088447a05SGarrett D'Amore 
238188447a05SGarrett D'Amore /*
238288447a05SGarrett D'Amore  * audiohd_reinit_hda()
238388447a05SGarrett D'Amore  *
238488447a05SGarrett D'Amore  * Description:
238588447a05SGarrett D'Amore  *	This routine is used to re-initialize HD controller and codec.
238688447a05SGarrett D'Amore  */
238788447a05SGarrett D'Amore static int
audiohd_reinit_hda(audiohd_state_t * statep)238888447a05SGarrett D'Amore audiohd_reinit_hda(audiohd_state_t *statep)
238988447a05SGarrett D'Amore {
239088447a05SGarrett D'Amore 	uint64_t	addr;
239188447a05SGarrett D'Amore 
239288447a05SGarrett D'Amore 	/* set PCI configure space in case it's not restored OK */
239388447a05SGarrett D'Amore 	(void) audiohd_init_pci(statep, &hda_dev_accattr);
239488447a05SGarrett D'Amore 
239588447a05SGarrett D'Amore 	/* reset controller */
2396c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_reset_controller(statep) != DDI_SUCCESS)
2397c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
239888447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_SYNC, 0); /* needn't sync stream */
239988447a05SGarrett D'Amore 
240088447a05SGarrett D'Amore 	/* Initialize controller RIRB */
240188447a05SGarrett D'Amore 	addr = statep->hda_dma_rirb.ad_paddr;
240288447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBLBASE, (uint32_t)addr);
240388447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBUBASE,
240488447a05SGarrett D'Amore 	    (uint32_t)(addr >> 32));
240588447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_RIRBWP, AUDIOHDR_RIRBWP_RESET);
240688447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSIZE, AUDIOHDR_RIRBSZ_256);
240788447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, AUDIOHDR_RIRBCTL_DMARUN |
240888447a05SGarrett D'Amore 	    AUDIOHDR_RIRBCTL_RINTCTL);
240988447a05SGarrett D'Amore 
241088447a05SGarrett D'Amore 	/* Initialize controller CORB */
241188447a05SGarrett D'Amore 	addr = statep->hda_dma_corb.ad_paddr;
241288447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, AUDIOHDR_CORBRP_RESET);
241388447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBLBASE, (uint32_t)addr);
241488447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBUBASE,
241588447a05SGarrett D'Amore 	    (uint32_t)(addr >> 32));
241688447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBSIZE, AUDIOHDR_CORBSZ_256);
241788447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, 0);
241888447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, 0);
241988447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, AUDIOHDR_CORBCTL_DMARUN);
242088447a05SGarrett D'Amore 
242188447a05SGarrett D'Amore 	audiohd_restore_codec_gpio(statep);
242288447a05SGarrett D'Amore 	audiohd_restore_path(statep);
242388447a05SGarrett D'Amore 	audiohd_init_path(statep);
242488447a05SGarrett D'Amore 
2425c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
242688447a05SGarrett D'Amore }	/* audiohd_reinit_hda */
242788447a05SGarrett D'Amore 
242888447a05SGarrett D'Amore /*
242988447a05SGarrett D'Amore  * audiohd_init_controller()
243088447a05SGarrett D'Amore  *
243188447a05SGarrett D'Amore  * Description:
243288447a05SGarrett D'Amore  *	This routine is used to initialize HD controller. It
243388447a05SGarrett D'Amore  *	allocates DMA memory for CORB/RIRB, buffer descriptor
243488447a05SGarrett D'Amore  *	list and cylic data buffer for both play and record
243588447a05SGarrett D'Amore  *	stream.
243688447a05SGarrett D'Amore  */
243788447a05SGarrett D'Amore static int
audiohd_init_controller(audiohd_state_t * statep)243888447a05SGarrett D'Amore audiohd_init_controller(audiohd_state_t *statep)
243988447a05SGarrett D'Amore {
244088447a05SGarrett D'Amore 	uint64_t	addr;
244188447a05SGarrett D'Amore 	uint16_t	gcap;
244288447a05SGarrett D'Amore 	int		retval;
244388447a05SGarrett D'Amore 
244488447a05SGarrett D'Amore 	ddi_dma_attr_t	dma_attr = {
244588447a05SGarrett D'Amore 		DMA_ATTR_V0,		/* version */
244688447a05SGarrett D'Amore 		0,			/* addr_lo */
244788447a05SGarrett D'Amore 		0xffffffffffffffffULL,	/* addr_hi */
244888447a05SGarrett D'Amore 		0x00000000ffffffffULL,	/* count_max */
244988447a05SGarrett D'Amore 		128,			/* 128-byte alignment as HD spec */
245088447a05SGarrett D'Amore 		0xfff,			/* burstsize */
245188447a05SGarrett D'Amore 		1,			/* minxfer */
245288447a05SGarrett D'Amore 		0xffffffff,		/* maxxfer */
245388447a05SGarrett D'Amore 		0xffffffff,		/* seg */
245488447a05SGarrett D'Amore 		1,			/* sgllen */
245588447a05SGarrett D'Amore 		1,			/* granular */
245688447a05SGarrett D'Amore 		0			/* flags */
245788447a05SGarrett D'Amore 	};
245888447a05SGarrett D'Amore 
245988447a05SGarrett D'Amore 	gcap = AUDIOHD_REG_GET16(AUDIOHD_REG_GCAP);
246088447a05SGarrett D'Amore 
246188447a05SGarrett D'Amore 	/*
246288447a05SGarrett D'Amore 	 * If the device doesn't support 64-bit DMA, we should not
246388447a05SGarrett D'Amore 	 * allocate DMA memory from 4G above
246488447a05SGarrett D'Amore 	 */
246588447a05SGarrett D'Amore 	if ((gcap & AUDIOHDR_GCAP_64OK) == 0)
246688447a05SGarrett D'Amore 		dma_attr.dma_attr_addr_hi = 0xffffffffUL;
246788447a05SGarrett D'Amore 
246888447a05SGarrett D'Amore 	statep->hda_input_streams = (gcap & AUDIOHDR_GCAP_INSTREAMS) >>
246988447a05SGarrett D'Amore 	    AUDIOHD_INSTR_NUM_OFF;
247088447a05SGarrett D'Amore 	statep->hda_output_streams = (gcap & AUDIOHDR_GCAP_OUTSTREAMS) >>
247188447a05SGarrett D'Amore 	    AUDIOHD_OUTSTR_NUM_OFF;
247288447a05SGarrett D'Amore 	statep->hda_streams_nums = statep->hda_input_streams +
247388447a05SGarrett D'Amore 	    statep->hda_output_streams;
247488447a05SGarrett D'Amore 
247588447a05SGarrett D'Amore 	statep->hda_record_regbase = AUDIOHD_REG_SD_BASE;
247688447a05SGarrett D'Amore 	statep->hda_play_regbase = AUDIOHD_REG_SD_BASE + AUDIOHD_REG_SD_LEN *
247788447a05SGarrett D'Amore 	    statep->hda_input_streams;
247888447a05SGarrett D'Amore 
247988447a05SGarrett D'Amore 	/* stop all dma before starting to reset controller */
248088447a05SGarrett D'Amore 	audiohd_stop_dma(statep);
248188447a05SGarrett D'Amore 
2482c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_reset_controller(statep) != DDI_SUCCESS)
2483c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
248488447a05SGarrett D'Amore 
248588447a05SGarrett D'Amore 	/* check codec */
248688447a05SGarrett D'Amore 	statep->hda_codec_mask = AUDIOHD_REG_GET16(AUDIOHD_REG_STATESTS);
248788447a05SGarrett D'Amore 	if (!statep->hda_codec_mask) {
248888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
248988447a05SGarrett D'Amore 		    "no codec exists");
2490c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
249188447a05SGarrett D'Amore 	}
249288447a05SGarrett D'Amore 
249388447a05SGarrett D'Amore 	/* allocate DMA for CORB */
249488447a05SGarrett D'Amore 	retval = audiohd_alloc_dma_mem(statep, &statep->hda_dma_corb,
249588447a05SGarrett D'Amore 	    AUDIOHD_CDBIO_CORB_LEN, &dma_attr,
249688447a05SGarrett D'Amore 	    DDI_DMA_WRITE | DDI_DMA_STREAMING);
2497c6e681c0SYang-Rong Jerry Zhou 	if (retval != DDI_SUCCESS) {
249888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
249988447a05SGarrett D'Amore 		    "failed to alloc DMA for CORB");
2500c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
250188447a05SGarrett D'Amore 	}
250288447a05SGarrett D'Amore 
250388447a05SGarrett D'Amore 	/* allocate DMA for RIRB */
250488447a05SGarrett D'Amore 	retval = audiohd_alloc_dma_mem(statep, &statep->hda_dma_rirb,
250588447a05SGarrett D'Amore 	    AUDIOHD_CDBIO_RIRB_LEN, &dma_attr,
250688447a05SGarrett D'Amore 	    DDI_DMA_READ | DDI_DMA_STREAMING);
2507c6e681c0SYang-Rong Jerry Zhou 	if (retval != DDI_SUCCESS) {
250888447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
250988447a05SGarrett D'Amore 		    "failed to alloc DMA for RIRB");
2510c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
251188447a05SGarrett D'Amore 	}
251288447a05SGarrett D'Amore 
251388447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_SYNC, 0); /* needn't sync stream */
251488447a05SGarrett D'Amore 
251588447a05SGarrett D'Amore 	/* Initialize RIRB */
251688447a05SGarrett D'Amore 	addr = statep->hda_dma_rirb.ad_paddr;
251788447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBLBASE, (uint32_t)addr);
2518c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBUBASE, (uint32_t)(addr >> 32));
251988447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_RIRBWP, AUDIOHDR_RIRBWP_RESET);
252088447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSIZE, AUDIOHDR_RIRBSZ_256);
252188447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, AUDIOHDR_RIRBCTL_DMARUN |
252288447a05SGarrett D'Amore 	    AUDIOHDR_RIRBCTL_RINTCTL);
252388447a05SGarrett D'Amore 
252488447a05SGarrett D'Amore 	/* initialize CORB */
252588447a05SGarrett D'Amore 	addr = statep->hda_dma_corb.ad_paddr;
252688447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, AUDIOHDR_CORBRP_RESET);
252788447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBLBASE, (uint32_t)addr);
2528c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBUBASE, (uint32_t)(addr >> 32));
252988447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBSIZE, AUDIOHDR_CORBSZ_256);
253088447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, 0);
253188447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, 0);
253288447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, AUDIOHDR_CORBCTL_DMARUN);
253388447a05SGarrett D'Amore 
2534c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
253588447a05SGarrett D'Amore }	/* audiohd_init_controller() */
253688447a05SGarrett D'Amore 
253788447a05SGarrett D'Amore /*
253888447a05SGarrett D'Amore  * audiohd_fini_controller()
253988447a05SGarrett D'Amore  *
254088447a05SGarrett D'Amore  * Description:
254188447a05SGarrett D'Amore  *	Releases DMA memory allocated in audiohd_init_controller()
254288447a05SGarrett D'Amore  */
254388447a05SGarrett D'Amore static void
audiohd_fini_controller(audiohd_state_t * statep)254488447a05SGarrett D'Amore audiohd_fini_controller(audiohd_state_t *statep)
254588447a05SGarrett D'Amore {
254688447a05SGarrett D'Amore 	audiohd_release_dma_mem(&statep->hda_dma_rirb);
254788447a05SGarrett D'Amore 	audiohd_release_dma_mem(&statep->hda_dma_corb);
254888447a05SGarrett D'Amore 
254988447a05SGarrett D'Amore }	/* audiohd_fini_controller() */
255088447a05SGarrett D'Amore 
255188447a05SGarrett D'Amore /*
255288447a05SGarrett D'Amore  * audiohd_get_conns_from_entry()
255388447a05SGarrett D'Amore  *
255488447a05SGarrett D'Amore  * Description:
255588447a05SGarrett D'Amore  *	Get connection list from every entry for a widget
255688447a05SGarrett D'Amore  */
255788447a05SGarrett D'Amore static void
audiohd_get_conns_from_entry(hda_codec_t * codec,audiohd_widget_t * widget,uint32_t entry,audiohd_entry_prop_t * prop)255888447a05SGarrett D'Amore audiohd_get_conns_from_entry(hda_codec_t *codec, audiohd_widget_t *widget,
255988447a05SGarrett D'Amore     uint32_t entry, audiohd_entry_prop_t *prop)
256088447a05SGarrett D'Amore {
256188447a05SGarrett D'Amore 	int	i, k, num;
256288447a05SGarrett D'Amore 	wid_t	input_wid;
256388447a05SGarrett D'Amore 
256488447a05SGarrett D'Amore 	for (i = 0; i < prop->conns_per_entry &&
256588447a05SGarrett D'Amore 	    widget->nconns < prop->conn_len;
256688447a05SGarrett D'Amore 	    i++, entry >>= prop->bits_per_conn) {
256788447a05SGarrett D'Amore 		ASSERT(widget->nconns < AUDIOHD_MAX_CONN);
256888447a05SGarrett D'Amore 		input_wid = entry & prop->mask_wid;
256988447a05SGarrett D'Amore 		if (entry & prop->mask_range) {
257088447a05SGarrett D'Amore 			if (widget->nconns == 0) {
257188447a05SGarrett D'Amore 				if (input_wid < codec->first_wid ||
257288447a05SGarrett D'Amore 				    (input_wid > codec->last_wid)) {
257388447a05SGarrett D'Amore 					break;
257488447a05SGarrett D'Amore 				}
257588447a05SGarrett D'Amore 				widget->avail_conn[widget->nconns++] =
257688447a05SGarrett D'Amore 				    input_wid;
257788447a05SGarrett D'Amore 			} else {
257888447a05SGarrett D'Amore 				for (k = widget->avail_conn[widget->nconns-1] +
257988447a05SGarrett D'Amore 				    1; k <= input_wid; k++) {
258088447a05SGarrett D'Amore 					ASSERT(widget->nconns <
258188447a05SGarrett D'Amore 					    AUDIOHD_MAX_CONN);
258288447a05SGarrett D'Amore 					if (k < codec->first_wid ||
258388447a05SGarrett D'Amore 					    (k > codec->last_wid)) {
258488447a05SGarrett D'Amore 						break;
258588447a05SGarrett D'Amore 					} else {
258688447a05SGarrett D'Amore 						num = widget->nconns;
258788447a05SGarrett D'Amore 						widget->avail_conn[num] = k;
258888447a05SGarrett D'Amore 						widget->nconns++;
258988447a05SGarrett D'Amore 					}
259088447a05SGarrett D'Amore 				}
259188447a05SGarrett D'Amore 			}
259288447a05SGarrett D'Amore 		} else {
259388447a05SGarrett D'Amore 			if ((codec->first_wid <= input_wid) && (input_wid <=
259488447a05SGarrett D'Amore 			    codec->last_wid))
259588447a05SGarrett D'Amore 				widget->avail_conn[widget->nconns++] =
259688447a05SGarrett D'Amore 				    input_wid;
259788447a05SGarrett D'Amore 		}
259888447a05SGarrett D'Amore 	}
259988447a05SGarrett D'Amore }
260088447a05SGarrett D'Amore 
260188447a05SGarrett D'Amore /*
260288447a05SGarrett D'Amore  * audiohd_get_conns()
260388447a05SGarrett D'Amore  *
260488447a05SGarrett D'Amore  * Description:
260588447a05SGarrett D'Amore  *	Get all connection list for a widget. The connection list is used for
260688447a05SGarrett D'Amore  *	build output path, input path, and monitor path
260788447a05SGarrett D'Amore  */
260888447a05SGarrett D'Amore static void
audiohd_get_conns(hda_codec_t * codec,wid_t wid)260988447a05SGarrett D'Amore audiohd_get_conns(hda_codec_t *codec, wid_t wid)
261088447a05SGarrett D'Amore {
2611ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
261288447a05SGarrett D'Amore 	audiohd_widget_t	*widget = codec->widget[wid];
261388447a05SGarrett D'Amore 	uint8_t	caddr = codec->index;
261488447a05SGarrett D'Amore 	uint32_t	entry;
261588447a05SGarrett D'Amore 	audiohd_entry_prop_t	prop;
261688447a05SGarrett D'Amore 	wid_t	input_wid;
261788447a05SGarrett D'Amore 	int	i;
261888447a05SGarrett D'Amore 
261988447a05SGarrett D'Amore 	prop.conn_len = audioha_codec_verb_get(statep, caddr, wid,
262088447a05SGarrett D'Amore 	    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_CONNLIST_LEN);
262188447a05SGarrett D'Amore 
262288447a05SGarrett D'Amore 	if (prop.conn_len & AUDIOHD_FORM_MASK) {
262388447a05SGarrett D'Amore 		prop.conns_per_entry = 2;
262488447a05SGarrett D'Amore 		prop.bits_per_conn = 16;
262588447a05SGarrett D'Amore 		prop.mask_range = 0x00008000;
262688447a05SGarrett D'Amore 		prop.mask_wid = 0x00007fff;
262788447a05SGarrett D'Amore 	} else {
262888447a05SGarrett D'Amore 		prop.conns_per_entry = 4;
262988447a05SGarrett D'Amore 		prop.bits_per_conn = 8;
263088447a05SGarrett D'Amore 		prop.mask_range = 0x00000080;
263188447a05SGarrett D'Amore 		prop.mask_wid = 0x0000007f;
263288447a05SGarrett D'Amore 	}
263388447a05SGarrett D'Amore 	prop.conn_len &= AUDIOHD_LEN_MASK;
263488447a05SGarrett D'Amore 
263588447a05SGarrett D'Amore 	/*
263688447a05SGarrett D'Amore 	 * This should not happen since the ConnectionList bit of
263788447a05SGarrett D'Amore 	 * widget capabilities already told us that this widget
263888447a05SGarrett D'Amore 	 * has a connection list
263988447a05SGarrett D'Amore 	 */
264088447a05SGarrett D'Amore 	if (prop.conn_len == 0) {
264188447a05SGarrett D'Amore 		widget->nconns = 0;
264207bec7ccSZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(statep->adev,
264307bec7ccSZhao Edgar Liu - Sun Microsystems 		    "node %d has 0 connections", wid);
264488447a05SGarrett D'Amore 		return;
264588447a05SGarrett D'Amore 	}
264688447a05SGarrett D'Amore 
264788447a05SGarrett D'Amore 	if (prop.conn_len == 1) {
264888447a05SGarrett D'Amore 		entry = audioha_codec_verb_get(statep, caddr,
264988447a05SGarrett D'Amore 		    wid, AUDIOHDC_VERB_GET_CONN_LIST_ENT, 0);
265088447a05SGarrett D'Amore 		input_wid = entry & prop.mask_wid;
265188447a05SGarrett D'Amore 		if ((input_wid < codec->first_wid) ||
265288447a05SGarrett D'Amore 		    (input_wid > codec->last_wid)) {
265388447a05SGarrett D'Amore 			return;
265488447a05SGarrett D'Amore 		}
265588447a05SGarrett D'Amore 		widget->avail_conn[0] = input_wid;
265688447a05SGarrett D'Amore 		widget->nconns = 1;
265788447a05SGarrett D'Amore 		return;
265888447a05SGarrett D'Amore 	}
265988447a05SGarrett D'Amore 	widget->nconns = 0;
266088447a05SGarrett D'Amore 	for (i = 0; i < prop.conn_len; i += prop.conns_per_entry) {
266188447a05SGarrett D'Amore 		entry = audioha_codec_verb_get(statep, caddr, wid,
266288447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_CONN_LIST_ENT, i);
266388447a05SGarrett D'Amore 		audiohd_get_conns_from_entry(codec, widget, entry, &prop);
266488447a05SGarrett D'Amore 	}
266588447a05SGarrett D'Amore }
266688447a05SGarrett D'Amore 
266788447a05SGarrett D'Amore /*
266888447a05SGarrett D'Amore  * Read PinCapabilities & default configuration
266988447a05SGarrett D'Amore  */
267088447a05SGarrett D'Amore static void
audiohd_get_pin_config(audiohd_widget_t * widget)267188447a05SGarrett D'Amore audiohd_get_pin_config(audiohd_widget_t *widget)
267288447a05SGarrett D'Amore {
267388447a05SGarrett D'Amore 	hda_codec_t		*codec = widget->codec;
2674ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
267588447a05SGarrett D'Amore 	audiohd_pin_t		*pin, *prev, *p;
267688447a05SGarrett D'Amore 
267788447a05SGarrett D'Amore 	int		caddr = codec->index;
267888447a05SGarrett D'Amore 	wid_t		wid = widget->wid_wid;
267988447a05SGarrett D'Amore 	uint32_t	cap, config, pinctrl;
268088447a05SGarrett D'Amore 	uint8_t		urctrl, vrefbits;
268188447a05SGarrett D'Amore 
268288447a05SGarrett D'Amore 	cap = audioha_codec_verb_get(statep, caddr, wid,
268388447a05SGarrett D'Amore 	    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PIN_CAP);
268488447a05SGarrett D'Amore 	config = audioha_codec_verb_get(statep, caddr,
268588447a05SGarrett D'Amore 	    wid, AUDIOHDC_VERB_GET_DEFAULT_CONF, 0);
268688447a05SGarrett D'Amore 	pinctrl = audioha_codec_verb_get(statep, caddr,
268788447a05SGarrett D'Amore 	    wid, AUDIOHDC_VERB_GET_PIN_CTRL, 0);
268888447a05SGarrett D'Amore 
268988447a05SGarrett D'Amore 	pin = (audiohd_pin_t *)kmem_zalloc(sizeof (audiohd_pin_t), KM_SLEEP);
269088447a05SGarrett D'Amore 	widget->priv = pin;
269188447a05SGarrett D'Amore 
269288447a05SGarrett D'Amore 	/*
269388447a05SGarrett D'Amore 	 * If the pin has no physical connection for port,
269488447a05SGarrett D'Amore 	 * we won't link it to pin linkage list ???
269588447a05SGarrett D'Amore 	 */
269688447a05SGarrett D'Amore 	if (((config >> AUDIOHD_PIN_CON_STEP) & AUDIOHD_PIN_CON_MASK) == 0x1) {
269788447a05SGarrett D'Amore 		pin->no_phys_conn = 1;
269888447a05SGarrett D'Amore 	}
269988447a05SGarrett D'Amore 
270088447a05SGarrett D'Amore 	/* bit 4:3 are reserved, read-modify-write is needed */
270188447a05SGarrett D'Amore 	pin->ctrl = pinctrl & AUDIOHD_PIN_IO_MASK;
270288447a05SGarrett D'Amore 	pin->wid = wid;
270388447a05SGarrett D'Amore 	pin->cap = cap;
270488447a05SGarrett D'Amore 	pin->config = config;
270588447a05SGarrett D'Amore 	pin->num = 0;
270688447a05SGarrett D'Amore 	pin->finish = 0;
270788447a05SGarrett D'Amore 
270888447a05SGarrett D'Amore 	vrefbits = (cap >> AUDIOHD_PIN_VREF_OFF) & AUDIOHD_PIN_VREF_MASK;
270988447a05SGarrett D'Amore 	if (vrefbits & AUDIOHD_PIN_VREF_L1)
271088447a05SGarrett D'Amore 		pin->vrefvalue = 0x5;
271188447a05SGarrett D'Amore 	else if (vrefbits & AUDIOHD_PIN_VREF_L2)
271288447a05SGarrett D'Amore 		pin->vrefvalue = 0x4;
271388447a05SGarrett D'Amore 	else if (vrefbits & AUDIOHD_PIN_VREF_L3)
271488447a05SGarrett D'Amore 		pin->vrefvalue = 0x2;
271588447a05SGarrett D'Amore 	else
271688447a05SGarrett D'Amore 		pin->vrefvalue = 0x1;
271788447a05SGarrett D'Amore 
271888447a05SGarrett D'Amore 	pin->seq = config & AUDIOHD_PIN_SEQ_MASK;
271988447a05SGarrett D'Amore 	pin->assoc = (config & AUDIOHD_PIN_ASO_MASK) >> AUDIOHD_PIN_ASO_OFF;
272088447a05SGarrett D'Amore 	pin->device = (config & AUDIOHD_PIN_DEV_MASK) >> AUDIOHD_PIN_DEV_OFF;
272188447a05SGarrett D'Amore 
272288447a05SGarrett D'Amore 	/* enable the unsolicited response of the pin */
272388447a05SGarrett D'Amore 	if ((widget->widget_cap & AUDIOHD_URCAP_MASK) &&
272488447a05SGarrett D'Amore 	    (pin->cap & AUDIOHD_DTCCAP_MASK) &&
272588447a05SGarrett D'Amore 	    ((pin->device == DTYPE_LINEOUT) ||
272688447a05SGarrett D'Amore 	    (pin->device == DTYPE_SPDIF_OUT) ||
272788447a05SGarrett D'Amore 	    (pin->device == DTYPE_HP_OUT) ||
272888447a05SGarrett D'Amore 	    (pin->device == DTYPE_MIC_IN))) {
272988447a05SGarrett D'Amore 			urctrl = (uint8_t)(1 << (AUDIOHD_UR_ENABLE_OFF - 1));
273088447a05SGarrett D'Amore 			urctrl |= (wid & AUDIOHD_UR_TAG_MASK);
273188447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, caddr,
2732989b958fSZhao Edgar Liu - Sun Microsystems 			    wid, AUDIOHDC_VERB_SET_UNS_ENABLE, urctrl);
273388447a05SGarrett D'Amore 	}
273488447a05SGarrett D'Amore 	/* accommodate all the pins in a link list sorted by assoc and seq */
273588447a05SGarrett D'Amore 	if (codec->first_pin == NULL) {
273688447a05SGarrett D'Amore 		codec->first_pin = pin;
273788447a05SGarrett D'Amore 	} else {
273888447a05SGarrett D'Amore 		prev = NULL;
273988447a05SGarrett D'Amore 		p = codec->first_pin;
274088447a05SGarrett D'Amore 		while (p) {
274188447a05SGarrett D'Amore 			if (p->assoc > pin->assoc)
274288447a05SGarrett D'Amore 				break;
274388447a05SGarrett D'Amore 			if ((p->assoc == pin->assoc) &&
274488447a05SGarrett D'Amore 			    (p->seq > pin->seq))
274588447a05SGarrett D'Amore 				break;
274688447a05SGarrett D'Amore 			prev = p;
274788447a05SGarrett D'Amore 			p = p->next;
274888447a05SGarrett D'Amore 		}
274988447a05SGarrett D'Amore 		if (prev) {
275088447a05SGarrett D'Amore 			pin->next = prev->next;
275188447a05SGarrett D'Amore 			prev->next = pin;
275288447a05SGarrett D'Amore 		} else {
275388447a05SGarrett D'Amore 			pin->next = codec->first_pin;
275488447a05SGarrett D'Amore 			codec->first_pin = pin;
275588447a05SGarrett D'Amore 		}
275688447a05SGarrett D'Amore 	}
275788447a05SGarrett D'Amore 
275888447a05SGarrett D'Amore }	/* audiohd_get_pin_config() */
275988447a05SGarrett D'Amore 
276088447a05SGarrett D'Amore /*
276188447a05SGarrett D'Amore  * audiohd_create_widgets()
276288447a05SGarrett D'Amore  *
276388447a05SGarrett D'Amore  * Description:
276488447a05SGarrett D'Amore  *	All widgets are created and stored in an array of codec
276588447a05SGarrett D'Amore  */
276688447a05SGarrett D'Amore static int
audiohd_create_widgets(hda_codec_t * codec)276788447a05SGarrett D'Amore audiohd_create_widgets(hda_codec_t *codec)
276888447a05SGarrett D'Amore {
276988447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
2770ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
277188447a05SGarrett D'Amore 	wid_t	wid;
277288447a05SGarrett D'Amore 	uint32_t	type, widcap;
277388447a05SGarrett D'Amore 	int		caddr = codec->index;
277488447a05SGarrett D'Amore 
277588447a05SGarrett D'Amore 	for (wid = codec->first_wid;
277688447a05SGarrett D'Amore 	    wid <= codec->last_wid; wid++) {
277788447a05SGarrett D'Amore 		widget = (audiohd_widget_t *)
277888447a05SGarrett D'Amore 		    kmem_zalloc(sizeof (audiohd_widget_t), KM_SLEEP);
277988447a05SGarrett D'Amore 		codec->widget[wid] = widget;
278088447a05SGarrett D'Amore 		widget->codec = codec;
2781b96a6eceSZhao Edgar Liu - Sun Microsystems 		widget->output_path_next = AUDIOHD_NULL_CONN;
2782b96a6eceSZhao Edgar Liu - Sun Microsystems 		widget->input_path_next = AUDIOHD_NULL_CONN;
2783b96a6eceSZhao Edgar Liu - Sun Microsystems 		widget->beep_path_next = AUDIOHD_NULL_CONN;
2784e7236f70SZhao Edgar Liu - Sun Microsystems 		widget->loopback_path_next = AUDIOHD_NULL_CONN;
278588447a05SGarrett D'Amore 
278688447a05SGarrett D'Amore 		widcap = audioha_codec_verb_get(statep, caddr, wid,
278788447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_AUDIO_WID_CAP);
278888447a05SGarrett D'Amore 		type = AUDIOHD_WIDCAP_TO_WIDTYPE(widcap);
278988447a05SGarrett D'Amore 		widget->wid_wid = wid;
279088447a05SGarrett D'Amore 		widget->type = type;
279188447a05SGarrett D'Amore 		widget->widget_cap = widcap;
279288447a05SGarrett D'Amore 		widget->finish = 0;
279388447a05SGarrett D'Amore 		widget->used = 0;
279488447a05SGarrett D'Amore 
279588447a05SGarrett D'Amore 		/* if there's connection list */
279688447a05SGarrett D'Amore 		if (widcap & AUDIOHD_WIDCAP_CONNLIST) {
279788447a05SGarrett D'Amore 			audiohd_get_conns(codec, wid);
279888447a05SGarrett D'Amore 		}
279988447a05SGarrett D'Amore 
280088447a05SGarrett D'Amore 		/* if power control, power it up to D0 state */
280188447a05SGarrett D'Amore 		if (widcap & AUDIOHD_WIDCAP_PWRCTRL) {
280288447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, caddr, wid,
280388447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_POWER_STATE, 0);
280488447a05SGarrett D'Amore 		}
280588447a05SGarrett D'Amore 
280688447a05SGarrett D'Amore 		/*
280788447a05SGarrett D'Amore 		 * if this widget has format override, we read it.
280888447a05SGarrett D'Amore 		 * Otherwise, it uses the format of audio function.
280988447a05SGarrett D'Amore 		 */
281088447a05SGarrett D'Amore 		if (widcap & AUDIOHD_WIDCAP_FMT_OVRIDE) {
281188447a05SGarrett D'Amore 			widget->pcm_format =
281288447a05SGarrett D'Amore 			    audioha_codec_verb_get(statep, caddr, wid,
281388447a05SGarrett D'Amore 			    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PCM);
281488447a05SGarrett D'Amore 		} else {
281588447a05SGarrett D'Amore 			widget->pcm_format = codec->pcm_format;
281688447a05SGarrett D'Amore 		}
281788447a05SGarrett D'Amore 
281888447a05SGarrett D'Amore 		/*
281988447a05SGarrett D'Amore 		 * Input amplifier. Has the widget input amplifier ?
282088447a05SGarrett D'Amore 		 */
282188447a05SGarrett D'Amore 		if (widcap & AUDIOHD_WIDCAP_INAMP) {
282288447a05SGarrett D'Amore 			/*
282388447a05SGarrett D'Amore 			 * if overrided bit is 0, use the default
282488447a05SGarrett D'Amore 			 * amplifier of audio function as HD spec.
282588447a05SGarrett D'Amore 			 * Otherwise, we read it.
282688447a05SGarrett D'Amore 			 */
282788447a05SGarrett D'Amore 			if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
282888447a05SGarrett D'Amore 				widget->inamp_cap = codec->inamp_cap;
282988447a05SGarrett D'Amore 			else
283088447a05SGarrett D'Amore 				widget->inamp_cap =
283188447a05SGarrett D'Amore 				    audioha_codec_verb_get(statep, caddr, wid,
283288447a05SGarrett D'Amore 				    AUDIOHDC_VERB_GET_PARAM,
283388447a05SGarrett D'Amore 				    AUDIOHDC_PAR_INAMP_CAP);
283488447a05SGarrett D'Amore 		} else {
283588447a05SGarrett D'Amore 			widget->inamp_cap = 0;
283688447a05SGarrett D'Amore 		}
283788447a05SGarrett D'Amore 
283888447a05SGarrett D'Amore 		/*
283988447a05SGarrett D'Amore 		 * output amplifier. Has this widget output amplifier ?
284088447a05SGarrett D'Amore 		 */
284188447a05SGarrett D'Amore 		if (widcap & AUDIOHD_WIDCAP_OUTAMP) {
284288447a05SGarrett D'Amore 			if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
284388447a05SGarrett D'Amore 				widget->outamp_cap = codec->outamp_cap;
284488447a05SGarrett D'Amore 			else
284588447a05SGarrett D'Amore 				widget->outamp_cap =
284688447a05SGarrett D'Amore 				    audioha_codec_verb_get(statep, caddr, wid,
284788447a05SGarrett D'Amore 				    AUDIOHDC_VERB_GET_PARAM,
284888447a05SGarrett D'Amore 				    AUDIOHDC_PAR_OUTAMP_CAP);
284988447a05SGarrett D'Amore 		} else {
285088447a05SGarrett D'Amore 			widget->outamp_cap = 0;
285188447a05SGarrett D'Amore 		}
285288447a05SGarrett D'Amore 
285388447a05SGarrett D'Amore 		switch (type) {
285488447a05SGarrett D'Amore 		case WTYPE_AUDIO_OUT:
285588447a05SGarrett D'Amore 		case WTYPE_AUDIO_IN:
285688447a05SGarrett D'Amore 		case WTYPE_AUDIO_MIX:
285788447a05SGarrett D'Amore 		case WTYPE_AUDIO_SEL:
285888447a05SGarrett D'Amore 		case WTYPE_VENDOR:
285988447a05SGarrett D'Amore 		case WTYPE_POWER:
286088447a05SGarrett D'Amore 		case WTYPE_VOL_KNOB:
286188447a05SGarrett D'Amore 			break;
286288447a05SGarrett D'Amore 		case WTYPE_PIN:
2863ee97b734SZhao Edgar Liu - Sun Microsystems 			/*
2864ee97b734SZhao Edgar Liu - Sun Microsystems 			 * Some codec(like ALC262) don't provide beep widget,
2865ee97b734SZhao Edgar Liu - Sun Microsystems 			 * it only has input Pin to connect an external beep
2866ee97b734SZhao Edgar Liu - Sun Microsystems 			 * (maybe in motherboard or elsewhere). So we open
2867ee97b734SZhao Edgar Liu - Sun Microsystems 			 * all PINs here in order to enable external beep
2868ee97b734SZhao Edgar Liu - Sun Microsystems 			 * source.
2869ee97b734SZhao Edgar Liu - Sun Microsystems 			 */
2870ee97b734SZhao Edgar Liu - Sun Microsystems 			if ((codec->codec_info->flags & EN_PIN_BEEP) == 0) {
2871ee97b734SZhao Edgar Liu - Sun Microsystems 				(void) audioha_codec_4bit_verb_get(statep,
2872ee97b734SZhao Edgar Liu - Sun Microsystems 				    caddr, widget->wid_wid,
2873ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_VERB_SET_AMP_MUTE,
2874ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
2875ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_GAIN_MAX);
2876ee97b734SZhao Edgar Liu - Sun Microsystems 			}
2877ee97b734SZhao Edgar Liu - Sun Microsystems 
287888447a05SGarrett D'Amore 			audiohd_get_pin_config(widget);
287988447a05SGarrett D'Amore 			break;
288088447a05SGarrett D'Amore 		case WTYPE_BEEP:
288142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			/*
288242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * Get the audiohd_beep_switch value from audiohd.conf,
288342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * which is for turning on/off widget beep.
288442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 */
288542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			audiohd_beep = ddi_prop_get_int(DDI_DEV_T_ANY,
288642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    statep->hda_dip,
288742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    DDI_PROP_DONTPASS, "audiohd_beep", 1);
288842c41cf8Slipeng sang - Sun Microsystems - Beijing China 
288942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			if (audiohd_beep) {
289042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				(void) beep_fini();
289142c41cf8Slipeng sang - Sun Microsystems - Beijing China 				(void) beep_init((void *) widget,
289242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    audiohd_beep_on,
289342c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    audiohd_beep_off,
289442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    audiohd_beep_freq);
289542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
289688447a05SGarrett D'Amore 			break;
289788447a05SGarrett D'Amore 		default:
289888447a05SGarrett D'Amore 			break;
289988447a05SGarrett D'Amore 		}
290088447a05SGarrett D'Amore 	}
290188447a05SGarrett D'Amore 
290288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
290388447a05SGarrett D'Amore 
290488447a05SGarrett D'Amore }	/* audiohd_create_widgets() */
290588447a05SGarrett D'Amore 
290688447a05SGarrett D'Amore /*
290788447a05SGarrett D'Amore  * audiohd_destroy_widgets()
290888447a05SGarrett D'Amore  */
290988447a05SGarrett D'Amore static void
audiohd_destroy_widgets(hda_codec_t * codec)291088447a05SGarrett D'Amore audiohd_destroy_widgets(hda_codec_t *codec)
291188447a05SGarrett D'Amore {
291288447a05SGarrett D'Amore 	for (int i = 0; i < AUDIOHD_MAX_WIDGET; i++) {
291388447a05SGarrett D'Amore 		if (codec->widget[i]) {
291488447a05SGarrett D'Amore 			kmem_free(codec->widget[i], sizeof (audiohd_widget_t));
291588447a05SGarrett D'Amore 			codec->widget[i] = NULL;
291688447a05SGarrett D'Amore 		}
291788447a05SGarrett D'Amore 	}
291888447a05SGarrett D'Amore 
291988447a05SGarrett D'Amore }	/* audiohd_destroy_widgets() */
292088447a05SGarrett D'Amore 
292188447a05SGarrett D'Amore /*
292288447a05SGarrett D'Amore  * audiohd_create_codec()
292388447a05SGarrett D'Amore  *
292488447a05SGarrett D'Amore  * Description:
292588447a05SGarrett D'Amore  *	Searching for supported CODEC. If find, allocate memory
292688447a05SGarrett D'Amore  *	to hold codec structure.
292788447a05SGarrett D'Amore  */
292888447a05SGarrett D'Amore static int
audiohd_create_codec(audiohd_state_t * statep)292988447a05SGarrett D'Amore audiohd_create_codec(audiohd_state_t *statep)
293088447a05SGarrett D'Amore {
293188447a05SGarrett D'Amore 	hda_codec_t	*codec;
293288447a05SGarrett D'Amore 	uint32_t	mask, type;
293388447a05SGarrett D'Amore 	uint32_t	nums;
2934cbe6566fSZhao Edgar Liu - Sun Microsystems 	uint32_t	i, j, len;
293588447a05SGarrett D'Amore 	wid_t		wid;
2936cbe6566fSZhao Edgar Liu - Sun Microsystems 	char		buf[128];
2937a33ad26eSZhao Edgar Liu - Sun Microsystems 	int		rate, bits;
2938a33ad26eSZhao Edgar Liu - Sun Microsystems 	dev_info_t	*dip = statep->hda_dip;
2939a33ad26eSZhao Edgar Liu - Sun Microsystems 
294088447a05SGarrett D'Amore 
294188447a05SGarrett D'Amore 	mask = statep->hda_codec_mask;
294288447a05SGarrett D'Amore 	ASSERT(mask != 0);
294388447a05SGarrett D'Amore 
294488447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
294588447a05SGarrett D'Amore 		if ((mask & (1 << i)) == 0)
294688447a05SGarrett D'Amore 			continue;
294788447a05SGarrett D'Amore 		codec = (hda_codec_t *)kmem_zalloc(
294888447a05SGarrett D'Amore 		    sizeof (hda_codec_t), KM_SLEEP);
294988447a05SGarrett D'Amore 		codec->index = i;
295088447a05SGarrett D'Amore 		codec->vid = audioha_codec_verb_get(statep, i,
295188447a05SGarrett D'Amore 		    AUDIOHDC_NODE_ROOT, AUDIOHDC_VERB_GET_PARAM,
295288447a05SGarrett D'Amore 		    AUDIOHDC_PAR_VENDOR_ID);
295326ae4a35SZhao Edgar Liu - Sun Microsystems 		if (codec->vid == (uint32_t)(-1)) {
295426ae4a35SZhao Edgar Liu - Sun Microsystems 			kmem_free(codec, sizeof (hda_codec_t));
29550c240c64SZhao Edgar Liu - Sun Microsystems 			continue;
295626ae4a35SZhao Edgar Liu - Sun Microsystems 		}
29570c240c64SZhao Edgar Liu - Sun Microsystems 
295888447a05SGarrett D'Amore 		codec->revid =
295988447a05SGarrett D'Amore 		    audioha_codec_verb_get(statep, i,
296088447a05SGarrett D'Amore 		    AUDIOHDC_NODE_ROOT, AUDIOHDC_VERB_GET_PARAM,
296188447a05SGarrett D'Amore 		    AUDIOHDC_PAR_REV_ID);
296288447a05SGarrett D'Amore 
296388447a05SGarrett D'Amore 		nums = audioha_codec_verb_get(statep,
296488447a05SGarrett D'Amore 		    i, AUDIOHDC_NODE_ROOT,
296588447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_NODE_COUNT);
296688447a05SGarrett D'Amore 		if (nums == (uint32_t)(-1)) {
296788447a05SGarrett D'Amore 			kmem_free(codec, sizeof (hda_codec_t));
296888447a05SGarrett D'Amore 			continue;
296988447a05SGarrett D'Amore 		}
297088447a05SGarrett D'Amore 		wid = (nums >> AUDIOHD_CODEC_STR_OFF) & AUDIOHD_CODEC_STR_MASK;
297188447a05SGarrett D'Amore 		nums = nums & AUDIOHD_CODEC_NUM_MASK;
297288447a05SGarrett D'Amore 
297388447a05SGarrett D'Amore 		/*
297488447a05SGarrett D'Amore 		 * Assume that each codec has just one audio function group
297588447a05SGarrett D'Amore 		 */
297688447a05SGarrett D'Amore 		for (j = 0; j < nums; j++, wid++) {
297788447a05SGarrett D'Amore 			type = audioha_codec_verb_get(statep, i, wid,
297888447a05SGarrett D'Amore 			    AUDIOHDC_VERB_GET_PARAM,
297988447a05SGarrett D'Amore 			    AUDIOHDC_PAR_FUNCTION_TYPE);
298088447a05SGarrett D'Amore 			if ((type & AUDIOHD_CODEC_TYPE_MASK) ==
298188447a05SGarrett D'Amore 			    AUDIOHDC_AUDIO_FUNC_GROUP) {
298288447a05SGarrett D'Amore 				codec->wid_afg = wid;
298388447a05SGarrett D'Amore 				break;
298488447a05SGarrett D'Amore 			}
298588447a05SGarrett D'Amore 		}
298688447a05SGarrett D'Amore 
298788447a05SGarrett D'Amore 		if (codec->wid_afg == 0) {
298888447a05SGarrett D'Amore 			kmem_free(codec, sizeof (hda_codec_t));
298988447a05SGarrett D'Amore 			continue;
299088447a05SGarrett D'Amore 		}
299188447a05SGarrett D'Amore 
299288447a05SGarrett D'Amore 		ASSERT(codec->wid_afg == wid);
299388447a05SGarrett D'Amore 
2994cbe6566fSZhao Edgar Liu - Sun Microsystems 		len = sizeof (audiohd_codecs) / sizeof (audiohd_codec_info_t);
2995cbe6566fSZhao Edgar Liu - Sun Microsystems 		for (j = 0; j < len-1; j++) {
2996cbe6566fSZhao Edgar Liu - Sun Microsystems 			if (audiohd_codecs[j].devid == codec->vid) {
2997cbe6566fSZhao Edgar Liu - Sun Microsystems 				codec->codec_info = &(audiohd_codecs[j]);
2998cbe6566fSZhao Edgar Liu - Sun Microsystems 				break;
2999cbe6566fSZhao Edgar Liu - Sun Microsystems 			}
3000cbe6566fSZhao Edgar Liu - Sun Microsystems 		}
3001cbe6566fSZhao Edgar Liu - Sun Microsystems 
3002cbe6566fSZhao Edgar Liu - Sun Microsystems 		if (codec->codec_info == NULL) {
3003cbe6566fSZhao Edgar Liu - Sun Microsystems 			codec->codec_info = &(audiohd_codecs[len-1]);
3004cbe6566fSZhao Edgar Liu - Sun Microsystems 			(void) snprintf(buf, sizeof (buf),
3005cbe6566fSZhao Edgar Liu - Sun Microsystems 			    "Unknown HD codec: 0x%x", codec->vid);
3006cbe6566fSZhao Edgar Liu - Sun Microsystems 		} else {
3007cbe6566fSZhao Edgar Liu - Sun Microsystems 			(void) snprintf(buf, sizeof (buf), "HD codec: %s",
3008cbe6566fSZhao Edgar Liu - Sun Microsystems 			    codec->codec_info->buf);
3009cbe6566fSZhao Edgar Liu - Sun Microsystems 		}
3010cbe6566fSZhao Edgar Liu - Sun Microsystems 		audio_dev_add_info(statep->adev, buf);
3011cbe6566fSZhao Edgar Liu - Sun Microsystems 
30129ba19c87SYang-Rong Jerry Zhou 		/* work around for Sony VAIO laptop with specific codec */
3013cbe6566fSZhao Edgar Liu - Sun Microsystems 		if ((codec->codec_info->flags & NO_GPIO) == 0) {
30149ba19c87SYang-Rong Jerry Zhou 			/*
30159ba19c87SYang-Rong Jerry Zhou 			 * GPIO controls which are laptop specific workarounds
30169ba19c87SYang-Rong Jerry Zhou 			 * and might be changed. Some laptops use GPIO,
30179ba19c87SYang-Rong Jerry Zhou 			 * so we need to enable and set the GPIO correctly.
30189ba19c87SYang-Rong Jerry Zhou 			 */
30199ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
30209ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_MASK, AUDIOHDC_GPIO_ENABLE);
30219ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
3022989b958fSZhao Edgar Liu - Sun Microsystems 			    AUDIOHDC_VERB_SET_UNSOL_ENABLE_MASK,
3023989b958fSZhao Edgar Liu - Sun Microsystems 			    AUDIOHDC_GPIO_ENABLE);
3024989b958fSZhao Edgar Liu - Sun Microsystems 			(void) audioha_codec_verb_get(statep, i, wid,
30259ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_DIREC, AUDIOHDC_GPIO_DIRECT);
30269ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
30279ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_STCK,
30289ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_GPIO_DATA_CTRL);
30299ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
30309ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_DATA,
30319ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_GPIO_STCK_CTRL);
30329ba19c87SYang-Rong Jerry Zhou 		}
30339ba19c87SYang-Rong Jerry Zhou 
303488447a05SGarrett D'Amore 		/* power-up audio function group */
303588447a05SGarrett D'Amore 		(void) audioha_codec_verb_get(statep, i, wid,
30365ec2209cSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_VERB_SET_POWER_STATE, AUDIOHD_PW_D0);
303788447a05SGarrett D'Amore 
303888447a05SGarrett D'Amore 		/* subsystem id is attached to funtion group */
303988447a05SGarrett D'Amore 		codec->outamp_cap = audioha_codec_verb_get(statep, i, wid,
304088447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_OUTAMP_CAP);
304188447a05SGarrett D'Amore 		codec->inamp_cap = audioha_codec_verb_get(statep, i, wid,
304288447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_INAMP_CAP);
304388447a05SGarrett D'Amore 		codec->stream_format = audioha_codec_verb_get(statep, i, wid,
304488447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_STREAM);
304588447a05SGarrett D'Amore 		codec->pcm_format = audioha_codec_verb_get(statep, i, wid,
304688447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PCM);
304788447a05SGarrett D'Amore 
3048a33ad26eSZhao Edgar Liu - Sun Microsystems 		statep->sample_rate = 48000;
3049a33ad26eSZhao Edgar Liu - Sun Microsystems 		rate = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
3050a33ad26eSZhao Edgar Liu - Sun Microsystems 		    DDI_PROP_DONTPASS, "sample-rate", 48000);
3051a33ad26eSZhao Edgar Liu - Sun Microsystems 		if (rate == 192000 &&
3052a33ad26eSZhao Edgar Liu - Sun Microsystems 		    (codec->pcm_format & AUDIOHD_SAMP_RATE192)) {
3053a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_rate = 192000;
3054a33ad26eSZhao Edgar Liu - Sun Microsystems 		} else if (rate == 96000 &&
3055a33ad26eSZhao Edgar Liu - Sun Microsystems 		    (codec->pcm_format & AUDIOHD_SAMP_RATE96)) {
3056a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_rate = 96000;
3057a33ad26eSZhao Edgar Liu - Sun Microsystems 		} else {
3058a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_rate = 48000;
3059a33ad26eSZhao Edgar Liu - Sun Microsystems 		}
3060a33ad26eSZhao Edgar Liu - Sun Microsystems 
3061a33ad26eSZhao Edgar Liu - Sun Microsystems 		statep->sample_bit_depth = AUDIOHD_BIT_DEPTH16;
3062a33ad26eSZhao Edgar Liu - Sun Microsystems 		bits = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
3063a33ad26eSZhao Edgar Liu - Sun Microsystems 		    DDI_PROP_DONTPASS, "sample-bits", 16);
3064a33ad26eSZhao Edgar Liu - Sun Microsystems 		if (bits == 24 &&
3065a33ad26eSZhao Edgar Liu - Sun Microsystems 		    (codec->pcm_format & AUDIOHD_BIT_DEPTH24)) {
3066a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_bit_depth = AUDIOHD_BIT_DEPTH24;
3067a33ad26eSZhao Edgar Liu - Sun Microsystems 		} else {
3068a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_bit_depth = AUDIOHD_BIT_DEPTH16;
3069a33ad26eSZhao Edgar Liu - Sun Microsystems 		}
3070a33ad26eSZhao Edgar Liu - Sun Microsystems 
307188447a05SGarrett D'Amore 		nums = audioha_codec_verb_get(statep, i, wid,
307288447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PARAM,
307388447a05SGarrett D'Amore 		    AUDIOHDC_PAR_NODE_COUNT);
307488447a05SGarrett D'Amore 		wid = (nums >> AUDIOHD_CODEC_STR_OFF) & AUDIOHD_CODEC_STR_MASK;
307588447a05SGarrett D'Amore 		nums = nums & AUDIOHD_CODEC_NUM_MASK;
307688447a05SGarrett D'Amore 		codec->first_wid = wid;
307788447a05SGarrett D'Amore 		codec->last_wid = wid + nums;
307888447a05SGarrett D'Amore 		codec->nnodes = nums;
307988447a05SGarrett D'Amore 
308088447a05SGarrett D'Amore 		/*
308188447a05SGarrett D'Amore 		 * We output the codec information to syslog
308288447a05SGarrett D'Amore 		 */
308388447a05SGarrett D'Amore 		statep->codec[i] = codec;
3084ea463888SZhao Edgar Liu - Sun Microsystems 		codec->statep = statep;
308588447a05SGarrett D'Amore 		(void) audiohd_create_widgets(codec);
308688447a05SGarrett D'Amore 	}
308788447a05SGarrett D'Amore 
3088c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
308988447a05SGarrett D'Amore 
309088447a05SGarrett D'Amore }	/* audiohd_create_codec() */
309188447a05SGarrett D'Amore 
309288447a05SGarrett D'Amore /*
309388447a05SGarrett D'Amore  * audiohd_destroy_codec()
309488447a05SGarrett D'Amore  *
309588447a05SGarrett D'Amore  * Description:
309688447a05SGarrett D'Amore  *	destroy codec structure, and release its memory
309788447a05SGarrett D'Amore  */
309888447a05SGarrett D'Amore static void
audiohd_destroy_codec(audiohd_state_t * statep)309988447a05SGarrett D'Amore audiohd_destroy_codec(audiohd_state_t *statep)
310088447a05SGarrett D'Amore {
310188447a05SGarrett D'Amore 	int			i;
310288447a05SGarrett D'Amore 	audiohd_pin_t		*pin, *npin;
310388447a05SGarrett D'Amore 
310488447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
310588447a05SGarrett D'Amore 		if (statep->codec[i]) {
310688447a05SGarrett D'Amore 			audiohd_destroy_widgets(statep->codec[i]);
310788447a05SGarrett D'Amore 			/*
310888447a05SGarrett D'Amore 			 * free pins
310988447a05SGarrett D'Amore 			 */
311088447a05SGarrett D'Amore 			pin = statep->codec[i]->first_pin;
311188447a05SGarrett D'Amore 			while (pin) {
311288447a05SGarrett D'Amore 				npin = pin;
311388447a05SGarrett D'Amore 				pin = pin->next;
311488447a05SGarrett D'Amore 				kmem_free(npin, sizeof (audiohd_pin_t));
311588447a05SGarrett D'Amore 			}
311688447a05SGarrett D'Amore 
311788447a05SGarrett D'Amore 			kmem_free(statep->codec[i], sizeof (hda_codec_t));
311888447a05SGarrett D'Amore 			statep->codec[i] = NULL;
311988447a05SGarrett D'Amore 		}
312088447a05SGarrett D'Amore 	}
312188447a05SGarrett D'Amore }	/* audiohd_destroy_codec() */
312288447a05SGarrett D'Amore 
312388447a05SGarrett D'Amore /*
312488447a05SGarrett D'Amore  * audiohd_find_dac()
312588447a05SGarrett D'Amore  * Description:
312688447a05SGarrett D'Amore  *	Find a dac for a output path. Then the play data can be sent to the out
312788447a05SGarrett D'Amore  *	put pin through the output path.
312888447a05SGarrett D'Amore  *
312988447a05SGarrett D'Amore  * Arguments:
313088447a05SGarrett D'Amore  *	hda_codec_t	*codec		where the dac widget exists
313188447a05SGarrett D'Amore  *	wid_t		wid		the no. of a widget
313288447a05SGarrett D'Amore  *	int		mixer		whether the path need mixer or not
313388447a05SGarrett D'Amore  *	int		*mixernum	the total of mixer in the output path
313488447a05SGarrett D'Amore  *	int		exclusive	an exclusive path or share path
313588447a05SGarrett D'Amore  *	int		depth		the depth of search
313688447a05SGarrett D'Amore  *
313788447a05SGarrett D'Amore  * Return:
313888447a05SGarrett D'Amore  *	1) wid of the first shared widget in the path from
313988447a05SGarrett D'Amore  *	   pin to DAC if exclusive is 0;
314088447a05SGarrett D'Amore  *	2) wid of DAC widget;
314188447a05SGarrett D'Amore  *	3) 0 if no path
314288447a05SGarrett D'Amore  */
314388447a05SGarrett D'Amore static wid_t
audiohd_find_dac(hda_codec_t * codec,wid_t wid,int mixer,int * mixernum,int exclusive,int depth)314488447a05SGarrett D'Amore audiohd_find_dac(hda_codec_t *codec, wid_t wid,
314588447a05SGarrett D'Amore     int mixer, int *mixernum,
314688447a05SGarrett D'Amore     int exclusive, int depth)
314788447a05SGarrett D'Amore {
314888447a05SGarrett D'Amore 	audiohd_widget_t	*widget = codec->widget[wid];
3149c6e681c0SYang-Rong Jerry Zhou 	wid_t	wdac = (uint32_t)(DDI_FAILURE);
315088447a05SGarrett D'Amore 	wid_t	retval;
315188447a05SGarrett D'Amore 
315288447a05SGarrett D'Amore 	if (depth > AUDIOHD_MAX_DEPTH)
3153c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
315488447a05SGarrett D'Amore 
315588447a05SGarrett D'Amore 	if (widget == NULL)
3156c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
315788447a05SGarrett D'Amore 
315888447a05SGarrett D'Amore 	/*
315988447a05SGarrett D'Amore 	 * If exclusive is true, we try to find a path which doesn't
316088447a05SGarrett D'Amore 	 * share any widget with other paths.
316188447a05SGarrett D'Amore 	 */
316288447a05SGarrett D'Amore 	if (exclusive) {
316388447a05SGarrett D'Amore 		if (widget->path_flags & AUDIOHD_PATH_DAC)
3164c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
316588447a05SGarrett D'Amore 	} else {
316688447a05SGarrett D'Amore 		if (widget->path_flags & AUDIOHD_PATH_DAC)
316788447a05SGarrett D'Amore 			return (wid);
316888447a05SGarrett D'Amore 	}
316988447a05SGarrett D'Amore 
317088447a05SGarrett D'Amore 	switch (widget->type) {
317188447a05SGarrett D'Amore 	case WTYPE_AUDIO_OUT:
317288447a05SGarrett D'Amore 		/* We need mixer widget, but the the mixer num is 0, failed  */
317388447a05SGarrett D'Amore 		if (mixer && !*mixernum)
3174c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
317588447a05SGarrett D'Amore 		widget->path_flags |= AUDIOHD_PATH_DAC;
317688447a05SGarrett D'Amore 		widget->out_weight++;
317788447a05SGarrett D'Amore 		wdac = widget->wid_wid;
317888447a05SGarrett D'Amore 		break;
317988447a05SGarrett D'Amore 
318088447a05SGarrett D'Amore 	case WTYPE_AUDIO_MIX:
318188447a05SGarrett D'Amore 		(*mixernum)++;
3182b96a6eceSZhao Edgar Liu - Sun Microsystems 		/* FALLTHRU */
3183b96a6eceSZhao Edgar Liu - Sun Microsystems 	case WTYPE_AUDIO_SEL:
318488447a05SGarrett D'Amore 		for (int i = 0; i < widget->nconns; i++) {
318588447a05SGarrett D'Amore 			retval = audiohd_find_dac(codec,
318688447a05SGarrett D'Amore 			    widget->avail_conn[i],
318788447a05SGarrett D'Amore 			    mixer, mixernum,
318888447a05SGarrett D'Amore 			    exclusive, depth + 1);
3189c6e681c0SYang-Rong Jerry Zhou 			if (retval != (uint32_t)DDI_FAILURE) {
3190b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next ==
3191b96a6eceSZhao Edgar Liu - Sun Microsystems 				    AUDIOHD_NULL_CONN) {
3192b96a6eceSZhao Edgar Liu - Sun Microsystems 					widget->output_path_next = i;
319388447a05SGarrett D'Amore 					wdac = retval;
319488447a05SGarrett D'Amore 				}
319588447a05SGarrett D'Amore 				widget->path_flags |= AUDIOHD_PATH_DAC;
319688447a05SGarrett D'Amore 				widget->out_weight++;
319788447a05SGarrett D'Amore 
319888447a05SGarrett D'Amore 				/* return when found a path */
319988447a05SGarrett D'Amore 				return (wdac);
320088447a05SGarrett D'Amore 			}
320188447a05SGarrett D'Amore 		}
320288447a05SGarrett D'Amore 	default:
320388447a05SGarrett D'Amore 		break;
320488447a05SGarrett D'Amore 	}
320588447a05SGarrett D'Amore 
320688447a05SGarrett D'Amore 	return (wdac);
320788447a05SGarrett D'Amore }	/* audiohd_find_dac() */
320888447a05SGarrett D'Amore 
320988447a05SGarrett D'Amore /*
321088447a05SGarrett D'Amore  * audiohd_do_build_output_path()
321188447a05SGarrett D'Amore  *
321288447a05SGarrett D'Amore  * Description:
321388447a05SGarrett D'Amore  *	Search an output path for each pin in the codec.
321488447a05SGarrett D'Amore  * Arguments:
321588447a05SGarrett D'Amore  *	hda_codec_t	*codec		where the output path exists
3216b96a6eceSZhao Edgar Liu - Sun Microsystems  *	int		mixer		whether the path needs mixer widget
321788447a05SGarrett D'Amore  *	int		*mnum		total of mixer widget in the path
321888447a05SGarrett D'Amore  *	int		exclusive	an exclusive path or shared path
321988447a05SGarrett D'Amore  *	int		depth		search depth
322088447a05SGarrett D'Amore  */
322188447a05SGarrett D'Amore static void
audiohd_do_build_output_path(hda_codec_t * codec,int mixer,int * mnum,int exclusive,int depth)322288447a05SGarrett D'Amore audiohd_do_build_output_path(hda_codec_t *codec, int mixer, int *mnum,
322388447a05SGarrett D'Amore     int exclusive, int depth)
322488447a05SGarrett D'Amore {
322588447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
322688447a05SGarrett D'Amore 	audiohd_widget_t	*widget, *wdac;
322788447a05SGarrett D'Amore 	audiohd_path_t		*path;
322888447a05SGarrett D'Amore 	wid_t			wid;
322988447a05SGarrett D'Amore 	audiohd_state_t		*statep;
323088447a05SGarrett D'Amore 	int			i;
323188447a05SGarrett D'Amore 
3232ea463888SZhao Edgar Liu - Sun Microsystems 	statep = codec->statep;
323388447a05SGarrett D'Amore 
323488447a05SGarrett D'Amore 	for (pin = codec->first_pin; pin; pin = pin->next) {
323588447a05SGarrett D'Amore 		if ((pin->cap & AUDIOHD_PIN_CAP_MASK) == 0)
323688447a05SGarrett D'Amore 			continue;
323788447a05SGarrett D'Amore 		if ((pin->config & AUDIOHD_PIN_CONF_MASK) ==
323888447a05SGarrett D'Amore 		    AUDIOHD_PIN_NO_CONN)
323988447a05SGarrett D'Amore 			continue;
324088447a05SGarrett D'Amore 		if ((pin->device != DTYPE_LINEOUT) &&
324188447a05SGarrett D'Amore 		    (pin->device != DTYPE_SPEAKER) &&
324288447a05SGarrett D'Amore 		    (pin->device != DTYPE_SPDIF_OUT) &&
324388447a05SGarrett D'Amore 		    (pin->device != DTYPE_HP_OUT))
324488447a05SGarrett D'Amore 			continue;
324588447a05SGarrett D'Amore 		if (pin->finish)
324688447a05SGarrett D'Amore 			continue;
324788447a05SGarrett D'Amore 		widget = codec->widget[pin->wid];
324888447a05SGarrett D'Amore 
324988447a05SGarrett D'Amore 		widget->inamp_cap = 0;
325088447a05SGarrett D'Amore 		for (i = 0; i < widget->nconns; i++) {
325188447a05SGarrett D'Amore 			/*
325288447a05SGarrett D'Amore 			 * If a dac found, the return value is the wid of the
325388447a05SGarrett D'Amore 			 * widget on the path, or the return value is
3254c6e681c0SYang-Rong Jerry Zhou 			 * DDI_FAILURE
325588447a05SGarrett D'Amore 			 */
325688447a05SGarrett D'Amore 			wid = audiohd_find_dac(codec,
325788447a05SGarrett D'Amore 			    widget->avail_conn[i], mixer, mnum, exclusive,
325888447a05SGarrett D'Amore 			    depth);
325988447a05SGarrett D'Amore 			/*
326088447a05SGarrett D'Amore 			 * A dac was not found
326188447a05SGarrett D'Amore 			 */
3262c6e681c0SYang-Rong Jerry Zhou 			if (wid == (wid_t)DDI_FAILURE)
326388447a05SGarrett D'Amore 				continue;
3264c1aa074aSYang-Rong Jerry Zhou 			if (pin->device != DTYPE_SPEAKER &&
3265c1aa074aSYang-Rong Jerry Zhou 			    pin->device != DTYPE_HP_OUT)
326688447a05SGarrett D'Amore 				statep->chann[pin->assoc] += 2;
326788447a05SGarrett D'Amore 			path = (audiohd_path_t *)
326888447a05SGarrett D'Amore 			    kmem_zalloc(sizeof (audiohd_path_t),
326988447a05SGarrett D'Amore 			    KM_SLEEP);
327088447a05SGarrett D'Amore 			path->adda_wid = wid;
327188447a05SGarrett D'Amore 			path->pin_wid[0] = widget->wid_wid;
327288447a05SGarrett D'Amore 			path->pin_nums = 1;
327388447a05SGarrett D'Amore 			path->path_type = PLAY;
327488447a05SGarrett D'Amore 			path->codec = codec;
327588447a05SGarrett D'Amore 			path->statep = statep;
327688447a05SGarrett D'Amore 			wdac = codec->widget[wid];
327788447a05SGarrett D'Amore 			wdac->priv = path;
3278e7236f70SZhao Edgar Liu - Sun Microsystems 			pin->dac_wid = wid;
327988447a05SGarrett D'Amore 			pin->finish = 1;
328088447a05SGarrett D'Amore 			widget->path_flags |= AUDIOHD_PATH_DAC;
328188447a05SGarrett D'Amore 			widget->out_weight++;
3282b96a6eceSZhao Edgar Liu - Sun Microsystems 			widget->output_path_next = i;
328388447a05SGarrett D'Amore 			statep->path[statep->pathnum++] = path;
328488447a05SGarrett D'Amore 			break;
328588447a05SGarrett D'Amore 		}
328688447a05SGarrett D'Amore 	}
328788447a05SGarrett D'Amore 
328888447a05SGarrett D'Amore }	/* audiohd_do_build_output_path() */
328988447a05SGarrett D'Amore 
329088447a05SGarrett D'Amore /*
329188447a05SGarrett D'Amore  * audiohd_build_output_path()
329288447a05SGarrett D'Amore  *
329388447a05SGarrett D'Amore  * Description:
329488447a05SGarrett D'Amore  *	Build the output path in the codec for every pin.
329588447a05SGarrett D'Amore  *	First we try to search output path with mixer widget exclusively
329688447a05SGarrett D'Amore  *	Then we try to search shared output path with mixer widget.
329788447a05SGarrett D'Amore  *	Then we try to search output path without mixer widget exclusively.
329888447a05SGarrett D'Amore  *	At last we try to search shared ouput path for the remained pins
329988447a05SGarrett D'Amore  */
330088447a05SGarrett D'Amore static void
audiohd_build_output_path(hda_codec_t * codec)330188447a05SGarrett D'Amore audiohd_build_output_path(hda_codec_t *codec)
330288447a05SGarrett D'Amore {
330388447a05SGarrett D'Amore 	int 			mnum = 0;
330488447a05SGarrett D'Amore 	uint8_t			mixer_allow = 1;
330588447a05SGarrett D'Amore 
3306f0109389SYang-Rong Jerry Zhou 	/*
3307368517c9SYang-Rong Jerry Zhou 	 * Work around for laptops which have IDT or AD audio chipset, such as
330807bec7ccSZhao Edgar Liu - Sun Microsystems 	 * HP mini 1000 laptop, Dell Lattitude 6400, Lenovo T60, Lenove R61e.
330907bec7ccSZhao Edgar Liu - Sun Microsystems 	 * We don't allow mixer widget on such path, which leads to speaker
3310368517c9SYang-Rong Jerry Zhou 	 * loud hiss noise.
3311f0109389SYang-Rong Jerry Zhou 	 */
3312cbe6566fSZhao Edgar Liu - Sun Microsystems 	if (codec->codec_info->flags & NO_MIXER)
331388447a05SGarrett D'Amore 		mixer_allow = 0;
3314cbe6566fSZhao Edgar Liu - Sun Microsystems 
331588447a05SGarrett D'Amore 	/* search an exclusive mixer widget path. This is preferred */
331688447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, mixer_allow, &mnum, 1, 0);
331788447a05SGarrett D'Amore 
331888447a05SGarrett D'Amore 	/* search a shared mixer widget path for the remained pins */
331988447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, mixer_allow, &mnum, 0, 0);
332088447a05SGarrett D'Amore 
332188447a05SGarrett D'Amore 	/* search an exclusive widget path without mixer for the remained pin */
332288447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, 0, &mnum, 1, 0);
332388447a05SGarrett D'Amore 
332488447a05SGarrett D'Amore 	/* search a shared widget path without mixer for the remained pin */
332588447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, 0, &mnum, 0, 0);
332688447a05SGarrett D'Amore 
332788447a05SGarrett D'Amore }	/* audiohd_build_output_path */
332888447a05SGarrett D'Amore 
332988447a05SGarrett D'Amore /*
333088447a05SGarrett D'Amore  * audiohd_build_output_amp
333188447a05SGarrett D'Amore  *
333288447a05SGarrett D'Amore  * Description:
333388447a05SGarrett D'Amore  *	Find the gain control and mute control widget
333488447a05SGarrett D'Amore  */
333588447a05SGarrett D'Amore static void
audiohd_build_output_amp(hda_codec_t * codec)333688447a05SGarrett D'Amore audiohd_build_output_amp(hda_codec_t *codec)
333788447a05SGarrett D'Amore {
333888447a05SGarrett D'Amore 	audiohd_path_t		*path;
333988447a05SGarrett D'Amore 	audiohd_widget_t	*w, *widget, *wpin, *wdac;
334088447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
3341b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t		wid, next;
334288447a05SGarrett D'Amore 	int		weight;
334388447a05SGarrett D'Amore 	int		i, j;
334488447a05SGarrett D'Amore 	uint32_t	gain;
334588447a05SGarrett D'Amore 
3346ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
3347ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
3348b96a6eceSZhao Edgar Liu - Sun Microsystems 		if (path == NULL || path->path_type != PLAY ||
334988447a05SGarrett D'Amore 		    path->codec != codec)
335088447a05SGarrett D'Amore 			continue;
335188447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
335288447a05SGarrett D'Amore 			wid = path->pin_wid[j];
335388447a05SGarrett D'Amore 			wpin = codec->widget[wid];
335488447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)wpin->priv;
335588447a05SGarrett D'Amore 			weight = wpin->out_weight;
335688447a05SGarrett D'Amore 
335788447a05SGarrett D'Amore 			/*
335888447a05SGarrett D'Amore 			 * search a node which can mute this pin while
335988447a05SGarrett D'Amore 			 * the mute functionality doesn't effect other
336088447a05SGarrett D'Amore 			 * pins.
336188447a05SGarrett D'Amore 			 */
336288447a05SGarrett D'Amore 			widget = wpin;
336388447a05SGarrett D'Amore 			while (widget) {
336488447a05SGarrett D'Amore 				if (widget->outamp_cap &
336588447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
336688447a05SGarrett D'Amore 					pin->mute_wid = widget->wid_wid;
336788447a05SGarrett D'Amore 					pin->mute_dir = AUDIOHDC_AMP_SET_OUTPUT;
336888447a05SGarrett D'Amore 					break;
336988447a05SGarrett D'Amore 				}
337088447a05SGarrett D'Amore 				if (widget->inamp_cap &
337188447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
337288447a05SGarrett D'Amore 					pin->mute_wid = widget->wid_wid;
337388447a05SGarrett D'Amore 					pin->mute_dir = AUDIOHDC_AMP_SET_INPUT;
337488447a05SGarrett D'Amore 					break;
337588447a05SGarrett D'Amore 				}
3376b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3377b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
337888447a05SGarrett D'Amore 					break;
3379b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
338088447a05SGarrett D'Amore 				widget = codec->widget[wid];
338188447a05SGarrett D'Amore 				if (widget && widget->out_weight != weight)
338288447a05SGarrett D'Amore 					break;
338388447a05SGarrett D'Amore 			}
338488447a05SGarrett D'Amore 
338588447a05SGarrett D'Amore 			/*
338688447a05SGarrett D'Amore 			 * We select the wid which has maxium gain range in
338788447a05SGarrett D'Amore 			 * the output path. Meanwhile, the gain controlling
338888447a05SGarrett D'Amore 			 * of this node doesn't effect other pins if this
338988447a05SGarrett D'Amore 			 * output stream has multiple pins.
339088447a05SGarrett D'Amore 			 */
339188447a05SGarrett D'Amore 			gain = 0;
339288447a05SGarrett D'Amore 			widget = wpin;
339388447a05SGarrett D'Amore 			while (widget) {
339488447a05SGarrett D'Amore 				gain = (widget->outamp_cap &
339588447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
339688447a05SGarrett D'Amore 				if (gain && gain > pin->gain_bits) {
339788447a05SGarrett D'Amore 					pin->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
339888447a05SGarrett D'Amore 					pin->gain_bits = gain;
339988447a05SGarrett D'Amore 					pin->gain_wid = widget->wid_wid;
340088447a05SGarrett D'Amore 				}
340188447a05SGarrett D'Amore 				gain = widget->inamp_cap &
340288447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
340388447a05SGarrett D'Amore 				if (gain && gain > pin->gain_bits) {
340488447a05SGarrett D'Amore 					pin->gain_dir = AUDIOHDC_AMP_SET_INPUT;
340588447a05SGarrett D'Amore 					pin->gain_bits = gain;
340688447a05SGarrett D'Amore 					pin->gain_wid = widget->wid_wid;
340788447a05SGarrett D'Amore 				}
3408b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3409b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
341088447a05SGarrett D'Amore 					break;
3411b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
341288447a05SGarrett D'Amore 				widget = codec->widget[wid];
341388447a05SGarrett D'Amore 				if (widget && widget->out_weight != weight)
341488447a05SGarrett D'Amore 					break;
341588447a05SGarrett D'Amore 			}
341688447a05SGarrett D'Amore 			pin->gain_bits >>= AUDIOHD_GAIN_OFF;
341788447a05SGarrett D'Amore 		}
341888447a05SGarrett D'Amore 
341988447a05SGarrett D'Amore 		/*
342088447a05SGarrett D'Amore 		 * if this stream has multiple pins, we try to find
342188447a05SGarrett D'Amore 		 * a mute & gain-controlling nodes which can effect
342288447a05SGarrett D'Amore 		 * all output pins of this stream to be used for the
342388447a05SGarrett D'Amore 		 * whole stream
342488447a05SGarrett D'Amore 		 */
342588447a05SGarrett D'Amore 		if (path->pin_nums == 1) {
342688447a05SGarrett D'Amore 			path->mute_wid = pin->mute_wid;
342788447a05SGarrett D'Amore 			path->mute_dir = pin->mute_dir;
342888447a05SGarrett D'Amore 			path->gain_wid = pin->gain_wid;
342988447a05SGarrett D'Amore 			path->gain_dir = pin->gain_dir;
343088447a05SGarrett D'Amore 			path->gain_bits = pin->gain_bits;
343188447a05SGarrett D'Amore 		} else {
343288447a05SGarrett D'Amore 			wdac = codec->widget[path->adda_wid];
343388447a05SGarrett D'Amore 			weight = wdac->out_weight;
343488447a05SGarrett D'Amore 			wid = path->pin_wid[0];
343588447a05SGarrett D'Amore 			w = codec->widget[wid];
343688447a05SGarrett D'Amore 			while (w && w->out_weight != weight) {
3437b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[w->output_path_next];
343888447a05SGarrett D'Amore 				w = codec->widget[wid];
343988447a05SGarrett D'Amore 			}
344088447a05SGarrett D'Amore 
344188447a05SGarrett D'Amore 			/* find mute controlling node for this stream */
344288447a05SGarrett D'Amore 			widget = w;
344388447a05SGarrett D'Amore 			while (widget) {
344488447a05SGarrett D'Amore 				if (widget->outamp_cap &
344588447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
344688447a05SGarrett D'Amore 					path->mute_wid = widget->wid_wid;
344788447a05SGarrett D'Amore 					path->mute_dir =
344888447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_OUTPUT;
344988447a05SGarrett D'Amore 					break;
345088447a05SGarrett D'Amore 				}
345188447a05SGarrett D'Amore 				if (widget->inamp_cap &
345288447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
345388447a05SGarrett D'Amore 					path->mute_wid = widget->wid_wid;
345488447a05SGarrett D'Amore 					path->mute_dir =
345588447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_INPUT;
345688447a05SGarrett D'Amore 					break;
345788447a05SGarrett D'Amore 				}
3458b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3459b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
346088447a05SGarrett D'Amore 					break;
3461b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
346288447a05SGarrett D'Amore 				widget = codec->widget[wid];
346388447a05SGarrett D'Amore 			}
346488447a05SGarrett D'Amore 
346588447a05SGarrett D'Amore 			/* find volume controlling node for this stream */
346688447a05SGarrett D'Amore 			gain = 0;
346788447a05SGarrett D'Amore 			widget = w;
346888447a05SGarrett D'Amore 			while (widget) {
346988447a05SGarrett D'Amore 				gain = (widget->outamp_cap &
347088447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
347188447a05SGarrett D'Amore 				if (gain && gain > pin->gain_bits) {
347288447a05SGarrett D'Amore 					path->gain_dir =
347388447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_OUTPUT;
347488447a05SGarrett D'Amore 					path->gain_bits = gain;
347588447a05SGarrett D'Amore 					path->gain_wid = widget->wid_wid;
347688447a05SGarrett D'Amore 				}
347788447a05SGarrett D'Amore 				gain = widget->inamp_cap &
347888447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
347988447a05SGarrett D'Amore 				if (gain && (gain > pin->gain_bits) &&
348088447a05SGarrett D'Amore 				    (widget->type != WTYPE_AUDIO_MIX)) {
348188447a05SGarrett D'Amore 					path->gain_dir =
348288447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_INPUT;
348388447a05SGarrett D'Amore 					path->gain_bits = gain;
348488447a05SGarrett D'Amore 					path->gain_wid = widget->wid_wid;
348588447a05SGarrett D'Amore 				}
3486b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3487b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
348888447a05SGarrett D'Amore 					break;
3489b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
349088447a05SGarrett D'Amore 				widget = codec->widget[wid];
349188447a05SGarrett D'Amore 			}
349288447a05SGarrett D'Amore 			path->gain_bits >>= AUDIOHD_GAIN_OFF;
349388447a05SGarrett D'Amore 		}
349488447a05SGarrett D'Amore 
349588447a05SGarrett D'Amore 	}
349688447a05SGarrett D'Amore 
349788447a05SGarrett D'Amore }	/* audiohd_build_output_amp */
349888447a05SGarrett D'Amore 
349988447a05SGarrett D'Amore /*
350088447a05SGarrett D'Amore  * audiohd_finish_output_path()
350188447a05SGarrett D'Amore  *
350288447a05SGarrett D'Amore  * Description:
350388447a05SGarrett D'Amore  *	Enable the widgets on the output path
350488447a05SGarrett D'Amore  */
350588447a05SGarrett D'Amore static void
audiohd_finish_output_path(hda_codec_t * codec)350688447a05SGarrett D'Amore audiohd_finish_output_path(hda_codec_t *codec)
350788447a05SGarrett D'Amore {
3508ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
350988447a05SGarrett D'Amore 	audiohd_path_t		*path;
351088447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
351188447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
351288447a05SGarrett D'Amore 	uint_t			caddr = codec->index;
3513b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
351488447a05SGarrett D'Amore 	int			i, j;
351588447a05SGarrett D'Amore 
3516ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
3517ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
351888447a05SGarrett D'Amore 		if (!path || path->path_type != PLAY || path->codec != codec)
351988447a05SGarrett D'Amore 			continue;
352088447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
352188447a05SGarrett D'Amore 			wid = path->pin_wid[j];
352288447a05SGarrett D'Amore 			widget = codec->widget[wid];
352388447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)widget->priv;
352488447a05SGarrett D'Amore 			{
352588447a05SGarrett D'Amore 			uint32_t    lTmp;
352688447a05SGarrett D'Amore 
352788447a05SGarrett D'Amore 			lTmp = audioha_codec_verb_get(statep, caddr, wid,
352888447a05SGarrett D'Amore 			    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
352988447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, caddr, wid,
353088447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_PIN_CTRL, (lTmp |
353188447a05SGarrett D'Amore 			    pin->vrefvalue |
353288447a05SGarrett D'Amore 			    AUDIOHDC_PIN_CONTROL_OUT_ENABLE |
353388447a05SGarrett D'Amore 			    AUDIOHDC_PIN_CONTROL_HP_ENABLE) &
353488447a05SGarrett D'Amore 			    ~ AUDIOHDC_PIN_CONTROL_IN_ENABLE);
353588447a05SGarrett D'Amore 			}
353688447a05SGarrett D'Amore 			/* If this pin has external amplifier, enable it */
353788447a05SGarrett D'Amore 			if (pin->cap & AUDIOHD_EXT_AMP_MASK)
353888447a05SGarrett D'Amore 				(void) audioha_codec_verb_get(statep, caddr,
353988447a05SGarrett D'Amore 				    wid, AUDIOHDC_VERB_SET_EAPD,
354088447a05SGarrett D'Amore 				    AUDIOHD_EXT_AMP_ENABLE);
354188447a05SGarrett D'Amore 
354288447a05SGarrett D'Amore 			if (widget->outamp_cap) {
354388447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(statep,
354488447a05SGarrett D'Amore 				    caddr, wid, AUDIOHDC_VERB_SET_AMP_MUTE,
354588447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
354688447a05SGarrett D'Amore 				    AUDIOHDC_GAIN_MAX);
354788447a05SGarrett D'Amore 			}
354888447a05SGarrett D'Amore 
354988447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, caddr, wid,
3550b96a6eceSZhao Edgar Liu - Sun Microsystems 			    AUDIOHDC_VERB_SET_CONN_SEL,
3551b96a6eceSZhao Edgar Liu - Sun Microsystems 			    widget->output_path_next);
355288447a05SGarrett D'Amore 
3553b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = widget->avail_conn[widget->output_path_next];
355488447a05SGarrett D'Amore 			widget = codec->widget[wid];
355588447a05SGarrett D'Amore 
355688447a05SGarrett D'Amore 			while (widget) {
355788447a05SGarrett D'Amore 				/*
355888447a05SGarrett D'Amore 				 * Set all amplifiers in this path to
3559b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * the maximum volume and unmute them.
356088447a05SGarrett D'Amore 				 */
356188447a05SGarrett D'Amore 				if (widget->outamp_cap) {
356288447a05SGarrett D'Amore 					(void) audioha_codec_4bit_verb_get(
3563b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
356488447a05SGarrett D'Amore 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
356588447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_LR_OUTPUT |
356688447a05SGarrett D'Amore 					    AUDIOHDC_GAIN_MAX);
356788447a05SGarrett D'Amore 				}
356888447a05SGarrett D'Amore 				if (widget->inamp_cap) {
356988447a05SGarrett D'Amore 					(void) audioha_codec_4bit_verb_get(
3570b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
357188447a05SGarrett D'Amore 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
357288447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_LR_INPUT |
357388447a05SGarrett D'Amore 					    AUDIOHDC_GAIN_MAX |
3574b96a6eceSZhao Edgar Liu - Sun Microsystems 					    (widget->output_path_next <<
357588447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_INDEX_OFFSET));
357688447a05SGarrett D'Amore 				}
357788447a05SGarrett D'Amore 
3578b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3579b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
358088447a05SGarrett D'Amore 					break;
358188447a05SGarrett D'Amore 				/*
358288447a05SGarrett D'Amore 				 * Accoding to HD spec, mixer doesn't support
358388447a05SGarrett D'Amore 				 * "select connection"
358488447a05SGarrett D'Amore 				 */
3585b96a6eceSZhao Edgar Liu - Sun Microsystems 				if ((widget->type == WTYPE_AUDIO_SEL) &&
358688447a05SGarrett D'Amore 				    (widget->nconns > 1))
358788447a05SGarrett D'Amore 					(void) audioha_codec_verb_get(statep,
3588b96a6eceSZhao Edgar Liu - Sun Microsystems 					    caddr, wid,
358988447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_CONN_SEL,
3590b96a6eceSZhao Edgar Liu - Sun Microsystems 					    widget->output_path_next);
359188447a05SGarrett D'Amore 
3592b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
359388447a05SGarrett D'Amore 				widget = codec->widget[wid];
359488447a05SGarrett D'Amore 			}
359588447a05SGarrett D'Amore 		}
359688447a05SGarrett D'Amore 	}
359788447a05SGarrett D'Amore }	/* audiohd_finish_output_path() */
359888447a05SGarrett D'Amore 
359988447a05SGarrett D'Amore /*
360088447a05SGarrett D'Amore  * audiohd_find_input_pins()
360188447a05SGarrett D'Amore  *
360288447a05SGarrett D'Amore  * Description:
360388447a05SGarrett D'Amore  * 	Here we consider a mixer/selector with multi-input as a real sum
360488447a05SGarrett D'Amore  * 	widget. Only the first real mixer/selector widget is permitted in
360588447a05SGarrett D'Amore  * 	an input path(recording path). If there are more mixers/selectors
360688447a05SGarrett D'Amore  * 	execept the first one, only the first input/connection of those
360788447a05SGarrett D'Amore  * 	widgets will be used by our driver, that means, we ignore other
360888447a05SGarrett D'Amore  * 	inputs of those mixers/selectors.
360988447a05SGarrett D'Amore  */
361088447a05SGarrett D'Amore static int
audiohd_find_input_pins(hda_codec_t * codec,wid_t wid,int allowmixer,int depth,audiohd_path_t * path)361188447a05SGarrett D'Amore audiohd_find_input_pins(hda_codec_t *codec, wid_t wid, int allowmixer,
361288447a05SGarrett D'Amore     int depth, audiohd_path_t *path)
361388447a05SGarrett D'Amore {
361488447a05SGarrett D'Amore 	audiohd_widget_t	*widget = codec->widget[wid];
361588447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
3616ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
361788447a05SGarrett D'Amore 	uint_t			caddr = codec->index;
361888447a05SGarrett D'Amore 	int			retval = -1;
361988447a05SGarrett D'Amore 	int			num, i;
362088447a05SGarrett D'Amore 	uint32_t		pinctrl;
362188447a05SGarrett D'Amore 
362288447a05SGarrett D'Amore 	if (depth > AUDIOHD_MAX_DEPTH)
3623c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
362488447a05SGarrett D'Amore 	if (widget == NULL)
3625c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
362688447a05SGarrett D'Amore 
362788447a05SGarrett D'Amore 	/* we don't share widgets */
362865a41de7SYang-Rong Jerry Zhou 	if (widget->path_flags & AUDIOHD_PATH_ADC ||
362965a41de7SYang-Rong Jerry Zhou 	    widget->path_flags & AUDIOHD_PATH_DAC)
3630c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
363188447a05SGarrett D'Amore 
363288447a05SGarrett D'Amore 	switch (widget->type) {
363388447a05SGarrett D'Amore 	case WTYPE_PIN:
363488447a05SGarrett D'Amore 		pin = (audiohd_pin_t *)widget->priv;
363588447a05SGarrett D'Amore 		if (pin->no_phys_conn)
3636c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
363788447a05SGarrett D'Amore 		/* enable the pins' input capability */
363888447a05SGarrett D'Amore 		pinctrl = audioha_codec_verb_get(statep, caddr, wid,
363988447a05SGarrett D'Amore 		    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
364088447a05SGarrett D'Amore 		(void) audioha_codec_verb_get(statep, caddr, wid,
364188447a05SGarrett D'Amore 		    AUDIOHDC_VERB_SET_PIN_CTRL,
364288447a05SGarrett D'Amore 		    pinctrl | AUDIOHD_PIN_IN_ENABLE);
364388447a05SGarrett D'Amore 		if (pin->cap & AUDIOHD_EXT_AMP_MASK) {
364488447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, caddr,
364588447a05SGarrett D'Amore 			    wid, AUDIOHDC_VERB_SET_EAPD,
364688447a05SGarrett D'Amore 			    AUDIOHD_EXT_AMP_ENABLE);
364788447a05SGarrett D'Amore 		}
364888447a05SGarrett D'Amore 		switch (pin->device) {
364988447a05SGarrett D'Amore 		case DTYPE_CD:
365088447a05SGarrett D'Amore 		case DTYPE_LINE_IN:
365188447a05SGarrett D'Amore 		case DTYPE_MIC_IN:
365288447a05SGarrett D'Amore 		case DTYPE_AUX:
365388447a05SGarrett D'Amore 			widget->path_flags |= AUDIOHD_PATH_ADC;
365488447a05SGarrett D'Amore 			widget->in_weight++;
365588447a05SGarrett D'Amore 			path->pin_wid[path->pin_nums++] = wid;
3656e7236f70SZhao Edgar Liu - Sun Microsystems 			pin->adc_wid = path->adda_wid;
3657c6e681c0SYang-Rong Jerry Zhou 			return (DDI_SUCCESS);
365888447a05SGarrett D'Amore 		}
365988447a05SGarrett D'Amore 		break;
366088447a05SGarrett D'Amore 	case WTYPE_AUDIO_MIX:
366188447a05SGarrett D'Amore 	case WTYPE_AUDIO_SEL:
366288447a05SGarrett D'Amore 		/*
366388447a05SGarrett D'Amore 		 * If the sum widget has only one input, we don't
366488447a05SGarrett D'Amore 		 * consider it as a real sum widget.
366588447a05SGarrett D'Amore 		 */
366688447a05SGarrett D'Amore 		if (widget->nconns == 1) {
3667b96a6eceSZhao Edgar Liu - Sun Microsystems 			widget->input_path_next = 0;
366888447a05SGarrett D'Amore 			retval = audiohd_find_input_pins(codec,
366988447a05SGarrett D'Amore 			    widget->avail_conn[0],
367088447a05SGarrett D'Amore 			    allowmixer, depth + 1, path);
3671b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (retval == DDI_SUCCESS) {
367288447a05SGarrett D'Amore 				widget->path_flags |= AUDIOHD_PATH_ADC;
367388447a05SGarrett D'Amore 				widget->in_weight++;
367488447a05SGarrett D'Amore 			}
367588447a05SGarrett D'Amore 			break;
367688447a05SGarrett D'Amore 		}
367788447a05SGarrett D'Amore 
367888447a05SGarrett D'Amore 		if (allowmixer) {
367988447a05SGarrett D'Amore 			/*
368088447a05SGarrett D'Amore 			 * This is a real sum widget, we will reject
368188447a05SGarrett D'Amore 			 * other real sum widget when we find more in
368288447a05SGarrett D'Amore 			 * the following path-searching.
368388447a05SGarrett D'Amore 			 */
368488447a05SGarrett D'Amore 			for (int i = 0; i < widget->nconns; i++) {
368588447a05SGarrett D'Amore 				retval = audiohd_find_input_pins(codec,
368688447a05SGarrett D'Amore 				    widget->avail_conn[i], 0, depth + 1,
368788447a05SGarrett D'Amore 				    path);
3688b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (retval == DDI_SUCCESS) {
3689b96a6eceSZhao Edgar Liu - Sun Microsystems 					widget->input_path_next = i;
369088447a05SGarrett D'Amore 					widget->in_weight++;
369188447a05SGarrett D'Amore 					num = path->pin_nums - 1;
369288447a05SGarrett D'Amore 					path->sum_selconn[num] = i;
369388447a05SGarrett D'Amore 					path->sum_wid = wid;
369488447a05SGarrett D'Amore 					widget->path_flags |=
369588447a05SGarrett D'Amore 					    AUDIOHD_PATH_ADC;
369688447a05SGarrett D'Amore 				}
369788447a05SGarrett D'Amore 			}
369888447a05SGarrett D'Amore 
369988447a05SGarrett D'Amore 			/* return SUCCESS if we found at least one input path */
370088447a05SGarrett D'Amore 			if (path->pin_nums > 0)
3701c6e681c0SYang-Rong Jerry Zhou 				retval = DDI_SUCCESS;
370288447a05SGarrett D'Amore 		} else {
370388447a05SGarrett D'Amore 			/*
370488447a05SGarrett D'Amore 			 * We had already found a real sum before this one since
370588447a05SGarrett D'Amore 			 * allowmixer is 0.
370688447a05SGarrett D'Amore 			 */
370788447a05SGarrett D'Amore 			for (i = 0; i < widget->nconns; i++) {
370888447a05SGarrett D'Amore 				retval = audiohd_find_input_pins(codec,
370988447a05SGarrett D'Amore 				    widget->avail_conn[i], 0, depth + 1,
371088447a05SGarrett D'Amore 				    path);
3711b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (retval == DDI_SUCCESS) {
3712b96a6eceSZhao Edgar Liu - Sun Microsystems 					widget->input_path_next = i;
371388447a05SGarrett D'Amore 					widget->path_flags |= AUDIOHD_PATH_ADC;
371488447a05SGarrett D'Amore 					widget->in_weight++;
371588447a05SGarrett D'Amore 					break;
371688447a05SGarrett D'Amore 				}
371788447a05SGarrett D'Amore 			}
371888447a05SGarrett D'Amore 		}
371988447a05SGarrett D'Amore 		break;
372088447a05SGarrett D'Amore 	default:
372188447a05SGarrett D'Amore 		break;
372288447a05SGarrett D'Amore 	}
372388447a05SGarrett D'Amore 
372488447a05SGarrett D'Amore 	return (retval);
372588447a05SGarrett D'Amore }	/* audiohd_find_input_pins */
372688447a05SGarrett D'Amore 
372788447a05SGarrett D'Amore /*
372888447a05SGarrett D'Amore  * audiohd_build_input_path()
372988447a05SGarrett D'Amore  *
373088447a05SGarrett D'Amore  * Description:
373188447a05SGarrett D'Amore  *	Find input path for the codec
373288447a05SGarrett D'Amore  */
373388447a05SGarrett D'Amore static void
audiohd_build_input_path(hda_codec_t * codec)373488447a05SGarrett D'Amore audiohd_build_input_path(hda_codec_t *codec)
373588447a05SGarrett D'Amore {
373688447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
373788447a05SGarrett D'Amore 	audiohd_path_t		*path = NULL;
373888447a05SGarrett D'Amore 	wid_t			wid;
373988447a05SGarrett D'Amore 	int			i;
374088447a05SGarrett D'Amore 	int			retval;
374188447a05SGarrett D'Amore 	uint8_t			rtag = 0;
3742ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
374388447a05SGarrett D'Amore 
374488447a05SGarrett D'Amore 	for (wid = codec->first_wid; wid <= codec->last_wid; wid++) {
374588447a05SGarrett D'Amore 
374688447a05SGarrett D'Amore 		widget = codec->widget[wid];
374788447a05SGarrett D'Amore 
374888447a05SGarrett D'Amore 		/* check if it is an ADC widget */
3749b96a6eceSZhao Edgar Liu - Sun Microsystems 		if (widget == NULL || widget->type != WTYPE_AUDIO_IN)
375088447a05SGarrett D'Amore 			continue;
375188447a05SGarrett D'Amore 
375288447a05SGarrett D'Amore 		if (path == NULL)
375388447a05SGarrett D'Amore 			path = kmem_zalloc(sizeof (audiohd_path_t),
375488447a05SGarrett D'Amore 			    KM_SLEEP);
375588447a05SGarrett D'Amore 		else
375688447a05SGarrett D'Amore 			bzero(path, sizeof (audiohd_port_t));
375788447a05SGarrett D'Amore 
375888447a05SGarrett D'Amore 		path->adda_wid = wid;
375988447a05SGarrett D'Amore 
376088447a05SGarrett D'Amore 		/*
376188447a05SGarrett D'Amore 		 * Is there any ADC widget which has more than one input ??
376288447a05SGarrett D'Amore 		 * I don't believe. Anyway, we carefully deal with this. But
376388447a05SGarrett D'Amore 		 * if hardware vendors embed a selector in a ADC, we just use
376488447a05SGarrett D'Amore 		 * the first available input, which has connection to input pin
376588447a05SGarrett D'Amore 		 * widget. Because selector cannot perform mixer functionality,
376688447a05SGarrett D'Amore 		 * and we just permit one selector or mixer in a recording path,
376788447a05SGarrett D'Amore 		 * if we use the selector embedded in ADC,we cannot use possible
376888447a05SGarrett D'Amore 		 * mixer during path searching.
376988447a05SGarrett D'Amore 		 */
377088447a05SGarrett D'Amore 		for (i = 0; i < widget->nconns; i++) {
377188447a05SGarrett D'Amore 			retval = audiohd_find_input_pins(codec,
377288447a05SGarrett D'Amore 			    widget->avail_conn[i], 1, 0, path);
3773c6e681c0SYang-Rong Jerry Zhou 			if (retval == DDI_SUCCESS) {
377488447a05SGarrett D'Amore 				path->codec = codec;
377588447a05SGarrett D'Amore 				path->statep = statep;
377688447a05SGarrett D'Amore 				path->path_type = RECORD;
377788447a05SGarrett D'Amore 				path->tag = ++rtag;
377888447a05SGarrett D'Amore 				codec->nistream++;
377988447a05SGarrett D'Amore 				statep->path[statep->pathnum++] = path;
3780b96a6eceSZhao Edgar Liu - Sun Microsystems 				widget->input_path_next = i;
378188447a05SGarrett D'Amore 				widget->priv = path;
378288447a05SGarrett D'Amore 				path = NULL;
378388447a05SGarrett D'Amore 				break;
378488447a05SGarrett D'Amore 			}
378588447a05SGarrett D'Amore 		}
378688447a05SGarrett D'Amore 	}
378788447a05SGarrett D'Amore 	if (path)
378888447a05SGarrett D'Amore 		kmem_free(path, sizeof (audiohd_path_t));
378988447a05SGarrett D'Amore }	/* audiohd_build_input_path */
379088447a05SGarrett D'Amore 
379188447a05SGarrett D'Amore /*
379288447a05SGarrett D'Amore  * audiohd_build_input_amp()
379388447a05SGarrett D'Amore  *
379488447a05SGarrett D'Amore  * Description:
379588447a05SGarrett D'Amore  *	Find gain and mute control widgets on the input path
379688447a05SGarrett D'Amore  */
379788447a05SGarrett D'Amore static void
audiohd_build_input_amp(hda_codec_t * codec)379888447a05SGarrett D'Amore audiohd_build_input_amp(hda_codec_t *codec)
379988447a05SGarrett D'Amore {
380088447a05SGarrett D'Amore 	audiohd_path_t		*path;
380188447a05SGarrett D'Amore 	audiohd_widget_t	*wsum, *wadc, *w;
380288447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
380388447a05SGarrett D'Amore 	uint_t			gain;
3804b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
380588447a05SGarrett D'Amore 	int			i, j;
380688447a05SGarrett D'Amore 	int			weight;
380788447a05SGarrett D'Amore 
3808ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
3809ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
3810a4c3d128SYang-Rong Jerry Zhou 		if (path == NULL || path->path_type != RECORD ||
381188447a05SGarrett D'Amore 		    path->codec != codec)
381288447a05SGarrett D'Amore 			continue;
381388447a05SGarrett D'Amore 
381488447a05SGarrett D'Amore 		wid = path->adda_wid;
381588447a05SGarrett D'Amore 		wadc = path->codec->widget[wid];
381688447a05SGarrett D'Amore 		weight = wadc->in_weight;
381788447a05SGarrett D'Amore 
381888447a05SGarrett D'Amore 		/*
381988447a05SGarrett D'Amore 		 * Search node which has mute functionality for
382088447a05SGarrett D'Amore 		 * the whole input path
382188447a05SGarrett D'Amore 		 */
382288447a05SGarrett D'Amore 		w = wadc;
382388447a05SGarrett D'Amore 		while (w) {
382488447a05SGarrett D'Amore 			if (w->outamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) {
382588447a05SGarrett D'Amore 				path->mute_wid = w->wid_wid;
382688447a05SGarrett D'Amore 				path->mute_dir = AUDIOHDC_AMP_SET_OUTPUT;
382788447a05SGarrett D'Amore 				break;
382888447a05SGarrett D'Amore 			}
382988447a05SGarrett D'Amore 			if ((w->inamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) &&
383088447a05SGarrett D'Amore 			    (w->wid_wid != path->sum_wid)) {
383188447a05SGarrett D'Amore 				path->mute_wid = w->wid_wid;
383288447a05SGarrett D'Amore 				path->mute_dir = AUDIOHDC_AMP_SET_INPUT;
383388447a05SGarrett D'Amore 				break;
383488447a05SGarrett D'Amore 			}
383588447a05SGarrett D'Amore 
3836b96a6eceSZhao Edgar Liu - Sun Microsystems 			next = w->input_path_next;
3837b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (next == AUDIOHD_NULL_CONN)
383888447a05SGarrett D'Amore 				break;
3839b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[next];
384088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
384188447a05SGarrett D'Amore 			if (w && w->in_weight != weight)
384288447a05SGarrett D'Amore 				break;
384388447a05SGarrett D'Amore 		}
384488447a05SGarrett D'Amore 
384588447a05SGarrett D'Amore 		/*
384688447a05SGarrett D'Amore 		 * Search a node for amplifier adjusting for the whole
384788447a05SGarrett D'Amore 		 * input path
384888447a05SGarrett D'Amore 		 */
384988447a05SGarrett D'Amore 		w = wadc;
385088447a05SGarrett D'Amore 		gain = 0;
385188447a05SGarrett D'Amore 		while (w) {
385288447a05SGarrett D'Amore 			gain = (w->outamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS);
385388447a05SGarrett D'Amore 			if (gain && gain > path->gain_bits) {
385488447a05SGarrett D'Amore 				path->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
385588447a05SGarrett D'Amore 				path->gain_bits = gain;
385688447a05SGarrett D'Amore 				path->gain_wid = w->wid_wid;
385788447a05SGarrett D'Amore 			}
385888447a05SGarrett D'Amore 			gain = w->inamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS;
385988447a05SGarrett D'Amore 			if (gain && (gain > path->gain_bits) &&
386088447a05SGarrett D'Amore 			    (w->wid_wid != path->sum_wid)) {
386188447a05SGarrett D'Amore 				path->gain_dir = AUDIOHDC_AMP_SET_INPUT;
386288447a05SGarrett D'Amore 				path->gain_bits = gain;
386388447a05SGarrett D'Amore 				path->gain_wid = w->wid_wid;
386488447a05SGarrett D'Amore 			}
3865b96a6eceSZhao Edgar Liu - Sun Microsystems 
3866b96a6eceSZhao Edgar Liu - Sun Microsystems 			next = w->input_path_next;
3867b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (next == AUDIOHD_NULL_CONN)
386888447a05SGarrett D'Amore 				break;
3869b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[next];
387088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
387188447a05SGarrett D'Amore 		}
387288447a05SGarrett D'Amore 		path->gain_bits >>= AUDIOHD_GAIN_OFF;
387388447a05SGarrett D'Amore 
387488447a05SGarrett D'Amore 		/*
387588447a05SGarrett D'Amore 		 * If the input path has one pin only, the mute/amp
387688447a05SGarrett D'Amore 		 * controlling is shared by the whole path and pin
387788447a05SGarrett D'Amore 		 */
387888447a05SGarrett D'Amore 		if (path->pin_nums == 1) {
387988447a05SGarrett D'Amore 			wid = path->pin_wid[0];
388088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
388188447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)w->priv;
388288447a05SGarrett D'Amore 			pin->gain_dir = path->gain_dir;
388388447a05SGarrett D'Amore 			pin->gain_bits = path->gain_bits;
388488447a05SGarrett D'Amore 			pin->gain_wid = path->gain_wid;
388588447a05SGarrett D'Amore 			pin->mute_wid = path->mute_wid;
388688447a05SGarrett D'Amore 			pin->mute_dir = path->mute_dir;
388788447a05SGarrett D'Amore 			continue;
388888447a05SGarrett D'Amore 		}
388988447a05SGarrett D'Amore 
389088447a05SGarrett D'Amore 		/*
389188447a05SGarrett D'Amore 		 * For multi-pin device, there must be a selector
389288447a05SGarrett D'Amore 		 * or mixer along the input path, and the sum_wid
389388447a05SGarrett D'Amore 		 * is the widget's node id.
389488447a05SGarrett D'Amore 		 */
389588447a05SGarrett D'Amore 		wid = path->sum_wid;
389688447a05SGarrett D'Amore 		wsum = path->codec->widget[wid]; /* sum widget */
389788447a05SGarrett D'Amore 
389888447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
389988447a05SGarrett D'Amore 			wid = path->pin_wid[j];
390088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
390188447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)w->priv;
390288447a05SGarrett D'Amore 
390388447a05SGarrett D'Amore 			/* find node for mute */
390488447a05SGarrett D'Amore 			if (wsum->inamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) {
390588447a05SGarrett D'Amore 				pin->mute_wid = wsum->wid_wid;
390688447a05SGarrett D'Amore 				pin->mute_dir = AUDIOHDC_AMP_SET_INPUT;
390788447a05SGarrett D'Amore 			} else {
390888447a05SGarrett D'Amore 				wid = wsum->avail_conn[path->sum_selconn[i]];
390988447a05SGarrett D'Amore 				w = path->codec->widget[wid];
391088447a05SGarrett D'Amore 				while (w) {
391188447a05SGarrett D'Amore 					if (w->outamp_cap &
391288447a05SGarrett D'Amore 					    AUDIOHDC_AMP_CAP_MUTE_CAP) {
391388447a05SGarrett D'Amore 						pin->mute_wid = w->wid_wid;
391488447a05SGarrett D'Amore 						pin->mute_dir =
391588447a05SGarrett D'Amore 						    AUDIOHDC_AMP_SET_OUTPUT;
391688447a05SGarrett D'Amore 						break;
391788447a05SGarrett D'Amore 					}
391888447a05SGarrett D'Amore 					if (w->inamp_cap &
391988447a05SGarrett D'Amore 					    AUDIOHDC_AMP_CAP_MUTE_CAP) {
392088447a05SGarrett D'Amore 						pin->mute_wid = w->wid_wid;
392188447a05SGarrett D'Amore 						pin->mute_dir =
392288447a05SGarrett D'Amore 						    AUDIOHDC_AMP_SET_INPUT;
392388447a05SGarrett D'Amore 						break;
392488447a05SGarrett D'Amore 					}
392588447a05SGarrett D'Amore 
3926b96a6eceSZhao Edgar Liu - Sun Microsystems 					next = w->input_path_next;
3927b96a6eceSZhao Edgar Liu - Sun Microsystems 					if (next == AUDIOHD_NULL_CONN)
392888447a05SGarrett D'Amore 						break;
3929b96a6eceSZhao Edgar Liu - Sun Microsystems 					wid = w->avail_conn[next];
393088447a05SGarrett D'Amore 					w = path->codec->widget[wid];
393188447a05SGarrett D'Amore 				}
393288447a05SGarrett D'Amore 			}
393388447a05SGarrett D'Amore 
393488447a05SGarrett D'Amore 			/* find node for amp controlling */
393588447a05SGarrett D'Amore 			gain = (wsum->inamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS);
393688447a05SGarrett D'Amore 			wid = wsum->avail_conn[path->sum_selconn[i]];
393788447a05SGarrett D'Amore 			w = path->codec->widget[wid];
393888447a05SGarrett D'Amore 			while (w) {
393988447a05SGarrett D'Amore 				gain = (w->outamp_cap &
394088447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
394188447a05SGarrett D'Amore 				if (gain && gain > pin->gain_bits) {
394288447a05SGarrett D'Amore 					pin->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
394388447a05SGarrett D'Amore 					pin->gain_bits = gain;
394488447a05SGarrett D'Amore 					pin->gain_wid = w->wid_wid;
394588447a05SGarrett D'Amore 				}
394688447a05SGarrett D'Amore 				gain = w->inamp_cap &
394788447a05SGarrett D'Amore 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
394888447a05SGarrett D'Amore 				if (gain && (gain > pin->gain_bits)) {
394988447a05SGarrett D'Amore 					pin->gain_dir = AUDIOHDC_AMP_SET_INPUT;
395088447a05SGarrett D'Amore 					pin->gain_bits = gain;
395188447a05SGarrett D'Amore 					pin->gain_wid = w->wid_wid;
395288447a05SGarrett D'Amore 				}
3953b96a6eceSZhao Edgar Liu - Sun Microsystems 
3954b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = w->input_path_next;
3955b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
395688447a05SGarrett D'Amore 					break;
3957b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[next];
395888447a05SGarrett D'Amore 				w = path->codec->widget[wid];
395988447a05SGarrett D'Amore 			}
396088447a05SGarrett D'Amore 			pin->gain_bits >>= AUDIOHD_GAIN_OFF;
396188447a05SGarrett D'Amore 		}
396288447a05SGarrett D'Amore 	}
396388447a05SGarrett D'Amore }	/* audiohd_build_input_amp() */
396488447a05SGarrett D'Amore 
396588447a05SGarrett D'Amore /*
396688447a05SGarrett D'Amore  * audiohd_finish_input_path()
396788447a05SGarrett D'Amore  *
396888447a05SGarrett D'Amore  * Description:
396988447a05SGarrett D'Amore  *	Enable the widgets on the input path
397088447a05SGarrett D'Amore  */
397188447a05SGarrett D'Amore static void
audiohd_finish_input_path(hda_codec_t * codec)397288447a05SGarrett D'Amore audiohd_finish_input_path(hda_codec_t *codec)
397388447a05SGarrett D'Amore {
3974ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
397588447a05SGarrett D'Amore 	audiohd_path_t		*path;
397688447a05SGarrett D'Amore 	audiohd_widget_t	*w, *wsum;
397788447a05SGarrett D'Amore 	uint_t			caddr = codec->index;
397888447a05SGarrett D'Amore 	wid_t			wid;
397988447a05SGarrett D'Amore 	int			i, j;
398088447a05SGarrett D'Amore 
3981ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
3982ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
3983a4c3d128SYang-Rong Jerry Zhou 		if (path == NULL || path->path_type != RECORD ||
398488447a05SGarrett D'Amore 		    path->codec != codec)
398588447a05SGarrett D'Amore 			continue;
398688447a05SGarrett D'Amore 		wid = path->adda_wid;
398788447a05SGarrett D'Amore 		w = path->codec->widget[wid];
398888447a05SGarrett D'Amore 		while (w && (w->wid_wid != path->sum_wid) &&
398988447a05SGarrett D'Amore 		    (w->type != WTYPE_PIN)) {
399088447a05SGarrett D'Amore 			if ((w->type == WTYPE_AUDIO_SEL) && (w->nconns > 1))
399188447a05SGarrett D'Amore 				(void) audioha_codec_verb_get(statep, caddr,
3992b96a6eceSZhao Edgar Liu - Sun Microsystems 				    w->wid_wid, AUDIOHDC_VERB_SET_CONN_SEL,
3993b96a6eceSZhao Edgar Liu - Sun Microsystems 				    w->input_path_next);
399488447a05SGarrett D'Amore 
399588447a05SGarrett D'Amore 			if (w->outamp_cap) {
399688447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(statep,
399788447a05SGarrett D'Amore 				    caddr,
399888447a05SGarrett D'Amore 				    w->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
399988447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
400088447a05SGarrett D'Amore 				    AUDIOHDC_GAIN_MAX);
400188447a05SGarrett D'Amore 			}
400288447a05SGarrett D'Amore 
400388447a05SGarrett D'Amore 			if (w->inamp_cap) {
400488447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(statep,
400588447a05SGarrett D'Amore 				    caddr,
400688447a05SGarrett D'Amore 				    w->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
400788447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_LR_INPUT |
400888447a05SGarrett D'Amore 				    AUDIOHDC_GAIN_MAX |
4009b96a6eceSZhao Edgar Liu - Sun Microsystems 				    (w->input_path_next <<
401088447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_INDEX_OFFSET));
401188447a05SGarrett D'Amore 			}
401288447a05SGarrett D'Amore 
4013b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[w->input_path_next];
401488447a05SGarrett D'Amore 			w = path->codec->widget[wid];
401588447a05SGarrett D'Amore 		}
401688447a05SGarrett D'Amore 
401788447a05SGarrett D'Amore 		/*
401888447a05SGarrett D'Amore 		 * After exiting from the above loop, the widget pointed
401988447a05SGarrett D'Amore 		 * by w can be a pin widget or select/mixer widget. If it
402088447a05SGarrett D'Amore 		 * is a pin widget, we already finish "select connection"
402188447a05SGarrett D'Amore 		 * operation for the whole path.
402288447a05SGarrett D'Amore 		 */
402388447a05SGarrett D'Amore 		if (w && w->type == WTYPE_PIN)
402488447a05SGarrett D'Amore 			continue;
402588447a05SGarrett D'Amore 
402688447a05SGarrett D'Amore 		/*
402788447a05SGarrett D'Amore 		 * deal with multi-pin input devices.
402888447a05SGarrett D'Amore 		 */
402988447a05SGarrett D'Amore 		wid = path->sum_wid;
403088447a05SGarrett D'Amore 		wsum = path->codec->widget[wid];
403188447a05SGarrett D'Amore 		if (wsum == NULL)
403288447a05SGarrett D'Amore 			continue;
403388447a05SGarrett D'Amore 		if (wsum->outamp_cap) {
403488447a05SGarrett D'Amore 			(void) audioha_codec_4bit_verb_get(statep,
403588447a05SGarrett D'Amore 			    caddr,
403688447a05SGarrett D'Amore 			    wsum->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
403788447a05SGarrett D'Amore 			    AUDIOHDC_AMP_SET_LR_OUTPUT |
403888447a05SGarrett D'Amore 			    AUDIOHDC_GAIN_MAX);
403988447a05SGarrett D'Amore 		}
404088447a05SGarrett D'Amore 
404188447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
404288447a05SGarrett D'Amore 			if (wsum->inamp_cap) {
404388447a05SGarrett D'Amore 				(void) audioha_codec_4bit_verb_get(statep,
404488447a05SGarrett D'Amore 				    caddr,
404588447a05SGarrett D'Amore 				    wsum->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
404688447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_LR_INPUT |
404788447a05SGarrett D'Amore 				    AUDIOHDC_GAIN_MAX |
404888447a05SGarrett D'Amore 				    (path->sum_selconn[j] <<
404988447a05SGarrett D'Amore 				    AUDIOHDC_AMP_SET_INDEX_OFFSET));
405088447a05SGarrett D'Amore 			}
405188447a05SGarrett D'Amore 			if (wsum->type == WTYPE_AUDIO_SEL) {
405288447a05SGarrett D'Amore 				(void) audioha_codec_verb_get(statep, caddr,
405388447a05SGarrett D'Amore 				    wsum->wid_wid,
405488447a05SGarrett D'Amore 				    AUDIOHDC_VERB_SET_CONN_SEL,
405588447a05SGarrett D'Amore 				    path->sum_selconn[j]);
405688447a05SGarrett D'Amore 			}
405788447a05SGarrett D'Amore 
405888447a05SGarrett D'Amore 			wid = wsum->avail_conn[path->sum_selconn[j]];
405988447a05SGarrett D'Amore 			w = path->codec->widget[wid];
406088447a05SGarrett D'Amore 			while (w && w->type != WTYPE_PIN) {
406188447a05SGarrett D'Amore 				if ((w->type != WTYPE_AUDIO_MIX) &&
406288447a05SGarrett D'Amore 				    (w->nconns > 1))
406388447a05SGarrett D'Amore 					(void) audioha_codec_verb_get(statep,
406488447a05SGarrett D'Amore 					    caddr, w->wid_wid,
406588447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_CONN_SEL,
4066b96a6eceSZhao Edgar Liu - Sun Microsystems 					    w->input_path_next);
406788447a05SGarrett D'Amore 
406888447a05SGarrett D'Amore 				if (w->outamp_cap) {
406988447a05SGarrett D'Amore 					(void) audioha_codec_4bit_verb_get(
407088447a05SGarrett D'Amore 					    statep,
407188447a05SGarrett D'Amore 					    caddr,
407288447a05SGarrett D'Amore 					    w->wid_wid,
407388447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_AMP_MUTE,
407488447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_LR_OUTPUT |
407588447a05SGarrett D'Amore 					    AUDIOHDC_GAIN_MAX);
407688447a05SGarrett D'Amore 				}
407788447a05SGarrett D'Amore 
407888447a05SGarrett D'Amore 				if (w->inamp_cap) {
407988447a05SGarrett D'Amore 					(void) audioha_codec_4bit_verb_get(
408088447a05SGarrett D'Amore 					    statep,
408188447a05SGarrett D'Amore 					    caddr,
408288447a05SGarrett D'Amore 					    w->wid_wid,
408388447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_AMP_MUTE,
408488447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_LR_INPUT |
408588447a05SGarrett D'Amore 					    AUDIOHDC_GAIN_MAX |
4086b96a6eceSZhao Edgar Liu - Sun Microsystems 					    (w->input_path_next <<
408788447a05SGarrett D'Amore 					    AUDIOHDC_AMP_SET_INDEX_OFFSET));
408888447a05SGarrett D'Amore 				}
4089b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[w->input_path_next];
409088447a05SGarrett D'Amore 				w = path->codec->widget[wid];
409188447a05SGarrett D'Amore 			}
409288447a05SGarrett D'Amore 		}
409388447a05SGarrett D'Amore 	}	/* end of istream loop */
409488447a05SGarrett D'Amore }	/* audiohd_finish_input_path */
409588447a05SGarrett D'Amore 
409688447a05SGarrett D'Amore /*
409788447a05SGarrett D'Amore  * audiohd_find_inpin_for_monitor()
409888447a05SGarrett D'Amore  *
409988447a05SGarrett D'Amore  * Description:
410088447a05SGarrett D'Amore  *	Find input pin for monitor path.
410188447a05SGarrett D'Amore  *
410288447a05SGarrett D'Amore  * Arguments:
410388447a05SGarrett D'Amore  *	hda_codec_t		*codec		where the monitor path exists
410488447a05SGarrett D'Amore  *	wid_t			id		no. of widget being searched
410588447a05SGarrett D'Amore  *	int			mixer		share or not
410688447a05SGarrett D'Amore  */
410788447a05SGarrett D'Amore static int
audiohd_find_inpin_for_monitor(hda_codec_t * codec,wid_t id,int mixer)410870feb41cSZhao Edgar Liu - Sun Microsystems audiohd_find_inpin_for_monitor(hda_codec_t *codec, wid_t id, int mixer)
410988447a05SGarrett D'Amore {
411088447a05SGarrett D'Amore 	wid_t 			wid;
4111b96a6eceSZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget, *w;
411288447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
411388447a05SGarrett D'Amore 	int 			i, find = 0;
411488447a05SGarrett D'Amore 
411588447a05SGarrett D'Amore 	wid = id;
411688447a05SGarrett D'Amore 	widget = codec->widget[wid];
411788447a05SGarrett D'Amore 	if (widget == NULL)
4118c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
411988447a05SGarrett D'Amore 
412088447a05SGarrett D'Amore 	if (widget->type == WTYPE_PIN) {
412188447a05SGarrett D'Amore 		pin = (audiohd_pin_t *)widget->priv;
412288447a05SGarrett D'Amore 		if (pin->no_phys_conn)
4123c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
412488447a05SGarrett D'Amore 		switch (pin->device) {
412588447a05SGarrett D'Amore 			case DTYPE_SPDIF_IN:
412688447a05SGarrett D'Amore 			case DTYPE_CD:
412788447a05SGarrett D'Amore 			case DTYPE_LINE_IN:
412888447a05SGarrett D'Amore 			case DTYPE_MIC_IN:
412988447a05SGarrett D'Amore 			case DTYPE_AUX:
413088447a05SGarrett D'Amore 				widget->path_flags |= AUDIOHD_PATH_MON;
4131c6e681c0SYang-Rong Jerry Zhou 				return (DDI_SUCCESS);
413288447a05SGarrett D'Amore 			default:
4133c6e681c0SYang-Rong Jerry Zhou 				return (uint32_t)(DDI_FAILURE);
413488447a05SGarrett D'Amore 		}
413588447a05SGarrett D'Amore 	}
413688447a05SGarrett D'Amore 	/* the widget has been visited and can't be directed to input pin */
413788447a05SGarrett D'Amore 	if (widget->path_flags & AUDIOHD_PATH_NOMON) {
4138c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
413988447a05SGarrett D'Amore 	}
414088447a05SGarrett D'Amore 	/* the widget has been used by the monitor path, and we can share it */
414188447a05SGarrett D'Amore 	if (widget->path_flags & AUDIOHD_PATH_MON) {
414288447a05SGarrett D'Amore 		if (mixer)
4143c6e681c0SYang-Rong Jerry Zhou 			return (DDI_SUCCESS);
414488447a05SGarrett D'Amore 		else
4145c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
414688447a05SGarrett D'Amore 	}
414788447a05SGarrett D'Amore 	switch (widget->type) {
414888447a05SGarrett D'Amore 		case WTYPE_AUDIO_MIX:
414988447a05SGarrett D'Amore 			for (i = 0; i < widget->nconns; i++) {
4150b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next == i)
415188447a05SGarrett D'Amore 					continue;
415288447a05SGarrett D'Amore 				if (audiohd_find_inpin_for_monitor(codec,
415388447a05SGarrett D'Amore 				    widget->avail_conn[i], mixer) ==
4154c6e681c0SYang-Rong Jerry Zhou 				    DDI_SUCCESS) {
4155b96a6eceSZhao Edgar Liu - Sun Microsystems 					w = widget;
4156b96a6eceSZhao Edgar Liu - Sun Microsystems 					w->monitor_path_next[w->used++] = i;
4157b96a6eceSZhao Edgar Liu - Sun Microsystems 					w->path_flags |= AUDIOHD_PATH_MON;
415888447a05SGarrett D'Amore 					find = 1;
415988447a05SGarrett D'Amore 				}
416088447a05SGarrett D'Amore 			}
416188447a05SGarrett D'Amore 			break;
416288447a05SGarrett D'Amore 		case WTYPE_AUDIO_SEL:
416388447a05SGarrett D'Amore 			for (i = 0; i < widget->nconns; i++) {
4164b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next == i)
416588447a05SGarrett D'Amore 					continue;
416688447a05SGarrett D'Amore 				if (audiohd_find_inpin_for_monitor(codec,
4167b96a6eceSZhao Edgar Liu - Sun Microsystems 				    widget->avail_conn[i], mixer) ==
4168c6e681c0SYang-Rong Jerry Zhou 				    DDI_SUCCESS) {
4169b96a6eceSZhao Edgar Liu - Sun Microsystems 					widget->monitor_path_next[0] = i;
417088447a05SGarrett D'Amore 					widget->path_flags |= AUDIOHD_PATH_MON;
4171b96a6eceSZhao Edgar Liu - Sun Microsystems 					find = 1;
4172b96a6eceSZhao Edgar Liu - Sun Microsystems 					break;
417388447a05SGarrett D'Amore 				}
417488447a05SGarrett D'Amore 			}
4175b96a6eceSZhao Edgar Liu - Sun Microsystems 			break;
417688447a05SGarrett D'Amore 		default:
417788447a05SGarrett D'Amore 			break;
417888447a05SGarrett D'Amore 	}
417988447a05SGarrett D'Amore 	if (!find) {
418088447a05SGarrett D'Amore 		widget->path_flags |= AUDIOHD_PATH_NOMON;
4181c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
418288447a05SGarrett D'Amore 	}
418388447a05SGarrett D'Amore 	else
4184c6e681c0SYang-Rong Jerry Zhou 		return (DDI_SUCCESS);
418588447a05SGarrett D'Amore }	/* audiohd_find_inpin_for_monitor */
418688447a05SGarrett D'Amore 
418788447a05SGarrett D'Amore /*
418888447a05SGarrett D'Amore  * audiohd_build_monitor_path()
418988447a05SGarrett D'Amore  *
419088447a05SGarrett D'Amore  * Description:
419188447a05SGarrett D'Amore  * 	The functionality of mixer is to mix inputs, such as CD-IN, MIC,
419288447a05SGarrett D'Amore  * 	Line-in, etc, with DAC outputs, so as to minitor what is being
419388447a05SGarrett D'Amore  * 	recorded and implement "What you hear is what you get". However,
419488447a05SGarrett D'Amore  * 	this functionality are really hardware-dependent: the inputs
419588447a05SGarrett D'Amore  * 	must be directed to MIXER if they can be directed to ADC as
419688447a05SGarrett D'Amore  * 	recording sources.
419788447a05SGarrett D'Amore  */
419888447a05SGarrett D'Amore static void
audiohd_build_monitor_path(hda_codec_t * codec)419988447a05SGarrett D'Amore audiohd_build_monitor_path(hda_codec_t *codec)
420088447a05SGarrett D'Amore {
420188447a05SGarrett D'Amore 	audiohd_path_t		*path;
4202b96a6eceSZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget, *w;
4203ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
4204b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
420588447a05SGarrett D'Amore 	int			i, j, k, l, find;
420688447a05SGarrett D'Amore 	int			mixernum = 0;
420788447a05SGarrett D'Amore 
420888447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
420988447a05SGarrett D'Amore 		path = statep->path[i];
4210b96a6eceSZhao Edgar Liu - Sun Microsystems 		if (path == NULL || path->codec != codec ||
4211b96a6eceSZhao Edgar Liu - Sun Microsystems 		    path->path_type != PLAY)
421288447a05SGarrett D'Amore 			continue;
421388447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
421488447a05SGarrett D'Amore 			wid = path->pin_wid[j];
421588447a05SGarrett D'Amore 			widget = codec->widget[wid];
421688447a05SGarrett D'Amore 			l = 0;
421788447a05SGarrett D'Amore 			while (widget) {
421888447a05SGarrett D'Amore 				while (widget &&
421988447a05SGarrett D'Amore 				    ((widget->type != WTYPE_AUDIO_MIX) ||
422088447a05SGarrett D'Amore 				    (widget->nconns < 2))) {
4221b96a6eceSZhao Edgar Liu - Sun Microsystems 					next = widget->output_path_next;
4222b96a6eceSZhao Edgar Liu - Sun Microsystems 					if (next == AUDIOHD_NULL_CONN)
422388447a05SGarrett D'Amore 						break;
4224b96a6eceSZhao Edgar Liu - Sun Microsystems 					wid = widget->avail_conn[next];
422588447a05SGarrett D'Amore 					widget = codec->widget[wid];
422688447a05SGarrett D'Amore 				}
422788447a05SGarrett D'Amore 
422888447a05SGarrett D'Amore 				/*
422988447a05SGarrett D'Amore 				 * No mixer in this output path, we cannot build
423088447a05SGarrett D'Amore 				 * mixer path for this path, skip it,
4231b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * and continue for next output path.
423288447a05SGarrett D'Amore 				 */
4233b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget == NULL ||
4234b96a6eceSZhao Edgar Liu - Sun Microsystems 				    widget->output_path_next ==
423588447a05SGarrett D'Amore 				    AUDIOHD_NULL_CONN) {
423688447a05SGarrett D'Amore 					break;
423788447a05SGarrett D'Amore 				}
423888447a05SGarrett D'Amore 				mixernum++;
423988447a05SGarrett D'Amore 				for (k = 0; k < widget->nconns; k++) {
424088447a05SGarrett D'Amore 
424188447a05SGarrett D'Amore 					/*
424288447a05SGarrett D'Amore 					 * this connection must be routined
424388447a05SGarrett D'Amore 					 * to DAC instead of an input pin
424488447a05SGarrett D'Amore 					 * widget, we needn't waste time for
424588447a05SGarrett D'Amore 					 * it
424688447a05SGarrett D'Amore 					 */
4247b96a6eceSZhao Edgar Liu - Sun Microsystems 					if (widget->output_path_next == k)
424888447a05SGarrett D'Amore 						continue;
424988447a05SGarrett D'Amore 					find = 0;
425088447a05SGarrett D'Amore 					if (audiohd_find_inpin_for_monitor(
425188447a05SGarrett D'Amore 					    codec,
425288447a05SGarrett D'Amore 					    widget->avail_conn[k], 0) ==
4253c6e681c0SYang-Rong Jerry Zhou 					    DDI_SUCCESS) {
425488447a05SGarrett D'Amore 						path->mon_wid[j][l] = wid;
4255b96a6eceSZhao Edgar Liu - Sun Microsystems 						w = widget;
4256b96a6eceSZhao Edgar Liu - Sun Microsystems 						w->monitor_path_next[w->used++]
4257b96a6eceSZhao Edgar Liu - Sun Microsystems 						    = k;
4258b96a6eceSZhao Edgar Liu - Sun Microsystems 						w->path_flags |=
425988447a05SGarrett D'Amore 						    AUDIOHD_PATH_MON;
426088447a05SGarrett D'Amore 						find = 1;
426188447a05SGarrett D'Amore 					} else if (
426288447a05SGarrett D'Amore 					    audiohd_find_inpin_for_monitor(
426388447a05SGarrett D'Amore 					    codec,
426488447a05SGarrett D'Amore 					    widget->avail_conn[k], 1) ==
4265c6e681c0SYang-Rong Jerry Zhou 					    DDI_SUCCESS) {
426688447a05SGarrett D'Amore 						path->mon_wid[j][l] = wid;
4267b96a6eceSZhao Edgar Liu - Sun Microsystems 						w = widget;
4268b96a6eceSZhao Edgar Liu - Sun Microsystems 						w->monitor_path_next[w->used++]
4269b96a6eceSZhao Edgar Liu - Sun Microsystems 						    = k;
4270b96a6eceSZhao Edgar Liu - Sun Microsystems 						w->path_flags |=
427188447a05SGarrett D'Amore 						    AUDIOHD_PATH_MON;
427288447a05SGarrett D'Amore 						find = 1;
427388447a05SGarrett D'Amore 					}
427488447a05SGarrett D'Amore 
427588447a05SGarrett D'Amore 				}
427688447a05SGarrett D'Amore 
427788447a05SGarrett D'Amore 				/*
4278b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * we needn't check widget->output_path_next
4279b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * here since this widget is a selector or
4280b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * mixer, it cannot be NULL connection.
428188447a05SGarrett D'Amore 				 */
428288447a05SGarrett D'Amore 				if (!find) {
428370feb41cSZhao Edgar Liu - Sun Microsystems 					path->mon_wid[j][l] = 0;
428488447a05SGarrett D'Amore 					widget->path_flags |=
428588447a05SGarrett D'Amore 					    AUDIOHD_PATH_NOMON;
428688447a05SGarrett D'Amore 				}
4287b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
4288b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
428988447a05SGarrett D'Amore 				widget = codec->widget[wid];
429088447a05SGarrett D'Amore 				l++;
429188447a05SGarrett D'Amore 			}
429288447a05SGarrett D'Amore 			path->maxmixer[j] = l;
429388447a05SGarrett D'Amore 		}
429488447a05SGarrett D'Amore 
429588447a05SGarrett D'Amore 	}
429688447a05SGarrett D'Amore 	if (mixernum == 0)
4297e7236f70SZhao Edgar Liu - Sun Microsystems 		statep->monitor_supported = B_FALSE;
429888447a05SGarrett D'Amore 	else
4299e7236f70SZhao Edgar Liu - Sun Microsystems 		statep->monitor_supported = B_TRUE;
430088447a05SGarrett D'Amore }	/* audiohd_build_monitor_path */
430188447a05SGarrett D'Amore 
430288447a05SGarrett D'Amore /*
430388447a05SGarrett D'Amore  * audiohd_do_finish_monitor_path
430488447a05SGarrett D'Amore  *
430588447a05SGarrett D'Amore  * Description:
430688447a05SGarrett D'Amore  *	Enable the widgets on the monitor path
430788447a05SGarrett D'Amore  */
430888447a05SGarrett D'Amore static void
audiohd_do_finish_monitor_path(hda_codec_t * codec,audiohd_widget_t * wgt)430988447a05SGarrett D'Amore audiohd_do_finish_monitor_path(hda_codec_t *codec, audiohd_widget_t *wgt)
431088447a05SGarrett D'Amore {
431188447a05SGarrett D'Amore 	uint_t			caddr = codec->index;
431288447a05SGarrett D'Amore 	audiohd_widget_t 	*widget = wgt;
431388447a05SGarrett D'Amore 	audiohd_widget_t	*w;
4314ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
431588447a05SGarrett D'Amore 	wid_t			wid;
431688447a05SGarrett D'Amore 	int			i;
431788447a05SGarrett D'Amore 	int			share = 0;
431888447a05SGarrett D'Amore 
431988447a05SGarrett D'Amore 	if (!widget || widget->finish)
432088447a05SGarrett D'Amore 		return;
432188447a05SGarrett D'Amore 	if (widget->path_flags & AUDIOHD_PATH_ADC)
432288447a05SGarrett D'Amore 		share = 1;
432388447a05SGarrett D'Amore 	if ((widget->outamp_cap) && !share)
432488447a05SGarrett D'Amore 		(void) audioha_codec_4bit_verb_get(statep, caddr,
4325b96a6eceSZhao Edgar Liu - Sun Microsystems 		    widget->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
4326b96a6eceSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_AMP_SET_LR_OUTPUT | AUDIOHDC_GAIN_MAX);
432788447a05SGarrett D'Amore 	if ((widget->inamp_cap) && !share) {
432888447a05SGarrett D'Amore 		for (i = 0; i < widget->used; i++) {
432988447a05SGarrett D'Amore 		(void) audioha_codec_4bit_verb_get(statep, caddr,
433088447a05SGarrett D'Amore 		    widget->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
4331b96a6eceSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_AMP_SET_LR_INPUT | AUDIOHDC_GAIN_MAX |
4332b96a6eceSZhao Edgar Liu - Sun Microsystems 		    (widget->monitor_path_next[i]
4333b96a6eceSZhao Edgar Liu - Sun Microsystems 		    << AUDIOHDC_AMP_SET_INDEX_OFFSET));
433488447a05SGarrett D'Amore 		}
433588447a05SGarrett D'Amore 	}
433688447a05SGarrett D'Amore 	if ((widget->type == WTYPE_AUDIO_SEL) && (widget->nconns > 1) &&
433788447a05SGarrett D'Amore 	    !share) {
4338b96a6eceSZhao Edgar Liu - Sun Microsystems 		(void) audioha_codec_verb_get(statep, caddr, widget->wid_wid,
4339b96a6eceSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_VERB_SET_CONN_SEL, widget->monitor_path_next[0]);
434088447a05SGarrett D'Amore 	}
434188447a05SGarrett D'Amore 	widget->finish = 1;
434288447a05SGarrett D'Amore 	if (widget->used == 0)
434388447a05SGarrett D'Amore 		return;
434488447a05SGarrett D'Amore 	if (widget->used > 0) {
434588447a05SGarrett D'Amore 		for (i = 0; i < widget->used; i++) {
4346b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = widget->avail_conn[widget->monitor_path_next[i]];
434788447a05SGarrett D'Amore 			w = codec->widget[wid];
434888447a05SGarrett D'Amore 			audiohd_do_finish_monitor_path(codec, w);
434988447a05SGarrett D'Amore 		}
435088447a05SGarrett D'Amore 	}
435188447a05SGarrett D'Amore }	/* audiohd_do_finish_monitor_path */
435288447a05SGarrett D'Amore 
435388447a05SGarrett D'Amore /*
435488447a05SGarrett D'Amore  * audiohd_finish_monitor_path
435588447a05SGarrett D'Amore  *
435688447a05SGarrett D'Amore  * Description:
435788447a05SGarrett D'Amore  *	Enable the monitor path for every ostream path
435888447a05SGarrett D'Amore  */
435988447a05SGarrett D'Amore static void
audiohd_finish_monitor_path(hda_codec_t * codec)436088447a05SGarrett D'Amore audiohd_finish_monitor_path(hda_codec_t *codec)
436188447a05SGarrett D'Amore {
436288447a05SGarrett D'Amore 	audiohd_path_t		*path;
436388447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
4364ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
436588447a05SGarrett D'Amore 	wid_t			wid;
436688447a05SGarrett D'Amore 	int 			i, j, k;
436788447a05SGarrett D'Amore 
436888447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
436988447a05SGarrett D'Amore 		path = statep->path[i];
437088447a05SGarrett D'Amore 		if (!path || path->codec != codec || path->path_type != PLAY)
437188447a05SGarrett D'Amore 			continue;
437288447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
437388447a05SGarrett D'Amore 			for (k = 0; k < path->maxmixer[j]; k++) {
437488447a05SGarrett D'Amore 				wid = path->mon_wid[j][k];
437588447a05SGarrett D'Amore 				if (wid == 0) {
437688447a05SGarrett D'Amore 					continue;
437788447a05SGarrett D'Amore 				}
437888447a05SGarrett D'Amore 				widget = codec->widget[wid];
437988447a05SGarrett D'Amore 				audiohd_do_finish_monitor_path(codec, widget);
438088447a05SGarrett D'Amore 			}
438188447a05SGarrett D'Amore 		}
438288447a05SGarrett D'Amore 	}
438388447a05SGarrett D'Amore }	/* audiohd_finish_monitor_path */
438488447a05SGarrett D'Amore 
438588447a05SGarrett D'Amore /*
438688447a05SGarrett D'Amore  * audiohd_do_build_monit_amp()
438788447a05SGarrett D'Amore  *
438888447a05SGarrett D'Amore  * Description:
438988447a05SGarrett D'Amore  *	Search for the gain control widget for the monitor path
439088447a05SGarrett D'Amore  */
439188447a05SGarrett D'Amore static void
audiohd_do_build_monitor_amp(hda_codec_t * codec,audiohd_pin_t * pin,audiohd_widget_t * widget)439288447a05SGarrett D'Amore audiohd_do_build_monitor_amp(hda_codec_t *codec, audiohd_pin_t *pin,
439388447a05SGarrett D'Amore     audiohd_widget_t *widget)
439488447a05SGarrett D'Amore {
439588447a05SGarrett D'Amore 	audiohd_widget_t	*w = widget;
439688447a05SGarrett D'Amore 	uint32_t		gain;
439788447a05SGarrett D'Amore 	int			i;
439888447a05SGarrett D'Amore 	wid_t			wid;
439988447a05SGarrett D'Amore 
440088447a05SGarrett D'Amore 	if (!w ||
440188447a05SGarrett D'Amore 	    (w->type == WTYPE_PIN) ||
440288447a05SGarrett D'Amore 	    !w->used ||
440388447a05SGarrett D'Amore 	    (pin->num == AUDIOHD_MAX_CONN) ||
440488447a05SGarrett D'Amore 	    (w->path_flags & AUDIOHD_PATH_ADC))
440588447a05SGarrett D'Amore 		return;
440688447a05SGarrett D'Amore 	if (!(w->path_flags & AUDIOHD_PATH_DAC)) {
440788447a05SGarrett D'Amore 		gain = w->outamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS;
440888447a05SGarrett D'Amore 		if (gain) {
440988447a05SGarrett D'Amore 			pin->mg_dir[pin->num] = AUDIOHDC_AMP_SET_OUTPUT;
441088447a05SGarrett D'Amore 			pin->mg_gain[pin->num] = gain;
441188447a05SGarrett D'Amore 			pin->mg_wid[pin->num] = w->wid_wid;
441288447a05SGarrett D'Amore 			pin->mg_gain[pin->num] >>= AUDIOHD_GAIN_OFF;
441388447a05SGarrett D'Amore 			pin->num++;
441488447a05SGarrett D'Amore 			return;
441588447a05SGarrett D'Amore 		}
441688447a05SGarrett D'Amore 		gain = w->inamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS;
441788447a05SGarrett D'Amore 		if (gain) {
441888447a05SGarrett D'Amore 			pin->mg_dir[pin->num] = AUDIOHDC_AMP_SET_INPUT;
441988447a05SGarrett D'Amore 			pin->mg_gain[pin->num] = gain;
442088447a05SGarrett D'Amore 			pin->mg_wid[pin->num] = w->wid_wid;
442188447a05SGarrett D'Amore 			pin->mg_gain[pin->num] >>= AUDIOHD_GAIN_OFF;
442288447a05SGarrett D'Amore 			pin->num++;
442388447a05SGarrett D'Amore 			return;
442488447a05SGarrett D'Amore 		}
442588447a05SGarrett D'Amore 	}
442688447a05SGarrett D'Amore 	for (i = 0; i < w->used; i++) {
4427b96a6eceSZhao Edgar Liu - Sun Microsystems 		wid = w->avail_conn[w->monitor_path_next[i]];
442888447a05SGarrett D'Amore 		audiohd_do_build_monitor_amp(codec, pin, codec->widget[wid]);
442988447a05SGarrett D'Amore 	}
443088447a05SGarrett D'Amore 
443188447a05SGarrett D'Amore 
443288447a05SGarrett D'Amore }	/* audiohd_do_build_monitor_amp() */
443388447a05SGarrett D'Amore 
443488447a05SGarrett D'Amore /*
443588447a05SGarrett D'Amore  * audiohd_build_monitor_amp()
443688447a05SGarrett D'Amore  *
443788447a05SGarrett D'Amore  * Description:
443888447a05SGarrett D'Amore  *	Search gain control widget for every ostream monitor
443988447a05SGarrett D'Amore  */
444088447a05SGarrett D'Amore static void
audiohd_build_monitor_amp(hda_codec_t * codec)444188447a05SGarrett D'Amore audiohd_build_monitor_amp(hda_codec_t *codec)
444288447a05SGarrett D'Amore {
444388447a05SGarrett D'Amore 	audiohd_path_t		*path;
444488447a05SGarrett D'Amore 	audiohd_widget_t	*widget, *w;
4445ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
444688447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
444788447a05SGarrett D'Amore 	wid_t			wid, id;
444888447a05SGarrett D'Amore 	int			i, j, k;
444988447a05SGarrett D'Amore 
445088447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
445188447a05SGarrett D'Amore 		path = statep->path[i];
445288447a05SGarrett D'Amore 		if (!path || path->codec != codec || path->path_type != PLAY)
445388447a05SGarrett D'Amore 			continue;
445488447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
445588447a05SGarrett D'Amore 			id = path->pin_wid[j];
445688447a05SGarrett D'Amore 			w = codec->widget[id];
445788447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)(w->priv);
445888447a05SGarrett D'Amore 			for (k = 0; k < path->maxmixer[j]; k++) {
445988447a05SGarrett D'Amore 				wid = path->mon_wid[j][k];
446088447a05SGarrett D'Amore 				if (!wid)
446188447a05SGarrett D'Amore 					continue;
446288447a05SGarrett D'Amore 				widget = codec->widget[wid];
446388447a05SGarrett D'Amore 				audiohd_do_build_monitor_amp(codec, pin,
446488447a05SGarrett D'Amore 				    widget);
446588447a05SGarrett D'Amore 			}
446688447a05SGarrett D'Amore 		}
446788447a05SGarrett D'Amore 	}
446888447a05SGarrett D'Amore }
446988447a05SGarrett D'Amore 
447042c41cf8Slipeng sang - Sun Microsystems - Beijing China /*
447142c41cf8Slipeng sang - Sun Microsystems - Beijing China  * audiohd_find_beep()
447242c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Description:
447342c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      Find a beep for a beep path. Then the play data can be sent to the out
447442c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      put pin through the beep path.
447542c41cf8Slipeng sang - Sun Microsystems - Beijing China  *
447642c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Arguments:
447742c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      hda_codec_t     *codec          where the beep widget exists
447842c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      wid_t           wid             the no. of a widget
447942c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      int             depth           the depth of search
448042c41cf8Slipeng sang - Sun Microsystems - Beijing China  *
448142c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Return:
448242c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      1) wid of Beep widget;
448342c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      2) 0 if no path
448442c41cf8Slipeng sang - Sun Microsystems - Beijing China  */
448542c41cf8Slipeng sang - Sun Microsystems - Beijing China static wid_t
audiohd_find_beep(hda_codec_t * codec,wid_t wid,int depth)448642c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_find_beep(hda_codec_t *codec, wid_t wid, int depth)
448742c41cf8Slipeng sang - Sun Microsystems - Beijing China {
448842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_widget_t	*widget = codec->widget[wid];
448942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	wid_t   		wbeep = (uint32_t)(DDI_FAILURE);
449042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	wid_t   		retval;
449142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
449242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (depth > AUDIOHD_MAX_DEPTH)
449342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		return (uint32_t)(DDI_FAILURE);
449442c41cf8Slipeng sang - Sun Microsystems - Beijing China 
449542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (widget == NULL)
449642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		return (uint32_t)(DDI_FAILURE);
449742c41cf8Slipeng sang - Sun Microsystems - Beijing China 
449842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	switch (widget->type) {
449942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	case WTYPE_BEEP:
450042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		widget->path_flags |= AUDIOHD_PATH_BEEP;
450142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		wbeep = widget->wid_wid;
450242c41cf8Slipeng sang - Sun Microsystems - Beijing China 		break;
450342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	case WTYPE_AUDIO_MIX:
450442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	case WTYPE_AUDIO_SEL:
450542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		for (int i = 0; i < widget->nconns; i++) {
450642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			retval = audiohd_find_beep(codec,
450742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    widget->avail_conn[i], depth + 1);
4508b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (retval == DDI_SUCCESS) {
4509b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next !=
4510b96a6eceSZhao Edgar Liu - Sun Microsystems 				    AUDIOHD_NULL_CONN)
451142c41cf8Slipeng sang - Sun Microsystems - Beijing China 					continue;
4512b96a6eceSZhao Edgar Liu - Sun Microsystems 				widget->beep_path_next = i;
451342c41cf8Slipeng sang - Sun Microsystems - Beijing China 				wbeep = retval;
451442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				widget->path_flags |= AUDIOHD_PATH_BEEP;
451542c41cf8Slipeng sang - Sun Microsystems - Beijing China 				return (wbeep);
451642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
451742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
4518b96a6eceSZhao Edgar Liu - Sun Microsystems 		break;
451942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	default:
452042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		break;
452142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
452242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
452342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	return (wbeep);
452442c41cf8Slipeng sang - Sun Microsystems - Beijing China }       /* audiohd_find_beep() */
452542c41cf8Slipeng sang - Sun Microsystems - Beijing China 
452642c41cf8Slipeng sang - Sun Microsystems - Beijing China /*
452742c41cf8Slipeng sang - Sun Microsystems - Beijing China  * audiohd_build_beep_path()
452842c41cf8Slipeng sang - Sun Microsystems - Beijing China  *
452942c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Description:
453042c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      Search an beep path for each pin in the codec.
453142c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Arguments:
453242c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      hda_codec_t     *codec          where the beep path exists
453342c41cf8Slipeng sang - Sun Microsystems - Beijing China  */
453442c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_build_beep_path(hda_codec_t * codec)453542c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_build_beep_path(hda_codec_t *codec)
453642c41cf8Slipeng sang - Sun Microsystems - Beijing China {
453742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_pin_t		*pin;
453842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_widget_t	*widget;
453942c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_path_t		*path;
454042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	wid_t			wid;
454142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_state_t		*statep;
454242c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int			i;
454342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	boolean_t		beeppath = B_FALSE;
454442c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4545ea463888SZhao Edgar Liu - Sun Microsystems 	statep = codec->statep;
454642c41cf8Slipeng sang - Sun Microsystems - Beijing China 
454742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	for (pin = codec->first_pin; pin; pin = pin->next) {
454842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if ((pin->cap & AUDIOHD_PIN_CAP_MASK) == 0)
454942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
455042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if ((pin->config & AUDIOHD_PIN_CONF_MASK) ==
455142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    AUDIOHD_PIN_NO_CONN)
455242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
455342c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if ((pin->device != DTYPE_LINEOUT) &&
455442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    (pin->device != DTYPE_SPEAKER) &&
455542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    (pin->device != DTYPE_SPDIF_OUT) &&
455642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    (pin->device != DTYPE_HP_OUT))
455742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
455842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		widget = codec->widget[pin->wid];
455942c41cf8Slipeng sang - Sun Microsystems - Beijing China 
456042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		widget->inamp_cap = 0;
456142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		for (i = 0; i < widget->nconns; i++) {
456242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			/*
456342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * If a beep found, the return value is the wid of the
456442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * widget on the path, or the return value is
456542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * DDI_FAILURE
456642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 */
456742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wid = audiohd_find_beep(codec,
456842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    widget->avail_conn[i], 0);
456942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			/*
457042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 * A beep was not found
457142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			 */
457242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			if (wid == (wid_t)DDI_FAILURE)
457342c41cf8Slipeng sang - Sun Microsystems - Beijing China 				continue;
4574b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (widget->output_path_next != AUDIOHD_NULL_CONN)
457542c41cf8Slipeng sang - Sun Microsystems - Beijing China 				continue;
457642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path = (audiohd_path_t *)
457742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    kmem_zalloc(sizeof (audiohd_path_t),
457842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    KM_SLEEP);
457942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->beep_wid = wid;
458042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->pin_wid[0] = widget->wid_wid;
458142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->pin_nums = 1;
458242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->path_type = BEEP;
458342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			beeppath = 1;
458442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->codec = codec;
458542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->statep = statep;
458642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			widget->path_flags |= AUDIOHD_PATH_BEEP;
4587b96a6eceSZhao Edgar Liu - Sun Microsystems 			widget->beep_path_next = i;
458842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			statep->path[statep->pathnum++] = path;
458942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			break;
459042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
459142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
459242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
459342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	if (!beeppath) {
459442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		for (int i = 0; i < AUDIOHD_CODEC_MAX; i++) {
459542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			codec = statep->codec[i];
4596b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (codec == NULL)
459742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				continue;
459842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			for (wid = codec->first_wid; wid <= codec->last_wid;
459942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    wid++) {
460042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				widget = codec->widget[wid];
4601ee97b734SZhao Edgar Liu - Sun Microsystems 
460242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->type == WTYPE_BEEP) {
460342c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path = (audiohd_path_t *)
460442c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    kmem_zalloc(sizeof (audiohd_path_t),
460542c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    KM_SLEEP);
460642c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->beep_wid = wid;
460742c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->pin_nums = 0;
460842c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->path_type = BEEP;
460942c41cf8Slipeng sang - Sun Microsystems - Beijing China 					beeppath = 1;
461042c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->codec = codec;
461142c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->statep = statep;
461242c41cf8Slipeng sang - Sun Microsystems - Beijing China 					widget->path_flags |= AUDIOHD_PATH_BEEP;
461342c41cf8Slipeng sang - Sun Microsystems - Beijing China 					statep->path[statep->pathnum++] = path;
461442c41cf8Slipeng sang - Sun Microsystems - Beijing China 					break;
461542c41cf8Slipeng sang - Sun Microsystems - Beijing China 				}
461642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
461742c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
461842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
461942c41cf8Slipeng sang - Sun Microsystems - Beijing China }       /* audiohd_build_beep_path() */
462042c41cf8Slipeng sang - Sun Microsystems - Beijing China 
462142c41cf8Slipeng sang - Sun Microsystems - Beijing China /*
462242c41cf8Slipeng sang - Sun Microsystems - Beijing China  * audiohd_build_beep_amp
462342c41cf8Slipeng sang - Sun Microsystems - Beijing China  *
462442c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Description:
462542c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      Find the gain control and mute control widget
462642c41cf8Slipeng sang - Sun Microsystems - Beijing China  */
462742c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_build_beep_amp(hda_codec_t * codec)462842c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_build_beep_amp(hda_codec_t *codec)
462942c41cf8Slipeng sang - Sun Microsystems - Beijing China {
463042c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_path_t		*path;
463142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_widget_t	*widget, *wpin, *wbeep;
4632b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
463342c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int			i, j;
463442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint32_t		gain;
463542c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4636ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
4637ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
463842c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if (path == NULL || path->path_type != BEEP ||
463942c41cf8Slipeng sang - Sun Microsystems - Beijing China 		    path->codec != codec)
464042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
464142c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if (path->pin_nums == 0) {
464242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->mute_wid = path->beep_wid;
464342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->mute_dir = AUDIOHDC_AMP_SET_OUTPUT;
464442c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wbeep = codec->widget[path->beep_wid];
464542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			gain = (wbeep->outamp_cap &
464642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			    AUDIOHDC_AMP_CAP_STEP_NUMS);
464742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			if (gain) {
464842c41cf8Slipeng sang - Sun Microsystems - Beijing China 				path->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
464942c41cf8Slipeng sang - Sun Microsystems - Beijing China 				path->gain_bits = gain;
465042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				path->gain_wid = path->beep_wid;
465142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
465242c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->gain_bits >>= AUDIOHD_GAIN_OFF;
465342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			break;
465442c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
465542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		for (j = 0; j < path->pin_nums; j++) {
465642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wid = path->pin_wid[j];
465742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wpin = codec->widget[wid];
465842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wbeep = codec->widget[path->beep_wid];
465942c41cf8Slipeng sang - Sun Microsystems - Beijing China 
466042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			widget = wpin;
466142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			while (widget) {
466242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->out_weight == 0 &&
466342c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    widget->outamp_cap &
466442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
466542c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->mute_wid = widget->wid_wid;
466642c41cf8Slipeng sang - Sun Microsystems - Beijing China 					path->mute_dir =
466742c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_AMP_SET_OUTPUT;
466842c41cf8Slipeng sang - Sun Microsystems - Beijing China 					break;
466942c41cf8Slipeng sang - Sun Microsystems - Beijing China 				}
4670b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->beep_path_next;
4671b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
467242c41cf8Slipeng sang - Sun Microsystems - Beijing China 					break;
4673b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
467442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				widget = codec->widget[wid];
467542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
467642c41cf8Slipeng sang - Sun Microsystems - Beijing China 
467742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			gain = 0;
467842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			widget = wpin;
467942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			while (widget) {
468042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->out_weight == 0 &&
468142c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    widget->outamp_cap &
468242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    AUDIOHDC_AMP_CAP_STEP_NUMS) {
468342c41cf8Slipeng sang - Sun Microsystems - Beijing China 					gain = (widget->outamp_cap &
468442c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_AMP_CAP_STEP_NUMS);
468542c41cf8Slipeng sang - Sun Microsystems - Beijing China 					if (gain && gain > path->gain_bits) {
468642c41cf8Slipeng sang - Sun Microsystems - Beijing China 						path->gain_dir =
468742c41cf8Slipeng sang - Sun Microsystems - Beijing China 						    AUDIOHDC_AMP_SET_OUTPUT;
468842c41cf8Slipeng sang - Sun Microsystems - Beijing China 						path->gain_bits = gain;
468942c41cf8Slipeng sang - Sun Microsystems - Beijing China 						path->gain_wid =
469042c41cf8Slipeng sang - Sun Microsystems - Beijing China 						    widget->wid_wid;
469142c41cf8Slipeng sang - Sun Microsystems - Beijing China 					}
469242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				}
4693b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->beep_path_next;
4694b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
469542c41cf8Slipeng sang - Sun Microsystems - Beijing China 					break;
4696b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
469742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				widget = codec->widget[wid];
469842c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
469942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			path->gain_bits >>= AUDIOHD_GAIN_OFF;
470042c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
470142c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
470242c41cf8Slipeng sang - Sun Microsystems - Beijing China }       /* audiohd_build_beep_amp */
470342c41cf8Slipeng sang - Sun Microsystems - Beijing China 
470442c41cf8Slipeng sang - Sun Microsystems - Beijing China /*
470542c41cf8Slipeng sang - Sun Microsystems - Beijing China  * audiohd_finish_beep_path()
470642c41cf8Slipeng sang - Sun Microsystems - Beijing China  *
470742c41cf8Slipeng sang - Sun Microsystems - Beijing China  * Description:
470842c41cf8Slipeng sang - Sun Microsystems - Beijing China  *      Enable the widgets on the beep path
470942c41cf8Slipeng sang - Sun Microsystems - Beijing China  */
471042c41cf8Slipeng sang - Sun Microsystems - Beijing China static void
audiohd_finish_beep_path(hda_codec_t * codec)471142c41cf8Slipeng sang - Sun Microsystems - Beijing China audiohd_finish_beep_path(hda_codec_t *codec)
471242c41cf8Slipeng sang - Sun Microsystems - Beijing China {
4713ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
471442c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_path_t		*path;
471542c41cf8Slipeng sang - Sun Microsystems - Beijing China 	audiohd_widget_t	*widget;
471642c41cf8Slipeng sang - Sun Microsystems - Beijing China 	uint_t			caddr = codec->index;
4717b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
471842c41cf8Slipeng sang - Sun Microsystems - Beijing China 	int			i, j;
471942c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4720ea463888SZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < codec->statep->pathnum; i++) {
4721ea463888SZhao Edgar Liu - Sun Microsystems 		path = codec->statep->path[i];
472242c41cf8Slipeng sang - Sun Microsystems - Beijing China 		if (!path || path->path_type != BEEP || path->codec != codec)
472342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			continue;
4724ee97b734SZhao Edgar Liu - Sun Microsystems 		if (path->pin_nums == 0) {
4725ee97b734SZhao Edgar Liu - Sun Microsystems 			widget = codec->widget[path->beep_wid];
4726ee97b734SZhao Edgar Liu - Sun Microsystems 			if (widget->outamp_cap) {
4727ee97b734SZhao Edgar Liu - Sun Microsystems 				(void) audioha_codec_4bit_verb_get(
4728ee97b734SZhao Edgar Liu - Sun Microsystems 				    statep, caddr,
4729ee97b734SZhao Edgar Liu - Sun Microsystems 				    path->beep_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
4730ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
4731ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_GAIN_MAX);
4732ee97b734SZhao Edgar Liu - Sun Microsystems 			}
4733ee97b734SZhao Edgar Liu - Sun Microsystems 			if (widget->inamp_cap) {
4734ee97b734SZhao Edgar Liu - Sun Microsystems 				(void) audioha_codec_4bit_verb_get(
4735ee97b734SZhao Edgar Liu - Sun Microsystems 				    statep, caddr,
4736ee97b734SZhao Edgar Liu - Sun Microsystems 				    path->beep_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
4737ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_AMP_SET_LR_INPUT |
4738ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_GAIN_MAX |
4739b96a6eceSZhao Edgar Liu - Sun Microsystems 				    (widget->beep_path_next <<
4740ee97b734SZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_AMP_SET_INDEX_OFFSET));
4741ee97b734SZhao Edgar Liu - Sun Microsystems 			}
4742ee97b734SZhao Edgar Liu - Sun Microsystems 			continue;
4743ee97b734SZhao Edgar Liu - Sun Microsystems 		}
4744ee97b734SZhao Edgar Liu - Sun Microsystems 
474542c41cf8Slipeng sang - Sun Microsystems - Beijing China 		for (j = 0; j < path->pin_nums; j++) {
474642c41cf8Slipeng sang - Sun Microsystems - Beijing China 			wid = path->pin_wid[j];
474742c41cf8Slipeng sang - Sun Microsystems - Beijing China 			widget = codec->widget[wid];
474842c41cf8Slipeng sang - Sun Microsystems - Beijing China 
474942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			(void) audioha_codec_verb_get(statep, caddr, wid,
4750b96a6eceSZhao Edgar Liu - Sun Microsystems 			    AUDIOHDC_VERB_SET_CONN_SEL, widget->beep_path_next);
475142c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4752b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = widget->avail_conn[widget->beep_path_next];
475342c41cf8Slipeng sang - Sun Microsystems - Beijing China 			widget = codec->widget[wid];
475442c41cf8Slipeng sang - Sun Microsystems - Beijing China 
475542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			while (widget) {
475642c41cf8Slipeng sang - Sun Microsystems - Beijing China 				/*
475742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				 * Set all amplifiers in this path to
4758b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * the maximum volume and unmute them.
475942c41cf8Slipeng sang - Sun Microsystems - Beijing China 				 */
476042c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->out_weight != 0)
476142c41cf8Slipeng sang - Sun Microsystems - Beijing China 					continue;
476242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->outamp_cap) {
476342c41cf8Slipeng sang - Sun Microsystems - Beijing China 					(void) audioha_codec_4bit_verb_get(
4764b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
476542c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
476642c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_AMP_SET_LR_OUTPUT |
476742c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_GAIN_MAX);
476842c41cf8Slipeng sang - Sun Microsystems - Beijing China 				}
476942c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if (widget->inamp_cap) {
477042c41cf8Slipeng sang - Sun Microsystems - Beijing China 					(void) audioha_codec_4bit_verb_get(
4771b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
477242c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
477342c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_AMP_SET_LR_INPUT |
477442c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_GAIN_MAX |
4775b96a6eceSZhao Edgar Liu - Sun Microsystems 					    (widget->beep_path_next <<
477642c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_AMP_SET_INDEX_OFFSET));
477742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				}
477842c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4779b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->beep_path_next;
4780b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
478142c41cf8Slipeng sang - Sun Microsystems - Beijing China 					break;
478242c41cf8Slipeng sang - Sun Microsystems - Beijing China 				/*
478342c41cf8Slipeng sang - Sun Microsystems - Beijing China 				 * Accoding to HD spec, mixer doesn't support
478442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				 * "select connection"
478542c41cf8Slipeng sang - Sun Microsystems - Beijing China 				 */
478642c41cf8Slipeng sang - Sun Microsystems - Beijing China 				if ((widget->type != WTYPE_AUDIO_MIX) &&
478742c41cf8Slipeng sang - Sun Microsystems - Beijing China 				    (widget->nconns > 1))
478842c41cf8Slipeng sang - Sun Microsystems - Beijing China 					(void) audioha_codec_verb_get(statep,
4789b96a6eceSZhao Edgar Liu - Sun Microsystems 					    caddr, wid,
479042c41cf8Slipeng sang - Sun Microsystems - Beijing China 					    AUDIOHDC_VERB_SET_CONN_SEL,
4791b96a6eceSZhao Edgar Liu - Sun Microsystems 					    widget->beep_path_next);
479242c41cf8Slipeng sang - Sun Microsystems - Beijing China 
4793b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
479442c41cf8Slipeng sang - Sun Microsystems - Beijing China 				widget = codec->widget[wid];
479542c41cf8Slipeng sang - Sun Microsystems - Beijing China 			}
479642c41cf8Slipeng sang - Sun Microsystems - Beijing China 		}
479742c41cf8Slipeng sang - Sun Microsystems - Beijing China 	}
479842c41cf8Slipeng sang - Sun Microsystems - Beijing China }       /* audiohd_finish_beep_path */
479988447a05SGarrett D'Amore 
4800e7236f70SZhao Edgar Liu - Sun Microsystems static int
audiohd_find_output_pins(hda_codec_t * codec,wid_t wid,int depth,audiohd_path_t * path)4801e7236f70SZhao Edgar Liu - Sun Microsystems audiohd_find_output_pins(hda_codec_t *codec, wid_t wid, int depth,
4802e7236f70SZhao Edgar Liu - Sun Microsystems     audiohd_path_t *path)
4803e7236f70SZhao Edgar Liu - Sun Microsystems {
4804e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget = codec->widget[wid];
4805e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_pin_t		*pin = NULL;
4806e7236f70SZhao Edgar Liu - Sun Microsystems 	int			num, retval = (DDI_FAILURE);
4807e7236f70SZhao Edgar Liu - Sun Microsystems 
4808e7236f70SZhao Edgar Liu - Sun Microsystems 	if (depth > AUDIOHD_MAX_DEPTH)
4809e7236f70SZhao Edgar Liu - Sun Microsystems 		return (retval);
4810e7236f70SZhao Edgar Liu - Sun Microsystems 	if (widget == NULL)
4811e7236f70SZhao Edgar Liu - Sun Microsystems 		return (retval);
4812e7236f70SZhao Edgar Liu - Sun Microsystems 
4813e7236f70SZhao Edgar Liu - Sun Microsystems 	switch (widget->type) {
4814e7236f70SZhao Edgar Liu - Sun Microsystems 	case WTYPE_PIN:
4815e7236f70SZhao Edgar Liu - Sun Microsystems 		pin = (audiohd_pin_t *)widget->priv;
4816e7236f70SZhao Edgar Liu - Sun Microsystems 		if (pin->no_phys_conn)
4817e7236f70SZhao Edgar Liu - Sun Microsystems 			return (DDI_FAILURE);
4818e7236f70SZhao Edgar Liu - Sun Microsystems 
4819e7236f70SZhao Edgar Liu - Sun Microsystems 		switch (pin->device) {
4820e7236f70SZhao Edgar Liu - Sun Microsystems 		case DTYPE_LINE_IN:
4821e7236f70SZhao Edgar Liu - Sun Microsystems 			/* Connection between line-in and output pins */
4822e7236f70SZhao Edgar Liu - Sun Microsystems 			path->pin_wid[path->pin_nums++] = wid;
4823e7236f70SZhao Edgar Liu - Sun Microsystems 			break;
4824e7236f70SZhao Edgar Liu - Sun Microsystems 		case DTYPE_LINEOUT:
4825e7236f70SZhao Edgar Liu - Sun Microsystems 		case DTYPE_HP_OUT:
4826e7236f70SZhao Edgar Liu - Sun Microsystems 		case DTYPE_SPDIF_OUT:
4827e7236f70SZhao Edgar Liu - Sun Microsystems 			widget->path_flags |= AUDIOHD_PATH_LOOPBACK;
4828e7236f70SZhao Edgar Liu - Sun Microsystems 			widget->in_weight++;
4829e7236f70SZhao Edgar Liu - Sun Microsystems 			pin->adc_wid = path->adda_wid;
4830e7236f70SZhao Edgar Liu - Sun Microsystems 			path->pin_wid[path->pin_nums++] = wid;
4831e7236f70SZhao Edgar Liu - Sun Microsystems 			retval = (DDI_SUCCESS);
4832e7236f70SZhao Edgar Liu - Sun Microsystems 			break;
4833e7236f70SZhao Edgar Liu - Sun Microsystems 		default:
4834e7236f70SZhao Edgar Liu - Sun Microsystems 			break;
4835e7236f70SZhao Edgar Liu - Sun Microsystems 		}
4836e7236f70SZhao Edgar Liu - Sun Microsystems 		break;
4837e7236f70SZhao Edgar Liu - Sun Microsystems 	case WTYPE_AUDIO_MIX:
4838e7236f70SZhao Edgar Liu - Sun Microsystems 	case WTYPE_AUDIO_SEL:
4839e7236f70SZhao Edgar Liu - Sun Microsystems 		/*
4840e7236f70SZhao Edgar Liu - Sun Microsystems 		 * If the sum widget has only one input, we don't
4841e7236f70SZhao Edgar Liu - Sun Microsystems 		 * consider it as a real sum widget.
4842e7236f70SZhao Edgar Liu - Sun Microsystems 		 */
4843e7236f70SZhao Edgar Liu - Sun Microsystems 		if (widget->nconns == 1) {
4844e7236f70SZhao Edgar Liu - Sun Microsystems 			widget->loopback_path_next = 0;
4845e7236f70SZhao Edgar Liu - Sun Microsystems 			retval = audiohd_find_output_pins(codec,
4846e7236f70SZhao Edgar Liu - Sun Microsystems 			    widget->avail_conn[0], depth + 1, path);
4847e7236f70SZhao Edgar Liu - Sun Microsystems 			if (retval == (DDI_SUCCESS)) {
4848e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->path_flags |= AUDIOHD_PATH_LOOPBACK;
4849e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->in_weight++;
4850e7236f70SZhao Edgar Liu - Sun Microsystems 			}
4851e7236f70SZhao Edgar Liu - Sun Microsystems 			break;
4852e7236f70SZhao Edgar Liu - Sun Microsystems 		}
4853e7236f70SZhao Edgar Liu - Sun Microsystems 
4854e7236f70SZhao Edgar Liu - Sun Microsystems 		for (int i = 0; i < widget->nconns; i++) {
4855e7236f70SZhao Edgar Liu - Sun Microsystems 			retval = audiohd_find_output_pins(codec,
4856e7236f70SZhao Edgar Liu - Sun Microsystems 			    widget->avail_conn[i], depth + 1, path);
4857e7236f70SZhao Edgar Liu - Sun Microsystems 			if (retval == (DDI_SUCCESS)) {
4858e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->loopback_path_next = i;
4859e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->in_weight++;
4860e7236f70SZhao Edgar Liu - Sun Microsystems 				num = path->pin_nums - 1;
4861e7236f70SZhao Edgar Liu - Sun Microsystems 				path->sum_selconn[num] = i;
4862e7236f70SZhao Edgar Liu - Sun Microsystems 				path->sum_wid = wid;
4863e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->path_flags |= AUDIOHD_PATH_LOOPBACK;
4864e7236f70SZhao Edgar Liu - Sun Microsystems 				break;
4865e7236f70SZhao Edgar Liu - Sun Microsystems 			}
4866e7236f70SZhao Edgar Liu - Sun Microsystems 		}
4867e7236f70SZhao Edgar Liu - Sun Microsystems 		break;
4868e7236f70SZhao Edgar Liu - Sun Microsystems 	default:
4869e7236f70SZhao Edgar Liu - Sun Microsystems 		break;
4870e7236f70SZhao Edgar Liu - Sun Microsystems 	}
4871e7236f70SZhao Edgar Liu - Sun Microsystems 
4872e7236f70SZhao Edgar Liu - Sun Microsystems 	return (retval);
4873e7236f70SZhao Edgar Liu - Sun Microsystems }
4874e7236f70SZhao Edgar Liu - Sun Microsystems 
4875e7236f70SZhao Edgar Liu - Sun Microsystems static void
audiohd_build_loopback_path(hda_codec_t * codec)4876e7236f70SZhao Edgar Liu - Sun Microsystems audiohd_build_loopback_path(hda_codec_t *codec)
4877e7236f70SZhao Edgar Liu - Sun Microsystems {
4878e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
4879e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget;
4880e7236f70SZhao Edgar Liu - Sun Microsystems 	audiohd_path_t		*path = NULL;
4881e7236f70SZhao Edgar Liu - Sun Microsystems 	wid_t			wid;
4882e7236f70SZhao Edgar Liu - Sun Microsystems 	int			i, retval;
4883e7236f70SZhao Edgar Liu - Sun Microsystems 	uint8_t			rtag = 0;
4884e7236f70SZhao Edgar Liu - Sun Microsystems 
4885e7236f70SZhao Edgar Liu - Sun Microsystems 	for (wid = codec->first_wid; wid <= codec->last_wid; wid++) {
4886e7236f70SZhao Edgar Liu - Sun Microsystems 		widget = codec->widget[wid];
4887e7236f70SZhao Edgar Liu - Sun Microsystems 
4888e7236f70SZhao Edgar Liu - Sun Microsystems 		/* check if it is an ADC widget */
4889e7236f70SZhao Edgar Liu - Sun Microsystems 		if (widget == NULL || widget->type != WTYPE_AUDIO_IN)
4890e7236f70SZhao Edgar Liu - Sun Microsystems 			continue;
4891e7236f70SZhao Edgar Liu - Sun Microsystems 
4892e7236f70SZhao Edgar Liu - Sun Microsystems 		if (path == NULL)
4893e7236f70SZhao Edgar Liu - Sun Microsystems 			path = kmem_zalloc(sizeof (audiohd_path_t), KM_SLEEP);
4894e7236f70SZhao Edgar Liu - Sun Microsystems 		else
4895e7236f70SZhao Edgar Liu - Sun Microsystems 			bzero(path, sizeof (audiohd_port_t));
4896e7236f70SZhao Edgar Liu - Sun Microsystems 		path->adda_wid = wid;
4897e7236f70SZhao Edgar Liu - Sun Microsystems 
4898e7236f70SZhao Edgar Liu - Sun Microsystems 		for (i = 0; i < widget->nconns; i++) {
4899e7236f70SZhao Edgar Liu - Sun Microsystems 			retval = audiohd_find_output_pins(codec,
4900e7236f70SZhao Edgar Liu - Sun Microsystems 			    widget->avail_conn[i], 0, path);
4901e7236f70SZhao Edgar Liu - Sun Microsystems 			if (retval == (DDI_SUCCESS)) {
4902e7236f70SZhao Edgar Liu - Sun Microsystems 				path->codec = codec;
4903e7236f70SZhao Edgar Liu - Sun Microsystems 				path->statep = statep;
4904e7236f70SZhao Edgar Liu - Sun Microsystems 				path->path_type = LOOPBACK;
4905e7236f70SZhao Edgar Liu - Sun Microsystems 				path->tag = ++rtag;
4906e7236f70SZhao Edgar Liu - Sun Microsystems 				codec->nistream++;
4907e7236f70SZhao Edgar Liu - Sun Microsystems 				statep->path[statep->pathnum++] = path;
4908e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->loopback_path_next = i;
4909e7236f70SZhao Edgar Liu - Sun Microsystems 				widget->priv = path;
4910e7236f70SZhao Edgar Liu - Sun Microsystems 				path = NULL;
4911e7236f70SZhao Edgar Liu - Sun Microsystems 				statep->loopback_supported = B_TRUE;
4912e7236f70SZhao Edgar Liu - Sun Microsystems 				break;
4913e7236f70SZhao Edgar Liu - Sun Microsystems 			}
4914e7236f70SZhao Edgar Liu - Sun Microsystems 		}
4915e7236f70SZhao Edgar Liu - Sun Microsystems 	}
4916e7236f70SZhao Edgar Liu - Sun Microsystems 
4917e7236f70SZhao Edgar Liu - Sun Microsystems 
4918e7236f70SZhao Edgar Liu - Sun Microsystems 	if (path)
4919e7236f70SZhao Edgar Liu - Sun Microsystems 		kmem_free(path, sizeof (audiohd_path_t));
4920e7236f70SZhao Edgar Liu - Sun Microsystems }	/* audiohd_build_loopback_path() */
4921e7236f70SZhao Edgar Liu - Sun Microsystems 
492288447a05SGarrett D'Amore /*
492388447a05SGarrett D'Amore  * audiohd_build_path()
492488447a05SGarrett D'Amore  *
492588447a05SGarrett D'Amore  * Description:
492688447a05SGarrett D'Amore  *	Here we build the output, input, monitor path.
492788447a05SGarrett D'Amore  *	And also enable the path in default.
492888447a05SGarrett D'Amore  *	Search for the gain and mute control for the path
492988447a05SGarrett D'Amore  */
493088447a05SGarrett D'Amore static void
audiohd_build_path(audiohd_state_t * statep)493188447a05SGarrett D'Amore audiohd_build_path(audiohd_state_t *statep)
493288447a05SGarrett D'Amore {
493388447a05SGarrett D'Amore 	int		i;
493488447a05SGarrett D'Amore 
493588447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
493688447a05SGarrett D'Amore 		if (statep->codec[i]) {
493788447a05SGarrett D'Amore 			audiohd_build_output_path(statep->codec[i]);
493888447a05SGarrett D'Amore 			audiohd_build_output_amp(statep->codec[i]);
493988447a05SGarrett D'Amore 			audiohd_finish_output_path(statep->codec[i]);
494088447a05SGarrett D'Amore 
494188447a05SGarrett D'Amore 			audiohd_build_input_path(statep->codec[i]);
494288447a05SGarrett D'Amore 			audiohd_build_input_amp(statep->codec[i]);
494388447a05SGarrett D'Amore 			audiohd_finish_input_path(statep->codec[i]);
494488447a05SGarrett D'Amore 
494588447a05SGarrett D'Amore 			audiohd_build_monitor_path(statep->codec[i]);
494688447a05SGarrett D'Amore 			audiohd_build_monitor_amp(statep->codec[i]);
494788447a05SGarrett D'Amore 			audiohd_finish_monitor_path(statep->codec[i]);
494842c41cf8Slipeng sang - Sun Microsystems - Beijing China 
494942c41cf8Slipeng sang - Sun Microsystems - Beijing China 			audiohd_build_beep_path(statep->codec[i]);
495042c41cf8Slipeng sang - Sun Microsystems - Beijing China 			audiohd_build_beep_amp(statep->codec[i]);
495142c41cf8Slipeng sang - Sun Microsystems - Beijing China 			audiohd_finish_beep_path(statep->codec[i]);
4952e7236f70SZhao Edgar Liu - Sun Microsystems 
4953e7236f70SZhao Edgar Liu - Sun Microsystems 			audiohd_build_loopback_path(statep->codec[i]);
495488447a05SGarrett D'Amore 		}
495588447a05SGarrett D'Amore 	}
495688447a05SGarrett D'Amore }	/* audiohd_build_path */
495788447a05SGarrett D'Amore 
495888447a05SGarrett D'Amore /*
495988447a05SGarrett D'Amore  * audiohd_allocate_port()
496088447a05SGarrett D'Amore  */
496188447a05SGarrett D'Amore static int
audiohd_allocate_port(audiohd_state_t * statep)496288447a05SGarrett D'Amore audiohd_allocate_port(audiohd_state_t *statep)
496388447a05SGarrett D'Amore {
496488447a05SGarrett D'Amore 	int			i, j;
496588447a05SGarrett D'Amore 	audiohd_port_t		*port;
496688447a05SGarrett D'Amore 	int			dir;
496788447a05SGarrett D'Amore 	unsigned		caps;
496888447a05SGarrett D'Amore 	int			rc;
496988447a05SGarrett D'Amore 	audio_dev_t		*adev;
497088447a05SGarrett D'Amore 	dev_info_t		*dip;
497188447a05SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
497288447a05SGarrett D'Amore 	uint_t			count;
497388447a05SGarrett D'Amore 	uint64_t		buf_phys_addr;
497488447a05SGarrett D'Amore 	sd_bdle_t		*entry;
497588447a05SGarrett D'Amore 	uint16_t		gcap;
497688447a05SGarrett D'Amore 	size_t			real_size;
497788447a05SGarrett D'Amore 
497888447a05SGarrett D'Amore 	adev = statep->adev;
497988447a05SGarrett D'Amore 	dip = statep->hda_dip;
498088447a05SGarrett D'Amore 
498188447a05SGarrett D'Amore 	ddi_dma_attr_t	dma_attr = {
498288447a05SGarrett D'Amore 		DMA_ATTR_V0,		/* version */
498388447a05SGarrett D'Amore 		0,			/* addr_lo */
498488447a05SGarrett D'Amore 		0xffffffffffffffffULL,	/* addr_hi */
498588447a05SGarrett D'Amore 		0x00000000ffffffffULL,	/* count_max */
498688447a05SGarrett D'Amore 		128,			/* 128-byte alignment as HD spec */
498788447a05SGarrett D'Amore 		0xfff,			/* burstsize */
498888447a05SGarrett D'Amore 		1,			/* minxfer */
498988447a05SGarrett D'Amore 		0xffffffff,		/* maxxfer */
499088447a05SGarrett D'Amore 		0xffffffff,		/* seg */
499188447a05SGarrett D'Amore 		1,			/* sgllen */
499288447a05SGarrett D'Amore 		1,			/* granular */
499388447a05SGarrett D'Amore 		0			/* flags */
499488447a05SGarrett D'Amore 	};
499588447a05SGarrett D'Amore 
499688447a05SGarrett D'Amore 	gcap = AUDIOHD_REG_GET16(AUDIOHD_REG_GCAP);
499788447a05SGarrett D'Amore 	if ((gcap & AUDIOHDR_GCAP_64OK) == 0)
499888447a05SGarrett D'Amore 		dma_attr.dma_attr_addr_hi = 0xffffffffUL;
499988447a05SGarrett D'Amore 
500088447a05SGarrett D'Amore 	for (i = 0; i < PORT_MAX; i++) {
500188447a05SGarrett D'Amore 		port = kmem_zalloc(sizeof (*port), KM_SLEEP);
500288447a05SGarrett D'Amore 		statep->port[i] = port;
500388447a05SGarrett D'Amore 		port->statep = statep;
500488447a05SGarrett D'Amore 		switch (i) {
500588447a05SGarrett D'Amore 		case PORT_ADC:
500688447a05SGarrett D'Amore 			dir = DDI_DMA_READ | DDI_DMA_CONSISTENT;
500788447a05SGarrett D'Amore 			caps = ENGINE_INPUT_CAP;
500888447a05SGarrett D'Amore 			port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
500988447a05SGarrett D'Amore 			port->nchan = statep->rchan;
501088447a05SGarrett D'Amore 			port->index = 1;
501188447a05SGarrett D'Amore 			port->regoff = AUDIOHD_REG_SD_BASE;
501288447a05SGarrett D'Amore 			break;
501388447a05SGarrett D'Amore 		case PORT_DAC:
501488447a05SGarrett D'Amore 			dir = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
501588447a05SGarrett D'Amore 			caps = ENGINE_OUTPUT_CAP;
501688447a05SGarrett D'Amore 			port->sync_dir = DDI_DMA_SYNC_FORDEV;
501788447a05SGarrett D'Amore 			port->nchan = statep->pchan;
501888447a05SGarrett D'Amore 			port->index = statep->hda_input_streams + 1;
501988447a05SGarrett D'Amore 			port->regoff = AUDIOHD_REG_SD_BASE +
502088447a05SGarrett D'Amore 			    AUDIOHD_REG_SD_LEN *
502188447a05SGarrett D'Amore 			    statep->hda_input_streams;
502288447a05SGarrett D'Amore 			break;
502388447a05SGarrett D'Amore 		default:
502488447a05SGarrett D'Amore 			return (DDI_FAILURE);
502588447a05SGarrett D'Amore 		}
502688447a05SGarrett D'Amore 
5027a33ad26eSZhao Edgar Liu - Sun Microsystems 		switch (statep->sample_rate) {
5028a33ad26eSZhao Edgar Liu - Sun Microsystems 		case 192000:
5029a33ad26eSZhao Edgar Liu - Sun Microsystems 			port->format = 0x18 << 4;
5030a33ad26eSZhao Edgar Liu - Sun Microsystems 			break;
5031a33ad26eSZhao Edgar Liu - Sun Microsystems 		case 96000:
5032a33ad26eSZhao Edgar Liu - Sun Microsystems 			port->format = 0x08 << 4;
5033a33ad26eSZhao Edgar Liu - Sun Microsystems 			break;
5034a33ad26eSZhao Edgar Liu - Sun Microsystems 		case 48000:
5035a33ad26eSZhao Edgar Liu - Sun Microsystems 		default: /* 48kHz is default */
5036a33ad26eSZhao Edgar Liu - Sun Microsystems 			port->format = 0x00;
5037a33ad26eSZhao Edgar Liu - Sun Microsystems 			break;
5038a33ad26eSZhao Edgar Liu - Sun Microsystems 		}
5039a33ad26eSZhao Edgar Liu - Sun Microsystems 
5040a33ad26eSZhao Edgar Liu - Sun Microsystems 		switch (statep->sample_bit_depth) {
5041a33ad26eSZhao Edgar Liu - Sun Microsystems 		case AUDIOHD_BIT_DEPTH24:
5042a33ad26eSZhao Edgar Liu - Sun Microsystems 			port->format |= 0x3;
5043a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_packed_bytes = 4;
5044a33ad26eSZhao Edgar Liu - Sun Microsystems 			break;
5045a33ad26eSZhao Edgar Liu - Sun Microsystems 		case AUDIOHD_BIT_DEPTH16:
5046a33ad26eSZhao Edgar Liu - Sun Microsystems 		default: /* 16 bits is default */
5047a33ad26eSZhao Edgar Liu - Sun Microsystems 			port->format |= 0x1;
5048a33ad26eSZhao Edgar Liu - Sun Microsystems 			statep->sample_packed_bytes = 2;
5049a33ad26eSZhao Edgar Liu - Sun Microsystems 			break;
5050a33ad26eSZhao Edgar Liu - Sun Microsystems 		}
5051a33ad26eSZhao Edgar Liu - Sun Microsystems 
5052a33ad26eSZhao Edgar Liu - Sun Microsystems 		port->nframes = 1024 * AUDIOHD_BDLE_NUMS *
5053a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->sample_rate / 48000;
5054a33ad26eSZhao Edgar Liu - Sun Microsystems 		port->fragsize = 1024 * port->nchan *
5055a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->sample_packed_bytes *
5056a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->sample_rate / 48000;
5057a33ad26eSZhao Edgar Liu - Sun Microsystems 		port->bufsize = port->nframes * port->nchan *
5058a33ad26eSZhao Edgar Liu - Sun Microsystems 		    statep->sample_packed_bytes;
505988447a05SGarrett D'Amore 
506088447a05SGarrett D'Amore 		/* allocate dma handle */
506188447a05SGarrett D'Amore 		rc = ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_SLEEP,
506288447a05SGarrett D'Amore 		    NULL, &port->samp_dmah);
506388447a05SGarrett D'Amore 		if (rc != DDI_SUCCESS) {
506488447a05SGarrett D'Amore 			audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d",
506588447a05SGarrett D'Amore 			    rc);
5066c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
506788447a05SGarrett D'Amore 		}
50680c240c64SZhao Edgar Liu - Sun Microsystems 
5069c0e48486SYang-Rong Jerry Zhou 		/*
5070c0e48486SYang-Rong Jerry Zhou 		 * Warning: please be noted that allocating the dma memory
5071c0e48486SYang-Rong Jerry Zhou 		 * with the flag IOMEM_DATA_UNCACHED is a hack due
5072c0e48486SYang-Rong Jerry Zhou 		 * to an incorrect cache synchronization on NVidia MCP79
5073c0e48486SYang-Rong Jerry Zhou 		 * chipset which causes the audio distortion problem,
5074c0e48486SYang-Rong Jerry Zhou 		 * and that it should be fixed later. There should be
5075c0e48486SYang-Rong Jerry Zhou 		 * no reason you have to allocate UNCACHED memory. In
5076c0e48486SYang-Rong Jerry Zhou 		 * complex architectures with nested IO caches,
5077c0e48486SYang-Rong Jerry Zhou 		 * reliance on this flag might lead to failure.
5078c0e48486SYang-Rong Jerry Zhou 		 */
507968c47f65SGarrett D'Amore 		rc = ddi_dma_mem_alloc(port->samp_dmah, port->bufsize,
508068c47f65SGarrett D'Amore 		    &hda_dev_accattr, DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
508188447a05SGarrett D'Amore 		    DDI_DMA_SLEEP, NULL, &port->samp_kaddr,
508288447a05SGarrett D'Amore 		    &real_size, &port->samp_acch);
508388447a05SGarrett D'Amore 		if (rc == DDI_FAILURE) {
508468c47f65SGarrett D'Amore 			if (ddi_dma_mem_alloc(port->samp_dmah, port->bufsize,
508568c47f65SGarrett D'Amore 			    &hda_dev_accattr, DDI_DMA_CONSISTENT,
5086c0e48486SYang-Rong Jerry Zhou 			    DDI_DMA_SLEEP, NULL,
5087c0e48486SYang-Rong Jerry Zhou 			    &port->samp_kaddr, &real_size,
5088c0e48486SYang-Rong Jerry Zhou 			    &port->samp_acch) != DDI_SUCCESS) {
5089c0e48486SYang-Rong Jerry Zhou 				audio_dev_warn(adev,
5090c0e48486SYang-Rong Jerry Zhou 				    "ddi_dma_mem_alloc failed");
5091c6e681c0SYang-Rong Jerry Zhou 				return (DDI_FAILURE);
509288447a05SGarrett D'Amore 			}
5093c0e48486SYang-Rong Jerry Zhou 		}
509488447a05SGarrett D'Amore 
509588447a05SGarrett D'Amore 		/* bind DMA buffer */
509688447a05SGarrett D'Amore 		rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL,
509788447a05SGarrett D'Amore 		    port->samp_kaddr, real_size, dir,
509888447a05SGarrett D'Amore 		    DDI_DMA_SLEEP, NULL, &cookie, &count);
509988447a05SGarrett D'Amore 		if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
510088447a05SGarrett D'Amore 			audio_dev_warn(adev,
510188447a05SGarrett D'Amore 			    "ddi_dma_addr_bind_handle failed: %d", rc);
5102c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
510388447a05SGarrett D'Amore 		}
510488447a05SGarrett D'Amore 		port->samp_paddr = (uint64_t)cookie.dmac_laddress;
510588447a05SGarrett D'Amore 
510688447a05SGarrett D'Amore 		/*
510788447a05SGarrett D'Amore 		 * now, from here we allocate DMA
510888447a05SGarrett D'Amore 		 * memory for buffer descriptor list.
510988447a05SGarrett D'Amore 		 * we allocate adjacent DMA memory for all DMA engines.
511088447a05SGarrett D'Amore 		 */
511188447a05SGarrett D'Amore 		rc = ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_SLEEP,
511288447a05SGarrett D'Amore 		    NULL, &port->bdl_dmah);
511388447a05SGarrett D'Amore 		if (rc != DDI_SUCCESS) {
511488447a05SGarrett D'Amore 			audio_dev_warn(adev,
511588447a05SGarrett D'Amore 			    "ddi_dma_alloc_handle(bdlist) failed");
5116c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
511788447a05SGarrett D'Amore 		}
511888447a05SGarrett D'Amore 
511988447a05SGarrett D'Amore 		/*
512088447a05SGarrett D'Amore 		 * we allocate all buffer descriptors lists in continuous
512188447a05SGarrett D'Amore 		 * dma memory.
512288447a05SGarrett D'Amore 		 */
512388447a05SGarrett D'Amore 		port->bdl_size = sizeof (sd_bdle_t) * AUDIOHD_BDLE_NUMS;
512488447a05SGarrett D'Amore 		rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size,
512588447a05SGarrett D'Amore 		    &hda_dev_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
512688447a05SGarrett D'Amore 		    &port->bdl_kaddr, &real_size, &port->bdl_acch);
512788447a05SGarrett D'Amore 		if (rc != DDI_SUCCESS) {
512888447a05SGarrett D'Amore 			audio_dev_warn(adev,
512988447a05SGarrett D'Amore 			    "ddi_dma_mem_alloc(bdlist) failed");
5130c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
513188447a05SGarrett D'Amore 		}
513288447a05SGarrett D'Amore 
513388447a05SGarrett D'Amore 		rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL,
513488447a05SGarrett D'Amore 		    port->bdl_kaddr,
513588447a05SGarrett D'Amore 		    real_size, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
513688447a05SGarrett D'Amore 		    DDI_DMA_SLEEP,
513788447a05SGarrett D'Amore 		    NULL, &cookie, &count);
513888447a05SGarrett D'Amore 		if ((rc != DDI_DMA_MAPPED) || (count != 1)) {
513988447a05SGarrett D'Amore 			audio_dev_warn(adev, "addr_bind_handle failed");
5140c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
514188447a05SGarrett D'Amore 		}
514288447a05SGarrett D'Amore 		port->bdl_paddr = (uint64_t)cookie.dmac_laddress;
514388447a05SGarrett D'Amore 
514488447a05SGarrett D'Amore 		entry = (sd_bdle_t *)port->bdl_kaddr;
514588447a05SGarrett D'Amore 		buf_phys_addr = port->samp_paddr;
514688447a05SGarrett D'Amore 
514788447a05SGarrett D'Amore 		for (j = 0; j < AUDIOHD_BDLE_NUMS; j++) {
514888447a05SGarrett D'Amore 			entry->sbde_addr = buf_phys_addr;
514968c47f65SGarrett D'Amore 			entry->sbde_len = port->fragsize;
515088447a05SGarrett D'Amore 			entry->sbde_ioc = 1;
515168c47f65SGarrett D'Amore 			buf_phys_addr += port->fragsize;
515288447a05SGarrett D'Amore 			entry++;
515388447a05SGarrett D'Amore 		}
515488447a05SGarrett D'Amore 		(void) ddi_dma_sync(port->bdl_dmah, 0, sizeof (sd_bdle_t) *
515588447a05SGarrett D'Amore 		    AUDIOHD_BDLE_NUMS, DDI_DMA_SYNC_FORDEV);
515688447a05SGarrett D'Amore 		port->curpos = 0;
515788447a05SGarrett D'Amore 
515888447a05SGarrett D'Amore 		port->engine = audio_engine_alloc(&audiohd_engine_ops, caps);
515988447a05SGarrett D'Amore 		if (port->engine == NULL) {
5160c6e681c0SYang-Rong Jerry Zhou 			return (DDI_FAILURE);
516188447a05SGarrett D'Amore 		}
516288447a05SGarrett D'Amore 
516388447a05SGarrett D'Amore 		audio_engine_set_private(port->engine, port);
516488447a05SGarrett D'Amore 		audio_dev_add_engine(adev, port->engine);
516588447a05SGarrett D'Amore 	}
516688447a05SGarrett D'Amore 
516788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
516888447a05SGarrett D'Amore }
516988447a05SGarrett D'Amore 
517088447a05SGarrett D'Amore static void
audiohd_free_port(audiohd_state_t * statep)517188447a05SGarrett D'Amore audiohd_free_port(audiohd_state_t *statep)
517288447a05SGarrett D'Amore {
517388447a05SGarrett D'Amore 	int			i;
517488447a05SGarrett D'Amore 	audiohd_port_t		*port;
517588447a05SGarrett D'Amore 
517688447a05SGarrett D'Amore 	for (i = 0; i < PORT_MAX; i++) {
517788447a05SGarrett D'Amore 		port = statep->port[i];
517888447a05SGarrett D'Amore 		if (port == NULL)
517988447a05SGarrett D'Amore 			continue;
518088447a05SGarrett D'Amore 		if (port->engine) {
518188447a05SGarrett D'Amore 			audio_dev_remove_engine(statep->adev,
518288447a05SGarrett D'Amore 			    port->engine);
518388447a05SGarrett D'Amore 			audio_engine_free(port->engine);
518488447a05SGarrett D'Amore 		}
518588447a05SGarrett D'Amore 		if (port->samp_dmah) {
518688447a05SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->samp_dmah);
518788447a05SGarrett D'Amore 		}
518888447a05SGarrett D'Amore 		if (port->samp_acch) {
518988447a05SGarrett D'Amore 			ddi_dma_mem_free(&port->samp_acch);
519088447a05SGarrett D'Amore 		}
519188447a05SGarrett D'Amore 		if (port->samp_dmah) {
519288447a05SGarrett D'Amore 			ddi_dma_free_handle(&port->samp_dmah);
519388447a05SGarrett D'Amore 		}
519488447a05SGarrett D'Amore 		if (port->bdl_dmah) {
519588447a05SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->bdl_dmah);
519688447a05SGarrett D'Amore 		}
519788447a05SGarrett D'Amore 		if (port->bdl_acch) {
519888447a05SGarrett D'Amore 			ddi_dma_mem_free(&port->bdl_acch);
519988447a05SGarrett D'Amore 		}
520088447a05SGarrett D'Amore 		if (port->bdl_dmah) {
520188447a05SGarrett D'Amore 			ddi_dma_free_handle(&port->bdl_dmah);
520288447a05SGarrett D'Amore 		}
520388447a05SGarrett D'Amore 
520488447a05SGarrett D'Amore 		kmem_free(port, sizeof (audiohd_port_t));
520588447a05SGarrett D'Amore 	}
520688447a05SGarrett D'Amore }
520788447a05SGarrett D'Amore 
520888447a05SGarrett D'Amore /*
52095ec2209cSZhao Edgar Liu - Sun Microsystems  * audiohd_change_widget_power_state(audiohd_state_t *statep, int state)
521088447a05SGarrett D'Amore  * Description:
521188447a05SGarrett D'Amore  * 	This routine is used to change the widget power betwen D0 and D2.
521288447a05SGarrett D'Amore  * 	D0 is fully on; D2 allows the lowest possible power consuming state
521388447a05SGarrett D'Amore  * 	from which it can return to the fully on state: D0.
521488447a05SGarrett D'Amore  */
521588447a05SGarrett D'Amore static void
audiohd_change_widget_power_state(audiohd_state_t * statep,int state)52165ec2209cSZhao Edgar Liu - Sun Microsystems audiohd_change_widget_power_state(audiohd_state_t *statep, int state)
521788447a05SGarrett D'Amore {
521888447a05SGarrett D'Amore 	int			i;
521988447a05SGarrett D'Amore 	wid_t			wid;
522088447a05SGarrett D'Amore 	hda_codec_t		*codec;
522188447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
522288447a05SGarrett D'Amore 
522388447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
522488447a05SGarrett D'Amore 		codec = statep->codec[i];
52255ec2209cSZhao Edgar Liu - Sun Microsystems 		if (codec == NULL)
522688447a05SGarrett D'Amore 			continue;
522788447a05SGarrett D'Amore 		for (wid = codec->first_wid; wid <= codec->last_wid;
522888447a05SGarrett D'Amore 		    wid++) {
522988447a05SGarrett D'Amore 			widget = codec->widget[wid];
523088447a05SGarrett D'Amore 			if (widget->widget_cap &
523188447a05SGarrett D'Amore 			    AUDIOHD_WIDCAP_PWRCTRL) {
523288447a05SGarrett D'Amore 				(void) audioha_codec_verb_get(statep,
523388447a05SGarrett D'Amore 				    codec->index, wid,
523488447a05SGarrett D'Amore 				    AUDIOHDC_VERB_SET_POWER_STATE,
52355ec2209cSZhao Edgar Liu - Sun Microsystems 				    state);
523688447a05SGarrett D'Amore 			}
523788447a05SGarrett D'Amore 		}
523888447a05SGarrett D'Amore 	}
523988447a05SGarrett D'Amore }
524088447a05SGarrett D'Amore /*
524188447a05SGarrett D'Amore  * audiohd_restore_path()
524288447a05SGarrett D'Amore  * Description:
524388447a05SGarrett D'Amore  * 	This routine is used to restore the path on the codec.
524488447a05SGarrett D'Amore  */
524588447a05SGarrett D'Amore static void
audiohd_restore_path(audiohd_state_t * statep)524688447a05SGarrett D'Amore audiohd_restore_path(audiohd_state_t *statep)
524788447a05SGarrett D'Amore {
524888447a05SGarrett D'Amore 	int			i;
524988447a05SGarrett D'Amore 	hda_codec_t		*codec;
525088447a05SGarrett D'Amore 
525188447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
525288447a05SGarrett D'Amore 		codec = statep->codec[i];
52535ec2209cSZhao Edgar Liu - Sun Microsystems 		if (codec == NULL)
525488447a05SGarrett D'Amore 			continue;
525588447a05SGarrett D'Amore 		audiohd_finish_output_path(statep->codec[i]);
525688447a05SGarrett D'Amore 		audiohd_finish_input_path(statep->codec[i]);
525788447a05SGarrett D'Amore 		audiohd_finish_monitor_path(statep->codec[i]);
5258e7236f70SZhao Edgar Liu - Sun Microsystems 		audiohd_finish_beep_path(statep->codec[i]);
525988447a05SGarrett D'Amore 	}
526088447a05SGarrett D'Amore }
526188447a05SGarrett D'Amore 
526288447a05SGarrett D'Amore /*
526388447a05SGarrett D'Amore  * audiohd_reset_pins_ur_cap()
526488447a05SGarrett D'Amore  * Description:
526588447a05SGarrett D'Amore  * 	Enable the unsolicited response of the pins which have the unsolicited
526688447a05SGarrett D'Amore  * 	response capability
526788447a05SGarrett D'Amore  */
526888447a05SGarrett D'Amore static void
audiohd_reset_pins_ur_cap(audiohd_state_t * statep)526988447a05SGarrett D'Amore audiohd_reset_pins_ur_cap(audiohd_state_t *statep)
527088447a05SGarrett D'Amore {
527188447a05SGarrett D'Amore 	hda_codec_t		*codec;
527288447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
527388447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
527488447a05SGarrett D'Amore 	uint32_t		urctrl;
527588447a05SGarrett D'Amore 	int			i;
527688447a05SGarrett D'Amore 
5277ffdc8841SYang-Rong Jerry Zhou 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
527888447a05SGarrett D'Amore 		codec = statep->codec[i];
52795ec2209cSZhao Edgar Liu - Sun Microsystems 		if (codec == NULL)
528088447a05SGarrett D'Amore 			continue;
528188447a05SGarrett D'Amore 		pin = codec->first_pin;
528288447a05SGarrett D'Amore 		while (pin) {
528388447a05SGarrett D'Amore 			/* enable the unsolicited response of the pin */
528488447a05SGarrett D'Amore 			widget = codec->widget[pin->wid];
528588447a05SGarrett D'Amore 			if ((widget->widget_cap &
528688447a05SGarrett D'Amore 			    (AUDIOHD_URCAP_MASK) &&
528788447a05SGarrett D'Amore 			    (pin->cap & AUDIOHD_DTCCAP_MASK)) &&
528888447a05SGarrett D'Amore 			    ((pin->device == DTYPE_LINEOUT) ||
528988447a05SGarrett D'Amore 			    (pin->device == DTYPE_SPDIF_OUT) ||
529088447a05SGarrett D'Amore 			    (pin->device == DTYPE_HP_OUT) ||
529188447a05SGarrett D'Amore 			    (pin->device == DTYPE_MIC_IN))) {
529288447a05SGarrett D'Amore 				urctrl = (uint8_t)(1 <<
529388447a05SGarrett D'Amore 				    (AUDIOHD_UR_ENABLE_OFF - 1));
529488447a05SGarrett D'Amore 				urctrl |= (pin->wid & AUDIOHD_UR_TAG_MASK);
529588447a05SGarrett D'Amore 				(void) audioha_codec_verb_get(statep,
529688447a05SGarrett D'Amore 				    codec->index,
529788447a05SGarrett D'Amore 				    pin->wid,
5298989b958fSZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_VERB_SET_UNS_ENABLE, urctrl);
529988447a05SGarrett D'Amore 			}
530088447a05SGarrett D'Amore 			pin = pin->next;
530188447a05SGarrett D'Amore 		}
530288447a05SGarrett D'Amore 	}
530388447a05SGarrett D'Amore }
530488447a05SGarrett D'Amore static void
audiohd_restore_codec_gpio(audiohd_state_t * statep)530588447a05SGarrett D'Amore audiohd_restore_codec_gpio(audiohd_state_t *statep)
530688447a05SGarrett D'Amore {
530788447a05SGarrett D'Amore 	int		i;
530888447a05SGarrett D'Amore 	wid_t		wid;
530988447a05SGarrett D'Amore 	hda_codec_t	*codec;
531088447a05SGarrett D'Amore 
531188447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
531288447a05SGarrett D'Amore 		codec = statep->codec[i];
531388447a05SGarrett D'Amore 		if (codec == NULL)
531488447a05SGarrett D'Amore 			continue;
531588447a05SGarrett D'Amore 		wid = codec->wid_afg;
531688447a05SGarrett D'Amore 
531788447a05SGarrett D'Amore 		/* power-up audio function group */
531888447a05SGarrett D'Amore 		(void) audioha_codec_verb_get(statep, i, wid,
53195ec2209cSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_VERB_SET_POWER_STATE, AUDIOHD_PW_D0);
53205ec2209cSZhao Edgar Liu - Sun Microsystems 
532188447a05SGarrett D'Amore 		/* work around for Sony VAIO laptop with specific codec */
5322cbe6566fSZhao Edgar Liu - Sun Microsystems 		if ((codec->codec_info->flags & NO_GPIO) == 0) {
532388447a05SGarrett D'Amore 			/*
53249ba19c87SYang-Rong Jerry Zhou 			 * GPIO controls which are laptop specific workarounds
53259ba19c87SYang-Rong Jerry Zhou 			 * and might be changed. Some laptops use GPIO,
53269ba19c87SYang-Rong Jerry Zhou 			 * so we need to enable and set the GPIO correctly.
532788447a05SGarrett D'Amore 			 */
532888447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, i, wid,
532988447a05SGarrett D'Amore 			    AUDIOHDC_VERB_SET_GPIO_MASK, AUDIOHDC_GPIO_ENABLE);
533088447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, i, wid,
53319ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_DIREC, AUDIOHDC_GPIO_DIRECT);
533288447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, i, wid,
53339ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_STCK,
53349ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_GPIO_DATA_CTRL);
533588447a05SGarrett D'Amore 			(void) audioha_codec_verb_get(statep, i, wid,
53369ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_DATA,
53379ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_GPIO_STCK_CTRL);
53389ba19c87SYang-Rong Jerry Zhou 		}
533988447a05SGarrett D'Amore 	}
534088447a05SGarrett D'Amore }
534188447a05SGarrett D'Amore /*
534288447a05SGarrett D'Amore  * audiohd_resume()
534388447a05SGarrett D'Amore  */
534488447a05SGarrett D'Amore static int
audiohd_resume(audiohd_state_t * statep)534588447a05SGarrett D'Amore audiohd_resume(audiohd_state_t *statep)
534688447a05SGarrett D'Amore {
534788447a05SGarrett D'Amore 	uint8_t		rirbsts;
534888447a05SGarrett D'Amore 
534988447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
535088447a05SGarrett D'Amore 	statep->suspended = B_FALSE;
535188447a05SGarrett D'Amore 	/* Restore the hda state */
5352c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_reinit_hda(statep) == DDI_FAILURE) {
535388447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,
535488447a05SGarrett D'Amore 		    "hda reinit failed");
535588447a05SGarrett D'Amore 		mutex_exit(&statep->hda_mutex);
53565ec2209cSZhao Edgar Liu - Sun Microsystems 		return (DDI_FAILURE);
535788447a05SGarrett D'Amore 	}
535888447a05SGarrett D'Amore 	/* reset to enable the capability of unsolicited response for pin */
535988447a05SGarrett D'Amore 	audiohd_reset_pins_ur_cap(statep);
536088447a05SGarrett D'Amore 	/* clear the unsolicited response interrupt */
536188447a05SGarrett D'Amore 	rirbsts = AUDIOHD_REG_GET8(AUDIOHD_REG_RIRBSTS);
536288447a05SGarrett D'Amore 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSTS, rirbsts);
536388447a05SGarrett D'Amore 	/* set widget power to D0 */
53645ec2209cSZhao Edgar Liu - Sun Microsystems 	audiohd_change_widget_power_state(statep, AUDIOHD_PW_D0);
536588447a05SGarrett D'Amore 
536668c47f65SGarrett D'Amore 	audiohd_configure_output(statep);
536768c47f65SGarrett D'Amore 	audiohd_configure_input(statep);
536868c47f65SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
536968c47f65SGarrett D'Amore 
537068c47f65SGarrett D'Amore 	audio_dev_resume(statep->adev);
537168c47f65SGarrett D'Amore 
537288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
537388447a05SGarrett D'Amore }	/* audiohd_resume */
537488447a05SGarrett D'Amore 
537588447a05SGarrett D'Amore /*
537688447a05SGarrett D'Amore  * audiohd_suspend()
537788447a05SGarrett D'Amore  */
537888447a05SGarrett D'Amore static int
audiohd_suspend(audiohd_state_t * statep)537988447a05SGarrett D'Amore audiohd_suspend(audiohd_state_t *statep)
538088447a05SGarrett D'Amore {
538168c47f65SGarrett D'Amore 	audio_dev_suspend(statep->adev);
538268c47f65SGarrett D'Amore 
538388447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
538488447a05SGarrett D'Amore 	statep->suspended = B_TRUE;
538588447a05SGarrett D'Amore 
538688447a05SGarrett D'Amore 	/* set widget power to D2 */
53875ec2209cSZhao Edgar Liu - Sun Microsystems 	audiohd_change_widget_power_state(statep, AUDIOHD_PW_D2);
538888447a05SGarrett D'Amore 	/* Disable h/w */
538988447a05SGarrett D'Amore 	audiohd_stop_dma(statep);
539072a6cf5eSGarrett D'Amore 	audiohd_fini_pci(statep);
539188447a05SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
539288447a05SGarrett D'Amore 
539388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
539488447a05SGarrett D'Amore }	/* audiohd_suspend */
539588447a05SGarrett D'Amore 
539688447a05SGarrett D'Amore /*
539788447a05SGarrett D'Amore  * audiohd_disable_pin()
539888447a05SGarrett D'Amore  */
539968c47f65SGarrett D'Amore static void
audiohd_disable_pin(audiohd_state_t * statep,int caddr,wid_t wid)540088447a05SGarrett D'Amore audiohd_disable_pin(audiohd_state_t *statep, int caddr, wid_t wid)
540188447a05SGarrett D'Amore {
540268c47f65SGarrett D'Amore 	uint32_t	tmp;
540368c47f65SGarrett D'Amore 
540468c47f65SGarrett D'Amore 	tmp = audioha_codec_verb_get(statep, caddr, wid,
540568c47f65SGarrett D'Amore 	    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
540668c47f65SGarrett D'Amore 	if (tmp == AUDIOHD_CODEC_FAILURE)
540768c47f65SGarrett D'Amore 		return;
540868c47f65SGarrett D'Amore 	tmp = audioha_codec_verb_get(statep, caddr, wid,
540968c47f65SGarrett D'Amore 	    AUDIOHDC_VERB_SET_PIN_CTRL,
541068c47f65SGarrett D'Amore 	    (tmp & ~AUDIOHDC_PIN_CONTROL_OUT_ENABLE));
541188447a05SGarrett D'Amore }
541288447a05SGarrett D'Amore 
541388447a05SGarrett D'Amore /*
541488447a05SGarrett D'Amore  * audiohd_enable_pin()
541588447a05SGarrett D'Amore  */
541668c47f65SGarrett D'Amore static void
audiohd_enable_pin(audiohd_state_t * statep,int caddr,wid_t wid)541788447a05SGarrett D'Amore audiohd_enable_pin(audiohd_state_t *statep, int caddr, wid_t wid)
541888447a05SGarrett D'Amore {
541968c47f65SGarrett D'Amore 	uint32_t	tmp;
542068c47f65SGarrett D'Amore 
542168c47f65SGarrett D'Amore 	tmp = audioha_codec_verb_get(statep, caddr, wid,
542268c47f65SGarrett D'Amore 	    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
542368c47f65SGarrett D'Amore 	if (tmp == AUDIOHD_CODEC_FAILURE)
542468c47f65SGarrett D'Amore 		return;
542568c47f65SGarrett D'Amore 	tmp = audioha_codec_verb_get(statep, caddr, wid,
542668c47f65SGarrett D'Amore 	    AUDIOHDC_VERB_SET_PIN_CTRL,
542768c47f65SGarrett D'Amore 	    tmp | AUDIOHDC_PIN_CONTROL_OUT_ENABLE |
542868c47f65SGarrett D'Amore 	    AUDIOHDC_PIN_CONTROL_HP_ENABLE);
542988447a05SGarrett D'Amore }
543068c47f65SGarrett D'Amore 
543188447a05SGarrett D'Amore /*
543288447a05SGarrett D'Amore  * audiohd_change_speaker_state()
543388447a05SGarrett D'Amore  */
543488447a05SGarrett D'Amore static void
audiohd_change_speaker_state(audiohd_state_t * statep,int on)543588447a05SGarrett D'Amore audiohd_change_speaker_state(audiohd_state_t *statep, int on)
543688447a05SGarrett D'Amore {
543788447a05SGarrett D'Amore 	audiohd_path_t		*path;
543888447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
543988447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
544088447a05SGarrett D'Amore 	int			i, j;
544188447a05SGarrett D'Amore 	wid_t			wid;
544288447a05SGarrett D'Amore 
544388447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
544488447a05SGarrett D'Amore 		path = statep->path[i];
544588447a05SGarrett D'Amore 		if (!path || path->path_type != PLAY)
544688447a05SGarrett D'Amore 			continue;
544788447a05SGarrett D'Amore 		if (on) {
544888447a05SGarrett D'Amore 			for (j = 0; j < path->pin_nums; j++) {
544988447a05SGarrett D'Amore 				wid = path->pin_wid[j];
545088447a05SGarrett D'Amore 				widget = path->codec->widget[wid];
545188447a05SGarrett D'Amore 				pin = (audiohd_pin_t *)widget->priv;
545288447a05SGarrett D'Amore 				if (pin->device == DTYPE_SPEAKER) {
545368c47f65SGarrett D'Amore 					audiohd_enable_pin(statep,
545488447a05SGarrett D'Amore 					    path->codec->index,
545588447a05SGarrett D'Amore 					    pin->wid);
545688447a05SGarrett D'Amore 				}
545788447a05SGarrett D'Amore 			}
545888447a05SGarrett D'Amore 
545988447a05SGarrett D'Amore 		} else {
546088447a05SGarrett D'Amore 			for (j = 0; j < path->pin_nums; j++) {
546188447a05SGarrett D'Amore 				wid = path->pin_wid[j];
546288447a05SGarrett D'Amore 				widget = path->codec->widget[wid];
546388447a05SGarrett D'Amore 				pin = (audiohd_pin_t *)widget->priv;
546488447a05SGarrett D'Amore 				if (pin->device == DTYPE_SPEAKER) {
546568c47f65SGarrett D'Amore 					audiohd_disable_pin(statep,
546688447a05SGarrett D'Amore 					    path->codec->index,
546788447a05SGarrett D'Amore 					    pin->wid);
546888447a05SGarrett D'Amore 				}
546988447a05SGarrett D'Amore 			}
547088447a05SGarrett D'Amore 		}
547188447a05SGarrett D'Amore 	}
547288447a05SGarrett D'Amore }
547388447a05SGarrett D'Amore /*
547488447a05SGarrett D'Amore  * audiohd_select_mic()
547588447a05SGarrett D'Amore  *
547688447a05SGarrett D'Amore  * Description:
547788447a05SGarrett D'Amore  *	This function is used for the recording path which has a selector
547888447a05SGarrett D'Amore  *	as the sumwidget. We select the external MIC if it is plugged into the
547988447a05SGarrett D'Amore  *	MIC jack, otherwise the internal integrated MIC is selected.
548088447a05SGarrett D'Amore  */
548188447a05SGarrett D'Amore static void
audiohd_select_mic(audiohd_state_t * statep,uint8_t index,uint8_t id,int select)548288447a05SGarrett D'Amore audiohd_select_mic(audiohd_state_t *statep, uint8_t index,
548388447a05SGarrett D'Amore uint8_t id, int select)
548488447a05SGarrett D'Amore {
548588447a05SGarrett D'Amore 	hda_codec_t		*codec;
548688447a05SGarrett D'Amore 	audiohd_path_t		*path;
5487aa5c9fd8SZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget, *sumwgt = NULL;
548888447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
548988447a05SGarrett D'Amore 	int			i, j;
549088447a05SGarrett D'Amore 	wid_t			wid;
549188447a05SGarrett D'Amore 
549288447a05SGarrett D'Amore 	codec = statep->codec[index];
549388447a05SGarrett D'Amore 	if (codec == NULL)
549488447a05SGarrett D'Amore 		return;
5495aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
549688447a05SGarrett D'Amore 	for (i = 0; i < statep->pathnum; i++) {
549788447a05SGarrett D'Amore 		path = statep->path[i];
549888447a05SGarrett D'Amore 		if (path->codec != codec || path->path_type != RECORD)
549988447a05SGarrett D'Amore 			continue;
550088447a05SGarrett D'Amore 		sumwgt = codec->widget[path->sum_wid];
5501aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
550288447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
550388447a05SGarrett D'Amore 			wid = path->pin_wid[j];
550488447a05SGarrett D'Amore 			widget = codec->widget[wid];
550588447a05SGarrett D'Amore 			pin = (audiohd_pin_t *)widget->priv;
5506aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
5507aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			if (pin->device != DTYPE_MIC_IN)
5508aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				continue;
5509aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
5510aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			if (sumwgt != NULL &&
5511aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			    sumwgt->type == WTYPE_AUDIO_SEL) {
5512aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				/* Have a selector to choose input pin */
5513aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
5514aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				if (select && pin->wid == id &&
551588447a05SGarrett D'Amore 				    (((pin->config >>
551688447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_OFF) &
551788447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_MASK) ==
551888447a05SGarrett D'Amore 				    AUDIOHD_PIN_CON_JACK)) {
551988447a05SGarrett D'Amore 					(void) audioha_codec_verb_get(
552088447a05SGarrett D'Amore 					    statep,
552188447a05SGarrett D'Amore 					    index,
552288447a05SGarrett D'Amore 					    path->sum_wid,
552388447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_CONN_SEL,
552488447a05SGarrett D'Amore 					    path->sum_selconn[j]);
552588447a05SGarrett D'Amore 					statep->port[PORT_ADC]->index =
552688447a05SGarrett D'Amore 					    path->tag;
552788447a05SGarrett D'Amore 					return;
5528aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				} else if (!select && pin->wid != id &&
552988447a05SGarrett D'Amore 				    (((pin->config >>
553088447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_OFF) &
553188447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_MASK) ==
5532aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				    AUDIOHD_PIN_CON_FIXED)) {
553388447a05SGarrett D'Amore 					(void) audioha_codec_verb_get(
553488447a05SGarrett D'Amore 					    statep,
553588447a05SGarrett D'Amore 					    index,
553688447a05SGarrett D'Amore 					    path->sum_wid,
553788447a05SGarrett D'Amore 					    AUDIOHDC_VERB_SET_CONN_SEL,
553888447a05SGarrett D'Amore 					    path->sum_selconn[j]);
553988447a05SGarrett D'Amore 					statep->port[PORT_ADC]->index =
554088447a05SGarrett D'Amore 					    path->tag;
554188447a05SGarrett D'Amore 					return;
554288447a05SGarrett D'Amore 				}
5543aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			} else {
5544aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				/*
5545aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				 * No selector widget in the path,
5546aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				 * mute unselected input pin
5547aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				 */
5548aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
5549aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				/* Open all input pin, and then mute others */
5550aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				audiohd_set_pin_volume(statep, DTYPE_MIC_IN);
5551aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
5552aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				if (select == 1) {
5553aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					/* Select external mic, mute internal */
5554aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					if (wid != id) {
5555aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						(void)
5556aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    audioha_codec_4bit_verb_get(
5557aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    statep, path->codec->index,
5558aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    wid,
5559aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_VERB_SET_AMP_MUTE,
5560aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    path->mute_dir |
5561aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_AMP_SET_LNR |
5562aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_AMP_SET_MUTE);
556388447a05SGarrett D'Amore 					}
5564aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				} else {
5565aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					/* Select internal mic, mute external */
5566aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					if (wid == id) {
5567aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						(void)
5568aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    audioha_codec_4bit_verb_get(
5569aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    statep, path->codec->index,
5570aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    wid,
5571aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_VERB_SET_AMP_MUTE,
5572aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    path->mute_dir |
5573aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_AMP_SET_LNR |
5574aa5c9fd8SZhao Edgar Liu - Sun Microsystems 						    AUDIOHDC_AMP_SET_MUTE);
557588447a05SGarrett D'Amore 					}
557688447a05SGarrett D'Amore 				}
5577aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			}
5578aa5c9fd8SZhao Edgar Liu - Sun Microsystems 		}
5579aa5c9fd8SZhao Edgar Liu - Sun Microsystems 	}
5580aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
558188447a05SGarrett D'Amore 	/*
558288447a05SGarrett D'Amore 	 * If the input istream > 1, we should set the record stream tag
558388447a05SGarrett D'Amore 	 * respectively. All the input streams sharing one tag may make the
558488447a05SGarrett D'Amore 	 * record sound distorted.
558588447a05SGarrett D'Amore 	 */
558688447a05SGarrett D'Amore 	if (codec->nistream > 1) {
558788447a05SGarrett D'Amore 		for (i = 0; i < statep->pathnum; i++) {
558888447a05SGarrett D'Amore 			path = statep->path[i];
558988447a05SGarrett D'Amore 			if (!path || path->path_type != RECORD)
559088447a05SGarrett D'Amore 				continue;
559188447a05SGarrett D'Amore 			for (j = 0; j < path->pin_nums; j++) {
559288447a05SGarrett D'Amore 				wid = path->pin_wid[j];
559388447a05SGarrett D'Amore 				widget = codec->widget[wid];
559488447a05SGarrett D'Amore 				if (widget == NULL)
559588447a05SGarrett D'Amore 					return;
559688447a05SGarrett D'Amore 				pin = (audiohd_pin_t *)widget->priv;
559788447a05SGarrett D'Amore 				if (select &&
559888447a05SGarrett D'Amore 				    pin->device == DTYPE_MIC_IN &&
559988447a05SGarrett D'Amore 				    pin->wid == id &&
560088447a05SGarrett D'Amore 				    (((pin->config >>
560188447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_OFF) &
560288447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_MASK) ==
560388447a05SGarrett D'Amore 				    AUDIOHD_PIN_CON_JACK)) {
560488447a05SGarrett D'Amore 					statep->port[PORT_ADC]->index =
560588447a05SGarrett D'Amore 					    path->tag;
560688447a05SGarrett D'Amore 					return;
560788447a05SGarrett D'Amore 				} else if (!select &&
560888447a05SGarrett D'Amore 				    pin->device == DTYPE_MIC_IN &&
560988447a05SGarrett D'Amore 				    (((pin->config >>
561088447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_OFF) &
561188447a05SGarrett D'Amore 				    AUDIOHD_PIN_CONTP_MASK) ==
561288447a05SGarrett D'Amore 				    AUDIOHD_PIN_CON_FIXED)) {
561388447a05SGarrett D'Amore 					statep->port[PORT_ADC]->index =
561488447a05SGarrett D'Amore 					    path->tag;
561588447a05SGarrett D'Amore 					return;
561688447a05SGarrett D'Amore 				}
561788447a05SGarrett D'Amore 			}
561888447a05SGarrett D'Amore 		}
561988447a05SGarrett D'Amore 	}
562088447a05SGarrett D'Amore }
562188447a05SGarrett D'Amore /*
562288447a05SGarrett D'Amore  * audiohd_pin_sense()
562388447a05SGarrett D'Amore  *
562488447a05SGarrett D'Amore  * Description
562588447a05SGarrett D'Amore  *
562688447a05SGarrett D'Amore  * 	When the earphone is plugged into the jack associtated with the pin
562788447a05SGarrett D'Amore  * 	complex, we disable the built in speaker. When the earphone is plugged
562888447a05SGarrett D'Amore  * 	out of the jack, we enable the built in speaker.
562988447a05SGarrett D'Amore  */
563088447a05SGarrett D'Amore static void
audiohd_pin_sense(audiohd_state_t * statep,uint32_t resp,uint32_t respex)563188447a05SGarrett D'Amore audiohd_pin_sense(audiohd_state_t *statep, uint32_t resp, uint32_t respex)
563288447a05SGarrett D'Amore {
563388447a05SGarrett D'Amore 	uint8_t			index;
563488447a05SGarrett D'Amore 	uint8_t			id;
563588447a05SGarrett D'Amore 	uint32_t		rs;
563688447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
563788447a05SGarrett D'Amore 	audiohd_pin_t		*pin;
563888447a05SGarrett D'Amore 	hda_codec_t		*codec;
563988447a05SGarrett D'Amore 
564088447a05SGarrett D'Amore 	index = respex & AUDIOHD_RIRB_CODEC_MASK;
564188447a05SGarrett D'Amore 	id = resp >> (AUDIOHD_RIRB_WID_OFF - 1);
564288447a05SGarrett D'Amore 
564388447a05SGarrett D'Amore 	codec = statep->codec[index];
564488447a05SGarrett D'Amore 	if (codec == NULL)
564588447a05SGarrett D'Amore 		return;
564688447a05SGarrett D'Amore 	widget = codec->widget[id];
564788447a05SGarrett D'Amore 	if (widget == NULL)
564888447a05SGarrett D'Amore 		return;
564988447a05SGarrett D'Amore 
565088447a05SGarrett D'Amore 	rs = audioha_codec_verb_get(statep, index, id,
565188447a05SGarrett D'Amore 	    AUDIOHDC_VERB_GET_PIN_SENSE, 0);
5652989b958fSZhao Edgar Liu - Sun Microsystems 	if (rs & AUDIOHD_PIN_PRES_MASK) {
565388447a05SGarrett D'Amore 		/* A MIC is plugged in, we select the MIC as input */
565488447a05SGarrett D'Amore 		if ((widget->type == WTYPE_PIN) &&
565588447a05SGarrett D'Amore 		    (pin = (audiohd_pin_t *)widget->priv) &&
565688447a05SGarrett D'Amore 		    (pin->device == DTYPE_MIC_IN)) {
565788447a05SGarrett D'Amore 			audiohd_select_mic(statep, index, id, 1);
565888447a05SGarrett D'Amore 			return;
565988447a05SGarrett D'Amore 		}
566088447a05SGarrett D'Amore 		/* output pin is plugged */
566188447a05SGarrett D'Amore 		audiohd_change_speaker_state(statep, AUDIOHD_SP_OFF);
566288447a05SGarrett D'Amore 	} else {
566388447a05SGarrett D'Amore 		/*
566488447a05SGarrett D'Amore 		 * A MIC is unplugged, we select the built in MIC
566588447a05SGarrett D'Amore 		 * as input.
566688447a05SGarrett D'Amore 		 */
566788447a05SGarrett D'Amore 		if ((widget->type == WTYPE_PIN) &&
566888447a05SGarrett D'Amore 		    (pin = (audiohd_pin_t *)widget->priv) &&
566988447a05SGarrett D'Amore 		    (pin->device == DTYPE_MIC_IN)) {
567088447a05SGarrett D'Amore 			audiohd_select_mic(statep, index, id, 0);
567188447a05SGarrett D'Amore 			return;
567288447a05SGarrett D'Amore 		}
567388447a05SGarrett D'Amore 		/* output pin is unplugged */
567488447a05SGarrett D'Amore 		audiohd_change_speaker_state(statep, AUDIOHD_SP_ON);
567588447a05SGarrett D'Amore 	}
567688447a05SGarrett D'Amore 
567788447a05SGarrett D'Amore }
567888447a05SGarrett D'Amore 
567988447a05SGarrett D'Amore /*
568088447a05SGarrett D'Amore  * audiohd_disable_intr()
568188447a05SGarrett D'Amore  *
568288447a05SGarrett D'Amore  * Description:
568388447a05SGarrett D'Amore  *	Disable all possible interrupts.
568488447a05SGarrett D'Amore  */
568588447a05SGarrett D'Amore static void
audiohd_disable_intr(audiohd_state_t * statep)568688447a05SGarrett D'Amore audiohd_disable_intr(audiohd_state_t *statep)
568788447a05SGarrett D'Amore {
568888447a05SGarrett D'Amore 	int		i;
568988447a05SGarrett D'Amore 	uint32_t	base;
569088447a05SGarrett D'Amore 
569188447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_INTCTL, 0);
569288447a05SGarrett D'Amore 	base = AUDIOHD_REG_SD_BASE;
569388447a05SGarrett D'Amore 	for (i = 0; i < statep->hda_streams_nums; i++) {
569488447a05SGarrett D'Amore 		AUDIOHD_REG_SET8(base + AUDIOHD_SDREG_OFFSET_STS,
569588447a05SGarrett D'Amore 		    AUDIOHDR_SD_STS_INTRS);
569688447a05SGarrett D'Amore 		base += AUDIOHD_REG_SD_LEN;
569788447a05SGarrett D'Amore 	}
569888447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_INTSTS, (uint32_t)(-1));
569988447a05SGarrett D'Amore 
570088447a05SGarrett D'Amore }	/* audiohd_disable_intr() */
570188447a05SGarrett D'Amore 
570288447a05SGarrett D'Amore 
570388447a05SGarrett D'Amore /*
570488447a05SGarrett D'Amore  * audiohd_12bit_verb_to_codec()
570588447a05SGarrett D'Amore  *
570688447a05SGarrett D'Amore  * Description:
570788447a05SGarrett D'Amore  *
570888447a05SGarrett D'Amore  */
570988447a05SGarrett D'Amore static int
audiohd_12bit_verb_to_codec(audiohd_state_t * statep,uint8_t caddr,uint8_t wid,uint16_t cmd,uint8_t param)571088447a05SGarrett D'Amore audiohd_12bit_verb_to_codec(audiohd_state_t *statep, uint8_t caddr,
571188447a05SGarrett D'Amore     uint8_t wid,
571288447a05SGarrett D'Amore     uint16_t cmd, uint8_t param)
571388447a05SGarrett D'Amore {
571488447a05SGarrett D'Amore 	uint32_t	verb;
571588447a05SGarrett D'Amore 	uint16_t	wptr;
571688447a05SGarrett D'Amore 	uint16_t	rptr;
571788447a05SGarrett D'Amore 
571888447a05SGarrett D'Amore 	ASSERT((cmd & AUDIOHDC_12BIT_VERB_MASK) == 0);
571988447a05SGarrett D'Amore 
572088447a05SGarrett D'Amore 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBWP) & AUDIOHD_CMDIO_ENT_MASK;
572188447a05SGarrett D'Amore 	rptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBRP) & AUDIOHD_CMDIO_ENT_MASK;
572288447a05SGarrett D'Amore 
572388447a05SGarrett D'Amore 	wptr++;
572488447a05SGarrett D'Amore 	wptr &= AUDIOHD_CMDIO_ENT_MASK;
572588447a05SGarrett D'Amore 
572688447a05SGarrett D'Amore 	/* overflow */
572788447a05SGarrett D'Amore 	if (wptr == rptr) {
5728c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
572988447a05SGarrett D'Amore 	}
573088447a05SGarrett D'Amore 
573188447a05SGarrett D'Amore 	verb = (caddr & 0x0f) << AUDIOHD_VERB_ADDR_OFF;
573288447a05SGarrett D'Amore 	verb |= wid << AUDIOHD_VERB_NID_OFF;
573388447a05SGarrett D'Amore 	verb |= cmd << AUDIOHD_VERB_CMD_OFF;
573488447a05SGarrett D'Amore 	verb |= param;
573588447a05SGarrett D'Amore 
573688447a05SGarrett D'Amore 	*((uint32_t *)(statep->hda_dma_corb.ad_vaddr) + wptr) = verb;
573788447a05SGarrett D'Amore 	(void) ddi_dma_sync(statep->hda_dma_corb.ad_dmahdl, 0,
573888447a05SGarrett D'Amore 	    sizeof (sd_bdle_t) * AUDIOHD_BDLE_NUMS, DDI_DMA_SYNC_FORDEV);
573988447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, wptr);
574088447a05SGarrett D'Amore 
5741c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
574288447a05SGarrett D'Amore 
574388447a05SGarrett D'Amore }	/* audiohd_12bit_verb_to_codec() */
574488447a05SGarrett D'Amore 
574588447a05SGarrett D'Amore /*
574688447a05SGarrett D'Amore  * audiohd_4bit_verb_to_codec()
574788447a05SGarrett D'Amore  *
574888447a05SGarrett D'Amore  * Description:
574988447a05SGarrett D'Amore  *
575088447a05SGarrett D'Amore  */
575188447a05SGarrett D'Amore static int
audiohd_4bit_verb_to_codec(audiohd_state_t * statep,uint8_t caddr,uint8_t wid,uint32_t cmd,uint16_t param)575288447a05SGarrett D'Amore audiohd_4bit_verb_to_codec(audiohd_state_t *statep, uint8_t caddr,
575388447a05SGarrett D'Amore     uint8_t wid,
575488447a05SGarrett D'Amore     uint32_t cmd, uint16_t param)
575588447a05SGarrett D'Amore {
575688447a05SGarrett D'Amore 	uint32_t	verb;
575788447a05SGarrett D'Amore 	uint16_t	wptr;
575888447a05SGarrett D'Amore 	uint16_t	rptr;
575988447a05SGarrett D'Amore 
576088447a05SGarrett D'Amore 	ASSERT((cmd & AUDIOHDC_4BIT_VERB_MASK) == 0);
576188447a05SGarrett D'Amore 
576288447a05SGarrett D'Amore 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBWP) & AUDIOHD_CMDIO_ENT_MASK;
576388447a05SGarrett D'Amore 	rptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBRP) & AUDIOHD_CMDIO_ENT_MASK;
576488447a05SGarrett D'Amore 
576588447a05SGarrett D'Amore 	wptr++;
576688447a05SGarrett D'Amore 	wptr &= AUDIOHD_CMDIO_ENT_MASK;
576788447a05SGarrett D'Amore 
576888447a05SGarrett D'Amore 	/* overflow */
576988447a05SGarrett D'Amore 	if (wptr == rptr) {
5770c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
577188447a05SGarrett D'Amore 	}
577288447a05SGarrett D'Amore 
577388447a05SGarrett D'Amore 	verb = (caddr & 0x0f) << AUDIOHD_VERB_ADDR_OFF;
577488447a05SGarrett D'Amore 	verb |= wid << AUDIOHD_VERB_NID_OFF;
577588447a05SGarrett D'Amore 	verb |= cmd << AUDIOHD_VERB_CMD16_OFF;
577688447a05SGarrett D'Amore 	verb |= param;
577788447a05SGarrett D'Amore 
577888447a05SGarrett D'Amore 	*((uint32_t *)(statep->hda_dma_corb.ad_vaddr) + wptr) = verb;
577988447a05SGarrett D'Amore 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, wptr);
578088447a05SGarrett D'Amore 
5781c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
578288447a05SGarrett D'Amore 
578388447a05SGarrett D'Amore }	/* audiohd_4bit_verb_to_codec() */
578488447a05SGarrett D'Amore 
578588447a05SGarrett D'Amore /*
578688447a05SGarrett D'Amore  * audiohd_response_from_codec()
578788447a05SGarrett D'Amore  *
578888447a05SGarrett D'Amore  * Description:
578988447a05SGarrett D'Amore  *
579088447a05SGarrett D'Amore  */
579188447a05SGarrett D'Amore static int
audiohd_response_from_codec(audiohd_state_t * statep,uint32_t * resp,uint32_t * respex)579288447a05SGarrett D'Amore audiohd_response_from_codec(audiohd_state_t *statep, uint32_t *resp,
579388447a05SGarrett D'Amore     uint32_t *respex)
579488447a05SGarrett D'Amore {
579588447a05SGarrett D'Amore 	uint16_t	wptr;
579688447a05SGarrett D'Amore 	uint16_t	rptr;
579788447a05SGarrett D'Amore 	uint32_t	*lp;
579888447a05SGarrett D'Amore 
579988447a05SGarrett D'Amore 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_RIRBWP) & 0x00ff;
580088447a05SGarrett D'Amore 	rptr = statep->hda_rirb_rp;
580188447a05SGarrett D'Amore 
580288447a05SGarrett D'Amore 	if (rptr == wptr) {
5803c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
580488447a05SGarrett D'Amore 	}
580588447a05SGarrett D'Amore 
580688447a05SGarrett D'Amore 	rptr++;
580788447a05SGarrett D'Amore 	rptr &= AUDIOHD_RING_MAX_SIZE;
580888447a05SGarrett D'Amore 
580988447a05SGarrett D'Amore 	lp = (uint32_t *)(statep->hda_dma_rirb.ad_vaddr) + (rptr << 1);
581088447a05SGarrett D'Amore 	*resp = *(lp);
581188447a05SGarrett D'Amore 	*respex = *(lp + 1);
581288447a05SGarrett D'Amore 
581388447a05SGarrett D'Amore 	statep->hda_rirb_rp = rptr;
581488447a05SGarrett D'Amore 
5815c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
581688447a05SGarrett D'Amore 
581788447a05SGarrett D'Amore }	/* audiohd_response_from_codec() */
581888447a05SGarrett D'Amore 
581988447a05SGarrett D'Amore 
582088447a05SGarrett D'Amore /*
582188447a05SGarrett D'Amore  * audioha_codec_verb_get()
582288447a05SGarrett D'Amore  */
582388447a05SGarrett D'Amore static uint32_t
audioha_codec_verb_get(void * arg,uint8_t caddr,uint8_t wid,uint16_t verb,uint8_t param)582488447a05SGarrett D'Amore audioha_codec_verb_get(void *arg, uint8_t caddr, uint8_t wid,
582588447a05SGarrett D'Amore     uint16_t verb,
582688447a05SGarrett D'Amore     uint8_t param)
582788447a05SGarrett D'Amore {
582888447a05SGarrett D'Amore 	audiohd_state_t	*statep = (audiohd_state_t *)arg;
582988447a05SGarrett D'Amore 	uint32_t	resp;
583088447a05SGarrett D'Amore 	uint32_t	respex;
583188447a05SGarrett D'Amore 	int		ret;
583288447a05SGarrett D'Amore 	int		i;
583388447a05SGarrett D'Amore 
583488447a05SGarrett D'Amore 	ret = audiohd_12bit_verb_to_codec(statep, caddr, wid, verb, param);
5835c6e681c0SYang-Rong Jerry Zhou 	if (ret != DDI_SUCCESS) {
583688447a05SGarrett D'Amore 		return (uint32_t)(-1);
583788447a05SGarrett D'Amore 	}
583888447a05SGarrett D'Amore 
583988447a05SGarrett D'Amore 	/*
584088447a05SGarrett D'Amore 	 * Empirical testing times. 50 times is enough for audiohd spec 1.0.
584188447a05SGarrett D'Amore 	 * But we need to make it work for audiohd spec 0.9, which is just a
584288447a05SGarrett D'Amore 	 * draft version and requires more time to wait.
584388447a05SGarrett D'Amore 	 */
584488447a05SGarrett D'Amore 	for (i = 0; i < 500; i++) {
584588447a05SGarrett D'Amore 		ret = audiohd_response_from_codec(statep, &resp, &respex);
584688447a05SGarrett D'Amore 		if (((respex & AUDIOHD_BDLE_RIRB_SDI) == caddr) &&
584788447a05SGarrett D'Amore 		    ((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
5848c6e681c0SYang-Rong Jerry Zhou 		    (ret == DDI_SUCCESS))
584988447a05SGarrett D'Amore 			break;
585013084339SYang-Rong Jerry Zhou 		/* Empirical testing time, which works well */
585113084339SYang-Rong Jerry Zhou 		drv_usecwait(30);
585288447a05SGarrett D'Amore 	}
585388447a05SGarrett D'Amore 
5854c6e681c0SYang-Rong Jerry Zhou 	if (ret == DDI_SUCCESS) {
585588447a05SGarrett D'Amore 		return (resp);
585688447a05SGarrett D'Amore 	}
585788447a05SGarrett D'Amore 
58580c240c64SZhao Edgar Liu - Sun Microsystems 	if (wid != AUDIOHDC_NODE_ROOT && param != AUDIOHDC_PAR_VENDOR_ID) {
585988447a05SGarrett D'Amore 		audio_dev_warn(statep->adev,  "timeout when get "
586088447a05SGarrett D'Amore 		    "response from codec: wid=%d, verb=0x%04x, param=0x%04x",
586188447a05SGarrett D'Amore 		    wid, verb, param);
58620c240c64SZhao Edgar Liu - Sun Microsystems 	}
586388447a05SGarrett D'Amore 
586488447a05SGarrett D'Amore 	return ((uint32_t)(-1));
586588447a05SGarrett D'Amore 
586688447a05SGarrett D'Amore }	/* audioha_codec_verb_get() */
586788447a05SGarrett D'Amore 
586888447a05SGarrett D'Amore 
586988447a05SGarrett D'Amore /*
587088447a05SGarrett D'Amore  * audioha_codec_4bit_verb_get()
587188447a05SGarrett D'Amore  */
587288447a05SGarrett D'Amore static uint32_t
audioha_codec_4bit_verb_get(void * arg,uint8_t caddr,uint8_t wid,uint16_t verb,uint16_t param)587388447a05SGarrett D'Amore audioha_codec_4bit_verb_get(void *arg, uint8_t caddr, uint8_t wid,
587488447a05SGarrett D'Amore     uint16_t verb, uint16_t param)
587588447a05SGarrett D'Amore {
587688447a05SGarrett D'Amore 	audiohd_state_t	*statep = (audiohd_state_t *)arg;
587788447a05SGarrett D'Amore 	uint32_t	resp;
587888447a05SGarrett D'Amore 	uint32_t	respex;
587988447a05SGarrett D'Amore 	int		ret;
588088447a05SGarrett D'Amore 	int		i;
588188447a05SGarrett D'Amore 
588288447a05SGarrett D'Amore 	ret = audiohd_4bit_verb_to_codec(statep, caddr, wid, verb, param);
5883c6e681c0SYang-Rong Jerry Zhou 	if (ret != DDI_SUCCESS) {
588488447a05SGarrett D'Amore 		return (uint32_t)(-1);
588588447a05SGarrett D'Amore 	}
588688447a05SGarrett D'Amore 
588788447a05SGarrett D'Amore 	for (i = 0; i < 500; i++) {
588888447a05SGarrett D'Amore 		ret = audiohd_response_from_codec(statep, &resp, &respex);
588988447a05SGarrett D'Amore 		if (((respex & AUDIOHD_BDLE_RIRB_SDI) == caddr) &&
589088447a05SGarrett D'Amore 		    ((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
5891c6e681c0SYang-Rong Jerry Zhou 		    (ret == DDI_SUCCESS))
589288447a05SGarrett D'Amore 			break;
589313084339SYang-Rong Jerry Zhou 		/* Empirical testing time, which works well */
589413084339SYang-Rong Jerry Zhou 		drv_usecwait(30);
589588447a05SGarrett D'Amore 	}
589688447a05SGarrett D'Amore 
5897c6e681c0SYang-Rong Jerry Zhou 	if (ret == DDI_SUCCESS) {
589888447a05SGarrett D'Amore 		return (resp);
589988447a05SGarrett D'Amore 	}
590088447a05SGarrett D'Amore 
590188447a05SGarrett D'Amore 	audio_dev_warn(statep->adev,  "timeout when get "
590288447a05SGarrett D'Amore 	    "response from codec: wid=%d, verb=0x%04x, param=0x%04x",
590388447a05SGarrett D'Amore 	    wid, verb, param);
590488447a05SGarrett D'Amore 
590588447a05SGarrett D'Amore 	return ((uint32_t)(-1));
590688447a05SGarrett D'Amore 
590788447a05SGarrett D'Amore }	/* audioha_codec_4bit_verb_get() */
5908