144f16897SAxel Dörfler/*
241828f15SAxel Dörfler * Copyright 2007-2012, Haiku, Inc. All Rights Reserved.
344f16897SAxel Dörfler * Distributed under the terms of the MIT License.
444f16897SAxel Dörfler *
544f16897SAxel Dörfler * Authors:
644f16897SAxel Dörfler *		Ithamar Adema, ithamar AT unet DOT nl
717959555SAxel Dörfler *		Axel D��rfler, axeld@pinc-software.de
8bd7b0937SJérôme Duval *		J��r��me Duval, korli@users.berlios.de
944f16897SAxel Dörfler */
1044f16897SAxel Dörfler
1144f16897SAxel Dörfler
1289db0a5bSIthamar R. Adema#include "driver.h"
1389db0a5bSIthamar R. Adema#include "hda_codec_defs.h"
1489db0a5bSIthamar R. Adema
1541828f15SAxel Dörfler
16144c4c62SJérôme Duval#undef TRACE
17144c4c62SJérôme Duval#define TRACE_CODEC
18144c4c62SJérôme Duval#ifdef TRACE_CODEC
199457b769SAdrien Destugues#	define TRACE(a...) dprintf("hda: " a)
20144c4c62SJérôme Duval#else
2141828f15SAxel Dörfler#	define TRACE(a...)
22144c4c62SJérôme Duval#endif
239457b769SAdrien Destugues#define ERROR(a...) dprintf("hda: " a)
24144c4c62SJérôme Duval
2541828f15SAxel Dörfler
26144c4c62SJérôme Duval#define HDA_ALL 0xffffffff
27a9bff40aSJérôme Duval#define HDA_QUIRK_GPIO_COUNT	8
28144c4c62SJérôme Duval#define HDA_QUIRK_GPIO0		(1 << 0)
29144c4c62SJérôme Duval#define HDA_QUIRK_GPIO1		(1 << 1)
30144c4c62SJérôme Duval#define HDA_QUIRK_GPIO2		(1 << 2)
31144c4c62SJérôme Duval#define HDA_QUIRK_GPIO3		(1 << 3)
32144c4c62SJérôme Duval#define HDA_QUIRK_GPIO4		(1 << 4)
33144c4c62SJérôme Duval#define HDA_QUIRK_GPIO5		(1 << 5)
34144c4c62SJérôme Duval#define HDA_QUIRK_GPIO6		(1 << 6)
35144c4c62SJérôme Duval#define HDA_QUIRK_GPIO7		(1 << 7)
36144c4c62SJérôme Duval#define HDA_QUIRK_IVREF50	(1 << 8)
37144c4c62SJérôme Duval#define HDA_QUIRK_IVREF80	(1 << 9)
38144c4c62SJérôme Duval#define HDA_QUIRK_IVREF100	(1 << 10)
39144c4c62SJérôme Duval#define HDA_QUIRK_OVREF50	(1 << 11)
40144c4c62SJérôme Duval#define HDA_QUIRK_OVREF80	(1 << 12)
41144c4c62SJérôme Duval#define HDA_QUIRK_OVREF100	(1 << 13)
42144c4c62SJérôme Duval#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 \
43144c4c62SJérôme Duval	| HDA_QUIRK_IVREF100)
44144c4c62SJérôme Duval#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 \
45144c4c62SJérôme Duval	| HDA_QUIRK_OVREF100)
463a58749cSIngo Weinhold
473a58749cSIngo Weinhold
48144c4c62SJérôme Duval#define ANALOGDEVICES_VENDORID		0x11d4
49144c4c62SJérôme Duval#define CIRRUSLOGIC_VENDORID		0x1013
50144c4c62SJérôme Duval#define CONEXANT_VENDORID			0x14f1
51e43d37dbSJérôme Duval#define IDT_VENDORID				0x111d
52144c4c62SJérôme Duval#define REALTEK_VENDORID			0x10ec
53144c4c62SJérôme Duval#define SIGMATEL_VENDORID			0x8384
54144c4c62SJérôme Duval
5544f16897SAxel Dörfler
5644f16897SAxel Dörflerstatic const char* kPortConnector[] = {
5789db0a5bSIthamar R. Adema	"Jack", "None", "Fixed", "Dual"
5889db0a5bSIthamar R. Adema};
5989db0a5bSIthamar R. Adema
6044f16897SAxel Dörflerstatic const char* kDefaultDevice[] = {
61ab496a4bSStephan Aßmus	"Line out", "Speaker", "HP out", "CD", "SPDIF out", "Digital other out",
62ab496a4bSStephan Aßmus	"Modem line side", "Modem hand side", "Line in", "AUX", "Mic in",
63ab496a4bSStephan Aßmus	"Telephony", "SPDIF in", "Digital other in", "Reserved", "Other"
6489db0a5bSIthamar R. Adema};
6589db0a5bSIthamar R. Adema
6644f16897SAxel Dörflerstatic const char* kConnectionType[] = {
6744f16897SAxel Dörfler	"N/A", "1/8\"", "1/4\"", "ATAPI internal", "RCA", "Optical",
68ab496a4bSStephan Aßmus	"Other digital", "Other analog", "Multichannel analog (DIN)",
69ab496a4bSStephan Aßmus	"XLR/Professional", "RJ-11 (modem)", "Combination", "-", "-", "-", "Other"
7089db0a5bSIthamar R. Adema};
7189db0a5bSIthamar R. Adema
7244f16897SAxel Dörflerstatic const char* kJackColor[] = {
7344f16897SAxel Dörfler	"N/A", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow",
7444f16897SAxel Dörfler	"Purple", "Pink", "-", "-", "-", "-", "White", "Other"
7589db0a5bSIthamar R. Adema};
7689db0a5bSIthamar R. Adema
7741828f15SAxel Dörflerstatic const struct {
7841828f15SAxel Dörfler	uint32 subsystem_vendor_id, subsystem_id;
7941828f15SAxel Dörfler	uint32 codec_vendor_id, codec_id;
8041828f15SAxel Dörfler	uint32 quirks, nonquirks;
8141828f15SAxel Dörfler} kCodecQuirks[] = {
8241828f15SAxel Dörfler	{ HDA_ALL, HDA_ALL, HDA_ALL, HDA_ALL, HDA_QUIRK_IVREF, 0 },
8341828f15SAxel Dörfler	{ HDA_ALL, HDA_ALL, HDA_ALL, HDA_ALL, HDA_QUIRK_IVREF, 0 },
844d9dca19SJérôme Duval	{ 0x10de, 0x0d94, CIRRUSLOGIC_VENDORID, HDA_ALL,
854d9dca19SJérôme Duval		HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 },		// MacBookAir 3,1(2)
8641828f15SAxel Dörfler	{ 0x10de, 0xcb79, CIRRUSLOGIC_VENDORID, 0x4206,
874d9dca19SJérôme Duval		HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 },		// MacBook Pro 5,5
884d9dca19SJérôme Duval	{ 0x10de, 0xcb89, CIRRUSLOGIC_VENDORID, 0x4206,
894d9dca19SJérôme Duval		HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 },		// MacBookPro 7,1
9041828f15SAxel Dörfler	{ 0x8384, 0x7680, SIGMATEL_VENDORID, 0x7680,
9141828f15SAxel Dörfler		HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0},		// Apple Intel Mac
922501f22bSTim De Jong	{ 0x106b, 0x00a0, REALTEK_VENDORID, 0x0885,
932501f22bSTim De Jong		HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF80, 0},	// iMac 8,1, Macbook Pro 3,1
9441828f15SAxel Dörfler	{ 0x106b, 0x00a1, REALTEK_VENDORID, 0x0885,
9541828f15SAxel Dörfler		HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF50, 0},	// MacBook
9641828f15SAxel Dörfler	{ 0x106b, 0x00a3, REALTEK_VENDORID, 0x0885,
9741828f15SAxel Dörfler		HDA_QUIRK_GPIO0, 0},						// MacBook
9841828f15SAxel Dörfler	{ HDA_ALL, HDA_ALL, IDT_VENDORID, 0x76b2, HDA_QUIRK_GPIO0, 0},
9941828f15SAxel Dörfler};
10041828f15SAxel Dörfler
10144f16897SAxel Dörfler
10217959555SAxel Dörflerstatic const char*
10317959555SAxel Dörflerget_widget_type_name(hda_widget_type type)
10417959555SAxel Dörfler{
10517959555SAxel Dörfler	switch (type) {
10617959555SAxel Dörfler		case WT_AUDIO_OUTPUT:
107ab496a4bSStephan Aßmus			return "Audio output";
10817959555SAxel Dörfler		case WT_AUDIO_INPUT:
109ab496a4bSStephan Aßmus			return "Audio input";
11017959555SAxel Dörfler		case WT_AUDIO_MIXER:
111ab496a4bSStephan Aßmus			return "Audio mixer";
11217959555SAxel Dörfler		case WT_AUDIO_SELECTOR:
113ab496a4bSStephan Aßmus			return "Audio selector";
11417959555SAxel Dörfler		case WT_PIN_COMPLEX:
115ab496a4bSStephan Aßmus			return "Pin complex";
11617959555SAxel Dörfler		case WT_POWER:
11717959555SAxel Dörfler			return "Power";
11817959555SAxel Dörfler		case WT_VOLUME_KNOB:
119ab496a4bSStephan Aßmus			return "Volume knob";
12017959555SAxel Dörfler		case WT_BEEP_GENERATOR:
121ab496a4bSStephan Aßmus			return "Beep generator";
12217959555SAxel Dörfler		case WT_VENDOR_DEFINED:
123ab496a4bSStephan Aßmus			return "Vendor defined";
12417959555SAxel Dörfler		default:
12517959555SAxel Dörfler			return "Unknown";
12617959555SAxel Dörfler	}
12717959555SAxel Dörfler}
12817959555SAxel Dörfler
12917959555SAxel Dörfler
130e4e70475SJérôme Duvalconst char*
131e4e70475SJérôme Duvalget_widget_location(uint32 location)
132e4e70475SJérôme Duval{
133e4e70475SJérôme Duval	switch (location >> 4) {
134e4e70475SJérôme Duval		case 0:
135e4e70475SJérôme Duval		case 2:
136e4e70475SJérôme Duval			switch (location & 0xf) {
137e4e70475SJérôme Duval				case 2:
138e4e70475SJérôme Duval					return "Front";
139e4e70475SJérôme Duval				case 3:
140e4e70475SJérôme Duval					return "Left";
141e4e70475SJérôme Duval				case 4:
142e4e70475SJérôme Duval					return "Right";
143e4e70475SJérôme Duval				case 5:
144e4e70475SJérôme Duval					return "Top";
145e4e70475SJérôme Duval				case 6:
146e4e70475SJérôme Duval					return "Bottom";
147e4e70475SJérôme Duval				case 7:
148e4e70475SJérôme Duval					return "Rear panel";
149e4e70475SJérôme Duval				case 8:
150e4e70475SJérôme Duval					return "Drive bay";
151e43d37dbSJérôme Duval				case 0:
152e4e70475SJérôme Duval				case 1:
153e4e70475SJérôme Duval				default:
154e43d37dbSJérôme Duval					return NULL;
155e4e70475SJérôme Duval			}
156e4e70475SJérôme Duval		case 1:
157e4e70475SJérôme Duval			switch (location & 0xf) {
158e4e70475SJérôme Duval				case 7:
159e4e70475SJérôme Duval					return "Riser";
160e4e70475SJérôme Duval				case 8:
161e4e70475SJérôme Duval					return "HDMI";
162e4e70475SJérôme Duval				default:
163e4e70475SJérôme Duval					return NULL;
164e4e70475SJérôme Duval			}
165e4e70475SJérôme Duval		case 3:
166e4e70475SJérôme Duval			switch (location & 0xf) {
167e4e70475SJérôme Duval				case 6:
168e4e70475SJérôme Duval					return "Bottom";
169e4e70475SJérôme Duval				case 7:
170e4e70475SJérôme Duval					return "Inside lid";
171e4e70475SJérôme Duval				case 8:
172e4e70475SJérôme Duval					return "Outside lid";
173e4e70475SJérôme Duval				default:
174e4e70475SJérôme Duval					return NULL;
175e4e70475SJérôme Duval			}
176e4e70475SJérôme Duval	}
177e4e70475SJérôme Duval	return NULL;
178e4e70475SJérôme Duval}
179e4e70475SJérôme Duval
180e4e70475SJérôme Duval
18117959555SAxel Dörflerstatic void
18217959555SAxel Dörflerdump_widget_audio_capabilities(uint32 capabilities)
18317959555SAxel Dörfler{
1848c3da8ddSJérôme Duval	static const struct {
18517959555SAxel Dörfler		uint32		flag;
18617959555SAxel Dörfler		const char*	name;
18717959555SAxel Dörfler	} kFlags[] = {
188992c945aSJérôme Duval		{AUDIO_CAP_CP_CAPS, "CP caps"},
189ab496a4bSStephan Aßmus		{AUDIO_CAP_LEFT_RIGHT_SWAP, "L-R swap"},
19017959555SAxel Dörfler		{AUDIO_CAP_POWER_CONTROL, "Power"},
19117959555SAxel Dörfler		{AUDIO_CAP_DIGITAL, "Digital"},
192ab496a4bSStephan Aßmus		{AUDIO_CAP_CONNECTION_LIST, "Conn. list"},
193ab496a4bSStephan Aßmus		{AUDIO_CAP_UNSOLICITED_RESPONSES, "Unsol. responses"},
194ab496a4bSStephan Aßmus		{AUDIO_CAP_PROCESSING_CONTROLS, "Proc widget"},
19517959555SAxel Dörfler		{AUDIO_CAP_STRIPE, "Stripe"},
196ab496a4bSStephan Aßmus		{AUDIO_CAP_FORMAT_OVERRIDE, "Format override"},
197ab496a4bSStephan Aßmus		{AUDIO_CAP_AMPLIFIER_OVERRIDE, "Amplifier override"},
198ab496a4bSStephan Aßmus		{AUDIO_CAP_OUTPUT_AMPLIFIER, "Out amplifier"},
199ab496a4bSStephan Aßmus		{AUDIO_CAP_INPUT_AMPLIFIER, "In amplifier"},
2003a58749cSIngo Weinhold		{AUDIO_CAP_STEREO, "Stereo"},
20117959555SAxel Dörfler	};
20217959555SAxel Dörfler
20317959555SAxel Dörfler	char buffer[256];
20417959555SAxel Dörfler	int offset = 0;
20517959555SAxel Dörfler
20617959555SAxel Dörfler	for (uint32 j = 0; j < sizeof(kFlags) / sizeof(kFlags[0]); j++) {
20741828f15SAxel Dörfler		if ((capabilities & kFlags[j].flag) != 0)
20817959555SAxel Dörfler			offset += sprintf(buffer + offset, "[%s] ", kFlags[j].name);
20917959555SAxel Dörfler	}
21017959555SAxel Dörfler
21117959555SAxel Dörfler	if (offset != 0)
212144c4c62SJérôme Duval		TRACE("\t%s\n", buffer);
21317959555SAxel Dörfler}
21417959555SAxel Dörfler
21517959555SAxel Dörfler
21617959555SAxel Dörflerstatic void
21717959555SAxel Dörflerdump_widget_inputs(hda_widget& widget)
21817959555SAxel Dörfler{
21917959555SAxel Dörfler	// dump connections
22017959555SAxel Dörfler
22117959555SAxel Dörfler	char buffer[256];
22217959555SAxel Dörfler	int offset = 0;
22317959555SAxel Dörfler
22417959555SAxel Dörfler	for (uint32 i = 0; i < widget.num_inputs; i++) {
22517959555SAxel Dörfler		int32 input = widget.inputs[i];
22617959555SAxel Dörfler
22717959555SAxel Dörfler		if ((int32)i != widget.active_input)
2285caa9c86SMurai Takashi			offset += sprintf(buffer + offset, "%" B_PRId32 " ", input);
22917959555SAxel Dörfler		else
2305caa9c86SMurai Takashi			offset += sprintf(buffer + offset, "<%" B_PRId32 "> ", input);
23117959555SAxel Dörfler	}
23217959555SAxel Dörfler
23317959555SAxel Dörfler	if (offset != 0)
234144c4c62SJérôme Duval		TRACE("\tInputs: %s\n", buffer);
23517959555SAxel Dörfler}
23617959555SAxel Dörfler
23717959555SAxel Dörfler
23817959555SAxel Dörflerstatic void
23917959555SAxel Dörflerdump_widget_amplifier_capabilities(hda_widget& widget, bool input)
24017959555SAxel Dörfler{
24117959555SAxel Dörfler	uint32 capabilities;
24217959555SAxel Dörfler	if (input)
24317959555SAxel Dörfler		capabilities = widget.capabilities.input_amplifier;
24417959555SAxel Dörfler	else
24517959555SAxel Dörfler		capabilities = widget.capabilities.output_amplifier;
24617959555SAxel Dörfler
24717959555SAxel Dörfler	if (capabilities == 0)
24817959555SAxel Dörfler		return;
24917959555SAxel Dörfler
2505caa9c86SMurai Takashi	TRACE("\t%s Amp: %sstep size: %f dB, # steps: %" B_PRIu32 ", "
2515caa9c86SMurai Takashi		"offset to 0 dB: %" B_PRIu32 "\n",
2525caa9c86SMurai Takashi		input ? "In" : "Out",
25317959555SAxel Dörfler		(capabilities & AMP_CAP_MUTE) != 0 ? "supports mute, " : "",
25424283c48SJérôme Duval		AMP_CAP_STEP_SIZE(capabilities),
25524283c48SJérôme Duval		AMP_CAP_NUM_STEPS(capabilities),
25624283c48SJérôme Duval		AMP_CAP_OFFSET(capabilities));
25717959555SAxel Dörfler}
25817959555SAxel Dörfler
25917959555SAxel Dörfler
2601fb04337SJérôme Duvalstatic void
2611fb04337SJérôme Duvaldump_widget_pm_support(hda_widget& widget)
2621fb04337SJérôme Duval{
263cdd1c5c6SJérôme Duval	TRACE("\tSupported power states: %s%s%s%s%s%s%s%s\n",
2641fb04337SJérôme Duval		widget.pm & POWER_STATE_D0 ? "D0 " : "",
2651fb04337SJérôme Duval		widget.pm & POWER_STATE_D1 ? "D1 " : "",
2661fb04337SJérôme Duval		widget.pm & POWER_STATE_D2 ? "D2 " : "",
267cdd1c5c6SJérôme Duval		widget.pm & POWER_STATE_D3 ? "D3 " : "",
268cdd1c5c6SJérôme Duval		widget.pm & POWER_STATE_D3COLD ? "D3COLD " : "",
269cdd1c5c6SJérôme Duval		widget.pm & POWER_STATE_S3D3COLD ? "S3D3COLD " : "",
270cdd1c5c6SJérôme Duval		widget.pm & POWER_STATE_CLKSTOP ? "CLKSTOP " : "",
271cdd1c5c6SJérôme Duval		widget.pm & POWER_STATE_EPSS ? "EPSS " : "");
2721fb04337SJérôme Duval}
2731fb04337SJérôme Duval
2741fb04337SJérôme Duval
2754489296bSJérôme Duvalstatic void
2764489296bSJérôme Duvaldump_widget_stream_support(hda_widget& widget)
2774489296bSJérôme Duval{
278144c4c62SJérôme Duval	TRACE("\tSupported formats: %s%s%s%s%s%s%s%s%s\n",
2794489296bSJérôme Duval		widget.d.io.formats & B_FMT_8BIT_S ? "8bits " : "",
2804489296bSJérôme Duval		widget.d.io.formats & B_FMT_16BIT ? "16bits " : "",
2814489296bSJérôme Duval		widget.d.io.formats & B_FMT_20BIT ? "20bits " : "",
2824489296bSJérôme Duval		widget.d.io.formats & B_FMT_24BIT ? "24bits " : "",
283bd7b0937SJérôme Duval		widget.d.io.formats & B_FMT_32BIT ? "32bits " : "",
284bd7b0937SJérôme Duval		widget.d.io.formats & B_FMT_FLOAT ? "float " : "",
285bd7b0937SJérôme Duval		widget.d.io.formats & B_FMT_DOUBLE ? "double " : "",
286ba4d7ed2SJérôme Duval		widget.d.io.formats & B_FMT_EXTENDED ? "extended " : "",
287ba4d7ed2SJérôme Duval		widget.d.io.formats & B_FMT_BITSTREAM ? "bitstream " : "");
288144c4c62SJérôme Duval	TRACE("\tSupported rates: %s%s%s%s%s%s%s%s%s%s%s%s\n",
2894489296bSJérôme Duval		widget.d.io.rates & B_SR_8000 ? "8khz " : "",
2904489296bSJérôme Duval		widget.d.io.rates & B_SR_11025 ? "11khz " : "",
2914489296bSJérôme Duval		widget.d.io.rates & B_SR_16000 ? "16khz " : "",
2924489296bSJérôme Duval		widget.d.io.rates & B_SR_22050 ? "22khz " : "",
2934489296bSJérôme Duval		widget.d.io.rates & B_SR_32000 ? "32khz " : "",
2944489296bSJérôme Duval		widget.d.io.rates & B_SR_44100 ? "44khz " : "",
2954489296bSJérôme Duval		widget.d.io.rates & B_SR_48000 ? "48khz " : "",
2964489296bSJérôme Duval		widget.d.io.rates & B_SR_88200 ? "88khz " : "",
2974489296bSJérôme Duval		widget.d.io.rates & B_SR_96000 ? "96khz " : "",
2984489296bSJérôme Duval		widget.d.io.rates & B_SR_176400 ? "176khz " : "",
2994489296bSJérôme Duval		widget.d.io.rates & B_SR_192000 ? "192khz " : "",
3004489296bSJérôme Duval		widget.d.io.rates & B_SR_384000 ? "384khz " : "");
3013a58749cSIngo Weinhold
3024489296bSJérôme Duval}
3034489296bSJérôme Duval
3044489296bSJérôme Duval
305144c4c62SJérôme Duvalstatic void
306144c4c62SJérôme Duvaldump_pin_complex_capabilities(hda_widget& widget)
307144c4c62SJérôme Duval{
3083a58749cSIngo Weinhold	TRACE("\t%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
309144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_IMP_SENSE ? "[Imp Sense] " : "",
310144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_TRIGGER_REQ ? "[Trigger Req]" : "",
311144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_PRES_DETECT ? "[Pres Detect]" : "",
312144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_HP_DRIVE ? "[HP Drive]" : "",
313144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_OUTPUT ? "[Output]" : "",
314144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_INPUT ? "[Input]" : "",
315144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_BALANCE ? "[Balance]" : "",
316144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_HIZ ? "[VRef HIZ]" : "",
317144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_50 ? "[VRef 50]" : "",
318144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_GROUND ? "[VRef Gr]" : "",
319144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_80 ? "[VRef 80]" : "",
320144c4c62SJérôme Duval		widget.d.pin.capabilities & PIN_CAP_VREF_CTRL_100 ? "[VRef 100]" : "",
3213a58749cSIngo Weinhold		widget.d.pin.capabilities & PIN_CAP_EAPD_CAP ? "[EAPD]" : "");
322144c4c62SJérôme Duval}
323144c4c62SJérôme Duval
324144c4c62SJérôme Duval
3251fb04337SJérôme Duvalstatic void
3261fb04337SJérôme Duvaldump_audiogroup_widgets(hda_audio_group* audioGroup)
3271fb04337SJérôme Duval{
328144c4c62SJérôme Duval	TRACE("\tAudiogroup:\n");
32941828f15SAxel Dörfler	// Iterate over all widgets and collect info
3301fb04337SJérôme Duval	for (uint32 i = 0; i < audioGroup->widget_count; i++) {
3311fb04337SJérôme Duval		hda_widget& widget = audioGroup->widgets[i];
3321fb04337SJérôme Duval		uint32 nodeID = audioGroup->widget_start + i;
3331fb04337SJérôme Duval
3345caa9c86SMurai Takashi		TRACE("%" B_PRIu32 ": %s\n", nodeID, get_widget_type_name(widget.type));
3351fb04337SJérôme Duval
3361fb04337SJérôme Duval		switch (widget.type) {
3371fb04337SJérôme Duval			case WT_AUDIO_OUTPUT:
3381fb04337SJérôme Duval			case WT_AUDIO_INPUT:
3391fb04337SJérôme Duval				break;
3401fb04337SJérôme Duval
3411fb04337SJérôme Duval			case WT_PIN_COMPLEX:
342144c4c62SJérôme Duval				dump_pin_complex_capabilities(widget);
3431fb04337SJérôme Duval				break;
3441fb04337SJérôme Duval
3451fb04337SJérôme Duval			default:
3461fb04337SJérôme Duval				break;
3471fb04337SJérôme Duval		}
3481fb04337SJérôme Duval
3491fb04337SJérôme Duval		dump_widget_pm_support(widget);
3501fb04337SJérôme Duval		dump_widget_audio_capabilities(widget.capabilities.audio);
3511fb04337SJérôme Duval		dump_widget_amplifier_capabilities(widget, true);
3521fb04337SJérôme Duval		dump_widget_amplifier_capabilities(widget, false);
3531fb04337SJérôme Duval		dump_widget_inputs(widget);
3541fb04337SJérôme Duval	}
3551fb04337SJérôme Duval}
3561fb04337SJérôme Duval
3571fb04337SJérôme Duval
35817959555SAxel Dörfler//	#pragma mark -
35917959555SAxel Dörfler
36017959555SAxel Dörfler
36141828f15SAxel Dörflerstatic void
36241828f15SAxel Dörflerhda_codec_get_quirks(hda_codec* codec)
36341828f15SAxel Dörfler{
36441828f15SAxel Dörfler	codec->quirks = 0;
36541828f15SAxel Dörfler
36641828f15SAxel Dörfler	uint32 subSystemID = codec->controller->pci_info.u.h0.subsystem_id;
36741828f15SAxel Dörfler	uint32 subSystemVendorID
36841828f15SAxel Dörfler		= codec->controller->pci_info.u.h0.subsystem_vendor_id;
36941828f15SAxel Dörfler
37041828f15SAxel Dörfler	for (uint32 i = 0;
37141828f15SAxel Dörfler			i < (sizeof(kCodecQuirks) / sizeof(kCodecQuirks[0])); i++) {
37241828f15SAxel Dörfler		if (kCodecQuirks[i].subsystem_id != HDA_ALL
37341828f15SAxel Dörfler			&& kCodecQuirks[i].subsystem_id != subSystemID)
37441828f15SAxel Dörfler			continue;
37541828f15SAxel Dörfler		if (kCodecQuirks[i].subsystem_vendor_id != HDA_ALL
37641828f15SAxel Dörfler			&& kCodecQuirks[i].subsystem_vendor_id != subSystemVendorID)
37741828f15SAxel Dörfler			continue;
37841828f15SAxel Dörfler		if (kCodecQuirks[i].codec_vendor_id != HDA_ALL
37941828f15SAxel Dörfler			&& kCodecQuirks[i].codec_vendor_id != codec->vendor_id)
38041828f15SAxel Dörfler			continue;
38141828f15SAxel Dörfler		if (kCodecQuirks[i].codec_id != HDA_ALL
38241828f15SAxel Dörfler			&& kCodecQuirks[i].codec_id != codec->product_id)
38341828f15SAxel Dörfler			continue;
38441828f15SAxel Dörfler
38541828f15SAxel Dörfler		codec->quirks |= kCodecQuirks[i].quirks;
38641828f15SAxel Dörfler		codec->quirks &= ~kCodecQuirks[i].nonquirks;
38741828f15SAxel Dörfler	}
38841828f15SAxel Dörfler}
38941828f15SAxel Dörfler
39041828f15SAxel Dörfler
39189db0a5bSIthamar R. Ademastatic status_t
39217959555SAxel Dörflerhda_get_pm_support(hda_codec* codec, uint32 nodeID, uint32* pm)
39389db0a5bSIthamar R. Adema{
394438500b0SAxel Dörfler	corb_t verb = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAMETER,
39544f16897SAxel Dörfler		PID_POWERSTATE_SUPPORT);
39689db0a5bSIthamar R. Adema
39741828f15SAxel Dörfler	uint32 response;
39841828f15SAxel Dörfler	status_t status = hda_send_verbs(codec, &verb, &response, 1);
39941828f15SAxel Dörfler	if (status == B_OK)
40017959555SAxel Dörfler		*pm = response & 0xf;
40144f16897SAxel Dörfler
40241828f15SAxel Dörfler	return status;
40389db0a5bSIthamar R. Adema}
40489db0a5bSIthamar R. Adema
40544f16897SAxel Dörfler
40689db0a5bSIthamar R. Ademastatic status_t
40717959555SAxel Dörflerhda_get_stream_support(hda_codec* codec, uint32 nodeID, uint32* formats,
40844f16897SAxel Dörfler	uint32* rates)
40989db0a5bSIthamar R. Adema{
41089db0a5bSIthamar R. Adema	corb_t verbs[2];
41189db0a5bSIthamar R. Adema	uint32 resp[2];
4124919a4dcSAxel Dörfler	status_t status;
4134919a4dcSAxel Dörfler
414438500b0SAxel Dörfler	verbs[0] = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAMETER,
415438500b0SAxel Dörfler		PID_STREAM_SUPPORT);
416438500b0SAxel Dörfler	verbs[1] = MAKE_VERB(codec->addr, nodeID, VID_GET_PARAMETER,
417438500b0SAxel Dörfler		PID_PCM_SUPPORT);
4184919a4dcSAxel Dörfler
4194919a4dcSAxel Dörfler	status = hda_send_verbs(codec, verbs, resp, 2);
4204919a4dcSAxel Dörfler	if (status != B_OK)
4214919a4dcSAxel Dörfler		return status;
4224919a4dcSAxel Dörfler
4234919a4dcSAxel Dörfler	*formats = 0;
4244919a4dcSAxel Dörfler	*rates = 0;
4254919a4dcSAxel Dörfler
4264919a4dcSAxel Dörfler	if ((resp[0] & (STREAM_FLOAT | STREAM_PCM)) != 0) {
4274919a4dcSAxel Dörfler		if (resp[1] & (1 << 0))
4284919a4dcSAxel Dörfler			*rates |= B_SR_8000;
4294919a4dcSAxel Dörfler		if (resp[1] & (1 << 1))
4304919a4dcSAxel Dörfler			*rates |= B_SR_11025;
4314919a4dcSAxel Dörfler		if (resp[1] & (1 << 2))
4324919a4dcSAxel Dörfler			*rates |= B_SR_16000;
4334919a4dcSAxel Dörfler		if (resp[1] & (1 << 3))
4344919a4dcSAxel Dörfler			*rates |= B_SR_22050;
4354919a4dcSAxel Dörfler		if (resp[1] & (1 << 4))
4364919a4dcSAxel Dörfler			*rates |= B_SR_32000;
4374919a4dcSAxel Dörfler		if (resp[1] & (1 << 5))
4384919a4dcSAxel Dörfler			*rates |= B_SR_44100;
4394919a4dcSAxel Dörfler		if (resp[1] & (1 << 6))
4404919a4dcSAxel Dörfler			*rates |= B_SR_48000;
4414919a4dcSAxel Dörfler		if (resp[1] & (1 << 7))
4424919a4dcSAxel Dörfler			*rates |= B_SR_88200;
4434919a4dcSAxel Dörfler		if (resp[1] & (1 << 8))
4444919a4dcSAxel Dörfler			*rates |= B_SR_96000;
4454919a4dcSAxel Dörfler		if (resp[1] & (1 << 9))
4464919a4dcSAxel Dörfler			*rates |= B_SR_176400;
4474919a4dcSAxel Dörfler		if (resp[1] & (1 << 10))
4484919a4dcSAxel Dörfler			*rates |= B_SR_192000;
4494919a4dcSAxel Dörfler		if (resp[1] & (1 << 11))
4504919a4dcSAxel Dörfler			*rates |= B_SR_384000;
4514919a4dcSAxel Dörfler
4524919a4dcSAxel Dörfler		if (resp[1] & PCM_8_BIT)
4534919a4dcSAxel Dörfler			*formats |= B_FMT_8BIT_S;
4544919a4dcSAxel Dörfler		if (resp[1] & PCM_16_BIT)
4554919a4dcSAxel Dörfler			*formats |= B_FMT_16BIT;
4564919a4dcSAxel Dörfler		if (resp[1] & PCM_20_BIT)
4574919a4dcSAxel Dörfler			*formats |= B_FMT_20BIT;
4584919a4dcSAxel Dörfler		if (resp[1] & PCM_24_BIT)
4594919a4dcSAxel Dörfler			*formats |= B_FMT_24BIT;
4604919a4dcSAxel Dörfler		if (resp[1] & PCM_32_BIT)
4614919a4dcSAxel Dörfler			*formats |= B_FMT_32BIT;
46289db0a5bSIthamar R. Adema	}
4634919a4dcSAxel Dörfler	if ((resp[0] & STREAM_FLOAT) != 0)
4644919a4dcSAxel Dörfler		*formats |= B_FMT_FLOAT;
4658c3da8ddSJérôme Duval	if ((resp[0] & STREAM_AC3) != 0) {
4668c3da8ddSJérôme Duval		*formats |= B_FMT_BITSTREAM;
4678c3da8ddSJérôme Duval	}
4684919a4dcSAxel Dörfler
4694919a4dcSAxel Dörfler	return B_OK;
47089db0a5bSIthamar R. Adema}
47189db0a5bSIthamar R. Adema
47244f16897SAxel Dörfler
47317959555SAxel Dörfler//	#pragma mark - widget functions
47417959555SAxel Dörfler
47517959555SAxel Dörfler
47689db0a5bSIthamar R. Ademastatic status_t
47717959555SAxel Dörflerhda_widget_get_pm_support(hda_audio_group* audioGroup, hda_widget* widget)
47889db0a5bSIthamar R. Adema{
47941828f15SAxel Dörfler	return hda_get_pm_support(audioGroup->codec, widget->node_id, &widget->pm);
48017959555SAxel Dörfler}
48117959555SAxel Dörfler
48217959555SAxel Dörfler
48317959555SAxel Dörflerstatic status_t
48417959555SAxel Dörflerhda_widget_get_stream_support(hda_audio_group* audioGroup, hda_widget* widget)
48517959555SAxel Dörfler{
48634bda3aeSJérôme Duval	if (audioGroup->widget.node_id != widget->node_id
48734bda3aeSJérôme Duval		&& (widget->capabilities.audio & AUDIO_CAP_FORMAT_OVERRIDE) == 0) {
48817959555SAxel Dörfler		// adopt capabilities of the audio group
48934bda3aeSJérôme Duval		widget->d.io.formats = audioGroup->widget.d.io.formats;
49034bda3aeSJérôme Duval		widget->d.io.rates = audioGroup->widget.d.io.rates;
49117959555SAxel Dörfler		return B_OK;
49217959555SAxel Dörfler	}
49317959555SAxel Dörfler
49417959555SAxel Dörfler	return hda_get_stream_support(audioGroup->codec, widget->node_id,
49530f55bc9SAxel Dörfler		&widget->d.io.formats, &widget->d.io.rates);
49617959555SAxel Dörfler}
49717959555SAxel Dörfler
49817959555SAxel Dörfler
49917959555SAxel Dörflerstatic status_t
50017959555SAxel Dörflerhda_widget_get_amplifier_capabilities(hda_audio_group* audioGroup,
50117959555SAxel Dörfler	hda_widget* widget)
50217959555SAxel Dörfler{
50317959555SAxel Dörfler	uint32 response;
50489db0a5bSIthamar R. Adema	corb_t verb;
50517959555SAxel Dörfler
50634bda3aeSJérôme Duval	if ((widget->capabilities.audio & AUDIO_CAP_OUTPUT_AMPLIFIER) != 0
50734bda3aeSJérôme Duval		|| audioGroup->widget.node_id == widget->node_id) {
50834bda3aeSJérôme Duval		if ((widget->capabilities.audio & AUDIO_CAP_AMPLIFIER_OVERRIDE) != 0
50934bda3aeSJérôme Duval			|| audioGroup->widget.node_id == widget->node_id) {
51017959555SAxel Dörfler			verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id,
51117959555SAxel Dörfler				VID_GET_PARAMETER, PID_OUTPUT_AMPLIFIER_CAP);
51217959555SAxel Dörfler			status_t status = hda_send_verbs(audioGroup->codec, &verb,
51317959555SAxel Dörfler				&response, 1);
51441828f15SAxel Dörfler			if (status != B_OK)
51517959555SAxel Dörfler				return status;
51617959555SAxel Dörfler
51717959555SAxel Dörfler			widget->capabilities.output_amplifier = response;
51817959555SAxel Dörfler		} else {
51917959555SAxel Dörfler			// adopt capabilities from the audio function group
52017959555SAxel Dörfler			widget->capabilities.output_amplifier
52134bda3aeSJérôme Duval				= audioGroup->widget.capabilities.output_amplifier;
52217959555SAxel Dörfler		}
52389db0a5bSIthamar R. Adema	}
52444f16897SAxel Dörfler
52534bda3aeSJérôme Duval	if ((widget->capabilities.audio & AUDIO_CAP_INPUT_AMPLIFIER) != 0
52634bda3aeSJérôme Duval		|| audioGroup->widget.node_id == widget->node_id) {
52734bda3aeSJérôme Duval		if ((widget->capabilities.audio & AUDIO_CAP_AMPLIFIER_OVERRIDE
52834bda3aeSJérôme Duval			|| audioGroup->widget.node_id == widget->node_id) != 0) {
52917959555SAxel Dörfler			verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id,
53017959555SAxel Dörfler				VID_GET_PARAMETER, PID_INPUT_AMPLIFIER_CAP);
53117959555SAxel Dörfler			status_t status = hda_send_verbs(audioGroup->codec, &verb,
53217959555SAxel Dörfler				&response, 1);
53341828f15SAxel Dörfler			if (status != B_OK)
53417959555SAxel Dörfler				return status;
53517959555SAxel Dörfler
53617959555SAxel Dörfler			widget->capabilities.input_amplifier = response;
53717959555SAxel Dörfler		} else {
53817959555SAxel Dörfler			// adopt capabilities from the audio function group
53917959555SAxel Dörfler			widget->capabilities.input_amplifier
54034bda3aeSJérôme Duval				= audioGroup->widget.capabilities.input_amplifier;
54117959555SAxel Dörfler		}
54217959555SAxel Dörfler	}
54317959555SAxel Dörfler
54417959555SAxel Dörfler	return B_OK;
54589db0a5bSIthamar R. Adema}
54689db0a5bSIthamar R. Adema
54744f16897SAxel Dörfler
5488c3da8ddSJérôme Duvalhda_widget*
54930f55bc9SAxel Dörflerhda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID)
55030f55bc9SAxel Dörfler{
55130f55bc9SAxel Dörfler	if (audioGroup->widget_start > nodeID
55230f55bc9SAxel Dörfler		|| audioGroup->widget_start + audioGroup->widget_count < nodeID)
55330f55bc9SAxel Dörfler		return NULL;
55430f55bc9SAxel Dörfler
55530f55bc9SAxel Dörfler	return &audioGroup->widgets[nodeID - audioGroup->widget_start];
55630f55bc9SAxel Dörfler}
55730f55bc9SAxel Dörfler
55830f55bc9SAxel Dörfler
559313492b6SAxel Dörflerstatic status_t
56017959555SAxel Dörflerhda_widget_get_connections(hda_audio_group* audioGroup, hda_widget* widget)
561313492b6SAxel Dörfler{
562144c4c62SJérôme Duval	corb_t verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id,
56317959555SAxel Dörfler		VID_GET_PARAMETER, PID_CONNECTION_LIST_LENGTH);
564313492b6SAxel Dörfler	uint32 response;
565313492b6SAxel Dörfler
56617959555SAxel Dörfler	if (hda_send_verbs(audioGroup->codec, &verb, &response, 1) != B_OK)
567313492b6SAxel Dörfler		return B_ERROR;
568313492b6SAxel Dörfler
569313492b6SAxel Dörfler	uint32 listEntries = response & 0x7f;
570313492b6SAxel Dörfler	bool longForm = (response & 0xf0) != 0;
571313492b6SAxel Dörfler
572313492b6SAxel Dörfler	if (listEntries == 0)
573313492b6SAxel Dörfler		return B_OK;
574313492b6SAxel Dörfler
575313492b6SAxel Dörfler#if 1
576313492b6SAxel Dörfler	if (widget->num_inputs > 1) {
577313492b6SAxel Dörfler		// Get currently active connection
57817959555SAxel Dörfler		verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id,
57917959555SAxel Dörfler			VID_GET_CONNECTION_SELECT, 0);
58017959555SAxel Dörfler		if (hda_send_verbs(audioGroup->codec, &verb, &response, 1) == B_OK)
581313492b6SAxel Dörfler			widget->active_input = response & 0xff;
582313492b6SAxel Dörfler	}
583313492b6SAxel Dörfler#endif
584313492b6SAxel Dörfler
585313492b6SAxel Dörfler	uint32 valuesPerEntry = longForm ? 2 : 4;
586313492b6SAxel Dörfler	uint32 shift = 32 / valuesPerEntry;
587313492b6SAxel Dörfler	uint32 rangeMask = (1 << (shift - 1));
588313492b6SAxel Dörfler	int32 previousInput = -1;
589313492b6SAxel Dörfler	uint32 numInputs = 0;
590313492b6SAxel Dörfler
591313492b6SAxel Dörfler	for (uint32 i = 0; i < listEntries; i++) {
592313492b6SAxel Dörfler		if ((i % valuesPerEntry) == 0) {
593313492b6SAxel Dörfler			// We get 2 or 4 answers per call depending on if we're
594313492b6SAxel Dörfler			// in short or long list mode
59517959555SAxel Dörfler			verb = MAKE_VERB(audioGroup->codec->addr, widget->node_id,
596313492b6SAxel Dörfler				VID_GET_CONNECTION_LIST_ENTRY, i);
59717959555SAxel Dörfler			if (hda_send_verbs(audioGroup->codec, &verb, &response, 1)
59817959555SAxel Dörfler					!= B_OK) {
5995caa9c86SMurai Takashi				ERROR("Error parsing inputs for widget %" B_PRIu32 "!\n",
60017959555SAxel Dörfler					widget->node_id);
601313492b6SAxel Dörfler				break;
602313492b6SAxel Dörfler			}
603313492b6SAxel Dörfler		}
604313492b6SAxel Dörfler
605313492b6SAxel Dörfler		int32 input = (response >> (shift * (i % valuesPerEntry)))
606313492b6SAxel Dörfler			& ((1 << shift) - 1);
607313492b6SAxel Dörfler
608313492b6SAxel Dörfler		if (input & rangeMask) {
609313492b6SAxel Dörfler			// found range
610313492b6SAxel Dörfler			input &= ~rangeMask;
611313492b6SAxel Dörfler
612313492b6SAxel Dörfler			if (input < previousInput || previousInput == -1) {
6135caa9c86SMurai Takashi				ERROR("invalid range from %" B_PRId32 " to %" B_PRId32 "\n",
6145caa9c86SMurai Takashi					previousInput, input);
615313492b6SAxel Dörfler				continue;
616313492b6SAxel Dörfler			}
617313492b6SAxel Dörfler
618313492b6SAxel Dörfler			for (int32 rangeInput = previousInput + 1; rangeInput <= input
619313492b6SAxel Dörfler					&& numInputs < MAX_INPUTS; rangeInput++) {
620313492b6SAxel Dörfler				widget->inputs[numInputs++] = rangeInput;
621313492b6SAxel Dörfler			}
622313492b6SAxel Dörfler
623313492b6SAxel Dörfler			previousInput = -1;
624313492b6SAxel Dörfler		} else if (numInputs < MAX_INPUTS) {
625313492b6SAxel Dörfler			// standard value
626313492b6SAxel Dörfler			widget->inputs[numInputs++] = input;
627313492b6SAxel Dörfler			previousInput = input;
628313492b6SAxel Dörfler		}
629313492b6SAxel Dörfler	}
630313492b6SAxel Dörfler
631313492b6SAxel Dörfler	widget->num_inputs = numInputs;
63217959555SAxel Dörfler
63317959555SAxel Dörfler	if (widget->num_inputs == 1)
63430f55bc9SAxel Dörfler		widget->active_input = 0;
63517959555SAxel Dörfler
636313492b6SAxel Dörfler	return B_OK;
637313492b6SAxel Dörfler}
638313492b6SAxel Dörfler
639313492b6SAxel Dörfler
6408c3da8ddSJérôme Duvalstatic status_t
6418c3da8ddSJérôme Duvalhda_widget_get_associations(hda_audio_group* audioGroup)
6428c3da8ddSJérôme Duval{
6438c3da8ddSJérôme Duval	uint32 index = 0;
6448c3da8ddSJérôme Duval	for (uint32 i = 0; i < MAX_ASSOCIATIONS; i++) {
6458c3da8ddSJérôme Duval		for (uint32 j = 0; j < audioGroup->widget_count; j++) {
64682530463SJessica Hamilton			if (index >= MAX_ASSOCIATIONS) {
64782530463SJessica Hamilton				TRACE("too many associations, bailing!\n");
64882530463SJessica Hamilton				return B_ERROR;
64982530463SJessica Hamilton			}
6508c3da8ddSJérôme Duval			hda_widget& widget = audioGroup->widgets[j];
6518c3da8ddSJérôme Duval
6528c3da8ddSJérôme Duval			if (widget.type != WT_PIN_COMPLEX)
6538c3da8ddSJérôme Duval				continue;
6548c3da8ddSJérôme Duval			if (CONF_DEFAULT_ASSOCIATION(widget.d.pin.config) != i)
6558c3da8ddSJérôme Duval				continue;
6568c3da8ddSJérôme Duval			if (audioGroup->associations[index].pin_count == 0) {
6578c3da8ddSJérôme Duval				audioGroup->associations[index].index = index;
6588c3da8ddSJérôme Duval				audioGroup->associations[index].enabled = true;
6598c3da8ddSJérôme Duval			}
6608c3da8ddSJérôme Duval			uint32 sequence = CONF_DEFAULT_SEQUENCE(widget.d.pin.config);
6618c3da8ddSJérôme Duval			if (audioGroup->associations[index].pins[sequence] != 0) {
6628c3da8ddSJérôme Duval				audioGroup->associations[index].enabled = false;
6638c3da8ddSJérôme Duval			}
6648c3da8ddSJérôme Duval			audioGroup->associations[index].pins[sequence] = widget.node_id;
6658c3da8ddSJérôme Duval			audioGroup->associations[index].pin_count++;
6668c3da8ddSJérôme Duval			if (i == 15)
6678c3da8ddSJérôme Duval				index++;
6688c3da8ddSJérôme Duval		}
6698c3da8ddSJérôme Duval		if (i != 15 && audioGroup->associations[index].pin_count != 0)
6708c3da8ddSJérôme Duval			index++;
6718c3da8ddSJérôme Duval	}
6728c3da8ddSJérôme Duval	audioGroup->association_count = index;
6738c3da8ddSJérôme Duval
6748c3da8ddSJérôme Duval	return B_OK;
6758c3da8ddSJérôme Duval}
6768c3da8ddSJérôme Duval
6778c3da8ddSJérôme Duval
678144c4c62SJérôme Duvalstatic uint32
67941828f15SAxel Dörflerhda_widget_prepare_pin_ctrl(hda_audio_group* audioGroup, hda_widget* widget,
680144c4c62SJérôme Duval	bool isOutput)
681144c4c62SJérôme Duval{
682144c4c62SJérôme Duval	uint32 ctrl = 0;
683144c4c62SJérôme Duval	if (isOutput)
684144c4c62SJérôme Duval		ctrl = PIN_ENABLE_HEAD_PHONE | PIN_ENABLE_OUTPUT;
685144c4c62SJérôme Duval	else
686144c4c62SJérôme Duval		ctrl = PIN_ENABLE_INPUT;
6873a58749cSIngo Weinhold
688144c4c62SJérôme Duval	if (PIN_CAP_IS_VREF_CTRL_50_CAP(widget->d.pin.capabilities)
689144c4c62SJérôme Duval		&& (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF50
690144c4c62SJérôme Duval			: HDA_QUIRK_IVREF50))) {
691144c4c62SJérôme Duval		ctrl |= PIN_ENABLE_VREF_50;
692144c4c62SJérôme Duval		TRACE("%s vref 50 enabled\n", isOutput ? "output" : "input");
693144c4c62SJérôme Duval	}
694144c4c62SJérôme Duval	if (PIN_CAP_IS_VREF_CTRL_80_CAP(widget->d.pin.capabilities)
695144c4c62SJérôme Duval		&& (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF80
696144c4c62SJérôme Duval			: HDA_QUIRK_IVREF80))) {
697144c4c62SJérôme Duval		ctrl |= PIN_ENABLE_VREF_80;
698144c4c62SJérôme Duval		TRACE("%s vref 80 enabled\n", isOutput ? "output" : "input");
699144c4c62SJérôme Duval	}
700144c4c62SJérôme Duval	if (PIN_CAP_IS_VREF_CTRL_100_CAP(widget->d.pin.capabilities)
701144c4c62SJérôme Duval		&& (audioGroup->codec->quirks & (isOutput ? HDA_QUIRK_OVREF100
702144c4c62SJérôme Duval			: HDA_QUIRK_IVREF100))) {
703144c4c62SJérôme Duval		ctrl |= PIN_ENABLE_VREF_100;
704144c4c62SJérôme Duval		TRACE("%s vref 100 enabled\n", isOutput ? "output" : "input");
705144c4c62SJérôme Duval	}
7063a58749cSIngo Weinhold
707144c4c62SJérôme Duval	return ctrl;
708144c4c62SJérôme Duval}
709144c4c62SJérôme Duval
710144c4c62SJérôme Duval
71117959555SAxel Dörfler//	#pragma mark - audio group functions
7128fa3f81aSAxel Dörfler
7138fa3f81aSAxel Dörfler
71489db0a5bSIthamar R. Ademastatic status_t
7154919a4dcSAxel Dörflerhda_codec_parse_audio_group(hda_audio_group* audioGroup)
71689db0a5bSIthamar R. Adema{
7178fa3f81aSAxel Dörfler	corb_t verbs[3];
7188fa3f81aSAxel Dörfler	uint32 resp[3];
71989db0a5bSIthamar R. Adema
7209a0a4979SJérôme Duval	hda_codec* codec = audioGroup->codec;
7219a0a4979SJérôme Duval	uint32 codec_id = (codec->vendor_id << 16) | codec->product_id;
72234bda3aeSJérôme Duval	hda_widget_get_stream_support(audioGroup, &audioGroup->widget);
72334bda3aeSJérôme Duval	hda_widget_get_pm_support(audioGroup, &audioGroup->widget);
72434bda3aeSJérôme Duval	hda_widget_get_amplifier_capabilities(audioGroup, &audioGroup->widget);
7253a58749cSIngo Weinhold
72634bda3aeSJérôme Duval	verbs[0] = MAKE_VERB(audioGroup->codec->addr, audioGroup->widget.node_id,
727438500b0SAxel Dörfler		VID_GET_PARAMETER, PID_AUDIO_GROUP_CAP);
72834bda3aeSJérôme Duval	verbs[1] = MAKE_VERB(audioGroup->codec->addr, audioGroup->widget.node_id,
729438500b0SAxel Dörfler		VID_GET_PARAMETER, PID_GPIO_COUNT);
73034bda3aeSJérôme Duval	verbs[2] = MAKE_VERB(audioGroup->codec->addr, audioGroup->widget.node_id,
73130f55bc9SAxel Dörfler		VID_GET_PARAMETER, PID_SUB_NODE_COUNT);
7324919a4dcSAxel Dörfler
7334919a4dcSAxel Dörfler	if (hda_send_verbs(audioGroup->codec, verbs, resp, 3) != B_OK)
7344919a4dcSAxel Dörfler		return B_ERROR;
7354919a4dcSAxel Dörfler
7365caa9c86SMurai Takashi	TRACE("Audio Group: Output delay: %" B_PRIu32 " "
7375caa9c86SMurai Takashi		"samples, Input delay: %" B_PRIu32 " "
73834bda3aeSJérôme Duval		"samples, Beep Generator: %s\n", AUDIO_GROUP_CAP_OUTPUT_DELAY(resp[0]),
7393a58749cSIngo Weinhold		AUDIO_GROUP_CAP_INPUT_DELAY(resp[0]),
74034bda3aeSJérôme Duval		AUDIO_GROUP_CAP_BEEPGEN(resp[0]) ? "yes" : "no");
7414919a4dcSAxel Dörfler
7425caa9c86SMurai Takashi	TRACE("  #GPIO: %" B_PRIu32 ", #GPO: %" B_PRIu32 ", #GPI: %" B_PRIu32 ", "
7435caa9c86SMurai Takashi		"unsol: %s, wake: %s\n",
74434bda3aeSJérôme Duval		GPIO_COUNT_NUM_GPIO(resp[1]), GPIO_COUNT_NUM_GPO(resp[1]),
74534bda3aeSJérôme Duval		GPIO_COUNT_NUM_GPI(resp[1]), GPIO_COUNT_GPIUNSOL(resp[1]) ? "yes" : "no",
74634bda3aeSJérôme Duval		GPIO_COUNT_GPIWAKE(resp[1]) ? "yes" : "no");
7474489296bSJérôme Duval	dump_widget_stream_support(audioGroup->widget);
7484919a4dcSAxel Dörfler
7494aafe92aSJérôme Duval	audioGroup->gpio = resp[1];
75034bda3aeSJérôme Duval	audioGroup->widget_start = SUB_NODE_COUNT_START(resp[2]);
75134bda3aeSJérôme Duval	audioGroup->widget_count = SUB_NODE_COUNT_TOTAL(resp[2]);
7528fa3f81aSAxel Dörfler
7535caa9c86SMurai Takashi	TRACE("  widget start %" B_PRIu32 ", count %" B_PRIu32 "\n",
7545caa9c86SMurai Takashi		audioGroup->widget_start, audioGroup->widget_count);
7554919a4dcSAxel Dörfler
756340dd4feSAxel Dörfler	audioGroup->widgets = (hda_widget*)calloc(audioGroup->widget_count,
7574919a4dcSAxel Dörfler		sizeof(*audioGroup->widgets));
7584919a4dcSAxel Dörfler	if (audioGroup->widgets == NULL) {
759144c4c62SJérôme Duval		ERROR("ERROR: Not enough memory!\n");
7604919a4dcSAxel Dörfler		return B_NO_MEMORY;
7614919a4dcSAxel Dörfler	}
76244f16897SAxel Dörfler
76341828f15SAxel Dörfler	// Iterate over all Widgets and collect info
7648fa3f81aSAxel Dörfler	for (uint32 i = 0; i < audioGroup->widget_count; i++) {
76517959555SAxel Dörfler		hda_widget& widget = audioGroup->widgets[i];
76617959555SAxel Dörfler		uint32 nodeID = audioGroup->widget_start + i;
76717959555SAxel Dörfler		uint32 capabilities;
76844f16897SAxel Dörfler
76917959555SAxel Dörfler		verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID, VID_GET_PARAMETER,
7704919a4dcSAxel Dörfler			PID_AUDIO_WIDGET_CAP);
77117959555SAxel Dörfler		if (hda_send_verbs(audioGroup->codec, verbs, &capabilities, 1) != B_OK)
7728fa3f81aSAxel Dörfler			return B_ERROR;
7738fa3f81aSAxel Dörfler
77417959555SAxel Dörfler		widget.type = (hda_widget_type)((capabilities & AUDIO_CAP_TYPE_MASK)
77517959555SAxel Dörfler			>> AUDIO_CAP_TYPE_SHIFT);
7763a58749cSIngo Weinhold
77741828f15SAxel Dörfler		// Check specific node ids declared as inputs as beepers
7789a0a4979SJérôme Duval		switch (codec_id) {
779fa9ec9b1SJérôme Duval			case 0x11d41882:
780fa9ec9b1SJérôme Duval			case 0x11d41883:
781fa9ec9b1SJérôme Duval			case 0x11d41884:
782fa9ec9b1SJérôme Duval			case 0x11d4194a:
783fa9ec9b1SJérôme Duval			case 0x11d4194b:
784fa9ec9b1SJérôme Duval			case 0x11d41987:
7850e1e3de5SJérôme Duval			case 0x11d41988:
7860e1e3de5SJérôme Duval			case 0x11d4198b:
787fa9ec9b1SJérôme Duval			case 0x11d4989b:
7880e1e3de5SJérôme Duval				if (nodeID == 26)
7890e1e3de5SJérôme Duval					widget.type = WT_BEEP_GENERATOR;
7900e1e3de5SJérôme Duval				break;
7913fa33c82SJérôme Duval			case 0x10ec0260:
7923fa33c82SJérôme Duval				if (nodeID == 23)
7933fa33c82SJérôme Duval					widget.type = WT_BEEP_GENERATOR;
7943fa33c82SJérôme Duval				break;
7953fa33c82SJérôme Duval			case 0x10ec0262:
7963fa33c82SJérôme Duval			case 0x10ec0268:
7973fa33c82SJérôme Duval			case 0x10ec0880:
7983fa33c82SJérôme Duval			case 0x10ec0882:
7993fa33c82SJérôme Duval			case 0x10ec0883:
8003fa33c82SJérôme Duval			case 0x10ec0885:
8013fa33c82SJérôme Duval			case 0x10ec0888:
8023fa33c82SJérôme Duval			case 0x10ec0889:
8033fa33c82SJérôme Duval				if (nodeID == 29)
8043fa33c82SJérôme Duval					widget.type = WT_BEEP_GENERATOR;
8053fa33c82SJérôme Duval				break;
8063fa33c82SJérôme Duval		}
80717959555SAxel Dörfler		widget.active_input = -1;
80817959555SAxel Dörfler		widget.capabilities.audio = capabilities;
80917959555SAxel Dörfler		widget.node_id = nodeID;
81089db0a5bSIthamar R. Adema
81117959555SAxel Dörfler		if ((capabilities & AUDIO_CAP_POWER_CONTROL) != 0) {
81241828f15SAxel Dörfler			// We support power; switch us on!
81317959555SAxel Dörfler			verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID,
814438500b0SAxel Dörfler				VID_SET_POWER_STATE, 0);
81517959555SAxel Dörfler			hda_send_verbs(audioGroup->codec, verbs, NULL, 1);
81617959555SAxel Dörfler
81717959555SAxel Dörfler			snooze(1000);
8184919a4dcSAxel Dörfler		}
81917959555SAxel Dörfler		if ((capabilities & (AUDIO_CAP_INPUT_AMPLIFIER
82017959555SAxel Dörfler				| AUDIO_CAP_OUTPUT_AMPLIFIER)) != 0) {
82117959555SAxel Dörfler			hda_widget_get_amplifier_capabilities(audioGroup, &widget);
82217959555SAxel Dörfler		}
82317959555SAxel Dörfler
8245caa9c86SMurai Takashi		TRACE("%" B_PRIu32 ": %s\n", nodeID, get_widget_type_name(widget.type));
82544f16897SAxel Dörfler
82617959555SAxel Dörfler		switch (widget.type) {
8274919a4dcSAxel Dörfler			case WT_AUDIO_OUTPUT:
8284919a4dcSAxel Dörfler			case WT_AUDIO_INPUT:
82917959555SAxel Dörfler				hda_widget_get_stream_support(audioGroup, &widget);
8304489296bSJérôme Duval				dump_widget_stream_support(widget);
8314919a4dcSAxel Dörfler				break;
83217959555SAxel Dörfler
8334919a4dcSAxel Dörfler			case WT_PIN_COMPLEX:
83417959555SAxel Dörfler				verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID,
8358fa3f81aSAxel Dörfler					VID_GET_PARAMETER, PID_PIN_CAP);
8364919a4dcSAxel Dörfler				if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) {
837c71a6614SJérôme Duval					widget.d.pin.capabilities = resp[0];
8383a58749cSIngo Weinhold
839144c4c62SJérôme Duval					TRACE("\t%s%s\n", PIN_CAP_IS_INPUT(resp[0]) ? "[Input] " : "",
840c71a6614SJérôme Duval						PIN_CAP_IS_OUTPUT(resp[0]) ? "[Output]" : "");
8414919a4dcSAxel Dörfler				} else {
842144c4c62SJérôme Duval					ERROR("%s: Error getting Pin Complex IO\n", __func__);
8434919a4dcSAxel Dörfler				}
84489db0a5bSIthamar R. Adema
84517959555SAxel Dörfler				verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID,
846438500b0SAxel Dörfler					VID_GET_CONFIGURATION_DEFAULT, 0);
8474919a4dcSAxel Dörfler				if (hda_send_verbs(audioGroup->codec, verbs, resp, 1) == B_OK) {
8488c3da8ddSJérôme Duval					widget.d.pin.config = resp[0];
849e43d37dbSJérôme Duval					const char* location =
850e4e70475SJérôme Duval						get_widget_location(CONF_DEFAULT_LOCATION(resp[0]));
8515caa9c86SMurai Takashi					TRACE("\t%s, %s%s%s, %s, %s, Association:%" B_PRIu32 "\n",
8528c3da8ddSJérôme Duval						kPortConnector[CONF_DEFAULT_CONNECTIVITY(resp[0])],
853e4e70475SJérôme Duval						location ? location : "",
854e4e70475SJérôme Duval						location ? " " : "",
8558c3da8ddSJérôme Duval						kDefaultDevice[CONF_DEFAULT_DEVICE(resp[0])],
8568c3da8ddSJérôme Duval						kConnectionType[CONF_DEFAULT_CONNTYPE(resp[0])],
8578c3da8ddSJérôme Duval						kJackColor[CONF_DEFAULT_COLOR(resp[0])],
8588c3da8ddSJérôme Duval						CONF_DEFAULT_ASSOCIATION(resp[0]));
8594919a4dcSAxel Dörfler				}
8604919a4dcSAxel Dörfler				break;
8618fa3f81aSAxel Dörfler
8628f7c0754SJérôme Duval			case WT_VOLUME_KNOB:
8638f7c0754SJérôme Duval				verbs[0] = MAKE_VERB(audioGroup->codec->addr, nodeID,
8648f7c0754SJérôme Duval					VID_SET_VOLUME_KNOB_CONTROL, 0x0);
8658f7c0754SJérôme Duval				hda_send_verbs(audioGroup->codec, verbs, NULL, 1);
8668f7c0754SJérôme Duval				break;
8678fa3f81aSAxel Dörfler			default:
8684919a4dcSAxel Dörfler				break;
8694919a4dcSAxel Dörfler		}
87044f16897SAxel Dörfler
87117959555SAxel Dörfler		hda_widget_get_pm_support(audioGroup, &widget);
87217959555SAxel Dörfler		hda_widget_get_connections(audioGroup, &widget);
873313492b6SAxel Dörfler
8741fb04337SJérôme Duval		dump_widget_pm_support(widget);
87517959555SAxel Dörfler		dump_widget_audio_capabilities(capabilities);
87617959555SAxel Dörfler		dump_widget_amplifier_capabilities(widget, true);
87717959555SAxel Dörfler		dump_widget_amplifier_capabilities(widget, false);
87817959555SAxel Dörfler		dump_widget_inputs(widget);
87989db0a5bSIthamar R. Adema	}
88044f16897SAxel Dörfler
8818c3da8ddSJérôme Duval	hda_widget_get_associations(audioGroup);
8823a58749cSIngo Weinhold
8839a0a4979SJérôme Duval	// init the codecs
8849a0a4979SJérôme Duval	switch (codec_id) {
8859a0a4979SJérôme Duval		case 0x10ec0888: {
8869a0a4979SJérôme Duval			hda_verb_write(codec, 0x20, VID_SET_COEFFICIENT_INDEX, 0x0);
8879a0a4979SJérôme Duval			uint32 tmp;
8889a0a4979SJérôme Duval			hda_verb_read(codec, 0x20, VID_GET_PROCESSING_COEFFICIENT, &tmp);
8899a0a4979SJérôme Duval			hda_verb_write(codec, 0x20, VID_SET_COEFFICIENT_INDEX, 0x7);
89041828f15SAxel Dörfler			hda_verb_write(codec, 0x20, VID_SET_PROCESSING_COEFFICIENT,
8919a0a4979SJérôme Duval				(tmp & 0xf0) == 0x20 ? 0x830 : 0x3030);
8929a0a4979SJérôme Duval			break;
8939a0a4979SJérôme Duval		}
8949a0a4979SJérôme Duval	}
8959a0a4979SJérôme Duval
89689db0a5bSIthamar R. Adema	return B_OK;
89789db0a5bSIthamar R. Adema}
89889db0a5bSIthamar R. Adema
89944f16897SAxel Dörfler
90030f55bc9SAxel Dörfler/*! Find output path for widget */
90130f55bc9SAxel Dörflerstatic bool
90230f55bc9SAxel Dörflerhda_widget_find_output_path(hda_audio_group* audioGroup, hda_widget* widget,
903e43d37dbSJérôme Duval	uint32 depth, bool &alreadyUsed)
90489db0a5bSIthamar R. Adema{
905e43d37dbSJérôme Duval	alreadyUsed = false;
906e43d37dbSJérôme Duval
90730f55bc9SAxel Dörfler	if (widget == NULL || depth > 16)
90830f55bc9SAxel Dörfler		return false;
909