User:Greasemonkey/Intel GenX: Difference between revisions
(references are safer than replicating the manuals. also, updated VGA disabling method.) |
(→Getting a display: Gen6-compatible code, and a few fixes) |
||
Line 35: | Line 35: | ||
Tested on: |
Tested on: |
||
* GM45 1366x768 CQ60-210TU |
* GM45 1366x768 CQ60-210TU |
||
* HD3000 1366x768 dv6-6c35tx |
|||
===Proper mode switch methodology=== |
===Proper mode switch methodology=== |
||
Line 43: | Line 44: | ||
===Simplified version=== |
===Simplified version=== |
||
This assumes that the BIOS configured the LVDS panel properly and/or in a way that we can just take its values and run with them. If it does, you can avoid an awful lot of pain |
This assumes that the BIOS configured the LVDS panel properly and/or in a way that we can just take its values and run with them. If it does, you can avoid an awful lot of pain. |
||
This code works on a GM45. It does not quite work on an HD3000, although the VGA compatibility mode is successfully disabled. |
|||
This also assumes that your panel is less than 2048 pixels wide, but that can be fixed by adding a few "if" statements. However, the 1366x768 assumption is dropped from this version. |
This also assumes that your panel is less than 2048 pixels wide, but that can be fixed by adding a few "if" statements. However, the 1366x768 assumption is dropped from this version. |
||
There is a chance that this will work on systems that don't have an LVDS panel, but you will be limited to whatever resolution the BIOS spews out. |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
<pre> |
<pre> |
||
Line 61: | Line 62: | ||
wait_us(100); |
wait_us(100); |
||
// |
// Get correct VGA pipe |
||
// WARNING: Gen5 changes the location of this register! |
// WARNING: Gen5 changes the location of this register! |
||
// |
// Pre-Gen5 uses 0x71400, Gen5+ uses 0x41000. |
||
int real_VGACNTRL = (genx_typ < 0x5000 ? VGACNTRL : VGACNTRL_ILK); |
|||
// select the right location for the architecture version. |
|||
genx_pipe = (genx_reg32[real_VGACNTRL]>>29)&1; |
|||
⚫ | |||
// Disable VGA |
|||
⚫ | |||
// Disable Display n |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
// XXX: Lacking information on DevSNB's panelfitter. |
|||
⚫ | |||
// Seems to work fine without disabling it. |
|||
⚫ | |||
// For Gen4.5, however, disabling it is pretty much mandatory, |
|||
⚫ | |||
// unless you can't stand GPUs that lack antialiasing. |
|||
if(genx_typ >= 0x5000) |
|||
{ |
|||
genx_reg32[PIPEnCONF(genx_pipe)] &= ~(1<<31); // Disable Pipe n |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
// XXX: Panelfitter location unknown for DevSNB. |
|||
} |
|||
// It mentions a spreadsheet which appears to be publically unavailable. |
|||
⚫ | |||
⚫ | |||
// Set up Display |
// Set up Display n and enable |
||
genx_reg32[ |
genx_reg32[DSPnLINOFF(genx_pipe)] = 0x00000000; // linear offset |
||
genx_reg32[ |
genx_reg32[DSPnSTRIDE(genx_pipe)] = 2048*4; // scanline pitch |
||
genx_reg32[ |
genx_reg32[DSPnSURF(genx_pipe)] = 0x00000000; // surface base |
||
genx_reg32[ |
genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(15<<26)) | (6<<26); // bit depth select, 6 = 32bpp BGRX |
||
genx_reg32[ |
genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(3<<20)) | (0<<20); // pixel multiply |
||
genx_reg32[ |
genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(1<<10)) | (0<<10); // tiling flag |
||
genx_reg32[ |
genx_reg32[DSPnCNTR(genx_pipe)] |= (1<<31); // enable |
||
</pre> |
</pre> |
||
Line 94: | Line 106: | ||
===Setting monitor timings=== |
===Setting monitor timings=== |
||
Not tested on the dv6-6c35tx. |
|||
If you need to set the monitor timings, this code should be useful for a start. These timings work on the CQ60-210TU's surprisingly tolerant LVDS panel: |
If you need to set the monitor timings, this code should be useful for a start. These timings work on the CQ60-210TU's surprisingly tolerant LVDS panel: |
||
Line 112: | Line 125: | ||
uint32_t vis_blank_w = vis_sblank_w + vis_sync_w + vis_eblank_w; |
uint32_t vis_blank_w = vis_sblank_w + vis_sync_w + vis_eblank_w; |
||
uint32_t vis_blank_h = vis_sblank_h + vis_sync_h + vis_eblank_h; |
uint32_t vis_blank_h = vis_sblank_h + vis_sync_h + vis_eblank_h; |
||
genx_reg32[ |
genx_reg32[HTOTAL_n(genx_pipe)] = ((vis_w+vis_blank_w-1)<<16) | (vis_w-1); |
||
genx_reg32[ |
genx_reg32[HBLANK_n(genx_pipe)] = ((vis_w+vis_blank_w-1)<<16) | (vis_w-1); |
||
genx_reg32[ |
genx_reg32[HSYNC_n(genx_pipe)] = ((vis_w+vis_sblank_w+vis_sync_w-1)<<16) | (vis_w+vis_sblank_w-1); |
||
genx_reg32[ |
genx_reg32[VTOTAL_n(genx_pipe)] = ((vis_h+vis_blank_h-1)<<16) | (vis_h-1); |
||
genx_reg32[ |
genx_reg32[VBLANK_n(genx_pipe)] = ((vis_h+vis_blank_h-1)<<16) | (vis_h-1); |
||
genx_reg32[ |
genx_reg32[VSYNC_n(genx_pipe)] = ((vis_h+vis_sblank_h+vis_sync_h-1)<<16) | (vis_h+vis_sblank_h-1); |
||
genx_reg32[ |
genx_reg32[PIPEnSRC(genx_pipe)] = ((vis_stretch_w-1)<<16)|(vis_stretch_h-1); |
||
</pre> |
</pre> |
||
Revision as of 04:15, 28 May 2015
The Intel GenX GPU architecture (probably) covers the Intel HD and the later Intel GMA series of GPUs, starting with the Intel 965. Later revisions tend to add and remove and readd features and instructions over time, but otherwise remain fairly similar.
This will cover Gen4 (963, 965, G35) and later. Earlier GPUs do not have open documentation and are apparently very different.
As there is an awful lot to document, this is probably a better place for tutorials.
Systems tested
- Compaq CQ60-210TU: GM45 DevCTG (GMA 4500MHD; Cantiga) with 1366x768 LVDS panel
- HP Pavilion dv6-6c35tx: i5-2450M DevSNB (HD 3000) with 1366x768 LVDS panel
Anything that doesn't exactly match the specs listed here may differ. Approach at your own risk.
Finding the GPU
Tested on:
- GM45 1366x768 CQ60-210TU
- HD3000 1366x768 dv6-6c35tx
Probe the PCI bus. It should be located at Bus 0, Device 2, Function 0.
GMAs tend to be advertised as at least two functions, so you may wish to check Function 1 as well. HDs tend to be advertised as just one.
Either way, there appears to be no difference between using BAR0 of Function 0 and BAR0 of Function 1, and only Function 0 gives the initial AGP aperture space information, so you might be able to get away with just using Function 0.
With that said, make sure you check the IDs to ensure that they match hardware that you've actually tested. Different GPUs have different bugs and thus require different workarounds - even when they're within the same family.
Here's the information, assuming you are accessing everything through Function 0:
- uint64_t @ PCI 0x10: Location of MMIO registers. (XXX: for devices with a Function 1, this appears to do more than just MMIO registers - Function 1 should be able to provide a space with "just" MMIO registers.)
- uint64_t @ PCI 0x18: Location of AGP stolen memory location.
The 64-bit pointers have the lower 4 bits set to 0x4, so remember to mask it out before you use it, and remember to maintain that mask before writing back.
Getting a display
Tested on:
- GM45 1366x768 CQ60-210TU
- HD3000 1366x768 dv6-6c35tx
Proper mode switch methodology
Consult the appropriate section of your manual. It tends to be in Volume 3 at the start of one of the parts.
- G45: Vol3, pg 28
- SNB: Vol3.2, pg 8
Simplified version
This assumes that the BIOS configured the LVDS panel properly and/or in a way that we can just take its values and run with them. If it does, you can avoid an awful lot of pain.
This also assumes that your panel is less than 2048 pixels wide, but that can be fixed by adding a few "if" statements. However, the 1366x768 assumption is dropped from this version.
There is a chance that this will work on systems that don't have an LVDS panel, but you will be limited to whatever resolution the BIOS spews out.
If you want a non-native resolution, enable the panelfitter and adjust PIPEnSRC to suit. Note, the GMA panelfitter does love to blur everything. (HD 3000 seems fine.)
Here's how you get from VGA text mode to native 32bpp full-res BGRX mode the easy way, and by easy we mean this is very much empirical, about as risky, and assumes the BIOS doesn't have stupid bugs in it:
// Set VGA screen off outportb(0x3C4, 0x01); outportb(0x3C5, inportb(0x3C5)|(1<<5)); // Wait at least 100us (Gen4.5 only needs 20us, but Gen6 needs 100us) wait_us(100); // Get correct VGA pipe // WARNING: Gen5 changes the location of this register! // Pre-Gen5 uses 0x71400, Gen5+ uses 0x41000. int real_VGACNTRL = (genx_typ < 0x5000 ? VGACNTRL : VGACNTRL_ILK); genx_pipe = (genx_reg32[real_VGACNTRL]>>29)&1; // Disable VGA genx_reg32[real_VGACNTRL] |= (1<<31); // Disable Display n genx_reg32[DSPnCNTR(genx_pipe)] &= ~(1<<31); // Set PIPEnSRC to screen resolution uint32_t vis_w = (genx_reg32[HTOTAL_n(genx_pipe)] & 0xFFFF)+1; uint32_t vis_h = (genx_reg32[VTOTAL_n(genx_pipe)] & 0xFFFF)+1; genx_reg32[PIPEnSRC(genx_pipe)] = ((vis_w-1)<<16)|(vis_h-1); // XXX: Lacking information on DevSNB's panelfitter. // Seems to work fine without disabling it. // For Gen4.5, however, disabling it is pretty much mandatory, // unless you can't stand GPUs that lack antialiasing. if(genx_typ >= 0x5000) { genx_reg32[PIPEnCONF(genx_pipe)] &= ~(1<<31); // Disable Pipe n while((genx_reg32[PIPEnCONF(genx_pipe)] & (1<<30))) {} // Wait for Pipe n to stop genx_reg32[PFIT_CONTROL] &= ~(1<<31); // Disable panelfitter genx_reg32[PIPEnCONF(genx_pipe)] |= (1<<31); // Enable Pipe n } // Set up Display n and enable genx_reg32[DSPnLINOFF(genx_pipe)] = 0x00000000; // linear offset genx_reg32[DSPnSTRIDE(genx_pipe)] = 2048*4; // scanline pitch genx_reg32[DSPnSURF(genx_pipe)] = 0x00000000; // surface base genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(15<<26)) | (6<<26); // bit depth select, 6 = 32bpp BGRX genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(3<<20)) | (0<<20); // pixel multiply genx_reg32[DSPnCNTR(genx_pipe)] = (genx_reg32[DSPnCNTR(genx_pipe)] & ~(1<<10)) | (0<<10); // tiling flag genx_reg32[DSPnCNTR(genx_pipe)] |= (1<<31); // enable
With the above config, your framebuffer should be located at the start of the AGP aperture, and be 2048 32bpp BGRX pixels wide internally.
Setting monitor timings
Not tested on the dv6-6c35tx.
If you need to set the monitor timings, this code should be useful for a start. These timings work on the CQ60-210TU's surprisingly tolerant LVDS panel:
uint32_t vis_w = 1366; uint32_t vis_stretch_w = vis_w; uint32_t vis_sblank_w = 80; uint32_t vis_sync_w = 128; uint32_t vis_eblank_w = 200; uint32_t vis_h = 768; uint32_t vis_stretch_h = vis_h; uint32_t vis_sblank_h = 3; uint32_t vis_sync_h = 5; uint32_t vis_eblank_h = 22; uint32_t vis_blank_w = vis_sblank_w + vis_sync_w + vis_eblank_w; uint32_t vis_blank_h = vis_sblank_h + vis_sync_h + vis_eblank_h; genx_reg32[HTOTAL_n(genx_pipe)] = ((vis_w+vis_blank_w-1)<<16) | (vis_w-1); genx_reg32[HBLANK_n(genx_pipe)] = ((vis_w+vis_blank_w-1)<<16) | (vis_w-1); genx_reg32[HSYNC_n(genx_pipe)] = ((vis_w+vis_sblank_w+vis_sync_w-1)<<16) | (vis_w+vis_sblank_w-1); genx_reg32[VTOTAL_n(genx_pipe)] = ((vis_h+vis_blank_h-1)<<16) | (vis_h-1); genx_reg32[VBLANK_n(genx_pipe)] = ((vis_h+vis_blank_h-1)<<16) | (vis_h-1); genx_reg32[VSYNC_n(genx_pipe)] = ((vis_h+vis_sblank_h+vis_sync_h-1)<<16) | (vis_h+vis_sblank_h-1); genx_reg32[PIPEnSRC(genx_pipe)] = ((vis_stretch_w-1)<<16)|(vis_stretch_h-1);
See Also
Articles
External Links
- Official documentation from Intel
- Official documentation on X.org - covers up to 2012, but also has Vol_1b_G45_core.pdf which is missing from the official Intel PRM list
- 1366x768 LCD timings - thread with several different sets of timings
- List of Intel graphics processing units on Wikipedia - useful for finding out PCI device IDs and exactly what generation you're using.