2 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Michael Lotz, mmlr@mlotz.ch
8 * Alexander von Gluck IV, kallisti5@unixzen.com
18 #include <KernelExport.h>
20 #include "accelerant.h"
21 #include "accelerant_protos.h"
22 #include "FlexibleDisplayInterface.h"
23 #include "intel_extreme.h"
31 # define TRACE(x...) _sPrintf("intel_extreme: " x)
36 #define ERROR(x...) _sPrintf("intel_extreme: " x)
37 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
41 wait_for_set(addr_t address
, uint32 mask
, uint32 timeout
)
45 for(i
= 0; i
<= timeout
; i
+= interval
) {
47 if ((read32(address
) & mask
) != 0)
55 wait_for_clear(addr_t address
, uint32 mask
, uint32 timeout
)
59 for(i
= 0; i
<= timeout
; i
+= interval
) {
61 if ((read32(address
) & mask
) == 0)
68 Port::Port(port_index index
, const char* baseName
)
71 fEDIDState(B_NO_INIT
),
76 portID
[0] = 'A' + index
- INTEL_PORT_A
;
82 strlcat(buffer
, baseName
, sizeof(buffer
));
83 strlcat(buffer
, " ", sizeof(buffer
));
84 strlcat(buffer
, portID
, sizeof(buffer
));
85 fPortName
= strdup(buffer
);
98 if (fEDIDState
== B_NO_INIT
)
101 return fEDIDState
== B_OK
;
106 Port::SetPipe(Pipe
* pipe
)
111 ERROR("%s: Invalid pipe provided!\n", __func__
);
115 uint32 portRegister
= _PortRegister();
116 if (portRegister
== 0) {
117 ERROR("%s: Invalid PortRegister ((0x%" B_PRIx32
") for %s\n", __func__
,
118 portRegister
, PortName());
122 // TODO: UnAssignPipe? This likely needs reworked a little
124 ERROR("%s: Can't reassign display pipe (yet)\n", __func__
);
128 TRACE("%s: Assigning %s (0x%" B_PRIx32
") to pipe %s\n", __func__
,
129 PortName(), portRegister
, (pipe
->Index() == INTEL_PIPE_A
) ? "A" : "B");
131 uint32 portState
= read32(portRegister
);
133 if (gInfo
->shared_info
->pch_info
== INTEL_PCH_CPT
) {
134 portState
&= PORT_TRANS_SEL_MASK
;
135 if (pipe
->Index() == INTEL_PIPE_A
)
136 write32(portRegister
, portState
| PORT_TRANS_A_SEL_CPT
);
138 write32(portRegister
, portState
| PORT_TRANS_B_SEL_CPT
);
140 if (pipe
->Index() == INTEL_PIPE_A
)
141 write32(portRegister
, portState
& ~DISPLAY_MONITOR_PIPE_B
);
143 write32(portRegister
, portState
| DISPLAY_MONITOR_PIPE_B
);
151 // Disable display pipe until modesetting enables it
152 if (fPipe
->IsEnabled())
153 fPipe
->Enable(false);
155 read32(portRegister
);
162 Port::Power(bool enabled
)
164 fPipe
->Enable(enabled
);
171 Port::GetEDID(edid1_info
* edid
, bool forceRead
)
175 if (fEDIDState
== B_NO_INIT
|| forceRead
) {
176 TRACE("%s: trying to read EDID\n", PortName());
178 addr_t ddcRegister
= _DDCRegister();
179 if (ddcRegister
== 0) {
180 TRACE("%s: no DDC register found\n", PortName());
181 fEDIDState
= B_ERROR
;
185 TRACE("%s: using ddc @ 0x%" B_PRIxADDR
"\n", PortName(), ddcRegister
);
188 bus
.cookie
= (void*)ddcRegister
;
189 bus
.set_signals
= &_SetI2CSignals
;
190 bus
.get_signals
= &_GetI2CSignals
;
191 ddc2_init_timing(&bus
);
193 fEDIDState
= ddc2_read_edid1(&bus
, &fEDIDInfo
, NULL
, NULL
);
195 if (fEDIDState
== B_OK
) {
196 TRACE("%s: found EDID information!\n", PortName());
197 edid_dump(&fEDIDInfo
);
201 if (fEDIDState
!= B_OK
) {
202 TRACE("%s: no EDID information found.\n", PortName());
207 memcpy(edid
, &fEDIDInfo
, sizeof(edid1_info
));
214 Port::GetPLLLimits(pll_limits
& limits
)
221 Port::_GetI2CSignals(void* cookie
, int* _clock
, int* _data
)
223 addr_t ioRegister
= (addr_t
)cookie
;
224 uint32 value
= read32(ioRegister
);
226 *_clock
= (value
& I2C_CLOCK_VALUE_IN
) != 0;
227 *_data
= (value
& I2C_DATA_VALUE_IN
) != 0;
234 Port::_SetI2CSignals(void* cookie
, int clock
, int data
)
236 addr_t ioRegister
= (addr_t
)cookie
;
239 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_83x
)) {
240 // on these chips, the reserved values are fixed
243 // on all others, we have to preserve them manually
244 value
= read32(ioRegister
) & I2C_RESERVED
;
248 value
|= I2C_DATA_DIRECTION_MASK
;
250 value
|= I2C_DATA_DIRECTION_MASK
| I2C_DATA_DIRECTION_OUT
251 | I2C_DATA_VALUE_MASK
;
255 value
|= I2C_CLOCK_DIRECTION_MASK
;
257 value
|= I2C_CLOCK_DIRECTION_MASK
| I2C_CLOCK_DIRECTION_OUT
258 | I2C_CLOCK_VALUE_MASK
;
261 write32(ioRegister
, value
);
263 // make sure the PCI bus has flushed the write
269 // #pragma mark - Analog Port
272 AnalogPort::AnalogPort()
274 Port(INTEL_PORT_A
, "Analog")
280 AnalogPort::IsConnected()
282 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
289 AnalogPort::_DDCRegister()
292 return INTEL_I2C_IO_A
;
297 AnalogPort::_PortRegister()
300 return INTEL_ANALOG_PORT
;
305 AnalogPort::SetDisplayMode(display_mode
* target
, uint32 colorMode
)
307 TRACE("%s: %s %dx%d\n", __func__
, PortName(), target
->virtual_width
,
308 target
->virtual_height
);
311 ERROR("%s: Setting display mode without assigned pipe!\n", __func__
);
315 // Train FDI if it exists
316 FDILink
* link
= fPipe
->FDI();
320 pll_divisors divisors
;
321 compute_pll_divisors(target
, &divisors
, false);
323 uint32 extraPLLFlags
= 0;
324 if (gInfo
->shared_info
->device_type
.Generation() >= 3)
325 extraPLLFlags
|= DISPLAY_PLL_MODE_NORMAL
;
327 // Program general pipe config
328 fPipe
->Configure(target
);
330 // Program pipe PLL's
331 fPipe
->ConfigureClocks(divisors
, target
->timing
.pixel_clock
, extraPLLFlags
);
333 write32(_PortRegister(), (read32(_PortRegister())
334 & ~(DISPLAY_MONITOR_POLARITY_MASK
| DISPLAY_MONITOR_VGA_POLARITY
))
335 | ((target
->timing
.flags
& B_POSITIVE_HSYNC
) != 0
336 ? DISPLAY_MONITOR_POSITIVE_HSYNC
: 0)
337 | ((target
->timing
.flags
& B_POSITIVE_VSYNC
) != 0
338 ? DISPLAY_MONITOR_POSITIVE_VSYNC
: 0));
340 // Program target display mode
341 fPipe
->ConfigureTimings(target
);
343 // Set fCurrentMode to our set display mode
344 memcpy(&fCurrentMode
, target
, sizeof(display_mode
));
350 // #pragma mark - LVDS Panel
355 Port(INTEL_PORT_C
, "LVDS")
357 // Always unlock LVDS port as soon as we start messing with it.
358 uint32 panelControl
= INTEL_PANEL_CONTROL
;
359 if (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
) {
360 // FIXME writing there results in black screen on SandyBridge
362 // panelControl = PCH_PANEL_CONTROL;
364 write32(panelControl
, read32(panelControl
) | PANEL_REGISTER_UNLOCK
);
369 LVDSPort::PipePreference()
371 // TODO: Technically INTEL_PIPE_B is only required on < gen 4
372 // otherwise we can use INTEL_PIPE_ANY, however it seems to break
373 // modesetting atm. (likely due to a bug on our end)
374 //if (gInfo->shared_info->device_type.Generation() < 4)
375 // return INTEL_PIPE_B;
382 LVDSPort::IsConnected()
384 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
387 if (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
) {
388 uint32 registerValue
= read32(_PortRegister());
389 // there's a detection bit we can use
390 if ((registerValue
& PCH_LVDS_DETECTED
) == 0) {
391 TRACE("LVDS: Not detected\n");
394 // TODO: Skip if eDP support
395 } else if (gInfo
->shared_info
->device_type
.Generation() <= 4) {
396 // Older generations don't have LVDS detection. If not mobile skip.
397 if (!gInfo
->shared_info
->device_type
.IsMobile()) {
398 TRACE("LVDS: Skipping LVDS detection due to gen and not mobile\n");
401 // If mobile, try to grab EDID
402 // Linux seems to look at lid status for LVDS port detection
403 // If we don't get EDID, we can use vbios native mode or vesa?
406 if (gInfo
->shared_info
->got_vbt
) {
407 // TODO: Fake EDID from vbios native mode?
408 // I feel like this would be more accurate
411 if (gInfo
->shared_info
->has_vesa_edid_info
) {
412 TRACE("LVDS: Using VESA edid info\n");
413 memcpy(&fEDIDInfo
, &gInfo
->shared_info
->vesa_edid_info
,
418 TRACE("LVDS: Couldn't find any valid EDID!\n");
424 // Try getting EDID, as the LVDS port doesn't overlap with anything else,
425 // we don't run the risk of getting someone else's data.
431 LVDSPort::_DDCRegister()
434 return INTEL_I2C_IO_C
;
439 LVDSPort::_PortRegister()
442 return INTEL_DIGITAL_LVDS_PORT
;
447 LVDSPort::SetDisplayMode(display_mode
* target
, uint32 colorMode
)
450 if (target
== NULL
) {
451 ERROR("%s: Invalid target mode passed!\n", __func__
);
455 TRACE("%s: %s-%d %dx%d\n", __func__
, PortName(), PortIndex(),
456 target
->virtual_width
, target
->virtual_height
);
459 ERROR("%s: Setting display mode without assigned pipe!\n", __func__
);
463 addr_t panelControl
= INTEL_PANEL_CONTROL
;
464 addr_t panelStatus
= INTEL_PANEL_STATUS
;
465 if (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
) {
466 panelControl
= PCH_PANEL_CONTROL
;
467 panelStatus
= PCH_PANEL_STATUS
;
471 write32(panelControl
, read32(panelControl
) & ~PANEL_CONTROL_POWER_TARGET_ON
);
472 read32(panelControl
);
474 if (!wait_for_clear(panelStatus
, PANEL_STATUS_POWER_ON
, 1000))
475 ERROR("%s: %s didn't power off within 1000ms!\n", __func__
, PortName());
477 // Train FDI if it exists
478 FDILink
* link
= fPipe
->FDI();
483 // Disable PanelFitter for now
484 addr_t panelFitterControl
= PCH_PANEL_FITTER_BASE_REGISTER
485 + PCH_PANEL_FITTER_CONTROL
;
486 if (fPipe
->Index() == INTEL_PIPE_B
)
487 panelFitterControl
+= PCH_PANEL_FITTER_PIPE_OFFSET
;
488 write32(panelFitterControl
, (read32(panelFitterControl
) & ~PANEL_FITTER_ENABLED
));
489 read32(panelFitterControl
);
492 // For LVDS panels, we actually always set the native mode in hardware
493 // Then we use the panel fitter to scale the picture to that.
494 display_mode hardwareTarget
;
495 bool needsScaling
= false;
496 // Try to get the panel preferred screen mode from EDID info
498 if (gInfo
->shared_info
->got_vbt
) {
499 // Set vbios hardware panel mode as base
500 memcpy(&hardwareTarget
, &gInfo
->shared_info
->panel_mode
,
501 sizeof(display_mode
));
502 hardwareTarget
.space
= target
->space
;
504 if ((hardwareTarget
.virtual_width
<= target
->virtual_width
505 && hardwareTarget
.virtual_height
<= target
->virtual_height
506 && hardwareTarget
.space
<= target
->space
)
507 || intel_propose_display_mode(&hardwareTarget
, target
, target
)) {
508 hardwareTarget
= *target
;
512 TRACE("%s: hardware mode will actually be %dx%d (%s)\n", __func__
,
513 hardwareTarget
.virtual_width
, hardwareTarget
.virtual_height
,
514 needsScaling
? "scaled" : "unscaled");
516 // We don't have EDID data, try to set the requested mode directly
517 hardwareTarget
= *target
;
520 pll_divisors divisors
;
522 compute_pll_divisors(&hardwareTarget
, &divisors
, true);
524 compute_pll_divisors(target
, &divisors
, true);
526 uint32 lvds
= read32(_PortRegister())
527 | LVDS_PORT_EN
| LVDS_A0A2_CLKA_POWER_UP
;
529 if (gInfo
->shared_info
->device_type
.Generation() == 4) {
530 // LVDS_A3_POWER_UP == 24bpp
532 if ((lvds
& LVDS_A3_POWER_MASK
) != LVDS_A3_POWER_UP
)
533 lvds
|= LVDS_18BIT_DITHER
;
536 // LVDS on PCH needs set before display enable
537 if (gInfo
->shared_info
->pch_info
== INTEL_PCH_CPT
) {
538 lvds
&= PORT_TRANS_SEL_MASK
;
539 if (fPipe
->Index() == INTEL_PIPE_A
)
540 lvds
|= PORT_TRANS_A_SEL_CPT
;
542 lvds
|= PORT_TRANS_B_SEL_CPT
;
545 // Set the B0-B3 data pairs corresponding to whether we're going to
546 // set the DPLLs for dual-channel mode or not.
547 if (divisors
.p2
== 5 || divisors
.p2
== 7) {
548 TRACE("LVDS: dual channel\n");
549 lvds
|= LVDS_B0B3_POWER_UP
| LVDS_CLKB_POWER_UP
;
551 TRACE("LVDS: single channel\n");
552 lvds
&= ~(LVDS_B0B3_POWER_UP
| LVDS_CLKB_POWER_UP
);
555 // LVDS port control moves polarity bits because Intel hates you.
556 // Set LVDS sync polarity
557 lvds
&= ~(LVDS_HSYNC_POLARITY
| LVDS_VSYNC_POLARITY
);
559 // set on - polarity.
560 if ((target
->timing
.flags
& B_POSITIVE_HSYNC
) == 0)
561 lvds
|= LVDS_HSYNC_POLARITY
;
562 if ((target
->timing
.flags
& B_POSITIVE_VSYNC
) == 0)
563 lvds
|= LVDS_VSYNC_POLARITY
;
565 TRACE("%s: LVDS Write: 0x%" B_PRIx32
"\n", __func__
, lvds
);
566 write32(_PortRegister(), lvds
);
567 read32(_PortRegister());
569 uint32 extraPLLFlags
= 0;
571 // DPLL mode LVDS for i915+
572 if (gInfo
->shared_info
->device_type
.Generation() >= 3)
573 extraPLLFlags
|= DISPLAY_PLL_MODE_LVDS
;
575 // Program general pipe config
576 fPipe
->Configure(target
);
578 // Program pipe PLL's (pixel_clock is *always* the hardware pixel clock)
579 fPipe
->ConfigureClocks(divisors
, hardwareTarget
.timing
.pixel_clock
,
582 // Disable panel fitting, but enable 8 to 6-bit dithering
583 write32(INTEL_PANEL_FIT_CONTROL
, 0x4);
584 // TODO: do not do this if the connected panel is 24-bit
585 // (I don't know how to detect that)
588 write32(panelControl
, read32(panelControl
) | PANEL_CONTROL_POWER_TARGET_ON
);
589 read32(panelControl
);
591 if (!wait_for_set(panelStatus
, PANEL_STATUS_POWER_ON
, 1000))
592 ERROR("%s: %s didn't power on within 1000ms!\n", __func__
, PortName());
594 // Program target display mode
595 fPipe
->ConfigureTimings(target
);
598 // update timing parameters
600 // TODO: Alternatively, it should be possible to use the panel
601 // fitter and scale the picture.
603 // TODO: Perform some sanity check, for example if the target is
604 // wider than the hardware mode we end up with negative borders and
606 uint32 borderWidth
= hardwareTarget
.timing
.h_display
607 - target
->timing
.h_display
;
609 uint32 syncWidth
= hardwareTarget
.timing
.h_sync_end
610 - hardwareTarget
.timing
.h_sync_start
;
612 uint32 syncCenter
= target
->timing
.h_display
613 + (hardwareTarget
.timing
.h_total
614 - target
->timing
.h_display
) / 2;
616 write32(INTEL_DISPLAY_B_HTOTAL
,
617 ((uint32
)(hardwareTarget
.timing
.h_total
- 1) << 16)
618 | ((uint32
)target
->timing
.h_display
- 1));
619 write32(INTEL_DISPLAY_B_HBLANK
,
620 ((uint32
)(hardwareTarget
.timing
.h_total
- borderWidth
/ 2 - 1)
622 | ((uint32
)target
->timing
.h_display
+ borderWidth
/ 2 - 1));
623 write32(INTEL_DISPLAY_B_HSYNC
,
624 ((uint32
)(syncCenter
+ syncWidth
/ 2 - 1) << 16)
625 | ((uint32
)syncCenter
- syncWidth
/ 2 - 1));
627 uint32 borderHeight
= hardwareTarget
.timing
.v_display
628 - target
->timing
.v_display
;
630 uint32 syncHeight
= hardwareTarget
.timing
.v_sync_end
631 - hardwareTarget
.timing
.v_sync_start
;
633 syncCenter
= target
->timing
.v_display
634 + (hardwareTarget
.timing
.v_total
635 - target
->timing
.v_display
) / 2;
637 write32(INTEL_DISPLAY_B_VTOTAL
,
638 ((uint32
)(hardwareTarget
.timing
.v_total
- 1) << 16)
639 | ((uint32
)target
->timing
.v_display
- 1));
640 write32(INTEL_DISPLAY_B_VBLANK
,
641 ((uint32
)(hardwareTarget
.timing
.v_total
- borderHeight
/ 2 - 1)
643 | ((uint32
)target
->timing
.v_display
644 + borderHeight
/ 2 - 1));
645 write32(INTEL_DISPLAY_B_VSYNC
,
646 ((uint32
)(syncCenter
+ syncHeight
/ 2 - 1) << 16)
647 | ((uint32
)syncCenter
- syncHeight
/ 2 - 1));
649 // This is useful for debugging: it sets the border to red, so you
650 // can see what is border and what is porch (black area around the
652 // write32(0x61020, 0x00FF0000);
654 write32(INTEL_DISPLAY_B_HTOTAL
,
655 ((uint32
)(target
->timing
.h_total
- 1) << 16)
656 | ((uint32
)target
->timing
.h_display
- 1));
657 write32(INTEL_DISPLAY_B_HBLANK
,
658 ((uint32
)(target
->timing
.h_total
- 1) << 16)
659 | ((uint32
)target
->timing
.h_display
- 1));
660 write32(INTEL_DISPLAY_B_HSYNC
,
661 ((uint32
)(target
->timing
.h_sync_end
- 1) << 16)
662 | ((uint32
)target
->timing
.h_sync_start
- 1));
664 write32(INTEL_DISPLAY_B_VTOTAL
,
665 ((uint32
)(target
->timing
.v_total
- 1) << 16)
666 | ((uint32
)target
->timing
.v_display
- 1));
667 write32(INTEL_DISPLAY_B_VBLANK
,
668 ((uint32
)(target
->timing
.v_total
- 1) << 16)
669 | ((uint32
)target
->timing
.v_display
- 1));
670 write32(INTEL_DISPLAY_B_VSYNC
, (
671 (uint32
)(target
->timing
.v_sync_end
- 1) << 16)
672 | ((uint32
)target
->timing
.v_sync_start
- 1));
676 // Set fCurrentMode to our set display mode
677 memcpy(&fCurrentMode
, target
, sizeof(display_mode
));
683 // #pragma mark - DVI/SDVO/generic
686 DigitalPort::DigitalPort(port_index index
, const char* baseName
)
688 Port(index
, baseName
)
694 DigitalPort::IsConnected()
696 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
699 // As this port overlaps with pretty much everything, this must be called
700 // after having ruled out all other port types.
706 DigitalPort::_DDCRegister()
708 //TODO: IS BROXTON, B = B, C = C, D = NIL
709 switch (PortIndex()) {
711 return INTEL_I2C_IO_E
;
713 return INTEL_I2C_IO_D
;
715 return INTEL_I2C_IO_F
;
725 DigitalPort::_PortRegister()
727 switch (PortIndex()) {
729 return INTEL_DIGITAL_PORT_A
;
731 return INTEL_DIGITAL_PORT_B
;
733 return INTEL_DIGITAL_PORT_C
;
742 DigitalPort::SetDisplayMode(display_mode
* target
, uint32 colorMode
)
744 TRACE("%s: %s %dx%d\n", __func__
, PortName(), target
->virtual_width
,
745 target
->virtual_height
);
748 ERROR("%s: Setting display mode without assigned pipe!\n", __func__
);
752 // Train FDI if it exists
753 FDILink
* link
= fPipe
->FDI();
757 pll_divisors divisors
;
758 compute_pll_divisors(target
, &divisors
, false);
760 uint32 extraPLLFlags
= 0;
761 if (gInfo
->shared_info
->device_type
.Generation() >= 3)
762 extraPLLFlags
|= DISPLAY_PLL_MODE_NORMAL
;
764 // Program general pipe config
765 fPipe
->Configure(target
);
767 // Program pipe PLL's
768 fPipe
->ConfigureClocks(divisors
, target
->timing
.pixel_clock
, extraPLLFlags
);
770 // Program target display mode
771 fPipe
->ConfigureTimings(target
);
773 // Set fCurrentMode to our set display mode
774 memcpy(&fCurrentMode
, target
, sizeof(display_mode
));
780 // #pragma mark - LVDS Panel
781 // #pragma mark - HDMI
784 HDMIPort::HDMIPort(port_index index
)
786 DigitalPort(index
, "HDMI")
792 HDMIPort::IsConnected()
794 if (!gInfo
->shared_info
->device_type
.SupportsHDMI())
797 addr_t portRegister
= _PortRegister();
798 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
801 if (portRegister
== 0)
804 bool hasPCH
= (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
);
805 if (!hasPCH
&& PortIndex() == INTEL_PORT_C
) {
806 // there's no detection bit on this port
807 } else if ((read32(portRegister
) & DISPLAY_MONITOR_PORT_DETECTED
) == 0)
815 HDMIPort::_PortRegister()
817 // on PCH there's an additional port sandwiched in
818 bool hasPCH
= (gInfo
->shared_info
->pch_info
!= INTEL_PCH_NONE
);
819 bool fourthGen
= gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
);
821 switch (PortIndex()) {
824 return GEN4_HDMI_PORT_B
;
825 return hasPCH
? PCH_HDMI_PORT_B
: INTEL_HDMI_PORT_B
;
828 return GEN4_HDMI_PORT_C
;
829 return hasPCH
? PCH_HDMI_PORT_C
: INTEL_HDMI_PORT_C
;
831 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_CHV
))
832 return CHV_HDMI_PORT_D
;
833 return hasPCH
? PCH_HDMI_PORT_D
: 0;
842 // #pragma mark - DisplayPort
845 DisplayPort::DisplayPort(port_index index
, const char* baseName
)
847 Port(index
, baseName
)
853 DisplayPort::IsConnected()
855 addr_t portRegister
= _PortRegister();
857 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
860 if (portRegister
== 0)
863 if ((read32(portRegister
) & DISPLAY_MONITOR_PORT_DETECTED
) == 0) {
864 TRACE("%s: %s link not detected\n", __func__
, PortName());
873 DisplayPort::_DDCRegister()
875 // TODO: Do VLV + CHV use the VLV_DP_AUX_CTL_B + VLV_DP_AUX_CTL_C?
876 switch (PortIndex()) {
878 return INTEL_DP_AUX_CTL_A
;
880 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
881 return VLV_DP_AUX_CTL_B
;
882 return INTEL_DP_AUX_CTL_B
;
884 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
885 return VLV_DP_AUX_CTL_C
;
886 return INTEL_DP_AUX_CTL_C
;
888 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_CHV
))
889 return CHV_DP_AUX_CTL_D
;
890 else if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
892 return INTEL_DP_AUX_CTL_D
;
902 DisplayPort::_PortRegister()
904 // There are 6000 lines of intel linux code probing DP registers
905 // to properly detect DP vs eDP to then in-turn properly figure out
906 // what is DP and what is HDMI. It only takes 3 lines to
907 // ignore DisplayPort on ValleyView / CherryView
909 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
)
910 || gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_CHV
)) {
911 ERROR("TODO: DisplayPort on ValleyView / CherryView");
915 // Intel, are humans even involved anymore?
916 // This is a lot more complex than this code makes it look. (see defines)
917 // INTEL_DISPLAY_PORT_X moves around a lot based on PCH
918 // except on ValleyView and CherryView.
919 switch (PortIndex()) {
921 return INTEL_DISPLAY_PORT_A
;
923 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
924 return VLV_DISPLAY_PORT_B
;
925 return INTEL_DISPLAY_PORT_B
;
927 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
928 return VLV_DISPLAY_PORT_C
;
929 return INTEL_DISPLAY_PORT_C
;
931 if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_CHV
))
932 return CHV_DISPLAY_PORT_D
;
933 else if (gInfo
->shared_info
->device_type
.InGroup(INTEL_GROUP_VLV
))
935 return INTEL_DISPLAY_PORT_D
;
945 DisplayPort::SetDisplayMode(display_mode
* target
, uint32 colorMode
)
947 TRACE("%s: %s %dx%d\n", __func__
, PortName(), target
->virtual_width
,
948 target
->virtual_height
);
950 ERROR("TODO: DisplayPort\n");
955 // #pragma mark - Embedded DisplayPort
958 EmbeddedDisplayPort::EmbeddedDisplayPort()
960 DisplayPort(INTEL_PORT_A
, "Embedded DisplayPort")
966 EmbeddedDisplayPort::IsConnected()
968 addr_t portRegister
= _PortRegister();
970 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
973 if (!gInfo
->shared_info
->device_type
.IsMobile()) {
974 TRACE("%s: skipping eDP on non-mobile GPU\n", __func__
);
978 if ((read32(portRegister
) & DISPLAY_MONITOR_PORT_DETECTED
) == 0) {
979 TRACE("%s: %s link not detected\n", __func__
, PortName());
985 // If eDP has EDID, awesome. We use it.
986 // No EDID? The modesetting code falls back to VBIOS panel_mode
991 // #pragma mark - Digital Display Port
994 DigitalDisplayInterface::DigitalDisplayInterface(port_index index
,
995 const char* baseName
)
997 Port(index
, baseName
)
999 // As of Haswell, Intel decided to change eDP ports to a "DDI" bus...
1000 // on a dare because the hardware engineers were drunk one night.
1005 DigitalDisplayInterface::_PortRegister()
1007 // TODO: Linux does a DDI_BUF_CTL(INTEL_PORT_A) which is cleaner
1008 // (but we have to ensure the offsets + region base is correct)
1009 switch (PortIndex()) {
1011 return DDI_BUF_CTL_A
;
1013 return DDI_BUF_CTL_B
;
1015 return DDI_BUF_CTL_C
;
1017 return DDI_BUF_CTL_D
;
1019 return DDI_BUF_CTL_E
;
1028 DigitalDisplayInterface::_DDCRegister()
1030 // TODO: No idea, does DDI have DDC?
1036 DigitalDisplayInterface::Power(bool enabled
)
1038 TRACE("%s: %s DDI enabled: %s\n", __func__
, PortName(),
1039 enabled
? "true" : "false");
1041 fPipe
->Enable(enabled
);
1043 addr_t portRegister
= _PortRegister();
1044 uint32 state
= read32(portRegister
);
1045 write32(portRegister
,
1046 enabled
? (state
| DDI_BUF_CTL_ENABLE
) : (state
& ~DDI_BUF_CTL_ENABLE
));
1047 read32(portRegister
);
1054 DigitalDisplayInterface::IsConnected()
1056 addr_t portRegister
= _PortRegister();
1058 TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR
"\n", __func__
, PortName(),
1061 if (portRegister
== 0)
1064 if ((read32(portRegister
) & DDI_INIT_DISPLAY_DETECTED
) == 0) {
1065 TRACE("%s: %s link not detected\n", __func__
, PortName());
1069 // Probe a little port info.
1070 if ((read32(DDI_BUF_CTL_A
) & DDI_A_4_LANES
) != 0) {
1071 switch (PortIndex()) {
1083 switch (PortIndex()) {
1096 TRACE("%s: %s Maximum Lanes: %" B_PRId8
"\n", __func__
,
1097 PortName(), fMaxLanes
);
1106 DigitalDisplayInterface::SetDisplayMode(display_mode
* target
, uint32 colorMode
)
1108 TRACE("%s: %s %dx%d\n", __func__
, PortName(), target
->virtual_width
,
1109 target
->virtual_height
);
1111 if (fPipe
== NULL
) {
1112 ERROR("%s: Setting display mode without assigned pipe!\n", __func__
);
1116 // Train FDI if it exists
1117 FDILink
* link
= fPipe
->FDI();
1119 link
->Train(target
);
1121 pll_divisors divisors
;
1122 compute_pll_divisors(target
, &divisors
, false);
1124 uint32 extraPLLFlags
= 0;
1125 if (gInfo
->shared_info
->device_type
.Generation() >= 3)
1126 extraPLLFlags
|= DISPLAY_PLL_MODE_NORMAL
;
1128 // Program general pipe config
1129 fPipe
->Configure(target
);
1131 // Program pipe PLL's
1132 fPipe
->ConfigureClocks(divisors
, target
->timing
.pixel_clock
, extraPLLFlags
);
1134 // Program target display mode
1135 fPipe
->ConfigureTimings(target
);
1137 // Set fCurrentMode to our set display mode
1138 memcpy(&fCurrentMode
, target
, sizeof(display_mode
));