Browse Source

[vesafb] Select an optimal mode, rather than the first acceptable mode

There is no requirement for VBE modes to be listed in increasing order
of resolution.  With the present logic, this can cause e.g. a 1024x768
mode to be selected if the user asks for 640x480, if the 1024x768 mode
is earlier in the mode list.

Define a scoring system for modes as

  score = ( width * height - bpp )

and choose the mode with the lowest score among all acceptable modes.
This should prefer to choose the mode closest to the requested
resolution, with a slight preference for higher colour depths.

Reported-by: Robin Smidsrød <robin@smidsrod.no>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
d4f7816de7
1 changed files with 91 additions and 49 deletions
  1. 91
    49
      src/arch/i386/interface/pcbios/vesafb.c

+ 91
- 49
src/arch/i386/interface/pcbios/vesafb.c View File

27
 
27
 
28
 #include <stdlib.h>
28
 #include <stdlib.h>
29
 #include <errno.h>
29
 #include <errno.h>
30
+#include <limits.h>
30
 #include <realmode.h>
31
 #include <realmode.h>
31
 #include <ipxe/console.h>
32
 #include <ipxe/console.h>
32
 #include <ipxe/io.h>
33
 #include <ipxe/io.h>
211
 }
212
 }
212
 
213
 
213
 /**
214
 /**
214
- * Set video mode
215
+ * Get video mode information
215
  *
216
  *
216
  * @v mode_number	Mode number
217
  * @v mode_number	Mode number
217
- * @v mode		Mode information
218
  * @ret rc		Return status code
218
  * @ret rc		Return status code
219
  */
219
  */
220
-static int vesafb_set_mode ( unsigned int mode_number,
221
-			     struct vbe_mode_info *mode ) {
220
+static int vesafb_mode_info ( unsigned int mode_number ) {
221
+	struct vbe_mode_info *mode = &vbe_buf.mode;
222
 	uint16_t status;
222
 	uint16_t status;
223
 	int rc;
223
 	int rc;
224
 
224
 
225
-	/* Select this mode */
225
+	/* Get mode information */
226
 	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
226
 	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
227
 			       : "=a" ( status )
227
 			       : "=a" ( status )
228
-			       : "a" ( VBE_SET_MODE ),
229
-				 "b" ( mode_number ) );
228
+			       : "a" ( VBE_MODE_INFO ),
229
+				 "c" ( mode_number ),
230
+				 "D" ( __from_data16 ( mode ) )
231
+			       : "memory" );
230
 	if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
232
 	if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
231
-		DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n",
232
-		       mode_number, status, strerror ( rc ) );
233
+		DBGC ( &vbe_buf, "VESAFB could not get mode %04x information: "
234
+		       "[%04x] %s\n", mode_number, status, strerror ( rc ) );
233
 		return rc;
235
 		return rc;
234
 	}
236
 	}
237
+	DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) model "
238
+	       "%02x [x%d]%s%s%s%s%s\n", mode_number, mode->x_resolution,
239
+	       mode->y_resolution, mode->bits_per_pixel, mode->rsvd_mask_size,
240
+	       mode->red_mask_size, mode->green_mask_size, mode->blue_mask_size,
241
+	       mode->memory_model, ( mode->number_of_image_pages + 1 ),
242
+	       ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
243
+		 "" : " [unsupported]" ),
244
+	       ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
245
+		 " [tty]" : "" ),
246
+	       ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
247
+		 "" : " [text]" ),
248
+	       ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
249
+		 "" : " [nonlinear]" ),
250
+	       ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
251
+		 " [buf]" : "" ) );
252
+
253
+	return 0;
254
+}
255
+
256
+/**
257
+ * Set video mode
258
+ *
259
+ * @v mode_number	Mode number
260
+ * @ret rc		Return status code
261
+ */
262
+static int vesafb_set_mode ( unsigned int mode_number ) {
263
+	struct vbe_mode_info *mode = &vbe_buf.mode;
264
+	uint16_t status;
265
+	int rc;
266
+
267
+	/* Get mode information */
268
+	if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
269
+		return rc;
235
 
270
 
236
 	/* Record mode parameters */
271
 	/* Record mode parameters */
237
 	vesafb.start = mode->phys_base_ptr;
272
 	vesafb.start = mode->phys_base_ptr;
250
 	vesafb.map.green_lsb = mode->green_field_position;
285
 	vesafb.map.green_lsb = mode->green_field_position;
251
 	vesafb.map.blue_lsb = mode->blue_field_position;
286
 	vesafb.map.blue_lsb = mode->blue_field_position;
252
 
287
 
288
+	/* Select this mode */
289
+	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
290
+			       : "=a" ( status )
291
+			       : "a" ( VBE_SET_MODE ),
292
+				 "b" ( mode_number ) );
293
+	if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
294
+		DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n",
295
+		       mode_number, status, strerror ( rc ) );
296
+		return rc;
297
+	}
298
+
253
 	return 0;
299
 	return 0;
254
 }
300
 }
255
 
301
 
256
 /**
302
 /**
257
- * Select and set video mode
303
+ * Select video mode
258
  *
304
  *
259
  * @v mode_numbers	Mode number list (terminated with VBE_MODE_END)
305
  * @v mode_numbers	Mode number list (terminated with VBE_MODE_END)
260
  * @v min_width		Minimum required width (in pixels)
306
  * @v min_width		Minimum required width (in pixels)
261
  * @v min_height	Minimum required height (in pixels)
307
  * @v min_height	Minimum required height (in pixels)
262
  * @v min_bpp		Minimum required colour depth (in bits per pixel)
308
  * @v min_bpp		Minimum required colour depth (in bits per pixel)
263
- * @ret rc		Return status code
309
+ * @ret mode_number	Mode number, or negative error
264
  */
310
  */
265
 static int vesafb_select_mode ( const uint16_t *mode_numbers,
311
 static int vesafb_select_mode ( const uint16_t *mode_numbers,
266
 				unsigned int min_width, unsigned int min_height,
312
 				unsigned int min_width, unsigned int min_height,
267
 				unsigned int min_bpp ) {
313
 				unsigned int min_bpp ) {
268
 	struct vbe_mode_info *mode = &vbe_buf.mode;
314
 	struct vbe_mode_info *mode = &vbe_buf.mode;
315
+	int best_mode_number = -ENOENT;
316
+	unsigned int best_score = INT_MAX;
317
+	unsigned int score;
269
 	uint16_t mode_number;
318
 	uint16_t mode_number;
270
-	uint16_t status;
271
 	int rc;
319
 	int rc;
272
 
320
 
273
 	/* Find the first suitable mode */
321
 	/* Find the first suitable mode */
277
 		mode_number |= VBE_MODE_LINEAR;
325
 		mode_number |= VBE_MODE_LINEAR;
278
 
326
 
279
 		/* Get mode information */
327
 		/* Get mode information */
280
-		__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
281
-				       : "=a" ( status )
282
-				       : "a" ( VBE_MODE_INFO ),
283
-					 "c" ( mode_number ),
284
-					 "D" ( __from_data16 ( mode ) )
285
-				       : "memory" );
286
-		if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
287
-			DBGC ( &vbe_buf, "VESAFB could not get mode %04x "
288
-			       "information: [%04x] %s\n", mode_number,
289
-			       status, strerror ( rc ) );
328
+		if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
290
 			continue;
329
 			continue;
291
-		}
292
-		DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) "
293
-		       "model %02x [x%d]%s%s%s%s%s\n", mode_number,
294
-		       mode->x_resolution, mode->y_resolution,
295
-		       mode->bits_per_pixel, mode->rsvd_mask_size,
296
-		       mode->red_mask_size,  mode->green_mask_size,
297
-		       mode->blue_mask_size, mode->memory_model,
298
-		       ( mode->number_of_image_pages + 1 ),
299
-		       ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
300
-			 "" : " [unsupported]" ),
301
-		       ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
302
-			 " [tty]" : "" ),
303
-		       ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
304
-			 "" : " [text]" ),
305
-		       ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
306
-			 "" : " [nonlinear]" ),
307
-		       ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
308
-			 " [buf]" : "" ) );
309
 
330
 
310
 		/* Skip unusable modes */
331
 		/* Skip unusable modes */
311
 		if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
332
 		if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
325
 			continue;
346
 			continue;
326
 		}
347
 		}
327
 
348
 
328
-		/* Select this mode */
329
-		if ( ( rc = vesafb_set_mode ( mode_number, mode ) ) != 0 )
330
-			return rc;
349
+		/* Select this mode if it has the best (i.e. lowest)
350
+		 * score.  We choose the scoring system to favour
351
+		 * modes close to the specified width and height;
352
+		 * within modes of the same width and height we prefer
353
+		 * a higher colour depth.
354
+		 */
355
+		score = ( ( mode->x_resolution * mode->y_resolution ) -
356
+			  mode->bits_per_pixel );
357
+		if ( score < best_score ) {
358
+			best_mode_number = mode_number;
359
+			best_score = score;
360
+		}
361
+	}
331
 
362
 
332
-		return 0;
363
+	if ( best_mode_number >= 0 ) {
364
+		DBGC ( &vbe_buf, "VESAFB selected mode %04x\n",
365
+		       best_mode_number );
366
+	} else {
367
+		DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" );
333
 	}
368
 	}
334
 
369
 
335
-	DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" );
336
-	return -ENOENT;
370
+	return best_mode_number;
337
 }
371
 }
338
 
372
 
339
 /**
373
 /**
349
 			 unsigned int min_bpp, struct pixel_buffer *pixbuf ) {
383
 			 unsigned int min_bpp, struct pixel_buffer *pixbuf ) {
350
 	uint32_t discard_b;
384
 	uint32_t discard_b;
351
 	uint16_t *mode_numbers;
385
 	uint16_t *mode_numbers;
386
+	int mode_number;
352
 	int rc;
387
 	int rc;
353
 
388
 
354
 	/* Record current VGA mode */
389
 	/* Record current VGA mode */
361
 	if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
396
 	if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
362
 		goto err_mode_list;
397
 		goto err_mode_list;
363
 
398
 
364
-	/* Select and set mode */
365
-	if ( ( rc = vesafb_select_mode ( mode_numbers, min_width, min_height,
366
-					 min_bpp ) ) != 0 )
399
+	/* Select mode */
400
+	if ( ( mode_number = vesafb_select_mode ( mode_numbers, min_width,
401
+						  min_height, min_bpp ) ) < 0 ){
402
+		rc = mode_number;
367
 		goto err_select_mode;
403
 		goto err_select_mode;
404
+	}
405
+
406
+	/* Set mode */
407
+	if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 )
408
+		goto err_set_mode;
368
 
409
 
369
 	/* Get font data */
410
 	/* Get font data */
370
 	vesafb_font();
411
 	vesafb_font();
373
 	fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
414
 	fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
374
 		     &vesafb.pixel, &vesafb.map, &vesafb.font, pixbuf );
415
 		     &vesafb.pixel, &vesafb.map, &vesafb.font, pixbuf );
375
 
416
 
417
+ err_set_mode:
376
  err_select_mode:
418
  err_select_mode:
377
 	free ( mode_numbers );
419
 	free ( mode_numbers );
378
  err_mode_list:
420
  err_mode_list:

Loading…
Cancel
Save