2 * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings
4 * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
6 * Based from the VESA(TM) Coordinated Video Timing Generator by
7 * Graham Loveridge April 9, 2003 available at
8 * http://www.vesa.org/public/CVT/CVTd6r1.xls
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file COPYING in the main directory of this archive
16 #include <linux/slab.h>
18 #define FB_CVT_CELLSIZE 8
19 #define FB_CVT_GTF_C 40
20 #define FB_CVT_GTF_J 20
21 #define FB_CVT_GTF_K 128
22 #define FB_CVT_GTF_M 600
23 #define FB_CVT_MIN_VSYNC_BP 550
24 #define FB_CVT_MIN_VPORCH 3
25 #define FB_CVT_MIN_BPORCH 6
27 #define FB_CVT_RB_MIN_VBLANK 460
28 #define FB_CVT_RB_HBLANK 160
29 #define FB_CVT_RB_V_FPORCH 3
31 #define FB_CVT_FLAG_REDUCED_BLANK 1
32 #define FB_CVT_FLAG_MARGINS 2
33 #define FB_CVT_FLAG_INTERLACED 4
61 static const unsigned char fb_cvt_vbi_tab
[] = {
72 /* returns hperiod * 1000 */
73 static u32
fb_cvt_hperiod(struct fb_cvt_data
*cvt
)
75 u32 num
= 1000000000/cvt
->f_refresh
;
78 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
79 num
-= FB_CVT_RB_MIN_VBLANK
* 1000;
80 den
= 2 * (cvt
->yres
/cvt
->interlace
+ 2 * cvt
->v_margin
);
82 num
-= FB_CVT_MIN_VSYNC_BP
* 1000;
83 den
= 2 * (cvt
->yres
/cvt
->interlace
+ cvt
->v_margin
* 2
84 + FB_CVT_MIN_VPORCH
+ cvt
->interlace
/2);
90 /* returns ideal duty cycle * 1000 */
91 static u32
fb_cvt_ideal_duty_cycle(struct fb_cvt_data
*cvt
)
93 u32 c_prime
= (FB_CVT_GTF_C
- FB_CVT_GTF_J
) *
94 (FB_CVT_GTF_K
) + 256 * FB_CVT_GTF_J
;
95 u32 m_prime
= (FB_CVT_GTF_K
* FB_CVT_GTF_M
);
96 u32 h_period_est
= cvt
->hperiod
;
98 return (1000 * c_prime
- ((m_prime
* h_period_est
)/1000))/256;
101 static u32
fb_cvt_hblank(struct fb_cvt_data
*cvt
)
105 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
106 hblank
= FB_CVT_RB_HBLANK
;
108 u32 ideal_duty_cycle
= fb_cvt_ideal_duty_cycle(cvt
);
109 u32 active_pixels
= cvt
->active_pixels
;
111 if (ideal_duty_cycle
< 20000)
112 hblank
= (active_pixels
* 20000)/
115 hblank
= (active_pixels
* ideal_duty_cycle
)/
116 (100000 - ideal_duty_cycle
);
120 hblank
&= ~((2 * FB_CVT_CELLSIZE
) - 1);
125 static u32
fb_cvt_hsync(struct fb_cvt_data
*cvt
)
129 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
132 hsync
= (FB_CVT_CELLSIZE
* cvt
->htotal
)/100;
134 hsync
&= ~(FB_CVT_CELLSIZE
- 1);
138 static u32
fb_cvt_vbi_lines(struct fb_cvt_data
*cvt
)
140 u32 vbi_lines
, min_vbi_lines
, act_vbi_lines
;
142 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
143 vbi_lines
= (1000 * FB_CVT_RB_MIN_VBLANK
)/cvt
->hperiod
+ 1;
144 min_vbi_lines
= FB_CVT_RB_V_FPORCH
+ cvt
->vsync
+
148 vbi_lines
= (FB_CVT_MIN_VSYNC_BP
* 1000)/cvt
->hperiod
+ 1 +
150 min_vbi_lines
= cvt
->vsync
+ FB_CVT_MIN_BPORCH
+
154 if (vbi_lines
< min_vbi_lines
)
155 act_vbi_lines
= min_vbi_lines
;
157 act_vbi_lines
= vbi_lines
;
159 return act_vbi_lines
;
162 static u32
fb_cvt_vtotal(struct fb_cvt_data
*cvt
)
164 u32 vtotal
= cvt
->yres
/cvt
->interlace
;
166 vtotal
+= 2 * cvt
->v_margin
+ cvt
->interlace
/2 + fb_cvt_vbi_lines(cvt
);
167 vtotal
|= cvt
->interlace
/2;
172 static u32
fb_cvt_pixclock(struct fb_cvt_data
*cvt
)
176 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
177 pixclock
= (cvt
->f_refresh
* cvt
->vtotal
* cvt
->htotal
)/1000;
179 pixclock
= (cvt
->htotal
* 1000000)/cvt
->hperiod
;
188 static u32
fb_cvt_aspect_ratio(struct fb_cvt_data
*cvt
)
190 u32 xres
= cvt
->xres
;
191 u32 yres
= cvt
->yres
;
194 if (xres
== (yres
* 4)/3 && !((yres
* 4) % 3))
196 else if (xres
== (yres
* 16)/9 && !((yres
* 16) % 9))
198 else if (xres
== (yres
* 16)/10 && !((yres
* 16) % 10))
200 else if (xres
== (yres
* 5)/4 && !((yres
* 5) % 4))
202 else if (xres
== (yres
* 15)/9 && !((yres
* 15) % 9))
205 printk(KERN_INFO
"fbcvt: Aspect ratio not CVT "
214 static void fb_cvt_print_name(struct fb_cvt_data
*cvt
)
216 u32 pixcount
, pixcount_mod
;
217 int cnt
= 255, offset
= 0, read
= 0;
218 u8
*buf
= kzalloc(256, GFP_KERNEL
);
223 pixcount
= (cvt
->xres
* (cvt
->yres
/cvt
->interlace
))/1000000;
224 pixcount_mod
= (cvt
->xres
* (cvt
->yres
/cvt
->interlace
)) % 1000000;
225 pixcount_mod
/= 1000;
227 read
= snprintf(buf
+offset
, cnt
, "fbcvt: %dx%d@%d: CVT Name - ",
228 cvt
->xres
, cvt
->yres
, cvt
->refresh
);
233 snprintf(buf
+offset
, cnt
, "Not a CVT standard - %d.%03d Mega "
234 "Pixel Image\n", pixcount
, pixcount_mod
);
237 read
= snprintf(buf
+offset
, cnt
, "%d", pixcount
);
242 read
= snprintf(buf
+offset
, cnt
, ".%03dM", pixcount_mod
);
246 if (cvt
->aspect_ratio
== 0)
247 read
= snprintf(buf
+offset
, cnt
, "3");
248 else if (cvt
->aspect_ratio
== 3)
249 read
= snprintf(buf
+offset
, cnt
, "4");
250 else if (cvt
->aspect_ratio
== 1 || cvt
->aspect_ratio
== 4)
251 read
= snprintf(buf
+offset
, cnt
, "9");
252 else if (cvt
->aspect_ratio
== 2)
253 read
= snprintf(buf
+offset
, cnt
, "A");
259 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
260 read
= snprintf(buf
+offset
, cnt
, "-R");
266 printk(KERN_INFO
"%s\n", buf
);
270 static void fb_cvt_convert_to_mode(struct fb_cvt_data
*cvt
,
271 struct fb_videomode
*mode
)
273 mode
->refresh
= cvt
->f_refresh
;
274 mode
->pixclock
= KHZ2PICOS(cvt
->pixclock
/1000);
275 mode
->left_margin
= cvt
->h_back_porch
;
276 mode
->right_margin
= cvt
->h_front_porch
;
277 mode
->hsync_len
= cvt
->hsync
;
278 mode
->upper_margin
= cvt
->v_back_porch
;
279 mode
->lower_margin
= cvt
->v_front_porch
;
280 mode
->vsync_len
= cvt
->vsync
;
282 mode
->sync
&= ~(FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
);
284 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
285 mode
->sync
|= FB_SYNC_HOR_HIGH_ACT
;
287 mode
->sync
|= FB_SYNC_VERT_HIGH_ACT
;
291 * fb_find_mode_cvt - calculate mode using VESA(TM) CVT
292 * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be
293 * pre-filled with the desired values
294 * @margins: add margin to calculation (1.8% of xres and yres)
295 * @rb: compute with reduced blanking (for flatpanels)
299 * @mode is filled with computed values. If interlaced, the refresh field
300 * will be filled with the field rate (2x the frame rate)
303 * Computes video timings using VESA(TM) Coordinated Video Timings
305 int fb_find_mode_cvt(struct fb_videomode
*mode
, int margins
, int rb
)
307 struct fb_cvt_data cvt
;
309 memset(&cvt
, 0, sizeof(cvt
));
312 cvt
.flags
|= FB_CVT_FLAG_MARGINS
;
315 cvt
.flags
|= FB_CVT_FLAG_REDUCED_BLANK
;
317 if (mode
->vmode
& FB_VMODE_INTERLACED
)
318 cvt
.flags
|= FB_CVT_FLAG_INTERLACED
;
320 cvt
.xres
= mode
->xres
;
321 cvt
.yres
= mode
->yres
;
322 cvt
.refresh
= mode
->refresh
;
323 cvt
.f_refresh
= cvt
.refresh
;
326 if (!cvt
.xres
|| !cvt
.yres
|| !cvt
.refresh
) {
327 printk(KERN_INFO
"fbcvt: Invalid input parameters\n");
331 if (!(cvt
.refresh
== 50 || cvt
.refresh
== 60 || cvt
.refresh
== 70 ||
332 cvt
.refresh
== 85)) {
333 printk(KERN_INFO
"fbcvt: Refresh rate not CVT "
338 cvt
.xres
&= ~(FB_CVT_CELLSIZE
- 1);
340 if (cvt
.flags
& FB_CVT_FLAG_INTERLACED
) {
345 if (cvt
.flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
346 if (cvt
.refresh
!= 60) {
347 printk(KERN_INFO
"fbcvt: 60Hz refresh rate "
348 "advised for reduced blanking\n");
353 if (cvt
.flags
& FB_CVT_FLAG_MARGINS
) {
354 cvt
.h_margin
= (cvt
.xres
* 18)/1000;
355 cvt
.h_margin
&= ~(FB_CVT_CELLSIZE
- 1);
356 cvt
.v_margin
= ((cvt
.yres
/cvt
.interlace
)* 18)/1000;
359 cvt
.aspect_ratio
= fb_cvt_aspect_ratio(&cvt
);
360 cvt
.active_pixels
= cvt
.xres
+ 2 * cvt
.h_margin
;
361 cvt
.hperiod
= fb_cvt_hperiod(&cvt
);
362 cvt
.vsync
= fb_cvt_vbi_tab
[cvt
.aspect_ratio
];
363 cvt
.vtotal
= fb_cvt_vtotal(&cvt
);
364 cvt
.hblank
= fb_cvt_hblank(&cvt
);
365 cvt
.htotal
= cvt
.active_pixels
+ cvt
.hblank
;
366 cvt
.hsync
= fb_cvt_hsync(&cvt
);
367 cvt
.pixclock
= fb_cvt_pixclock(&cvt
);
368 cvt
.hfreq
= cvt
.pixclock
/cvt
.htotal
;
369 cvt
.h_back_porch
= cvt
.hblank
/2 + cvt
.h_margin
;
370 cvt
.h_front_porch
= cvt
.hblank
- cvt
.hsync
- cvt
.h_back_porch
+
372 cvt
.v_back_porch
= 3 + cvt
.v_margin
;
373 cvt
.v_front_porch
= cvt
.vtotal
- cvt
.yres
/cvt
.interlace
-
374 cvt
.v_back_porch
- cvt
.vsync
;
375 fb_cvt_print_name(&cvt
);
376 fb_cvt_convert_to_mode(&cvt
, mode
);