diff options
-rw-r--r-- | drivers/video/ps3fb.c | 158 |
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; | |||
276 | static int ps3fb_cmp_mode(const struct fb_videomode *vmode, | 276 | static 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 | ||
304 | static const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id) | 324 | static 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) | |||
324 | static unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var, | 344 | static 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 | ||
339 | found: | ||
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; |