Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fbdev: Distinguish between interlaced and progressive modes

I discovered the problem when developing a frame buffer driver for the
PlayStation 2 (not yet merged), using the following video modes for the
PlayStation 3 in drivers/video/fbdev/ps3fb.c:

}, {
/* 1080if */
"1080if", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
}, {
/* 1080pf */
"1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5,
FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
},

In ps3fb_probe, the mode_option module parameter is used with fb_find_mode
but it can only select the interlaced variant of 1920x1080 since the loop
matching the modes does not take the difference between interlaced and
progressive modes into account.

In short, without the patch, progressive 1920x1080 cannot be chosen as a
mode_option parameter since fb_find_mode (falsely) thinks interlace is a
perfect match.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
Cc: "Maciej W. Rozycki" <macro@linux-mips.org>
[b.zolnierkie: updated patch description]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>

authored by

Fredrik Noring and committed by
Bartlomiej Zolnierkiewicz
1ba0a59c 34db50e5

+30 -11
+30 -11
drivers/video/fbdev/core/modedb.c
··· 644 644 * 645 645 * Valid mode specifiers for @mode_option: 646 646 * 647 - * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or 647 + * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][p][m] or 648 648 * <name>[-<bpp>][@<refresh>] 649 649 * 650 650 * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and ··· 653 653 * If 'M' is present after yres (and before refresh/bpp if present), 654 654 * the function will compute the timings using VESA(tm) Coordinated 655 655 * Video Timings (CVT). If 'R' is present after 'M', will compute with 656 - * reduced blanking (for flatpanels). If 'i' is present, compute 657 - * interlaced mode. If 'm' is present, add margins equal to 1.8% 658 - * of xres rounded down to 8 pixels, and 1.8% of yres. The char 659 - * 'i' and 'm' must be after 'M' and 'R'. Example: 656 + * reduced blanking (for flatpanels). If 'i' or 'p' are present, compute 657 + * interlaced or progressive mode. If 'm' is present, add margins equal 658 + * to 1.8% of xres rounded down to 8 pixels, and 1.8% of yres. The chars 659 + * 'i', 'p' and 'm' must be after 'M' and 'R'. Example: 660 660 * 661 661 * 1024x768MR-8@60m - Reduced blank with margins at 60Hz. 662 662 * ··· 697 697 unsigned int namelen = strlen(name); 698 698 int res_specified = 0, bpp_specified = 0, refresh_specified = 0; 699 699 unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0; 700 - int yres_specified = 0, cvt = 0, rb = 0, interlace = 0; 700 + int yres_specified = 0, cvt = 0, rb = 0; 701 + int interlace_specified = 0, interlace = 0; 701 702 int margins = 0; 702 703 u32 best, diff, tdiff; 703 704 ··· 749 748 if (!cvt) 750 749 margins = 1; 751 750 break; 751 + case 'p': 752 + if (!cvt) { 753 + interlace = 0; 754 + interlace_specified = 1; 755 + } 756 + break; 752 757 case 'i': 753 - if (!cvt) 758 + if (!cvt) { 754 759 interlace = 1; 760 + interlace_specified = 1; 761 + } 755 762 break; 756 763 default: 757 764 goto done; ··· 828 819 if ((name_matches(db[i], name, namelen) || 829 820 (res_specified && res_matches(db[i], xres, yres))) && 830 821 !fb_try_mode(var, info, &db[i], bpp)) { 831 - if (refresh_specified && db[i].refresh == refresh) 832 - return 1; 822 + const int db_interlace = (db[i].vmode & 823 + FB_VMODE_INTERLACED ? 1 : 0); 824 + int score = abs(db[i].refresh - refresh); 833 825 834 - if (abs(db[i].refresh - refresh) < diff) { 835 - diff = abs(db[i].refresh - refresh); 826 + if (interlace_specified) 827 + score += abs(db_interlace - interlace); 828 + 829 + if (!interlace_specified || 830 + db_interlace == interlace) 831 + if (refresh_specified && 832 + db[i].refresh == refresh) 833 + return 1; 834 + 835 + if (score < diff) { 836 + diff = score; 836 837 best = i; 837 838 } 838 839 }