435 lines
13 KiB
C
435 lines
13 KiB
C
|
// Geode GX2/LX VGA functions
|
||
|
//
|
||
|
// Copyright (C) 2009 Chris Kindt
|
||
|
//
|
||
|
// Written for Google Summer of Code 2009 for the coreboot project
|
||
|
//
|
||
|
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
||
|
|
||
|
#include "biosvar.h" // GET_BDA
|
||
|
#include "farptr.h" // SET_FARVAR
|
||
|
#include "geodevga.h" // geodevga_setup
|
||
|
#include "hw/pci.h" // pci_config_readl
|
||
|
#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0
|
||
|
#include "output.h" // dprintf
|
||
|
#include "stdvga.h" // stdvga_crtc_write
|
||
|
#include "vgabios.h" // SET_VGA
|
||
|
#include "vgautil.h" // VBE_total_memory
|
||
|
|
||
|
|
||
|
/****************************************************************
|
||
|
* MSR and High Mem access through VSA Virtual Register
|
||
|
****************************************************************/
|
||
|
|
||
|
static u64 geode_msr_read(u32 msrAddr)
|
||
|
{
|
||
|
union u64_u32_u val;
|
||
|
asm __volatile__ (
|
||
|
"movw $0x0AC1C, %%dx \n"
|
||
|
"movl $0xFC530007, %%eax \n"
|
||
|
"outl %%eax, %%dx \n"
|
||
|
"addb $2, %%dl \n"
|
||
|
"inw %%dx, %%ax \n"
|
||
|
: "=a" (val.lo), "=d"(val.hi)
|
||
|
: "c"(msrAddr)
|
||
|
: "cc"
|
||
|
);
|
||
|
|
||
|
dprintf(4, "%s(0x%08x) = 0x%08x-0x%08x\n"
|
||
|
, __func__, msrAddr, val.hi, val.lo);
|
||
|
return val.val;
|
||
|
}
|
||
|
|
||
|
static void geode_msr_mask(u32 msrAddr, u64 off, u64 on)
|
||
|
{
|
||
|
union u64_u32_u uand, uor;
|
||
|
uand.val = ~off;
|
||
|
uor.val = on;
|
||
|
|
||
|
dprintf(4, "%s(0x%08x, 0x%016llx, 0x%016llx)\n"
|
||
|
, __func__, msrAddr, off, on);
|
||
|
|
||
|
asm __volatile__ (
|
||
|
"push %%eax \n"
|
||
|
"movw $0x0AC1C, %%dx \n"
|
||
|
"movl $0xFC530007, %%eax \n"
|
||
|
"outl %%eax, %%dx \n"
|
||
|
"addb $2, %%dl \n"
|
||
|
"pop %%eax \n"
|
||
|
"outw %%ax, %%dx \n"
|
||
|
:
|
||
|
: "c"(msrAddr), "S" (uand.hi), "D" (uand.lo), "b" (uor.hi), "a" (uor.lo)
|
||
|
: "%edx","cc"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static u32 geode_mem_read(u32 addr)
|
||
|
{
|
||
|
u32 val;
|
||
|
asm __volatile__ (
|
||
|
"movw $0x0AC1C, %%dx \n"
|
||
|
"movl $0xFC530001, %%eax \n"
|
||
|
"outl %%eax, %%dx \n"
|
||
|
"addb $2, %%dl \n"
|
||
|
"inw %%dx, %%ax \n"
|
||
|
: "=a" (val)
|
||
|
: "b"(addr)
|
||
|
: "cc"
|
||
|
);
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static void geode_mem_mask(u32 addr, u32 off, u32 or)
|
||
|
{
|
||
|
asm __volatile__ (
|
||
|
"movw $0x0AC1C, %%dx \n"
|
||
|
"movl $0xFC530001, %%eax \n"
|
||
|
"outl %%eax, %%dx \n"
|
||
|
"addb $2, %%dl \n"
|
||
|
"outw %%ax, %%dx \n"
|
||
|
:
|
||
|
: "b"(addr), "S" (~off), "D" (or)
|
||
|
: "%eax","cc"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#define VP_FP_START 0x400
|
||
|
|
||
|
static u32 GeodeFB VAR16;
|
||
|
static u32 GeodeDC VAR16;
|
||
|
static u32 GeodeVP VAR16;
|
||
|
|
||
|
static u32 geode_dc_read(int reg)
|
||
|
{
|
||
|
u32 val = geode_mem_read(GET_GLOBAL(GeodeDC) + reg);
|
||
|
dprintf(4, "%s(0x%08x) = 0x%08x\n"
|
||
|
, __func__, GET_GLOBAL(GeodeDC) + reg, val);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static void geode_dc_write(int reg, u32 val)
|
||
|
{
|
||
|
dprintf(4, "%s(0x%08x, 0x%08x)\n"
|
||
|
, __func__, GET_GLOBAL(GeodeDC) + reg, val);
|
||
|
geode_mem_mask(GET_GLOBAL(GeodeDC) + reg, ~0, val);
|
||
|
}
|
||
|
|
||
|
static void geode_dc_mask(int reg, u32 off, u32 on)
|
||
|
{
|
||
|
dprintf(4, "%s(0x%08x, 0x%08x, 0x%08x)\n"
|
||
|
, __func__, GET_GLOBAL(GeodeDC) + reg, off, on);
|
||
|
geode_mem_mask(GET_GLOBAL(GeodeDC) + reg, off, on);
|
||
|
}
|
||
|
|
||
|
static u32 geode_vp_read(int reg)
|
||
|
{
|
||
|
u32 val = geode_mem_read(GET_GLOBAL(GeodeVP) + reg);
|
||
|
dprintf(4, "%s(0x%08x) = 0x%08x\n"
|
||
|
, __func__, GET_GLOBAL(GeodeVP) + reg, val);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static void geode_vp_write(int reg, u32 val)
|
||
|
{
|
||
|
dprintf(4, "%s(0x%08x, 0x%08x)\n"
|
||
|
, __func__, GET_GLOBAL(GeodeVP) + reg, val);
|
||
|
geode_mem_mask(GET_GLOBAL(GeodeVP) + reg, ~0, val);
|
||
|
}
|
||
|
|
||
|
static void geode_vp_mask(int reg, u32 off, u32 on)
|
||
|
{
|
||
|
dprintf(4, "%s(0x%08x, 0x%08x, 0x%08x)\n"
|
||
|
, __func__, GET_GLOBAL(GeodeVP) + reg, off, on);
|
||
|
geode_mem_mask(GET_GLOBAL(GeodeVP) + reg, off, on);
|
||
|
}
|
||
|
|
||
|
static u32 geode_fp_read(int reg)
|
||
|
{
|
||
|
u32 val = geode_mem_read(GET_GLOBAL(GeodeVP) + VP_FP_START + reg);
|
||
|
dprintf(4, "%s(0x%08x) = 0x%08x\n"
|
||
|
, __func__, GET_GLOBAL(GeodeVP) + VP_FP_START + reg, val);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static void geode_fp_write(int reg, u32 val)
|
||
|
{
|
||
|
dprintf(4, "%s(0x%08x, 0x%08x)\n"
|
||
|
, __func__, GET_GLOBAL(GeodeVP) + VP_FP_START + reg, val);
|
||
|
geode_mem_mask(GET_GLOBAL(GeodeVP) + VP_FP_START + reg, ~0, val);
|
||
|
}
|
||
|
|
||
|
/****************************************************************
|
||
|
* Helper functions
|
||
|
****************************************************************/
|
||
|
|
||
|
static int legacyio_check(void)
|
||
|
{
|
||
|
int ret=0;
|
||
|
u64 val;
|
||
|
|
||
|
if (CONFIG_VGA_GEODEGX2)
|
||
|
val = geode_msr_read(GLIU0_P2D_BM_4);
|
||
|
else
|
||
|
val = geode_msr_read(MSR_GLIU0_BASE4);
|
||
|
if ((val & 0xffffffff) != 0x0A0fffe0)
|
||
|
ret|=1;
|
||
|
|
||
|
val = geode_msr_read(GLIU0_IOD_BM_0);
|
||
|
if ((val & 0xffffffff) != 0x3c0ffff0)
|
||
|
ret|=2;
|
||
|
|
||
|
val = geode_msr_read(GLIU0_IOD_BM_1);
|
||
|
if ((val & 0xffffffff) != 0x3d0ffff0)
|
||
|
ret|=4;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static u32 framebuffer_size(void)
|
||
|
{
|
||
|
/* We use the P2D_R0 msr to read out the number of pages.
|
||
|
* One page has a size of 4k
|
||
|
*
|
||
|
* Bit Name Description
|
||
|
* 39:20 PMAX Physical Memory Address Max
|
||
|
* 19:0 PMIX Physical Memory Address Min
|
||
|
*
|
||
|
*/
|
||
|
u64 msr = geode_msr_read(GLIU0_P2D_RO);
|
||
|
|
||
|
u32 pmax = (msr >> 20) & 0x000fffff;
|
||
|
u32 pmin = msr & 0x000fffff;
|
||
|
|
||
|
u32 val = pmax - pmin;
|
||
|
val += 1;
|
||
|
|
||
|
/* The page size is 4k */
|
||
|
return (val << 12);
|
||
|
}
|
||
|
|
||
|
/****************************************************************
|
||
|
* Init Functions
|
||
|
****************************************************************/
|
||
|
|
||
|
static void geodevga_set_output_mode(void)
|
||
|
{
|
||
|
u64 msr_addr;
|
||
|
u64 msr;
|
||
|
|
||
|
/* set output to crt and RGB/YUV */
|
||
|
if (CONFIG_VGA_GEODEGX2)
|
||
|
msr_addr = VP_MSR_CONFIG_GX2;
|
||
|
else
|
||
|
msr_addr = VP_MSR_CONFIG_LX;
|
||
|
|
||
|
/* set output mode (RGB/YUV) */
|
||
|
msr = geode_msr_read(msr_addr);
|
||
|
msr &= ~VP_MSR_CONFIG_FMT; // mask out FMT (bits 5:3)
|
||
|
|
||
|
if (CONFIG_VGA_OUTPUT_PANEL || CONFIG_VGA_OUTPUT_CRT_PANEL) {
|
||
|
msr |= VP_MSR_CONFIG_FMT_FP; // flat panel
|
||
|
|
||
|
if (CONFIG_VGA_OUTPUT_CRT_PANEL) {
|
||
|
msr |= VP_MSR_CONFIG_FPC; // simultaneous Flat Panel and CRT
|
||
|
dprintf(1, "output: simultaneous Flat Panel and CRT\n");
|
||
|
} else {
|
||
|
msr &= ~VP_MSR_CONFIG_FPC; // no simultaneous Flat Panel and CRT
|
||
|
dprintf(1, "ouput: flat panel\n");
|
||
|
}
|
||
|
} else {
|
||
|
msr |= VP_MSR_CONFIG_FMT_CRT; // CRT only
|
||
|
dprintf(1, "output: CRT\n");
|
||
|
}
|
||
|
geode_msr_mask(msr_addr, ~msr, msr);
|
||
|
}
|
||
|
|
||
|
/* Set up the dc (display controller) portion of the geodelx
|
||
|
* The dc provides hardware support for VGA graphics.
|
||
|
*/
|
||
|
static void dc_setup(void)
|
||
|
{
|
||
|
dprintf(2, "DC_SETUP\n");
|
||
|
|
||
|
geode_dc_write(DC_UNLOCK, DC_LOCK_UNLOCK);
|
||
|
|
||
|
/* zero memory config */
|
||
|
geode_dc_write(DC_FB_ST_OFFSET, 0x0);
|
||
|
geode_dc_write(DC_CB_ST_OFFSET, 0x0);
|
||
|
geode_dc_write(DC_CURS_ST_OFFSET, 0x0);
|
||
|
|
||
|
geode_dc_mask(DC_DISPLAY_CFG, ~DC_CFG_MSK, DC_DISPLAY_CFG_GDEN|DC_DISPLAY_CFG_TRUP);
|
||
|
geode_dc_write(DC_GENERAL_CFG, DC_GENERAL_CFG_VGAE);
|
||
|
|
||
|
geode_dc_write(DC_UNLOCK, DC_LOCK_LOCK);
|
||
|
}
|
||
|
|
||
|
/* Setup the vp (video processor) portion of the geodelx
|
||
|
* Under VGA modes the vp was handled by softvg from inside VSA2.
|
||
|
* Without a softvg module, access is only available through a pci bar.
|
||
|
* The High Mem Access virtual register is used to configure the
|
||
|
* pci mmio bar from 16bit friendly io space.
|
||
|
*/
|
||
|
static void vp_setup(void)
|
||
|
{
|
||
|
dprintf(2,"VP_SETUP\n");
|
||
|
|
||
|
geodevga_set_output_mode();
|
||
|
|
||
|
/* Set mmio registers
|
||
|
* there may be some timing issues here, the reads seem
|
||
|
* to slow things down enough work reliably
|
||
|
*/
|
||
|
|
||
|
u32 reg = geode_vp_read(VP_MISC);
|
||
|
dprintf(1,"VP_SETUP VP_MISC=0x%08x\n",reg);
|
||
|
geode_vp_write(VP_MISC, VP_DCFG_BYP_BOTH);
|
||
|
reg = geode_vp_read(VP_MISC);
|
||
|
dprintf(1,"VP_SETUP VP_MISC=0x%08x\n",reg);
|
||
|
|
||
|
reg = geode_vp_read(VP_DCFG);
|
||
|
dprintf(1,"VP_SETUP VP_DCFG=0x%08x\n",reg);
|
||
|
geode_vp_mask(VP_DCFG, 0, VP_DCFG_CRT_EN|VP_DCFG_HSYNC_EN|VP_DCFG_VSYNC_EN|VP_DCFG_DAC_BL_EN|VP_DCFG_CRT_SKEW);
|
||
|
reg = geode_vp_read(VP_DCFG);
|
||
|
dprintf(1,"VP_SETUP VP_DCFG=0x%08x\n",reg);
|
||
|
|
||
|
/* setup flat panel */
|
||
|
if (CONFIG_VGA_OUTPUT_PANEL || CONFIG_VGA_OUTPUT_CRT_PANEL) {
|
||
|
u64 msr;
|
||
|
|
||
|
dprintf(1, "Setting up flat panel\n");
|
||
|
/* write timing register */
|
||
|
geode_fp_write(FP_PT1, 0x0);
|
||
|
geode_fp_write(FP_PT2, FP_PT2_SCRC);
|
||
|
|
||
|
/* set pad select for TFT/LVDS */
|
||
|
msr = VP_MSR_PADSEL_TFT_SEL_HIGH;
|
||
|
msr = msr << 32;
|
||
|
msr |= VP_MSR_PADSEL_TFT_SEL_LOW;
|
||
|
geode_msr_mask(VP_MSR_PADSEL, ~msr, msr);
|
||
|
|
||
|
/* turn the panel on (if it isn't already) */
|
||
|
reg = geode_fp_read(FP_PM);
|
||
|
reg |= FP_PM_P;
|
||
|
geode_fp_write(FP_PM, reg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static u8 geode_crtc_01[] VAR16 = {
|
||
|
0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f,
|
||
|
0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x14, 0x1f, 0x97, 0xb9, 0xa3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_03[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x28, 0x1f, 0x97, 0xb9, 0xa3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_04[] VAR16 = {
|
||
|
0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f,
|
||
|
0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xa2,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_05[] VAR16 = {
|
||
|
0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f,
|
||
|
0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8e, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xa2,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_06[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x28, 0x00, 0x97, 0xb9, 0xc2,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_07[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x28, 0x0f, 0x97, 0xb9, 0xa3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_0d[] VAR16 = {
|
||
|
0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f,
|
||
|
0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xe3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_0e[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x28, 0x00, 0x97, 0xb9, 0xe3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_0f[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x83, 0x85, 0x5d, 0x28, 0x0f, 0x65, 0xb9, 0xe3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_11[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0x0b, 0x3e,
|
||
|
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0xe9, 0x8b, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
|
||
|
0xff };
|
||
|
static u8 geode_crtc_13[] VAR16 = {
|
||
|
0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f,
|
||
|
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x9b, 0x8d, 0x8f, 0x28, 0x40, 0x98, 0xb9, 0xa3,
|
||
|
0xff };
|
||
|
|
||
|
int geodevga_setup(void)
|
||
|
{
|
||
|
int ret = stdvga_setup();
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
dprintf(1,"GEODEVGA_SETUP\n");
|
||
|
|
||
|
if ((ret=legacyio_check())) {
|
||
|
dprintf(1,"GEODEVGA_SETUP legacyio_check=0x%x\n",ret);
|
||
|
}
|
||
|
|
||
|
// Updated timings from geode datasheets, table 6-53 in particular
|
||
|
static u8 *new_crtc[] VAR16 = {
|
||
|
geode_crtc_01, geode_crtc_01, geode_crtc_03, geode_crtc_03,
|
||
|
geode_crtc_04, geode_crtc_05, geode_crtc_06, geode_crtc_07,
|
||
|
0, 0, 0, 0, 0,
|
||
|
geode_crtc_0d, geode_crtc_0e, geode_crtc_0f, geode_crtc_0f,
|
||
|
geode_crtc_11, geode_crtc_11, geode_crtc_13 };
|
||
|
int i;
|
||
|
for (i=0; i<ARRAY_SIZE(new_crtc); i++) {
|
||
|
u8 *crtc = GET_GLOBAL(new_crtc[i]);
|
||
|
if (crtc)
|
||
|
stdvga_override_crtc(i, crtc);
|
||
|
}
|
||
|
|
||
|
if (GET_GLOBAL(VgaBDF) < 0)
|
||
|
// Device should be at 00:01.1
|
||
|
SET_VGA(VgaBDF, pci_to_bdf(0, 1, 1));
|
||
|
|
||
|
// setup geode struct which is used for register access
|
||
|
SET_VGA(GeodeFB, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_0));
|
||
|
SET_VGA(GeodeDC, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_2));
|
||
|
SET_VGA(GeodeVP, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_3));
|
||
|
|
||
|
dprintf(1, "fb addr: 0x%08x\n", GET_GLOBAL(GeodeFB));
|
||
|
dprintf(1, "dc addr: 0x%08x\n", GET_GLOBAL(GeodeDC));
|
||
|
dprintf(1, "vp addr: 0x%08x\n", GET_GLOBAL(GeodeVP));
|
||
|
|
||
|
/* setup framebuffer */
|
||
|
geode_dc_write(DC_UNLOCK, DC_LOCK_UNLOCK);
|
||
|
|
||
|
/* read fb-bar from pci, then point dc to the fb base */
|
||
|
u32 fb = GET_GLOBAL(GeodeFB);
|
||
|
if (geode_dc_read(DC_GLIU0_MEM_OFFSET) != fb)
|
||
|
geode_dc_write(DC_GLIU0_MEM_OFFSET, fb);
|
||
|
|
||
|
geode_dc_write(DC_UNLOCK, DC_LOCK_LOCK);
|
||
|
|
||
|
u32 fb_size = framebuffer_size(); // in byte
|
||
|
dprintf(1, "%d KB of video memory at 0x%08x\n", fb_size / 1024, fb);
|
||
|
|
||
|
/* update VBE variables */
|
||
|
SET_VGA(VBE_framebuffer, fb);
|
||
|
SET_VGA(VBE_total_memory, fb_size / 1024 / 64); // number of 64K blocks
|
||
|
|
||
|
vp_setup();
|
||
|
dc_setup();
|
||
|
|
||
|
return 0;
|
||
|
}
|