* Fixed PLL timing computation for the i9xx chips - I mixed post2 min/max values, and did

not take the VCO limits into account; both could (and would during testing) create invalid
  frequencies.
* Also reverted the order in which the PLL divisors are traversed to match the order of what
  is used in the X driver to create comparable output (our error computation is based on float,
  though, and should therefore create more accurate values).
* The i965 introduced a special register for the surface; the former display base register
  is now only used for the view offset. Instead of setting the base manually here and there,
  there is now a set_frame_buffer_base() function.
* The DPMS code will now also turn off/on the PLL clock generator.
* The code needs some more cleanup, and while the driver now produces the correct timing on
  my i965 system, I'm now greeted by a black screen after startup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22289 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-09-24 09:02:35 +00:00
parent 9c7408528e
commit cbd4081064
5 changed files with 126 additions and 64 deletions

View File

@ -194,6 +194,7 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_A_CONTROL 0x70180
#define INTEL_DISPLAY_A_BASE 0x70184
#define INTEL_DISPLAY_A_BYTES_PER_ROW 0x70188
#define INTEL_DISPLAY_A_SURFACE 0x7019c // i965 and up only
#define DISPLAY_CONTROL_ENABLED (1UL << 31)
#define DISPLAY_CONTROL_GAMMA (1UL << 30)
#define DISPLAY_CONTROL_COLOR_MASK (0x0fUL << 26)
@ -238,6 +239,7 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_A_ANALOG_PORT 0x61100
#define DISPLAY_MONITOR_PORT_ENABLED (1UL << 31)
#define DISPLAY_MONITOR_PIPE_B (1UL << 30)
#define DISPLAY_MONITOR_VGA_POLARITY (1UL << 15)
#define DISPLAY_MONITOR_MODE_MASK (3UL << 10)
#define DISPLAY_MONITOR_ON 0
@ -257,6 +259,7 @@ struct intel_free_graphics_memory {
#define INTEL_DISPLAY_B_CONTROL 0x71180
#define INTEL_DISPLAY_B_BASE 0x71184
#define INTEL_DISPLAY_B_BYTES_PER_ROW 0x71188
#define INTEL_DISPLAY_B_SURFACE 0x7119c // i965 and up only
#define INTEL_DISPLAY_B_PALETTE 0x0a800

View File

@ -86,6 +86,7 @@ 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 status_t create_mode_list(void);
// memory.cpp

View File

@ -17,6 +17,8 @@
extern "C" {
#endif
void spin(bigtime_t delay);
// general
status_t intel_init_accelerant(int fd);
ssize_t intel_accelerant_clone_info_size(void);

View File

@ -32,17 +32,18 @@ enable_display_plane(bool enable)
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) {
if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
write32(INTEL_DISPLAY_A_CONTROL, planeAControl & ~DISPLAY_CONTROL_ENABLED);
write32(INTEL_DISPLAY_A_BASE, gInfo->shared_info->frame_buffer_offset);
}
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
write32(INTEL_DISPLAY_B_CONTROL, planeBControl & ~DISPLAY_CONTROL_ENABLED);
write32(INTEL_DISPLAY_B_BASE, gInfo->shared_info->frame_buffer_offset);
}
set_frame_buffer_base();
}
}
@ -64,6 +65,9 @@ enable_display_pipe(bool enable)
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
write32(INTEL_DISPLAY_B_PIPE_CONTROL, pipeBControl & ~DISPLAY_PIPE_ENABLED);
}
read32(INTEL_DISPLAY_A_BASE);
// flush the eventually cached PCI bus writes
}
@ -73,6 +77,20 @@ set_display_power_mode(uint32 mode)
uint32 monitorMode = 0;
if (mode == B_DPMS_ON) {
uint32 pll = read32(INTEL_DISPLAY_A_PLL);
if ((pll & DISPLAY_PLL_ENABLED) == 0) {
// reactivate PLL
write32(INTEL_DISPLAY_A_PLL, pll);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
write32(INTEL_DISPLAY_A_PLL, pll | DISPLAY_PLL_ENABLED);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
write32(INTEL_DISPLAY_A_PLL, pll | DISPLAY_PLL_ENABLED);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
}
enable_display_pipe(true);
enable_display_plane(true);
}
@ -107,9 +125,16 @@ set_display_power_mode(uint32 mode)
if (mode != B_DPMS_ON) {
enable_display_plane(false);
wait_for_vblank();
enable_display_pipe(false);
}
if (mode == B_DPMS_OFF) {
write32(INTEL_DISPLAY_A_PLL, read32(INTEL_DISPLAY_A_PLL) | DISPLAY_PLL_ENABLED);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
}
read32(INTEL_DISPLAY_A_BASE);
// flush the eventually cached PCI bus writes
}

View File

@ -68,7 +68,9 @@ struct pll_divisors {
struct pll_limits {
pll_divisors min;
pll_divisors max;
float min_post2_frequency;
uint32 min_post2_frequency;
uint32 min_vco;
uint32 max_vco;
};
static const display_mode kBaseModeList[] = {
@ -156,6 +158,37 @@ set_i2c_signals(void* cookie, int clock, int data)
}
void
set_frame_buffer_base()
{
intel_shared_info &sharedInfo = *gInfo->shared_info;
display_mode &mode = sharedInfo.current_mode;
uint32 baseRegister;
uint32 surfaceRegister;
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 == (INTEL_TYPE_9xx | INTEL_TYPE_965)) {
write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
read32(baseRegister);
write32(surfaceRegister, sharedInfo.frame_buffer_offset);
read32(surfaceRegister);
} else {
write32(baseRegister, sharedInfo.frame_buffer_offset
+ mode.v_display_start * sharedInfo.bytes_per_row
+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
read32(baseRegister);
}
}
/*!
Creates the initial mode list of the primary accelerant.
It's called from intel_init_accelerant().
@ -227,18 +260,18 @@ get_pll_limits(pll_limits &limits)
// TODO: support LVDS output limits as well
static const pll_limits kLimits = {
// p, p1, p2, high, n, m, m1, m2
{ 5, 1, 5, false, 5, 70, 12, 7}, // min
{ 80, 8, 10, true, 10, 120, 22, 11}, // max
200000
{ 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, 2, false, 5, 96, 20, 8},
{128, 33, 4, true, 18, 140, 28, 18},
165000
{ 4, 2, 4, false, 5, 96, 20, 8},
{128, 33, 2, true, 18, 140, 28, 18},
165000, 930000, 1400000
};
limits = kLimits;
}
@ -261,6 +294,7 @@ valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
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;
@ -279,9 +313,11 @@ compute_pll_divisors(const display_mode &current, pll_divisors& divisors)
TRACE(("required MHz: %g\n", requestedPixelClock));
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;
}
@ -289,13 +325,13 @@ compute_pll_divisors(const display_mode &current, pll_divisors& divisors)
float best = requestedPixelClock;
pll_divisors bestDivisors;
for (divisors.post1 = limits.min.post1; divisors.post1 <= limits.max.post1;
divisors.post1++) {
for (divisors.n = limits.min.n; divisors.n <= limits.max.n; divisors.n++) {
for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1;
divisors.m1++) {
for (divisors.m2 = limits.min.m2; divisors.m2 < divisors.m1
&& divisors.m2 <= limits.max.m2; divisors.m2++) {
for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1; divisors.m1++) {
for (divisors.m2 = limits.min.m2; divisors.m2 < divisors.m1
&& divisors.m2 <= limits.max.m2; 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;
@ -455,7 +491,7 @@ if (first) {
* sharedInfo.bytes_per_row, gInfo->frame_buffer_handle,
offset) == B_OK) {
sharedInfo.frame_buffer_offset = offset;
write32(INTEL_DISPLAY_A_BASE, offset);
set_frame_buffer_base();
}
return B_NO_MEMORY;
@ -464,33 +500,13 @@ if (first) {
sharedInfo.frame_buffer_offset = offset;
// make sure VGA display is disabled
write32(INTEL_VGA_DISPLAY_CONTROL, read32(INTEL_VGA_DISPLAY_CONTROL)
| VGA_DISPLAY_DISABLED);
write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
read32(INTEL_VGA_DISPLAY_CONTROL);
if (gInfo->shared_info->device_type != (INTEL_TYPE_8xx | INTEL_TYPE_85x)) {
}
if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
// 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.timing.h_display - 1) << 16)
| ((uint32)target.timing.v_display - 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));
pll_divisors divisors;
compute_pll_divisors(target, divisors);
@ -527,12 +543,39 @@ if (first) {
debug_printf("PLL is %#lx, write: %#lx\n", read32(INTEL_DISPLAY_A_PLL), pll);
write32(INTEL_DISPLAY_A_PLL, pll);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
write32(INTEL_DISPLAY_A_PLL, pll);
read32(INTEL_DISPLAY_A_PLL);
spin(150);
#if 0
write32(INTEL_DISPLAY_A_PLL, DISPLAY_PLL_ENABLED | DISPLAY_PLL_2X_CLOCK
| DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_DIVIDE_4X
| (((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) & DISPLAY_PLL_POST1_DIVISOR_MASK)
| (divisorRegister == INTEL_DISPLAY_A_PLL_DIVISOR_1 ? DISPLAY_PLL_DIVISOR_1 : 0));
#endif
// 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.timing.h_display - 1) << 16)
| ((uint32)target.timing.v_display - 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));
}
// These two have to be set for display B, too - this obviously means
@ -553,16 +596,13 @@ if (first) {
// changing bytes per row seems to be ignored if the plane/pipe is turned off
if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset);
// triggers writing back double-buffered registers
}
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset);
// triggers writing back double-buffered registers
}
set_frame_buffer_base();
// triggers writing back double-buffered registers
// update shared info
sharedInfo.bytes_per_row = bytesPerRow;
@ -727,16 +767,7 @@ intel_move_display(uint16 horizontalStart, uint16 verticalStart)
mode.h_display_start = horizontalStart;
mode.v_display_start = verticalStart;
if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset
+ verticalStart * sharedInfo.bytes_per_row
+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
}
if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset
+ verticalStart * sharedInfo.bytes_per_row
+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
}
set_frame_buffer_base();
return B_OK;
}