video.c 21.6 KB
Newer Older
1 2
/*
 *  GRUB  --  GRand Unified Bootloader
3
 *  Copyright (C) 2006,2007,2008,2009  Free Software Foundation, Inc.
4
 *
5
 *  GRUB is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation, either version 3 of the License, or
8 9
 *  (at your option) any later version.
 *
10
 *  GRUB is distributed in the hope that it will be useful,
11 12 13 14 15
 *  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
16
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17 18 19 20 21
 */

#include <grub/video.h>
#include <grub/types.h>
#include <grub/dl.h>
22 23
#include <grub/misc.h>
#include <grub/mm.h>
24
#include <grub/i18n.h>
25

26 27
GRUB_MOD_LICENSE ("GPLv3+");

28
/* The list of video adapters registered to system.  */
29
grub_video_adapter_t grub_video_adapter_list = NULL;
30 31

/* Active video adapter.  */
32
grub_video_adapter_t grub_video_adapter_active;
33

34
/* Restore back to initial mode (where applicable).  */
35 36 37 38 39 40 41 42
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;
43

44 45 46 47 48
      grub_video_adapter_active = 0;
    }
  return GRUB_ERR_NONE;
}

49
/* Get information about active video mode.  */
50 51 52 53
grub_err_t
grub_video_get_info (struct grub_video_mode_info *mode_info)
{
  if (! grub_video_adapter_active)
54
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
55

56 57 58 59 60 61
  /* If mode_info is NULL just report that video adapter is active.  */
  if (! mode_info)
    {
      grub_errno = GRUB_ERR_NONE;
      return grub_errno;
    }
62

63 64 65
  return grub_video_adapter_active->get_info (mode_info);
}

66 67 68 69 70 71 72 73
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;
}

74 75 76 77 78 79 80 81
/* 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)
82
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
83 84 85 86 87 88 89 90 91

  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;
}

92
/* Determine optimized blitting formation for specified video mode info.  */
93 94 95
enum grub_video_blit_format
grub_video_get_blit_format (struct grub_video_mode_info *mode_info)
{
96
  /* Check if we have any known 32 bit modes.  */
97 98 99
  if (mode_info->bpp == 32)
    {
      if ((mode_info->red_mask_size == 8)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	  && (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;
	}
117 118
    }
  /* Check if we have any known 24 bit modes.  */
119
  else if (mode_info->bpp == 24)
120 121
    {
      if ((mode_info->red_mask_size == 8)
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	  && (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;
	}
139
    }
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  /* 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;
	}
    }
162 163
  else if (mode_info->bpp == 1)
    return GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
164 165

  /* Backup route.  Unknown format.  */
166 167 168 169 170

  /* 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)
171 172 173 174 175 176 177
	{
	  return GRUB_VIDEO_BLIT_FORMAT_RGBA;
	}
      else
	{
	  return GRUB_VIDEO_BLIT_FORMAT_RGB;
	}
178 179 180 181 182 183
    }

  /* Assume as indexcolor mode.  */
  return GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR;
}

184
/* Set new indexed color palette entries.  */
185 186 187 188 189
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)
190
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
191

192 193 194
  return grub_video_adapter_active->set_palette (start, count, palette_data);
}

195
/* Get indexed color palette entries.  */
196 197 198 199 200
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)
201
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
202

203 204 205
  return grub_video_adapter_active->get_palette (start, count, palette_data);
}

206
/* Set viewport dimensions.  */
207 208 209 210 211
grub_err_t
grub_video_set_viewport (unsigned int x, unsigned int y,
                         unsigned int width, unsigned int height)
{
  if (! grub_video_adapter_active)
212
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
213 214 215 216

  return grub_video_adapter_active->set_viewport (x, y, width, height);
}

217
/* Get viewport dimensions.  */
218 219 220 221 222
grub_err_t
grub_video_get_viewport (unsigned int *x, unsigned int *y,
                         unsigned int *width, unsigned int *height)
{
  if (! grub_video_adapter_active)
223
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
224 225 226 227

  return grub_video_adapter_active->get_viewport (x, y, width, height);
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
/* 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);
}

270
/* Map color name to adapter specific color.  */
271 272 273 274 275 276 277 278 279
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);
}

280
/* Map RGB value to adapter specific color.  */
281 282 283 284 285 286 287 288 289
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);
}

290
/* Map RGBA value to adapter specific color.  */
291 292 293 294 295 296 297 298 299 300
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);
}

301 302
/* Unmap video color back to RGBA components.  */
grub_err_t
303 304
grub_video_unmap_color (grub_video_color_t color, grub_uint8_t *red,
                        grub_uint8_t *green, grub_uint8_t *blue,
305 306 307
                        grub_uint8_t *alpha)
{
  if (! grub_video_adapter_active)
308
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
309 310 311 312 313 314 315 316

  return grub_video_adapter_active->unmap_color (color,
                                                 red,
                                                 green,
                                                 blue,
                                                 alpha);
}

317
/* Fill rectangle using specified color.  */
318 319 320 321 322
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)
323
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
324 325 326 327

  return grub_video_adapter_active->fill_rect (color, x, y, width, height);
}

328
/* Blit bitmap to screen.  */
329 330
grub_err_t
grub_video_blit_bitmap (struct grub_video_bitmap *bitmap,
331
                        enum grub_video_blit_operators oper,
332 333 334 335
                        int x, int y, int offset_x, int offset_y,
                        unsigned int width, unsigned int height)
{
  if (! grub_video_adapter_active)
336
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
337

338
  return grub_video_adapter_active->blit_bitmap (bitmap, oper, x, y,
339 340 341 342
                                                 offset_x, offset_y,
                                                 width, height);
}

343
/* Blit render target to active render target.  */
344 345
grub_err_t
grub_video_blit_render_target (struct grub_video_render_target *target,
346
                               enum grub_video_blit_operators oper,
347 348 349 350
                               int x, int y, int offset_x, int offset_y,
                               unsigned int width, unsigned int height)
{
  if (! grub_video_adapter_active)
351
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
352

353 354
  return grub_video_adapter_active->blit_render_target (target, oper, x, y,
                                                        offset_x, offset_y,
355 356 357
                                                        width, height);
}

358
/* Scroll viewport and fill new areas with specified color.  */
359 360 361 362
grub_err_t
grub_video_scroll (grub_video_color_t color, int dx, int dy)
{
  if (! grub_video_adapter_active)
363
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
364 365 366 367

  return grub_video_adapter_active->scroll (color, dx, dy);
}

368
/* Swap buffers (swap active render target).  */
369 370 371 372
grub_err_t
grub_video_swap_buffers (void)
{
  if (! grub_video_adapter_active)
373
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
374 375 376 377

  return grub_video_adapter_active->swap_buffers ();
}

378
/* Create new render target.  */
379 380 381 382 383
grub_err_t
grub_video_create_render_target (struct grub_video_render_target **result,
                                 unsigned int width, unsigned int height,
                                 unsigned int mode_type)
{
384
  *result = 0;
385
  if (! grub_video_adapter_active)
386
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
387 388 389 390 391 392

  return grub_video_adapter_active->create_render_target (result,
                                                          width, height,
                                                          mode_type);
}

393
/* Delete render target.  */
394 395 396
grub_err_t
grub_video_delete_render_target (struct grub_video_render_target *target)
{
397 398
  if (!target)
    return GRUB_ERR_NONE;
399
  if (! grub_video_adapter_active)
400
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
401 402 403 404

  return grub_video_adapter_active->delete_render_target (target);
}

405
/* Set active render target.  */
406 407 408 409
grub_err_t
grub_video_set_active_render_target (struct grub_video_render_target *target)
{
  if (! grub_video_adapter_active)
410
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
411 412 413 414

  return grub_video_adapter_active->set_active_render_target (target);
}

415 416 417 418 419
/* Get active render target.  */
grub_err_t
grub_video_get_active_render_target (struct grub_video_render_target **target)
{
  if (! grub_video_adapter_active)
420
    return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated");
421 422 423 424

  return grub_video_adapter_active->get_active_render_target (target);
}

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
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;
}

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
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);
463 464
      if (*width && *height)
	return GRUB_ERR_NONE;
465 466 467 468 469
    }

  return grub_error (GRUB_ERR_BAD_DEVICE, "no preferred mode available");
}

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
/* 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,
490
		       N_("invalid video mode specification `%s'"),
491 492 493 494 495 496 497
		       current_mode);

  param++;
  
  *width = grub_strtoul (value, 0, 0);
  if (grub_errno != GRUB_ERR_NONE)
      return grub_error (GRUB_ERR_BAD_ARGUMENT,
498
			 N_("invalid video mode specification `%s'"),
499 500 501 502 503 504 505 506 507 508
			 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,
509
			   N_("invalid video mode specification `%s'"),
510 511 512 513 514 515 516 517 518 519
			   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,
520
			   N_("invalid video mode specification `%s'"),
521 522 523 524 525 526 527
			   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,
528
			   N_("invalid video mode specification `%s'"),
529 530 531 532 533
			   current_mode);
    }
  return GRUB_ERR_NONE;
}

534
grub_err_t
535
grub_video_set_mode (const char *modestring,
536 537
		     unsigned int modemask,
		     unsigned int modevalue)
538 539 540 541 542
{
  char *tmp;
  char *next_mode;
  char *current_mode;
  char *modevar;
543

544 545 546
  if (grub_video_adapter_active && grub_video_adapter_active->id == GRUB_VIDEO_ADAPTER_CAPTURE)
    return GRUB_ERR_NONE;

547
  modevalue &= modemask;
548 549 550 551 552 553 554 555

  /* 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)
556
    return grub_errno;
557 558 559 560 561 562 563

  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;
564

565 566
      if (grub_video_adapter_active)
	{
567 568
	  struct grub_video_mode_info mode_info;
	  grub_memset (&mode_info, 0, sizeof (mode_info));
569 570 571 572 573 574
	  err = grub_video_get_info (&mode_info);
	  if (err)
	    {
	      suitable = 0;
	      grub_errno = GRUB_ERR_NONE;
	    }
575 576
	  if ((mode_info.mode_type & modemask) != modevalue)
	    suitable = 0;
577
	}
578 579 580
      else if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) != 0)
	       && ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) == 0))
	suitable = 0;
581 582 583 584 585 586 587 588 589 590

      if (suitable)
	{
	  grub_free (modevar);
	  return GRUB_ERR_NONE;
	}
      next_mode += sizeof ("keep") - 1;
      if (! *next_mode)
	{
	  grub_free (modevar);
591

592 593 594
	  /* TRANSLATORS: This doesn't imply that there is no available video
	     mode at all. All modes may have been filtered out by some criteria.
	   */
595
	  return grub_error (GRUB_ERR_BAD_ARGUMENT,
596
			     N_("no suitable video mode found"));
597
	}
598

599 600 601 602 603 604 605 606 607 608 609
      /* 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;
610

611 612 613
      /* Mark active adapter as not set.  */
      grub_video_adapter_active = 0;
    }
614

615 616 617
  /* Loop until all modes has been tested out.  */
  while (next_mode != NULL)
    {
618 619 620 621 622 623 624
      int width = -1;
      int height = -1;
      int depth = -1;
      grub_err_t err;
      unsigned int flags = modevalue;
      unsigned int flagmask = modemask;

625 626
      /* Use last next_mode as current mode.  */
      tmp = next_mode;
627

628 629 630 631 632 633 634 635 636 637 638
      /* 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;
639

640 641 642
      /* Skip whitespace.  */
      while (grub_isspace (*tmp))
	tmp++;
643

644 645 646
      /* Initialize token holders.  */
      current_mode = tmp;

647
      /* XXX: we assume that we're in pure text mode if
648
	 no video mode is initialized. Is it always true? */
649
      if (grub_strcmp (current_mode, "text") == 0)
650 651
	{
	  struct grub_video_mode_info mode_info;
652

653
	  grub_memset (&mode_info, 0, sizeof (mode_info));
654 655
	  if (((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modemask) == 0)
	      || ((GRUB_VIDEO_MODE_TYPE_PURE_TEXT & modevalue) != 0))
656 657 658 659 660 661 662 663 664 665 666 667
	    {
	      /* 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;
	    }
	}

668 669
      err = parse_modespec (current_mode, &width, &height, &depth);
      if (err)
670 671 672
	{
	  /* Free memory before returning.  */
	  grub_free (modevar);
673

674
	  return err;
675 676 677
	}

      /* Try out video mode.  */
678

679 680 681 682 683 684 685
      /* 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;
686

687
      if (depth != -1)
688 689 690 691 692
	{
	  flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
	    & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
	  flagmask |= GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
	}
693

694 695 696 697 698 699 700
      /* 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;
701

702 703 704 705 706 707 708 709 710 711 712
	  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.  */
713
	  err = p->setup (width, height, flags, flagmask);
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
	  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;
	    }

729 730 731 732 733 734 735 736 737
	  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))
738 739 740 741 742
	    {
	      p->fini ();
	      grub_errno = GRUB_ERR_NONE;
	      continue;
	    }
743

744 745 746
	  /* Valid mode found from adapter, and it has been activated.
	     Specify it as active adapter.  */
	  grub_video_adapter_active = p;
747

748 749
	  /* Free memory.  */
	  grub_free (modevar);
750

751 752 753 754 755 756 757
	  return GRUB_ERR_NONE;
	}

    }

  /* Free memory.  */
  grub_free (modevar);
758

759
  return grub_error (GRUB_ERR_BAD_ARGUMENT,
760
		     N_("no suitable video mode found"));
761
}