i945/gma.c: use linux code to calculate divisors

The code to compute n, m1, m2, p1 divisors is not correct in coreboot and
on some targets hits a working mode at lower refresh rate, which is why
display is working on some targets.

The divisors must be such "refclk * (5 * (m1 + 2) + (m2 + 2))/ (n + 2)
/ (p1 * p2)" is as close as possible to the target frequency (which
is defined by the resolution and refresh rate).

This patch also fixes the reference frequency.

This patch reuses linux (4.1) code from drivers/gpu/drm/i915/intel_display.c
to correctly compute divisors.

The result is that some previously not working displays, like many
displays found on the Lenovo T60 might work now.
Some examples of T60 displays that were known to not work (in payload):
Samsung LTN141XA-L01 (14.1" 1024x768)
LG-Philips LP150X09 (15.1" 1024x768)
IDtech N150U3-L01 (15.1" 1600x1200)
IDtech IAQX10N (15.1" 2048x1536)
Samsung LTN154X3-L0A (15.4" 1280x800)
LG-Philips LP150E06-A5K4 (15.1" 1400x1050)

Tested on T60 with 1024x786.

Change-Id: I2c7f3bb0024ac005029eaebe3ecdc70c38ac777e
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/16504
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martinroth@google.com>
This commit is contained in:
Arthur Heymans 2016-09-02 22:35:32 +02:00 committed by Martin Roth
parent 494d398ae4
commit 7dfc8a5ebd
1 changed files with 38 additions and 43 deletions

View File

@ -26,6 +26,7 @@
#include <string.h>
#include <pc80/vga.h>
#include <pc80/vga_io.h>
#include <commonlib/helpers.h>
#include "i945.h"
#include "chip.h"
@ -43,7 +44,7 @@
#define PGETBL_CTL 0x2020
#define PGETBL_ENABLED 0x00000001
#define BASE_FREQUENCY 120000
#define BASE_FREQUENCY 100000
#if CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT
@ -85,10 +86,10 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
u8 edid_data[128];
unsigned long temp;
int hpolarity, vpolarity;
u32 candp1, candn;
u32 best_delta = 0xffffffff;
u32 smallest_err = 0xffffffff;
u32 target_frequency;
u32 pixel_p1 = 1;
u32 pixel_p2;
u32 pixel_n = 1;
u32 pixel_m1 = 1;
u32 pixel_m2 = 1;
@ -158,43 +159,39 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
write32(pmmio + PORT_HOTPLUG_EN, conf->gpu_hotplug);
write32(pmmio + INSTPM, 0x08000000 | INSTPM_AGPBUSY_DIS);
target_frequency = mode->lvds_dual_channel ? mode->pixel_clock
: (2 * mode->pixel_clock);
/* p2 divisor must 7 for dual channel LVDS */
/* and 14 for single channel LVDS */
pixel_p2 = mode->lvds_dual_channel ? 7 : 14;
target_frequency = mode->pixel_clock;
/* Find suitable divisors. */
/* Find suitable divisors, m1, m2, p1, n. */
/* refclock * (5 * (m1 + 2) + (m1 + 2)) / (n + 2) / p1 / p2 */
/* should be closest to target frequency as possible */
u32 candn, candm1, candm2, candp1;
for (candm1 = 8; candm1 <= 18; candm1++) {
for (candm2 = 3; candm2 <= 7; candm2++) {
for (candn = 1; candn <= 6; candn++) {
for (candp1 = 1; candp1 <= 8; candp1++) {
for (candn = 5; candn <= 10; candn++) {
u32 cur_frequency;
u32 m; /* 77 - 131. */
u32 denom; /* 35 - 560. */
u32 current_delta;
denom = candn * candp1 * 7;
/* Doesnt overflow for up to
5000000 kHz = 5 GHz. */
m = (target_frequency * denom
+ BASE_FREQUENCY / 2) / BASE_FREQUENCY;
if (m < 77 || m > 131)
u32 m = 5 * (candm1 + 2) + (candm2 + 2);
u32 p = candp1 * pixel_p2;
u32 vco = DIV_ROUND_CLOSEST(BASE_FREQUENCY * m, candn + 2);
u32 dot = DIV_ROUND_CLOSEST(vco, p);
u32 this_err = ABS(dot - target_frequency);
if ((m < 70) || (m > 120))
continue;
cur_frequency = (BASE_FREQUENCY * m) / denom;
if (target_frequency > cur_frequency)
current_delta = target_frequency - cur_frequency;
else
current_delta = cur_frequency - target_frequency;
if (best_delta > current_delta) {
best_delta = current_delta;
if (this_err < smallest_err) {
smallest_err = this_err;
pixel_n = candn;
pixel_m1 = candm1;
pixel_m2 = candm2;
pixel_p1 = candp1;
pixel_m2 = ((m + 3) % 5) + 7;
pixel_m1 = (m - pixel_m2) / 5;
}
}
}
}
}
if (best_delta == 0xffffffff) {
if (smallest_err == 0xffffffff) {
printk (BIOS_ERR, "Couldn't find GFX clock divisors\n");
return -1;
}
@ -216,8 +213,8 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
printk(BIOS_DEBUG, "Pixel N=%d, M1=%d, M2=%d, P1=%d\n",
pixel_n, pixel_m1, pixel_m2, pixel_p1);
printk(BIOS_DEBUG, "Pixel clock %d kHz\n",
BASE_FREQUENCY * (5 * pixel_m1 + pixel_m2) / pixel_n
/ (pixel_p1 * 7));
BASE_FREQUENCY * (5 * (pixel_m1 + 2) + (pixel_m2 + 2)) /
(pixel_n + 2) / (pixel_p1 * pixel_p2));
#if !IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)
write32(pmmio + PF_WIN_SZ(0), vactive | (hactive << 16));
@ -242,8 +239,8 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
write32(pmmio + PP_CONTROL, PANEL_UNLOCK_REGS
| (read32(pmmio + PP_CONTROL) & ~PANEL_UNLOCK_MASK));
write32(pmmio + FP0(1),
((pixel_n - 2) << 16)
| ((pixel_m1 - 2) << 8) | pixel_m2);
(pixel_n << 16)
| (pixel_m1 << 8) | pixel_m2);
write32(pmmio + DPLL(1),
DPLL_VGA_MODE_DIS |
DPLL_VCO_ENABLE | DPLLB_MODE_LVDS
@ -252,8 +249,7 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
| (conf->gpu_lvds_use_spread_spectrum_clock
? DPLL_INTEGRATED_CLOCK_VLV | DPLL_INTEGRATED_CRI_CLK_VLV
: 0)
| (pixel_p1 << 16)
| (pixel_p1));
| (0x10000 << (pixel_p1 - 1)));
mdelay(1);
write32(pmmio + DPLL(1),
DPLL_VGA_MODE_DIS |
@ -261,8 +257,7 @@ static int intel_gma_init(struct northbridge_intel_i945_config *conf,
| (mode->lvds_dual_channel ? DPLLB_LVDS_P2_CLOCK_DIV_7
: DPLLB_LVDS_P2_CLOCK_DIV_14)
| ((conf->gpu_lvds_use_spread_spectrum_clock ? 3 : 0) << 13)
| (pixel_p1 << 16)
| (pixel_p1));
| (0x10000 << (pixel_p1 - 1)));
mdelay(1);
write32(pmmio + HTOTAL(1),
((hactive + right_border + hblank - 1) << 16)