diff mbox

video: fbdev: Add CVT timing calculations.

Message ID 1415746455-28701-1-git-send-email-davidu@nvidia.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Ung Nov. 11, 2014, 10:54 p.m. UTC
Currently fbmon is still relying on the old GTF timings when parsing
the standard timings of the EDID.
This causes problem with some monitor eg DELL U2410 which advertises
high resolutions like 1920x1200@60 and GTF timing gives it 193mhz
clock which is out of spec of the monitor which has dclkmax of 170mhz.
This patch address the above problem by adding support for CVT with
reduced timings.

Signed-off-by: David Ung <davidu@nvidia.com>
---
 drivers/video/fbdev/core/fbmon.c | 92 +++++++++++++++++++++++++++++++++-------
 1 file changed, 77 insertions(+), 15 deletions(-)

Comments

David Ung Nov. 18, 2014, 11:40 p.m. UTC | #1
> -----Original Message-----
> From: David Ung [mailto:davidu@nvidia.com]
> Sent: Tuesday, November 11, 2014 2:54 PM
> To: plagnioj@jcrosoft.com; tomi.valkeinen@ti.com
> Cc: linux-fbdev@vger.kernel.org; David Ung
> Subject: [PATCH] video: fbdev: Add CVT timing calculations.
> 
> Currently fbmon is still relying on the old GTF timings when parsing the
> standard timings of the EDID.
> This causes problem with some monitor eg DELL U2410 which advertises
> high resolutions like 1920x1200@60 and GTF timing gives it 193mhz clock
> which is out of spec of the monitor which has dclkmax of 170mhz.
> This patch address the above problem by adding support for CVT with
> reduced timings.
> 
> Signed-off-by: David Ung <davidu@nvidia.com>

Ping?
--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tomi Valkeinen Nov. 20, 2014, 12:51 p.m. UTC | #2
Hi,

On 12/11/14 00:54, David Ung wrote:
> Currently fbmon is still relying on the old GTF timings when parsing
> the standard timings of the EDID.
> This causes problem with some monitor eg DELL U2410 which advertises
> high resolutions like 1920x1200@60 and GTF timing gives it 193mhz
> clock which is out of spec of the monitor which has dclkmax of 170mhz.
> This patch address the above problem by adding support for CVT with
> reduced timings.

These timings are quite complex, and I don't claim to fully understand
all the details, so I have a few questions:

So you say the monitor has a standard timing for 1920x1200@60. If I read
the EDID standard right, a standard timing entry either has to match a
timing from VESA DMT, or it shall be calculated using GTF.

Don't we have or shouldn't we have a table for the VESA DMT modes, from
which to search for standard timings? 1920x1200@60 should be found there.

> Signed-off-by: David Ung <davidu@nvidia.com>
> ---
>  drivers/video/fbdev/core/fbmon.c | 92 +++++++++++++++++++++++++++++++++-------
>  1 file changed, 77 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
> index 5b0e313..8d4ec9f 100644
> --- a/drivers/video/fbdev/core/fbmon.c
> +++ b/drivers/video/fbdev/core/fbmon.c
> @@ -21,6 +21,11 @@
>   *      GTF Spreadsheet by Andy Morrish (1/5/97)
>   *      available at http://www.vesa.org
>   *
> + * Coordinated Video Timing is derived from:
> + *
> + *      CVT Spreadsheet by Graham Loveridge (9/Apr/2003)
> + *      available at http://www.vesa.org
> + *
>   * This file is subject to the terms and conditions of the GNU General Public
>   * License.  See the file COPYING in the main directory of this archive
>   * for more details.
> @@ -383,17 +388,21 @@ static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
>  }
>  
>  static void calc_mode_timings(int xres, int yres, int refresh,
> -			      struct fb_videomode *mode)
> +			      struct fb_videomode *mode,
> +			      struct fb_monspecs *specs)
>  {
>  	struct fb_var_screeninfo *var;
> +	struct fb_info info;
>  
>  	var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
> +	if (specs)
> +		memcpy(&info.monspecs, specs, sizeof(struct fb_monspecs));
>  
>  	if (var) {
>  		var->xres = xres;
>  		var->yres = yres;
> -		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
> -			    refresh, var, NULL);
> +		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, refresh, var,
> +			    specs && !specs->gtf ? &info : NULL);

This juggling with fb_monspecs, creating a temporary fb_info (and even
leaving most of it uninitialized) doesn't look very nice.

Why allow NULL specs in any case? Don't we always have it?

If the specs is really only used for the GTF flag, could we just pass
that flag, not the whole specs?

>  		mode->xres = xres;
>  		mode->yres = yres;
>  		mode->pixclock = var->pixclock;
> @@ -417,12 +426,12 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
>  
>  	c = block[0];
>  	if (c&0x80) {
> -		calc_mode_timings(720, 400, 70, &mode[num]);
> +		calc_mode_timings(720, 400, 70, &mode[num], NULL);
>  		mode[num++].flag = FB_MODE_IS_CALCULATED;
>  		DPRINTK("      720x400@70Hz\n");
>  	}
>  	if (c&0x40) {
> -		calc_mode_timings(720, 400, 88, &mode[num]);
> +		calc_mode_timings(720, 400, 88, &mode[num], NULL);
>  		mode[num++].flag = FB_MODE_IS_CALCULATED;
>  		DPRINTK("      720x400@88Hz\n");
>  	}
> @@ -431,7 +440,7 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
>  		DPRINTK("      640x480@60Hz\n");
>  	}
>  	if (c&0x10) {
> -		calc_mode_timings(640, 480, 67, &mode[num]);
> +		calc_mode_timings(640, 480, 67, &mode[num], NULL);
>  		mode[num++].flag = FB_MODE_IS_CALCULATED;
>  		DPRINTK("      640x480@67Hz\n");
>  	}
> @@ -462,7 +471,7 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
>  		DPRINTK("      800x600@75Hz\n");
>  	}
>  	if (c&0x20) {
> -		calc_mode_timings(832, 624, 75, &mode[num]);
> +		calc_mode_timings(832, 624, 75, &mode[num], NULL);
>  		mode[num++].flag = FB_MODE_IS_CALCULATED;
>  		DPRINTK("      832x624@75Hz\n");
>  	}
> @@ -496,7 +505,7 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
>  }
>  
>  static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
> -		int ver, int rev)
> +			  int ver, int rev, struct fb_monspecs *specs)
>  {
>  	int xres, yres = 0, refresh, ratio, i;
>  
> @@ -535,7 +544,7 @@ static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
>  			return 1;
>  		}
>  	}
> -	calc_mode_timings(xres, yres, refresh, mode);
> +	calc_mode_timings(xres, yres, refresh, mode, specs);
>  	return 1;
>  }
>  
> @@ -545,7 +554,7 @@ static int get_dst_timing(unsigned char *block,
>  	int j, num = 0;
>  
>  	for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
> -		num += get_std_timing(block, &mode[num], ver, rev);
> +		num += get_std_timing(block, &mode[num], ver, rev, NULL);

Hmm, why is this call using NULL?

>  
>  	return num;
>  }
> @@ -601,7 +610,8 @@ static void get_detailed_timing(unsigned char *block,
>   * This function builds a mode database using the contents of the EDID
>   * data
>   */
> -static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
> +static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize,
> +					     struct fb_monspecs *specs)
>  {
>  	struct fb_videomode *mode, *m;
>  	unsigned char *block;
> @@ -643,7 +653,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
>  	DPRINTK("   Standard Timings\n");
>  	block = edid + STD_TIMING_DESCRIPTIONS_START;
>  	for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
> -		num += get_std_timing(block, &mode[num], ver, rev);
> +		num += get_std_timing(block, &mode[num], ver, rev, specs);
>  
>  	block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
>  	for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
> @@ -707,7 +717,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
>  		int num_modes, hz, hscan, pixclock;
>  		int vtotal, htotal;
>  
> -		modes = fb_create_modedb(edid, &num_modes);
> +		modes = fb_create_modedb(edid, &num_modes, NULL);

Same here.

>  		if (!modes) {
>  			DPRINTK("None Available\n");
>  			return 1;
> @@ -964,7 +974,7 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
>  	DPRINTK("   Display Characteristics:\n");
>  	get_monspecs(edid, specs);
>  
> -	specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
> +	specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs);
>  
>  	/*
>  	 * Workaround for buggy EDIDs that sets that the first
> @@ -1255,6 +1265,55 @@ static void fb_timings_dclk(struct __fb_timings *timings)
>  }
>  
>  /*
> + * All calculations are based on the VESA CVT Spreadsheet
> + * available at VESA's public ftp (http://www.vesa.org).
> + *
> + * The names of variables are kept to match as closely to the names
> + * that appears in the spreadsheet.
> + */
> +#define RB_MIN_V_BLANK  460
> +#define RB_H_BLANK      160
> +#define CELL_GRAN       8
> +#define CLOCK_STEP      250
> +#define RB_V_FPORCH     3
> +#define V_SYNC_RND      6
> +#define MIN_V_BPORCH    6
> +
> +static void fb_timings_vfreq_cvt(struct __fb_timings *timings)
> +{
> +	u32 v_lines_rnd, h_period_est, vbi_line, rb_min_vbi, act_vbi_lines;
> +	u32 total_v_lines, h_pixels_rnd, total_active_pixels, total_pixels;
> +	u32 act_pixel_freq;
> +	u32 v_field_rate_rqd = timings->vfreq;
> +	u32 h_pixels = timings->hactive;
> +	u32 v_lines = timings->vactive;
> +
> +	v_lines_rnd = v_lines;
> +	h_period_est = 1000000 / v_field_rate_rqd - RB_MIN_V_BLANK;
> +	h_period_est = h_period_est / v_lines_rnd;
> +	vbi_line = RB_MIN_V_BLANK / h_period_est;
> +	rb_min_vbi = RB_V_FPORCH + V_SYNC_RND + MIN_V_BPORCH;
> +	if (vbi_line < rb_min_vbi)
> +		act_vbi_lines = rb_min_vbi;
> +	else
> +		act_vbi_lines = vbi_line;
> +	total_v_lines = act_vbi_lines + v_lines_rnd;
> +	h_pixels_rnd = (h_pixels / CELL_GRAN) * CELL_GRAN;
> +	total_active_pixels = h_pixels_rnd;
> +	total_pixels = RB_H_BLANK + total_active_pixels;
> +	act_pixel_freq = v_field_rate_rqd * total_v_lines * total_pixels;
> +	act_pixel_freq /= 1000;
> +	act_pixel_freq = act_pixel_freq / CLOCK_STEP * CLOCK_STEP * 1000;
> +
> +	timings->hfreq = act_pixel_freq / total_pixels;
> +	timings->vblank = act_vbi_lines;
> +	timings->vtotal = timings->vactive + timings->vblank;
> +	timings->hblank = RB_H_BLANK;
> +	timings->htotal = timings->hactive + timings->hblank;
> +	timings->dclk = timings->htotal * timings->hfreq;
> +}

What about fbcvt.c? You add fb_timings_vfreq_cvt function, but it sounds
something which we either have in fbcvt.c, or should be in fbcvt.c.

> +
> +/*
>   * fb_get_mode - calculates video mode using VESA GTF
>   * @flags: if: 0 - maximize vertical refresh rate
>   *             1 - vrefresh-driven calculation;
> @@ -1347,7 +1406,10 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
>  		break;
>  	case FB_VSYNCTIMINGS: /* vrefresh driven */
>  		timings->vfreq = val;
> -		fb_timings_vfreq(timings);
> +		if (info && !info->monspecs.gtf)
> +			fb_timings_vfreq_cvt(timings);
> +		else
> +			fb_timings_vfreq(timings);
>  		break;
>  	case FB_HSYNCTIMINGS: /* hsync driven */
>  		timings->hfreq = val;
> 

With your patch, fb_get_mode() does something else than the comments
say. It explicitly talks about calculating with GTF. Does any of the
callers expect it to use GTF, and won't work with CVT? In the minimum
the comments should be changed to reflect he new behavior.

 Tomi
David Ung Nov. 20, 2014, 9 p.m. UTC | #3
> On 12/11/14 00:54, David Ung wrote:
> > Currently fbmon is still relying on the old GTF timings when parsing
> > the standard timings of the EDID.
> > This causes problem with some monitor eg DELL U2410 which advertises
> > high resolutions like 1920x1200@60 and GTF timing gives it 193mhz
> > clock which is out of spec of the monitor which has dclkmax of 170mhz.
> > This patch address the above problem by adding support for CVT with
> > reduced timings.
> 
> These timings are quite complex, and I don't claim to fully understand all
> the details, so I have a few questions:
> 
> So you say the monitor has a standard timing for 1920x1200@60. If I read the
> EDID standard right, a standard timing entry either has to match a timing
> from VESA DMT, or it shall be calculated using GTF.
> 
> Don't we have or shouldn't we have a table for the VESA DMT modes, from
> which to search for standard timings? 1920x1200@60 should be found there.

Agree, there ought to be a DMT table of modes that it should match against, though this table does not exist currently in fbdev.
After reading your comment, I had a look at the edid again for DELL U2410 and in the standard timing, it defines - (D1, 00)h
According to the DMT document, that matches DMT ID 45h - which the table defines as 193.25mhz pixel clock.

Clearly my patch is wrong in this approach which tries to correct the problem with cvt reduce timing.
The correct approach should be to invalidate this mode since the monspec defines a max clock of 170mhz.

This should make the patch much more simpler.  I shall abandon this patch and post a different one.

David

--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
index 5b0e313..8d4ec9f 100644
--- a/drivers/video/fbdev/core/fbmon.c
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -21,6 +21,11 @@ 
  *      GTF Spreadsheet by Andy Morrish (1/5/97)
  *      available at http://www.vesa.org
  *
+ * Coordinated Video Timing is derived from:
+ *
+ *      CVT Spreadsheet by Graham Loveridge (9/Apr/2003)
+ *      available at http://www.vesa.org
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -383,17 +388,21 @@  static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
 }
 
 static void calc_mode_timings(int xres, int yres, int refresh,
-			      struct fb_videomode *mode)
+			      struct fb_videomode *mode,
+			      struct fb_monspecs *specs)
 {
 	struct fb_var_screeninfo *var;
+	struct fb_info info;
 
 	var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
+	if (specs)
+		memcpy(&info.monspecs, specs, sizeof(struct fb_monspecs));
 
 	if (var) {
 		var->xres = xres;
 		var->yres = yres;
-		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
-			    refresh, var, NULL);
+		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, refresh, var,
+			    specs && !specs->gtf ? &info : NULL);
 		mode->xres = xres;
 		mode->yres = yres;
 		mode->pixclock = var->pixclock;
@@ -417,12 +426,12 @@  static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
 
 	c = block[0];
 	if (c&0x80) {
-		calc_mode_timings(720, 400, 70, &mode[num]);
+		calc_mode_timings(720, 400, 70, &mode[num], NULL);
 		mode[num++].flag = FB_MODE_IS_CALCULATED;
 		DPRINTK("      720x400@70Hz\n");
 	}
 	if (c&0x40) {
-		calc_mode_timings(720, 400, 88, &mode[num]);
+		calc_mode_timings(720, 400, 88, &mode[num], NULL);
 		mode[num++].flag = FB_MODE_IS_CALCULATED;
 		DPRINTK("      720x400@88Hz\n");
 	}
@@ -431,7 +440,7 @@  static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
 		DPRINTK("      640x480@60Hz\n");
 	}
 	if (c&0x10) {
-		calc_mode_timings(640, 480, 67, &mode[num]);
+		calc_mode_timings(640, 480, 67, &mode[num], NULL);
 		mode[num++].flag = FB_MODE_IS_CALCULATED;
 		DPRINTK("      640x480@67Hz\n");
 	}
@@ -462,7 +471,7 @@  static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
 		DPRINTK("      800x600@75Hz\n");
 	}
 	if (c&0x20) {
-		calc_mode_timings(832, 624, 75, &mode[num]);
+		calc_mode_timings(832, 624, 75, &mode[num], NULL);
 		mode[num++].flag = FB_MODE_IS_CALCULATED;
 		DPRINTK("      832x624@75Hz\n");
 	}
@@ -496,7 +505,7 @@  static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
 }
 
 static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
-		int ver, int rev)
+			  int ver, int rev, struct fb_monspecs *specs)
 {
 	int xres, yres = 0, refresh, ratio, i;
 
@@ -535,7 +544,7 @@  static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
 			return 1;
 		}
 	}
-	calc_mode_timings(xres, yres, refresh, mode);
+	calc_mode_timings(xres, yres, refresh, mode, specs);
 	return 1;
 }
 
@@ -545,7 +554,7 @@  static int get_dst_timing(unsigned char *block,
 	int j, num = 0;
 
 	for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
-		num += get_std_timing(block, &mode[num], ver, rev);
+		num += get_std_timing(block, &mode[num], ver, rev, NULL);
 
 	return num;
 }
@@ -601,7 +610,8 @@  static void get_detailed_timing(unsigned char *block,
  * This function builds a mode database using the contents of the EDID
  * data
  */
-static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
+static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize,
+					     struct fb_monspecs *specs)
 {
 	struct fb_videomode *mode, *m;
 	unsigned char *block;
@@ -643,7 +653,7 @@  static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
 	DPRINTK("   Standard Timings\n");
 	block = edid + STD_TIMING_DESCRIPTIONS_START;
 	for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
-		num += get_std_timing(block, &mode[num], ver, rev);
+		num += get_std_timing(block, &mode[num], ver, rev, specs);
 
 	block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 	for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
@@ -707,7 +717,7 @@  static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
 		int num_modes, hz, hscan, pixclock;
 		int vtotal, htotal;
 
-		modes = fb_create_modedb(edid, &num_modes);
+		modes = fb_create_modedb(edid, &num_modes, NULL);
 		if (!modes) {
 			DPRINTK("None Available\n");
 			return 1;
@@ -964,7 +974,7 @@  void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
 	DPRINTK("   Display Characteristics:\n");
 	get_monspecs(edid, specs);
 
-	specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
+	specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs);
 
 	/*
 	 * Workaround for buggy EDIDs that sets that the first
@@ -1255,6 +1265,55 @@  static void fb_timings_dclk(struct __fb_timings *timings)
 }
 
 /*
+ * All calculations are based on the VESA CVT Spreadsheet
+ * available at VESA's public ftp (http://www.vesa.org).
+ *
+ * The names of variables are kept to match as closely to the names
+ * that appears in the spreadsheet.
+ */
+#define RB_MIN_V_BLANK  460
+#define RB_H_BLANK      160
+#define CELL_GRAN       8
+#define CLOCK_STEP      250
+#define RB_V_FPORCH     3
+#define V_SYNC_RND      6
+#define MIN_V_BPORCH    6
+
+static void fb_timings_vfreq_cvt(struct __fb_timings *timings)
+{
+	u32 v_lines_rnd, h_period_est, vbi_line, rb_min_vbi, act_vbi_lines;
+	u32 total_v_lines, h_pixels_rnd, total_active_pixels, total_pixels;
+	u32 act_pixel_freq;
+	u32 v_field_rate_rqd = timings->vfreq;
+	u32 h_pixels = timings->hactive;
+	u32 v_lines = timings->vactive;
+
+	v_lines_rnd = v_lines;
+	h_period_est = 1000000 / v_field_rate_rqd - RB_MIN_V_BLANK;
+	h_period_est = h_period_est / v_lines_rnd;
+	vbi_line = RB_MIN_V_BLANK / h_period_est;
+	rb_min_vbi = RB_V_FPORCH + V_SYNC_RND + MIN_V_BPORCH;
+	if (vbi_line < rb_min_vbi)
+		act_vbi_lines = rb_min_vbi;
+	else
+		act_vbi_lines = vbi_line;
+	total_v_lines = act_vbi_lines + v_lines_rnd;
+	h_pixels_rnd = (h_pixels / CELL_GRAN) * CELL_GRAN;
+	total_active_pixels = h_pixels_rnd;
+	total_pixels = RB_H_BLANK + total_active_pixels;
+	act_pixel_freq = v_field_rate_rqd * total_v_lines * total_pixels;
+	act_pixel_freq /= 1000;
+	act_pixel_freq = act_pixel_freq / CLOCK_STEP * CLOCK_STEP * 1000;
+
+	timings->hfreq = act_pixel_freq / total_pixels;
+	timings->vblank = act_vbi_lines;
+	timings->vtotal = timings->vactive + timings->vblank;
+	timings->hblank = RB_H_BLANK;
+	timings->htotal = timings->hactive + timings->hblank;
+	timings->dclk = timings->htotal * timings->hfreq;
+}
+
+/*
  * fb_get_mode - calculates video mode using VESA GTF
  * @flags: if: 0 - maximize vertical refresh rate
  *             1 - vrefresh-driven calculation;
@@ -1347,7 +1406,10 @@  int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
 		break;
 	case FB_VSYNCTIMINGS: /* vrefresh driven */
 		timings->vfreq = val;
-		fb_timings_vfreq(timings);
+		if (info && !info->monspecs.gtf)
+			fb_timings_vfreq_cvt(timings);
+		else
+			fb_timings_vfreq(timings);
 		break;
 	case FB_HSYNCTIMINGS: /* hsync driven */
 		timings->hfreq = val;