summaryrefslogtreecommitdiffstats
path: root/qemu/roms/seabios/vgasrc/geodevga.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/seabios/vgasrc/geodevga.c')
-rw-r--r--qemu/roms/seabios/vgasrc/geodevga.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/qemu/roms/seabios/vgasrc/geodevga.c b/qemu/roms/seabios/vgasrc/geodevga.c
new file mode 100644
index 000000000..f8f61c3a1
--- /dev/null
+++ b/qemu/roms/seabios/vgasrc/geodevga.c
@@ -0,0 +1,433 @@
+// 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" // VGAREG_*
+
+
+/****************************************************************
+* 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;
+}