From 067da5c45dcd6c45a44630795dc702e32112c53c Mon Sep 17 00:00:00 2001 From: Jan Dolezal Date: Thu, 20 Nov 2014 15:00:33 +0100 Subject: i386/pc386: VESA based frame buffer utilizing real mode interrupt 10h --- c/src/lib/libbsp/i386/pc386/Makefile.am | 5 + c/src/lib/libbsp/i386/pc386/configure.ac | 13 + c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c | 858 +++++++++++++++++++++++ c/src/lib/libbsp/i386/pc386/include/fb_vesa.h | 131 ++++ c/src/lib/libbsp/i386/pc386/preinstall.am | 6 + c/src/lib/libbsp/i386/pc386/start/start.S | 8 + 6 files changed, 1021 insertions(+) create mode 100644 c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c create mode 100644 c/src/lib/libbsp/i386/pc386/include/fb_vesa.h diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am b/c/src/lib/libbsp/i386/pc386/Makefile.am index de970a34f3..391bfa99e3 100644 --- a/c/src/lib/libbsp/i386/pc386/Makefile.am +++ b/c/src/lib/libbsp/i386/pc386/Makefile.am @@ -106,13 +106,18 @@ libbsp_a_SOURCES += console/printk_support.c libbsp_a_SOURCES += console/vgacons.c libbsp_a_SOURCES += console/exar17d15x.c libbsp_a_SOURCES += console/rtd316.c +if USE_VBE_RM include_bsp_HEADERS += include/vbe3.h include_HEADERS += include/edid.h +include_bsp_HEADERS += include/fb_vesa.h +libbsp_a_SOURCES += console/fb_vesa_rm.c +else if USE_CIRRUS_GD5446 libbsp_a_SOURCES += console/fb_cirrus.c else libbsp_a_SOURCES += console/fb_vga.c endif +endif # gdb libbsp_a_SOURCES += ../../i386/shared/comm/i386-stub.c diff --git a/c/src/lib/libbsp/i386/pc386/configure.ac b/c/src/lib/libbsp/i386/pc386/configure.ac index ecec056bff..a74d6cbc3c 100644 --- a/c/src/lib/libbsp/i386/pc386/configure.ac +++ b/c/src/lib/libbsp/i386/pc386/configure.ac @@ -82,6 +82,19 @@ RTEMS_BSPOPTS_HELP([USE_CIRRUS_GD5446], NOTE: This has only been tested on Qemu.]) AM_CONDITIONAL(USE_CIRRUS_GD5446,test "$USE_CIRRUS_GD5446" = "1") +RTEMS_BSPOPTS_SET([USE_VBE_RM],[*],[0]) +RTEMS_BSPOPTS_HELP([USE_VBE_RM], +[If defined, enables use of the Vesa Bios Extensions - real mode interface, + which enables graphical mode and introduce it upon bootup.]) +AM_CONDITIONAL(USE_VBE_RM,test "$USE_VBE_RM" = "1") + +if test "${USE_VBE_RM}" = "1" ; then + if test -z "${NUM_APP_DRV_GDT_DESCRIPTORS}"; then + NUM_APP_DRV_GDT_DESCRIPTORS=2 ; + else + NUM_APP_DRV_GDT_DESCRIPTORS+=2 ; + fi +fi RTEMS_BSPOPTS_SET([NUM_APP_DRV_GDT_DESCRIPTORS],[*],[0]) RTEMS_BSPOPTS_HELP([NUM_APP_DRV_GDT_DESCRIPTORS], [Defines how many descriptors in GDT may be allocated for application or diff --git a/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c b/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c new file mode 100644 index 0000000000..6b26d35fca --- /dev/null +++ b/c/src/lib/libbsp/i386/pc386/console/fb_vesa_rm.c @@ -0,0 +1,858 @@ +/* + * FB driver for graphic hardware compatible with VESA Bios Extension + * Real mode interface utilized + * Tested on real HW. + * + * Copyright (c) 2014 - CTU in Prague + * Jan Doležal ( dolezj21@fel.cvut.cz ) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * The code for rtems_buffer_* functions were greatly + * inspired or coppied from: + * - RTEMS fb_cirrus.c - Alexandru-Sever Horin (alex.sever.h@gmail.com) + * + * Public sources related: + * - VESA BIOS EXTENSION (VBE) Core Function Standard, Ver: 3.0, Sep 16, 1998 + * - VESA Enhanced Extended Display Identification Data (E-EDID) Standard + * Release A, Revision 2, September 25, 2006 + */ + +/* + * Hardware is completely initialized upon boot of the system. + * Therefore there is no way to change graphics mode later. + * + * Interrupt 0x10 is used for entering graphics BIOS. + * + * Driver reads parameter from multiboot command line to setup video: + * "--video=x[-]" + * If cmdline parameter is not specified an attempt for obtaining + * resolution from display attached is made. + */ + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +#define FB_VESA_NAME "FB_VESA_RM" + +void vesa_realmode_bootup_init(void); + +/* mutex for protection against multiple opens, when called frame_buffer_open */ +static pthread_mutex_t vesa_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* screen information for the VGA driver + * standard structures - from RTEMS fb interface + */ +static struct fb_var_screeninfo fb_var; +static struct fb_fix_screeninfo fb_fix; + +static uint16_t vbe_usedMode; + +inline uint32_t VBEControllerInformation( struct VBE_VbeInfoBlock *infoBlock, + uint16_t queriedVBEVersion) +{ + uint16_t size; + struct VBE_VbeInfoBlock *VBE_buffer = + (struct VBE_VbeInfoBlock *)i386_get_default_rm_buffer(&size); + i386_realmode_interrupt_registers parret; + parret.reg_eax = VBE_RetVBEConInf; + uint16_t seg, off; + i386_Physical_to_real(VBE_buffer, &seg, &off); + parret.reg_edi = (uint32_t)off; + parret.reg_es = seg; + /* indicate to graphic's bios that VBE2.0 extended information is desired */ + if (queriedVBEVersion >= 0x200) + { + strncpy( + (char *)&VBE_buffer->VbeSignature, + VBE20plus_SIGNATURE, + 4*sizeof(size_t) + ); + } + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + if ((parret.reg_eax & 0xFFFF) == + (VBE_callSuccessful<<8 | VBE_functionSupported)) + { + *infoBlock = *VBE_buffer; + } + return (parret.reg_eax & 0xFFFF); +} + +inline uint32_t VBEModeInformation( struct VBE_ModeInfoBlock *infoBlock, + uint16_t modeNumber) +{ + uint16_t size; + struct VBE_ModeInfoBlock *VBE_buffer = + (struct VBE_ModeInfoBlock *)i386_get_default_rm_buffer(&size); + i386_realmode_interrupt_registers parret; + parret.reg_eax = VBE_RetVBEModInf; + parret.reg_ecx = modeNumber; + uint16_t seg, off; + i386_Physical_to_real(VBE_buffer, &seg, &off); + parret.reg_edi = (uint32_t)off; + parret.reg_es = seg; + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + if ((parret.reg_eax & 0xFFFF) == + (VBE_callSuccessful<<8 | VBE_functionSupported)) + { + *infoBlock = *VBE_buffer; + } + return (parret.reg_eax & 0xFFFF); +} + +inline uint32_t VBESetMode( uint16_t modeNumber, + struct VBE_CRTCInfoBlock *infoBlock) +{ + uint16_t size; + struct VBE_CRTCInfoBlock *VBE_buffer = + (struct VBE_CRTCInfoBlock *)i386_get_default_rm_buffer(&size); + i386_realmode_interrupt_registers parret; + /* copy CRTC */ + *VBE_buffer = *infoBlock; + parret.reg_eax = VBE_SetVBEMod; + parret.reg_ebx = modeNumber; + uint16_t seg, off; + i386_Physical_to_real(VBE_buffer, &seg, &off); + parret.reg_edi = (uint32_t)off; + parret.reg_es = seg; + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + return (parret.reg_eax & 0xFFFF); +} + +inline uint32_t VBECurrentMode(uint16_t *modeNumber) +{ + i386_realmode_interrupt_registers parret; + parret.reg_eax = VBE_RetCurVBEMod; + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + *modeNumber = (uint16_t)parret.reg_ebx; + return (parret.reg_eax & 0xFFFF); +} + +inline uint32_t VBEReportDDCCapabilities( uint16_t controllerUnitNumber, + uint8_t *secondsToTransferEDIDBlock, + uint8_t *DDCLevelSupported) +{ + i386_realmode_interrupt_registers parret; + parret.reg_eax = VBE_DisDatCha; + parret.reg_ebx = VBEDDC_Capabilities; + parret.reg_ecx = controllerUnitNumber; + parret.reg_edi = 0; + parret.reg_es = 0; + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + *secondsToTransferEDIDBlock = (uint8_t)parret.reg_ebx >> 8; + *DDCLevelSupported = (uint8_t)parret.reg_ebx; + return (parret.reg_eax & 0xFFFF); +} + +inline uint32_t VBEReadEDID(uint16_t controllerUnitNumber, + uint16_t EDIDBlockNumber, + struct edid1 *buffer) +{ + uint16_t size; + struct edid1 *VBE_buffer = (struct edid1 *)i386_get_default_rm_buffer(&size); + i386_realmode_interrupt_registers parret; + parret.reg_eax = VBE_DisDatCha; + parret.reg_ebx = VBEDDC_ReadEDID; + parret.reg_ecx = controllerUnitNumber; + parret.reg_edx = EDIDBlockNumber; + uint16_t seg, off; + i386_Physical_to_real(VBE_buffer, &seg, &off); + parret.reg_edi = (uint32_t)off; + parret.reg_es = seg; + if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0) + return -1; + if ((parret.reg_eax & 0xFFFF) == + (VBE_callSuccessful<<8 | VBE_functionSupported)) + { + *buffer = *VBE_buffer; + } + return (parret.reg_eax & 0xFFFF); +} + +typedef struct { + uint16_t modeNumber; + uint16_t resX; + uint16_t resY; + uint8_t bpp; +} modeParams; + +/* finds mode in 'modeList' of 'listLength' length according to resolution + given in 'searchedResolution'. If bpp is given in that struct as well + mode with such color depth and resolution is searched for. Otherwise bpp + has to be zero. Mode number found is returned and also filled into + 'searchedResolution'. bpp is also filled into 'searchedResolution' if it + was 0 before call. */ +static uint16_t findModeByResolution( modeParams *modeList, + uint8_t listLength, + modeParams *searchedResolution) +{ + uint8_t i = 0; + while (i < listLength) + { + if (searchedResolution->resX == modeList[i].resX && + searchedResolution->resY == modeList[i].resY) + { + if (searchedResolution->bpp==0 || + searchedResolution->bpp==modeList[i].bpp) + { + searchedResolution->bpp = modeList[i].bpp; + searchedResolution->modeNumber = modeList[i].modeNumber; + return modeList[i].modeNumber; + } + } + i++; + } + return -1; +} + +/* + * Parse comandline option "--video=" if available. + * expected format + * --video=x[-] + * numbers , and are decadic + * + * @retval video mode number to be set + * -1 on parsing error or when no suitable mode found + */ +static uint16_t findModeUsingCmdline( modeParams *modeList, + uint8_t listLength) +{ + const char* opt; + modeParams cmdlineMode; + char* endptr; + cmdlineMode.bpp = 0; + opt = bsp_cmdline_arg("--video="); + if (opt) + { + opt += sizeof("--video=")-1; + cmdlineMode.resX = strtol(opt, &endptr, 10); + if (*endptr != 'x') + { + return -1; + } + opt = endptr+1; + cmdlineMode.resY = strtol(opt, &endptr, 10); + switch (*endptr) + { + case '-': + opt = endptr+1; + if (strlen(opt) <= 2) + cmdlineMode.bpp = strtol(opt, &endptr, 10); + else + { + cmdlineMode.bpp = strtol(opt, &endptr, 10); + if (*endptr != ' ') + { + return -1; + } + } + case ' ': + case 0: + break; + default: + return -1; + } + + if (findModeByResolution(modeList, listLength, &cmdlineMode) != + (uint16_t)-1) + return cmdlineMode.modeNumber; + } + return -1; +} + +/* + * returns mode number best fitting to monitor attached + * + * @retval video mode number to be set + * -1 on parsing error or when no suitable mode found + */ +static uint16_t findModeUsingEDID( modeParams *modeList, + uint8_t listLength) +{ + struct edid1 edid; + uint8_t checksum, iterator; + uint8_t index, j; + modeParams EDIDmode; + checksum = 0; + iterator = 0; + EDIDmode.bpp = 0; + if (VBEReadEDID(0, 0, &edid) != + (VBE_callSuccessful<<8 | VBE_functionSupported)) + { + printk(FB_VESA_NAME " Function 15h (read EDID) not supported.\n"); + return -1; + } +/* version of EDID structure */ + if (edid.Version == 1) + { /* EDID version 1 */ + while (iterator < sizeof(struct edid1)) + { + checksum += *((uint8_t *)&edid+iterator); + iterator++; + } + if (checksum) + /* not implemented: try to read EDID again */ + printk(FB_VESA_NAME " EDID v1 checksum failed\n"); + + /* try to find Detailed Timing Descriptor (defined in BASE EDID) + in controller mode list; first should be preffered mode */ + index = 0; + while (index < 4) + { + /* skip if it is monitor descriptor */ + if (edid.dtd_md[index].md.Flag0[0] == 0 && + edid.dtd_md[index].md.Flag0[1] == 0 && + edid.dtd_md[index].md.Flag1 == 0) + { + index++; + continue; + } + EDIDmode.resX = DTD_HorizontalActive(&edid.dtd_md[0].dtd); + EDIDmode.resY = DTD_VerticalActive(&edid.dtd_md[0].dtd); + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + + index++; + } + /* try to find Detailed Timing Descriptor (defined in optional EXTENSION + Blocks) in controller mode list */ + if (edid.ExtensionFlag > 0) + { + /* not implemented */ + } + /* try to find CVT (defined in BASE EDID) in controller mode list */ + index = 1; + while (index < 4) + { + if (edid.dtd_md[index].md.DataTypeTag == + EDID_DTT_CVT3ByteTimingCodes && + edid.dtd_md[index].md.Flag0[0] == 0 && + edid.dtd_md[index].md.Flag0[1] == 0 && + edid.dtd_md[index].md.Flag1 == 0 && + edid.dtd_md[index].md.Flag2 == 0) + { + struct CVTTimingCodes3B *cvt = (struct CVTTimingCodes3B *) + &edid.dtd_md[index].md.DescriptorData[0]; + j = 0; + while (j < 4) + { + EDIDmode.resY = edid1_CVT_AddressableLinesHigh(&cvt->cvt[j]); + switch (edid1_CVT_AspectRatio(&cvt->cvt[j])) + { + case EDID_CVT_AspectRatio_4_3: + EDIDmode.resX = (EDIDmode.resY*4)/3; + break; + case EDID_CVT_AspectRatio_16_9: + EDIDmode.resX = (EDIDmode.resY*16)/9; + break; + case EDID_CVT_AspectRatio_16_10: + EDIDmode.resX = (EDIDmode.resY*16)/10; + break; + case EDID_CVT_AspectRatio_15_9: + EDIDmode.resX = (EDIDmode.resY*15)/9; + break; + } + EDIDmode.resX = (EDIDmode.resX/8)*8; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + + j++; + } + } + index++; + } + /* try to find CVT (defined in optional EXTENSION Blocks) + in controller mode list */ + /* not implemented */ + /* try to find Standard Timings (listed in BASE EDID) + in controller mode list */ + index = 0; + while (index < 8) + { + /* check if descriptor is unused */ + if (*(uint16_t*)&edid.STI[index] == EDID_STI_DescriptorUnused) + { + index++; + continue; + } + EDIDmode.resX = (edid.STI[index].HorizontalActivePixels+31)*8; + switch (edid.STI[index].ImageAspectRatio_RefreshRate & EDID1_STI_ImageAspectRatioMask) + { + case EDID_STI_AspectRatio_16_10: + EDIDmode.resY = (EDIDmode.resX*10)/16; + break; + case EDID_STI_AspectRatio_4_3: + EDIDmode.resY = (EDIDmode.resX*3)/4; + break; + case EDID_STI_AspectRatio_5_4: + EDIDmode.resY = (EDIDmode.resX*4)/5; + break; + case EDID_STI_AspectRatio_16_9: + EDIDmode.resY = (EDIDmode.resX*9)/16; + break; + } + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + + index++; + } + /* try to find Standard Timings (listed in optional EXTENSION Blocks) + in controller mode list */ + /* not implemented */ + /* use Established Timings */ + if (edid1_EstablishedTim(&edid, EST_1280x1024_75Hz)) + { + EDIDmode.resX = 1280; + EDIDmode.resY = 1024; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_1152x870_75Hz)) + { + EDIDmode.resX = 1152; + EDIDmode.resY = 870; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_1024x768_75Hz) || + edid1_EstablishedTim(&edid, EST_1024x768_70Hz) || + edid1_EstablishedTim(&edid, EST_1024x768_60Hz) || + edid1_EstablishedTim(&edid, EST_1024x768_87Hz)) + { + EDIDmode.resX = 1024; + EDIDmode.resY = 768; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_832x624_75Hz)) + { + EDIDmode.resX = 832; + EDIDmode.resY = 624; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_800x600_60Hz) || + edid1_EstablishedTim(&edid, EST_800x600_56Hz) || + edid1_EstablishedTim(&edid, EST_800x600_75Hz) || + edid1_EstablishedTim(&edid, EST_800x600_72Hz)) + { + EDIDmode.resX = 800; + EDIDmode.resY = 600; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_720x400_88Hz) || + edid1_EstablishedTim(&edid, EST_720x400_70Hz)) + { + EDIDmode.resX = 720; + EDIDmode.resY = 400; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + if (edid1_EstablishedTim(&edid, EST_640x480_75Hz) || + edid1_EstablishedTim(&edid, EST_640x480_72Hz) || + edid1_EstablishedTim(&edid, EST_640x480_67Hz) || + edid1_EstablishedTim(&edid, EST_640x480_60Hz)) + { + EDIDmode.resX = 640; + EDIDmode.resY = 480; + EDIDmode.bpp = 0; + if (findModeByResolution(modeList, listLength, &EDIDmode) != + (uint16_t)-1) + return EDIDmode.modeNumber; + } + } + else + printk(FB_VESA_NAME " error reading EDID: unsupported version\n"); + return (uint16_t)-1; +} + +void vesa_realmode_bootup_init(void) +{ + uint32_t vbe_ret_val; + uint16_t size; + struct VBE_VbeInfoBlock *vib = (struct VBE_VbeInfoBlock *) + i386_get_default_rm_buffer(&size); + vbe_ret_val = VBEControllerInformation(vib, 0x300); + if (vbe_ret_val == -1) + { + printk(FB_VESA_NAME " error calling real mode interrupt.\n"); + return; + } + if (vbe_ret_val != (VBE_callSuccessful<<8 | VBE_functionSupported)) + { + printk(FB_VESA_NAME " Function 00h (read VBE info block)" + "not supported.\n"); + } +/* Helper array is later filled with mode numbers and their parameters + sorted from the biggest values to the smalest where priorities of + parameters are from the highest to the lowest: resolution X, + resolution Y, bits per pixel. + The array is used for search the monitor provided parameters in EDID + structure and if found we set such mode using corresponding + VESA function. */ +#define MAX_NO_OF_SORTED_MODES 100 + modeParams sortModeParams[MAX_NO_OF_SORTED_MODES]; + + uint16_t *vmpSegOff = (uint16_t *)&vib->VideoModePtr; + uint16_t *modeNOPtr = (uint16_t*) + i386_Real_to_physical(*(vmpSegOff+1), *vmpSegOff); + uint16_t iterator = 0; + if (*(uint16_t*)vib->VideoModePtr == VBE_STUB_VideoModeList) + { + printk(FB_VESA_NAME " VBE Core not implemented!\n"); + } + else + { + /* prepare list of modes */ + while (*(modeNOPtr+iterator) != VBE_END_OF_VideoModeList && + *(modeNOPtr+iterator) != 0) + { /* some bios implementations ends the list incorrectly with 0 */ + if (iterator < MAX_NO_OF_SORTED_MODES) + { + sortModeParams[iterator].modeNumber = *(modeNOPtr+iterator); + iterator ++; + } + else + break; + } + if (iterator < MAX_NO_OF_SORTED_MODES) + sortModeParams[iterator].modeNumber = 0; + } + + struct VBE_ModeInfoBlock *mib = (struct VBE_ModeInfoBlock *) + i386_get_default_rm_buffer(&size); + iterator = 0; + uint8_t nextFilteredMode = 0; + uint16_t required_mode_attributes = VBE_modSupInHWMask | + VBE_ColorModeMask | VBE_GraphicsModeMask | VBE_LinFraBufModeAvaiMask; + /* get parameters of modes and filter modes according to set + required parameters */ + while (iterator < MAX_NO_OF_SORTED_MODES && + sortModeParams[iterator].modeNumber!=0) + { + VBEModeInformation(mib, sortModeParams[iterator].modeNumber); + if ((mib->ModeAttributes&required_mode_attributes) == + required_mode_attributes) + { + sortModeParams[nextFilteredMode].modeNumber = + sortModeParams[iterator].modeNumber; + sortModeParams[nextFilteredMode].resX = mib->XResolution; + sortModeParams[nextFilteredMode].resY = mib->YResolution; + sortModeParams[nextFilteredMode].bpp = mib->BitsPerPixel; + nextFilteredMode ++; + } + iterator ++; + } + sortModeParams[nextFilteredMode].modeNumber = 0; + + uint8_t numberOfModes = nextFilteredMode; + /* sort filtered modes */ + modeParams modeXchgPlace; + iterator = 0; + uint8_t j; + uint8_t idxBestMode; + while (iterator < numberOfModes) + { + idxBestMode = iterator; + j = iterator+1; + while (j < numberOfModes) + { + if (sortModeParams[j].resX > sortModeParams[idxBestMode].resX) + idxBestMode = j; + else if (sortModeParams[j].resX == sortModeParams[idxBestMode].resX) + { + if (sortModeParams[j].resY > sortModeParams[idxBestMode].resY) + idxBestMode = j; + else if (sortModeParams[j].resY == + sortModeParams[idxBestMode].resY) + { + if (sortModeParams[j].bpp > sortModeParams[idxBestMode].bpp) + idxBestMode = j; + } + } + j++; + } + if (idxBestMode != iterator) + { + modeXchgPlace = sortModeParams[iterator]; + sortModeParams[iterator] = sortModeParams[idxBestMode]; + sortModeParams[idxBestMode] = modeXchgPlace; + } + iterator++; + } + + /* first search for video argument in multiboot options */ + vbe_usedMode = findModeUsingCmdline(sortModeParams, numberOfModes); + if (vbe_usedMode == (uint16_t)-1) + { + printk(FB_VESA_NAME " video on command line not provided" + "\n\ttrying EDID ...\n"); + /* second search monitor for good resolution */ + vbe_usedMode = findModeUsingEDID(sortModeParams, numberOfModes); + if (vbe_usedMode == (uint16_t)-1) + { + printk(FB_VESA_NAME" monitor's EDID video parameters not supported" + "\n\tusing mode with highest resolution, bpp\n"); + /* third set highest values */ + vbe_usedMode = sortModeParams[0].modeNumber; + } + } + + /* fill framebuffer structs with info about selected mode */ + vbe_ret_val = VBEModeInformation(mib, vbe_usedMode); + if ((vbe_ret_val&0xff)!=VBE_functionSupported || + (vbe_ret_val>>8)!=VBE_callSuccessful) + { + printk(FB_VESA_NAME " Cannot get mode info anymore. ax=0x%x\n", + vbe_ret_val); + } + + fb_var.xres = mib->XResolution; + fb_var.yres = mib->YResolution; + fb_var.bits_per_pixel = mib->BitsPerPixel; + fb_var.red.offset = mib->LinRedFieldPosition; + fb_var.red.length = mib->LinRedMaskSize; + fb_var.red.msb_right = 0; + fb_var.green.offset = mib->LinGreenFieldPosition; + fb_var.green.length = mib->LinGreenMaskSize; + fb_var.green.msb_right = 0; + fb_var.blue.offset = mib->LinBlueFieldPosition; + fb_var.blue.length = mib->LinBlueMaskSize; + fb_var.blue.msb_right = 0; + fb_var.transp.offset = mib->LinRsvdFieldPosition; + fb_var.transp.length = mib->LinRsvdMaskSize; + fb_var.transp.msb_right =0; + + fb_fix.smem_start = (char *)mib->PhysBasePtr; + fb_fix.line_length = mib->LinBytesPerScanLine; + fb_fix.smem_len = fb_fix.line_length*fb_var.yres; + fb_fix.type = FB_TYPE_PACKED_PIXELS; + if (fb_var.bits_per_pixel < 24) + fb_fix.visual = FB_VISUAL_DIRECTCOLOR; + else + fb_fix.visual = FB_VISUAL_TRUECOLOR; + + /* set selected mode */ + vbe_ret_val = VBESetMode(vbe_usedMode | VBE_linearFlatFrameBufMask, + (struct VBE_CRTCInfoBlock *)(i386_get_default_rm_buffer(&size))); + if (vbe_ret_val>>8 == VBE_callFailed) + printk(FB_VESA_NAME " VBE: Requested mode is not available."); + + if ((vbe_ret_val&0xff)!= (VBE_functionSupported | VBE_callSuccessful<<8)) + printk(FB_VESA_NAME " Call to function 2h (set VBE mode) failed. " + "ax=0x%x\n", vbe_ret_val); + + vib = (void *) 0; + mib = (void *) 0; +} + +/* + * fb_vesa device driver INITIALIZE entry point. + */ +rtems_device_driver +frame_buffer_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + printk(FB_VESA_NAME " frame buffer -- driver initializing..\n" ); + +/* + * Register the device. + */ + status = rtems_io_register_name(FRAMEBUFFER_DEVICE_0_NAME, major, 0); + if (status != RTEMS_SUCCESSFUL) + { + printk("Error registering " FRAMEBUFFER_DEVICE_0_NAME + " - " FB_VESA_NAME " frame buffer device!\n"); + rtems_fatal_error_occurred( status ); + } + + return RTEMS_SUCCESSFUL; +} + +/* + * fb_vesa device driver OPEN entry point + */ +rtems_device_driver +frame_buffer_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + printk( FB_VESA_NAME " open device\n" ); + + if (pthread_mutex_trylock(&vesa_mutex) != 0) + { + printk( FB_VESA_NAME " could not lock vesa_mutex\n" ); + + return RTEMS_UNSATISFIED; + } + + return RTEMS_SUCCESSFUL; + +} + +/* + * fb_vesa device driver CLOSE entry point + */ +rtems_device_driver +frame_buffer_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + printk( FB_VESA_NAME " close device\n" ); + if (pthread_mutex_unlock(&vesa_mutex) == 0) + { + /* restore previous state. for VGA this means return to text mode. + * leave out if graphics hardware has been initialized in + * frame_buffer_initialize() */ + + printk(FB_VESA_NAME ": close called.\n" ); + return RTEMS_SUCCESSFUL; + } + + return RTEMS_UNSATISFIED; +} + +/* + * fb_vesa device driver READ entry point. + */ +rtems_device_driver +frame_buffer_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + printk( FB_VESA_NAME " read device\n" ); + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rw_args->bytes_moved = + ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ? + (fb_fix.smem_len - rw_args->offset) : + rw_args->count; + memcpy(rw_args->buffer, (const void *) + (fb_fix.smem_start + rw_args->offset), rw_args->bytes_moved); + return RTEMS_SUCCESSFUL; +} + +/* + * frame_vesa device driver WRITE entry point. + */ +rtems_device_driver +frame_buffer_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + printk( FB_VESA_NAME " write device\n" ); + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rw_args->bytes_moved = + ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ? + (fb_fix.smem_len - rw_args->offset) : + rw_args->count; + memcpy( (void *) (fb_fix.smem_start + rw_args->offset), + rw_args->buffer, rw_args->bytes_moved); + return RTEMS_SUCCESSFUL; +} + +static int get_fix_screen_info( struct fb_fix_screeninfo *info ) +{ + printk("get_fix_screen_info\n"); + *info = fb_fix; + return 0; +} + +static int get_var_screen_info( struct fb_var_screeninfo *info ) +{ + printk("get_var_screen_info\n"); + *info = fb_var; + return 0; +} + +/* + * IOCTL entry point -- This method is called to carry + * all services of this interface. + */ +rtems_device_driver +frame_buffer_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + + printk( FB_VESA_NAME " ioctl called, cmd=%x\n", args->command ); + printk("fbxres %d, fbyres %d\n", fb_var.xres, fb_var.yres); + printk("fbbpp %d\n", fb_var.bits_per_pixel); + + switch (args->command) + { + case FBIOGET_FSCREENINFO: + args->ioctl_return = + get_fix_screen_info( ( struct fb_fix_screeninfo * ) args->buffer ); + break; + case FBIOGET_VSCREENINFO: + args->ioctl_return = + get_var_screen_info( ( struct fb_var_screeninfo * ) args->buffer ); + break; + case FBIOPUT_VSCREENINFO: + /* not implemented yet */ + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + case FBIOGETCMAP: + /* no palette - truecolor mode */ + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + case FBIOPUTCMAP: + /* no palette - truecolor mode */ + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + default: + args->ioctl_return = 0; + break; + } + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h b/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h new file mode 100644 index 0000000000..5638b50ca2 --- /dev/null +++ b/c/src/lib/libbsp/i386/pc386/include/fb_vesa.h @@ -0,0 +1,131 @@ +/** + * @file fb_vesa.h + * + * @ingroup i386_pc386 + * + * @brief Definitioins for vesa based framebuffer drivers. + */ + +/* + * Headers specific for framebuffer drivers utilizing VESA VBE. + * + * Copyright (C) 2014 Jan Doležal (dolezj21@fel.cvut.cz) + * CTU in Prague. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include + +#ifndef _FB_VESA_H +#define _FB_VESA_H + +#ifndef ASM /* ASM */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* ----- Prototypes ----- */ + +/** + * Returns information about graphic's controller in the infoBlock structure. + * + * @param infoBlock pointer to the struct to be filled with + controller information + * @param queriedVBEVersion if >0x200 then video bios is asked to fill in + * parameters which appeared with second version + * of VBE. + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBEControllerInformation ( + struct VBE_VbeInfoBlock *infoBlock, + uint16_t queriedVBEVersion +); + +/** + * Fills structure infoBlock with informations about selected mode in + * modeNumber variable. + * + * @param infoBlock pointer to the struct to be filled with mode information + * @param modeNumber detailes of this mode to be filled + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBEModeInformation ( + struct VBE_ModeInfoBlock *infoBlock, + uint16_t modeNumber +); + +/** + * Sets graphics mode selected. If mode has refreshRateCtrl bit set, than the + * infoBlock must be filled accordingly. + * + * @param modeNumber number of mode to be set + * @param infoBlock pointer to struct containing refresh rate control info + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBESetMode ( + uint16_t modeNumber, + struct VBE_CRTCInfoBlock *infoBlock +); + +/** + * Get currently set mode number. + * + * @param modeNumber variable to be filled with current mode number + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBECurrentMode ( + uint16_t *modeNumber +); + +/** + * Gets information about display data channel implemented in the + * graphic's controller. + * + * @param controllerUnitNumber + * @param secondsToTransferEDIDBlock approximate time to transfer one EDID block + * rounded up to seconds + * @param DDCLevelSupported after call contains DDC version supported and + * screen blanking state during transfer + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBEReportDDCCapabilities ( + uint16_t controllerUnitNumber, + uint8_t *secondsToTransferEDIDBlock, + uint8_t *DDCLevelSupported +); + +/** + * Reads selected EDID block from display attached to controller's interface. + * + * @param controllerUnitNumber + * @param EDIDBlockNumber block no. to be read from the display + * @param buffer place to store block fetched from the display + * @retval register ax content as defined in VBE RETURN STATUS paragraph + * -1 error calling graphical bios + */ +uint32_t VBEReadEDID ( + uint16_t controllerUnitNumber, + uint16_t EDIDBlockNumber, + struct edid1 *buffer +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ASM */ + +#endif /* _FB_VESA_H */ diff --git a/c/src/lib/libbsp/i386/pc386/preinstall.am b/c/src/lib/libbsp/i386/pc386/preinstall.am index b4ac86bc25..33a230cc5f 100644 --- a/c/src/lib/libbsp/i386/pc386/preinstall.am +++ b/c/src/lib/libbsp/i386/pc386/preinstall.am @@ -147,6 +147,7 @@ $(PROJECT_INCLUDE)/i386_io.h: ../../i386/shared/comm/i386_io.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i386_io.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/i386_io.h +if USE_VBE_RM $(PROJECT_INCLUDE)/bsp/vbe3.h: include/vbe3.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vbe3.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vbe3.h @@ -155,6 +156,11 @@ $(PROJECT_INCLUDE)/edid.h: include/edid.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/edid.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/edid.h +$(PROJECT_INCLUDE)/bsp/fb_vesa.h: include/fb_vesa.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/fb_vesa.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/fb_vesa.h +endif + $(PROJECT_INCLUDE)/pcibios.h: ../../i386/shared/pci/pcibios.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/pcibios.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/pcibios.h diff --git a/c/src/lib/libbsp/i386/pc386/start/start.S b/c/src/lib/libbsp/i386/pc386/start/start.S index 1cba12451d..ab92a4f1de 100644 --- a/c/src/lib/libbsp/i386/pc386/start/start.S +++ b/c/src/lib/libbsp/i386/pc386/start/start.S @@ -41,6 +41,7 @@ #include #include +#include /*----------------------------------------------------------------------------+ | Size of heap and stack: @@ -61,6 +62,9 @@ BEGIN_CODE PUBLIC (start) # GNU default entry point EXTERN (boot_card) +#ifdef USE_VBE_RM + EXTERN (vesa_realmode_bootup_init) +#endif EXTERN (_load_segments) EXTERN (_return_to_monitor) EXTERN (_IBMPC_initVideo) @@ -201,6 +205,10 @@ SYM (zero_bss): +-------------------------------------------------------------------*/ call _IBMPC_initVideo +#ifdef USE_VBE_RM + call vesa_realmode_bootup_init +#endif + /*---------------------------------------------------------------------+ | Check CPU type. Enable Cache and init coprocessor if needed. +---------------------------------------------------------------------*/ -- cgit v1.2.3