Currently lenovo/x60 gfx init provides vbe_mode_info_valid in incompatible way. Use EDID framework as do other inits. Change-Id: I887abd5a09064f26f473a2bf9caa2eb33e269c07 Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com> Reviewed-on: http://review.coreboot.org/5238 Tested-by: build bot (Jenkins) Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net> Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
374 lines
9.7 KiB
C
374 lines
9.7 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2012 Google Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <device/device.h>
|
|
#include <device/device.h>
|
|
#include <device/pci_def.h>
|
|
#include <device/pci_ops.h>
|
|
#include <console/console.h>
|
|
#include <delay.h>
|
|
#include <pc80/mc146818rtc.h>
|
|
#include <arch/acpi.h>
|
|
#include <arch/io.h>
|
|
#include <arch/interrupt.h>
|
|
#include <boot/coreboot_tables.h>
|
|
#include <southbridge/intel/bd82x6x/pch.h>
|
|
#include <smbios.h>
|
|
#include <device/pci.h>
|
|
#include <ec/google/chromeec/ec.h>
|
|
#include <cbfs_core.h>
|
|
|
|
#include <cpu/x86/tsc.h>
|
|
#include <cpu/x86/cache.h>
|
|
#include <cpu/x86/mtrr.h>
|
|
#include <cpu/x86/msr.h>
|
|
#include <edid.h>
|
|
#include "i915io.h"
|
|
|
|
enum {
|
|
vmsg = 1, vio = 2, vspin = 4,
|
|
};
|
|
|
|
#define PGETLB_CTL 0x2020
|
|
|
|
static int verbose = 0;
|
|
|
|
static unsigned int *mmio;
|
|
static unsigned int graphics;
|
|
static unsigned short addrport;
|
|
static unsigned short dataport;
|
|
static unsigned int physbase;
|
|
|
|
#define PGETBL_CTL 0x2020
|
|
#define PGETBL_ENABLED 0x00000001
|
|
|
|
|
|
static u32 htotal, hblank, hsync, vtotal, vblank, vsync;
|
|
|
|
const u8 x60_edid_data[] = {
|
|
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0xae, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0f, 0x01, 0x03, 0x80, 0x19, 0x12, 0x78, 0xea, 0xed, 0x75, 0x91, 0x57, 0x4f, 0x8b, 0x26,
|
|
0x21, 0x50, 0x54, 0x21, 0x08, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x15, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88,
|
|
0x36, 0x00, 0xf6, 0xb9, 0x00, 0x00, 0x00, 0x18, 0xed, 0x10, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30,
|
|
0x18, 0x88, 0x36, 0x00, 0xf6, 0xb9, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x61,
|
|
0x43, 0x32, 0x61, 0x43, 0x28, 0x0f, 0x01, 0x00, 0x4c, 0xa3, 0x58, 0x4a, 0x00, 0x00, 0x00, 0xfe,
|
|
0x00, 0x4c, 0x54, 0x4e, 0x31, 0x32, 0x31, 0x58, 0x4a, 0x2d, 0x4c, 0x30, 0x37, 0x0a, 0x00, 0x00,
|
|
};
|
|
#define READ32(addr) io_i915_READ32(addr)
|
|
#define WRITE32(val, addr) io_i915_WRITE32(val, addr)
|
|
|
|
static char *regname(unsigned long addr)
|
|
{
|
|
static char name[16];
|
|
snprintf(name, sizeof (name), "0x%lx", addr);
|
|
return name;
|
|
}
|
|
|
|
unsigned long io_i915_READ32(unsigned long addr)
|
|
{
|
|
unsigned long val;
|
|
outl(addr, addrport);
|
|
val = inl(dataport);
|
|
if (verbose & vio)
|
|
printk(BIOS_SPEW, "%s: Got %08lx\n", regname(addr), val);
|
|
return val;
|
|
}
|
|
|
|
void io_i915_WRITE32(unsigned long val, unsigned long addr)
|
|
{
|
|
if (verbose & vio)
|
|
printk(BIOS_SPEW, "%s: outl %08lx\n", regname(addr), val);
|
|
outl(addr, addrport);
|
|
outl(val, dataport);
|
|
}
|
|
|
|
|
|
/*
|
|
2560
|
|
4 words per
|
|
4 *p
|
|
10240
|
|
4k bytes per page
|
|
4096/p
|
|
2.50
|
|
1700 lines
|
|
1700 * p
|
|
4250.00
|
|
PTEs
|
|
*/
|
|
static void
|
|
setgtt(int start, int end, unsigned long base, int inc)
|
|
{
|
|
int i;
|
|
|
|
printk(BIOS_INFO, "%s(%d,%d,0x%08lx,%d);\n",
|
|
__func__, start, end, base, inc);
|
|
|
|
for (i = start; i < end; i++) {
|
|
u32 word = base + i*inc;
|
|
WRITE32(word|1, (i*4)|1);
|
|
}
|
|
}
|
|
|
|
int gtt_setup(unsigned int mmiobase);
|
|
int gtt_setup(unsigned int mmiobase)
|
|
{
|
|
unsigned long PGETBL_save;
|
|
|
|
PGETBL_save = read32(mmiobase + PGETBL_CTL) & ~PGETBL_ENABLED;
|
|
PGETBL_save |= PGETBL_ENABLED;
|
|
|
|
PGETBL_save |= pci_read_config32(dev_find_slot(0, PCI_DEVFN(2,0)), 0x5c) & 0xfffff000;
|
|
PGETBL_save |= 2; /* set GTT to 256kb */
|
|
|
|
write32(mmiobase + GFX_FLSH_CNTL, 0);
|
|
|
|
write32(mmiobase + PGETBL_CTL, PGETBL_save);
|
|
|
|
/* verify */
|
|
if (read32(mmiobase + PGETBL_CTL) & PGETBL_ENABLED) {
|
|
printk(BIOS_DEBUG, "gtt_setup is enabled.\n");
|
|
} else {
|
|
printk(BIOS_DEBUG, "gtt_setup failed!!!\n");
|
|
return 1;
|
|
}
|
|
write32(mmiobase + GFX_FLSH_CNTL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static unsigned long tickspermicrosecond = 1795;
|
|
static unsigned long long globalstart;
|
|
|
|
static unsigned long
|
|
microseconds(unsigned long long start, unsigned long long end)
|
|
{
|
|
unsigned long ret;
|
|
ret = ((end - start)/tickspermicrosecond);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned long globalmicroseconds(void)
|
|
{
|
|
return microseconds(globalstart, rdtscll());
|
|
}
|
|
|
|
extern struct iodef iodefs[];
|
|
extern int niodefs;
|
|
|
|
/* fill the palette. This runs when the P opcode is hit. */
|
|
static void palette(void)
|
|
{
|
|
int i;
|
|
unsigned long color = 0;
|
|
|
|
for (i = 0; i < 256; i++, color += 0x010101)
|
|
io_i915_WRITE32(color, _LGC_PALETTE_A + (i<<2));
|
|
}
|
|
|
|
static unsigned long times[4096];
|
|
|
|
static int run(int index)
|
|
{
|
|
int i, prev = 0;
|
|
struct iodef *id, *lastidread = 0;
|
|
unsigned long u, t;
|
|
if (index >= niodefs)
|
|
return index;
|
|
/* state machine! */
|
|
for (i = index, id = &iodefs[i]; id->op; i++, id++) {
|
|
switch (id->op) {
|
|
case M:
|
|
if (verbose & vmsg)
|
|
printk(BIOS_SPEW, "%ld: %s\n",
|
|
globalmicroseconds(), id->msg);
|
|
break;
|
|
case P:
|
|
palette();
|
|
break;
|
|
case R:
|
|
u = READ32(id->addr);
|
|
if (verbose & vio)
|
|
printk(BIOS_SPEW, "\texpect %08lx\n", id->data);
|
|
/* we're looking for something. */
|
|
if (lastidread->addr == id->addr) {
|
|
/* they're going to be polling.
|
|
* just do it 1000 times
|
|
*/
|
|
for (t = 0; t < 1000 && id->data != u; t++)
|
|
u = READ32(id->addr);
|
|
|
|
if (verbose & vspin)
|
|
printk(BIOS_SPEW,
|
|
"%s: # loops %ld got %08lx want %08lx\n",
|
|
regname(id->addr),
|
|
t, u, id->data);
|
|
}
|
|
lastidread = id;
|
|
break;
|
|
case W:
|
|
WRITE32(id->data, id->addr);
|
|
if (id->addr == PCH_PP_CONTROL) {
|
|
if (verbose & vio)
|
|
printk(BIOS_SPEW, "PCH_PP_CONTROL\n");
|
|
switch (id->data & 0xf) {
|
|
case 8:
|
|
break;
|
|
case 7:
|
|
break;
|
|
default:
|
|
udelay(100000);
|
|
if (verbose & vio)
|
|
printk(BIOS_SPEW, "U %d\n",
|
|
100000);
|
|
}
|
|
}
|
|
break;
|
|
case V:
|
|
if (id->count < 8) {
|
|
prev = verbose;
|
|
verbose = id->count;
|
|
} else {
|
|
verbose = prev;
|
|
}
|
|
printk(BIOS_SPEW, "Change verbosity to %d\n", verbose);
|
|
break;
|
|
case I:
|
|
printk(BIOS_SPEW, "run: return %d\n", i+1);
|
|
return i+1;
|
|
break;
|
|
default:
|
|
printk(BIOS_SPEW, "BAD TABLE, opcode %d @ %d\n",
|
|
id->op, i);
|
|
return -1;
|
|
}
|
|
if (id->udelay)
|
|
udelay(id->udelay);
|
|
if (i < ARRAY_SIZE(times))
|
|
times[i] = globalmicroseconds();
|
|
}
|
|
printk(BIOS_SPEW, "run: return %d\n", i);
|
|
return i+1;
|
|
}
|
|
|
|
int i915lightup(unsigned int physbase, unsigned int iobase, unsigned int mmio,
|
|
unsigned int gfx);
|
|
|
|
int i915lightup(unsigned int pphysbase, unsigned int piobase,
|
|
unsigned int pmmio, unsigned int pgfx)
|
|
{
|
|
static struct edid edid;
|
|
|
|
int index;
|
|
unsigned long temp;
|
|
mmio = (void *)pmmio;
|
|
addrport = piobase;
|
|
dataport = addrport + 4;
|
|
physbase = pphysbase;
|
|
graphics = pgfx;
|
|
printk(BIOS_SPEW,
|
|
"i915lightup: graphics %p mmio %p addrport %04x physbase %08x\n",
|
|
(void *)graphics, mmio, addrport, physbase);
|
|
globalstart = rdtscll();
|
|
|
|
|
|
decode_edid((unsigned char *)&x60_edid_data,
|
|
sizeof(x60_edid_data), &edid);
|
|
|
|
htotal = (edid.ha - 1) | ((edid.ha + edid.hbl - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(HTOTAL(pipe), %08x)\n", htotal);
|
|
|
|
hblank = (edid.ha - 1) | ((edid.ha + edid.hbl - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(HBLANK(pipe),0x%08x)\n", hblank);
|
|
|
|
hsync = (edid.ha + edid.hso - 1) |
|
|
((edid.ha + edid.hso + edid.hspw - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(HSYNC(pipe),0x%08x)\n", hsync);
|
|
|
|
vtotal = (edid.va - 1) | ((edid.va + edid.vbl - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(VTOTAL(pipe), %08x)\n", vtotal);
|
|
|
|
vblank = (edid.va - 1) | ((edid.va + edid.vbl - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(VBLANK(pipe),0x%08x)\n", vblank);
|
|
|
|
vsync = (edid.va + edid.vso - 1) |
|
|
((edid.va + edid.vso + edid.vspw - 1) << 16);
|
|
printk(BIOS_SPEW, "I915_WRITE(VSYNC(pipe),0x%08x)\n", vsync);
|
|
|
|
printk(BIOS_SPEW, "Table has %d elements\n", niodefs);
|
|
|
|
index = run(0);
|
|
printk(BIOS_SPEW, "Run returns %d\n", index);
|
|
|
|
verbose = 0;
|
|
/* GTT is the Global Translation Table for the graphics pipeline.
|
|
* It is used to translate graphics addresses to physical
|
|
* memory addresses. As in the CPU, GTTs map 4K pages.
|
|
* There are 32 bits per pixel, or 4 bytes,
|
|
* which means 1024 pixels per page.
|
|
* There are 4250 GTTs on Link:
|
|
* 2650 (X) * 1700 (Y) pixels / 1024 pixels per page.
|
|
* The setgtt function adds a further bit of flexibility:
|
|
* it allows you to set a range (the first two parameters) to point
|
|
* to a physical address (third parameter);the physical address is
|
|
* incremented by a count (fourth parameter) for each GTT in the
|
|
* range.
|
|
* Why do it this way? For ultrafast startup,
|
|
* we can point all the GTT entries to point to one page,
|
|
* and set that page to 0s:
|
|
* memset(physbase, 0, 4096);
|
|
* setgtt(0, 4250, physbase, 0);
|
|
* this takes about 2 ms, and is a win because zeroing
|
|
* the page takes a up to 200 ms. We will be exploiting this
|
|
* trick in a later rev of this code.
|
|
* This call sets the GTT to point to a linear range of pages
|
|
* starting at physbase.
|
|
*/
|
|
|
|
if (gtt_setup(pmmio)) {
|
|
printk(BIOS_ERR, "ERROR: GTT Setup Failed!!!\n");
|
|
return 0;
|
|
}
|
|
|
|
setgtt(0, 800 , physbase, 4096);
|
|
|
|
temp = READ32(PGETLB_CTL);
|
|
printk(BIOS_INFO, "GTT PGETLB_CTL register: 0x%lx\n", temp);
|
|
|
|
if (temp & 1)
|
|
printk(BIOS_INFO, "GTT Enabled\n");
|
|
else
|
|
printk(BIOS_ERR, "ERROR: GTT is still Disabled!!!\n");
|
|
|
|
printk(BIOS_SPEW, "memset %p to 0x00 for %d bytes\n",
|
|
(void *)graphics, FRAME_BUFFER_BYTES);
|
|
memset((void *)graphics, 0x00, FRAME_BUFFER_BYTES);
|
|
|
|
printk(BIOS_SPEW, "%ld microseconds\n", globalmicroseconds());
|
|
|
|
set_vbe_mode_info_valid(&edid, graphics);
|
|
|
|
return 0;
|
|
}
|