1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
  
     | 
    
      /* ----------------------------------------------------------------------- *
 *
 *   Copyright 1999-2012 H. Peter Anvin - All Rights Reserved
 *   Chandramouli Narayanan - extended for EFI support
 *
 *   Permission is hereby granted, free of charge, to any person
 *   obtaining a copy of this software and associated documentation
 *   files (the "Software"), to deal in the Software without
 *   restriction, including without limitation the rights to use,
 *   copy, modify, merge, publish, distribute, sublicense, and/or
 *   sell copies of the Software, and to permit persons to whom
 *   the Software is furnished to do so, subject to the following
 *   conditions:
 *
 *   The above copyright notice and this permission notice shall
 *   be included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 *
 * ----------------------------------------------------------------------- */
#include <inttypes.h>
#include <com32.h>
#include <stdlib.h>
#include <string.h>
#include <sys/fpu.h>
#include <syslinux/video.h>
#include <dprintf.h>
#include "efi.h"
/* We use cp865_8x16.psf as the standard font for EFI implementation
 * the header file below contains raw data parsed from cp865_8x16.psf
 */
#include "cp865_8x16.h"
#include "sys/vesa/vesa.h"
#include "sys/vesa/video.h"
#include "sys/vesa/fill.h"
#include "sys/vesa/debug.h"
/* EFI GOP support
 * Note GOP support uses the VESA info structure as much as possible and
 * extends it as needed for EFI support. Not all of the vesa info structure
 * is populated. Care must be taken in the routines that rely the vesa
 * informataion structure
 */
static void find_pixmask_bits(uint32_t mask, uint8_t *first_bit, uint8_t *len) {
    uint8_t bit_pos = 0, bit_len = 0;
    *first_bit = 0;
    *len = 0;
    if (mask == 0)
	return;
    while (!(mask & 0x1)) {
	mask = mask >> 1;
	bit_pos++;
    }
    while (mask & 0x1) {
	mask = mask >> 1;
	bit_len++;
    }
    *first_bit = bit_pos;
    *len = bit_len;
}
unsigned long lfb_size;
uint16_t lfb_line_size;
uint8_t lfb_rsize;
uint8_t lfb_gsize;
uint8_t lfb_bsize;
uint8_t lfb_resv_size;
static int efi_vesacon_set_mode(struct vesa_info *vesa_info, int *x, int *y,
				enum vesa_pixel_format *bestpxf)
{
    EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *gop_mode;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
    EFI_STATUS st;
    UINT32 mode_num = 0, bestmode;
    BOOLEAN mode_match = FALSE;
    UINTN sz_info;
    struct vesa_info *vi;
    struct vesa_mode_info *mi;
    int err = 0;
    //debug("Hello, World!\r\n");
    /* At this point, we assume that gnu-efi library is initialized */
    st = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(st)) {
	debug("LiblocateProtocol for GOP failed %d\n", st);
	return 1; /* function call failed */
    }
    /* We use the VESA info structure to store relevant GOP info as much as possible */
    gop_mode = GraphicsOutput->Mode;
    mode_info = gop_mode->Info;
    dprintf("mode %d version %d pixlfmt %d hres=%d vres=%d\n", mode_num, 
			mode_info->Version, mode_info->PixelFormat,
			mode_info->HorizontalResolution, mode_info->VerticalResolution);
    
    /* simply pick the best mode that suits the caller's resolution */
    for (mode_num = 0; mode_num < gop_mode->MaxMode; mode_num++) {
	st = uefi_call_wrapper(GraphicsOutput->QueryMode, 4, GraphicsOutput, mode_num, &sz_info, &mode_info);
	debug("mode_num = %d query_status %d\n", mode_num, st);
	if (st == EFI_SUCCESS && sz_info >= sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)) {
		/* For now, simply pick the best mode that suits caller's resolution (x,y)
		 * FIXME: Consider any additional criteria for matching mode
		 */
		mode_match = ((uint32_t)*x == mode_info->HorizontalResolution && (uint32_t)*y == mode_info->VerticalResolution);
		debug("mode %d hres=%d vres=%d\n", mode_num, mode_info->HorizontalResolution, mode_info->VerticalResolution);
		if (mode_match) {
			bestmode = mode_num;
			break;
		}
	}
    }
    if (!mode_match) {
	/* Instead of bailing out, set the mode to the system default.
 	 * Some systems do not have support for 640x480 for instance
 	 * This code deals with such cases.
 	 */
	mode_info = gop_mode->Info;
	*x = mode_info->HorizontalResolution;
	*y = mode_info->VerticalResolution;
	bestmode = gop_mode->Mode;
	debug("No matching mode, setting to available default mode %d (x=%d, y=%d)\n", bestmode, *x, *y);
    }
    /* Allocate space in the bounce buffer for these structures */
    vi = malloc(sizeof(*vi));
    if (!vi) {
	err = 10;		/* Out of memory */
	goto exit;
    }
    /* Note that the generic info is untouched as we don't find any relevance to EFI */
    mi = &vi->mi;
    /* Set up mode-specific information */
    mi->h_res = *x;
    mi->v_res = *y;
    mi->lfb_ptr = (uint8_t *)(VOID *)(UINTN)gop_mode->FrameBufferBase;
    lfb_size = gop_mode->FrameBufferSize;
    /* FIXME: 
     * The code below treats bpp == lfb_depth ; verify
     */
    switch (mode_info->PixelFormat) {
    case PixelRedGreenBlueReserved8BitPerColor:
	dprintf("RGB8bit ");
	mi->mode_attr = 0x0080;		/* supports physical frame buffer */
	mi->bpp = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * 8;
	mi->rpos = 0;
	mi->gpos = 8;
	mi->bpos = 16;
	mi->resv_pos = 24;
	lfb_resv_size = 8;
	mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8;
	*bestpxf = PXF_BGRA32;
	dprintf("bpp %d pixperScanLine %d logical_scan %d bytesperPix %d\n", mi->bpp, mode_info->PixelsPerScanLine, 
		mi->logical_scan, (mi->bpp + 7)>>3);
	break;
    case PixelBlueGreenRedReserved8BitPerColor:
	dprintf("BGR8bit ");
	mi->mode_attr = 0x0080;		/* supports physical frame buffer */
	mi->bpp = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * 8;
	mi->bpos = 0;
	mi->gpos = 8;
	mi->rpos = 16;
	mi->resv_pos = 24;
	lfb_resv_size = 8;
	mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8;
	*bestpxf = PXF_BGRA32;
	dprintf("bpp %d pixperScanLine %d logical_scan %d bytesperPix %d\n", mi->bpp, mode_info->PixelsPerScanLine, 
		mi->logical_scan, (mi->bpp + 7)>>3);
	break;
    case PixelBitMask:
	mi->mode_attr = 0x0080;		/* supports physical frame buffer */
	dprintf("RedMask 0x%x GrnMask 0x%x BluMask 0x%x RsvMask 0x%x\n",
		mode_info->PixelInformation.RedMask,
		mode_info->PixelInformation.GreenMask,
		mode_info->PixelInformation.BlueMask,
		mode_info->PixelInformation.ReservedMask);
	find_pixmask_bits(mode_info->PixelInformation.RedMask,
                          &mi->rpos, &lfb_rsize);
	find_pixmask_bits(mode_info->PixelInformation.GreenMask,
                          &mi->gpos, &lfb_gsize);
	find_pixmask_bits(mode_info->PixelInformation.BlueMask,
                          &mi->bpos, &lfb_bsize);
	find_pixmask_bits(mode_info->PixelInformation.ReservedMask,
                          &mi->resv_pos, &lfb_resv_size);
	mi->bpp = lfb_rsize + lfb_gsize +
                                  lfb_bsize + lfb_resv_size;
	mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8;
	dprintf("RPos %d Rsize %d GPos %d Gsize %d\n", mi->rpos, lfb_rsize, mi->gpos, lfb_gsize);
	dprintf("BPos %d Bsize %d RsvP %d RsvSz %d\n", mi->bpos, lfb_bsize, mi->resv_pos, lfb_resv_size);
	dprintf("bpp %d logical_scan %d bytesperPix %d\n", mi->bpp, mi->logical_scan, (mi->bpp + 7)>>3);
	switch (mi->bpp) {
	case 32:
		*bestpxf = PXF_BGRA32;
		break;
	case 24:
		*bestpxf = PXF_BGR24;
		break;
	case 16:
		*bestpxf = PXF_LE_RGB16_565;
		break;
	default:
		dprintf("Unable to handle bits per pixel %d, bailing out\n", mi->bpp);
		err = 4;
		goto exit;
	}
	break;
    case PixelBltOnly:
	/* FIXME: unsupported */
	mi->mode_attr = 0x0000;		/* no support for physical frame buffer */
	err = 4; /* no mode found */
	goto exit;
	break;
    default:
	/* should not get here, but let's error out */
	err = 4; /* no mode found */
	goto exit;
	break;
    }		   
    
    memcpy(&vesa_info->mi, mi, sizeof *mi);
    /* Now set video mode */
    st = uefi_call_wrapper(GraphicsOutput->SetMode, 2, GraphicsOutput, bestmode);
    if (EFI_ERROR(st)) {
	err = 9;		/* Failed to set mode */
	dprintf("Failed to set mode %d\n", bestmode);
	goto exit;
    }	
    /* TODO: Follow the code usage of vesacon_background & vesacon_shadowfb */
    /*
     __vesacon_background = calloc(mi->h_res*mi->v_res, 4);
     __vesacon_shadowfb = calloc(mi->h_res*mi->v_res, 4);
     */
     /* FIXME: the allocation takes the possible padding into account
      * whereas   BIOS code simply allocates hres * vres bytes.
      * Which is correct?
      */
     /*
      * For performance reasons, or due to hardware restrictions, scan lines
      * may be padded to an amount of memory alignment. These padding pixel elements
      * are outside the area covered by HorizontalResolution and are not visible.
      * For direct frame buffer access, this number is used as a span between starts
      * of pixel lines in video memory. Based on the size of an individual pixel element
      * and PixelsPerScanline, the offset in video memory from pixel element (x, y)
      * to pixel element (x, y+1) has to be calculated as 
      * "sizeof( PixelElement ) * PixelsPerScanLine", and not 
      * "sizeof( PixelElement ) * HorizontalResolution", though in many cases
      * those values can coincide.
      */
exit:
    if (vi)
	free(vi);
    return err;
}
static void efi_vesacon_screencpy(size_t dst, const uint32_t *s,
				  size_t bytes, struct win_info *wi)
{
    size_t win_off;
    char *win_base = wi->win_base;
 
    /* For EFI, we simply take the offset from the framebuffer and write to it
     * FIXME: any gotchas?
     */
    win_off = dst;
    memcpy(win_base + win_off, s, bytes);
}
static int efi_vesacon_font_query(uint8_t **font)
{
    /* set up font info
     * For now, font info is stored as raw data and used
     * as such. Altenatively, the font data stored in a file 
     * could be read and parsed. (note: for this, EFI
     * file support should be exposed via firmware structure)
     */
    *font = (uint8_t *)cp865_8x16_font_data;
    return cp865_8x16_font_height;
}
__export int __vesacon_i915resolution(int x, int y)
{
	/* We don't support this function */
	return 1;
}
struct vesa_ops efi_vesa_ops = {
	.set_mode = efi_vesacon_set_mode,
	.screencpy = efi_vesacon_screencpy,
	.font_query = efi_vesacon_font_query,
};
 
     |