xref: /haiku/src/add-ons/accelerants/radeon_hd/connector.cpp (revision 40e20c10768c3dcfc54074b8886e3e91455332e1)
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 		uint16 flags = B_LENDIAN_TO_HOST_INT16(
204 			lvdsInfo->info.sLCDTiming.susModeMiscInfo.usAccess);
205 
206 		if ((flags & ATOM_VSYNC_POLARITY) == 0)
207 			timing->flags |= B_POSITIVE_VSYNC;
208 		if ((flags & ATOM_HSYNC_POLARITY) == 0)
209 			timing->flags |= B_POSITIVE_HSYNC;
210 
211 		// Extra flags
212 		if ((flags & ATOM_INTERLACE) != 0)
213 			timing->flags |= B_TIMING_INTERLACED;
214 
215 		#if 0
216 		// We don't use these timing flags at the moment
217 		if ((flags & ATOM_COMPOSITESYNC) != 0)
218 			timing->flags |= MODE_FLAG_CSYNC;
219 		if ((flags & ATOM_DOUBLE_CLOCK_MODE) != 0)
220 			timing->flags |= MODE_FLAG_DBLSCAN;
221 		#endif
222 
223 		mode->h_display_start = 0;
224 		mode->v_display_start = 0;
225 		mode->virtual_width = timing->h_display;
226 		mode->virtual_height = timing->v_display;
227 
228 		// Assume 32-bit color
229 		mode->space = B_RGB32_LITTLE;
230 
231 		TRACE("%s: %" B_PRIu32 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16
232 			" %" B_PRIu16  " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16
233 			" %" B_PRIu16 "\n", __func__, timing->pixel_clock,
234 			timing->h_display, timing->h_sync_start, timing->h_sync_end,
235 			timing->h_total, timing->v_display, timing->v_sync_start,
236 			timing->v_sync_end, timing->v_total);
237 
238 		return true;
239 	}
240 	return false;
241 }
242 
243 
244 status_t
245 connector_attach_gpio(uint32 connectorIndex, uint8 hwPin)
246 {
247 	gConnector[connectorIndex]->gpioID = 0;
248 	for (uint32 i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
249 		if (gGPIOInfo[i]->hwPin != hwPin)
250 			continue;
251 		gConnector[connectorIndex]->gpioID = i;
252 		return B_OK;
253 	}
254 
255 	TRACE("%s: couldn't find GPIO for connector %" B_PRIu32 "\n",
256 		__func__, connectorIndex);
257 	return B_ERROR;
258 }
259 
260 
261 status_t
262 gpio_probe()
263 {
264 	radeon_shared_info &info = *gInfo->shared_info;
265 
266 	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
267 
268 	uint8 tableMajor;
269 	uint8 tableMinor;
270 	uint16 tableOffset;
271 	uint16 tableSize;
272 
273 	if (atom_parse_data_header(gAtomContext, index, &tableSize,
274 		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
275 		ERROR("%s: could't read GPIO_I2C_Info table from AtomBIOS index %d!\n",
276 			__func__, index);
277 		return B_ERROR;
278 	}
279 
280 	struct _ATOM_GPIO_I2C_INFO* i2cInfo
281 		= (struct _ATOM_GPIO_I2C_INFO*)(gAtomContext->bios + tableOffset);
282 
283 	uint32 numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER))
284 		/ sizeof(ATOM_GPIO_I2C_ASSIGMENT);
285 
286 	if (numIndices > ATOM_MAX_SUPPORTED_DEVICE) {
287 		ERROR("%s: ERROR: AtomBIOS contains more GPIO_Info items then I"
288 			"was prepared for! (seen: %" B_PRIu32 "; max: %" B_PRIu32 ")\n",
289 			__func__, numIndices, (uint32)ATOM_MAX_SUPPORTED_DEVICE);
290 		return B_ERROR;
291 	}
292 
293 	for (uint32 i = 0; i < numIndices; i++) {
294 		ATOM_GPIO_I2C_ASSIGMENT* gpio = &i2cInfo->asGPIO_Info[i];
295 
296 		if (info.dceMajor >= 3) {
297 			if (i == 4 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex)
298 				== 0x1fda && gpio->sucI2cId.ucAccess == 0x94) {
299 				gpio->sucI2cId.ucAccess = 0x14;
300 				TRACE("%s: BUG: GPIO override for DCE 3 occured\n", __func__);
301 			}
302 		}
303 
304 		if (info.dceMajor >= 4) {
305 			if (i == 7 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex)
306 				== 0x1936 && gpio->sucI2cId.ucAccess == 0) {
307 				gpio->sucI2cId.ucAccess = 0x97;
308 				gpio->ucDataMaskShift = 8;
309 				gpio->ucDataEnShift = 8;
310 				gpio->ucDataY_Shift = 8;
311 				gpio->ucDataA_Shift = 8;
312 				TRACE("%s: BUG: GPIO override for DCE 4 occured\n", __func__);
313 			}
314 		}
315 
316 		// populate gpio information
317 		gGPIOInfo[i]->hwPin = gpio->sucI2cId.ucAccess;
318 		gGPIOInfo[i]->hwCapable
319 			= (gpio->sucI2cId.sbfAccess.bfHW_Capable) ? true : false;
320 
321 		// GPIO mask (Allows software to control the GPIO pad)
322 		// 0 = chip access; 1 = only software;
323 		gGPIOInfo[i]->sclMaskReg
324 			= B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) * 4;
325 		gGPIOInfo[i]->sdaMaskReg
326 			= B_LENDIAN_TO_HOST_INT16(gpio->usDataMaskRegisterIndex) * 4;
327 		gGPIOInfo[i]->sclMask = 1 << gpio->ucClkMaskShift;
328 		gGPIOInfo[i]->sdaMask = 1 << gpio->ucDataMaskShift;
329 
330 		// GPIO output / write (A) enable
331 		// 0 = GPIO input (Y); 1 = GPIO output (A);
332 		gGPIOInfo[i]->sclEnReg
333 			= B_LENDIAN_TO_HOST_INT16(gpio->usClkEnRegisterIndex) * 4;
334 		gGPIOInfo[i]->sdaEnReg
335 			= B_LENDIAN_TO_HOST_INT16(gpio->usDataEnRegisterIndex) * 4;
336 		gGPIOInfo[i]->sclEnMask = 1 << gpio->ucClkEnShift;
337 		gGPIOInfo[i]->sdaEnMask = 1 << gpio->ucDataEnShift;
338 
339 		// GPIO output / write (A)
340 		gGPIOInfo[i]->sclAReg
341 			= B_LENDIAN_TO_HOST_INT16(gpio->usClkA_RegisterIndex) * 4;
342 		gGPIOInfo[i]->sdaAReg
343 			= B_LENDIAN_TO_HOST_INT16(gpio->usDataA_RegisterIndex) * 4;
344 		gGPIOInfo[i]->sclAMask = 1 << gpio->ucClkA_Shift;
345 		gGPIOInfo[i]->sdaAMask = 1 << gpio->ucDataA_Shift;
346 
347 		// GPIO input / read (Y)
348 		gGPIOInfo[i]->sclYReg
349 			= B_LENDIAN_TO_HOST_INT16(gpio->usClkY_RegisterIndex) * 4;
350 		gGPIOInfo[i]->sdaYReg
351 			= B_LENDIAN_TO_HOST_INT16(gpio->usDataY_RegisterIndex) * 4;
352 		gGPIOInfo[i]->sclYMask = 1 << gpio->ucClkY_Shift;
353 		gGPIOInfo[i]->sdaYMask = 1 << gpio->ucDataY_Shift;
354 
355 		// ensure data is valid
356 		gGPIOInfo[i]->valid = gGPIOInfo[i]->sclMaskReg ? true : false;
357 
358 		TRACE("%s: GPIO @ %" B_PRIu32 ", valid: %s, hwPin: 0x%" B_PRIX32 "\n",
359 			__func__, i, gGPIOInfo[i]->valid ? "true" : "false",
360 			gGPIOInfo[i]->hwPin);
361 	}
362 
363 	return B_OK;
364 }
365 
366 
367 status_t
368 connector_probe_legacy()
369 {
370 	int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo);
371 	uint8 tableMajor;
372 	uint8 tableMinor;
373 	uint16 tableSize;
374 	uint16 tableOffset;
375 
376 	if (atom_parse_data_header(gAtomContext, index, &tableSize,
377 		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
378 		ERROR("%s: unable to parse data header!\n", __func__);
379 		return B_ERROR;
380 	}
381 
382 	union atomSupportedDevices {
383 		struct _ATOM_SUPPORTED_DEVICES_INFO info;
384 		struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2;
385 		struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
386 	};
387 	union atomSupportedDevices* supportedDevices;
388 	supportedDevices = (union atomSupportedDevices*)
389 		(gAtomContext->bios + tableOffset);
390 
391 	uint16 deviceSupport
392 		= B_LENDIAN_TO_HOST_INT16(supportedDevices->info.usDeviceSupport);
393 
394 	uint32 maxDevice;
395 
396 	if (tableMajor > 1)
397 		maxDevice = ATOM_MAX_SUPPORTED_DEVICE;
398 	else
399 		maxDevice = ATOM_MAX_SUPPORTED_DEVICE_INFO;
400 
401 	uint32 i;
402 	uint32 connectorIndex = 0;
403 	for (i = 0; i < maxDevice; i++) {
404 
405 		gConnector[connectorIndex]->valid = false;
406 
407 		// check if this connector is used
408 		if ((deviceSupport & (1 << i)) == 0)
409 			continue;
410 
411 		if (i == ATOM_DEVICE_CV_INDEX) {
412 			TRACE("%s: skipping component video\n",
413 				__func__);
414 			continue;
415 		}
416 
417 		ATOM_CONNECTOR_INFO_I2C ci
418 			= supportedDevices->info.asConnInfo[i];
419 
420 		gConnector[connectorIndex]->type = kConnectorConvertLegacy[
421 			ci.sucConnectorInfo.sbfAccess.bfConnectorType];
422 
423 		if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_UNKNOWN) {
424 			TRACE("%s: skipping unknown connector at %" B_PRId32
425 				" of 0x%" B_PRIX8 "\n", __func__, i,
426 				ci.sucConnectorInfo.sbfAccess.bfConnectorType);
427 			continue;
428 		}
429 
430 		// TODO: give tv unique connector ids
431 
432 		// Always set CRT1 and CRT2 as VGA, some cards incorrectly set
433 		// VGA ports as DVI
434 		if (i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX)
435 			gConnector[connectorIndex]->type = VIDEO_CONNECTOR_VGA;
436 
437 		uint8 dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC;
438 		uint32 encoderObject = encoder_object_lookup((1 << i), dac);
439 		uint32 encoderID = (encoderObject & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
440 
441 		gConnector[connectorIndex]->valid = true;
442 		gConnector[connectorIndex]->encoder.flags = (1 << i);
443 		gConnector[connectorIndex]->encoder.valid = true;
444 		gConnector[connectorIndex]->encoder.objectID = encoderID;
445 		gConnector[connectorIndex]->encoder.type
446 			= encoder_type_lookup(encoderID, (1 << i));
447 
448 		// TODO: Eval external encoders on legacy connector probe
449 		gConnector[connectorIndex]->encoderExternal.valid = false;
450 		// encoder_is_external(encoderID);
451 
452 		connector_attach_gpio(connectorIndex, ci.sucI2cId.ucAccess);
453 
454 		pll_limit_probe(&gConnector[connectorIndex]->encoder.pll);
455 
456 		connectorIndex++;
457 	}
458 
459 	// TODO: combine shared connectors
460 
461 	if (connectorIndex == 0) {
462 		TRACE("%s: zero connectors found using legacy detection\n", __func__);
463 		return B_ERROR;
464 	}
465 
466 	return B_OK;
467 }
468 
469 
470 // r600+
471 status_t
472 connector_probe()
473 {
474 	int index = GetIndexIntoMasterTable(DATA, Object_Header);
475 	uint8 tableMajor;
476 	uint8 tableMinor;
477 	uint16 tableSize;
478 	uint16 tableOffset;
479 
480 	if (atom_parse_data_header(gAtomContext, index, &tableSize,
481 		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
482 		ERROR("%s: ERROR: parsing data header failed!\n", __func__);
483 		return B_ERROR;
484 	}
485 
486 	if (tableMinor < 2) {
487 		ERROR("%s: ERROR: table minor version unknown! "
488 			"(%" B_PRIu8 ".%" B_PRIu8 ")\n", __func__, tableMajor, tableMinor);
489 		return B_ERROR;
490 	}
491 
492 	ATOM_CONNECTOR_OBJECT_TABLE* connectorObject;
493 	ATOM_ENCODER_OBJECT_TABLE* encoderObject;
494 	ATOM_OBJECT_TABLE* routerObject;
495 	ATOM_DISPLAY_OBJECT_PATH_TABLE* pathObject;
496 	ATOM_OBJECT_HEADER* objectHeader;
497 
498 	objectHeader = (ATOM_OBJECT_HEADER*)(gAtomContext->bios + tableOffset);
499 	pathObject = (ATOM_DISPLAY_OBJECT_PATH_TABLE*)
500 		(gAtomContext->bios + tableOffset
501 		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usDisplayPathTableOffset));
502 	connectorObject = (ATOM_CONNECTOR_OBJECT_TABLE*)
503 		(gAtomContext->bios + tableOffset
504 		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usConnectorObjectTableOffset));
505 	encoderObject = (ATOM_ENCODER_OBJECT_TABLE*)
506 		(gAtomContext->bios + tableOffset
507 		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usEncoderObjectTableOffset));
508 	routerObject = (ATOM_OBJECT_TABLE*)
509 		(gAtomContext->bios + tableOffset
510 		+ B_LENDIAN_TO_HOST_INT16(objectHeader->usRouterObjectTableOffset));
511 	int deviceSupport = B_LENDIAN_TO_HOST_INT16(objectHeader->usDeviceSupport);
512 
513 	int pathSize = 0;
514 	int32 i = 0;
515 
516 	TRACE("%s: found %" B_PRIu8 " potential display paths.\n", __func__,
517 		pathObject->ucNumOfDispPath);
518 
519 	uint32 connectorIndex = 0;
520 	for (i = 0; i < pathObject->ucNumOfDispPath; i++) {
521 
522 		if (connectorIndex >= ATOM_MAX_SUPPORTED_DEVICE)
523 			continue;
524 
525 		uint8* address = (uint8*)pathObject->asDispPath;
526 		ATOM_DISPLAY_OBJECT_PATH* path;
527 		address += pathSize;
528 		path = (ATOM_DISPLAY_OBJECT_PATH*)address;
529 		pathSize += B_LENDIAN_TO_HOST_INT16(path->usSize);
530 
531 		uint32 connectorType;
532 		uint16 connectorFlags = B_LENDIAN_TO_HOST_INT16(path->usDeviceTag);
533 
534 		if ((deviceSupport & connectorFlags) != 0) {
535 
536 			uint16 connectorObjectID
537 				= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
538 					& OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
539 			//uint8 con_obj_num
540 			//	= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
541 			//	& ENUM_ID_MASK) >> ENUM_ID_SHIFT;
542 			//uint8 con_obj_type
543 			//	= (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
544 			//	& OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
545 
546 			if (connectorFlags == ATOM_DEVICE_CV_SUPPORT) {
547 				TRACE("%s: Path #%" B_PRId32 ": skipping component video.\n",
548 					__func__, i);
549 				continue;
550 			}
551 
552 			radeon_shared_info &info = *gInfo->shared_info;
553 
554 			uint16 igpLaneInfo;
555 			if ((info.chipsetFlags & CHIP_IGP) != 0) {
556 				ERROR("%s: TODO: IGP chip connector detection\n", __func__);
557 				// try non-IGP method for now
558 				igpLaneInfo = 0;
559 				connectorType = kConnectorConvert[connectorObjectID];
560 			} else {
561 				igpLaneInfo = 0;
562 				connectorType = kConnectorConvert[connectorObjectID];
563 			}
564 
565 			if (connectorType == VIDEO_CONNECTOR_UNKNOWN) {
566 				ERROR("%s: Path #%" B_PRId32 ": skipping unknown connector.\n",
567 					__func__, i);
568 				continue;
569 			}
570 
571 			connector_info* connector = gConnector[connectorIndex];
572 
573 			int32 j;
574 			for (j = 0; j < ((B_LENDIAN_TO_HOST_INT16(path->usSize) - 8) / 2);
575 				j++) {
576 				//uint16 grph_obj_id
577 				//	= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j])
578 				//	& OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
579 				//uint8 grph_obj_num
580 				//	= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) &
581 				//	ENUM_ID_MASK) >> ENUM_ID_SHIFT;
582 				uint8 graphicObjectType
583 					= (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) &
584 					OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
585 
586 				if (graphicObjectType == GRAPH_OBJECT_TYPE_ENCODER) {
587 					// Found an encoder
588 					int32 k;
589 					for (k = 0; k < encoderObject->ucNumberOfObjects; k++) {
590 						uint16 encoderObjectRaw
591 							= B_LENDIAN_TO_HOST_INT16(
592 							encoderObject->asObjects[k].usObjectID);
593 						if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j])
594 							== encoderObjectRaw) {
595 							ATOM_COMMON_RECORD_HEADER* record
596 								= (ATOM_COMMON_RECORD_HEADER*)
597 								((uint16*)gAtomContext->bios + tableOffset
598 								+ B_LENDIAN_TO_HOST_INT16(
599 								encoderObject->asObjects[k].usRecordOffset));
600 							ATOM_ENCODER_CAP_RECORD* capRecord;
601 							uint16 caps = 0;
602 							while (record->ucRecordSize > 0
603 								&& record->ucRecordType > 0
604 								&& record->ucRecordType
605 								<= ATOM_MAX_OBJECT_RECORD_NUMBER) {
606 								switch (record->ucRecordType) {
607 									case ATOM_ENCODER_CAP_RECORD_TYPE:
608 										capRecord = (ATOM_ENCODER_CAP_RECORD*)
609 											record;
610 										caps = B_LENDIAN_TO_HOST_INT16(
611 											capRecord->usEncoderCap);
612 										break;
613 								}
614 								record = (ATOM_COMMON_RECORD_HEADER*)
615 									((char*)record + record->ucRecordSize);
616 							}
617 
618 							uint32 encoderID
619 								= (encoderObjectRaw & OBJECT_ID_MASK)
620 									>> OBJECT_ID_SHIFT;
621 
622 							uint32 encoderType = encoder_type_lookup(encoderID,
623 								connectorFlags);
624 
625 							if (encoderType == VIDEO_ENCODER_NONE) {
626 								ERROR("%s: Path #%" B_PRId32 ":"
627 									"skipping unknown encoder.\n",
628 									__func__, i);
629 								continue;
630 							}
631 
632 							// External encoders are behind DVO or UNIPHY
633 							if (encoder_is_external(encoderID)) {
634 								encoder_info* encoder
635 									= &connector->encoderExternal;
636 								encoder->isExternal = true;
637 
638 								// Set up found connector
639 								encoder->valid = true;
640 								encoder->flags = connectorFlags;
641 								encoder->objectID = encoderID;
642 								encoder->type = encoderType;
643 								encoder->linkEnumeration
644 									= (encoderObjectRaw & ENUM_ID_MASK)
645 										>> ENUM_ID_SHIFT;
646 								encoder->isDPBridge
647 									= encoder_is_dp_bridge(encoderID);
648 
649 								pll_limit_probe(&encoder->pll);
650 							} else {
651 								encoder_info* encoder
652 									= &connector->encoder;
653 								encoder->isExternal = false;
654 
655 								// Set up found connector
656 								encoder->valid = true;
657 								encoder->flags = connectorFlags;
658 								encoder->objectID = encoderID;
659 								encoder->type = encoderType;
660 								encoder->linkEnumeration
661 									= (encoderObjectRaw & ENUM_ID_MASK)
662 										>> ENUM_ID_SHIFT;
663 								encoder->isDPBridge = false;
664 
665 								pll_limit_probe(&encoder->pll);
666 							}
667 						}
668 					}
669 					// END if object is encoder
670 				} else if (graphicObjectType == GRAPH_OBJECT_TYPE_ROUTER) {
671 					ERROR("%s: TODO: Found router object?\n", __func__);
672 				} // END if object is router
673 			}
674 
675 			// Set up information buses such as ddc
676 			if (((connectorFlags & ATOM_DEVICE_TV_SUPPORT) == 0)
677 				&& (connectorFlags & ATOM_DEVICE_CV_SUPPORT) == 0) {
678 				for (j = 0; j < connectorObject->ucNumberOfObjects; j++) {
679 					if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId)
680 						== B_LENDIAN_TO_HOST_INT16(
681 						connectorObject->asObjects[j].usObjectID)) {
682 						ATOM_COMMON_RECORD_HEADER* record
683 							= (ATOM_COMMON_RECORD_HEADER*)(gAtomContext->bios
684 							+ tableOffset + B_LENDIAN_TO_HOST_INT16(
685 							connectorObject->asObjects[j].usRecordOffset));
686 						while (record->ucRecordSize > 0
687 							&& record->ucRecordType > 0
688 							&& record->ucRecordType
689 								<= ATOM_MAX_OBJECT_RECORD_NUMBER) {
690 							ATOM_I2C_RECORD* i2cRecord;
691 							ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig;
692 							//ATOM_HPD_INT_RECORD* hpd_record;
693 
694 							switch (record->ucRecordType) {
695 								case ATOM_I2C_RECORD_TYPE:
696 									i2cRecord
697 										= (ATOM_I2C_RECORD*)record;
698 									i2cConfig
699 										= (ATOM_I2C_ID_CONFIG_ACCESS*)
700 										&i2cRecord->sucI2cId;
701 									// attach i2c gpio information for connector
702 									connector_attach_gpio(connectorIndex,
703 										i2cConfig->ucAccess);
704 									break;
705 								case ATOM_HPD_INT_RECORD_TYPE:
706 									// TODO: HPD (Hot Plug)
707 									break;
708 							}
709 
710 							// move to next record
711 							record = (ATOM_COMMON_RECORD_HEADER*)
712 								((char*)record + record->ucRecordSize);
713 						}
714 					}
715 				}
716 			}
717 
718 			// TODO: aux chan transactions
719 
720 			connector->valid = true;
721 			connector->flags = connectorFlags;
722 			connector->type = connectorType;
723 			connector->objectID = connectorObjectID;
724 
725 			connectorIndex++;
726 		} // END for each valid connector
727 	} // end for each display path
728 
729 	return B_OK;
730 }
731 
732 
733 bool
734 connector_is_dp(uint32 connectorIndex)
735 {
736 	connector_info* connector = gConnector[connectorIndex];
737 
738 	// Traditional DisplayPort connector
739 	if (connector->type == VIDEO_CONNECTOR_DP
740 		|| connector->type == VIDEO_CONNECTOR_EDP) {
741 		return true;
742 	}
743 
744 	// DisplayPort bridge on external encoder
745 	if (connector->encoderExternal.valid == true
746 		&& connector->encoderExternal.isDPBridge == true) {
747 		return true;
748 	}
749 
750 	return false;
751 }
752 
753 
754 void
755 debug_connectors()
756 {
757 	ERROR("Currently detected connectors=============\n");
758 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
759 		if (gConnector[id]->valid == true) {
760 			uint32 connectorType = gConnector[id]->type;
761 			uint16 gpioID = gConnector[id]->gpioID;
762 
763 			ERROR("Connector #%" B_PRIu32 ")\n", id);
764 			ERROR(" + connector:          %s\n", get_connector_name(connectorType));
765 			ERROR(" + gpio table id:      %" B_PRIu16 "\n", gpioID);
766 			ERROR(" + gpio hw pin:        0x%" B_PRIX32 "\n",
767 				gGPIOInfo[gpioID]->hwPin);
768 			ERROR(" + gpio valid:         %s\n",
769 				gGPIOInfo[gpioID]->valid ? "true" : "false");
770 
771 			encoder_info* encoder = &gConnector[id]->encoder;
772 			ERROR(" + encoder:            %s\n", get_encoder_name(encoder->type));
773 			ERROR("   - id:               %" B_PRIu16 "\n", encoder->objectID);
774 			ERROR("   - type:             %s\n",
775 				encoder_name_lookup(encoder->objectID));
776 			ERROR("   - enumeration:      %" B_PRIu32 "\n",
777 				encoder->linkEnumeration);
778 
779 			encoder = &gConnector[id]->encoderExternal;
780 
781 			ERROR("   - is bridge:        %s\n",
782 				encoder->valid ? "true" : "false");
783 
784 			if (!encoder->valid)
785 				ERROR("   + external encoder: none\n");
786 			else {
787 			ERROR("   + external encoder: %s\n",
788 					get_encoder_name(encoder->type));
789 				ERROR("     - valid:          true\n");
790 				ERROR("     - id:             %" B_PRIu16 "\n",
791 					encoder->objectID);
792 				ERROR("     - type:           %s\n",
793 					encoder_name_lookup(encoder->objectID));
794 				ERROR("     - enumeration:    %" B_PRIu32 "\n",
795 					encoder->linkEnumeration);
796 			}
797 
798 			uint32 encoderFlags = gConnector[id]->encoder.flags;
799 			bool flags = false;
800 			ERROR(" + flags:\n");
801 			if ((encoderFlags & ATOM_DEVICE_CRT1_SUPPORT) != 0) {
802 				ERROR("   * device CRT1 support\n");
803 				flags = true;
804 			}
805 			if ((encoderFlags & ATOM_DEVICE_CRT2_SUPPORT) != 0) {
806 				ERROR("   * device CRT2 support\n");
807 				flags = true;
808 			}
809 			if ((encoderFlags & ATOM_DEVICE_LCD1_SUPPORT) != 0) {
810 				ERROR("   * device LCD1 support\n");
811 				flags = true;
812 			}
813 			if ((encoderFlags & ATOM_DEVICE_LCD2_SUPPORT) != 0) {
814 				ERROR("   * device LCD2 support\n");
815 				flags = true;
816 			}
817 			if ((encoderFlags & ATOM_DEVICE_TV1_SUPPORT) != 0) {
818 				ERROR("   * device TV1 support\n");
819 				flags = true;
820 			}
821 			if ((encoderFlags & ATOM_DEVICE_CV_SUPPORT) != 0) {
822 				ERROR("   * device CV support\n");
823 				flags = true;
824 			}
825 			if ((encoderFlags & ATOM_DEVICE_DFP1_SUPPORT) != 0) {
826 				ERROR("   * device DFP1 support\n");
827 				flags = true;
828 			}
829 			if ((encoderFlags & ATOM_DEVICE_DFP2_SUPPORT) != 0) {
830 				ERROR("   * device DFP2 support\n");
831 				flags = true;
832 			}
833 			if ((encoderFlags & ATOM_DEVICE_DFP3_SUPPORT) != 0) {
834 				ERROR("   * device DFP3 support\n");
835 				flags = true;
836 			}
837 			if ((encoderFlags & ATOM_DEVICE_DFP4_SUPPORT) != 0) {
838 				ERROR("   * device DFP4 support\n");
839 				flags = true;
840 			}
841 			if ((encoderFlags & ATOM_DEVICE_DFP5_SUPPORT) != 0) {
842 				ERROR("   * device DFP5 support\n");
843 				flags = true;
844 			}
845 			if ((encoderFlags & ATOM_DEVICE_DFP6_SUPPORT) != 0) {
846 				ERROR("   * device DFP6 support\n");
847 				flags = true;
848 			}
849 			if (flags == false)
850 				ERROR("   * no known flags\n");
851 		}
852 	}
853 	ERROR("==========================================\n");
854 }
855