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