1/*
2 * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *      Alexander von Gluck, kallisti5@unixzen.com
7 */
8
9
10#include "connector.h"
11
12#include <Debug.h>
13
14#include "accelerant_protos.h"
15#include "accelerant.h"
16#include "bios.h"
17#include "encoder.h"
18#include "gpu.h"
19#include "utility.h"
20
21
22#undef TRACE
23
24#define TRACE_CONNECTOR
25#ifdef TRACE_CONNECTOR
26#   define TRACE(x...) _sPrintf("radeon_hd: " x)
27#else
28#   define TRACE(x...) ;
29#endif
30
31#define ERROR(x...) _sPrintf("radeon_hd: " x)
32
33
34static void
35gpio_lock_i2c(void* cookie, bool lock)
36{
37	gpio_info* info = (gpio_info*)cookie;
38
39	uint32 buffer = 0;
40
41	if (lock == true) {
42		// hwCapable and > DCE3
43		if (info->hwCapable == true && gInfo->shared_info->dceMajor >= 3) {
44			// Switch GPIO pads to ddc mode
45			buffer = Read32(OUT, info->sclMaskReg);
46			buffer &= ~(1 << 16);
47			Write32(OUT, info->sclMaskReg, buffer);
48		}
49
50		// Clear pins
51		buffer = Read32(OUT, info->sclAReg) & ~info->sclAMask;
52		Write32(OUT, info->sclAReg, buffer);
53		buffer = Read32(OUT, info->sdaAReg) & ~info->sdaAMask;
54		Write32(OUT, info->sdaAReg, buffer);
55	}
56
57	// Set pins to input
58	buffer = Read32(OUT, info->sclEnReg) & ~info->sclEnMask;
59	Write32(OUT, info->sclEnReg, buffer);
60	buffer = Read32(OUT, info->sdaEnReg) & ~info->sdaEnMask;
61	Write32(OUT, info->sdaEnReg, buffer);
62
63	// mask clock GPIO pins for software use
64	buffer = Read32(OUT, info->sclMaskReg);
65	if (lock == true)
66		buffer |= info->sclMask;
67	else
68		buffer &= ~info->sclMask;
69
70	Write32(OUT, info->sclMaskReg, buffer);
71	Read32(OUT, info->sclMaskReg);
72
73	// mask data GPIO pins for software use
74	buffer = Read32(OUT, info->sdaMaskReg);
75	if (lock == true)
76		buffer |= info->sdaMask;
77	else
78		buffer &= ~info->sdaMask;
79
80	Write32(OUT, info->sdaMaskReg, buffer);
81	Read32(OUT, info->sdaMaskReg);
82}
83
84
85static status_t
86gpio_get_i2c_bit(void* cookie, int* _clock, int* _data)
87{
88	gpio_info* info = (gpio_info*)cookie;
89
90	uint32 scl = Read32(OUT, info->sclYReg) & info->sclYMask;
91	uint32 sda = Read32(OUT, info->sdaYReg) & info->sdaYMask;
92
93	*_clock = scl != 0;
94	*_data = sda != 0;
95
96	return B_OK;
97}
98
99
100static status_t
101gpio_set_i2c_bit(void* cookie, int clock, int data)
102{
103	gpio_info* info = (gpio_info*)cookie;
104
105	uint32 scl = Read32(OUT, info->sclEnReg) & ~info->sclEnMask;
106	scl |= clock ? 0 : info->sclEnMask;
107	Write32(OUT, info->sclEnReg, scl);
108	Read32(OUT, info->sclEnReg);
109
110	uint32 sda = Read32(OUT, info->sdaEnReg) & ~info->sdaEnMask;
111	sda |= data ? 0 : info->sdaEnMask;
112	Write32(OUT, info->sdaEnReg, sda);
113	Read32(OUT, info->sdaEnReg);
114
115	return B_OK;
116}
117
118
119bool
120connector_read_edid(uint32 connectorIndex, edid1_info* edid)
121{
122	// ensure things are sane
123	uint32 gpioID = gConnector[connectorIndex]->gpioID;
124	if (gGPIOInfo[gpioID]->valid == false) {
125		ERROR("%s: invalid gpio %" B_PRIu32 " for connector %" B_PRIu32 "\n",
126			__func__, gpioID, connectorIndex);
127		return false;
128	}
129
130	i2c_bus bus;
131
132	ddc2_init_timing(&bus);
133	bus.cookie = (void*)gGPIOInfo[gpioID];
134	bus.set_signals = &gpio_set_i2c_bit;
135	bus.get_signals = &gpio_get_i2c_bit;
136
137	gpio_lock_i2c(bus.cookie, true);
138	status_t edid_result = ddc2_read_edid1(&bus, edid, NULL, NULL);
139	gpio_lock_i2c(bus.cookie, false);
140
141	if (edid_result != B_OK)
142		return false;
143
144	TRACE("%s: found edid monitor on connector #%" B_PRId32 "\n",
145		__func__, connectorIndex);
146
147	return true;
148}
149
150
151bool
152connector_read_mode_lvds(uint32 connectorIndex, display_mode* mode)
153{
154	uint8 dceMajor;
155	uint8 dceMinor;
156	int index = GetIndexIntoMasterTable(DATA, LVDS_Info);
157	uint16 offset;
158
159	union atomLVDSInfo {
160		struct _ATOM_LVDS_INFO info;
161		struct _ATOM_LVDS_INFO_V12 info_12;
162	};
163
164	if (atom_parse_data_header(gAtomContext, index, NULL,
165		&dceMajor, &dceMinor, &offset) == B_OK) {
166
167		union atomLVDSInfo* lvdsInfo
168			= (union atomLVDSInfo*)(gAtomContext->bios + offset);
169
170		display_timing* timing = &mode->timing;
171
172		// Pixel Clock
173		timing->pixel_clock
174			= B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usPixClk) * 10;
175		// Horizontal
176		timing->h_display
177			= B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHActive);
178		timing->h_total = timing->h_display + B_LENDIAN_TO_HOST_INT16(
179			lvdsInfo->info.sLCDTiming.usHBlanking_Time);
180		timing->h_sync_start = timing->h_display
181			+ B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncOffset);
182		timing->h_sync_end = timing->h_sync_start
183			+ B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncWidth);
184		// Vertical
185		timing->v_display
186			= B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVActive);
187		timing->v_total = timing->v_display + B_LENDIAN_TO_HOST_INT16(
188			lvdsInfo->info.sLCDTiming.usVBlanking_Time);
189		timing->v_sync_start = timing->v_display
190			+ B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncOffset);
191		timing->v_sync_end = timing->v_sync_start
192			+ B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncWidth);
193
194		#if 0
195		// Who cares.
196		uint32 powerDelay
197			= B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.usOffDelayInMs);
198		#endif
199
200		// Store special lvds flags the encoder setup needs
201		gConnector[connectorIndex]->lvdsFlags = lvdsInfo->info.ucLVDS_Misc;
202
203		// Spread Spectrum ID (in SS table)
204		gInfo->lvdsSpreadSpectrumID = lvdsInfo->info.ucSS_Id;
205
206		uint16 flags = B_LENDIAN_TO_HOST_INT16(
207			lvdsInfo->info.sLCDTiming.susModeMiscInfo.usAccess);
208
209		if ((flags & ATOM_VSYNC_POLARITY) == 0)
210			timing->flags |= B_POSITIVE_VSYNC;
211		if ((flags & ATOM_HSYNC_POLARITY) == 0)
212			timing->flags |= B_POSITIVE_HSYNC;
213
214		// Extra flags
215		if ((flags & ATOM_INTERLACE) != 0)
216			timing->flags |= B_TIMING_INTERLACED;
217
218		#if 0
219		// We don't use these timing flags at the moment
220		if ((flags & ATOM_COMPOSITESYNC) != 0)
221			timing->flags |= MODE_FLAG_CSYNC;
222		if ((flags & ATOM_DOUBLE_CLOCK_MODE) != 0)
223			timing->flags |= MODE_FLAG_DBLSCAN;
224		#endif
225
226		mode->h_display_start = 0;
227		mode->v_display_start = 0;
228		mode->virtual_width = timing->h_display;
229		mode->virtual_height = timing->v_display;
230
231		// Assume 32-bit color
232		mode->space = B_RGB32_LITTLE;
233
234		TRACE("%s: %" B_PRIu32 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16
235			" %" B_PRIu16  " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16
236			" %" B_PRIu16 "\n", __func__, timing->pixel_clock,
237			timing->h_display, timing->h_sync_start, timing->h_sync_end,
238			timing->h_total, timing->v_display, timing->v_sync_start,
239			timing->v_sync_end, timing->v_total);
240
241		return true;
242	}
243	return false;
244}
245
246
247status_t
248connector_attach_gpio(uint32 connectorIndex, uint8 hwPin)
249{
250	gConnector[connectorIndex]->gpioID = 0;
251	for (uint32 i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
252		if (gGPIOInfo[i]->hwPin != hwPin)
253			continue;
254		gConnector[connectorIndex]->gpioID = i;
255		return B_OK;
256	}
257
258	TRACE("%s: couldn't find GPIO for connector %" B_PRIu32 "\n",
259		__func__, connectorIndex);
260	return B_ERROR;
261}
262
263
264status_t
265gpio_probe()
266{
267	radeon_shared_info &info = *gInfo->shared_info;
268
269	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
270
271	uint8 tableMajor;
272	uint8 tableMinor;
273	uint16 tableOffset;
274	uint16 tableSize;
275
276	if (atom_parse_data_header(gAtomContext, index, &tableSize,
277		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
278		ERROR("%s: could't read GPIO_I2C_Info table from AtomBIOS index %d!\n",
279			__func__, index);
280		return B_ERROR;
281	}
282
283	struct _ATOM_GPIO_I2C_INFO* i2cInfo
284		= (struct _ATOM_GPIO_I2C_INFO*)(gAtomContext->bios + tableOffset);
285
286	uint32 numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER))
287		/ sizeof(ATOM_GPIO_I2C_ASSIGMENT);
288
289	if (numIndices > ATOM_MAX_SUPPORTED_DEVICE) {
290		ERROR("%s: ERROR: AtomBIOS contains more GPIO_Info items then I"
291			"was prepared for! (seen: %" B_PRIu32 "; max: %" B_PRIu32 ")\n",
292			__func__, numIndices, (uint32)ATOM_MAX_SUPPORTED_DEVICE);
293		return B_ERROR;
294	}
295
296	for (uint32 i = 0; i < numIndices; i++) {
297		ATOM_GPIO_I2C_ASSIGMENT* gpio = &i2cInfo->asGPIO_Info[i];
298
299		if (info.dceMajor >= 3) {
300			if (i == 4 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex)
301				== 0x1fda && gpio->sucI2cId.ucAccess == 0x94) {
302				gpio->sucI2cId.ucAccess = 0x14;
303				TRACE("%s: BUG: GPIO override for DCE 3 occured\n", __func__);
304			}
305		}
306
307		if (info.dceMajor >= 4) {
308			if (i == 7 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex)
309				== 0x1936 && gpio->sucI2cId.ucAccess == 0) {
310				gpio->sucI2cId.ucAccess = 0x97;
311				gpio->ucDataMaskShift = 8;
312				gpio->ucDataEnShift = 8;
313				gpio->ucDataY_Shift = 8;
314				gpio->ucDataA_Shift = 8;
315				TRACE("%s: BUG: GPIO override for DCE 4 occured\n", __func__);
316			}
317		}
318
319		// populate gpio information
320		gGPIOInfo[i]->hwPin = gpio->sucI2cId.ucAccess;
321		gGPIOInfo[i]->hwCapable
322			= (gpio->sucI2cId.sbfAccess.bfHW_Capable) ? true : false;
323
324		// GPIO mask (Allows software to control the GPIO pad)
325		// 0 = chip access; 1 = only software;
326		gGPIOInfo[i]->sclMaskReg
327			= B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) * 4;
328		gGPIOInfo[i]->sdaMaskReg
329			= B_LENDIAN_TO_HOST_INT16(gpio->usDataMaskRegisterIndex) * 4;
330		gGPIOInfo[i]->sclMask = 1 << gpio->ucClkMaskShift;
331		gGPIOInfo[i]->sdaMask = 1 << gpio->ucDataMaskShift;
332
333		// GPIO output / write (A) enable
334		// 0 = GPIO input (Y); 1 = GPIO output (A);
335		gGPIOInfo[i]->sclEnReg
336			= B_LENDIAN_TO_HOST_INT16(gpio->usClkEnRegisterIndex) * 4;
337		gGPIOInfo[i]->sdaEnReg
338			= B_LENDIAN_TO_HOST_INT16(gpio->usDataEnRegisterIndex) * 4;
339		gGPIOInfo[i]->sclEnMask = 1 << gpio->ucClkEnShift;
340		gGPIOInfo[i]->sdaEnMask = 1 << gpio->ucDataEnShift;
341
342		// GPIO output / write (A)
343		gGPIOInfo[i]->sclAReg
344			= B_LENDIAN_TO_HOST_INT16(gpio->usClkA_RegisterIndex) * 4;
345		gGPIOInfo[i]->sdaAReg
346			= B_LENDIAN_TO_HOST_INT16(gpio->usDataA_RegisterIndex) * 4;
347		gGPIOInfo[i]->sclAMask = 1 << gpio->ucClkA_Shift;
348		gGPIOInfo[i]->sdaAMask = 1 << gpio->ucDataA_Shift;
349
350		// GPIO input / read (Y)
351		gGPIOInfo[i]->sclYReg
352			= B_LENDIAN_TO_HOST_INT16(gpio->usClkY_RegisterIndex) * 4;
353		gGPIOInfo[i]->sdaYReg
354			= B_LENDIAN_TO_HOST_INT16(gpio->usDataY_RegisterIndex) * 4;
355		gGPIOInfo[i]->sclYMask = 1 << gpio->ucClkY_Shift;
356		gGPIOInfo[i]->sdaYMask = 1 << gpio->ucDataY_Shift;
357
358		// ensure data is valid
359		gGPIOInfo[i]->valid = gGPIOInfo[i]->sclMaskReg ? true : false;
360
361		TRACE("%s: GPIO @ %" B_PRIu32 ", valid: %s, hwPin: 0x%" B_PRIX32 "\n",
362			__func__, i, gGPIOInfo[i]->valid ? "true" : "false",
363			gGPIOInfo[i]->hwPin);
364	}
365
366	return B_OK;
367}
368
369
370status_t
371connector_probe_legacy()
372{
373	int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo);
374	uint8 tableMajor;
375	uint8 tableMinor;
376	uint16 tableSize;
377	uint16 tableOffset;
378
379	if (atom_parse_data_header(gAtomContext, index, &tableSize,
380		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
381		ERROR("%s: unable to parse data header!\n", __func__);
382		return B_ERROR;
383	}
384
385	union atomSupportedDevices {
386		struct _ATOM_SUPPORTED_DEVICES_INFO info;
387		struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2;
388		struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
389	};
390	union atomSupportedDevices* supportedDevices;
391	supportedDevices = (union atomSupportedDevices*)
392		(gAtomContext->bios + tableOffset);
393
394	uint16 deviceSupport
395		= B_LENDIAN_TO_HOST_INT16(supportedDevices->info.usDeviceSupport);
396
397	uint32 maxDevice;
398
399	if (tableMajor > 1)
400		maxDevice = ATOM_MAX_SUPPORTED_DEVICE;
401	else
402		maxDevice = ATOM_MAX_SUPPORTED_DEVICE_INFO;
403
404	uint32 i;
405	uint32 connectorIndex = 0;
406	for (i = 0; i < maxDevice; i++) {
407
408		gConnector[connectorIndex]->valid = false;
409
410		// check if this connector is used
411		if ((deviceSupport & (1 << i)) == 0)
412			continue;
413
414		if (i == ATOM_DEVICE_CV_INDEX) {
415			TRACE("%s: skipping component video\n",
416				__func__);
417			continue;
418		}
419
420		ATOM_CONNECTOR_INFO_I2C ci
421			= supportedDevices->info.asConnInfo[i];
422
423		gConnector[connectorIndex]->type = kConnectorConvertLegacy[
424			ci.sucConnectorInfo.sbfAccess.bfConnectorType];
425
426		if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_UNKNOWN) {
427			TRACE("%s: skipping unknown connector at %" B_PRId32
428				" of 0x%" B_PRIX8 "\n", __func__, i,
429				ci.sucConnectorInfo.sbfAccess.bfConnectorType);
430			continue;
431		}
432
433		// TODO: give tv unique connector ids
434
435		// Always set CRT1 and CRT2 as VGA, some cards incorrectly set
436		// VGA ports as DVI
437		if (i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX)
438			gConnector[connectorIndex]->type = VIDEO_CONNECTOR_VGA;
439
440		uint8 dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC;
441		uint32 encoderObject = encoder_object_lookup((1 << i), dac);
442		uint32 encoderID = (encoderObject & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
443
444		gConnector[connectorIndex]->valid = true;
445		gConnector[connectorIndex]->encoder.flags = (1 << i);
446		gConnector[connectorIndex]->encoder.valid = true;
447		gConnector[connectorIndex]->encoder.objectID = encoderID;
448		gConnector[connectorIndex]->encoder.type
449			= encoder_type_lookup(encoderID, (1 << i));
450
451		// TODO: Eval external encoders on legacy connector probe
452		gConnector[connectorIndex]->encoderExternal.valid = false;
453		// encoder_is_external(encoderID);
454
455		connector_attach_gpio(connectorIndex, ci.sucI2cId.ucAccess);
456
457		pll_limit_probe(&gConnector[connectorIndex]->encoder.pll);
458
459		connectorIndex++;
460	}
461
462	// TODO: combine shared connectors
463
464	if (connectorIndex == 0) {
465		TRACE("%s: zero connectors found using legacy detection\n", __func__);
466		return B_ERROR;
467	}
468
469	return B_OK;
470}
471
472
473// r600+
474status_t
475connector_probe()
476{
477	int index = GetIndexIntoMasterTable(DATA, Object_Header);
478	uint8 tableMajor;
479	uint8 tableMinor;
480	uint16 tableSize;
481	uint16 tableOffset;
482
483	if (atom_parse_data_header(gAtomContext, index, &tableSize,
484		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
485		ERROR("%s: ERROR: parsing data header failed!\n", __func__);
486		return B_ERROR;
487	}
488
489	if (tableMinor < 2) {
490		ERROR("%s: ERROR: table minor version unknown! "
491			"(%" B_PRIu8 ".%" B_PRIu8 ")\n", __func__, tableMajor, tableMinor);
492		return B_ERROR;
493	}
494
495	ATOM_CONNECTOR_OBJECT_TABLE* connectorObject;
496	ATOM_ENCODER_OBJECT_TABLE* encoderObject;
497	ATOM_OBJECT_TABLE* routerObject;
498	ATOM_DISPLAY_OBJECT_PATH_TABLE* pathObject;
499	ATOM_OBJECT_HEADER* objectHeader;
500
501	objectHeader = (ATOM_OBJECT_HEADER*)(gAtomContext->bios + tableOffset);
502	pathObject = (ATOM_DISPLAY_OBJECT_PATH_TABLE*)
503		(gAtomContext->bios + tableOffset
504		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usDisplayPathTableOffset));
505	connectorObject = (ATOM_CONNECTOR_OBJECT_TABLE*)
506		(gAtomContext->bios + tableOffset
507		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usConnectorObjectTableOffset));
508	encoderObject = (ATOM_ENCODER_OBJECT_TABLE*)
509		(gAtomContext->bios + tableOffset
510		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usEncoderObjectTableOffset));
511	routerObject = (ATOM_OBJECT_TABLE*)
512		(gAtomContext->bios + tableOffset
513		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usRouterObjectTableOffset));
514	int deviceSupport = B_LENDIAN_TO_HOST_INT16(objectHeader->usDeviceSupport);
515
516	int pathSize = 0;
517	int32 i = 0;
518
519	TRACE("%s: found %" B_PRIu8 " potential display paths.\n", __func__,
520		pathObject->ucNumOfDispPath);
521
522	uint32 connectorIndex = 0;
523	for (i = 0; i < pathObject->ucNumOfDispPath; i++) {
524
525		if (connectorIndex >= ATOM_MAX_SUPPORTED_DEVICE)
526			continue;
527
528		uint8* address = (uint8*)pathObject->asDispPath;
529		ATOM_DISPLAY_OBJECT_PATH* path;
530		address += pathSize;
531		path = (ATOM_DISPLAY_OBJECT_PATH*)address;
532		pathSize += B_LENDIAN_TO_HOST_INT16(path->usSize);
533
534		uint32 connectorType;
535		uint16 connectorFlags = B_LENDIAN_TO_HOST_INT16(path->usDeviceTag);
536
537		if ((deviceSupport & connectorFlags) != 0) {
538
539			uint16 connectorObjectID
540				= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
541					& OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
542			//uint8 con_obj_num
543			//	= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
544			//	& ENUM_ID_MASK) >> ENUM_ID_SHIFT;
545			//uint8 con_obj_type
546			//	= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
547			//	& OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
548
549			if (connectorFlags == ATOM_DEVICE_CV_SUPPORT) {
550				TRACE("%s: Path #%" B_PRId32 ": skipping component video.\n",
551					__func__, i);
552				continue;
553			}
554
555			radeon_shared_info &info = *gInfo->shared_info;
556
557			uint16 igpLaneInfo;
558			if ((info.chipsetFlags & CHIP_IGP) != 0) {
559				ERROR("%s: TODO: IGP chip connector detection\n", __func__);
560				// try non-IGP method for now
561				igpLaneInfo = 0;
562				connectorType = kConnectorConvert[connectorObjectID];
563			} else {
564				igpLaneInfo = 0;
565				connectorType = kConnectorConvert[connectorObjectID];
566			}
567
568			if (connectorType == VIDEO_CONNECTOR_UNKNOWN) {
569				ERROR("%s: Path #%" B_PRId32 ": skipping unknown connector.\n",
570					__func__, i);
571				continue;
572			}
573
574			connector_info* connector = gConnector[connectorIndex];
575
576			int32 j;
577			for (j = 0; j < ((B_LENDIAN_TO_HOST_INT16(path->usSize) - 8) / 2);
578				j++) {
579				//uint16 grph_obj_id
580				//	= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j])
581				//	& OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
582				//uint8 grph_obj_num
583				//	= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) &
584				//	ENUM_ID_MASK) >> ENUM_ID_SHIFT;
585				uint8 graphicObjectType
586					= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) &
587					OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
588
589				if (graphicObjectType == GRAPH_OBJECT_TYPE_ENCODER) {
590					// Found an encoder
591					int32 k;
592					for (k = 0; k < encoderObject->ucNumberOfObjects; k++) {
593						uint16 encoderObjectRaw
594							= B_LENDIAN_TO_HOST_INT16(
595							encoderObject->asObjects[k].usObjectID);
596						if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j])
597							== encoderObjectRaw) {
598							ATOM_COMMON_RECORD_HEADER* record
599								= (ATOM_COMMON_RECORD_HEADER*)
600								((uint16*)gAtomContext->bios + tableOffset
601								+ B_LENDIAN_TO_HOST_INT16(
602								encoderObject->asObjects[k].usRecordOffset));
603							ATOM_ENCODER_CAP_RECORD* capRecord;
604							uint16 caps = 0;
605							while (record->ucRecordSize > 0
606								&& record->ucRecordType > 0
607								&& record->ucRecordType
608								<= ATOM_MAX_OBJECT_RECORD_NUMBER) {
609								switch (record->ucRecordType) {
610									case ATOM_ENCODER_CAP_RECORD_TYPE:
611										capRecord = (ATOM_ENCODER_CAP_RECORD*)
612											record;
613										caps = B_LENDIAN_TO_HOST_INT16(
614											capRecord->usEncoderCap);
615										break;
616								}
617								record = (ATOM_COMMON_RECORD_HEADER*)
618									((char*)record + record->ucRecordSize);
619							}
620
621							uint32 encoderID
622								= (encoderObjectRaw & OBJECT_ID_MASK)
623									>> OBJECT_ID_SHIFT;
624
625							uint32 encoderType = encoder_type_lookup(encoderID,
626								connectorFlags);
627
628							if (encoderType == VIDEO_ENCODER_NONE) {
629								ERROR("%s: Path #%" B_PRId32 ":"
630									"skipping unknown encoder.\n",
631									__func__, i);
632								continue;
633							}
634
635							// External encoders are behind DVO or UNIPHY
636							if (encoder_is_external(encoderID)) {
637								encoder_info* encoder
638									= &connector->encoderExternal;
639								encoder->isExternal = true;
640
641								// Set up found connector
642								encoder->valid = true;
643								encoder->flags = connectorFlags;
644								encoder->objectID = encoderID;
645								encoder->type = encoderType;
646								encoder->linkEnumeration
647									= (encoderObjectRaw & ENUM_ID_MASK)
648										>> ENUM_ID_SHIFT;
649								encoder->isDPBridge
650									= encoder_is_dp_bridge(encoderID);
651
652								pll_limit_probe(&encoder->pll);
653							} else {
654								encoder_info* encoder
655									= &connector->encoder;
656								encoder->isExternal = false;
657
658								// Set up found connector
659								encoder->valid = true;
660								encoder->flags = connectorFlags;
661								encoder->objectID = encoderID;
662								encoder->type = encoderType;
663								encoder->linkEnumeration
664									= (encoderObjectRaw & ENUM_ID_MASK)
665										>> ENUM_ID_SHIFT;
666								encoder->isDPBridge = false;
667
668								pll_limit_probe(&encoder->pll);
669							}
670						}
671					}
672					// END if object is encoder
673				} else if (graphicObjectType == GRAPH_OBJECT_TYPE_ROUTER) {
674					ERROR("%s: TODO: Found router object?\n", __func__);
675				} // END if object is router
676			}
677
678			// Set up information buses such as ddc
679			if (((connectorFlags & ATOM_DEVICE_TV_SUPPORT) == 0)
680				&& (connectorFlags & ATOM_DEVICE_CV_SUPPORT) == 0) {
681				for (j = 0; j < connectorObject->ucNumberOfObjects; j++) {
682					if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
683						== B_LENDIAN_TO_HOST_INT16(
684						connectorObject->asObjects[j].usObjectID)) {
685						ATOM_COMMON_RECORD_HEADER* record
686							= (ATOM_COMMON_RECORD_HEADER*)(gAtomContext->bios
687							+ tableOffset + B_LENDIAN_TO_HOST_INT16(
688							connectorObject->asObjects[j].usRecordOffset));
689						while (record->ucRecordSize > 0
690							&& record->ucRecordType > 0
691							&& record->ucRecordType
692								<= ATOM_MAX_OBJECT_RECORD_NUMBER) {
693							ATOM_I2C_RECORD* i2cRecord;
694							ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig;
695							//ATOM_HPD_INT_RECORD* hpd_record;
696
697							switch (record->ucRecordType) {
698								case ATOM_I2C_RECORD_TYPE:
699									i2cRecord
700										= (ATOM_I2C_RECORD*)record;
701									i2cConfig
702										= (ATOM_I2C_ID_CONFIG_ACCESS*)
703										&i2cRecord->sucI2cId;
704									// attach i2c gpio information for connector
705									connector_attach_gpio(connectorIndex,
706										i2cConfig->ucAccess);
707									break;
708								case ATOM_HPD_INT_RECORD_TYPE:
709									// TODO: HPD (Hot Plug)
710									break;
711							}
712
713							// move to next record
714							record = (ATOM_COMMON_RECORD_HEADER*)
715								((char*)record + record->ucRecordSize);
716						}
717					}
718				}
719			}
720
721			// TODO: aux chan transactions
722
723			connector->valid = true;
724			connector->flags = connectorFlags;
725			connector->type = connectorType;
726			connector->objectID = connectorObjectID;
727
728			connectorIndex++;
729		} // END for each valid connector
730	} // end for each display path
731
732	return B_OK;
733}
734
735
736bool
737connector_is_dp(uint32 connectorIndex)
738{
739	connector_info* connector = gConnector[connectorIndex];
740
741	// Traditional DisplayPort connector
742	if (connector->type == VIDEO_CONNECTOR_DP
743		|| connector->type == VIDEO_CONNECTOR_EDP) {
744		return true;
745	}
746
747	// DisplayPort bridge on external encoder
748	if (connector->encoderExternal.valid == true
749		&& connector->encoderExternal.isDPBridge == true) {
750		return true;
751	}
752
753	return false;
754}
755
756
757void
758debug_connectors()
759{
760	ERROR("Currently detected connectors=============\n");
761	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
762		if (gConnector[id]->valid == true) {
763			uint32 connectorType = gConnector[id]->type;
764			uint16 gpioID = gConnector[id]->gpioID;
765
766			ERROR("Connector #%" B_PRIu32 ")\n", id);
767			ERROR(" + connector:          %s\n",
768				get_connector_name(connectorType));
769			ERROR(" + gpio table id:      %" B_PRIu16 "\n", gpioID);
770			ERROR(" + gpio hw pin:        0x%" B_PRIX32 "\n",
771				gGPIOInfo[gpioID]->hwPin);
772			ERROR(" + gpio valid:         %s\n",
773				gGPIOInfo[gpioID]->valid ? "true" : "false");
774
775			encoder_info* encoder = &gConnector[id]->encoder;
776			ERROR(" + encoder:            %s\n",
777				get_encoder_name(encoder->type));
778			ERROR("   - id:               %" B_PRIu16 "\n", encoder->objectID);
779			ERROR("   - type:             %s\n",
780				encoder_name_lookup(encoder->objectID));
781			ERROR("   - enumeration:      %" B_PRIu32 "\n",
782				encoder->linkEnumeration);
783
784			encoder = &gConnector[id]->encoderExternal;
785
786			ERROR("   - is bridge:        %s\n",
787				encoder->valid ? "true" : "false");
788
789			if (!encoder->valid)
790				ERROR("   + external encoder: none\n");
791			else {
792			ERROR("   + external encoder: %s\n",
793					get_encoder_name(encoder->type));
794				ERROR("     - valid:          true\n");
795				ERROR("     - id:             %" B_PRIu16 "\n",
796					encoder->objectID);
797				ERROR("     - type:           %s\n",
798					encoder_name_lookup(encoder->objectID));
799				ERROR("     - enumeration:    %" B_PRIu32 "\n",
800					encoder->linkEnumeration);
801			}
802
803			uint32 encoderFlags = gConnector[id]->encoder.flags;
804			bool flags = false;
805			ERROR(" + flags:\n");
806			if ((encoderFlags & ATOM_DEVICE_CRT1_SUPPORT) != 0) {
807				ERROR("   * device CRT1 support\n");
808				flags = true;
809			}
810			if ((encoderFlags & ATOM_DEVICE_CRT2_SUPPORT) != 0) {
811				ERROR("   * device CRT2 support\n");
812				flags = true;
813			}
814			if ((encoderFlags & ATOM_DEVICE_LCD1_SUPPORT) != 0) {
815				ERROR("   * device LCD1 support\n");
816				flags = true;
817			}
818			if ((encoderFlags & ATOM_DEVICE_LCD2_SUPPORT) != 0) {
819				ERROR("   * device LCD2 support\n");
820				flags = true;
821			}
822			if ((encoderFlags & ATOM_DEVICE_TV1_SUPPORT) != 0) {
823				ERROR("   * device TV1 support\n");
824				flags = true;
825			}
826			if ((encoderFlags & ATOM_DEVICE_CV_SUPPORT) != 0) {
827				ERROR("   * device CV support\n");
828				flags = true;
829			}
830			if ((encoderFlags & ATOM_DEVICE_DFP1_SUPPORT) != 0) {
831				ERROR("   * device DFP1 support\n");
832				flags = true;
833			}
834			if ((encoderFlags & ATOM_DEVICE_DFP2_SUPPORT) != 0) {
835				ERROR("   * device DFP2 support\n");
836				flags = true;
837			}
838			if ((encoderFlags & ATOM_DEVICE_DFP3_SUPPORT) != 0) {
839				ERROR("   * device DFP3 support\n");
840				flags = true;
841			}
842			if ((encoderFlags & ATOM_DEVICE_DFP4_SUPPORT) != 0) {
843				ERROR("   * device DFP4 support\n");
844				flags = true;
845			}
846			if ((encoderFlags & ATOM_DEVICE_DFP5_SUPPORT) != 0) {
847				ERROR("   * device DFP5 support\n");
848				flags = true;
849			}
850			if ((encoderFlags & ATOM_DEVICE_DFP6_SUPPORT) != 0) {
851				ERROR("   * device DFP6 support\n");
852				flags = true;
853			}
854			if (flags == false)
855				ERROR("   * no known flags\n");
856		}
857	}
858	ERROR("==========================================\n");
859}
860