diff options
Diffstat (limited to 'grub-core/video')
27 files changed, 15184 insertions, 0 deletions
diff --git a/grub-core/video/bitmap.c b/grub-core/video/bitmap.c new file mode 100644 index 0000000..6256e20 --- /dev/null +++ b/grub-core/video/bitmap.c @@ -0,0 +1,238 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/video.h> +#include <grub/bitmap.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/i18n.h> +#include <grub/safemath.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* List of bitmap readers registered to system. */ +static grub_video_bitmap_reader_t bitmap_readers_list; + +/* Register bitmap reader. */ +void +grub_video_bitmap_reader_register (grub_video_bitmap_reader_t reader) +{ + reader->next = bitmap_readers_list; + bitmap_readers_list = reader; +} + +/* Unregister bitmap reader. */ +void +grub_video_bitmap_reader_unregister (grub_video_bitmap_reader_t reader) +{ + grub_video_bitmap_reader_t *p, q; + + for (p = &bitmap_readers_list, q = *p; q; p = &(q->next), q = q->next) + if (q == reader) + { + *p = q->next; + break; + } +} + +/* Creates new bitmap, saves created bitmap on success to *bitmap. */ +grub_err_t +grub_video_bitmap_create (struct grub_video_bitmap **bitmap, + unsigned int width, unsigned int height, + enum grub_video_blit_format blit_format) +{ + struct grub_video_mode_info *mode_info; + grub_size_t size; + + if (!bitmap) + return grub_error (GRUB_ERR_BUG, "invalid argument"); + + *bitmap = 0; + + if (width == 0 || height == 0) + return grub_error (GRUB_ERR_BUG, "invalid argument"); + + *bitmap = (struct grub_video_bitmap *)grub_malloc (sizeof (struct grub_video_bitmap)); + if (! *bitmap) + return grub_errno; + + mode_info = &((*bitmap)->mode_info); + + /* Populate mode_info. */ + mode_info->width = width; + mode_info->height = height; + mode_info->blit_format = blit_format; + + switch (blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA; + mode_info->bpp = 32; + mode_info->bytes_per_pixel = 4; + mode_info->number_of_colors = 256; + mode_info->red_mask_size = 8; + mode_info->red_field_pos = 0; + mode_info->green_mask_size = 8; + mode_info->green_field_pos = 8; + mode_info->blue_mask_size = 8; + mode_info->blue_field_pos = 16; + mode_info->reserved_mask_size = 8; + mode_info->reserved_field_pos = 24; + break; + + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + mode_info->bpp = 24; + mode_info->bytes_per_pixel = 3; + mode_info->number_of_colors = 256; + mode_info->red_mask_size = 8; + mode_info->red_field_pos = 0; + mode_info->green_mask_size = 8; + mode_info->green_field_pos = 8; + mode_info->blue_mask_size = 8; + mode_info->blue_field_pos = 16; + mode_info->reserved_mask_size = 0; + mode_info->reserved_field_pos = 0; + break; + + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + mode_info->bpp = 8; + mode_info->bytes_per_pixel = 1; + mode_info->number_of_colors = 256; + mode_info->red_mask_size = 0; + mode_info->red_field_pos = 0; + mode_info->green_mask_size = 0; + mode_info->green_field_pos = 0; + mode_info->blue_mask_size = 0; + mode_info->blue_field_pos = 0; + mode_info->reserved_mask_size = 0; + mode_info->reserved_field_pos = 0; + break; + + default: + grub_free (*bitmap); + *bitmap = 0; + + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported bitmap format"); + } + + mode_info->pitch = width * mode_info->bytes_per_pixel; + + /* Calculate size needed for the data. */ + if (grub_mul (width, mode_info->bytes_per_pixel, &size) || + grub_mul (size, height, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + goto fail; + } + + (*bitmap)->data = grub_zalloc (size); + if (! (*bitmap)->data) + goto fail; + + return GRUB_ERR_NONE; + + fail: + grub_free (*bitmap); + *bitmap = NULL; + + return grub_errno; +} + +/* Frees all resources allocated by bitmap. */ +grub_err_t +grub_video_bitmap_destroy (struct grub_video_bitmap *bitmap) +{ + if (! bitmap) + return GRUB_ERR_NONE; + + grub_free (bitmap->data); + grub_free (bitmap); + + return GRUB_ERR_NONE; +} + +/* Match extension to filename. */ +static int +match_extension (const char *filename, const char *ext) +{ + int pos; + int ext_len; + + pos = grub_strlen (filename); + ext_len = grub_strlen (ext); + + if (! pos || ! ext_len || ext_len > pos) + return 0; + + pos -= ext_len; + + return grub_strcasecmp (filename + pos, ext) == 0; +} + +/* Loads bitmap using registered bitmap readers. */ +grub_err_t +grub_video_bitmap_load (struct grub_video_bitmap **bitmap, + const char *filename) +{ + grub_video_bitmap_reader_t reader = bitmap_readers_list; + + if (!bitmap) + return grub_error (GRUB_ERR_BUG, "invalid argument"); + + *bitmap = 0; + + while (reader) + { + if (match_extension (filename, reader->extension)) + return reader->reader (bitmap, filename); + + reader = reader->next; + } + + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + /* TRANSLATORS: We're speaking about bitmap images like + JPEG or PNG. */ + N_("bitmap file `%s' is of" + " unsupported format"), filename); +} + +/* Return mode info for bitmap. */ +void grub_video_bitmap_get_mode_info (struct grub_video_bitmap *bitmap, + struct grub_video_mode_info *mode_info) +{ + if (!bitmap) + return; + + *mode_info = bitmap->mode_info; +} + +/* Return pointer to bitmap's raw data. */ +void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap) +{ + if (!bitmap) + return 0; + + return bitmap->data; +} + diff --git a/grub-core/video/bitmap_scale.c b/grub-core/video/bitmap_scale.c new file mode 100644 index 0000000..70c32f0 --- /dev/null +++ b/grub-core/video/bitmap_scale.c @@ -0,0 +1,515 @@ +/* bitmap_scale.c - Bitmap scaling. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/video.h> +#include <grub/bitmap.h> +#include <grub/bitmap_scale.h> +#include <grub/types.h> +#include <grub/dl.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Prototypes for module-local functions. */ +static grub_err_t scale_nn (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); +static grub_err_t scale_bilinear (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); + +static grub_err_t +verify_source_bitmap (struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (src == 0) + return grub_error (GRUB_ERR_BUG, + "null src bitmap in grub_video_bitmap_create_scaled"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BUG, + "src format not supported for scale"); + if (src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BUG, + "source bitmap has a zero dimension"); + if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp) + return grub_error (GRUB_ERR_BUG, + "bitmap to scale has inconsistent Bpp and bpp"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_bitmap_scale (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method scale_method) +{ + switch (scale_method) + { + case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST: + return scale_nn (dst, src); + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR: + return scale_bilinear (dst, src); + default: + return grub_error (GRUB_ERR_BUG, "Invalid scale_method value"); + } +} + +/* This function creates a new scaled version of the bitmap SRC. The new + bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm + is given by SCALE_METHOD. If an error is encountered, the return code is + not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or + it is destroyed before this function returns. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method + scale_method) +{ + *dst = 0; + + grub_err_t err = verify_source_bitmap(src); + if (err != GRUB_ERR_NONE) + return err; + if (dst_width <= 0 || dst_height <= 0) + return grub_error (GRUB_ERR_BUG, + "requested to scale to a size w/ a zero dimension"); + + /* Create the new bitmap. */ + grub_err_t ret; + ret = grub_video_bitmap_create (dst, dst_width, dst_height, + src->mode_info.blit_format); + if (ret != GRUB_ERR_NONE) + return ret; /* Error. */ + + ret = grub_video_bitmap_scale (*dst, src, scale_method); + + if (ret == GRUB_ERR_NONE) + { + /* Success: *dst is now a pointer to the scaled bitmap. */ + return GRUB_ERR_NONE; + } + else + { + /* Destroy the bitmap and return the error code. */ + grub_video_bitmap_destroy (*dst); + *dst = 0; + return ret; + } +} + +static grub_err_t +make_h_align (unsigned *x, unsigned *w, unsigned new_w, + grub_video_bitmap_h_align_t h_align) +{ + grub_err_t ret = GRUB_ERR_NONE; + if (new_w >= *w) + { + *x = 0; + *w = new_w; + return GRUB_ERR_NONE; + } + switch (h_align) + { + case GRUB_VIDEO_BITMAP_H_ALIGN_LEFT: + *x = 0; + break; + case GRUB_VIDEO_BITMAP_H_ALIGN_CENTER: + *x = (*w - new_w) / 2; + break; + case GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT: + *x = *w - new_w; + break; + default: + ret = grub_error (GRUB_ERR_BUG, "Invalid h_align value"); + break; + } + *w = new_w; + return ret; +} + +static grub_err_t +make_v_align (unsigned *y, unsigned *h, unsigned new_h, + grub_video_bitmap_v_align_t v_align) +{ + grub_err_t ret = GRUB_ERR_NONE; + if (new_h >= *h) + { + *y = 0; + *h = new_h; + return GRUB_ERR_NONE; + } + switch (v_align) + { + case GRUB_VIDEO_BITMAP_V_ALIGN_TOP: + *y = 0; + break; + case GRUB_VIDEO_BITMAP_V_ALIGN_CENTER: + *y = (*h - new_h) / 2; + break; + case GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM: + *y = *h - new_h; + break; + default: + ret = grub_error (GRUB_ERR_BUG, "Invalid v_align value"); + break; + } + *h = new_h; + return ret; +} + +grub_err_t +grub_video_bitmap_scale_proportional (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method + scale_method, + grub_video_bitmap_selection_method_t + selection_method, + grub_video_bitmap_v_align_t v_align, + grub_video_bitmap_h_align_t h_align) +{ + *dst = 0; + grub_err_t ret = verify_source_bitmap(src); + if (ret != GRUB_ERR_NONE) + return ret; + if (dst_width <= 0 || dst_height <= 0) + return grub_error (GRUB_ERR_BUG, + "requested to scale to a size w/ a zero dimension"); + + ret = grub_video_bitmap_create (dst, dst_width, dst_height, + src->mode_info.blit_format); + if (ret != GRUB_ERR_NONE) + return ret; /* Error. */ + + unsigned dx0 = 0; + unsigned dy0 = 0; + unsigned dw = dst_width; + unsigned dh = dst_height; + unsigned sx0 = 0; + unsigned sy0 = 0; + unsigned sw = src->mode_info.width; + unsigned sh = src->mode_info.height; + + switch (selection_method) + { + case GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP: + /* Comparing sw/sh VS dw/dh. */ + if (sw * dh < dw * sh) + ret = make_v_align (&sy0, &sh, sw * dh / dw, v_align); + else + ret = make_h_align (&sx0, &sw, sh * dw / dh, h_align); + break; + case GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING: + if (sw * dh < dw * sh) + ret = make_h_align (&dx0, &dw, sw * dh / sh, h_align); + else + ret = make_v_align (&dy0, &dh, sh * dw / sw, v_align); + break; + case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH: + if (sw * dh < dw * sh) + ret = make_v_align (&sy0, &sh, sw * dh / dw, v_align); + else + ret = make_v_align (&dy0, &dh, sh * dw / sw, v_align); + break; + case GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT: + if (sw * dh < dw * sh) + ret = make_h_align (&dx0, &dw, sw * dh / sh, h_align); + else + ret = make_h_align (&sx0, &sw, sh * dw / dh, h_align); + break; + default: + ret = grub_error (GRUB_ERR_BUG, "Invalid selection_method value"); + break; + } + + if (ret == GRUB_ERR_NONE) + { + /* Backup original data. */ + int src_width_orig = src->mode_info.width; + int src_height_orig = src->mode_info.height; + grub_uint8_t *src_data_orig = src->data; + int dst_width_orig = (*dst)->mode_info.width; + int dst_height_orig = (*dst)->mode_info.height; + grub_uint8_t *dst_data_orig = (*dst)->data; + + int dstride = (*dst)->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = src->mode_info.bytes_per_pixel; + + /* Crop src and dst. */ + src->mode_info.width = sw; + src->mode_info.height = sh; + src->data = (grub_uint8_t *) src->data + sx0 * bytes_per_pixel + + sy0 * sstride; + (*dst)->mode_info.width = dw; + (*dst)->mode_info.height = dh; + (*dst)->data = (grub_uint8_t *) (*dst)->data + dx0 * bytes_per_pixel + + dy0 * dstride; + + /* Scale our image. */ + ret = grub_video_bitmap_scale (*dst, src, scale_method); + + /* Restore original data. */ + src->mode_info.width = src_width_orig; + src->mode_info.height = src_height_orig; + src->data = src_data_orig; + (*dst)->mode_info.width = dst_width_orig; + (*dst)->mode_info.height = dst_height_orig; + (*dst)->data = dst_data_orig; + } + + if (ret == GRUB_ERR_NONE) + { + /* Success: *dst is now a pointer to the scaled bitmap. */ + return GRUB_ERR_NONE; + } + else + { + /* Destroy the bitmap and return the error code. */ + grub_video_bitmap_destroy (*dst); + *dst = 0; + return ret; + } +} + +static grub_err_t +verify_bitmaps (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BUG, "null bitmap in scale function"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BUG, + "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BUG, + "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BUG, + "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BUG, "bitmap has a zero dimension"); + + return GRUB_ERR_NONE; +} + +/* Nearest neighbor bitmap scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the nearest neighbor algorithm to + interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + grub_err_t err = verify_bitmaps(dst, src); + if (err != GRUB_ERR_NONE) + return err; + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + unsigned dw = dst->mode_info.width; + unsigned dh = dst->mode_info.height; + unsigned sw = src->mode_info.width; + unsigned sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + unsigned dy, sy, ystep, yfrac, yover; + unsigned sx, xstep, xfrac, xover; + grub_uint8_t *dptr, *dline_end, *sline; + + xstep = sw / dw; + xover = sw % dw; + ystep = sh / dh; + yover = sh % dh; + + for (dy = 0, sy = 0, yfrac = 0; dy < dh; dy++, sy += ystep, yfrac += yover) + { + if (yfrac >= dh) + { + yfrac -= dh; + sy++; + } + dptr = ddata + dy * dstride; + dline_end = dptr + dw * bytes_per_pixel; + sline = sdata + sy * sstride; + for (sx = 0, xfrac = 0; dptr < dline_end; sx += xstep, xfrac += xover, dptr += bytes_per_pixel) + { + grub_uint8_t *sptr; + int comp; + + if (xfrac >= dw) + { + xfrac -= dw; + sx++; + } + + /* Get the address of the pixels in src and dst. */ + sptr = sline + sx * bytes_per_pixel; + + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + return GRUB_ERR_NONE; +} + +/* Bilinear interpolation image scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the bilinear interpolation algorithm + to interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + grub_err_t err = verify_bitmaps(dst, src); + if (err != GRUB_ERR_NONE) + return err; + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + unsigned dw = dst->mode_info.width; + unsigned dh = dst->mode_info.height; + unsigned sw = src->mode_info.width; + unsigned sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + unsigned dy, syf, sy, ystep, yfrac, yover; + unsigned sxf, sx, xstep, xfrac, xover; + grub_uint8_t *dptr, *dline_end, *sline; + + xstep = (sw << 8) / dw; + xover = (sw << 8) % dw; + ystep = (sh << 8) / dh; + yover = (sh << 8) % dh; + + for (dy = 0, syf = 0, yfrac = 0; dy < dh; dy++, syf += ystep, yfrac += yover) + { + if (yfrac >= dh) + { + yfrac -= dh; + syf++; + } + sy = syf >> 8; + dptr = ddata + dy * dstride; + dline_end = dptr + dw * bytes_per_pixel; + sline = sdata + sy * sstride; + for (sxf = 0, xfrac = 0; dptr < dline_end; sxf += xstep, xfrac += xover, dptr += bytes_per_pixel) + { + grub_uint8_t *sptr; + int comp; + + if (xfrac >= dw) + { + xfrac -= dw; + sxf++; + } + + /* Get the address of the pixels in src and dst. */ + sx = sxf >> 8; + sptr = sline + sx * bytes_per_pixel; + + /* If we have enough space to do so, use bilinear interpolation. + Otherwise, fall back to nearest neighbor for this pixel. */ + if (sx < sw - 1 && sy < sh - 1) + { + /* Do bilinear interpolation. */ + + /* Fixed-point .8 numbers representing the fraction of the + distance in the x (u) and y (v) direction within the + box of 4 pixels in the source. */ + unsigned u = sxf & 0xff; + unsigned v = syf & 0xff; + + for (comp = 0; comp < bytes_per_pixel; comp++) + { + /* Get the component's values for the + four source corner pixels. */ + unsigned f00 = sptr[comp]; + unsigned f10 = sptr[comp + bytes_per_pixel]; + unsigned f01 = sptr[comp + sstride]; + unsigned f11 = sptr[comp + sstride + bytes_per_pixel]; + + /* Count coeffecients. */ + unsigned c00 = (256 - u) * (256 - v); + unsigned c10 = u * (256 - v); + unsigned c01 = (256 - u) * v; + unsigned c11 = u * v; + + /* Interpolate. */ + unsigned fxy = c00 * f00 + c01 * f01 + c10 * f10 + c11 * f11; + fxy = fxy >> 16; + + dptr[comp] = fxy; + } + } + else + { + /* Fall back to nearest neighbor interpolation. */ + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + } + return GRUB_ERR_NONE; +} diff --git a/grub-core/video/bochs.c b/grub-core/video/bochs.c new file mode 100644 index 0000000..30ea1bd --- /dev/null +++ b/grub-core/video/bochs.c @@ -0,0 +1,439 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static struct +{ + struct grub_video_mode_info mode_info; + + grub_uint8_t *ptr; + int mapped; + grub_uint32_t base; + grub_pci_device_t dev; +} framebuffer; + +#define BOCHS_APERTURE_SIZE 0x800000 +#define BOCHS_MAX_WIDTH 1600 +#define BOCHS_MAX_HEIGHT 1200 +#define BOCHS_WIDTH_ALIGN 8 + +enum + { + BOCHS_VBE_INDEX = 0x1ce, + BOCHS_VBE_DATA = 0x1cf, + }; + +enum + { + BOCHS_VBE_WIDTH = 1, + BOCHS_VBE_HEIGHT = 2, + BOCHS_VBE_BPP = 3, + BOCHS_VBE_ENABLE = 4, + BOCHS_VBE_Y_OFFSET = 9, + BOCHS_VBE_MAX + }; + +static void +vbe_write (grub_uint16_t val, grub_uint16_t addr) +{ + grub_outw (addr, BOCHS_VBE_INDEX); + grub_outw (val, BOCHS_VBE_DATA); +} + +static grub_uint16_t +vbe_read (grub_uint16_t addr) +{ + grub_outw (addr, BOCHS_VBE_INDEX); + return grub_inw (BOCHS_VBE_DATA); +} + +struct saved_state +{ + grub_uint8_t cr[256]; + grub_uint8_t gr[256]; + grub_uint8_t sr[256]; + grub_uint8_t r[256]; + grub_uint8_t g[256]; + grub_uint8_t b[256]; + grub_uint8_t vbe[BOCHS_VBE_MAX]; + int vbe_enable; + /* We need to preserve VGA font and VGA text. */ + grub_uint8_t vram[32 * 4 * 256]; +}; + +static struct saved_state initial_state; +static int state_saved = 0; + +static void +save_state (struct saved_state *st) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE (st->cr); i++) + st->cr[i] = grub_vga_cr_read (i); + for (i = 0; i < ARRAY_SIZE (st->gr); i++) + st->gr[i] = grub_vga_gr_read (i); + for (i = 0; i < ARRAY_SIZE (st->sr); i++) + st->sr[i] = grub_vga_sr_read (i); + + for (i = 0; i < 256; i++) + grub_vga_palette_read (i, st->r + i, st->g + i, st->b + i); + + st->vbe_enable = vbe_read (BOCHS_VBE_ENABLE) & 1; + if (st->vbe_enable) + for (i = 0; i < ARRAY_SIZE (st->vbe); i++) + st->vbe[i] = vbe_read (i); + + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE); + grub_memcpy (st->vram, framebuffer.ptr, sizeof (st->vram)); + grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE); +} + +static void +restore_state (struct saved_state *st) +{ + unsigned i; + + if (st->vbe_enable) + for (i = 0; i < ARRAY_SIZE (st->vbe); i++) + vbe_write (st->vbe[i], i); + else + vbe_write (0, BOCHS_VBE_ENABLE); + + grub_vga_cr_write (0, 0x11); + for (i = 0; i < ARRAY_SIZE (st->cr); i++) + grub_vga_cr_write (st->cr[i], i); + for (i = 0; i < ARRAY_SIZE (st->sr); i++) + grub_vga_sr_write (st->sr[i], i); + for (i = 0; i < ARRAY_SIZE (st->gr); i++) + grub_vga_gr_write (st->gr[i], i); + + for (i = 0; i < 256; i++) + grub_vga_palette_write (i, st->r[i], st->g[i], st->b[i]); + + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE); + grub_memcpy (framebuffer.ptr, st->vram, sizeof (st->vram)); + grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE); +} + +static grub_err_t +grub_video_bochs_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_bochs_video_fini (void) +{ + if (framebuffer.mapped) + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + BOCHS_APERTURE_SIZE); + + if (state_saved) + { + restore_state (&initial_state); + state_saved = 0; + } + + return grub_video_fb_fini (); +} + +static grub_err_t +doublebuf_pageflipping_set_page (int page) +{ + int start = framebuffer.mode_info.height * page; + + vbe_write (start, BOCHS_VBE_Y_OFFSET); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_bochs_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + { + unsigned i; + if (start >= 0x100) + return GRUB_ERR_NONE; + if (start + count >= 0x100) + count = 0x100 - start; + + for (i = 0; i < count; i++) + grub_vga_palette_write (start + i, palette_data[i].r, palette_data[i].g, + palette_data[i].b); + } + + /* Then set color to emulated palette. */ + return grub_video_fb_set_palette (start, count, palette_data); +} + +/* Helper for grub_video_bochs_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != 0x0300 || pciid != 0x11111234) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK; + if (!framebuffer.base) + return 0; + *found = 1; + framebuffer.dev = dev; + + /* Enable address spaces. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, 0x7); + + return 1; +} + +static grub_err_t +grub_video_bochs_setup (unsigned int width, unsigned int height, + grub_video_mode_type_t mode_type, + grub_video_mode_type_t mode_mask) +{ + int depth; + grub_err_t err; + int found = 0; + int pitch, bytes_per_pixel; + grub_size_t page_size; /* The size of a page in bytes. */ + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (width == 0 || height == 0) + { + width = 800; + height = 600; + } + + if (width > BOCHS_MAX_WIDTH) + return grub_error (GRUB_ERR_IO, "width must be at most %d", + BOCHS_MAX_WIDTH); + + if (height > BOCHS_MAX_HEIGHT) + return grub_error (GRUB_ERR_IO, "height must be at most %d", + BOCHS_MAX_HEIGHT); + + if (width & (BOCHS_WIDTH_ALIGN - 1)) + return grub_error (GRUB_ERR_IO, "width must be a multiple of %d", + BOCHS_WIDTH_ALIGN); + + if (depth == 0 + && !grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, 0)) + depth = 24; + + if (depth == 0) + depth = 8; + + if (depth != 32 && depth != 24 && depth != 16 && depth != 15 && depth != 8 + && depth != 4) + return grub_error (GRUB_ERR_IO, "only 32, 24, 16, 15 and 8-bpp are" + " supported by bochs video"); + + if (depth == 4) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "4-bpp isn't supported"); + + bytes_per_pixel = (depth + 7) / 8; + if (depth == 4) + pitch = width / 2; + else + pitch = width * bytes_per_pixel; + + page_size = pitch * height; + + if (page_size > BOCHS_APERTURE_SIZE) + return grub_error (GRUB_ERR_IO, "Not enough video memory for this mode"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); + + if (found && framebuffer.base == 0) + { + /* FIXME: change framebuffer base */ + return grub_error (GRUB_ERR_IO, "PCI BAR not set"); + } + + /* We can safely discard volatile attribute. */ + framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + BOCHS_APERTURE_SIZE); + framebuffer.mapped = 1; + + if (!state_saved) + { + save_state (&initial_state); + state_saved = 1; + } + + { + vbe_write (0, BOCHS_VBE_ENABLE); + + vbe_write (width, BOCHS_VBE_WIDTH); + vbe_write (height, BOCHS_VBE_HEIGHT); + vbe_write (depth, BOCHS_VBE_BPP); + + vbe_write (1, BOCHS_VBE_ENABLE); + doublebuf_pageflipping_set_page (0); + } + + /* Fill mode info details. */ + framebuffer.mode_info.width = width; + framebuffer.mode_info.height = height; + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.bpp = depth; + framebuffer.mode_info.bytes_per_pixel = bytes_per_pixel; + framebuffer.mode_info.pitch = pitch; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; + + switch (depth) + { + case 4: + case 8: + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + framebuffer.mode_info.number_of_colors = 16; + break; + case 16: + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 11; + framebuffer.mode_info.green_mask_size = 6; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + break; + + case 15: + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 10; + framebuffer.mode_info.green_mask_size = 5; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + break; + + case 32: + framebuffer.mode_info.reserved_mask_size = 8; + framebuffer.mode_info.reserved_field_pos = 24; + /* Fallthrough. */ + + case 24: + framebuffer.mode_info.red_mask_size = 8; + framebuffer.mode_info.red_field_pos = 16; + framebuffer.mode_info.green_mask_size = 8; + framebuffer.mode_info.green_field_pos = 8; + framebuffer.mode_info.blue_mask_size = 8; + framebuffer.mode_info.blue_field_pos = 0; + break; + } + + framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); + + if (BOCHS_APERTURE_SIZE >= 2 * page_size) + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, + doublebuf_pageflipping_set_page, + framebuffer.ptr + page_size); + else + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, 0, 0); + + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_bochs_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + return err; +} + +static struct grub_video_adapter grub_video_bochs_adapter = + { + .name = "Bochs PCI Video Driver", + .id = GRUB_VIDEO_DRIVER_BOCHS, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_bochs_video_init, + .fini = grub_video_bochs_video_fini, + .setup = grub_video_bochs_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_fb_get_info_and_fini, + .set_palette = grub_video_bochs_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_fb_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_bochs) +{ + grub_video_register (&grub_video_bochs_adapter); +} + +GRUB_MOD_FINI(video_bochs) +{ + grub_video_unregister (&grub_video_bochs_adapter); +} diff --git a/grub-core/video/capture.c b/grub-core/video/capture.c new file mode 100644 index 0000000..4d3195e --- /dev/null +++ b/grub-core/video/capture.c @@ -0,0 +1,140 @@ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/mm.h> +#include <grub/misc.h> + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + grub_uint8_t *ptr; +} framebuffer; + +void (*grub_video_capture_refresh_cb) (void); + +static grub_err_t +grub_video_capture_swap_buffers (void) +{ + if (grub_video_capture_refresh_cb) + grub_video_capture_refresh_cb (); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_capture_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_capture_fini (void) +{ + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_capture_adapter = + { + .name = "Render capture", + + .prio = 0, + .id = GRUB_VIDEO_ADAPTER_CAPTURE, + + .fini = grub_video_capture_fini, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = 0, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_capture_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_capture_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +static struct grub_video_adapter *saved; +static struct grub_video_mode_info saved_mode_info; + +grub_err_t +grub_video_capture_start (const struct grub_video_mode_info *mode_info, + struct grub_video_palette_data *palette, + unsigned int palette_size) +{ + grub_err_t err; + grub_memset (&framebuffer, 0, sizeof (framebuffer)); + + grub_video_fb_init (); + + framebuffer.mode_info = *mode_info; + framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); + + framebuffer.ptr = grub_calloc (framebuffer.mode_info.height, framebuffer.mode_info.pitch); + if (!framebuffer.ptr) + return grub_errno; + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.render_target, + &framebuffer.mode_info, + framebuffer.ptr); + if (err) + return err; + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + if (err) + return err; + err = grub_video_fb_set_palette (0, palette_size, palette); + if (err) + return err; + + saved = grub_video_adapter_active; + if (saved) + { + grub_video_get_info (&saved_mode_info); + if (saved->fini) + saved->fini (); + } + grub_video_adapter_active = &grub_video_capture_adapter; + + return GRUB_ERR_NONE; +} + +void * +grub_video_capture_get_framebuffer (void) +{ + return framebuffer.ptr; +} + +void +grub_video_capture_end (void) +{ + grub_video_fb_delete_render_target (framebuffer.render_target); + grub_free (framebuffer.ptr); + grub_video_fb_fini (); + grub_video_adapter_active = saved; + if (saved) + { + if (saved->init) + saved->init (); + if (saved->setup) + saved->setup (saved_mode_info.width, saved_mode_info.height, 0, 0); + } +} diff --git a/grub-core/video/cirrus.c b/grub-core/video/cirrus.c new file mode 100644 index 0000000..e2149e8 --- /dev/null +++ b/grub-core/video/cirrus.c @@ -0,0 +1,520 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static struct +{ + struct grub_video_mode_info mode_info; + grub_size_t page_size; /* The size of a page in bytes. */ + + grub_uint8_t *ptr; + int mapped; + grub_uint32_t base; + grub_pci_device_t dev; +} framebuffer; + +#define CIRRUS_APERTURE_SIZE 0x1000000 + +#define CIRRUS_MAX_WIDTH 0x800 +#define CIRRUS_MAX_HEIGHT 0x800 +#define CIRRUS_MAX_PITCH (0x1ff * GRUB_VGA_CR_PITCH_DIVISOR) + +enum + { + CIRRUS_CR_EXTENDED_DISPLAY = 0x1b, + CIRRUS_CR_EXTENDED_OVERLAY = 0x1d, + CIRRUS_CR_MAX + }; + +#define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK 0x10 +#define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT 4 +#define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1 0x1 +#define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT1 16 +#define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2 0xc +#define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT2 15 + +#define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK 0x80 +#define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_SHIFT 12 + +enum + { + CIRRUS_SR_EXTENDED_MODE = 7, + CIRRUS_SR_MAX + }; + +#define CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE 0xf0 +#define CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT 0x01 +#define CIRRUS_SR_EXTENDED_MODE_8BPP 0x00 +#define CIRRUS_SR_EXTENDED_MODE_16BPP 0x06 +#define CIRRUS_SR_EXTENDED_MODE_24BPP 0x04 +#define CIRRUS_SR_EXTENDED_MODE_32BPP 0x08 + +#define CIRRUS_HIDDEN_DAC_ENABLE_EXT 0x80 +#define CIRRUS_HIDDEN_DAC_ENABLE_ALL 0x40 +#define CIRRUS_HIDDEN_DAC_8BPP 0 +#define CIRRUS_HIDDEN_DAC_15BPP (CIRRUS_HIDDEN_DAC_ENABLE_EXT \ + | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 0) +#define CIRRUS_HIDDEN_DAC_16BPP (CIRRUS_HIDDEN_DAC_ENABLE_EXT \ + | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 1) +#define CIRRUS_HIDDEN_DAC_888COLOR (CIRRUS_HIDDEN_DAC_ENABLE_EXT \ + | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 5) + +static void +write_hidden_dac (grub_uint8_t data) +{ + grub_inb (GRUB_VGA_IO_PALLETTE_WRITE_INDEX); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_outb (data, GRUB_VGA_IO_PIXEL_MASK); +} + +static grub_uint8_t +read_hidden_dac (void) +{ + grub_inb (GRUB_VGA_IO_PALLETTE_WRITE_INDEX); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + grub_inb (GRUB_VGA_IO_PIXEL_MASK); + return grub_inb (GRUB_VGA_IO_PIXEL_MASK); +} + +struct saved_state +{ + grub_uint8_t cr[CIRRUS_CR_MAX]; + grub_uint8_t gr[GRUB_VGA_GR_MAX]; + grub_uint8_t sr[CIRRUS_SR_MAX]; + grub_uint8_t hidden_dac; + /* We need to preserve VGA font and VGA text. */ + grub_uint8_t vram[32 * 4 * 256]; + grub_uint8_t r[256]; + grub_uint8_t g[256]; + grub_uint8_t b[256]; +}; + +static struct saved_state initial_state; +static int state_saved = 0; + +static void +save_state (struct saved_state *st) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE (st->cr); i++) + st->cr[i] = grub_vga_cr_read (i); + for (i = 0; i < ARRAY_SIZE (st->sr); i++) + st->sr[i] = grub_vga_sr_read (i); + for (i = 0; i < ARRAY_SIZE (st->gr); i++) + st->gr[i] = grub_vga_gr_read (i); + for (i = 0; i < 256; i++) + grub_vga_palette_read (i, st->r + i, st->g + i, st->b + i); + + st->hidden_dac = read_hidden_dac (); + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE); + grub_memcpy (st->vram, framebuffer.ptr, sizeof (st->vram)); +} + +static void +restore_state (struct saved_state *st) +{ + unsigned i; + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE); + grub_memcpy (framebuffer.ptr, st->vram, sizeof (st->vram)); + for (i = 0; i < ARRAY_SIZE (st->cr); i++) + grub_vga_cr_write (st->cr[i], i); + for (i = 0; i < ARRAY_SIZE (st->sr); i++) + grub_vga_sr_write (st->sr[i], i); + for (i = 0; i < ARRAY_SIZE (st->gr); i++) + grub_vga_gr_write (st->gr[i], i); + for (i = 0; i < 256; i++) + grub_vga_palette_write (i, st->r[i], st->g[i], st->b[i]); + + write_hidden_dac (st->hidden_dac); +} + +static grub_err_t +grub_video_cirrus_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_cirrus_video_fini (void) +{ + if (framebuffer.mapped) + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + CIRRUS_APERTURE_SIZE); + + if (state_saved) + { + restore_state (&initial_state); + state_saved = 0; + } + + return grub_video_fb_fini (); +} + +static grub_err_t +doublebuf_pageflipping_set_page (int page) +{ + int start = framebuffer.page_size * page / 4; + grub_uint8_t cr_ext, cr_overlay; + + grub_vga_cr_write (start & 0xff, GRUB_VGA_CR_START_ADDR_LOW_REGISTER); + grub_vga_cr_write ((start & 0xff00) >> 8, + GRUB_VGA_CR_START_ADDR_HIGH_REGISTER); + + cr_ext = grub_vga_cr_read (CIRRUS_CR_EXTENDED_DISPLAY); + cr_ext &= ~(CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1 + | CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2); + cr_ext |= ((start >> CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT1) + & CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1); + cr_ext |= ((start >> CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT2) + & CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2); + grub_vga_cr_write (cr_ext, CIRRUS_CR_EXTENDED_DISPLAY); + + cr_overlay = grub_vga_cr_read (CIRRUS_CR_EXTENDED_OVERLAY); + cr_overlay &= ~(CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK); + cr_overlay |= ((start >> CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_SHIFT) + & CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK); + grub_vga_cr_write (cr_overlay, CIRRUS_CR_EXTENDED_OVERLAY); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_cirrus_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + { + unsigned i; + if (start >= 0x100) + return GRUB_ERR_NONE; + if (start + count >= 0x100) + count = 0x100 - start; + + for (i = 0; i < count; i++) + grub_vga_palette_write (start + i, palette_data[i].r, palette_data[i].g, + palette_data[i].b); + } + + /* Then set color to emulated palette. */ + return grub_video_fb_set_palette (start, count, palette_data); +} + +/* Helper for grub_video_cirrus_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != 0x0300 || pciid != 0x00b81013) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK; + if (!framebuffer.base) + return 0; + + *found = 1; + + /* Enable address spaces. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, 0x7); + + framebuffer.dev = dev; + + return 1; +} + +static grub_err_t +grub_video_cirrus_setup (unsigned int width, unsigned int height, + grub_video_mode_type_t mode_type, + grub_video_mode_type_t mode_mask) +{ + int depth; + grub_err_t err; + int found = 0; + int pitch, bytes_per_pixel; + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (width == 0 || height == 0) + { + width = 800; + height = 600; + } + + if (width & (GRUB_VGA_CR_WIDTH_DIVISOR - 1)) + return grub_error (GRUB_ERR_IO, + "screen width must be a multiple of %d", + GRUB_VGA_CR_WIDTH_DIVISOR); + + if (width > CIRRUS_MAX_WIDTH) + return grub_error (GRUB_ERR_IO, + "screen width must be at most %d", CIRRUS_MAX_WIDTH); + + if (height > CIRRUS_MAX_HEIGHT) + return grub_error (GRUB_ERR_IO, + "screen height must be at most %d", CIRRUS_MAX_HEIGHT); + + if (depth == 0 + && !grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, 0)) + depth = 24; + else if (depth == 0) + depth = 8; + + if (depth != 32 && depth != 24 && depth != 16 && depth != 15 && depth != 8) + return grub_error (GRUB_ERR_IO, "only 32, 24, 16, 15 and 8-bit bpp are" + " supported by cirrus video"); + + bytes_per_pixel = (depth + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; + pitch = width * bytes_per_pixel; + + if (pitch > CIRRUS_MAX_PITCH) + return grub_error (GRUB_ERR_IO, + "screen width must be at most %d at bitdepth %d", + CIRRUS_MAX_PITCH / bytes_per_pixel, depth); + + framebuffer.page_size = pitch * height; + + if (framebuffer.page_size > CIRRUS_APERTURE_SIZE) + return grub_error (GRUB_ERR_IO, "Not enough video memory for this mode"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); + + if (found && framebuffer.base == 0) + { + /* FIXME: change framebuffer base */ + return grub_error (GRUB_ERR_IO, "PCI BAR not set"); + } + + /* We can safely discard volatile attribute. */ + framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + CIRRUS_APERTURE_SIZE); + framebuffer.mapped = 1; + + if (!state_saved) + { + save_state (&initial_state); + state_saved = 1; + } + + { + struct grub_video_hw_config config = { + .pitch = pitch / GRUB_VGA_CR_PITCH_DIVISOR, + .line_compare = 0x3ff, + .vdisplay_end = height - 1, + .horizontal_end = width / GRUB_VGA_CR_WIDTH_DIVISOR + }; + grub_uint8_t sr_ext = 0, hidden_dac = 0; + + grub_vga_set_geometry (&config, grub_vga_cr_write); + + grub_vga_gr_write (GRUB_VGA_GR_MODE_256_COLOR | GRUB_VGA_GR_MODE_READ_MODE1, + GRUB_VGA_GR_MODE); + grub_vga_gr_write (GRUB_VGA_GR_GR6_GRAPHICS_MODE, GRUB_VGA_GR_GR6); + + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL, GRUB_VGA_SR_MEMORY_MODE); + + grub_vga_cr_write ((config.pitch >> CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT) + & CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK, + CIRRUS_CR_EXTENDED_DISPLAY); + + grub_vga_cr_write (GRUB_VGA_CR_MODE_TIMING_ENABLE + | GRUB_VGA_CR_MODE_BYTE_MODE + | GRUB_VGA_CR_MODE_NO_HERCULES | GRUB_VGA_CR_MODE_NO_CGA, + GRUB_VGA_CR_MODE); + + doublebuf_pageflipping_set_page (0); + + sr_ext = CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE + | CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT; + switch (depth) + { + /* FIXME: support 8-bit grayscale and 8-bit RGB. */ + case 32: + hidden_dac = CIRRUS_HIDDEN_DAC_888COLOR; + sr_ext |= CIRRUS_SR_EXTENDED_MODE_32BPP; + break; + case 24: + hidden_dac = CIRRUS_HIDDEN_DAC_888COLOR; + sr_ext |= CIRRUS_SR_EXTENDED_MODE_24BPP; + break; + case 16: + hidden_dac = CIRRUS_HIDDEN_DAC_16BPP; + sr_ext |= CIRRUS_SR_EXTENDED_MODE_16BPP; + break; + case 15: + hidden_dac = CIRRUS_HIDDEN_DAC_15BPP; + sr_ext |= CIRRUS_SR_EXTENDED_MODE_16BPP; + break; + case 8: + hidden_dac = CIRRUS_HIDDEN_DAC_8BPP; + sr_ext |= CIRRUS_SR_EXTENDED_MODE_8BPP; + break; + } + grub_vga_sr_write (sr_ext, CIRRUS_SR_EXTENDED_MODE); + write_hidden_dac (hidden_dac); + } + + /* Fill mode info details. */ + framebuffer.mode_info.width = width; + framebuffer.mode_info.height = height; + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.bpp = depth; + framebuffer.mode_info.bytes_per_pixel = bytes_per_pixel; + framebuffer.mode_info.pitch = pitch; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; + + switch (depth) + { + case 8: + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + framebuffer.mode_info.number_of_colors = 16; + break; + case 16: + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 11; + framebuffer.mode_info.green_mask_size = 6; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + break; + + case 15: + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 10; + framebuffer.mode_info.green_mask_size = 5; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + break; + + case 32: + framebuffer.mode_info.reserved_mask_size = 8; + framebuffer.mode_info.reserved_field_pos = 24; + /* Fallthrough. */ + + case 24: + framebuffer.mode_info.red_mask_size = 8; + framebuffer.mode_info.red_field_pos = 16; + framebuffer.mode_info.green_mask_size = 8; + framebuffer.mode_info.green_field_pos = 8; + framebuffer.mode_info.blue_mask_size = 8; + framebuffer.mode_info.blue_field_pos = 0; + break; + } + + framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); + + if (CIRRUS_APERTURE_SIZE >= 2 * framebuffer.page_size) + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, + doublebuf_pageflipping_set_page, + framebuffer.ptr + framebuffer.page_size); + else + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, 0, 0); + + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_cirrus_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + return err; +} + +static struct grub_video_adapter grub_video_cirrus_adapter = + { + .name = "Cirrus CLGD 5446 PCI Video Driver", + .id = GRUB_VIDEO_DRIVER_CIRRUS, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_cirrus_video_init, + .fini = grub_video_cirrus_video_fini, + .setup = grub_video_cirrus_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_fb_get_info_and_fini, + .set_palette = grub_video_cirrus_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_fb_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_cirrus) +{ + grub_video_register (&grub_video_cirrus_adapter); +} + +GRUB_MOD_FINI(video_cirrus) +{ + grub_video_unregister (&grub_video_cirrus_adapter); +} diff --git a/grub-core/video/colors.c b/grub-core/video/colors.c new file mode 100644 index 0000000..485ebb4 --- /dev/null +++ b/grub-core/video/colors.c @@ -0,0 +1,333 @@ +/* named_colors.c - Named color values. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/types.h> +#include <grub/gui.h> +#include <grub/gui_string_util.h> +#include <grub/misc.h> +#include <grub/dl.h> +#include <grub/i18n.h> +#include <grub/color.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct named_color +{ + const char *name; + grub_video_rgba_color_t color; +}; + +/* + Named color list generated from the list of SVG color keywords from + <http://www.w3.org/TR/css3-color/#svg-color>, + processed through the following Perl command: + perl -ne 'chomp;split;print "{ \"$_[0]\", RGB_COLOR($_[2]) },\n"' + */ + +#define RGB_COLOR(r,g,b) {.red = r, .green = g, .blue = b, .alpha = 255} + +static struct named_color named_colors[] = +{ + { "aliceblue", RGB_COLOR(240,248,255) }, + { "antiquewhite", RGB_COLOR(250,235,215) }, + { "aqua", RGB_COLOR(0,255,255) }, + { "aquamarine", RGB_COLOR(127,255,212) }, + { "azure", RGB_COLOR(240,255,255) }, + { "beige", RGB_COLOR(245,245,220) }, + { "bisque", RGB_COLOR(255,228,196) }, + { "black", RGB_COLOR(0,0,0) }, + { "blanchedalmond", RGB_COLOR(255,235,205) }, + { "blue", RGB_COLOR(0,0,255) }, + { "blueviolet", RGB_COLOR(138,43,226) }, + { "brown", RGB_COLOR(165,42,42) }, + { "burlywood", RGB_COLOR(222,184,135) }, + { "cadetblue", RGB_COLOR(95,158,160) }, + { "chartreuse", RGB_COLOR(127,255,0) }, + { "chocolate", RGB_COLOR(210,105,30) }, + { "coral", RGB_COLOR(255,127,80) }, + { "cornflowerblue", RGB_COLOR(100,149,237) }, + { "cornsilk", RGB_COLOR(255,248,220) }, + { "crimson", RGB_COLOR(220,20,60) }, + { "cyan", RGB_COLOR(0,255,255) }, + { "darkblue", RGB_COLOR(0,0,139) }, + { "darkcyan", RGB_COLOR(0,139,139) }, + { "darkgoldenrod", RGB_COLOR(184,134,11) }, + { "darkgray", RGB_COLOR(169,169,169) }, + { "darkgreen", RGB_COLOR(0,100,0) }, + { "darkgrey", RGB_COLOR(169,169,169) }, + { "darkkhaki", RGB_COLOR(189,183,107) }, + { "darkmagenta", RGB_COLOR(139,0,139) }, + { "darkolivegreen", RGB_COLOR(85,107,47) }, + { "darkorange", RGB_COLOR(255,140,0) }, + { "darkorchid", RGB_COLOR(153,50,204) }, + { "darkred", RGB_COLOR(139,0,0) }, + { "darksalmon", RGB_COLOR(233,150,122) }, + { "darkseagreen", RGB_COLOR(143,188,143) }, + { "darkslateblue", RGB_COLOR(72,61,139) }, + { "darkslategray", RGB_COLOR(47,79,79) }, + { "darkslategrey", RGB_COLOR(47,79,79) }, + { "darkturquoise", RGB_COLOR(0,206,209) }, + { "darkviolet", RGB_COLOR(148,0,211) }, + { "deeppink", RGB_COLOR(255,20,147) }, + { "deepskyblue", RGB_COLOR(0,191,255) }, + { "dimgray", RGB_COLOR(105,105,105) }, + { "dimgrey", RGB_COLOR(105,105,105) }, + { "dodgerblue", RGB_COLOR(30,144,255) }, + { "firebrick", RGB_COLOR(178,34,34) }, + { "floralwhite", RGB_COLOR(255,250,240) }, + { "forestgreen", RGB_COLOR(34,139,34) }, + { "fuchsia", RGB_COLOR(255,0,255) }, + { "gainsboro", RGB_COLOR(220,220,220) }, + { "ghostwhite", RGB_COLOR(248,248,255) }, + { "gold", RGB_COLOR(255,215,0) }, + { "goldenrod", RGB_COLOR(218,165,32) }, + { "gray", RGB_COLOR(128,128,128) }, + { "green", RGB_COLOR(0,128,0) }, + { "greenyellow", RGB_COLOR(173,255,47) }, + { "grey", RGB_COLOR(128,128,128) }, + { "honeydew", RGB_COLOR(240,255,240) }, + { "hotpink", RGB_COLOR(255,105,180) }, + { "indianred", RGB_COLOR(205,92,92) }, + { "indigo", RGB_COLOR(75,0,130) }, + { "ivory", RGB_COLOR(255,255,240) }, + { "khaki", RGB_COLOR(240,230,140) }, + { "lavender", RGB_COLOR(230,230,250) }, + { "lavenderblush", RGB_COLOR(255,240,245) }, + { "lawngreen", RGB_COLOR(124,252,0) }, + { "lemonchiffon", RGB_COLOR(255,250,205) }, + { "lightblue", RGB_COLOR(173,216,230) }, + { "lightcoral", RGB_COLOR(240,128,128) }, + { "lightcyan", RGB_COLOR(224,255,255) }, + { "lightgoldenrodyellow", RGB_COLOR(250,250,210) }, + { "lightgray", RGB_COLOR(211,211,211) }, + { "lightgreen", RGB_COLOR(144,238,144) }, + { "lightgrey", RGB_COLOR(211,211,211) }, + { "lightpink", RGB_COLOR(255,182,193) }, + { "lightsalmon", RGB_COLOR(255,160,122) }, + { "lightseagreen", RGB_COLOR(32,178,170) }, + { "lightskyblue", RGB_COLOR(135,206,250) }, + { "lightslategray", RGB_COLOR(119,136,153) }, + { "lightslategrey", RGB_COLOR(119,136,153) }, + { "lightsteelblue", RGB_COLOR(176,196,222) }, + { "lightyellow", RGB_COLOR(255,255,224) }, + { "lime", RGB_COLOR(0,255,0) }, + { "limegreen", RGB_COLOR(50,205,50) }, + { "linen", RGB_COLOR(250,240,230) }, + { "magenta", RGB_COLOR(255,0,255) }, + { "maroon", RGB_COLOR(128,0,0) }, + { "mediumaquamarine", RGB_COLOR(102,205,170) }, + { "mediumblue", RGB_COLOR(0,0,205) }, + { "mediumorchid", RGB_COLOR(186,85,211) }, + { "mediumpurple", RGB_COLOR(147,112,219) }, + { "mediumseagreen", RGB_COLOR(60,179,113) }, + { "mediumslateblue", RGB_COLOR(123,104,238) }, + { "mediumspringgreen", RGB_COLOR(0,250,154) }, + { "mediumturquoise", RGB_COLOR(72,209,204) }, + { "mediumvioletred", RGB_COLOR(199,21,133) }, + { "midnightblue", RGB_COLOR(25,25,112) }, + { "mintcream", RGB_COLOR(245,255,250) }, + { "mistyrose", RGB_COLOR(255,228,225) }, + { "moccasin", RGB_COLOR(255,228,181) }, + { "navajowhite", RGB_COLOR(255,222,173) }, + { "navy", RGB_COLOR(0,0,128) }, + { "oldlace", RGB_COLOR(253,245,230) }, + { "olive", RGB_COLOR(128,128,0) }, + { "olivedrab", RGB_COLOR(107,142,35) }, + { "orange", RGB_COLOR(255,165,0) }, + { "orangered", RGB_COLOR(255,69,0) }, + { "orchid", RGB_COLOR(218,112,214) }, + { "palegoldenrod", RGB_COLOR(238,232,170) }, + { "palegreen", RGB_COLOR(152,251,152) }, + { "paleturquoise", RGB_COLOR(175,238,238) }, + { "palevioletred", RGB_COLOR(219,112,147) }, + { "papayawhip", RGB_COLOR(255,239,213) }, + { "peachpuff", RGB_COLOR(255,218,185) }, + { "peru", RGB_COLOR(205,133,63) }, + { "pink", RGB_COLOR(255,192,203) }, + { "plum", RGB_COLOR(221,160,221) }, + { "powderblue", RGB_COLOR(176,224,230) }, + { "purple", RGB_COLOR(128,0,128) }, + { "red", RGB_COLOR(255,0,0) }, + { "rosybrown", RGB_COLOR(188,143,143) }, + { "royalblue", RGB_COLOR(65,105,225) }, + { "saddlebrown", RGB_COLOR(139,69,19) }, + { "salmon", RGB_COLOR(250,128,114) }, + { "sandybrown", RGB_COLOR(244,164,96) }, + { "seagreen", RGB_COLOR(46,139,87) }, + { "seashell", RGB_COLOR(255,245,238) }, + { "sienna", RGB_COLOR(160,82,45) }, + { "silver", RGB_COLOR(192,192,192) }, + { "skyblue", RGB_COLOR(135,206,235) }, + { "slateblue", RGB_COLOR(106,90,205) }, + { "slategray", RGB_COLOR(112,128,144) }, + { "slategrey", RGB_COLOR(112,128,144) }, + { "snow", RGB_COLOR(255,250,250) }, + { "springgreen", RGB_COLOR(0,255,127) }, + { "steelblue", RGB_COLOR(70,130,180) }, + { "tan", RGB_COLOR(210,180,140) }, + { "teal", RGB_COLOR(0,128,128) }, + { "thistle", RGB_COLOR(216,191,216) }, + { "tomato", RGB_COLOR(255,99,71) }, + { "turquoise", RGB_COLOR(64,224,208) }, + { "violet", RGB_COLOR(238,130,238) }, + { "wheat", RGB_COLOR(245,222,179) }, + { "white", RGB_COLOR(255,255,255) }, + { "whitesmoke", RGB_COLOR(245,245,245) }, + { "yellow", RGB_COLOR(255,255,0) }, + { "yellowgreen", RGB_COLOR(154,205,50) }, + { 0, { 0, 0, 0, 0 } } /* Terminator. */ +}; + +/* Get the color named NAME. If the color was found, returns 1 and + stores the color into *COLOR. If the color was not found, returns 0 and + does not modify *COLOR. */ +int +grub_video_get_named_color (const char *name, + grub_video_rgba_color_t *color) +{ + int i; + for (i = 0; named_colors[i].name; i++) + { + if (grub_strcmp (named_colors[i].name, name) == 0) + { + *color = named_colors[i].color; + return 1; + } + } + return 0; +} + +static int +parse_hex_color_component (const char *s, unsigned start, unsigned end) +{ + unsigned len; + char buf[3]; + + len = end - start; + /* Check the limits so we don't overrun the buffer. */ + if (len < 1 || len > 2) + return 0; + + if (len == 1) + { + buf[0] = s[start]; /* Get the first and only hex digit. */ + buf[1] = buf[0]; /* Duplicate the hex digit. */ + } + else if (len == 2) + { + buf[0] = s[start]; + buf[1] = s[start + 1]; + } + + buf[2] = '\0'; + + return grub_strtoul (buf, 0, 16); +} + +/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA", + "#RRGGBB", or "#RRGGBBAA". */ +grub_err_t +grub_video_parse_color (const char *s, grub_video_rgba_color_t *color) +{ + grub_video_rgba_color_t c; + const char *s0; + + /* Skip whitespace. */ + while (*s && grub_isspace (*s)) + s++; + + s0 = s; + + if (*s == '#') + { + /* HTML-style. Number if hex digits: + [6] #RRGGBB [3] #RGB + [8] #RRGGBBAA [4] #RGBA */ + + s++; /* Skip the '#'. */ + /* Count the hexits to determine the format. */ + int hexits = 0; + const char *end = s; + while (grub_isxdigit (*end)) + { + end++; + hexits++; + } + + /* Parse the color components based on the format. */ + if (hexits == 3 || hexits == 4) + { + c.red = parse_hex_color_component (s, 0, 1); + c.green = parse_hex_color_component (s, 1, 2); + c.blue = parse_hex_color_component (s, 2, 3); + if (hexits == 4) + c.alpha = parse_hex_color_component (s, 3, 4); + else + c.alpha = 255; + } + else if (hexits == 6 || hexits == 8) + { + c.red = parse_hex_color_component (s, 0, 2); + c.green = parse_hex_color_component (s, 2, 4); + c.blue = parse_hex_color_component (s, 4, 6); + if (hexits == 8) + c.alpha = parse_hex_color_component (s, 6, 8); + else + c.alpha = 255; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid color specification `%s'"), s0); + } + else if (grub_isdigit (*s)) + { + /* Comma separated decimal values. */ + c.red = grub_strtoul (s, 0, 0); + s = grub_strchr (s, ','); + if (!s) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid color specification `%s'"), s0); + s++; + c.green = grub_strtoul (s, 0, 0); + s = grub_strchr (s, ','); + if (!s) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid color specification `%s'"), s0); + s++; + c.blue = grub_strtoul (s, 0, 0); + s = grub_strchr (s, ','); + if (!s) + c.alpha = 255; + else + { + s++; + c.alpha = grub_strtoul (s, 0, 0); + } + } + else + { + if (! grub_video_get_named_color (s, &c)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid color specification `%s'"), s0); + } + + if (grub_errno == GRUB_ERR_NONE) + *color = c; + return grub_errno; +} diff --git a/grub-core/video/coreboot/cbfb.c b/grub-core/video/coreboot/cbfb.c new file mode 100644 index 0000000..9af81fa --- /dev/null +++ b/grub-core/video/coreboot/cbfb.c @@ -0,0 +1,190 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/coreboot/lbio.h> +#include <grub/machine/console.h> + +struct grub_linuxbios_table_framebuffer *grub_video_coreboot_fbtable; + +static struct +{ + struct grub_video_mode_info mode_info; + grub_uint8_t *ptr; +} framebuffer; + +static grub_err_t +grub_video_cbfb_init (void) +{ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_cbfb_fill_mode_info (struct grub_video_mode_info *out) +{ + grub_memset (out, 0, sizeof (*out)); + + out->width = grub_video_coreboot_fbtable->width; + out->height = grub_video_coreboot_fbtable->height; + out->pitch = grub_video_coreboot_fbtable->pitch; + + out->red_field_pos = grub_video_coreboot_fbtable->red_field_pos; + out->red_mask_size = grub_video_coreboot_fbtable->red_mask_size; + out->green_field_pos = grub_video_coreboot_fbtable->green_field_pos; + out->green_mask_size = grub_video_coreboot_fbtable->green_mask_size; + out->blue_field_pos = grub_video_coreboot_fbtable->blue_field_pos; + out->blue_mask_size = grub_video_coreboot_fbtable->blue_mask_size; + out->reserved_field_pos = grub_video_coreboot_fbtable->reserved_field_pos; + out->reserved_mask_size = grub_video_coreboot_fbtable->reserved_mask_size; + + out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + out->bpp = grub_video_coreboot_fbtable->bpp; + out->bytes_per_pixel = (grub_video_coreboot_fbtable->bpp + 7) / 8; + out->number_of_colors = 256; + + out->blit_format = grub_video_get_blit_format (out); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_cbfb_setup (unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused)), + unsigned int mode_mask __attribute__ ((unused))) +{ + grub_err_t err; + + if (!grub_video_coreboot_fbtable) + return grub_error (GRUB_ERR_IO, "Couldn't find display device."); + + if (!((width == grub_video_coreboot_fbtable->width && height == grub_video_coreboot_fbtable->height) + || (width == 0 && height == 0))) + return grub_error (GRUB_ERR_IO, "can't set mode %dx%d", width, height); + + err = grub_video_cbfb_fill_mode_info (&framebuffer.mode_info); + if (err) + { + grub_dprintf ("video", "CBFB: couldn't fill mode info\n"); + return err; + } + + framebuffer.ptr = (void *) (grub_addr_t) grub_video_coreboot_fbtable->lfb; + + grub_dprintf ("video", "CBFB: initialising FB @ %p %dx%dx%d\n", + framebuffer.ptr, framebuffer.mode_info.width, + framebuffer.mode_info.height, framebuffer.mode_info.bpp); + + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, NULL, NULL); + if (err) + return err; + + grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + return err; +} + +static grub_err_t +grub_video_cbfb_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_cbfb_adapter = + { + .name = "Coreboot video driver", + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE_DIRTY, + .id = GRUB_VIDEO_DRIVER_COREBOOT, + + .init = grub_video_cbfb_init, + .fini = grub_video_fb_fini, + .setup = grub_video_cbfb_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_cbfb_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_fb_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +static int +iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, + void *data __attribute__ ((unused))) +{ + if (table_item->tag != GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER) + return 0; + grub_video_coreboot_fbtable = (struct grub_linuxbios_table_framebuffer *) (table_item + 1); + return 1; +} + +void +grub_video_coreboot_fb_early_init (void) +{ + grub_linuxbios_table_iterate (iterate_linuxbios_table, 0); +} + +void +grub_video_coreboot_fb_late_init (void) +{ + if (grub_video_coreboot_fbtable) + grub_video_register (&grub_video_cbfb_adapter); +} + +void +grub_video_coreboot_fb_fini (void) +{ + if (grub_video_coreboot_fbtable) + grub_video_unregister (&grub_video_cbfb_adapter); +} diff --git a/grub-core/video/efi_gop.c b/grub-core/video/efi_gop.c new file mode 100644 index 0000000..b7590dc --- /dev/null +++ b/grub-core/video/efi_gop.c @@ -0,0 +1,621 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> +#include <grub/efi/edid.h> +#include <grub/efi/graphics_output.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID; +static grub_efi_guid_t active_edid_guid = GRUB_EFI_EDID_ACTIVE_GUID; +static grub_efi_guid_t discovered_edid_guid = GRUB_EFI_EDID_DISCOVERED_GUID; +static grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; +static struct grub_efi_gop *gop; +static unsigned old_mode; +static int restore_needed; +static grub_efi_handle_t gop_handle; + +static int +grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info, void *hook_arg), void *hook_arg); + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + grub_uint8_t *ptr; + grub_uint8_t *offscreen; +} framebuffer; + +static int +check_protocol_hook (const struct grub_video_mode_info *info __attribute__ ((unused)), void *hook_arg) +{ + int *have_usable_mode = hook_arg; + *have_usable_mode = 1; + return 1; +} + + +static int +check_protocol (void) +{ + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles, i; + int have_usable_mode = 0; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, + &graphics_output_guid, NULL, &num_handles); + if (!handles || num_handles == 0) + { + grub_dprintf ("video", "GOP: no handles\n"); + return 0; + } + + for (i = 0; i < num_handles; i++) + { + gop_handle = handles[i]; + gop = grub_efi_open_protocol (gop_handle, &graphics_output_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + grub_video_gop_iterate (check_protocol_hook, &have_usable_mode); + if (have_usable_mode) + { + grub_dprintf ("video", "GOP: found usable mode\n"); + grub_free (handles); + return 1; + } + } + + gop = 0; + gop_handle = 0; + + grub_dprintf ("video", "GOP: no usable mode\n"); + + return 0; +} + +static grub_err_t +grub_video_gop_init (void) +{ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_gop_fini (void) +{ + if (restore_needed) + { + efi_call_2 (gop->set_mode, gop, old_mode); + restore_needed = 0; + } + grub_free (framebuffer.offscreen); + framebuffer.offscreen = 0; + return grub_video_fb_fini (); +} + +static int +grub_video_gop_get_bpp (struct grub_efi_gop_mode_info *in) +{ + grub_uint32_t total_mask; + int i; + switch (in->pixel_format) + { + case GRUB_EFI_GOT_BGRA8: + case GRUB_EFI_GOT_RGBA8: + case GRUB_EFI_GOT_BLT_ONLY: + return 32; + + case GRUB_EFI_GOT_BITMASK: + /* Check overlaps. */ + if ((in->pixel_bitmask.r & in->pixel_bitmask.g) + || (in->pixel_bitmask.r & in->pixel_bitmask.b) + || (in->pixel_bitmask.g & in->pixel_bitmask.b) + || (in->pixel_bitmask.r & in->pixel_bitmask.a) + || (in->pixel_bitmask.g & in->pixel_bitmask.a) + || (in->pixel_bitmask.b & in->pixel_bitmask.a)) + return 0; + + total_mask = in->pixel_bitmask.r | in->pixel_bitmask.g + | in->pixel_bitmask.b | in->pixel_bitmask.a; + + for (i = 31; i >= 0; i--) + if (total_mask & (1 << i)) + return i + 1; + + /* Fall through. */ + default: + return 0; + } +} + +static void +grub_video_gop_get_bitmask (grub_uint32_t mask, unsigned int *mask_size, + unsigned int *field_pos) +{ + int i; + int last_p; + for (i = 31; i >= 0; i--) + if (mask & (1 << i)) + break; + if (i == -1) + { + *mask_size = *field_pos = 0; + return; + } + last_p = i; + for (; i >= 0; i--) + if (!(mask & (1 << i))) + break; + *field_pos = i + 1; + *mask_size = last_p - *field_pos + 1; +} + +static grub_err_t +grub_video_gop_fill_real_mode_info (unsigned mode, + struct grub_efi_gop_mode_info *in, + struct grub_video_mode_info *out) +{ + out->mode_number = mode; + out->number_of_colors = 256; + out->width = in->width; + out->height = in->height; + out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + out->bpp = grub_video_gop_get_bpp (in); + out->bytes_per_pixel = out->bpp >> 3; + if (!out->bpp) + return grub_error (GRUB_ERR_IO, "unsupported video mode"); + out->pitch = in->pixels_per_scanline * out->bytes_per_pixel; + + switch (in->pixel_format) + { + case GRUB_EFI_GOT_RGBA8: + case GRUB_EFI_GOT_BLT_ONLY: + out->red_mask_size = 8; + out->red_field_pos = 0; + out->green_mask_size = 8; + out->green_field_pos = 8; + out->blue_mask_size = 8; + out->blue_field_pos = 16; + out->reserved_mask_size = 8; + out->reserved_field_pos = 24; + break; + + case GRUB_EFI_GOT_BGRA8: + out->red_mask_size = 8; + out->red_field_pos = 16; + out->green_mask_size = 8; + out->green_field_pos = 8; + out->blue_mask_size = 8; + out->blue_field_pos = 0; + out->reserved_mask_size = 8; + out->reserved_field_pos = 24; + break; + + case GRUB_EFI_GOT_BITMASK: + grub_video_gop_get_bitmask (in->pixel_bitmask.r, &out->red_mask_size, + &out->red_field_pos); + grub_video_gop_get_bitmask (in->pixel_bitmask.g, &out->green_mask_size, + &out->green_field_pos); + grub_video_gop_get_bitmask (in->pixel_bitmask.b, &out->blue_mask_size, + &out->blue_field_pos); + grub_video_gop_get_bitmask (in->pixel_bitmask.a, &out->reserved_mask_size, + &out->reserved_field_pos); + break; + + default: + return grub_error (GRUB_ERR_IO, "unsupported video mode"); + } + + out->blit_format = grub_video_get_blit_format (out); + return GRUB_ERR_NONE; +} + +static void +grub_video_gop_fill_mode_info (unsigned mode, + struct grub_efi_gop_mode_info *in, + struct grub_video_mode_info *out) +{ + out->mode_number = mode; + out->number_of_colors = 256; + out->width = in->width; + out->height = in->height; + out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + out->bytes_per_pixel = sizeof (struct grub_efi_gop_blt_pixel); + out->bpp = out->bytes_per_pixel << 3; + out->pitch = in->width * out->bytes_per_pixel; + out->red_mask_size = 8; + out->red_field_pos = 16; + out->green_mask_size = 8; + out->green_field_pos = 8; + out->blue_mask_size = 8; + out->blue_field_pos = 0; + out->reserved_mask_size = 8; + out->reserved_field_pos = 24; + + out->blit_format = GRUB_VIDEO_BLIT_FORMAT_BGRA_8888; + out->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); +} + +static int +grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info, void *hook_arg), void *hook_arg) +{ + unsigned mode; + + for (mode = 0; mode < gop->mode->max_mode; mode++) + { + grub_efi_uintn_t size; + grub_efi_status_t status; + struct grub_efi_gop_mode_info *info = NULL; + struct grub_video_mode_info mode_info; + + status = efi_call_4 (gop->query_mode, gop, mode, &size, &info); + + if (status) + { + info = 0; + continue; + } + + grub_video_gop_fill_mode_info (mode, info, &mode_info); + if (hook (&mode_info, hook_arg)) + return 1; + } + return 0; +} + +static grub_err_t +grub_video_gop_get_edid (struct grub_video_edid_info *edid_info) +{ + struct grub_efi_active_edid *edid; + grub_size_t copy_size; + + grub_memset (edid_info, 0, sizeof (*edid_info)); + + edid = grub_efi_open_protocol (gop_handle, &active_edid_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!edid || edid->size_of_edid == 0) + edid = grub_efi_open_protocol (gop_handle, &discovered_edid_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!edid || edid->size_of_edid == 0) + { + char edidname[] = "agp-internal-edid"; + grub_size_t datasize; + grub_uint8_t *data; + grub_efi_get_variable (edidname, &efi_var_guid, &datasize, (void **) &data); + if (data && datasize > 16) + { + copy_size = datasize - 16; + if (copy_size > sizeof (*edid_info)) + copy_size = sizeof (*edid_info); + grub_memcpy (edid_info, data + 16, copy_size); + grub_free (data); + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available"); + } + + copy_size = edid->size_of_edid; + if (copy_size > sizeof (*edid_info)) + copy_size = sizeof (*edid_info); + grub_memcpy (edid_info, edid->edid, copy_size); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_gop_get_preferred_mode (unsigned int *width, unsigned int *height) +{ + struct grub_video_edid_info edid_info; + grub_err_t err; + + err = grub_video_gop_get_edid (&edid_info); + if (err) + return err; + err = grub_video_edid_checksum (&edid_info); + if (err) + return err; + err = grub_video_edid_preferred_mode (&edid_info, width, height); + if (err) + return err; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_gop_setup (unsigned int width, unsigned int height, + unsigned int mode_type, + unsigned int mode_mask __attribute__ ((unused))) +{ + unsigned int depth; + struct grub_efi_gop_mode_info *info = NULL; + unsigned best_mode = 0; + grub_err_t err; + unsigned bpp; + int found = 0; + unsigned long long best_volume = 0; + unsigned int preferred_width = 0, preferred_height = 0; + grub_uint8_t *buffer; + + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (width == 0 && height == 0) + { + err = grub_gop_get_preferred_mode (&preferred_width, &preferred_height); + if (err || preferred_width >= 4096 || preferred_height >= 4096) + { + preferred_width = 800; + preferred_height = 600; + grub_errno = GRUB_ERR_NONE; + } + } + + /* Keep current mode if possible. */ + if (gop->mode->info) + { + bpp = grub_video_gop_get_bpp (gop->mode->info); + if (bpp && ((width == gop->mode->info->width + && height == gop->mode->info->height) + || (width == 0 && height == 0)) + && (depth == bpp || depth == 0)) + { + grub_dprintf ("video", "GOP: keeping mode %d\n", gop->mode->mode); + best_mode = gop->mode->mode; + found = 1; + } + } + + if (!found) + { + unsigned mode; + grub_dprintf ("video", "GOP: %d modes detected\n", gop->mode->max_mode); + for (mode = 0; mode < gop->mode->max_mode; mode++) + { + grub_efi_uintn_t size; + grub_efi_status_t status; + + status = efi_call_4 (gop->query_mode, gop, mode, &size, &info); + if (status) + { + info = 0; + continue; + } + + grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width, + info->height); + + if (preferred_width && (info->width > preferred_width + || info->height > preferred_height)) + { + grub_dprintf ("video", "GOP: mode %d: too large\n", mode); + continue; + } + + bpp = grub_video_gop_get_bpp (info); + if (!bpp) + { + grub_dprintf ("video", "GOP: mode %d: incompatible pixel mode\n", + mode); + continue; + } + + grub_dprintf ("video", "GOP: mode %d: depth %d\n", mode, bpp); + + if (!(((info->width == width && info->height == height) + || (width == 0 && height == 0)) + && (bpp == depth || depth == 0))) + { + grub_dprintf ("video", "GOP: mode %d: rejected\n", mode); + continue; + } + + if (best_volume < ((unsigned long long) info->width) + * ((unsigned long long) info->height) + * ((unsigned long long) bpp)) + { + best_volume = ((unsigned long long) info->width) + * ((unsigned long long) info->height) + * ((unsigned long long) bpp); + best_mode = mode; + } + found = 1; + } + } + + if (!found) + { + grub_dprintf ("video", "GOP: no mode found\n"); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); + } + + if (best_mode != gop->mode->mode) + { + if (!restore_needed) + { + old_mode = gop->mode->mode; + restore_needed = 1; + } + efi_call_2 (gop->set_mode, gop, best_mode); + } + + info = gop->mode->info; + + grub_video_gop_fill_mode_info (gop->mode->mode, info, + &framebuffer.mode_info); + + framebuffer.ptr = (void *) (grub_addr_t) gop->mode->fb_base; + framebuffer.offscreen + = grub_malloc (framebuffer.mode_info.height + * framebuffer.mode_info.width + * sizeof (struct grub_efi_gop_blt_pixel)); + + buffer = framebuffer.offscreen; + + if (!buffer) + { + grub_dprintf ("video", "GOP: couldn't allocate shadow\n"); + grub_errno = 0; + grub_video_gop_fill_mode_info (gop->mode->mode, info, + &framebuffer.mode_info); + buffer = framebuffer.ptr; + } + + grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n", + framebuffer.ptr, framebuffer.mode_info.width, + framebuffer.mode_info.height, framebuffer.mode_info.bpp); + + err = grub_video_fb_create_render_target_from_pointer + (&framebuffer.render_target, &framebuffer.mode_info, buffer); + + if (err) + { + grub_dprintf ("video", "GOP: Couldn't create FB target\n"); + return err; + } + + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + + if (err) + { + grub_dprintf ("video", "GOP: Couldn't set FB target\n"); + return err; + } + + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + if (err) + grub_dprintf ("video", "GOP: Couldn't set palette\n"); + else + grub_dprintf ("video", "GOP: Success\n"); + + return err; +} + +static grub_err_t +grub_video_gop_swap_buffers (void) +{ + if (framebuffer.offscreen) + { + efi_call_10 (gop->blt, gop, framebuffer.offscreen, + GRUB_EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, 0, + framebuffer.mode_info.width, framebuffer.mode_info.height, + framebuffer.mode_info.width * 4); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_gop_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_gop_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_err_t err; + + err = grub_video_gop_fill_real_mode_info (gop->mode->mode, gop->mode->info, + mode_info); + if (err) + { + grub_dprintf ("video", "GOP: couldn't fill mode info\n"); + return err; + } + + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + grub_free (framebuffer.offscreen); + framebuffer.offscreen = 0; + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_gop_adapter = + { + .name = "EFI GOP driver", + .id = GRUB_VIDEO_DRIVER_EFI_GOP, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE, + + .init = grub_video_gop_init, + .fini = grub_video_gop_fini, + .setup = grub_video_gop_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_gop_get_info_and_fini, + .get_edid = grub_video_gop_get_edid, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_gop_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_gop_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + .iterate = grub_video_gop_iterate, + + .next = 0 + }; + +GRUB_MOD_INIT(efi_gop) +{ + if (check_protocol ()) + grub_video_register (&grub_video_gop_adapter); +} + +GRUB_MOD_FINI(efi_gop) +{ + if (restore_needed) + { + efi_call_2 (gop->set_mode, gop, old_mode); + restore_needed = 0; + } + if (gop) + grub_video_unregister (&grub_video_gop_adapter); +} diff --git a/grub-core/video/efi_uga.c b/grub-core/video/efi_uga.c new file mode 100644 index 0000000..e74d6c2 --- /dev/null +++ b/grub-core/video/efi_uga.c @@ -0,0 +1,369 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> +#include <grub/efi/uga_draw.h> +#include <grub/pci.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID; +static struct grub_efi_uga_draw_protocol *uga; +static grub_uint64_t uga_fb; +static grub_uint32_t uga_pitch; + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + grub_uint8_t *ptr; +} framebuffer; + +#define RGB_MASK 0xffffff +#define RGB_MAGIC 0x121314 +#define LINE_MIN 800 +#define LINE_MAX 4096 +#define FBTEST_STEP (0x10000 >> 2) +#define FBTEST_COUNT 8 + +static int +find_line_len (grub_uint64_t *fb_base, grub_uint32_t *line_len) +{ + grub_uint32_t *base = (grub_uint32_t *) (grub_addr_t) *fb_base; + int i; + + for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP) + { + if ((*base & RGB_MASK) == RGB_MAGIC) + { + int j; + + for (j = LINE_MIN; j <= LINE_MAX; j++) + { + if ((base[j] & RGB_MASK) == RGB_MAGIC) + { + *fb_base = (grub_uint64_t) (grub_addr_t) base; + *line_len = j << 2; + + return 1; + } + } + + break; + } + } + + return 0; +} + +/* Context for find_framebuf. */ +struct find_framebuf_ctx +{ + grub_uint64_t *fb_base; + grub_uint32_t *line_len; + int found; +}; + +/* Helper for find_framebuf. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + struct find_framebuf_ctx *ctx = data; + grub_pci_address_t addr, rcaddr; + grub_uint32_t subclass; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + subclass = (grub_pci_read (addr) >> 16) & 0xffff; + + if (subclass != GRUB_PCI_CLASS_SUBCLASS_VGA) + return 0; + + /* Enable MEM address spaces */ + rcaddr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (rcaddr, grub_pci_read_word (rcaddr) | GRUB_PCI_COMMAND_MEM_ENABLED); + + { + int i; + + grub_dprintf ("video", "Display controller: %d:%d.%d\nDevice id: %x\n", + grub_pci_get_bus (dev), grub_pci_get_device (dev), + grub_pci_get_function (dev), pciid); + addr += 8; + for (i = 0; i < 6; i++, addr += 4) + { + grub_uint32_t old_bar1, old_bar2, type; + grub_uint64_t base64; + + old_bar1 = grub_pci_read (addr); + if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO)) + continue; + + type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK; + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + { + if (i == 5) + break; + + i++; + addr += 4; + old_bar2 = grub_pci_read (addr); + } + else + old_bar2 = 0; + + base64 = old_bar2; + base64 <<= 32; + base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK); + + grub_dprintf ("video", "%s(%d): 0x%" PRIxGRUB_UINT64_T "\n", + ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) ? + "VMEM" : "MMIO"), type == GRUB_PCI_ADDR_MEM_TYPE_64 ? i - 1 : i, + base64); + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + if (old_bar2) + continue; +#endif + + if ((old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH) && (! ctx->found)) + { + *ctx->fb_base = base64; + if (find_line_len (ctx->fb_base, ctx->line_len)) + ctx->found++; + } + } + } + + return ctx->found; +} + +static int +find_framebuf (grub_uint64_t *fb_base, grub_uint32_t *line_len) +{ + struct find_framebuf_ctx ctx = { + .fb_base = fb_base, + .line_len = line_len, + .found = 0 + }; + + grub_pci_iterate (find_card, &ctx); + return ctx.found; +} + +static int +check_protocol (void) +{ + grub_efi_uga_draw_protocol_t *c; + + c = grub_efi_locate_protocol (&uga_draw_guid, 0); + if (c) + { + grub_uint32_t width, height, depth, rate, pixel; + int ret; + + if (efi_call_5 (c->get_mode, c, &width, &height, &depth, &rate)) + return 0; + + grub_efi_set_text_mode (0); + pixel = RGB_MAGIC; + efi_call_10 (c->blt, c, (struct grub_efi_uga_pixel *) &pixel, + GRUB_EFI_UGA_VIDEO_FILL, 0, 0, 0, 0, 1, height, 0); + ret = find_framebuf (&uga_fb, &uga_pitch); + grub_efi_set_text_mode (1); + + if (ret) + { + uga = c; + return 1; + } + } + + return 0; +} + +static grub_err_t +grub_video_uga_init (void) +{ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_uga_fini (void) +{ + return grub_video_fb_fini (); +} + +static grub_err_t +grub_video_uga_setup (unsigned int width, unsigned int height, + unsigned int mode_type, + unsigned int mode_mask __attribute__ ((unused))) +{ + unsigned int depth; + int found = 0; + + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + { + grub_uint32_t w; + grub_uint32_t h; + grub_uint32_t d; + grub_uint32_t r; + + if ((! efi_call_5 (uga->get_mode, uga, &w, &h, &d, &r)) && + ((! width) || (width == w)) && + ((! height) || (height == h)) && + ((! depth) || (depth == d))) + { + framebuffer.mode_info.width = w; + framebuffer.mode_info.height = h; + framebuffer.mode_info.pitch = uga_pitch; + framebuffer.ptr = (grub_uint8_t *) (grub_addr_t) uga_fb; + + found = 1; + } + } + + if (found) + { + grub_err_t err; + + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.bpp = 32; + framebuffer.mode_info.bytes_per_pixel = 4; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.red_mask_size = 8; + framebuffer.mode_info.red_field_pos = 16; + framebuffer.mode_info.green_mask_size = 8; + framebuffer.mode_info.green_field_pos = 8; + framebuffer.mode_info.blue_mask_size = 8; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 8; + framebuffer.mode_info.reserved_field_pos = 24; + + framebuffer.mode_info.blit_format = + grub_video_get_blit_format (&framebuffer.mode_info); + + err = grub_video_fb_create_render_target_from_pointer + (&framebuffer.render_target, + &framebuffer.mode_info, + framebuffer.ptr); + + if (err) + return err; + + err = grub_video_fb_set_active_render_target + (framebuffer.render_target); + + if (err) + return err; + + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + return err; + } + + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); +} + +static grub_err_t +grub_video_uga_swap_buffers (void) +{ + /* TODO: Implement buffer swapping. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_uga_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_uga_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_uga_adapter = + { + .name = "EFI UGA driver", + .id = GRUB_VIDEO_DRIVER_EFI_UGA, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE_DIRTY, + + .init = grub_video_uga_init, + .fini = grub_video_uga_fini, + .setup = grub_video_uga_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_uga_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_uga_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_uga_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + }; + +GRUB_MOD_INIT(efi_uga) +{ + if (check_protocol ()) + grub_video_register (&grub_video_uga_adapter); +} + +GRUB_MOD_FINI(efi_uga) +{ + if (uga) + grub_video_unregister (&grub_video_uga_adapter); +} diff --git a/grub-core/video/emu/sdl.c b/grub-core/video/emu/sdl.c new file mode 100644 index 0000000..0ebab6f --- /dev/null +++ b/grub-core/video/emu/sdl.c @@ -0,0 +1,252 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <SDL/SDL.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static SDL_Surface *window = 0; +static struct grub_video_render_target *sdl_render_target; +static struct grub_video_mode_info mode_info; + +static grub_err_t +grub_video_sdl_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); + +static grub_err_t +grub_video_sdl_init (void) +{ + window = 0; + + if (SDL_Init (SDL_INIT_VIDEO) < 0) + return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't init SDL: %s", + SDL_GetError ()); + + grub_memset (&mode_info, 0, sizeof (mode_info)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_sdl_fini (void) +{ + SDL_Quit (); + window = 0; + + grub_memset (&mode_info, 0, sizeof (mode_info)); + + return grub_video_fb_fini (); +} + +static inline unsigned int +get_mask_size (grub_uint32_t mask) +{ + unsigned i; + for (i = 0; mask > 1U << i; i++); + return i; +} + +static grub_err_t +grub_video_sdl_setup (unsigned int width, unsigned int height, + unsigned int mode_type, unsigned int mode_mask) +{ + int depth; + int flags = 0; + grub_err_t err; + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (depth == 0) + depth = 32; + + if (width == 0 && height == 0) + { + width = 800; + height = 600; + } + + if ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) + || !(mode_mask & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)) + flags |= SDL_DOUBLEBUF; + + window = SDL_SetVideoMode (width, height, depth, flags | SDL_HWSURFACE); + if (! window) + window = SDL_SetVideoMode (width, height, depth, flags | SDL_SWSURFACE); + if (! window) + return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't open window: %s", + SDL_GetError ()); + + grub_memset (&sdl_render_target, 0, sizeof (sdl_render_target)); + + mode_info.width = window->w; + mode_info.height = window->h; + mode_info.mode_type = 0; + if (window->flags & SDL_DOUBLEBUF) + mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (window->format->palette) + mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + else + mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_RGB; + + mode_info.bpp = window->format->BitsPerPixel; + mode_info.bytes_per_pixel = window->format->BytesPerPixel; + mode_info.pitch = window->pitch; + + /* In index color mode, number of colors. In RGB mode this is 256. */ + if (window->format->palette) + mode_info.number_of_colors + = 1 << window->format->BitsPerPixel; + else + mode_info.number_of_colors = 256; + + if (! window->format->palette) + { + mode_info.red_mask_size + = get_mask_size (window->format->Rmask >> window->format->Rshift); + mode_info.red_field_pos = window->format->Rshift; + mode_info.green_mask_size + = get_mask_size (window->format->Gmask >> window->format->Gshift); + mode_info.green_field_pos = window->format->Gshift; + mode_info.blue_mask_size + = get_mask_size (window->format->Bmask >> window->format->Bshift); + mode_info.blue_field_pos = window->format->Bshift; + mode_info.reserved_mask_size + = get_mask_size (window->format->Amask >> window->format->Ashift); + mode_info.reserved_field_pos = window->format->Ashift; + mode_info.blit_format + = grub_video_get_blit_format (&mode_info); + } + + err = grub_video_fb_create_render_target_from_pointer (&sdl_render_target, + &mode_info, + window->pixels); + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + grub_video_sdl_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + /* Reset render target to SDL one. */ + return grub_video_fb_set_active_render_target (sdl_render_target); +} + +static grub_err_t +grub_video_sdl_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned i; + if (window->format->palette) + { + SDL_Color *tmp; + if (start >= mode_info.number_of_colors) + return GRUB_ERR_NONE; + + if (start + count > mode_info.number_of_colors) + count = mode_info.number_of_colors - start; + + tmp = grub_calloc (count, sizeof (tmp[0])); + for (i = 0; i < count; i++) + { + tmp[i].r = palette_data[i].r; + tmp[i].g = palette_data[i].g; + tmp[i].b = palette_data[i].b; + tmp[i].unused = palette_data[i].a; + } + SDL_SetColors (window, tmp, start, count); + grub_free (tmp); + } + + return grub_video_fb_set_palette (start, count, palette_data); +} + +static grub_err_t +grub_video_sdl_swap_buffers (void) +{ + if (SDL_Flip (window) < 0) + return grub_error (GRUB_ERR_BAD_DEVICE, "couldn't swap buffers: %s", + SDL_GetError ()); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_sdl_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + return grub_video_fb_set_active_render_target (sdl_render_target); + + return grub_video_fb_set_active_render_target (target); +} + +static struct grub_video_adapter grub_video_sdl_adapter = + { + .name = "SDL Video Driver", + .id = GRUB_VIDEO_DRIVER_SDL, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE, + + .init = grub_video_sdl_init, + .fini = grub_video_sdl_fini, + .setup = grub_video_sdl_setup, + .get_info = grub_video_fb_get_info, + .set_palette = grub_video_sdl_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_sdl_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_sdl_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(sdl) +{ + grub_video_register (&grub_video_sdl_adapter); +} + +GRUB_MOD_FINI(sdl) +{ + grub_video_unregister (&grub_video_sdl_adapter); +} diff --git a/grub-core/video/fb/fbblit.c b/grub-core/video/fb/fbblit.c new file mode 100644 index 0000000..d559248 --- /dev/null +++ b/grub-core/video/fb/fbblit.c @@ -0,0 +1,2227 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +/* SPECIAL NOTES! + + Please note following when reading the code below: + + - In this driver we assume that every memory can be accessed by same memory + bus. If there are different address spaces do not use this code as a base + code for other archs. + + - Every function in this code assumes that bounds checking has been done in + previous phase and they are opted out in here. */ + +#include <grub/video_fb.h> +#include <grub/fbblit.h> +#include <grub/fbutil.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/video.h> + +/* Generic replacing blitter (slow). Works for every supported format. */ +static void +grub_video_fbblit_replace (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t src_red; + grub_uint8_t src_green; + grub_uint8_t src_blue; + grub_uint8_t src_alpha; + grub_video_color_t src_color; + grub_video_color_t dst_color; + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + src_color = get_pixel (src, i + offset_x, j + offset_y); + + grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, + &src_blue, &src_alpha); + + dst_color = grub_video_fb_map_rgba (src_red, src_green, + src_blue, src_alpha); + + set_pixel (dst, x + i, y + j, dst_color); + } + } +} + +/* Block copy replacing blitter. Works with modes multiple of 8 bits. */ +static void +grub_video_fbblit_replace_directN (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + int j; + grub_uint32_t *srcptr; + grub_uint32_t *dstptr; + int bpp; + + bpp = src->mode_info->bytes_per_pixel; + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + grub_memmove (dstptr, srcptr, width * bpp); + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, src->mode_info->pitch); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dst->mode_info->pitch); + } +} + +/* Optimized replacing blitter for 1-bit to 32bit. */ +static void +grub_video_fbblit_replace_32bit_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint32_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint32_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr & srcmask) + *dstptr = fgcolor; + else + *dstptr = bgcolor; + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr++; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + + +#ifdef GRUB_HAVE_UNALIGNED_ACCESS +/* Optimized replacing blitter for 1-bit to 24-bit. */ +static void +grub_video_fbblit_replace_24bit_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint32_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width - 1; i++) + { + if (*srcptr & srcmask) + *(grub_uint32_t *) dstptr = fgcolor; + else + *(grub_uint32_t *) dstptr = bgcolor; + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr += 3; + } + + if (*srcptr & srcmask) + { + *dstptr++ = fgcolor & 0xff; + *dstptr++ = (fgcolor & 0xff00) >> 8; + *dstptr++ = (fgcolor & 0xff0000) >> 16; + } + else + { + *dstptr++ = bgcolor & 0xff; + *dstptr++ = (bgcolor & 0xff00) >> 8; + *dstptr++ = (bgcolor & 0xff0000) >> 16; + } + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + dstptr += dstrowskip; + } +} +#endif + +/* Optimized replacing blitter for 1-bit to 16-bit. */ +static void +grub_video_fbblit_replace_16bit_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint16_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint16_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr & srcmask) + *dstptr = fgcolor; + else + *dstptr = bgcolor; + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr++; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for 1-bit to 8-bit. */ +static void +grub_video_fbblit_replace_8bit_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint8_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr & srcmask) + *dstptr = fgcolor; + else + *dstptr = bgcolor; + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr++; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + dstptr += dstrowskip; + } +} + +static void +grub_video_fbblit_replace_32bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint32_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint32_t palette[17]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + palette[16] = grub_video_fb_map_rgba (0, 0, 0, 0); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr == 0xf0) + *dstptr = palette[16]; + else + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for 1-bit to 16bit. */ +static void +grub_video_fbblit_replace_24bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint32_t palette[17]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + palette[16] = grub_video_fb_map_rgba (0, 0, 0, 0); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + register grub_uint32_t col; + if (*srcptr == 0xf0) + col = palette[16]; + else + col = palette[*srcptr & 0xf]; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = col >> 16; + *dstptr++ = col >> 8; + *dstptr++ = col >> 0; +#else + *dstptr++ = col >> 0; + *dstptr++ = col >> 8; + *dstptr++ = col >> 16; +#endif + srcptr++; + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for 1-bit to 16bit. */ +static void +grub_video_fbblit_replace_16bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint16_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint16_t palette[17]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + palette[16] = grub_video_fb_map_rgba (0, 0, 0, 0); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr == 0xf0) + *dstptr = palette[16]; + else + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for 1-bit to 8bit. */ +static void +grub_video_fbblit_replace_8bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint8_t palette[17]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + palette[16] = grub_video_fb_map_rgba (0, 0, 0, 0); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr == 0xf0) + *dstptr = palette[16]; + else + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + + +static void +grub_video_fbblit_blend_32bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint32_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint32_t palette[16]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr != 0xf0) + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for 1-bit to 16bit. */ +static void +grub_video_fbblit_blend_24bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint32_t palette[16]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + register grub_uint32_t col; + if (*srcptr != 0xf0) + { + col = palette[*srcptr & 0xf]; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = col >> 16; + *dstptr++ = col >> 8; + *dstptr++ = col >> 0; +#else + *dstptr++ = col >> 0; + *dstptr++ = col >> 8; + *dstptr++ = col >> 16; +#endif + } + else + dstptr += 3; + srcptr++; + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for 1-bit to 16bit. */ +static void +grub_video_fbblit_blend_16bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint16_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint16_t palette[17]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr != 0xf0) + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for 1-bit to 8bit. */ +static void +grub_video_fbblit_blend_8bit_indexa (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int dstrowskip; + unsigned int srcrowskip; + grub_uint8_t palette[16]; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskip = src->mode_info->pitch - width; + + for (i = 0; i < 16; i++) + palette[i] = grub_video_fb_map_color (i); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + if (*srcptr != 0xf0) + *dstptr = palette[*srcptr & 0xf]; + srcptr++; + dstptr++; + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + + +/* Optimized replacing blitter for RGBX8888 to BGRX8888. */ +static void +grub_video_fbblit_replace_BGRX8888_RGBX8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { +#ifdef GRUB_CPU_WORDS_BIGENDIAN + grub_uint8_t a = *srcptr++; +#endif + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; +#ifndef GRUB_CPU_WORDS_BIGENDIAN + grub_uint8_t a = *srcptr++; +#endif + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = a; +#endif + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; +#ifndef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = a; +#endif + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for RGB888 to BGRX8888. */ +static void +grub_video_fbblit_replace_BGRX8888_RGB888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + /* Set alpha component as opaque. */ + *dstptr++ = 255; +#endif + + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + /* Set alpha component as opaque. */ + *dstptr++ = 255; +#endif + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for RGBX8888 to BGR888. */ +static void +grub_video_fbblit_replace_BGR888_RGBX8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + grub_uint8_t sr; + grub_uint8_t sg; + grub_uint8_t sb; + + color = *srcptr++; + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = sr; + *dstptr++ = sg; + *dstptr++ = sb; +#else + *dstptr++ = sb; + *dstptr++ = sg; + *dstptr++ = sr; +#endif + } + + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for RGB888 to BGR888. */ +static void +grub_video_fbblit_replace_BGR888_RGB888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; + + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; + } + + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +/* Optimized replacing blitter for RGB888 to RGBX8888. */ +static void +grub_video_fbblit_replace_RGBX8888_RGB888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint8_t *srcptr; + grub_uint32_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 3 * width; + dstrowskip = dst->mode_info->pitch - 4 * width; + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { +#ifdef GRUB_CPU_WORDS_BIGENDIAN + sb = *srcptr++; + sg = *srcptr++; + sr = *srcptr++; +#else + sr = *srcptr++; + sg = *srcptr++; + sb = *srcptr++; +#endif + /* Set alpha as opaque. */ + color = 0xFF000000 | (sb << 16) | (sg << 8) | sr; + + *dstptr++ = color; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for RGBX8888 to RGB888. */ +static void +grub_video_fbblit_replace_RGB888_RGBX8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + srcrowskip = src->mode_info->pitch - 4 * width; + dstrowskip = dst->mode_info->pitch - 3 * width; + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + color = *srcptr++; + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = sr; + *dstptr++ = sg; + *dstptr++ = sb; +#else + *dstptr++ = sb; + *dstptr++ = sg; + *dstptr++ = sr; +#endif + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for RGBX8888 to indexed color. */ +static void +grub_video_fbblit_replace_index_RGBX8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 4 * width; + dstrowskip = dst->mode_info->pitch - width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + color = *srcptr++; + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + color = grub_video_fb_map_rgb(sr, sg, sb); + *dstptr++ = color & 0xFF; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized replacing blitter for RGB888 to indexed color. */ +static void +grub_video_fbblit_replace_index_RGB888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 3 * width; + dstrowskip = dst->mode_info->pitch - width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { +#ifndef GRUB_CPU_WORDS_BIGENDIAN + sr = *srcptr++; + sg = *srcptr++; + sb = *srcptr++; +#else + sb = *srcptr++; + sg = *srcptr++; + sr = *srcptr++; +#endif + + color = grub_video_fb_map_rgb(sr, sg, sb); + + *dstptr++ = color & 0xFF; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +static inline grub_uint8_t +alpha_dilute (grub_uint8_t bg, grub_uint8_t fg, grub_uint8_t alpha) +{ + grub_uint16_t s; + grub_uint16_t h, l; + s = (fg * alpha) + (bg * (255 ^ alpha)); + /* Optimised division by 255. */ + h = s >> 8; + l = s & 0xff; + if (h + l >= 255) + h++; + return h; +} + +/* Generic blending blitter. Works for every supported format. */ +static void +grub_video_fbblit_blend (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t src_red; + grub_uint8_t src_green; + grub_uint8_t src_blue; + grub_uint8_t src_alpha; + grub_uint8_t dst_red; + grub_uint8_t dst_green; + grub_uint8_t dst_blue; + grub_uint8_t dst_alpha; + grub_video_color_t src_color; + grub_video_color_t dst_color; + + src_color = get_pixel (src, i + offset_x, j + offset_y); + grub_video_fb_unmap_color_int (src, src_color, &src_red, &src_green, + &src_blue, &src_alpha); + + if (src_alpha == 0) + continue; + + if (src_alpha == 255) + { + dst_color = grub_video_fb_map_rgba (src_red, src_green, + src_blue, src_alpha); + set_pixel (dst, x + i, y + j, dst_color); + continue; + } + + dst_color = get_pixel (dst, x + i, y + j); + + grub_video_fb_unmap_color_int (dst, dst_color, &dst_red, + &dst_green, &dst_blue, &dst_alpha); + + dst_red = alpha_dilute (dst_red, src_red, src_alpha); + dst_green = alpha_dilute (dst_green, src_green, src_alpha); + dst_blue = alpha_dilute (dst_blue, src_blue, src_alpha); + + dst_alpha = src_alpha; + dst_color = grub_video_fb_map_rgba (dst_red, dst_green, dst_blue, + dst_alpha); + + set_pixel (dst, x + i, y + j, dst_color); + } + } +} + +/* Optimized blending blitter for RGBA8888 to BGRA8888. */ +static void +grub_video_fbblit_blend_BGRA8888_RGBA8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint32_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + /* Skip transparent source pixels. */ + dstptr++; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { + /* Opaque pixel shortcut. */ + dr = sr; + dg = sg; + db = sb; + } + else + { + /* General pixel color blending. */ + color = *dstptr; + + dr = (color >> 16) & 0xFF; + dr = alpha_dilute (dr, sr, a); + dg = (color >> 8) & 0xFF; + dg = alpha_dilute (dg, sg, a); + db = (color >> 0) & 0xFF; + db = alpha_dilute (db, sb, a); + } + + color = (a << 24) | (dr << 16) | (dg << 8) | db; + + *dstptr++ = color; + } + + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized blending blitter for RGBA8888 to BGR888. */ +static void +grub_video_fbblit_blend_BGR888_RGBA8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int srcrowskip; + unsigned int dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + srcrowskip = src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + /* Skip transparent source pixels. */ + dstptr += 3; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { + /* Opaque pixel shortcut. */ + dr = sr; + dg = sg; + db = sb; + } + else + { + /* General pixel color blending. */ + color = *dstptr; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + db = dstptr[0]; + dg = dstptr[1]; + dr = dstptr[2]; +#else + dr = dstptr[0]; + dg = dstptr[1]; + db = dstptr[2]; +#endif + + db = alpha_dilute (db, sb, a); + dg = alpha_dilute (dg, sg, a); + dr = alpha_dilute (dr, sr, a); + } + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = db; + *dstptr++ = dg; + *dstptr++ = dr; +#else + *dstptr++ = dr; + *dstptr++ = dg; + *dstptr++ = db; +#endif + } + + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + dstptr += dstrowskip; + } +} + +/* Optimized blending blitter for RGBA888 to RGBA8888. */ +static void +grub_video_fbblit_blend_RGBA8888_RGBA8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint32_t *srcptr; + grub_uint32_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 4 * width; + dstrowskip = dst->mode_info->pitch - 4 * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + dstptr++; + continue; + } + + if (a == 255) + { + *dstptr++ = color; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + color = *dstptr; + + dr = (color >> 0) & 0xFF; + dg = (color >> 8) & 0xFF; + db = (color >> 16) & 0xFF; + + dr = alpha_dilute (dr, sr, a); + dg = alpha_dilute (dg, sg, a); + db = alpha_dilute (db, sb, a); + + color = (a << 24) | (db << 16) | (dg << 8) | dr; + + *dstptr++ = color; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized blending blitter for RGBA8888 to RGB888. */ +static void +grub_video_fbblit_blend_RGB888_RGBA8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 4 * width; + dstrowskip = dst->mode_info->pitch - 3 * width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + dstptr += 3; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { +#ifndef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = sr; + *dstptr++ = sg; + *dstptr++ = sb; +#else + *dstptr++ = sb; + *dstptr++ = sg; + *dstptr++ = sr; +#endif + + continue; + } + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + dr = dstptr[0]; + dg = dstptr[1]; + db = dstptr[2]; +#else + db = dstptr[0]; + dg = dstptr[1]; + dr = dstptr[2]; +#endif + + dr = alpha_dilute (dr, sr, a); + dg = alpha_dilute (dg, sg, a); + db = alpha_dilute (db, sb, a); + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + *dstptr++ = dr; + *dstptr++ = dg; + *dstptr++ = db; +#else + *dstptr++ = db; + *dstptr++ = dg; + *dstptr++ = dr; +#endif + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized blending blitter for RGBA8888 to indexed color. */ +static void +grub_video_fbblit_blend_index_RGBA8888 (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t color; + int i; + int j; + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned char dr; + unsigned char dg; + unsigned char db; + unsigned char da; + grub_size_t srcrowskip; + grub_size_t dstrowskip; + + srcrowskip = src->mode_info->pitch - 4 * width; + dstrowskip = dst->mode_info->pitch - width; + + srcptr = grub_video_fb_get_video_ptr (src, offset_x, offset_y); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + dstptr++; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { + color = grub_video_fb_map_rgb(sr, sg, sb); + *dstptr++ = color & 0xFF; + continue; + } + + grub_video_fb_unmap_color_int (dst, *dstptr, &dr, &dg, &db, &da); + + dr = alpha_dilute (dr, sr, a); + dg = alpha_dilute (dg, sg, a); + db = alpha_dilute (db, sb, a); + + color = grub_video_fb_map_rgb(dr, dg, db); + + *dstptr++ = color & 0xFF; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (srcptr, srcrowskip); + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized blending blitter for 1-bit to XXXA8888. */ +static void +grub_video_fbblit_blend_XXXA8888_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint32_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint32_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + grub_uint8_t a; + + if (*srcptr & srcmask) + { + color = fgcolor; + a = src->mode_info->fg_alpha; + } + else + { + color = bgcolor; + a = src->mode_info->bg_alpha; + } + + if (a == 255) + *dstptr = color; + else if (a != 0) + { + grub_uint8_t s1 = (color >> 0) & 0xFF; + grub_uint8_t s2 = (color >> 8) & 0xFF; + grub_uint8_t s3 = (color >> 16) & 0xFF; + + grub_uint8_t d1 = (*dstptr >> 0) & 0xFF; + grub_uint8_t d2 = (*dstptr >> 8) & 0xFF; + grub_uint8_t d3 = (*dstptr >> 16) & 0xFF; + + d1 = alpha_dilute (d1, s1, a); + d2 = alpha_dilute (d2, s2, a); + d3 = alpha_dilute (d3, s3, a); + + *dstptr = (a << 24) | (d3 << 16) | (d2 << 8) | d1; + } + + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr++; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* Optimized blending blitter for 1-bit to XXX888. */ +#ifdef GRUB_HAVE_UNALIGNED_ACCESS +static void +grub_video_fbblit_blend_XXX888_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint32_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + grub_uint8_t a; + if (*srcptr & srcmask) + { + color = fgcolor; + a = src->mode_info->fg_alpha; + } + else + { + color = bgcolor; + a = src->mode_info->bg_alpha; + } + + if (a == 255) + { +#ifndef GRUB_CPU_WORDS_BIGENDIAN + ((grub_uint8_t *) dstptr)[0] = color & 0xff; + ((grub_uint8_t *) dstptr)[1] = (color & 0xff00) >> 8; + ((grub_uint8_t *) dstptr)[2] = (color & 0xff0000) >> 16; +#else + ((grub_uint8_t *) dstptr)[2] = color & 0xff; + ((grub_uint8_t *) dstptr)[1] = (color & 0xff00) >> 8; + ((grub_uint8_t *) dstptr)[0] = (color & 0xff0000) >> 16; +#endif + } + else if (a != 0) + { + grub_uint8_t s1 = (color >> 0) & 0xFF; + grub_uint8_t s2 = (color >> 8) & 0xFF; + grub_uint8_t s3 = (color >> 16) & 0xFF; + + grub_uint8_t d1 = (*(grub_uint32_t *) dstptr >> 0) & 0xFF; + grub_uint8_t d2 = (*(grub_uint32_t *) dstptr >> 8) & 0xFF; + grub_uint8_t d3 = (*(grub_uint32_t *) dstptr >> 16) & 0xFF; + + ((grub_uint8_t *) dstptr)[0] = alpha_dilute (d1, s1, a); + ((grub_uint8_t *) dstptr)[1] = alpha_dilute (d2, s2, a); + ((grub_uint8_t *) dstptr)[2] = alpha_dilute (d3, s3, a); + } + + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr += 3; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + dstptr += dstrowskip; + } +} +#endif + +/* Optimized blending blitter for 1-bit to XXX888. */ +static void +grub_video_fbblit_blend_XXX565_1bit (struct grub_video_fbblit_info *dst, + struct grub_video_fbblit_info *src, + int x, int y, + int width, int height, + int offset_x, int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint16_t *dstptr; + grub_uint8_t srcmask; + unsigned int dstrowskip; + unsigned int srcrowskipbyte, srcrowskipbit; + grub_uint16_t fgcolor, bgcolor; + int bit_index; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + dstrowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + srcrowskipbyte = (src->mode_info->width - width) >> 3; + srcrowskipbit = (src->mode_info->width - width) & 7; + + bit_index = offset_y * src->mode_info->width + offset_x; + srcptr = (grub_uint8_t *) src->data + (bit_index >> 3); + srcmask = 1 << (~bit_index & 7); + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + fgcolor = grub_video_fb_map_rgba (src->mode_info->fg_red, + src->mode_info->fg_green, + src->mode_info->fg_blue, + src->mode_info->fg_alpha); + + bgcolor = grub_video_fb_map_rgba (src->mode_info->bg_red, + src->mode_info->bg_green, + src->mode_info->bg_blue, + src->mode_info->bg_alpha); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + grub_uint8_t a; + if (*srcptr & srcmask) + { + color = fgcolor; + a = src->mode_info->fg_alpha; + } + else + { + color = bgcolor; + a = src->mode_info->bg_alpha; + } + + if (a == 255) + *dstptr = color; + else if (a != 0) + { + grub_uint8_t s1 = (color >> 0) & 0x1F; + grub_uint8_t s2 = (color >> 5) & 0x3F; + grub_uint8_t s3 = (color >> 11) & 0x1F; + + grub_uint8_t d1 = (*dstptr >> 0) & 0x1F; + grub_uint8_t d2 = (*dstptr >> 5) & 0x3F; + grub_uint8_t d3 = (*dstptr >> 11) & 0x1F; + + d1 = alpha_dilute (d1, s1, a); + d2 = alpha_dilute (d2, s2, a); + d3 = alpha_dilute (d3, s3, a); + + *dstptr = (d1 & 0x1f) | ((d2 & 0x3f) << 5) | ((d3 & 0x1f) << 11); + } + + srcmask >>= 1; + if (!srcmask) + { + srcptr++; + srcmask = 0x80; + } + + dstptr++; + } + + srcptr += srcrowskipbyte; + if (srcmask >> srcrowskipbit) + srcmask >>= srcrowskipbit; + else + { + srcptr++; + srcmask <<= 8 - srcrowskipbit; + } + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, dstrowskip); + } +} + +/* NOTE: This function assumes that given coordinates are within bounds of + handled data. */ +void +grub_video_fb_dispatch_blit (struct grub_video_fbblit_info *target, + struct grub_video_fbblit_info *source, + enum grub_video_blit_operators oper, int x, int y, + unsigned int width, unsigned int height, + int offset_x, int offset_y) +{ + if (oper == GRUB_VIDEO_BLIT_REPLACE) + { + /* Try to figure out more optimized version for replace operator. */ + switch (source->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + grub_video_fbblit_replace_BGRX8888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_BGR_888: + grub_video_fbblit_replace_BGR888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + grub_video_fbblit_replace_RGB888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + grub_video_fbblit_replace_index_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + grub_video_fbblit_replace_BGRX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + grub_video_fbblit_replace_RGBX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_BGR_888: + grub_video_fbblit_replace_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + grub_video_fbblit_replace_index_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR_ALPHA: + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR_ALPHA: + switch (target->mode_info->bytes_per_pixel) + { + case 4: + grub_video_fbblit_replace_32bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 3: + grub_video_fbblit_replace_24bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 2: + grub_video_fbblit_replace_16bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 1: + grub_video_fbblit_replace_8bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED: + switch (target->mode_info->bytes_per_pixel) + { + case 4: + grub_video_fbblit_replace_32bit_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; +#ifdef GRUB_HAVE_UNALIGNED_ACCESS + case 3: + grub_video_fbblit_replace_24bit_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; +#endif + case 2: + grub_video_fbblit_replace_16bit_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 1: + grub_video_fbblit_replace_8bit_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + break; + default: + break; + } + + /* No optimized replace operator found, use default (slow) blitter. */ + grub_video_fbblit_replace (target, source, x, y, width, height, + offset_x, offset_y); + } + else + { + /* Try to figure out more optimized blend operator. */ + switch (source->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + grub_video_fbblit_blend_BGRA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + grub_video_fbblit_blend_RGBA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_BGR_888: + grub_video_fbblit_blend_BGR888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + grub_video_fbblit_blend_RGB888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + grub_video_fbblit_blend_index_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + /* Note: There is really no alpha information here, so blend is + changed to replace. */ + + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + grub_video_fbblit_replace_BGRX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + grub_video_fbblit_replace_RGBX8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_BGR_888: + grub_video_fbblit_replace_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + grub_video_fbblit_replace_directN (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR: + grub_video_fbblit_replace_index_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED: + switch (target->mode_info->blit_format) + { + case GRUB_VIDEO_BLIT_FORMAT_BGRA_8888: + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: + grub_video_fbblit_blend_XXXA8888_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; +#ifdef GRUB_HAVE_UNALIGNED_ACCESS + case GRUB_VIDEO_BLIT_FORMAT_BGR_888: + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: + grub_video_fbblit_blend_XXX888_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; +#endif + case GRUB_VIDEO_BLIT_FORMAT_BGR_565: + case GRUB_VIDEO_BLIT_FORMAT_RGB_565: + grub_video_fbblit_blend_XXX565_1bit (target, source, + x, y, width, height, + offset_x, offset_y); + return; + default: + break; + } + break; + case GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR_ALPHA: + switch (target->mode_info->bytes_per_pixel) + { + case 4: + grub_video_fbblit_blend_32bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 3: + grub_video_fbblit_blend_24bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 2: + grub_video_fbblit_blend_16bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + case 1: + grub_video_fbblit_blend_8bit_indexa (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + break; + default: + break; + } + + /* No optimized blend operation found, use default (slow) blitter. */ + grub_video_fbblit_blend (target, source, x, y, width, height, + offset_x, offset_y); + } +} diff --git a/grub-core/video/fb/fbfill.c b/grub-core/video/fb/fbfill.c new file mode 100644 index 0000000..49fa16b --- /dev/null +++ b/grub-core/video/fb/fbfill.c @@ -0,0 +1,212 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +/* SPECIAL NOTES! + + Please note following when reading the code below: + + - In this driver we assume that every memory can be accessed by same memory + bus. If there are different address spaces do not use this code as a base + code for other archs. + + - Every function in this code assumes that bounds checking has been done in + previous phase and they are opted out in here. */ + +#include <grub/video_fb.h> +#include <grub/fbfill.h> +#include <grub/fbutil.h> +#include <grub/types.h> +#include <grub/safemath.h> +#include <grub/video.h> + +/* Generic filler that works for every supported mode. */ +static void +grub_video_fbfill (struct grub_video_fbblit_info *dst, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + unsigned int i, j; + + for (j = 0; j < height; j++) + for (i = 0; i < width; i++) + set_pixel (dst, x + i, y + j, color); +} + +/* Optimized filler for direct color 32 bit modes. It is assumed that color + is already mapped to destination format. */ +static void +grub_video_fbfill_direct32 (struct grub_video_fbblit_info *dst, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + unsigned int i, j; + grub_uint32_t *dstptr; + grub_size_t rowskip; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + if (grub_mul (dst->mode_info->bytes_per_pixel, width, &rowskip) || + grub_sub (dst->mode_info->pitch, rowskip, &rowskip)) + return; + + /* Get the start address. */ + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + *dstptr++ = color; + + /* Advance the dest pointer to the right location on the next line. */ + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, rowskip); + } +} + +/* Optimized filler for direct color 24 bit modes. It is assumed that color + is already mapped to destination format. */ +static void +grub_video_fbfill_direct24 (struct grub_video_fbblit_info *dst, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + unsigned int i, j; + grub_size_t rowskip; + grub_uint8_t *dstptr; +#ifndef GRUB_CPU_WORDS_BIGENDIAN + grub_uint8_t fill0 = (grub_uint8_t)((color >> 0) & 0xFF); + grub_uint8_t fill1 = (grub_uint8_t)((color >> 8) & 0xFF); + grub_uint8_t fill2 = (grub_uint8_t)((color >> 16) & 0xFF); +#else + grub_uint8_t fill2 = (grub_uint8_t)((color >> 0) & 0xFF); + grub_uint8_t fill1 = (grub_uint8_t)((color >> 8) & 0xFF); + grub_uint8_t fill0 = (grub_uint8_t)((color >> 16) & 0xFF); +#endif + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + if (grub_mul (dst->mode_info->bytes_per_pixel, width, &rowskip) || + grub_sub (dst->mode_info->pitch, rowskip, &rowskip)) + return; + + /* Get the start address. */ + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + *dstptr++ = fill0; + *dstptr++ = fill1; + *dstptr++ = fill2; + } + + /* Advance the dest pointer to the right location on the next line. */ + dstptr += rowskip; + } +} + +/* Optimized filler for direct color 16 bit modes. It is assumed that color + is already mapped to destination format. */ +static void +grub_video_fbfill_direct16 (struct grub_video_fbblit_info *dst, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + unsigned int i, j; + grub_size_t rowskip; + grub_uint16_t *dstptr; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + if (grub_mul (dst->mode_info->bytes_per_pixel, width, &rowskip) || + grub_sub (dst->mode_info->pitch, rowskip, &rowskip)) + return; + + /* Get the start address. */ + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + *dstptr++ = color; + + /* Advance the dest pointer to the right location on the next line. */ + GRUB_VIDEO_FB_ADVANCE_POINTER (dstptr, rowskip); + } +} + +/* Optimized filler for index color. It is assumed that color + is already mapped to destination format. */ +static void +grub_video_fbfill_direct8 (struct grub_video_fbblit_info *dst, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + unsigned int i, j; + grub_size_t rowskip; + grub_uint8_t *dstptr; + grub_uint8_t fill = (grub_uint8_t)color & 0xFF; + + /* Calculate the number of bytes to advance from the end of one line + to the beginning of the next line. */ + if (grub_mul (dst->mode_info->bytes_per_pixel, width, &rowskip) || + grub_sub (dst->mode_info->pitch, rowskip, &rowskip)) + return; + + /* Get the start address. */ + dstptr = grub_video_fb_get_video_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + *dstptr++ = fill; + + /* Advance the dest pointer to the right location on the next line. */ + dstptr += rowskip; + } +} + +void +grub_video_fb_fill_dispatch (struct grub_video_fbblit_info *target, + grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + /* Try to figure out more optimized version. Note that color is already + mapped to target format so we can make assumptions based on that. */ + switch (target->mode_info->bytes_per_pixel) + { + case 4: + grub_video_fbfill_direct32 (target, color, x, y, + width, height); + return; + case 3: + grub_video_fbfill_direct24 (target, color, x, y, + width, height); + return; + case 2: + grub_video_fbfill_direct16 (target, color, x, y, + width, height); + return; + case 1: + grub_video_fbfill_direct8 (target, color, x, y, + width, height); + return; + } + + /* No optimized version found, use default (slow) filler. */ + grub_video_fbfill (target, color, x, y, width, height); +} diff --git a/grub-core/video/fb/fbutil.c b/grub-core/video/fb/fbutil.c new file mode 100644 index 0000000..b98bb51 --- /dev/null +++ b/grub-core/video/fb/fbutil.c @@ -0,0 +1,151 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +/* SPECIAL NOTES! + + Please note following when reading the code below: + + - In this driver we assume that every memory can be accessed by same memory + bus. If there are different address spaces do not use this code as a base + code for other archs. + + - Every function in this code assumes that bounds checking has been done in + previous phase and they are opted out in here. */ + +#include <grub/fbutil.h> +#include <grub/types.h> +#include <grub/video.h> + +grub_video_color_t +get_pixel (struct grub_video_fbblit_info *source, + unsigned int x, unsigned int y) +{ + grub_video_color_t color = 0; + + switch (source->mode_info->bpp) + { + case 32: + color = *(grub_uint32_t *)grub_video_fb_get_video_ptr (source, x, y); + break; + + case 24: + { + grub_uint8_t *ptr; + ptr = grub_video_fb_get_video_ptr (source, x, y); +#ifdef GRUB_CPU_WORDS_BIGENDIAN + color = ptr[2] | (ptr[1] << 8) | (ptr[0] << 16); +#else + color = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); +#endif + } + break; + + case 16: + case 15: + color = *(grub_uint16_t *)grub_video_fb_get_video_ptr (source, x, y); + break; + + case 8: + color = *(grub_uint8_t *)grub_video_fb_get_video_ptr (source, x, y); + break; + + case 1: + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) + { + int bit_index = y * source->mode_info->width + x; + grub_uint8_t *ptr = source->data + bit_index / 8; + int bit_pos = 7 - bit_index % 8; + color = (*ptr >> bit_pos) & 0x01; + } + break; + + default: + break; + } + + return color; +} + +void +set_pixel (struct grub_video_fbblit_info *source, + unsigned int x, unsigned int y, grub_video_color_t color) +{ + switch (source->mode_info->bpp) + { + case 32: + { + grub_uint32_t *ptr; + + ptr = (grub_uint32_t *)grub_video_fb_get_video_ptr (source, x, y); + + *ptr = color; + } + break; + + case 24: + { + grub_uint8_t *ptr; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + grub_uint8_t *colorptr = ((grub_uint8_t *)&color) + 1; +#else + grub_uint8_t *colorptr = (grub_uint8_t *)&color; +#endif + + ptr = grub_video_fb_get_video_ptr (source, x, y); + + ptr[0] = colorptr[0]; + ptr[1] = colorptr[1]; + ptr[2] = colorptr[2]; + } + break; + + case 16: + case 15: + { + grub_uint16_t *ptr; + + ptr = (grub_uint16_t *)grub_video_fb_get_video_ptr (source, x, y); + + *ptr = (grub_uint16_t) (color & 0xFFFF); + } + break; + + case 8: + { + grub_uint8_t *ptr; + + ptr = (grub_uint8_t *)grub_video_fb_get_video_ptr (source, x, y); + + *ptr = (grub_uint8_t) (color & 0xFF); + } + break; + + case 1: + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) + { + int bit_index = y * source->mode_info->width + x; + grub_uint8_t *ptr = source->data + bit_index / 8; + int bit_pos = 7 - bit_index % 8; + *ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos); + } + break; + + default: + break; + } +} diff --git a/grub-core/video/fb/video_fb.c b/grub-core/video/fb/video_fb.c new file mode 100644 index 0000000..ae6b89f --- /dev/null +++ b/grub-core/video/fb/video_fb.c @@ -0,0 +1,1709 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/fbblit.h> +#include <grub/fbfill.h> +#include <grub/fbutil.h> +#include <grub/bitmap.h> +#include <grub/dl.h> +#include <grub/safemath.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef grub_err_t (*grub_video_fb_doublebuf_update_screen_t) (void); +typedef volatile void *framebuf_t; + +struct dirty +{ + int first_line; + int last_line; +}; + +static struct +{ + struct grub_video_fbrender_target *render_target; + struct grub_video_fbrender_target *back_target; + struct grub_video_palette_data *palette; + framebuf_t pages[2]; + + unsigned int palette_size; + + struct dirty current_dirty; + struct dirty previous_dirty; + + /* For page flipping strategy. */ + int displayed_page; /* The page # that is the front buffer. */ + int render_page; /* The page # that is the back buffer. */ + grub_video_fb_set_page_t set_page; + char *offscreen_buffer; + grub_video_fb_doublebuf_update_screen_t update_screen; +} framebuffer; + +/* Specify "standard" VGA palette, some video cards may + need this and this will also be used when using RGB modes. */ +struct grub_video_palette_data grub_video_fbstd_colors[GRUB_VIDEO_FBSTD_EXT_NUMCOLORS] = + { + /* Standard (3-bit) colors. */ + + // {R, G, B, A} + {0x00, 0x00, 0x00, 0xFF}, // 0 = black + {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue + {0x00, 0xA8, 0x00, 0xFF}, // 2 = green + {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan + {0xA8, 0x00, 0x00, 0xFF}, // 4 = red + {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta + {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown + {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray + + /* Bright (4-bit) colors. */ + {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray + {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue + {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green + {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan + {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red + {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta + {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow + {0xFE, 0xFE, 0xFE, 0xFF}, // 15 = white + + /* Extended (8-bit) colors. Completes preceding colors to full RGB332. */ + {0x00, 0x00, 0x55, 0xFF}, // RGB332 = (0, 0, 1) + {0x00, 0x00, 0xFF, 0xFF}, // RGB332 = (0, 0, 3) + {0x00, 0x24, 0x00, 0xFF}, // RGB332 = (0, 1, 0) + {0x00, 0x24, 0x55, 0xFF}, // RGB332 = (0, 1, 1) + {0x00, 0x24, 0xAA, 0xFF}, // RGB332 = (0, 1, 2) + {0x00, 0x24, 0xFF, 0xFF}, // RGB332 = (0, 1, 3) + {0x00, 0x48, 0x00, 0xFF}, // RGB332 = (0, 2, 0) + {0x00, 0x48, 0x55, 0xFF}, // RGB332 = (0, 2, 1) + {0x00, 0x48, 0xAA, 0xFF}, // RGB332 = (0, 2, 2) + {0x00, 0x48, 0xFF, 0xFF}, // RGB332 = (0, 2, 3) + {0x00, 0x6C, 0x00, 0xFF}, // RGB332 = (0, 3, 0) + {0x00, 0x6C, 0x55, 0xFF}, // RGB332 = (0, 3, 1) + {0x00, 0x6C, 0xAA, 0xFF}, // RGB332 = (0, 3, 2) + {0x00, 0x6C, 0xFF, 0xFF}, // RGB332 = (0, 3, 3) + {0x00, 0x90, 0x00, 0xFF}, // RGB332 = (0, 4, 0) + {0x00, 0x90, 0x55, 0xFF}, // RGB332 = (0, 4, 1) + {0x00, 0x90, 0xAA, 0xFF}, // RGB332 = (0, 4, 2) + {0x00, 0x90, 0xFF, 0xFF}, // RGB332 = (0, 4, 3) + {0x00, 0xB4, 0x55, 0xFF}, // RGB332 = (0, 5, 1) + {0x00, 0xB4, 0xFF, 0xFF}, // RGB332 = (0, 5, 3) + {0x00, 0xD8, 0x00, 0xFF}, // RGB332 = (0, 6, 0) + {0x00, 0xD8, 0x55, 0xFF}, // RGB332 = (0, 6, 1) + {0x00, 0xD8, 0xAA, 0xFF}, // RGB332 = (0, 6, 2) + {0x00, 0xD8, 0xFF, 0xFF}, // RGB332 = (0, 6, 3) + {0x00, 0xFC, 0x00, 0xFF}, // RGB332 = (0, 7, 0) + {0x00, 0xFC, 0x55, 0xFF}, // RGB332 = (0, 7, 1) + {0x00, 0xFC, 0xAA, 0xFF}, // RGB332 = (0, 7, 2) + {0x00, 0xFC, 0xFF, 0xFF}, // RGB332 = (0, 7, 3) + {0x24, 0x00, 0x00, 0xFF}, // RGB332 = (1, 0, 0) + {0x24, 0x00, 0x55, 0xFF}, // RGB332 = (1, 0, 1) + {0x24, 0x00, 0xAA, 0xFF}, // RGB332 = (1, 0, 2) + {0x24, 0x00, 0xFF, 0xFF}, // RGB332 = (1, 0, 3) + {0x24, 0x24, 0x00, 0xFF}, // RGB332 = (1, 1, 0) + {0x24, 0x24, 0x55, 0xFF}, // RGB332 = (1, 1, 1) + {0x24, 0x24, 0xAA, 0xFF}, // RGB332 = (1, 1, 2) + {0x24, 0x24, 0xFF, 0xFF}, // RGB332 = (1, 1, 3) + {0x24, 0x48, 0x00, 0xFF}, // RGB332 = (1, 2, 0) + {0x24, 0x48, 0x55, 0xFF}, // RGB332 = (1, 2, 1) + {0x24, 0x48, 0xAA, 0xFF}, // RGB332 = (1, 2, 2) + {0x24, 0x48, 0xFF, 0xFF}, // RGB332 = (1, 2, 3) + {0x24, 0x6C, 0x00, 0xFF}, // RGB332 = (1, 3, 0) + {0x24, 0x6C, 0x55, 0xFF}, // RGB332 = (1, 3, 1) + {0x24, 0x6C, 0xAA, 0xFF}, // RGB332 = (1, 3, 2) + {0x24, 0x6C, 0xFF, 0xFF}, // RGB332 = (1, 3, 3) + {0x24, 0x90, 0x00, 0xFF}, // RGB332 = (1, 4, 0) + {0x24, 0x90, 0x55, 0xFF}, // RGB332 = (1, 4, 1) + {0x24, 0x90, 0xAA, 0xFF}, // RGB332 = (1, 4, 2) + {0x24, 0x90, 0xFF, 0xFF}, // RGB332 = (1, 4, 3) + {0x24, 0xB4, 0x00, 0xFF}, // RGB332 = (1, 5, 0) + {0x24, 0xB4, 0x55, 0xFF}, // RGB332 = (1, 5, 1) + {0x24, 0xB4, 0xAA, 0xFF}, // RGB332 = (1, 5, 2) + {0x24, 0xB4, 0xFF, 0xFF}, // RGB332 = (1, 5, 3) + {0x24, 0xD8, 0x00, 0xFF}, // RGB332 = (1, 6, 0) + {0x24, 0xD8, 0x55, 0xFF}, // RGB332 = (1, 6, 1) + {0x24, 0xD8, 0xAA, 0xFF}, // RGB332 = (1, 6, 2) + {0x24, 0xD8, 0xFF, 0xFF}, // RGB332 = (1, 6, 3) + {0x24, 0xFC, 0x00, 0xFF}, // RGB332 = (1, 7, 0) + {0x24, 0xFC, 0x55, 0xFF}, // RGB332 = (1, 7, 1) + {0x24, 0xFC, 0xAA, 0xFF}, // RGB332 = (1, 7, 2) + {0x24, 0xFC, 0xFF, 0xFF}, // RGB332 = (1, 7, 3) + {0x48, 0x00, 0x00, 0xFF}, // RGB332 = (2, 0, 0) + {0x48, 0x00, 0x55, 0xFF}, // RGB332 = (2, 0, 1) + {0x48, 0x00, 0xAA, 0xFF}, // RGB332 = (2, 0, 2) + {0x48, 0x00, 0xFF, 0xFF}, // RGB332 = (2, 0, 3) + {0x48, 0x24, 0x00, 0xFF}, // RGB332 = (2, 1, 0) + {0x48, 0x24, 0x55, 0xFF}, // RGB332 = (2, 1, 1) + {0x48, 0x24, 0xAA, 0xFF}, // RGB332 = (2, 1, 2) + {0x48, 0x24, 0xFF, 0xFF}, // RGB332 = (2, 1, 3) + {0x48, 0x48, 0x00, 0xFF}, // RGB332 = (2, 2, 0) + {0x48, 0x48, 0xAA, 0xFF}, // RGB332 = (2, 2, 2) + {0x48, 0x6C, 0x00, 0xFF}, // RGB332 = (2, 3, 0) + {0x48, 0x6C, 0x55, 0xFF}, // RGB332 = (2, 3, 1) + {0x48, 0x6C, 0xAA, 0xFF}, // RGB332 = (2, 3, 2) + {0x48, 0x6C, 0xFF, 0xFF}, // RGB332 = (2, 3, 3) + {0x48, 0x90, 0x00, 0xFF}, // RGB332 = (2, 4, 0) + {0x48, 0x90, 0x55, 0xFF}, // RGB332 = (2, 4, 1) + {0x48, 0x90, 0xAA, 0xFF}, // RGB332 = (2, 4, 2) + {0x48, 0x90, 0xFF, 0xFF}, // RGB332 = (2, 4, 3) + {0x48, 0xB4, 0x00, 0xFF}, // RGB332 = (2, 5, 0) + {0x48, 0xB4, 0x55, 0xFF}, // RGB332 = (2, 5, 1) + {0x48, 0xB4, 0xAA, 0xFF}, // RGB332 = (2, 5, 2) + {0x48, 0xB4, 0xFF, 0xFF}, // RGB332 = (2, 5, 3) + {0x48, 0xD8, 0x00, 0xFF}, // RGB332 = (2, 6, 0) + {0x48, 0xD8, 0x55, 0xFF}, // RGB332 = (2, 6, 1) + {0x48, 0xD8, 0xAA, 0xFF}, // RGB332 = (2, 6, 2) + {0x48, 0xD8, 0xFF, 0xFF}, // RGB332 = (2, 6, 3) + {0x48, 0xFC, 0x00, 0xFF}, // RGB332 = (2, 7, 0) + {0x48, 0xFC, 0xAA, 0xFF}, // RGB332 = (2, 7, 2) + {0x6C, 0x00, 0x00, 0xFF}, // RGB332 = (3, 0, 0) + {0x6C, 0x00, 0x55, 0xFF}, // RGB332 = (3, 0, 1) + {0x6C, 0x00, 0xAA, 0xFF}, // RGB332 = (3, 0, 2) + {0x6C, 0x00, 0xFF, 0xFF}, // RGB332 = (3, 0, 3) + {0x6C, 0x24, 0x00, 0xFF}, // RGB332 = (3, 1, 0) + {0x6C, 0x24, 0x55, 0xFF}, // RGB332 = (3, 1, 1) + {0x6C, 0x24, 0xAA, 0xFF}, // RGB332 = (3, 1, 2) + {0x6C, 0x24, 0xFF, 0xFF}, // RGB332 = (3, 1, 3) + {0x6C, 0x48, 0x00, 0xFF}, // RGB332 = (3, 2, 0) + {0x6C, 0x48, 0x55, 0xFF}, // RGB332 = (3, 2, 1) + {0x6C, 0x48, 0xAA, 0xFF}, // RGB332 = (3, 2, 2) + {0x6C, 0x48, 0xFF, 0xFF}, // RGB332 = (3, 2, 3) + {0x6C, 0x6C, 0x00, 0xFF}, // RGB332 = (3, 3, 0) + {0x6C, 0x6C, 0x55, 0xFF}, // RGB332 = (3, 3, 1) + {0x6C, 0x6C, 0xAA, 0xFF}, // RGB332 = (3, 3, 2) + {0x6C, 0x6C, 0xFF, 0xFF}, // RGB332 = (3, 3, 3) + {0x6C, 0x90, 0x00, 0xFF}, // RGB332 = (3, 4, 0) + {0x6C, 0x90, 0x55, 0xFF}, // RGB332 = (3, 4, 1) + {0x6C, 0x90, 0xAA, 0xFF}, // RGB332 = (3, 4, 2) + {0x6C, 0x90, 0xFF, 0xFF}, // RGB332 = (3, 4, 3) + {0x6C, 0xB4, 0x00, 0xFF}, // RGB332 = (3, 5, 0) + {0x6C, 0xB4, 0x55, 0xFF}, // RGB332 = (3, 5, 1) + {0x6C, 0xB4, 0xAA, 0xFF}, // RGB332 = (3, 5, 2) + {0x6C, 0xB4, 0xFF, 0xFF}, // RGB332 = (3, 5, 3) + {0x6C, 0xD8, 0x00, 0xFF}, // RGB332 = (3, 6, 0) + {0x6C, 0xD8, 0x55, 0xFF}, // RGB332 = (3, 6, 1) + {0x6C, 0xD8, 0xAA, 0xFF}, // RGB332 = (3, 6, 2) + {0x6C, 0xD8, 0xFF, 0xFF}, // RGB332 = (3, 6, 3) + {0x6C, 0xFC, 0x00, 0xFF}, // RGB332 = (3, 7, 0) + {0x6C, 0xFC, 0x55, 0xFF}, // RGB332 = (3, 7, 1) + {0x6C, 0xFC, 0xAA, 0xFF}, // RGB332 = (3, 7, 2) + {0x6C, 0xFC, 0xFF, 0xFF}, // RGB332 = (3, 7, 3) + {0x90, 0x00, 0x00, 0xFF}, // RGB332 = (4, 0, 0) + {0x90, 0x00, 0x55, 0xFF}, // RGB332 = (4, 0, 1) + {0x90, 0x00, 0xAA, 0xFF}, // RGB332 = (4, 0, 2) + {0x90, 0x00, 0xFF, 0xFF}, // RGB332 = (4, 0, 3) + {0x90, 0x24, 0x00, 0xFF}, // RGB332 = (4, 1, 0) + {0x90, 0x24, 0x55, 0xFF}, // RGB332 = (4, 1, 1) + {0x90, 0x24, 0xAA, 0xFF}, // RGB332 = (4, 1, 2) + {0x90, 0x24, 0xFF, 0xFF}, // RGB332 = (4, 1, 3) + {0x90, 0x48, 0x00, 0xFF}, // RGB332 = (4, 2, 0) + {0x90, 0x48, 0x55, 0xFF}, // RGB332 = (4, 2, 1) + {0x90, 0x48, 0xAA, 0xFF}, // RGB332 = (4, 2, 2) + {0x90, 0x48, 0xFF, 0xFF}, // RGB332 = (4, 2, 3) + {0x90, 0x6C, 0x00, 0xFF}, // RGB332 = (4, 3, 0) + {0x90, 0x6C, 0x55, 0xFF}, // RGB332 = (4, 3, 1) + {0x90, 0x6C, 0xAA, 0xFF}, // RGB332 = (4, 3, 2) + {0x90, 0x6C, 0xFF, 0xFF}, // RGB332 = (4, 3, 3) + {0x90, 0x90, 0x00, 0xFF}, // RGB332 = (4, 4, 0) + {0x90, 0x90, 0x55, 0xFF}, // RGB332 = (4, 4, 1) + {0x90, 0x90, 0xAA, 0xFF}, // RGB332 = (4, 4, 2) + {0x90, 0x90, 0xFF, 0xFF}, // RGB332 = (4, 4, 3) + {0x90, 0xB4, 0x00, 0xFF}, // RGB332 = (4, 5, 0) + {0x90, 0xB4, 0x55, 0xFF}, // RGB332 = (4, 5, 1) + {0x90, 0xB4, 0xAA, 0xFF}, // RGB332 = (4, 5, 2) + {0x90, 0xB4, 0xFF, 0xFF}, // RGB332 = (4, 5, 3) + {0x90, 0xD8, 0x00, 0xFF}, // RGB332 = (4, 6, 0) + {0x90, 0xD8, 0x55, 0xFF}, // RGB332 = (4, 6, 1) + {0x90, 0xD8, 0xAA, 0xFF}, // RGB332 = (4, 6, 2) + {0x90, 0xD8, 0xFF, 0xFF}, // RGB332 = (4, 6, 3) + {0x90, 0xFC, 0x00, 0xFF}, // RGB332 = (4, 7, 0) + {0x90, 0xFC, 0x55, 0xFF}, // RGB332 = (4, 7, 1) + {0x90, 0xFC, 0xAA, 0xFF}, // RGB332 = (4, 7, 2) + {0x90, 0xFC, 0xFF, 0xFF}, // RGB332 = (4, 7, 3) + {0xB4, 0x00, 0x55, 0xFF}, // RGB332 = (5, 0, 1) + {0xB4, 0x00, 0xFF, 0xFF}, // RGB332 = (5, 0, 3) + {0xB4, 0x24, 0x00, 0xFF}, // RGB332 = (5, 1, 0) + {0xB4, 0x24, 0x55, 0xFF}, // RGB332 = (5, 1, 1) + {0xB4, 0x24, 0xAA, 0xFF}, // RGB332 = (5, 1, 2) + {0xB4, 0x24, 0xFF, 0xFF}, // RGB332 = (5, 1, 3) + {0xB4, 0x48, 0x55, 0xFF}, // RGB332 = (5, 2, 1) + {0xB4, 0x48, 0xAA, 0xFF}, // RGB332 = (5, 2, 2) + {0xB4, 0x48, 0xFF, 0xFF}, // RGB332 = (5, 2, 3) + {0xB4, 0x6C, 0x00, 0xFF}, // RGB332 = (5, 3, 0) + {0xB4, 0x6C, 0x55, 0xFF}, // RGB332 = (5, 3, 1) + {0xB4, 0x6C, 0xAA, 0xFF}, // RGB332 = (5, 3, 2) + {0xB4, 0x6C, 0xFF, 0xFF}, // RGB332 = (5, 3, 3) + {0xB4, 0x90, 0x00, 0xFF}, // RGB332 = (5, 4, 0) + {0xB4, 0x90, 0x55, 0xFF}, // RGB332 = (5, 4, 1) + {0xB4, 0x90, 0xAA, 0xFF}, // RGB332 = (5, 4, 2) + {0xB4, 0x90, 0xFF, 0xFF}, // RGB332 = (5, 4, 3) + {0xB4, 0xB4, 0x00, 0xFF}, // RGB332 = (5, 5, 0) + {0xB4, 0xB4, 0x55, 0xFF}, // RGB332 = (5, 5, 1) + {0xB4, 0xB4, 0xFF, 0xFF}, // RGB332 = (5, 5, 3) + {0xB4, 0xD8, 0x00, 0xFF}, // RGB332 = (5, 6, 0) + {0xB4, 0xD8, 0x55, 0xFF}, // RGB332 = (5, 6, 1) + {0xB4, 0xD8, 0xAA, 0xFF}, // RGB332 = (5, 6, 2) + {0xB4, 0xD8, 0xFF, 0xFF}, // RGB332 = (5, 6, 3) + {0xB4, 0xFC, 0x00, 0xFF}, // RGB332 = (5, 7, 0) + {0xB4, 0xFC, 0x55, 0xFF}, // RGB332 = (5, 7, 1) + {0xB4, 0xFC, 0xAA, 0xFF}, // RGB332 = (5, 7, 2) + {0xB4, 0xFC, 0xFF, 0xFF}, // RGB332 = (5, 7, 3) + {0xD8, 0x00, 0x00, 0xFF}, // RGB332 = (6, 0, 0) + {0xD8, 0x00, 0x55, 0xFF}, // RGB332 = (6, 0, 1) + {0xD8, 0x00, 0xAA, 0xFF}, // RGB332 = (6, 0, 2) + {0xD8, 0x00, 0xFF, 0xFF}, // RGB332 = (6, 0, 3) + {0xD8, 0x24, 0x00, 0xFF}, // RGB332 = (6, 1, 0) + {0xD8, 0x24, 0x55, 0xFF}, // RGB332 = (6, 1, 1) + {0xD8, 0x24, 0xAA, 0xFF}, // RGB332 = (6, 1, 2) + {0xD8, 0x24, 0xFF, 0xFF}, // RGB332 = (6, 1, 3) + {0xD8, 0x48, 0x00, 0xFF}, // RGB332 = (6, 2, 0) + {0xD8, 0x48, 0x55, 0xFF}, // RGB332 = (6, 2, 1) + {0xD8, 0x48, 0xAA, 0xFF}, // RGB332 = (6, 2, 2) + {0xD8, 0x48, 0xFF, 0xFF}, // RGB332 = (6, 2, 3) + {0xD8, 0x6C, 0x00, 0xFF}, // RGB332 = (6, 3, 0) + {0xD8, 0x6C, 0x55, 0xFF}, // RGB332 = (6, 3, 1) + {0xD8, 0x6C, 0xAA, 0xFF}, // RGB332 = (6, 3, 2) + {0xD8, 0x6C, 0xFF, 0xFF}, // RGB332 = (6, 3, 3) + {0xD8, 0x90, 0x00, 0xFF}, // RGB332 = (6, 4, 0) + {0xD8, 0x90, 0x55, 0xFF}, // RGB332 = (6, 4, 1) + {0xD8, 0x90, 0xAA, 0xFF}, // RGB332 = (6, 4, 2) + {0xD8, 0x90, 0xFF, 0xFF}, // RGB332 = (6, 4, 3) + {0xD8, 0xB4, 0x00, 0xFF}, // RGB332 = (6, 5, 0) + {0xD8, 0xB4, 0x55, 0xFF}, // RGB332 = (6, 5, 1) + {0xD8, 0xB4, 0xAA, 0xFF}, // RGB332 = (6, 5, 2) + {0xD8, 0xB4, 0xFF, 0xFF}, // RGB332 = (6, 5, 3) + {0xD8, 0xD8, 0x00, 0xFF}, // RGB332 = (6, 6, 0) + {0xD8, 0xD8, 0x55, 0xFF}, // RGB332 = (6, 6, 1) + {0xD8, 0xD8, 0xAA, 0xFF}, // RGB332 = (6, 6, 2) + {0xD8, 0xD8, 0xFF, 0xFF}, // RGB332 = (6, 6, 3) + {0xD8, 0xFC, 0x00, 0xFF}, // RGB332 = (6, 7, 0) + {0xD8, 0xFC, 0x55, 0xFF}, // RGB332 = (6, 7, 1) + {0xD8, 0xFC, 0xAA, 0xFF}, // RGB332 = (6, 7, 2) + {0xD8, 0xFC, 0xFF, 0xFF}, // RGB332 = (6, 7, 3) + {0xFC, 0x00, 0x00, 0xFF}, // RGB332 = (7, 0, 0) + {0xFC, 0x00, 0x55, 0xFF}, // RGB332 = (7, 0, 1) + {0xFC, 0x00, 0xAA, 0xFF}, // RGB332 = (7, 0, 2) + {0xFC, 0x00, 0xFF, 0xFF}, // RGB332 = (7, 0, 3) + {0xFC, 0x24, 0x00, 0xFF}, // RGB332 = (7, 1, 0) + {0xFC, 0x24, 0x55, 0xFF}, // RGB332 = (7, 1, 1) + {0xFC, 0x24, 0xAA, 0xFF}, // RGB332 = (7, 1, 2) + {0xFC, 0x24, 0xFF, 0xFF}, // RGB332 = (7, 1, 3) + {0xFC, 0x48, 0x00, 0xFF}, // RGB332 = (7, 2, 0) + {0xFC, 0x48, 0xAA, 0xFF}, // RGB332 = (7, 2, 2) + {0xFC, 0x6C, 0x00, 0xFF}, // RGB332 = (7, 3, 0) + {0xFC, 0x6C, 0x55, 0xFF}, // RGB332 = (7, 3, 1) + {0xFC, 0x6C, 0xAA, 0xFF}, // RGB332 = (7, 3, 2) + {0xFC, 0x6C, 0xFF, 0xFF}, // RGB332 = (7, 3, 3) + {0xFC, 0x90, 0x00, 0xFF}, // RGB332 = (7, 4, 0) + {0xFC, 0x90, 0x55, 0xFF}, // RGB332 = (7, 4, 1) + {0xFC, 0x90, 0xAA, 0xFF}, // RGB332 = (7, 4, 2) + {0xFC, 0x90, 0xFF, 0xFF}, // RGB332 = (7, 4, 3) + {0xFC, 0xB4, 0x00, 0xFF}, // RGB332 = (7, 5, 0) + {0xFC, 0xB4, 0x55, 0xFF}, // RGB332 = (7, 5, 1) + {0xFC, 0xB4, 0xAA, 0xFF}, // RGB332 = (7, 5, 2) + {0xFC, 0xB4, 0xFF, 0xFF}, // RGB332 = (7, 5, 3) + {0xFC, 0xD8, 0x00, 0xFF}, // RGB332 = (7, 6, 0) + {0xFC, 0xD8, 0x55, 0xFF}, // RGB332 = (7, 6, 1) + {0xFC, 0xD8, 0xAA, 0xFF}, // RGB332 = (7, 6, 2) + {0xFC, 0xD8, 0xFF, 0xFF}, // RGB332 = (7, 6, 3) + {0xFC, 0xFC, 0x00, 0xFF}, // RGB332 = (7, 7, 0) + {0xFC, 0xFC, 0xAA, 0xFF}, // RGB332 = (7, 7, 2) + }; + +grub_err_t +grub_video_fb_init (void) +{ + grub_free (framebuffer.palette); + framebuffer.render_target = 0; + framebuffer.back_target = 0; + framebuffer.palette = 0; + framebuffer.palette_size = 0; + framebuffer.set_page = 0; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_fini (void) +{ + /* TODO: destroy render targets. */ + + grub_free (framebuffer.offscreen_buffer); + grub_free (framebuffer.palette); + framebuffer.render_target = 0; + framebuffer.back_target = 0; + framebuffer.palette = 0; + framebuffer.palette_size = 0; + framebuffer.set_page = 0; + framebuffer.offscreen_buffer = 0; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_info (struct grub_video_mode_info *mode_info) +{ + /* Copy mode info from active render target. */ + grub_memcpy (mode_info, &framebuffer.render_target->mode_info, + sizeof (struct grub_video_mode_info)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned int i; + + /* Assume that we know everything from index color palette. */ + for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++) + palette_data[i] = framebuffer.palette[start + i]; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned i; + if (start + count > framebuffer.palette_size) + { + framebuffer.palette_size = start + count; + framebuffer.palette = grub_realloc (framebuffer.palette, + sizeof (framebuffer.palette[0]) + * framebuffer.palette_size); + if (!framebuffer.palette) + { + grub_video_fb_fini (); + return grub_errno; + } + } + for (i = 0; (i < count) && ((i + start) < framebuffer.palette_size); i++) + framebuffer.palette[start + i] = palette_data[i]; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_fb_set_area (void) +{ + unsigned int viewport_x1 = framebuffer.render_target->viewport.x; + unsigned int viewport_y1 = framebuffer.render_target->viewport.y; + unsigned int viewport_width = framebuffer.render_target->viewport.width; + unsigned int viewport_height = framebuffer.render_target->viewport.height; + unsigned int viewport_x2 = viewport_x1 + viewport_width; + unsigned int viewport_y2 = viewport_y1 + viewport_height; + + unsigned int region_x1 = framebuffer.render_target->region.x; + unsigned int region_y1 = framebuffer.render_target->region.y; + unsigned int region_width = framebuffer.render_target->region.width; + unsigned int region_height = framebuffer.render_target->region.height; + unsigned int region_x2 = region_x1 + region_width; + unsigned int region_y2 = region_y1 + region_height; + + unsigned int max_x1 = grub_max (viewport_x1, region_x1); + unsigned int min_x2 = grub_min (viewport_x2, region_x2); + unsigned int max_y1 = grub_max (viewport_y1, region_y1); + unsigned int min_y2 = grub_min (viewport_y2, region_y2); + + /* Viewport and region do not intersect. */ + if (viewport_width == 0 || viewport_height == 0 || region_width == 0 + || region_height == 0 || max_x1 >= min_x2 || max_y1 >= min_y2) + { + framebuffer.render_target->area.x = 0; + framebuffer.render_target->area.y = 0; + framebuffer.render_target->area.width = 0; + framebuffer.render_target->area.height = 0; + framebuffer.render_target->area_offset_x = 0; + framebuffer.render_target->area_offset_y = 0; + return GRUB_ERR_NONE; + } + + /* There is non-zero intersection. */ + framebuffer.render_target->area.x = max_x1; + framebuffer.render_target->area.y = max_y1; + framebuffer.render_target->area.width = min_x2 - max_x1; + framebuffer.render_target->area.height = min_y2 - max_y1; + + if (region_x1 > viewport_x1) + framebuffer.render_target->area_offset_x = (int)region_x1 + - (int)viewport_x1; + else + framebuffer.render_target->area_offset_x = 0; + if (region_y1 > viewport_y1) + framebuffer.render_target->area_offset_y = (int)region_y1 + - (int)viewport_y1; + else + framebuffer.render_target->area_offset_y = 0; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_viewport (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + /* Make sure viewport is within screen dimensions. If viewport was set + to be out of the screen, mark its size as zero. */ + if (x > framebuffer.render_target->mode_info.width) + { + x = 0; + width = 0; + } + + if (y > framebuffer.render_target->mode_info.height) + { + y = 0; + height = 0; + } + + if (x + width > framebuffer.render_target->mode_info.width) + width = framebuffer.render_target->mode_info.width - x; + + if (y + height > framebuffer.render_target->mode_info.height) + height = framebuffer.render_target->mode_info.height - y; + + framebuffer.render_target->viewport.x = x; + framebuffer.render_target->viewport.y = y; + framebuffer.render_target->viewport.width = width; + framebuffer.render_target->viewport.height = height; + + /* Count drawing area only if needed. */ + if (framebuffer.render_target->area_enabled) + grub_video_fb_set_area (); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_viewport (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height) +{ + if (x) *x = framebuffer.render_target->viewport.x; + if (y) *y = framebuffer.render_target->viewport.y; + if (width) *width = framebuffer.render_target->viewport.width; + if (height) *height = framebuffer.render_target->viewport.height; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_region (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + /* Make sure region is within screen dimensions. If region was set + to be out of the screen, mark its size as zero. */ + if (x > framebuffer.render_target->mode_info.width) + { + x = 0; + width = 0; + } + + if (y > framebuffer.render_target->mode_info.height) + { + y = 0; + height = 0; + } + + if (x + width > framebuffer.render_target->mode_info.width) + width = framebuffer.render_target->mode_info.width - x; + + if (y + height > framebuffer.render_target->mode_info.height) + height = framebuffer.render_target->mode_info.height - y; + + framebuffer.render_target->region.x = x; + framebuffer.render_target->region.y = y; + framebuffer.render_target->region.width = width; + framebuffer.render_target->region.height = height; + + /* If we have called set_region then area is needed. */ + grub_video_fb_set_area (); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_region (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height) +{ + if (x) *x = framebuffer.render_target->region.x; + if (y) *y = framebuffer.render_target->region.y; + if (width) *width = framebuffer.render_target->region.width; + if (height) *height = framebuffer.render_target->region.height; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_area_status (grub_video_area_status_t area_status) +{ + if (area_status == GRUB_VIDEO_AREA_ENABLED) + framebuffer.render_target->area_enabled = 1; + else + framebuffer.render_target->area_enabled = 0; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_area_status (grub_video_area_status_t *area_status) +{ + if (!area_status) + return GRUB_ERR_NONE; + + if (framebuffer.render_target->area_enabled) + *area_status = GRUB_VIDEO_AREA_ENABLED; + else + *area_status = GRUB_VIDEO_AREA_DISABLED; + + return GRUB_ERR_NONE; +} + +/* Maps color name to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_color (grub_uint32_t color_name) +{ + /* TODO: implement color theme mapping code. */ + + if (color_name < framebuffer.palette_size) + { + if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + return color_name; + else + { + grub_video_color_t color; + + color = grub_video_fb_map_rgb (framebuffer.palette[color_name].r, + framebuffer.palette[color_name].g, + framebuffer.palette[color_name].b); + + return color; + } + } + + return 0; +} + +/* Maps RGB to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_rgb (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue) +{ + if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + { + int minindex = 0; + int delta = 0; + int tmp; + int val; + unsigned i; + + /* Find best matching color. */ + for (i = 0; i < framebuffer.palette_size; i++) + { + val = framebuffer.palette[i].r - red; + tmp = val * val; + val = framebuffer.palette[i].g - green; + tmp += val * val; + val = framebuffer.palette[i].b - blue; + tmp += val * val; + + if (i == 0) + delta = tmp; + + if (tmp < delta) + { + delta = tmp; + minindex = i; + if (tmp == 0) + break; + } + } + + return minindex; + } + else if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == framebuffer.render_target->mode_info.fg_red + && green == framebuffer.render_target->mode_info.fg_green + && blue == framebuffer.render_target->mode_info.fg_blue) + return 1; + else + return 0; + } + else + { + grub_uint32_t value; + grub_uint8_t alpha = 255; /* Opaque color. */ + + red >>= 8 - framebuffer.render_target->mode_info.red_mask_size; + green >>= 8 - framebuffer.render_target->mode_info.green_mask_size; + blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size; + alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size; + + value = red << framebuffer.render_target->mode_info.red_field_pos; + value |= green << framebuffer.render_target->mode_info.green_field_pos; + value |= blue << framebuffer.render_target->mode_info.blue_field_pos; + value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos; + + return value; + } + +} + +/* Maps RGBA to target optimized color format. */ +grub_video_color_t +grub_video_fb_map_rgba (grub_uint8_t red, grub_uint8_t green, + grub_uint8_t blue, grub_uint8_t alpha) +{ + + if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + { + if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_ALPHA) != 0 + && alpha == 0) + return 0xf0; + /* No alpha available in index color modes, just use + same value as in only RGB modes. */ + return grub_video_fb_map_rgb (red, green, blue); + } + else if ((framebuffer.render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == framebuffer.render_target->mode_info.fg_red + && green == framebuffer.render_target->mode_info.fg_green + && blue == framebuffer.render_target->mode_info.fg_blue + && alpha == framebuffer.render_target->mode_info.fg_alpha) + return 1; + else + return 0; + } + else + { + grub_uint32_t value; + + red >>= 8 - framebuffer.render_target->mode_info.red_mask_size; + green >>= 8 - framebuffer.render_target->mode_info.green_mask_size; + blue >>= 8 - framebuffer.render_target->mode_info.blue_mask_size; + alpha >>= 8 - framebuffer.render_target->mode_info.reserved_mask_size; + + value = red << framebuffer.render_target->mode_info.red_field_pos; + value |= green << framebuffer.render_target->mode_info.green_field_pos; + value |= blue << framebuffer.render_target->mode_info.blue_field_pos; + value |= alpha << framebuffer.render_target->mode_info.reserved_field_pos; + + return value; + } +} + +/* Splits target optimized format to components. */ +grub_err_t +grub_video_fb_unmap_color (grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha) +{ + struct grub_video_fbblit_info target_info; + + target_info.mode_info = &framebuffer.render_target->mode_info; + target_info.data = framebuffer.render_target->data; + + grub_video_fb_unmap_color_int (&target_info, color, red, green, blue, alpha); + + return GRUB_ERR_NONE; +} + +/* Splits color in source format to components. */ +void +grub_video_fb_unmap_color_int (struct grub_video_fbblit_info * source, + grub_video_color_t color, + grub_uint8_t *red, grub_uint8_t *green, + grub_uint8_t *blue, grub_uint8_t *alpha) +{ + struct grub_video_mode_info *mode_info; + mode_info = source->mode_info; + + if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != 0) + { + if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_ALPHA) != 0 + && color == 0xf0) + { + *red = 0; + *green = 0; + *blue = 0; + *alpha = 0; + return; + } + + /* If we have an out-of-bounds color, return transparent black. */ + if (color > 255) + { + *red = 0; + *green = 0; + *blue = 0; + *alpha = 0; + return; + } + + *red = framebuffer.palette[color].r; + *green = framebuffer.palette[color].g; + *blue = framebuffer.palette[color].b; + *alpha = framebuffer.palette[color].a; + return; + } + else if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (color & 1) + { + *red = mode_info->fg_red; + *green = mode_info->fg_green; + *blue = mode_info->fg_blue; + *alpha = mode_info->fg_alpha; + } + else + { + *red = mode_info->bg_red; + *green = mode_info->bg_green; + *blue = mode_info->bg_blue; + *alpha = mode_info->bg_alpha; + } + } + else + { + grub_uint32_t tmp; + + /* Get red component. */ + tmp = color >> mode_info->red_field_pos; + tmp &= (1 << mode_info->red_mask_size) - 1; + tmp <<= 8 - mode_info->red_mask_size; + tmp |= (1 << (8 - mode_info->red_mask_size)) - 1; + *red = tmp & 0xFF; + + /* Get green component. */ + tmp = color >> mode_info->green_field_pos; + tmp &= (1 << mode_info->green_mask_size) - 1; + tmp <<= 8 - mode_info->green_mask_size; + tmp |= (1 << (8 - mode_info->green_mask_size)) - 1; + *green = tmp & 0xFF; + + /* Get blue component. */ + tmp = color >> mode_info->blue_field_pos; + tmp &= (1 << mode_info->blue_mask_size) - 1; + tmp <<= 8 - mode_info->blue_mask_size; + tmp |= (1 << (8 - mode_info->blue_mask_size)) - 1; + *blue = tmp & 0xFF; + + /* Get alpha component. */ + if (source->mode_info->reserved_mask_size > 0) + { + tmp = color >> mode_info->reserved_field_pos; + tmp &= (1 << mode_info->reserved_mask_size) - 1; + tmp <<= 8 - mode_info->reserved_mask_size; + tmp |= (1 << (8 - mode_info->reserved_mask_size)) - 1; + } + else + /* If there is no alpha component, assume it opaque. */ + tmp = 255; + + *alpha = tmp & 0xFF; + } +} + +static void +dirty (int y, int height) +{ + if (framebuffer.render_target != framebuffer.back_target) + return; + if (framebuffer.current_dirty.first_line > y) + framebuffer.current_dirty.first_line = y; + if (framebuffer.current_dirty.last_line < y + height) + framebuffer.current_dirty.last_line = y + height; +} + +grub_err_t +grub_video_fb_fill_rect (grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info target; + unsigned int area_x; + unsigned int area_y; + unsigned int area_width; + unsigned int area_height; + if (framebuffer.render_target->area_enabled) + { + area_x = framebuffer.render_target->area.x; + area_y = framebuffer.render_target->area.y; + area_width = framebuffer.render_target->area.width; + area_height = framebuffer.render_target->area.height; + x -= framebuffer.render_target->area_offset_x; + y -= framebuffer.render_target->area_offset_y; + } + else + { + area_x = framebuffer.render_target->viewport.x; + area_y = framebuffer.render_target->viewport.y; + area_width = framebuffer.render_target->viewport.width; + area_height = framebuffer.render_target->viewport.height; + } + + /* Make sure there is something to do. */ + if ((area_width == 0) || (area_height == 0)) + return GRUB_ERR_NONE; + if ((x >= (int)area_width) || (x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((y >= (int)area_height) || (y + (int)height < 0)) + return GRUB_ERR_NONE; + + /* Do not allow drawing out of area. */ + if (x < 0) + { + width += x; + x = 0; + } + if (y < 0) + { + height += y; + y = 0; + } + + if ((x + width) > area_width) + width = area_width - x; + if ((y + height) > area_height) + height = area_height - y; + + /* Add area offset. */ + x += area_x; + y += area_y; + + dirty (y, height); + + /* Use fbblit_info to encapsulate rendering. */ + target.mode_info = &framebuffer.render_target->mode_info; + target.data = framebuffer.render_target->data; + + grub_video_fb_fill_dispatch (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; +} + +static inline grub_err_t __attribute__ ((always_inline)) +grub_video_fb_blit_source (struct grub_video_fbblit_info *source, + enum grub_video_blit_operators oper, int x, int y, + int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info target; + unsigned int area_x; + unsigned int area_y; + unsigned int area_width; + unsigned int area_height; + if (framebuffer.render_target->area_enabled) + { + area_x = framebuffer.render_target->area.x; + area_y = framebuffer.render_target->area.y; + area_width = framebuffer.render_target->area.width; + area_height = framebuffer.render_target->area.height; + x -= framebuffer.render_target->area_offset_x; + y -= framebuffer.render_target->area_offset_y; + } + else + { + area_x = framebuffer.render_target->viewport.x; + area_y = framebuffer.render_target->viewport.y; + area_width = framebuffer.render_target->viewport.width; + area_height = framebuffer.render_target->viewport.height; + } + + /* Make sure there is something to do. */ + if ((area_width == 0) || (area_height == 0) || (width == 0) || (height == 0)) + return GRUB_ERR_NONE; + if ((x >= (int)area_width) || (x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((y >= (int)area_height) || (y + (int)height < 0)) + return GRUB_ERR_NONE; + if ((x + (int)source->mode_info->width) < 0) + return GRUB_ERR_NONE; + if ((y + (int)source->mode_info->height) < 0) + return GRUB_ERR_NONE; + if ((offset_x >= (int)source->mode_info->width) + || (offset_x + (int)width < 0)) + return GRUB_ERR_NONE; + if ((offset_y >= (int)source->mode_info->height) + || (offset_y + (int)height < 0)) + return GRUB_ERR_NONE; + + /* If we have negative coordinates, optimize drawing to minimum. */ + if (offset_x < 0) + { + width += offset_x; + x -= offset_x; + offset_x = 0; + } + + if (offset_y < 0) + { + height += offset_y; + y -= offset_y; + offset_y = 0; + } + + if (x < 0) + { + width += x; + offset_x -= x; + x = 0; + } + + if (y < 0) + { + height += y; + offset_y -= y; + y = 0; + } + + /* Do not allow drawing out of area. */ + if ((x + width) > area_width) + width = area_width - x; + if ((y + height) > area_height) + height = area_height - y; + + if ((offset_x + width) > source->mode_info->width) + width = source->mode_info->width - offset_x; + if ((offset_y + height) > source->mode_info->height) + height = source->mode_info->height - offset_y; + + /* Limit drawing to source render target dimensions. */ + if (width > source->mode_info->width) + width = source->mode_info->width; + + if (height > source->mode_info->height) + height = source->mode_info->height; + + /* Add viewport offset. */ + x += area_x; + y += area_y; + + /* Use fbblit_info to encapsulate rendering. */ + target.mode_info = &framebuffer.render_target->mode_info; + target.data = framebuffer.render_target->data; + + /* Do actual blitting. */ + dirty (y, height); + grub_video_fb_dispatch_blit (&target, source, oper, x, y, width, height, + offset_x, offset_y); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_blit_bitmap (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, int x, int y, + int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info source_info; + source_info.mode_info = &bitmap->mode_info; + source_info.data = bitmap->data; + + return grub_video_fb_blit_source (&source_info, oper, x, y, + offset_x, offset_y, width, height); +} + +grub_err_t +grub_video_fb_blit_render_target (struct grub_video_fbrender_target *source, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + struct grub_video_fbblit_info source_info; + source_info.mode_info = &source->mode_info; + source_info.data = source->data; + + return grub_video_fb_blit_source (&source_info, oper, x, y, + offset_x, offset_y, width, height); +} + +grub_err_t +grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) +{ + int width; + int height; + int src_x; + int src_y; + int dst_x; + int dst_y; + + /* 1. Check if we have something to do. */ + if ((dx == 0) && (dy == 0)) + return GRUB_ERR_NONE; + + width = framebuffer.render_target->viewport.width - grub_abs (dx); + height = framebuffer.render_target->viewport.height - grub_abs (dy); + + dirty (framebuffer.render_target->viewport.y, + framebuffer.render_target->viewport.height); + + if (dx < 0) + { + src_x = framebuffer.render_target->viewport.x - dx; + dst_x = framebuffer.render_target->viewport.x; + } + else + { + src_x = framebuffer.render_target->viewport.x; + dst_x = framebuffer.render_target->viewport.x + dx; + } + + if (dy < 0) + { + src_y = framebuffer.render_target->viewport.y - dy; + dst_y = framebuffer.render_target->viewport.y; + } + else + { + src_y = framebuffer.render_target->viewport.y; + dst_y = framebuffer.render_target->viewport.y + dy; + } + + /* 2. Check if there is need to copy data. */ + if ((grub_abs (dx) < framebuffer.render_target->viewport.width) + && (grub_abs (dy) < framebuffer.render_target->viewport.height)) + { + /* 3. Move data in render target. */ + struct grub_video_fbblit_info target; + int i, j; + int linedelta, linelen; + + target.mode_info = &framebuffer.render_target->mode_info; + target.data = framebuffer.render_target->data; + + linedelta = target.mode_info->pitch + - width * target.mode_info->bytes_per_pixel; + linelen = width * target.mode_info->bytes_per_pixel; +#define DO_SCROLL \ + /* Check vertical direction of the move. */ \ + if (dy < 0 || (dy == 0 && dx < 0)) \ + { \ + dst = (void *) grub_video_fb_get_video_ptr (&target, \ + dst_x, dst_y); \ + src = (void *) grub_video_fb_get_video_ptr (&target, \ + src_x, src_y); \ + /* 3a. Move data upwards. */ \ + for (j = 0; j < height; j++) \ + { \ + for (i = 0; i < linelen; i++) \ + *(dst++) = *(src++); \ + dst += linedelta; \ + src += linedelta; \ + } \ + } \ + else \ + { \ + /* 3b. Move data downwards. */ \ + dst = (void *) grub_video_fb_get_video_ptr (&target, \ + dst_x + width, \ + dst_y + height - 1); \ + src = (void *) grub_video_fb_get_video_ptr (&target, \ + src_x + width, \ + src_y + height - 1); \ + dst--; \ + src--; \ + for (j = 0; j < height; j++) \ + { \ + for (i = 0; i < linelen; i++) \ + *(dst--) = *(src--); \ + dst -= linedelta; \ + src -= linedelta; \ + } \ + } + + /* If everything is aligned on 32-bit use 32-bit copy. */ + if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y) + % sizeof (grub_uint32_t) == 0 + && (grub_addr_t) grub_video_fb_get_video_ptr (&target, dst_x, dst_y) + % sizeof (grub_uint32_t) == 0 + && linelen % sizeof (grub_uint32_t) == 0 + && linedelta % sizeof (grub_uint32_t) == 0) + { + grub_uint32_t *src, *dst; + linelen /= sizeof (grub_uint32_t); + linedelta /= sizeof (grub_uint32_t); + DO_SCROLL + } + /* If everything is aligned on 16-bit use 16-bit copy. */ + else if ((grub_addr_t) grub_video_fb_get_video_ptr (&target, src_x, src_y) + % sizeof (grub_uint16_t) == 0 + && (grub_addr_t) grub_video_fb_get_video_ptr (&target, + dst_x, dst_y) + % sizeof (grub_uint16_t) == 0 + && linelen % sizeof (grub_uint16_t) == 0 + && linedelta % sizeof (grub_uint16_t) == 0) + { + grub_uint16_t *src, *dst; + linelen /= sizeof (grub_uint16_t); + linedelta /= sizeof (grub_uint16_t); + DO_SCROLL + } + /* If not aligned at all use 8-bit copy. */ + else + { + grub_uint8_t *src, *dst; + DO_SCROLL + } + } + + /* 4. Fill empty space with specified color. In this implementation + there might be colliding areas but at the moment there is no need + to optimize this. */ + + /* 4a. Fill top & bottom parts. */ + if (dy > 0) + grub_video_fb_fill_rect (color, 0, 0, framebuffer.render_target->viewport.width, dy); + else if (dy < 0) + { + if (framebuffer.render_target->viewport.height < grub_abs (dy)) + dy = -framebuffer.render_target->viewport.height; + + grub_video_fb_fill_rect (color, 0, framebuffer.render_target->viewport.height + dy, + framebuffer.render_target->viewport.width, -dy); + } + + /* 4b. Fill left & right parts. */ + if (dx > 0) + grub_video_fb_fill_rect (color, 0, 0, + dx, framebuffer.render_target->viewport.height); + else if (dx < 0) + { + if (framebuffer.render_target->viewport.width < grub_abs (dx)) + dx = -framebuffer.render_target->viewport.width; + + grub_video_fb_fill_rect (color, framebuffer.render_target->viewport.width + dx, 0, + -dx, framebuffer.render_target->viewport.height); + } + + return GRUB_ERR_NONE; +} + + +grub_err_t +grub_video_fb_create_render_target (struct grub_video_fbrender_target **result, + unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused))) +{ + struct grub_video_fbrender_target *target; + unsigned int size; + + /* Validate arguments. */ + if ((! result) + || (width == 0) + || (height == 0)) + return grub_error (GRUB_ERR_BUG, + "invalid argument given"); + + /* Allocate memory for render target. */ + target = grub_malloc (sizeof (struct grub_video_fbrender_target)); + if (! target) + return grub_errno; + + /* TODO: Implement other types too. + Currently only 32bit render targets are supported. */ + + /* Mark render target as allocated. */ + target->is_allocated = 1; + + /* Maximize viewport, region and area. */ + target->viewport.x = 0; + target->viewport.y = 0; + target->viewport.width = width; + target->viewport.height = height; + + target->region.x = 0; + target->region.y = 0; + target->region.width = width; + target->region.height = height; + + target->area_enabled = 0; + target->area.x = 0; + target->area.y = 0; + target->area.width = width; + target->area.height = height; + target->area_offset_x = 0; + target->area_offset_y = 0; + + /* Setup render target format. */ + target->mode_info.width = width; + target->mode_info.height = height; + switch (mode_type) + { + case GRUB_VIDEO_MODE_TYPE_INDEX_COLOR + | GRUB_VIDEO_MODE_TYPE_ALPHA: + target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR + | GRUB_VIDEO_MODE_TYPE_ALPHA; + target->mode_info.bpp = 8; + target->mode_info.bytes_per_pixel = 1; + target->mode_info.number_of_colors = 16; + target->mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR_ALPHA; + break; + default: + target->mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA; + target->mode_info.bpp = 32; + target->mode_info.bytes_per_pixel = 4; + target->mode_info.red_mask_size = 8; + target->mode_info.red_field_pos = 0; + target->mode_info.green_mask_size = 8; + target->mode_info.green_field_pos = 8; + target->mode_info.blue_mask_size = 8; + target->mode_info.blue_field_pos = 16; + target->mode_info.reserved_mask_size = 8; + target->mode_info.reserved_field_pos = 24; + target->mode_info.number_of_colors = framebuffer.palette_size; /* Emulated palette. */ + target->mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_RGBA_8888; + break; + } + target->mode_info.pitch = target->mode_info.bytes_per_pixel * width; + + /* Calculate size needed for the data. */ + size = (width * target->mode_info.bytes_per_pixel) * height; + + target->data = grub_malloc (size); + if (! target->data) + { + grub_free (target); + return grub_errno; + } + + /* Clear render target with black and maximum transparency. */ + if (mode_type == (GRUB_VIDEO_MODE_TYPE_INDEX_COLOR + | GRUB_VIDEO_MODE_TYPE_ALPHA)) + grub_memset (target->data, 0xf0, size); + else + grub_memset (target->data, 0, size); + + /* TODO: Add render target to render target list. */ + + /* Save result to caller. */ + *result = target; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_create_render_target_from_pointer (struct grub_video_fbrender_target **result, + const struct grub_video_mode_info *mode_info, + void *ptr) +{ + struct grub_video_fbrender_target *target; + unsigned y; + +#ifndef GRUB_HAVE_UNALIGNED_ACCESS + if (!(mode_info->bytes_per_pixel & (mode_info->bytes_per_pixel - 1)) + && ((grub_addr_t) ptr & (mode_info->bytes_per_pixel - 1))) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unaligned pointer"); + if (!(mode_info->bytes_per_pixel & (mode_info->bytes_per_pixel - 1)) + && (mode_info->pitch & (mode_info->bytes_per_pixel - 1))) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unaligned pitch"); +#endif + + /* Allocate memory for render target. */ + target = grub_malloc (sizeof (struct grub_video_fbrender_target)); + if (! target) + return grub_errno; + + /* Mark framebuffer memory as non allocated. */ + target->is_allocated = 0; + target->data = ptr; + + grub_memcpy (&(target->mode_info), mode_info, sizeof (target->mode_info)); + + /* Reset viewport, region and area to match new mode. */ + target->viewport.x = 0; + target->viewport.y = 0; + target->viewport.width = mode_info->width; + target->viewport.height = mode_info->height; + + target->region.x = 0; + target->region.y = 0; + target->region.width = mode_info->width; + target->region.height = mode_info->height; + + target->area_enabled = 0; + target->area.x = 0; + target->area.y = 0; + target->area.width = mode_info->width; + target->area.height = mode_info->height; + target->area_offset_x = 0; + target->area_offset_y = 0; + + /* Clear render target with black and maximum transparency. */ + for (y = 0; y < mode_info->height; y++) + grub_memset (target->data + mode_info->pitch * y, 0, + mode_info->bytes_per_pixel * mode_info->width); + + /* Save result to caller. */ + *result = target; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_delete_render_target (struct grub_video_fbrender_target *target) +{ + /* If there is no target, then just return without error. */ + if (! target) + return GRUB_ERR_NONE; + + /* TODO: Delist render target from render target list. */ + + /* If this is software render target, free it's memory. */ + if (target->is_allocated) + grub_free (target->data); + + /* Free render target. */ + grub_free (target); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target) +{ + if (target == (struct grub_video_fbrender_target *) + GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.back_target; + + if (! target->data) + return grub_error (GRUB_ERR_BUG, + "invalid render target given"); + + framebuffer.render_target = target; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **target) +{ + *target = framebuffer.render_target; + + if (*target == framebuffer.back_target) + *target = (struct grub_video_fbrender_target *) GRUB_VIDEO_RENDER_TARGET_DISPLAY; + + return GRUB_ERR_NONE; +} + +static grub_err_t +doublebuf_blit_update_screen (void) +{ + if (framebuffer.current_dirty.first_line + <= framebuffer.current_dirty.last_line) + { + grub_size_t copy_size; + + if (grub_sub (framebuffer.current_dirty.last_line, + framebuffer.current_dirty.first_line, ©_size) || + grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, ©_size)) + { + /* Shouldn't happen, but if it does we've a bug. */ + return GRUB_ERR_BUG; + } + + grub_memcpy ((char *) framebuffer.pages[0] + framebuffer.current_dirty.first_line * + framebuffer.back_target->mode_info.pitch, + (char *) framebuffer.back_target->data + framebuffer.current_dirty.first_line * + framebuffer.back_target->mode_info.pitch, + copy_size); + } + framebuffer.current_dirty.first_line + = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.last_line = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **back, + struct grub_video_mode_info mode_info, + volatile void *framebuf) +{ + grub_err_t err; + grub_size_t page_size = (grub_size_t) mode_info.pitch * mode_info.height; + + framebuffer.offscreen_buffer = grub_zalloc (page_size); + if (! framebuffer.offscreen_buffer) + return grub_errno; + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, + &mode_info, + framebuffer.offscreen_buffer); + + if (err) + { + grub_free (framebuffer.offscreen_buffer); + framebuffer.offscreen_buffer = 0; + return grub_errno; + } + (*back)->is_allocated = 1; + + framebuffer.update_screen = doublebuf_blit_update_screen; + framebuffer.pages[0] = framebuf; + framebuffer.displayed_page = 0; + framebuffer.render_page = 0; + framebuffer.current_dirty.first_line = mode_info.height; + framebuffer.current_dirty.last_line = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +doublebuf_pageflipping_update_screen (void) +{ + int new_displayed_page; + grub_err_t err; + int first_line, last_line; + + first_line = framebuffer.current_dirty.first_line; + last_line = framebuffer.current_dirty.last_line; + if (first_line > framebuffer.previous_dirty.first_line) + first_line = framebuffer.previous_dirty.first_line; + if (last_line < framebuffer.previous_dirty.last_line) + last_line = framebuffer.previous_dirty.last_line; + + if (first_line <= last_line) + { + grub_size_t copy_size; + + if (grub_sub (last_line, first_line, ©_size) || + grub_mul (framebuffer.back_target->mode_info.pitch, copy_size, ©_size)) + { + /* Shouldn't happen, but if it does we've a bug. */ + return GRUB_ERR_BUG; + } + + grub_memcpy ((char *) framebuffer.pages[framebuffer.render_page] + first_line * + framebuffer.back_target->mode_info.pitch, + (char *) framebuffer.back_target->data + first_line * + framebuffer.back_target->mode_info.pitch, + copy_size); + } + + framebuffer.previous_dirty = framebuffer.current_dirty; + framebuffer.current_dirty.first_line + = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.last_line = 0; + + /* Swap the page numbers in the framebuffer struct. */ + new_displayed_page = framebuffer.render_page; + framebuffer.render_page = framebuffer.displayed_page; + framebuffer.displayed_page = new_displayed_page; + + err = framebuffer.set_page (framebuffer.displayed_page); + if (err) + { + /* Restore previous state. */ + framebuffer.render_page = framebuffer.displayed_page; + framebuffer.displayed_page = new_displayed_page; + return err; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +doublebuf_pageflipping_init (struct grub_video_mode_info *mode_info, + volatile void *page0_ptr, + grub_video_fb_set_page_t set_page_in, + volatile void *page1_ptr) +{ + grub_err_t err; + grub_size_t page_size = 0; + + if (grub_mul (mode_info->pitch, mode_info->height, &page_size)) + { + /* Shouldn't happen, but if it does we've a bug. */ + return GRUB_ERR_BUG; + } + + framebuffer.offscreen_buffer = grub_malloc (page_size); + if (! framebuffer.offscreen_buffer) + { + return grub_errno; + } + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, + mode_info, + framebuffer.offscreen_buffer); + + if (err) + { + grub_free (framebuffer.offscreen_buffer); + framebuffer.offscreen_buffer = 0; + return grub_errno; + } + framebuffer.back_target->is_allocated = 1; + + framebuffer.displayed_page = 0; + framebuffer.render_page = 1; + + framebuffer.update_screen = doublebuf_pageflipping_update_screen; + framebuffer.pages[0] = page0_ptr; + framebuffer.pages[1] = page1_ptr; + + framebuffer.current_dirty.first_line + = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.last_line = 0; + framebuffer.previous_dirty.first_line + = framebuffer.back_target->mode_info.height; + framebuffer.previous_dirty.last_line = 0; + + /* Set the framebuffer memory data pointer and display the right page. */ + err = set_page_in (framebuffer.displayed_page); + if (err) + { + grub_video_fb_delete_render_target (framebuffer.back_target); + return err; + } + framebuffer.set_page = set_page_in; + + return GRUB_ERR_NONE; +} + +/* Select the best double buffering mode available. */ +grub_err_t +grub_video_fb_setup (unsigned int mode_type, unsigned int mode_mask, + struct grub_video_mode_info *mode_info, + volatile void *page0_ptr, + grub_video_fb_set_page_t set_page_in, + volatile void *page1_ptr) +{ + grub_err_t err; + + /* Do double buffering only if it's either requested or efficient. */ + if (set_page_in && grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + 1)) + { + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; + + err = doublebuf_pageflipping_init (mode_info, page0_ptr, + set_page_in, + page1_ptr); + if (!err) + { + framebuffer.render_target = framebuffer.back_target; + return GRUB_ERR_NONE; + } + + mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + if (grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + 1)) + { + /* It was much nicer with the cast directly at function call but + some older gcc versions don't accept it properly.*/ + void *tmp = (void *) page0_ptr; + mode_info->mode_type |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + err = grub_video_fb_doublebuf_blit_init (&framebuffer.back_target, + *mode_info, + tmp); + + if (!err) + { + framebuffer.render_target = framebuffer.back_target; + return GRUB_ERR_NONE; + } + + mode_info->mode_type &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + /* Fall back to no double buffering. */ + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, + mode_info, + (void *) page0_ptr); + + if (err) + return err; + + framebuffer.update_screen = 0; + framebuffer.pages[0] = page0_ptr; + framebuffer.displayed_page = 0; + framebuffer.render_page = 0; + framebuffer.set_page = 0; + framebuffer.current_dirty.first_line + = framebuffer.back_target->mode_info.height; + framebuffer.current_dirty.last_line = 0; + + mode_info->mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + framebuffer.render_target = framebuffer.back_target; + + return GRUB_ERR_NONE; +} + + +grub_err_t +grub_video_fb_swap_buffers (void) +{ + grub_err_t err; + if (!framebuffer.update_screen) + return GRUB_ERR_NONE; + + err = framebuffer.update_screen (); + if (err) + return err; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.back_target->mode_info), + sizeof (*mode_info)); + + /* We are about to load a kernel. Switch back to page zero, since some + kernel drivers expect that. */ + if (framebuffer.set_page && framebuffer.displayed_page != 0) + { + framebuffer.update_screen (); + } + + *framebuf = (void *) framebuffer.pages[framebuffer.displayed_page]; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/video/i386/pc/vbe.c b/grub-core/video/i386/pc/vbe.c new file mode 100644 index 0000000..b7f9119 --- /dev/null +++ b/grub-core/video/i386/pc/vbe.c @@ -0,0 +1,1250 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/machine/memory.h> +#include <grub/i386/pc/vbe.h> +#include <grub/video_fb.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/i386/pc/int.h> +#include <grub/i18n.h> +#include <grub/cpu/cpuid.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static int vbe_detected = -1; + +static struct grub_vbe_info_block controller_info; + +/* Track last mode to support cards which fail on get_mode. */ +static grub_uint32_t last_set_mode = 3; + +static struct +{ + struct grub_video_mode_info mode_info; + + grub_uint8_t *ptr; + int mtrr; +} framebuffer; + +static grub_uint32_t initial_vbe_mode; +static grub_uint16_t *vbe_mode_list; + +static void * +real2pm (grub_vbe_farptr_t ptr) +{ + return (void *) ((((unsigned long) ptr & 0xFFFF0000) >> 12UL) + + ((unsigned long) ptr & 0x0000FFFF)); +} + +#define rdmsr(num,a,d) \ + asm volatile ("rdmsr" : "=a" (a), "=d" (d) : "c" (num)) + +#define wrmsr(num,lo,hi) \ + asm volatile ("wrmsr" : : "c" (num), "a" (lo), "d" (hi) : "memory") + +#define mtrr_base(reg) (0x200 + (reg) * 2) +#define mtrr_mask(reg) (0x200 + (reg) * 2 + 1) + +/* Try to set up a variable-range write-combining MTRR for a memory region. + This is best-effort; if it seems too hard, we just accept the performance + degradation rather than risking undefined behaviour. It is intended + exclusively to work around BIOS bugs, as the BIOS should already be + setting up a suitable MTRR. */ +static void +grub_vbe_enable_mtrr_entry (int mtrr) +{ + grub_uint32_t eax, edx; + grub_uint32_t mask_lo, mask_hi; + + rdmsr (mtrr_mask (mtrr), eax, edx); + mask_lo = eax; + mask_hi = edx; + + mask_lo |= 0x800 /* valid */; + wrmsr (mtrr_mask (mtrr), mask_lo, mask_hi); +} + +static void +grub_vbe_enable_mtrr (grub_uint8_t *base, grub_size_t size) +{ + grub_uint32_t eax, ebx, ecx, edx; + grub_uint32_t features; + grub_uint32_t mtrrcap; + int var_mtrrs; + grub_uint32_t max_extended_cpuid; + grub_uint32_t maxphyaddr; + grub_uint64_t fb_base, fb_size; + grub_uint64_t size_bits, fb_mask; + grub_uint32_t bits_lo, bits_hi; + grub_uint64_t bits; + int i, first_unused = -1; + grub_uint32_t base_lo, base_hi, mask_lo, mask_hi; + + fb_base = (grub_uint64_t) (grub_size_t) base; + fb_size = (grub_uint64_t) size; + + /* Check that fb_base and fb_size can be represented using a single + MTRR. */ + + if (fb_base < (1 << 20)) + return; /* under 1MB, so covered by fixed-range MTRRs */ + if (fb_base >= (1LL << 36)) + return; /* over 36 bits, so out of range */ + if (fb_size < (1 << 12)) + return; /* variable-range MTRRs must cover at least 4KB */ + + size_bits = fb_size; + while (size_bits > 1) + size_bits >>= 1; + if (size_bits != 1) + return; /* not a power of two */ + + if (fb_base & (fb_size - 1)) + return; /* not aligned on size boundary */ + + fb_mask = ~(fb_size - 1); + + /* Check CPU capabilities. */ + + if (! grub_cpu_is_cpuid_supported ()) + return; + + grub_cpuid (1, eax, ebx, ecx, edx); + features = edx; + if (! (features & 0x00001000)) /* MTRR */ + return; + + rdmsr (0xFE, eax, edx); + mtrrcap = eax; + if (! (mtrrcap & 0x00000400)) /* write-combining */ + return; + var_mtrrs = (mtrrcap & 0xFF); + + grub_cpuid (0x80000000, eax, ebx, ecx, edx); + max_extended_cpuid = eax; + if (max_extended_cpuid >= 0x80000008) + { + grub_cpuid (0x80000008, eax, ebx, ecx, edx); + maxphyaddr = (eax & 0xFF); + } + else + maxphyaddr = 36; + bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */ + bits_hi = (1 << (maxphyaddr - 32)) - 1; + bits = bits_lo | ((grub_uint64_t) bits_hi << 32); + + /* Check whether an MTRR already covers this region. If not, take an + unused one if possible. */ + for (i = 0; i < var_mtrrs; i++) + { + rdmsr (mtrr_mask (i), eax, edx); + mask_lo = eax; + mask_hi = edx; + if (mask_lo & 0x800) /* valid */ + { + grub_uint64_t real_base, real_mask; + + rdmsr (mtrr_base (i), eax, edx); + base_lo = eax; + base_hi = edx; + + real_base = ((grub_uint64_t) (base_hi & bits_hi) << 32) | + (base_lo & bits_lo); + real_mask = ((grub_uint64_t) (mask_hi & bits_hi) << 32) | + (mask_lo & bits_lo); + if (real_base < (fb_base + fb_size) && + real_base + (~real_mask & bits) >= fb_base) + return; /* existing MTRR overlaps this region */ + } + else if (first_unused < 0) + first_unused = i; + } + + if (first_unused < 0) + return; /* all MTRRs in use */ + + /* Set up the first unused MTRR we found. */ + rdmsr (mtrr_base (first_unused), eax, edx); + base_lo = eax; + base_hi = edx; + rdmsr (mtrr_mask (first_unused), eax, edx); + mask_lo = eax; + mask_hi = edx; + + base_lo = (base_lo & ~bits_lo & ~0xFF) | + (fb_base & bits_lo) | 0x01 /* WC */; + base_hi = (base_hi & ~bits_hi) | ((fb_base >> 32) & bits_hi); + wrmsr (mtrr_base (first_unused), base_lo, base_hi); + mask_lo = (mask_lo & ~bits_lo) | (fb_mask & bits_lo) | 0x800 /* valid */; + mask_hi = (mask_hi & ~bits_hi) | ((fb_mask >> 32) & bits_hi); + wrmsr (mtrr_mask (first_unused), mask_lo, mask_hi); + + framebuffer.mtrr = first_unused; +} + +static void +grub_vbe_disable_mtrr (int mtrr) +{ + grub_uint32_t eax, edx; + grub_uint32_t mask_lo, mask_hi; + + rdmsr (mtrr_mask (mtrr), eax, edx); + mask_lo = eax; + mask_hi = edx; + + mask_lo &= ~0x800 /* valid */; + wrmsr (mtrr_mask (mtrr), mask_lo, mask_hi); +} + +/* Call VESA BIOS 0x4f09 to set palette data, return status. */ +static grub_vbe_status_t +grub_vbe_bios_set_palette_data (grub_uint32_t color_count, + grub_uint32_t start_index, + struct grub_vbe_palette_data *palette_data) +{ + struct grub_bios_int_registers regs; + regs.eax = 0x4f09; + regs.ebx = 0; + regs.ecx = color_count; + regs.edx = start_index; + regs.es = (((grub_addr_t) palette_data) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) palette_data) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_controller_info (struct grub_vbe_info_block *ci) +{ + struct grub_bios_int_registers regs; + /* Store *controller_info to %es:%di. */ + regs.es = (((grub_addr_t) ci) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) ci) & 0xffff; + regs.eax = 0x4f00; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f01 to get VBE Mode Information, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_mode_info (grub_uint32_t mode, + struct grub_vbe_mode_info_block *mode_info) +{ + struct grub_bios_int_registers regs; + regs.eax = 0x4f01; + regs.ecx = mode; + /* Store *mode_info to %es:%di. */ + regs.es = ((grub_addr_t) mode_info & 0xffff0000) >> 4; + regs.edi = (grub_addr_t) mode_info & 0x0000ffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f02 to set video mode, return status. */ +static grub_vbe_status_t +grub_vbe_bios_set_mode (grub_uint32_t mode, + struct grub_vbe_crtc_info_block *crtc_info) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f02; + regs.ebx = mode; + /* Store *crtc_info to %es:%di. */ + regs.es = (((grub_addr_t) crtc_info) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) crtc_info) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f03 to return current VBE Mode, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_mode (grub_uint32_t *mode) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f03; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + *mode = regs.ebx & 0xffff; + + return regs.eax & 0xffff; +} + +grub_vbe_status_t +grub_vbe_bios_getset_dac_palette_width (int set, int *dac_mask_size) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f08; + regs.ebx = ((*dac_mask_size & 0xff) << 8) | (set ? 1 : 0); + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + *dac_mask_size = (regs.ebx >> 8) & 0xff; + + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f05 to set memory window, return status. */ +grub_vbe_status_t +grub_vbe_bios_set_memory_window (grub_uint32_t window, + grub_uint32_t position) +{ + struct grub_bios_int_registers regs; + + /* BL = window, BH = 0, Set memory window. */ + regs.ebx = window & 0x00ff; + regs.edx = position; + regs.eax = 0x4f05; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f05 to return memory window, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_memory_window (grub_uint32_t window, + grub_uint32_t *position) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f05; + /* BH = 1, Get memory window. BL = window. */ + regs.ebx = (window & 0x00ff) | 0x100; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + *position = regs.edx & 0xffff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f06 to set scanline length (in bytes), return status. */ +grub_vbe_status_t +grub_vbe_bios_set_scanline_length (grub_uint32_t length) +{ + struct grub_bios_int_registers regs; + + regs.ecx = length; + regs.eax = 0x4f06; + /* BL = 2, Set Scan Line in Bytes. */ + regs.ebx = 0x0002; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f06 to return scanline length (in bytes), return status. */ +grub_vbe_status_t +grub_vbe_bios_get_scanline_length (grub_uint32_t *length) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f06; + regs.ebx = 0x0001; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + /* BL = 1, Get Scan Line Length (in bytes). */ + grub_bios_interrupt (0x10, ®s); + + *length = regs.ebx & 0xffff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f07 to set display start, return status. */ +static grub_vbe_status_t +grub_vbe_bios_set_display_start (grub_uint32_t x, grub_uint32_t y) +{ + struct grub_bios_int_registers regs; + + if (framebuffer.mtrr >= 0) + grub_vbe_disable_mtrr (framebuffer.mtrr); + + /* Store x in %ecx. */ + regs.ecx = x; + regs.edx = y; + regs.eax = 0x4f07; + /* BL = 80h, Set Display Start during Vertical Retrace. */ + regs.ebx = 0x0080; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + if (framebuffer.mtrr >= 0) + grub_vbe_enable_mtrr_entry (framebuffer.mtrr); + + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f07 to get display start, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_display_start (grub_uint32_t *x, + grub_uint32_t *y) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f07; + /* BL = 1, Get Display Start. */ + regs.ebx = 0x0001; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + *x = regs.ecx & 0xffff; + *y = regs.edx & 0xffff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f0a. */ +grub_vbe_status_t +grub_vbe_bios_get_pm_interface (grub_uint16_t *segment, grub_uint16_t *offset, + grub_uint16_t *length) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f0a; + regs.ebx = 0x0000; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + if ((regs.eax & 0xffff) != GRUB_VBE_STATUS_OK) + { + *segment = 0; + *offset = 0; + *length = 0; + } + + *segment = regs.es & 0xffff; + *offset = regs.edi & 0xffff; + *length = regs.ecx & 0xffff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f11 to get flat panel information, return status. */ +static grub_vbe_status_t +grub_vbe_bios_get_flat_panel_info (struct grub_vbe_flat_panel_info *flat_panel_info) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f11; + regs.ebx = 0x0001; + regs.es = (((grub_addr_t) flat_panel_info) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) flat_panel_info) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f15 to get DDC availability, return status. */ +static grub_vbe_status_t +grub_vbe_bios_get_ddc_capabilities (grub_uint8_t *level) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f15; + regs.ebx = 0x0000; + regs.ecx = 0x0000; + regs.es = 0x0000; + regs.edi = 0x0000; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + *level = regs.ebx & 0xff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f15 to read EDID information, return status. */ +static grub_vbe_status_t +grub_vbe_bios_read_edid (struct grub_video_edid_info *edid_info) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f15; + regs.ebx = 0x0001; + regs.ecx = 0x0000; + regs.edx = 0x0000; + regs.es = (((grub_addr_t) edid_info) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) edid_info) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +grub_err_t +grub_vbe_probe (struct grub_vbe_info_block *info_block) +{ + struct grub_vbe_info_block *vbe_ib; + grub_vbe_status_t status; + + /* Clear caller's controller info block. */ + if (info_block) + grub_memset (info_block, 0, sizeof (*info_block)); + + /* Do not probe more than one time, if not necessary. */ + if (vbe_detected == -1 || info_block) + { + /* Clear old copy of controller info block. */ + grub_memset (&controller_info, 0, sizeof (controller_info)); + + /* Mark VESA BIOS extension as undetected. */ + vbe_detected = 0; + + /* Use low memory scratch area as temporary storage + for VESA BIOS call. */ + vbe_ib = (struct grub_vbe_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + + /* Prepare info block. */ + grub_memset (vbe_ib, 0, sizeof (*vbe_ib)); + + vbe_ib->signature[0] = 'V'; + vbe_ib->signature[1] = 'B'; + vbe_ib->signature[2] = 'E'; + vbe_ib->signature[3] = '2'; + + /* Try to get controller info block. */ + status = grub_vbe_bios_get_controller_info (vbe_ib); + if (status == GRUB_VBE_STATUS_OK) + { + /* Copy it for later usage. */ + grub_memcpy (&controller_info, vbe_ib, sizeof (controller_info)); + + /* Mark VESA BIOS extension as detected. */ + vbe_detected = 1; + } + } + + if (! vbe_detected) + return grub_error (GRUB_ERR_BAD_DEVICE, "VESA BIOS Extension not found"); + + /* Make copy of controller info block to caller. */ + if (info_block) + grub_memcpy (info_block, &controller_info, sizeof (*info_block)); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vbe_get_edid (struct grub_video_edid_info *edid_info) +{ + struct grub_video_edid_info *edid_info_lowmem; + + /* Use low memory scratch area as temporary storage for VESA BIOS calls. */ + edid_info_lowmem = + (struct grub_video_edid_info *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_memset (edid_info_lowmem, 0, sizeof (*edid_info_lowmem)); + + if (grub_vbe_bios_read_edid (edid_info_lowmem) != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available"); + + grub_memcpy (edid_info, edid_info_lowmem, sizeof (*edid_info)); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_vbe_get_preferred_mode (unsigned int *width, unsigned int *height) +{ + grub_vbe_status_t status; + grub_uint8_t ddc_level; + struct grub_video_edid_info edid_info; + struct grub_vbe_flat_panel_info *flat_panel_info; + + /* Use low memory scratch area as temporary storage for VESA BIOS calls. */ + flat_panel_info = (struct grub_vbe_flat_panel_info *) + (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + sizeof (struct grub_video_edid_info)); + + if (controller_info.version >= 0x200 + && (grub_vbe_bios_get_ddc_capabilities (&ddc_level) & 0xff) + == GRUB_VBE_STATUS_OK) + { + if (grub_video_vbe_get_edid (&edid_info) == GRUB_ERR_NONE + && grub_video_edid_checksum (&edid_info) == GRUB_ERR_NONE + && grub_video_edid_preferred_mode (&edid_info, width, height) + == GRUB_ERR_NONE && *width < 4096 && *height < 4096) + return GRUB_ERR_NONE; + + grub_errno = GRUB_ERR_NONE; + } + + grub_memset (flat_panel_info, 0, sizeof (*flat_panel_info)); + status = grub_vbe_bios_get_flat_panel_info (flat_panel_info); + if (status == GRUB_VBE_STATUS_OK + && flat_panel_info->horizontal_size && flat_panel_info->vertical_size + && flat_panel_info->horizontal_size < 4096 + && flat_panel_info->vertical_size < 4096) + { + *width = flat_panel_info->horizontal_size; + *height = flat_panel_info->vertical_size; + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot get preferred mode"); +} + +static grub_err_t +grub_vbe_set_video_mode (grub_uint32_t vbe_mode, + struct grub_vbe_mode_info_block *vbe_mode_info) +{ + grub_vbe_status_t status; + grub_uint32_t old_vbe_mode; + struct grub_vbe_mode_info_block new_vbe_mode_info; + grub_err_t err; + + /* Make sure that VBE is supported. */ + grub_vbe_probe (0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Try to get mode info. */ + grub_vbe_get_video_mode_info (vbe_mode, &new_vbe_mode_info); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* For all VESA BIOS modes, force linear frame buffer. */ + if (vbe_mode >= 0x100) + { + /* We only want linear frame buffer modes. */ + vbe_mode |= 1 << 14; + + /* Determine frame buffer pixel format. */ + if (new_vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL + && new_vbe_mode_info.memory_model + != GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported pixel format 0x%x", + new_vbe_mode_info.memory_model); + } + + /* Get current mode. */ + grub_vbe_get_video_mode (&old_vbe_mode); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Try to set video mode. */ + status = grub_vbe_bios_set_mode (vbe_mode, 0); + if (status != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot set VBE mode %x", vbe_mode); + last_set_mode = vbe_mode; + + if (vbe_mode < 0x100) + { + /* If this is not a VESA mode, guess address. */ + framebuffer.ptr = (grub_uint8_t *) 0xa0000; + } + else + { + framebuffer.ptr = (grub_uint8_t *) new_vbe_mode_info.phys_base_addr; + } + + /* Check whether mode is text mode or graphics mode. */ + if (new_vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_TEXT) + { + /* Text mode. */ + + /* No special action needed for text mode as it is not supported for + graphical support. */ + } + else + { + /* Graphics mode. */ + + /* If video mode is in indexed color, setup default VGA palette. */ + if (vbe_mode < 0x100 || new_vbe_mode_info.memory_model + == GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL) + { + struct grub_vbe_palette_data *palette + = (struct grub_vbe_palette_data *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + unsigned i; + + /* Make sure that the BIOS can reach the palette. */ + for (i = 0; i < GRUB_VIDEO_FBSTD_NUMCOLORS; i++) + { + palette[i].red = grub_video_fbstd_colors[i].r; + palette[i].green = grub_video_fbstd_colors[i].g; + palette[i].blue = grub_video_fbstd_colors[i].b; + palette[i].alignment = 0; + } + + status = grub_vbe_bios_set_palette_data (GRUB_VIDEO_FBSTD_NUMCOLORS, + 0, palette); + + /* Just ignore the status. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + if (err) + return err; + + } + } + + /* Copy mode info for caller. */ + if (vbe_mode_info) + grub_memcpy (vbe_mode_info, &new_vbe_mode_info, sizeof (*vbe_mode_info)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_vbe_get_video_mode (grub_uint32_t *mode) +{ + grub_vbe_status_t status; + + /* Make sure that VBE is supported. */ + grub_vbe_probe (0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Try to query current mode from VESA BIOS. */ + status = grub_vbe_bios_get_mode (mode); + /* XXX: ATI cards don't support get_mode. */ + if (status != GRUB_VBE_STATUS_OK) + *mode = last_set_mode; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_vbe_get_video_mode_info (grub_uint32_t mode, + struct grub_vbe_mode_info_block *mode_info) +{ + struct grub_vbe_mode_info_block *mi_tmp + = (struct grub_vbe_mode_info_block *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + grub_vbe_status_t status; + + /* Make sure that VBE is supported. */ + grub_vbe_probe (0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* If mode is not VESA mode, skip mode info query. */ + if (mode >= 0x100) + { + /* Try to get mode info from VESA BIOS. */ + status = grub_vbe_bios_get_mode_info (mode, mi_tmp); + if (status != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_BAD_DEVICE, + "cannot get information on the mode %x", mode); + + /* Make copy of mode info block. */ + grub_memcpy (mode_info, mi_tmp, sizeof (*mode_info)); + } + else + /* Just clear mode info block if it isn't a VESA mode. */ + grub_memset (mode_info, 0, sizeof (*mode_info)); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vbe_init (void) +{ + grub_uint16_t *rm_vbe_mode_list; + grub_uint16_t *p; + grub_size_t vbe_mode_list_size; + struct grub_vbe_info_block info_block; + + /* Check if there is adapter present. + + Firmware note: There has been a report that some cards store video mode + list in temporary memory. So we must first use vbe probe to get + refreshed information to receive valid pointers and data, and then + copy this information to somewhere safe. */ + grub_vbe_probe (&info_block); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Copy modelist to local memory. */ + p = rm_vbe_mode_list = real2pm (info_block.video_mode_ptr); + while(*p++ != 0xFFFF) + ; + + vbe_mode_list_size = (grub_addr_t) p - (grub_addr_t) rm_vbe_mode_list; + vbe_mode_list = grub_malloc (vbe_mode_list_size); + if (! vbe_mode_list) + return grub_errno; + grub_memcpy (vbe_mode_list, rm_vbe_mode_list, vbe_mode_list_size); + + /* Adapter could be found, figure out initial video mode. */ + grub_vbe_get_video_mode (&initial_vbe_mode); + if (grub_errno != GRUB_ERR_NONE) + { + /* Free allocated resources. */ + grub_free (vbe_mode_list); + vbe_mode_list = NULL; + + return grub_errno; + } + + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + framebuffer.mtrr = -1; + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_vbe_fini (void) +{ + grub_vbe_status_t status; + grub_err_t err; + + /* Restore old video mode. */ + if (last_set_mode != initial_vbe_mode) + { + status = grub_vbe_bios_set_mode (initial_vbe_mode, 0); + if (status != GRUB_VBE_STATUS_OK) + /* TODO: Decide, is this something we want to do. */ + return grub_errno; + } + last_set_mode = initial_vbe_mode; + + /* TODO: Free any resources allocated by driver. */ + grub_free (vbe_mode_list); + vbe_mode_list = NULL; + + err = grub_video_fb_fini (); + if (framebuffer.mtrr >= 0) + { + grub_vbe_disable_mtrr (framebuffer.mtrr); + framebuffer.mtrr = -1; + } + return err; +} + +/* + Set framebuffer render target page and display the proper page, based on + `doublebuf_state.render_page' and `doublebuf_state.displayed_page', + respectively. +*/ +static grub_err_t +doublebuf_pageflipping_set_page (int page) +{ + /* Tell the video adapter to display the new front page. */ + int display_start_line + = framebuffer.mode_info.height * page; + + grub_vbe_status_t vbe_err = + grub_vbe_bios_set_display_start (0, display_start_line); + + if (vbe_err != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_IO, "couldn't commit pageflip"); + + return 0; +} + +static void +vbe2videoinfo (grub_uint32_t mode, + const struct grub_vbe_mode_info_block *vbeinfo, + struct grub_video_mode_info *mode_info) +{ + mode_info->mode_number = mode; + + mode_info->width = vbeinfo->x_resolution; + mode_info->height = vbeinfo->y_resolution; + mode_info->mode_type = 0; + switch (vbeinfo->memory_model) + { + case GRUB_VBE_MEMORY_MODEL_TEXT: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_PURE_TEXT; + break; + + /* CGA is basically 4-bit packed pixel. */ + case GRUB_VBE_MEMORY_MODEL_CGA: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_CGA; + /* Fallthrough. */ + case GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + break; + + case GRUB_VBE_MEMORY_MODEL_HERCULES: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_HERCULES + | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; + break; + + /* Non chain 4 is a special case of planar. */ + case GRUB_VBE_MEMORY_MODEL_NONCHAIN4_256: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_NONCHAIN4; + /* Fallthrough. */ + case GRUB_VBE_MEMORY_MODEL_PLANAR: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_PLANAR + | GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + break; + + case GRUB_VBE_MEMORY_MODEL_YUV: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_YUV; + break; + + case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_RGB; + break; + default: + mode_info->mode_type |= GRUB_VIDEO_MODE_TYPE_UNKNOWN; + break; + } + + mode_info->bpp = vbeinfo->bits_per_pixel; + /* Calculate bytes_per_pixel value. */ + switch(vbeinfo->bits_per_pixel) + { + case 32: + mode_info->bytes_per_pixel = 4; + break; + case 24: + mode_info->bytes_per_pixel = 3; + break; + case 16: + mode_info->bytes_per_pixel = 2; + break; + case 15: + mode_info->bytes_per_pixel = 2; + break; + case 8: + mode_info->bytes_per_pixel = 1; + break; + case 4: + mode_info->bytes_per_pixel = 0; + break; + } + + if (controller_info.version >= 0x300) + mode_info->pitch = vbeinfo->lin_bytes_per_scan_line; + else + mode_info->pitch = vbeinfo->bytes_per_scan_line; + + if (mode_info->mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + mode_info->number_of_colors = 16; + else + mode_info->number_of_colors = 256; + mode_info->red_mask_size = vbeinfo->red_mask_size; + mode_info->red_field_pos = vbeinfo->red_field_position; + mode_info->green_mask_size = vbeinfo->green_mask_size; + mode_info->green_field_pos = vbeinfo->green_field_position; + mode_info->blue_mask_size = vbeinfo->blue_mask_size; + mode_info->blue_field_pos = vbeinfo->blue_field_position; + mode_info->reserved_mask_size = vbeinfo->rsvd_mask_size; + mode_info->reserved_field_pos = vbeinfo->rsvd_field_position; + + mode_info->blit_format = grub_video_get_blit_format (mode_info); +} + +static int +grub_video_vbe_iterate (int (*hook) (const struct grub_video_mode_info *info, void *hook_arg), void *hook_arg) +{ + grub_uint16_t *p; + struct grub_vbe_mode_info_block vbe_mode_info; + struct grub_video_mode_info mode_info; + + for (p = vbe_mode_list; *p != 0xFFFF; p++) + { + grub_vbe_get_video_mode_info (*p, &vbe_mode_info); + if (grub_errno != GRUB_ERR_NONE) + { + /* Could not retrieve mode info, retreat. */ + grub_errno = GRUB_ERR_NONE; + break; + } + + vbe2videoinfo (*p, &vbe_mode_info, &mode_info); + if (hook (&mode_info, hook_arg)) + return 1; + } + return 0; +} + +static grub_err_t +grub_video_vbe_setup (unsigned int width, unsigned int height, + grub_video_mode_type_t mode_type, + grub_video_mode_type_t mode_mask) +{ + grub_uint16_t *p; + struct grub_vbe_mode_info_block vbe_mode_info; + struct grub_vbe_mode_info_block best_vbe_mode_info; + grub_uint32_t best_vbe_mode = 0; + int depth; + int preferred_mode = 0; + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (width == 0 && height == 0) + { + grub_vbe_get_preferred_mode (&width, &height); + if (grub_errno == GRUB_ERR_NONE) + preferred_mode = 1; + else + { + /* Fall back to 640x480. This is conservative, but the largest + mode supported by the graphics card may not be safe for the + display device. */ + grub_errno = GRUB_ERR_NONE; + width = 640; + height = 480; + } + } + + /* Walk thru mode list and try to find matching mode. */ + for (p = vbe_mode_list; *p != 0xFFFF; p++) + { + grub_uint32_t vbe_mode = *p; + + grub_vbe_get_video_mode_info (vbe_mode, &vbe_mode_info); + if (grub_errno != GRUB_ERR_NONE) + { + /* Could not retrieve mode info, retreat. */ + grub_errno = GRUB_ERR_NONE; + break; + } + + if ((vbe_mode_info.mode_attributes & GRUB_VBE_MODEATTR_SUPPORTED) == 0) + /* If not available, skip it. */ + continue; + + if ((vbe_mode_info.mode_attributes & GRUB_VBE_MODEATTR_COLOR) == 0) + /* Monochrome is unusable. */ + continue; + + if ((vbe_mode_info.mode_attributes & GRUB_VBE_MODEATTR_LFB_AVAIL) == 0) + /* We support only linear frame buffer modes. */ + continue; + + if ((vbe_mode_info.mode_attributes & GRUB_VBE_MODEATTR_GRAPHICS) == 0) + /* We allow only graphical modes. */ + continue; + + if ((vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL) + && (vbe_mode_info.memory_model != GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR)) + /* Not compatible memory model. */ + continue; + + if (vbe_mode_info.bits_per_pixel != 8 + && vbe_mode_info.bits_per_pixel != 15 + && vbe_mode_info.bits_per_pixel != 16 + && vbe_mode_info.bits_per_pixel != 24 + && vbe_mode_info.bits_per_pixel != 32) + /* Unsupported bitdepth . */ + continue; + + if (preferred_mode) + { + if (vbe_mode_info.x_resolution > width + || vbe_mode_info.y_resolution > height) + /* Resolution exceeds that of preferred mode. */ + continue; + } + else + { + if (((vbe_mode_info.x_resolution != width) + || (vbe_mode_info.y_resolution != height)) + && width != 0 && height != 0) + /* Non matching resolution. */ + continue; + } + + /* Check if user requested RGB or index color mode. */ + if ((mode_mask & GRUB_VIDEO_MODE_TYPE_COLOR_MASK) != 0) + { + unsigned my_mode_type = 0; + + if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL) + my_mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + + if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR) + my_mode_type |= GRUB_VIDEO_MODE_TYPE_RGB; + + if ((my_mode_type & mode_mask + & (GRUB_VIDEO_MODE_TYPE_RGB | GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)) + != (mode_type & mode_mask + & (GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_INDEX_COLOR))) + continue; + } + + /* If there is a request for specific depth, ignore others. */ + if ((depth != 0) && (vbe_mode_info.bits_per_pixel != depth)) + continue; + + /* Select mode with most of "volume" (size of framebuffer in bits). */ + if (best_vbe_mode != 0) + if ((grub_uint64_t) vbe_mode_info.bits_per_pixel + * vbe_mode_info.x_resolution * vbe_mode_info.y_resolution + < (grub_uint64_t) best_vbe_mode_info.bits_per_pixel + * best_vbe_mode_info.x_resolution * best_vbe_mode_info.y_resolution) + continue; + + /* Save so far best mode information for later use. */ + best_vbe_mode = vbe_mode; + grub_memcpy (&best_vbe_mode_info, &vbe_mode_info, sizeof (vbe_mode_info)); + } + + /* Try to initialize best mode found. */ + if (best_vbe_mode != 0) + { + grub_err_t err; + static struct grub_vbe_mode_info_block active_vbe_mode_info; + /* If this fails, then we have mode selection heuristics problem, + or adapter failure. */ + grub_vbe_set_video_mode (best_vbe_mode, &active_vbe_mode_info); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + /* Fill mode info details. */ + vbe2videoinfo (best_vbe_mode, &active_vbe_mode_info, + &framebuffer.mode_info); + + { + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; + grub_size_t page_size; /* The size of a page in bytes. */ + + page_size = framebuffer.mode_info.pitch * framebuffer.mode_info.height; + + if (vram_size >= 2 * page_size) + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, + doublebuf_pageflipping_set_page, + framebuffer.ptr + page_size); + else + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, 0, 0); + } + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + grub_vbe_enable_mtrr (framebuffer.ptr, + controller_info.total_memory << 16); + + return err; + } + + /* Couldn't found matching mode. */ + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); +} + +static grub_err_t +grub_video_vbe_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + { + /* TODO: Implement setting indexed color mode palette to hardware. */ + //status = grub_vbe_bios_set_palette_data (sizeof (vga_colors) + // / sizeof (struct grub_vbe_palette_data), + // 0, + // palette); + + } + + /* Then set color to emulated palette. */ + + return grub_video_fb_set_palette (start, count, palette_data); +} + +static grub_err_t +grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_err_t err; + grub_free (vbe_mode_list); + vbe_mode_list = NULL; + err = grub_video_fb_get_info_and_fini (mode_info, framebuf); + if (err) + return err; + if (framebuffer.mtrr >= 0) + { + grub_vbe_disable_mtrr (framebuffer.mtrr); + framebuffer.mtrr = -1; + } + return GRUB_ERR_NONE; +} + +static void +grub_video_vbe_print_adapter_specific_info (void) +{ + grub_printf_ (N_(" VBE info: version: %d.%d OEM software rev: %d.%d\n"), + controller_info.version >> 8, + controller_info.version & 0xFF, + controller_info.oem_software_rev >> 8, + controller_info.oem_software_rev & 0xFF); + + /* The total_memory field is in 64 KiB units. */ + grub_printf_ (N_(" total memory: %d KiB\n"), + (controller_info.total_memory << 6)); +} + +static struct grub_video_adapter grub_video_vbe_adapter = + { + .name = "VESA BIOS Extension Video Driver", + .id = GRUB_VIDEO_DRIVER_VBE, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE, + + .init = grub_video_vbe_init, + .fini = grub_video_vbe_fini, + .setup = grub_video_vbe_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_vbe_get_info_and_fini, + .set_palette = grub_video_vbe_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_fb_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + .iterate = grub_video_vbe_iterate, + .get_edid = grub_video_vbe_get_edid, + .print_adapter_specific_info = grub_video_vbe_print_adapter_specific_info, + + .next = 0 + }; + +GRUB_MOD_INIT(video_i386_pc_vbe) +{ + grub_video_register (&grub_video_vbe_adapter); +} + +GRUB_MOD_FINI(video_i386_pc_vbe) +{ + grub_video_unregister (&grub_video_vbe_adapter); +} diff --git a/grub-core/video/i386/pc/vga.c b/grub-core/video/i386/pc/vga.c new file mode 100644 index 0000000..b2f776c --- /dev/null +++ b/grub-core/video/i386/pc/vga.c @@ -0,0 +1,404 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/i386/pc/int.h> +#include <grub/machine/console.h> +#include <grub/cpu/io.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/vga.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define VGA_WIDTH 640 +#define VGA_MEM ((grub_uint8_t *) 0xa0000) +#define PAGE_OFFSET(x) ((x) * (VGA_WIDTH * vga_height / 8)) + +static unsigned char text_mode; +static unsigned char saved_map_mask; +static int vga_height; + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + grub_uint8_t *temporary_buffer; + int front_page; + int back_page; +} framebuffer; + +static unsigned char +grub_vga_set_mode (unsigned char mode) +{ + struct grub_bios_int_registers regs; + unsigned char ret; + /* get current mode */ + regs.eax = 0x0f00; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + ret = regs.eax & 0xff; + regs.eax = mode; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + return ret; +} + +static inline void +wait_vretrace (void) +{ + /* Wait until there is a vertical retrace. */ + while (! (grub_inb (GRUB_VGA_IO_INPUT_STATUS1_REGISTER) + & GRUB_VGA_IO_INPUT_STATUS1_VERTR_BIT)); +} + +/* Get Map Mask Register. */ +static unsigned char +get_map_mask (void) +{ + return grub_vga_sr_read (GRUB_VGA_SR_MAP_MASK_REGISTER); +} + +/* Set Map Mask Register. */ +static void +set_map_mask (unsigned char mask) +{ + grub_vga_sr_write (mask, GRUB_VGA_SR_MAP_MASK_REGISTER); +} + +#if 0 +/* Set Read Map Register. */ +static void +set_read_map (unsigned char map) +{ + grub_vga_gr_write (map, GRUB_VGA_GR_READ_MAP_REGISTER); +} +#endif + +/* Set start address. */ +static void +set_start_address (unsigned int start) +{ + grub_vga_cr_write (start & 0xFF, GRUB_VGA_CR_START_ADDR_LOW_REGISTER); + grub_vga_cr_write (start >> 8, GRUB_VGA_CR_START_ADDR_HIGH_REGISTER); +} + +static int setup = 0; +static int is_target = 0; + +static grub_err_t +grub_video_vga_init (void) +{ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vga_setup (unsigned int width, unsigned int height, + grub_video_mode_type_t mode_type, + grub_video_mode_type_t mode_mask) +{ + grub_err_t err; + + if ((width && width != VGA_WIDTH) || (height && height != 350 && height != 480)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found"); + + vga_height = height ? : 480; + + framebuffer.temporary_buffer = grub_calloc (vga_height, VGA_WIDTH); + framebuffer.front_page = 0; + framebuffer.back_page = 0; + if (!framebuffer.temporary_buffer) + return grub_errno; + + saved_map_mask = get_map_mask (); + + text_mode = grub_vga_set_mode (vga_height == 480 ? 0x12 : 0x10); + setup = 1; + set_map_mask (0x0f); + set_start_address (PAGE_OFFSET (framebuffer.front_page)); + + framebuffer.mode_info.width = VGA_WIDTH; + framebuffer.mode_info.height = vga_height; + + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + + if (grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + (VGA_WIDTH * vga_height <= (1 << 18)))) + { + framebuffer.back_page = 1; + framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; + } + + framebuffer.mode_info.bpp = 8; + framebuffer.mode_info.bytes_per_pixel = 1; + framebuffer.mode_info.pitch = VGA_WIDTH; + framebuffer.mode_info.number_of_colors = 16; + framebuffer.mode_info.red_mask_size = 0; + framebuffer.mode_info.red_field_pos = 0; + framebuffer.mode_info.green_mask_size = 0; + framebuffer.mode_info.green_field_pos = 0; + framebuffer.mode_info.blue_mask_size = 0; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; + + framebuffer.mode_info.blit_format + = grub_video_get_blit_format (&framebuffer.mode_info); + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.render_target, + &framebuffer.mode_info, + framebuffer.temporary_buffer); + + if (err) + { + grub_dprintf ("video", "Couldn't create FB target\n"); + return err; + } + + is_target = 1; + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + + if (err) + return err; + + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vga_fini (void) +{ + if (setup) + { + set_map_mask (saved_map_mask); + grub_vga_set_mode (text_mode); + } + setup = 0; + grub_free (framebuffer.temporary_buffer); + framebuffer.temporary_buffer = 0; + return GRUB_ERR_NONE; +} + +static inline void +update_target (void) +{ + int plane; + + if (!is_target) + return; + + for (plane = 0x01; plane <= 0x08; plane <<= 1) + { + grub_uint8_t *ptr; + volatile grub_uint8_t *ptr2; + unsigned cbyte = 0; + int shift = 7; + set_map_mask (plane); + for (ptr = framebuffer.temporary_buffer, + ptr2 = VGA_MEM + PAGE_OFFSET (framebuffer.back_page); + ptr < framebuffer.temporary_buffer + VGA_WIDTH * vga_height; ptr++) + { + cbyte |= (!!(plane & *ptr)) << shift; + shift--; + if (shift == -1) + { + *ptr2++ = cbyte; + shift = 7; + cbyte = 0; + } + } + } +} + +static grub_err_t +grub_video_vga_blit_bitmap (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, int x, int y, + int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + grub_err_t ret; + ret = grub_video_fb_blit_bitmap (bitmap, oper, x, y, offset_x, offset_y, + width, height); + if (!(framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)) + update_target (); + return ret; +} + +static grub_err_t +grub_video_vga_blit_render_target (struct grub_video_fbrender_target *source, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + grub_err_t ret; + + ret = grub_video_fb_blit_render_target (source, oper, x, y, + offset_x, offset_y, width, height); + if (!(framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)) + update_target (); + + return ret; +} + +static grub_err_t +grub_video_vga_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + { + is_target = 1; + target = framebuffer.render_target; + } + else + is_target = 0; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_vga_get_active_render_target (struct grub_video_render_target **target) +{ + grub_err_t err; + err = grub_video_fb_get_active_render_target (target); + if (err) + return err; + + if (*target == framebuffer.render_target) + *target = GRUB_VIDEO_RENDER_TARGET_DISPLAY; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vga_swap_buffers (void) +{ + if (!(framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)) + return GRUB_ERR_NONE; + + update_target (); + + if ((VGA_WIDTH * vga_height <= (1 << 18))) + { + /* Activate the other page. */ + framebuffer.front_page = !framebuffer.front_page; + framebuffer.back_page = !framebuffer.back_page; + wait_vretrace (); + set_start_address (PAGE_OFFSET (framebuffer.front_page)); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_vga_set_palette (unsigned int start __attribute__ ((unused)), + unsigned int count __attribute__ ((unused)), + struct grub_video_palette_data *palette_data __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_IO, "can't change palette"); +} + +static grub_err_t +grub_video_vga_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + set_map_mask (0xf); + + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + mode_info->bpp = 1; + mode_info->bytes_per_pixel = 0; + mode_info->pitch = VGA_WIDTH / 8; + mode_info->number_of_colors = 1; + + mode_info->bg_red = 0; + mode_info->bg_green = 0; + mode_info->bg_blue = 0; + mode_info->bg_alpha = 255; + + mode_info->fg_red = 255; + mode_info->fg_green = 255; + mode_info->fg_blue = 255; + mode_info->fg_alpha = 255; + + *framebuf = VGA_MEM + PAGE_OFFSET (framebuffer.front_page); + + grub_video_fb_fini (); + grub_free (framebuffer.temporary_buffer); + framebuffer.temporary_buffer = 0; + setup = 0; + + return GRUB_ERR_NONE; +} + + +static struct grub_video_adapter grub_video_vga_adapter = + { + .name = "VGA Video Driver", + .id = GRUB_VIDEO_DRIVER_VGA, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FALLBACK, + + .init = grub_video_vga_init, + .fini = grub_video_vga_fini, + .setup = grub_video_vga_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_vga_get_info_and_fini, + .set_palette = grub_video_vga_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_vga_blit_bitmap, + .blit_render_target = grub_video_vga_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_vga_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_vga_set_active_render_target, + .get_active_render_target = grub_video_vga_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(vga) +{ + grub_video_register (&grub_video_vga_adapter); +} + +GRUB_MOD_FINI(vga) +{ + grub_video_unregister (&grub_video_vga_adapter); +} diff --git a/grub-core/video/ieee1275.c b/grub-core/video/ieee1275.c new file mode 100644 index 0000000..17a3dbb --- /dev/null +++ b/grub-core/video/ieee1275.c @@ -0,0 +1,370 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/ieee1275/ieee1275.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Only 8-bit indexed color is supported for now. */ + +static unsigned old_width, old_height; +static int restore_needed; +static char *display; +static grub_ieee1275_ihandle_t stdout_ihandle; +static int have_setcolors = 0; + +static struct +{ + struct grub_video_mode_info mode_info; + grub_uint8_t *ptr; +} framebuffer; + +static struct grub_video_palette_data serial_colors[] = + { + // {R, G, B} + {0x00, 0x00, 0x00, 0xFF}, // 0 = black + {0xA8, 0x00, 0x00, 0xFF}, // 1 = red + {0x00, 0xA8, 0x00, 0xFF}, // 2 = green + {0xFE, 0xFE, 0x54, 0xFF}, // 3 = yellow + {0x00, 0x00, 0xA8, 0xFF}, // 4 = blue + {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta + {0x00, 0xA8, 0xA8, 0xFF}, // 6 = cyan + {0xFE, 0xFE, 0xFE, 0xFF} // 7 = white + }; + + +static grub_err_t +grub_video_ieee1275_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); + +static void +set_video_mode (unsigned width __attribute__ ((unused)), + unsigned height __attribute__ ((unused))) +{ + /* TODO */ +} + +static int +find_display_hook (struct grub_ieee1275_devalias *alias) +{ + if (grub_strcmp (alias->type, "display") == 0) + { + grub_dprintf ("video", "Found display %s\n", alias->path); + display = grub_strdup (alias->path); + return 1; + } + return 0; +} + +static void +find_display (void) +{ + grub_ieee1275_devices_iterate (find_display_hook); +} + +static grub_err_t +grub_video_ieee1275_init (void) +{ + grub_ssize_t actual; + + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS) + && !grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "stdout", &stdout_ihandle, + sizeof (stdout_ihandle), &actual) + && actual == sizeof (stdout_ihandle)) + have_setcolors = 1; + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_ieee1275_fini (void) +{ + if (restore_needed) + { + set_video_mode (old_width, old_height); + restore_needed = 0; + } + return grub_video_fb_fini (); +} + +static grub_err_t +grub_video_ieee1275_fill_mode_info (grub_ieee1275_phandle_t dev, + struct grub_video_mode_info *out) +{ + grub_uint32_t tmp; + + grub_memset (out, 0, sizeof (*out)); + + if (grub_ieee1275_get_integer_property (dev, "width", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width."); + out->width = tmp; + + if (grub_ieee1275_get_integer_property (dev, "height", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height."); + out->height = tmp; + + if (grub_ieee1275_get_integer_property (dev, "linebytes", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display pitch."); + out->pitch = tmp; + + if (grub_ieee1275_get_integer_property (dev, "depth", &tmp, + sizeof (tmp), 0)) + tmp = 4; + + out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + out->bpp = tmp; + out->bytes_per_pixel = (out->bpp + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; + out->number_of_colors = 256; + + switch (tmp) + { + case 4: + case 8: + out->mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + out->bpp = 8; + if (have_setcolors) + out->number_of_colors = 1 << tmp; + else + out->number_of_colors = 8; + break; + + /* FIXME: we may need byteswapping for the following. Currently it + was seen only on qemu and no byteswap was needed. */ + case 15: + out->red_mask_size = 5; + out->red_field_pos = 10; + out->green_mask_size = 5; + out->green_field_pos = 5; + out->blue_mask_size = 5; + out->blue_field_pos = 0; + break; + + case 16: + out->red_mask_size = 5; + out->red_field_pos = 11; + out->green_mask_size = 6; + out->green_field_pos = 5; + out->blue_mask_size = 5; + out->blue_field_pos = 0; + break; + + case 32: + out->reserved_mask_size = 8; + out->reserved_field_pos = 24; + /* FALLTHROUGH */ + + case 24: + out->red_mask_size = 8; + out->red_field_pos = 16; + out->green_mask_size = 8; + out->green_field_pos = 8; + out->blue_mask_size = 8; + out->blue_field_pos = 0; + break; + default: + return grub_error (GRUB_ERR_IO, "unsupported video depth %d", tmp); + } + + out->blit_format = grub_video_get_blit_format (out); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_ieee1275_setup (unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused)), + unsigned int mode_mask __attribute__ ((unused))) +{ + grub_uint32_t current_width, current_height, address; + grub_err_t err; + grub_ieee1275_phandle_t dev; + + if (!display) + return grub_error (GRUB_ERR_IO, "Couldn't find display device."); + + if (grub_ieee1275_finddevice (display, &dev)) + return grub_error (GRUB_ERR_IO, "Couldn't open display device."); + + if (grub_ieee1275_get_integer_property (dev, "width", ¤t_width, + sizeof (current_width), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width."); + + if (grub_ieee1275_get_integer_property (dev, "height", ¤t_height, + sizeof (current_width), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height."); + + if ((width == current_width && height == current_height) + || (width == 0 && height == 0)) + { + grub_dprintf ("video", "IEEE1275: keeping current mode %dx%d\n", + current_width, current_height); + } + else + { + grub_dprintf ("video", "IEEE1275: Setting mode %dx%d\n", width, height); + /* TODO. */ + return grub_error (GRUB_ERR_IO, "can't set mode %dx%d", width, height); + } + + err = grub_video_ieee1275_fill_mode_info (dev, &framebuffer.mode_info); + if (err) + { + grub_dprintf ("video", "IEEE1275: couldn't fill mode info\n"); + return err; + } + + if (grub_ieee1275_get_integer_property (dev, "address", (void *) &address, + sizeof (address), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display address."); + + /* For some reason sparc64 uses 32-bit pointer too. */ + framebuffer.ptr = (void *) (grub_addr_t) address; + + grub_dprintf ("video", "IEEE1275: initialising FB @ %p %dx%dx%d\n", + framebuffer.ptr, framebuffer.mode_info.width, + framebuffer.mode_info.height, framebuffer.mode_info.bpp); + + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, NULL, NULL); + if (err) + return err; + + grub_video_ieee1275_set_palette (0, framebuffer.mode_info.number_of_colors, + grub_video_fbstd_colors); + + return err; +} + +static grub_err_t +grub_video_ieee1275_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_ieee1275_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned col; + struct grub_video_palette_data fb_palette_data[256]; + grub_err_t err; + + if (!(framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)) + return grub_video_fb_set_palette (start, count, palette_data); + + if (!have_setcolors) + return grub_video_fb_set_palette (0, ARRAY_SIZE (serial_colors), + serial_colors); + + if (start >= framebuffer.mode_info.number_of_colors) + return GRUB_ERR_NONE; + + if (start + count > framebuffer.mode_info.number_of_colors) + count = framebuffer.mode_info.number_of_colors - start; + + err = grub_video_fb_set_palette (start, count, palette_data); + if (err) + return err; + + /* Set colors. */ + grub_video_fb_get_palette (0, ARRAY_SIZE (fb_palette_data), + fb_palette_data); + + for (col = 0; col < ARRAY_SIZE (fb_palette_data); col++) + grub_ieee1275_set_color (stdout_ihandle, col, fb_palette_data[col].r, + fb_palette_data[col].g, + fb_palette_data[col].b); + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_ieee1275_adapter = + { + .name = "IEEE1275 video driver", + + .prio = GRUB_VIDEO_ADAPTER_PRIO_FIRMWARE, + .id = GRUB_VIDEO_DRIVER_IEEE1275, + + .init = grub_video_ieee1275_init, + .fini = grub_video_ieee1275_fini, + .setup = grub_video_ieee1275_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_ieee1275_get_info_and_fini, + .set_palette = grub_video_ieee1275_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_fb_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(ieee1275_fb) +{ + find_display (); + if (display) + grub_video_register (&grub_video_ieee1275_adapter); +} + +GRUB_MOD_FINI(ieee1275_fb) +{ + if (restore_needed) + { + set_video_mode (old_width, old_height); + restore_needed = 0; + } + if (display) + grub_video_unregister (&grub_video_ieee1275_adapter); + grub_free (display); +} diff --git a/grub-core/video/radeon_fuloong2e.c b/grub-core/video/radeon_fuloong2e.c new file mode 100644 index 0000000..b4da34b --- /dev/null +++ b/grub-core/video/radeon_fuloong2e.c @@ -0,0 +1,239 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> + +#define GRUB_RADEON_FULOONG2E_TOTAL_MEMORY_SPACE 1048576 + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + + grub_uint8_t *ptr; + int mapped; + grub_uint32_t base; + grub_pci_device_t dev; +} framebuffer; + +static grub_err_t +grub_video_radeon_fuloong2e_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_radeon_fuloong2e_video_fini (void) +{ + if (framebuffer.mapped) + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + GRUB_RADEON_FULOONG2E_TOTAL_MEMORY_SPACE); + + return grub_video_fb_fini (); +} + +#ifndef TEST +/* Helper for grub_video_radeon_fuloong2e_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != GRUB_PCI_CLASS_SUBCLASS_VGA + || pciid != 0x515a1002) + return 0; + + *found = 1; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr); + framebuffer.dev = dev; + + return 1; +} +#endif + +static grub_err_t +grub_video_radeon_fuloong2e_setup (unsigned int width, unsigned int height, + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) +{ + int depth; + grub_err_t err; + int found = 0; + +#ifndef TEST + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if ((width != 640 && width != 0) || (height != 480 && height != 0) + || (depth != 16 && depth != 0)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 640x480x16 is supported"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); +#endif + /* Fill mode info details. */ + framebuffer.mode_info.width = 640; + framebuffer.mode_info.height = 480; + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.bpp = 16; + framebuffer.mode_info.bytes_per_pixel = 2; + framebuffer.mode_info.pitch = 640 * 2; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 11; + framebuffer.mode_info.green_mask_size = 6; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; +#ifndef TEST + framebuffer.mode_info.blit_format + = grub_video_get_blit_format (&framebuffer.mode_info); +#endif + + /* We can safely discard volatile attribute. */ +#ifndef TEST + framebuffer.ptr + = (void *) grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + GRUB_RADEON_FULOONG2E_TOTAL_MEMORY_SPACE); +#endif + framebuffer.mapped = 1; + + /* Prevent garbage from appearing on the screen. */ + grub_memset (framebuffer.ptr, 0x55, + framebuffer.mode_info.height * framebuffer.mode_info.pitch); + +#ifndef TEST + err = grub_video_fb_create_render_target_from_pointer (&framebuffer + .render_target, + &framebuffer.mode_info, + framebuffer.ptr); + + if (err) + return err; + + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); +#endif + return err; +} + +static grub_err_t +grub_video_radeon_fuloong2e_swap_buffers (void) +{ + /* TODO: Implement buffer swapping. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_radeon_fuloong2e_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_radeon_fuloong2e_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_radeon_fuloong2e_adapter = + { + .name = "RADEON RV100 QZ (Fuloong2E) Video Driver", + .id = GRUB_VIDEO_DRIVER_RADEON_FULOONG2E, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_radeon_fuloong2e_video_init, + .fini = grub_video_radeon_fuloong2e_video_fini, + .setup = grub_video_radeon_fuloong2e_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_radeon_fuloong2e_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_radeon_fuloong2e_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_radeon_fuloong2e_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_radeon_fuloong2e) +{ + grub_video_register (&grub_video_radeon_fuloong2e_adapter); +} + +GRUB_MOD_FINI(video_radeon_fuloong2e) +{ + grub_video_unregister (&grub_video_radeon_fuloong2e_adapter); +} diff --git a/grub-core/video/radeon_yeeloong3a.c b/grub-core/video/radeon_yeeloong3a.c new file mode 100644 index 0000000..52614fe --- /dev/null +++ b/grub-core/video/radeon_yeeloong3a.c @@ -0,0 +1,237 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010,2011,2013 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> + +#define GRUB_RADEON_YEELOONG3A_TOTAL_MEMORY_SPACE 1048576 + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + + grub_uint8_t *ptr; + int mapped; + grub_uint32_t base; + grub_pci_device_t dev; +} framebuffer; + +static grub_err_t +grub_video_radeon_yeeloong3a_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_radeon_yeeloong3a_video_fini (void) +{ + if (framebuffer.mapped) + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + GRUB_RADEON_YEELOONG3A_TOTAL_MEMORY_SPACE); + return grub_video_fb_fini (); +} + +#ifndef TEST +/* Helper for grub_video_radeon_yeeloong3a_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != GRUB_PCI_CLASS_SUBCLASS_VGA + || pciid != 0x96151002) + return 0; + + *found = 1; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr); + framebuffer.dev = dev; + + return 1; +} +#endif + +static grub_err_t +grub_video_radeon_yeeloong3a_setup (unsigned int width, unsigned int height, + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) +{ + int depth; + grub_err_t err; + int found = 0; + +#ifndef TEST + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if ((width != 800 && width != 0) || (height != 600 && height != 0) + || (depth != 16 && depth != 0)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 640x480x16 is supported"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); +#endif + /* Fill mode info details. */ + framebuffer.mode_info.width = 800; + framebuffer.mode_info.height = 600; + framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + framebuffer.mode_info.bpp = 16; + framebuffer.mode_info.bytes_per_pixel = 2; + framebuffer.mode_info.pitch = 800 * 2; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 11; + framebuffer.mode_info.green_mask_size = 6; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; +#ifndef TEST + framebuffer.mode_info.blit_format + = grub_video_get_blit_format (&framebuffer.mode_info); +#endif + + /* We can safely discard volatile attribute. */ +#ifndef TEST + framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + GRUB_RADEON_YEELOONG3A_TOTAL_MEMORY_SPACE); + framebuffer.mapped = 1; +#endif + + /* Prevent garbage from appearing on the screen. */ + grub_memset (framebuffer.ptr, 0, + framebuffer.mode_info.height * framebuffer.mode_info.pitch); + +#ifndef TEST + err = grub_video_fb_create_render_target_from_pointer (&framebuffer + .render_target, + &framebuffer.mode_info, + framebuffer.ptr); + + if (err) + return err; + + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); +#endif + return err; +} + +static grub_err_t +grub_video_radeon_yeeloong3a_swap_buffers (void) +{ + /* TODO: Implement buffer swapping. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_radeon_yeeloong3a_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_radeon_yeeloong3a_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_radeon_yeeloong3a_adapter = + { + .name = "RADEON (Yeeloong3a) Video Driver", + .id = GRUB_VIDEO_DRIVER_RADEON_YEELOONG3A, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_radeon_yeeloong3a_video_init, + .fini = grub_video_radeon_yeeloong3a_video_fini, + .setup = grub_video_radeon_yeeloong3a_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_radeon_yeeloong3a_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_radeon_yeeloong3a_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_radeon_yeeloong3a_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_radeon_yeeloong3a) +{ + grub_video_register (&grub_video_radeon_yeeloong3a_adapter); +} + +GRUB_MOD_FINI(video_radeon_yeeloong3a) +{ + grub_video_unregister (&grub_video_radeon_yeeloong3a_adapter); +} diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c new file mode 100644 index 0000000..e31602f --- /dev/null +++ b/grub-core/video/readers/jpeg.c @@ -0,0 +1,879 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/bitmap.h> +#include <grub/types.h> +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/bufio.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Uncomment following define to enable JPEG debug. */ +//#define JPEG_DEBUG + +#define JPEG_ESC_CHAR 0xFF + +#define JPEG_SAMPLING_1x1 0x11 + +enum + { + JPEG_MARKER_SOF0 = 0xc0, + JPEG_MARKER_DHT = 0xc4, + JPEG_MARKER_SOI = 0xd8, + JPEG_MARKER_EOI = 0xd9, + JPEG_MARKER_RST0 = 0xd0, + JPEG_MARKER_RST1 = 0xd1, + JPEG_MARKER_RST2 = 0xd2, + JPEG_MARKER_RST3 = 0xd3, + JPEG_MARKER_RST4 = 0xd4, + JPEG_MARKER_RST5 = 0xd5, + JPEG_MARKER_RST6 = 0xd6, + JPEG_MARKER_RST7 = 0xd7, + JPEG_MARKER_SOS = 0xda, + JPEG_MARKER_DQT = 0xdb, + JPEG_MARKER_DRI = 0xdd, + }; + +#define SHIFT_BITS 8 +#define CONST(x) ((int) ((x) * (1L << SHIFT_BITS) + 0.5)) + +#define JPEG_UNIT_SIZE 8 + +static const grub_uint8_t jpeg_zigzag_order[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +#ifdef JPEG_DEBUG +static grub_command_t cmd; +#endif + +typedef int jpeg_data_unit_t[64]; + +struct grub_jpeg_data +{ + grub_file_t file; + struct grub_video_bitmap **bitmap; + grub_uint8_t *bitmap_ptr; + + unsigned image_width; + unsigned image_height; + + grub_uint8_t *huff_value[4]; + int huff_offset[4][16]; + int huff_maxval[4][16]; + + grub_uint8_t quan_table[2][64]; + int comp_index[3][3]; + + jpeg_data_unit_t ydu[4]; + jpeg_data_unit_t crdu; + jpeg_data_unit_t cbdu; + + unsigned log_vs, log_hs; + int dri; + unsigned r1; + + int dc_value[3]; + + int color_components; + + int bit_mask, bit_save; +}; + +static grub_uint8_t +grub_jpeg_get_byte (struct grub_jpeg_data *data) +{ + grub_uint8_t r; + + r = 0; + grub_file_read (data->file, &r, 1); + + return r; +} + +static grub_uint16_t +grub_jpeg_get_word (struct grub_jpeg_data *data) +{ + grub_uint16_t r; + + r = 0; + grub_file_read (data->file, &r, sizeof (grub_uint16_t)); + + return grub_be_to_cpu16 (r); +} + +static int +grub_jpeg_get_bit (struct grub_jpeg_data *data) +{ + int ret; + + if (data->bit_mask == 0) + { + data->bit_save = grub_jpeg_get_byte (data); + if (data->bit_save == JPEG_ESC_CHAR) + { + if (grub_jpeg_get_byte (data) != 0) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: invalid 0xFF in data stream"); + return 0; + } + } + data->bit_mask = 0x80; + } + + ret = ((data->bit_save & data->bit_mask) != 0); + data->bit_mask >>= 1; + return ret; +} + +static int +grub_jpeg_get_number (struct grub_jpeg_data *data, int num) +{ + int value, i, msb; + + if (num == 0) + return 0; + + msb = value = grub_jpeg_get_bit (data); + for (i = 1; i < num; i++) + value = (value << 1) + (grub_jpeg_get_bit (data) != 0); + if (!msb) + value += 1 - (1 << num); + + return value; +} + +static int +grub_jpeg_get_huff_code (struct grub_jpeg_data *data, int id) +{ + int code; + unsigned i; + + code = 0; + for (i = 0; i < ARRAY_SIZE (data->huff_maxval[id]); i++) + { + code <<= 1; + if (grub_jpeg_get_bit (data)) + code++; + if (code < data->huff_maxval[id][i]) + return data->huff_value[id][code + data->huff_offset[id][i]]; + } + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: huffman decode fails"); + return 0; +} + +static grub_err_t +grub_jpeg_decode_huff_table (struct grub_jpeg_data *data) +{ + int id, ac, n, base, ofs; + grub_uint32_t next_marker; + grub_uint8_t count[16]; + unsigned i; + + next_marker = data->file->offset; + next_marker += grub_jpeg_get_word (data); + + while (data->file->offset + sizeof (count) + 1 <= next_marker) + { + id = grub_jpeg_get_byte (data); + ac = (id >> 4) & 1; + id &= 0xF; + if (id > 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: too many huffman tables"); + + if (grub_file_read (data->file, &count, sizeof (count)) != + sizeof (count)) + return grub_errno; + + n = 0; + for (i = 0; i < ARRAY_SIZE (count); i++) + n += count[i]; + + id += ac * 2; + data->huff_value[id] = grub_malloc (n); + if (grub_errno) + return grub_errno; + + if (grub_file_read (data->file, data->huff_value[id], n) != n) + return grub_errno; + + base = 0; + ofs = 0; + for (i = 0; i < ARRAY_SIZE (count); i++) + { + base += count[i]; + ofs += count[i]; + + data->huff_maxval[id][i] = base; + data->huff_offset[id][i] = ofs - base; + + base <<= 1; + } + } + + if (data->file->offset != next_marker) + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in huffman table"); + + return grub_errno; +} + +static grub_err_t +grub_jpeg_decode_quan_table (struct grub_jpeg_data *data) +{ + int id; + grub_uint32_t next_marker; + + next_marker = data->file->offset; + next_marker += grub_jpeg_get_word (data); + + if (next_marker > data->file->size) + { + /* Should never be set beyond the size of the file. */ + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid next reference"); + } + + while (data->file->offset + sizeof (data->quan_table[id]) + 1 + <= next_marker) + { + id = grub_jpeg_get_byte (data); + if (id >= 0x10) /* Upper 4-bit is precision. */ + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: only 8-bit precision is supported"); + + if (id > 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: too many quantization tables"); + + if (grub_file_read (data->file, &data->quan_table[id], + sizeof (data->quan_table[id])) + != sizeof (data->quan_table[id])) + return grub_errno; + + } + + if (data->file->offset != next_marker) + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: extra byte in quantization table"); + + return grub_errno; +} + +static grub_err_t +grub_jpeg_decode_sof (struct grub_jpeg_data *data) +{ + int i, cc; + grub_uint32_t next_marker; + + next_marker = data->file->offset; + next_marker += grub_jpeg_get_word (data); + + if (grub_jpeg_get_byte (data) != 8) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: only 8-bit precision is supported"); + + data->image_height = grub_jpeg_get_word (data); + data->image_width = grub_jpeg_get_word (data); + + if ((!data->image_height) || (!data->image_width)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid image size"); + + cc = grub_jpeg_get_byte (data); + if (cc != 1 && cc != 3) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: component count must be 1 or 3"); + data->color_components = cc; + + for (i = 0; i < cc; i++) + { + int id, ss; + + id = grub_jpeg_get_byte (data) - 1; + if ((id < 0) || (id >= 3)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); + + ss = grub_jpeg_get_byte (data); /* Sampling factor. */ + if (!id) + { + grub_uint8_t vs, hs; + vs = ss & 0xF; /* Vertical sampling. */ + hs = ss >> 4; /* Horizontal sampling. */ + if ((vs > 2) || (hs > 2) || (vs == 0) || (hs == 0)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: sampling method not supported"); + data->log_vs = (vs == 2); + data->log_hs = (hs == 2); + } + else if (ss != JPEG_SAMPLING_1x1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: sampling method not supported"); + + data->comp_index[id][0] = grub_jpeg_get_byte (data); + if (data->comp_index[id][0] > 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: too many quantization tables"); + } + + if (data->file->offset != next_marker) + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sof"); + + return grub_errno; +} + +static grub_err_t +grub_jpeg_decode_dri (struct grub_jpeg_data *data) +{ + if (grub_jpeg_get_word (data) != 4) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: DRI marker length must be 4"); + + data->dri = grub_jpeg_get_word (data); + + return grub_errno; +} + +static void +grub_jpeg_idct_transform (jpeg_data_unit_t du) +{ + int *pd; + int i; + int t0, t1, t2, t3, t4, t5, t6, t7; + int v0, v1, v2, v3, v4; + + pd = du; + for (i = 0; i < JPEG_UNIT_SIZE; i++, pd++) + { + if ((pd[JPEG_UNIT_SIZE * 1] | pd[JPEG_UNIT_SIZE * 2] | + pd[JPEG_UNIT_SIZE * 3] | pd[JPEG_UNIT_SIZE * 4] | + pd[JPEG_UNIT_SIZE * 5] | pd[JPEG_UNIT_SIZE * 6] | + pd[JPEG_UNIT_SIZE * 7]) == 0) + { + pd[JPEG_UNIT_SIZE * 0] <<= SHIFT_BITS; + + pd[JPEG_UNIT_SIZE * 1] = pd[JPEG_UNIT_SIZE * 2] + = pd[JPEG_UNIT_SIZE * 3] = pd[JPEG_UNIT_SIZE * 4] + = pd[JPEG_UNIT_SIZE * 5] = pd[JPEG_UNIT_SIZE * 6] + = pd[JPEG_UNIT_SIZE * 7] = pd[JPEG_UNIT_SIZE * 0]; + + continue; + } + + t0 = pd[JPEG_UNIT_SIZE * 0]; + t1 = pd[JPEG_UNIT_SIZE * 2]; + t2 = pd[JPEG_UNIT_SIZE * 4]; + t3 = pd[JPEG_UNIT_SIZE * 6]; + + v4 = (t1 + t3) * CONST (0.541196100); + + v0 = ((t0 + t2) << SHIFT_BITS); + v1 = ((t0 - t2) << SHIFT_BITS); + v2 = v4 - t3 * CONST (1.847759065); + v3 = v4 + t1 * CONST (0.765366865); + + t0 = v0 + v3; + t3 = v0 - v3; + t1 = v1 + v2; + t2 = v1 - v2; + + t4 = pd[JPEG_UNIT_SIZE * 7]; + t5 = pd[JPEG_UNIT_SIZE * 5]; + t6 = pd[JPEG_UNIT_SIZE * 3]; + t7 = pd[JPEG_UNIT_SIZE * 1]; + + v0 = t4 + t7; + v1 = t5 + t6; + v2 = t4 + t6; + v3 = t5 + t7; + + v4 = (v2 + v3) * CONST (1.175875602); + + v0 *= CONST (0.899976223); + v1 *= CONST (2.562915447); + v2 = v2 * CONST (1.961570560) - v4; + v3 = v3 * CONST (0.390180644) - v4; + + t4 = t4 * CONST (0.298631336) - v0 - v2; + t5 = t5 * CONST (2.053119869) - v1 - v3; + t6 = t6 * CONST (3.072711026) - v1 - v2; + t7 = t7 * CONST (1.501321110) - v0 - v3; + + pd[JPEG_UNIT_SIZE * 0] = t0 + t7; + pd[JPEG_UNIT_SIZE * 7] = t0 - t7; + pd[JPEG_UNIT_SIZE * 1] = t1 + t6; + pd[JPEG_UNIT_SIZE * 6] = t1 - t6; + pd[JPEG_UNIT_SIZE * 2] = t2 + t5; + pd[JPEG_UNIT_SIZE * 5] = t2 - t5; + pd[JPEG_UNIT_SIZE * 3] = t3 + t4; + pd[JPEG_UNIT_SIZE * 4] = t3 - t4; + } + + pd = du; + for (i = 0; i < JPEG_UNIT_SIZE; i++, pd += JPEG_UNIT_SIZE) + { + if ((pd[1] | pd[2] | pd[3] | pd[4] | pd[5] | pd[6] | pd[7]) == 0) + { + pd[0] >>= (SHIFT_BITS + 3); + pd[1] = pd[2] = pd[3] = pd[4] = pd[5] = pd[6] = pd[7] = pd[0]; + continue; + } + + v4 = (pd[2] + pd[6]) * CONST (0.541196100); + + v0 = (pd[0] + pd[4]) << SHIFT_BITS; + v1 = (pd[0] - pd[4]) << SHIFT_BITS; + v2 = v4 - pd[6] * CONST (1.847759065); + v3 = v4 + pd[2] * CONST (0.765366865); + + t0 = v0 + v3; + t3 = v0 - v3; + t1 = v1 + v2; + t2 = v1 - v2; + + t4 = pd[7]; + t5 = pd[5]; + t6 = pd[3]; + t7 = pd[1]; + + v0 = t4 + t7; + v1 = t5 + t6; + v2 = t4 + t6; + v3 = t5 + t7; + + v4 = (v2 + v3) * CONST (1.175875602); + + v0 *= CONST (0.899976223); + v1 *= CONST (2.562915447); + v2 = v2 * CONST (1.961570560) - v4; + v3 = v3 * CONST (0.390180644) - v4; + + t4 = t4 * CONST (0.298631336) - v0 - v2; + t5 = t5 * CONST (2.053119869) - v1 - v3; + t6 = t6 * CONST (3.072711026) - v1 - v2; + t7 = t7 * CONST (1.501321110) - v0 - v3; + + pd[0] = (t0 + t7) >> (SHIFT_BITS * 2 + 3); + pd[7] = (t0 - t7) >> (SHIFT_BITS * 2 + 3); + pd[1] = (t1 + t6) >> (SHIFT_BITS * 2 + 3); + pd[6] = (t1 - t6) >> (SHIFT_BITS * 2 + 3); + pd[2] = (t2 + t5) >> (SHIFT_BITS * 2 + 3); + pd[5] = (t2 - t5) >> (SHIFT_BITS * 2 + 3); + pd[3] = (t3 + t4) >> (SHIFT_BITS * 2 + 3); + pd[4] = (t3 - t4) >> (SHIFT_BITS * 2 + 3); + } + + for (i = 0; i < JPEG_UNIT_SIZE * JPEG_UNIT_SIZE; i++) + { + du[i] += 128; + + if (du[i] < 0) + du[i] = 0; + if (du[i] > 255) + du[i] = 255; + } +} + +static void +grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du) +{ + int h1, h2, qt; + unsigned pos; + + grub_memset (du, 0, sizeof (jpeg_data_unit_t)); + + qt = data->comp_index[id][0]; + h1 = data->comp_index[id][1]; + h2 = data->comp_index[id][2]; + + data->dc_value[id] += + grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1)); + + du[0] = data->dc_value[id] * (int) data->quan_table[qt][0]; + pos = 1; + while (pos < ARRAY_SIZE (data->quan_table[qt])) + { + int num, val; + + num = grub_jpeg_get_huff_code (data, h2); + if (!num) + break; + + val = grub_jpeg_get_number (data, num & 0xF); + num >>= 4; + pos += num; + + if (pos >= ARRAY_SIZE (jpeg_zigzag_order)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: invalid position in zigzag order!?"); + return; + } + + du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos]; + pos++; + } + + grub_jpeg_idct_transform (du); +} + +static void +grub_jpeg_ycrcb_to_rgb (int yy, int cr, int cb, grub_uint8_t * rgb) +{ + int dd; + + cr -= 128; + cb -= 128; + + /* Red */ + dd = yy + ((cr * CONST (1.402)) >> SHIFT_BITS); + if (dd < 0) + dd = 0; + if (dd > 255) + dd = 255; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + rgb[2] = dd; +#else + *(rgb++) = dd; +#endif + + /* Green */ + dd = yy - ((cb * CONST (0.34414) + cr * CONST (0.71414)) >> SHIFT_BITS); + if (dd < 0) + dd = 0; + if (dd > 255) + dd = 255; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + rgb[1] = dd; +#else + *(rgb++) = dd; +#endif + + /* Blue */ + dd = yy + ((cb * CONST (1.772)) >> SHIFT_BITS); + if (dd < 0) + dd = 0; + if (dd > 255) + dd = 255; +#ifdef GRUB_CPU_WORDS_BIGENDIAN + rgb[0] = dd; + rgb += 3; +#else + *(rgb++) = dd; +#endif +} + +static grub_err_t +grub_jpeg_decode_sos (struct grub_jpeg_data *data) +{ + int i, cc; + grub_uint32_t data_offset; + + data_offset = data->file->offset; + data_offset += grub_jpeg_get_word (data); + + cc = grub_jpeg_get_byte (data); + + if (cc != 3 && cc != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: component count must be 1 or 3"); + data->color_components = cc; + + for (i = 0; i < cc; i++) + { + int id, ht; + + id = grub_jpeg_get_byte (data) - 1; + if ((id < 0) || (id >= 3)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index"); + + ht = grub_jpeg_get_byte (data); + data->comp_index[id][1] = (ht >> 4); + data->comp_index[id][2] = (ht & 0xF) + 2; + + if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) || + (data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index"); + } + + grub_jpeg_get_byte (data); /* Skip 3 unused bytes. */ + grub_jpeg_get_word (data); + + if (data->file->offset != data_offset) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos"); + + if (grub_video_bitmap_create (data->bitmap, data->image_width, + data->image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888)) + return grub_errno; + + data->bitmap_ptr = (*data->bitmap)->data; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_jpeg_decode_data (struct grub_jpeg_data *data) +{ + unsigned c1, vb, hb, nr1, nc1; + int rst = data->dri; + + vb = 8 << data->log_vs; + hb = 8 << data->log_hs; + nr1 = (data->image_height + vb - 1) >> (3 + data->log_vs); + nc1 = (data->image_width + hb - 1) >> (3 + data->log_hs); + + if (data->bitmap_ptr == NULL) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, + "jpeg: attempted to decode data before start of stream"); + + for (; data->r1 < nr1 && (!data->dri || rst); + data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3) + for (c1 = 0; c1 < nc1 && (!data->dri || rst); + c1++, rst--, data->bitmap_ptr += hb * 3) + { + unsigned r2, c2, nr2, nc2; + grub_uint8_t *ptr2; + + for (r2 = 0; r2 < (1U << data->log_vs); r2++) + for (c2 = 0; c2 < (1U << data->log_hs); c2++) + grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]); + + if (data->color_components >= 3) + { + grub_jpeg_decode_du (data, 1, data->cbdu); + grub_jpeg_decode_du (data, 2, data->crdu); + } + + if (grub_errno) + return grub_errno; + + nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb; + nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb; + + ptr2 = data->bitmap_ptr; + for (r2 = 0; r2 < nr2; r2++, ptr2 += (data->image_width - nc2) * 3) + for (c2 = 0; c2 < nc2; c2++, ptr2 += 3) + { + unsigned i0; + int yy; + + i0 = (r2 >> data->log_vs) * 8 + (c2 >> data->log_hs); + yy = data->ydu[(r2 / 8) * 2 + (c2 / 8)][(r2 % 8) * 8 + (c2 % 8)]; + + if (data->color_components >= 3) + { + int cr, cb; + cr = data->crdu[i0]; + cb = data->cbdu[i0]; + grub_jpeg_ycrcb_to_rgb (yy, cr, cb, ptr2); + } + else + { + ptr2[0] = yy; + ptr2[1] = yy; + ptr2[2] = yy; + } + } + } + + return grub_errno; +} + +static void +grub_jpeg_reset (struct grub_jpeg_data *data) +{ + data->bit_mask = 0x0; + + data->dc_value[0] = 0; + data->dc_value[1] = 0; + data->dc_value[2] = 0; +} + +static grub_uint8_t +grub_jpeg_get_marker (struct grub_jpeg_data *data) +{ + grub_uint8_t r; + + r = grub_jpeg_get_byte (data); + + if (r != JPEG_ESC_CHAR) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid maker"); + return 0; + } + + return grub_jpeg_get_byte (data); +} + +static grub_err_t +grub_jpeg_decode_jpeg (struct grub_jpeg_data *data) +{ + if (grub_jpeg_get_marker (data) != JPEG_MARKER_SOI) /* Start Of Image. */ + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid jpeg file"); + + while (grub_errno == 0) + { + grub_uint8_t marker; + + marker = grub_jpeg_get_marker (data); + if (grub_errno) + break; + + grub_dprintf ("jpeg", "jpeg marker: %x\n", marker); + + switch (marker) + { + case JPEG_MARKER_DHT: /* Define Huffman Table. */ + grub_jpeg_decode_huff_table (data); + break; + case JPEG_MARKER_DQT: /* Define Quantization Table. */ + grub_jpeg_decode_quan_table (data); + break; + case JPEG_MARKER_SOF0: /* Start Of Frame 0. */ + grub_jpeg_decode_sof (data); + break; + case JPEG_MARKER_DRI: /* Define Restart Interval. */ + grub_jpeg_decode_dri (data); + break; + case JPEG_MARKER_SOS: /* Start Of Scan. */ + if (grub_jpeg_decode_sos (data)) + break; + /* FALLTHROUGH */ + case JPEG_MARKER_RST0: /* Restart. */ + case JPEG_MARKER_RST1: + case JPEG_MARKER_RST2: + case JPEG_MARKER_RST3: + case JPEG_MARKER_RST4: + case JPEG_MARKER_RST5: + case JPEG_MARKER_RST6: + case JPEG_MARKER_RST7: + grub_jpeg_decode_data (data); + grub_jpeg_reset (data); + break; + case JPEG_MARKER_EOI: /* End Of Image. */ + return grub_errno; + default: /* Skip unrecognized marker. */ + { + grub_uint16_t sz; + + sz = grub_jpeg_get_word (data); + if (grub_errno) + return (grub_errno); + grub_file_seek (data->file, data->file->offset + sz - 2); + } + } + } + + return grub_errno; +} + +static grub_err_t +grub_video_reader_jpeg (struct grub_video_bitmap **bitmap, + const char *filename) +{ + grub_file_t file; + struct grub_jpeg_data *data; + + file = grub_buffile_open (filename, GRUB_FILE_TYPE_PIXMAP, 0); + if (!file) + return grub_errno; + + data = grub_zalloc (sizeof (*data)); + if (data != NULL) + { + int i; + + data->file = file; + data->bitmap = bitmap; + grub_jpeg_decode_jpeg (data); + + for (i = 0; i < 4; i++) + grub_free (data->huff_value[i]); + + grub_free (data); + } + + if (grub_errno != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (*bitmap); + *bitmap = 0; + } + + grub_file_close (file); + return grub_errno; +} + +#if defined(JPEG_DEBUG) +static grub_err_t +grub_cmd_jpegtest (grub_command_t cmdd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_video_bitmap *bitmap = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_video_reader_jpeg (&bitmap, args[0]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + grub_video_bitmap_destroy (bitmap); + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_video_bitmap_reader jpg_reader = { + .extension = ".jpg", + .reader = grub_video_reader_jpeg, + .next = 0 +}; + +static struct grub_video_bitmap_reader jpeg_reader = { + .extension = ".jpeg", + .reader = grub_video_reader_jpeg, + .next = 0 +}; + +GRUB_MOD_INIT (jpeg) +{ + grub_video_bitmap_reader_register (&jpg_reader); + grub_video_bitmap_reader_register (&jpeg_reader); +#if defined(JPEG_DEBUG) + cmd = grub_register_command ("jpegtest", grub_cmd_jpegtest, + "FILE", "Tests loading of JPEG bitmap."); +#endif +} + +GRUB_MOD_FINI (jpeg) +{ +#if defined(JPEG_DEBUG) + grub_unregister_command (cmd); +#endif + grub_video_bitmap_reader_unregister (&jpeg_reader); + grub_video_bitmap_reader_unregister (&jpg_reader); +} diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c new file mode 100644 index 0000000..0157ff7 --- /dev/null +++ b/grub-core/video/readers/png.c @@ -0,0 +1,1166 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/bitmap.h> +#include <grub/types.h> +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/bufio.h> +#include <grub/safemath.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Uncomment following define to enable PNG debug. */ +//#define PNG_DEBUG + +enum + { + PNG_COLOR_TYPE_GRAY = 0, + PNG_COLOR_MASK_PALETTE = 1, + PNG_COLOR_MASK_COLOR = 2, + PNG_COLOR_MASK_ALPHA = 4, + PNG_COLOR_TYPE_PALETTE = (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE), + }; + +#define PNG_COMPRESSION_BASE 0 + +#define PNG_INTERLACE_NONE 0 +#define PNG_INTERLACE_ADAM7 1 + +#define PNG_FILTER_TYPE_BASE 0 + +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +enum + { + PNG_CHUNK_IHDR = 0x49484452, + PNG_CHUNK_IDAT = 0x49444154, + PNG_CHUNK_IEND = 0x49454e44, + PNG_CHUNK_PLTE = 0x504c5445 + }; + +#define Z_DEFLATED 8 +#define Z_FLAG_DICT 32 + +#define INFLATE_STORED 0 +#define INFLATE_FIXED 1 +#define INFLATE_DYNAMIC 2 + +#define WSIZE 0x8000 + +#define DEFLATE_HCLEN_BASE 4 +#define DEFLATE_HCLEN_MAX 19 +#define DEFLATE_HLIT_BASE 257 +#define DEFLATE_HLIT_MAX 288 +#define DEFLATE_HDIST_BASE 1 +#define DEFLATE_HDIST_MAX 30 + +#define DEFLATE_HUFF_LEN 16 + +#ifdef PNG_DEBUG +static grub_command_t cmd; +#endif + +struct huff_table +{ + int *values, *maxval, *offset; + int num_values, max_length; +}; + +struct grub_png_data +{ + grub_file_t file; + struct grub_video_bitmap **bitmap; + + int bit_count, bit_save; + + grub_uint32_t next_offset; + + unsigned image_width, image_height; + int bpp, is_16bit; + int raw_bytes, is_gray, is_alpha, is_palette; + int row_bytes, color_bits; + grub_uint8_t *image_data; + + int inside_idat, idat_remain; + + int code_values[DEFLATE_HLIT_MAX]; + int code_maxval[DEFLATE_HUFF_LEN]; + int code_offset[DEFLATE_HUFF_LEN]; + + int dist_values[DEFLATE_HDIST_MAX]; + int dist_maxval[DEFLATE_HUFF_LEN]; + int dist_offset[DEFLATE_HUFF_LEN]; + + grub_uint8_t palette[256][3]; + + struct huff_table code_table; + struct huff_table dist_table; + + grub_uint8_t slide[WSIZE]; + int wp; + + grub_uint8_t *cur_rgb; + + int cur_column, cur_filter, first_line; +}; + +static grub_uint32_t +grub_png_get_dword (struct grub_png_data *data) +{ + grub_uint32_t r; + + r = 0; + grub_file_read (data->file, &r, sizeof (grub_uint32_t)); + + return grub_be_to_cpu32 (r); +} + +static grub_uint8_t +grub_png_get_byte (struct grub_png_data *data) +{ + grub_uint8_t r; + + if ((data->inside_idat) && (data->idat_remain == 0)) + { + grub_uint32_t len, type; + + do + { + /* Skip crc checksum. */ + grub_png_get_dword (data); + + if (data->file->offset != data->next_offset) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: chunk size error"); + return 0; + } + + len = grub_png_get_dword (data); + type = grub_png_get_dword (data); + if (type != PNG_CHUNK_IDAT) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: unexpected end of data"); + return 0; + } + + data->next_offset = data->file->offset + len + 4; + } + while (len == 0); + data->idat_remain = len; + } + + r = 0; + grub_file_read (data->file, &r, 1); + + if (data->inside_idat) + data->idat_remain--; + + return r; +} + +static int +grub_png_get_bits (struct grub_png_data *data, int num) +{ + int code, shift; + + if (data->bit_count == 0) + { + data->bit_save = grub_png_get_byte (data); + data->bit_count = 8; + } + + code = 0; + shift = 0; + while (grub_errno == 0) + { + int n; + + n = data->bit_count; + if (n > num) + n = num; + + code += (int) (data->bit_save & ((1 << n) - 1)) << shift; + num -= n; + if (!num) + { + data->bit_count -= n; + data->bit_save >>= n; + break; + } + + shift += n; + + data->bit_save = grub_png_get_byte (data); + data->bit_count = 8; + } + + return code; +} + +static grub_err_t +grub_png_decode_image_palette (struct grub_png_data *data, + unsigned len) +{ + unsigned i = 0, j; + + if (len == 0) + return GRUB_ERR_NONE; + + for (i = 0; 3 * i < len && i < 256; i++) + for (j = 0; j < 3; j++) + data->palette[i][j] = grub_png_get_byte (data); + for (i *= 3; i < len; i++) + grub_png_get_byte (data); + + grub_png_get_dword (data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_png_decode_image_header (struct grub_png_data *data) +{ + int color_type; + int color_bits; + enum grub_video_blit_format blt; + + data->image_width = grub_png_get_dword (data); + data->image_height = grub_png_get_dword (data); + + if ((!data->image_height) || (!data->image_width)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size"); + + color_bits = grub_png_get_byte (data); + data->is_16bit = (color_bits == 16); + + color_type = grub_png_get_byte (data); + + /* According to PNG spec, no other types are valid. */ + if ((color_type & ~(PNG_COLOR_MASK_ALPHA | PNG_COLOR_MASK_COLOR)) + && (color_type != PNG_COLOR_TYPE_PALETTE)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: color type not supported"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + data->is_palette = 1; + if (data->is_16bit && data->is_palette) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: color type not supported"); + if (color_type & PNG_COLOR_MASK_ALPHA) + blt = GRUB_VIDEO_BLIT_FORMAT_RGBA_8888; + else + blt = GRUB_VIDEO_BLIT_FORMAT_RGB_888; + if (data->is_palette) + data->bpp = 1; + else if (color_type & PNG_COLOR_MASK_COLOR) + data->bpp = 3; + else + { + data->is_gray = 1; + data->bpp = 1; + } + + if ((color_bits != 8) && (color_bits != 16) + && (color_bits != 4 + || !(data->is_gray || data->is_palette))) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: bit depth must be 8 or 16"); + + if (color_type & PNG_COLOR_MASK_ALPHA) + data->bpp++; + + if (grub_video_bitmap_create (data->bitmap, data->image_width, + data->image_height, + blt)) + return grub_errno; + + if (data->is_16bit) + data->bpp <<= 1; + + data->color_bits = color_bits; + + if (grub_mul (data->image_width, data->bpp, &data->row_bytes)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + if (data->color_bits <= 4) + { + if (grub_mul (data->image_width, data->color_bits + 7, &data->row_bytes)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + data->row_bytes >>= 3; + } + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + if (data->is_16bit || data->is_gray || data->is_palette) +#endif + { + data->image_data = grub_calloc (data->image_height, data->row_bytes); + if (grub_errno) + return grub_errno; + + data->cur_rgb = data->image_data; + } +#ifndef GRUB_CPU_WORDS_BIGENDIAN + else + { + data->image_data = 0; + data->cur_rgb = (*data->bitmap)->data; + } +#endif + + data->raw_bytes = data->image_height * (data->row_bytes + 1); + + data->cur_column = 0; + data->first_line = 1; + + if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: compression method not supported"); + + if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: filter method not supported"); + + if (grub_png_get_byte (data) != PNG_INTERLACE_NONE) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: interlace method not supported"); + + /* Skip crc checksum. */ + grub_png_get_dword (data); + + return grub_errno; +} + +/* Order of the bit length code lengths. */ +static const grub_uint8_t bitorder[] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +/* Copy lengths for literal codes 257..285. */ +static const int cplens[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* Extra bits for literal codes 257..285. */ +static const grub_uint8_t cplext[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 +}; /* 99==invalid */ + +/* Copy offsets for distance codes 0..29. */ +static const int cpdist[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes. */ +static const grub_uint8_t cpdext[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 +}; + +static void +grub_png_init_huff_table (struct huff_table *ht, int cur_maxlen, + int *cur_values, int *cur_maxval, int *cur_offset) +{ + ht->values = cur_values; + ht->maxval = cur_maxval; + ht->offset = cur_offset; + ht->num_values = 0; + ht->max_length = cur_maxlen; + grub_memset (cur_maxval, 0, sizeof (int) * cur_maxlen); +} + +static void +grub_png_insert_huff_item (struct huff_table *ht, int code, int len) +{ + int i, n; + + if (len == 0) + return; + + if (len > ht->max_length) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid code length"); + return; + } + + n = 0; + for (i = len; i < ht->max_length; i++) + n += ht->maxval[i]; + + for (i = 0; i < n; i++) + ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1]; + + ht->values[ht->num_values - n] = code; + ht->num_values++; + ht->maxval[len - 1]++; +} + +static void +grub_png_build_huff_table (struct huff_table *ht) +{ + int base, ofs, i; + + base = 0; + ofs = 0; + for (i = 0; i < ht->max_length; i++) + { + base += ht->maxval[i]; + ofs += ht->maxval[i]; + + ht->maxval[i] = base; + ht->offset[i] = ofs - base; + + base <<= 1; + } +} + +static int +grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht) +{ + int code, i; + + code = 0; + for (i = 0; i < ht->max_length; i++) + { + code = (code << 1) + grub_png_get_bits (data, 1); + if (code < ht->maxval[i]) + return ht->values[code + ht->offset[i]]; + } + return 0; +} + +static grub_err_t +grub_png_init_fixed_block (struct grub_png_data *data) +{ + int i; + + grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN, + data->code_values, data->code_maxval, + data->code_offset); + + for (i = 0; i < 144; i++) + grub_png_insert_huff_item (&data->code_table, i, 8); + + for (; i < 256; i++) + grub_png_insert_huff_item (&data->code_table, i, 9); + + for (; i < 280; i++) + grub_png_insert_huff_item (&data->code_table, i, 7); + + for (; i < DEFLATE_HLIT_MAX; i++) + grub_png_insert_huff_item (&data->code_table, i, 8); + + grub_png_build_huff_table (&data->code_table); + + grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN, + data->dist_values, data->dist_maxval, + data->dist_offset); + + for (i = 0; i < DEFLATE_HDIST_MAX; i++) + grub_png_insert_huff_item (&data->dist_table, i, 5); + + grub_png_build_huff_table (&data->dist_table); + + return grub_errno; +} + +static grub_err_t +grub_png_init_dynamic_block (struct grub_png_data *data) +{ + int nl, nd, nb, i, prev; + struct huff_table cl; + int cl_values[sizeof (bitorder)]; + int cl_maxval[8]; + int cl_offset[8]; + grub_uint8_t lens[DEFLATE_HCLEN_MAX]; + + nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5); + nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5); + nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4); + + if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) || + (nb > DEFLATE_HCLEN_MAX)) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: too much data"); + + grub_png_init_huff_table (&cl, 8, cl_values, cl_maxval, cl_offset); + + for (i = 0; i < nb; i++) + lens[bitorder[i]] = grub_png_get_bits (data, 3); + + for (; i < DEFLATE_HCLEN_MAX; i++) + lens[bitorder[i]] = 0; + + for (i = 0; i < DEFLATE_HCLEN_MAX; i++) + grub_png_insert_huff_item (&cl, i, lens[i]); + + grub_png_build_huff_table (&cl); + + grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN, + data->code_values, data->code_maxval, + data->code_offset); + + grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN, + data->dist_values, data->dist_maxval, + data->dist_offset); + + prev = 0; + for (i = 0; i < nl + nd; i++) + { + int n, code; + struct huff_table *ht; + + if (grub_errno) + return grub_errno; + + if (i < nl) + { + ht = &data->code_table; + code = i; + } + else + { + ht = &data->dist_table; + code = i - nl; + } + + n = grub_png_get_huff_code (data, &cl); + if (n < 16) + { + grub_png_insert_huff_item (ht, code, n); + prev = n; + } + else if (n == 16) + { + int c; + + c = 3 + grub_png_get_bits (data, 2); + while (c > 0) + { + grub_png_insert_huff_item (ht, code++, prev); + i++; + c--; + } + i--; + } + else if (n == 17) + i += 3 + grub_png_get_bits (data, 3) - 1; + else + i += 11 + grub_png_get_bits (data, 7) - 1; + } + + grub_png_build_huff_table (&data->code_table); + grub_png_build_huff_table (&data->dist_table); + + return grub_errno; +} + +static grub_err_t +grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n) +{ + if (--data->raw_bytes < 0) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflown"); + + if (data->cur_column == 0) + { + if (n >= PNG_FILTER_VALUE_LAST) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid filter value"); + + data->cur_filter = n; + } + else + *data->cur_rgb++ = n; + + data->cur_column++; + if (data->cur_column == data->row_bytes + 1) + { + grub_uint8_t *blank_line = NULL; + grub_uint8_t *cur = data->cur_rgb - data->row_bytes; + grub_uint8_t *left = cur; + grub_uint8_t *up; + + if (data->first_line) + { + blank_line = grub_zalloc (data->row_bytes); + if (blank_line == NULL) + return grub_errno; + + up = blank_line; + } + else + up = cur - data->row_bytes; + + switch (data->cur_filter) + { + case PNG_FILTER_VALUE_SUB: + { + int i; + + cur += data->bpp; + for (i = data->bpp; i < data->row_bytes; i++, cur++, left++) + *cur += *left; + + break; + } + case PNG_FILTER_VALUE_UP: + { + int i; + + for (i = 0; i < data->row_bytes; i++, cur++, up++) + *cur += *up; + + break; + } + case PNG_FILTER_VALUE_AVG: + { + int i; + + for (i = 0; i < data->bpp; i++, cur++, up++) + *cur += *up >> 1; + + for (; i < data->row_bytes; i++, cur++, up++, left++) + *cur += ((int) *up + (int) *left) >> 1; + + break; + } + case PNG_FILTER_VALUE_PAETH: + { + int i; + grub_uint8_t *upper_left = up; + + for (i = 0; i < data->bpp; i++, cur++, up++) + *cur += *up; + + for (; i < data->row_bytes; i++, cur++, up++, left++, upper_left++) + { + int a, b, c, pa, pb, pc; + + a = *left; + b = *up; + c = *upper_left; + + pa = b - c; + pb = a - c; + pc = pa + pb; + + if (pa < 0) + pa = -pa; + + if (pb < 0) + pb = -pb; + + if (pc < 0) + pc = -pc; + + *cur += ((pa <= pb) && (pa <= pc)) ? a : (pb <= pc) ? b : c; + } + } + } + + grub_free (blank_line); + + data->cur_column = 0; + data->first_line = 0; + } + + return grub_errno; +} + +static grub_err_t +grub_png_read_dynamic_block (struct grub_png_data *data) +{ + while (grub_errno == 0) + { + int n; + + n = grub_png_get_huff_code (data, &data->code_table); + if (n < 256) + { + data->slide[data->wp] = n; + grub_png_output_byte (data, n); + + data->wp++; + if (data->wp >= WSIZE) + data->wp = 0; + } + else if (n == 256) + break; + else + { + int len, dist, pos; + + n -= 257; + len = cplens[n]; + if (cplext[n]) + len += grub_png_get_bits (data, cplext[n]); + + n = grub_png_get_huff_code (data, &data->dist_table); + dist = cpdist[n]; + if (cpdext[n]) + dist += grub_png_get_bits (data, cpdext[n]); + + pos = data->wp - dist; + if (pos < 0) + pos += WSIZE; + + while (len > 0) + { + data->slide[data->wp] = data->slide[pos]; + grub_png_output_byte (data, data->slide[data->wp]); + + data->wp++; + if (data->wp >= WSIZE) + data->wp = 0; + + pos++; + if (pos >= WSIZE) + pos = 0; + + len--; + } + } + } + + return grub_errno; +} + +static grub_err_t +grub_png_decode_image_data (struct grub_png_data *data) +{ + grub_uint8_t cmf, flg; + int final; + + cmf = grub_png_get_byte (data); + flg = grub_png_get_byte (data); + + if ((cmf & 0xF) != Z_DEFLATED) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: only support deflate compression method"); + + if (flg & Z_FLAG_DICT) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: dictionary not supported"); + + do + { + int block_type; + + final = grub_png_get_bits (data, 1); + block_type = grub_png_get_bits (data, 2); + + switch (block_type) + { + case INFLATE_STORED: + { + grub_uint16_t i, len; + + data->bit_count = 0; + len = grub_png_get_byte (data); + len += ((grub_uint16_t) grub_png_get_byte (data)) << 8; + + /* Skip NLEN field. */ + grub_png_get_byte (data); + grub_png_get_byte (data); + + for (i = 0; i < len; i++) + grub_png_output_byte (data, grub_png_get_byte (data)); + + break; + } + + case INFLATE_FIXED: + grub_png_init_fixed_block (data); + grub_png_read_dynamic_block (data); + break; + + case INFLATE_DYNAMIC: + grub_png_init_dynamic_block (data); + grub_png_read_dynamic_block (data); + break; + + default: + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: unknown block type"); + } + } + while ((!final) && (grub_errno == 0)); + + /* Skip adler checksum. */ + grub_png_get_dword (data); + + /* Skip crc checksum. */ + grub_png_get_dword (data); + + return grub_errno; +} + +static const grub_uint8_t png_magic[8] = + { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a }; + +static void +grub_png_convert_image (struct grub_png_data *data) +{ + unsigned i; + grub_uint8_t *d1, *d2; + + d1 = (*data->bitmap)->data; + d2 = data->image_data + data->is_16bit; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN +#define R4 3 +#define G4 2 +#define B4 1 +#define A4 0 +#define R3 2 +#define G3 1 +#define B3 0 +#else +#define R4 0 +#define G4 1 +#define B4 2 +#define A4 3 +#define R3 0 +#define G3 1 +#define B3 2 +#endif + + if (data->color_bits <= 4) + { + grub_uint8_t palette[16][3]; + grub_uint8_t *d1c, *d2c; + int shift; + int mask = (1 << data->color_bits) - 1; + unsigned j; + if (data->is_gray) + { + /* Generic formula is + (0xff * i) / ((1U << data->color_bits) - 1) + but for allowed bit depth of 1, 2 and for it's + equivalent to + (0xff / ((1U << data->color_bits) - 1)) * i + Precompute the multipliers to avoid division. + */ + + const grub_uint8_t multipliers[5] = { 0xff, 0xff, 0x55, 0x24, 0x11 }; + for (i = 0; i < (1U << data->color_bits); i++) + { + grub_uint8_t col = multipliers[data->color_bits] * i; + palette[i][0] = col; + palette[i][1] = col; + palette[i][2] = col; + } + } + else + grub_memcpy (palette, data->palette, 3 << data->color_bits); + d1c = d1; + d2c = d2; + for (j = 0; j < data->image_height; j++, d1c += data->image_width * 3, + d2c += data->row_bytes) + { + d1 = d1c; + d2 = d2c; + shift = 8 - data->color_bits; + for (i = 0; i < data->image_width; i++, d1 += 3) + { + grub_uint8_t col = (d2[0] >> shift) & mask; + d1[R3] = data->palette[col][2]; + d1[G3] = data->palette[col][1]; + d1[B3] = data->palette[col][0]; + shift -= data->color_bits; + if (shift < 0) + { + d2++; + shift += 8; + } + } + } + return; + } + + if (data->is_palette) + { + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 3, d2++) + { + d1[R3] = data->palette[d2[0]][2]; + d1[G3] = data->palette[d2[0]][1]; + d1[B3] = data->palette[d2[0]][0]; + } + return; + } + + if (data->is_gray) + { + switch (data->bpp) + { + case 4: + /* 16-bit gray with alpha. */ + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 4, d2 += 4) + { + d1[R4] = d2[3]; + d1[G4] = d2[3]; + d1[B4] = d2[3]; + d1[A4] = d2[1]; + } + break; + case 2: + if (data->is_16bit) + /* 16-bit gray without alpha. */ + { + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 4, d2 += 2) + { + d1[R3] = d2[1]; + d1[G3] = d2[1]; + d1[B3] = d2[1]; + } + } + else + /* 8-bit gray with alpha. */ + { + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 4, d2 += 2) + { + d1[R4] = d2[1]; + d1[G4] = d2[1]; + d1[B4] = d2[1]; + d1[A4] = d2[0]; + } + } + break; + /* 8-bit gray without alpha. */ + case 1: + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 3, d2++) + { + d1[R3] = d2[0]; + d1[G3] = d2[0]; + d1[B3] = d2[0]; + } + break; + } + return; + } + + { + /* Only copy the upper 8 bit. */ +#ifndef GRUB_CPU_WORDS_BIGENDIAN + for (i = 0; i < (data->image_width * data->image_height * data->bpp >> 1); + i++, d1++, d2 += 2) + *d1 = *d2; +#else + switch (data->bpp) + { + /* 16-bit with alpha. */ + case 8: + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 4, d2+=8) + { + d1[0] = d2[7]; + d1[1] = d2[5]; + d1[2] = d2[3]; + d1[3] = d2[1]; + } + break; + /* 16-bit without alpha. */ + case 6: + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 3, d2+=6) + { + d1[0] = d2[5]; + d1[1] = d2[3]; + d1[2] = d2[1]; + } + break; + case 4: + /* 8-bit with alpha. */ + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 4, d2 += 4) + { + d1[0] = d2[3]; + d1[1] = d2[2]; + d1[2] = d2[1]; + d1[3] = d2[0]; + } + break; + /* 8-bit without alpha. */ + case 3: + for (i = 0; i < (data->image_width * data->image_height); + i++, d1 += 3, d2 += 3) + { + d1[0] = d2[2]; + d1[1] = d2[1]; + d1[2] = d2[0]; + } + break; + } +#endif + } + +} + +static grub_err_t +grub_png_decode_png (struct grub_png_data *data) +{ + grub_uint8_t magic[8]; + + if (grub_file_read (data->file, &magic[0], 8) != 8) + return grub_errno; + + if (grub_memcmp (magic, png_magic, sizeof (png_magic))) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: not a png file"); + + while (1) + { + grub_uint32_t len, type; + + len = grub_png_get_dword (data); + type = grub_png_get_dword (data); + data->next_offset = data->file->offset + len + 4; + + switch (type) + { + case PNG_CHUNK_IHDR: + grub_png_decode_image_header (data); + break; + + case PNG_CHUNK_PLTE: + grub_png_decode_image_palette (data, len); + break; + + case PNG_CHUNK_IDAT: + data->inside_idat = 1; + data->idat_remain = len; + data->bit_count = 0; + + grub_png_decode_image_data (data); + + data->inside_idat = 0; + break; + + case PNG_CHUNK_IEND: + if (data->image_data) + grub_png_convert_image (data); + + return grub_errno; + + default: + grub_file_seek (data->file, data->file->offset + len + 4); + } + + if (grub_errno) + break; + + if (data->file->offset != data->next_offset) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "png: chunk size error"); + } + + return grub_errno; +} + +static grub_err_t +grub_video_reader_png (struct grub_video_bitmap **bitmap, + const char *filename) +{ + grub_file_t file; + struct grub_png_data *data; + + file = grub_buffile_open (filename, GRUB_FILE_TYPE_PIXMAP, 0); + if (!file) + return grub_errno; + + data = grub_zalloc (sizeof (*data)); + if (data != NULL) + { + data->file = file; + data->bitmap = bitmap; + + grub_png_decode_png (data); + + grub_free (data->image_data); + grub_free (data); + } + + if (grub_errno != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (*bitmap); + *bitmap = 0; + } + + grub_file_close (file); + return grub_errno; +} + +#if defined(PNG_DEBUG) +static grub_err_t +grub_cmd_pngtest (grub_command_t cmd_d __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_video_bitmap *bitmap = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + grub_video_reader_png (&bitmap, args[0]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + grub_video_bitmap_destroy (bitmap); + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_video_bitmap_reader png_reader = { + .extension = ".png", + .reader = grub_video_reader_png, + .next = 0 +}; + +GRUB_MOD_INIT (png) +{ + grub_video_bitmap_reader_register (&png_reader); +#if defined(PNG_DEBUG) + cmd = grub_register_command ("pngtest", grub_cmd_pngtest, + "FILE", + "Tests loading of PNG bitmap."); +#endif +} + +GRUB_MOD_FINI (png) +{ +#if defined(PNG_DEBUG) + grub_unregister_command (cmd); +#endif + grub_video_bitmap_reader_unregister (&png_reader); +} diff --git a/grub-core/video/readers/tga.c b/grub-core/video/readers/tga.c new file mode 100644 index 0000000..7cb9d1d --- /dev/null +++ b/grub-core/video/readers/tga.c @@ -0,0 +1,511 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/bitmap.h> +#include <grub/types.h> +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/bufio.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Uncomment following define to enable TGA debug. */ +//#define TGA_DEBUG + +#define dump_int_field(x) grub_dprintf ("tga", #x " = %d (0x%04x)\n", x, x); +#if defined(TGA_DEBUG) +static grub_command_t cmd; +#endif + +enum +{ + GRUB_TGA_IMAGE_TYPE_NONE = 0, + GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR = 1, + GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR = 2, + GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE = 3, + GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR = 9, + GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR = 10, + GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE = 11, +}; + +enum +{ + GRUB_TGA_COLOR_MAP_TYPE_NONE = 0, + GRUB_TGA_COLOR_MAP_TYPE_INCLUDED = 1 +}; + +enum +{ + GRUB_TGA_IMAGE_ORIGIN_RIGHT = 0x10, + GRUB_TGA_IMAGE_ORIGIN_TOP = 0x20 +}; + +struct grub_tga_header +{ + grub_uint8_t id_length; + grub_uint8_t color_map_type; + grub_uint8_t image_type; + + /* Color Map Specification. */ + grub_uint16_t color_map_first_index; + grub_uint16_t color_map_length; + grub_uint8_t color_map_bpp; + + /* Image Specification. */ + grub_uint16_t image_x_origin; + grub_uint16_t image_y_origin; + grub_uint16_t image_width; + grub_uint16_t image_height; + grub_uint8_t image_bpp; + grub_uint8_t image_descriptor; +} GRUB_PACKED; + +struct tga_data +{ + struct grub_tga_header hdr; + int uses_rle; + int pktlen; + int bpp; + int is_rle; + grub_uint8_t pixel[4]; + grub_uint8_t palette[256][3]; + struct grub_video_bitmap *bitmap; + grub_file_t file; + unsigned image_width; + unsigned image_height; +}; + +static grub_err_t +fetch_pixel (struct tga_data *data) +{ + if (!data->uses_rle) + { + if (grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + return GRUB_ERR_NONE; + } + if (!data->pktlen) + { + grub_uint8_t type; + if (grub_file_read (data->file, &type, sizeof (type)) != sizeof(type)) + return grub_errno; + data->is_rle = !!(type & 0x80); + data->pktlen = (type & 0x7f) + 1; + if (data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + } + if (!data->is_rle && grub_file_read (data->file, &data->pixel[0], data->bpp) + != data->bpp) + return grub_errno; + data->pktlen--; + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_palette (struct tga_data *data) +{ + grub_size_t len = grub_le_to_cpu32 (data->hdr.color_map_length) * 3; + + if (len > sizeof (data->palette)) + len = sizeof (data->palette); + + if (grub_file_read (data->file, &data->palette, len) + != (grub_ssize_t) len) + return grub_errno; + +#ifndef GRUB_CPU_WORDS_BIGENDIAN + { + grub_size_t i; + for (i = 0; 3 * i < len; i++) + { + grub_uint8_t t; + t = data->palette[i][0]; + data->palette[i][0] = data->palette[i][2]; + data->palette[i][2] = t; + } + } +#endif + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_index_color (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + + ptr[0] = data->palette[data->pixel[0]][0]; + ptr[1] = data->palette[data->pixel[0]][1]; + ptr[2] = data->palette[data->pixel[0]][2]; + + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_grayscale (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[0]; + ptr[2] = data->pixel[0]; + + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_truecolor_R8G8B8 (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[2]; +#else + ptr[0] = data->pixel[2]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[0]; +#endif + ptr += 3; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +tga_load_truecolor_R8G8B8A8 (struct tga_data *data) +{ + unsigned int x; + unsigned int y; + grub_uint8_t *ptr; + + for (y = 0; y < data->image_height; y++) + { + ptr = data->bitmap->data; + if ((data->hdr.image_descriptor & GRUB_TGA_IMAGE_ORIGIN_TOP) != 0) + ptr += y * data->bitmap->mode_info.pitch; + else + ptr += (data->image_height - 1 - y) * data->bitmap->mode_info.pitch; + + for (x = 0; x < data->image_width; x++) + { + grub_err_t err; + err = fetch_pixel (data); + if (err) + return err; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + ptr[0] = data->pixel[0]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[2]; + ptr[3] = data->pixel[3]; +#else + ptr[0] = data->pixel[2]; + ptr[1] = data->pixel[1]; + ptr[2] = data->pixel[0]; + ptr[3] = data->pixel[3]; +#endif + + ptr += 4; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_reader_tga (struct grub_video_bitmap **bitmap, + const char *filename) +{ + grub_ssize_t pos; + struct tga_data data; + + grub_memset (&data, 0, sizeof (data)); + + data.file = grub_buffile_open (filename, GRUB_FILE_TYPE_PIXMAP, 0); + if (! data.file) + return grub_errno; + + /* TGA Specification states that we SHOULD start by reading + ID from end of file, but we really don't care about that as we are + not going to support developer area & extensions at this point. */ + + /* Read TGA header from beginning of file. */ + if (grub_file_read (data.file, &data.hdr, sizeof (data.hdr)) + != sizeof (data.hdr)) + { + grub_file_close (data.file); + return grub_errno; + } + + /* Skip ID field. */ + pos = grub_file_tell (data.file); + pos += data.hdr.id_length; + grub_file_seek (data.file, pos); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + grub_dprintf("tga", "tga: header\n"); + dump_int_field(data.hdr.id_length); + dump_int_field(data.hdr.color_map_type); + dump_int_field(data.hdr.image_type); + dump_int_field(data.hdr.color_map_first_index); + dump_int_field(data.hdr.color_map_length); + dump_int_field(data.hdr.color_map_bpp); + dump_int_field(data.hdr.image_x_origin); + dump_int_field(data.hdr.image_y_origin); + dump_int_field(data.hdr.image_width); + dump_int_field(data.hdr.image_height); + dump_int_field(data.hdr.image_bpp); + dump_int_field(data.hdr.image_descriptor); + + data.image_width = grub_le_to_cpu16 (data.hdr.image_width); + data.image_height = grub_le_to_cpu16 (data.hdr.image_height); + + /* Check that bitmap encoding is supported. */ + switch (data.hdr.image_type) + { + case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR: + data.uses_rle = 1; + break; + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR: + data.uses_rle = 0; + break; + + default: + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (unknown encoding %d)", data.hdr.image_type); + } + + data.bpp = data.hdr.image_bpp / 8; + + /* Check that bitmap depth is supported. */ + switch (data.hdr.image_type) + { + case GRUB_TGA_IMAGE_TYPE_RLE_BLACK_AND_WHITE: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_BLACK_AND_WHITE: + if (data.hdr.image_bpp != 8) + { + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_grayscale (&data); + break; + + case GRUB_TGA_IMAGE_TYPE_RLE_INDEXCOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_INDEXCOLOR: + if (data.hdr.image_bpp != 8 + || data.hdr.color_map_bpp != 24 + || data.hdr.color_map_first_index != 0) + { + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_palette (&data); + tga_load_index_color (&data); + break; + + case GRUB_TGA_IMAGE_TYPE_RLE_TRUECOLOR: + case GRUB_TGA_IMAGE_TYPE_UNCOMPRESSED_TRUECOLOR: + switch (data.hdr.image_bpp) + { + case 24: + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_truecolor_R8G8B8 (&data); + break; + + case 32: + grub_video_bitmap_create (bitmap, data.image_width, + data.image_height, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888); + if (grub_errno != GRUB_ERR_NONE) + { + grub_file_close (data.file); + return grub_errno; + } + + data.bitmap = *bitmap; + /* Load bitmap data. */ + tga_load_truecolor_R8G8B8A8 (&data); + break; + + default: + grub_file_close (data.file); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "unsupported bitmap format (bpp=%d)", + data.hdr.image_bpp); + } + } + + /* If there was a loading problem, destroy bitmap. */ + if (grub_errno != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (*bitmap); + *bitmap = 0; + } + + grub_file_close (data.file); + return grub_errno; +} + +#if defined(TGA_DEBUG) +static grub_err_t +grub_cmd_tgatest (grub_command_t cmd_d __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_video_bitmap *bitmap = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + + grub_video_reader_tga (&bitmap, args[0]); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + grub_video_bitmap_destroy (bitmap); + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_video_bitmap_reader tga_reader = { + .extension = ".tga", + .reader = grub_video_reader_tga, + .next = 0 +}; + +GRUB_MOD_INIT(tga) +{ + grub_video_bitmap_reader_register (&tga_reader); +#if defined(TGA_DEBUG) + cmd = grub_register_command ("tgatest", grub_cmd_tgatest, + "FILE", "Tests loading of TGA bitmap."); +#endif +} + +GRUB_MOD_FINI(tga) +{ +#if defined(TGA_DEBUG) + grub_unregister_command (cmd); +#endif + grub_video_bitmap_reader_unregister (&tga_reader); +} diff --git a/grub-core/video/sis315_init.c b/grub-core/video/sis315_init.c new file mode 100644 index 0000000..ae5c141 --- /dev/null +++ b/grub-core/video/sis315_init.c @@ -0,0 +1,158 @@ +static const struct { grub_uint8_t reg; grub_uint8_t val; } sr_dump [] = +{ + { 0x28, 0x81 }, + { 0x2a, 0x00 }, + { 0x29, 0xe1 }, + { 0x2b, 0x81 }, + { 0x2d, 0x00 }, + { 0x2c, 0xe1 }, + { 0x2e, 0x81 }, + { 0x30, 0x00 }, + { 0x2f, 0xe1 }, + { 0x28, 0x01 }, + { 0x29, 0x22 }, + { 0x28, 0x3b }, + { 0x29, 0x22 }, + { 0x2a, 0x01 }, + { 0x2e, 0x01 }, + { 0x2f, 0x22 }, + { 0x2e, 0x3b }, + { 0x2f, 0x22 }, + { 0x30, 0x01 }, + { 0x15, 0x00 }, + { 0x1b, 0x30 }, + + { 0x16, 0x0f }, + { 0x16, 0x8f }, + { 0x17, 0xba }, + { 0x16, 0x0f }, + { 0x16, 0x8f }, + { 0x1f, 0x00 }, + { 0x20, 0x20 }, + { 0x23, 0xf6 }, + { 0x24, 0x0d }, + { 0x25, 0x33 }, + { 0x21, 0x84 }, + { 0x22, 0x00 }, + { 0x27, 0x1f }, + { 0x31, 0x00 }, + { 0x33, 0x00 }, + { 0x32, 0x11 }, + + { 0x25, 0x33 }, + + { 0x05, 0x86 }, + { 0x01, 0x20 }, + { 0x32, 0x11 }, + { 0x1e, 0x00 }, + { 0x1d, 0x00 }, + { 0x00, 0x03 }, + { 0x01, 0x21 }, + { 0x02, 0x0f }, + { 0x03, 0x00 }, + { 0x04, 0x0e }, + + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, + { 0x0c, 0x00 }, + { 0x0d, 0x00 }, + { 0x0e, 0x00 }, + { 0x37, 0x00 }, + + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, + { 0x0c, 0x05 }, + { 0x0e, 0x00 }, + + { 0x0e, 0x00 }, + + { 0x10, 0x0b }, + { 0x31, 0x00 }, + { 0x2b, 0x01 }, + { 0x2c, 0xe1 }, + { 0x2b, 0x1b }, + { 0x2c, 0xe1 }, + { 0x2d, 0x01 }, + { 0x3d, 0x00 }, + { 0x08, 0x84 }, + { 0x09, 0x00 }, + { 0x3d, 0x01 }, + { 0x1f, 0x00 }, + { 0x06, 0x02 }, + + { 0x0f, 0x00 }, + { 0x17, 0xba }, + { 0x21, 0xa4 }, + { 0x32, 0x11 }, + { 0x07, 0x18 }, + + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + + { 0x01, 0x01 }, + { 0x21, 0x84 }, + { 0x01, 0x21 }, + { 0x16, 0x8f }, + { 0x18, 0xa9 }, + { 0x19, 0xa0 }, + { 0x1b, 0x30 }, + { 0x17, 0xb8 }, + { 0x19, 0xa3 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x14, 0x02 }, + { 0x15, 0x00 }, + { 0x16, 0x0f }, + { 0x16, 0x8f }, + { 0x1d, 0x00 }, + { 0x14, 0x00 }, + { 0x14, 0x01 }, + { 0x15, 0x00 }, + { 0x16, 0x0f }, + { 0x16, 0x8f }, + { 0x1d, 0x00 }, + { 0x14, 0x01 }, + { 0x14, 0x01 }, + { 0x15, 0x10 }, + { 0x13, 0x35 }, + { 0x14, 0x51 }, + { 0x16, 0x0f }, + { 0x16, 0x8f }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x11 }, + { 0x1d, 0x11 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x1d, 0x00 }, + { 0x17, 0xba }, + { 0x19, 0xa0 }, + { 0x19, 0xa0 }, + { 0x01, 0x01 }, + { 0x16, 0x0f }, + { 0x18, 0xa9 }, + { 0x19, 0xa0 }, + { 0x1b, 0x30 }, + { 0x21, 0xa4 }, + { 0x05, 0x86 }, +}; + +static const grub_uint8_t gr[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/grub-core/video/sis315pro.c b/grub-core/video/sis315pro.c new file mode 100644 index 0000000..22a0c85 --- /dev/null +++ b/grub-core/video/sis315pro.c @@ -0,0 +1,459 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> +#include <grub/cache.h> + +#define GRUB_SIS315PRO_PCIID 0x03251039 +#define GRUB_SIS315PRO_TOTAL_MEMORY_SPACE 0x800000 +#define GRUB_SIS315PRO_MMIO_SPACE 0x1000 + +static struct +{ + struct grub_video_mode_info mode_info; + + grub_uint8_t *ptr; + volatile grub_uint8_t *direct_ptr; + int mapped; + grub_uint32_t base; + grub_uint32_t mmiobase; + volatile grub_uint32_t *mmioptr; + grub_pci_device_t dev; + grub_port_t io; +} framebuffer; + +static grub_uint8_t +read_sis_cmd (grub_uint8_t addr) +{ + grub_outb (addr, framebuffer.io + 0x44); + return grub_inb (framebuffer.io + 0x45); +} + +static void +write_sis_cmd (grub_uint8_t val, grub_uint8_t addr) +{ + grub_outb (addr, framebuffer.io + 0x44); + grub_outb (val, framebuffer.io + 0x45); +} + +#ifndef TEST +static grub_err_t +grub_video_sis315pro_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_sis315pro_video_fini (void) +{ + if (framebuffer.mapped) + { + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + GRUB_SIS315PRO_TOTAL_MEMORY_SPACE); + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.direct_ptr, + GRUB_SIS315PRO_TOTAL_MEMORY_SPACE); + } + + return grub_video_fb_fini (); +} +#endif + +#include "sis315_init.c" + +#ifndef TEST +/* Helper for grub_video_sis315pro_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != GRUB_PCI_CLASS_SUBCLASS_VGA + || pciid != GRUB_SIS315PRO_PCIID) + return 0; + + *found = 1; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1); + framebuffer.mmiobase = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG2); + framebuffer.io = (grub_pci_read (addr) & GRUB_PCI_ADDR_IO_MASK) + + GRUB_MACHINE_PCI_IO_BASE; + framebuffer.dev = dev; + + return 1; +} +#endif + +static grub_err_t +grub_video_sis315pro_setup (unsigned int width, unsigned int height, + unsigned int mode_type, + unsigned int mode_mask __attribute__ ((unused))) +{ + int depth; + grub_err_t err; + int found = 0; + unsigned i; + +#ifndef TEST + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if ((width != 640 && width != 0) || (height != 480 && height != 0) + || (depth != 8 && depth != 0)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 640x480x8 is supported"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); +#endif + /* Fill mode info details. */ + framebuffer.mode_info.width = 640; + framebuffer.mode_info.height = 480; + framebuffer.mode_info.mode_type = (GRUB_VIDEO_MODE_TYPE_INDEX_COLOR + | GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + framebuffer.mode_info.bpp = 8; + framebuffer.mode_info.bytes_per_pixel = 1; + framebuffer.mode_info.pitch = 640 * 1; + framebuffer.mode_info.number_of_colors = 16; + framebuffer.mode_info.red_mask_size = 0; + framebuffer.mode_info.red_field_pos = 0; + framebuffer.mode_info.green_mask_size = 0; + framebuffer.mode_info.green_field_pos = 0; + framebuffer.mode_info.blue_mask_size = 0; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; +#ifndef TEST + framebuffer.mode_info.blit_format + = grub_video_get_blit_format (&framebuffer.mode_info); +#endif + +#ifndef TEST + if (found && (framebuffer.base == 0 || framebuffer.mmiobase == 0)) + { + grub_pci_address_t addr; + /* FIXME: choose address dynamically if needed. */ + framebuffer.base = 0x40000000; + framebuffer.mmiobase = 0x04000000; + framebuffer.io = 0xb300; + + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_ADDRESS_REG0); + grub_pci_write (addr, framebuffer.base | GRUB_PCI_ADDR_MEM_PREFETCH); + + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_ADDRESS_REG1); + grub_pci_write (addr, framebuffer.mmiobase); + + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_ADDRESS_REG2); + grub_pci_write (addr, framebuffer.io | GRUB_PCI_ADDR_SPACE_IO); + + /* Set latency. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_CACHELINE); + grub_pci_write (addr, 0x80004700); + + /* Enable address spaces. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, 0x7); + + addr = grub_pci_make_address (framebuffer.dev, 0x30); + grub_pci_write (addr, 0x04060001); + + framebuffer.io += GRUB_MACHINE_PCI_IO_BASE; + } +#endif + + + /* We can safely discard volatile attribute. */ +#ifndef TEST + framebuffer.ptr + = grub_pci_device_map_range_cached (framebuffer.dev, + framebuffer.base, + GRUB_SIS315PRO_TOTAL_MEMORY_SPACE); + framebuffer.direct_ptr + = grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + GRUB_SIS315PRO_TOTAL_MEMORY_SPACE); + framebuffer.mmioptr = grub_pci_device_map_range (framebuffer.dev, + framebuffer.mmiobase, + GRUB_SIS315PRO_MMIO_SPACE); +#endif + framebuffer.mapped = 1; + +#ifndef TEST + /* Prevent garbage from appearing on the screen. */ + grub_memset (framebuffer.ptr, 0, + framebuffer.mode_info.height * framebuffer.mode_info.pitch); + grub_arch_sync_dma_caches (framebuffer.ptr, + framebuffer.mode_info.height + * framebuffer.mode_info.pitch); +#endif + + grub_outb (GRUB_VGA_IO_MISC_NEGATIVE_VERT_POLARITY + | GRUB_VGA_IO_MISC_NEGATIVE_HORIZ_POLARITY + | GRUB_VGA_IO_MISC_UPPER_64K + | GRUB_VGA_IO_MISC_EXTERNAL_CLOCK_0 + | GRUB_VGA_IO_MISC_28MHZ + | GRUB_VGA_IO_MISC_ENABLE_VRAM_ACCESS + | GRUB_VGA_IO_MISC_COLOR, + GRUB_VGA_IO_MISC_WRITE + GRUB_MACHINE_PCI_IO_BASE); + + grub_vga_sr_write (0x86, 5); + for (i = 6; i <= 0x27; i++) + grub_vga_sr_write (0, i); + + for (i = 0x31; i <= 0x3d; i++) + grub_vga_sr_write (0, i); + + for (i = 0; i < ARRAY_SIZE (sr_dump); i++) + grub_vga_sr_write (sr_dump[i].val, sr_dump[i].reg); + + for (i = 0x30; i < 0x40; i++) + grub_vga_cr_write (0, i); + + grub_vga_cr_write (0x77, 0x40); + grub_vga_cr_write (0x77, 0x41); + grub_vga_cr_write (0x00, 0x42); + grub_vga_cr_write (0x5b, 0x43); + grub_vga_cr_write (0x00, 0x44); + grub_vga_cr_write (0x23, 0x48); + grub_vga_cr_write (0xaa, 0x49); + grub_vga_cr_write (0x02, 0x37); + grub_vga_cr_write (0x20, 0x5b); + grub_vga_cr_write (0x00, 0x83); + grub_vga_cr_write (0x80, 0x63); + + grub_vga_cr_write (0x0c, GRUB_VGA_CR_VSYNC_END); + grub_vga_cr_write (0x5f, GRUB_VGA_CR_HTOTAL); + grub_vga_cr_write (0x4f, GRUB_VGA_CR_HORIZ_END); + grub_vga_cr_write (0x50, GRUB_VGA_CR_HBLANK_START); + grub_vga_cr_write (0x82, GRUB_VGA_CR_HBLANK_END); + grub_vga_cr_write (0x54, GRUB_VGA_CR_HORIZ_SYNC_PULSE_START); + grub_vga_cr_write (0x80, GRUB_VGA_CR_HORIZ_SYNC_PULSE_END); + grub_vga_cr_write (0x0b, GRUB_VGA_CR_VERT_TOTAL); + grub_vga_cr_write (0x3e, GRUB_VGA_CR_OVERFLOW); + grub_vga_cr_write (0x00, GRUB_VGA_CR_BYTE_PANNING); + grub_vga_cr_write (0x40, GRUB_VGA_CR_CELL_HEIGHT); + grub_vga_cr_write (0x00, GRUB_VGA_CR_CURSOR_START); + grub_vga_cr_write (0x00, GRUB_VGA_CR_CURSOR_END); + grub_vga_cr_write (0x00, GRUB_VGA_CR_START_ADDR_HIGH_REGISTER); + grub_vga_cr_write (0x00, GRUB_VGA_CR_START_ADDR_LOW_REGISTER); + grub_vga_cr_write (0x00, GRUB_VGA_CR_CURSOR_ADDR_HIGH); + grub_vga_cr_write (0x00, GRUB_VGA_CR_CURSOR_ADDR_LOW); + grub_vga_cr_write (0xea, GRUB_VGA_CR_VSYNC_START); + grub_vga_cr_write (0x8c, GRUB_VGA_CR_VSYNC_END); + grub_vga_cr_write (0xdf, GRUB_VGA_CR_VDISPLAY_END); + grub_vga_cr_write (0x28, GRUB_VGA_CR_PITCH); + grub_vga_cr_write (0x40, GRUB_VGA_CR_UNDERLINE_LOCATION); + grub_vga_cr_write (0xe7, GRUB_VGA_CR_VERTICAL_BLANK_START); + grub_vga_cr_write (0x04, GRUB_VGA_CR_VERTICAL_BLANK_END); + grub_vga_cr_write (0xa3, GRUB_VGA_CR_MODE); + grub_vga_cr_write (0xff, GRUB_VGA_CR_LINE_COMPARE); + + grub_vga_cr_write (0x0c, GRUB_VGA_CR_VSYNC_END); + grub_vga_cr_write (0x5f, GRUB_VGA_CR_HTOTAL); + grub_vga_cr_write (0x4f, GRUB_VGA_CR_HORIZ_END); + grub_vga_cr_write (0x50, GRUB_VGA_CR_HBLANK_START); + grub_vga_cr_write (0x82, GRUB_VGA_CR_HBLANK_END); + grub_vga_cr_write (0x55, GRUB_VGA_CR_HORIZ_SYNC_PULSE_START); + grub_vga_cr_write (0x81, GRUB_VGA_CR_HORIZ_SYNC_PULSE_END); + grub_vga_cr_write (0x0b, GRUB_VGA_CR_VERT_TOTAL); + grub_vga_cr_write (0x3e, GRUB_VGA_CR_OVERFLOW); + grub_vga_cr_write (0xe9, GRUB_VGA_CR_VSYNC_START); + grub_vga_cr_write (0x8b, GRUB_VGA_CR_VSYNC_END); + grub_vga_cr_write (0xdf, GRUB_VGA_CR_VDISPLAY_END); + grub_vga_cr_write (0xe7, GRUB_VGA_CR_VERTICAL_BLANK_START); + grub_vga_cr_write (0x04, GRUB_VGA_CR_VERTICAL_BLANK_END); + grub_vga_cr_write (0x40, GRUB_VGA_CR_CELL_HEIGHT); + grub_vga_cr_write (0x50, GRUB_VGA_CR_PITCH); + + grub_vga_cr_write (0x00, 0x19); + grub_vga_cr_write (0x00, 0x1a); + grub_vga_cr_write (0x6c, 0x52); + grub_vga_cr_write (0x2e, 0x34); + grub_vga_cr_write (0x00, 0x31); + + + grub_vga_cr_write (0, GRUB_VGA_CR_START_ADDR_HIGH_REGISTER); + grub_vga_cr_write (0, GRUB_VGA_CR_START_ADDR_LOW_REGISTER); + + for (i = 0; i < 16; i++) + grub_vga_write_arx (i, i); + grub_vga_write_arx (1, GRUB_VGA_ARX_MODE); + grub_vga_write_arx (0, GRUB_VGA_ARX_OVERSCAN); + grub_vga_write_arx (0, GRUB_VGA_ARX_COLOR_PLANE_ENABLE); + grub_vga_write_arx (0, GRUB_VGA_ARX_HORIZONTAL_PANNING); + grub_vga_write_arx (0, GRUB_VGA_ARX_COLOR_SELECT); + + grub_outb (0xff, GRUB_VGA_IO_PIXEL_MASK + GRUB_MACHINE_PCI_IO_BASE); + + for (i = 0; i < ARRAY_SIZE (gr); i++) + grub_vga_gr_write (gr[i], i); + + for (i = 0; i < GRUB_VIDEO_FBSTD_NUMCOLORS; i++) + grub_vga_palette_write (i, grub_video_fbstd_colors[i].r, + grub_video_fbstd_colors[i].g, + grub_video_fbstd_colors[i].b); + +#if 1 + { + if (read_sis_cmd (0x5) != 0xa1) + write_sis_cmd (0x86, 0x5); + + write_sis_cmd (read_sis_cmd (0x20) | 0xa1, 0x20); + write_sis_cmd (read_sis_cmd (0x1e) | 0xda, 0x1e); + +#define IND_SIS_CMDQUEUE_SET 0x26 +#define IND_SIS_CMDQUEUE_THRESHOLD 0x27 + +#define COMMAND_QUEUE_THRESHOLD 0x1F +#define SIS_CMD_QUEUE_RESET 0x01 + +#define SIS_AGP_CMDQUEUE_ENABLE 0x80 /* 315/330/340 series SR26 */ +#define SIS_VRAM_CMDQUEUE_ENABLE 0x40 +#define SIS_MMIO_CMD_ENABLE 0x20 +#define SIS_CMD_QUEUE_SIZE_512k 0x00 +#define SIS_CMD_QUEUE_SIZE_1M 0x04 +#define SIS_CMD_QUEUE_SIZE_2M 0x08 +#define SIS_CMD_QUEUE_SIZE_4M 0x0C +#define SIS_CMD_QUEUE_RESET 0x01 +#define SIS_CMD_AUTO_CORR 0x02 + + + write_sis_cmd (COMMAND_QUEUE_THRESHOLD, IND_SIS_CMDQUEUE_THRESHOLD); + write_sis_cmd (SIS_CMD_QUEUE_RESET, IND_SIS_CMDQUEUE_SET); + framebuffer.mmioptr[0x85C4 / 4] = framebuffer.mmioptr[0x85C8 / 4]; + write_sis_cmd (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR, IND_SIS_CMDQUEUE_SET); + framebuffer.mmioptr[0x85C0 / 4] = (0x1000000 - (512 * 1024)); + } +#endif + +#ifndef TEST + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.ptr, NULL, NULL); + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_EXT_NUMCOLORS, + grub_video_fbstd_colors); +#endif + return err; +} + +#ifndef TEST + +static grub_err_t +grub_video_sis315pro_swap_buffers (void) +{ + grub_size_t s; + s = (framebuffer.mode_info.height + * framebuffer.mode_info.pitch + * framebuffer.mode_info.bytes_per_pixel); + grub_video_fb_swap_buffers (); + grub_arch_sync_dma_caches (framebuffer.ptr, s); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_sis315pro_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (void *) framebuffer.direct_ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_sis315pro_adapter = + { + .name = "SIS315PRO Video Driver", + .id = GRUB_VIDEO_DRIVER_SIS315PRO, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_sis315pro_video_init, + .fini = grub_video_sis315pro_video_fini, + .setup = grub_video_sis315pro_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_sis315pro_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_sis315pro_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_sis315pro) +{ + grub_video_register (&grub_video_sis315pro_adapter); +} + +GRUB_MOD_FINI(video_sis315pro) +{ + grub_video_unregister (&grub_video_sis315pro_adapter); +} +#else +int +main () +{ + grub_video_sis315pro_setup (640, 400, 0, 0); +} +#endif diff --git a/grub-core/video/sm712.c b/grub-core/video/sm712.c new file mode 100644 index 0000000..10c46eb --- /dev/null +++ b/grub-core/video/sm712.c @@ -0,0 +1,818 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#define grub_video_render_target grub_video_fbrender_target + +#if !defined (TEST) && !defined(GENINIT) +#include <grub/err.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/video.h> +#include <grub/video_fb.h> +#include <grub/pci.h> +#include <grub/vga.h> +#include <grub/cache.h> +#else +typedef unsigned char grub_uint8_t; +typedef unsigned short grub_uint16_t; +typedef unsigned int grub_uint32_t; +typedef int grub_err_t; +#include <grub/vgaregs.h> +#include <stdio.h> +#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0])) +#endif + +#include "sm712_init.c" + +#pragma GCC diagnostic ignored "-Wcast-align" + +#define GRUB_SM712_TOTAL_MEMORY_SPACE 0x700400 +#define GRUB_SM712_REG_BASE 0x700000 +#define GRUB_SM712_PCIID 0x0712126f + +enum + { + GRUB_SM712_SR_TV_CONTROL = 0x65, + GRUB_SM712_SR_RAM_LUT = 0x66, + GRUB_SM712_SR_CLOCK_CONTROL1 = 0x68, + GRUB_SM712_SR_CLOCK_CONTROL2 = 0x69, + GRUB_SM712_SR_VCLK_NUM = 0x6c, + GRUB_SM712_SR_VCLK_DENOM = 0x6d, + GRUB_SM712_SR_VCLK2_NUM = 0x6e, + GRUB_SM712_SR_VCLK2_DENOM = 0x6f, + GRUB_SM712_SR_POPUP_ICON_LOW = 0x80, + GRUB_SM712_SR_POPUP_ICON_HIGH = 0x81, + GRUB_SM712_SR_POPUP_ICON_CTRL = 0x82, + GRUB_SM712_SR_POPUP_ICON_COLOR1 = 0x84, + GRUB_SM712_SR_POPUP_ICON_COLOR2 = 0x85, + GRUB_SM712_SR_POPUP_ICON_COLOR3 = 0x86, + + GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_X_LOW = 0x88, + GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_X_HIGH = 0x89, + GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_Y_LOW = 0x8a, + GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_Y_HIGH = 0x8b, + GRUB_SM712_SR_HW_CURSOR_FG_COLOR = 0x8c, + GRUB_SM712_SR_HW_CURSOR_BG_COLOR = 0x8d, + + GRUB_SM712_SR_POPUP_ICON_X_LOW = 0x90, + GRUB_SM712_SR_POPUP_ICON_X_HIGH = 0x91, + GRUB_SM712_SR_POPUP_ICON_Y_LOW = 0x92, + GRUB_SM712_SR_POPUP_ICON_Y_HIGH = 0x93, + GRUB_SM712_SR_PANEL_HW_VIDEO_CONTROL = 0xa0, + GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_LOW = 0xa1, + GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_HIGH = 0xa2, + GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_MASK_LOW = 0xa3, + GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_MASK_HIGH = 0xa4, + GRUB_SM712_SR_PANEL_HW_VIDEO_RED_CONSTANT = 0xa5, + GRUB_SM712_SR_PANEL_HW_VIDEO_GREEN_CONSTANT = 0xa6, + GRUB_SM712_SR_PANEL_HW_VIDEO_BLUE_CONSTANT = 0xa7, + GRUB_SM712_SR_PANEL_HW_VIDEO_TOP_BOUNDARY = 0xa8, + GRUB_SM712_SR_PANEL_HW_VIDEO_LEFT_BOUNDARY = 0xa9, + GRUB_SM712_SR_PANEL_HW_VIDEO_BOTTOM_BOUNDARY = 0xaa, + GRUB_SM712_SR_PANEL_HW_VIDEO_RIGHT_BOUNDARY = 0xab, + GRUB_SM712_SR_PANEL_HW_VIDEO_TOP_LEFT_OVERFLOW_BOUNDARY = 0xac, + GRUB_SM712_SR_PANEL_HW_VIDEO_BOTTOM_RIGHT_OVERFLOW_BOUNDARY = 0xad, + GRUB_SM712_SR_PANEL_HW_VIDEO_VERTICAL_STRETCH_FACTOR = 0xae, + GRUB_SM712_SR_PANEL_HW_VIDEO_HORIZONTAL_STRETCH_FACTOR = 0xaf, + }; +enum + { + GRUB_SM712_SR_TV_CRT_SRAM = 0x00, + GRUB_SM712_SR_TV_LCD_SRAM = 0x08 + }; +enum + { + GRUB_SM712_SR_TV_ALT_CLOCK = 0x00, + GRUB_SM712_SR_TV_FREE_RUN_CLOCK = 0x04 + }; +enum + { + GRUB_SM712_SR_TV_CLOCK_CKIN_NTSC = 0x00, + GRUB_SM712_SR_TV_CLOCK_REFCLK_PAL = 0x04 + }; +enum + { + GRUB_SM712_SR_TV_HSYNC = 0x00, + GRUB_SM712_SR_TV_COMPOSITE_HSYNC = 0x01 + }; +enum + { + GRUB_SM712_SR_RAM_LUT_NORMAL = 0, + GRUB_SM712_SR_RAM_LUT_LCD_RAM_OFF = 0x80, + GRUB_SM712_SR_RAM_LUT_CRT_RAM_OFF = 0x40, + GRUB_SM712_SR_RAM_LUT_LCD_RAM_NO_WRITE = 0x20, + GRUB_SM712_SR_RAM_LUT_CRT_RAM_NO_WRITE = 0x10, + GRUB_SM712_SR_RAM_LUT_CRT_8BIT = 0x08, + GRUB_SM712_SR_RAM_LUT_CRT_GAMMA = 0x04 + }; + +enum + { + GRUB_SM712_SR_CLOCK_CONTROL1_VCLK_FROM_CCR = 0x40, + GRUB_SM712_SR_CLOCK_CONTROL1_8DOT_CLOCK = 0x10, + }; + +enum + { + GRUB_SM712_SR_CLOCK_CONTROL2_PROGRAM_VCLOCK = 0x03 + }; + +#define GRUB_SM712_SR_POPUP_ICON_HIGH_MASK 0x7 +#define GRUB_SM712_SR_POPUP_ICON_HIGH_HW_CURSOR_EN 0x80 + enum + { + GRUB_SM712_SR_POPUP_ICON_CTRL_DISABLED = 0, + GRUB_SM712_SR_POPUP_ICON_CTRL_ZOOM_ENABLED = 0x40, + GRUB_SM712_SR_POPUP_ICON_CTRL_ENABLED = 0x80 + }; +#define RGB332_BLACK 0 +#define RGB332_WHITE 0xff + + enum + { + GRUB_SM712_CR_OVERFLOW_INTERLACE = 0x30, + GRUB_SM712_CR_INTERLACE_RETRACE = 0x31, + GRUB_SM712_CR_TV_VDISPLAY_START = 0x32, + GRUB_SM712_CR_TV_VDISPLAY_END_HIGH = 0x33, + GRUB_SM712_CR_TV_VDISPLAY_END_LOW = 0x34, + GRUB_SM712_CR_DDA_CONTROL_LOW = 0x35, + GRUB_SM712_CR_DDA_CONTROL_HIGH = 0x36, + GRUB_SM712_CR_TV_EQUALIZER = 0x38, + GRUB_SM712_CR_TV_SERRATION = 0x39, + GRUB_SM712_CR_HSYNC_CTRL = 0x3a, + GRUB_SM712_CR_DEBUG = 0x3c, + GRUB_SM712_CR_SHADOW_VGA_HTOTAL = 0x40, + GRUB_SM712_CR_SHADOW_VGA_HBLANK_START = 0x41, + GRUB_SM712_CR_SHADOW_VGA_HBLANK_END = 0x42, + GRUB_SM712_CR_SHADOW_VGA_HRETRACE_START = 0x43, + GRUB_SM712_CR_SHADOW_VGA_HRETRACE_END = 0x44, + GRUB_SM712_CR_SHADOW_VGA_VERTICAL_TOTAL = 0x45, + GRUB_SM712_CR_SHADOW_VGA_VBLANK_START = 0x46, + GRUB_SM712_CR_SHADOW_VGA_VBLANK_END = 0x47, + GRUB_SM712_CR_SHADOW_VGA_VRETRACE_START = 0x48, + GRUB_SM712_CR_SHADOW_VGA_VRETRACE_END = 0x49, + GRUB_SM712_CR_SHADOW_VGA_OVERFLOW = 0x4a, + GRUB_SM712_CR_SHADOW_VGA_CELL_HEIGHT = 0x4b, + GRUB_SM712_CR_SHADOW_VGA_HDISPLAY_END = 0x4c, + GRUB_SM712_CR_SHADOW_VGA_VDISPLAY_END = 0x4d, + GRUB_SM712_CR_DDA_LOOKUP_REG3_START = 0x90, + GRUB_SM712_CR_DDA_LOOKUP_REG2_START = 0x91, + GRUB_SM712_CR_DDA_LOOKUP_REG1_START = 0xa0, + GRUB_SM712_CR_VCENTERING_OFFSET = 0xa6, + GRUB_SM712_CR_HCENTERING_OFFSET = 0xa7, + }; + +#define GRUB_SM712_CR_DEBUG_NONE 0 + +#define SM712_DDA_REG3_COMPARE_SHIFT 2 +#define SM712_DDA_REG3_COMPARE_MASK 0xfc +#define SM712_DDA_REG3_DDA_SHIFT 8 +#define SM712_DDA_REG3_DDA_MASK 0x3 +#define SM712_DDA_REG2_DDA_MASK 0xff +#define SM712_DDA_REG2_VCENTER_MASK 0x3f + +static struct +{ + grub_uint8_t compare; + grub_uint16_t dda; + grub_uint8_t vcentering; +} dda_lookups[] = { + { 21, 469, 2}, + { 23, 477, 2}, + { 33, 535, 2}, + { 35, 682, 21}, + { 34, 675, 2}, + { 55, 683, 6}, +}; + +static struct +{ +#if !defined (TEST) && !defined(GENINIT) + struct grub_video_mode_info mode_info; +#endif + + volatile grub_uint8_t *ptr; + grub_uint8_t *cached_ptr; + int mapped; + grub_uint32_t base; +#if !defined (TEST) && !defined(GENINIT) + grub_pci_device_t dev; +#endif +} framebuffer; + +#if !defined (TEST) && !defined(GENINIT) +static grub_err_t +grub_video_sm712_video_init (void) +{ + /* Reset frame buffer. */ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_sm712_video_fini (void) +{ + if (framebuffer.mapped) + { + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr, + GRUB_SM712_TOTAL_MEMORY_SPACE); + grub_pci_device_unmap_range (framebuffer.dev, framebuffer.cached_ptr, + GRUB_SM712_TOTAL_MEMORY_SPACE); + } + return grub_video_fb_fini (); +} +#endif + +static inline void +grub_sm712_write_reg (grub_uint8_t val, grub_uint16_t addr) +{ +#ifdef TEST + printf (" {1, 0x%x, 0x%x},\n", addr, val); +#elif defined (GENINIT) + printf (" .byte 0x%02x, 0x%02x\n", (addr - 0x3c0), val); + if ((addr - 0x3c0) & ~0x7f) + printf ("FAIL\n"); +#else + *(volatile grub_uint8_t *) (framebuffer.ptr + GRUB_SM712_REG_BASE + + addr) = val; +#endif +} + +static inline grub_uint8_t +grub_sm712_read_reg (grub_uint16_t addr) +{ +#ifdef TEST + printf (" {-1, 0x%x, 0x5},\n", addr); +#elif defined (GENINIT) + if ((addr - 0x3c0) & ~0x7f) + printf ("FAIL\n"); + printf (" .byte 0x%04x, 0x00\n", (addr - 0x3c0) | 0x80); +#else + return *(volatile grub_uint8_t *) (framebuffer.ptr + GRUB_SM712_REG_BASE + + addr); +#endif +} + +static inline grub_uint8_t +grub_sm712_sr_read (grub_uint8_t addr) +{ + grub_sm712_write_reg (addr, GRUB_VGA_IO_SR_INDEX); + return grub_sm712_read_reg (GRUB_VGA_IO_SR_DATA); +} + +static inline void +grub_sm712_sr_write (grub_uint8_t val, grub_uint8_t addr) +{ + grub_sm712_write_reg (addr, GRUB_VGA_IO_SR_INDEX); + grub_sm712_write_reg (val, GRUB_VGA_IO_SR_DATA); +} + +static inline void +grub_sm712_gr_write (grub_uint8_t val, grub_uint8_t addr) +{ + grub_sm712_write_reg (addr, GRUB_VGA_IO_GR_INDEX); + grub_sm712_write_reg (val, GRUB_VGA_IO_GR_DATA); +} + +static inline void +grub_sm712_cr_write (grub_uint8_t val, grub_uint8_t addr) +{ + grub_sm712_write_reg (addr, GRUB_VGA_IO_CR_INDEX); + grub_sm712_write_reg (val, GRUB_VGA_IO_CR_DATA); +} + +static inline void +grub_sm712_write_arx (grub_uint8_t val, grub_uint8_t addr) +{ + grub_sm712_read_reg (GRUB_VGA_IO_INPUT_STATUS1_REGISTER); + grub_sm712_write_reg (addr, GRUB_VGA_IO_ARX); + grub_sm712_read_reg (GRUB_VGA_IO_ARX_READ); + grub_sm712_write_reg (val, GRUB_VGA_IO_ARX); +} + +static inline void +grub_sm712_cr_shadow_write (grub_uint8_t val, grub_uint8_t addr) +{ + grub_uint8_t mapping[] = + { + [GRUB_VGA_CR_HTOTAL] = GRUB_SM712_CR_SHADOW_VGA_HTOTAL, + [GRUB_VGA_CR_HORIZ_END] = 0xff, + [GRUB_VGA_CR_HBLANK_START] = GRUB_SM712_CR_SHADOW_VGA_HBLANK_START, + [GRUB_VGA_CR_HBLANK_END] = GRUB_SM712_CR_SHADOW_VGA_HBLANK_END, + [GRUB_VGA_CR_HORIZ_SYNC_PULSE_START] = GRUB_SM712_CR_SHADOW_VGA_HRETRACE_START, + [GRUB_VGA_CR_HORIZ_SYNC_PULSE_END] = GRUB_SM712_CR_SHADOW_VGA_HRETRACE_END, + [GRUB_VGA_CR_VERT_TOTAL] = GRUB_SM712_CR_SHADOW_VGA_VERTICAL_TOTAL, + [GRUB_VGA_CR_OVERFLOW] = GRUB_SM712_CR_SHADOW_VGA_OVERFLOW, + [GRUB_VGA_CR_BYTE_PANNING] = 0xff, + [GRUB_VGA_CR_CELL_HEIGHT] = GRUB_SM712_CR_SHADOW_VGA_CELL_HEIGHT, + [GRUB_VGA_CR_CURSOR_START] = 0xff, + [GRUB_VGA_CR_CURSOR_END] = 0xff, + [GRUB_VGA_CR_START_ADDR_HIGH_REGISTER] = 0xff, + [GRUB_VGA_CR_START_ADDR_LOW_REGISTER] = 0xff, + [GRUB_VGA_CR_CURSOR_ADDR_HIGH] = 0xff, + [GRUB_VGA_CR_CURSOR_ADDR_LOW] = 0xff, + [GRUB_VGA_CR_VSYNC_START] = GRUB_SM712_CR_SHADOW_VGA_VRETRACE_START, + [GRUB_VGA_CR_VSYNC_END] = GRUB_SM712_CR_SHADOW_VGA_VRETRACE_END, + [GRUB_VGA_CR_VDISPLAY_END] = GRUB_SM712_CR_SHADOW_VGA_VDISPLAY_END, + [GRUB_VGA_CR_PITCH] = GRUB_SM712_CR_SHADOW_VGA_HDISPLAY_END, + [GRUB_VGA_CR_UNDERLINE_LOCATION] = 0xff, + + [GRUB_VGA_CR_VERTICAL_BLANK_START] = GRUB_SM712_CR_SHADOW_VGA_VBLANK_START, + [GRUB_VGA_CR_VERTICAL_BLANK_END] = GRUB_SM712_CR_SHADOW_VGA_VBLANK_END, + [GRUB_VGA_CR_MODE] = 0xff, + [GRUB_VGA_CR_LINE_COMPARE] = 0xff + }; + if (addr >= ARRAY_SIZE (mapping) || mapping[addr] == 0xff) + return; + grub_sm712_cr_write (val, mapping[addr]); +} + +static inline void +grub_sm712_write_dda_lookup (int idx, grub_uint8_t compare, grub_uint16_t dda, + grub_uint8_t vcentering) +{ + grub_sm712_cr_write (((compare << SM712_DDA_REG3_COMPARE_SHIFT) + & SM712_DDA_REG3_COMPARE_MASK) + | ((dda >> SM712_DDA_REG3_DDA_SHIFT) + & SM712_DDA_REG3_DDA_MASK), + GRUB_SM712_CR_DDA_LOOKUP_REG3_START + 2 * idx); + grub_sm712_cr_write (dda & SM712_DDA_REG2_DDA_MASK, + GRUB_SM712_CR_DDA_LOOKUP_REG2_START + 2 * idx); + grub_sm712_cr_write (vcentering & SM712_DDA_REG2_VCENTER_MASK, + GRUB_SM712_CR_DDA_LOOKUP_REG1_START + idx); +} + +#if !defined (TEST) && !defined(GENINIT) +/* Helper for grub_video_sm712_setup. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + int *found = data; + grub_pci_address_t addr; + grub_uint32_t class; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + if (((class >> 16) & 0xffff) != GRUB_PCI_CLASS_SUBCLASS_VGA + || pciid != GRUB_SM712_PCIID) + return 0; + + *found = 1; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + framebuffer.base = grub_pci_read (addr); + framebuffer.dev = dev; + + return 1; +} +#endif + +static grub_err_t +grub_video_sm712_setup (unsigned int width, unsigned int height, + unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused))) +{ + unsigned i; +#if !defined (TEST) && !defined(GENINIT) + int depth; + grub_err_t err; + int found = 0; + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if ((width != 1024 && width != 0) || (height != 600 && height != 0) + || (depth != 16 && depth != 0)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 1024x600x16 is supported"); + + grub_pci_iterate (find_card, &found); + if (!found) + return grub_error (GRUB_ERR_IO, "Couldn't find graphics card"); + /* Fill mode info details. */ + framebuffer.mode_info.width = 1024; + framebuffer.mode_info.height = 600; + framebuffer.mode_info.mode_type = (GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + framebuffer.mode_info.bpp = 16; + framebuffer.mode_info.bytes_per_pixel = 2; + framebuffer.mode_info.pitch = 1024 * 2; + framebuffer.mode_info.number_of_colors = 256; + framebuffer.mode_info.red_mask_size = 5; + framebuffer.mode_info.red_field_pos = 11; + framebuffer.mode_info.green_mask_size = 6; + framebuffer.mode_info.green_field_pos = 5; + framebuffer.mode_info.blue_mask_size = 5; + framebuffer.mode_info.blue_field_pos = 0; + framebuffer.mode_info.reserved_mask_size = 0; + framebuffer.mode_info.reserved_field_pos = 0; + framebuffer.mode_info.blit_format + = grub_video_get_blit_format (&framebuffer.mode_info); +#endif + +#if !defined (TEST) && !defined(GENINIT) + if (found && framebuffer.base == 0) + { + grub_pci_address_t addr; + /* FIXME: choose address dynamically if needed. */ + framebuffer.base = 0x04000000; + + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_ADDRESS_REG0); + grub_pci_write (addr, framebuffer.base); + + /* Set latency. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_CACHELINE); + grub_pci_write (addr, 0x8); + + /* Enable address spaces. */ + addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, 0x7); + } +#endif + + /* We can safely discard volatile attribute. */ +#if !defined (TEST) && !defined(GENINIT) + framebuffer.ptr + = grub_pci_device_map_range (framebuffer.dev, + framebuffer.base, + GRUB_SM712_TOTAL_MEMORY_SPACE); + framebuffer.cached_ptr + = grub_pci_device_map_range_cached (framebuffer.dev, + framebuffer.base, + GRUB_SM712_TOTAL_MEMORY_SPACE); +#endif + framebuffer.mapped = 1; + + /* Initialise SM712. */ +#if !defined (TEST) && !defined(GENINIT) + /* FIXME */ + grub_vga_sr_write (0x11, 0x18); +#endif + +#if !defined (TEST) && !defined(GENINIT) + /* Prevent garbage from appearing on the screen. */ + grub_memset ((void *) framebuffer.cached_ptr, 0, + framebuffer.mode_info.height * framebuffer.mode_info.pitch); +#endif + + /* FIXME */ + grub_sm712_sr_write (0, 0x21); + grub_sm712_sr_write (0x7a, 0x62); + grub_sm712_sr_write (0x16, 0x6a); + grub_sm712_sr_write (0x2, 0x6b); + grub_sm712_write_reg (0, GRUB_VGA_IO_PIXEL_MASK); + grub_sm712_sr_write (GRUB_VGA_SR_RESET_ASYNC, GRUB_VGA_SR_RESET); + grub_sm712_write_reg (GRUB_VGA_IO_MISC_NEGATIVE_VERT_POLARITY + | GRUB_VGA_IO_MISC_NEGATIVE_HORIZ_POLARITY + | GRUB_VGA_IO_MISC_UPPER_64K + | GRUB_VGA_IO_MISC_EXTERNAL_CLOCK_0 + | GRUB_VGA_IO_MISC_ENABLE_VRAM_ACCESS + | GRUB_VGA_IO_MISC_COLOR, GRUB_VGA_IO_MISC_WRITE); + grub_sm712_sr_write (GRUB_VGA_SR_RESET_ASYNC | GRUB_VGA_SR_RESET_SYNC, + GRUB_VGA_SR_RESET); + grub_sm712_sr_write (GRUB_VGA_SR_CLOCKING_MODE_8_DOT_CLOCK, + GRUB_VGA_SR_CLOCKING_MODE); + grub_sm712_sr_write (GRUB_VGA_ALL_PLANES, GRUB_VGA_SR_MAP_MASK_REGISTER); + grub_sm712_sr_write (0, GRUB_VGA_SR_CHAR_MAP_SELECT); + grub_sm712_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4 + | GRUB_VGA_SR_MEMORY_MODE_SEQUENTIAL_ADDRESSING + | GRUB_VGA_SR_MEMORY_MODE_EXTERNAL_VIDEO_MEMORY, + GRUB_VGA_SR_MEMORY_MODE); + + for (i = 0; i < ARRAY_SIZE (sm712_sr_seq1); i++) + grub_sm712_sr_write (sm712_sr_seq1[i], 0x10 + i); + + for (i = 0; i < ARRAY_SIZE (sm712_sr_seq2); i++) + grub_sm712_sr_write (sm712_sr_seq2[i], 0x30 + i); + + /* Undocumented. */ + grub_sm712_sr_write (0x1a, 0x63); + /* Undocumented. */ + grub_sm712_sr_write (0x1a, 0x64); + + grub_sm712_sr_write (GRUB_SM712_SR_TV_CRT_SRAM | GRUB_SM712_SR_TV_ALT_CLOCK + | GRUB_SM712_SR_TV_CLOCK_CKIN_NTSC + | GRUB_SM712_SR_TV_HSYNC, + GRUB_SM712_SR_TV_CONTROL); + + grub_sm712_sr_write (GRUB_SM712_SR_RAM_LUT_NORMAL, GRUB_SM712_SR_RAM_LUT); + + /* Undocumented. */ + grub_sm712_sr_write (0x00, 0x67); + + grub_sm712_sr_write (GRUB_SM712_SR_CLOCK_CONTROL1_VCLK_FROM_CCR + | GRUB_SM712_SR_CLOCK_CONTROL1_8DOT_CLOCK, + GRUB_SM712_SR_CLOCK_CONTROL1); + grub_sm712_sr_write (GRUB_SM712_SR_CLOCK_CONTROL2_PROGRAM_VCLOCK, + GRUB_SM712_SR_CLOCK_CONTROL2); + + grub_sm712_sr_write (82, GRUB_SM712_SR_VCLK_NUM); + grub_sm712_sr_write (137, GRUB_SM712_SR_VCLK_DENOM); + + grub_sm712_sr_write (9, GRUB_SM712_SR_VCLK2_NUM); + grub_sm712_sr_write (2, GRUB_SM712_SR_VCLK2_DENOM); + /* FIXME */ + grub_sm712_sr_write (0x04, 0x70); + /* FIXME */ + grub_sm712_sr_write (0x45, 0x71); + /* Undocumented */ + grub_sm712_sr_write (0x30, 0x72); + /* Undocumented */ + grub_sm712_sr_write (0x30, 0x73); + /* Undocumented */ + grub_sm712_sr_write (0x40, 0x74); + /* Undocumented */ + grub_sm712_sr_write (0x20, 0x75); + + grub_sm712_sr_write (0xff, GRUB_SM712_SR_POPUP_ICON_LOW); + grub_sm712_sr_write (GRUB_SM712_SR_POPUP_ICON_HIGH_MASK, + GRUB_SM712_SR_POPUP_ICON_HIGH); + grub_sm712_sr_write (GRUB_SM712_SR_POPUP_ICON_CTRL_DISABLED, + GRUB_SM712_SR_POPUP_ICON_CTRL); + /* Undocumented */ + grub_sm712_sr_write (0x0, 0x83); + + grub_sm712_sr_write (8, GRUB_SM712_SR_POPUP_ICON_COLOR1); + grub_sm712_sr_write (0, GRUB_SM712_SR_POPUP_ICON_COLOR2); + grub_sm712_sr_write (0x42, GRUB_SM712_SR_POPUP_ICON_COLOR3); + + /* Undocumented */ + grub_sm712_sr_write (0x3a, 0x87); + + /* Why theese coordinates? */ + grub_sm712_sr_write (0x59, GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_X_LOW); + grub_sm712_sr_write (0x02, GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_X_HIGH); + grub_sm712_sr_write (0x44, GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_Y_LOW); + grub_sm712_sr_write (0x02, GRUB_SM712_SR_HW_CURSOR_UPPER_LEFT_Y_HIGH); + + grub_sm712_sr_write (RGB332_BLACK, GRUB_SM712_SR_HW_CURSOR_FG_COLOR); + grub_sm712_sr_write (RGB332_WHITE, GRUB_SM712_SR_HW_CURSOR_BG_COLOR); + + /* Undocumented */ + grub_sm712_sr_write (0x3a, 0x8e); + grub_sm712_sr_write (0x3a, 0x8f); + + grub_sm712_sr_write (0, GRUB_SM712_SR_POPUP_ICON_X_LOW); + grub_sm712_sr_write (0, GRUB_SM712_SR_POPUP_ICON_X_HIGH); + grub_sm712_sr_write (0, GRUB_SM712_SR_POPUP_ICON_Y_LOW); + grub_sm712_sr_write (0, GRUB_SM712_SR_POPUP_ICON_Y_HIGH); + + grub_sm712_sr_write (0, GRUB_SM712_SR_PANEL_HW_VIDEO_CONTROL); + grub_sm712_sr_write (0x10, GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_LOW); + grub_sm712_sr_write (0x08, GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_HIGH); + grub_sm712_sr_write (0x00, GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_MASK_LOW); + grub_sm712_sr_write (0x02, GRUB_SM712_SR_PANEL_HW_VIDEO_COLOR_KEY_MASK_HIGH); + grub_sm712_sr_write (0xed, GRUB_SM712_SR_PANEL_HW_VIDEO_RED_CONSTANT); + grub_sm712_sr_write (0xed, GRUB_SM712_SR_PANEL_HW_VIDEO_GREEN_CONSTANT); + grub_sm712_sr_write (0xed, GRUB_SM712_SR_PANEL_HW_VIDEO_BLUE_CONSTANT); + + grub_sm712_sr_write (0x7b, GRUB_SM712_SR_PANEL_HW_VIDEO_TOP_BOUNDARY); + grub_sm712_sr_write (0xfb, GRUB_SM712_SR_PANEL_HW_VIDEO_LEFT_BOUNDARY); + grub_sm712_sr_write (0xff, GRUB_SM712_SR_PANEL_HW_VIDEO_BOTTOM_BOUNDARY); + grub_sm712_sr_write (0xff, GRUB_SM712_SR_PANEL_HW_VIDEO_RIGHT_BOUNDARY); + /* Doesn't match documentation? */ + grub_sm712_sr_write (0x97, GRUB_SM712_SR_PANEL_HW_VIDEO_TOP_LEFT_OVERFLOW_BOUNDARY); + grub_sm712_sr_write (0xef, GRUB_SM712_SR_PANEL_HW_VIDEO_BOTTOM_RIGHT_OVERFLOW_BOUNDARY); + + grub_sm712_sr_write (0xbf, GRUB_SM712_SR_PANEL_HW_VIDEO_VERTICAL_STRETCH_FACTOR); + grub_sm712_sr_write (0xdf, GRUB_SM712_SR_PANEL_HW_VIDEO_HORIZONTAL_STRETCH_FACTOR); + + grub_sm712_gr_write (GRUB_VGA_NO_PLANES, GRUB_VGA_GR_SET_RESET_PLANE); + grub_sm712_gr_write (GRUB_VGA_NO_PLANES, GRUB_VGA_GR_SET_RESET_PLANE_ENABLE); + grub_sm712_gr_write (GRUB_VGA_NO_PLANES, GRUB_VGA_GR_COLOR_COMPARE); + grub_sm712_gr_write (GRUB_VGA_GR_DATA_ROTATE_NOP, GRUB_VGA_GR_DATA_ROTATE); + grub_sm712_gr_write (GRUB_VGA_NO_PLANES, GRUB_VGA_GR_READ_MAP_REGISTER); + grub_sm712_gr_write (GRUB_VGA_GR_MODE_256_COLOR, GRUB_VGA_GR_MODE); + grub_sm712_gr_write (GRUB_VGA_GR_GR6_MMAP_A0 + | GRUB_VGA_GR_GR6_GRAPHICS_MODE, GRUB_VGA_GR_GR6); + grub_sm712_gr_write (GRUB_VGA_ALL_PLANES, GRUB_VGA_GR_COLOR_COMPARE_DISABLE); + grub_sm712_gr_write (0xff, GRUB_VGA_GR_BITMASK); + + /* Write palette mapping. */ + for (i = 0; i < 16; i++) + grub_sm712_write_arx (i, i); + + grub_sm712_write_arx (GRUB_VGA_ARX_MODE_ENABLE_256COLOR + | GRUB_VGA_ARX_MODE_GRAPHICS, GRUB_VGA_ARX_MODE); + grub_sm712_write_arx (0, GRUB_VGA_ARX_OVERSCAN); + grub_sm712_write_arx (GRUB_VGA_ALL_PLANES, GRUB_VGA_ARX_COLOR_PLANE_ENABLE); + grub_sm712_write_arx (0, GRUB_VGA_ARX_HORIZONTAL_PANNING); + grub_sm712_write_arx (0, GRUB_VGA_ARX_COLOR_SELECT); + + /* FIXME: compute this generically. */ + { + struct grub_video_hw_config config = + { + .vertical_total = 806, + .vertical_blank_start = 0x300, + .vertical_blank_end = 0, + .vertical_sync_start = 0x303, + .vertical_sync_end = 0x9, + .line_compare = 0x3ff, + .vdisplay_end = 0x300, + .pitch = 0x80, + .horizontal_total = 164, + .horizontal_end = 128, + .horizontal_blank_start = 128, + .horizontal_blank_end = 0, + .horizontal_sync_pulse_start = 133, + .horizontal_sync_pulse_end = 22 + }; + grub_vga_set_geometry (&config, grub_sm712_cr_write); + config.horizontal_sync_pulse_start = 134; + config.horizontal_sync_pulse_end = 21; + config.vertical_sync_start = 0x301; + config.vertical_sync_end = 0x0; + config.line_compare = 0x0ff; + config.vdisplay_end = 0x258; + config.pitch = 0x7f; + grub_vga_set_geometry (&config, grub_sm712_cr_shadow_write); + } + + grub_sm712_cr_write (GRUB_VGA_CR_BYTE_PANNING_NORMAL, + GRUB_VGA_CR_BYTE_PANNING); + grub_sm712_cr_write (0, GRUB_VGA_CR_CURSOR_START); + grub_sm712_cr_write (0, GRUB_VGA_CR_CURSOR_END); + grub_sm712_cr_write (0, GRUB_VGA_CR_START_ADDR_HIGH_REGISTER); + grub_sm712_cr_write (0, GRUB_VGA_CR_START_ADDR_LOW_REGISTER); + grub_sm712_cr_write (0, GRUB_VGA_CR_CURSOR_ADDR_HIGH); + grub_sm712_cr_write (0, GRUB_VGA_CR_CURSOR_ADDR_LOW); + grub_sm712_cr_write (GRUB_VGA_CR_UNDERLINE_LOCATION_DWORD_MODE, + GRUB_VGA_CR_UNDERLINE_LOCATION); + grub_sm712_cr_write (GRUB_VGA_CR_MODE_ADDRESS_WRAP + | GRUB_VGA_CR_MODE_BYTE_MODE + | GRUB_VGA_CR_MODE_TIMING_ENABLE + | GRUB_VGA_CR_MODE_NO_CGA + | GRUB_VGA_CR_MODE_NO_HERCULES, + GRUB_VGA_CR_MODE); + + grub_sm712_cr_write (0, GRUB_SM712_CR_OVERFLOW_INTERLACE); + grub_sm712_cr_write (0, GRUB_SM712_CR_INTERLACE_RETRACE); + grub_sm712_cr_write (0, GRUB_SM712_CR_TV_VDISPLAY_START); + grub_sm712_cr_write (0, GRUB_SM712_CR_TV_VDISPLAY_END_HIGH); + grub_sm712_cr_write (0, GRUB_SM712_CR_TV_VDISPLAY_END_LOW); + grub_sm712_cr_write (0x80, GRUB_SM712_CR_DDA_CONTROL_LOW); + grub_sm712_cr_write (0x02, GRUB_SM712_CR_DDA_CONTROL_HIGH); + + /* Undocumented */ + grub_sm712_cr_write (0x20, 0x37); + + grub_sm712_cr_write (0, GRUB_SM712_CR_TV_EQUALIZER); + grub_sm712_cr_write (0, GRUB_SM712_CR_TV_SERRATION); + grub_sm712_cr_write (0, GRUB_SM712_CR_HSYNC_CTRL); + + /* Undocumented */ + grub_sm712_cr_write (0x40, 0x3b); + + grub_sm712_cr_write (GRUB_SM712_CR_DEBUG_NONE, GRUB_SM712_CR_DEBUG); + + /* Undocumented */ + grub_sm712_cr_write (0xff, 0x3d); + grub_sm712_cr_write (0x46, 0x3e); + grub_sm712_cr_write (0x91, 0x3f); + + for (i = 0; i < ARRAY_SIZE (dda_lookups); i++) + grub_sm712_write_dda_lookup (i, dda_lookups[i].compare, dda_lookups[i].dda, + dda_lookups[i].vcentering); + + /* Undocumented */ + grub_sm712_cr_write (0, 0x9c); + grub_sm712_cr_write (0, 0x9d); + grub_sm712_cr_write (0, 0x9e); + grub_sm712_cr_write (0, 0x9f); + + grub_sm712_cr_write (0, GRUB_SM712_CR_VCENTERING_OFFSET); + grub_sm712_cr_write (0, GRUB_SM712_CR_HCENTERING_OFFSET); + + grub_sm712_write_reg (GRUB_VGA_IO_MISC_NEGATIVE_HORIZ_POLARITY + | GRUB_VGA_IO_MISC_UPPER_64K + | GRUB_VGA_IO_MISC_28MHZ + | GRUB_VGA_IO_MISC_ENABLE_VRAM_ACCESS + | GRUB_VGA_IO_MISC_COLOR, + GRUB_VGA_IO_MISC_WRITE); + +#if !defined (TEST) && !defined(GENINIT) + /* Undocumented? */ + *(volatile grub_uint32_t *) ((char *) framebuffer.ptr + 0x40c00c) = 0; + *(volatile grub_uint32_t *) ((char *) framebuffer.ptr + 0x40c040) = 0; + *(volatile grub_uint32_t *) ((char *) framebuffer.ptr + 0x40c000) = 0x20000; + *(volatile grub_uint32_t *) ((char *) framebuffer.ptr + 0x40c010) = 0x1020100; +#endif + + (void) grub_sm712_sr_read (0x16); + +#if !defined (TEST) && !defined(GENINIT) + err = grub_video_fb_setup (mode_type, mode_mask, + &framebuffer.mode_info, + framebuffer.cached_ptr, NULL, NULL); + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + return err; +#else + return 0; +#endif +} + +#if !defined (TEST) && !defined(GENINIT) + +static grub_err_t +grub_video_sm712_swap_buffers (void) +{ + grub_size_t s; + s = (framebuffer.mode_info.height + * framebuffer.mode_info.pitch + * framebuffer.mode_info.bytes_per_pixel); + grub_video_fb_swap_buffers (); + grub_arch_sync_dma_caches (framebuffer.cached_ptr, s); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_sm712_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_sm712_adapter = + { + .name = "SM712 Video Driver", + .id = GRUB_VIDEO_DRIVER_SM712, + + .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE, + + .init = grub_video_sm712_video_init, + .fini = grub_video_sm712_video_fini, + .setup = grub_video_sm712_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_sm712_get_info_and_fini, + .set_palette = grub_video_fb_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .set_region = grub_video_fb_set_region, + .get_region = grub_video_fb_get_region, + .set_area_status = grub_video_fb_set_area_status, + .get_area_status = grub_video_fb_get_area_status, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_sm712_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_fb_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(video_sm712) +{ + grub_video_register (&grub_video_sm712_adapter); +} + +GRUB_MOD_FINI(video_sm712) +{ + grub_video_unregister (&grub_video_sm712_adapter); +} +#else +int +main () +{ + grub_video_sm712_setup (1024, 600, 0, 0); +} +#endif diff --git a/grub-core/video/sm712_init.c b/grub-core/video/sm712_init.c new file mode 100644 index 0000000..cdb0b73 --- /dev/null +++ b/grub-core/video/sm712_init.c @@ -0,0 +1,14 @@ +/* Following sequence is a capture of sm712 initialisation sequence. */ +static grub_uint8_t sm712_sr_seq1[] = + { 0xc8, 0x40, 0x14, 0x60, 0x0, 0xa, 0x92, 0x0, + 0x51, 0x00, 0x01, 0x00, 0x0, 0x0, 0x00, 0x0, + 0xc4, 0x30, 0x02, 0x00, 0x1 }; + +static grub_uint8_t sm712_sr_seq2[] = + { 0x28, 0x03, 0x24, 0x09, 0xc0, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x00, 0x00, 0x03, 0xff, + 0x00, 0xfc, 0x00, 0x00, 0x20, 0x18, 0x00, 0xfc, + 0x20, 0x0c, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3a, + 0x06, 0x68, 0xa7, 0x7f, 0x83, 0x24, 0xff, 0x03, + 0x00, 0x60, 0x59, 0x3a, 0x3a, 0x00, 0x00, 0x3a, + 0x01, 0x80 }; diff --git a/grub-core/video/video.c b/grub-core/video/video.c new file mode 100644 index 0000000..9834241 --- /dev/null +++ b/grub-core/video/video.c @@ -0,0 +1,763 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/video.h> +#include <grub/types.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/i18n.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* The list of video adapters registered to system. */ +grub_video_adapter_t grub_video_adapter_list = NULL; + +/* Active video adapter. */ +grub_video_adapter_t grub_video_adapter_active; + +/* Restore back to initial mode (where applicable). */ +grub_err_t +grub_video_restore (void) +{ + if (grub_video_adapter_active) + { + grub_video_adapter_active->fini (); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + grub_video_adapter_active = 0; + } + return GRUB_ERR_NONE; +} + +/* Get information about active video mode. */ +grub_err_t +grub_video_get_info (struct grub_video_mode_info *mode_info) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + /* If mode_info is NULL just report that video adapter is active. */ + if (! mode_info) + { + grub_errno = GRUB_ERR_NONE; + return grub_errno; + } + + return grub_video_adapter_active->get_info (mode_info); +} + +grub_video_driver_id_t +grub_video_get_driver_id (void) +{ + if (! grub_video_adapter_active) + return GRUB_VIDEO_DRIVER_NONE; + return grub_video_adapter_active->id; +} + +/* Get information about active video mode. */ +grub_err_t +grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuffer) +{ + grub_err_t err; + + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + err = grub_video_adapter_active->get_info_and_fini (mode_info, framebuffer); + if (err) + return err; + + grub_video_adapter_active = 0; + return GRUB_ERR_NONE; +} + +/* Determine optimized blitting formation for specified video mode info. */ +enum grub_video_blit_format +grub_video_get_blit_format (struct grub_video_mode_info *mode_info) +{ + /* Check if we have any known 32 bit modes. */ + if (mode_info->bpp == 32) + { + if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 16) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 0)) + { + return GRUB_VIDEO_BLIT_FORMAT_BGRA_8888; + } + else if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 0) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 16)) + { + return GRUB_VIDEO_BLIT_FORMAT_RGBA_8888; + } + } + /* Check if we have any known 24 bit modes. */ + else if (mode_info->bpp == 24) + { + if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 16) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 0)) + { + return GRUB_VIDEO_BLIT_FORMAT_BGR_888; + } + else if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 0) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 16)) + { + return GRUB_VIDEO_BLIT_FORMAT_RGB_888; + } + } + /* Check if we have any known 16 bit modes. */ + else if (mode_info->bpp == 16) + { + if ((mode_info->red_mask_size == 5) + && (mode_info->red_field_pos == 11) + && (mode_info->green_mask_size == 6) + && (mode_info->green_field_pos == 5) + && (mode_info->blue_mask_size == 5) + && (mode_info->blue_field_pos == 0)) + { + return GRUB_VIDEO_BLIT_FORMAT_BGR_565; + } + else if ((mode_info->red_mask_size == 5) + && (mode_info->red_field_pos == 0) + && (mode_info->green_mask_size == 6) + && (mode_info->green_field_pos == 5) + && (mode_info->blue_mask_size == 5) + && (mode_info->blue_field_pos == 11)) + { + return GRUB_VIDEO_BLIT_FORMAT_RGB_565; + } + } + else if (mode_info->bpp == 1) + return GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; + + /* Backup route. Unknown format. */ + + /* If there are more than 8 bits per color, assume RGB(A) mode. */ + if (mode_info->bpp > 8) + { + if (mode_info->reserved_mask_size > 0) + { + return GRUB_VIDEO_BLIT_FORMAT_RGBA; + } + else + { + return GRUB_VIDEO_BLIT_FORMAT_RGB; + } + } + + /* Assume as indexcolor mode. */ + return GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR; +} + +/* Set new indexed color palette entries. */ +grub_err_t +grub_video_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->set_palette (start, count, palette_data); +} + +/* Get indexed color palette entries. */ +grub_err_t +grub_video_get_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_palette (start, count, palette_data); +} + +/* Set viewport dimensions. */ +grub_err_t +grub_video_set_viewport (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->set_viewport (x, y, width, height); +} + +/* Get viewport dimensions. */ +grub_err_t +grub_video_get_viewport (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_viewport (x, y, width, height); +} + +/* Set region dimensions. */ +grub_err_t +grub_video_set_region (unsigned int x, unsigned int y, + unsigned int width, unsigned int height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->set_region (x, y, width, height); +} + +/* Get region dimensions. */ +grub_err_t +grub_video_get_region (unsigned int *x, unsigned int *y, + unsigned int *width, unsigned int *height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_region (x, y, width, height); +} + +/* Set status of the intersection of the viewport and the region. */ +grub_err_t +grub_video_set_area_status (grub_video_area_status_t area_status) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->set_area_status (area_status); +} + +/* Get status of the intersection of the viewport and the region. */ +grub_err_t +grub_video_get_area_status (grub_video_area_status_t *area_status) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_area_status (area_status); +} + +/* Map color name to adapter specific color. */ +grub_video_color_t +grub_video_map_color (grub_uint32_t color_name) +{ + if (! grub_video_adapter_active) + return 0; + + return grub_video_adapter_active->map_color (color_name); +} + +/* Map RGB value to adapter specific color. */ +grub_video_color_t +grub_video_map_rgb (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue) +{ + if (! grub_video_adapter_active) + return 0; + + return grub_video_adapter_active->map_rgb (red, green, blue); +} + +/* Map RGBA value to adapter specific color. */ +grub_video_color_t +grub_video_map_rgba (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue, + grub_uint8_t alpha) +{ + if (! grub_video_adapter_active) + return 0; + + return grub_video_adapter_active->map_rgba (red, green, blue, alpha); +} + +/* Unmap video color back to RGBA components. */ +grub_err_t +grub_video_unmap_color (grub_video_color_t color, grub_uint8_t *red, + grub_uint8_t *green, grub_uint8_t *blue, + grub_uint8_t *alpha) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->unmap_color (color, + red, + green, + blue, + alpha); +} + +/* Fill rectangle using specified color. */ +grub_err_t +grub_video_fill_rect (grub_video_color_t color, int x, int y, + unsigned int width, unsigned int height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->fill_rect (color, x, y, width, height); +} + +/* Blit bitmap to screen. */ +grub_err_t +grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->blit_bitmap (bitmap, oper, x, y, + offset_x, offset_y, + width, height); +} + +/* Blit render target to active render target. */ +grub_err_t +grub_video_blit_render_target (struct grub_video_render_target *target, + enum grub_video_blit_operators oper, + int x, int y, int offset_x, int offset_y, + unsigned int width, unsigned int height) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->blit_render_target (target, oper, x, y, + offset_x, offset_y, + width, height); +} + +/* Scroll viewport and fill new areas with specified color. */ +grub_err_t +grub_video_scroll (grub_video_color_t color, int dx, int dy) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->scroll (color, dx, dy); +} + +/* Swap buffers (swap active render target). */ +grub_err_t +grub_video_swap_buffers (void) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->swap_buffers (); +} + +/* Create new render target. */ +grub_err_t +grub_video_create_render_target (struct grub_video_render_target **result, + unsigned int width, unsigned int height, + unsigned int mode_type) +{ + *result = 0; + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->create_render_target (result, + width, height, + mode_type); +} + +/* Delete render target. */ +grub_err_t +grub_video_delete_render_target (struct grub_video_render_target *target) +{ + if (!target) + return GRUB_ERR_NONE; + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->delete_render_target (target); +} + +/* Set active render target. */ +grub_err_t +grub_video_set_active_render_target (struct grub_video_render_target *target) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->set_active_render_target (target); +} + +/* Get active render target. */ +grub_err_t +grub_video_get_active_render_target (struct grub_video_render_target **target) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + return grub_video_adapter_active->get_active_render_target (target); +} + +grub_err_t +grub_video_edid_checksum (struct grub_video_edid_info *edid_info) +{ + const char *edid_bytes = (const char *) edid_info; + int i; + char checksum = 0; + + /* Check EDID checksum. */ + for (i = 0; i < 128; ++i) + checksum += edid_bytes[i]; + + if (checksum != 0) + return grub_error (GRUB_ERR_BAD_DEVICE, + "invalid EDID checksum %d", checksum); + + grub_errno = GRUB_ERR_NONE; + return grub_errno; +} + +grub_err_t +grub_video_edid_preferred_mode (struct grub_video_edid_info *edid_info, + unsigned int *width, unsigned int *height) +{ + /* Bit 1 in the Feature Support field indicates that the first + Detailed Timing Description is the preferred timing mode. */ + if (edid_info->version == 1 /* we don't understand later versions */ + && (edid_info->feature_support + & GRUB_VIDEO_EDID_FEATURE_PREFERRED_TIMING_MODE) + && edid_info->detailed_timings[0].pixel_clock) + { + *width = edid_info->detailed_timings[0].horizontal_active_lo + | (((unsigned int) + (edid_info->detailed_timings[0].horizontal_hi & 0xf0)) + << 4); + *height = edid_info->detailed_timings[0].vertical_active_lo + | (((unsigned int) + (edid_info->detailed_timings[0].vertical_hi & 0xf0)) + << 4); + if (*width && *height) + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_BAD_DEVICE, "no preferred mode available"); +} + +/* Parse <width>x<height>[x<depth>]*/ +static grub_err_t +parse_modespec (const char *current_mode, int *width, int *height, int *depth) +{ + const char *value; + const char *param = current_mode; + + *width = *height = *depth = -1; + + if (grub_strcmp (param, "auto") == 0) + { + *width = *height = 0; + return GRUB_ERR_NONE; + } + + /* Find width value. */ + value = param; + param = grub_strchr(param, 'x'); + if (param == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + current_mode); + + param++; + + *width = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + current_mode); + + /* Find height value. */ + value = param; + param = grub_strchr(param, 'x'); + if (param == NULL) + { + *height = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + current_mode); + } + else + { + /* We have optional color depth value. */ + param++; + + *height = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + current_mode); + + /* Convert color depth value. */ + value = param; + *depth = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + current_mode); + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_set_mode (const char *modestring, + unsigned int modemask, + unsigned int modevalue) +{ + char *tmp; + char *next_mode; + char *current_mode; + char *modevar; + + if (grub_video_adapter_active && grub_video_adapter_active->id == GRUB_VIDEO_ADAPTER_CAPTURE) + return GRUB_ERR_NONE; + + modevalue &= modemask; + + /* Take copy of env.var. as we don't want to modify that. */ + modevar = grub_strdup (modestring); + + /* Initialize next mode. */ + next_mode = modevar; + + if (! modevar) + return grub_errno; + + if (grub_memcmp (next_mode, "keep", sizeof ("keep")) == 0 + || grub_memcmp (next_mode, "keep,", sizeof ("keep,") - 1) == 0 + || grub_memcmp (next_mode, "keep;", sizeof ("keep;") - 1) == 0) + { + int suitable = 1; + grub_err_t err; + + if (grub_video_adapter_active) + { + struct grub_video_mode_info mode_info; + grub_memset (&mode_info, 0, sizeof (mode_info)); + err = grub_video_get_info (&mode_info); + if (err) + { + suitable = 0; + grub_errno = GRUB_ERR_NONE; + } + if ((mode_info.mode_type & modemask) != modevalue) + suitable = 0; + } + else if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) != 0) + && ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) == 0)) + suitable = 0; + + if (suitable) + { + grub_free (modevar); + return GRUB_ERR_NONE; + } + next_mode += sizeof ("keep") - 1; + if (! *next_mode) + { + grub_free (modevar); + + /* TRANSLATORS: This doesn't imply that there is no available video + mode at all. All modes may have been filtered out by some criteria. + */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("no suitable video mode found")); + } + + /* Skip separator. */ + next_mode++; + } + + /* De-activate last set video adapter. */ + if (grub_video_adapter_active) + { + /* Finalize adapter. */ + grub_video_adapter_active->fini (); + if (grub_errno != GRUB_ERR_NONE) + grub_errno = GRUB_ERR_NONE; + + /* Mark active adapter as not set. */ + grub_video_adapter_active = 0; + } + + /* Loop until all modes has been tested out. */ + while (next_mode != NULL) + { + int width = -1; + int height = -1; + int depth = -1; + grub_err_t err; + unsigned int flags = modevalue; + unsigned int flagmask = modemask; + + /* Use last next_mode as current mode. */ + tmp = next_mode; + + /* Save position of next mode and separate modes. */ + for (; *next_mode; next_mode++) + if (*next_mode == ',' || *next_mode == ';') + break; + if (*next_mode) + { + *next_mode = 0; + next_mode++; + } + else + next_mode = 0; + + /* Skip whitespace. */ + while (grub_isspace (*tmp)) + tmp++; + + /* Initialize token holders. */ + current_mode = tmp; + + /* XXX: we assume that we're in pure text mode if + no video mode is initialized. Is it always true? */ + if (grub_strcmp (current_mode, "text") == 0) + { + struct grub_video_mode_info mode_info; + + grub_memset (&mode_info, 0, sizeof (mode_info)); + if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) == 0) + || ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) != 0)) + { + /* Valid mode found from adapter, and it has been activated. + Specify it as active adapter. */ + grub_video_adapter_active = NULL; + + /* Free memory. */ + grub_free (modevar); + + return GRUB_ERR_NONE; + } + else + continue; + } + + err = parse_modespec (current_mode, &width, &height, &depth); + if (err) + { + /* Free memory before returning. */ + grub_free (modevar); + + return err; + } + + /* Try out video mode. */ + + /* If user requested specific depth check if this depth is supported. */ + if (depth != -1 && (flagmask & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + && + (((flags & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + != ((depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)))) + continue; + + if (depth != -1) + { + flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + flagmask |= GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + } + + /* Try to initialize requested mode. Ignore any errors. */ + grub_video_adapter_t p; + + /* Loop thru all possible video adapter trying to find requested mode. */ + for (p = grub_video_adapter_list; p; p = p->next) + { + struct grub_video_mode_info mode_info; + + grub_memset (&mode_info, 0, sizeof (mode_info)); + + /* Try to initialize adapter, if it fails, skip to next adapter. */ + err = p->init (); + if (err != GRUB_ERR_NONE) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + /* Try to initialize video mode. */ + err = p->setup (width, height, flags, flagmask); + if (err != GRUB_ERR_NONE) + { + p->fini (); + grub_errno = GRUB_ERR_NONE; + continue; + } + + err = p->get_info (&mode_info); + if (err != GRUB_ERR_NONE) + { + p->fini (); + grub_errno = GRUB_ERR_NONE; + continue; + } + + flags = mode_info.mode_type & ~GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + flags |= (mode_info.bpp << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + + /* Check that mode is suitable for upper layer. */ + if ((flags & GRUB_VIDEO_MODE_TYPE_PURE_TEXT) + ? (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) != 0) + && ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) == 0)) + : ((flags & modemask) != modevalue)) + { + p->fini (); + grub_errno = GRUB_ERR_NONE; + continue; + } + + /* Valid mode found from adapter, and it has been activated. + Specify it as active adapter. */ + grub_video_adapter_active = p; + + /* Free memory. */ + grub_free (modevar); + + return GRUB_ERR_NONE; + } + + } + + /* Free memory. */ + grub_free (modevar); + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("no suitable video mode found")); +} |