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 10 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,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
27 27
 
28 28
 #include <stdlib.h>
29 29
 #include <errno.h>
30
+#include <limits.h>
30 31
 #include <realmode.h>
31 32
 #include <ipxe/console.h>
32 33
 #include <ipxe/io.h>
@@ -211,27 +212,61 @@ static int vesafb_mode_list ( uint16_t **mode_numbers ) {
211 212
 }
212 213
 
213 214
 /**
214
- * Set video mode
215
+ * Get video mode information
215 216
  *
216 217
  * @v mode_number	Mode number
217
- * @v mode		Mode information
218 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 222
 	uint16_t status;
223 223
 	int rc;
224 224
 
225
-	/* Select this mode */
225
+	/* Get mode information */
226 226
 	__asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
227 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 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 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 271
 	/* Record mode parameters */
237 272
 	vesafb.start = mode->phys_base_ptr;
@@ -250,24 +285,37 @@ static int vesafb_set_mode ( unsigned int mode_number,
250 285
 	vesafb.map.green_lsb = mode->green_field_position;
251 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 299
 	return 0;
254 300
 }
255 301
 
256 302
 /**
257
- * Select and set video mode
303
+ * Select video mode
258 304
  *
259 305
  * @v mode_numbers	Mode number list (terminated with VBE_MODE_END)
260 306
  * @v min_width		Minimum required width (in pixels)
261 307
  * @v min_height	Minimum required height (in pixels)
262 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 311
 static int vesafb_select_mode ( const uint16_t *mode_numbers,
266 312
 				unsigned int min_width, unsigned int min_height,
267 313
 				unsigned int min_bpp ) {
268 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 318
 	uint16_t mode_number;
270
-	uint16_t status;
271 319
 	int rc;
272 320
 
273 321
 	/* Find the first suitable mode */
@@ -277,35 +325,8 @@ static int vesafb_select_mode ( const uint16_t *mode_numbers,
277 325
 		mode_number |= VBE_MODE_LINEAR;
278 326
 
279 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 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 331
 		/* Skip unusable modes */
311 332
 		if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
@@ -325,15 +346,28 @@ static int vesafb_select_mode ( const uint16_t *mode_numbers,
325 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,6 +383,7 @@ static int vesafb_init ( unsigned int min_width, unsigned int min_height,
349 383
 			 unsigned int min_bpp, struct pixel_buffer *pixbuf ) {
350 384
 	uint32_t discard_b;
351 385
 	uint16_t *mode_numbers;
386
+	int mode_number;
352 387
 	int rc;
353 388
 
354 389
 	/* Record current VGA mode */
@@ -361,10 +396,16 @@ static int vesafb_init ( unsigned int min_width, unsigned int min_height,
361 396
 	if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
362 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 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 410
 	/* Get font data */
370 411
 	vesafb_font();
@@ -373,6 +414,7 @@ static int vesafb_init ( unsigned int min_width, unsigned int min_height,
373 414
 	fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
374 415
 		     &vesafb.pixel, &vesafb.map, &vesafb.font, pixbuf );
375 416
 
417
+ err_set_mode:
376 418
  err_select_mode:
377 419
 	free ( mode_numbers );
378 420
  err_mode_list:

Loading…
Cancel
Save