[PATCH] fbdev: Add VESA Coordinated Video Timings (CVT) support

The Coordinated Video Timings (CVT) is the latest standard approved by VESA
concerning video timings generation.  It addresses the limitation of GTF which
is designed mainly for CRT displays.  CRT's have a high blanking requirement
(as much as 25% of the horizontal frame length) which artificially increases
the pixelclock.  Digital displays, on the other hand, needs to conserve the
pixelclock as much as possible.  The GTF also does not take into account the
different aspect ratios in its calculation.

The new function added is fb_find_mode_cvt().  It is called by fb_find_mode()
if it recognizes a mode option string formatted for CVT.  The format is:

<xres>x<yres>[M][R][-<bpp>][<at-sign><refresh>][i][m]

The 'M' tells the function to calculate using CVT.  On it's own, it will
compute a timing for CRT displays at 60Hz.  If the 'R' is specified, 'reduced
blanking' computation will be used, best for flatpanels.  The 'i' and the 'm'
is for 'interlaced mode' and 'with margins' respectively.

To determine if CVT was used, check for dmesg for something like this:

CVT Mode - <pix>M<n>[-R], ie: .480M3-R  (800x600 reduced blanking)

where: pix - product of xres and yres, in MB
    M   - is a CVT mode
    n   - the aspect ratio (3 - 4:3; 4 - 5:4; 9 - 16:9, 15:9; A - 16:10)
    -R   - reduced blanking

Signed-off-by: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index 3edc9f4..47516c4 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -456,12 +456,22 @@
  *
  *	Valid mode specifiers for @mode_option:
  *
- *	<xres>x<yres>[-<bpp>][@<refresh>] or
+ *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
  *	<name>[-<bpp>][@<refresh>]
  *
  *	with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
  *	<name> a string.
  *
+ *      If 'M' is present after yres (and before refresh/bpp if present),
+ *      the function will compute the timings using VESA(tm) Coordinated
+ *      Video Timings (CVT).  If 'R' is present after 'M', will compute with
+ *      reduced blanking (for flatpanels).  If 'i' is present, compute
+ *      interlaced mode.  If 'm' is present, add margins equal to 1.8%
+ *      of xres rounded down to 8 pixels, and 1.8% of yres. The char
+ *      'i' and 'm' must be after 'M' and 'R'. Example:
+ *
+ *      1024x768MR-8@60m - Reduced blank with margins at 60Hz.
+ *
  *	NOTE: The passed struct @var is _not_ cleared!  This allows you
  *	to supply values for e.g. the grayscale and accel_flags fields.
  *
@@ -495,7 +505,7 @@
 	unsigned int namelen = strlen(name);
 	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
 	unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
-	int yres_specified = 0;
+	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
 	u32 best, diff;
 
 	for (i = namelen-1; i >= 0; i--) {
@@ -506,6 +516,8 @@
 			!yres_specified) {
 			refresh = my_atoi(&name[i+1]);
 			refresh_specified = 1;
+			if (cvt || rb)
+			    cvt = 0;
 		    } else
 			goto done;
 		    break;
@@ -514,6 +526,8 @@
 		    if (!bpp_specified && !yres_specified) {
 			bpp = my_atoi(&name[i+1]);
 			bpp_specified = 1;
+			if (cvt || rb)
+			    cvt = 0;
 		    } else
 			goto done;
 		    break;
@@ -526,6 +540,22 @@
 		    break;
 		case '0'...'9':
 		    break;
+		case 'M':
+		    if (!yres_specified)
+			cvt = 1;
+		    break;
+		case 'R':
+		    if (!cvt)
+			rb = 1;
+		    break;
+		case 'm':
+		    if (!cvt)
+			margins = 1;
+		    break;
+		case 'i':
+		    if (!cvt)
+			interlace = 1;
+		    break;
 		default:
 		    goto done;
 	    }
@@ -535,6 +565,34 @@
 	    res_specified = 1;
 	}
 done:
+	if (cvt) {
+	    struct fb_videomode cvt_mode;
+	    int ret;
+
+	    DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
+		    (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
+		    "", (margins) ? " with margins" : "", (interlace) ?
+		    " interlaced" : "");
+
+	    cvt_mode.xres = xres;
+	    cvt_mode.yres = yres;
+	    cvt_mode.refresh = (refresh) ? refresh : 60;
+
+	    if (interlace)
+		cvt_mode.vmode |= FB_VMODE_INTERLACED;
+	    else
+		cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
+
+	    ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
+
+	    if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
+		DPRINTK("modedb CVT: CVT mode ok\n");
+		return 1;
+	    }
+
+	    DPRINTK("CVT mode invalid, getting mode from database\n");
+	}
+
 	DPRINTK("Trying specified video mode%s %ix%i\n",
 	    refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);