|
@@ -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:
|