Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:40

0001 /**
0002  * @file
0003  *
0004  * @ingroup RTEMSBSPsI386
0005  *
0006  * @brief FB driver for graphic hardware compatible with VESA Bios Extension
0007  *     Real mode interface utilized
0008  *     Tested on real HW.
0009  *
0010  * Public sources related:
0011  *   - VESA BIOS EXTENSION (VBE) Core Function Standard, Ver: 3.0, Sep 16, 1998
0012  *   - VESA Enhanced Extended Display Identification Data (E-EDID) Standard
0013  *     Release A, Revision 2, September 25, 2006
0014  *
0015  * Hardware is completely initialized upon boot of the system.
0016  * Therefore there is no way to change graphics mode later.
0017  *
0018  * Interrupt 0x10 is used for entering graphics BIOS.
0019  *
0020  * Driver reads parameter from multiboot command line to setup video:
0021  * "--video=<resX>x<resY>[-<bpp>]"
0022  * "--video=auto" - try EDID to find mode that fits the display attached best
0023  * "--video=none" / "--video=off" - do not initialize the driver
0024  * If cmdline parameter is not specified the rtems_fb_default_mode
0025  * variable content is tested (see doc below).
0026  * Command line option has higher priority. rtems_fb_default_mode is probed
0027  * only if cmdline "--video=" is not specified at all.
0028  *
0029  * If neither of the above options is specified the driver is not initialized.
0030  */
0031 
0032 /*
0033  * Copyright (c) 2014 - CTU in Prague
0034  *                      Jan Doležal ( dolezj21@fel.cvut.cz )
0035  *
0036  * The license and distribution terms for this file may be
0037  * found in the file LICENSE in this distribution or at
0038  * http://www.rtems.org/license/LICENSE.
0039  *
0040  * The code for rtems_buffer_* functions was greatly
0041  * inspired or coppied from:
0042  *   - RTEMS fb_cirrus.c - Alexandru-Sever Horin (alex.sever.h@gmail.com)
0043  */
0044 
0045 #include <inttypes.h>
0046 
0047 #include <bsp.h>
0048 
0049 #include <bsp/fb_default_mode.h>
0050 #include <bsp/fb_vesa.h>
0051 #include <bsp/realmode_int.h>
0052 
0053 #include <pthread.h>
0054 
0055 #include <rtems/libio.h>
0056 
0057 #include <rtems/fb.h>
0058 #include <rtems/framebuffer.h>
0059 
0060 #include <rtems/score/atomic.h>
0061 
0062 #include <stdlib.h>
0063 
0064 #define FB_VESA_NAME    "FB_VESA_RM"
0065 
0066 /*
0067  * GCC complains that access to packed data may not be aligned and
0068  * fair enough. The warning is:
0069  *
0070  *   warning: taking address of packed member of 'struct <anonymous>' may
0071  *   result in an unaligned pointer value [-Waddress-of-packed-member]
0072  *
0073  * Disable the warning.
0074  */
0075 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
0076 
0077 /**
0078  * @brief Allows to enable initialization of VESA real mode driver from
0079  * an application by setting the value of this variable to non null value in
0080  * user's module. The value of this variable will be then updated
0081  * when linked with application's object.
0082  *
0083  * Further the value should point to string in the following format:
0084  * "<resX>x<resY>[-<bpp>]" - e.g. "1024x768-32"
0085  * "auto" - try EDID to find mode that fits the display attached best
0086  * "none" / "off" - do not initialize the driver
0087  * the given parameters are used if applicable.
0088  *
0089  * Command line argument "--video=" has priority over this string.
0090  */
0091 const char * const rtems_fb_default_mode;
0092 
0093 /**
0094  * @brief Initializes VBE framebuffer during bootup.
0095  *
0096  * utilizes switches to real mode interrupts and therefore must be
0097  * called during bootup before tick is set up and real-time
0098  * interrupt vectors utilized
0099  */
0100 void vesa_realmode_bootup_init(void);
0101 
0102 /* flag to limit driver to protect against multiple opens */
0103 static Atomic_Flag driver_mutex;
0104 
0105 /* screen information for the VGA driver
0106  * standard structures - from RTEMS fb interface
0107  */
0108 static struct fb_var_screeninfo fb_var;
0109 static struct fb_fix_screeninfo fb_fix;
0110 
0111 static int32_t vbe_used_mode;
0112 
0113 uint32_t VBE_controller_information( VBE_vbe_info_block *info_block,
0114                                             uint16_t queried_VBE_Version)
0115 {
0116     uint16_t size;
0117     VBE_vbe_info_block *VBE_buffer =
0118         (VBE_vbe_info_block *)i386_get_default_rm_buffer(&size);
0119     i386_realmode_interrupt_registers parret;
0120     parret.reg_eax = VBE_RetVBEConInf;
0121     uint16_t seg, off;
0122     i386_Physical_to_real(VBE_buffer, &seg, &off);
0123     parret.reg_edi = (uint32_t)off;
0124     parret.reg_es = seg;
0125     /* indicate to graphic's bios that VBE2.0 extended information is desired */
0126     if (queried_VBE_Version >= 0x200)
0127     {
0128         memcpy(
0129             &VBE_buffer->VbeSignature,
0130             VBE20plus_SIGNATURE,
0131             sizeof(VBE_buffer->VbeSignature)
0132         );
0133     }
0134     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0135         return -1;
0136     if ((parret.reg_eax & 0xFFFF) ==
0137         (VBE_callSuccessful<<8 | VBE_functionSupported))
0138     {
0139         *info_block = *VBE_buffer;
0140     }
0141     return (parret.reg_eax & 0xFFFF);
0142 }
0143 
0144 uint32_t VBE_mode_information( VBE_mode_info_block *info_block,
0145                                     uint16_t mode_number)
0146 {
0147     uint16_t size;
0148     VBE_mode_info_block *VBE_buffer =
0149         (VBE_mode_info_block *)i386_get_default_rm_buffer(&size);
0150     i386_realmode_interrupt_registers parret;
0151     parret.reg_eax = VBE_RetVBEModInf;
0152     parret.reg_ecx = mode_number;
0153     uint16_t seg, off;
0154     i386_Physical_to_real(VBE_buffer, &seg, &off);
0155     parret.reg_edi = (uint32_t)off;
0156     parret.reg_es = seg;
0157     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0158         return -1;
0159     if ((parret.reg_eax & 0xFFFF) ==
0160         (VBE_callSuccessful<<8 | VBE_functionSupported))
0161     {
0162         *info_block = *VBE_buffer;
0163     }
0164     return (parret.reg_eax & 0xFFFF);
0165 }
0166 
0167 uint32_t VBE_set_mode( uint16_t mode_number,
0168                             VBE_CRTC_info_block *info_block)
0169 {
0170     uint16_t size;
0171     VBE_CRTC_info_block *VBE_buffer =
0172         (VBE_CRTC_info_block *)i386_get_default_rm_buffer(&size);
0173     i386_realmode_interrupt_registers parret;
0174     /* copy CRTC */
0175     *VBE_buffer = *info_block;
0176     parret.reg_eax = VBE_SetVBEMod;
0177     parret.reg_ebx = mode_number;
0178     uint16_t seg, off;
0179     i386_Physical_to_real(VBE_buffer, &seg, &off);
0180     parret.reg_edi = (uint32_t)off;
0181     parret.reg_es = seg;
0182     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0183         return -1;
0184     return (parret.reg_eax & 0xFFFF);
0185 }
0186 
0187 uint32_t VBE_current_mode(uint16_t *mode_number)
0188 {
0189     i386_realmode_interrupt_registers parret;
0190     parret.reg_eax = VBE_RetCurVBEMod;
0191     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0192         return -1;
0193     *mode_number = (uint16_t)parret.reg_ebx;
0194     return (parret.reg_eax & 0xFFFF);
0195 }
0196 
0197 uint32_t VBE_report_DDC_capabilities(uint16_t controller_unit_number,
0198                                         uint8_t *seconds_to_transfer_EDID_block,
0199                                         uint8_t *DDC_level_supported)
0200 {
0201     i386_realmode_interrupt_registers parret;
0202     parret.reg_eax = VBE_DisDatCha;
0203     parret.reg_ebx = VBEDDC_Capabilities;
0204     parret.reg_ecx = controller_unit_number;
0205     parret.reg_edi = 0;
0206     parret.reg_es = 0;
0207     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0208         return -1;
0209     *seconds_to_transfer_EDID_block = (uint8_t)parret.reg_ebx >> 8;
0210     *DDC_level_supported = (uint8_t)parret.reg_ebx;
0211     return (parret.reg_eax & 0xFFFF);
0212 }
0213 
0214 uint32_t VBE_read_EDID(uint16_t controller_unit_number,
0215                             uint16_t EDID_block_number,
0216                             EDID_edid1 *buffer)
0217 {
0218     uint16_t size;
0219     EDID_edid1 *VBE_buffer = (EDID_edid1*)i386_get_default_rm_buffer(&size);
0220     i386_realmode_interrupt_registers parret;
0221     parret.reg_eax = VBE_DisDatCha;
0222     parret.reg_ebx = VBEDDC_ReadEDID;
0223     parret.reg_ecx = controller_unit_number;
0224     parret.reg_edx = EDID_block_number;
0225     uint16_t seg, off;
0226     i386_Physical_to_real(VBE_buffer, &seg, &off);
0227     parret.reg_edi = (uint32_t)off;
0228     parret.reg_es = seg;
0229     if (i386_real_interrupt_call(INTERRUPT_NO_VIDEO_SERVICES, &parret) == 0)
0230         return -1;
0231     if ((parret.reg_eax & 0xFFFF) ==
0232         (VBE_callSuccessful<<8 | VBE_functionSupported))
0233     {
0234         *buffer = *VBE_buffer;
0235     }
0236     return (parret.reg_eax & 0xFFFF);
0237 }
0238 
0239 /**
0240  * @brief Basic graphic's mode parameters
0241  */
0242 typedef struct {
0243     /** number of the graphic's mode */
0244     uint16_t mode_number;
0245     /** number of pixels in one line */
0246     uint16_t resX;
0247     /** number of lines */
0248     uint16_t resY;
0249     /** bits per pixel */
0250     uint8_t bpp;
0251 } Mode_params;
0252 
0253 typedef enum {
0254     NO_SUITABLE_MODE    = -1,
0255     BAD_FORMAT          = -2,
0256     AUTO_SELECT         = -3,
0257     DONT_INIT           = -4,
0258     NO_MODE_REQ         = -5,
0259 } mode_err_ret_val;
0260 
0261 /**
0262  * @brief Find mode by resolution in the given list of modes
0263  *
0264  * finds mode in \p mode_list of \p list_length length according to resolution
0265  * given in \p searched_resolution . If bpp is given in that struct as well
0266  * mode with such color depth and resolution is searched for. Otherwise bpp
0267  * has to be zero. Mode number found is returned and also filled into
0268  * \p searched_resolution . bpp is also filled into \p searchedResolution if it
0269  * was 0 before call.
0270  *
0271  * @param[in] mode_list list of modes to be searched
0272  * @param[in] list_length number of modes in the list
0273  * @param[in,out] searched_resolution element filled with searched resolution
0274  *                or/and bpp; mode_number is filled in if appropriate mode found
0275  * @retval mode number satisfying given parameters
0276  * @retval -1 no suitable mode found
0277  */
0278 static int32_t find_mode_by_resolution(Mode_params *mode_list,
0279                                         uint8_t list_length,
0280                                         Mode_params *searched_resolution)
0281 {
0282     uint8_t i = 0;
0283     while (i < list_length)
0284     {
0285         if (searched_resolution->resX == mode_list[i].resX &&
0286             searched_resolution->resY == mode_list[i].resY)
0287         {
0288             if (searched_resolution->bpp==0 ||
0289                 searched_resolution->bpp==mode_list[i].bpp)
0290             {
0291                 searched_resolution->bpp = mode_list[i].bpp;
0292                 searched_resolution->mode_number = mode_list[i].mode_number;
0293                 return mode_list[i].mode_number;
0294             }
0295         }
0296         i++;
0297     }
0298     return NO_SUITABLE_MODE;
0299 }
0300 
0301 /**
0302  * @brief Find mode given in string format.
0303  *
0304  *  expected format
0305  *  <resX>x<resY>[-<bpp>]
0306  *  numbers <resX>, <resY> and <bpp> are decadic
0307  *
0308  * @param[in] mode_list list of modes to be searched
0309  * @param[in] list_length number of modes in the list
0310  * @param[in] video_string string to be parsed
0311  * @retval video mode number to be set
0312  * @retval -1 no suitable mode found
0313  * @retval -2 bad format of the video_string
0314  * @retval -3 automatic mode selection requested
0315  * @retval -4 request to not initialize graphics
0316  * @retval -5 no mode requested/empty video string
0317  */
0318 static int32_t find_mode_from_string(Mode_params *mode_list,
0319                                       uint8_t list_length,
0320                                       const char *video_string)
0321 {
0322     const char* opt;
0323     Mode_params cmdline_mode;
0324     char* endptr;
0325     cmdline_mode.bpp = 16; /* default bpp */
0326     opt = video_string;
0327     if (opt)
0328     {
0329         if (strncmp(opt, "auto", 4) == 0)
0330             return AUTO_SELECT;
0331         if (strncmp(opt, "none", 4) == 0 ||
0332             strncmp(opt, "off", 3) == 0)
0333         {
0334             return DONT_INIT;
0335         }
0336         cmdline_mode.resX = strtol(opt, &endptr, 10);
0337         if (*endptr != 'x')
0338         {
0339             return BAD_FORMAT;
0340         }
0341         opt = endptr+1;
0342         cmdline_mode.resY = strtol(opt, &endptr, 10);
0343         switch (*endptr)
0344         {
0345             case '-':
0346                 opt = endptr+1;
0347                 if (strlen(opt) <= 2)
0348                     cmdline_mode.bpp = strtol(opt, &endptr, 10);
0349                 else
0350                 {
0351                     cmdline_mode.bpp = strtol(opt, &endptr, 10);
0352                     if (*endptr != ' ')
0353                     {
0354                         return BAD_FORMAT;
0355                     }
0356                 }
0357             case ' ':
0358             case 0:
0359                 break;
0360             default:
0361                 return BAD_FORMAT;
0362         }
0363 
0364         return find_mode_by_resolution(mode_list, list_length, &cmdline_mode);
0365     }
0366     return NO_MODE_REQ;
0367 }
0368 
0369 /**
0370  * @brief Find mode given within command line.
0371  *
0372  * Parse command line option "--video=" if available.
0373  *  expected format
0374  *  --video=<resX>x<resY>[-<bpp>]
0375  *  numbers <resX>, <resY> and <bpp> are decadic
0376  *
0377  * @param[in] mode_list list of modes to be searched
0378  * @param[in] list_length number of modes in the list
0379  * @retval video mode number to be set
0380  * @retval -1 no suitable mode found
0381  * @retval -2 bad format of the video_string
0382  * @retval -3 automatic mode selection requested
0383  * @retval -4 request to not initialize graphics
0384  * @retval -5 no mode requested/empty video string
0385  */
0386 static int32_t find_mode_using_cmdline(Mode_params *mode_list,
0387                                         uint8_t list_length)
0388 {
0389     const char* opt;
0390     opt = bsp_cmdline_arg("--video=");
0391     if (opt)
0392     {
0393         opt += sizeof("--video=")-1;
0394         return find_mode_from_string(mode_list, list_length, opt);
0395     }
0396     return NO_MODE_REQ;
0397 }
0398 
0399 /**
0400  * @brief Find mode number best fitting to monitor attached
0401  *
0402  * @param[in] mode_list list of modes to be searched
0403  * @param[in] list_length number of modes in the list
0404  * @retval video mode number to be set
0405  * @retval -1 on parsing error or when no suitable mode found
0406  */
0407 static int32_t find_mode_using_EDID( Mode_params *mode_list,
0408                                       uint8_t list_length)
0409 {
0410     EDID_edid1 edid;
0411     uint8_t checksum, iterator;
0412     uint8_t index, j;
0413     Mode_params EDIDmode;
0414     checksum = 0;
0415     iterator = 0;
0416     EDIDmode.bpp = 0;
0417     if (VBE_read_EDID(0, 0, &edid) !=
0418         (VBE_callSuccessful<<8 | VBE_functionSupported))
0419     {
0420         printk(FB_VESA_NAME " Function 15h (read EDID) not supported.\n");
0421         return -1;
0422     }
0423 /* version of EDID structure */
0424     if (edid.Version == 1)
0425     { /* EDID version 1 */
0426         while (iterator < sizeof(EDID_edid1))
0427         {
0428             checksum += *((uint8_t *)&edid+iterator);
0429             iterator++;
0430         }
0431         if (checksum)
0432             /* not implemented: try to read EDID again */
0433             printk(FB_VESA_NAME " EDID v1 checksum failed\n");
0434 
0435         /* try to find Detailed Timing Descriptor (defined in BASE EDID)
0436            in controller mode list; first should be preffered mode */
0437         index = 0;
0438         while (index < 4)
0439         {
0440             /* skip if it is monitor descriptor */
0441             if (edid.dtd_md[index].md.Flag0[0] == 0 &&
0442                 edid.dtd_md[index].md.Flag0[1] == 0 &&
0443                 edid.dtd_md[index].md.Flag1 == 0)
0444             {
0445                 index++;
0446                 continue;
0447             }
0448             EDIDmode.resX = DTD_horizontal_active(&edid.dtd_md[0].dtd);
0449             EDIDmode.resY = DTD_vertical_active(&edid.dtd_md[0].dtd);
0450             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0451                 -1)
0452                 return EDIDmode.mode_number;
0453 
0454             index++;
0455         }
0456         /* try to find Detailed Timing Descriptor (defined in optional EXTENSION
0457         Blocks) in controller mode list */
0458         if (edid.ExtensionFlag > 0)
0459         {
0460             /* not implemented */
0461         }
0462         /* try to find CVT (defined in BASE EDID) in controller mode list */
0463         index = 1;
0464         while (index < 4)
0465         {
0466             if (edid.dtd_md[index].md.DataTypeTag ==
0467                     EDID_DTT_CVT3ByteTimingCodes     &&
0468                 edid.dtd_md[index].md.Flag0[0] == 0  &&
0469                 edid.dtd_md[index].md.Flag0[1] == 0  &&
0470                 edid.dtd_md[index].md.Flag1 == 0     &&
0471                 edid.dtd_md[index].md.Flag2 == 0)
0472             {
0473                 EDID_CVT_timing_codes_3B *cvt = (EDID_CVT_timing_codes_3B *)
0474                     &edid.dtd_md[index].md.DescriptorData[0];
0475                 j = 0;
0476                 while (j < 4)
0477                 {
0478                     EDIDmode.resY = edid1_CVT_addressable_lines_high(
0479                         &cvt->cvt[j]
0480                     );
0481                     switch (edid1_CVT_aspect_ratio(&cvt->cvt[j]))
0482                     {
0483                         case EDID_CVT_AspectRatio_4_3:
0484                             EDIDmode.resX = (EDIDmode.resY*4)/3;
0485                             break;
0486                         case EDID_CVT_AspectRatio_16_9:
0487                             EDIDmode.resX = (EDIDmode.resY*16)/9;
0488                             break;
0489                         case EDID_CVT_AspectRatio_16_10:
0490                             EDIDmode.resX = (EDIDmode.resY*16)/10;
0491                             break;
0492                         case EDID_CVT_AspectRatio_15_9:
0493                             EDIDmode.resX = (EDIDmode.resY*15)/9;
0494                             break;
0495                     }
0496                     EDIDmode.resX = (EDIDmode.resX/8)*8;
0497                     if (find_mode_by_resolution(
0498                             mode_list, list_length, &EDIDmode) != -1)
0499                         return EDIDmode.mode_number;
0500 
0501                     j++;
0502                 }
0503             }
0504             index++;
0505         }
0506         /* try to find CVT (defined in optional EXTENSION Blocks)
0507         in controller mode list */
0508         /* not implemented */
0509         /* try to find Standard Timings (listed in BASE EDID)
0510         in controller mode list */
0511         index = 0;
0512         while (index < 8)
0513         {
0514             /* check if descriptor is unused */
0515             if (edid1_STI_is_unused(&edid.STI[index]))
0516             {
0517                 index++;
0518                 continue;
0519             }
0520             EDIDmode.resX = (edid.STI[index].HorizontalActivePixels+31)*8;
0521             switch (edid.STI[index].ImageAspectRatio_RefreshRate &
0522                     EDID1_STI_ImageAspectRatioMask)
0523             {
0524                 case EDID_STI_AspectRatio_16_10:
0525                     EDIDmode.resY = (EDIDmode.resX*10)/16;
0526                     break;
0527                 case EDID_STI_AspectRatio_4_3:
0528                     EDIDmode.resY = (EDIDmode.resX*3)/4;
0529                     break;
0530                 case EDID_STI_AspectRatio_5_4:
0531                     EDIDmode.resY = (EDIDmode.resX*4)/5;
0532                     break;
0533                 case EDID_STI_AspectRatio_16_9:
0534                     EDIDmode.resY = (EDIDmode.resX*9)/16;
0535                     break;
0536             }
0537             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0538                 -1)
0539                 return EDIDmode.mode_number;
0540 
0541             index++;
0542         }
0543         /* try to find Standard Timings (listed in optional EXTENSION Blocks)
0544         in controller mode list */
0545         /* not implemented */
0546         /* use Established Timings */
0547         if (edid1_established_tim(&edid, EST_1280x1024_75Hz))
0548         {
0549             EDIDmode.resX = 1280;
0550             EDIDmode.resY = 1024;
0551             EDIDmode.bpp = 0;
0552             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0553                 -1)
0554                 return EDIDmode.mode_number;
0555         }
0556         if (edid1_established_tim(&edid, EST_1152x870_75Hz))
0557         {
0558             EDIDmode.resX = 1152;
0559             EDIDmode.resY = 870;
0560             EDIDmode.bpp = 0;
0561             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0562                 -1)
0563                 return EDIDmode.mode_number;
0564         }
0565         if (edid1_established_tim(&edid, EST_1024x768_75Hz) ||
0566             edid1_established_tim(&edid, EST_1024x768_70Hz) ||
0567             edid1_established_tim(&edid, EST_1024x768_60Hz) ||
0568             edid1_established_tim(&edid, EST_1024x768_87Hz))
0569         {
0570             EDIDmode.resX = 1024;
0571             EDIDmode.resY = 768;
0572             EDIDmode.bpp = 0;
0573             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0574                 -1)
0575                 return EDIDmode.mode_number;
0576         }
0577         if (edid1_established_tim(&edid, EST_832x624_75Hz))
0578         {
0579             EDIDmode.resX = 832;
0580             EDIDmode.resY = 624;
0581             EDIDmode.bpp = 0;
0582             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0583                 -1)
0584                 return EDIDmode.mode_number;
0585         }
0586         if (edid1_established_tim(&edid, EST_800x600_60Hz) ||
0587             edid1_established_tim(&edid, EST_800x600_56Hz) ||
0588             edid1_established_tim(&edid, EST_800x600_75Hz) ||
0589             edid1_established_tim(&edid, EST_800x600_72Hz))
0590         {
0591             EDIDmode.resX = 800;
0592             EDIDmode.resY = 600;
0593             EDIDmode.bpp = 0;
0594             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0595                 -1)
0596                 return EDIDmode.mode_number;
0597         }
0598         if (edid1_established_tim(&edid, EST_720x400_88Hz) ||
0599             edid1_established_tim(&edid, EST_720x400_70Hz))
0600         {
0601             EDIDmode.resX = 720;
0602             EDIDmode.resY = 400;
0603             EDIDmode.bpp = 0;
0604             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0605                 -1)
0606                 return EDIDmode.mode_number;
0607         }
0608         if (edid1_established_tim(&edid, EST_640x480_75Hz) ||
0609             edid1_established_tim(&edid, EST_640x480_72Hz) ||
0610             edid1_established_tim(&edid, EST_640x480_67Hz) ||
0611             edid1_established_tim(&edid, EST_640x480_60Hz))
0612         {
0613             EDIDmode.resX = 640;
0614             EDIDmode.resY = 480;
0615             EDIDmode.bpp = 0;
0616             if (find_mode_by_resolution(mode_list, list_length, &EDIDmode) !=
0617                 -1)
0618                 return EDIDmode.mode_number;
0619         }
0620     }
0621     else
0622         printk(FB_VESA_NAME " error reading EDID: unsupported version\n");
0623     return -1;
0624 }
0625 
0626 void vesa_realmode_bootup_init(void)
0627 {
0628     uint32_t vbe_ret_val;
0629     uint16_t size;
0630     VBE_vbe_info_block *vib = (VBE_vbe_info_block *)
0631         i386_get_default_rm_buffer(&size);
0632     vbe_ret_val = VBE_controller_information(vib, 0x300);
0633     if (vbe_ret_val == -1)
0634     {
0635         printk(FB_VESA_NAME " error calling real mode interrupt.\n");
0636         return;
0637     }
0638     if (vbe_ret_val != (VBE_callSuccessful<<8 | VBE_functionSupported))
0639     {
0640         printk(FB_VESA_NAME " Function 00h (read VBE info block)"
0641             "not supported.\n");
0642     }
0643 /*  Helper array is later filled with mode numbers and their parameters
0644     sorted from the biggest values to the smalest where priorities of
0645     parameters are from the highest to the lowest: resolution X,
0646     resolution Y, bits per pixel.
0647     The array is used for search the monitor provided parameters in EDID
0648     structure and if found we set such mode using corresponding
0649     VESA function. */
0650 #define MAX_NO_OF_SORTED_MODES 100
0651     Mode_params sorted_mode_params[MAX_NO_OF_SORTED_MODES];
0652 
0653     uint16_t *vmpSegOff = (uint16_t *)&vib->VideoModePtr;
0654     uint16_t *modeNOPtr = (uint16_t*)
0655         i386_Real_to_physical(*(vmpSegOff+1), *vmpSegOff);
0656     uint16_t iterator = 0;
0657 
0658     if (*(uint16_t*)vib->VideoModePtr == VBE_STUB_VideoModeList)
0659     {
0660         printk(FB_VESA_NAME " VBE Core not implemented!\n");
0661     }
0662     else
0663     {
0664         /* prepare list of modes */
0665         while (*(modeNOPtr+iterator) != VBE_END_OF_VideoModeList &&
0666             *(modeNOPtr+iterator) != 0)
0667         { /* some bios implementations ends the list incorrectly with 0 */
0668             if (iterator < MAX_NO_OF_SORTED_MODES)
0669             {
0670                 sorted_mode_params[iterator].mode_number =*(modeNOPtr+iterator);
0671                 iterator ++;
0672             }
0673             else
0674                 break;
0675         }
0676         if (iterator < MAX_NO_OF_SORTED_MODES)
0677             sorted_mode_params[iterator].mode_number = 0;
0678     }
0679 
0680     VBE_mode_info_block *mib = (VBE_mode_info_block *)
0681         i386_get_default_rm_buffer(&size);
0682     iterator = 0;
0683     uint8_t nextFilteredMode = 0;
0684     uint16_t required_mode_attributes = VBE_modSupInHWMask |
0685         VBE_ColorModeMask | VBE_GraphicsModeMask | VBE_LinFraBufModeAvaiMask;
0686     /* get parameters of modes and filter modes according to set
0687         required parameters */
0688     while (iterator < MAX_NO_OF_SORTED_MODES &&
0689         sorted_mode_params[iterator].mode_number!=0)
0690     {
0691         VBE_mode_information(mib, sorted_mode_params[iterator].mode_number);
0692         if ((mib->ModeAttributes&required_mode_attributes) ==
0693             required_mode_attributes)
0694         {
0695             sorted_mode_params[nextFilteredMode].mode_number =
0696                 sorted_mode_params[iterator].mode_number;
0697             sorted_mode_params[nextFilteredMode].resX = mib->XResolution;
0698             sorted_mode_params[nextFilteredMode].resY = mib->YResolution;
0699             sorted_mode_params[nextFilteredMode].bpp  = mib->BitsPerPixel;
0700             nextFilteredMode ++;
0701         }
0702         iterator ++;
0703     }
0704     sorted_mode_params[nextFilteredMode].mode_number = 0;
0705 
0706     uint8_t number_of_modes = nextFilteredMode;
0707 
0708     /* first search for video argument in multiboot options */
0709     vbe_used_mode = find_mode_using_cmdline(sorted_mode_params,
0710                                             number_of_modes);
0711 
0712     if (vbe_used_mode == NO_MODE_REQ) {
0713         vbe_used_mode = find_mode_from_string(sorted_mode_params,
0714                             number_of_modes, rtems_fb_default_mode);
0715         if (vbe_used_mode != NO_MODE_REQ) {
0716             printk(FB_VESA_NAME " using application option to select"
0717                 " video mode\n");
0718         }
0719     }
0720     else
0721     {
0722         printk(FB_VESA_NAME " using command line option '--video='"
0723             "to select video mode\n");
0724     }
0725 
0726     switch (vbe_used_mode) {
0727         case NO_SUITABLE_MODE:
0728             printk(FB_VESA_NAME " requested mode not found\n");
0729             return;
0730         case BAD_FORMAT:
0731             printk(FB_VESA_NAME " bad format of video requested\n");
0732             return;
0733         case DONT_INIT:
0734             printk(FB_VESA_NAME " selected not to initialize graphics\n");
0735             return;
0736         case NO_MODE_REQ:
0737             printk(FB_VESA_NAME " not initialized, no video selected\n");
0738             return;
0739     }
0740 
0741     /* sort filtered modes */
0742     Mode_params modeXchgPlace;
0743     iterator = 0;
0744     uint8_t j;
0745     uint8_t idxBestMode;
0746     while (iterator < number_of_modes)
0747     {
0748         idxBestMode = iterator;
0749         j = iterator+1;
0750         while (j < number_of_modes)
0751         {
0752             if (sorted_mode_params[j].resX >
0753                     sorted_mode_params[idxBestMode].resX)
0754                 idxBestMode = j;
0755             else if (sorted_mode_params[j].resX ==
0756                      sorted_mode_params[idxBestMode].resX)
0757             {
0758                 if (sorted_mode_params[j].resY >
0759                         sorted_mode_params[idxBestMode].resY)
0760                     idxBestMode = j;
0761                 else if (sorted_mode_params[j].resY ==
0762                     sorted_mode_params[idxBestMode].resY)
0763                 {
0764                     if (sorted_mode_params[j].bpp >
0765                             sorted_mode_params[idxBestMode].bpp)
0766                         idxBestMode = j;
0767                 }
0768             }
0769             j++;
0770         }
0771         if (idxBestMode != iterator)
0772         {
0773             modeXchgPlace = sorted_mode_params[iterator];
0774             sorted_mode_params[iterator] = sorted_mode_params[idxBestMode];
0775             sorted_mode_params[idxBestMode] = modeXchgPlace;
0776         }
0777         iterator++;
0778     }
0779 
0780     if (vbe_used_mode == AUTO_SELECT)
0781     {
0782         printk(FB_VESA_NAME " auto video mode selected"
0783             "\n\ttrying EDID ...\n");
0784         /* second search monitor for good resolution */
0785         vbe_used_mode = find_mode_using_EDID(sorted_mode_params,
0786                                              number_of_modes);
0787         if (vbe_used_mode == -1)
0788         {
0789             printk(FB_VESA_NAME" monitor's EDID video parameters not supported"
0790                                "\n\tusing mode with highest resolution, bpp\n");
0791             /* third set highest values */
0792             vbe_used_mode = sorted_mode_params[0].mode_number;
0793         }
0794     }
0795 
0796     /* fill framebuffer structs with info about selected mode */
0797     vbe_ret_val = VBE_mode_information(mib, vbe_used_mode);
0798     if ((vbe_ret_val&0xff)!=VBE_functionSupported ||
0799         (vbe_ret_val>>8)!=VBE_callSuccessful)
0800     {
0801         printk(FB_VESA_NAME " Cannot get mode info anymore. ax=0x%lx\n",
0802                (uintptr_t) vbe_ret_val);
0803     }
0804 
0805     fb_var.xres = mib->XResolution;
0806     fb_var.yres = mib->YResolution;
0807     fb_var.bits_per_pixel = mib->BitsPerPixel;
0808     fb_var.red.offset =      mib->LinRedFieldPosition;
0809     fb_var.red.length =      mib->LinRedMaskSize;
0810     fb_var.red.msb_right =   0;
0811     fb_var.green.offset =    mib->LinGreenFieldPosition;
0812     fb_var.green.length =    mib->LinGreenMaskSize;
0813     fb_var.green.msb_right = 0;
0814     fb_var.blue.offset =     mib->LinBlueFieldPosition;
0815     fb_var.blue.length =     mib->LinBlueMaskSize;
0816     fb_var.blue.msb_right =  0;
0817     fb_var.transp.offset =   mib->LinRsvdFieldPosition;
0818     fb_var.transp.length =   mib->LinRsvdMaskSize;
0819     fb_var.transp.msb_right =0;
0820 
0821     fb_fix.smem_start  = (char *)mib->PhysBasePtr;
0822     fb_fix.line_length = mib->LinBytesPerScanLine;
0823     fb_fix.smem_len    = fb_fix.line_length*fb_var.yres;
0824     fb_fix.type        = FB_TYPE_PACKED_PIXELS;
0825     if (fb_var.bits_per_pixel < 24)
0826         fb_fix.visual  = FB_VISUAL_DIRECTCOLOR;
0827     else
0828         fb_fix.visual  = FB_VISUAL_TRUECOLOR;
0829 
0830     /* set selected mode */
0831     vbe_ret_val = VBE_set_mode(vbe_used_mode | VBE_linearFlatFrameBufMask,
0832         (VBE_CRTC_info_block *)(i386_get_default_rm_buffer(&size)));
0833     if (vbe_ret_val>>8 == VBE_callFailed)
0834         printk(FB_VESA_NAME " VBE: Requested mode is not available.");
0835 
0836     if ((vbe_ret_val&0xff)!= (VBE_functionSupported | VBE_callSuccessful<<8))
0837         printk(FB_VESA_NAME " Call to function 2h (set VBE mode) failed. "
0838             "ax=0x%" PRIx32 "\n", vbe_ret_val);
0839 
0840     vib = (void *) 0;
0841     mib = (void *) 0;
0842 }
0843 
0844 /*
0845  * fb_vesa device driver INITIALIZE entry point.
0846  */
0847 rtems_device_driver
0848 frame_buffer_initialize(
0849     rtems_device_major_number  major,
0850     rtems_device_minor_number  minor,
0851     void                      *arg
0852 )
0853 {
0854     rtems_status_code status;
0855 
0856     printk(FB_VESA_NAME " frame buffer -- driver initializing..\n" );
0857 
0858    /*
0859     * Register the device.
0860     */
0861     status = rtems_io_register_name(FRAMEBUFFER_DEVICE_0_NAME, major, 0);
0862     if (status != RTEMS_SUCCESSFUL)
0863     {
0864         printk("Error registering " FRAMEBUFFER_DEVICE_0_NAME
0865         " - " FB_VESA_NAME " frame buffer device!\n");
0866         rtems_fatal_error_occurred( status );
0867     }
0868 
0869     _Atomic_Flag_clear(&driver_mutex, ATOMIC_ORDER_RELEASE);
0870 
0871     return RTEMS_SUCCESSFUL;
0872 }
0873 
0874 /*
0875  * fb_vesa device driver OPEN entry point
0876  */
0877 rtems_device_driver
0878 frame_buffer_open(
0879     rtems_device_major_number  major,
0880     rtems_device_minor_number  minor,
0881     void                      *arg
0882 )
0883 {
0884     printk( FB_VESA_NAME " open device\n" );
0885 
0886     if (_Atomic_Flag_test_and_set(&driver_mutex, ATOMIC_ORDER_ACQUIRE) != 0 )
0887     {
0888         printk( FB_VESA_NAME " could not lock vesa_mutex\n" );
0889 
0890         return RTEMS_UNSATISFIED;
0891     }
0892 
0893     return RTEMS_SUCCESSFUL;
0894 
0895 }
0896 
0897 /*
0898  * fb_vesa device driver CLOSE entry point
0899  */
0900 rtems_device_driver
0901 frame_buffer_close(
0902     rtems_device_major_number  major,
0903     rtems_device_minor_number  minor,
0904     void                      *arg
0905 )
0906 {
0907   printk( FB_VESA_NAME " close device\n" );
0908   _Atomic_Flag_clear(&driver_mutex, ATOMIC_ORDER_RELEASE);
0909   /* restore previous state.  for VGA this means return to text mode.
0910    * leave out if graphics hardware has been initialized in
0911    * frame_buffer_initialize() */
0912 
0913   printk(FB_VESA_NAME ": close called.\n" );
0914   return RTEMS_SUCCESSFUL;
0915 }
0916 
0917 /*
0918  * fb_vesa device driver READ entry point.
0919  */
0920 rtems_device_driver
0921 frame_buffer_read(
0922     rtems_device_major_number  major,
0923     rtems_device_minor_number  minor,
0924     void                      *arg
0925 )
0926 {
0927   printk( FB_VESA_NAME " read device\n" );
0928   rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
0929   rw_args->bytes_moved =
0930     ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ?
0931         (fb_fix.smem_len - rw_args->offset) :
0932         rw_args->count;
0933   memcpy(rw_args->buffer, (const void *)
0934     (fb_fix.smem_start + rw_args->offset), rw_args->bytes_moved);
0935   return RTEMS_SUCCESSFUL;
0936 }
0937 
0938 /*
0939  * frame_vesa device driver WRITE entry point.
0940  */
0941 rtems_device_driver
0942 frame_buffer_write(
0943     rtems_device_major_number  major,
0944     rtems_device_minor_number  minor,
0945     void                      *arg
0946 )
0947 {
0948   printk( FB_VESA_NAME " write device\n" );
0949   rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
0950   rw_args->bytes_moved =
0951     ((rw_args->offset + rw_args->count) > fb_fix.smem_len ) ?
0952         (fb_fix.smem_len - rw_args->offset) :
0953         rw_args->count;
0954   memcpy( (void *) (fb_fix.smem_start + rw_args->offset),
0955     rw_args->buffer, rw_args->bytes_moved);
0956   return RTEMS_SUCCESSFUL;
0957 }
0958 
0959 static int get_fix_screen_info( struct fb_fix_screeninfo *info )
0960 {
0961   *info = fb_fix;
0962   return 0;
0963 }
0964 
0965 static int get_var_screen_info( struct fb_var_screeninfo *info )
0966 {
0967   *info =  fb_var;
0968   return 0;
0969 }
0970 
0971 /*
0972  * IOCTL entry point -- This method is called to carry
0973  * all services of this interface.
0974  */
0975 rtems_device_driver
0976 frame_buffer_control(
0977     rtems_device_major_number  major,
0978     rtems_device_minor_number  minor,
0979     void                      *arg
0980 )
0981 {
0982   rtems_libio_ioctl_args_t *args = arg;
0983 
0984   printk( FB_VESA_NAME " ioctl called, cmd=%lx\n", (uintptr_t) args->command  );
0985   printk("fbxres %d, fbyres %d\n", (int) fb_var.xres, (int) fb_var.yres);
0986   printk("fbbpp %d\n", (int) fb_var.bits_per_pixel);
0987 
0988   switch (args->command)
0989   {
0990   case FBIOGET_FSCREENINFO:
0991       args->ioctl_return =
0992         get_fix_screen_info( ( struct fb_fix_screeninfo * ) args->buffer );
0993       break;
0994   case FBIOGET_VSCREENINFO:
0995       args->ioctl_return =
0996         get_var_screen_info( ( struct fb_var_screeninfo * ) args->buffer );
0997       break;
0998   case FBIOPUT_VSCREENINFO:
0999     /* not implemented yet */
1000     args->ioctl_return = -1;
1001     return RTEMS_UNSATISFIED;
1002   case FBIOGETCMAP:
1003     /* no palette - truecolor mode */
1004     args->ioctl_return = -1;
1005     return RTEMS_UNSATISFIED;
1006   case FBIOPUTCMAP:
1007     /* no palette - truecolor mode */
1008     args->ioctl_return = -1;
1009     return RTEMS_UNSATISFIED;
1010   default:
1011     args->ioctl_return = 0;
1012     break;
1013   }
1014   return RTEMS_SUCCESSFUL;
1015 }