xref: /haiku/src/add-ons/accelerants/radeon_hd/connector.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 
34 static void
35 gpio_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 
85 static status_t
86 gpio_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 
100 static status_t
101 gpio_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 
119 bool
120 connector_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 
151 bool
152 connector_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 
247 status_t
248 connector_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 
264 status_t
265 gpio_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 
370 status_t
371 connector_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+
474 status_t
475 connector_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 
736 bool
737 connector_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 
757 void
758 debug_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