aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/video/ps3fb.c101
1 files changed, 60 insertions, 41 deletions
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index 64af173d18f9..b3463ddcfd60 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -55,6 +55,7 @@
55#define GPU_CMD_BUF_SIZE (64 * 1024) 55#define GPU_CMD_BUF_SIZE (64 * 1024)
56#define GPU_IOIF (0x0d000000UL) 56#define GPU_IOIF (0x0d000000UL)
57#define GPU_ALIGN_UP(x) _ALIGN_UP((x), 64) 57#define GPU_ALIGN_UP(x) _ALIGN_UP((x), 64)
58#define GPU_MAX_LINE_LENGTH (65536 - 64)
58 59
59#define PS3FB_FULL_MODE_BIT 0x80 60#define PS3FB_FULL_MODE_BIT 0x80
60 61
@@ -335,7 +336,7 @@ static int ps3fb_get_res_table(u32 xres, u32 yres, int mode)
335} 336}
336 337
337static unsigned int ps3fb_find_mode(const struct fb_var_screeninfo *var, 338static unsigned int ps3fb_find_mode(const struct fb_var_screeninfo *var,
338 u32 *line_length) 339 u32 *ddr_line_length, u32 *xdr_line_length)
339{ 340{
340 unsigned int i, mode; 341 unsigned int i, mode;
341 342
@@ -350,19 +351,30 @@ static unsigned int ps3fb_find_mode(const struct fb_var_screeninfo *var,
350 var->upper_margin == ps3fb_modedb[i].upper_margin && 351 var->upper_margin == ps3fb_modedb[i].upper_margin &&
351 var->lower_margin == ps3fb_modedb[i].lower_margin && 352 var->lower_margin == ps3fb_modedb[i].lower_margin &&
352 var->sync == ps3fb_modedb[i].sync && 353 var->sync == ps3fb_modedb[i].sync &&
353 (var->vmode & FB_VMODE_MASK) == ps3fb_modedb[i].vmode) { 354 (var->vmode & FB_VMODE_MASK) == ps3fb_modedb[i].vmode)
354 /* Cropped broadcast modes use the full line_length */ 355 goto found;
355 *line_length =
356 ps3fb_modedb[i < 10 ? i + 13 : i].xres * 4;
357 /* Full broadcast modes have the full mode bit set */
358 mode = i > 12 ? (i - 12) | PS3FB_FULL_MODE_BIT : i + 1;
359
360 pr_debug("ps3fb_find_mode: mode %u\n", mode);
361 return mode;
362 }
363 356
364 pr_debug("ps3fb_find_mode: mode not found\n"); 357 pr_debug("ps3fb_find_mode: mode not found\n");
365 return 0; 358 return 0;
359
360found:
361 /* Cropped broadcast modes use the full line length */
362 *ddr_line_length = ps3fb_modedb[i < 10 ? i + 13 : i].xres * BPP;
363
364 if (ps3_compare_firmware_version(1, 9, 0) >= 0) {
365 *xdr_line_length = GPU_ALIGN_UP(max(var->xres,
366 var->xres_virtual) * BPP);
367 if (*xdr_line_length > GPU_MAX_LINE_LENGTH)
368 *xdr_line_length = GPU_MAX_LINE_LENGTH;
369 } else
370 *xdr_line_length = *ddr_line_length;
371
372 /* Full broadcast modes have the full mode bit set */
373 mode = i > 12 ? (i - 12) | PS3FB_FULL_MODE_BIT : i + 1;
374
375 pr_debug("ps3fb_find_mode: mode %u\n", mode);
376
377 return mode;
366} 378}
367 379
368static const struct fb_videomode *ps3fb_default_mode(int id) 380static const struct fb_videomode *ps3fb_default_mode(int id)
@@ -385,9 +397,15 @@ static const struct fb_videomode *ps3fb_default_mode(int id)
385 397
386static void ps3fb_sync_image(struct device *dev, u64 frame_offset, 398static void ps3fb_sync_image(struct device *dev, u64 frame_offset,
387 u64 dst_offset, u64 src_offset, u32 width, 399 u64 dst_offset, u64 src_offset, u32 width,
388 u32 height, u64 line_length) 400 u32 height, u32 dst_line_length,
401 u32 src_line_length)
389{ 402{
390 int status; 403 int status;
404 u64 line_length;
405
406 line_length = dst_line_length;
407 if (src_line_length != dst_line_length)
408 line_length |= (u64)src_line_length << 32;
391 409
392 status = lv1_gpu_context_attribute(ps3fb.context_handle, 410 status = lv1_gpu_context_attribute(ps3fb.context_handle,
393 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 411 L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
@@ -421,8 +439,8 @@ static int ps3fb_sync(struct fb_info *info, u32 frame)
421{ 439{
422 struct ps3fb_par *par = info->par; 440 struct ps3fb_par *par = info->par;
423 int i, error = 0; 441 int i, error = 0;
424 u32 xres, yres; 442 u32 ddr_line_length, xdr_line_length;
425 u64 line_length, base; 443 u64 ddr_base, xdr_base;
426 444
427 acquire_console_sem(); 445 acquire_console_sem();
428 446
@@ -434,15 +452,15 @@ static int ps3fb_sync(struct fb_info *info, u32 frame)
434 } 452 }
435 453
436 i = par->res_index; 454 i = par->res_index;
437 xres = ps3fb_res[i].xres; 455 xdr_line_length = info->fix.line_length;
438 yres = ps3fb_res[i].yres; 456 ddr_line_length = ps3fb_res[i].xres * BPP;
439 457 xdr_base = frame * info->var.yres_virtual * xdr_line_length;
440 line_length = xres * BPP; 458 ddr_base = frame * ps3fb_res[i].yres * ddr_line_length;
441 base = frame * yres * line_length;
442 459
443 ps3fb_sync_image(info->device, base + par->full_offset, 460 ps3fb_sync_image(info->device, ddr_base + par->full_offset,
444 base + par->fb_offset, base + par->pan_offset, 461 ddr_base + par->fb_offset, xdr_base + par->pan_offset,
445 par->width, par->height, line_length); 462 par->width, par->height, ddr_line_length,
463 xdr_line_length);
446 464
447out: 465out:
448 release_console_sem(); 466 release_console_sem();
@@ -476,7 +494,7 @@ static int ps3fb_release(struct fb_info *info, int user)
476 494
477static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 495static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
478{ 496{
479 u32 line_length; 497 u32 xdr_line_length, ddr_line_length;
480 int mode; 498 int mode;
481 499
482 dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres, 500 dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres,
@@ -485,7 +503,7 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
485 info->var.yres); 503 info->var.yres);
486 504
487 /* FIXME For now we do exact matches only */ 505 /* FIXME For now we do exact matches only */
488 mode = ps3fb_find_mode(var, &line_length); 506 mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length);
489 if (!mode) 507 if (!mode)
490 return -EINVAL; 508 return -EINVAL;
491 509
@@ -495,7 +513,7 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
495 if (var->yres_virtual < var->yres) 513 if (var->yres_virtual < var->yres)
496 var->yres_virtual = var->yres; 514 var->yres_virtual = var->yres;
497 515
498 if (var->xres_virtual > line_length / BPP) { 516 if (var->xres_virtual > xdr_line_length / BPP) {
499 dev_dbg(info->device, 517 dev_dbg(info->device,
500 "Horizontal virtual screen size too large\n"); 518 "Horizontal virtual screen size too large\n");
501 return -EINVAL; 519 return -EINVAL;
@@ -540,7 +558,7 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
540 } 558 }
541 559
542 /* Memory limit */ 560 /* Memory limit */
543 if (var->yres_virtual * line_length > ps3fb.xdr_size) { 561 if (var->yres_virtual * xdr_line_length > ps3fb.xdr_size) {
544 dev_dbg(info->device, "Not enough memory\n"); 562 dev_dbg(info->device, "Not enough memory\n");
545 return -ENOMEM; 563 return -ENOMEM;
546 } 564 }
@@ -558,15 +576,16 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
558static int ps3fb_set_par(struct fb_info *info) 576static int ps3fb_set_par(struct fb_info *info)
559{ 577{
560 struct ps3fb_par *par = info->par; 578 struct ps3fb_par *par = info->par;
561 unsigned int mode, line_length, lines, maxlines; 579 unsigned int mode, ddr_line_length, xdr_line_length, lines, maxlines;
562 int i; 580 int i;
563 unsigned long offset, dst; 581 unsigned long offset;
582 u64 dst;
564 583
565 dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n", 584 dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n",
566 info->var.xres, info->var.xres_virtual, 585 info->var.xres, info->var.xres_virtual,
567 info->var.yres, info->var.yres_virtual, info->var.pixclock); 586 info->var.yres, info->var.yres_virtual, info->var.pixclock);
568 587
569 mode = ps3fb_find_mode(&info->var, &line_length); 588 mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length);
570 if (!mode) 589 if (!mode)
571 return -EINVAL; 590 return -EINVAL;
572 591
@@ -577,12 +596,13 @@ static int ps3fb_set_par(struct fb_info *info)
577 info->fix.smem_len = ps3fb.xdr_size; 596 info->fix.smem_len = ps3fb.xdr_size;
578 info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; 597 info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
579 info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; 598 info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
580 info->fix.line_length = line_length; 599 info->fix.line_length = xdr_line_length;
581 600
582 info->screen_base = (char __iomem *)ps3fb.xdr_ea; 601 info->screen_base = (char __iomem *)ps3fb.xdr_ea;
583 602
584 par->num_frames = info->fix.smem_len/ 603 par->num_frames = ps3fb.xdr_size /
585 (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP); 604 max(ps3fb_res[i].yres * ddr_line_length,
605 info->var.yres_virtual * xdr_line_length);
586 606
587 /* Keep the special bits we cannot set using fb_var_screeninfo */ 607 /* Keep the special bits we cannot set using fb_var_screeninfo */
588 par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode; 608 par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode;
@@ -592,7 +612,7 @@ static int ps3fb_set_par(struct fb_info *info)
592 offset = VP_OFF(i); 612 offset = VP_OFF(i);
593 par->fb_offset = GPU_ALIGN_UP(offset); 613 par->fb_offset = GPU_ALIGN_UP(offset);
594 par->full_offset = par->fb_offset - offset; 614 par->full_offset = par->fb_offset - offset;
595 par->pan_offset = info->var.yoffset * line_length + 615 par->pan_offset = info->var.yoffset * xdr_line_length +
596 info->var.xoffset * BPP; 616 info->var.xoffset * BPP;
597 617
598 if (par->new_mode_id != par->mode_id) { 618 if (par->new_mode_id != par->mode_id) {
@@ -610,11 +630,11 @@ static int ps3fb_set_par(struct fb_info *info)
610 lines = ps3fb_res[i].yres * par->num_frames; 630 lines = ps3fb_res[i].yres * par->num_frames;
611 if (par->full_offset) 631 if (par->full_offset)
612 lines++; 632 lines++;
613 maxlines = ps3fb.xdr_size / line_length; 633 maxlines = ps3fb.xdr_size / ddr_line_length;
614 for (dst = 0; lines; dst += maxlines * line_length) { 634 for (dst = 0; lines; dst += maxlines * ddr_line_length) {
615 unsigned int l = min(lines, maxlines); 635 unsigned int l = min(lines, maxlines);
616 ps3fb_sync_image(info->device, 0, dst, 0, ps3fb_res[i].xres, l, 636 ps3fb_sync_image(info->device, 0, dst, 0, ps3fb_res[i].xres, l,
617 line_length); 637 ddr_line_length, ddr_line_length);
618 lines -= l; 638 lines -= l;
619 } 639 }
620 640
@@ -816,12 +836,11 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd,
816 { 836 {
817 struct ps3fb_par *par = info->par; 837 struct ps3fb_par *par = info->par;
818 struct ps3fb_ioctl_res res; 838 struct ps3fb_ioctl_res res;
819 int i = par->res_index;
820 dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n"); 839 dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n");
821 res.xres = ps3fb_res[i].xres; 840 res.xres = info->fix.line_length / BPP;
822 res.yres = ps3fb_res[i].yres; 841 res.yres = info->var.yres_virtual;
823 res.xoff = ps3fb_res[i].xoff; 842 res.xoff = (res.xres - info->var.xres) / 2;
824 res.yoff = ps3fb_res[i].yoff; 843 res.yoff = (res.yres - info->var.yres) / 2;
825 res.num_frames = par->num_frames; 844 res.num_frames = par->num_frames;
826 if (!copy_to_user(argp, &res, sizeof(res))) 845 if (!copy_to_user(argp, &res, sizeof(res)))
827 retval = 0; 846 retval = 0;