1582eadeeSfl /*
2582eadeeSfl  * CDDL HEADER START
3582eadeeSfl  *
4582eadeeSfl  * The contents of this file are subject to the terms of the
5582eadeeSfl  * Common Development and Distribution License (the "License").
6582eadeeSfl  * You may not use this file except in compliance with the License.
7582eadeeSfl  *
8582eadeeSfl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9582eadeeSfl  * or http://www.opensolaris.org/os/licensing.
10582eadeeSfl  * See the License for the specific language governing permissions
11582eadeeSfl  * and limitations under the License.
12582eadeeSfl  *
13582eadeeSfl  * When distributing Covered Code, include this CDDL HEADER in each
14582eadeeSfl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15582eadeeSfl  * If applicable, add the following below this CDDL HEADER, with the
16582eadeeSfl  * fields enclosed by brackets "[]" replaced with your own identifying
17582eadeeSfl  * information: Portions Copyright [yyyy] [name of copyright owner]
18582eadeeSfl  *
19582eadeeSfl  * CDDL HEADER END
20582eadeeSfl  */
21582eadeeSfl /*
222c30fa45SGarrett D'Amore  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23582eadeeSfl  */
24582eadeeSfl 
2588447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
26582eadeeSfl #include <sys/note.h>
2742c41cf8Slipeng sang - Sun Microsystems - Beijing China #include <sys/beep.h>
28582eadeeSfl #include <sys/pci.h>
2988447a05SGarrett D'Amore #include "audiohd.h"
30582eadeeSfl 
3188447a05SGarrett D'Amore #define	DRVNAME			"audiohd"
3268c47f65SGarrett D'Amore 
33582eadeeSfl /*
34582eadeeSfl  * Module linkage routines for the kernel
35582eadeeSfl  */
36582eadeeSfl static int audiohd_attach(dev_info_t *, ddi_attach_cmd_t);
37582eadeeSfl static int audiohd_detach(dev_info_t *, ddi_detach_cmd_t);
3819397407SSherry Moore static int audiohd_quiesce(dev_info_t *);
39d5247f45Sgs static int audiohd_resume(audiohd_state_t *);
40d5247f45Sgs static int audiohd_suspend(audiohd_state_t *);
41582eadeeSfl 
42582eadeeSfl /*
43582eadeeSfl  * Local routines
44582eadeeSfl  */
45582eadeeSfl static int audiohd_init_state(audiohd_state_t *, dev_info_t *);
46582eadeeSfl static int audiohd_init_pci(audiohd_state_t *, ddi_device_acc_attr_t *);
47582eadeeSfl static void audiohd_fini_pci(audiohd_state_t *);
48582eadeeSfl static int audiohd_reset_controller(audiohd_state_t *);
49582eadeeSfl static int audiohd_init_controller(audiohd_state_t *);
50582eadeeSfl static void audiohd_fini_controller(audiohd_state_t *);
51582eadeeSfl static void audiohd_stop_dma(audiohd_state_t *);
52582eadeeSfl static void audiohd_disable_intr(audiohd_state_t *);
53582eadeeSfl static int audiohd_create_codec(audiohd_state_t *);
543a49c214SYang-Rong Jerry Zhou static void audiohd_build_path(audiohd_state_t *);
55582eadeeSfl static void audiohd_destroy_codec(audiohd_state_t *);
56582eadeeSfl static int audiohd_alloc_dma_mem(audiohd_state_t *, audiohd_dma_t *,
57582eadeeSfl     size_t, ddi_dma_attr_t *, uint_t);
585ec2209cSZhao Edgar Liu - Sun Microsystems static void audiohd_finish_output_path(hda_codec_t *);
59582eadeeSfl static uint32_t audioha_codec_verb_get(void *, uint8_t,
60582eadeeSfl     uint8_t, uint16_t, uint8_t);
61582eadeeSfl static uint32_t audioha_codec_4bit_verb_get(void *, uint8_t,
62582eadeeSfl     uint8_t, uint16_t, uint16_t);
633a49c214SYang-Rong Jerry Zhou 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;
92582eadeeSfl 
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 
98582eadeeSfl static ddi_device_acc_attr_t hda_dev_accattr = {
99582eadeeSfl 	DDI_DEVICE_ATTR_V0,
100582eadeeSfl 	DDI_STRUCTURE_LE_ACC,
101582eadeeSfl 	DDI_STRICTORDER_ACC
102582eadeeSfl };
103582eadeeSfl 
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,
122582eadeeSfl 	NULL,
123582eadeeSfl };
124582eadeeSfl 
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)
203582eadeeSfl {
20488447a05SGarrett D'Amore 	uint32_t		devid;
20588447a05SGarrett D'Amore 	const char		*name;
20688447a05SGarrett D'Amore 	const char		*vers;
207582eadeeSfl 
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;
212582eadeeSfl 
21388447a05SGarrett D'Amore 	name = AUDIOHD_DEV_CONFIG;
21488447a05SGarrett D'Amore 	vers = AUDIOHD_DEV_VERSION;
215582eadeeSfl 
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";
272582eadeeSfl 		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;
297582eadeeSfl 	}
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 }
302582eadeeSfl 
30388447a05SGarrett D'Amore static int
audiohd_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)304582eadeeSfl audiohd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
305582eadeeSfl {
306582eadeeSfl 	audiohd_state_t		*statep;
307582eadeeSfl 	int			instance;
308582eadeeSfl 
309582eadeeSfl 	instance = ddi_get_instance(dip);
310582eadeeSfl 	switch (cmd) {
311582eadeeSfl 	case DDI_ATTACH:
312582eadeeSfl 		break;
313582eadeeSfl 
314582eadeeSfl 	case DDI_RESUME:
31588447a05SGarrett D'Amore 		statep = ddi_get_driver_private(dip);
316d5247f45Sgs 		ASSERT(statep != NULL);
317d5247f45Sgs 		return (audiohd_resume(statep));
318582eadeeSfl 
319582eadeeSfl 	default:
320582eadeeSfl 		return (DDI_FAILURE);
321582eadeeSfl 	}
322582eadeeSfl 
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);
326582eadeeSfl 
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 
330644f3c1fScg 	/* 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;
334582eadeeSfl 	}
335582eadeeSfl 
336582eadeeSfl 	/* 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;
341582eadeeSfl 	}
342582eadeeSfl 
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;
349582eadeeSfl 	}
350582eadeeSfl 
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;
355582eadeeSfl 	}
356582eadeeSfl 
3573a49c214SYang-Rong Jerry Zhou 	audiohd_build_path(statep);
358a234d95bScg 
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;
363582eadeeSfl 	}
36488447a05SGarrett D'Amore 	audiohd_init_path(statep);
365582eadeeSfl 	/* set up kernel statistics */
36688447a05SGarrett D'Amore 	if ((statep->hda_ksp = kstat_create(DRVNAME, instance,
36788447a05SGarrett D'Amore 	    DRVNAME, "controller", KSTAT_TYPE_INTR, 1,
368582eadeeSfl 	    KSTAT_FLAG_PERSISTENT)) != NULL) {
369582eadeeSfl 		kstat_install(statep->hda_ksp);
370582eadeeSfl 	}
371582eadeeSfl 
372582eadeeSfl 	/* disable interrupts and clear interrupt status */
373582eadeeSfl 	audiohd_disable_intr(statep);
374582eadeeSfl 
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 	}
385582eadeeSfl 	ddi_report_dev(dip);
386582eadeeSfl 
387ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
388582eadeeSfl 	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);
392582eadeeSfl 	return (DDI_FAILURE);
39388447a05SGarrett D'Amore }
394582eadeeSfl 
39588447a05SGarrett D'Amore static int
audiohd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)396582eadeeSfl audiohd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
397582eadeeSfl {
398582eadeeSfl 	audiohd_state_t		*statep;
399582eadeeSfl 
40088447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
40188447a05SGarrett D'Amore 	ASSERT(statep != NULL);
402582eadeeSfl 
403582eadeeSfl 	switch (cmd) {
404582eadeeSfl 	case DDI_DETACH:
405582eadeeSfl 		break;
406582eadeeSfl 
407582eadeeSfl 	case DDI_SUSPEND:
408d5247f45Sgs 		return (audiohd_suspend(statep));
409582eadeeSfl 
410582eadeeSfl 	default:
411582eadeeSfl 		return (DDI_FAILURE);
412582eadeeSfl 	}
41388447a05SGarrett D'Amore 	if (audio_dev_unregister(statep->adev) != DDI_SUCCESS)
41488447a05SGarrett D'Amore 		return (DDI_FAILURE);
415582eadeeSfl 
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);
419582eadeeSfl 	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 }
459582eadeeSfl 
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 }
476582eadeeSfl 
47719397407SSherry Moore /*
47888447a05SGarrett D'Amore  * Audio routines
47919397407SSherry Moore  */
48088447a05SGarrett D'Amore 
48119397407SSherry Moore static int
audiohd_engine_format(void * arg)48288447a05SGarrett D'Amore audiohd_engine_format(void *arg)
48319397407SSherry Moore {
484a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_port_t *port = arg;
485a33ad26eSZhao Edgar Liu - Sun Microsystems 	audiohd_state_t *statep = port->statep;
48619397407SSherry Moore 
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:
492a33ad26eSZhao Edgar Liu - Sun Microsystems 		return (AUDIO_FORMAT_S16_LE);
493a33ad26eSZhao Edgar Liu - Sun Microsystems 	}
49488447a05SGarrett D'Amore }
49519397407SSherry Moore 
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;
50019397407SSherry Moore 
50188447a05SGarrett D'Amore 	return (port->nchan);
50288447a05SGarrett D'Amore }
50319397407SSherry Moore 
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);
51119397407SSherry Moore }
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;
517c6e681c0SYang-Rong Jerry Zhou 
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 }
5443a49c214SYang-Rong Jerry Zhou /*
54588447a05SGarrett D'Amore  * get the max channels the hardware supported
5463a49c214SYang-Rong Jerry Zhou  */
5473a49c214SYang-Rong Jerry Zhou static void
audiohd_get_channels(audiohd_state_t * statep)54888447a05SGarrett D'Amore audiohd_get_channels(audiohd_state_t *statep)
5493a49c214SYang-Rong Jerry Zhou {
55088447a05SGarrett D'Amore 	int		i;
55188447a05SGarrett D'Amore 	uint8_t		maxp, assoc;
5523a49c214SYang-Rong Jerry Zhou 
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;
5583a49c214SYang-Rong Jerry Zhou 		}
5593a49c214SYang-Rong Jerry Zhou 	}
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;
5643a49c214SYang-Rong Jerry Zhou }
5653a49c214SYang-Rong Jerry Zhou static void
audiohd_init_play_path(audiohd_path_t * path)56688447a05SGarrett D'Amore audiohd_init_play_path(audiohd_path_t *path)
5673a49c214SYang-Rong Jerry Zhou {
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;
60988447a05SGarrett D'Amore 		}
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);
6683a49c214SYang-Rong Jerry Zhou 	}
6693a49c214SYang-Rong Jerry Zhou }
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;
6793a49c214SYang-Rong Jerry Zhou 
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 
7343a49c214SYang-Rong Jerry Zhou 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) {
74568c47f65SGarrett D'Amore 		case PLAY:
74668c47f65SGarrett D'Amore 			audiohd_init_play_path(path);
74768c47f65SGarrett D'Amore 			break;
74868c47f65SGarrett D'Amore 		case RECORD:
74968c47f65SGarrett D'Amore 			audiohd_init_record_path(path);
75068c47f65SGarrett D'Amore 			break;
75168c47f65SGarrett D'Amore 		default:
75268c47f65SGarrett 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)
7603a49c214SYang-Rong Jerry Zhou {
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 	}
8183a49c214SYang-Rong Jerry Zhou 
8193a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_BDLPL,
82088447a05SGarrett D'Amore 	    (uint32_t)port->bdl_paddr);
8213a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_BDLPU,
82288447a05SGarrett D'Amore 	    (uint32_t)(port->bdl_paddr >> 32));
8233a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_LVI,
8243a49c214SYang-Rong Jerry Zhou 	    AUDIOHD_BDLE_NUMS - 1);
82568c47f65SGarrett D'Amore 	AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_CBL, port->bufsize);
8263a49c214SYang-Rong Jerry Zhou 
8273a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_FORMAT,
82888447a05SGarrett D'Amore 	    port->format << 4 | port->nchan - 1);
8293a49c214SYang-Rong Jerry Zhou 
8303a49c214SYang-Rong Jerry Zhou 	/* clear status */
8313a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_STS,
8323a49c214SYang-Rong Jerry Zhou 	    AUDIOHDR_SD_STS_BCIS | AUDIOHDR_SD_STS_FIFOE |
8333a49c214SYang-Rong Jerry Zhou 	    AUDIOHDR_SD_STS_DESE);
8343a49c214SYang-Rong Jerry Zhou 
83588447a05SGarrett D'Amore 	/* set stream tag */
8363a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_CTL +
8373a49c214SYang-Rong Jerry Zhou 	    AUDIOHD_PLAY_CTL_OFF,
83888447a05SGarrett D'Amore 	    (port->index) << AUDIOHD_PLAY_TAG_OFF);
8393a49c214SYang-Rong Jerry Zhou 
84068c47f65SGarrett D'Amore 	return (0);
84188447a05SGarrett D'Amore }
84289e1f902SZhao Edgar Liu - Sun Microsystems 
84388447a05SGarrett 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 {
84688447a05SGarrett D'Amore 	audiohd_port_t	*port = arg;
847ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t	*statep = port->statep;
8483a49c214SYang-Rong Jerry Zhou 
84988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
8503a49c214SYang-Rong Jerry Zhou 
851ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_enter(&statep->hda_mutex);
85288447a05SGarrett D'Amore 	port->count = 0;
85388447a05SGarrett D'Amore 	port->curpos = 0;
85468c47f65SGarrett D'Amore 	*nframes = port->nframes;
85588447a05SGarrett D'Amore 	*bufp = port->samp_kaddr;
856ea463888SZhao Edgar Liu - Sun Microsystems 	mutex_exit(&statep->hda_mutex);
8573a49c214SYang-Rong Jerry Zhou 
85888447a05SGarrett D'Amore 	return (0);
8593a49c214SYang-Rong Jerry Zhou }
86088447a05SGarrett D'Amore 
861d5247f45Sgs static int
audiohd_engine_start(void * arg)86288447a05SGarrett D'Amore audiohd_engine_start(void *arg)
863d5247f45Sgs {
86488447a05SGarrett D'Amore 	audiohd_port_t		*port = arg;
86588447a05SGarrett D'Amore 	audiohd_state_t		*statep = port->statep;
86668c47f65SGarrett D'Amore 	int			rv;
8673a49c214SYang-Rong Jerry Zhou 
868d5247f45Sgs 	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);
873d5247f45Sgs 	}
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 
878d5247f45Sgs 	mutex_exit(&statep->hda_mutex);
87988447a05SGarrett D'Amore 	return (0);
88088447a05SGarrett D'Amore }
881d5247f45Sgs 
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;
8873a49c214SYang-Rong Jerry Zhou 
888d5247f45Sgs 	mutex_enter(&statep->hda_mutex);
88968c47f65SGarrett D'Amore 	AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, 0);
890d5247f45Sgs 	mutex_exit(&statep->hda_mutex);
89188447a05SGarrett D'Amore }
892d5247f45Sgs 
89388447a05SGarrett D'Amore static void
audiohd_update_port(audiohd_port_t * port)89488447a05SGarrett D'Amore audiohd_update_port(audiohd_port_t *port)
895d5247f45Sgs {
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;
911d5247f45Sgs 	}
9123a49c214SYang-Rong Jerry Zhou 
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)
9603a49c214SYang-Rong Jerry Zhou {
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);
96668c47f65SGarrett 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);
9703a49c214SYang-Rong Jerry Zhou }
9713a49c214SYang-Rong Jerry Zhou 
97288447a05SGarrett D'Amore static void
audiohd_engine_close(void * arg)97388447a05SGarrett D'Amore audiohd_engine_close(void *arg)
9743a49c214SYang-Rong Jerry Zhou {
97568c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
9763a49c214SYang-Rong Jerry Zhou }
97788447a05SGarrett D'Amore 
9783a49c214SYang-Rong Jerry Zhou static void
audiohd_engine_sync(void * arg,unsigned nframes)97988447a05SGarrett D'Amore audiohd_engine_sync(void *arg, unsigned nframes)
9803a49c214SYang-Rong Jerry Zhou {
98188447a05SGarrett D'Amore 	audiohd_port_t *port = arg;
9823a49c214SYang-Rong Jerry Zhou 
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 
101813084339SYang-Rong Jerry Zhou 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 |
104613084339SYang-Rong Jerry Zhou 	    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 }
10753a49c214SYang-Rong Jerry Zhou 
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);
11383a49c214SYang-Rong Jerry Zhou 			}
11393a49c214SYang-Rong Jerry Zhou 		}
11403a49c214SYang-Rong Jerry Zhou 	}
11413a49c214SYang-Rong Jerry Zhou }
114288447a05SGarrett D'Amore 
114388447a05SGarrett D'Amore 
11443a49c214SYang-Rong Jerry Zhou 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)
11473a49c214SYang-Rong Jerry Zhou {
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;
11533a49c214SYang-Rong Jerry Zhou 	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;
12143a49c214SYang-Rong Jerry Zhou 	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;
1220d5247f45Sgs 
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];
12353a49c214SYang-Rong Jerry Zhou 				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);
124616600ba1SYang-Rong Jerry Zhou 				}
12473a49c214SYang-Rong Jerry Zhou 			}
12483a49c214SYang-Rong Jerry Zhou 			break;
124988447a05SGarrett D'Amore 		default:
125088447a05SGarrett D'Amore 			break;
125188447a05SGarrett D'Amore 		}
12523a49c214SYang-Rong Jerry Zhou 	}
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) {
1271e7236f70SZhao Edgar Liu - Sun Microsystems 					(void) audioha_codec_verb_get(
1272e7236f70SZhao Edgar Liu - Sun Microsystems 					    statep, codec->index, path->sum_wid,
1273e7236f70SZhao Edgar Liu - Sun Microsystems 					    AUDIOHDC_VERB_SET_CONN_SEL,
1274e7236f70SZhao Edgar Liu - Sun Microsystems 					    path->sum_selconn[i]);
1275a4c3d128SYang-Rong Jerry Zhou 					break;
1276e7236f70SZhao Edgar Liu - Sun Microsystems 				}
1277e7236f70SZhao Edgar Liu - Sun Microsystems 			}
1278a4c3d128SYang-Rong Jerry Zhou 		}
1279a4c3d128SYang-Rong Jerry Zhou 	}
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);
133516600ba1SYang-Rong Jerry Zhou 			}
133616600ba1SYang-Rong Jerry Zhou 		}
133716600ba1SYang-Rong Jerry Zhou 	}
13383a49c214SYang-Rong Jerry Zhou }
133988447a05SGarrett D'Amore 
13403a49c214SYang-Rong Jerry Zhou static void
audiohd_set_monitor_gain(audiohd_state_t * statep)134188447a05SGarrett D'Amore audiohd_set_monitor_gain(audiohd_state_t *statep)
13423a49c214SYang-Rong Jerry Zhou {
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;
13483a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
1349c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		ctrl;
135088447a05SGarrett D'Amore 	uint64_t		val;
135116600ba1SYang-Rong Jerry Zhou 
1352c1cfefcdSZhao Edgar Liu - Sun Microsystems 	ctrl = statep->ctrls[CTL_MONGAIN];
1353c1cfefcdSZhao Edgar Liu - Sun Microsystems 	val = ctrl.val;
13543a49c214SYang-Rong Jerry Zhou 
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);
13663a49c214SYang-Rong Jerry Zhou 		}
13673a49c214SYang-Rong Jerry Zhou 	}
1368d5247f45Sgs 
13693a49c214SYang-Rong Jerry Zhou }
137088447a05SGarrett D'Amore 
137142c41cf8Slipeng sang - Sun Microsystems - Beijing China 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 
148288447a05SGarrett D'Amore static void
audiohd_configure_output(audiohd_state_t * statep)1483211ec5c5SYang-Rong Jerry Zhou audiohd_configure_output(audiohd_state_t *statep)
1484582eadeeSfl {
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;
15103a49c214SYang-Rong Jerry Zhou 
151188447a05SGarrett D'Amore 	if (val & ~(statep->inmask))
151288447a05SGarrett D'Amore 		return (EINVAL);
1513582eadeeSfl 
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 }
1520582eadeeSfl 
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);
1527582eadeeSfl 
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);
1546582eadeeSfl 	mutex_exit(&statep->hda_mutex);
1547582eadeeSfl 
154888447a05SGarrett D'Amore 	return (0);
154988447a05SGarrett D'Amore }
1550582eadeeSfl 
1551582eadeeSfl static int
audiohd_set_surround(void * arg,uint64_t val)155288447a05SGarrett D'Amore audiohd_set_surround(void *arg, uint64_t val)
1553582eadeeSfl {
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);
1557582eadeeSfl 
1558582eadeeSfl 	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);
1562582eadeeSfl 
156388447a05SGarrett D'Amore 	return (0);
156488447a05SGarrett D'Amore }
1565582eadeeSfl 
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);
1572582eadeeSfl 
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);
1577582eadeeSfl 
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);
1586582eadeeSfl 
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 
1639e7236f70SZhao Edgar Liu - Sun Microsystems 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 
170188447a05SGarrett D'Amore 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);
1722582eadeeSfl 
172388447a05SGarrett D'Amore 	mutex_enter(&statep->hda_mutex);
172488447a05SGarrett D'Amore 	pc->val = val;
172588447a05SGarrett D'Amore 	audiohd_set_pin_volume(statep, DTYPE_CD);
1726582eadeeSfl 	mutex_exit(&statep->hda_mutex);
1727582eadeeSfl 
172888447a05SGarrett D'Amore 	return (0);
172988447a05SGarrett D'Amore }
1730582eadeeSfl 
1731582eadeeSfl static int
audiohd_set_mongain(void * arg,uint64_t val)173288447a05SGarrett D'Amore audiohd_set_mongain(void *arg, uint64_t val)
1733582eadeeSfl {
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);
1737582eadeeSfl 
1738582eadeeSfl 	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 
1769c1cfefcdSZhao Edgar Liu - Sun Microsystems static void
audiohd_del_controls(audiohd_state_t * statep)1770c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_del_controls(audiohd_state_t *statep)
177188447a05SGarrett D'Amore {
1772c1cfefcdSZhao Edgar Liu - Sun Microsystems 	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 		}
1779c1cfefcdSZhao Edgar Liu - Sun Microsystems 	}
1780c1cfefcdSZhao Edgar Liu - Sun Microsystems }
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)
1785c1cfefcdSZhao Edgar Liu - Sun Microsystems {
1786c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_ctrl_t		*ac;
1787c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audio_ctrl_desc_t	desc;
178888447a05SGarrett D'Amore 
178988447a05SGarrett D'Amore 	bzero(&desc, sizeof (desc));
179088447a05SGarrett D'Amore 
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;
1794582eadeeSfl 
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;
1800582eadeeSfl 
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);
1804c1cfefcdSZhao Edgar Liu - Sun Microsystems }
1805582eadeeSfl 
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;
1812582eadeeSfl 
1813c1cfefcdSZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
1814582eadeeSfl 
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;
1818582eadeeSfl 
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;
1824582eadeeSfl 
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 }
1829582eadeeSfl 
1830e7236f70SZhao 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 
1854c1cfefcdSZhao 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;
1859582eadeeSfl 
1860c1cfefcdSZhao Edgar Liu - Sun Microsystems 	bzero(&desc, sizeof (desc));
18613a49c214SYang-Rong Jerry Zhou 
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;
186588447a05SGarrett D'Amore 
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];
1873582eadeeSfl 	}
1874582eadeeSfl 
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);
187888447a05SGarrett D'Amore }
1879582eadeeSfl 
1880582eadeeSfl static void
audiohd_create_controls(audiohd_state_t * statep)1881c1cfefcdSZhao Edgar Liu - Sun Microsystems audiohd_create_controls(audiohd_state_t *statep)
1882582eadeeSfl {
188388447a05SGarrett D'Amore 	wid_t			wid;
1884c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget;
1885c1cfefcdSZhao Edgar Liu - Sun Microsystems 	audiohd_path_t		*path;
188688447a05SGarrett D'Amore 	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;
1890582eadeeSfl 
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) &
1908c1cfefcdSZhao Edgar Liu - Sun Microsystems 			    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 &&
1914c1cfefcdSZhao Edgar Liu - Sun Microsystems 			    pin->device != DTYPE_HP_OUT &&
1915c1cfefcdSZhao Edgar Liu - Sun Microsystems 			    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 			}
193165a41de7SYang-Rong Jerry Zhou 			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);
193588447a05SGarrett D'Amore 			} 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);
193988447a05SGarrett D'Amore 			} 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);
194388447a05SGarrett D'Amore 			} 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);
194788447a05SGarrett D'Amore 			} 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);
195188447a05SGarrett D'Amore 			}
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 		}
1964582eadeeSfl 	}
1965582eadeeSfl 
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 	}
1971582eadeeSfl 
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 }
1981582eadeeSfl 
1982582eadeeSfl /*
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.
1991582eadeeSfl  */
199288447a05SGarrett D'Amore static int
audiohd_quiesce(dev_info_t * dip)199388447a05SGarrett D'Amore audiohd_quiesce(dev_info_t *dip)
1994582eadeeSfl {
199588447a05SGarrett D'Amore 	audiohd_state_t		*statep;
1996582eadeeSfl 
199788447a05SGarrett D'Amore 	statep = ddi_get_driver_private(dip);
19983a49c214SYang-Rong Jerry Zhou 
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);
2002582eadeeSfl 
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 
2073582eadeeSfl /*
2074582eadeeSfl  * audiohd_init_state()
2075582eadeeSfl  *
2076582eadeeSfl  * Description
2077582eadeeSfl  *	This routine initailizes soft state of driver instance,
2078582eadeeSfl  *	also, it requests an interrupt cookie and initializes
2079582eadeeSfl  *	mutex for soft state.
2080582eadeeSfl  */
2081582eadeeSfl /*ARGSUSED*/
2082582eadeeSfl static int
audiohd_init_state(audiohd_state_t * statep,dev_info_t * dip)2083582eadeeSfl audiohd_init_state(audiohd_state_t *statep, dev_info_t *dip)
2084582eadeeSfl {
2085c7b817cfSZhao Edgar Liu - Sun Microsystems 	audio_dev_t	*adev;
2086582eadeeSfl 
2087582eadeeSfl 	statep->hda_dip = dip;
2088c7b817cfSZhao Edgar Liu - Sun Microsystems 	statep->hda_rirb_rp = 0;
2089582eadeeSfl 
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);
2093582eadeeSfl 	}
209488447a05SGarrett D'Amore 	statep->adev = adev;
2095582eadeeSfl 
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);
2099582eadeeSfl 
2100c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
2101582eadeeSfl }	/* audiohd_init_state() */
2102582eadeeSfl 
2103582eadeeSfl /*
2104582eadeeSfl  * audiohd_init_pci()
2105582eadeeSfl  *
2106582eadeeSfl  * Description
2107582eadeeSfl  *	enable driver to access PCI configure space and memory
2108582eadeeSfl  *	I/O space.
2109582eadeeSfl  */
2110582eadeeSfl static int
audiohd_init_pci(audiohd_state_t * statep,ddi_device_acc_attr_t * acc_attr)2111582eadeeSfl audiohd_init_pci(audiohd_state_t *statep, ddi_device_acc_attr_t *acc_attr)
2112582eadeeSfl {
2113582eadeeSfl 	uint16_t	cmdreg;
2114555989a4Scg 	uint16_t	vid;
2115555989a4Scg 	uint8_t		cTmp;
2116582eadeeSfl 	dev_info_t	*dip = statep->hda_dip;
2117c7b817cfSZhao Edgar Liu - Sun Microsystems 	audio_dev_t	*adev = statep->adev;
2118582eadeeSfl 
21193a49c214SYang-Rong Jerry Zhou 	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);
21233a49c214SYang-Rong Jerry Zhou 	}
2124582eadeeSfl 
21253a49c214SYang-Rong Jerry Zhou 	if (ddi_regs_map_setup(dip, 1, &statep->hda_reg_base, 0,
21263a49c214SYang-Rong Jerry Zhou 	    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);
2130582eadeeSfl 	}
2131582eadeeSfl 
2132582eadeeSfl 	/*
2133582eadeeSfl 	 * HD audio control uses memory I/O only, enable it here.
2134582eadeeSfl 	 */
2135582eadeeSfl 	cmdreg = pci_config_get16(statep->hda_pci_handle, PCI_CONF_COMM);
2136582eadeeSfl 	pci_config_put16(statep->hda_pci_handle, PCI_CONF_COMM,
2137582eadeeSfl 	    cmdreg | PCI_COMM_MAE | PCI_COMM_ME);
2138582eadeeSfl 
2139555989a4Scg 	vid = pci_config_get16(statep->hda_pci_handle, PCI_CONF_VENID);
214014707ff3Scg 	switch (vid) {
214114707ff3Scg 	case AUDIOHD_VID_INTEL:
214214707ff3Scg 		/*
214314707ff3Scg 		 * Currently, Intel (G)MCH and ICHx chipsets support PCI
214414707ff3Scg 		 * Express QoS. It implemenets two VCs(virtual channels)
214514707ff3Scg 		 * and allows OS software to map 8 traffic classes to the
214614707ff3Scg 		 * two VCs. Some BIOSes initialize HD audio hardware to
214714707ff3Scg 		 * use TC7 (traffic class 7) and to map TC7 to VC1 as Intel
214814707ff3Scg 		 * recommended. However, solaris doesn't support PCI express
214914707ff3Scg 		 * QoS yet. As a result, this driver can not work for those
215014707ff3Scg 		 * hardware without touching PCI express control registers.
215114707ff3Scg 		 * Here, we set TCSEL to 0 so as to use TC0/VC0 (VC0 is
215214707ff3Scg 		 * always enabled and TC0 is always mapped to VC0) for all
215314707ff3Scg 		 * Intel HD audio controllers.
215414707ff3Scg 		 */
2155555989a4Scg 		cTmp = pci_config_get8(statep->hda_pci_handle,
2156555989a4Scg 		    AUDIOHD_INTEL_PCI_TCSEL);
2157555989a4Scg 		pci_config_put8(statep->hda_pci_handle,
21583a49c214SYang-Rong Jerry Zhou 		    AUDIOHD_INTEL_PCI_TCSEL, (cTmp & AUDIOHD_INTEL_TCS_MASK));
215914707ff3Scg 		break;
216014707ff3Scg 	case AUDIOHD_VID_ATI:
216114707ff3Scg 		/*
216214707ff3Scg 		 * Refer to ATI SB450 datesheet. We set snoop for SB450
216314707ff3Scg 		 * like hardware.
216414707ff3Scg 		 */
216514707ff3Scg 		cTmp = pci_config_get8(statep->hda_pci_handle,
216614707ff3Scg 		    AUDIOHD_ATI_PCI_MISC2);
216714707ff3Scg 		pci_config_put8(statep->hda_pci_handle, AUDIOHD_ATI_PCI_MISC2,
21683a49c214SYang-Rong Jerry Zhou 		    (cTmp & AUDIOHD_ATI_MISC2_MASK) | AUDIOHD_ATI_MISC2_SNOOP);
21693a49c214SYang-Rong Jerry Zhou 		break;
2170989b958fSZhao Edgar Liu - Sun Microsystems 	case AUDIOHD_VID_NVIDIA:
21713a49c214SYang-Rong Jerry Zhou 		/*
21723a49c214SYang-Rong Jerry Zhou 		 * Refer to the datasheet, we set snoop for NVIDIA
21733a49c214SYang-Rong Jerry Zhou 		 * like hardware
21743a49c214SYang-Rong Jerry Zhou 		 */
21753a49c214SYang-Rong Jerry Zhou 		cTmp = pci_config_get8(statep->hda_pci_handle,
21763a49c214SYang-Rong Jerry Zhou 		    AUDIOHD_CORB_SIZE_OFF);
21773a49c214SYang-Rong Jerry Zhou 		pci_config_put8(statep->hda_pci_handle, AUDIOHD_CORB_SIZE_OFF,
21783a49c214SYang-Rong Jerry Zhou 		    cTmp | AUDIOHD_NVIDIA_SNOOP);
217914707ff3Scg 		break;
218014707ff3Scg 	default:
218114707ff3Scg 		break;
218214707ff3Scg 	}
2183582eadeeSfl 
2184c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
2185582eadeeSfl }	/* audiohd_init_pci() */
2186582eadeeSfl 
2187582eadeeSfl 
2188582eadeeSfl /*
2189582eadeeSfl  * audiohd_fini_pci()
2190582eadeeSfl  *
2191582eadeeSfl  * Description
2192582eadeeSfl  *	Release mapping for PCI configure space.
2193582eadeeSfl  */
2194582eadeeSfl static void
audiohd_fini_pci(audiohd_state_t * statep)2195582eadeeSfl audiohd_fini_pci(audiohd_state_t *statep)
2196582eadeeSfl {
2197582eadeeSfl 	if (statep->hda_reg_handle != NULL) {
2198582eadeeSfl 		ddi_regs_map_free(&statep->hda_reg_handle);
2199582eadeeSfl 	}
2200582eadeeSfl 
2201582eadeeSfl 	if (statep->hda_pci_handle != NULL) {
2202582eadeeSfl 		pci_config_teardown(&statep->hda_pci_handle);
2203582eadeeSfl 	}
2204582eadeeSfl 
2205582eadeeSfl }	/* audiohd_fini_pci() */
2206582eadeeSfl 
2207582eadeeSfl /*
2208582eadeeSfl  * audiohd_stop_dma()
2209582eadeeSfl  *
2210582eadeeSfl  * Description
2211582eadeeSfl  *	Stop all DMA behaviors of controllers, for command I/O
2212582eadeeSfl  *	and each audio stream.
2213582eadeeSfl  */
2214582eadeeSfl static void
audiohd_stop_dma(audiohd_state_t * statep)2215582eadeeSfl audiohd_stop_dma(audiohd_state_t *statep)
2216582eadeeSfl {
2217582eadeeSfl 	int	i;
2218582eadeeSfl 	uint_t	base;
2219582eadeeSfl 	uint8_t	bTmp;
2220582eadeeSfl 
2221582eadeeSfl 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, 0);
2222582eadeeSfl 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, 0);
2223582eadeeSfl 
2224582eadeeSfl 	base = AUDIOHD_REG_SD_BASE;
2225582eadeeSfl 	for (i = 0; i < statep->hda_streams_nums; i++) {
2226582eadeeSfl 		bTmp = AUDIOHD_REG_GET8(base + AUDIOHD_SDREG_OFFSET_CTL);
2227582eadeeSfl 
2228582eadeeSfl 		/* for input/output stream, it is the same */
2229582eadeeSfl 		bTmp &= ~AUDIOHDR_RIRBCTL_DMARUN;
2230582eadeeSfl 
2231582eadeeSfl 		AUDIOHD_REG_SET8(base + AUDIOHD_SDREG_OFFSET_CTL, bTmp);
2232582eadeeSfl 		base += AUDIOHD_REG_SD_LEN;
2233582eadeeSfl 	}
2234582eadeeSfl 
2235582eadeeSfl 	/* wait 40us for stream DMA to stop */
2236582eadeeSfl 	drv_usecwait(40);
2237582eadeeSfl 
2238582eadeeSfl }	/* audiohd_stop_dma() */
2239582eadeeSfl 
2240582eadeeSfl /*
2241582eadeeSfl  * audiohd_reset_controller()
2242582eadeeSfl  *
2243582eadeeSfl  * Description:
2244582eadeeSfl  *	This routine is just used to reset controller and
2245582eadeeSfl  *	CODEC as well by HW reset bit in global control
2246582eadeeSfl  *	register of HD controller.
2247582eadeeSfl  */
2248582eadeeSfl static int
audiohd_reset_controller(audiohd_state_t * statep)2249582eadeeSfl audiohd_reset_controller(audiohd_state_t *statep)
2250582eadeeSfl {
2251582eadeeSfl 	int		i;
22523a49c214SYang-Rong Jerry Zhou 	uint16_t	sTmp;
22533a49c214SYang-Rong Jerry Zhou 	uint32_t	gctl;
22543a49c214SYang-Rong Jerry Zhou 
22553a49c214SYang-Rong Jerry Zhou 	/* Reset Status register but preserve the first bit */
22563a49c214SYang-Rong Jerry Zhou 	sTmp = AUDIOHD_REG_GET16(AUDIOHD_REG_STATESTS);
22573a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET16(AUDIOHD_REG_STATESTS, sTmp & 0x8000);
2258582eadeeSfl 
2259582eadeeSfl 	/* reset controller */
22603a49c214SYang-Rong Jerry Zhou 	gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
22613a49c214SYang-Rong Jerry Zhou 	gctl &= ~AUDIOHDR_GCTL_CRST;
22623a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL, gctl);  /* entering reset state */
22633a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
22643a49c214SYang-Rong Jerry Zhou 		/* Empirical testing time: 150 */
22653a49c214SYang-Rong Jerry Zhou 		drv_usecwait(150);
22663a49c214SYang-Rong Jerry Zhou 		gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
22673a49c214SYang-Rong Jerry Zhou 		if ((gctl & AUDIOHDR_GCTL_CRST) == 0)
2268582eadeeSfl 			break;
2269582eadeeSfl 	}
2270582eadeeSfl 
22713a49c214SYang-Rong Jerry Zhou 	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);
2275582eadeeSfl 	}
2276582eadeeSfl 
22773a49c214SYang-Rong Jerry Zhou 	/* Empirical testing time:300 */
2278582eadeeSfl 	drv_usecwait(300);
2279582eadeeSfl 
2280582eadeeSfl 	/* exit reset state */
22813a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL, gctl | AUDIOHDR_GCTL_CRST);
2282582eadeeSfl 
22833a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < AUDIOHD_RETRY_TIMES; i++) {
22843a49c214SYang-Rong Jerry Zhou 		/* Empirical testing time: 150, which works well */
22853a49c214SYang-Rong Jerry Zhou 		drv_usecwait(150);
22863a49c214SYang-Rong Jerry Zhou 		gctl = AUDIOHD_REG_GET32(AUDIOHD_REG_GCTL);
22873a49c214SYang-Rong Jerry Zhou 		if (gctl & AUDIOHDR_GCTL_CRST)
2288582eadeeSfl 			break;
2289582eadeeSfl 	}
2290582eadeeSfl 
22913a49c214SYang-Rong Jerry Zhou 	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);
2295582eadeeSfl 	}
2296582eadeeSfl 
22973a49c214SYang-Rong Jerry Zhou 	/* HD spec requires to wait 250us at least. we use 500us */
22983a49c214SYang-Rong Jerry Zhou 	drv_usecwait(500);
22993a49c214SYang-Rong Jerry Zhou 
2300582eadeeSfl 	/* enable unsolicited response */
2301582eadeeSfl 	AUDIOHD_REG_SET32(AUDIOHD_REG_GCTL,
23023a49c214SYang-Rong Jerry Zhou 	    gctl |  AUDIOHDR_GCTL_URESPE);
2303582eadeeSfl 
2304c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
2305582eadeeSfl 
2306582eadeeSfl }	/* audiohd_reset_controller() */
2307582eadeeSfl 
2308582eadeeSfl /*
2309582eadeeSfl  * audiohd_alloc_dma_mem()
2310582eadeeSfl  *
2311582eadeeSfl  * Description:
2312582eadeeSfl  *	This is an utility routine. It is used to allocate DMA
2313582eadeeSfl  *	memory.
2314582eadeeSfl  */
2315582eadeeSfl 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)2316582eadeeSfl audiohd_alloc_dma_mem(audiohd_state_t *statep, audiohd_dma_t *pdma,
2317582eadeeSfl     size_t memsize, ddi_dma_attr_t *dma_attr_p, uint_t dma_flags)
2318582eadeeSfl {
2319582eadeeSfl 	ddi_dma_cookie_t	cookie;
2320582eadeeSfl 	uint_t			count;
2321582eadeeSfl 	dev_info_t		*dip = statep->hda_dip;
232288447a05SGarrett D'Amore 	audio_dev_t		*ahandle = statep->adev;
2323582eadeeSfl 
2324582eadeeSfl 	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);
2329582eadeeSfl 	}
2330582eadeeSfl 
2331582eadeeSfl 	if (ddi_dma_mem_alloc(pdma->ad_dmahdl, memsize, &hda_dev_accattr,
2332582eadeeSfl 	    dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING),
23333a49c214SYang-Rong Jerry Zhou 	    DDI_DMA_SLEEP, NULL,
23343a49c214SYang-Rong Jerry Zhou 	    (caddr_t *)&pdma->ad_vaddr, &pdma->ad_real_sz,
23353a49c214SYang-Rong Jerry Zhou 	    &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);
2339582eadeeSfl 	}
2340582eadeeSfl 
2341582eadeeSfl 	if (ddi_dma_addr_bind_handle(pdma->ad_dmahdl, NULL,
2342582eadeeSfl 	    (caddr_t)pdma->ad_vaddr, pdma->ad_real_sz, dma_flags,
2343582eadeeSfl 	    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);
2347582eadeeSfl 	}
2348582eadeeSfl 
2349555989a4Scg 	pdma->ad_paddr = (uint64_t)(cookie.dmac_laddress);
2350582eadeeSfl 	pdma->ad_req_sz = memsize;
2351582eadeeSfl 
2352c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
2353582eadeeSfl }	/* audiohd_alloc_dma_mem() */
2354582eadeeSfl 
2355582eadeeSfl /*
2356582eadeeSfl  * audiohd_release_dma_mem()
2357582eadeeSfl  *
2358582eadeeSfl  * Description:
2359582eadeeSfl  *	Release DMA memory.
2360582eadeeSfl  */
2361582eadeeSfl 
2362582eadeeSfl static void
audiohd_release_dma_mem(audiohd_dma_t * pdma)2363582eadeeSfl audiohd_release_dma_mem(audiohd_dma_t *pdma)
2364582eadeeSfl {
2365582eadeeSfl 	if (pdma->ad_dmahdl != NULL) {
2366582eadeeSfl 		(void) ddi_dma_unbind_handle(pdma->ad_dmahdl);
2367582eadeeSfl 	}
2368582eadeeSfl 
2369582eadeeSfl 	if (pdma->ad_acchdl != NULL) {
2370582eadeeSfl 		ddi_dma_mem_free(&pdma->ad_acchdl);
2371582eadeeSfl 		pdma->ad_acchdl = NULL;
2372582eadeeSfl 	}
2373582eadeeSfl 
2374582eadeeSfl 	if (pdma->ad_dmahdl != NULL) {
2375582eadeeSfl 		ddi_dma_free_handle(&pdma->ad_dmahdl);
2376582eadeeSfl 		pdma->ad_dmahdl = NULL;
2377582eadeeSfl 	}
2378582eadeeSfl 
2379582eadeeSfl }	/* audiohd_release_dma_mem() */
2380582eadeeSfl 
2381d5247f45Sgs /*
2382d5247f45Sgs  * audiohd_reinit_hda()
2383d5247f45Sgs  *
2384d5247f45Sgs  * Description:
2385d5247f45Sgs  *	This routine is used to re-initialize HD controller and codec.
2386d5247f45Sgs  */
2387d5247f45Sgs static int
audiohd_reinit_hda(audiohd_state_t * statep)2388d5247f45Sgs audiohd_reinit_hda(audiohd_state_t *statep)
2389d5247f45Sgs {
2390d5247f45Sgs 	uint64_t	addr;
2391d5247f45Sgs 
2392d5247f45Sgs 	/* set PCI configure space in case it's not restored OK */
2393d5247f45Sgs 	(void) audiohd_init_pci(statep, &hda_dev_accattr);
2394d5247f45Sgs 
2395d5247f45Sgs 	/* reset controller */
2396c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_reset_controller(statep) != DDI_SUCCESS)
2397c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
2398d5247f45Sgs 	AUDIOHD_REG_SET32(AUDIOHD_REG_SYNC, 0); /* needn't sync stream */
2399d5247f45Sgs 
2400d5247f45Sgs 	/* Initialize controller RIRB */
2401d5247f45Sgs 	addr = statep->hda_dma_rirb.ad_paddr;
2402d5247f45Sgs 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBLBASE, (uint32_t)addr);
2403d5247f45Sgs 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBUBASE,
2404d5247f45Sgs 	    (uint32_t)(addr >> 32));
2405d5247f45Sgs 	AUDIOHD_REG_SET16(AUDIOHD_REG_RIRBWP, AUDIOHDR_RIRBWP_RESET);
2406d5247f45Sgs 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSIZE, AUDIOHDR_RIRBSZ_256);
24073a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, AUDIOHDR_RIRBCTL_DMARUN |
24083a49c214SYang-Rong Jerry Zhou 	    AUDIOHDR_RIRBCTL_RINTCTL);
2409d5247f45Sgs 
2410d5247f45Sgs 	/* Initialize controller CORB */
2411d5247f45Sgs 	addr = statep->hda_dma_corb.ad_paddr;
2412d5247f45Sgs 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, AUDIOHDR_CORBRP_RESET);
2413d5247f45Sgs 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBLBASE, (uint32_t)addr);
2414d5247f45Sgs 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBUBASE,
2415d5247f45Sgs 	    (uint32_t)(addr >> 32));
2416d5247f45Sgs 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBSIZE, AUDIOHDR_CORBSZ_256);
2417d5247f45Sgs 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, 0);
2418d5247f45Sgs 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, 0);
2419d5247f45Sgs 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, AUDIOHDR_CORBCTL_DMARUN);
2420d5247f45Sgs 
242116600ba1SYang-Rong Jerry Zhou 	audiohd_restore_codec_gpio(statep);
24223a49c214SYang-Rong Jerry Zhou 	audiohd_restore_path(statep);
242388447a05SGarrett D'Amore 	audiohd_init_path(statep);
242488447a05SGarrett D'Amore 
2425c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
24263a49c214SYang-Rong Jerry Zhou }	/* audiohd_reinit_hda */
2427d5247f45Sgs 
2428582eadeeSfl /*
2429582eadeeSfl  * audiohd_init_controller()
2430582eadeeSfl  *
2431582eadeeSfl  * Description:
2432582eadeeSfl  *	This routine is used to initialize HD controller. It
2433582eadeeSfl  *	allocates DMA memory for CORB/RIRB, buffer descriptor
2434582eadeeSfl  *	list and cylic data buffer for both play and record
2435582eadeeSfl  *	stream.
2436582eadeeSfl  */
2437582eadeeSfl static int
audiohd_init_controller(audiohd_state_t * statep)2438582eadeeSfl audiohd_init_controller(audiohd_state_t *statep)
2439582eadeeSfl {
2440555989a4Scg 	uint64_t	addr;
2441582eadeeSfl 	uint16_t	gcap;
2442582eadeeSfl 	int		retval;
2443582eadeeSfl 
2444582eadeeSfl 	ddi_dma_attr_t	dma_attr = {
2445582eadeeSfl 		DMA_ATTR_V0,		/* version */
2446582eadeeSfl 		0,			/* addr_lo */
2447555989a4Scg 		0xffffffffffffffffULL,	/* addr_hi */
2448555989a4Scg 		0x00000000ffffffffULL,	/* count_max */
2449582eadeeSfl 		128,			/* 128-byte alignment as HD spec */
2450582eadeeSfl 		0xfff,			/* burstsize */
2451582eadeeSfl 		1,			/* minxfer */
2452582eadeeSfl 		0xffffffff,		/* maxxfer */
2453582eadeeSfl 		0xffffffff,		/* seg */
2454582eadeeSfl 		1,			/* sgllen */
2455582eadeeSfl 		1,			/* granular */
2456582eadeeSfl 		0			/* flags */
2457582eadeeSfl 	};
2458582eadeeSfl 
2459582eadeeSfl 	gcap = AUDIOHD_REG_GET16(AUDIOHD_REG_GCAP);
2460555989a4Scg 
2461555989a4Scg 	/*
2462555989a4Scg 	 * If the device doesn't support 64-bit DMA, we should not
2463555989a4Scg 	 * allocate DMA memory from 4G above
2464555989a4Scg 	 */
2465555989a4Scg 	if ((gcap & AUDIOHDR_GCAP_64OK) == 0)
2466555989a4Scg 		dma_attr.dma_attr_addr_hi = 0xffffffffUL;
2467555989a4Scg 
24683a49c214SYang-Rong Jerry Zhou 	statep->hda_input_streams = (gcap & AUDIOHDR_GCAP_INSTREAMS) >>
24693a49c214SYang-Rong Jerry Zhou 	    AUDIOHD_INSTR_NUM_OFF;
24703a49c214SYang-Rong Jerry Zhou 	statep->hda_output_streams = (gcap & AUDIOHDR_GCAP_OUTSTREAMS) >>
24713a49c214SYang-Rong Jerry Zhou 	    AUDIOHD_OUTSTR_NUM_OFF;
2472582eadeeSfl 	statep->hda_streams_nums = statep->hda_input_streams +
2473582eadeeSfl 	    statep->hda_output_streams;
2474582eadeeSfl 
2475582eadeeSfl 	statep->hda_record_regbase = AUDIOHD_REG_SD_BASE;
2476582eadeeSfl 	statep->hda_play_regbase = AUDIOHD_REG_SD_BASE + AUDIOHD_REG_SD_LEN *
2477582eadeeSfl 	    statep->hda_input_streams;
2478582eadeeSfl 
2479582eadeeSfl 	/* stop all dma before starting to reset controller */
2480582eadeeSfl 	audiohd_stop_dma(statep);
2481582eadeeSfl 
2482c6e681c0SYang-Rong Jerry Zhou 	if (audiohd_reset_controller(statep) != DDI_SUCCESS)
2483c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
2484582eadeeSfl 
2485582eadeeSfl 	/* check codec */
2486582eadeeSfl 	statep->hda_codec_mask = AUDIOHD_REG_GET16(AUDIOHD_REG_STATESTS);
2487c6e681c0SYang-Rong Jerry Zhou 	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);
2491582eadeeSfl 	}
2492582eadeeSfl 
2493582eadeeSfl 	/* allocate DMA for CORB */
2494582eadeeSfl 	retval = audiohd_alloc_dma_mem(statep, &statep->hda_dma_corb,
2495582eadeeSfl 	    AUDIOHD_CDBIO_CORB_LEN, &dma_attr,
2496a234d95bScg 	    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);
2501582eadeeSfl 	}
2502582eadeeSfl 
2503582eadeeSfl 	/* allocate DMA for RIRB */
2504582eadeeSfl 	retval = audiohd_alloc_dma_mem(statep, &statep->hda_dma_rirb,
2505582eadeeSfl 	    AUDIOHD_CDBIO_RIRB_LEN, &dma_attr,
2506a234d95bScg 	    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);
2511582eadeeSfl 	}
2512582eadeeSfl 
2513582eadeeSfl 	AUDIOHD_REG_SET32(AUDIOHD_REG_SYNC, 0); /* needn't sync stream */
2514582eadeeSfl 
2515582eadeeSfl 	/* Initialize RIRB */
2516582eadeeSfl 	addr = statep->hda_dma_rirb.ad_paddr;
2517555989a4Scg 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBLBASE, (uint32_t)addr);
2518c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_REG_SET32(AUDIOHD_REG_RIRBUBASE, (uint32_t)(addr >> 32));
2519582eadeeSfl 	AUDIOHD_REG_SET16(AUDIOHD_REG_RIRBWP, AUDIOHDR_RIRBWP_RESET);
2520582eadeeSfl 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSIZE, AUDIOHDR_RIRBSZ_256);
25213a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBCTL, AUDIOHDR_RIRBCTL_DMARUN |
25223a49c214SYang-Rong Jerry Zhou 	    AUDIOHDR_RIRBCTL_RINTCTL);
2523582eadeeSfl 
2524582eadeeSfl 	/* initialize CORB */
2525582eadeeSfl 	addr = statep->hda_dma_corb.ad_paddr;
2526582eadeeSfl 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, AUDIOHDR_CORBRP_RESET);
2527555989a4Scg 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBLBASE, (uint32_t)addr);
2528c7b817cfSZhao Edgar Liu - Sun Microsystems 	AUDIOHD_REG_SET32(AUDIOHD_REG_CORBUBASE, (uint32_t)(addr >> 32));
2529582eadeeSfl 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBSIZE, AUDIOHDR_CORBSZ_256);
2530582eadeeSfl 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, 0);
2531582eadeeSfl 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBRP, 0);
2532582eadeeSfl 	AUDIOHD_REG_SET8(AUDIOHD_REG_CORBCTL, AUDIOHDR_CORBCTL_DMARUN);
2533582eadeeSfl 
2534c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
2535582eadeeSfl }	/* audiohd_init_controller() */
2536582eadeeSfl 
2537582eadeeSfl /*
2538582eadeeSfl  * audiohd_fini_controller()
2539582eadeeSfl  *
2540582eadeeSfl  * Description:
2541582eadeeSfl  *	Releases DMA memory allocated in audiohd_init_controller()
2542582eadeeSfl  */
2543582eadeeSfl static void
audiohd_fini_controller(audiohd_state_t * statep)2544582eadeeSfl audiohd_fini_controller(audiohd_state_t *statep)
2545582eadeeSfl {
2546582eadeeSfl 	audiohd_release_dma_mem(&statep->hda_dma_rirb);
2547582eadeeSfl 	audiohd_release_dma_mem(&statep->hda_dma_corb);
2548582eadeeSfl 
2549582eadeeSfl }	/* audiohd_fini_controller() */
2550582eadeeSfl 
25513a49c214SYang-Rong Jerry Zhou /*
25523a49c214SYang-Rong Jerry Zhou  * audiohd_get_conns_from_entry()
25533a49c214SYang-Rong Jerry Zhou  *
25543a49c214SYang-Rong Jerry Zhou  * Description:
25553a49c214SYang-Rong Jerry Zhou  *	Get connection list from every entry for a widget
25563a49c214SYang-Rong Jerry Zhou  */
25573a49c214SYang-Rong Jerry Zhou static void
audiohd_get_conns_from_entry(hda_codec_t * codec,audiohd_widget_t * widget,uint32_t entry,audiohd_entry_prop_t * prop)25583a49c214SYang-Rong Jerry Zhou audiohd_get_conns_from_entry(hda_codec_t *codec, audiohd_widget_t *widget,
25593a49c214SYang-Rong Jerry Zhou     uint32_t entry, audiohd_entry_prop_t *prop)
25603a49c214SYang-Rong Jerry Zhou {
25613a49c214SYang-Rong Jerry Zhou 	int	i, k, num;
25623a49c214SYang-Rong Jerry Zhou 	wid_t	input_wid;
25633a49c214SYang-Rong Jerry Zhou 
25643a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < prop->conns_per_entry &&
25653a49c214SYang-Rong Jerry Zhou 	    widget->nconns < prop->conn_len;
25663a49c214SYang-Rong Jerry Zhou 	    i++, entry >>= prop->bits_per_conn) {
25673a49c214SYang-Rong Jerry Zhou 		ASSERT(widget->nconns < AUDIOHD_MAX_CONN);
25683a49c214SYang-Rong Jerry Zhou 		input_wid = entry & prop->mask_wid;
25693a49c214SYang-Rong Jerry Zhou 		if (entry & prop->mask_range) {
25703a49c214SYang-Rong Jerry Zhou 			if (widget->nconns == 0) {
25713a49c214SYang-Rong Jerry Zhou 				if (input_wid < codec->first_wid ||
25723a49c214SYang-Rong Jerry Zhou 				    (input_wid > codec->last_wid)) {
25733a49c214SYang-Rong Jerry Zhou 					break;
25743a49c214SYang-Rong Jerry Zhou 				}
25753a49c214SYang-Rong Jerry Zhou 				widget->avail_conn[widget->nconns++] =
25763a49c214SYang-Rong Jerry Zhou 				    input_wid;
25773a49c214SYang-Rong Jerry Zhou 			} else {
25783a49c214SYang-Rong Jerry Zhou 				for (k = widget->avail_conn[widget->nconns-1] +
25793a49c214SYang-Rong Jerry Zhou 				    1; k <= input_wid; k++) {
25803a49c214SYang-Rong Jerry Zhou 					ASSERT(widget->nconns <
25813a49c214SYang-Rong Jerry Zhou 					    AUDIOHD_MAX_CONN);
25823a49c214SYang-Rong Jerry Zhou 					if (k < codec->first_wid ||
25833a49c214SYang-Rong Jerry Zhou 					    (k > codec->last_wid)) {
25843a49c214SYang-Rong Jerry Zhou 						break;
25853a49c214SYang-Rong Jerry Zhou 					} else {
25863a49c214SYang-Rong Jerry Zhou 						num = widget->nconns;
25873a49c214SYang-Rong Jerry Zhou 						widget->avail_conn[num] = k;
25883a49c214SYang-Rong Jerry Zhou 						widget->nconns++;
25893a49c214SYang-Rong Jerry Zhou 					}
25903a49c214SYang-Rong Jerry Zhou 				}
25913a49c214SYang-Rong Jerry Zhou 			}
25923a49c214SYang-Rong Jerry Zhou 		} else {
259316600ba1SYang-Rong Jerry Zhou 			if ((codec->first_wid <= input_wid) && (input_wid <=
259416600ba1SYang-Rong Jerry Zhou 			    codec->last_wid))
259516600ba1SYang-Rong Jerry Zhou 				widget->avail_conn[widget->nconns++] =
259616600ba1SYang-Rong Jerry Zhou 				    input_wid;
25973a49c214SYang-Rong Jerry Zhou 		}
25983a49c214SYang-Rong Jerry Zhou 	}
25993a49c214SYang-Rong Jerry Zhou }
26003a49c214SYang-Rong Jerry Zhou 
26013a49c214SYang-Rong Jerry Zhou /*
26023a49c214SYang-Rong Jerry Zhou  * audiohd_get_conns()
26033a49c214SYang-Rong Jerry Zhou  *
26043a49c214SYang-Rong Jerry Zhou  * Description:
26053a49c214SYang-Rong Jerry Zhou  *	Get all connection list for a widget. The connection list is used for
26063a49c214SYang-Rong Jerry Zhou  *	build output path, input path, and monitor path
26073a49c214SYang-Rong Jerry Zhou  */
26083a49c214SYang-Rong Jerry Zhou static void
audiohd_get_conns(hda_codec_t * codec,wid_t wid)26093a49c214SYang-Rong Jerry Zhou audiohd_get_conns(hda_codec_t *codec, wid_t wid)
26103a49c214SYang-Rong Jerry Zhou {
2611ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
26123a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget = codec->widget[wid];
26133a49c214SYang-Rong Jerry Zhou 	uint8_t	caddr = codec->index;
26143a49c214SYang-Rong Jerry Zhou 	uint32_t	entry;
26153a49c214SYang-Rong Jerry Zhou 	audiohd_entry_prop_t	prop;
26163a49c214SYang-Rong Jerry Zhou 	wid_t	input_wid;
26173a49c214SYang-Rong Jerry Zhou 	int	i;
26183a49c214SYang-Rong Jerry Zhou 
26193a49c214SYang-Rong Jerry Zhou 	prop.conn_len = audioha_codec_verb_get(statep, caddr, wid,
26203a49c214SYang-Rong Jerry Zhou 	    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_CONNLIST_LEN);
26213a49c214SYang-Rong Jerry Zhou 
26223a49c214SYang-Rong Jerry Zhou 	if (prop.conn_len & AUDIOHD_FORM_MASK) {
26233a49c214SYang-Rong Jerry Zhou 		prop.conns_per_entry = 2;
26243a49c214SYang-Rong Jerry Zhou 		prop.bits_per_conn = 16;
26253a49c214SYang-Rong Jerry Zhou 		prop.mask_range = 0x00008000;
26263a49c214SYang-Rong Jerry Zhou 		prop.mask_wid = 0x00007fff;
26273a49c214SYang-Rong Jerry Zhou 	} else {
26283a49c214SYang-Rong Jerry Zhou 		prop.conns_per_entry = 4;
26293a49c214SYang-Rong Jerry Zhou 		prop.bits_per_conn = 8;
26303a49c214SYang-Rong Jerry Zhou 		prop.mask_range = 0x00000080;
26313a49c214SYang-Rong Jerry Zhou 		prop.mask_wid = 0x0000007f;
26323a49c214SYang-Rong Jerry Zhou 	}
26333a49c214SYang-Rong Jerry Zhou 	prop.conn_len &= AUDIOHD_LEN_MASK;
26343a49c214SYang-Rong Jerry Zhou 
26353a49c214SYang-Rong Jerry Zhou 	/*
26363a49c214SYang-Rong Jerry Zhou 	 * This should not happen since the ConnectionList bit of
26373a49c214SYang-Rong Jerry Zhou 	 * widget capabilities already told us that this widget
26383a49c214SYang-Rong Jerry Zhou 	 * has a connection list
26393a49c214SYang-Rong Jerry Zhou 	 */
26403a49c214SYang-Rong Jerry Zhou 	if (prop.conn_len == 0) {
26413a49c214SYang-Rong Jerry Zhou 		widget->nconns = 0;
264207bec7ccSZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(statep->adev,
264307bec7ccSZhao Edgar Liu - Sun Microsystems 		    "node %d has 0 connections", wid);
26443a49c214SYang-Rong Jerry Zhou 		return;
26453a49c214SYang-Rong Jerry Zhou 	}
26463a49c214SYang-Rong Jerry Zhou 
26473a49c214SYang-Rong Jerry Zhou 	if (prop.conn_len == 1) {
26483a49c214SYang-Rong Jerry Zhou 		entry = audioha_codec_verb_get(statep, caddr,
26493a49c214SYang-Rong Jerry Zhou 		    wid, AUDIOHDC_VERB_GET_CONN_LIST_ENT, 0);
26503a49c214SYang-Rong Jerry Zhou 		input_wid = entry & prop.mask_wid;
26513a49c214SYang-Rong Jerry Zhou 		if ((input_wid < codec->first_wid) ||
26523a49c214SYang-Rong Jerry Zhou 		    (input_wid > codec->last_wid)) {
26533a49c214SYang-Rong Jerry Zhou 			return;
26543a49c214SYang-Rong Jerry Zhou 		}
26553a49c214SYang-Rong Jerry Zhou 		widget->avail_conn[0] = input_wid;
26563a49c214SYang-Rong Jerry Zhou 		widget->nconns = 1;
26573a49c214SYang-Rong Jerry Zhou 		return;
26583a49c214SYang-Rong Jerry Zhou 	}
26593a49c214SYang-Rong Jerry Zhou 	widget->nconns = 0;
26603a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < prop.conn_len; i += prop.conns_per_entry) {
26613a49c214SYang-Rong Jerry Zhou 		entry = audioha_codec_verb_get(statep, caddr, wid,
26623a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_CONN_LIST_ENT, i);
26633a49c214SYang-Rong Jerry Zhou 		audiohd_get_conns_from_entry(codec, widget, entry, &prop);
26643a49c214SYang-Rong Jerry Zhou 	}
26653a49c214SYang-Rong Jerry Zhou }
26663a49c214SYang-Rong Jerry Zhou 
26673a49c214SYang-Rong Jerry Zhou /*
26683a49c214SYang-Rong Jerry Zhou  * Read PinCapabilities & default configuration
26693a49c214SYang-Rong Jerry Zhou  */
26703a49c214SYang-Rong Jerry Zhou static void
audiohd_get_pin_config(audiohd_widget_t * widget)26713a49c214SYang-Rong Jerry Zhou audiohd_get_pin_config(audiohd_widget_t *widget)
26723a49c214SYang-Rong Jerry Zhou {
26733a49c214SYang-Rong Jerry Zhou 	hda_codec_t		*codec = widget->codec;
2674ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
26753a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin, *prev, *p;
26763a49c214SYang-Rong Jerry Zhou 
26773a49c214SYang-Rong Jerry Zhou 	int		caddr = codec->index;
26783a49c214SYang-Rong Jerry Zhou 	wid_t		wid = widget->wid_wid;
26793a49c214SYang-Rong Jerry Zhou 	uint32_t	cap, config, pinctrl;
2680d5145224SYang-Rong Jerry Zhou 	uint8_t		urctrl, vrefbits;
26813a49c214SYang-Rong Jerry Zhou 
26823a49c214SYang-Rong Jerry Zhou 	cap = audioha_codec_verb_get(statep, caddr, wid,
26833a49c214SYang-Rong Jerry Zhou 	    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PIN_CAP);
26843a49c214SYang-Rong Jerry Zhou 	config = audioha_codec_verb_get(statep, caddr,
26853a49c214SYang-Rong Jerry Zhou 	    wid, AUDIOHDC_VERB_GET_DEFAULT_CONF, 0);
26863a49c214SYang-Rong Jerry Zhou 	pinctrl = audioha_codec_verb_get(statep, caddr,
26873a49c214SYang-Rong Jerry Zhou 	    wid, AUDIOHDC_VERB_GET_PIN_CTRL, 0);
26883a49c214SYang-Rong Jerry Zhou 
26893a49c214SYang-Rong Jerry Zhou 	pin = (audiohd_pin_t *)kmem_zalloc(sizeof (audiohd_pin_t), KM_SLEEP);
26903a49c214SYang-Rong Jerry Zhou 	widget->priv = pin;
26913a49c214SYang-Rong Jerry Zhou 
26923a49c214SYang-Rong Jerry Zhou 	/*
26933a49c214SYang-Rong Jerry Zhou 	 * If the pin has no physical connection for port,
26943a49c214SYang-Rong Jerry Zhou 	 * we won't link it to pin linkage list ???
26953a49c214SYang-Rong Jerry Zhou 	 */
26963a49c214SYang-Rong Jerry Zhou 	if (((config >> AUDIOHD_PIN_CON_STEP) & AUDIOHD_PIN_CON_MASK) == 0x1) {
26973a49c214SYang-Rong Jerry Zhou 		pin->no_phys_conn = 1;
26983a49c214SYang-Rong Jerry Zhou 	}
26993a49c214SYang-Rong Jerry Zhou 
27003a49c214SYang-Rong Jerry Zhou 	/* bit 4:3 are reserved, read-modify-write is needed */
27013a49c214SYang-Rong Jerry Zhou 	pin->ctrl = pinctrl & AUDIOHD_PIN_IO_MASK;
27023a49c214SYang-Rong Jerry Zhou 	pin->wid = wid;
27033a49c214SYang-Rong Jerry Zhou 	pin->cap = cap;
27043a49c214SYang-Rong Jerry Zhou 	pin->config = config;
27053a49c214SYang-Rong Jerry Zhou 	pin->num = 0;
27063a49c214SYang-Rong Jerry Zhou 	pin->finish = 0;
270788447a05SGarrett D'Amore 
2708d5145224SYang-Rong Jerry Zhou 	vrefbits = (cap >> AUDIOHD_PIN_VREF_OFF) & AUDIOHD_PIN_VREF_MASK;
2709d5145224SYang-Rong Jerry Zhou 	if (vrefbits & AUDIOHD_PIN_VREF_L1)
2710d5145224SYang-Rong Jerry Zhou 		pin->vrefvalue = 0x5;
2711d5145224SYang-Rong Jerry Zhou 	else if (vrefbits & AUDIOHD_PIN_VREF_L2)
2712d5145224SYang-Rong Jerry Zhou 		pin->vrefvalue = 0x4;
2713d5145224SYang-Rong Jerry Zhou 	else if (vrefbits & AUDIOHD_PIN_VREF_L3)
2714d5145224SYang-Rong Jerry Zhou 		pin->vrefvalue = 0x2;
2715d5145224SYang-Rong Jerry Zhou 	else
2716d5145224SYang-Rong Jerry Zhou 		pin->vrefvalue = 0x1;
27173a49c214SYang-Rong Jerry Zhou 
27183a49c214SYang-Rong Jerry Zhou 	pin->seq = config & AUDIOHD_PIN_SEQ_MASK;
27193a49c214SYang-Rong Jerry Zhou 	pin->assoc = (config & AUDIOHD_PIN_ASO_MASK) >> AUDIOHD_PIN_ASO_OFF;
27203a49c214SYang-Rong Jerry Zhou 	pin->device = (config & AUDIOHD_PIN_DEV_MASK) >> AUDIOHD_PIN_DEV_OFF;
27213a49c214SYang-Rong Jerry Zhou 
27223a49c214SYang-Rong Jerry Zhou 	/* enable the unsolicited response of the pin */
272316600ba1SYang-Rong Jerry Zhou 	if ((widget->widget_cap & AUDIOHD_URCAP_MASK) &&
272416600ba1SYang-Rong Jerry Zhou 	    (pin->cap & AUDIOHD_DTCCAP_MASK) &&
27253a49c214SYang-Rong Jerry Zhou 	    ((pin->device == DTYPE_LINEOUT) ||
27263a49c214SYang-Rong Jerry Zhou 	    (pin->device == DTYPE_SPDIF_OUT) ||
27273a49c214SYang-Rong Jerry Zhou 	    (pin->device == DTYPE_HP_OUT) ||
27283a49c214SYang-Rong Jerry Zhou 	    (pin->device == DTYPE_MIC_IN))) {
27293a49c214SYang-Rong Jerry Zhou 			urctrl = (uint8_t)(1 << (AUDIOHD_UR_ENABLE_OFF - 1));
27303a49c214SYang-Rong Jerry Zhou 			urctrl |= (wid & AUDIOHD_UR_TAG_MASK);
27313a49c214SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, caddr,
2732989b958fSZhao Edgar Liu - Sun Microsystems 			    wid, AUDIOHDC_VERB_SET_UNS_ENABLE, urctrl);
27333a49c214SYang-Rong Jerry Zhou 	}
27343a49c214SYang-Rong Jerry Zhou 	/* accommodate all the pins in a link list sorted by assoc and seq */
27353a49c214SYang-Rong Jerry Zhou 	if (codec->first_pin == NULL) {
27363a49c214SYang-Rong Jerry Zhou 		codec->first_pin = pin;
27373a49c214SYang-Rong Jerry Zhou 	} else {
27383a49c214SYang-Rong Jerry Zhou 		prev = NULL;
27393a49c214SYang-Rong Jerry Zhou 		p = codec->first_pin;
27403a49c214SYang-Rong Jerry Zhou 		while (p) {
27413a49c214SYang-Rong Jerry Zhou 			if (p->assoc > pin->assoc)
27423a49c214SYang-Rong Jerry Zhou 				break;
27433a49c214SYang-Rong Jerry Zhou 			if ((p->assoc == pin->assoc) &&
27443a49c214SYang-Rong Jerry Zhou 			    (p->seq > pin->seq))
27453a49c214SYang-Rong Jerry Zhou 				break;
27463a49c214SYang-Rong Jerry Zhou 			prev = p;
27473a49c214SYang-Rong Jerry Zhou 			p = p->next;
27483a49c214SYang-Rong Jerry Zhou 		}
27493a49c214SYang-Rong Jerry Zhou 		if (prev) {
27503a49c214SYang-Rong Jerry Zhou 			pin->next = prev->next;
27513a49c214SYang-Rong Jerry Zhou 			prev->next = pin;
27523a49c214SYang-Rong Jerry Zhou 		} else {
27533a49c214SYang-Rong Jerry Zhou 			pin->next = codec->first_pin;
27543a49c214SYang-Rong Jerry Zhou 			codec->first_pin = pin;
27553a49c214SYang-Rong Jerry Zhou 		}
27563a49c214SYang-Rong Jerry Zhou 	}
27573a49c214SYang-Rong Jerry Zhou 
27583a49c214SYang-Rong Jerry Zhou }	/* audiohd_get_pin_config() */
27593a49c214SYang-Rong Jerry Zhou 
27603a49c214SYang-Rong Jerry Zhou /*
27613a49c214SYang-Rong Jerry Zhou  * audiohd_create_widgets()
27623a49c214SYang-Rong Jerry Zhou  *
27633a49c214SYang-Rong Jerry Zhou  * Description:
27643a49c214SYang-Rong Jerry Zhou  *	All widgets are created and stored in an array of codec
27653a49c214SYang-Rong Jerry Zhou  */
27663a49c214SYang-Rong Jerry Zhou static int
audiohd_create_widgets(hda_codec_t * codec)27673a49c214SYang-Rong Jerry Zhou audiohd_create_widgets(hda_codec_t *codec)
27683a49c214SYang-Rong Jerry Zhou {
27693a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget;
2770ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
27713a49c214SYang-Rong Jerry Zhou 	wid_t	wid;
27723a49c214SYang-Rong Jerry Zhou 	uint32_t	type, widcap;
27733a49c214SYang-Rong Jerry Zhou 	int		caddr = codec->index;
27743a49c214SYang-Rong Jerry Zhou 
27753a49c214SYang-Rong Jerry Zhou 	for (wid = codec->first_wid;
27763a49c214SYang-Rong Jerry Zhou 	    wid <= codec->last_wid; wid++) {
27773a49c214SYang-Rong Jerry Zhou 		widget = (audiohd_widget_t *)
27783a49c214SYang-Rong Jerry Zhou 		    kmem_zalloc(sizeof (audiohd_widget_t), KM_SLEEP);
27793a49c214SYang-Rong Jerry Zhou 		codec->widget[wid] = widget;
27803a49c214SYang-Rong Jerry Zhou 		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;
27853a49c214SYang-Rong Jerry Zhou 
27863a49c214SYang-Rong Jerry Zhou 		widcap = audioha_codec_verb_get(statep, caddr, wid,
27873a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_AUDIO_WID_CAP);
27883a49c214SYang-Rong Jerry Zhou 		type = AUDIOHD_WIDCAP_TO_WIDTYPE(widcap);
27893a49c214SYang-Rong Jerry Zhou 		widget->wid_wid = wid;
27903a49c214SYang-Rong Jerry Zhou 		widget->type = type;
27913a49c214SYang-Rong Jerry Zhou 		widget->widget_cap = widcap;
27923a49c214SYang-Rong Jerry Zhou 		widget->finish = 0;
27933a49c214SYang-Rong Jerry Zhou 		widget->used = 0;
27943a49c214SYang-Rong Jerry Zhou 
27953a49c214SYang-Rong Jerry Zhou 		/* if there's connection list */
27963a49c214SYang-Rong Jerry Zhou 		if (widcap & AUDIOHD_WIDCAP_CONNLIST) {
27973a49c214SYang-Rong Jerry Zhou 			audiohd_get_conns(codec, wid);
27983a49c214SYang-Rong Jerry Zhou 		}
27993a49c214SYang-Rong Jerry Zhou 
28003a49c214SYang-Rong Jerry Zhou 		/* if power control, power it up to D0 state */
28013a49c214SYang-Rong Jerry Zhou 		if (widcap & AUDIOHD_WIDCAP_PWRCTRL) {
28023a49c214SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, caddr, wid,
28033a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_POWER_STATE, 0);
28043a49c214SYang-Rong Jerry Zhou 		}
28053a49c214SYang-Rong Jerry Zhou 
28063a49c214SYang-Rong Jerry Zhou 		/*
28073a49c214SYang-Rong Jerry Zhou 		 * if this widget has format override, we read it.
28083a49c214SYang-Rong Jerry Zhou 		 * Otherwise, it uses the format of audio function.
28093a49c214SYang-Rong Jerry Zhou 		 */
28103a49c214SYang-Rong Jerry Zhou 		if (widcap & AUDIOHD_WIDCAP_FMT_OVRIDE) {
28113a49c214SYang-Rong Jerry Zhou 			widget->pcm_format =
28123a49c214SYang-Rong Jerry Zhou 			    audioha_codec_verb_get(statep, caddr, wid,
28133a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PCM);
28143a49c214SYang-Rong Jerry Zhou 		} else {
28153a49c214SYang-Rong Jerry Zhou 			widget->pcm_format = codec->pcm_format;
28163a49c214SYang-Rong Jerry Zhou 		}
28173a49c214SYang-Rong Jerry Zhou 
28183a49c214SYang-Rong Jerry Zhou 		/*
28193a49c214SYang-Rong Jerry Zhou 		 * Input amplifier. Has the widget input amplifier ?
28203a49c214SYang-Rong Jerry Zhou 		 */
28213a49c214SYang-Rong Jerry Zhou 		if (widcap & AUDIOHD_WIDCAP_INAMP) {
28223a49c214SYang-Rong Jerry Zhou 			/*
28233a49c214SYang-Rong Jerry Zhou 			 * if overrided bit is 0, use the default
28243a49c214SYang-Rong Jerry Zhou 			 * amplifier of audio function as HD spec.
28253a49c214SYang-Rong Jerry Zhou 			 * Otherwise, we read it.
28263a49c214SYang-Rong Jerry Zhou 			 */
28273a49c214SYang-Rong Jerry Zhou 			if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
28283a49c214SYang-Rong Jerry Zhou 				widget->inamp_cap = codec->inamp_cap;
28293a49c214SYang-Rong Jerry Zhou 			else
28303a49c214SYang-Rong Jerry Zhou 				widget->inamp_cap =
28313a49c214SYang-Rong Jerry Zhou 				    audioha_codec_verb_get(statep, caddr, wid,
28323a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_VERB_GET_PARAM,
28333a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_PAR_INAMP_CAP);
28343a49c214SYang-Rong Jerry Zhou 		} else {
28353a49c214SYang-Rong Jerry Zhou 			widget->inamp_cap = 0;
28363a49c214SYang-Rong Jerry Zhou 		}
28373a49c214SYang-Rong Jerry Zhou 
28383a49c214SYang-Rong Jerry Zhou 		/*
28393a49c214SYang-Rong Jerry Zhou 		 * output amplifier. Has this widget output amplifier ?
28403a49c214SYang-Rong Jerry Zhou 		 */
28413a49c214SYang-Rong Jerry Zhou 		if (widcap & AUDIOHD_WIDCAP_OUTAMP) {
28423a49c214SYang-Rong Jerry Zhou 			if ((widcap & AUDIOHD_WIDCAP_AMP_OVRIDE) == 0)
28433a49c214SYang-Rong Jerry Zhou 				widget->outamp_cap = codec->outamp_cap;
28443a49c214SYang-Rong Jerry Zhou 			else
28453a49c214SYang-Rong Jerry Zhou 				widget->outamp_cap =
28463a49c214SYang-Rong Jerry Zhou 				    audioha_codec_verb_get(statep, caddr, wid,
28473a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_VERB_GET_PARAM,
28483a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_PAR_OUTAMP_CAP);
28493a49c214SYang-Rong Jerry Zhou 		} else {
28503a49c214SYang-Rong Jerry Zhou 			widget->outamp_cap = 0;
28513a49c214SYang-Rong Jerry Zhou 		}
28523a49c214SYang-Rong Jerry Zhou 
28533a49c214SYang-Rong Jerry Zhou 		switch (type) {
28543a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_OUT:
28553a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_IN:
28563a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_MIX:
28573a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_SEL:
28583a49c214SYang-Rong Jerry Zhou 		case WTYPE_VENDOR:
28593a49c214SYang-Rong Jerry Zhou 		case WTYPE_POWER:
28603a49c214SYang-Rong Jerry Zhou 		case WTYPE_VOL_KNOB:
28613a49c214SYang-Rong Jerry Zhou 			break;
28623a49c214SYang-Rong Jerry Zhou 		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 
28783a49c214SYang-Rong Jerry Zhou 			audiohd_get_pin_config(widget);
28793a49c214SYang-Rong Jerry Zhou 			break;
28803a49c214SYang-Rong Jerry Zhou 		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 			}
28963a49c214SYang-Rong Jerry Zhou 			break;
28973a49c214SYang-Rong Jerry Zhou 		default:
28983a49c214SYang-Rong Jerry Zhou 			break;
28993a49c214SYang-Rong Jerry Zhou 		}
29003a49c214SYang-Rong Jerry Zhou 	}
29013a49c214SYang-Rong Jerry Zhou 
29023a49c214SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
29033a49c214SYang-Rong Jerry Zhou 
29043a49c214SYang-Rong Jerry Zhou }	/* audiohd_create_widgets() */
29053a49c214SYang-Rong Jerry Zhou 
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 
2921582eadeeSfl /*
2922582eadeeSfl  * audiohd_create_codec()
2923582eadeeSfl  *
2924582eadeeSfl  * Description:
2925582eadeeSfl  *	Searching for supported CODEC. If find, allocate memory
2926582eadeeSfl  *	to hold codec structure.
2927582eadeeSfl  */
2928582eadeeSfl static int
audiohd_create_codec(audiohd_state_t * statep)2929582eadeeSfl audiohd_create_codec(audiohd_state_t *statep)
2930582eadeeSfl {
29313a49c214SYang-Rong Jerry Zhou 	hda_codec_t	*codec;
2932582eadeeSfl 	uint32_t	mask, type;
29333a49c214SYang-Rong Jerry Zhou 	uint32_t	nums;
2934cbe6566fSZhao Edgar Liu - Sun Microsystems 	uint32_t	i, j, len;
29353a49c214SYang-Rong Jerry Zhou 	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 
2940582eadeeSfl 
2941582eadeeSfl 	mask = statep->hda_codec_mask;
2942582eadeeSfl 	ASSERT(mask != 0);
2943582eadeeSfl 
2944582eadeeSfl 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
2945582eadeeSfl 		if ((mask & (1 << i)) == 0)
2946582eadeeSfl 			continue;
29473a49c214SYang-Rong Jerry Zhou 		codec = (hda_codec_t *)kmem_zalloc(
29483a49c214SYang-Rong Jerry Zhou 		    sizeof (hda_codec_t), KM_SLEEP);
29493a49c214SYang-Rong Jerry Zhou 		codec->index = i;
29503a49c214SYang-Rong Jerry Zhou 		codec->vid = audioha_codec_verb_get(statep, i,
2951582eadeeSfl 		    AUDIOHDC_NODE_ROOT, AUDIOHDC_VERB_GET_PARAM,
2952582eadeeSfl 		    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 
29583a49c214SYang-Rong Jerry Zhou 		codec->revid =
29593a49c214SYang-Rong Jerry Zhou 		    audioha_codec_verb_get(statep, i,
2960582eadeeSfl 		    AUDIOHDC_NODE_ROOT, AUDIOHDC_VERB_GET_PARAM,
2961582eadeeSfl 		    AUDIOHDC_PAR_REV_ID);
2962582eadeeSfl 
29633a49c214SYang-Rong Jerry Zhou 		nums = audioha_codec_verb_get(statep,
29643a49c214SYang-Rong Jerry Zhou 		    i, AUDIOHDC_NODE_ROOT,
2965582eadeeSfl 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_NODE_COUNT);
29663a49c214SYang-Rong Jerry Zhou 		if (nums == (uint32_t)(-1)) {
29673a49c214SYang-Rong Jerry Zhou 			kmem_free(codec, sizeof (hda_codec_t));
29683a49c214SYang-Rong Jerry Zhou 			continue;
29693a49c214SYang-Rong Jerry Zhou 		}
29703a49c214SYang-Rong Jerry Zhou 		wid = (nums >> AUDIOHD_CODEC_STR_OFF) & AUDIOHD_CODEC_STR_MASK;
29713a49c214SYang-Rong Jerry Zhou 		nums = nums & AUDIOHD_CODEC_NUM_MASK;
2972582eadeeSfl 
29733a49c214SYang-Rong Jerry Zhou 		/*
29743a49c214SYang-Rong Jerry Zhou 		 * Assume that each codec has just one audio function group
29753a49c214SYang-Rong Jerry Zhou 		 */
29763a49c214SYang-Rong Jerry Zhou 		for (j = 0; j < nums; j++, wid++) {
29773a49c214SYang-Rong Jerry Zhou 			type = audioha_codec_verb_get(statep, i, wid,
2978582eadeeSfl 			    AUDIOHDC_VERB_GET_PARAM,
2979582eadeeSfl 			    AUDIOHDC_PAR_FUNCTION_TYPE);
29803a49c214SYang-Rong Jerry Zhou 			if ((type & AUDIOHD_CODEC_TYPE_MASK) ==
29813a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_AUDIO_FUNC_GROUP) {
29823a49c214SYang-Rong Jerry Zhou 				codec->wid_afg = wid;
2983582eadeeSfl 				break;
2984582eadeeSfl 			}
2985582eadeeSfl 		}
2986582eadeeSfl 
29873a49c214SYang-Rong Jerry Zhou 		if (codec->wid_afg == 0) {
29883a49c214SYang-Rong Jerry Zhou 			kmem_free(codec, sizeof (hda_codec_t));
29893a49c214SYang-Rong Jerry Zhou 			continue;
29903a49c214SYang-Rong Jerry Zhou 		}
2991582eadeeSfl 
29923a49c214SYang-Rong Jerry Zhou 		ASSERT(codec->wid_afg == wid);
2993582eadeeSfl 
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);
3021989b958fSZhao Edgar Liu - Sun Microsystems 			(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);
30249ba19c87SYang-Rong Jerry Zhou 			(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 
30343a49c214SYang-Rong Jerry Zhou 		/* power-up audio function group */
30353a49c214SYang-Rong Jerry Zhou 		(void) audioha_codec_verb_get(statep, i, wid,
30365ec2209cSZhao Edgar Liu - Sun Microsystems 		    AUDIOHDC_VERB_SET_POWER_STATE, AUDIOHD_PW_D0);
3037582eadeeSfl 
30383a49c214SYang-Rong Jerry Zhou 		/* subsystem id is attached to funtion group */
30393a49c214SYang-Rong Jerry Zhou 		codec->outamp_cap = audioha_codec_verb_get(statep, i, wid,
30403a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_OUTAMP_CAP);
30413a49c214SYang-Rong Jerry Zhou 		codec->inamp_cap = audioha_codec_verb_get(statep, i, wid,
30423a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_INAMP_CAP);
30433a49c214SYang-Rong Jerry Zhou 		codec->stream_format = audioha_codec_verb_get(statep, i, wid,
30443a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_STREAM);
30453a49c214SYang-Rong Jerry Zhou 		codec->pcm_format = audioha_codec_verb_get(statep, i, wid,
30463a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_PCM);
30473a49c214SYang-Rong Jerry Zhou 
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 
30713a49c214SYang-Rong Jerry Zhou 		nums = audioha_codec_verb_get(statep, i, wid,
30723a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PARAM,
30733a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_PAR_NODE_COUNT);
30743a49c214SYang-Rong Jerry Zhou 		wid = (nums >> AUDIOHD_CODEC_STR_OFF) & AUDIOHD_CODEC_STR_MASK;
30753a49c214SYang-Rong Jerry Zhou 		nums = nums & AUDIOHD_CODEC_NUM_MASK;
30763a49c214SYang-Rong Jerry Zhou 		codec->first_wid = wid;
30773a49c214SYang-Rong Jerry Zhou 		codec->last_wid = wid + nums;
30783a49c214SYang-Rong Jerry Zhou 		codec->nnodes = nums;
30793a49c214SYang-Rong Jerry Zhou 
30803a49c214SYang-Rong Jerry Zhou 		/*
30813a49c214SYang-Rong Jerry Zhou 		 * We output the codec information to syslog
30823a49c214SYang-Rong Jerry Zhou 		 */
30833a49c214SYang-Rong Jerry Zhou 		statep->codec[i] = codec;
3084ea463888SZhao Edgar Liu - Sun Microsystems 		codec->statep = statep;
30853a49c214SYang-Rong Jerry Zhou 		(void) audiohd_create_widgets(codec);
3086582eadeeSfl 	}
3087582eadeeSfl 
3088c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
3089582eadeeSfl 
3090582eadeeSfl }	/* audiohd_create_codec() */
3091582eadeeSfl 
3092582eadeeSfl /*
3093582eadeeSfl  * audiohd_destroy_codec()
3094582eadeeSfl  *
3095582eadeeSfl  * Description:
30963a49c214SYang-Rong Jerry Zhou  *	destroy codec structure, and release its memory
3097582eadeeSfl  */
3098582eadeeSfl static void
audiohd_destroy_codec(audiohd_state_t * statep)3099582eadeeSfl audiohd_destroy_codec(audiohd_state_t *statep)
3100582eadeeSfl {
310188447a05SGarrett D'Amore 	int			i;
31023a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin, *npin;
3103582eadeeSfl 
310488447a05SGarrett D'Amore 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
31053a49c214SYang-Rong Jerry Zhou 		if (statep->codec[i]) {
31063a49c214SYang-Rong Jerry Zhou 			audiohd_destroy_widgets(statep->codec[i]);
31073a49c214SYang-Rong Jerry Zhou 			/*
31083a49c214SYang-Rong Jerry Zhou 			 * free pins
31093a49c214SYang-Rong Jerry Zhou 			 */
31103a49c214SYang-Rong Jerry Zhou 			pin = statep->codec[i]->first_pin;
31113a49c214SYang-Rong Jerry Zhou 			while (pin) {
31123a49c214SYang-Rong Jerry Zhou 				npin = pin;
31133a49c214SYang-Rong Jerry Zhou 				pin = pin->next;
31143a49c214SYang-Rong Jerry Zhou 				kmem_free(npin, sizeof (audiohd_pin_t));
31153a49c214SYang-Rong Jerry Zhou 			}
31163a49c214SYang-Rong Jerry Zhou 
31173a49c214SYang-Rong Jerry Zhou 			kmem_free(statep->codec[i], sizeof (hda_codec_t));
31183a49c214SYang-Rong Jerry Zhou 			statep->codec[i] = NULL;
31193a49c214SYang-Rong Jerry Zhou 		}
31203a49c214SYang-Rong Jerry Zhou 	}
3121582eadeeSfl }	/* audiohd_destroy_codec() */
3122582eadeeSfl 
31237253a143Scg /*
31243a49c214SYang-Rong Jerry Zhou  * audiohd_find_dac()
31253a49c214SYang-Rong Jerry Zhou  * Description:
31263a49c214SYang-Rong Jerry Zhou  *	Find a dac for a output path. Then the play data can be sent to the out
31273a49c214SYang-Rong Jerry Zhou  *	put pin through the output path.
31283a49c214SYang-Rong Jerry Zhou  *
31293a49c214SYang-Rong Jerry Zhou  * Arguments:
31303a49c214SYang-Rong Jerry Zhou  *	hda_codec_t	*codec		where the dac widget exists
31313a49c214SYang-Rong Jerry Zhou  *	wid_t		wid		the no. of a widget
31323a49c214SYang-Rong Jerry Zhou  *	int		mixer		whether the path need mixer or not
31333a49c214SYang-Rong Jerry Zhou  *	int		*mixernum	the total of mixer in the output path
31343a49c214SYang-Rong Jerry Zhou  *	int		exclusive	an exclusive path or share path
31353a49c214SYang-Rong Jerry Zhou  *	int		depth		the depth of search
31363a49c214SYang-Rong Jerry Zhou  *
31373a49c214SYang-Rong Jerry Zhou  * Return:
31383a49c214SYang-Rong Jerry Zhou  *	1) wid of the first shared widget in the path from
31393a49c214SYang-Rong Jerry Zhou  *	   pin to DAC if exclusive is 0;
31403a49c214SYang-Rong Jerry Zhou  *	2) wid of DAC widget;
31413a49c214SYang-Rong Jerry Zhou  *	3) 0 if no path
31427253a143Scg  */
31433a49c214SYang-Rong Jerry Zhou static wid_t
audiohd_find_dac(hda_codec_t * codec,wid_t wid,int mixer,int * mixernum,int exclusive,int depth)31443a49c214SYang-Rong Jerry Zhou audiohd_find_dac(hda_codec_t *codec, wid_t wid,
31453a49c214SYang-Rong Jerry Zhou     int mixer, int *mixernum,
31463a49c214SYang-Rong Jerry Zhou     int exclusive, int depth)
31477253a143Scg {
31483a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget = codec->widget[wid];
3149c6e681c0SYang-Rong Jerry Zhou 	wid_t	wdac = (uint32_t)(DDI_FAILURE);
31503a49c214SYang-Rong Jerry Zhou 	wid_t	retval;
31517253a143Scg 
31523a49c214SYang-Rong Jerry Zhou 	if (depth > AUDIOHD_MAX_DEPTH)
3153c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
31547253a143Scg 
315531a3f136SYang-Rong Jerry Zhou 	if (widget == NULL)
3156c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
315788447a05SGarrett D'Amore 
31583a49c214SYang-Rong Jerry Zhou 	/*
31593a49c214SYang-Rong Jerry Zhou 	 * If exclusive is true, we try to find a path which doesn't
31603a49c214SYang-Rong Jerry Zhou 	 * share any widget with other paths.
31613a49c214SYang-Rong Jerry Zhou 	 */
31623a49c214SYang-Rong Jerry Zhou 	if (exclusive) {
31633a49c214SYang-Rong Jerry Zhou 		if (widget->path_flags & AUDIOHD_PATH_DAC)
3164c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
31653a49c214SYang-Rong Jerry Zhou 	} else {
31663a49c214SYang-Rong Jerry Zhou 		if (widget->path_flags & AUDIOHD_PATH_DAC)
31673a49c214SYang-Rong Jerry Zhou 			return (wid);
31683a49c214SYang-Rong Jerry Zhou 	}
31693a49c214SYang-Rong Jerry Zhou 
31703a49c214SYang-Rong Jerry Zhou 	switch (widget->type) {
31713a49c214SYang-Rong Jerry Zhou 	case WTYPE_AUDIO_OUT:
31723a49c214SYang-Rong Jerry Zhou 		/* We need mixer widget, but the the mixer num is 0, failed  */
31733a49c214SYang-Rong Jerry Zhou 		if (mixer && !*mixernum)
3174c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
31753a49c214SYang-Rong Jerry Zhou 		widget->path_flags |= AUDIOHD_PATH_DAC;
31763a49c214SYang-Rong Jerry Zhou 		widget->out_weight++;
31773a49c214SYang-Rong Jerry Zhou 		wdac = widget->wid_wid;
31783a49c214SYang-Rong Jerry Zhou 		break;
31793a49c214SYang-Rong Jerry Zhou 
31803a49c214SYang-Rong Jerry Zhou 	case WTYPE_AUDIO_MIX:
3181b96a6eceSZhao Edgar Liu - Sun Microsystems 		(*mixernum)++;
3182b96a6eceSZhao Edgar Liu - Sun Microsystems 		/* FALLTHRU */
31833a49c214SYang-Rong Jerry Zhou 	case WTYPE_AUDIO_SEL:
31843a49c214SYang-Rong Jerry Zhou 		for (int i = 0; i < widget->nconns; i++) {
31853a49c214SYang-Rong Jerry Zhou 			retval = audiohd_find_dac(codec,
31863a49c214SYang-Rong Jerry Zhou 			    widget->avail_conn[i],
31873a49c214SYang-Rong Jerry Zhou 			    mixer, mixernum,
31883a49c214SYang-Rong Jerry Zhou 			    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;
31933a49c214SYang-Rong Jerry Zhou 					wdac = retval;
31943a49c214SYang-Rong Jerry Zhou 				}
31953a49c214SYang-Rong Jerry Zhou 				widget->path_flags |= AUDIOHD_PATH_DAC;
31963a49c214SYang-Rong Jerry Zhou 				widget->out_weight++;
31973a49c214SYang-Rong Jerry Zhou 
31983a49c214SYang-Rong Jerry Zhou 				/* return when found a path */
31993a49c214SYang-Rong Jerry Zhou 				return (wdac);
32003a49c214SYang-Rong Jerry Zhou 			}
32013a49c214SYang-Rong Jerry Zhou 		}
32023a49c214SYang-Rong Jerry Zhou 	default:
32033a49c214SYang-Rong Jerry Zhou 		break;
32043a49c214SYang-Rong Jerry Zhou 	}
32053a49c214SYang-Rong Jerry Zhou 
32063a49c214SYang-Rong Jerry Zhou 	return (wdac);
32073a49c214SYang-Rong Jerry Zhou }	/* audiohd_find_dac() */
3208582eadeeSfl 
3209582eadeeSfl /*
32103a49c214SYang-Rong Jerry Zhou  * audiohd_do_build_output_path()
3211582eadeeSfl  *
3212582eadeeSfl  * Description:
32133a49c214SYang-Rong Jerry Zhou  *	Search an output path for each pin in the codec.
32143a49c214SYang-Rong Jerry Zhou  * Arguments:
32153a49c214SYang-Rong Jerry Zhou  *	hda_codec_t	*codec		where the output path exists
3216b96a6eceSZhao Edgar Liu - Sun Microsystems  *	int		mixer		whether the path needs mixer widget
32173a49c214SYang-Rong Jerry Zhou  *	int		*mnum		total of mixer widget in the path
32183a49c214SYang-Rong Jerry Zhou  *	int		exclusive	an exclusive path or shared path
32193a49c214SYang-Rong Jerry Zhou  *	int		depth		search depth
3220582eadeeSfl  */
3221582eadeeSfl static void
audiohd_do_build_output_path(hda_codec_t * codec,int mixer,int * mnum,int exclusive,int depth)32223a49c214SYang-Rong Jerry Zhou audiohd_do_build_output_path(hda_codec_t *codec, int mixer, int *mnum,
32233a49c214SYang-Rong Jerry Zhou     int exclusive, int depth)
3224582eadeeSfl {
32253a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
32263a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget, *wdac;
3227b96a6eceSZhao Edgar Liu - Sun Microsystems 	audiohd_path_t		*path;
32283a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
3229b96a6eceSZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep;
32303a49c214SYang-Rong Jerry Zhou 	int			i;
3231582eadeeSfl 
3232ea463888SZhao Edgar Liu - Sun Microsystems 	statep = codec->statep;
323388447a05SGarrett D'Amore 
32343a49c214SYang-Rong Jerry Zhou 	for (pin = codec->first_pin; pin; pin = pin->next) {
32353a49c214SYang-Rong Jerry Zhou 		if ((pin->cap & AUDIOHD_PIN_CAP_MASK) == 0)
32363a49c214SYang-Rong Jerry Zhou 			continue;
32373a49c214SYang-Rong Jerry Zhou 		if ((pin->config & AUDIOHD_PIN_CONF_MASK) ==
32383a49c214SYang-Rong Jerry Zhou 		    AUDIOHD_PIN_NO_CONN)
32393a49c214SYang-Rong Jerry Zhou 			continue;
32403a49c214SYang-Rong Jerry Zhou 		if ((pin->device != DTYPE_LINEOUT) &&
32413a49c214SYang-Rong Jerry Zhou 		    (pin->device != DTYPE_SPEAKER) &&
32423a49c214SYang-Rong Jerry Zhou 		    (pin->device != DTYPE_SPDIF_OUT) &&
32433a49c214SYang-Rong Jerry Zhou 		    (pin->device != DTYPE_HP_OUT))
32443a49c214SYang-Rong Jerry Zhou 			continue;
32453a49c214SYang-Rong Jerry Zhou 		if (pin->finish)
32463a49c214SYang-Rong Jerry Zhou 			continue;
32473a49c214SYang-Rong Jerry Zhou 		widget = codec->widget[pin->wid];
3248582eadeeSfl 
32493a49c214SYang-Rong Jerry Zhou 		widget->inamp_cap = 0;
32503a49c214SYang-Rong Jerry Zhou 		for (i = 0; i < widget->nconns; i++) {
32513a49c214SYang-Rong Jerry Zhou 			/*
32523a49c214SYang-Rong Jerry Zhou 			 * If a dac found, the return value is the wid of the
32533a49c214SYang-Rong Jerry Zhou 			 * widget on the path, or the return value is
3254c6e681c0SYang-Rong Jerry Zhou 			 * DDI_FAILURE
32553a49c214SYang-Rong Jerry Zhou 			 */
32563a49c214SYang-Rong Jerry Zhou 			wid = audiohd_find_dac(codec,
32573a49c214SYang-Rong Jerry Zhou 			    widget->avail_conn[i], mixer, mnum, exclusive,
32583a49c214SYang-Rong Jerry Zhou 			    depth);
32593a49c214SYang-Rong Jerry Zhou 			/*
32603a49c214SYang-Rong Jerry Zhou 			 * A dac was not found
32613a49c214SYang-Rong Jerry Zhou 			 */
3262c6e681c0SYang-Rong Jerry Zhou 			if (wid == (wid_t)DDI_FAILURE)
32633a49c214SYang-Rong Jerry Zhou 				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),
32693a49c214SYang-Rong Jerry Zhou 			    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;
32763a49c214SYang-Rong Jerry Zhou 			wdac = codec->widget[wid];
327788447a05SGarrett D'Amore 			wdac->priv = path;
3278e7236f70SZhao Edgar Liu - Sun Microsystems 			pin->dac_wid = wid;
32793a49c214SYang-Rong Jerry Zhou 			pin->finish = 1;
32803a49c214SYang-Rong Jerry Zhou 			widget->path_flags |= AUDIOHD_PATH_DAC;
32813a49c214SYang-Rong Jerry Zhou 			widget->out_weight++;
3282b96a6eceSZhao Edgar Liu - Sun Microsystems 			widget->output_path_next = i;
328388447a05SGarrett D'Amore 			statep->path[statep->pathnum++] = path;
32843a49c214SYang-Rong Jerry Zhou 			break;
32853a49c214SYang-Rong Jerry Zhou 		}
32863a49c214SYang-Rong Jerry Zhou 	}
3287582eadeeSfl 
32883a49c214SYang-Rong Jerry Zhou }	/* audiohd_do_build_output_path() */
3289582eadeeSfl 
3290582eadeeSfl /*
32913a49c214SYang-Rong Jerry Zhou  * audiohd_build_output_path()
3292582eadeeSfl  *
3293582eadeeSfl  * Description:
32943a49c214SYang-Rong Jerry Zhou  *	Build the output path in the codec for every pin.
32953a49c214SYang-Rong Jerry Zhou  *	First we try to search output path with mixer widget exclusively
32963a49c214SYang-Rong Jerry Zhou  *	Then we try to search shared output path with mixer widget.
32973a49c214SYang-Rong Jerry Zhou  *	Then we try to search output path without mixer widget exclusively.
32983a49c214SYang-Rong Jerry Zhou  *	At last we try to search shared ouput path for the remained pins
3299582eadeeSfl  */
33003a49c214SYang-Rong Jerry Zhou static void
audiohd_build_output_path(hda_codec_t * codec)33013a49c214SYang-Rong Jerry Zhou audiohd_build_output_path(hda_codec_t *codec)
3302582eadeeSfl {
33033a49c214SYang-Rong Jerry Zhou 	int 			mnum = 0;
330488447a05SGarrett D'Amore 	uint8_t			mixer_allow = 1;
3305582eadeeSfl 
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 
33153a49c214SYang-Rong Jerry Zhou 	/* search an exclusive mixer widget path. This is preferred */
331688447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, mixer_allow, &mnum, 1, 0);
3317582eadeeSfl 
33183a49c214SYang-Rong Jerry Zhou 	/* search a shared mixer widget path for the remained pins */
331988447a05SGarrett D'Amore 	audiohd_do_build_output_path(codec, mixer_allow, &mnum, 0, 0);
3320582eadeeSfl 
33213a49c214SYang-Rong Jerry Zhou 	/* search an exclusive widget path without mixer for the remained pin */
33223a49c214SYang-Rong Jerry Zhou 	audiohd_do_build_output_path(codec, 0, &mnum, 1, 0);
3323582eadeeSfl 
33243a49c214SYang-Rong Jerry Zhou 	/* search a shared widget path without mixer for the remained pin */
33253a49c214SYang-Rong Jerry Zhou 	audiohd_do_build_output_path(codec, 0, &mnum, 0, 0);
3326582eadeeSfl 
33273a49c214SYang-Rong Jerry Zhou }	/* audiohd_build_output_path */
3328582eadeeSfl 
33293a49c214SYang-Rong Jerry Zhou /*
33303a49c214SYang-Rong Jerry Zhou  * audiohd_build_output_amp
33313a49c214SYang-Rong Jerry Zhou  *
33323a49c214SYang-Rong Jerry Zhou  * Description:
33333a49c214SYang-Rong Jerry Zhou  *	Find the gain control and mute control widget
33343a49c214SYang-Rong Jerry Zhou  */
33353a49c214SYang-Rong Jerry Zhou static void
audiohd_build_output_amp(hda_codec_t * codec)33363a49c214SYang-Rong Jerry Zhou audiohd_build_output_amp(hda_codec_t *codec)
33373a49c214SYang-Rong Jerry Zhou {
333888447a05SGarrett D'Amore 	audiohd_path_t		*path;
33393a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*w, *widget, *wpin, *wdac;
33403a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
3341b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t		wid, next;
33423a49c214SYang-Rong Jerry Zhou 	int		weight;
334388447a05SGarrett D'Amore 	int		i, j;
33443a49c214SYang-Rong Jerry Zhou 	uint32_t	gain;
3345582eadeeSfl 
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];
33533a49c214SYang-Rong Jerry Zhou 			wpin = codec->widget[wid];
33543a49c214SYang-Rong Jerry Zhou 			pin = (audiohd_pin_t *)wpin->priv;
33553a49c214SYang-Rong Jerry Zhou 			weight = wpin->out_weight;
3356a234d95bScg 
33573a49c214SYang-Rong Jerry Zhou 			/*
33583a49c214SYang-Rong Jerry Zhou 			 * search a node which can mute this pin while
33593a49c214SYang-Rong Jerry Zhou 			 * the mute functionality doesn't effect other
33603a49c214SYang-Rong Jerry Zhou 			 * pins.
33613a49c214SYang-Rong Jerry Zhou 			 */
33623a49c214SYang-Rong Jerry Zhou 			widget = wpin;
33633a49c214SYang-Rong Jerry Zhou 			while (widget) {
33643a49c214SYang-Rong Jerry Zhou 				if (widget->outamp_cap &
33653a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
33663a49c214SYang-Rong Jerry Zhou 					pin->mute_wid = widget->wid_wid;
33673a49c214SYang-Rong Jerry Zhou 					pin->mute_dir = AUDIOHDC_AMP_SET_OUTPUT;
33683a49c214SYang-Rong Jerry Zhou 					break;
33693a49c214SYang-Rong Jerry Zhou 				}
33703a49c214SYang-Rong Jerry Zhou 				if (widget->inamp_cap &
33713a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
33723a49c214SYang-Rong Jerry Zhou 					pin->mute_wid = widget->wid_wid;
33733a49c214SYang-Rong Jerry Zhou 					pin->mute_dir = AUDIOHDC_AMP_SET_INPUT;
33743a49c214SYang-Rong Jerry Zhou 					break;
33753a49c214SYang-Rong Jerry Zhou 				}
3376b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3377b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
33783a49c214SYang-Rong Jerry Zhou 					break;
3379b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
33803a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
338131a3f136SYang-Rong Jerry Zhou 				if (widget && widget->out_weight != weight)
33823a49c214SYang-Rong Jerry Zhou 					break;
3383a234d95bScg 			}
3384a234d95bScg 
3385a234d95bScg 			/*
33863a49c214SYang-Rong Jerry Zhou 			 * We select the wid which has maxium gain range in
33873a49c214SYang-Rong Jerry Zhou 			 * the output path. Meanwhile, the gain controlling
33883a49c214SYang-Rong Jerry Zhou 			 * of this node doesn't effect other pins if this
33893a49c214SYang-Rong Jerry Zhou 			 * output stream has multiple pins.
3390a234d95bScg 			 */
33913a49c214SYang-Rong Jerry Zhou 			gain = 0;
33923a49c214SYang-Rong Jerry Zhou 			widget = wpin;
33933a49c214SYang-Rong Jerry Zhou 			while (widget) {
33943a49c214SYang-Rong Jerry Zhou 				gain = (widget->outamp_cap &
33953a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
33963a49c214SYang-Rong Jerry Zhou 				if (gain && gain > pin->gain_bits) {
33973a49c214SYang-Rong Jerry Zhou 					pin->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
33983a49c214SYang-Rong Jerry Zhou 					pin->gain_bits = gain;
33993a49c214SYang-Rong Jerry Zhou 					pin->gain_wid = widget->wid_wid;
34003a49c214SYang-Rong Jerry Zhou 				}
34013a49c214SYang-Rong Jerry Zhou 				gain = widget->inamp_cap &
34023a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
34033a49c214SYang-Rong Jerry Zhou 				if (gain && gain > pin->gain_bits) {
34043a49c214SYang-Rong Jerry Zhou 					pin->gain_dir = AUDIOHDC_AMP_SET_INPUT;
34053a49c214SYang-Rong Jerry Zhou 					pin->gain_bits = gain;
34063a49c214SYang-Rong Jerry Zhou 					pin->gain_wid = widget->wid_wid;
34073a49c214SYang-Rong Jerry Zhou 				}
3408b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3409b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
34103a49c214SYang-Rong Jerry Zhou 					break;
3411b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
34123a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
341331a3f136SYang-Rong Jerry Zhou 				if (widget && widget->out_weight != weight)
34143a49c214SYang-Rong Jerry Zhou 					break;
34153a49c214SYang-Rong Jerry Zhou 			}
34163a49c214SYang-Rong Jerry Zhou 			pin->gain_bits >>= AUDIOHD_GAIN_OFF;
3417a234d95bScg 		}
3418a234d95bScg 
34193a49c214SYang-Rong Jerry Zhou 		/*
34203a49c214SYang-Rong Jerry Zhou 		 * if this stream has multiple pins, we try to find
34213a49c214SYang-Rong Jerry Zhou 		 * a mute & gain-controlling nodes which can effect
34223a49c214SYang-Rong Jerry Zhou 		 * all output pins of this stream to be used for the
34233a49c214SYang-Rong Jerry Zhou 		 * whole stream
34243a49c214SYang-Rong Jerry Zhou 		 */
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;
34313a49c214SYang-Rong Jerry Zhou 		} else {
343288447a05SGarrett D'Amore 			wdac = codec->widget[path->adda_wid];
34333a49c214SYang-Rong Jerry Zhou 			weight = wdac->out_weight;
343488447a05SGarrett D'Amore 			wid = path->pin_wid[0];
34353a49c214SYang-Rong Jerry Zhou 			w = codec->widget[wid];
343631a3f136SYang-Rong Jerry Zhou 			while (w && w->out_weight != weight) {
3437b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[w->output_path_next];
34383a49c214SYang-Rong Jerry Zhou 				w = codec->widget[wid];
34393a49c214SYang-Rong Jerry Zhou 			}
3440582eadeeSfl 
34413a49c214SYang-Rong Jerry Zhou 			/* find mute controlling node for this stream */
34423a49c214SYang-Rong Jerry Zhou 			widget = w;
34433a49c214SYang-Rong Jerry Zhou 			while (widget) {
34443a49c214SYang-Rong Jerry Zhou 				if (widget->outamp_cap &
34453a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
344688447a05SGarrett D'Amore 					path->mute_wid = widget->wid_wid;
344788447a05SGarrett D'Amore 					path->mute_dir =
34483a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_OUTPUT;
34493a49c214SYang-Rong Jerry Zhou 					break;
34503a49c214SYang-Rong Jerry Zhou 				}
34513a49c214SYang-Rong Jerry Zhou 				if (widget->inamp_cap &
34523a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_MUTE_CAP) {
345388447a05SGarrett D'Amore 					path->mute_wid = widget->wid_wid;
345488447a05SGarrett D'Amore 					path->mute_dir =
34553a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_INPUT;
34563a49c214SYang-Rong Jerry Zhou 					break;
34573a49c214SYang-Rong Jerry Zhou 				}
3458b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3459b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
34603a49c214SYang-Rong Jerry Zhou 					break;
3461b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
34623a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
34633a49c214SYang-Rong Jerry Zhou 			}
3464582eadeeSfl 
34653a49c214SYang-Rong Jerry Zhou 			/* find volume controlling node for this stream */
34663a49c214SYang-Rong Jerry Zhou 			gain = 0;
34673a49c214SYang-Rong Jerry Zhou 			widget = w;
34683a49c214SYang-Rong Jerry Zhou 			while (widget) {
34693a49c214SYang-Rong Jerry Zhou 				gain = (widget->outamp_cap &
34703a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
34713a49c214SYang-Rong Jerry Zhou 				if (gain && gain > pin->gain_bits) {
347288447a05SGarrett D'Amore 					path->gain_dir =
34733a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_OUTPUT;
347488447a05SGarrett D'Amore 					path->gain_bits = gain;
347588447a05SGarrett D'Amore 					path->gain_wid = widget->wid_wid;
34763a49c214SYang-Rong Jerry Zhou 				}
34773a49c214SYang-Rong Jerry Zhou 				gain = widget->inamp_cap &
34783a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
34793a49c214SYang-Rong Jerry Zhou 				if (gain && (gain > pin->gain_bits) &&
34803a49c214SYang-Rong Jerry Zhou 				    (widget->type != WTYPE_AUDIO_MIX)) {
348188447a05SGarrett D'Amore 					path->gain_dir =
34823a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_INPUT;
348388447a05SGarrett D'Amore 					path->gain_bits = gain;
348488447a05SGarrett D'Amore 					path->gain_wid = widget->wid_wid;
34853a49c214SYang-Rong Jerry Zhou 				}
3486b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3487b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
34883a49c214SYang-Rong Jerry Zhou 					break;
3489b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
34903a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
34913a49c214SYang-Rong Jerry Zhou 			}
349288447a05SGarrett D'Amore 			path->gain_bits >>= AUDIOHD_GAIN_OFF;
34933a49c214SYang-Rong Jerry Zhou 		}
3494582eadeeSfl 
34953a49c214SYang-Rong Jerry Zhou 	}
3496582eadeeSfl 
34973a49c214SYang-Rong Jerry Zhou }	/* audiohd_build_output_amp */
3498582eadeeSfl 
3499582eadeeSfl /*
35003a49c214SYang-Rong Jerry Zhou  * audiohd_finish_output_path()
35017253a143Scg  *
35023a49c214SYang-Rong Jerry Zhou  * Description:
35033a49c214SYang-Rong Jerry Zhou  *	Enable the widgets on the output path
3504582eadeeSfl  */
35053a49c214SYang-Rong Jerry Zhou static void
audiohd_finish_output_path(hda_codec_t * codec)35063a49c214SYang-Rong Jerry Zhou audiohd_finish_output_path(hda_codec_t *codec)
3507582eadeeSfl {
3508ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
350988447a05SGarrett D'Amore 	audiohd_path_t		*path;
35103a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget;
35113a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
35123a49c214SYang-Rong Jerry Zhou 	uint_t			caddr = codec->index;
3513b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
351488447a05SGarrett D'Amore 	int			i, j;
3515a234d95bScg 
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];
35223a49c214SYang-Rong Jerry Zhou 			widget = codec->widget[wid];
35233a49c214SYang-Rong Jerry Zhou 			pin = (audiohd_pin_t *)widget->priv;
35243a49c214SYang-Rong Jerry Zhou 			{
35253a49c214SYang-Rong Jerry Zhou 			uint32_t    lTmp;
35263a49c214SYang-Rong Jerry Zhou 
35273a49c214SYang-Rong Jerry Zhou 			lTmp = audioha_codec_verb_get(statep, caddr, wid,
35283a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
35293a49c214SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, caddr, wid,
35303a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_PIN_CTRL, (lTmp |
3531d5145224SYang-Rong Jerry Zhou 			    pin->vrefvalue |
35323a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_PIN_CONTROL_OUT_ENABLE |
35333a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_PIN_CONTROL_HP_ENABLE) &
35343a49c214SYang-Rong Jerry Zhou 			    ~ AUDIOHDC_PIN_CONTROL_IN_ENABLE);
35353a49c214SYang-Rong Jerry Zhou 			}
35363a49c214SYang-Rong Jerry Zhou 			/* If this pin has external amplifier, enable it */
3537feccaf6dSYang-Rong Jerry Zhou 			if (pin->cap & AUDIOHD_EXT_AMP_MASK)
35383a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_verb_get(statep, caddr,
3539feccaf6dSYang-Rong Jerry Zhou 				    wid, AUDIOHDC_VERB_SET_EAPD,
3540feccaf6dSYang-Rong Jerry Zhou 				    AUDIOHD_EXT_AMP_ENABLE);
35411582740eScg 
35423a49c214SYang-Rong Jerry Zhou 			if (widget->outamp_cap) {
35433a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_4bit_verb_get(statep,
35443a49c214SYang-Rong Jerry Zhou 				    caddr, wid, AUDIOHDC_VERB_SET_AMP_MUTE,
35453a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
35463a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_GAIN_MAX);
35473a49c214SYang-Rong Jerry Zhou 			}
3548582eadeeSfl 
35493a49c214SYang-Rong Jerry Zhou 			(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);
35523a49c214SYang-Rong Jerry Zhou 
3553b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = widget->avail_conn[widget->output_path_next];
35543a49c214SYang-Rong Jerry Zhou 			widget = codec->widget[wid];
35553a49c214SYang-Rong Jerry Zhou 
35563a49c214SYang-Rong Jerry Zhou 			while (widget) {
35573a49c214SYang-Rong Jerry Zhou 				/*
35583a49c214SYang-Rong Jerry Zhou 				 * Set all amplifiers in this path to
3559b96a6eceSZhao Edgar Liu - Sun Microsystems 				 * the maximum volume and unmute them.
35603a49c214SYang-Rong Jerry Zhou 				 */
35613a49c214SYang-Rong Jerry Zhou 				if (widget->outamp_cap) {
35623a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_4bit_verb_get(
3563b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
35643a49c214SYang-Rong Jerry Zhou 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
35653a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_LR_OUTPUT |
35663a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_GAIN_MAX);
35673a49c214SYang-Rong Jerry Zhou 				}
35683a49c214SYang-Rong Jerry Zhou 				if (widget->inamp_cap) {
35693a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_4bit_verb_get(
3570b96a6eceSZhao Edgar Liu - Sun Microsystems 					    statep, caddr,
35713a49c214SYang-Rong Jerry Zhou 					    wid, AUDIOHDC_VERB_SET_AMP_MUTE,
35723a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_LR_INPUT |
35733a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_GAIN_MAX |
3574b96a6eceSZhao Edgar Liu - Sun Microsystems 					    (widget->output_path_next <<
35753a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_INDEX_OFFSET));
35763a49c214SYang-Rong Jerry Zhou 				}
35773a49c214SYang-Rong Jerry Zhou 
3578b96a6eceSZhao Edgar Liu - Sun Microsystems 				next = widget->output_path_next;
3579b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (next == AUDIOHD_NULL_CONN)
35803a49c214SYang-Rong Jerry Zhou 					break;
35813a49c214SYang-Rong Jerry Zhou 				/*
35823a49c214SYang-Rong Jerry Zhou 				 * Accoding to HD spec, mixer doesn't support
35833a49c214SYang-Rong Jerry Zhou 				 * "select connection"
35843a49c214SYang-Rong Jerry Zhou 				 */
3585b96a6eceSZhao Edgar Liu - Sun Microsystems 				if ((widget->type == WTYPE_AUDIO_SEL) &&
35863a49c214SYang-Rong Jerry Zhou 				    (widget->nconns > 1))
35873a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_verb_get(statep,
3588b96a6eceSZhao Edgar Liu - Sun Microsystems 					    caddr, wid,
35893a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_VERB_SET_CONN_SEL,
3590b96a6eceSZhao Edgar Liu - Sun Microsystems 					    widget->output_path_next);
35913a49c214SYang-Rong Jerry Zhou 
3592b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = widget->avail_conn[next];
35933a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
35943a49c214SYang-Rong Jerry Zhou 			}
35953a49c214SYang-Rong Jerry Zhou 		}
35963a49c214SYang-Rong Jerry Zhou 	}
35973a49c214SYang-Rong Jerry Zhou }	/* audiohd_finish_output_path() */
3598582eadeeSfl 
3599582eadeeSfl /*
36003a49c214SYang-Rong Jerry Zhou  * audiohd_find_input_pins()
36017253a143Scg  *
3602a234d95bScg  * Description:
36033a49c214SYang-Rong Jerry Zhou  * 	Here we consider a mixer/selector with multi-input as a real sum
36043a49c214SYang-Rong Jerry Zhou  * 	widget. Only the first real mixer/selector widget is permitted in
36053a49c214SYang-Rong Jerry Zhou  * 	an input path(recording path). If there are more mixers/selectors
36063a49c214SYang-Rong Jerry Zhou  * 	execept the first one, only the first input/connection of those
36073a49c214SYang-Rong Jerry Zhou  * 	widgets will be used by our driver, that means, we ignore other
36083a49c214SYang-Rong Jerry Zhou  * 	inputs of those mixers/selectors.
3609582eadeeSfl  */
3610582eadeeSfl static int
audiohd_find_input_pins(hda_codec_t * codec,wid_t wid,int allowmixer,int depth,audiohd_path_t * path)36113a49c214SYang-Rong Jerry Zhou audiohd_find_input_pins(hda_codec_t *codec, wid_t wid, int allowmixer,
361288447a05SGarrett D'Amore     int depth, audiohd_path_t *path)
3613582eadeeSfl {
36143a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget = codec->widget[wid];
36153a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
3616ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
36173a49c214SYang-Rong Jerry Zhou 	uint_t			caddr = codec->index;
36183a49c214SYang-Rong Jerry Zhou 	int			retval = -1;
3619feccaf6dSYang-Rong Jerry Zhou 	int			num, i;
36203a49c214SYang-Rong Jerry Zhou 	uint32_t		pinctrl;
36213a49c214SYang-Rong Jerry Zhou 
36223a49c214SYang-Rong Jerry Zhou 	if (depth > AUDIOHD_MAX_DEPTH)
3623c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
362431a3f136SYang-Rong Jerry Zhou 	if (widget == NULL)
3625c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
36263a49c214SYang-Rong Jerry Zhou 
36273a49c214SYang-Rong Jerry Zhou 	/* 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);
36313a49c214SYang-Rong Jerry Zhou 
36323a49c214SYang-Rong Jerry Zhou 	switch (widget->type) {
36333a49c214SYang-Rong Jerry Zhou 	case WTYPE_PIN:
36343a49c214SYang-Rong Jerry Zhou 		pin = (audiohd_pin_t *)widget->priv;
36353a49c214SYang-Rong Jerry Zhou 		if (pin->no_phys_conn)
3636c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
36373a49c214SYang-Rong Jerry Zhou 		/* enable the pins' input capability */
36383a49c214SYang-Rong Jerry Zhou 		pinctrl = audioha_codec_verb_get(statep, caddr, wid,
36393a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_GET_PIN_CTRL, 0);
36403a49c214SYang-Rong Jerry Zhou 		(void) audioha_codec_verb_get(statep, caddr, wid,
36413a49c214SYang-Rong Jerry Zhou 		    AUDIOHDC_VERB_SET_PIN_CTRL,
36423a49c214SYang-Rong Jerry Zhou 		    pinctrl | AUDIOHD_PIN_IN_ENABLE);
364316600ba1SYang-Rong Jerry Zhou 		if (pin->cap & AUDIOHD_EXT_AMP_MASK) {
364416600ba1SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, caddr,
364516600ba1SYang-Rong Jerry Zhou 			    wid, AUDIOHDC_VERB_SET_EAPD,
364616600ba1SYang-Rong Jerry Zhou 			    AUDIOHD_EXT_AMP_ENABLE);
364716600ba1SYang-Rong Jerry Zhou 		}
36483a49c214SYang-Rong Jerry Zhou 		switch (pin->device) {
36493a49c214SYang-Rong Jerry Zhou 		case DTYPE_CD:
36503a49c214SYang-Rong Jerry Zhou 		case DTYPE_LINE_IN:
36513a49c214SYang-Rong Jerry Zhou 		case DTYPE_MIC_IN:
36523a49c214SYang-Rong Jerry Zhou 		case DTYPE_AUX:
36533a49c214SYang-Rong Jerry Zhou 			widget->path_flags |= AUDIOHD_PATH_ADC;
36543a49c214SYang-Rong Jerry Zhou 			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);
36583a49c214SYang-Rong Jerry Zhou 		}
3659582eadeeSfl 		break;
36603a49c214SYang-Rong Jerry Zhou 	case WTYPE_AUDIO_MIX:
36613a49c214SYang-Rong Jerry Zhou 	case WTYPE_AUDIO_SEL:
36623a49c214SYang-Rong Jerry Zhou 		/*
36633a49c214SYang-Rong Jerry Zhou 		 * If the sum widget has only one input, we don't
36643a49c214SYang-Rong Jerry Zhou 		 * consider it as a real sum widget.
36653a49c214SYang-Rong Jerry Zhou 		 */
36663a49c214SYang-Rong Jerry Zhou 		if (widget->nconns == 1) {
3667b96a6eceSZhao Edgar Liu - Sun Microsystems 			widget->input_path_next = 0;
36683a49c214SYang-Rong Jerry Zhou 			retval = audiohd_find_input_pins(codec,
36693a49c214SYang-Rong Jerry Zhou 			    widget->avail_conn[0],
367088447a05SGarrett D'Amore 			    allowmixer, depth + 1, path);
3671b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (retval == DDI_SUCCESS) {
36723a49c214SYang-Rong Jerry Zhou 				widget->path_flags |= AUDIOHD_PATH_ADC;
36733a49c214SYang-Rong Jerry Zhou 				widget->in_weight++;
36743a49c214SYang-Rong Jerry Zhou 			}
36753a49c214SYang-Rong Jerry Zhou 			break;
36763a49c214SYang-Rong Jerry Zhou 		}
3677582eadeeSfl 
36783a49c214SYang-Rong Jerry Zhou 		if (allowmixer) {
36793a49c214SYang-Rong Jerry Zhou 			/*
36803a49c214SYang-Rong Jerry Zhou 			 * This is a real sum widget, we will reject
36813a49c214SYang-Rong Jerry Zhou 			 * other real sum widget when we find more in
36823a49c214SYang-Rong Jerry Zhou 			 * the following path-searching.
36833a49c214SYang-Rong Jerry Zhou 			 */
36843a49c214SYang-Rong Jerry Zhou 			for (int i = 0; i < widget->nconns; i++) {
36853a49c214SYang-Rong Jerry Zhou 				retval = audiohd_find_input_pins(codec,
36863a49c214SYang-Rong Jerry Zhou 				    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;
36903a49c214SYang-Rong Jerry Zhou 					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;
369431a3f136SYang-Rong Jerry Zhou 					widget->path_flags |=
369531a3f136SYang-Rong Jerry Zhou 					    AUDIOHD_PATH_ADC;
36963a49c214SYang-Rong Jerry Zhou 				}
36973a49c214SYang-Rong Jerry Zhou 			}
3698582eadeeSfl 
36993a49c214SYang-Rong Jerry Zhou 			/* 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;
37023a49c214SYang-Rong Jerry Zhou 		} else {
37033a49c214SYang-Rong Jerry Zhou 			/*
37043a49c214SYang-Rong Jerry Zhou 			 * We had already found a real sum before this one since
3705feccaf6dSYang-Rong Jerry Zhou 			 * allowmixer is 0.
37063a49c214SYang-Rong Jerry Zhou 			 */
3707feccaf6dSYang-Rong Jerry Zhou 			for (i = 0; i < widget->nconns; i++) {
3708feccaf6dSYang-Rong Jerry Zhou 				retval = audiohd_find_input_pins(codec,
3709feccaf6dSYang-Rong Jerry Zhou 				    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;
3713feccaf6dSYang-Rong Jerry Zhou 					widget->path_flags |= AUDIOHD_PATH_ADC;
3714feccaf6dSYang-Rong Jerry Zhou 					widget->in_weight++;
3715feccaf6dSYang-Rong Jerry Zhou 					break;
3716feccaf6dSYang-Rong Jerry Zhou 				}
37173a49c214SYang-Rong Jerry Zhou 			}
37183a49c214SYang-Rong Jerry Zhou 		}
37193a49c214SYang-Rong Jerry Zhou 		break;
37203a49c214SYang-Rong Jerry Zhou 	default:
37213a49c214SYang-Rong Jerry Zhou 		break;
37223a49c214SYang-Rong Jerry Zhou 	}
37237253a143Scg 
37243a49c214SYang-Rong Jerry Zhou 	return (retval);
37253a49c214SYang-Rong Jerry Zhou }	/* audiohd_find_input_pins */
37267253a143Scg 
3727a234d95bScg /*
37283a49c214SYang-Rong Jerry Zhou  * audiohd_build_input_path()
37297253a143Scg  *
3730a234d95bScg  * Description:
37313a49c214SYang-Rong Jerry Zhou  *	Find input path for the codec
3732a234d95bScg  */
3733a234d95bScg static void
audiohd_build_input_path(hda_codec_t * codec)37343a49c214SYang-Rong Jerry Zhou audiohd_build_input_path(hda_codec_t *codec)
3735a234d95bScg {
37363a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget;
373788447a05SGarrett D'Amore 	audiohd_path_t		*path = NULL;
37383a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
37393a49c214SYang-Rong Jerry Zhou 	int			i;
37403a49c214SYang-Rong Jerry Zhou 	int			retval;
374116600ba1SYang-Rong Jerry Zhou 	uint8_t			rtag = 0;
3742ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
37433a49c214SYang-Rong Jerry Zhou 
37443a49c214SYang-Rong Jerry Zhou 	for (wid = codec->first_wid; wid <= codec->last_wid; wid++) {
37453a49c214SYang-Rong Jerry Zhou 
37463a49c214SYang-Rong Jerry Zhou 		widget = codec->widget[wid];
3747a234d95bScg 
37483a49c214SYang-Rong Jerry Zhou 		/* check if it is an ADC widget */
3749b96a6eceSZhao Edgar Liu - Sun Microsystems 		if (widget == NULL || widget->type != WTYPE_AUDIO_IN)
37503a49c214SYang-Rong Jerry Zhou 			continue;
3751a234d95bScg 
375288447a05SGarrett D'Amore 		if (path == NULL)
375388447a05SGarrett D'Amore 			path = kmem_zalloc(sizeof (audiohd_path_t),
37543a49c214SYang-Rong Jerry Zhou 			    KM_SLEEP);
37553a49c214SYang-Rong Jerry Zhou 		else
375688447a05SGarrett D'Amore 			bzero(path, sizeof (audiohd_port_t));
3757a234d95bScg 
375888447a05SGarrett D'Amore 		path->adda_wid = wid;
37597253a143Scg 
37603a49c214SYang-Rong Jerry Zhou 		/*
37613a49c214SYang-Rong Jerry Zhou 		 * Is there any ADC widget which has more than one input ??
37623a49c214SYang-Rong Jerry Zhou 		 * I don't believe. Anyway, we carefully deal with this. But
37633a49c214SYang-Rong Jerry Zhou 		 * if hardware vendors embed a selector in a ADC, we just use
37643a49c214SYang-Rong Jerry Zhou 		 * the first available input, which has connection to input pin
37653a49c214SYang-Rong Jerry Zhou 		 * widget. Because selector cannot perform mixer functionality,
37663a49c214SYang-Rong Jerry Zhou 		 * and we just permit one selector or mixer in a recording path,
37673a49c214SYang-Rong Jerry Zhou 		 * if we use the selector embedded in ADC,we cannot use possible
37683a49c214SYang-Rong Jerry Zhou 		 * mixer during path searching.
37693a49c214SYang-Rong Jerry Zhou 		 */
37703a49c214SYang-Rong Jerry Zhou 		for (i = 0; i < widget->nconns; i++) {
37713a49c214SYang-Rong Jerry Zhou 			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;
377816600ba1SYang-Rong Jerry Zhou 				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;
37833a49c214SYang-Rong Jerry Zhou 				break;
37843a49c214SYang-Rong Jerry Zhou 			}
37853a49c214SYang-Rong Jerry Zhou 		}
37863a49c214SYang-Rong Jerry Zhou 	}
378788447a05SGarrett D'Amore 	if (path)
378888447a05SGarrett D'Amore 		kmem_free(path, sizeof (audiohd_path_t));
37893a49c214SYang-Rong Jerry Zhou }	/* audiohd_build_input_path */
37907253a143Scg 
37917253a143Scg /*
37923a49c214SYang-Rong Jerry Zhou  * audiohd_build_input_amp()
37933a49c214SYang-Rong Jerry Zhou  *
37943a49c214SYang-Rong Jerry Zhou  * Description:
37953a49c214SYang-Rong Jerry Zhou  *	Find gain and mute control widgets on the input path
37967253a143Scg  */
37973a49c214SYang-Rong Jerry Zhou static void
audiohd_build_input_amp(hda_codec_t * codec)37983a49c214SYang-Rong Jerry Zhou audiohd_build_input_amp(hda_codec_t *codec)
37997253a143Scg {
380088447a05SGarrett D'Amore 	audiohd_path_t		*path;
38013a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*wsum, *wadc, *w;
38023a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
38033a49c214SYang-Rong Jerry Zhou 	uint_t			gain;
3804b96a6eceSZhao Edgar Liu - Sun Microsystems 	wid_t			wid, next;
380588447a05SGarrett D'Amore 	int			i, j;
38063a49c214SYang-Rong Jerry Zhou 	int			weight;
38077253a143Scg 
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];
38163a49c214SYang-Rong Jerry Zhou 		weight = wadc->in_weight;
38177253a143Scg 
38183a49c214SYang-Rong Jerry Zhou 		/*
38193a49c214SYang-Rong Jerry Zhou 		 * Search node which has mute functionality for
38203a49c214SYang-Rong Jerry Zhou 		 * the whole input path
38213a49c214SYang-Rong Jerry Zhou 		 */
38223a49c214SYang-Rong Jerry Zhou 		w = wadc;
38233a49c214SYang-Rong Jerry Zhou 		while (w) {
38243a49c214SYang-Rong Jerry Zhou 			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;
38273a49c214SYang-Rong Jerry Zhou 				break;
38283a49c214SYang-Rong Jerry Zhou 			}
38293a49c214SYang-Rong Jerry Zhou 			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;
38333a49c214SYang-Rong Jerry Zhou 				break;
38343a49c214SYang-Rong Jerry Zhou 			}
38357253a143Scg 
3836b96a6eceSZhao Edgar Liu - Sun Microsystems 			next = w->input_path_next;
3837b96a6eceSZhao Edgar Liu - Sun Microsystems 			if (next == AUDIOHD_NULL_CONN)
38383a49c214SYang-Rong Jerry Zhou 				break;
3839b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[next];
384088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
384131a3f136SYang-Rong Jerry Zhou 			if (w && w->in_weight != weight)
38423a49c214SYang-Rong Jerry Zhou 				break;
38433a49c214SYang-Rong Jerry Zhou 		}
38447253a143Scg 
38453a49c214SYang-Rong Jerry Zhou 		/*
38463a49c214SYang-Rong Jerry Zhou 		 * Search a node for amplifier adjusting for the whole
38473a49c214SYang-Rong Jerry Zhou 		 * input path
38483a49c214SYang-Rong Jerry Zhou 		 */
38493a49c214SYang-Rong Jerry Zhou 		w = wadc;
38503a49c214SYang-Rong Jerry Zhou 		gain = 0;
38513a49c214SYang-Rong Jerry Zhou 		while (w) {
38523a49c214SYang-Rong Jerry Zhou 			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;
38573a49c214SYang-Rong Jerry Zhou 			}
38583a49c214SYang-Rong Jerry Zhou 			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;
38643a49c214SYang-Rong Jerry Zhou 			}
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)
38683a49c214SYang-Rong Jerry Zhou 				break;
3869b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[next];
387088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
38713a49c214SYang-Rong Jerry Zhou 		}
387288447a05SGarrett D'Amore 		path->gain_bits >>= AUDIOHD_GAIN_OFF;
38737253a143Scg 
38743a49c214SYang-Rong Jerry Zhou 		/*
38753a49c214SYang-Rong Jerry Zhou 		 * If the input path has one pin only, the mute/amp
38763a49c214SYang-Rong Jerry Zhou 		 * controlling is shared by the whole path and pin
38773a49c214SYang-Rong Jerry Zhou 		 */
387888447a05SGarrett D'Amore 		if (path->pin_nums == 1) {
387988447a05SGarrett D'Amore 			wid = path->pin_wid[0];
388088447a05SGarrett D'Amore 			w = path->codec->widget[wid];
38813a49c214SYang-Rong Jerry Zhou 			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;
38873a49c214SYang-Rong Jerry Zhou 			continue;
38883a49c214SYang-Rong Jerry Zhou 		}
38897253a143Scg 
38903a49c214SYang-Rong Jerry Zhou 		/*
38913a49c214SYang-Rong Jerry Zhou 		 * For multi-pin device, there must be a selector
38923a49c214SYang-Rong Jerry Zhou 		 * or mixer along the input path, and the sum_wid
38933a49c214SYang-Rong Jerry Zhou 		 * is the widget's node id.
38943a49c214SYang-Rong Jerry Zhou 		 */
389588447a05SGarrett D'Amore 		wid = path->sum_wid;
389688447a05SGarrett D'Amore 		wsum = path->codec->widget[wid]; /* sum widget */
38973a49c214SYang-Rong Jerry Zhou 
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];
39013a49c214SYang-Rong Jerry Zhou 			pin = (audiohd_pin_t *)w->priv;
39023a49c214SYang-Rong Jerry Zhou 
39033a49c214SYang-Rong Jerry Zhou 			/* find node for mute */
39043a49c214SYang-Rong Jerry Zhou 			if (wsum->inamp_cap & AUDIOHDC_AMP_CAP_MUTE_CAP) {
39053a49c214SYang-Rong Jerry Zhou 				pin->mute_wid = wsum->wid_wid;
39063a49c214SYang-Rong Jerry Zhou 				pin->mute_dir = AUDIOHDC_AMP_SET_INPUT;
39073a49c214SYang-Rong Jerry Zhou 			} else {
390888447a05SGarrett D'Amore 				wid = wsum->avail_conn[path->sum_selconn[i]];
390988447a05SGarrett D'Amore 				w = path->codec->widget[wid];
39103a49c214SYang-Rong Jerry Zhou 				while (w) {
39113a49c214SYang-Rong Jerry Zhou 					if (w->outamp_cap &
39123a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_CAP_MUTE_CAP) {
39133a49c214SYang-Rong Jerry Zhou 						pin->mute_wid = w->wid_wid;
39143a49c214SYang-Rong Jerry Zhou 						pin->mute_dir =
39153a49c214SYang-Rong Jerry Zhou 						    AUDIOHDC_AMP_SET_OUTPUT;
39163a49c214SYang-Rong Jerry Zhou 						break;
39173a49c214SYang-Rong Jerry Zhou 					}
39183a49c214SYang-Rong Jerry Zhou 					if (w->inamp_cap &
39193a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_CAP_MUTE_CAP) {
39203a49c214SYang-Rong Jerry Zhou 						pin->mute_wid = w->wid_wid;
39213a49c214SYang-Rong Jerry Zhou 						pin->mute_dir =
39223a49c214SYang-Rong Jerry Zhou 						    AUDIOHDC_AMP_SET_INPUT;
39233a49c214SYang-Rong Jerry Zhou 						break;
39243a49c214SYang-Rong Jerry Zhou 					}
39253a49c214SYang-Rong Jerry Zhou 
3926b96a6eceSZhao Edgar Liu - Sun Microsystems 					next = w->input_path_next;
3927b96a6eceSZhao Edgar Liu - Sun Microsystems 					if (next == AUDIOHD_NULL_CONN)
39283a49c214SYang-Rong Jerry Zhou 						break;
3929b96a6eceSZhao Edgar Liu - Sun Microsystems 					wid = w->avail_conn[next];
393088447a05SGarrett D'Amore 					w = path->codec->widget[wid];
39313a49c214SYang-Rong Jerry Zhou 				}
39323a49c214SYang-Rong Jerry Zhou 			}
39337253a143Scg 
39343a49c214SYang-Rong Jerry Zhou 			/* find node for amp controlling */
39353a49c214SYang-Rong Jerry Zhou 			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];
39383a49c214SYang-Rong Jerry Zhou 			while (w) {
39393a49c214SYang-Rong Jerry Zhou 				gain = (w->outamp_cap &
39403a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS);
39413a49c214SYang-Rong Jerry Zhou 				if (gain && gain > pin->gain_bits) {
39423a49c214SYang-Rong Jerry Zhou 					pin->gain_dir = AUDIOHDC_AMP_SET_OUTPUT;
39433a49c214SYang-Rong Jerry Zhou 					pin->gain_bits = gain;
39443a49c214SYang-Rong Jerry Zhou 					pin->gain_wid = w->wid_wid;
39453a49c214SYang-Rong Jerry Zhou 				}
39463a49c214SYang-Rong Jerry Zhou 				gain = w->inamp_cap &
39473a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_CAP_STEP_NUMS;
39483a49c214SYang-Rong Jerry Zhou 				if (gain && (gain > pin->gain_bits)) {
39493a49c214SYang-Rong Jerry Zhou 					pin->gain_dir = AUDIOHDC_AMP_SET_INPUT;
39503a49c214SYang-Rong Jerry Zhou 					pin->gain_bits = gain;
39513a49c214SYang-Rong Jerry Zhou 					pin->gain_wid = w->wid_wid;
39523a49c214SYang-Rong Jerry Zhou 				}
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)
39563a49c214SYang-Rong Jerry Zhou 					break;
3957b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[next];
395888447a05SGarrett D'Amore 				w = path->codec->widget[wid];
39593a49c214SYang-Rong Jerry Zhou 			}
39603a49c214SYang-Rong Jerry Zhou 			pin->gain_bits >>= AUDIOHD_GAIN_OFF;
39613a49c214SYang-Rong Jerry Zhou 		}
39623a49c214SYang-Rong Jerry Zhou 	}
39633a49c214SYang-Rong Jerry Zhou }	/* audiohd_build_input_amp() */
39647253a143Scg 
3965f90d8383Scg /*
39663a49c214SYang-Rong Jerry Zhou  * audiohd_finish_input_path()
39673a49c214SYang-Rong Jerry Zhou  *
39683a49c214SYang-Rong Jerry Zhou  * Description:
39693a49c214SYang-Rong Jerry Zhou  *	Enable the widgets on the input path
3970f90d8383Scg  */
39713a49c214SYang-Rong Jerry Zhou static void
audiohd_finish_input_path(hda_codec_t * codec)39723a49c214SYang-Rong Jerry Zhou audiohd_finish_input_path(hda_codec_t *codec)
3973f90d8383Scg {
3974ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
397565a41de7SYang-Rong Jerry Zhou 	audiohd_path_t		*path;
39763a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*w, *wsum;
39773a49c214SYang-Rong Jerry Zhou 	uint_t			caddr = codec->index;
39783a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
397988447a05SGarrett D'Amore 	int			i, j;
3980f90d8383Scg 
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) &&
39893a49c214SYang-Rong Jerry Zhou 		    (w->type != WTYPE_PIN)) {
39903a49c214SYang-Rong Jerry Zhou 			if ((w->type == WTYPE_AUDIO_SEL) && (w->nconns > 1))
39913a49c214SYang-Rong Jerry Zhou 				(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);
39943a49c214SYang-Rong Jerry Zhou 
39953a49c214SYang-Rong Jerry Zhou 			if (w->outamp_cap) {
39963a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_4bit_verb_get(statep,
39973a49c214SYang-Rong Jerry Zhou 				    caddr,
39983a49c214SYang-Rong Jerry Zhou 				    w->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
39993a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_LR_OUTPUT |
40003a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_GAIN_MAX);
40013a49c214SYang-Rong Jerry Zhou 			}
4002f90d8383Scg 
40033a49c214SYang-Rong Jerry Zhou 			if (w->inamp_cap) {
40043a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_4bit_verb_get(statep,
40053a49c214SYang-Rong Jerry Zhou 				    caddr,
40063a49c214SYang-Rong Jerry Zhou 				    w->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
40073a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_LR_INPUT |
40083a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_GAIN_MAX |
4009b96a6eceSZhao Edgar Liu - Sun Microsystems 				    (w->input_path_next <<
40103a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_INDEX_OFFSET));
40113a49c214SYang-Rong Jerry Zhou 			}
4012f90d8383Scg 
4013b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = w->avail_conn[w->input_path_next];
401488447a05SGarrett D'Amore 			w = path->codec->widget[wid];
40153a49c214SYang-Rong Jerry Zhou 		}
4016f90d8383Scg 
40173a49c214SYang-Rong Jerry Zhou 		/*
40183a49c214SYang-Rong Jerry Zhou 		 * After exiting from the above loop, the widget pointed
40193a49c214SYang-Rong Jerry Zhou 		 * by w can be a pin widget or select/mixer widget. If it
40203a49c214SYang-Rong Jerry Zhou 		 * is a pin widget, we already finish "select connection"
40213a49c214SYang-Rong Jerry Zhou 		 * operation for the whole path.
40223a49c214SYang-Rong Jerry Zhou 		 */
4023e56adb4fSYang-Rong Jerry Zhou 		if (w && w->type == WTYPE_PIN)
40243a49c214SYang-Rong Jerry Zhou 			continue;
4025f90d8383Scg 
40263a49c214SYang-Rong Jerry Zhou 		/*
40273a49c214SYang-Rong Jerry Zhou 		 * deal with multi-pin input devices.
40283a49c214SYang-Rong Jerry Zhou 		 */
402988447a05SGarrett D'Amore 		wid = path->sum_wid;
403088447a05SGarrett D'Amore 		wsum = path->codec->widget[wid];
4031e56adb4fSYang-Rong Jerry Zhou 		if (wsum == NULL)
4032e56adb4fSYang-Rong Jerry Zhou 			continue;
40333a49c214SYang-Rong Jerry Zhou 		if (wsum->outamp_cap) {
40343a49c214SYang-Rong Jerry Zhou 			(void) audioha_codec_4bit_verb_get(statep,
40353a49c214SYang-Rong Jerry Zhou 			    caddr,
40363a49c214SYang-Rong Jerry Zhou 			    wsum->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
40373a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_AMP_SET_LR_OUTPUT |
40383a49c214SYang-Rong Jerry Zhou 			    AUDIOHDC_GAIN_MAX);
40393a49c214SYang-Rong Jerry Zhou 		}
4040f90d8383Scg 
404188447a05SGarrett D'Amore 		for (j = 0; j < path->pin_nums; j++) {
40423a49c214SYang-Rong Jerry Zhou 			if (wsum->inamp_cap) {
40433a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_4bit_verb_get(statep,
40443a49c214SYang-Rong Jerry Zhou 				    caddr,
40453a49c214SYang-Rong Jerry Zhou 				    wsum->wid_wid, AUDIOHDC_VERB_SET_AMP_MUTE,
40463a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_LR_INPUT |
40473a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_GAIN_MAX |
404888447a05SGarrett D'Amore 				    (path->sum_selconn[j] <<
40493a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_AMP_SET_INDEX_OFFSET));
40503a49c214SYang-Rong Jerry Zhou 			}
40513a49c214SYang-Rong Jerry Zhou 			if (wsum->type == WTYPE_AUDIO_SEL) {
40523a49c214SYang-Rong Jerry Zhou 				(void) audioha_codec_verb_get(statep, caddr,
40533a49c214SYang-Rong Jerry Zhou 				    wsum->wid_wid,
40543a49c214SYang-Rong Jerry Zhou 				    AUDIOHDC_VERB_SET_CONN_SEL,
405588447a05SGarrett D'Amore 				    path->sum_selconn[j]);
40563a49c214SYang-Rong Jerry Zhou 			}
4057f90d8383Scg 
405888447a05SGarrett D'Amore 			wid = wsum->avail_conn[path->sum_selconn[j]];
405988447a05SGarrett D'Amore 			w = path->codec->widget[wid];
4060e56adb4fSYang-Rong Jerry Zhou 			while (w && w->type != WTYPE_PIN) {
40613a49c214SYang-Rong Jerry Zhou 				if ((w->type != WTYPE_AUDIO_MIX) &&
40623a49c214SYang-Rong Jerry Zhou 				    (w->nconns > 1))
40633a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_verb_get(statep,
40643a49c214SYang-Rong Jerry Zhou 					    caddr, w->wid_wid,
40653a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_VERB_SET_CONN_SEL,
4066b96a6eceSZhao Edgar Liu - Sun Microsystems 					    w->input_path_next);
40673a49c214SYang-Rong Jerry Zhou 
40683a49c214SYang-Rong Jerry Zhou 				if (w->outamp_cap) {
40693a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_4bit_verb_get(
40703a49c214SYang-Rong Jerry Zhou 					    statep,
40713a49c214SYang-Rong Jerry Zhou 					    caddr,
40723a49c214SYang-Rong Jerry Zhou 					    w->wid_wid,
40733a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_VERB_SET_AMP_MUTE,
40743a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_LR_OUTPUT |
40753a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_GAIN_MAX);
40763a49c214SYang-Rong Jerry Zhou 				}
40773a49c214SYang-Rong Jerry Zhou 
40783a49c214SYang-Rong Jerry Zhou 				if (w->inamp_cap) {
40793a49c214SYang-Rong Jerry Zhou 					(void) audioha_codec_4bit_verb_get(
40803a49c214SYang-Rong Jerry Zhou 					    statep,
40813a49c214SYang-Rong Jerry Zhou 					    caddr,
40823a49c214SYang-Rong Jerry Zhou 					    w->wid_wid,
40833a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_VERB_SET_AMP_MUTE,
40843a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_LR_INPUT |
40853a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_GAIN_MAX |
4086b96a6eceSZhao Edgar Liu - Sun Microsystems 					    (w->input_path_next <<
40873a49c214SYang-Rong Jerry Zhou 					    AUDIOHDC_AMP_SET_INDEX_OFFSET));
40883a49c214SYang-Rong Jerry Zhou 				}
4089b96a6eceSZhao Edgar Liu - Sun Microsystems 				wid = w->avail_conn[w->input_path_next];
409088447a05SGarrett D'Amore 				w = path->codec->widget[wid];
40913a49c214SYang-Rong Jerry Zhou 			}
40923a49c214SYang-Rong Jerry Zhou 		}
40933a49c214SYang-Rong Jerry Zhou 	}	/* end of istream loop */
40943a49c214SYang-Rong Jerry Zhou }	/* audiohd_finish_input_path */
4095f90d8383Scg 
40963a49c214SYang-Rong Jerry Zhou /*
40973a49c214SYang-Rong Jerry Zhou  * audiohd_find_inpin_for_monitor()
40983a49c214SYang-Rong Jerry Zhou  *
40993a49c214SYang-Rong Jerry Zhou  * Description:
41003a49c214SYang-Rong Jerry Zhou  *	Find input pin for monitor path.
41013a49c214SYang-Rong Jerry Zhou  *
41023a49c214SYang-Rong Jerry Zhou  * Arguments:
41033a49c214SYang-Rong Jerry Zhou  *	hda_codec_t		*codec		where the monitor path exists
41043a49c214SYang-Rong Jerry Zhou  *	wid_t			id		no. of widget being searched
41053a49c214SYang-Rong Jerry Zhou  *	int			mixer		share or not
41063a49c214SYang-Rong Jerry Zhou  */
41073a49c214SYang-Rong Jerry Zhou 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)
41093a49c214SYang-Rong Jerry Zhou {
411065a41de7SYang-Rong Jerry Zhou 	wid_t 			wid;
4111b96a6eceSZhao Edgar Liu - Sun Microsystems 	audiohd_widget_t	*widget, *w;
41123a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
41133a49c214SYang-Rong Jerry Zhou 	int 			i, find = 0;
41143a49c214SYang-Rong Jerry Zhou 
41153a49c214SYang-Rong Jerry Zhou 	wid = id;
41163a49c214SYang-Rong Jerry Zhou 	widget = codec->widget[wid];
411731a3f136SYang-Rong Jerry Zhou 	if (widget == NULL)
4118c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
411931a3f136SYang-Rong Jerry Zhou 
41203a49c214SYang-Rong Jerry Zhou 	if (widget->type == WTYPE_PIN) {
41213a49c214SYang-Rong Jerry Zhou 		pin = (audiohd_pin_t *)widget->priv;
41223a49c214SYang-Rong Jerry Zhou 		if (pin->no_phys_conn)
4123c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
41243a49c214SYang-Rong Jerry Zhou 		switch (pin->device) {
41253a49c214SYang-Rong Jerry Zhou 			case DTYPE_SPDIF_IN:
41263a49c214SYang-Rong Jerry Zhou 			case DTYPE_CD:
41273a49c214SYang-Rong Jerry Zhou 			case DTYPE_LINE_IN:
41283a49c214SYang-Rong Jerry Zhou 			case DTYPE_MIC_IN:
41293a49c214SYang-Rong Jerry Zhou 			case DTYPE_AUX:
41303a49c214SYang-Rong Jerry Zhou 				widget->path_flags |= AUDIOHD_PATH_MON;
4131c6e681c0SYang-Rong Jerry Zhou 				return (DDI_SUCCESS);
41323a49c214SYang-Rong Jerry Zhou 			default:
4133c6e681c0SYang-Rong Jerry Zhou 				return (uint32_t)(DDI_FAILURE);
41343a49c214SYang-Rong Jerry Zhou 		}
41353a49c214SYang-Rong Jerry Zhou 	}
41363a49c214SYang-Rong Jerry Zhou 	/* the widget has been visited and can't be directed to input pin */
41373a49c214SYang-Rong Jerry Zhou 	if (widget->path_flags & AUDIOHD_PATH_NOMON) {
4138c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
41393a49c214SYang-Rong Jerry Zhou 	}
41403a49c214SYang-Rong Jerry Zhou 	/* the widget has been used by the monitor path, and we can share it */
41413a49c214SYang-Rong Jerry Zhou 	if (widget->path_flags & AUDIOHD_PATH_MON) {
41423a49c214SYang-Rong Jerry Zhou 		if (mixer)
4143c6e681c0SYang-Rong Jerry Zhou 			return (DDI_SUCCESS);
41443a49c214SYang-Rong Jerry Zhou 		else
4145c6e681c0SYang-Rong Jerry Zhou 			return (uint32_t)(DDI_FAILURE);
41463a49c214SYang-Rong Jerry Zhou 	}
41473a49c214SYang-Rong Jerry Zhou 	switch (widget->type) {
41483a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_MIX:
41493a49c214SYang-Rong Jerry Zhou 			for (i = 0; i < widget->nconns; i++) {
4150b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next == i)
41513a49c214SYang-Rong Jerry Zhou 					continue;
41523a49c214SYang-Rong Jerry Zhou 				if (audiohd_find_inpin_for_monitor(codec,
41533a49c214SYang-Rong Jerry Zhou 				    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;
41583a49c214SYang-Rong Jerry Zhou 					find = 1;
41593a49c214SYang-Rong Jerry Zhou 				}
41603a49c214SYang-Rong Jerry Zhou 			}
41613a49c214SYang-Rong Jerry Zhou 			break;
41623a49c214SYang-Rong Jerry Zhou 		case WTYPE_AUDIO_SEL:
41633a49c214SYang-Rong Jerry Zhou 			for (i = 0; i < widget->nconns; i++) {
4164b96a6eceSZhao Edgar Liu - Sun Microsystems 				if (widget->output_path_next == i)
41653a49c214SYang-Rong Jerry Zhou 					continue;
41663a49c214SYang-Rong Jerry Zhou 				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;
41703a49c214SYang-Rong Jerry Zhou 					widget->path_flags |= AUDIOHD_PATH_MON;
4171b96a6eceSZhao Edgar Liu - Sun Microsystems 					find = 1;
4172b96a6eceSZhao Edgar Liu - Sun Microsystems 					break;
41733a49c214SYang-Rong Jerry Zhou 				}
41743a49c214SYang-Rong Jerry Zhou 			}
4175b96a6eceSZhao Edgar Liu - Sun Microsystems 			break;
41763a49c214SYang-Rong Jerry Zhou 		default:
41773a49c214SYang-Rong Jerry Zhou 			break;
41783a49c214SYang-Rong Jerry Zhou 	}
41793a49c214SYang-Rong Jerry Zhou 	if (!find) {
41803a49c214SYang-Rong Jerry Zhou 		widget->path_flags |= AUDIOHD_PATH_NOMON;
4181c6e681c0SYang-Rong Jerry Zhou 		return (uint32_t)(DDI_FAILURE);
41823a49c214SYang-Rong Jerry Zhou 	}
41833a49c214SYang-Rong Jerry Zhou 	else
4184c6e681c0SYang-Rong Jerry Zhou 		return (DDI_SUCCESS);
41853a49c214SYang-Rong Jerry Zhou }	/* audiohd_find_inpin_for_monitor */
4186f90d8383Scg 
41873a49c214SYang-Rong Jerry Zhou /*
41883a49c214SYang-Rong Jerry Zhou  * audiohd_build_monitor_path()
41893a49c214SYang-Rong Jerry Zhou  *
41903a49c214SYang-Rong Jerry Zhou  * Description:
41913a49c214SYang-Rong Jerry Zhou  * 	The functionality of mixer is to mix inputs, such as CD-IN, MIC,
41923a49c214SYang-Rong Jerry Zhou  * 	Line-in, etc, with DAC outputs, so as to minitor what is being
41933a49c214SYang-Rong Jerry Zhou  * 	recorded and implement "What you hear is what you get". However,
41943a49c214SYang-Rong Jerry Zhou  * 	this functionality are really hardware-dependent: the inputs
41953a49c214SYang-Rong Jerry Zhou  * 	must be directed to MIXER if they can be directed to ADC as
41963a49c214SYang-Rong Jerry Zhou  * 	recording sources.
41973a49c214SYang-Rong Jerry Zhou  */
41983a49c214SYang-Rong Jerry Zhou static void
audiohd_build_monitor_path(hda_codec_t * codec)41993a49c214SYang-Rong Jerry Zhou audiohd_build_monitor_path(hda_codec_t *codec)
42003a49c214SYang-Rong Jerry Zhou {
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;
4207f90d8383Scg 
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 				}
42273a49c214SYang-Rong Jerry Zhou 
42283a49c214SYang-Rong Jerry Zhou 				/*
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.
42323a49c214SYang-Rong Jerry Zhou 				 */
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 					}
42743a49c214SYang-Rong Jerry Zhou 
42753a49c214SYang-Rong Jerry Zhou 				}
4276f90d8383Scg 
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++;
42913a49c214SYang-Rong Jerry Zhou 			}
429288447a05SGarrett D'Amore 			path->maxmixer[j] = l;
42933a49c214SYang-Rong Jerry Zhou 		}
4294f90d8383Scg 
42953a49c214SYang-Rong Jerry Zhou 	}
42963a49c214SYang-Rong Jerry Zhou 	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;
43003a49c214SYang-Rong Jerry Zhou }	/* audiohd_build_monitor_path */
4301f90d8383Scg 
43023a49c214SYang-Rong Jerry Zhou /*
43033a49c214SYang-Rong Jerry Zhou  * audiohd_do_finish_monitor_path
43043a49c214SYang-Rong Jerry Zhou  *
43053a49c214SYang-Rong Jerry Zhou  * Description:
43063a49c214SYang-Rong Jerry Zhou  *	Enable the widgets on the monitor path
43073a49c214SYang-Rong Jerry Zhou  */
43083a49c214SYang-Rong Jerry Zhou static void
audiohd_do_finish_monitor_path(hda_codec_t * codec,audiohd_widget_t * wgt)43093a49c214SYang-Rong Jerry Zhou audiohd_do_finish_monitor_path(hda_codec_t *codec, audiohd_widget_t *wgt)
43103a49c214SYang-Rong Jerry Zhou {
43113a49c214SYang-Rong Jerry Zhou 	uint_t			caddr = codec->index;
43123a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t 	*widget = wgt;
43133a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*w;
4314ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
43153a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
43163a49c214SYang-Rong Jerry Zhou 	int			i;
43173a49c214SYang-Rong Jerry Zhou 	int			share = 0;
4318f90d8383Scg 
431931a3f136SYang-Rong Jerry Zhou 	if (!widget || widget->finish)
43203a49c214SYang-Rong Jerry Zhou 		return;
43213a49c214SYang-Rong Jerry Zhou 	if (widget->path_flags & AUDIOHD_PATH_ADC)
43223a49c214SYang-Rong Jerry Zhou 		share = 1;
4323b96a6eceSZhao Edgar Liu - Sun Microsystems 	if ((widget->outamp_cap) && !share)
4324b96a6eceSZhao Edgar Liu - Sun Microsystems 		(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);
4327b96a6eceSZhao Edgar Liu - Sun Microsystems 	if ((widget->inamp_cap) && !share) {
43283a49c214SYang-Rong Jerry Zhou 		for (i = 0; i < widget->used; i++) {
43293a49c214SYang-Rong Jerry Zhou 		(void) audioha_codec_4bit_verb_get(statep, caddr,
43303a49c214SYang-Rong Jerry Zhou 		    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));
43343a49c214SYang-Rong Jerry Zhou 		}
43353a49c214SYang-Rong Jerry Zhou 	}
433616600ba1SYang-Rong Jerry Zhou 	if ((widget->type == WTYPE_AUDIO_SEL) && (widget->nconns > 1) &&
433716600ba1SYang-Rong Jerry Zhou 	    !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]);
43403a49c214SYang-Rong Jerry Zhou 	}
43413a49c214SYang-Rong Jerry Zhou 	widget->finish = 1;
43423a49c214SYang-Rong Jerry Zhou 	if (widget->used == 0)
43433a49c214SYang-Rong Jerry Zhou 		return;
43443a49c214SYang-Rong Jerry Zhou 	if (widget->used > 0) {
43453a49c214SYang-Rong Jerry Zhou 		for (i = 0; i < widget->used; i++) {
4346b96a6eceSZhao Edgar Liu - Sun Microsystems 			wid = widget->avail_conn[widget->monitor_path_next[i]];
43473a49c214SYang-Rong Jerry Zhou 			w = codec->widget[wid];
43483a49c214SYang-Rong Jerry Zhou 			audiohd_do_finish_monitor_path(codec, w);
43493a49c214SYang-Rong Jerry Zhou 		}
43503a49c214SYang-Rong Jerry Zhou 	}
43513a49c214SYang-Rong Jerry Zhou }	/* audiohd_do_finish_monitor_path */
4352f90d8383Scg 
43537253a143Scg /*
43543a49c214SYang-Rong Jerry Zhou  * audiohd_finish_monitor_path
43553a49c214SYang-Rong Jerry Zhou  *
43563a49c214SYang-Rong Jerry Zhou  * Description:
43573a49c214SYang-Rong Jerry Zhou  *	Enable the monitor path for every ostream path
43587253a143Scg  */
43593a49c214SYang-Rong Jerry Zhou static void
audiohd_finish_monitor_path(hda_codec_t * codec)43603a49c214SYang-Rong Jerry Zhou audiohd_finish_monitor_path(hda_codec_t *codec)
43617253a143Scg {
436288447a05SGarrett D'Amore 	audiohd_path_t		*path;
43633a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget;
4364ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
43653a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
436688447a05SGarrett D'Amore 	int 			i, j, k;
43673a49c214SYang-Rong Jerry Zhou 
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];
43753a49c214SYang-Rong Jerry Zhou 				if (wid == 0) {
43763a49c214SYang-Rong Jerry Zhou 					continue;
43773a49c214SYang-Rong Jerry Zhou 				}
43783a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
43793a49c214SYang-Rong Jerry Zhou 				audiohd_do_finish_monitor_path(codec, widget);
43803a49c214SYang-Rong Jerry Zhou 			}
43813a49c214SYang-Rong Jerry Zhou 		}
43823a49c214SYang-Rong Jerry Zhou 	}
43833a49c214SYang-Rong Jerry Zhou }	/* audiohd_finish_monitor_path */
43847253a143Scg 
43853a49c214SYang-Rong Jerry Zhou /*
43863a49c214SYang-Rong Jerry Zhou  * audiohd_do_build_monit_amp()
43873a49c214SYang-Rong Jerry Zhou  *
43883a49c214SYang-Rong Jerry Zhou  * Description:
43893a49c214SYang-Rong Jerry Zhou  *	Search for the gain control widget for the monitor path
43903a49c214SYang-Rong Jerry Zhou  */
43913a49c214SYang-Rong Jerry Zhou static void
audiohd_do_build_monitor_amp(hda_codec_t * codec,audiohd_pin_t * pin,audiohd_widget_t * widget)43923a49c214SYang-Rong Jerry Zhou audiohd_do_build_monitor_amp(hda_codec_t *codec, audiohd_pin_t *pin,
43933a49c214SYang-Rong Jerry Zhou     audiohd_widget_t *widget)
43943a49c214SYang-Rong Jerry Zhou {
43953a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*w = widget;
43963a49c214SYang-Rong Jerry Zhou 	uint32_t		gain;
43973a49c214SYang-Rong Jerry Zhou 	int			i;
43983a49c214SYang-Rong Jerry Zhou 	wid_t			wid;
4399f90d8383Scg 
44003a49c214SYang-Rong Jerry Zhou 	if (!w ||
44013a49c214SYang-Rong Jerry Zhou 	    (w->type == WTYPE_PIN) ||
44023a49c214SYang-Rong Jerry Zhou 	    !w->used ||
44033a49c214SYang-Rong Jerry Zhou 	    (pin->num == AUDIOHD_MAX_CONN) ||
44043a49c214SYang-Rong Jerry Zhou 	    (w->path_flags & AUDIOHD_PATH_ADC))
44053a49c214SYang-Rong Jerry Zhou 		return;
44063a49c214SYang-Rong Jerry Zhou 	if (!(w->path_flags & AUDIOHD_PATH_DAC)) {
44073a49c214SYang-Rong Jerry Zhou 		gain = w->outamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS;
44083a49c214SYang-Rong Jerry Zhou 		if (gain) {
44093a49c214SYang-Rong Jerry Zhou 			pin->mg_dir[pin->num] = AUDIOHDC_AMP_SET_OUTPUT;
44103a49c214SYang-Rong Jerry Zhou 			pin->mg_gain[pin->num] = gain;
44113a49c214SYang-Rong Jerry Zhou 			pin->mg_wid[pin->num] = w->wid_wid;
44123a49c214SYang-Rong Jerry Zhou 			pin->mg_gain[pin->num] >>= AUDIOHD_GAIN_OFF;
44133a49c214SYang-Rong Jerry Zhou 			pin->num++;
44143a49c214SYang-Rong Jerry Zhou 			return;
44153a49c214SYang-Rong Jerry Zhou 		}
44163a49c214SYang-Rong Jerry Zhou 		gain = w->inamp_cap & AUDIOHDC_AMP_CAP_STEP_NUMS;
44173a49c214SYang-Rong Jerry Zhou 		if (gain) {
44183a49c214SYang-Rong Jerry Zhou 			pin->mg_dir[pin->num] = AUDIOHDC_AMP_SET_INPUT;
44193a49c214SYang-Rong Jerry Zhou 			pin->mg_gain[pin->num] = gain;
44203a49c214SYang-Rong Jerry Zhou 			pin->mg_wid[pin->num] = w->wid_wid;
44213a49c214SYang-Rong Jerry Zhou 			pin->mg_gain[pin->num] >>= AUDIOHD_GAIN_OFF;
44223a49c214SYang-Rong Jerry Zhou 			pin->num++;
44233a49c214SYang-Rong Jerry Zhou 			return;
4424f90d8383Scg 		}
44257253a143Scg 	}
44263a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < w->used; i++) {
4427b96a6eceSZhao Edgar Liu - Sun Microsystems 		wid = w->avail_conn[w->monitor_path_next[i]];
44283a49c214SYang-Rong Jerry Zhou 		audiohd_do_build_monitor_amp(codec, pin, codec->widget[wid]);
44293a49c214SYang-Rong Jerry Zhou 	}
44307253a143Scg 
44317253a143Scg 
44323a49c214SYang-Rong Jerry Zhou }	/* audiohd_do_build_monitor_amp() */
44337253a143Scg 
44347253a143Scg /*
44353a49c214SYang-Rong Jerry Zhou  * audiohd_build_monitor_amp()
44363a49c214SYang-Rong Jerry Zhou  *
44373a49c214SYang-Rong Jerry Zhou  * Description:
44383a49c214SYang-Rong Jerry Zhou  *	Search gain control widget for every ostream monitor
44397253a143Scg  */
44403a49c214SYang-Rong Jerry Zhou static void
audiohd_build_monitor_amp(hda_codec_t * codec)44413a49c214SYang-Rong Jerry Zhou audiohd_build_monitor_amp(hda_codec_t *codec)
44427253a143Scg {
444388447a05SGarrett D'Amore 	audiohd_path_t		*path;
44443a49c214SYang-Rong Jerry Zhou 	audiohd_widget_t	*widget, *w;
4445ea463888SZhao Edgar Liu - Sun Microsystems 	audiohd_state_t		*statep = codec->statep;
44463a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
44473a49c214SYang-Rong Jerry Zhou 	wid_t			wid, id;
444888447a05SGarrett D'Amore 	int			i, j, k;
44493a49c214SYang-Rong Jerry Zhou 
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];
44563a49c214SYang-Rong Jerry Zhou 			w = codec->widget[id];
44573a49c214SYang-Rong Jerry Zhou 			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];
44603a49c214SYang-Rong Jerry Zhou 				if (!wid)
44613a49c214SYang-Rong Jerry Zhou 					continue;
44623a49c214SYang-Rong Jerry Zhou 				widget = codec->widget[wid];
44633a49c214SYang-Rong Jerry Zhou 				audiohd_do_build_monitor_amp(codec, pin,
44643a49c214SYang-Rong Jerry Zhou 				    widget);
44653a49c214SYang-Rong Jerry Zhou 			}
44663a49c214SYang-Rong Jerry Zhou 		}
4467f90d8383Scg 	}
44683a49c214SYang-Rong Jerry Zhou }
44697253a143Scg 
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);
4768ee97b734SZhao Edgar Liu - Sun Microsystems 				}
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 
49223a49c214SYang-Rong Jerry Zhou /*
49233a49c214SYang-Rong Jerry Zhou  * audiohd_build_path()
49243a49c214SYang-Rong Jerry Zhou  *
49253a49c214SYang-Rong Jerry Zhou  * Description:
49263a49c214SYang-Rong Jerry Zhou  *	Here we build the output, input, monitor path.
49273a49c214SYang-Rong Jerry Zhou  *	And also enable the path in default.
49283a49c214SYang-Rong Jerry Zhou  *	Search for the gain and mute control for the path
49293a49c214SYang-Rong Jerry Zhou  */
49303a49c214SYang-Rong Jerry Zhou static void
audiohd_build_path(audiohd_state_t * statep)49313a49c214SYang-Rong Jerry Zhou audiohd_build_path(audiohd_state_t *statep)
49323a49c214SYang-Rong Jerry Zhou {
49333a49c214SYang-Rong Jerry Zhou 	int		i;
49347253a143Scg 
49353a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
49363a49c214SYang-Rong Jerry Zhou 		if (statep->codec[i]) {
49373a49c214SYang-Rong Jerry Zhou 			audiohd_build_output_path(statep->codec[i]);
49383a49c214SYang-Rong Jerry Zhou 			audiohd_build_output_amp(statep->codec[i]);
49393a49c214SYang-Rong Jerry Zhou 			audiohd_finish_output_path(statep->codec[i]);
49403a49c214SYang-Rong Jerry Zhou 
49413a49c214SYang-Rong Jerry Zhou 			audiohd_build_input_path(statep->codec[i]);
49423a49c214SYang-Rong Jerry Zhou 			audiohd_build_input_amp(statep->codec[i]);
49433a49c214SYang-Rong Jerry Zhou 			audiohd_finish_input_path(statep->codec[i]);
49443a49c214SYang-Rong Jerry Zhou 
49453a49c214SYang-Rong Jerry Zhou 			audiohd_build_monitor_path(statep->codec[i]);
49463a49c214SYang-Rong Jerry Zhou 			audiohd_build_monitor_amp(statep->codec[i]);
49473a49c214SYang-Rong Jerry Zhou 			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]);
49547253a143Scg 		}
49557253a143Scg 	}
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");
5091c0e48486SYang-Rong Jerry Zhou 				return (DDI_FAILURE);
5092c0e48486SYang-Rong Jerry Zhou 			}
509388447a05SGarrett D'Amore 		}
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 }
52077253a143Scg 
52087253a143Scg /*
52095ec2209cSZhao Edgar Liu - Sun Microsystems  * audiohd_change_widget_power_state(audiohd_state_t *statep, int state)
52103a49c214SYang-Rong Jerry Zhou  * 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.
52147253a143Scg  */
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)
52177253a143Scg {
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;
52223a49c214SYang-Rong Jerry Zhou 
52235ec2209cSZhao Edgar Liu - Sun Microsystems 	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
52245ec2209cSZhao Edgar Liu - Sun Microsystems 		codec = statep->codec[i];
52255ec2209cSZhao Edgar Liu - Sun Microsystems 		if (codec == NULL)
52265ec2209cSZhao Edgar Liu - Sun Microsystems 			continue;
52275ec2209cSZhao Edgar Liu - Sun Microsystems 		for (wid = codec->first_wid; wid <= codec->last_wid;
52285ec2209cSZhao Edgar Liu - Sun Microsystems 		    wid++) {
52295ec2209cSZhao Edgar Liu - Sun Microsystems 			widget = codec->widget[wid];
52305ec2209cSZhao Edgar Liu - Sun Microsystems 			if (widget->widget_cap &
52315ec2209cSZhao Edgar Liu - Sun Microsystems 			    AUDIOHD_WIDCAP_PWRCTRL) {
52325ec2209cSZhao Edgar Liu - Sun Microsystems 				(void) audioha_codec_verb_get(statep,
52335ec2209cSZhao Edgar Liu - Sun Microsystems 				    codec->index, wid,
52345ec2209cSZhao Edgar Liu - Sun Microsystems 				    AUDIOHDC_VERB_SET_POWER_STATE,
52355ec2209cSZhao Edgar Liu - Sun Microsystems 				    state);
523688447a05SGarrett D'Amore 			}
5237f90d8383Scg 		}
52387253a143Scg 	}
523988447a05SGarrett D'Amore }
52403a49c214SYang-Rong Jerry Zhou /*
524188447a05SGarrett D'Amore  * audiohd_restore_path()
52423a49c214SYang-Rong Jerry Zhou  * Description:
524388447a05SGarrett D'Amore  * 	This routine is used to restore the path on the codec.
52443a49c214SYang-Rong Jerry Zhou  */
52453a49c214SYang-Rong Jerry Zhou static void
audiohd_restore_path(audiohd_state_t * statep)524688447a05SGarrett D'Amore audiohd_restore_path(audiohd_state_t *statep)
52473a49c214SYang-Rong Jerry Zhou {
524888447a05SGarrett D'Amore 	int			i;
524988447a05SGarrett D'Amore 	hda_codec_t		*codec;
52507253a143Scg 
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]);
52597253a143Scg 	}
526088447a05SGarrett D'Amore }
52617253a143Scg 
52627253a143Scg /*
526388447a05SGarrett D'Amore  * audiohd_reset_pins_ur_cap()
52643a49c214SYang-Rong Jerry Zhou  * Description:
526588447a05SGarrett D'Amore  * 	Enable the unsolicited response of the pins which have the unsolicited
526688447a05SGarrett D'Amore  * 	response capability
52677253a143Scg  */
52683a49c214SYang-Rong Jerry Zhou static void
audiohd_reset_pins_ur_cap(audiohd_state_t * statep)526988447a05SGarrett D'Amore audiohd_reset_pins_ur_cap(audiohd_state_t *statep)
52707253a143Scg {
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;
52767253a143Scg 
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 		}
5302f90d8383Scg 	}
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)
5306644f3c1fScg {
53073a49c214SYang-Rong Jerry Zhou 	int		i;
530888447a05SGarrett D'Amore 	wid_t		wid;
530988447a05SGarrett D'Amore 	hda_codec_t	*codec;
5310644f3c1fScg 
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;
5316644f3c1fScg 
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) {
53239ba19c87SYang-Rong Jerry Zhou 			/*
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.
53279ba19c87SYang-Rong Jerry Zhou 			 */
53289ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
53299ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_MASK, AUDIOHDC_GPIO_ENABLE);
53309ba19c87SYang-Rong Jerry Zhou 			(void) audioha_codec_verb_get(statep, i, wid,
53319ba19c87SYang-Rong Jerry Zhou 			    AUDIOHDC_VERB_SET_GPIO_DIREC, AUDIOHDC_GPIO_DIRECT);
53329ba19c87SYang-Rong Jerry Zhou 			(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);
53359ba19c87SYang-Rong Jerry Zhou 			(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 }
53413a49c214SYang-Rong Jerry Zhou /*
534288447a05SGarrett D'Amore  * audiohd_resume()
53433a49c214SYang-Rong Jerry Zhou  */
534488447a05SGarrett D'Amore static int
audiohd_resume(audiohd_state_t * statep)534588447a05SGarrett D'Amore audiohd_resume(audiohd_state_t *statep)
53463a49c214SYang-Rong Jerry Zhou {
534788447a05SGarrett D'Amore 	uint8_t		rirbsts;
5348644f3c1fScg 
53493a49c214SYang-Rong Jerry Zhou 	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");
53553a49c214SYang-Rong Jerry Zhou 		mutex_exit(&statep->hda_mutex);
53565ec2209cSZhao Edgar Liu - Sun Microsystems 		return (DDI_FAILURE);
53573a49c214SYang-Rong Jerry Zhou 	}
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);
536368c47f65SGarrett D'Amore 	/* set widget power to D0 */
536468c47f65SGarrett D'Amore 	audiohd_change_widget_power_state(statep, AUDIOHD_PW_D0);
536588447a05SGarrett D'Amore 
536688447a05SGarrett D'Amore 	audiohd_configure_output(statep);
536788447a05SGarrett D'Amore 	audiohd_configure_input(statep);
536868c47f65SGarrett D'Amore 	mutex_exit(&statep->hda_mutex);
5369644f3c1fScg 
537068c47f65SGarrett D'Amore 	audio_dev_resume(statep->adev);
5371644f3c1fScg 
537288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
537388447a05SGarrett D'Amore }	/* audiohd_resume */
5374644f3c1fScg 
5375644f3c1fScg /*
537688447a05SGarrett D'Amore  * audiohd_suspend()
5377644f3c1fScg  */
537888447a05SGarrett D'Amore static int
audiohd_suspend(audiohd_state_t * statep)537988447a05SGarrett D'Amore audiohd_suspend(audiohd_state_t *statep)
5380644f3c1fScg {
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;
5385644f3c1fScg 
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);
53923a49c214SYang-Rong Jerry Zhou 
539388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
539488447a05SGarrett D'Amore }	/* audiohd_suspend */
5395644f3c1fScg 
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 }
5412644f3c1fScg 
5413644f3c1fScg /*
541488447a05SGarrett D'Amore  * audiohd_enable_pin()
5415644f3c1fScg  */
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)
5436644f3c1fScg {
543788447a05SGarrett D'Amore 	audiohd_path_t		*path;
543888447a05SGarrett D'Amore 	audiohd_widget_t	*widget;
54393a49c214SYang-Rong Jerry Zhou 	audiohd_pin_t		*pin;
544088447a05SGarrett D'Amore 	int			i, j;
544188447a05SGarrett D'Amore 	wid_t			wid;
54423a49c214SYang-Rong Jerry Zhou 
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);
54563a49c214SYang-Rong Jerry Zhou 				}
54573a49c214SYang-Rong Jerry Zhou 			}
54583a49c214SYang-Rong Jerry Zhou 
54593a49c214SYang-Rong Jerry Zhou 		} 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);
54683a49c214SYang-Rong Jerry Zhou 				}
54693a49c214SYang-Rong Jerry Zhou 			}
54703a49c214SYang-Rong Jerry Zhou 		}
54713a49c214SYang-Rong Jerry Zhou 	}
547288447a05SGarrett D'Amore }
5473644f3c1fScg /*
547488447a05SGarrett D'Amore  * audiohd_select_mic()
54753a49c214SYang-Rong Jerry Zhou  *
54763a49c214SYang-Rong Jerry Zhou  * 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.
5480644f3c1fScg  */
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)
5484644f3c1fScg {
54853a49c214SYang-Rong Jerry Zhou 	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;
54913a49c214SYang-Rong Jerry Zhou 
549288447a05SGarrett D'Amore 	codec = statep->codec[index];
54933a49c214SYang-Rong Jerry Zhou 	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 
5502aa5c9fd8SZhao Edgar Liu - Sun Microsystems 		for (j = 0; j < path->pin_nums; j++) {
5503aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			wid = path->pin_wid[j];
5504aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			widget = codec->widget[wid];
5505aa5c9fd8SZhao Edgar Liu - Sun Microsystems 			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;
55423a49c214SYang-Rong Jerry Zhou 				}
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);
5563aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					}
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);
5575aa5c9fd8SZhao Edgar Liu - Sun Microsystems 					}
5576aa5c9fd8SZhao Edgar Liu - Sun Microsystems 				}
55773a49c214SYang-Rong Jerry Zhou 			}
5578644f3c1fScg 		}
5579644f3c1fScg 	}
5580aa5c9fd8SZhao Edgar Liu - Sun Microsystems 
55813a49c214SYang-Rong Jerry Zhou 	/*
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.
55853a49c214SYang-Rong Jerry Zhou 	 */
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;
56163a49c214SYang-Rong Jerry Zhou 				}
56173a49c214SYang-Rong Jerry Zhou 			}
56183a49c214SYang-Rong Jerry Zhou 		}
5619644f3c1fScg 	}
562088447a05SGarrett D'Amore }
5621644f3c1fScg /*
562288447a05SGarrett D'Amore  * audiohd_pin_sense()
5623644f3c1fScg  *
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.
5629644f3c1fScg  */
5630644f3c1fScg 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)
5632644f3c1fScg {
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;
56383a49c214SYang-Rong Jerry Zhou 	hda_codec_t		*codec;
5639644f3c1fScg 
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;
56723a49c214SYang-Rong Jerry Zhou 		}
567388447a05SGarrett D'Amore 		/* output pin is unplugged */
567488447a05SGarrett D'Amore 		audiohd_change_speaker_state(statep, AUDIOHD_SP_ON);
56753a49c214SYang-Rong Jerry Zhou 	}
5676644f3c1fScg 
567788447a05SGarrett D'Amore }
567888447a05SGarrett D'Amore 
5679644f3c1fScg /*
568088447a05SGarrett D'Amore  * audiohd_disable_intr()
56813a49c214SYang-Rong Jerry Zhou  *
56823a49c214SYang-Rong Jerry Zhou  * Description:
568388447a05SGarrett D'Amore  *	Disable all possible interrupts.
5684644f3c1fScg  */
568588447a05SGarrett D'Amore static void
audiohd_disable_intr(audiohd_state_t * statep)568688447a05SGarrett D'Amore audiohd_disable_intr(audiohd_state_t *statep)
5687644f3c1fScg {
568888447a05SGarrett D'Amore 	int		i;
568988447a05SGarrett D'Amore 	uint32_t	base;
5690644f3c1fScg 
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;
56973a49c214SYang-Rong Jerry Zhou 	}
569888447a05SGarrett D'Amore 	AUDIOHD_REG_SET32(AUDIOHD_REG_INTSTS, (uint32_t)(-1));
5699644f3c1fScg 
570088447a05SGarrett D'Amore }	/* audiohd_disable_intr() */
5701644f3c1fScg 
5702644f3c1fScg 
57033a49c214SYang-Rong Jerry Zhou /*
57043a49c214SYang-Rong Jerry Zhou  * audiohd_12bit_verb_to_codec()
57053a49c214SYang-Rong Jerry Zhou  *
57063a49c214SYang-Rong Jerry Zhou  * Description:
57073a49c214SYang-Rong Jerry Zhou  *
57083a49c214SYang-Rong Jerry Zhou  */
57093a49c214SYang-Rong Jerry Zhou static int
audiohd_12bit_verb_to_codec(audiohd_state_t * statep,uint8_t caddr,uint8_t wid,uint16_t cmd,uint8_t param)57103a49c214SYang-Rong Jerry Zhou audiohd_12bit_verb_to_codec(audiohd_state_t *statep, uint8_t caddr,
57113a49c214SYang-Rong Jerry Zhou     uint8_t wid,
57123a49c214SYang-Rong Jerry Zhou     uint16_t cmd, uint8_t param)
57133a49c214SYang-Rong Jerry Zhou {
57143a49c214SYang-Rong Jerry Zhou 	uint32_t	verb;
57153a49c214SYang-Rong Jerry Zhou 	uint16_t	wptr;
57163a49c214SYang-Rong Jerry Zhou 	uint16_t	rptr;
5717644f3c1fScg 
57183a49c214SYang-Rong Jerry Zhou 	ASSERT((cmd & AUDIOHDC_12BIT_VERB_MASK) == 0);
5719644f3c1fScg 
57203a49c214SYang-Rong Jerry Zhou 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBWP) & AUDIOHD_CMDIO_ENT_MASK;
57213a49c214SYang-Rong Jerry Zhou 	rptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBRP) & AUDIOHD_CMDIO_ENT_MASK;
5722644f3c1fScg 
57233a49c214SYang-Rong Jerry Zhou 	wptr++;
57243a49c214SYang-Rong Jerry Zhou 	wptr &= AUDIOHD_CMDIO_ENT_MASK;
5725644f3c1fScg 
57263a49c214SYang-Rong Jerry Zhou 	/* overflow */
57273a49c214SYang-Rong Jerry Zhou 	if (wptr == rptr) {
5728c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
57293a49c214SYang-Rong Jerry Zhou 	}
5730644f3c1fScg 
57313a49c214SYang-Rong Jerry Zhou 	verb = (caddr & 0x0f) << AUDIOHD_VERB_ADDR_OFF;
57323a49c214SYang-Rong Jerry Zhou 	verb |= wid << AUDIOHD_VERB_NID_OFF;
57333a49c214SYang-Rong Jerry Zhou 	verb |= cmd << AUDIOHD_VERB_CMD_OFF;
57343a49c214SYang-Rong Jerry Zhou 	verb |= param;
5735644f3c1fScg 
57363a49c214SYang-Rong Jerry Zhou 	*((uint32_t *)(statep->hda_dma_corb.ad_vaddr) + wptr) = verb;
57373a49c214SYang-Rong Jerry Zhou 	(void) ddi_dma_sync(statep->hda_dma_corb.ad_dmahdl, 0,
57383a49c214SYang-Rong Jerry Zhou 	    sizeof (sd_bdle_t) * AUDIOHD_BDLE_NUMS, DDI_DMA_SYNC_FORDEV);
57393a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, wptr);
5740644f3c1fScg 
5741c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
57423a49c214SYang-Rong Jerry Zhou 
57433a49c214SYang-Rong Jerry Zhou }	/* audiohd_12bit_verb_to_codec() */
5744644f3c1fScg 
5745644f3c1fScg /*
57463a49c214SYang-Rong Jerry Zhou  * audiohd_4bit_verb_to_codec()
57473a49c214SYang-Rong Jerry Zhou  *
57483a49c214SYang-Rong Jerry Zhou  * Description:
57493a49c214SYang-Rong Jerry Zhou  *
5750644f3c1fScg  */
5751644f3c1fScg static int
audiohd_4bit_verb_to_codec(audiohd_state_t * statep,uint8_t caddr,uint8_t wid,uint32_t cmd,uint16_t param)57523a49c214SYang-Rong Jerry Zhou audiohd_4bit_verb_to_codec(audiohd_state_t *statep, uint8_t caddr,
57533a49c214SYang-Rong Jerry Zhou     uint8_t wid,
57543a49c214SYang-Rong Jerry Zhou     uint32_t cmd, uint16_t param)
5755644f3c1fScg {
57563a49c214SYang-Rong Jerry Zhou 	uint32_t	verb;
57573a49c214SYang-Rong Jerry Zhou 	uint16_t	wptr;
57583a49c214SYang-Rong Jerry Zhou 	uint16_t	rptr;
5759644f3c1fScg 
57603a49c214SYang-Rong Jerry Zhou 	ASSERT((cmd & AUDIOHDC_4BIT_VERB_MASK) == 0);
5761644f3c1fScg 
57623a49c214SYang-Rong Jerry Zhou 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBWP) & AUDIOHD_CMDIO_ENT_MASK;
57633a49c214SYang-Rong Jerry Zhou 	rptr = AUDIOHD_REG_GET16(AUDIOHD_REG_CORBRP) & AUDIOHD_CMDIO_ENT_MASK;
5764644f3c1fScg 
57653a49c214SYang-Rong Jerry Zhou 	wptr++;
57663a49c214SYang-Rong Jerry Zhou 	wptr &= AUDIOHD_CMDIO_ENT_MASK;
5767644f3c1fScg 
57683a49c214SYang-Rong Jerry Zhou 	/* overflow */
57693a49c214SYang-Rong Jerry Zhou 	if (wptr == rptr) {
5770c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
5771644f3c1fScg 	}
5772644f3c1fScg 
57733a49c214SYang-Rong Jerry Zhou 	verb = (caddr & 0x0f) << AUDIOHD_VERB_ADDR_OFF;
57743a49c214SYang-Rong Jerry Zhou 	verb |= wid << AUDIOHD_VERB_NID_OFF;
57753a49c214SYang-Rong Jerry Zhou 	verb |= cmd << AUDIOHD_VERB_CMD16_OFF;
57763a49c214SYang-Rong Jerry Zhou 	verb |= param;
57773a49c214SYang-Rong Jerry Zhou 
57783a49c214SYang-Rong Jerry Zhou 	*((uint32_t *)(statep->hda_dma_corb.ad_vaddr) + wptr) = verb;
57793a49c214SYang-Rong Jerry Zhou 	AUDIOHD_REG_SET16(AUDIOHD_REG_CORBWP, wptr);
57803a49c214SYang-Rong Jerry Zhou 
5781c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
5782644f3c1fScg 
57833a49c214SYang-Rong Jerry Zhou }	/* audiohd_4bit_verb_to_codec() */
5784644f3c1fScg 
5785644f3c1fScg /*
57863a49c214SYang-Rong Jerry Zhou  * audiohd_response_from_codec()
57873a49c214SYang-Rong Jerry Zhou  *
57883a49c214SYang-Rong Jerry Zhou  * Description:
57893a49c214SYang-Rong Jerry Zhou  *
5790644f3c1fScg  */
5791644f3c1fScg static int
audiohd_response_from_codec(audiohd_state_t * statep,uint32_t * resp,uint32_t * respex)57923a49c214SYang-Rong Jerry Zhou audiohd_response_from_codec(audiohd_state_t *statep, uint32_t *resp,
57933a49c214SYang-Rong Jerry Zhou     uint32_t *respex)
5794644f3c1fScg {
57953a49c214SYang-Rong Jerry Zhou 	uint16_t	wptr;
57963a49c214SYang-Rong Jerry Zhou 	uint16_t	rptr;
57973a49c214SYang-Rong Jerry Zhou 	uint32_t	*lp;
5798644f3c1fScg 
57993a49c214SYang-Rong Jerry Zhou 	wptr = AUDIOHD_REG_GET16(AUDIOHD_REG_RIRBWP) & 0x00ff;
58003a49c214SYang-Rong Jerry Zhou 	rptr = statep->hda_rirb_rp;
5801644f3c1fScg 
58023a49c214SYang-Rong Jerry Zhou 	if (rptr == wptr) {
5803c6e681c0SYang-Rong Jerry Zhou 		return (DDI_FAILURE);
5804644f3c1fScg 	}
5805644f3c1fScg 
58063a49c214SYang-Rong Jerry Zhou 	rptr++;
58073a49c214SYang-Rong Jerry Zhou 	rptr &= AUDIOHD_RING_MAX_SIZE;
5808644f3c1fScg 
58093a49c214SYang-Rong Jerry Zhou 	lp = (uint32_t *)(statep->hda_dma_rirb.ad_vaddr) + (rptr << 1);
58103a49c214SYang-Rong Jerry Zhou 	*resp = *(lp);
58113a49c214SYang-Rong Jerry Zhou 	*respex = *(lp + 1);
5812644f3c1fScg 
58133a49c214SYang-Rong Jerry Zhou 	statep->hda_rirb_rp = rptr;
5814644f3c1fScg 
5815c6e681c0SYang-Rong Jerry Zhou 	return (DDI_SUCCESS);
5816644f3c1fScg 
58173a49c214SYang-Rong Jerry Zhou }	/* audiohd_response_from_codec() */
58183a49c214SYang-Rong Jerry Zhou 
5819644f3c1fScg 
5820644f3c1fScg /*
58213a49c214SYang-Rong Jerry Zhou  * audioha_codec_verb_get()
5822644f3c1fScg  */
58233a49c214SYang-Rong Jerry Zhou static uint32_t
audioha_codec_verb_get(void * arg,uint8_t caddr,uint8_t wid,uint16_t verb,uint8_t param)58243a49c214SYang-Rong Jerry Zhou audioha_codec_verb_get(void *arg, uint8_t caddr, uint8_t wid,
58253a49c214SYang-Rong Jerry Zhou     uint16_t verb,
58263a49c214SYang-Rong Jerry Zhou     uint8_t param)
5827644f3c1fScg {
58283a49c214SYang-Rong Jerry Zhou 	audiohd_state_t	*statep = (audiohd_state_t *)arg;
58293a49c214SYang-Rong Jerry Zhou 	uint32_t	resp;
58303a49c214SYang-Rong Jerry Zhou 	uint32_t	respex;
58313a49c214SYang-Rong Jerry Zhou 	int		ret;
58323a49c214SYang-Rong Jerry Zhou 	int		i;
5833644f3c1fScg 
58343a49c214SYang-Rong Jerry Zhou 	ret = audiohd_12bit_verb_to_codec(statep, caddr, wid, verb, param);
5835c6e681c0SYang-Rong Jerry Zhou 	if (ret != DDI_SUCCESS) {
58363a49c214SYang-Rong Jerry Zhou 		return (uint32_t)(-1);
58373a49c214SYang-Rong Jerry Zhou 	}
58383a49c214SYang-Rong Jerry Zhou 
58393a49c214SYang-Rong Jerry Zhou 	/*
58403a49c214SYang-Rong Jerry Zhou 	 * Empirical testing times. 50 times is enough for audiohd spec 1.0.
58413a49c214SYang-Rong Jerry Zhou 	 * But we need to make it work for audiohd spec 0.9, which is just a
58423a49c214SYang-Rong Jerry Zhou 	 * draft version and requires more time to wait.
58433a49c214SYang-Rong Jerry Zhou 	 */
58443a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < 500; i++) {
58453a49c214SYang-Rong Jerry Zhou 		ret = audiohd_response_from_codec(statep, &resp, &respex);
58463a49c214SYang-Rong Jerry Zhou 		if (((respex & AUDIOHD_BDLE_RIRB_SDI) == caddr) &&
58473a49c214SYang-Rong Jerry Zhou 		    ((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
5848c6e681c0SYang-Rong Jerry Zhou 		    (ret == DDI_SUCCESS))
58493a49c214SYang-Rong Jerry Zhou 			break;
585013084339SYang-Rong Jerry Zhou 		/* Empirical testing time, which works well */
585113084339SYang-Rong Jerry Zhou 		drv_usecwait(30);
58523a49c214SYang-Rong Jerry Zhou 	}
5853644f3c1fScg 
5854c6e681c0SYang-Rong Jerry Zhou 	if (ret == DDI_SUCCESS) {
58553a49c214SYang-Rong Jerry Zhou 		return (resp);
5856644f3c1fScg 	}
5857644f3c1fScg 
58580c240c64SZhao Edgar Liu - Sun Microsystems 	if (wid != AUDIOHDC_NODE_ROOT && param != AUDIOHDC_PAR_VENDOR_ID) {
58590c240c64SZhao Edgar Liu - Sun Microsystems 		audio_dev_warn(statep->adev,  "timeout when get "
58600c240c64SZhao Edgar Liu - Sun Microsystems 		    "response from codec: wid=%d, verb=0x%04x, param=0x%04x",
58610c240c64SZhao Edgar Liu - Sun Microsystems 		    wid, verb, param);
58620c240c64SZhao Edgar Liu - Sun Microsystems 	}
5863644f3c1fScg 
58643a49c214SYang-Rong Jerry Zhou 	return ((uint32_t)(-1));
5865644f3c1fScg 
58663a49c214SYang-Rong Jerry Zhou }	/* audioha_codec_verb_get() */
5867644f3c1fScg 
5868644f3c1fScg 
5869644f3c1fScg /*
58703a49c214SYang-Rong Jerry Zhou  * audioha_codec_4bit_verb_get()
5871644f3c1fScg  */
58723a49c214SYang-Rong Jerry Zhou static uint32_t
audioha_codec_4bit_verb_get(void * arg,uint8_t caddr,uint8_t wid,uint16_t verb,uint16_t param)58733a49c214SYang-Rong Jerry Zhou audioha_codec_4bit_verb_get(void *arg, uint8_t caddr, uint8_t wid,
58743a49c214SYang-Rong Jerry Zhou     uint16_t verb, uint16_t param)
5875644f3c1fScg {
58763a49c214SYang-Rong Jerry Zhou 	audiohd_state_t	*statep = (audiohd_state_t *)arg;
58773a49c214SYang-Rong Jerry Zhou 	uint32_t	resp;
58783a49c214SYang-Rong Jerry Zhou 	uint32_t	respex;
58793a49c214SYang-Rong Jerry Zhou 	int		ret;
58803a49c214SYang-Rong Jerry Zhou 	int		i;
5881644f3c1fScg 
58823a49c214SYang-Rong Jerry Zhou 	ret = audiohd_4bit_verb_to_codec(statep, caddr, wid, verb, param);
5883c6e681c0SYang-Rong Jerry Zhou 	if (ret != DDI_SUCCESS) {
58843a49c214SYang-Rong Jerry Zhou 		return (uint32_t)(-1);
58853a49c214SYang-Rong Jerry Zhou 	}
5886644f3c1fScg 
58873a49c214SYang-Rong Jerry Zhou 	for (i = 0; i < 500; i++) {
58883a49c214SYang-Rong Jerry Zhou 		ret = audiohd_response_from_codec(statep, &resp, &respex);
58893a49c214SYang-Rong Jerry Zhou 		if (((respex & AUDIOHD_BDLE_RIRB_SDI) == caddr) &&
58903a49c214SYang-Rong Jerry Zhou 		    ((respex & AUDIOHD_BDLE_RIRB_UNSOLICIT) == 0) &&
5891c6e681c0SYang-Rong Jerry Zhou 		    (ret == DDI_SUCCESS))
58923a49c214SYang-Rong Jerry Zhou 			break;
589313084339SYang-Rong Jerry Zhou 		/* Empirical testing time, which works well */
589413084339SYang-Rong Jerry Zhou 		drv_usecwait(30);
58953a49c214SYang-Rong Jerry Zhou 	}
5896644f3c1fScg 
5897c6e681c0SYang-Rong Jerry Zhou 	if (ret == DDI_SUCCESS) {
58983a49c214SYang-Rong Jerry Zhou 		return (resp);
58993a49c214SYang-Rong Jerry Zhou 	}
5900644f3c1fScg 
590188447a05SGarrett D'Amore 	audio_dev_warn(statep->adev,  "timeout when get "
59020c240c64SZhao Edgar Liu - Sun Microsystems 	    "response from codec: wid=%d, verb=0x%04x, param=0x%04x",
590388447a05SGarrett D'Amore 	    wid, verb, param);
5904644f3c1fScg 
59053a49c214SYang-Rong Jerry Zhou 	return ((uint32_t)(-1));
5906644f3c1fScg 
59073a49c214SYang-Rong Jerry Zhou }	/* audioha_codec_4bit_verb_get() */
5908