x4x/gma.c: Add VESA native resolution mode

This patch implements native resolution, VESA mode, on the VGA output of
x4x.

It relies on EDID to modeset, but has a fallback-mode (640 x 480 @
60Hz) if this is no EDID could be found. This fallback mode only works
in textmode since in VESA mode some payloads (grub2) rely on VBE info,
which is being generated from an EDID.

Change-Id: I247ea7171ba3c5dc3b209d00e4dcb2d2069abd75
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/16498
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martinroth@google.com>
This commit is contained in:
Arthur Heymans 2016-09-04 16:01:11 +02:00 committed by Martin Roth
parent eff596b51a
commit de14ea77c3
1 changed files with 242 additions and 40 deletions

View File

@ -26,24 +26,68 @@
#include <cpu/x86/msr.h> #include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h> #include <cpu/x86/mtrr.h>
#include <kconfig.h> #include <kconfig.h>
#include <commonlib/helpers.h>
#include "drivers/intel/gma/i915_reg.h" #include "drivers/intel/gma/i915_reg.h"
#include "chip.h" #include "chip.h"
#include "x4x.h" #include "x4x.h"
#include <drivers/intel/gma/intel_bios.h> #include <drivers/intel/gma/intel_bios.h>
#include <drivers/intel/gma/edid.h>
#include <drivers/intel/gma/i915.h> #include <drivers/intel/gma/i915.h>
#include <pc80/vga.h> #include <pc80/vga.h>
#include <pc80/vga_io.h> #include <pc80/vga_io.h>
#define BASE_FREQUENCY 96000
static u8 edid_is_present(u8 *edid, u32 edid_size)
{
u32 i;
for (i = 0; i < edid_size; i++) {
if (*(edid + i) != 0)
return 1;
}
return 0;
}
static void intel_gma_init(const struct northbridge_intel_x4x_config *info, static void intel_gma_init(const struct northbridge_intel_x4x_config *info,
u8 *mmio) u8 *mmio, u32 physbase, u16 piobase, u32 lfb)
{ {
int i; int i;
u32 hactive, vactive; u8 edid_data[128];
struct edid edid;
struct edid_mode *mode;
u8 edid_is_found;
/* Initialise mode variables for 640 x 480 @ 60Hz */
u32 hactive = 640, vactive = 480;
u32 right_border = 0, bottom_border = 0;
int hpolarity = 0, vpolarity = 0;
u32 hsync = 96, vsync = 2;
u32 hblank = 160, vblank = 45;
u32 hfront_porch = 16, vfront_porch = 10;
u32 target_frequency = 25175;
u32 err_most = 0xffffffff;
u32 pixel_p1 = 1;
u32 pixel_n = 1;
u32 pixel_m1 = 1;
u32 pixel_m2 = 1;
u32 link_frequency = info->gfx.link_frequency_270_mhz ? 270000 : 162000;
u32 data_m1;
u32 data_n1 = 0x00800000;
u32 link_m1;
u32 link_n1 = 0x00040000;
vga_gr_write(0x18, 0); vga_gr_write(0x18, 0);
/* Set up GTT */
for (i = 0; i < 0x1000; i++) {
outl((i << 2) | 1, piobase);
outl(physbase + (i << 12) + 1, piobase + 4);
}
write32(mmio + VGA0, 0x31108); write32(mmio + VGA0, 0x31108);
write32(mmio + VGA1, 0x31406); write32(mmio + VGA1, 0x31406);
@ -73,107 +117,258 @@ static void intel_gma_init(const struct northbridge_intel_x4x_config *info,
for (i = 0; i <= 0x18; i++) for (i = 0; i <= 0x18; i++)
vga_cr_write(i, cr[i]); vga_cr_write(i, cr[i]);
udelay(1);
intel_gmbus_read_edid(mmio + GMBUS0, 2, 0x50, edid_data, 128);
intel_gmbus_stop(mmio + GMBUS0);
decode_edid(edid_data,
sizeof(edid_data), &edid);
mode = &edid.mode;
/* Disable screen memory to prevent garbage from appearing. */ /* Disable screen memory to prevent garbage from appearing. */
vga_sr_write(1, vga_sr_read(1) | 0x20); vga_sr_write(1, vga_sr_read(1) | 0x20);
hactive = 640; edid_is_found = edid_is_present(edid_data, sizeof(edid_data));
vactive = 400; if (edid_is_found) {
printk(BIOS_DEBUG, "EDID is not null");
hactive = edid.x_resolution;
vactive = edid.y_resolution;
right_border = mode->hborder;
bottom_border = mode->vborder;
hpolarity = (mode->phsync == '-');
vpolarity = (mode->pvsync == '-');
vsync = mode->vspw;
hsync = mode->hspw;
vblank = mode->vbl;
hblank = mode->hbl;
hfront_porch = mode->hso;
vfront_porch = mode->vso;
target_frequency = mode->pixel_clock;
} else
printk(BIOS_DEBUG, "EDID is null, using 640 x 480 @ 60Hz mode");
if (IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)) {
vga_sr_write(1, 1);
vga_sr_write(0x2, 0xf);
vga_sr_write(0x3, 0x0);
vga_sr_write(0x4, 0xe);
vga_gr_write(0, 0x0);
vga_gr_write(1, 0x0);
vga_gr_write(2, 0x0);
vga_gr_write(3, 0x0);
vga_gr_write(4, 0x0);
vga_gr_write(5, 0x0);
vga_gr_write(6, 0x5);
vga_gr_write(7, 0xf);
vga_gr_write(0x10, 0x1);
vga_gr_write(0x11, 0);
edid.bytes_per_line = (edid.bytes_per_line + 63) & ~63;
write32(mmio + DSPCNTR(0), DISPLAY_PLANE_ENABLE
| DISPPLANE_BGRX888);
write32(mmio + DSPADDR(0), 0);
write32(mmio + DSPSTRIDE(0), edid.bytes_per_line);
write32(mmio + DSPSURF(0), 0);
for (i = 0; i < 0x100; i++)
write32(mmio + LGC_PALETTE(0) + 4 * i, i * 0x010101);
} else {
vga_textmode_init();
}
u32 candn, candm1, candm2, candp1;
for (candn = 1; candn <= 4; candn++) {
for (candm1 = 23; candm1 >= 16; candm1--) {
for (candm2 = 11; candm2 >= 5; candm2--) {
for (candp1 = 8; candp1 >= 1; candp1--) {
u32 m = 5 * (candm1 + 2) + (candm2 + 2);
u32 p = candp1 * 10; /* 10 == 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 (this_err < err_most) {
err_most = this_err;
pixel_n = candn;
pixel_m1 = candm1;
pixel_m2 = candm2;
pixel_p1 = candp1;
}
}
}
}
}
if (err_most == 0xffffffff) {
printk(BIOS_ERR, "Couldn't find GFX clock divisors\n");
return;
}
link_m1 = ((uint64_t)link_n1 * mode->pixel_clock) / link_frequency;
data_m1 = ((uint64_t)data_n1 * 18 * mode->pixel_clock)
/ (link_frequency * 8 * 4);
printk(BIOS_INFO, "bringing up panel at resolution %d x %d\n",
hactive, vactive);
printk(BIOS_DEBUG, "Borders %d x %d\n",
right_border, bottom_border);
printk(BIOS_DEBUG, "Blank %d x %d\n",
hblank, vblank);
printk(BIOS_DEBUG, "Sync %d x %d\n",
hsync, vsync);
printk(BIOS_DEBUG, "Front porch %d x %d\n",
hfront_porch, vfront_porch);
printk(BIOS_DEBUG, (info->gfx.use_spread_spectrum_clock
? "Spread spectrum clock\n" : "DREF clock\n"));
printk(BIOS_DEBUG, "Polarities %d, %d\n",
hpolarity, vpolarity);
printk(BIOS_DEBUG, "Data M1=%d, N1=%d\n",
data_m1, data_n1);
printk(BIOS_DEBUG, "Link frequency %d kHz\n",
link_frequency);
printk(BIOS_DEBUG, "Link M1=%d, N1=%d\n",
link_m1, link_n1);
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 + 2) + (pixel_m2 + 2)) /
(pixel_n + 2) / (pixel_p1 * 10));
mdelay(1); mdelay(1);
write32(mmio + FP0(0), 0x31108); write32(mmio + FP0(0), (pixel_n << 16)
write32(mmio + DPLL(0), | (pixel_m1 << 8) | pixel_m2);
DPLL_VCO_ENABLE | DPLLB_MODE_DAC_SERIAL write32(mmio + DPLL(0), DPLL_VCO_ENABLE
| DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 | DPLL_VGA_MODE_DIS | DPLLB_MODE_DAC_SERIAL
| 0x10601 | (0x10000 << (pixel_p1 - 1))
); | (6 << 9));
mdelay(1); mdelay(1);
write32(mmio + DPLL(0), write32(mmio + DPLL(0), DPLL_VCO_ENABLE
DPLL_VCO_ENABLE | DPLLB_MODE_DAC_SERIAL | DPLL_VGA_MODE_DIS | DPLLB_MODE_DAC_SERIAL
| DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 | (0x10000 << (pixel_p1 - 1))
| 0x10601 | (6 << 9));
);
write32(mmio + ADPA, ADPA_DAC_ENABLE write32(mmio + ADPA, ADPA_DAC_ENABLE
| ADPA_PIPE_A_SELECT | ADPA_PIPE_A_SELECT
| ADPA_CRT_HOTPLUG_MONITOR_COLOR | ADPA_CRT_HOTPLUG_MONITOR_COLOR
| ADPA_CRT_HOTPLUG_ENABLE | ADPA_CRT_HOTPLUG_ENABLE
| ADPA_USE_VGA_HVPOLARITY
| ADPA_VSYNC_CNTL_ENABLE | ADPA_VSYNC_CNTL_ENABLE
| ADPA_HSYNC_CNTL_ENABLE | ADPA_HSYNC_CNTL_ENABLE
| ADPA_DPMS_ON | ADPA_DPMS_ON
); | (vpolarity ? ADPA_VSYNC_ACTIVE_LOW :
ADPA_VSYNC_ACTIVE_HIGH)
| (hpolarity ? ADPA_HSYNC_ACTIVE_LOW :
ADPA_HSYNC_ACTIVE_HIGH));
write32(mmio + HTOTAL(0), write32(mmio + HTOTAL(0),
((hactive - 1) << 16) ((hactive + right_border + hblank - 1) << 16)
| (hactive - 1)); | (hactive - 1));
write32(mmio + HBLANK(0), write32(mmio + HBLANK(0),
((hactive - 1) << 16) ((hactive + right_border + hblank - 1) << 16)
| (hactive - 1)); | (hactive + right_border - 1));
write32(mmio + HSYNC(0), write32(mmio + HSYNC(0),
((hactive - 1) << 16) ((hactive + right_border + hfront_porch + hsync - 1) << 16)
| (hactive - 1)); | (hactive + right_border + hfront_porch - 1));
write32(mmio + VTOTAL(0), ((vactive - 1) << 16) write32(mmio + VTOTAL(0), ((vactive + bottom_border + vblank - 1) << 16)
| (vactive - 1));
write32(mmio + VBLANK(0), ((vactive - 1) << 16)
| (vactive - 1)); | (vactive - 1));
write32(mmio + VBLANK(0), ((vactive + bottom_border + vblank - 1) << 16)
| (vactive + bottom_border - 1));
write32(mmio + VSYNC(0), write32(mmio + VSYNC(0),
((vactive - 1) << 16) ((vactive + bottom_border + vfront_porch + vsync - 1) << 16)
| (vactive - 1)); | (vactive + bottom_border + vfront_porch - 1));
write32(mmio + PIPECONF(0), PIPECONF_DISABLE); write32(mmio + PIPECONF(0), PIPECONF_DISABLE);
write32(mmio + PF_WIN_POS(0), 0); write32(mmio + PF_WIN_POS(0), 0);
if (IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)) {
write32(mmio + PIPESRC(0), (639 << 16) | 399); write32(mmio + PIPESRC(0), ((hactive - 1) << 16)
write32(mmio + PF_CTL(0),PF_ENABLE | PF_FILTER_MED_3x3); | (vactive - 1));
write32(mmio + PF_WIN_SZ(0), vactive | (hactive << 16)); write32(mmio + PF_CTL(0), 0);
write32(mmio + PFIT_CONTROL, 0xa0000000); write32(mmio + PF_WIN_SZ(0), 0);
write32(mmio + PFIT_CONTROL, 0);
} else {
write32(mmio + PIPESRC(0), (639 << 16) | 399);
write32(mmio + PF_CTL(0), PF_ENABLE | PF_FILTER_MED_3x3);
write32(mmio + PF_WIN_SZ(0), vactive | (hactive << 16));
write32(mmio + PFIT_CONTROL, 0x80000000);
}
mdelay(1); mdelay(1);
write32(mmio + PIPE_DATA_M1(0), 0x7e000000 | data_m1);
write32(mmio + PIPE_DATA_N1(0), data_n1);
write32(mmio + PIPE_LINK_M1(0), link_m1);
write32(mmio + PIPE_LINK_N1(0), link_n1);
write32(mmio + 0x000f000c, 0x00002040); write32(mmio + 0x000f000c, 0x00002040);
mdelay(1); mdelay(1);
write32(mmio + 0x000f000c, 0x00002050); write32(mmio + 0x000f000c, 0x00002050);
write32(mmio + 0x00060100, 0x00044000); write32(mmio + 0x00060100, 0x00044000);
mdelay(1); mdelay(1);
write32(mmio + PIPECONF(0), PIPECONF_BPP_6);
write32(mmio + 0x000f0008, 0x00000040);
write32(mmio + 0x000f000c, 0x00022050);
write32(mmio + PIPECONF(0), PIPECONF_BPP_6 | PIPECONF_DITHER_EN);
write32(mmio + PIPECONF(0), PIPECONF_ENABLE write32(mmio + PIPECONF(0), PIPECONF_ENABLE
| PIPECONF_BPP_6 | PIPECONF_DITHER_EN); | PIPECONF_BPP_6 | PIPECONF_DITHER_EN);
write32(mmio + VGACNTRL, 0x0); if (IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)) {
write32(mmio + DSPCNTR(0), DISPLAY_PLANE_ENABLE | DISPPLANE_BGRX888); write32(mmio + VGACNTRL, VGA_DISP_DISABLE);
mdelay(1); write32(mmio + DSPCNTR(0), DISPLAY_PLANE_ENABLE
| DISPPLANE_BGRX888);
mdelay(1);
} else {
write32(mmio + VGACNTRL, 0xc4008e);
}
write32(mmio + ADPA, ADPA_DAC_ENABLE write32(mmio + ADPA, ADPA_DAC_ENABLE
| ADPA_PIPE_A_SELECT | ADPA_PIPE_A_SELECT
| ADPA_CRT_HOTPLUG_MONITOR_COLOR | ADPA_CRT_HOTPLUG_MONITOR_COLOR
| ADPA_CRT_HOTPLUG_ENABLE | ADPA_CRT_HOTPLUG_ENABLE
| ADPA_USE_VGA_HVPOLARITY
| ADPA_VSYNC_CNTL_ENABLE | ADPA_VSYNC_CNTL_ENABLE
| ADPA_HSYNC_CNTL_ENABLE | ADPA_HSYNC_CNTL_ENABLE
| ADPA_DPMS_ON | ADPA_DPMS_ON
); | (vpolarity ? ADPA_VSYNC_ACTIVE_LOW :
ADPA_VSYNC_ACTIVE_HIGH)
| (hpolarity ? ADPA_HSYNC_ACTIVE_LOW :
ADPA_HSYNC_ACTIVE_HIGH));
vga_textmode_init(); write32(mmio + PP_CONTROL, PANEL_POWER_ON | PANEL_POWER_RESET);
/* Enable screen memory. */ /* Enable screen memory. */
vga_sr_write(1, vga_sr_read(1) & ~0x20); vga_sr_write(1, vga_sr_read(1) & ~0x20);
/* Clear interrupts. */ /* Clear interrupts. */
write32(mmio + DEIIR, 0xffffffff); write32(mmio + DEIIR, 0xffffffff);
write32(mmio + SDEIIR, 0xffffffff); write32(mmio + SDEIIR, 0xffffffff);
if (IS_ENABLED(CONFIG_FRAMEBUFFER_KEEP_VESA_MODE)) {
memset((void *) lfb, 0,
hactive * vactive * 4);
set_vbe_mode_info_valid(&edid, lfb);
}
} }
static void native_init(struct device *dev) static void native_init(struct device *dev)
{ {
struct resource *lfb_res;
struct resource *pio_res;
u32 physbase;
struct resource *gtt_res = find_resource(dev, PCI_BASE_ADDRESS_0); struct resource *gtt_res = find_resource(dev, PCI_BASE_ADDRESS_0);
struct northbridge_intel_x4x_config *conf = dev->chip_info; struct northbridge_intel_x4x_config *conf = dev->chip_info;
lfb_res = find_resource(dev, PCI_BASE_ADDRESS_2);
pio_res = find_resource(dev, PCI_BASE_ADDRESS_4);
physbase = pci_read_config32(dev, 0x5c) & ~0xf;
if (gtt_res && gtt_res->base) { if (gtt_res && gtt_res->base) {
printk(BIOS_SPEW, printk(BIOS_SPEW,
"Initializing VGA without OPROM. MMIO 0x%llx\n", "Initializing VGA without OPROM. MMIO 0x%llx\n",
gtt_res->base); gtt_res->base);
intel_gma_init(conf, res2mmio(gtt_res, 0, 0)); intel_gma_init(conf, res2mmio(gtt_res, 0, 0),
physbase, pio_res->base, lfb_res->base);
} }
/* Linux relies on VBT for panel info. */ /* Linux relies on VBT for panel info. */
@ -182,6 +377,7 @@ static void native_init(struct device *dev)
static void gma_func0_init(struct device *dev) static void gma_func0_init(struct device *dev)
{ {
u16 reg16;
u32 reg32; u32 reg32;
/* IGD needs to be Bus Master */ /* IGD needs to be Bus Master */
@ -189,6 +385,12 @@ static void gma_func0_init(struct device *dev)
reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
pci_write_config32(dev, PCI_COMMAND, reg32); pci_write_config32(dev, PCI_COMMAND, reg32);
/* configure GMBUSFREQ */
reg16 = pci_read_config16(dev_find_slot(0, PCI_DEVFN(0x2,0)), 0xcc);
reg16 &= ~0x1ff;
reg16 |= 0xbc;
pci_write_config16(dev_find_slot(0, PCI_DEVFN(0x2,0)), 0xcc, reg16);
if (IS_ENABLED(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT)) if (IS_ENABLED(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT))
native_init(dev); native_init(dev);
else else