diff --git a/headers/private/graphics/common/dp_raw.h b/headers/private/graphics/common/dp_raw.h index bad7fea0ca..081147921a 100644 --- a/headers/private/graphics/common/dp_raw.h +++ b/headers/private/graphics/common/dp_raw.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Haiku, Inc. All rights reserved. + * Copyright 2012-2016 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 7145aebf4e..fe4b9b20ef 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -1,9 +1,10 @@ /* - * Copyright 2006-2014, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2016, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de + * Alexander von Gluck, kallisti5@unixzen.com */ #ifndef INTEL_EXTREME_H #define INTEL_EXTREME_H @@ -20,50 +21,61 @@ #define VENDOR_ID_INTEL 0x8086 -#define INTEL_TYPE_FAMILY_MASK 0x000f0000 -#define INTEL_TYPE_GROUP_MASK 0x000ffff0 -#define INTEL_TYPE_MODEL_MASK 0x000fffff +#define INTEL_FAMILY_MASK 0x00ff0000 +#define INTEL_GROUP_MASK 0x00fffff0 +#define INTEL_MODEL_MASK 0x00ffffff +#define INTEL_TYPE_MASK 0x0000000f // families -#define INTEL_TYPE_7xx 0x00010000 -#define INTEL_TYPE_8xx 0x00020000 -#define INTEL_TYPE_9xx 0x00040000 +#define INTEL_FAMILY_7xx 0x00010000 // First Gen +#define INTEL_FAMILY_8xx 0x00020000 // Second Gen +#define INTEL_FAMILY_9xx 0x00040000 // Third Gen + +#define INTEL_FAMILY_SER5 0x00080000 // Intel5 Series +#define INTEL_FAMILY_POVR 0x00100000 // PowerVR (uugh) +#define INTEL_FAMILY_SOC0 0x00200000 // Atom SOC // groups -#define INTEL_TYPE_83x (INTEL_TYPE_8xx | 0x0010) -#define INTEL_TYPE_85x (INTEL_TYPE_8xx | 0x0020) -#define INTEL_TYPE_91x (INTEL_TYPE_9xx | 0x0040) -#define INTEL_TYPE_94x (INTEL_TYPE_9xx | 0x0080) -#define INTEL_TYPE_96x (INTEL_TYPE_9xx | 0x0100) -#define INTEL_TYPE_Gxx (INTEL_TYPE_9xx | 0x0200) -#define INTEL_TYPE_G4x (INTEL_TYPE_9xx | 0x0400) -#define INTEL_TYPE_IGD (INTEL_TYPE_9xx | 0x0800) -#define INTEL_TYPE_ILK (INTEL_TYPE_9xx | 0x1000) -#define INTEL_TYPE_SNB (INTEL_TYPE_9xx | 0x2000) -#define INTEL_TYPE_IVB (INTEL_TYPE_9xx | 0x4000) -#define INTEL_TYPE_VLV (INTEL_TYPE_9xx | 0x8000) +#define INTEL_GROUP_83x (INTEL_FAMILY_8xx | 0x0010) +#define INTEL_GROUP_85x (INTEL_FAMILY_8xx | 0x0020) +#define INTEL_GROUP_91x (INTEL_FAMILY_9xx | 0x0010) +#define INTEL_GROUP_94x (INTEL_FAMILY_9xx | 0x0020) +#define INTEL_GROUP_96x (INTEL_FAMILY_9xx | 0x0040) +#define INTEL_GROUP_Gxx (INTEL_FAMILY_9xx | 0x0080) +#define INTEL_GROUP_G4x (INTEL_FAMILY_9xx | 0x0100) +#define INTEL_GROUP_PIN (INTEL_FAMILY_9xx | 0x0200) // PineView +#define INTEL_GROUP_ILK (INTEL_FAMILY_SER5 | 0x0010) // IronLake +#define INTEL_GROUP_SNB (INTEL_FAMILY_SER5 | 0x0020) // SandyBridge +#define INTEL_GROUP_IVB (INTEL_FAMILY_SER5 | 0x0040) // IvyBridge +#define INTEL_GROUP_HAS (INTEL_FAMILY_SER5 | 0x0080) // Haswell +#define INTEL_GROUP_SLT (INTEL_FAMILY_POVR | 0x0010) // Saltwell +#define INTEL_GROUP_FSM (INTEL_FAMILY_POVR | 0x0020) // Fu.Silvermont +#define INTEL_GROUP_VLV (INTEL_FAMILY_SOC0 | 0x0010) // ValleyView +#define INTEL_GROUP_CHV (INTEL_FAMILY_SOC0 | 0x0020) // CherryView +#define INTEL_GROUP_BXT (INTEL_FAMILY_SOC0 | 0x0040) // Broxton // models -#define INTEL_TYPE_SERVER 0x0004 -#define INTEL_TYPE_MOBILE 0x0008 -#define INTEL_TYPE_915 (INTEL_TYPE_91x) -#define INTEL_TYPE_915M (INTEL_TYPE_91x | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_945 (INTEL_TYPE_94x) -#define INTEL_TYPE_945M (INTEL_TYPE_94x | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_965 (INTEL_TYPE_96x) -#define INTEL_TYPE_965M (INTEL_TYPE_96x | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_G33 (INTEL_TYPE_Gxx) -#define INTEL_TYPE_G45 (INTEL_TYPE_G4x) -#define INTEL_TYPE_GM45 (INTEL_TYPE_G4x | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_IGDG (INTEL_TYPE_IGD) -#define INTEL_TYPE_IGDGM (INTEL_TYPE_IGD | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_ILKG (INTEL_TYPE_ILK) -#define INTEL_TYPE_ILKGM (INTEL_TYPE_ILK | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_SNBG (INTEL_TYPE_SNB) -#define INTEL_TYPE_SNBGM (INTEL_TYPE_SNB | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_SNBGS (INTEL_TYPE_SNB | INTEL_TYPE_SERVER) -#define INTEL_TYPE_IVBG (INTEL_TYPE_IVB) -#define INTEL_TYPE_IVBGM (INTEL_TYPE_IVB | INTEL_TYPE_MOBILE) -#define INTEL_TYPE_IVBGS (INTEL_TYPE_IVB | INTEL_TYPE_SERVER) -#define INTEL_TYPE_VLVG (INTEL_TYPE_VLV) -#define INTEL_TYPE_VLVGM (INTEL_TYPE_VLV | INTEL_TYPE_MOBILE) +#define INTEL_TYPE_SERVER 0x0004 +#define INTEL_TYPE_MOBILE 0x0008 +#define INTEL_MODEL_915 (INTEL_GROUP_91x) +#define INTEL_MODEL_915M (INTEL_GROUP_91x | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_945 (INTEL_GROUP_94x) +#define INTEL_MODEL_945M (INTEL_GROUP_94x | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_965 (INTEL_GROUP_96x) +#define INTEL_MODEL_965M (INTEL_GROUP_96x | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_G33 (INTEL_GROUP_Gxx) +#define INTEL_MODEL_G45 (INTEL_GROUP_G4x) +#define INTEL_MODEL_GM45 (INTEL_GROUP_G4x | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_PINE (INTEL_GROUP_PIN) +#define INTEL_MODEL_PINEM (INTEL_GROUP_PIN | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_ILKG (INTEL_GROUP_ILK) +#define INTEL_MODEL_ILKGM (INTEL_GROUP_ILK | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_SNBG (INTEL_GROUP_SNB) +#define INTEL_MODEL_SNBGM (INTEL_GROUP_SNB | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_SNBGS (INTEL_GROUP_SNB | INTEL_TYPE_SERVER) +#define INTEL_MODEL_IVBG (INTEL_GROUP_IVB) +#define INTEL_MODEL_IVBGM (INTEL_GROUP_IVB | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_IVBGS (INTEL_GROUP_IVB | INTEL_TYPE_SERVER) +#define INTEL_MODEL_HAS (INTEL_GROUP_HAS) +#define INTEL_MODEL_HASM (INTEL_GROUP_HAS | INTEL_TYPE_MOBILE) +#define INTEL_MODEL_VLV (INTEL_GROUP_VLV) +#define INTEL_MODEL_VLVM (INTEL_GROUP_VLV | INTEL_TYPE_MOBILE) // ValleyView MMIO offset #define VLV_DISPLAY_BASE 0x180000 @@ -94,10 +106,10 @@ #define ICH_SHARED_REGISTER_BASE 0x00000 #define ICH_PORT_REGISTER_BASE 0x60000 -// PCH - Platform Control Hub - Newer hardware moves from a MCH/ICH based setup -// to a PCH based one, that means anything that used to communicate via (G)MCH -// registers needs to use different ones on PCH based platforms (Ironlake and -// up, SandyBridge, etc.). +// PCH - Platform Control Hub - Some hardware moves from a MCH/ICH based +// setup to a PCH based one, that means anything that used to communicate via +// (G)MCH registers needs to use different ones on PCH based platforms +// (Ironlake, SandyBridge, IvyBridge, Some Haswell). #define PCH_NORTH_SHARED_REGISTER_BASE 0x40000 #define PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE 0x60000 #define PCH_NORTH_PLANE_CONTROL_REGISTER_BASE 0x70000 @@ -121,23 +133,65 @@ struct DeviceType { bool InFamily(uint32 family) const { - return (type & INTEL_TYPE_FAMILY_MASK) == family; + return (type & INTEL_FAMILY_MASK) == family; } bool InGroup(uint32 group) const { - return (type & INTEL_TYPE_GROUP_MASK) == group; + return (type & INTEL_GROUP_MASK) == group; } bool IsModel(uint32 model) const { - return (type & INTEL_TYPE_MODEL_MASK) == model; + return (type & INTEL_MODEL_MASK) == model; + } + + bool IsMobile() const + { + return (type & INTEL_TYPE_MASK) == INTEL_TYPE_MOBILE; + } + + bool SupportsHDMI() const + { + return InGroup(INTEL_GROUP_G4x) || InFamily(INTEL_FAMILY_SER5) + || InFamily(INTEL_FAMILY_SOC0); } bool HasPlatformControlHub() const { - return InGroup(INTEL_TYPE_ILK) || InGroup(INTEL_TYPE_SNB) - || InGroup(INTEL_TYPE_IVB) || InGroup(INTEL_TYPE_VLV); + return InFamily(INTEL_FAMILY_SER5); + } + + bool HasDDI() const + { + // Intel Digital Display Interface + return InGroup(INTEL_GROUP_HAS) || (Generation() >= 8); + } + + int Generation() const + { + if (InFamily(INTEL_FAMILY_7xx)) + return 1; + if (InFamily(INTEL_FAMILY_8xx)) + return 2; + if (InGroup(INTEL_GROUP_91x) || InGroup(INTEL_GROUP_94x) + || IsModel(INTEL_MODEL_G33) || InGroup(INTEL_GROUP_PIN)) + return 3; + if (InFamily(INTEL_FAMILY_9xx)) + return 4; + if (InGroup(INTEL_GROUP_ILK)) + return 5; + if (InGroup(INTEL_GROUP_SNB)) + return 6; + if (InFamily(INTEL_FAMILY_SER5) || InGroup(INTEL_GROUP_VLV)) + return 7; + if (InGroup(INTEL_GROUP_CHV)) + return 8; + if (InGroup(INTEL_GROUP_BXT)) + return 9; + + // Generation 0 means somethins is wrong :-) + return 0; } }; @@ -165,12 +219,12 @@ struct intel_shared_info { area_id mode_list_area; // area containing display mode list uint32 mode_count; - display_mode current_mode; + display_mode panel_mode; // VBIOS VBT panel mode uint32 bytes_per_row; uint32 bits_per_pixel; uint32 dpms_mode; - area_id registers_area; // area of memory mapped registers + area_id registers_area; // area of memory mapped registers uint32 register_blocks[REGISTER_BLOCK_COUNT]; uint8* status_page; @@ -182,6 +236,8 @@ struct intel_shared_info { addr_t frame_buffer; uint32 frame_buffer_offset; + uint32 fdi_link_frequency; // In Mhz + bool got_vbt; bool single_head_locked; @@ -215,6 +271,12 @@ struct intel_shared_info { bool has_vesa_edid_info; }; +enum pipe_index { + INTEL_PIPE_ANY, + INTEL_PIPE_A, + INTEL_PIPE_B +}; + //----------------- ioctl() interface ---------------- // magic code for ioctls @@ -254,7 +316,8 @@ struct intel_free_graphics_memory { // Register definitions, taken from X driver // PCI bridge memory management -#define INTEL_GRAPHICS_MEMORY_CONTROL 0x52 +#define INTEL_GRAPHICS_MEMORY_CONTROL 0x52 // i830+ + // GGC - (G)MCH Graphics Control Register #define MEMORY_CONTROL_ENABLED 0x0004 #define MEMORY_MASK 0x0001 @@ -288,6 +351,7 @@ struct intel_free_graphics_memory { #define G4X_STOLEN_MEMORY_352MB 0xd0 // SandyBridge (SNB) + #define SNB_GRAPHICS_MEMORY_CONTROL 0x50 #define SNB_STOLEN_MEMORY_MASK 0xf8 @@ -374,7 +438,7 @@ struct intel_free_graphics_memory { #define PCH_INTERRUPT_VBLANK_PIPEA_SNB (1 << 7) #define PCH_INTERRUPT_VBLANK_PIPEB_SNB (1 << 15) -// display ports +// graphics port control #define DISPLAY_MONITOR_PORT_ENABLED (1UL << 31) #define DISPLAY_MONITOR_PIPE_B (1UL << 30) #define DISPLAY_MONITOR_VGA_POLARITY (1UL << 15) @@ -386,23 +450,31 @@ struct intel_free_graphics_memory { #define DISPLAY_MONITOR_POLARITY_MASK (3UL << 3) #define DISPLAY_MONITOR_POSITIVE_HSYNC (1UL << 3) #define DISPLAY_MONITOR_POSITIVE_VSYNC (2UL << 3) +#define DISPLAY_MONITOR_PORT_DETECTED (1UL << 2) // TMDS/DisplayPort only + #define LVDS_POST2_RATE_SLOW 14 // PLL Divisors #define LVDS_POST2_RATE_FAST 7 -#define LVDS_CLKB_POWER_MASK (3 << 4) -#define LVDS_CLKB_POWER_UP (3 << 4) -#define LVDS_PORT_EN (1 << 31) -#define LVDS_A0A2_CLKA_POWER_UP (3 << 8) -#define LVDS_PIPEB_SELECT (1 << 30) -#define LVDS_B0B3PAIRS_POWER_UP (3 << 2) -#define LVDS_PLL_MODE_LVDS (2 << 26) -#define LVDS_18BIT_DITHER (1 << 25) +#define LVDS_B0B3_POWER_MASK (3UL << 2) +#define LVDS_B0B3_POWER_UP (3UL << 2) +#define LVDS_CLKB_POWER_MASK (3UL << 4) +#define LVDS_CLKB_POWER_UP (3UL << 4) +#define LVDS_A3_POWER_MASK (3UL << 6) +#define LVDS_A3_POWER_UP (3UL << 6) +#define LVDS_A0A2_CLKA_POWER_UP (3UL << 8) +#define LVDS_BORDER_ENABLE (1UL << 15) +#define LVDS_HSYNC_POLARITY (1UL << 20) +#define LVDS_VSYNC_POLARITY (1UL << 21) +#define LVDS_18BIT_DITHER (1UL << 25) +#define LVDS_PORT_EN (1UL << 31) + // PLL flags #define DISPLAY_PLL_ENABLED (1UL << 31) #define DISPLAY_PLL_2X_CLOCK (1UL << 30) #define DISPLAY_PLL_SYNC_LOCK_ENABLED (1UL << 29) #define DISPLAY_PLL_NO_VGA_CONTROL (1UL << 28) -#define DISPLAY_PLL_MODE_ANALOG (1UL << 26) +#define DISPLAY_PLL_MODE_NORMAL (1UL << 26) +#define DISPLAY_PLL_MODE_LVDS (2UL << 26) #define DISPLAY_PLL_DIVIDE_HIGH (1UL << 24) #define DISPLAY_PLL_DIVIDE_4X (1UL << 23) #define DISPLAY_PLL_POST1_DIVIDE_2 (1UL << 21) @@ -423,35 +495,122 @@ struct intel_free_graphics_memory { #define DISPLAY_PLL_PULSE_PHASE_SHIFT 9 // display -#define INTEL_DISPLAY_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_HSYNC (0x0008 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_VTOTAL (0x000c | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_VBLANK (0x0010 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_VSYNC (0x0014 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_HTOTAL (0x1000 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_HBLANK (0x1004 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_HSYNC (0x1008 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_VTOTAL (0x100c | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_VBLANK (0x1010 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_VSYNC (0x1014 | REGS_SOUTH_TRANSCODER_PORT) + +#define INTEL_DISPLAY_OFFSET 0x1000 + +#define INTEL_DISPLAY_A_HTOTAL (0x0000 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_A_HBLANK (0x0004 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_A_HSYNC (0x0008 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_A_VTOTAL (0x000c | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_A_VBLANK (0x0010 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_A_VSYNC (0x0014 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_HTOTAL (0x1000 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_HBLANK (0x1004 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_HSYNC (0x1008 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_VTOTAL (0x100c | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_VBLANK (0x1010 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_B_VSYNC (0x1014 | REGS_NORTH_PIPE_AND_PORT) #define INTEL_DISPLAY_A_IMAGE_SIZE (0x001c | REGS_NORTH_PIPE_AND_PORT) #define INTEL_DISPLAY_B_IMAGE_SIZE (0x101c | REGS_NORTH_PIPE_AND_PORT) -#define INTEL_DISPLAY_A_ANALOG_PORT (0x1100 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_A_DIGITAL_PORT (0x1120 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_B_DIGITAL_PORT (0x1140 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_C_DIGITAL (0x1160 | REGS_SOUTH_TRANSCODER_PORT) -#define INTEL_DISPLAY_LVDS_PORT (0x1180 | REGS_SOUTH_TRANSCODER_PORT) +// on PCH we also have to set the transcoder +#define INTEL_TRANSCODER_A_HTOTAL (0x0000 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_A_HBLANK (0x0004 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_A_HSYNC (0x0008 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_A_VTOTAL (0x000c | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_A_VBLANK (0x0010 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_A_VSYNC (0x0014 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_HTOTAL (0x1000 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_HBLANK (0x1004 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_HSYNC (0x1008 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_VTOTAL (0x100c | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_VBLANK (0x1010 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_VSYNC (0x1014 | REGS_SOUTH_TRANSCODER_PORT) + +#define INTEL_TRANSCODER_A_IMAGE_SIZE (0x001c | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_TRANSCODER_B_IMAGE_SIZE (0x101c | REGS_SOUTH_TRANSCODER_PORT) + +#define INTEL_ANALOG_PORT (0x1100 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DIGITAL_PORT_A (0x1120 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DIGITAL_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DIGITAL_PORT_C (0x1160 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DIGITAL_LVDS_PORT (0x1180 | REGS_SOUTH_TRANSCODER_PORT) + +#define INTEL_HDMI_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_HDMI_PORT_C (0x1160 | REGS_SOUTH_TRANSCODER_PORT) + +#define PCH_HDMI_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT) +#define PCH_HDMI_PORT_C (0x1150 | REGS_SOUTH_TRANSCODER_PORT) +#define PCH_HDMI_PORT_D (0x1160 | REGS_SOUTH_TRANSCODER_PORT) + +#define GEN4_HDMI_PORT_B (0x1140 | REGS_SOUTH_TRANSCODER_PORT) +#define GEN4_HDMI_PORT_C (0x1160 | REGS_SOUTH_TRANSCODER_PORT) +#define CHV_HDMI_PORT_D (0x116C | REGS_SOUTH_TRANSCODER_PORT) + +// DDI Buffer Control (This replaces DP on Haswell+) +#define DDI_BUF_CTL_A (0x4000 | REGS_NORTH_PIPE_AND_PORT) +#define DDI_BUF_CTL_B (0x4100 | REGS_NORTH_PIPE_AND_PORT) +#define DDI_BUF_CTL_ENABLE (1 << 31) +#define DDI_BUF_TRANS_SELECT(n) ((n) << 24) +#define DDI_BUF_EMP_MASK (0xf << 24) +#define DDI_BUF_PORT_REVERSAL (1 << 16) +#define DDI_BUF_IS_IDLE (1 << 7) +#define DDI_A_4_LANES (1 << 4) +#define DDI_PORT_WIDTH(width) (((width) - 1) << 1) +#define DDI_INIT_DISPLAY_DETECTED (1 << 0) + +// DP_A always @ 6xxxx, DP_B-DP_D move with PCH +#define INTEL_DISPLAY_PORT_A (0x4000 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DISPLAY_PORT_B (0x4100 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DISPLAY_PORT_C (0x4200 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DISPLAY_PORT_D (0x4300 | REGS_SOUTH_TRANSCODER_PORT) + +// Unless you're a damn Valley/CherryView unicorn :-( +#define VLV_DISPLAY_PORT_B (VLV_DISPLAY_BASE + 0x64100) +#define VLV_DISPLAY_PORT_C (VLV_DISPLAY_BASE + 0x64200) +#define CHV_DISPLAY_PORT_D (VLV_DISPLAY_BASE + 0x64300) + +// DP AUX channels +#define INTEL_DP_AUX_CTL_A (0x4010 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_DP_AUX_CTL_B (0x4110 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DP_AUX_CTL_C (0x4210 | REGS_SOUTH_TRANSCODER_PORT) +#define INTEL_DP_AUX_CTL_D (0x4310 | REGS_SOUTH_TRANSCODER_PORT) + +#define VLV_DP_AUX_CTL_B (VLV_DISPLAY_BASE + 0x64110) +#define VLV_DP_AUX_CTL_C (VLV_DISPLAY_BASE + 0x64210) +#define CHV_DP_AUX_CTL_D (VLV_DISPLAY_BASE + 0x64310) + +#define INTEL_DP_AUX_CTL_BUSY (1 << 31) +#define INTEL_DP_AUX_CTL_DONE (1 << 30) +#define INTEL_DP_AUX_CTL_INTERRUPT (1 << 29) +#define INTEL_DP_AUX_CTL_TIMEOUT_ERROR (1 << 28) +#define INTEL_DP_AUX_CTL_TIMEOUT_400us (0 << 26) +#define INTEL_DP_AUX_CTL_TIMEOUT_600us (1 << 26) +#define INTEL_DP_AUX_CTL_TIMEOUT_800us (2 << 26) +#define INTEL_DP_AUX_CTL_TIMEOUT_1600us (3 << 26) +#define INTEL_DP_AUX_CTL_TIMEOUT_MASK (3 << 26) +#define INTEL_DP_AUX_CTL_RECEIVE_ERROR (1 << 25) +#define INTEL_DP_AUX_CTL_MSG_SIZE_MASK (0x1f << 20) +#define INTEL_DP_AUX_CTL_MSG_SIZE_SHIFT 20 +#define INTEL_DP_AUX_CTL_PRECHARGE_2US_MASK (0xf << 16) +#define INTEL_DP_AUX_CTL_PRECHARGE_2US_SHIFT 16 +#define INTEL_DP_AUX_CTL_BIT_CLOCK_2X_MASK (0x7ff) +#define INTEL_DP_AUX_CTL_BIT_CLOCK_2X_SHIFT 0 +#define INTEL_DP_AUX_CTL_SYNC_PULSE_SKL(c) ((c) - 1) // planes -#define INTEL_DISPLAY_A_PIPE_CONTROL (0x0008 | REGS_NORTH_PLANE_CONTROL) -#define INTEL_DISPLAY_B_PIPE_CONTROL (0x1008 | REGS_NORTH_PLANE_CONTROL) -#define DISPLAY_PIPE_ENABLED (1UL << 31) +#define INTEL_PIPE_ENABLED (1UL << 31) +#define INTEL_PIPE_CONTROL 0x0008 +#define INTEL_PIPE_STATUS 0x0024 +#define INTEL_PLANE_OFFSET 0x1000 + +#define INTEL_DISPLAY_A_PIPE_CONTROL (0x0008 | REGS_NORTH_PLANE_CONTROL) +#define INTEL_DISPLAY_B_PIPE_CONTROL (0x1008 | REGS_NORTH_PLANE_CONTROL) #define INTEL_DISPLAY_A_PIPE_STATUS (0x0024 | REGS_NORTH_PLANE_CONTROL) #define INTEL_DISPLAY_B_PIPE_STATUS (0x1024 | REGS_NORTH_PLANE_CONTROL) + #define DISPLAY_PIPE_VBLANK_ENABLED (1UL << 17) #define DISPLAY_PIPE_VBLANK_STATUS (1UL << 1) @@ -472,6 +631,7 @@ struct intel_free_graphics_memory { #define INTEL_DISPLAY_B_SURFACE (0x119c | REGS_NORTH_PLANE_CONTROL) // i965 and up only +// INTEL_DISPLAY_A_CONTROL source pixel format #define DISPLAY_CONTROL_ENABLED (1UL << 31) #define DISPLAY_CONTROL_GAMMA (1UL << 30) #define DISPLAY_CONTROL_COLOR_MASK (0x0fUL << 26) @@ -480,6 +640,19 @@ struct intel_free_graphics_memory { #define DISPLAY_CONTROL_RGB16 (5UL << 26) #define DISPLAY_CONTROL_RGB32 (6UL << 26) +// INTEL_DISPLAY_A_PIPE_CONTROL ILK+ +#define INTEL_PIPE_DITHER_TYPE_MASK (0x0000000c) +#define INTEL_PIPE_DITHER_TYPE_SP (0 << 2) +#define INTEL_PIPE_DITHER_TYPE_ST1 (1 << 2) +#define INTEL_PIPE_DITHER_TYPE_ST2 (2 << 2) +#define INTEL_PIPE_DITHER_TYPE_TEMP (3 << 2) +#define INTEL_PIPE_DITHER_EN (1 << 4) +#define INTEL_PIPE_8BPC (0 << 5) +#define INTEL_PIPE_10BPC (1 << 5) +#define INTEL_PIPE_6BPC (2 << 5) +#define INTEL_PIPE_12BPC (3 << 5) +#define INTEL_PIPE_PROGRESSIVE (0 << 21) + // cursors #define INTEL_CURSOR_CONTROL (0x0080 | REGS_NORTH_PLANE_CONTROL) #define INTEL_CURSOR_BASE (0x0084 | REGS_NORTH_PLANE_CONTROL) @@ -503,10 +676,13 @@ struct intel_free_graphics_memory { // PLL registers #define INTEL_DISPLAY_A_PLL (0x6014 | REGS_SOUTH_SHARED) #define INTEL_DISPLAY_B_PLL (0x6018 | REGS_SOUTH_SHARED) -#define INTEL_DISPLAY_A_PLL_MULTIPLIER_DIVISOR \ - (0x601c | REGS_SOUTH_SHARED) -#define INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR \ - (0x6020 | REGS_SOUTH_SHARED) +#define CHV_DISPLAY_C_PLL (0x6030 | REGS_SOUTH_SHARED) + +// Multiplier Divisor +#define INTEL_DISPLAY_A_PLL_MD (0x601C | REGS_SOUTH_SHARED) +#define INTEL_DISPLAY_B_PLL_MD (0x6020 | REGS_SOUTH_SHARED) +#define CHV_DISPLAY_B_PLL_MD (0x603C | REGS_SOUTH_SHARED) + #define INTEL_DISPLAY_A_PLL_DIVISOR_0 (0x6040 | REGS_SOUTH_SHARED) #define INTEL_DISPLAY_A_PLL_DIVISOR_1 (0x6044 | REGS_SOUTH_SHARED) #define INTEL_DISPLAY_B_PLL_DIVISOR_0 (0x6048 | REGS_SOUTH_SHARED) @@ -535,23 +711,41 @@ struct intel_free_graphics_memory { #define I2C_RESERVED ((1 << 13) | (1 << 5)) // TODO: on IronLake this is in the north shared block at 0x41000 -#define INTEL_VGA_DISPLAY_CONTROL 0x71400 +#define INTEL_VGA_DISPLAY_CONTROL (0x1400 | REGS_NORTH_PLANE_CONTROL) #define VGA_DISPLAY_DISABLED (1UL << 31) // LVDS panel -#define INTEL_PANEL_STATUS 0x61200 -#define PANEL_STATUS_POWER_ON (1UL << 31) -#define INTEL_PANEL_CONTROL 0x61204 -#define PANEL_CONTROL_POWER_TARGET_ON (1UL << 0) -#define INTEL_PANEL_FIT_CONTROL 0x61230 -#define INTEL_PANEL_FIT_RATIOS 0x61234 +#define INTEL_PANEL_STATUS (0x1200 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_PANEL_CONTROL (0x1204 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_PANEL_FIT_CONTROL (0x1230 | REGS_NORTH_PIPE_AND_PORT) +#define INTEL_PANEL_FIT_RATIOS (0x1234 | REGS_NORTH_PIPE_AND_PORT) // LVDS on IronLake and up -#define PCH_PANEL_CONTROL 0xc7200 -#define PCH_PANEL_STATUS 0xc7204 -#define PANEL_REGISTER_UNLOCK (0xabcd << 16) +#define PCH_PANEL_STATUS (0x7200 | REGS_SOUTH_SHARED) +#define PCH_PANEL_CONTROL (0x7204 | REGS_SOUTH_SHARED) +#define PCH_PANEL_ON_DELAYS (0x7208 | REGS_SOUTH_SHARED) +#define PCH_PANEL_OFF_DELAYS (0x720c | REGS_SOUTH_SHARED) +#define PCH_PANEL_DIVISOR (0x7210 | REGS_SOUTH_SHARED) #define PCH_LVDS_DETECTED (1 << 1) +#define PANEL_STATUS_POWER_ON (1UL << 31) +#define PANEL_CONTROL_POWER_TARGET_OFF (0UL << 0) +#define PANEL_CONTROL_POWER_TARGET_ON (1UL << 0) +#define PANEL_CONTROL_POWER_TARGET_RST (1UL << 1) +#define PANEL_REGISTER_UNLOCK (0xabcd << 16) + +// PCH_PANEL_ON_DELAYS +#define PANEL_DELAY_PORT_SELECT_MASK (3 << 30) +#define PANEL_DELAY_PORT_SELECT_LVDS (0 << 30) +#define PANEL_DELAY_PORT_SELECT_DPA (1 << 30) +#define PANEL_DELAY_PORT_SELECT_DPC (2 << 30) +#define PANEL_DELAY_PORT_SELECT_DPD (3 << 30) + +// PCH_PANEL_DIVISOR +#define PANEL_DIVISOR_REFERENCE_DIV_MASK 0xffffff00 +#define PANEL_DIVISOR_REFERENCE_DIV_SHIFT 8 +#define PANEL_DIVISOR_POW_CYCLE_DLY_MASK 0x1f +#define PANEL_DIVISOR_POW_CYCLE_DLY_SHIFT 0x1f // ring buffer commands @@ -594,6 +788,99 @@ struct intel_free_graphics_memory { #define INTEL_OVERLAY_GAMMA_1 0x30020 #define INTEL_OVERLAY_GAMMA_0 0x30024 +// FDI - Flexible Display Interface, the interface between the (CPU-internal) +// GPU and the PCH display outputs. Proprietary interface, based on DisplayPort +// though, so similar link training and all... +// There's an FDI transmitter (TX) on the CPU and an FDI receiver (RX) on the +// PCH for each display pipe. +// FDI receiver A is hooked up to transcoder A, FDI receiver B is hooked up to +// transcoder B, so we have the same mapping as with the display pipes. +#define PCH_FDI_RX_BASE_REGISTER 0xf0000 +#define PCH_FDI_RX_PIPE_OFFSET 0x01000 +#define PCH_FDI_RX_CONTROL 0x00c +#define FDI_RX_ENABLE (1 << 31) +#define FDI_RX_PLL_ENABLED (1 << 13) + +#define FDI_FS_ERRC_ENABLE (1 << 27) +#define FDI_FE_ERRC_ENABLE (1 << 26) + +#define PCH_FDI_RX_TRANS_UNIT_SIZE_1 0x30 +#define PCH_FDI_RX_TRANS_UNIT_SIZE_2 0x38 +#define FDI_RX_TRANS_UNIT_SIZE(x) ((x - 1) << 25) +#define FDI_RX_TRANS_UNIT_MASK 0x7e000000 + +#define FDI_RX_ENHANCE_FRAME_ENABLE (1 << 6) +#define FDI_RX_CLOCK_MASK (1 << 4) +#define FDI_RX_CLOCK_RAW (0 << 4) +#define FDI_RX_CLOCK_PCD (1 << 4) + +#define PCH_FDI_TX_BASE_REGISTER 0x60000 +#define PCH_FDI_TX_PIPE_OFFSET 0x01000 +#define PCH_FDI_TX_CONTROL 0x100 +#define FDI_TX_ENABLE (1 << 31) +#define FDI_TX_ENHANCE_FRAME_ENABLE (1 << 18) +#define FDI_TX_PLL_ENABLED (1 << 14) + +#define FDI_PLL_BIOS_0 0x46000 +#define FDI_PLL_FB_CLOCK_MASK 0xff +#define FDI_PLL_BIOS_1 0x46004 +#define FDI_PLL_BIOS_2 0x46008 + +#define FDI_LINK_TRAIN_PATTERN_1 (0 << 28) +#define FDI_LINK_TRAIN_PATTERN_2 (1 << 28) +#define FDI_LINK_TRAIN_PATTERN_IDLE (2 << 28) +#define FDI_LINK_TRAIN_NONE (3 << 28) +#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0 << 25) +#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1 << 25) +#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2 << 25) +#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3 << 25) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0 << 22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1 << 22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2 << 22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3 << 22) + +#define FDI_AUTO_TRAINING (1 << 10) +#define FDI_AUTO_TRAIN_DONE (1 << 1) + +// SNB A-stepping +#define FDI_LINK_TRAIN_400MV_0DB_SNB_A (0x38 << 22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_A (0x02 << 22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01 << 22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_A (0x00 << 22) + +// SNB B-stepping +#define FDI_LINK_TRAIN_400MV_0DB_SNB_B (0x00 << 22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_B (0x3a << 22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39 << 22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38 << 22) +#define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f << 22) + +#define FDI_LINK_TRAIN_PATTERN_1_CPT (0 << 8) +#define FDI_LINK_TRAIN_PATTERN_2_CPT (1 << 8) +#define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2 << 8) +#define FDI_LINK_TRAIN_NORMAL_CPT (3 << 8) +#define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3 << 8) + +// IvyBridge changes it up because... they hate developers? +#define FDI_LINK_TRAIN_PATTERN_1_IVB (0 << 8) +#define FDI_LINK_TRAIN_PATTERN_2_IVB (1 << 8) +#define FDI_LINK_TRAIN_PATTERN_IDLE_IVB (2 << 8) +#define FDI_LINK_TRAIN_NONE_IVB (3 << 8) + +// CPU Panel Fitters - These are for IronLake and up and are the CPU internal +// panel fitters. +#define PCH_PANEL_FITTER_BASE_REGISTER 0x68000 +#define PCH_PANEL_FITTER_PIPE_OFFSET 0x00800 + +#define PCH_PANEL_FITTER_WINDOW_POS 0x70 +#define PCH_PANEL_FITTER_WINDOW_SIZE 0x74 +#define PCH_PANEL_FITTER_CONTROL 0x80 +#define PCH_PANEL_FITTER_V_SCALE 0x84 +#define PCH_PANEL_FITTER_H_SCALE 0x90 + +#define PANEL_FITTER_ENABLED (1 << 31) +#define PANEL_FITTER_FILTER_MASK (3 << 23) + struct overlay_scale { uint32 _reserved0 : 3; uint32 horizontal_scale_fraction : 12; @@ -768,7 +1055,7 @@ struct overlay_registers { inline bool intel_uses_physical_overlay(intel_shared_info &info) { - return !info.device_type.InGroup(INTEL_TYPE_Gxx); + return !info.device_type.InGroup(INTEL_GROUP_Gxx); } diff --git a/src/add-ons/accelerants/common/dp.cpp b/src/add-ons/accelerants/common/dp.cpp index b4522ec8f1..294b2bef8c 100644 --- a/src/add-ons/accelerants/common/dp.cpp +++ b/src/add-ons/accelerants/common/dp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Copyright 2012-2016, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -13,12 +13,12 @@ #define TRACE_DISPLAY #ifdef TRACE_DISPLAY extern "C" void _sPrintf(const char* format, ...); -# define TRACE(x...) _sPrintf("radeon_hd: " x) +# define TRACE(x...) _sPrintf("dp_common: " x) #else # define TRACE(x...) ; #endif -#define ERROR(x...) _sPrintf("radeon_hd: " x) +#define ERROR(x...) _sPrintf("dp_common: " x) uint32 diff --git a/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp b/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp new file mode 100644 index 0000000000..56e5d46985 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp @@ -0,0 +1,364 @@ +/* + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ + + +#include "FlexibleDisplayInterface.h" + +#include +#include +#include +#include + +#include "accelerant.h" +#include "intel_extreme.h" + + +#undef TRACE +#define TRACE_FDI +#ifdef TRACE_FDI +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +// #pragma mark - FDITransmitter + + +FDITransmitter::FDITransmitter(pipe_index pipeIndex) + : + fRegisterBase(PCH_FDI_TX_BASE_REGISTER) +{ + if (pipeIndex == INTEL_PIPE_B) + fRegisterBase += PCH_FDI_TX_PIPE_OFFSET * 1; +} + + +FDITransmitter::~FDITransmitter() +{ +} + + +void +FDITransmitter::Enable() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; + uint32 value = read32(targetRegister); + + write32(targetRegister, value | FDI_TX_ENABLE); + read32(targetRegister); + spin(150); +} + + +void +FDITransmitter::Disable() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; + uint32 value = read32(targetRegister); + + write32(targetRegister, value & ~FDI_TX_ENABLE); + read32(targetRegister); + spin(150); +} + + +bool +FDITransmitter::IsPLLEnabled() +{ + CALLED(); + return (read32(fRegisterBase + PCH_FDI_TX_CONTROL) & FDI_TX_PLL_ENABLED) + != 0; +} + + +void +FDITransmitter::EnablePLL() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; + uint32 value = read32(targetRegister); + if ((value & FDI_TX_PLL_ENABLED) != 0) { + // already enabled, possibly IronLake where it always is + return; + } + + write32(targetRegister, value | FDI_TX_PLL_ENABLED); + read32(targetRegister); + spin(100); // warmup 10us + dmi delay 20us, be generous +} + + +void +FDITransmitter::DisablePLL() +{ + CALLED(); + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) { + // on IronLake the FDI PLL is alaways enabled, so no point in trying... + return; + } + + uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; + write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED); + read32(targetRegister); + spin(100); +} + + +// #pragma mark - FDIReceiver + + +FDIReceiver::FDIReceiver(pipe_index pipeIndex) + : + fRegisterBase(PCH_FDI_RX_BASE_REGISTER) +{ + if (pipeIndex == INTEL_PIPE_B) + fRegisterBase += PCH_FDI_RX_PIPE_OFFSET * 1; +} + + +FDIReceiver::~FDIReceiver() +{ +} + + +void +FDIReceiver::Enable() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; + uint32 value = read32(targetRegister); + + write32(targetRegister, value | FDI_RX_ENABLE); + read32(targetRegister); + spin(150); +} + + +void +FDIReceiver::Disable() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; + uint32 value = read32(targetRegister); + + write32(targetRegister, value & ~FDI_RX_ENABLE); + read32(targetRegister); + spin(150); +} + + +bool +FDIReceiver::IsPLLEnabled() +{ + CALLED(); + return (read32(fRegisterBase + PCH_FDI_RX_CONTROL) & FDI_RX_PLL_ENABLED) + != 0; +} + + +void +FDIReceiver::EnablePLL() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; + uint32 value = read32(targetRegister); + if ((value & FDI_RX_PLL_ENABLED) != 0) + return; + + write32(targetRegister, value | FDI_RX_PLL_ENABLED); + read32(targetRegister); + spin(200); // warmup 10us + dmi delay 20us, be generous +} + + +void +FDIReceiver::DisablePLL() +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; + write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED); + read32(targetRegister); + spin(100); +} + + +void +FDIReceiver::SwitchClock(bool toPCDClock) +{ + CALLED(); + uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; + write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK) + | (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW)); + read32(targetRegister); + spin(200); +} + + +// #pragma mark - FDILink + + +FDILink::FDILink(pipe_index pipeIndex) + : + fTransmitter(pipeIndex), + fReceiver(pipeIndex) +{ +} + + +status_t +FDILink::Train(display_mode* target) +{ + CALLED(); + + uint32 bitsPerPixel; + switch (target->space) { + case B_RGB32_LITTLE: + bitsPerPixel = 32; + break; + case B_RGB16_LITTLE: + bitsPerPixel = 16; + break; + case B_RGB15_LITTLE: + bitsPerPixel = 15; + break; + case B_CMAP8: + default: + bitsPerPixel = 8; + break; + } + + // Khz / 10. ( each output octet encoded as 10 bits. + uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10; + uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20; + + uint32 lanes = bps / (linkBandwidth * 8); + + TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes); + + // Enable FDI clocks + Receiver().EnablePLL(); + Receiver().SwitchClock(true); + Transmitter().EnablePLL(); + + status_t result = B_ERROR; + + // Over IVB supports AutoTraining of FDI + if (gInfo->shared_info->device_type.Generation() >= 7) { + result = _AutoTrain(lanes); + if (result != B_OK) { + ERROR("%s: FDI auto-training fault. Attempting manual train.\n", + __func__); + return _ManualTrain(lanes); + } + return B_OK; + } + return _ManualTrain(lanes); +} + + +status_t +FDILink::_ManualTrain(uint32 lanes) +{ + CALLED(); + + // This needs completed + ERROR("TODO: Manual FDI Link Training\n"); + + // Enable pipes + Transmitter().Enable(); + Receiver().Enable(); + return B_OK; +} + + +status_t +FDILink::_AutoTrain(uint32 lanes) +{ + CALLED(); + uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; + uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; + + uint32 buffer = read32(txControl); + + // Clear port width selection and set number of lanes + buffer &= ~(7 << 19); + buffer |= (lanes - 1) << 19; + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) + buffer &= ~FDI_LINK_TRAIN_NONE_IVB; + else + buffer &= ~FDI_LINK_TRAIN_NONE; + write32(txControl, buffer); + + static const int snb_b_fdi_train_param[] = { + FDI_LINK_TRAIN_400MV_0DB_SNB_B, + FDI_LINK_TRAIN_400MV_6DB_SNB_B, + FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, + FDI_LINK_TRAIN_800MV_0DB_SNB_B, + }; + + bool trained = false; + + for (uint32 i = 0; i < (sizeof(snb_b_fdi_train_param) + / sizeof(snb_b_fdi_train_param[0])); i++) { + for (int j = 0; j < 2; j++) { + buffer = read32(txControl); + buffer |= FDI_AUTO_TRAINING; + buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + buffer |= snb_b_fdi_train_param[i]; + write32(txControl, buffer | FDI_TX_ENABLE); + + write32(rxControl, read32(rxControl) | FDI_RX_ENABLE); + + spin(5); + + buffer = read32(txControl); + if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) { + TRACE("%s: FDI auto train complete!\n", __func__); + trained = true; + break; + } + + write32(txControl, read32(txControl) & ~FDI_TX_ENABLE); + write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE); + read32(rxControl); + + spin(31); + } + + // If Trained, we fall out of autotraining + if (trained) + break; + } + + if (!trained) { + ERROR("%s: FDI auto train failed!\n", __func__); + return B_ERROR; + } + + // Enable ecc on IVB + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) { + write32(rxControl, read32(rxControl) + | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); + read32(rxControl); + } + + return B_OK; +} + + +FDILink::~FDILink() +{ +} diff --git a/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.h b/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.h new file mode 100644 index 0000000000..33502ba076 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.h @@ -0,0 +1,79 @@ +/* + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ +#ifndef INTEL_FDI_H +#define INTEL_FDI_H + + +#include "intel_extreme.h" + + +class FDITransmitter { +public: + FDITransmitter(pipe_index pipeIndex); + ~FDITransmitter(); + + void Enable(); + void Disable(); + + bool IsPLLEnabled(); + void EnablePLL(); + void DisablePLL(); + + uint32 Base() + { return fRegisterBase; }; + +private: + uint32 fRegisterBase; +}; + + +class FDIReceiver { +public: + FDIReceiver(pipe_index pipeIndex); + ~FDIReceiver(); + + void Enable(); + void Disable(); + + bool IsPLLEnabled(); + void EnablePLL(); + void DisablePLL(); + + void SwitchClock(bool toPCDClock); + + uint32 Base() + { return fRegisterBase; }; + +protected: + uint32 fRegisterBase; +}; + + +class FDILink { +public: + FDILink(pipe_index pipeIndex); + ~FDILink(); + + FDITransmitter& Transmitter() + { return fTransmitter; }; + FDIReceiver& Receiver() + { return fReceiver; }; + + status_t Train(display_mode* target); + +private: + status_t _ManualTrain(uint32 lanes); + status_t _AutoTrain(uint32 lanes); + + FDITransmitter fTransmitter; + FDIReceiver fReceiver; +}; + + +#endif // INTEL_FDI_H diff --git a/src/add-ons/accelerants/intel_extreme/Jamfile b/src/add-ons/accelerants/intel_extreme/Jamfile index 3ffe818a56..a4f42b0372 100644 --- a/src/add-ons/accelerants/intel_extreme/Jamfile +++ b/src/add-ons/accelerants/intel_extreme/Jamfile @@ -16,5 +16,11 @@ Addon intel_extreme.accelerant : mode.cpp overlay.cpp # overlay_3d_i965.cpp - : be libaccelerantscommon.a + pll.cpp + # classes + FlexibleDisplayInterface.cpp +# PanelFitter.cpp + Ports.cpp + Pipes.cpp + : be $(TARGET_LIBSTDC++) libaccelerantscommon.a ; diff --git a/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp b/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp new file mode 100644 index 0000000000..8357d61109 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/PanelFitter.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2011, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + */ + + +#include "PanelFitter.h" + +#include "accelerant.h" +#include "intel_extreme.h" + +#include +#include + + +#undef TRACE +#define TRACE_FITTER +#ifdef TRACE_FITTER +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +// #pragma mark - PanelFitter + + +PanelFitter::PanelFitter(int32 pipeIndex) + : + fBaseRegister(PCH_PANEL_FITTER_BASE_REGISTER + + pipeIndex * PCH_PANEL_FITTER_PIPE_OFFSET) +{ +} + + +bool +PanelFitter::IsEnabled() +{ + return (read32(fBaseRegister + PCH_PANEL_FITTER_CONTROL) + & PANEL_FITTER_ENABLED) != 0; +} + + +void +PanelFitter::Enable(const display_mode& mode) +{ + // TODO: program the right window size and position based on the mode + _Enable(true); +} + + +void +PanelFitter::Disable() +{ + _Enable(false); +} + + +void +PanelFitter::_Enable(bool enable) +{ + uint32 targetRegister = fBaseRegister + PCH_PANEL_FITTER_CONTROL; + write32(targetRegister, read32(targetRegister) & ~PANEL_FITTER_ENABLED + | (enable ? PANEL_FITTER_ENABLED | 0)); + read32(targetRegister); +} diff --git a/src/add-ons/accelerants/intel_extreme/PanelFitter.h b/src/add-ons/accelerants/intel_extreme/PanelFitter.h new file mode 100644 index 0000000000..7648ad7074 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/PanelFitter.h @@ -0,0 +1,27 @@ +/* + * Copyright 2011, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + */ +#ifndef INTEL_FITTER_H +#define INTEL_FITTER_H + + +class PanelFitter { +public: + PanelFitter(int32 pipeIndex); +virtual ~PanelFitter(); + + bool IsEnabled(); + void Enable(const display_mode& mode); + void Disable(); + +private: + void _Enable(bool enable); + + uint32 fRegisterBase; +}; + +#endif // INTEL_FITTER_H diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.cpp b/src/add-ons/accelerants/intel_extreme/Pipes.cpp new file mode 100644 index 0000000000..4f36f620e5 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/Pipes.cpp @@ -0,0 +1,335 @@ +/* + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ +#include "Pipes.h" + +#include "accelerant.h" +#include "intel_extreme.h" +#include + +#include +#include + +#include + + +#define TRACE_PIPE +#ifdef TRACE_PIPE +extern "C" void _sPrintf(const char* format, ...); +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) ; +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +// PIPE: 6 +// PLANE: 7 + + +void +program_pipe_color_modes(uint32 colorMode) +{ + // All pipes get the same color mode + write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL) + & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) + | colorMode); + write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL) + & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) + | colorMode); +} + + +// #pragma mark - Pipe + + +Pipe::Pipe(pipe_index pipeIndex) + : + fHasTranscoder(false), + fFDILink(NULL), +// fPanelFitter(NULL), + fPipeIndex(pipeIndex), + fPipeOffset(0), + fPlaneOffset(0) +{ + if (pipeIndex == INTEL_PIPE_B) { + fPipeOffset = INTEL_DISPLAY_OFFSET; + fPlaneOffset = INTEL_PLANE_OFFSET; + } + + if (gInfo->shared_info->device_type.HasPlatformControlHub()) { + fHasTranscoder = true; + + // Program FDILink if PCH + if (fFDILink == NULL) + fFDILink = new(std::nothrow) FDILink(pipeIndex); + } + + TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR + " Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A) + ? "A" : "B", fPipeOffset, fPlaneOffset); +} + + +Pipe::~Pipe() +{ +} + + +bool +Pipe::IsEnabled() +{ + CALLED(); + + return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset) + & INTEL_PIPE_ENABLED) != 0; +} + + +void +Pipe::Configure(display_mode* mode) +{ + uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); + + // TODO: Haswell+ dithering changes. + if (gInfo->shared_info->device_type.Generation() >= 4) { + pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP); + switch (mode->space) { + case B_CMAP8: + case B_RGB15_LITTLE: + case B_RGB16_LITTLE: + pipeControl |= INTEL_PIPE_6BPC; + break; + case B_RGB24_LITTLE: + pipeControl |= INTEL_PIPE_8BPC; + break; + case B_RGB32_LITTLE: + default: + pipeControl |= INTEL_PIPE_10BPC; + break; + } + } + + // TODO: CxSR downclocking? + + // TODO: Interlaced modes + pipeControl |= INTEL_PIPE_PROGRESSIVE; + + write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl); + read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset); +} + + +void +Pipe::_ConfigureTranscoder(display_mode* target) +{ + // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) + write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset, + ((uint32)(target->timing.h_sync_end - 1) << 16) + | ((uint32)target->timing.h_sync_start - 1)); + + write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset, + ((uint32)(target->timing.v_sync_end - 1) << 16) + | ((uint32)target->timing.v_sync_start - 1)); + + #if 0 + // XXX: Is it ok to do these on non-digital? + write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0); + write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset, + ((uint32)(target->virtual_width - 1) << 16) + | ((uint32)target->virtual_height - 1)); + #endif +} + + +void +Pipe::ConfigureTimings(display_mode* target) +{ + CALLED(); + + if (target == NULL) { + ERROR("%s: Invalid display mode!\n", __func__); + return; + } + + // update timing (fPipeOffset bumps the DISPLAY_A to B when needed) + write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset, + ((uint32)(target->timing.h_sync_end - 1) << 16) + | ((uint32)target->timing.h_sync_start - 1)); + + write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset, + ((uint32)(target->timing.v_sync_end - 1) << 16) + | ((uint32)target->timing.v_sync_start - 1)); + + // XXX: Is it ok to do these on non-digital? + write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0); + write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset, + ((uint32)(target->virtual_width - 1) << 16) + | ((uint32)target->virtual_height - 1)); + + write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset, + ((uint32)(target->timing.v_display - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + + // This is useful for debugging: it sets the border to red, so you + // can see what is border and what is porch (black area around the + // sync) + //write32(INTEL_DISPLAY_A_RED + fPipeOffset, 0x00FF0000); + + if (fHasTranscoder) + _ConfigureTranscoder(target); +} + + +void +Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock, + uint32 extraFlags) +{ + CALLED(); + + addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0; + addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1; + addr_t pllControl = INTEL_DISPLAY_A_PLL; + addr_t pllMD = INTEL_DISPLAY_A_PLL_MD; + + if (fPipeIndex == INTEL_PIPE_B) { + pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0; + pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1; + pllControl = INTEL_DISPLAY_B_PLL; + pllMD = INTEL_DISPLAY_B_PLL_MD; + } + + float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { + float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.post; + uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f)); + write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8)); + } + + // XXX: For now we assume no LVDS downclocking and program the same divisor + // value to both divisor 0 (standard) and 1 (reduced divisor) + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { + write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_N_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); + write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_N_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); + } else { + write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_N_DIVISOR_MASK) + | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) + & DISPLAY_PLL_M1_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_M2_DIVISOR_MASK)); + write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_N_DIVISOR_MASK) + | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) + & DISPLAY_PLL_M1_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_M2_DIVISOR_MASK)); + } + + uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags; + + if (gInfo->shared_info->device_type.Generation() >= 3) { + // post1 divisor << 1 , 1-8 + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { + pll |= ((1 << (divisors.post1 - 1)) + << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK; + } else { + pll |= ((1 << (divisors.post1 - 1)) + << DISPLAY_PLL_POST1_DIVISOR_SHIFT) + & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; + // pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) + // & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; + } + + // p2 clock divider. 5 or 7 high + if (divisors.post2_high) + pll |= DISPLAY_PLL_DIVIDE_HIGH; + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) + pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT; + } else { + if (!divisors.post2_high) + pll |= DISPLAY_PLL_DIVIDE_4X; + + pll |= DISPLAY_PLL_2X_CLOCK; + + // TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK?? + if (divisors.post1 > 2) { + pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) + & DISPLAY_PLL_POST1_DIVISOR_MASK; + } else + pll |= DISPLAY_PLL_POST1_DIVIDE_2; + } + + // Allow the PLL to warm up by masking its bit. + write32(pllControl, pll & ~DISPLAY_PLL_NO_VGA_CONTROL); + read32(pllControl); + spin(150); + write32(pllControl, pll); + read32(pllControl); + spin(150); +} + + +void +Pipe::Enable(bool enable) +{ + CALLED(); + + addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset; + addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset; + + // Planes always have to operate on an enabled pipe + + if (enable) { + write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED); + wait_for_vblank(); + write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED); + } else { + write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED); + wait_for_vblank(); + write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED); + } + + read32(INTEL_DISPLAY_A_BASE); + // flush the eventually cached PCI bus writes +} diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.h b/src/add-ons/accelerants/intel_extreme/Pipes.h new file mode 100644 index 0000000000..c8cb5f4c36 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/Pipes.h @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ +#ifndef INTEL_PIPE_H +#define INTEL_PIPE_H + + +#include + +#include "intel_extreme.h" + +#include "pll.h" +#include "FlexibleDisplayInterface.h" + + +#define MAX_PIPES 2 + + +void program_pipe_color_modes(uint32 colorMode); + +//class FDILink; +//class PanelFitter; + +class Pipe { +public: + Pipe(pipe_index pipeIndex); + ~Pipe(); + + pipe_index Index() + { return fPipeIndex; } + + bool IsEnabled(); + void Enable(bool enable); + void Disable(); + + void Configure(display_mode* mode); + void ConfigureTimings(display_mode* mode); + void ConfigureClocks( + const pll_divisors& divisors, + uint32 pixelClock, + uint32 extraFlags); + + // access to the various parts of the pipe + ::FDILink* FDI() + { return fFDILink; } + // ::PanelFitter* PanelFitter() + // { return fPanelFitter; } + +private: + void _ConfigureTranscoder(display_mode* mode); + + bool fHasTranscoder; + + FDILink* fFDILink; + // PanelFitter* fPanelFitter; + + pipe_index fPipeIndex; + + addr_t fPipeOffset; + addr_t fPlaneOffset; +}; + + +#endif // INTEL_PIPE_H diff --git a/src/add-ons/accelerants/intel_extreme/Ports.cpp b/src/add-ons/accelerants/intel_extreme/Ports.cpp new file mode 100644 index 0000000000..f5a5d45e88 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -0,0 +1,1101 @@ +/* + * Copyright 2006-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ + + +#include "Ports.h" + +#include +#include +#include +#include +#include + +#include "accelerant.h" +#include "accelerant_protos.h" +#include "FlexibleDisplayInterface.h" +#include "intel_extreme.h" + +#include + + +#undef TRACE +#define TRACE_PORTS +#ifdef TRACE_PORTS +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +static bool +wait_for_set(addr_t address, uint32 mask, uint32 timeout) +{ + int interval = 50; + uint32 i = 0; + for(i = 0; i <= timeout; i += interval) { + spin(interval); + if ((read32(address) & mask) != 0) + return true; + } + return false; +} + + +static bool +wait_for_clear(addr_t address, uint32 mask, uint32 timeout) +{ + int interval = 50; + uint32 i = 0; + for(i = 0; i <= timeout; i += interval) { + spin(interval); + if ((read32(address) & mask) == 0) + return true; + } + return false; +} + + +Port::Port(port_index index, const char* baseName) + : + fPipe(NULL), + fPortIndex(index), + fPortName(NULL), + fEDIDState(B_NO_INIT) +{ + char portID[2]; + portID[0] = 'A' + index - INTEL_PORT_A; + portID[1] = 0; + + char buffer[32]; + buffer[0] = 0; + + strlcat(buffer, baseName, sizeof(buffer)); + strlcat(buffer, " ", sizeof(buffer)); + strlcat(buffer, portID, sizeof(buffer)); + fPortName = strdup(buffer); +} + + +Port::~Port() +{ + free(fPortName); +} + + +bool +Port::HasEDID() +{ + if (fEDIDState == B_NO_INIT) + GetEDID(NULL); + + return fEDIDState == B_OK; +} + + +status_t +Port::SetPipe(Pipe* pipe) +{ + CALLED(); + + if (pipe == NULL) { + ERROR("%s: Invalid pipe provided!\n", __func__); + return B_ERROR; + } + + uint32 portRegister = _PortRegister(); + if (portRegister == 0) { + ERROR("%s: Invalid PortRegister ((0x%" B_PRIx32 ") for %s\n", __func__, + portRegister, PortName()); + return B_ERROR; + } + + // TODO: UnAssignPipe? This likely needs reworked a little + if (fPipe != NULL) { + ERROR("%s: Can't reassign display pipe (yet)\n", __func__); + return B_ERROR; + } + + TRACE("%s: Assigning %s (0x%" B_PRIx32 ") to pipe %s\n", __func__, + PortName(), portRegister, (pipe->Index() == INTEL_PIPE_A) ? "A" : "B"); + + uint32 portState = read32(portRegister); + + if (pipe->Index() == INTEL_PIPE_A) + write32(portRegister, portState & ~DISPLAY_MONITOR_PIPE_B); + else + write32(portRegister, portState | DISPLAY_MONITOR_PIPE_B); + + fPipe = pipe; + + if (fPipe == NULL) + return B_NO_MEMORY; + + // Disable display pipe until modesetting enables it + if (fPipe->IsEnabled()) + fPipe->Enable(false); + + read32(portRegister); + + return B_OK; +} + + +status_t +Port::Power(bool enabled) +{ + fPipe->Enable(enabled); + + return B_OK; +} + + +status_t +Port::GetEDID(edid1_info* edid, bool forceRead) +{ + CALLED(); + + if (fEDIDState == B_NO_INIT || forceRead) { + TRACE("%s: trying to read EDID\n", PortName()); + + addr_t ddcRegister = _DDCRegister(); + if (ddcRegister == 0) { + TRACE("%s: no DDC register found\n", PortName()); + fEDIDState = B_ERROR; + return fEDIDState; + } + + TRACE("%s: using ddc @ 0x%" B_PRIxADDR "\n", PortName(), ddcRegister); + + i2c_bus bus; + bus.cookie = (void*)ddcRegister; + bus.set_signals = &_SetI2CSignals; + bus.get_signals = &_GetI2CSignals; + ddc2_init_timing(&bus); + + fEDIDState = ddc2_read_edid1(&bus, &fEDIDInfo, NULL, NULL); + + if (fEDIDState == B_OK) { + TRACE("%s: found EDID information!\n", PortName()); + edid_dump(&fEDIDInfo); + } + } + + if (fEDIDState != B_OK) { + TRACE("%s: no EDID information found.\n", PortName()); + return fEDIDState; + } + + if (edid != NULL) + memcpy(edid, &fEDIDInfo, sizeof(edid1_info)); + + return B_OK; +} + + +status_t +Port::GetPLLLimits(pll_limits& limits) +{ + return B_ERROR; +} + + +status_t +Port::_GetI2CSignals(void* cookie, int* _clock, int* _data) +{ + addr_t ioRegister = (addr_t)cookie; + uint32 value = read32(ioRegister); + + *_clock = (value & I2C_CLOCK_VALUE_IN) != 0; + *_data = (value & I2C_DATA_VALUE_IN) != 0; + + return B_OK; +} + + +status_t +Port::_SetI2CSignals(void* cookie, int clock, int data) +{ + addr_t ioRegister = (addr_t)cookie; + uint32 value; + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_83x)) { + // on these chips, the reserved values are fixed + value = 0; + } else { + // on all others, we have to preserve them manually + value = read32(ioRegister) & I2C_RESERVED; + } + + if (data != 0) + value |= I2C_DATA_DIRECTION_MASK; + else { + value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT + | I2C_DATA_VALUE_MASK; + } + + if (clock != 0) + value |= I2C_CLOCK_DIRECTION_MASK; + else { + value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT + | I2C_CLOCK_VALUE_MASK; + } + + write32(ioRegister, value); + read32(ioRegister); + // make sure the PCI bus has flushed the write + + return B_OK; +} + + +// #pragma mark - Analog Port + + +AnalogPort::AnalogPort() + : + Port(INTEL_PORT_A, "Analog") +{ +} + + +bool +AnalogPort::IsConnected() +{ + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + _PortRegister()); + return HasEDID(); +} + + +addr_t +AnalogPort::_DDCRegister() +{ + // always fixed + return INTEL_I2C_IO_A; +} + + +addr_t +AnalogPort::_PortRegister() +{ + // always fixed + return INTEL_ANALOG_PORT; +} + + +status_t +AnalogPort::SetDisplayMode(display_mode* target, uint32 colorMode) +{ + TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width, + target->virtual_height); + + if (fPipe == NULL) { + ERROR("%s: Setting display mode without assigned pipe!\n", __func__); + return B_ERROR; + } + + // Train FDI if it exists + FDILink* link = fPipe->FDI(); + if (link != NULL) + link->Train(target); + + pll_divisors divisors; + compute_pll_divisors(target, &divisors, false); + + uint32 extraPLLFlags = 0; + if (gInfo->shared_info->device_type.Generation() >= 3) + extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL; + + // Program general pipe config + fPipe->Configure(target); + + // Program pipe PLL's + fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + + write32(_PortRegister(), (read32(_PortRegister()) + & ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY)) + | ((target->timing.flags & B_POSITIVE_HSYNC) != 0 + ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0) + | ((target->timing.flags & B_POSITIVE_VSYNC) != 0 + ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0)); + + // Program target display mode + fPipe->ConfigureTimings(target); + + // Set fCurrentMode to our set display mode + memcpy(&fCurrentMode, target, sizeof(display_mode)); + + return B_OK; +} + + +// #pragma mark - LVDS Panel + + +LVDSPort::LVDSPort() + : + Port(INTEL_PORT_C, "LVDS") +{ + // Always unlock LVDS port as soon as we start messing with it. + uint32 panelControl = INTEL_PANEL_CONTROL; + if (gInfo->shared_info->device_type.HasPlatformControlHub()) + panelControl = PCH_PANEL_CONTROL; + write32(panelControl, read32(panelControl) | PANEL_REGISTER_UNLOCK); +} + + +bool +LVDSPort::IsConnected() +{ + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + _PortRegister()); + + // Older generations don't have LVDS detection. If not mobile skip. + if (gInfo->shared_info->device_type.Generation() <= 4) { + if (!gInfo->shared_info->device_type.IsMobile()) { + TRACE("LVDS: Skipping LVDS detection due to gen and not mobile\n"); + return false; + } + } + + uint32 registerValue = read32(_PortRegister()); + if (gInfo->shared_info->device_type.HasPlatformControlHub()) { + // there's a detection bit we can use + if ((registerValue & PCH_LVDS_DETECTED) == 0) { + TRACE("LVDS: Not detected\n"); + return false; + } + // TODO: Skip if eDP support + } + + // Try getting EDID, as the LVDS port doesn't overlap with anything else, + // we don't run the risk of getting someone else's data. + return HasEDID(); +} + + +addr_t +LVDSPort::_DDCRegister() +{ + // always fixed + return INTEL_I2C_IO_C; +} + + +addr_t +LVDSPort::_PortRegister() +{ + // always fixed + return INTEL_DIGITAL_LVDS_PORT; +} + + +status_t +LVDSPort::SetDisplayMode(display_mode* target, uint32 colorMode) +{ + CALLED(); + if (target == NULL) { + ERROR("%s: Invalid target mode passed!\n", __func__); + return B_ERROR; + } + + TRACE("%s: %s-%d %dx%d\n", __func__, PortName(), PortIndex(), + target->virtual_width, target->virtual_height); + + if (fPipe == NULL) { + ERROR("%s: Setting display mode without assigned pipe!\n", __func__); + return B_ERROR; + } + + addr_t panelControl = INTEL_PANEL_CONTROL; + addr_t panelStatus = INTEL_PANEL_STATUS; + if (gInfo->shared_info->device_type.HasPlatformControlHub()) { + panelControl = PCH_PANEL_CONTROL; + panelStatus = PCH_PANEL_STATUS; + } + + // Power off Panel + write32(panelControl, read32(panelControl) & ~PANEL_CONTROL_POWER_TARGET_ON); + read32(panelControl); + + if (!wait_for_clear(panelStatus, PANEL_STATUS_POWER_ON, 1000)) + ERROR("%s: %s didn't power off within 1000ms!\n", __func__, PortName()); + + // TODO: Fix software scaling? + + // Disable PanelFitter for now + addr_t panelFitterControl = PCH_PANEL_FITTER_BASE_REGISTER + + PCH_PANEL_FITTER_CONTROL; + if (fPipe->Index() == INTEL_PIPE_B) + panelFitterControl += PCH_PANEL_FITTER_PIPE_OFFSET; + write32(panelFitterControl, (read32(panelFitterControl) & ~PANEL_FITTER_ENABLED)); + read32(panelFitterControl); + +#if 0 + // For LVDS panels, we actually always set the native mode in hardware + // Then we use the panel fitter to scale the picture to that. + display_mode hardwareTarget; + bool needsScaling = false; + // Try to get the panel preferred screen mode from EDID info + if (gInfo->has_edid) { + hardwareTarget.space = target->space; + hardwareTarget.virtual_width + = gInfo->edid_info.std_timing[0].h_size; + hardwareTarget.virtual_height + = gInfo->edid_info.std_timing[0].v_size; + for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) { + if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type + == EDID1_IS_DETAILED_TIMING) { + hardwareTarget.virtual_width = gInfo->edid_info + .detailed_monitor[i].data.detailed_timing.h_active; + hardwareTarget.virtual_height = gInfo->edid_info + .detailed_monitor[i].data.detailed_timing.v_active; + break; + } + } + TRACE("%s: hardware mode will actually be %dx%d\n", __func__, + hardwareTarget.virtual_width, hardwareTarget.virtual_height); + + if ((hardwareTarget.virtual_width <= target->virtual_width + && hardwareTarget.virtual_height <= target->virtual_height + && hardwareTarget.space <= target->space) + || intel_propose_display_mode(&hardwareTarget, target, target)) { + hardwareTarget = *target; + } else + needsScaling = true; + } else { + // We don't have EDID data, try to set the requested mode directly + hardwareTarget = *target; + } + pll_divisors divisors; + if (needsScaling) + compute_pll_divisors(&hardwareTarget, &divisors, true); + else + compute_pll_divisors(target, &divisors, true); + + uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED; + if (gInfo->shared_info->device_type.Generation() >= 4) { + // DPLL mode LVDS for i915+ + dpll |= LVDS_PLL_MODE_LVDS; + } + + // Compute bitmask from p1 value + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IGD)) { + dpll |= (1 << (divisors.post1 - 1)) + << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; + } else { + dpll |= (1 << (divisors.post1 - 1)) + << DISPLAY_PLL_POST1_DIVISOR_SHIFT; + } + switch (divisors.post2) { + case 5: + case 7: + dpll |= DISPLAY_PLL_DIVIDE_HIGH; + break; + } + + // Disable panel fitting, but enable 8 to 6-bit dithering + write32(INTEL_PANEL_FIT_CONTROL, 0x4); + // TODO: do not do this if the connected panel is 24-bit + // (I don't know how to detect that) + + if ((dpll & DISPLAY_PLL_ENABLED) != 0) { + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IGD)) { + write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, + (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_N_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); + } else { + write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, + (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) + & DISPLAY_PLL_N_DIVISOR_MASK) + | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) + & DISPLAY_PLL_M1_DIVISOR_MASK) + | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) + & DISPLAY_PLL_M2_DIVISOR_MASK)); + } + write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED); + read32(INTEL_DISPLAY_B_PLL); + spin(150); + } +#endif + + + pll_divisors divisors; + compute_pll_divisors(target, &divisors, true); + + uint32 lvds = read32(_PortRegister()) + | LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + + if (gInfo->shared_info->device_type.Generation() == 4) { + // LVDS_A3_POWER_UP == 24bpp + // otherwise, 18bpp + if ((lvds & LVDS_A3_POWER_MASK) != LVDS_A3_POWER_UP) + lvds |= LVDS_18BIT_DITHER; + } + + // Set the B0-B3 data pairs corresponding to whether we're going to + // set the DPLLs for dual-channel mode or not. + if (divisors.post2_high) { + TRACE("LVDS: dual channel\n"); + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + } else { + TRACE("LVDS: single channel\n"); + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + } + + // LVDS port control moves polarity bits because Intel hates you. + // Set LVDS sync polarity + lvds &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); + + // set on - polarity. + if ((target->timing.flags & B_POSITIVE_HSYNC) == 0) + lvds |= LVDS_HSYNC_POLARITY; + if ((target->timing.flags & B_POSITIVE_VSYNC) == 0) + lvds |= LVDS_VSYNC_POLARITY; + + TRACE("%s: LVDS Write: 0x%" B_PRIx32 "\n", __func__, lvds); + write32(_PortRegister(), lvds); + read32(_PortRegister()); + + uint32 extraPLLFlags = 0; + + // DPLL mode LVDS for i915+ + if (gInfo->shared_info->device_type.Generation() >= 3) + extraPLLFlags |= DISPLAY_PLL_MODE_LVDS; + + // Program general pipe config + fPipe->Configure(target); + + // Program pipe PLL's (pixel_clock is *always* the hardware pixel clock) + fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + + // Power on Panel + write32(panelControl, read32(panelControl) | PANEL_CONTROL_POWER_TARGET_ON); + read32(panelControl); + + if (!wait_for_set(panelStatus, PANEL_STATUS_POWER_ON, 1000)) + ERROR("%s: %s didn't power on within 1000ms!\n", __func__, PortName()); + + // Program target display mode + fPipe->ConfigureTimings(target); + +#if 0 + + // update timing parameters + if (needsScaling) { + // TODO: Alternatively, it should be possible to use the panel + // fitter and scale the picture. + + // TODO: Perform some sanity check, for example if the target is + // wider than the hardware mode we end up with negative borders and + // broken timings + uint32 borderWidth = hardwareTarget.timing.h_display + - target->timing.h_display; + + uint32 syncWidth = hardwareTarget.timing.h_sync_end + - hardwareTarget.timing.h_sync_start; + + uint32 syncCenter = target->timing.h_display + + (hardwareTarget.timing.h_total + - target->timing.h_display) / 2; + + write32(INTEL_DISPLAY_B_HTOTAL, + ((uint32)(hardwareTarget.timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_DISPLAY_B_HBLANK, + ((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1) + << 16) + | ((uint32)target->timing.h_display + borderWidth / 2 - 1)); + write32(INTEL_DISPLAY_B_HSYNC, + ((uint32)(syncCenter + syncWidth / 2 - 1) << 16) + | ((uint32)syncCenter - syncWidth / 2 - 1)); + + uint32 borderHeight = hardwareTarget.timing.v_display + - target->timing.v_display; + + uint32 syncHeight = hardwareTarget.timing.v_sync_end + - hardwareTarget.timing.v_sync_start; + + syncCenter = target->timing.v_display + + (hardwareTarget.timing.v_total + - target->timing.v_display) / 2; + + write32(INTEL_DISPLAY_B_VTOTAL, + ((uint32)(hardwareTarget.timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_DISPLAY_B_VBLANK, + ((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1) + << 16) + | ((uint32)target->timing.v_display + + borderHeight / 2 - 1)); + write32(INTEL_DISPLAY_B_VSYNC, + ((uint32)(syncCenter + syncHeight / 2 - 1) << 16) + | ((uint32)syncCenter - syncHeight / 2 - 1)); + + // This is useful for debugging: it sets the border to red, so you + // can see what is border and what is porch (black area around the + // sync) + // write32(0x61020, 0x00FF0000); + } else { + write32(INTEL_DISPLAY_B_HTOTAL, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_DISPLAY_B_HBLANK, + ((uint32)(target->timing.h_total - 1) << 16) + | ((uint32)target->timing.h_display - 1)); + write32(INTEL_DISPLAY_B_HSYNC, + ((uint32)(target->timing.h_sync_end - 1) << 16) + | ((uint32)target->timing.h_sync_start - 1)); + + write32(INTEL_DISPLAY_B_VTOTAL, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_DISPLAY_B_VBLANK, + ((uint32)(target->timing.v_total - 1) << 16) + | ((uint32)target->timing.v_display - 1)); + write32(INTEL_DISPLAY_B_VSYNC, ( + (uint32)(target->timing.v_sync_end - 1) << 16) + | ((uint32)target->timing.v_sync_start - 1)); + } +#endif + + // Set fCurrentMode to our set display mode + memcpy(&fCurrentMode, target, sizeof(display_mode)); + + return B_OK; +} + + +// #pragma mark - DVI/SDVO/generic + + +DigitalPort::DigitalPort(port_index index, const char* baseName) + : + Port(index, baseName) +{ +} + + +bool +DigitalPort::IsConnected() +{ + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + _PortRegister()); + + // As this port overlaps with pretty much everything, this must be called + // after having ruled out all other port types. + return HasEDID(); +} + + +addr_t +DigitalPort::_DDCRegister() +{ + //TODO: IS BROXTON, B = B, C = C, D = NIL + switch (PortIndex()) { + case INTEL_PORT_B: + return INTEL_I2C_IO_E; + case INTEL_PORT_C: + return INTEL_I2C_IO_D; + case INTEL_PORT_D: + return INTEL_I2C_IO_F; + default: + return 0; + } + + return 0; +} + + +addr_t +DigitalPort::_PortRegister() +{ + switch (PortIndex()) { + case INTEL_PORT_A: + return INTEL_DIGITAL_PORT_A; + case INTEL_PORT_B: + return INTEL_DIGITAL_PORT_B; + case INTEL_PORT_C: + return INTEL_DIGITAL_PORT_C; + default: + return 0; + } + return 0; +} + + +status_t +DigitalPort::SetDisplayMode(display_mode* target, uint32 colorMode) +{ + TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width, + target->virtual_height); + + if (fPipe == NULL) { + ERROR("%s: Setting display mode without assigned pipe!\n", __func__); + return B_ERROR; + } + + // Train FDI if it exists + FDILink* link = fPipe->FDI(); + if (link != NULL) + link->Train(target); + + pll_divisors divisors; + compute_pll_divisors(target, &divisors, false); + + uint32 extraPLLFlags = 0; + if (gInfo->shared_info->device_type.Generation() >= 3) + extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL; + + // Program general pipe config + fPipe->Configure(target); + + // Program pipe PLL's + fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + + // Program target display mode + fPipe->ConfigureTimings(target); + + // Set fCurrentMode to our set display mode + memcpy(&fCurrentMode, target, sizeof(display_mode)); + + return B_OK; +} + + +// #pragma mark - LVDS Panel +// #pragma mark - HDMI + + +HDMIPort::HDMIPort(port_index index) + : + DigitalPort(index, "HDMI") +{ +} + + +bool +HDMIPort::IsConnected() +{ + if (!gInfo->shared_info->device_type.SupportsHDMI()) + return false; + + addr_t portRegister = _PortRegister(); + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + portRegister); + + if (portRegister == 0) + return false; + + if (!gInfo->shared_info->device_type.HasPlatformControlHub() + && PortIndex() == INTEL_PORT_C) { + // there's no detection bit on this port + } else if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0) + return false; + + return HasEDID(); +} + + +addr_t +HDMIPort::_PortRegister() +{ + // on PCH there's an additional port sandwiched in + bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub(); + bool fourthGen = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV); + + switch (PortIndex()) { + case INTEL_PORT_B: + if (fourthGen) + return GEN4_HDMI_PORT_B; + return hasPCH ? PCH_HDMI_PORT_B : INTEL_HDMI_PORT_B; + case INTEL_PORT_C: + if (fourthGen) + return GEN4_HDMI_PORT_C; + return hasPCH ? PCH_HDMI_PORT_C : INTEL_HDMI_PORT_C; + case INTEL_PORT_D: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) + return CHV_HDMI_PORT_D; + return hasPCH ? PCH_HDMI_PORT_D : 0; + default: + return 0; + } + + return 0; +} + + +// #pragma mark - DisplayPort + + +DisplayPort::DisplayPort(port_index index, const char* baseName) + : + Port(index, baseName) +{ +} + + +bool +DisplayPort::IsConnected() +{ + addr_t portRegister = _PortRegister(); + + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + portRegister); + + if (portRegister == 0) + return false; + + if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0) { + TRACE("%s: %s link not detected\n", __func__, PortName()); + return false; + } + + return HasEDID(); +} + + +addr_t +DisplayPort::_DDCRegister() +{ + // TODO: Do VLV + CHV use the VLV_DP_AUX_CTL_B + VLV_DP_AUX_CTL_C? + switch (PortIndex()) { + case INTEL_PORT_A: + return INTEL_DP_AUX_CTL_A; + case INTEL_PORT_B: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return VLV_DP_AUX_CTL_B; + return INTEL_DP_AUX_CTL_B; + case INTEL_PORT_C: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return VLV_DP_AUX_CTL_C; + return INTEL_DP_AUX_CTL_C; + case INTEL_PORT_D: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) + return CHV_DP_AUX_CTL_D; + else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return 0; + return INTEL_DP_AUX_CTL_D; + default: + return 0; + } + + return 0; +} + + +addr_t +DisplayPort::_PortRegister() +{ + // There are 6000 lines of intel linux code probing DP registers + // to properly detect DP vs eDP to then in-turn properly figure out + // what is DP and what is HDMI. It only takes 3 lines to + // ignore DisplayPort on ValleyView / CherryView + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) { + ERROR("TODO: DisplayPort on ValleyView / CherryView"); + return 0; + } + + // Intel, are humans even involved anymore? + // This is a lot more complex than this code makes it look. (see defines) + // INTEL_DISPLAY_PORT_X moves around a lot based on PCH + // except on ValleyView and CherryView. + switch (PortIndex()) { + case INTEL_PORT_A: + return INTEL_DISPLAY_PORT_A; + case INTEL_PORT_B: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return VLV_DISPLAY_PORT_B; + return INTEL_DISPLAY_PORT_B; + case INTEL_PORT_C: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return VLV_DISPLAY_PORT_C; + return INTEL_DISPLAY_PORT_C; + case INTEL_PORT_D: + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) + return CHV_DISPLAY_PORT_D; + else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) + return 0; + return INTEL_DISPLAY_PORT_D; + default: + return 0; + } + + return 0; +} + + +status_t +DisplayPort::SetDisplayMode(display_mode* target, uint32 colorMode) +{ + TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width, + target->virtual_height); + + ERROR("TODO: DisplayPort\n"); + return B_ERROR; +} + + +// #pragma mark - Embedded DisplayPort + + +EmbeddedDisplayPort::EmbeddedDisplayPort() + : + DisplayPort(INTEL_PORT_A, "Embedded DisplayPort") +{ +} + + +bool +EmbeddedDisplayPort::IsConnected() +{ + addr_t portRegister = _PortRegister(); + + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + portRegister); + + if (!gInfo->shared_info->device_type.IsMobile()) { + TRACE("%s: skipping eDP on non-mobile GPU\n", __func__); + return false; + } + + if ((read32(portRegister) & DISPLAY_MONITOR_PORT_DETECTED) == 0) { + TRACE("%s: %s link not detected\n", __func__, PortName()); + return false; + } + + HasEDID(); + + // If eDP has EDID, awesome. We use it. + // No EDID? The modesetting code falls back to VBIOS panel_mode + return true; +} + + +// #pragma mark - Digital Display Port + + +DigitalDisplayInterface::DigitalDisplayInterface(port_index index, + const char* baseName) + : + Port(index, baseName) +{ + // As of Haswell, Intel decided to change eDP ports to a "DDI" bus... + // on a dare because the hardware engineers were drunk one night. +} + + +addr_t +DigitalDisplayInterface::_PortRegister() +{ + switch (PortIndex()) { + case INTEL_PORT_A: + return DDI_BUF_CTL_A; + case INTEL_PORT_B: + return DDI_BUF_CTL_B; + default: + return 0; + } + return 0; +} + + +addr_t +DigitalDisplayInterface::_DDCRegister() +{ + // TODO: No idea, does DDI have DDC? + return 0; +} + + +status_t +DigitalDisplayInterface::Power(bool enabled) +{ + TRACE("%s: %s DDI enabled: %s\n", __func__, PortName(), + enabled ? "true" : "false"); + + fPipe->Enable(enabled); + + addr_t portRegister = _PortRegister(); + uint32 state = read32(portRegister); + write32(portRegister, + enabled ? (state | DDI_BUF_CTL_ENABLE) : (state & ~DDI_BUF_CTL_ENABLE)); + read32(portRegister); + + return B_OK; +} + + +bool +DigitalDisplayInterface::IsConnected() +{ + addr_t portRegister = _PortRegister(); + + TRACE("%s: %s PortRegister: 0x%" B_PRIxADDR "\n", __func__, PortName(), + portRegister); + + if (portRegister == 0) + return false; + + if ((read32(portRegister) & DDI_INIT_DISPLAY_DETECTED) == 0) { + TRACE("%s: %s link not detected\n", __func__, PortName()); + return false; + } + + HasEDID(); + + return true; +} + + +status_t +DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode) +{ + TRACE("%s: %s %dx%d\n", __func__, PortName(), target->virtual_width, + target->virtual_height); + + if (fPipe == NULL) { + ERROR("%s: Setting display mode without assigned pipe!\n", __func__); + return B_ERROR; + } + + // Train FDI if it exists + FDILink* link = fPipe->FDI(); + if (link != NULL) + link->Train(target); + + pll_divisors divisors; + compute_pll_divisors(target, &divisors, false); + + uint32 extraPLLFlags = 0; + if (gInfo->shared_info->device_type.Generation() >= 3) + extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL; + + // Program general pipe config + fPipe->Configure(target); + + // Program pipe PLL's + fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + + // Program target display mode + fPipe->ConfigureTimings(target); + + // Set fCurrentMode to our set display mode + memcpy(&fCurrentMode, target, sizeof(display_mode)); + + return B_OK; +} diff --git a/src/add-ons/accelerants/intel_extreme/Ports.h b/src/add-ons/accelerants/intel_extreme/Ports.h new file mode 100644 index 0000000000..dc2883f81e --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/Ports.h @@ -0,0 +1,225 @@ +/* + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz, mmlr@mlotz.ch + * Alexander von Gluck IV, kallisti5@unixzen.com + */ +#ifndef INTEL_PORTS_H +#define INTEL_PORTS_H + + +#include + +#include "intel_extreme.h" + +#include "Pipes.h" +#include "pll.h" + + +#define MAX_PORTS 20 // a generous upper bound + +struct pll_limits; + +enum port_type { + INTEL_PORT_TYPE_ANY, // wildcard for lookup functions + INTEL_PORT_TYPE_ANALOG, + INTEL_PORT_TYPE_DVI, + INTEL_PORT_TYPE_LVDS, + INTEL_PORT_TYPE_DP, + INTEL_PORT_TYPE_eDP, + INTEL_PORT_TYPE_HDMI +}; + +enum port_index { + INTEL_PORT_ANY, // wildcard for lookup functions + INTEL_PORT_A, + INTEL_PORT_B, + INTEL_PORT_C, + INTEL_PORT_D +}; + + +class Port { +public: + Port(port_index index, + const char* baseName); +virtual ~Port(); + +virtual uint32 Type() const = 0; + const char* PortName() const + { return fPortName; } + + port_index PortIndex() const + { return fPortIndex; } + +virtual bool IsConnected() = 0; + + status_t SetPipe(Pipe* pipe); + ::Pipe* GetPipe() + { return fPipe; }; + +virtual status_t Power(bool enabled); + + bool HasEDID(); +virtual status_t GetEDID(edid1_info* edid, + bool forceRead = false); + +virtual status_t GetPLLLimits(pll_limits& limits); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode) { return B_ERROR; }; + +virtual pipe_index PipePreference() + { return INTEL_PIPE_ANY; }; + +protected: + void _SetName(const char* name); + +static status_t _GetI2CSignals(void* cookie, int* _clock, + int* _data); +static status_t _SetI2CSignals(void* cookie, int clock, + int data); + + display_mode fCurrentMode; + Pipe* fPipe; + +private: +virtual addr_t _DDCRegister() = 0; +virtual addr_t _PortRegister() = 0; + + port_index fPortIndex; + char* fPortName; + + status_t fEDIDState; + edid1_info fEDIDInfo; +}; + + +class AnalogPort : public Port { +public: + AnalogPort(); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_ANALOG; } + +virtual bool IsConnected(); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode); + +protected: +virtual addr_t _DDCRegister(); +virtual addr_t _PortRegister(); +}; + + +class LVDSPort : public Port { +public: + LVDSPort(); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_LVDS; } + +virtual bool IsConnected(); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode); + +virtual pipe_index PipePreference() + { return INTEL_PIPE_B; }; + +protected: +virtual addr_t _DDCRegister(); +virtual addr_t _PortRegister(); +}; + + +class DigitalPort : public Port { +public: + DigitalPort( + port_index index = INTEL_PORT_B, + const char* baseName = "DVI"); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_DVI; } + +virtual bool IsConnected(); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode); + +protected: +virtual addr_t _DDCRegister(); +virtual addr_t _PortRegister(); +}; + + +class HDMIPort : public DigitalPort { +public: + HDMIPort(port_index index); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_HDMI; } + +virtual bool IsConnected(); + +protected: +virtual addr_t _PortRegister(); +}; + + +class DisplayPort : public Port { +public: + DisplayPort(port_index index, + const char* baseName = "DisplayPort"); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_DP; } + +virtual bool IsConnected(); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode); + +protected: +virtual addr_t _DDCRegister(); +virtual addr_t _PortRegister(); +}; + + +class EmbeddedDisplayPort : public DisplayPort { +public: + EmbeddedDisplayPort(); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_eDP; } + +virtual bool IsConnected(); +}; + + +class DigitalDisplayInterface : public Port { +public: + DigitalDisplayInterface( + port_index index = INTEL_PORT_A, + const char* baseName = "Digital Display Interface"); + +virtual uint32 Type() const + { return INTEL_PORT_TYPE_DVI; } + +virtual status_t Power(bool enabled); + +virtual bool IsConnected(); + +virtual status_t SetDisplayMode(display_mode* mode, + uint32 colorMode); + +protected: +virtual addr_t _DDCRegister(); +virtual addr_t _PortRegister(); +}; + + +#endif // INTEL_PORTS_H diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.cpp b/src/add-ons/accelerants/intel_extreme/accelerant.cpp index 903fbd28e0..d2dcf44d82 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.cpp +++ b/src/add-ons/accelerants/intel_extreme/accelerant.cpp @@ -19,22 +19,25 @@ #include #include +#include + #include #undef TRACE #define TRACE_ACCELERANT #ifdef TRACE_ACCELERANT -# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) +# define TRACE(x...) _sPrintf("intel_extreme: " x) #else # define TRACE(x...) #endif -#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) +#define ERROR(x...) _sPrintf("intel_extreme: " x) #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) struct accelerant_info* gInfo; +uint32 gDumpCount; class AreaCloner { @@ -87,6 +90,36 @@ AreaCloner::Keep() // #pragma mark - +// intel_reg --mmio=ie-0001.bin --devid=27a2 dump +void +dump_registers() +{ + char filename[255]; + + sprintf(filename, "/boot/system/cache/tmp/ie-%04" B_PRId32 ".bin", + gDumpCount); + + ERROR("%s: Taking register dump #%" B_PRId32 "\n", __func__, gDumpCount); + + int fd = open(filename, O_CREAT | O_WRONLY, 0644); + uint32 data = 0; + if (fd >= 0) { + for (int32 i = 0; i < 0x80000; i += sizeof(data)) { + //char line[512]; + //int length = sprintf(line, "%05" B_PRIx32 ": " + // "%08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 "\n", + // i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12)); + data = read32(i); + write(fd, &data, sizeof(data)); + } + close(fd); + sync(); + } + + gDumpCount++; +} + + /*! This is the common accelerant_info initializer. It is called by both, the first accelerant and all clones. */ @@ -95,6 +128,9 @@ init_common(int device, bool isClone) { // initialize global accelerant info structure + // Number of register dumps we have... taken. + gDumpCount = 0; + gInfo = (accelerant_info*)malloc(sizeof(accelerant_info)); if (gInfo == NULL) return B_NO_MEMORY; @@ -147,7 +183,7 @@ init_common(int device, bool isClone) + gInfo->shared_info->overlay_offset); } - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) { + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { // allocate some extra memory for the 3D context if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE, B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) { @@ -156,6 +192,26 @@ init_common(int device, bool isClone) } } + gInfo->pipe_count = 0; + + // Allocate all of our pipes + for (int i = 0; i < MAX_PIPES; i++) { + switch (i) { + case 0: + gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A); + break; + case 1: + gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B); + break; + default: + ERROR("%s: Unknown pipe %d\n", __func__, i); + } + if (gInfo->pipes[i] == NULL) + ERROR("%s: Error allocating pipe %d\n", __func__, i); + else + gInfo->pipe_count++; + } + return B_OK; } @@ -179,6 +235,200 @@ uninit_common(void) } +static void +dump_ports() +{ + if (gInfo->port_count == 0) { + TRACE("%s: No ports connected\n", __func__); + return; + } + + TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__, + gInfo->port_count); + + for (uint32 i = 0; i < gInfo->port_count; i++) { + Port* port = gInfo->ports[i]; + if (!port) { + TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i); + continue; + } + TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(), + port->IsConnected() ? "connected" : "disconnected"); + } +} + + +static bool +has_connected_port(port_index portIndex, uint32 type) +{ + for (uint32 i = 0; i < gInfo->port_count; i++) { + Port* port = gInfo->ports[i]; + if (type != INTEL_PORT_TYPE_ANY && port->Type() != type) + continue; + if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex) + continue; + + return true; + } + + return false; +} + + +static status_t +probe_ports() +{ + // Try to determine what ports to use. We use the following heuristic: + // * Check for DisplayPort, these can be more or less detected reliably. + // * Check for HDMI, it'll fail on devices not having HDMI for us to fall + // back to DVI. + // * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by + // reading EDID in the IsConnected() call. + // * Check for analog if possible (there's a detection bit on PCH), + // otherwise the assumed presence is confirmed by reading EDID in + // IsConnected(). + + TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT)); + TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32 + ", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A), + read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C)); + TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT)); + + gInfo->port_count = 0; + for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) { + Port* displayPort = new(std::nothrow) DisplayPort((port_index)i); + if (displayPort == NULL) + return B_NO_MEMORY; + + if (displayPort->IsConnected()) + gInfo->ports[gInfo->port_count++] = displayPort; + else + delete displayPort; + } + + // Digital Display Interface + if (gInfo->shared_info->device_type.HasDDI()) { + for (int i = INTEL_PORT_A; i <= INTEL_PORT_B; i++) { + Port* ddiPort + = new(std::nothrow) DigitalDisplayInterface((port_index)i); + + if (ddiPort == NULL) + return B_NO_MEMORY; + + if (ddiPort->IsConnected()) + gInfo->ports[gInfo->port_count++] = ddiPort; + else + delete ddiPort; + } + } + + // Ensure DP_A isn't already taken (or DDI) + if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) { + // also always try eDP, it'll also just fail if not applicable + Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort(); + if (eDPPort == NULL) + return B_NO_MEMORY; + if (eDPPort->IsConnected()) + gInfo->ports[gInfo->port_count++] = eDPPort; + else + delete eDPPort; + } + + for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) { + if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) { + // Ensure port not already claimed by something like DDI + continue; + } + + Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i); + if (hdmiPort == NULL) + return B_NO_MEMORY; + + if (hdmiPort->IsConnected()) + gInfo->ports[gInfo->port_count++] = hdmiPort; + else + delete hdmiPort; + } + + if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) { + // there's neither DisplayPort nor HDMI so far, assume DVI B + Port* dviPort = new(std::nothrow) DigitalPort(INTEL_PORT_B); + if (dviPort == NULL) + return B_NO_MEMORY; + + if (dviPort->IsConnected()) { + gInfo->ports[gInfo->port_count++] = dviPort; + gInfo->head_mode |= HEAD_MODE_B_DIGITAL; + } else + delete dviPort; + } + + // always try the LVDS port, it'll simply fail if not applicable + Port* lvdsPort = new(std::nothrow) LVDSPort(); + if (lvdsPort == NULL) + return B_NO_MEMORY; + if (lvdsPort->IsConnected()) { + gInfo->ports[gInfo->port_count++] = lvdsPort; + gInfo->head_mode |= HEAD_MODE_LVDS_PANEL; + gInfo->head_mode |= HEAD_MODE_B_DIGITAL; + } else + delete lvdsPort; + + // then finally always try the analog port + Port* analogPort = new(std::nothrow) AnalogPort(); + if (analogPort == NULL) + return B_NO_MEMORY; + if (analogPort->IsConnected()) { + gInfo->ports[gInfo->port_count++] = analogPort; + gInfo->head_mode |= HEAD_MODE_A_ANALOG; + } else + delete analogPort; + + if (gInfo->port_count == 0) + return B_ERROR; + + return B_OK; +} + + +static status_t +assign_pipes() +{ + // TODO: At some point we should "group" ports to pipes with the same mode. + // You can drive multiple ports from a single pipe as long as the mode is + // the same. For the moment we could get displays with the wrong pipes + // assigned when the count is > 1; + + uint32 current = 0; + for (uint32 i = 0; i < gInfo->port_count; i++) { + if (gInfo->ports[i] == NULL) + continue; + + pipe_index preference = gInfo->ports[i]->PipePreference(); + if (preference != INTEL_PIPE_ANY) { + // Some ports *really* need to be assigned a pipe due to + // implementation bugs. + int index = (preference == INTEL_PIPE_B) ? 1 : 0; + gInfo->ports[i]->SetPipe(gInfo->pipes[index]); + continue; + } + + if (gInfo->ports[i]->IsConnected()) { + if (current >= gInfo->pipe_count) { + ERROR("%s: No pipes left to assign to port %s!\n", __func__, + gInfo->ports[i]->PortName()); + continue; + } + + gInfo->ports[i]->SetPipe(gInfo->pipes[current]); + current++; + } + } + + return B_OK; +} + + // #pragma mark - public accelerant functions @@ -199,36 +449,22 @@ intel_init_accelerant(int device) setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer"); - // determine head depending on what's already enabled from the BIOS - // TODO: it would be nicer to retrieve this data via DDC - else the - // display is gone for good if the BIOS decides to only show the - // picture on the connected analog monitor! - gInfo->head_mode = 0; - if (read32(INTEL_DISPLAY_B_PIPE_CONTROL) & DISPLAY_PIPE_ENABLED) - gInfo->head_mode |= HEAD_MODE_B_DIGITAL; - if (read32(INTEL_DISPLAY_A_PIPE_CONTROL) & DISPLAY_PIPE_ENABLED) - gInfo->head_mode |= HEAD_MODE_A_ANALOG; + TRACE("pipe control for: 0x%" B_PRIx32 " 0x%" B_PRIx32 "\n", + read32(INTEL_PIPE_CONTROL), read32(INTEL_PIPE_CONTROL)); - uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT); + // Probe all ports + status = probe_ports(); - // If we have an enabled display pipe we save the passed information and - // assume it is the valid panel size.. - // Later we query for proper EDID info if it exists, or figure something - // else out. (Default modes, etc.) - bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub(); - if ((hasPCH && (lvds & PCH_LVDS_DETECTED) != 0) - || (!hasPCH && (lvds & DISPLAY_PIPE_ENABLED) != 0)) { - save_lvds_mode(); - gInfo->head_mode |= HEAD_MODE_LVDS_PANEL; - } + // On TRACE, dump ports and states + dump_ports(); - TRACE("head detected: %#x\n", gInfo->head_mode); - TRACE("adpa: %08" B_PRIx32 ", dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32 - ", lvds: %08" B_PRIx32 "\n", - read32(INTEL_DISPLAY_A_ANALOG_PORT), - read32(INTEL_DISPLAY_A_DIGITAL_PORT), - read32(INTEL_DISPLAY_B_DIGITAL_PORT), - read32(INTEL_DISPLAY_LVDS_PORT)); + if (status != B_OK) + ERROR("Warning: zero active displays were found!\n"); + + status = assign_pipes(); + + if (status != B_OK) + ERROR("Warning: error while assigning pipes!\n"); status = create_mode_list(); if (status != B_OK) { @@ -325,8 +561,22 @@ intel_get_accelerant_device_info(accelerant_device_info* info) CALLED(); info->version = B_ACCELERANT_VERSION; - strcpy(info->name, gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx) - ? "Intel Extreme Graphics 1" : "Intel Extreme Graphics 2"); + + DeviceType* type = &gInfo->shared_info->device_type; + + if (type->InFamily(INTEL_FAMILY_7xx)) + strcpy(info->name, "Intel Extreme Graphics"); + else if (type->InFamily(INTEL_FAMILY_8xx)) + strcpy(info->name, "Intel Extreme Graphics 2"); + else if (type->InFamily(INTEL_FAMILY_9xx)) + strcpy(info->name, "Intel Graphics Media Accelerator"); + else if (type->InFamily(INTEL_FAMILY_SER5)) + strcpy(info->name, "Intel HD/Iris Graphics"); + else if (type->InFamily(INTEL_FAMILY_POVR)) + strcpy(info->name, "Intel PowerVR Graphics"); + else if (type->InFamily(INTEL_FAMILY_SOC0)) + strcpy(info->name, "Intel Atom Graphics"); + strcpy(info->chipset, gInfo->shared_info->device_identifier); strcpy(info->serial_no, "None"); diff --git a/src/add-ons/accelerants/intel_extreme/accelerant.h b/src/add-ons/accelerants/intel_extreme/accelerant.h index df5e274859..0b34dda2f1 100644 --- a/src/add-ons/accelerants/intel_extreme/accelerant.h +++ b/src/add-ons/accelerants/intel_extreme/accelerant.h @@ -14,6 +14,9 @@ #include #include +#include "Ports.h" +#include "Pipes.h" + struct overlay { overlay_buffer buffer; @@ -37,6 +40,8 @@ struct accelerant_info { intel_shared_info* shared_info; area_id shared_info_area; + display_mode current_mode; // pretty much a hack until per-display modes + display_mode* mode_list; // cloned list of standard display modes area_id mode_list_area; @@ -48,6 +53,12 @@ struct accelerant_info { uint32 last_vertical_overlay_scale; uint32 overlay_position_buffer_offset; + uint32 port_count; + Port* ports[MAX_PORTS]; + + uint32 pipe_count; + Pipe* pipes[MAX_PIPES]; + edid1_info edid_info; bool has_edid; @@ -59,15 +70,15 @@ struct accelerant_info { int device; uint8 head_mode; bool is_clone; - - // LVDS panel mode passed from the bios/startup. - display_mode lvds_panel_mode; }; -#define HEAD_MODE_A_ANALOG 0x01 -#define HEAD_MODE_B_DIGITAL 0x02 -#define HEAD_MODE_CLONE 0x03 -#define HEAD_MODE_LVDS_PANEL 0x08 + +#define HEAD_MODE_A_ANALOG 0x0001 +#define HEAD_MODE_B_DIGITAL 0x0002 +#define HEAD_MODE_CLONE 0x0003 +#define HEAD_MODE_LVDS_PANEL 0x0008 +#define HEAD_MODE_TESTING 0x1000 +#define HEAD_MODE_STIPPI 0x2000 extern accelerant_info* gInfo; @@ -89,6 +100,7 @@ write32(uint32 encodedRegister, uint32 value) + REGISTER_REGISTER(encodedRegister)) = value; } +void dump_registers(void); // dpms.cpp extern void enable_display_plane(bool enable); @@ -101,7 +113,6 @@ extern void setup_ring_buffer(ring_buffer &ringBuffer, const char* name); // modes.cpp extern void wait_for_vblank(void); extern void set_frame_buffer_base(void); -extern void save_lvds_mode(void); extern status_t create_mode_list(void); // memory.cpp diff --git a/src/add-ons/accelerants/intel_extreme/dpms.cpp b/src/add-ons/accelerants/intel_extreme/dpms.cpp index 6d8dc4496f..babdca6f84 100644 --- a/src/add-ons/accelerants/intel_extreme/dpms.cpp +++ b/src/add-ons/accelerants/intel_extreme/dpms.cpp @@ -14,84 +14,32 @@ #undef TRACE //#define TRACE_DPMS #ifdef TRACE_DPMS -# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) +# define TRACE(x...) _sPrintf("intel_extreme: " x) #else # define TRACE(x...) #endif -#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) +#define ERROR(x...) _sPrintf("intel_extreme: " x) #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) -void -enable_display_plane(bool enable) -{ - uint32 planeAControl = read32(INTEL_DISPLAY_A_CONTROL); - uint32 planeBControl = read32(INTEL_DISPLAY_B_CONTROL); - - if (enable) { - // when enabling the display, the register values are updated - // automatically - if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - write32(INTEL_DISPLAY_A_CONTROL, - planeAControl | DISPLAY_CONTROL_ENABLED); - } - - if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - write32(INTEL_DISPLAY_B_CONTROL, - planeBControl | DISPLAY_CONTROL_ENABLED); - } - - read32(INTEL_DISPLAY_A_BASE); - // flush the eventually cached PCI bus writes - } else { - // when disabling it, we have to trigger the update using a write to - // the display base address - if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - write32(INTEL_DISPLAY_A_CONTROL, - planeAControl & ~DISPLAY_CONTROL_ENABLED); - } - - if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - write32(INTEL_DISPLAY_B_CONTROL, - planeBControl & ~DISPLAY_CONTROL_ENABLED); - } - - set_frame_buffer_base(); - } -} - - static void -enable_display_pipe(bool enable) +enable_all_pipes(bool enable) { - uint32 pipeAControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL); - uint32 pipeBControl = read32(INTEL_DISPLAY_B_PIPE_CONTROL); + // Go over each port and enable pipe/plane + for (uint32 i = 0; i < gInfo->port_count; i++) { + if (gInfo->ports[i] == NULL) + continue; + if (!gInfo->ports[i]->IsConnected()) + continue; - if (enable) { - if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - write32(INTEL_DISPLAY_A_PIPE_CONTROL, - pipeAControl | DISPLAY_PIPE_ENABLED); - } - - if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - write32(INTEL_DISPLAY_B_PIPE_CONTROL, - pipeBControl | DISPLAY_PIPE_ENABLED); - } - } else { - if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - write32(INTEL_DISPLAY_A_PIPE_CONTROL, - pipeAControl & ~DISPLAY_PIPE_ENABLED); - } - - if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - write32(INTEL_DISPLAY_B_PIPE_CONTROL, - pipeBControl & ~DISPLAY_PIPE_ENABLED); - } + gInfo->ports[i]->Power(enable); } read32(INTEL_DISPLAY_A_BASE); - // flush the eventually cached PCI bus writes + // flush the possibly cached PCI bus writes + + set_frame_buffer_base(); } @@ -99,10 +47,6 @@ static void enable_lvds_panel(bool enable) { bool hasPCH = gInfo->shared_info->device_type.HasPlatformControlHub(); - if (hasPCH) { - // TODO: fix for PCH - return; - } int controlRegister = hasPCH ? PCH_PANEL_CONTROL : INTEL_PANEL_CONTROL; int statusRegister = hasPCH ? PCH_PANEL_STATUS : INTEL_PANEL_STATUS; @@ -166,8 +110,7 @@ set_display_power_mode(uint32 mode) spin(150); } - enable_display_pipe(true); - enable_display_plane(true); + enable_all_pipes(true); } wait_for_vblank(); @@ -188,25 +131,21 @@ set_display_power_mode(uint32 mode) } if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - write32(INTEL_DISPLAY_A_ANALOG_PORT, - (read32(INTEL_DISPLAY_A_ANALOG_PORT) + write32(INTEL_ANALOG_PORT, (read32(INTEL_ANALOG_PORT) & ~(DISPLAY_MONITOR_MODE_MASK | DISPLAY_MONITOR_PORT_ENABLED)) | monitorMode | (mode != B_DPMS_OFF ? DISPLAY_MONITOR_PORT_ENABLED : 0)); } + if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - write32(INTEL_DISPLAY_B_DIGITAL_PORT, - (read32(INTEL_DISPLAY_B_DIGITAL_PORT) - & ~(DISPLAY_MONITOR_MODE_MASK | DISPLAY_MONITOR_PORT_ENABLED)) + write32(INTEL_DIGITAL_PORT_B, (read32(INTEL_DIGITAL_PORT_B) + & ~(/*DISPLAY_MONITOR_MODE_MASK |*/ DISPLAY_MONITOR_PORT_ENABLED)) | (mode != B_DPMS_OFF ? DISPLAY_MONITOR_PORT_ENABLED : 0)); // TODO: monitorMode? } - if (mode != B_DPMS_ON) { - enable_display_plane(false); - wait_for_vblank(); - enable_display_pipe(false); - } + if (mode != B_DPMS_ON) + enable_all_pipes(false); if (mode == B_DPMS_OFF) { write32(INTEL_DISPLAY_A_PLL, read32(INTEL_DISPLAY_A_PLL) diff --git a/src/add-ons/accelerants/intel_extreme/engine.cpp b/src/add-ons/accelerants/intel_extreme/engine.cpp index d6ca9af367..231fb78d80 100644 --- a/src/add-ons/accelerants/intel_extreme/engine.cpp +++ b/src/add-ons/accelerants/intel_extreme/engine.cpp @@ -17,12 +17,12 @@ #undef TRACE //#define TRACE_ENGINE #ifdef TRACE_ENGINE -# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) +# define TRACE(x...) _sPrintf("intel_extreme: " x) #else # define TRACE(x...) #endif -#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) +#define ERROR(x...) _sPrintf("intel_extreme: " x) #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) diff --git a/src/add-ons/accelerants/intel_extreme/hooks.cpp b/src/add-ons/accelerants/intel_extreme/hooks.cpp index 975551b644..7e65040683 100644 --- a/src/add-ons/accelerants/intel_extreme/hooks.cpp +++ b/src/add-ons/accelerants/intel_extreme/hooks.cpp @@ -111,15 +111,15 @@ get_accelerant_hook(uint32 feature, void* data) return (void*)intel_overlay_supported_features; case B_ALLOCATE_OVERLAY_BUFFER: // TODO: overlay doesn't seem to work on these chips - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_91x) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_94x) - || gInfo->shared_info->device_type.IsModel(INTEL_TYPE_965M) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_VLV)) + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x) + || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_965M) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK) + || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_SER5) + || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_POVR) + || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_SOC0)) return NULL; return (void*)intel_allocate_overlay_buffer; diff --git a/src/add-ons/accelerants/intel_extreme/mode.cpp b/src/add-ons/accelerants/intel_extreme/mode.cpp old mode 100755 new mode 100644 index 961356b769..2438e99b38 --- a/src/add-ons/accelerants/intel_extreme/mode.cpp +++ b/src/add-ons/accelerants/intel_extreme/mode.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006-2014, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Support for i915 chipset and up based on the X driver, @@ -10,314 +10,39 @@ */ -#include "accelerant_protos.h" -#include "accelerant.h" -#include "utility.h" - -#include +#include #include #include -#include + +#include #include #include #include #include +#include "accelerant_protos.h" +#include "accelerant.h" +#include "pll.h" +#include "Ports.h" +#include "utility.h" + #undef TRACE #define TRACE_MODE #ifdef TRACE_MODE -# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) +# define TRACE(x...) _sPrintf("intel_extreme: " x) #else # define TRACE(x...) #endif -#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) +#define ERROR(x...) _sPrintf("intel_extreme: " x) #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) -struct display_registers { - uint32 pll; - uint32 divisors; - uint32 control; - uint32 pipe_config; - uint32 horiz_total; - uint32 horiz_blank; - uint32 horiz_sync; - uint32 vert_total; - uint32 vert_blank; - uint32 vert_sync; - uint32 size; - uint32 stride; - uint32 position; - uint32 pipe_source; -}; - -struct pll_divisors { - uint32 post; - uint32 post1; - uint32 post2; - bool post2_high; - uint32 n; - uint32 m; - uint32 m1; - uint32 m2; -}; - -struct pll_limits { - pll_divisors min; - pll_divisors max; - uint32 min_post2_frequency; - uint32 min_vco; - uint32 max_vco; -}; - -struct gpio_map { - const char* name; - uint32 pin; - uint32 validOn; -}; - - -static void mode_fill_missing_bits(display_mode *, uint32); - - -static status_t -get_i2c_signals(void* cookie, int* _clock, int* _data) -{ - uint32 ioRegister = (uint32)(addr_t)cookie; - uint32 value = read32(ioRegister); - - *_clock = (value & I2C_CLOCK_VALUE_IN) != 0; - *_data = (value & I2C_DATA_VALUE_IN) != 0; - - return B_OK; -} - - -static status_t -set_i2c_signals(void* cookie, int clock, int data) -{ - uint32 ioRegister = (uint32)(addr_t)cookie; - uint32 value; - - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_83x)) { - // on these chips, the reserved values are fixed - value = 0; - } else { - // on all others, we have to preserve them manually - value = read32(ioRegister) & I2C_RESERVED; - } - - if (data != 0) - value |= I2C_DATA_DIRECTION_MASK; - else { - value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT - | I2C_DATA_VALUE_MASK; - } - - if (clock != 0) - value |= I2C_CLOCK_DIRECTION_MASK; - else { - value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT - | I2C_CLOCK_VALUE_MASK; - } - - write32(ioRegister, value); - read32(ioRegister); - // make sure the PCI bus has flushed the write - - return B_OK; -} - - -static void -get_pll_limits(pll_limits &limits) -{ - // Note, the limits are taken from the X driver; they have not yet been - // tested - - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB) - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_VLV)) { - // TODO: support LVDS output limits as well - static const pll_limits kLimits = { - // p, p1, p2, high, n, m, m1, m2 - { 5, 1, 10, false, 1, 79, 12, 5}, // min - { 80, 8, 5, true, 5, 127, 22, 9}, // max - 225000, 1760000, 3510000 - }; - limits = kLimits; - } else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) { - // TODO: support LVDS output limits as well - static const pll_limits kLimits = { - // p, p1, p2, high, n, m, m1, m2 - { 10, 1, 10, false, 1, 104, 17, 5}, // min - { 30, 3, 10, true, 4, 138, 23, 11}, // max - 270000, 1750000, 3500000 - }; - limits = kLimits; - } else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - // TODO: support LVDS output limits as well - // m1 is reserved and must be 0 - static const pll_limits kLimits = { - // p, p1, p2, high, n, m, m1, m2 - { 5, 1, 10, false, 3, 2, 0, 2}, // min - { 80, 8, 5, true, 6, 256, 0, 256}, // max - 200000, 1700000, 3500000 - }; - limits = kLimits; - } else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { - // TODO: support LVDS output limits as well - // (Update: Output limits are adjusted in the computation (post2=7/14)) - // Should move them here! - static const pll_limits kLimits = { - // p, p1, p2, high, n, m, m1, m2 - { 5, 1, 10, false, 5, 70, 12, 7}, // min - { 80, 8, 5, true, 10, 120, 22, 11}, // max - 200000, 1400000, 2800000 - }; - limits = kLimits; - } else { - // TODO: support LVDS output limits as well - static const pll_limits kLimits = { - // p, p1, p2, high, n, m, m1, m2 - { 4, 2, 4, false, 5, 96, 20, 8}, - {128, 33, 2, true, 18, 140, 28, 18}, - 165000, 930000, 1400000 - }; - limits = kLimits; - } - - TRACE("PLL limits, min: p %" B_PRIu32 " (p1 %" B_PRIu32 ", p2 %" B_PRIu32 - "), n %" B_PRIu32 ", m %" B_PRIu32 " (m1 %" B_PRIu32 ", m2 %" B_PRIu32 - ")\n", limits.min.post, limits.min.post1, limits.min.post2, - limits.min.n, limits.min.m, limits.min.m1, limits.min.m2); - TRACE("PLL limits, max: p %" B_PRIu32 " (p1 %" B_PRIu32 ", p2 %" B_PRIu32 - "), n %" B_PRIu32 ", m %" B_PRIu32 " (m1 %" B_PRIu32 ", m2 %" B_PRIu32 - ")\n", limits.max.post, limits.max.post1, limits.max.post2, - limits.max.n, limits.max.m, limits.max.m1, limits.max.m2); -} - - -static bool -valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits) -{ - pll_info &info = gInfo->shared_info->pll_info; - uint32 vco = info.reference_frequency * divisors.m / divisors.n; - uint32 frequency = vco / divisors.post; - - if (divisors.post < limits.min.post || divisors.post > limits.max.post - || divisors.m < limits.min.m || divisors.m > limits.max.m - || vco < limits.min_vco || vco > limits.max_vco - || frequency < info.min_frequency || frequency > info.max_frequency) - return false; - - return true; -} - - -static void -compute_pll_divisors(const display_mode ¤t, pll_divisors& divisors, - bool isLVDS) -{ - float requestedPixelClock = current.timing.pixel_clock / 1000.0f; - float referenceClock - = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; - pll_limits limits; - get_pll_limits(limits); - - TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock); - - if (isLVDS) { - if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK) - == LVDS_CLKB_POWER_UP) - divisors.post2 = LVDS_POST2_RATE_FAST; - else - divisors.post2 = LVDS_POST2_RATE_SLOW; - } else { - if (current.timing.pixel_clock < limits.min_post2_frequency) { - // slow DAC timing - divisors.post2 = limits.min.post2; - divisors.post2_high = limits.min.post2_high; - } else { - // fast DAC timing - divisors.post2 = limits.max.post2; - divisors.post2_high = limits.max.post2_high; - } - } - - float best = requestedPixelClock; - pll_divisors bestDivisors; - - bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD); - for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1; - divisors.m1++) { - for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2 - && ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) { - for (divisors.n = limits.min.n; divisors.n <= limits.max.n; - divisors.n++) { - for (divisors.post1 = limits.min.post1; - divisors.post1 <= limits.max.post1; divisors.post1++) { - divisors.m = 5 * divisors.m1 + divisors.m2; - divisors.post = divisors.post1 * divisors.post2; - - if (!valid_pll_divisors(divisors, limits)) - continue; - - float error = fabs(requestedPixelClock - - ((referenceClock * divisors.m) / divisors.n) - / divisors.post); - if (error < best) { - best = error; - bestDivisors = divisors; - - if (error == 0) - break; - } - } - } - } - } - - divisors = bestDivisors; - - TRACE("%s: found: %g MHz, p = %" B_PRIu32 " (p1 = %" B_PRIu32 ", p2 = %" - B_PRIu32 "), n = %" B_PRIu32 ", m = %" B_PRIu32 " (m1 = %" B_PRIu32 - ", m2 = %" B_PRIu32 ")\n", __func__, - ((referenceClock * divisors.m) / divisors.n) / divisors.post, - divisors.post, divisors.post1, divisors.post2, divisors.n, - divisors.m, divisors.m1, divisors.m2); -} - - -static void -mode_fill_missing_bits(display_mode *mode, uint32 cntrl) -{ - uint32 value = read32(cntrl); - - switch (value & DISPLAY_CONTROL_COLOR_MASK) { - case DISPLAY_CONTROL_RGB32: - default: - mode->space = B_RGB32; - break; - case DISPLAY_CONTROL_RGB16: - mode->space = B_RGB16; - break; - case DISPLAY_CONTROL_RGB15: - mode->space = B_RGB15; - break; - case DISPLAY_CONTROL_CMAP8: - mode->space = B_CMAP8; - break; - } - - mode->flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS; -} - - +#if 0 +// This hack needs to die. Leaving in for a little while +// incase we *really* need it. static void retrieve_current_mode(display_mode& mode, uint32 pllRegister) { @@ -351,13 +76,12 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister) imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE; controlRegister = INTEL_DISPLAY_B_CONTROL; } else { - // TODO: not supported - TRACE("%s: pllRegister not yet supported\n", __func__); + ERROR("%s: PLL not supported\n", __func__); return; } pll_divisors divisors; - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { divisors.m1 = 0; divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK) >> DISPLAY_PLL_M2_DIVISOR_SHIFT; @@ -373,10 +97,12 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister) } pll_limits limits; - get_pll_limits(limits); + get_pll_limits(&limits, false); + // TODO: Detect LVDS connector vs assume no - if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { + if (gInfo->shared_info->device_type.Generation() >= 3) { + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK) >> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; } else { @@ -385,7 +111,7 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister) } if (pllRegister == INTEL_DISPLAY_B_PLL - && !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) { + && !gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { // TODO: Fix this? Need to support dual channel LVDS. divisors.post2 = LVDS_POST2_RATE_SLOW; } else { @@ -448,14 +174,29 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister) if (mode.virtual_height < mode.timing.v_display) mode.virtual_height = mode.timing.v_display; - mode_fill_missing_bits(&mode, controlRegister); + value = read32(controlRegister); + switch (value & DISPLAY_CONTROL_COLOR_MASK) { + case DISPLAY_CONTROL_RGB32: + default: + mode.space = B_RGB32; + break; + case DISPLAY_CONTROL_RGB16: + mode.space = B_RGB16; + break; + case DISPLAY_CONTROL_RGB15: + mode.space = B_RGB15; + break; + case DISPLAY_CONTROL_CMAP8: + mode.space = B_CMAP8; + break; + } mode.h_display_start = 0; mode.v_display_start = 0; - if (gInfo->overlay_registers != NULL) { - mode.flags |= B_SUPPORTS_OVERLAYS; - } + mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS + | B_DPMS | B_SUPPORTS_OVERLAYS; } +#endif static void @@ -502,12 +243,15 @@ sanitize_display_mode(display_mode& mode) { // Some cards only support even pixel counts, while others require an odd // one. - bool olderCard = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_Gxx); - olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x); - olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_94x); - olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_91x); - olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_8xx); - olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx); + uint16 pixelCount = 1; + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x) + || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx) + || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_7xx)) { + pixelCount = 2; + } // TODO: verify constraints - these are more or less taken from the // radeon driver! @@ -518,32 +262,15 @@ sanitize_display_mode(display_mode& mode) gInfo->shared_info->pll_info.min_frequency, gInfo->shared_info->pll_info.max_frequency, // horizontal - {1, 0, 8160, 32, 8192, 0, 8192}, + {pixelCount, 0, 8160, 32, 8192, 0, 8192}, {1, 1, 4092, 2, 63, 1, 4096} }; - if (olderCard) - constraints.horizontal_timing.resolution = 2; - return sanitize_display_mode(mode, constraints, gInfo->has_edid ? &gInfo->edid_info : NULL); } -static bool -check_and_sanitize_display_mode(display_mode* mode) -{ - uint16 width = mode->timing.h_display; - uint16 height = mode->timing.v_display; - - // Only accept the mode if it is within the supported resolution - // TODO: sanitize_display_mode() should report resolution changes - // differently! - return !sanitize_display_mode(*mode) || (width == mode->timing.h_display - && height == mode->timing.v_display); -} - - // #pragma mark - @@ -551,24 +278,24 @@ void set_frame_buffer_base() { intel_shared_info &sharedInfo = *gInfo->shared_info; - display_mode &mode = sharedInfo.current_mode; + display_mode &mode = gInfo->current_mode; + uint32 baseRegister; uint32 surfaceRegister; - if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) { - baseRegister = INTEL_DISPLAY_B_BASE; - surfaceRegister = INTEL_DISPLAY_B_SURFACE; - } else { + if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { baseRegister = INTEL_DISPLAY_A_BASE; surfaceRegister = INTEL_DISPLAY_A_SURFACE; + } else { + baseRegister = INTEL_DISPLAY_B_BASE; + surfaceRegister = INTEL_DISPLAY_B_SURFACE; } - if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x) - || sharedInfo.device_type.InGroup(INTEL_TYPE_G4x) - || sharedInfo.device_type.InGroup(INTEL_TYPE_ILK) - || sharedInfo.device_type.InGroup(INTEL_TYPE_SNB) - || sharedInfo.device_type.InGroup(INTEL_TYPE_IVB) - || sharedInfo.device_type.InGroup(INTEL_TYPE_VLV)) { + if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x) + || sharedInfo.device_type.InGroup(INTEL_GROUP_G4x) + || sharedInfo.device_type.InGroup(INTEL_GROUP_ILK) + || sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5) + || sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) { write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); read32(baseRegister); @@ -589,96 +316,46 @@ set_frame_buffer_base() status_t create_mode_list(void) { - // TODO: We may want to choose different GPIO pin maps - // for different generations of cards... not sure - const gpio_map gpioPinMap[] = { - {"ssc", INTEL_I2C_IO_B, 0}, - {"vga", INTEL_I2C_IO_A, HEAD_MODE_A_ANALOG}, - {"lvds", INTEL_I2C_IO_C, HEAD_MODE_LVDS_PANEL}, - {"dpc", INTEL_I2C_IO_D, 0}, - {"dpb", INTEL_I2C_IO_E, 0}, - {"dpd", INTEL_I2C_IO_F, 0}, - }; - - // TODO: We may want to do extra validation on gpio validOn - // vs the HEAD_MODE_ in head_mode - for (uint32 i = 0; i < sizeof(gpioPinMap) / sizeof(gpioPinMap[0]); i++) { - i2c_bus bus; - bus.cookie = (void*)(uintptr_t)gpioPinMap[i].pin; - bus.set_signals = &set_i2c_signals; - bus.get_signals = &get_i2c_signals; - ddc2_init_timing(&bus); - - status_t result = ddc2_read_edid1(&bus, &gInfo->edid_info, - NULL, NULL); - - if (result != B_OK) + for (uint32 i = 0; i < gInfo->port_count; i++) { + if (gInfo->ports[i] == NULL) continue; - TRACE("found edid data on gpio '%s'\n", gpioPinMap[i].name); - - edid_dump(&gInfo->edid_info); - gInfo->has_edid = true; - if (gInfo->shared_info->single_head_locked) - gInfo->head_mode = HEAD_MODE_A_ANALOG; - - // TODO: We may want to probe multiple GPIO pins here - // someday and store valid ones for multi-head support. - // For now, we break on the first valid one. - break; + status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info); + if (status == B_OK) + gInfo->has_edid = true; } - if (!gInfo->has_edid) { + // If no EDID, but have vbt from driver, use that mode + if (!gInfo->has_edid && gInfo->shared_info->got_vbt) { // We could not read any EDID info. Fallback to creating a list with // only the mode set up by the BIOS. + // TODO: support lower modes via scaling and windowing - if (((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0 - && (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0) - || ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0 - && gInfo->shared_info->got_vbt)) { - size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) - & ~(B_PAGE_SIZE - 1); + size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) + & ~(B_PAGE_SIZE - 1); - display_mode* list; - area_id area = create_area("intel extreme modes", - (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, - B_READ_AREA | B_WRITE_AREA); - if (area < B_OK) - return area; + display_mode* list; + area_id area = create_area("intel extreme modes", + (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, + B_READ_AREA | B_WRITE_AREA); + if (area < 0) + return area; - // Prefer information dumped directly from VBT, as the BIOS - // one may have display scaling, but only do this if the VBT - // resolution is higher than the BIOS one. - if (gInfo->shared_info->got_vbt - && gInfo->shared_info->current_mode.virtual_width - >= gInfo->lvds_panel_mode.virtual_width - && gInfo->shared_info->current_mode.virtual_height - >= gInfo->lvds_panel_mode.virtual_height) { - memcpy(list, &gInfo->shared_info->current_mode, - sizeof(display_mode)); - mode_fill_missing_bits(list, INTEL_DISPLAY_B_CONTROL); - } else { - memcpy(list, &gInfo->lvds_panel_mode, - sizeof(display_mode)); + memcpy(list, &gInfo->shared_info->panel_mode, sizeof(display_mode)); - if (gInfo->shared_info->got_vbt) - TRACE("intel_extreme: ignoring VBT mode."); - } - - gInfo->mode_list_area = area; - gInfo->mode_list = list; - gInfo->shared_info->mode_list_area = gInfo->mode_list_area; - gInfo->shared_info->mode_count = 1; - return B_OK; - } + gInfo->mode_list_area = area; + gInfo->mode_list = list; + gInfo->shared_info->mode_list_area = gInfo->mode_list_area; + gInfo->shared_info->mode_count = 1; + return B_OK; } // Otherwise return the 'real' list of modes display_mode* list; uint32 count = 0; gInfo->mode_list_area = create_display_modes("intel extreme modes", - gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, - &check_and_sanitize_display_mode, &list, &count); + gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL, + &list, &count); if (gInfo->mode_list_area < B_OK) return gInfo->mode_list_area; @@ -700,24 +377,6 @@ wait_for_vblank(void) } -/*! Store away panel information if identified on startup - (used for pipe B->lvds). -*/ -void -save_lvds_mode(void) -{ - // dump currently programmed mode. - display_mode biosMode; - retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL); - - sanitize_display_mode(biosMode); - // The BIOS mode may not be a valid mode, as LVDS output does not - // really care about the sync values - - gInfo->lvds_panel_mode = biosMode; -} - - // #pragma mark - @@ -747,7 +406,7 @@ intel_propose_display_mode(display_mode* target, const display_mode* low, // first search for the specified mode in the list, if no mode is found // try to fix the target mode in sanitize_display_mode - // TODO: Only sanitize_display_mode should be used. However, at the moment + // TODO: Only sanitize_display_mode should be used. However, at the moments // the mode constraints are not optimal and do not work for all // configurations. for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { @@ -774,12 +433,12 @@ intel_propose_display_mode(display_mode* target, const display_mode* low, status_t intel_set_display_mode(display_mode* mode) { - if (mode == NULL) - return B_BAD_VALUE; - TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, mode->virtual_width, mode->virtual_height); + if (mode == NULL) + return B_BAD_VALUE; + display_mode target = *mode; // TODO: it may be acceptable to continue when using panel fitting or @@ -793,31 +452,16 @@ intel_set_display_mode(display_mode* mode) uint32 colorMode, bytesPerRow, bitsPerPixel; get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); - // TODO stop here, when the requested mode is the same as the current one. - // This would avoid screen flickering when setting a mode that's already in - // place. - -#if 0 -static bool first = true; -if (first) { - int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644); - if (fd >= 0) { - for (int32 i = 0; i < 0x80000; i += 16) { - char line[512]; - int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n", - i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12)); - write(fd, line, length); - } - close(fd); - sync(); - } - first = false; -} -#endif + // TODO: do not go further if the mode is identical to the current one. + // This would avoid the screen being off when switching workspaces when they + // have the same resolution. intel_shared_info &sharedInfo = *gInfo->shared_info; Autolock locker(sharedInfo.accelerant_lock); + // First register dump + dump_registers(); + // TODO: This may not be neccesary set_display_power_mode(B_DPMS_OFF); @@ -830,7 +474,7 @@ if (first) { base) < B_OK) { // oh, how did that happen? Unfortunately, there is no really good way // back - if (intel_allocate_memory(sharedInfo.current_mode.virtual_height + if (intel_allocate_memory(gInfo->current_mode.virtual_height * sharedInfo.bytes_per_row, 0, base) == B_OK) { sharedInfo.frame_buffer = base; sharedInfo.frame_buffer_offset = base @@ -847,374 +491,108 @@ if (first) { sharedInfo.frame_buffer = base; sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; +#if 0 + if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { + // 1. Enable panel power as needed to retrieve panel configuration + // (use AUX VDD enable bit) + // skip, did detection already, might need that before that though + + // 2. Enable PCH clock reference source and PCH SSC modulator, + // wait for warmup (Can be done anytime before enabling port) + // skip, most certainly already set up by bios to use other ports, + // will need for coldstart though + + // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime + // before enabling CPU pipe or port) + // a. Enable PCH 120MHz clock source output to CPU, wait for DMI + // latency + // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A + // register, wait for warmup + // skip, not doing eDP right now, should go into + // EmbeddedDisplayPort class though + + // 4. If enabling port on PCH: (Must be done before enabling CPU pipe + // or FDI) + // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency + // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) + // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup + // d. [DevILK] CPU FDI PLL is always on and does not need to be + // enabled + FDILink* link = pipe->FDILink(); + if (link != NULL) { + link->Receiver().EnablePLL(); + link->Receiver().SwitchClock(true); + link->Transmitter().EnablePLL(); + } + + // 5. Enable CPU panel fitter if needed for hires, required for VGA + // (Can be done anytime before enabling CPU pipe) + PanelFitter* fitter = pipe->PanelFitter(); + if (fitter != NULL) + fitter->Enable(mode); + + // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings + // (Can be done anytime before enabling CPU pipe) + pll_divisors divisors; + compute_pll_divisors(target, divisors, false); + pipe->ConfigureTimings(divisors); + + // 7. Enable CPU pipe + pipe->Enable(); + +8. Configure and enable CPU planes (VGA or hires) +9. If enabling port on PCH: + // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking + // b. Train FDI + // i. Set pre-emphasis and voltage (iterate if training steps fail) + ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. + iii. Wait for FDI training pattern 1 time + iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) + v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver + vi. Wait for FDI training pattern 2 time + vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no + lock) + viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver + ix. Wait for FDI idle pattern time for link to become active + c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling + PCH transcoder) + d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the + transcoder. + e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. + f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). + g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used + h. Enable PCH transcoder +10. Enable ports (DisplayPort must enable in training pattern 1) +11. Enable panel power through panel power sequencing +12. Wait for panel power sequencing to reach enabled steady state +13. Disable panel power override +14. If DisplayPort, complete link training +15. Enable panel backlight + } +#endif + // make sure VGA display is disabled write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); read32(INTEL_VGA_DISPLAY_CONTROL); - if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) { - // For LVDS panels, we actually always set the native mode in hardware - // Then we use the panel fitter to scale the picture to that. - display_mode hardwareTarget; - bool needsScaling = false; + // Go over each port and set the display mode + for (uint32 i = 0; i < gInfo->port_count; i++) { + if (gInfo->ports[i] == NULL) + continue; + if (!gInfo->ports[i]->IsConnected()) + continue; - // Try to get the panel preferred screen mode from EDID info - if (gInfo->has_edid) { - hardwareTarget.space = target.space; - hardwareTarget.virtual_width - = gInfo->edid_info.std_timing[0].h_size; - hardwareTarget.virtual_height - = gInfo->edid_info.std_timing[0].v_size; - for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) { - if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type - == EDID1_IS_DETAILED_TIMING) { - hardwareTarget.virtual_width = gInfo->edid_info - .detailed_monitor[i].data.detailed_timing.h_active; - hardwareTarget.virtual_height = gInfo->edid_info - .detailed_monitor[i].data.detailed_timing.v_active; - break; - } - } - TRACE("%s: hardware mode will actually be %dx%d\n", __func__, - hardwareTarget.virtual_width, hardwareTarget.virtual_height); - if ((hardwareTarget.virtual_width <= target.virtual_width - && hardwareTarget.virtual_height <= target.virtual_height - && hardwareTarget.space <= target.space) - || intel_propose_display_mode(&hardwareTarget, mode, mode)) { - hardwareTarget = target; - } else - needsScaling = true; - } else { - // We don't have EDID data, try to set the requested mode directly - hardwareTarget = target; - } - - pll_divisors divisors; - if (needsScaling) - compute_pll_divisors(hardwareTarget, divisors, true); - else - compute_pll_divisors(target, divisors, true); - - uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED; - if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { - dpll |= LVDS_PLL_MODE_LVDS; - // DPLL mode LVDS for i915+ - } - - // Compute bitmask from p1 value - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - dpll |= (1 << (divisors.post1 - 1)) - << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; - } else { - dpll |= (1 << (divisors.post1 - 1)) - << DISPLAY_PLL_POST1_DIVISOR_SHIFT; - } - switch (divisors.post2) { - case 5: - case 7: - dpll |= DISPLAY_PLL_DIVIDE_HIGH; - break; - } - - // Disable panel fitting, but enable 8 to 6-bit dithering - write32(INTEL_PANEL_FIT_CONTROL, 0x4); - // TODO: do not do this if the connected panel is 24-bit - // (I don't know how to detect that) - - if ((dpll & DISPLAY_PLL_ENABLED) != 0) { - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, - (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_N_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); - } else { - write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, - (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_N_DIVISOR_MASK) - | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) - & DISPLAY_PLL_M1_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_M2_DIVISOR_MASK)); - } - write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED); - read32(INTEL_DISPLAY_B_PLL); - spin(150); - } - - uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT) | LVDS_PORT_EN - | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; - - lvds |= LVDS_18BIT_DITHER; - // TODO: do not do this if the connected panel is 24-bit - // (I don't know how to detect that) - - float referenceClock = gInfo->shared_info->pll_info.reference_frequency - / 1000.0f; - - // Set the B0-B3 data pairs corresponding to whether we're going to - // set the DPLLs for dual-channel mode or not. - if (divisors.post2 == LVDS_POST2_RATE_FAST) - lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP; - else - lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP); - - write32(INTEL_DISPLAY_LVDS_PORT, lvds); - read32(INTEL_DISPLAY_LVDS_PORT); - - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, - (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_N_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); - } else { - write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, - (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_N_DIVISOR_MASK) - | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) - & DISPLAY_PLL_M1_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_M2_DIVISOR_MASK)); - } - - write32(INTEL_DISPLAY_B_PLL, dpll); - read32(INTEL_DISPLAY_B_PLL); - - // Wait for the clocks to stabilize - spin(150); - - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) { - float adjusted = ((referenceClock * divisors.m) / divisors.n) - / divisors.post; - uint32 pixelMultiply; - if (needsScaling) { - pixelMultiply = uint32(adjusted - / (hardwareTarget.timing.pixel_clock / 1000.0f)); - } else { - pixelMultiply = uint32(adjusted - / (target.timing.pixel_clock / 1000.0f)); - } - - write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24) - | ((pixelMultiply - 1) << 8)); - } else - write32(INTEL_DISPLAY_B_PLL, dpll); - - read32(INTEL_DISPLAY_B_PLL); - spin(150); - - // update timing parameters - if (needsScaling) { - // TODO: Alternatively, it should be possible to use the panel - // fitter and scale the picture. - - // TODO: Perform some sanity check, for example if the target is - // wider than the hardware mode we end up with negative borders and - // broken timings - uint32 borderWidth = hardwareTarget.timing.h_display - - target.timing.h_display; - - uint32 syncWidth = hardwareTarget.timing.h_sync_end - - hardwareTarget.timing.h_sync_start; - - uint32 syncCenter = target.timing.h_display - + (hardwareTarget.timing.h_total - - target.timing.h_display) / 2; - - write32(INTEL_DISPLAY_B_HTOTAL, - ((uint32)(hardwareTarget.timing.h_total - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - write32(INTEL_DISPLAY_B_HBLANK, - ((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1) - << 16) - | ((uint32)target.timing.h_display + borderWidth / 2 - 1)); - write32(INTEL_DISPLAY_B_HSYNC, - ((uint32)(syncCenter + syncWidth / 2 - 1) << 16) - | ((uint32)syncCenter - syncWidth / 2 - 1)); - - uint32 borderHeight = hardwareTarget.timing.v_display - - target.timing.v_display; - - uint32 syncHeight = hardwareTarget.timing.v_sync_end - - hardwareTarget.timing.v_sync_start; - - syncCenter = target.timing.v_display - + (hardwareTarget.timing.v_total - - target.timing.v_display) / 2; - - write32(INTEL_DISPLAY_B_VTOTAL, - ((uint32)(hardwareTarget.timing.v_total - 1) << 16) - | ((uint32)target.timing.v_display - 1)); - write32(INTEL_DISPLAY_B_VBLANK, - ((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1) - << 16) - | ((uint32)target.timing.v_display - + borderHeight / 2 - 1)); - write32(INTEL_DISPLAY_B_VSYNC, - ((uint32)(syncCenter + syncHeight / 2 - 1) << 16) - | ((uint32)syncCenter - syncHeight / 2 - 1)); - - // This is useful for debugging: it sets the border to red, so you - // can see what is border and what is porch (black area around the - // sync) - // write32(0x61020, 0x00FF0000); - } else { - write32(INTEL_DISPLAY_B_HTOTAL, - ((uint32)(target.timing.h_total - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - write32(INTEL_DISPLAY_B_HBLANK, - ((uint32)(target.timing.h_total - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - write32(INTEL_DISPLAY_B_HSYNC, - ((uint32)(target.timing.h_sync_end - 1) << 16) - | ((uint32)target.timing.h_sync_start - 1)); - - write32(INTEL_DISPLAY_B_VTOTAL, - ((uint32)(target.timing.v_total - 1) << 16) - | ((uint32)target.timing.v_display - 1)); - write32(INTEL_DISPLAY_B_VBLANK, - ((uint32)(target.timing.v_total - 1) << 16) - | ((uint32)target.timing.v_display - 1)); - write32(INTEL_DISPLAY_B_VSYNC, ( - (uint32)(target.timing.v_sync_end - 1) << 16) - | ((uint32)target.timing.v_sync_start - 1)); - } - - write32(INTEL_DISPLAY_B_IMAGE_SIZE, - ((uint32)(target.virtual_width - 1) << 16) - | ((uint32)target.virtual_height - 1)); - - write32(INTEL_DISPLAY_B_POS, 0); - write32(INTEL_DISPLAY_B_PIPE_SIZE, - ((uint32)(target.timing.v_display - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - - write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL) - & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) - | colorMode); - - write32(INTEL_DISPLAY_B_PIPE_CONTROL, - read32(INTEL_DISPLAY_B_PIPE_CONTROL) | DISPLAY_PIPE_ENABLED); - read32(INTEL_DISPLAY_B_PIPE_CONTROL); + status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); + if (status != B_OK) + ERROR("%s: Unable to set display mode!\n", __func__); } - if ((gInfo->head_mode & HEAD_MODE_A_ANALOG) != 0) { - pll_divisors divisors; - compute_pll_divisors(target, divisors, false); + TRACE("%s: Port configuration completed successfully!\n", __func__); - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - write32(INTEL_DISPLAY_A_PLL_DIVISOR_0, - (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_N_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); - } else { - write32(INTEL_DISPLAY_A_PLL_DIVISOR_0, - (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) - & DISPLAY_PLL_N_DIVISOR_MASK) - | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) - & DISPLAY_PLL_M1_DIVISOR_MASK) - | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) - & DISPLAY_PLL_M2_DIVISOR_MASK)); - } - - uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL; - if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { - pll |= ((1 << (divisors.post1 - 1)) - << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT) - & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK; - } else { - pll |= ((1 << (divisors.post1 - 1)) - << DISPLAY_PLL_POST1_DIVISOR_SHIFT) - & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; -// pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) -// & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; - } - if (divisors.post2_high) - pll |= DISPLAY_PLL_DIVIDE_HIGH; - - pll |= DISPLAY_PLL_MODE_ANALOG; - - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) - pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT; - } else { - if (!divisors.post2_high) - pll |= DISPLAY_PLL_DIVIDE_4X; - - pll |= DISPLAY_PLL_2X_CLOCK; - - if (divisors.post1 > 2) { - pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) - & DISPLAY_PLL_POST1_DIVISOR_MASK; - } else - pll |= DISPLAY_PLL_POST1_DIVIDE_2; - } - - // Programmer's Ref says we must allow the DPLL to "warm up" before starting the plane - // so mask its bit, wait, enable its bit - write32(INTEL_DISPLAY_A_PLL, pll & ~DISPLAY_PLL_NO_VGA_CONTROL); - read32(INTEL_DISPLAY_A_PLL); - spin(150); - write32(INTEL_DISPLAY_A_PLL, pll); - read32(INTEL_DISPLAY_A_PLL); - spin(150); - - // update timing parameters - write32(INTEL_DISPLAY_A_HTOTAL, - ((uint32)(target.timing.h_total - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - write32(INTEL_DISPLAY_A_HBLANK, - ((uint32)(target.timing.h_total - 1) << 16) - | ((uint32)target.timing.h_display - 1)); - write32(INTEL_DISPLAY_A_HSYNC, - ((uint32)(target.timing.h_sync_end - 1) << 16) - | ((uint32)target.timing.h_sync_start - 1)); - - write32(INTEL_DISPLAY_A_VTOTAL, - ((uint32)(target.timing.v_total - 1) << 16) - | ((uint32)target.timing.v_display - 1)); - write32(INTEL_DISPLAY_A_VBLANK, - ((uint32)(target.timing.v_total - 1) << 16) - | ((uint32)target.timing.v_display - 1)); - write32(INTEL_DISPLAY_A_VSYNC, - ((uint32)(target.timing.v_sync_end - 1) << 16) - | ((uint32)target.timing.v_sync_start - 1)); - - write32(INTEL_DISPLAY_A_IMAGE_SIZE, - ((uint32)(target.virtual_width - 1) << 16) - | ((uint32)target.virtual_height - 1)); - - write32(INTEL_DISPLAY_A_ANALOG_PORT, - (read32(INTEL_DISPLAY_A_ANALOG_PORT) - & ~(DISPLAY_MONITOR_POLARITY_MASK - | DISPLAY_MONITOR_VGA_POLARITY)) - | ((target.timing.flags & B_POSITIVE_HSYNC) != 0 - ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0) - | ((target.timing.flags & B_POSITIVE_VSYNC) != 0 - ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0)); - - // TODO: verify the two comments below: the X driver doesn't seem to - // care about both of them! - - // These two have to be set for display B, too - this obviously means - // that the second head always must adopt the color space of the first - // head. - write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL) - & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) - | colorMode); - - if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) { - write32(INTEL_DISPLAY_B_IMAGE_SIZE, - ((uint32)(target.virtual_width - 1) << 16) - | ((uint32)target.virtual_height - 1)); - - write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL) - & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) - | colorMode); - } - } + // We set the same color mode across all pipes + program_pipe_color_modes(colorMode); + // TODO: This may not be neccesary (see DPMS OFF at top) set_display_power_mode(sharedInfo.dpms_mode); // Changing bytes per row seems to be ignored if the plane/pipe is turned @@ -1225,13 +603,18 @@ if (first) { if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); + // update shared info + gInfo->current_mode = target; + + // TODO: move to gInfo + sharedInfo.bytes_per_row = bytesPerRow; + sharedInfo.bits_per_pixel = bitsPerPixel; + set_frame_buffer_base(); // triggers writing back double-buffered registers - // update shared info - sharedInfo.bytes_per_row = bytesPerRow; - sharedInfo.current_mode = target; - sharedInfo.bits_per_pixel = bitsPerPixel; + // Second register dump + dump_registers(); return B_OK; } @@ -1242,7 +625,10 @@ intel_get_display_mode(display_mode* _currentMode) { CALLED(); - retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); + *_currentMode = gInfo->current_mode; + + // This seems unreliable. We should always know the current_mode + //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); return B_OK; } @@ -1312,7 +698,7 @@ intel_move_display(uint16 horizontalStart, uint16 verticalStart) intel_shared_info &sharedInfo = *gInfo->shared_info; Autolock locker(sharedInfo.accelerant_lock); - display_mode &mode = sharedInfo.current_mode; + display_mode &mode = gInfo->current_mode; if (horizontalStart + mode.timing.h_display > mode.virtual_width || verticalStart + mode.timing.v_display > mode.virtual_height) @@ -1353,4 +739,3 @@ intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); } } - diff --git a/src/add-ons/accelerants/intel_extreme/overlay.cpp b/src/add-ons/accelerants/intel_extreme/overlay.cpp index 5b6d15122e..2ad5c4c94f 100644 --- a/src/add-ons/accelerants/intel_extreme/overlay.cpp +++ b/src/add-ons/accelerants/intel_extreme/overlay.cpp @@ -25,12 +25,12 @@ #undef TRACE //#define TRACE_OVERLAY #ifdef TRACE_OVERLAY -# define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) +# define TRACE(x...) _sPrintf("intel_extreme: " x) #else # define TRACE(x...) #endif -#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) +#define ERROR(x...) _sPrintf("intel_extreme: " x) #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) @@ -207,7 +207,7 @@ set_color_key(uint8 red, uint8 green, uint8 blue, uint8 redMask, static void set_color_key(const overlay_window* window) { - switch (gInfo->shared_info->current_mode.space) { + switch (gInfo->current_mode.space) { case B_CMAP8: set_color_key(0, 0, window->blue.value, 0x0, 0x0, 0xff); break; @@ -235,7 +235,7 @@ static void update_overlay(bool updateCoefficients) { if (!gInfo->shared_info->overlay_active - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_965)) + || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_965)) return; QueueCommands queue(gInfo->shared_info->primary_ring_buffer); @@ -259,7 +259,7 @@ static void show_overlay(void) { if (gInfo->shared_info->overlay_active - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_965)) + || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_965)) return; gInfo->shared_info->overlay_active = true; @@ -281,7 +281,7 @@ static void hide_overlay(void) { if (!gInfo->shared_info->overlay_active - || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_965)) + || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_965)) return; overlay_registers* registers = gInfo->overlay_registers; @@ -327,7 +327,7 @@ intel_overlay_supported_spaces(const display_mode* mode) static const uint32 kSupportedi965Spaces[] = {B_YCbCr422, 0}; intel_shared_info &sharedInfo = *gInfo->shared_info; - if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x)) + if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x)) return kSupportedi965Spaces; return kSupportedSpaces; @@ -380,7 +380,7 @@ intel_allocate_overlay_buffer(color_space colorSpace, uint16 width, // alloc graphics mem int32 alignment = 0x3f; - if (sharedInfo.device_type.InGroup(INTEL_TYPE_965)) + if (sharedInfo.device_type.IsModel(INTEL_MODEL_965)) alignment = 0xff; overlay_buffer* buffer = &overlay->buffer; @@ -396,7 +396,7 @@ intel_allocate_overlay_buffer(color_space colorSpace, uint16 width, return NULL; } - if (sharedInfo.device_type.InGroup(INTEL_TYPE_965)) { + if (sharedInfo.device_type.IsModel(INTEL_MODEL_965)) { status = intel_allocate_memory(INTEL_i965_OVERLAY_STATE_SIZE, B_APERTURE_NON_RESERVED, overlay->state_base); if (status < B_OK) { @@ -437,7 +437,7 @@ intel_release_overlay_buffer(const overlay_buffer* buffer) hide_overlay(); intel_free_memory(overlay->buffer_base); - if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_965)) + if (gInfo->shared_info->device_type.IsModel(INTEL_MODEL_965)) intel_free_memory(overlay->state_base); free(overlay); @@ -589,10 +589,10 @@ intel_configure_overlay(overlay_token overlayToken, left = 0; if (top < 0) top = 0; - if (right > gInfo->shared_info->current_mode.timing.h_display) - right = gInfo->shared_info->current_mode.timing.h_display; - if (bottom > gInfo->shared_info->current_mode.timing.v_display) - bottom = gInfo->shared_info->current_mode.timing.v_display; + if (right > gInfo->current_mode.timing.h_display) + right = gInfo->current_mode.timing.h_display; + if (bottom > gInfo->current_mode.timing.v_display) + bottom = gInfo->current_mode.timing.v_display; if (left >= right || top >= bottom) { // overlay is not within visible bounds hide_overlay(); @@ -630,7 +630,7 @@ intel_configure_overlay(overlay_token overlayToken, // the result will be wrong, too. registers->source_width_rgb = right - left; registers->source_height_rgb = bottom - top; - if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_8xx)) { + if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx)) { registers->source_bytes_per_row_rgb = (((overlay->buffer_offset + (view->width << 1) + 0x1f) >> 5) - (overlay->buffer_offset >> 5) - 1) << 2; diff --git a/src/add-ons/accelerants/intel_extreme/pll.cpp b/src/add-ons/accelerants/intel_extreme/pll.cpp new file mode 100644 index 0000000000..f6126d72ad --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/pll.cpp @@ -0,0 +1,279 @@ +/* + * Copyright 2006-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * Alexander von Gluck IV, kallisti5@unixzen.com + */ + + +#include "pll.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "accelerant_protos.h" +#include "accelerant.h" +#include "utility.h" + + +#undef TRACE +#define TRACE_MODE +#ifdef TRACE_MODE +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +void +get_pll_limits(pll_limits* limits, bool isLVDS) +{ + // Note, the limits are taken from the X driver; they have not yet been + // tested + + // TODO: Breakout BXT + + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) { + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 5, 2, 14, false, 1, 79, 2, 24 << 22}, // min + { 80, 4, 1, true, 1, 127, 2, 175 << 22}, // max + 225000, 4800000, 6480000 + }; + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) { + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 5, 2, 20, false, 1, 79, 2, 11}, // min + { 80, 3, 2, true, 7, 127, 3, 156}, // max + 225000, 4000000, 6000000 + }; + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_SER5) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_BXT)) { + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 5, 1, 10, false, 1, 79, 12, 5}, // min + { 80, 8, 5, true, 5, 127, 22, 9}, // max + 225000, 1760000, 3510000 + }; + // TODO: validate these LVDS dividers! + if (isLVDS) { + kLimits.min.post = 7; + kLimits.max.post = 98; + kLimits.min.post2 = 14; + kLimits.max.post2 = 7; + } + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_9xx)) { + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 5, 1, 10, false, 1, 70, 8, 3}, // min + { 80, 8, 5, true, 6, 120, 18, 7}, // max + 200000, 1400000, 2800000 + }; + if (isLVDS) { + kLimits.min.post = 7; + kLimits.max.post = 98; + kLimits.min.post2 = 14; + kLimits.max.post2 = 7; + } + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)) { + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 10, 1, 10, false, 1, 104, 17, 5}, // min + { 30, 3, 10, true, 4, 138, 23, 11}, // max + 270000, 1750000, 3500000 + }; + // TODO: validate these LVDS dividers! + if (isLVDS) { + kLimits.min.post = 7; + kLimits.max.post = 98; + kLimits.min.post2 = 14; + kLimits.max.post2 = 7; + } + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { + // m1 is reserved and must be 0 + pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 5, 1, 10, false, 3, 2, 0, 2}, // min + { 80, 8, 5, true, 6, 256, 0, 254}, // max + 200000, 1700000, 3500000 + }; + if (isLVDS) { + kLimits.min.post = 7; + kLimits.max.post = 112; + kLimits.min.post2 = 14; + kLimits.max.post2 = 14; + } + memcpy(limits, &kLimits, sizeof(pll_limits)); + } else { + static pll_limits kLimits = { + // p, p1, p2, high, n, m, m1, m2 + { 4, 2, 4, false, 5, 96, 20, 8}, + {128, 33, 2, true, 18, 140, 28, 18}, + 165000, 930000, 1400000 + }; + // TODO: Validate these LVDS dividers! + if (isLVDS) { + kLimits.min.post = 7; + kLimits.max.post = 98; + kLimits.min.post2 = 14; + kLimits.max.post2 = 7; + } + memcpy(limits, &kLimits, sizeof(pll_limits)); + } + + TRACE("PLL limits, min: p %" B_PRId32 " (p1 %" B_PRId32 ", " + "p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " " + "(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits->min.post, + limits->min.post1, limits->min.post2, limits->min.n, limits->min.m, + limits->min.m1, limits->min.m2); + TRACE("PLL limits, max: p %" B_PRId32 " (p1 %" B_PRId32 ", " + "p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " " + "(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits->max.post, + limits->max.post1, limits->max.post2, limits->max.n, limits->max.m, + limits->max.m1, limits->max.m2); +} + + +bool +valid_pll_divisors(pll_divisors* divisors, pll_limits* limits) +{ + pll_info &info = gInfo->shared_info->pll_info; + uint32 vco = info.reference_frequency * divisors->m / divisors->n; + uint32 frequency = vco / divisors->post; + + if (divisors->post < limits->min.post || divisors->post > limits->max.post + || divisors->m < limits->min.m || divisors->m > limits->max.m + || vco < limits->min_vco || vco > limits->max_vco + || frequency < info.min_frequency || frequency > info.max_frequency) + return false; + + return true; +} + + +static uint32 +compute_pll_m(pll_divisors* divisors) +{ + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV) + || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) { + return divisors->m1 * divisors->m2; + } + + // Pineview, m1 is reserved + if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) + return divisors->m2 + 2; + + if (gInfo->shared_info->device_type.Generation() >= 3) + return 5 * (divisors->m1 + 2) + (divisors->m2 + 2); + + // TODO: This logic needs validated... PLL's were calculated differently + // on 8xx chipsets + + return 5 * divisors->m1 + divisors->m2; +} + + +static uint32 +compute_pll_p(pll_divisors* divisors) +{ + return divisors->post1 * divisors->post2; +} + + +void +compute_pll_divisors(display_mode* current, pll_divisors* divisors, + bool isLVDS) +{ + float requestedPixelClock = current->timing.pixel_clock / 1000.0f; + float referenceClock + = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; + pll_limits limits; + get_pll_limits(&limits, isLVDS); + + TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock); + + // Calculate p2 + if (isLVDS) { + if (requestedPixelClock > 112.999 + || (read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK) + == LVDS_CLKB_POWER_UP) { + // fast DAC timing via 2 channels + divisors->post2 = limits.max.post2; + divisors->post2_high = limits.max.post2_high; + } else { + // slow DAC timing + divisors->post2 = limits.min.post2; + divisors->post2_high = limits.min.post2_high; + } + } else { + if (current->timing.pixel_clock < limits.min_post2_frequency) { + // slow DAC timing + divisors->post2 = limits.min.post2; + divisors->post2_high = limits.min.post2_high; + } else { + // fast DAC timing + divisors->post2 = limits.max.post2; + divisors->post2_high = limits.max.post2_high; + } + } + + float best = requestedPixelClock; + pll_divisors bestDivisors; + + bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN); + for (divisors->m1 = limits.min.m1; divisors->m1 <= limits.max.m1; + divisors->m1++) { + for (divisors->m2 = limits.min.m2; divisors->m2 <= limits.max.m2 + && ((divisors->m2 < divisors->m1) || is_pine); divisors->m2++) { + for (divisors->n = limits.min.n; divisors->n <= limits.max.n; + divisors->n++) { + for (divisors->post1 = limits.min.post1; + divisors->post1 <= limits.max.post1; divisors->post1++) { + divisors->m = compute_pll_m(divisors); + divisors->post = compute_pll_p(divisors); + + if (!valid_pll_divisors(divisors, &limits)) + continue; + + float error = fabs(requestedPixelClock + - ((referenceClock * divisors->m) / divisors->n) + / divisors->post); + if (error < best) { + best = error; + bestDivisors = *divisors; + + if (error == 0) + break; + } + } + } + } + } + + *divisors = bestDivisors; + + TRACE("%s: found: %g MHz, p = %" B_PRId32 " (p1 = %" B_PRId32 ", " + "p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " " + "(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__, + ((referenceClock * divisors->m) / divisors->n) / divisors->post, + divisors->post, divisors->post1, divisors->post2, divisors->n, + divisors->m, divisors->m1, divisors->m2); +} diff --git a/src/add-ons/accelerants/intel_extreme/pll.h b/src/add-ons/accelerants/intel_extreme/pll.h new file mode 100644 index 0000000000..bc243a537f --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/pll.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006-2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * Alexander von Gluck IV, kallisti5@unixzen.com + */ +#ifndef INTEL_EXTREME_PLL_H +#define INTEL_EXTREME_PLL_H + + +#include "intel_extreme.h" + + +struct pll_divisors { + uint32 post; + uint32 post1; + uint32 post2; + bool post2_high; + uint32 n; + uint32 m; + uint32 m1; + uint32 m2; +}; + +struct pll_limits { + pll_divisors min; + pll_divisors max; + uint32 min_post2_frequency; + uint32 min_vco; + uint32 max_vco; +}; + + +void get_pll_limits(pll_limits* limits, bool isLVDS); +bool valid_pll_divisors(pll_divisors* divisors, pll_limits* limits); +void compute_pll_divisors(display_mode* current, pll_divisors* divisors, + bool isLVDS); + + +#endif /* INTEL_EXTREME_PLL_H */ diff --git a/src/add-ons/kernel/busses/agp_gart/intel_gart.cpp b/src/add-ons/kernel/busses/agp_gart/intel_gart.cpp index 93b8271fad..1dd1412844 100644 --- a/src/add-ons/kernel/busses/agp_gart/intel_gart.cpp +++ b/src/add-ons/kernel/busses/agp_gart/intel_gart.cpp @@ -21,13 +21,16 @@ #include #include +#include -//#define TRACE_INTEL + +#define TRACE_INTEL #ifdef TRACE_INTEL -# define TRACE(x...) dprintf("\33[33magp-intel:\33[0m " x) +# define TRACE(x...) dprintf("intel_gart: " x) #else # define TRACE(x...) ; #endif +#define ERROR(x...) dprintf("intel_gart: " x) #ifndef __HAIKU__ # define B_KERNEL_READ_AREA 0 @@ -47,93 +50,95 @@ (*((volatile uint32*)(address))) +// PCI "Host bridge" is most cases :-) const struct supported_device { uint32 bridge_id; uint32 display_id; - uint32 type; + int32 type; const char *name; } kSupportedDevices[] = { - {0x3575, 0x3577, INTEL_TYPE_83x, "i830GM"}, - {0x2560, 0x2562, INTEL_TYPE_83x, "i845G"}, - {0x3580, 0x3582, INTEL_TYPE_85x, "i855G"}, - {0x358c, 0x358e, INTEL_TYPE_85x, "i855G"}, - {0x2570, 0x2572, INTEL_TYPE_85x, "i865G"}, + {0x3575, 0x3577, INTEL_GROUP_83x, "i830GM"}, + {0x2560, 0x2562, INTEL_GROUP_83x, "i845G"}, + {0x3580, 0x3582, INTEL_GROUP_85x, "i855G"}, + {0x358c, 0x358e, INTEL_GROUP_85x, "i855G"}, + {0x2570, 0x2572, INTEL_GROUP_85x, "i865G"}, -// {0x2792, INTEL_TYPE_91x, "i910"}, -// {0x258a, INTEL_TYPE_91x, "i915"}, - {0x2580, 0x2582, INTEL_TYPE_915, "i915G"}, - {0x2590, 0x2592, INTEL_TYPE_915M, "i915GM"}, - {0x2770, 0x2772, INTEL_TYPE_945, "i945G"}, - {0x27a0, 0x27a2, INTEL_TYPE_945M, "i945GM"}, - {0x27ac, 0x27ae, INTEL_TYPE_945M, "i945GME"}, +// {0x2792, INTEL_GROUP_91x, "i910"}, +// {0x258a, INTEL_GROUP_91x, "i915"}, + {0x2580, 0x2582, INTEL_MODEL_915, "i915G"}, + {0x2590, 0x2592, INTEL_MODEL_915M, "i915GM"}, + {0x2770, 0x2772, INTEL_MODEL_945, "i945G"}, + {0x27a0, 0x27a2, INTEL_MODEL_945M, "i945GM"}, + {0x27ac, 0x27ae, INTEL_MODEL_945M, "i945GME"}, - {0x2970, 0x2972, INTEL_TYPE_965, "i946GZ"}, - {0x2980, 0x2982, INTEL_TYPE_965, "G35"}, - {0x2990, 0x2992, INTEL_TYPE_965, "i965Q"}, - {0x29a0, 0x29a2, INTEL_TYPE_965, "i965G"}, - {0x2a00, 0x2a02, INTEL_TYPE_965, "i965GM"}, - {0x2a10, 0x2a12, INTEL_TYPE_965, "i965GME"}, + {0x2970, 0x2972, INTEL_MODEL_965, "i946GZ"}, + {0x2980, 0x2982, INTEL_MODEL_965, "G35"}, + {0x2990, 0x2992, INTEL_MODEL_965, "i965Q"}, + {0x29a0, 0x29a2, INTEL_MODEL_965, "i965G"}, + {0x2a00, 0x2a02, INTEL_MODEL_965, "i965GM"}, + {0x2a10, 0x2a12, INTEL_MODEL_965, "i965GME"}, - {0x29b0, 0x29b2, INTEL_TYPE_G33, "G33"}, - {0x29c0, 0x29c2, INTEL_TYPE_G33, "Q35"}, - {0x29d0, 0x29d2, INTEL_TYPE_G33, "Q33"}, + {0x29b0, 0x29b2, INTEL_MODEL_G33, "G33"}, + {0x29c0, 0x29c2, INTEL_MODEL_G33, "Q35"}, + {0x29d0, 0x29d2, INTEL_MODEL_G33, "Q33"}, - {0x2a40, 0x2a42, INTEL_TYPE_GM45, "GM45"}, - {0x2e00, 0x2e02, INTEL_TYPE_G45, "IGD"}, - {0x2e10, 0x2e12, INTEL_TYPE_G45, "Q45"}, - {0x2e20, 0x2e22, INTEL_TYPE_G45, "G45"}, - {0x2e30, 0x2e32, INTEL_TYPE_G45, "G41"}, - {0x2e40, 0x2e42, INTEL_TYPE_G45, "B43"}, - {0x2e90, 0x2e92, INTEL_TYPE_G45, "B43"}, + {0x2a40, 0x2a42, INTEL_MODEL_GM45, "GM45"}, + {0x2e00, 0x2e02, INTEL_MODEL_G45, "IGD"}, + {0x2e10, 0x2e12, INTEL_MODEL_G45, "Q45"}, + {0x2e20, 0x2e22, INTEL_MODEL_G45, "G45"}, + {0x2e30, 0x2e32, INTEL_MODEL_G45, "G41"}, + {0x2e40, 0x2e42, INTEL_MODEL_G45, "B43"}, + {0x2e90, 0x2e92, INTEL_MODEL_G45, "B43"}, - {0xa000, 0xa001, INTEL_TYPE_IGDG, "Atom_Dx10"}, - {0xa010, 0xa011, INTEL_TYPE_IGDGM, "Atom_N4x0"}, + {0xa000, 0xa001, INTEL_MODEL_PINE, "Atom_Dx10"}, + {0xa010, 0xa011, INTEL_MODEL_PINEM, "Atom_N4x0"}, - {0x0040, 0x0042, INTEL_TYPE_ILKG, "IronLake Desktop"}, - {0x0044, 0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, - {0x0062, 0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, - {0x006a, 0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, + {0x0040, 0x0042, INTEL_MODEL_ILKG, "IronLake Desktop"}, + {0x0044, 0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, + {0x0062, 0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, + {0x006a, 0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, - {0x0100, 0x0102, INTEL_TYPE_SNBG, "SandyBridge Desktop GT1"}, - {0x0100, 0x0112, INTEL_TYPE_SNBG, "SandyBridge Desktop GT2"}, - {0x0100, 0x0122, INTEL_TYPE_SNBG, "SandyBridge Desktop GT2+"}, - {0x0104, 0x0106, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT1"}, - {0x0104, 0x0116, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT2"}, - {0x0104, 0x0126, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT2+"}, - {0x0108, 0x010a, INTEL_TYPE_SNBGS, "SandyBridge Server"}, + {0x0100, 0x0102, INTEL_MODEL_SNBG, "SandyBridge Desktop GT1"}, + {0x0100, 0x0112, INTEL_MODEL_SNBG, "SandyBridge Desktop GT2"}, + {0x0100, 0x0122, INTEL_MODEL_SNBG, "SandyBridge Desktop GT2+"}, + {0x0104, 0x0106, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT1"}, + {0x0104, 0x0116, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT2"}, + {0x0104, 0x0126, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT2+"}, + {0x0108, 0x010a, INTEL_MODEL_SNBGS, "SandyBridge Server"}, - {0x0150, 0x0152, INTEL_TYPE_IVBG, "IvyBridge Desktop GT1"}, - {0x0150, 0x0162, INTEL_TYPE_IVBG, "IvyBridge Desktop GT2"}, - {0x0154, 0x0156, INTEL_TYPE_IVBGM, "IvyBridge Mobile GT1"}, - {0x0154, 0x0166, INTEL_TYPE_IVBGM, "IvyBridge Mobile GT2"}, - {0x0158, 0x015a, INTEL_TYPE_IVBGS, "IvyBridge Server GT1"}, - {0x0158, 0x016a, INTEL_TYPE_IVBGS, "IvyBridge Server GT2"}, + {0x0150, 0x0152, INTEL_MODEL_IVBG, "IvyBridge Desktop GT1"}, + {0x0150, 0x0162, INTEL_MODEL_IVBG, "IvyBridge Desktop GT2"}, + {0x0154, 0x0156, INTEL_MODEL_IVBGM, "IvyBridge Mobile GT1"}, + {0x0154, 0x0166, INTEL_MODEL_IVBGM, "IvyBridge Mobile GT2"}, + {0x0158, 0x015a, INTEL_MODEL_IVBGS, "IvyBridge Server GT1"}, + {0x0158, 0x016a, INTEL_MODEL_IVBGS, "IvyBridge Server GT2"}, - {0x0c00, 0x0412, INTEL_TYPE_IVBG, "Haswell Desktop"}, - {0x0c04, 0x0416, INTEL_TYPE_IVBGM, "Haswell Mobile"}, - {0x0d04, 0x0d26, INTEL_TYPE_IVBGM, "Haswell Mobile"}, + {0x0c00, 0x0412, INTEL_MODEL_HAS, "Haswell Desktop"}, + {0x0c04, 0x0416, INTEL_MODEL_HASM, "Haswell Mobile"}, + {0x0d04, 0x0d26, INTEL_MODEL_HASM, "Haswell Mobile"}, + {0x0a04, 0x0a16, INTEL_MODEL_HASM, "Haswell Mobile"}, // XXX: 0x0f00 only confirmed on 0x0f30, 0x0f31 - {0x0f00, 0x0155, INTEL_TYPE_VLVG, "ValleyView Desktop"}, - {0x0f00, 0x0f30, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f00, 0x0f31, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f00, 0x0f32, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f00, 0x0f33, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f00, 0x0157, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, + {0x0f00, 0x0155, INTEL_MODEL_VLV, "ValleyView Desktop"}, + {0x0f00, 0x0f30, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f00, 0x0f31, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f00, 0x0f32, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f00, 0x0f33, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f00, 0x0157, INTEL_MODEL_VLVM, "ValleyView Mobile"}, }; struct intel_info { pci_info bridge; pci_info display; - uint32 type; + DeviceType* type; - uint32 *gtt_base; + uint32* gtt_base; phys_addr_t gtt_physical_base; area_id gtt_area; size_t gtt_entries; size_t gtt_stolen_entries; - vuint32 *registers; + vuint32* registers; area_id registers_area; addr_t aperture_base; @@ -167,99 +172,29 @@ has_display_device(pci_info &info, uint32 deviceID) } -static void -determine_memory_sizes(intel_info &info, size_t >tSize, size_t &stolenSize) +static uint16 +gtt_memory_config(intel_info &info) { - // read stolen memory from the PCI configuration of the PCI bridge uint8 controlRegister = INTEL_GRAPHICS_MEMORY_CONTROL; - if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_SNB) + if (info.type->InGroup(INTEL_GROUP_SNB)) controlRegister = SNB_GRAPHICS_MEMORY_CONTROL; - uint16 memoryConfig = get_pci_config(info.bridge, controlRegister, 2); + return get_pci_config(info.bridge, controlRegister, 2); +} + + +static size_t +determine_gtt_stolen(intel_info &info) +{ + uint16 memoryConfig = gtt_memory_config(info); size_t memorySize = 1 << 20; // 1 MB - gttSize = 0; - stolenSize = 0; - if (info.type == INTEL_TYPE_965) { - switch (memoryConfig & i965_GTT_MASK) { - case i965_GTT_128K: - gttSize = 128 << 10; - break; - case i965_GTT_256K: - gttSize = 256 << 10; - break; - case i965_GTT_512K: - gttSize = 512 << 10; - break; - } - } else if (info.type == INTEL_TYPE_G33 - || (info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_IGD) { - switch (memoryConfig & G33_GTT_MASK) { - case G33_GTT_1M: - gttSize = 1 << 20; - break; - case G33_GTT_2M: - gttSize = 2 << 20; - break; - } - } else if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_G4x - || (info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_ILK) { - switch (memoryConfig & G4X_GTT_MASK) { - case G4X_GTT_NONE: - gttSize = 0; - break; - case G4X_GTT_1M_NO_IVT: - gttSize = 1 << 20; - break; - case G4X_GTT_2M_NO_IVT: - case G4X_GTT_2M_IVT: - gttSize = 2 << 20; - break; - case G4X_GTT_3M_IVT: - gttSize = 3 << 20; - break; - case G4X_GTT_4M_IVT: - gttSize = 4 << 20; - break; - } - } else if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_SNB) { - switch (memoryConfig & SNB_GTT_SIZE_MASK) { - case SNB_GTT_SIZE_NONE: - gttSize = 0; - break; - case SNB_GTT_SIZE_1MB: - gttSize = 1 << 20; - break; - case SNB_GTT_SIZE_2MB: - gttSize = 2 << 20; - break; - } - } else { - // older models have the GTT as large as their frame buffer mapping - // TODO: check if the i9xx version works with the i8xx chips as well - size_t frameBufferSize = 0; - if ((info.type & INTEL_TYPE_8xx) != 0) { - if (info.type == INTEL_TYPE_83x - && (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M) - frameBufferSize = 64 << 20; - else - frameBufferSize = 128 << 20; - } else if ((info.type & INTEL_TYPE_9xx) != 0) - frameBufferSize = info.display.u.h0.base_register_sizes[2]; - - TRACE("frame buffer size %lu MB\n", frameBufferSize >> 20); - gttSize = frameBufferSize / 1024; - } - - // TODO: test with different models! - - if (info.type == INTEL_TYPE_83x) { + if (info.type->InGroup(INTEL_GROUP_83x)) { // Older chips switch (memoryConfig & STOLEN_MEMORY_MASK) { case i830_LOCAL_MEMORY_ONLY: // TODO: determine its size! - dprintf("intel_gart: getting local memory size not " - "implemented.\n"); + ERROR("getting local memory size not implemented.\n"); break; case i830_STOLEN_512K: memorySize >>= 1; @@ -271,7 +206,7 @@ determine_memory_sizes(intel_info &info, size_t >tSize, size_t &stolenSize) memorySize *= 8; break; } - } else if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_SNB) { + } else if (info.type->InGroup(INTEL_GROUP_SNB)) { switch (memoryConfig & SNB_STOLEN_MEMORY_MASK) { case SNB_STOLEN_MEMORY_32MB: memorySize *= 32; @@ -322,8 +257,11 @@ determine_memory_sizes(intel_info &info, size_t >tSize, size_t &stolenSize) memorySize *= 512; break; } - } else if (info.type == INTEL_TYPE_85x - || (info.type & INTEL_TYPE_9xx) == INTEL_TYPE_9xx) { + } else if (info.type->InGroup(INTEL_GROUP_85x) + || info.type->InFamily(INTEL_FAMILY_9xx) + || info.type->InFamily(INTEL_FAMILY_SER5) + || info.type->InFamily(INTEL_FAMILY_SOC0) + || info.type->InFamily(INTEL_FAMILY_POVR)) { switch (memoryConfig & STOLEN_MEMORY_MASK) { case i855_STOLEN_MEMORY_4M: memorySize *= 4; @@ -366,14 +304,109 @@ determine_memory_sizes(intel_info &info, size_t >tSize, size_t &stolenSize) // TODO: error out! memorySize = 4096; } + return memorySize - 4096; +} - stolenSize = memorySize - 4096; + +static size_t +determine_gtt_size(intel_info &info) +{ + uint16 memoryConfig = gtt_memory_config(info); + size_t gttSize = 0; + + if (info.type->IsModel(INTEL_MODEL_965)) { + switch (memoryConfig & i965_GTT_MASK) { + case i965_GTT_128K: + gttSize = 128 << 10; + break; + case i965_GTT_256K: + gttSize = 256 << 10; + break; + case i965_GTT_512K: + gttSize = 512 << 10; + break; + } + } else if (info.type->IsModel(INTEL_MODEL_G33) + || info.type->InGroup(INTEL_GROUP_PIN)) { + switch (memoryConfig & G33_GTT_MASK) { + case G33_GTT_1M: + gttSize = 1 << 20; + break; + case G33_GTT_2M: + gttSize = 2 << 20; + break; + } + } else if (info.type->InGroup(INTEL_GROUP_G4x) + || info.type->InGroup(INTEL_GROUP_ILK)) { + switch (memoryConfig & G4X_GTT_MASK) { + case G4X_GTT_NONE: + gttSize = 0; + break; + case G4X_GTT_1M_NO_IVT: + gttSize = 1 << 20; + break; + case G4X_GTT_2M_NO_IVT: + case G4X_GTT_2M_IVT: + gttSize = 2 << 20; + break; + case G4X_GTT_3M_IVT: + gttSize = 3 << 20; + break; + case G4X_GTT_4M_IVT: + gttSize = 4 << 20; + break; + } + } else if (info.type->InGroup(INTEL_GROUP_SNB)) { + switch (memoryConfig & SNB_GTT_SIZE_MASK) { + case SNB_GTT_SIZE_NONE: + gttSize = 0; + break; + case SNB_GTT_SIZE_1MB: + gttSize = 1 << 20; + break; + case SNB_GTT_SIZE_2MB: + gttSize = 2 << 20; + break; + } + } else { + // older models have the GTT as large as their frame buffer mapping + // TODO: check if the i9xx version works with the i8xx chips as well + size_t frameBufferSize = 0; + if (info.type->InFamily(INTEL_FAMILY_8xx)) { + if (info.type->InGroup(INTEL_GROUP_83x) + && (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M) + frameBufferSize = 64 << 20; + else + frameBufferSize = 128 << 20; + } else if (info.type->Generation() >= 3) { + frameBufferSize = info.display.u.h0.base_register_sizes[2]; + } + + TRACE("frame buffer size %lu MB\n", frameBufferSize >> 20); + gttSize = frameBufferSize / 1024; + } + return gttSize; } static void set_gtt_entry(intel_info &info, uint32 offset, phys_addr_t physicalAddress) { + if (info.type->Generation() >= 8) { + // CHV + BXT + physicalAddress |= (physicalAddress >> 28) & 0x07f0; + // TODO: cache control? + } else if (info.type->Generation() >= 6) { + // SandyBridge, IronLake, IvyBridge, Haswell + physicalAddress |= (physicalAddress >> 28) & 0x0ff0; + physicalAddress |= 0x02; // cache control, l3 cacheable + } else if (info.type->Generation() >= 4) { + // Intel 9xx minus 91x, 94x, G33 + // possible high bits are stored in the lower end + physicalAddress |= (physicalAddress >> 28) & 0x00f0; + // TODO: cache control? + } + // TODO: this is not 64-bit safe! write32(info.gtt_base + (offset >> GTT_PAGE_SHIFT), (uint32)physicalAddress | GTT_ENTRY_VALID); @@ -396,7 +429,7 @@ intel_map(intel_info &info) { int fbIndex = 0; int mmioIndex = 1; - if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) { + if (info.type->Generation() >= 3) { // for some reason Intel saw the need to change the order of the // mappings with the introduction of the i9xx family mmioIndex = 0; @@ -410,7 +443,7 @@ intel_map(intel_info &info) B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&info.registers); if (mmioMapper.InitCheck() < B_OK) { - dprintf("agp_intel: could not map memory I/O!\n"); + ERROR("could not map memory I/O!\n"); return info.registers_area; } @@ -425,7 +458,7 @@ intel_map(intel_info &info) &scratchAddress, B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (scratchCreator.InitCheck() < B_OK) { - dprintf("agp_intel: could not create scratch page!\n"); + ERROR("could not create scratch page!\n"); return info.scratch_area; } @@ -433,29 +466,27 @@ intel_map(intel_info &info) if (get_memory_map(scratchAddress, B_PAGE_SIZE, &entry, 1) != B_OK) return B_ERROR; - if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) { - if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_G4x - || (info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_ILK - || (info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_SNB) { - info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex] - + (2UL << 20); - } else - info.gtt_physical_base - = get_pci_config(info.display, i915_GTT_BASE, 4); - } else { + // TODO: Review these + if (info.type->InFamily(INTEL_FAMILY_8xx)) { info.gtt_physical_base = read32(info.registers + INTEL_PAGE_TABLE_CONTROL) & ~PAGE_TABLE_ENABLED; if (info.gtt_physical_base == 0) { // TODO: not sure how this is supposed to work under Linux/FreeBSD, // but on my i865, this code is needed for Haiku. - dprintf("intel_gart: Use GTT address fallback.\n"); + ERROR("Use GTT address fallback.\n"); info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex] + i830_GTT_BASE; } + } else if (info.type->InGroup(INTEL_GROUP_91x)) { + info.gtt_physical_base = get_pci_config(info.display, i915_GTT_BASE, 4); + } else { + // 945+? + info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex] + + (2UL << 20); } - size_t gttSize, stolenSize; - determine_memory_sizes(info, gttSize, stolenSize); + size_t gttSize = determine_gtt_size(info); + size_t stolenSize = determine_gtt_stolen(info); info.gtt_entries = gttSize / 4096; info.gtt_stolen_entries = stolenSize / 4096; @@ -468,7 +499,7 @@ intel_map(intel_info &info) info.gtt_physical_base, gttSize, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&info.gtt_base); if (gttMapper.InitCheck() < B_OK) { - dprintf("intel_gart: could not map GTT!\n"); + ERROR("could not map GTT!\n"); return info.gtt_area; } @@ -477,16 +508,14 @@ intel_map(intel_info &info) if (info.aperture_size == 0) info.aperture_size = info.display.u.h0.base_register_sizes[fbIndex]; - dprintf("intel_gart: detected %ld MB of stolen memory, aperture " - "size %ld MB, GTT size %ld KB\n", (stolenSize + (1023 << 10)) >> 20, + ERROR("detected %ld MB of stolen memory, aperture size %ld MB, " + "GTT size %ld KB\n", (stolenSize + (1023 << 10)) >> 20, info.aperture_size >> 20, gttSize >> 10); - dprintf("intel_gart: GTT base = 0x%" B_PRIxPHYSADDR "\n", - info.gtt_physical_base); - dprintf("intel_gart: MMIO base = 0x%" B_PRIx32 "\n", + ERROR("GTT base = 0x%" B_PRIxPHYSADDR "\n", info.gtt_physical_base); + ERROR("MMIO base = 0x%" B_PRIx32 "\n", info.display.u.h0.base_registers[mmioIndex]); - dprintf("intel_gart: GMR base = 0x%" B_PRIxPHYSADDR "\n", - info.aperture_physical_base); + ERROR("GMR base = 0x%" B_PRIxPHYSADDR "\n", info.aperture_physical_base); AreaKeeper apertureMapper; info.aperture_area = apertureMapper.Map("intel graphics aperture", @@ -495,7 +524,7 @@ intel_map(intel_info &info) B_READ_AREA | B_WRITE_AREA, (void**)&info.aperture_base); if (apertureMapper.InitCheck() < B_OK) { // try again without write combining - dprintf(DEVICE_NAME ": enabling write combined mode failed.\n"); + ERROR("enabling write combined mode failed.\n"); info.aperture_area = apertureMapper.Map("intel graphics aperture", info.aperture_physical_base, info.aperture_size, @@ -503,7 +532,7 @@ intel_map(intel_info &info) (void**)&info.aperture_base); } if (apertureMapper.InitCheck() < B_OK) { - dprintf(DEVICE_NAME ": could not map graphics aperture!\n"); + ERROR("could not map graphics aperture!\n"); return info.aperture_area; } @@ -639,7 +668,7 @@ intel_init() for (uint32 i = 0; i < sizeof(kSupportedDevices) / sizeof(kSupportedDevices[0]); i++) { if (sInfo.bridge.device_id == kSupportedDevices[i].bridge_id) { - sInfo.type = kSupportedDevices[i].type; + sInfo.type = new DeviceType(kSupportedDevices[i].type); if (has_display_device(sInfo.display, kSupportedDevices[i].display_id)) { TRACE("found intel bridge\n"); @@ -656,6 +685,8 @@ intel_init() static void intel_uninit() { + if (sInfo.type) + delete sInfo.type; } diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp index 1c23bd8a6b..ecd6f36ae2 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp @@ -119,8 +119,6 @@ static struct vbios { } vbios; -/* TODO: move code to accelerant, if possible */ - /*! This is reimplementation, Haiku uses BIOS call and gets most current panel info, we're, otherwise, digging in VBIOS memory and parsing VBT tables to get native panel timings. This will allow to get non-updated, diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/driver.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/driver.cpp index 22dd3f0a2e..a13c4be30b 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/driver.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/driver.cpp @@ -42,71 +42,75 @@ const struct supported_device { int32 type; const char* name; } kSupportedDevices[] = { - {0x3577, INTEL_TYPE_83x, "i830GM"}, - {0x2562, INTEL_TYPE_83x, "i845G"}, + {0x3577, INTEL_GROUP_83x, "i830GM"}, + {0x2562, INTEL_GROUP_83x, "i845G"}, - {0x2572, INTEL_TYPE_85x, "i865G"}, - {0x3582, INTEL_TYPE_85x, "i855G"}, - {0x358e, INTEL_TYPE_85x, "i855G"}, + {0x2572, INTEL_GROUP_85x, "i865G"}, + {0x3582, INTEL_GROUP_85x, "i855G"}, + {0x358e, INTEL_GROUP_85x, "i855G"}, - {0x2582, INTEL_TYPE_915, "i915G"}, - {0x258a, INTEL_TYPE_915, "i915"}, - {0x2592, INTEL_TYPE_915M, "i915GM"}, - {0x2792, INTEL_TYPE_915, "i910"}, - {0x2772, INTEL_TYPE_945, "i945G"}, - {0x27a2, INTEL_TYPE_945M, "i945GM"}, - {0x27ae, INTEL_TYPE_945M, "i945GME"}, - {0x2972, INTEL_TYPE_965, "i946G"}, - {0x2982, INTEL_TYPE_965, "G35"}, - {0x2992, INTEL_TYPE_965, "i965Q"}, - {0x29a2, INTEL_TYPE_965, "i965G"}, - {0x2a02, INTEL_TYPE_965M, "i965GM"}, - {0x2a12, INTEL_TYPE_965M, "i965GME"}, - {0x29b2, INTEL_TYPE_G33, "G33G"}, - {0x29c2, INTEL_TYPE_G33, "Q35G"}, - {0x29d2, INTEL_TYPE_G33, "Q33G"}, + {0x2582, INTEL_MODEL_915, "i915G"}, + {0x258a, INTEL_MODEL_915, "i915"}, + {0x2592, INTEL_MODEL_915M, "i915GM"}, + {0x2792, INTEL_MODEL_915, "i910"}, + {0x2772, INTEL_MODEL_945, "i945G"}, + {0x27a2, INTEL_MODEL_945M, "i945GM"}, + {0x27ae, INTEL_MODEL_945M, "i945GME"}, + {0x2972, INTEL_MODEL_965, "i946G"}, + {0x2982, INTEL_MODEL_965, "G35"}, + {0x2992, INTEL_MODEL_965, "i965Q"}, + {0x29a2, INTEL_MODEL_965, "i965G"}, + {0x2a02, INTEL_MODEL_965M, "i965GM"}, + {0x2a12, INTEL_MODEL_965M, "i965GME"}, + {0x29b2, INTEL_MODEL_G33, "G33G"}, + {0x29c2, INTEL_MODEL_G33, "Q35G"}, + {0x29d2, INTEL_MODEL_G33, "Q33G"}, - {0x2a42, INTEL_TYPE_GM45, "GM45"}, - {0x2e02, INTEL_TYPE_G45, "IGD"}, - {0x2e12, INTEL_TYPE_G45, "Q45"}, - {0x2e22, INTEL_TYPE_G45, "G45"}, - {0x2e32, INTEL_TYPE_G45, "G41"}, - {0x2e42, INTEL_TYPE_G45, "B43"}, - {0x2e92, INTEL_TYPE_G45, "B43"}, + {0x2a42, INTEL_MODEL_GM45, "GM45"}, + {0x2e02, INTEL_MODEL_G45, "IGD"}, + {0x2e12, INTEL_MODEL_G45, "Q45"}, + {0x2e22, INTEL_MODEL_G45, "G45"}, + {0x2e32, INTEL_MODEL_G45, "G41"}, + {0x2e42, INTEL_MODEL_G45, "B43"}, + {0x2e92, INTEL_MODEL_G45, "B43"}, - {0xa001, INTEL_TYPE_IGDG, "Atom_Dx10"}, - {0xa011, INTEL_TYPE_IGDGM, "Atom_N4x0"}, + {0xa001, INTEL_MODEL_PINE, "Atom_Dx10"}, + {0xa011, INTEL_MODEL_PINEM, "Atom_N4x0"}, - {0x0042, INTEL_TYPE_ILKG, "IronLake Desktop"}, - {0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, - {0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, - {0x0046, INTEL_TYPE_ILKGM, "IronLake Mobile"}, + {0x0042, INTEL_MODEL_ILKG, "IronLake Desktop"}, + {0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, + {0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, + {0x0046, INTEL_MODEL_ILKGM, "IronLake Mobile"}, - {0x0102, INTEL_TYPE_SNBG, "SandyBridge Desktop GT1"}, - {0x0112, INTEL_TYPE_SNBG, "SandyBridge Desktop GT2"}, - {0x0122, INTEL_TYPE_SNBG, "SandyBridge Desktop GT2+"}, - {0x0106, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT1"}, - {0x0116, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT2"}, - {0x0126, INTEL_TYPE_SNBGM, "SandyBridge Mobile GT2+"}, - {0x010a, INTEL_TYPE_SNBGS, "SandyBridge Server"}, +// Experimental +#if 0 + {0x0102, INTEL_MODEL_SNBG, "SandyBridge Desktop GT1"}, + {0x0112, INTEL_MODEL_SNBG, "SandyBridge Desktop GT2"}, + {0x0122, INTEL_MODEL_SNBG, "SandyBridge Desktop GT2+"}, + {0x0106, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT1"}, + {0x0116, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT2"}, + {0x0126, INTEL_MODEL_SNBGM, "SandyBridge Mobile GT2+"}, + {0x010a, INTEL_MODEL_SNBGS, "SandyBridge Server"}, - {0x0152, INTEL_TYPE_IVBG, "IvyBridge Desktop GT1"}, - {0x0162, INTEL_TYPE_IVBG, "IvyBridge Desktop GT2"}, - {0x0156, INTEL_TYPE_IVBGM, "IvyBridge Mobile GT1"}, - {0x0166, INTEL_TYPE_IVBGM, "IvyBridge Mobile GT2"}, - {0x015a, INTEL_TYPE_IVBGS, "IvyBridge Server GT1"}, - {0x016a, INTEL_TYPE_IVBGS, "IvyBridge Server GT2"}, + {0x0152, INTEL_MODEL_IVBG, "IvyBridge Desktop GT1"}, + {0x0162, INTEL_MODEL_IVBG, "IvyBridge Desktop GT2"}, + {0x0156, INTEL_MODEL_IVBGM, "IvyBridge Mobile GT1"}, + {0x0166, INTEL_MODEL_IVBGM, "IvyBridge Mobile GT2"}, + {0x015a, INTEL_MODEL_IVBGS, "IvyBridge Server GT1"}, + {0x016a, INTEL_MODEL_IVBGS, "IvyBridge Server GT2"}, - {0x0412, INTEL_TYPE_IVBG, "Haswell Desktop"}, - {0x0416, INTEL_TYPE_IVBGM, "Haswell Mobile"}, - {0x0d26, INTEL_TYPE_IVBGM, "Haswell Mobile"}, + {0x0412, INTEL_MODEL_HAS, "Haswell Desktop"}, + {0x0416, INTEL_MODEL_HASM, "Haswell Mobile"}, + {0x0d26, INTEL_MODEL_HASM, "Haswell Mobile"}, + {0x0a16, INTEL_MODEL_HASM, "Haswell Mobile"}, - {0x0155, INTEL_TYPE_VLVG, "ValleyView Desktop"}, - {0x0f30, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f31, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f32, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0f33, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, - {0x0157, INTEL_TYPE_VLVGM, "ValleyView Mobile"}, + {0x0155, INTEL_MODEL_VLV, "ValleyView Desktop"}, + {0x0f30, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f31, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f32, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0f33, INTEL_MODEL_VLVM, "ValleyView Mobile"}, + {0x0157, INTEL_MODEL_VLVM, "ValleyView Mobile"}, +#endif }; int32 api_version = B_CUR_DRIVER_API_VERSION; diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp index 7637ab9da0..f9af40e1f3 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp @@ -98,7 +98,7 @@ intel_interrupt_handler(void* data) // Intel changed the PCH register mapping between Sandy Bridge and the // later generations (Ivy Bridge and up). - if (info.device_type.InFamily(INTEL_TYPE_SNB)) { + if (info.device_type.InGroup(INTEL_GROUP_SNB)) { mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA_SNB : INTERRUPT_VBLANK_PIPEA; if ((identity & mask) != 0) { @@ -287,7 +287,7 @@ intel_extreme_init(intel_info &info) int fbIndex = 0; int mmioIndex = 1; - if (info.device_type.InFamily(INTEL_TYPE_9xx)) { + if (info.device_type.Generation() >= 3) { // For some reason Intel saw the need to change the order of the // mappings with the introduction of the i9xx family mmioIndex = 0; @@ -316,12 +316,16 @@ intel_extreme_init(intel_info &info) return info.registers_area; } + ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n", + info.device_type.Generation(), + info.device_type.HasPlatformControlHub() ? "with" : "without"); + uint32* blocks = info.shared_info->register_blocks; blocks[REGISTER_BLOCK(REGS_FLAT)] = 0; // setup the register blocks for the different architectures if (info.device_type.HasPlatformControlHub()) { - // PCH based platforms (IronLake and up) + // PCH based platforms (IronLake through ultra-low-power Broadwells) blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] = PCH_NORTH_SHARED_REGISTER_BASE; blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)] @@ -346,14 +350,24 @@ intel_extreme_init(intel_info &info) = ICH_PORT_REGISTER_BASE; } - // "I nearly got violent with the hw guys when they told me..." - if (info.device_type.InFamily(INTEL_TYPE_VLV)) { - TRACE("%s: ValleyView MMIO offset engaged\n", __func__); - blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)] += VLV_DISPLAY_BASE; - blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] += VLV_DISPLAY_BASE; + // Everything in the display PRM gets +0x180000 + if (info.device_type.InGroup(INTEL_GROUP_VLV)) { + // "I nearly got violent with the hw guys when they told me..." blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] += VLV_DISPLAY_BASE; + blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] += VLV_DISPLAY_BASE; } + TRACE("REGS_NORTH_SHARED: 0x%X\n", + blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]); + TRACE("REGS_NORTH_PIPE_AND_PORT: 0x%X\n", + blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]); + TRACE("REGS_NORTH_PLANE_CONTROL: 0x%X\n", + blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]); + TRACE("REGS_SOUTH_SHARED: 0x%X\n", + blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]); + TRACE("REGS_SOUTH_TRANSCODER_PORT: 0x%X\n", + blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]); + // make sure bus master, memory-mapped I/O, and frame buffer is enabled set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2) | PCI_command_io | PCI_command_memory @@ -391,13 +405,16 @@ intel_extreme_init(intel_info &info) info.shared_info->frame_buffer = 0; info.shared_info->dpms_mode = B_DPMS_ON; + // Pull VBIOS panel mode for later use info.shared_info->got_vbt = get_lvds_mode_from_bios( - &info.shared_info->current_mode); + &info.shared_info->panel_mode); + /* at least 855gm can't drive more than one head at time */ - if (info.device_type.InFamily(INTEL_TYPE_8xx)) + if (info.device_type.InFamily(INTEL_FAMILY_8xx)) info.shared_info->single_head_locked = 1; - if (info.device_type.InFamily(INTEL_TYPE_9xx)) { + if (info.device_type.InFamily(INTEL_FAMILY_9xx) + | info.device_type.InFamily(INTEL_FAMILY_SER5)) { info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz info.shared_info->pll_info.max_frequency = 400000; // 400 MHz RAM DAC speed @@ -457,6 +474,18 @@ intel_extreme_init(intel_info &info) init_interrupt_handler(info); + if (info.device_type.HasPlatformControlHub()) { + if (info.device_type.Generation() == 5) { + info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0) + & FDI_PLL_FB_CLOCK_MASK) + 2; + info.shared_info->fdi_link_frequency *= 100; + } else { + info.shared_info->fdi_link_frequency = 2700; + } + } else { + info.shared_info->fdi_link_frequency = 0; + } + TRACE("%s: completed successfully!\n", __func__); return B_OK; } diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h index 45d6b2f9ae..0632c57bc9 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h @@ -70,7 +70,7 @@ find_reg(const intel_info& info, uint32 target) } -extern bool get_lvds_mode_from_bios(display_mode *shared_info); +extern bool get_lvds_mode_from_bios(display_mode *mode); extern status_t intel_free_memory(intel_info& info, addr_t offset); extern status_t intel_allocate_memory(intel_info& info, size_t size, size_t alignment, uint32 flags, addr_t* _offset, diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/power.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/power.cpp index 5864504b8f..9c33a9f8e1 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/power.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/power.cpp @@ -31,19 +31,19 @@ intel_en_gating(intel_info &info) if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) { TRACE("i965GM/i965GME quirk\n"); write32(info, 0x6204, (1L << 29)); - } else if (info.device_type.InGroup(INTEL_TYPE_SNB)) { + } else if (info.device_type.InGroup(INTEL_GROUP_SNB)) { TRACE("SandyBridge clock gating\n"); write32(info, 0x42020, (1L << 28) | (1L << 7) | (1L << 5)); - } else if (info.device_type.InGroup(INTEL_TYPE_IVB)) { + } else if (info.device_type.InGroup(INTEL_GROUP_IVB)) { TRACE("IvyBridge clock gating\n"); write32(info, 0x42020, (1L << 28)); - } else if (info.device_type.InGroup(INTEL_TYPE_VLV)) { + } else if (info.device_type.InGroup(INTEL_GROUP_VLV)) { TRACE("ValleyView clock gating\n"); write32(info, VLV_DISPLAY_BASE + 0x6200, (1L << 28)); - } else if (info.device_type.InGroup(INTEL_TYPE_ILK)) { + } else if (info.device_type.InGroup(INTEL_GROUP_ILK)) { TRACE("IronLake clock gating\n"); write32(info, 0x42020, (1L << 7) | (1L << 5)); - } else if (info.device_type.InGroup(INTEL_TYPE_G4x)) { + } else if (info.device_type.InGroup(INTEL_GROUP_G4x)) { TRACE("G4x clock gating\n"); write32(info, 0x6204, 0); write32(info, 0x6208, (1L << 9) | (1L << 7) | (1L << 6)); @@ -70,8 +70,8 @@ intel_en_downclock(intel_info &info) { CALLED(); - if (!info.device_type.InGroup(INTEL_TYPE_SNB) - && !info.device_type.InGroup(INTEL_TYPE_IVB)) { + if (!info.device_type.InGroup(INTEL_GROUP_SNB) + && !info.device_type.InGroup(INTEL_GROUP_IVB)) { TRACE("%s: Downclocking not supported on this chipset.\n", __func__); return B_NOT_ALLOWED; }