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