]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/services/gfx/mw/v2_0/src/drivers/scr_ecospcsvga.c
Initial revision
[karo-tx-redboot.git] / packages / services / gfx / mw / v2_0 / src / drivers / scr_ecospcsvga.c
1 //=============================================================================
2 //
3 //      scr_ecospcsvga.c
4 //
5 //      eCos support for a PC display using SVGA/VESA
6 //
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //=============================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):    bartv
44 // Date:         2002-04-19
45 // Purpose:      Implement a screen driver for PC's with a VESA video BIOS
46 //
47 //    PC graphics cards are problematical. Many graphic cards are still more
48 //    or less compatible with ancient VGA hardware, but this provides access
49 //    only to rather low resolutions. There is no other hardware compatibility,
50 //    and many different graphics card all needing their own driver.
51 //
52 //    Each graphics card comes with a video BIOS @ 0x000C0000, which can be
53 //    invoked via int 0x10 to do useful operations like querying the
54 //    available video modes and setting the desired one. However the video
55 //    BIOS can normally only be called in x86 real mode, not protected mode,
56 //    Currently eCos only runs in protected mode, and has no support for
57 //    briefly switching back into real mode.
58 //
59 //    Current VESA VBE2 compliant graphics cards do offer some protected
60 //    mode entry points, but not enough to perform mode switches etc.
61 //    VBE3 is supposed to provide additional support for protected mode
62 //    applications, but does not appear to be widely implemented yet.
63 //
64 //    So for now the only solution is to perform the mode switching
65 //    during bootstrap, and specifically inside RedBoot. This is controlled
66 //    by an option in the RedBoot configuration, which has the side
67 //    effect of disabling RedBoot's own use of the screen and keyboard.
68 //    SVGA graphics modes are not completely standardized, so it is the
69 //    user's responsibility to pick a suitable mode.
70 //
71 //    Because RedBoot is a separate application, it is not guaranteed
72 //    that the appropriate mode switch has actually occurred by the
73 //    time this code runs. Therefore RedBoot also places the main SVGA
74 //    info block at location 0x000A0000, normally a window into video
75 //    memory, since that memory is not being used for anything else
76 //    right now. Similarly the mode info block for the current mode
77 //    is placed @ 0x000A0200, and to make it easier to find out what
78 //    modes are available on the current hardware and allow RedBoot to
79 //    be reconfigured appropriately, all the mode info blocks are
80 //    stored @ 0x000A0400 at 256-byte boundaries. The main info block
81 //    can be used to find out which modes are actually available.
82 //
83 //####DESCRIPTIONEND####
84 //=============================================================================
85
86
87 #include <pkgconf/system.h>
88 #include <pkgconf/microwindows.h>
89 #include <cyg/infra/cyg_ass.h>
90 #include <cyg/infra/diag.h>
91 #include <microwin/device.h>
92
93 #define VTSWITCH 0
94 #include "fb.h"
95 #include "genfont.h"
96 #include "genmem.h"
97
98 // ----------------------------------------------------------------------------
99 // Information about the current mode and all available video modes,
100 // should have been provided by RedBoot.
101 struct VBESVGAInfoBlock {
102     unsigned char   signature[4];      /* VESA */
103     unsigned short  version;
104     char*           oem_string_ptr;
105     unsigned char   capabilities[4];
106     unsigned short* video_mode_ptr;
107     unsigned short  total_memory;
108     unsigned short  oem_software_rev;
109     char*           oem_vendor_name_ptr;
110     char*           oem_product_name_ptr;
111     char*           oem_product_rev_ptr;
112     /* Reserved data here */
113 } __attribute__((packed));
114
115 struct VBEModeInfoBlock {
116     unsigned short  mode_attributes;
117     unsigned char   win_a_atributes;
118     unsigned char   win_b_attributes;
119     unsigned short  win_granularity;
120     unsigned short  win_size;
121     unsigned short  win_a_segment;
122     unsigned short  win_b_segment;
123     unsigned int    win_func_ptr;
124     unsigned short  bytes_per_scanline;
125     unsigned short  x_resolution;
126     unsigned short  y_resolution;
127     unsigned char   x_char_size;
128     unsigned char   y_char_size;
129     unsigned char   number_of_planes;
130     unsigned char   bits_per_pixel;
131     unsigned char   number_of_banks;
132     unsigned char   memory_model;
133     unsigned char   bank_size;
134     unsigned char   number_of_image_pages;
135     unsigned char   reserved;
136     unsigned char   red_mask_size;
137     unsigned char   red_field_position;
138     unsigned char   green_mask_size;
139     unsigned char   green_field_position;
140     unsigned char   blue_mask_size;
141     unsigned char   blue_field_position;
142     unsigned char   reserved_mask_size;
143     unsigned char   reserved_field_position;
144     unsigned char   direct_color_mode_info;
145     void*           physical_base_ptr;
146     unsigned int    offscreen_memory_offset;
147     unsigned short  offscreen_memory_size;
148 } __attribute__((packed));
149
150 #if defined(CYGDBG_MICROWINDOWS_PCSVGA_VERBOSE)
151 static void*
152 segoff_to_phys(void* addr)
153 {
154     int x       = (int) addr;
155     int segment = (x >> 12) & 0x0FFFF0;
156     int offset  = x & 0x0FFFF;
157     return (void*) (segment | offset);
158 }
159 #endif
160
161 // ----------------------------------------------------------------------------
162 static PSD  ecos_pcsvga_open(PSD);
163 static void ecos_pcsvga_close(PSD);
164 static void ecos_pcsvga_setportrait(PSD, int);
165 static void ecos_pcsvga_setpalette(PSD, int, int, MWPALENTRY*);
166 static void ecos_pcsvga_getscreeninfo(PSD , PMWSCREENINFO);
167
168 SCREENDEVICE scrdev = {
169     xres:               0,
170     yres:               0,
171     xvirtres:           0,
172     yvirtres:           0,
173     planes:             1,
174     linelen:            0,
175     size:               0,
176 #if defined(CYGIMP_MICROWINDOWS_PCSVGA32)
177     bpp:                32,
178     ncolors:            1 << 24,
179     pixtype:            MWPF_TRUECOLOR0888,
180 #elif defined(CYGIMP_MICROWINDOWS_PCSVGA16)
181     bpp:                16,
182     ncolors:            1 << 16,
183     pixtype:            MWPF_TRUECOLOR565,
184 #else
185 # error Unsupported video mode.    
186 #endif    
187     flags:              PSF_SCREEN | PSF_HAVEBLIT,
188     addr:               0,
189
190     Open:               &ecos_pcsvga_open,
191     Close:              &ecos_pcsvga_close,
192     GetScreenInfo:      &ecos_pcsvga_getscreeninfo,
193     SetPalette:         &ecos_pcsvga_setpalette,
194     DrawPixel:          NULL,
195     ReadPixel:          NULL,
196     DrawHorzLine:       NULL,
197     DrawVertLine:       NULL,
198     FillRect:           NULL,
199     builtin_fonts:      gen_fonts,
200     Blit:               NULL,
201     PreSelect:          NULL,
202     DrawArea:           NULL,
203     SetIOPermissions:   NULL,
204     AllocateMemGC:      &gen_allocatememgc,
205     MapMemGC:           &fb_mapmemgc,
206     FreeMemGC:          &gen_freememgc,
207     StretchBlit:        NULL,
208     SetPortrait:        &ecos_pcsvga_setportrait,
209     portrait:           MWPORTRAIT_NONE,
210     orgsubdriver:       NULL
211 };
212
213 static PSD
214 ecos_pcsvga_open(PSD psd)
215 {
216     struct VBESVGAInfoBlock*    vesa_info_block;
217     struct VBEModeInfoBlock*    vesa_current_mode;
218     
219     // Detect repeated invocations. The information in the video
220     // memory will be blown away after the first call, so can
221     // only be consulted once.
222     static int  opened  = 0;
223     static PSD  result  = NULL;
224     if (opened) {
225         return result;
226     }
227     opened = 1;
228     
229     // First make sure that there is valid video information in video
230     // memory, i.e. that the system was booted by means of a suitable
231     // RedBoot instance, that nothing has overwritten video memory
232     // yet, and that we are running at the desired screen depth.
233     vesa_info_block   = (struct VBESVGAInfoBlock*) 0x000A0000;
234     vesa_current_mode = (struct VBEModeInfoBlock*) 0x000A0200;
235     if (('V' != vesa_info_block->signature[0]) || ('E' != vesa_info_block->signature[1]) ||
236         ('S' != vesa_info_block->signature[2]) || ('A' != vesa_info_block->signature[3])) {
237         EPRINTF("No Video BIOS information at location 0x000A0000\n"
238                 "Please use a suitably configured RedBoot for bootstrap\n");
239         return NULL;
240     }
241
242     // Optionally, provide lots of information about the graphics card,
243     // the various modes available, and the current mode.
244 #ifdef CYGDBG_MICROWINDOWS_PCSVGA_VERBOSE
245     {
246         diag_printf("VESA info: %c%c%c%c\n", vesa_info_block->signature[0], vesa_info_block->signature[1],
247                     vesa_info_block->signature[2], vesa_info_block->signature[3]);
248         diag_printf("Version %04x\n", vesa_info_block->version);
249         if (NULL != vesa_info_block->oem_string_ptr) {
250             vesa_info_block->oem_string_ptr = segoff_to_phys(vesa_info_block->oem_string_ptr);
251             diag_printf("OEM %s\n", vesa_info_block->oem_string_ptr);
252         }
253         diag_printf("Total memory %dK\n", 64 * vesa_info_block->total_memory);
254         diag_printf("OEM software rev %04x\n", vesa_info_block->oem_software_rev);
255         if (NULL != vesa_info_block->oem_vendor_name_ptr) {
256             vesa_info_block->oem_vendor_name_ptr = segoff_to_phys(vesa_info_block->oem_vendor_name_ptr);
257             diag_printf("OEM vendor %s\n", vesa_info_block->oem_vendor_name_ptr);
258         }
259         if (NULL != vesa_info_block->oem_product_name_ptr) {
260             vesa_info_block->oem_product_name_ptr = segoff_to_phys(vesa_info_block->oem_product_name_ptr);
261             diag_printf("OEM product name %s\n", vesa_info_block->oem_product_name_ptr);
262         }
263         if (NULL != vesa_info_block->oem_product_rev_ptr) {
264             vesa_info_block->oem_product_rev_ptr = segoff_to_phys(vesa_info_block->oem_product_rev_ptr);
265             diag_printf("OEM product revision %s\n", vesa_info_block->oem_product_rev_ptr);
266         }
267         diag_printf("Capabilities: %02x, %02x, %02x, %02x\n",
268                     vesa_info_block->capabilities[0], vesa_info_block->capabilities[1],
269                     vesa_info_block->capabilities[2], vesa_info_block->capabilities[3]);
270         vesa_info_block->video_mode_ptr = (unsigned short*) segoff_to_phys((char*) vesa_info_block->video_mode_ptr);
271         for (i = 0; 0x0FFFF != vesa_info_block->video_mode_ptr[i]; i++) {
272             int mode = vesa_info_block->video_mode_ptr[i];
273             struct VBEModeInfoBlock*   mode_data   = (struct VBEModeInfoBlock*) (0x0A0400 + (0x100 * i));
274             diag_printf("Mode %04x: %4d*%4d @ %2dbpp, %4d bytes/line, fb %s 0x%08x\n", mode,
275                         mode_data->x_resolution, mode_data->y_resolution, mode_data->bits_per_pixel,
276                         mode_data->bytes_per_scanline,
277                         (0 == (mode_data->mode_attributes & 0x0080)) ? "no " : "yes",
278                         mode_data->physical_base_ptr);
279             if (32 == mode_data->bits_per_pixel) {
280                 diag_printf("    red %d bits << %d, green %d bits << %d, blue %d bits << %d, reserved %d bits << %d\n",
281                             mode_data->red_mask_size, mode_data->red_field_position,
282                             mode_data->green_mask_size, mode_data->green_field_position,
283                             mode_data->blue_mask_size, mode_data->blue_field_position,
284                             mode_data->reserved_mask_size, mode_data->reserved_field_position);
285             }
286         }
287
288         diag_printf("Current mode: %4d*%4d @ %2dbpp, %4d bytes/line, fb %s 0x%08x\n", 
289                     vesa_current_mode->x_resolution, vesa_current_mode->y_resolution, vesa_current_mode->bits_per_pixel,
290                     vesa_current_mode->bytes_per_scanline,
291                     (0 == (vesa_current_mode->mode_attributes & 0x0080)) ? "no" : "yes",
292                     vesa_current_mode->physical_base_ptr);
293         if (32 == vesa_current_mode->bits_per_pixel) {
294             diag_printf("    red %d bits << %d, green %d bits << %d, blue %d bits << %d, reserved %d bits << %d\n",
295                         vesa_current_mode->red_mask_size, vesa_current_mode->red_field_position,
296                         vesa_current_mode->green_mask_size, vesa_current_mode->green_field_position,
297                         vesa_current_mode->blue_mask_size, vesa_current_mode->blue_field_position,
298                         vesa_current_mode->reserved_mask_size, vesa_current_mode->reserved_field_position);
299         }
300     }
301 #endif
302
303 #if defined(CYGIMP_MICROWINDOWS_PCSVGA32)    
304     // A 32-bit displays, 0888
305     if ((32 != vesa_current_mode->bits_per_pixel) ||
306         (8 != vesa_current_mode->red_mask_size)   || (16 != vesa_current_mode->red_field_position) ||
307         (8 != vesa_current_mode->green_mask_size) || ( 8 != vesa_current_mode->green_field_position) ||
308         (8 != vesa_current_mode->blue_mask_size)  || ( 0 != vesa_current_mode->blue_field_position)) {
309
310         EPRINTF("RedBoot has not set up a valid initial graphics mode.\n"
311                 "This screen driver requires 32 bits per pixel, 0RGB.\n",
312                 "Configuration option CYGDBG_MICROWINDOWS_PCSVGA_VERBOSE can be used to\n"
313                 "get information about the available video modes.\n");
314         return NULL;
315     }
316 #elif defined(CYGIMP_MICROWINDOWS_PCSVGA16)
317     // A 16-bit display, 565
318     if ((16 != vesa_current_mode->bits_per_pixel) ||
319         (5 != vesa_current_mode->red_mask_size)   || (11 != vesa_current_mode->red_field_position) ||
320         (6 != vesa_current_mode->green_mask_size) || ( 5 != vesa_current_mode->green_field_position) ||
321         (5 != vesa_current_mode->blue_mask_size)  || ( 0 != vesa_current_mode->blue_field_position)) {
322
323         EPRINTF("RedBoot has not set up a valid initial graphics mode.\n"
324                 "This screen driver requires 16 bits per pixel, RGB=565.\n",
325                 "Configuration option CYGDBG_MICROWINDOWS_PCSVGA_VERBOSE can be used to\n"
326                 "get information about the available video modes.\n");
327         return NULL;
328     }
329 #endif
330     
331     if ((0 == (vesa_current_mode->mode_attributes & 0x0080)) || (NULL == vesa_current_mode->physical_base_ptr)) {
332         EPRINTF("RedBoot has not set up a valid initial graphics mode.\n"
333                 "The frame buffer is not linearly accessible.\n");
334         return NULL;
335     }
336     
337
338     // OK, we are at the expected depth. Fill in the resolution etc. based
339     // on the current video mode.
340     psd->xres       = vesa_current_mode->x_resolution;
341     psd->yres       = vesa_current_mode->y_resolution;
342     psd->xvirtres   = vesa_current_mode->x_resolution;
343     psd->yvirtres   = vesa_current_mode->y_resolution;
344     psd->linelen    = vesa_current_mode->bytes_per_scanline;
345     psd->addr       = vesa_current_mode->physical_base_ptr;
346     // The remaining parameters in the structure are statically
347     // initialized for now, e.g. 32bpp, pixtype TRUECOLOR0888
348
349     // Use one of the standard framebuffer subdrivers.
350     psd->orgsubdriver  = select_fb_subdriver(psd);
351     if (NULL == psd->orgsubdriver) {
352         EPRINTF("There is no standard framebuffer driver for the current video mode.\n");
353         return NULL;
354     }
355     if (!set_subdriver(psd, psd->orgsubdriver, TRUE)) {
356         EPRINTF("Framebuffer subdriver initialization failed.\n");
357         return NULL;
358     }
359
360     // That should be all for now.
361     result = psd;
362     return result;
363 }
364
365 // Close is a no-op, no resources have been allocated.
366 // The open() code detects multiple invocations.
367 static void
368 ecos_pcsvga_close(PSD psd)
369 {
370     CYG_UNUSED_PARAM(PSD, psd);
371 }
372
373 // Setting the palette is a no-op for now since only true color
374 // modes are supported.
375 static void
376 ecos_pcsvga_setpalette(PSD psd, int first, int count, MWPALENTRY* pal)
377 {
378     CYG_UNUSED_PARAM(PSD, psd);
379     CYG_UNUSED_PARAM(int, first);
380     CYG_UNUSED_PARAM(int, count);
381     CYG_UNUSED_PARAM(MWPALENTRY*, pal);
382 }
383
384 // Setting the portrait mode. There are standard subdrivers to
385 // cope with this, manipulating the arguments and then calling
386 // the original subdriver.
387
388 extern SUBDRIVER fbportrait_left, fbportrait_right, fbportrait_down;
389
390 static void
391 ecos_pcsvga_setportrait(PSD psd, int portrait_mode)
392 {
393     psd->portrait   = portrait_mode;
394     if (portrait_mode & (MWPORTRAIT_LEFT | MWPORTRAIT_RIGHT)) {
395         psd->xvirtres   = psd->yres;
396         psd->yvirtres   = psd->xres;
397     } else {
398         psd->xvirtres   = psd->xres;
399         psd->yvirtres   = psd->yres;
400     }
401     
402     if (portrait_mode == MWPORTRAIT_LEFT) {
403         set_subdriver(psd, &fbportrait_left, FALSE);
404     } else if (portrait_mode == MWPORTRAIT_RIGHT) {
405         set_subdriver(psd, &fbportrait_right, FALSE);
406     } else if (portrait_mode == MWPORTRAIT_DOWN) {
407         set_subdriver(psd, &fbportrait_down, FALSE);
408     } else {
409         set_subdriver(psd, psd->orgsubdriver, FALSE);
410     }
411 }
412
413 // Getting screen info. It is not clear why this is part of the driver
414 // since nearly all the information is already available in psd.
415 static void
416 ecos_pcsvga_getscreeninfo(PSD psd, PMWSCREENINFO psi)
417 {
418     psi->rows       = psd->yvirtres;
419     psi->cols       = psd->xvirtres;
420     psi->planes     = psd->planes;
421     psi->bpp        = psd->bpp;
422     psi->ncolors    = psd->ncolors;
423     psi->portrait   = psd->portrait;
424     psi->fbdriver   = TRUE;
425     psi->pixtype    = psd->pixtype;
426 #if defined(CYGIMP_MICROWINDOWS_PCSVGA32)
427     psi->rmask      = 0x00FF0000;
428     psi->gmask      = 0x0000FF00;
429     psi->bmask      = 0x000000FF;
430 #elif defined(CYGIMP_MICROWINDOWS_PCSVGA16)
431     psi->rmask      = 0x0000F800;
432     psi->gmask      = 0x000007E0;
433     psi->bmask      = 0x0000001F;
434 #endif    
435
436     // The screen dimensions are not readily available.
437     // Assume an 18' monitor (actual visible diameter),
438     // which corresponds ~ to 36cm by 28cm
439     psi->xdpcm      = psd->xres / 36;
440     psi->ydpcm      = psd->yres / 28;
441
442     psi->fonts      = NUMBER_FONTS;
443
444     // Remaining information such as keyboard modifiers etc. cannot be
445     // known here.
446 }