aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2008-02-06 04:39:35 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:17 -0500
commita3665366b0cbf3af1e0949bb9ada9ce63eaaaac1 (patch)
tree3617fcebf52db6a796acb179027dea8b96bb8834 /drivers/video
parent34c422fb2435b1e18b7c36c3310e4f57e21d7ddf (diff)
ps3fb: round up video modes
Round up arbitrary video modes until they fit (if possible) Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: "Antonino A. Daplas" <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/ps3fb.c158
1 files changed, 113 insertions, 45 deletions
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index d61e321dc90b..d552610603b2 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -276,29 +276,49 @@ static char *mode_option __devinitdata;
276static int ps3fb_cmp_mode(const struct fb_videomode *vmode, 276static int ps3fb_cmp_mode(const struct fb_videomode *vmode,
277 const struct fb_var_screeninfo *var) 277 const struct fb_var_screeninfo *var)
278{ 278{
279 /* resolution + black border must match a native resolution */ 279 long xres, yres, left_margin, right_margin, upper_margin, lower_margin;
280 if (vmode->left_margin + vmode->xres + vmode->right_margin != 280 long dx, dy;
281 var->left_margin + var->xres + var->right_margin || 281
282 vmode->upper_margin + vmode->yres + vmode->lower_margin != 282 /* maximum values */
283 var->upper_margin + var->yres + var->lower_margin) 283 if (var->xres > vmode->xres || var->yres > vmode->yres ||
284 var->pixclock > vmode->pixclock ||
285 var->hsync_len > vmode->hsync_len ||
286 var->vsync_len > vmode->vsync_len)
284 return -1; 287 return -1;
285 288
286 /* minimum limits for margins */ 289 /* progressive/interlaced must match */
287 if (vmode->left_margin > var->left_margin || 290 if ((var->vmode & FB_VMODE_MASK) != vmode->vmode)
288 vmode->right_margin > var->right_margin ||
289 vmode->upper_margin > var->upper_margin ||
290 vmode->lower_margin > var->lower_margin)
291 return -1; 291 return -1;
292 292
293 /* these fields must match exactly */ 293 /* minimum resolution */
294 if (vmode->pixclock != var->pixclock || 294 xres = max(var->xres, 1U);
295 vmode->hsync_len != var->hsync_len || 295 yres = max(var->yres, 1U);
296 vmode->vsync_len != var->vsync_len || 296
297 vmode->sync != var->sync || 297 /* minimum margins */
298 vmode->vmode != (var->vmode & FB_VMODE_MASK)) 298 left_margin = max(var->left_margin, vmode->left_margin);
299 right_margin = max(var->right_margin, vmode->right_margin);
300 upper_margin = max(var->upper_margin, vmode->upper_margin);
301 lower_margin = max(var->lower_margin, vmode->lower_margin);
302
303 /* resolution + margins may not exceed native parameters */
304 dx = ((long)vmode->left_margin + (long)vmode->xres +
305 (long)vmode->right_margin) -
306 (left_margin + xres + right_margin);
307 if (dx < 0)
299 return -1; 308 return -1;
300 309
301 return 0; 310 dy = ((long)vmode->upper_margin + (long)vmode->yres +
311 (long)vmode->lower_margin) -
312 (upper_margin + yres + lower_margin);
313 if (dy < 0)
314 return -1;
315
316 /* exact match */
317 if (!dx && !dy)
318 return 0;
319
320 /* resolution difference */
321 return (vmode->xres - xres) * (vmode->yres - yres);
302} 322}
303 323
304static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id) 324static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id)
@@ -324,33 +344,96 @@ static const struct fb_videomode *ps3fb_vmode(int id)
324static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var, 344static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var,
325 u32 *ddr_line_length, u32 *xdr_line_length) 345 u32 *ddr_line_length, u32 *xdr_line_length)
326{ 346{
327 unsigned int id; 347 unsigned int id, best_id;
348 int diff, best_diff;
328 const struct fb_videomode *vmode; 349 const struct fb_videomode *vmode;
350 long gap;
329 351
352 best_id = 0;
353 best_diff = INT_MAX;
354 pr_debug("%s: wanted %u [%u] %u x %u [%u] %u\n", __func__,
355 var->left_margin, var->xres, var->right_margin,
356 var->upper_margin, var->yres, var->lower_margin);
330 for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) { 357 for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) {
331 vmode = ps3fb_native_vmode(id); 358 vmode = ps3fb_native_vmode(id);
332 if (!ps3fb_cmp_mode(vmode, var)) 359 diff = ps3fb_cmp_mode(vmode, var);
333 goto found; 360 pr_debug("%s: mode %u: %u [%u] %u x %u [%u] %u: diff = %d\n",
361 __func__, id, vmode->left_margin, vmode->xres,
362 vmode->right_margin, vmode->upper_margin,
363 vmode->yres, vmode->lower_margin, diff);
364 if (diff < 0)
365 continue;
366 if (diff < best_diff) {
367 best_id = id;
368 if (!diff)
369 break;
370 best_diff = diff;
371 }
334 } 372 }
335 373
336 pr_debug("%s: mode not found\n", __func__); 374 if (!best_id) {
337 return 0; 375 pr_debug("%s: no suitable mode found\n", __func__);
376 return 0;
377 }
378
379 id = best_id;
380 vmode = ps3fb_native_vmode(id);
338 381
339found:
340 *ddr_line_length = vmode->xres * BPP; 382 *ddr_line_length = vmode->xres * BPP;
341 383
342 if (!var->xres) { 384 /* minimum resolution */
385 if (!var->xres)
343 var->xres = 1; 386 var->xres = 1;
344 var->right_margin--; 387 if (!var->yres)
345 }
346 if (!var->yres) {
347 var->yres = 1; 388 var->yres = 1;
348 var->lower_margin--; 389
390 /* minimum virtual resolution */
391 if (var->xres_virtual < var->xres)
392 var->xres_virtual = var->xres;
393 if (var->yres_virtual < var->yres)
394 var->yres_virtual = var->yres;
395
396 /* minimum margins */
397 if (var->left_margin < vmode->left_margin)
398 var->left_margin = vmode->left_margin;
399 if (var->right_margin < vmode->right_margin)
400 var->right_margin = vmode->right_margin;
401 if (var->upper_margin < vmode->upper_margin)
402 var->upper_margin = vmode->upper_margin;
403 if (var->lower_margin < vmode->lower_margin)
404 var->lower_margin = vmode->lower_margin;
405
406 /* extra margins */
407 gap = ((long)vmode->left_margin + (long)vmode->xres +
408 (long)vmode->right_margin) -
409 ((long)var->left_margin + (long)var->xres +
410 (long)var->right_margin);
411 if (gap > 0) {
412 var->left_margin += gap/2;
413 var->right_margin += (gap+1)/2;
414 pr_debug("%s: rounded up H to %u [%u] %u\n", __func__,
415 var->left_margin, var->xres, var->right_margin);
349 } 416 }
350 417
418 gap = ((long)vmode->upper_margin + (long)vmode->yres +
419 (long)vmode->lower_margin) -
420 ((long)var->upper_margin + (long)var->yres +
421 (long)var->lower_margin);
422 if (gap > 0) {
423 var->upper_margin += gap/2;
424 var->lower_margin += (gap+1)/2;
425 pr_debug("%s: rounded up V to %u [%u] %u\n", __func__,
426 var->upper_margin, var->yres, var->lower_margin);
427 }
428
429 /* fixed fields */
430 var->pixclock = vmode->pixclock;
431 var->hsync_len = vmode->hsync_len;
432 var->vsync_len = vmode->vsync_len;
433 var->sync = vmode->sync;
434
351 if (ps3_compare_firmware_version(1, 9, 0) >= 0) { 435 if (ps3_compare_firmware_version(1, 9, 0) >= 0) {
352 *xdr_line_length = GPU_ALIGN_UP(max(var->xres, 436 *xdr_line_length = GPU_ALIGN_UP(var->xres_virtual * BPP);
353 var->xres_virtual) * BPP);
354 if (*xdr_line_length > GPU_MAX_LINE_LENGTH) 437 if (*xdr_line_length > GPU_MAX_LINE_LENGTH)
355 *xdr_line_length = GPU_MAX_LINE_LENGTH; 438 *xdr_line_length = GPU_MAX_LINE_LENGTH;
356 } else 439 } else
@@ -465,22 +548,11 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
465 u32 xdr_line_length, ddr_line_length; 548 u32 xdr_line_length, ddr_line_length;
466 int mode; 549 int mode;
467 550
468 dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres,
469 info->var.xres);
470 dev_dbg(info->device, "var->yres:%u info->var.yres:%u\n", var->yres,
471 info->var.yres);
472
473 /* FIXME For now we do exact matches only */
474 mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length); 551 mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length);
475 if (!mode) 552 if (!mode)
476 return -EINVAL; 553 return -EINVAL;
477 554
478 /* Virtual screen */ 555 /* Virtual screen */
479 if (var->xres_virtual < var->xres)
480 var->xres_virtual = var->xres;
481 if (var->yres_virtual < var->yres)
482 var->yres_virtual = var->yres;
483
484 if (var->xres_virtual > xdr_line_length / BPP) { 556 if (var->xres_virtual > xdr_line_length / BPP) {
485 dev_dbg(info->device, 557 dev_dbg(info->device,
486 "Horizontal virtual screen size too large\n"); 558 "Horizontal virtual screen size too large\n");
@@ -549,10 +621,6 @@ static int ps3fb_set_par(struct fb_info *info)
549 const struct fb_videomode *vmode; 621 const struct fb_videomode *vmode;
550 u64 dst; 622 u64 dst;
551 623
552 dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n",
553 info->var.xres, info->var.xres_virtual,
554 info->var.yres, info->var.yres_virtual, info->var.pixclock);
555
556 mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length); 624 mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length);
557 if (!mode) 625 if (!mode)
558 return -EINVAL; 626 return -EINVAL;