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