vgabios: Add support for vbe get/set line length function.
[seabios.git] / vgasrc / vgabios.c
index cd39cf1311a910e4e69ca6080d8a242bdabdab60..d00016685f884f9fb49e254b00c4fd4e046ef274 100644 (file)
 #include "stdvga.h" // stdvga_set_cursor_shape
 #include "clext.h" // clext_1012
 #include "vgahw.h" // vgahw_set_mode
+#include "vbe.h" // VBE_RETURN_STATUS_FAILED
+#include "pci.h" // pci_config_readw
+#include "pci_regs.h" // PCI_VENDOR_ID
 
 // XXX
 #define DEBUG_VGA_POST 1
 #define DEBUG_VGA_10 3
 
+// Standard Video Save Pointer Table
+struct VideoSavePointer_s {
+    struct segoff_s videoparam;
+    struct segoff_s paramdynamicsave;
+    struct segoff_s textcharset;
+    struct segoff_s graphcharset;
+    struct segoff_s secsavepointer;
+    u8 reserved[8];
+} PACKED;
+
+static struct VideoSavePointer_s video_save_pointer_table VAR16;
+
+struct VideoParam_s video_param_table[29] VAR16;
+
 
 /****************************************************************
  * PCI Data
@@ -46,6 +63,22 @@ struct pci_data rom_pci_data VAR16VISIBLE = {
  * Helper functions
  ****************************************************************/
 
+// Return the bits per pixel in system memory for a given mode.
+int
+vga_bpp(struct vgamode_s *vmode_g)
+{
+    switch (GET_GLOBAL(vmode_g->memmodel)) {
+    case MM_TEXT:
+        return 16;
+    case MM_PLANAR:
+        return 1;
+    }
+    u8 depth = GET_GLOBAL(vmode_g->depth);
+    if (depth > 8)
+        return ALIGN(depth, 8);
+    return depth;
+}
+
 u16
 calc_page_size(u8 memmodel, u16 width, u16 height)
 {
@@ -135,7 +168,7 @@ set_active_page(u8 page)
         return;
 
     // Get the mode
-    struct vgamode_s *vmode_g = vgahw_find_mode(GET_BDA(video_mode));
+    struct vgamode_s *vmode_g = get_current_mode();
     if (!vmode_g)
         return;
 
@@ -300,7 +333,9 @@ save_bda_state(u16 seg, struct saveBDAstate *info)
 static void
 restore_bda_state(u16 seg, struct saveBDAstate *info)
 {
-    SET_BDA(video_mode, GET_FARVAR(seg, info->video_mode));
+    u16 mode = GET_FARVAR(seg, info->video_mode);
+    SET_BDA(video_mode, mode);
+    SET_BDA(vbe_mode, mode);
     SET_BDA(video_cols, GET_FARVAR(seg, info->video_cols));
     SET_BDA(video_pagesize, GET_FARVAR(seg, info->video_pagesize));
     SET_BDA(crtc_address, GET_FARVAR(seg, info->crtc_address));
@@ -320,16 +355,40 @@ restore_bda_state(u16 seg, struct saveBDAstate *info)
     SET_IVT(0x43, GET_FARVAR(seg, info->font1));
 }
 
+
+/****************************************************************
+ * Mode setting
+ ****************************************************************/
+
+struct vgamode_s *
+get_current_mode(void)
+{
+    return vgahw_find_mode(GET_BDA(vbe_mode) & ~MF_VBEFLAGS);
+}
+
 // Setup BDA after a mode switch.
-void
-modeswitch_set_bda(int mode, int flags, struct vgamode_s *vmode_g)
+int
+vga_set_mode(int mode, int flags)
 {
+    dprintf(1, "set VGA mode %x\n", mode);
+    struct vgamode_s *vmode_g = vgahw_find_mode(mode);
+    if (!vmode_g)
+        return VBE_RETURN_STATUS_FAILED;
+
+    int ret = vgahw_set_mode(vmode_g, flags);
+    if (ret)
+        return ret;
+
     // Set the BIOS mem
     int width = GET_GLOBAL(vmode_g->width);
     int height = GET_GLOBAL(vmode_g->height);
     u8 memmodel = GET_GLOBAL(vmode_g->memmodel);
     int cheight = GET_GLOBAL(vmode_g->cheight);
-    SET_BDA(video_mode, mode);
+    if (mode < 0x100)
+        SET_BDA(video_mode, mode);
+    else
+        SET_BDA(video_mode, 0xff);
+    SET_BDA(vbe_mode, mode | (flags & MF_VBEFLAGS));
     if (memmodel == MM_TEXT) {
         SET_BDA(video_cols, width);
         SET_BDA(video_rows, height-1);
@@ -375,6 +434,8 @@ modeswitch_set_bda(int mode, int flags, struct vgamode_s *vmode_g)
         SET_IVT(0x43, SEGOFF(get_global_seg(), (u32)vgafont16));
         break;
     }
+
+    return 0;
 }
 
 
@@ -399,7 +460,7 @@ handle_1000(struct bregs *regs)
     if (regs->al & 0x80)
         flags |= MF_NOCLEARMEM;
 
-    vgahw_set_mode(mode, flags);
+    vga_set_mode(mode, flags);
 }
 
 static void
@@ -1212,14 +1273,19 @@ init_bios_area(void)
     SET_BDA(video_msr, 0x09);
 }
 
-u16 VgaBDF VAR16;
+int VgaBDF VAR16 = -1;
 
 void VISIBLE16
 vga_post(struct bregs *regs)
 {
     debug_enter(regs, DEBUG_VGA_POST);
 
-    SET_VGA(VgaBDF, regs->ax);
+    if (CONFIG_VGA_PCI) {
+        u16 bdf = regs->ax;
+        if (pci_config_readw(bdf, PCI_VENDOR_ID) == CONFIG_VGA_VID
+            && pci_config_readw(bdf, PCI_DEVICE_ID) == CONFIG_VGA_DID)
+            SET_VGA(VgaBDF, bdf);
+    }
 
     int ret = vgahw_init();
     if (ret) {
@@ -1229,7 +1295,9 @@ vga_post(struct bregs *regs)
 
     init_bios_area();
 
-    build_video_param();
+    SET_VGA(video_save_pointer_table.videoparam
+            , SEGOFF(get_global_seg(), (u32)video_param_table));
+    stdvga_build_video_param();
 
     extern void entry_10(void);
     SET_IVT(0x10, SEGOFF(get_global_seg(), (u32)entry_10));