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
17 #define FB_CVT_CELLSIZE 8
18 #define FB_CVT_GTF_C 40
19 #define FB_CVT_GTF_J 20
20 #define FB_CVT_GTF_K 128
21 #define FB_CVT_GTF_M 600
22 #define FB_CVT_MIN_VSYNC_BP 550
23 #define FB_CVT_MIN_VPORCH 3
24 #define FB_CVT_MIN_BPORCH 6
26 #define FB_CVT_RB_MIN_VBLANK 460
27 #define FB_CVT_RB_HBLANK 160
28 #define FB_CVT_RB_V_FPORCH 3
30 #define FB_CVT_FLAG_REDUCED_BLANK 1
31 #define FB_CVT_FLAG_MARGINS 2
32 #define FB_CVT_FLAG_INTERLACED 4
60 static int fb_cvt_vbi_tab
[] = {
71 /* returns hperiod * 1000 */
72 static u32
fb_cvt_hperiod(struct fb_cvt_data
*cvt
)
74 u32 num
= 1000000000/cvt
->f_refresh
;
77 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
78 num
-= FB_CVT_RB_MIN_VBLANK
* 1000;
79 den
= 2 * (cvt
->yres
/cvt
->interlace
+ 2 * cvt
->v_margin
);
81 num
-= FB_CVT_MIN_VSYNC_BP
* 1000;
82 den
= 2 * (cvt
->yres
/cvt
->interlace
+ cvt
->v_margin
* 2
83 + FB_CVT_MIN_VPORCH
+ cvt
->interlace
/2);
89 /* returns ideal duty cycle * 1000 */
90 static u32
fb_cvt_ideal_duty_cycle(struct fb_cvt_data
*cvt
)
92 u32 c_prime
= (FB_CVT_GTF_C
- FB_CVT_GTF_J
) *
93 (FB_CVT_GTF_K
) + 256 * FB_CVT_GTF_J
;
94 u32 m_prime
= (FB_CVT_GTF_K
* FB_CVT_GTF_M
);
95 u32 h_period_est
= cvt
->hperiod
;
97 return (1000 * c_prime
- ((m_prime
* h_period_est
)/1000))/256;
100 static u32
fb_cvt_hblank(struct fb_cvt_data
*cvt
)
104 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
105 hblank
= FB_CVT_RB_HBLANK
;
107 u32 ideal_duty_cycle
= fb_cvt_ideal_duty_cycle(cvt
);
108 u32 active_pixels
= cvt
->active_pixels
;
110 if (ideal_duty_cycle
< 20000)
111 hblank
= (active_pixels
* 20000)/
114 hblank
= (active_pixels
* ideal_duty_cycle
)/
115 (100000 - ideal_duty_cycle
);
119 hblank
&= ~((2 * FB_CVT_CELLSIZE
) - 1);
124 static u32
fb_cvt_hsync(struct fb_cvt_data
*cvt
)
128 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
131 hsync
= (FB_CVT_CELLSIZE
* cvt
->htotal
)/100;
133 hsync
&= ~(FB_CVT_CELLSIZE
- 1);
137 static u32
fb_cvt_vbi_lines(struct fb_cvt_data
*cvt
)
139 u32 vbi_lines
, min_vbi_lines
, act_vbi_lines
;
141 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
142 vbi_lines
= (1000 * FB_CVT_RB_MIN_VBLANK
)/cvt
->hperiod
+ 1;
143 min_vbi_lines
= FB_CVT_RB_V_FPORCH
+ cvt
->vsync
+
147 vbi_lines
= (FB_CVT_MIN_VSYNC_BP
* 1000)/cvt
->hperiod
+ 1 +
149 min_vbi_lines
= cvt
->vsync
+ FB_CVT_MIN_BPORCH
+
153 if (vbi_lines
< min_vbi_lines
)
154 act_vbi_lines
= min_vbi_lines
;
156 act_vbi_lines
= vbi_lines
;
158 return act_vbi_lines
;
161 static u32
fb_cvt_vtotal(struct fb_cvt_data
*cvt
)
163 u32 vtotal
= cvt
->yres
/cvt
->interlace
;
165 vtotal
+= 2 * cvt
->v_margin
+ cvt
->interlace
/2 + fb_cvt_vbi_lines(cvt
);
166 vtotal
|= cvt
->interlace
/2;
171 static u32
fb_cvt_pixclock(struct fb_cvt_data
*cvt
)
175 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
176 pixclock
= (cvt
->f_refresh
* cvt
->vtotal
* cvt
->htotal
)/1000;
178 pixclock
= (cvt
->htotal
* 1000000)/cvt
->hperiod
;
187 static u32
fb_cvt_aspect_ratio(struct fb_cvt_data
*cvt
)
189 u32 xres
= cvt
->xres
;
190 u32 yres
= cvt
->yres
;
193 if (xres
== (yres
* 4)/3 && !((yres
* 4) % 3))
195 else if (xres
== (yres
* 16)/9 && !((yres
* 16) % 9))
197 else if (xres
== (yres
* 16)/10 && !((yres
* 16) % 10))
199 else if (xres
== (yres
* 5)/4 && !((yres
* 5) % 4))
201 else if (xres
== (yres
* 15)/9 && !((yres
* 15) % 9))
204 printk(KERN_INFO
"fbcvt: Aspect ratio not CVT "
213 static void fb_cvt_print_name(struct fb_cvt_data
*cvt
)
215 u32 pixcount
, pixcount_mod
;
216 int cnt
= 255, offset
= 0, read
= 0;
217 u8
*buf
= kzalloc(256, GFP_KERNEL
);
222 pixcount
= (cvt
->xres
* (cvt
->yres
/cvt
->interlace
))/1000000;
223 pixcount_mod
= (cvt
->xres
* (cvt
->yres
/cvt
->interlace
)) % 1000000;
224 pixcount_mod
/= 1000;
226 read
= snprintf(buf
+offset
, cnt
, "fbcvt: %dx%d@%d: CVT Name - ",
227 cvt
->xres
, cvt
->yres
, cvt
->refresh
);
232 snprintf(buf
+offset
, cnt
, "Not a CVT standard - %d.%03d Mega "
233 "Pixel Image\n", pixcount
, pixcount_mod
);
236 read
= snprintf(buf
+offset
, cnt
, "%d", pixcount
);
241 read
= snprintf(buf
+offset
, cnt
, ".%03dM", pixcount_mod
);
245 if (cvt
->aspect_ratio
== 0)
246 read
= snprintf(buf
+offset
, cnt
, "3");
247 else if (cvt
->aspect_ratio
== 3)
248 read
= snprintf(buf
+offset
, cnt
, "4");
249 else if (cvt
->aspect_ratio
== 1 || cvt
->aspect_ratio
== 4)
250 read
= snprintf(buf
+offset
, cnt
, "9");
251 else if (cvt
->aspect_ratio
== 2)
252 read
= snprintf(buf
+offset
, cnt
, "A");
258 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
259 read
= snprintf(buf
+offset
, cnt
, "-R");
265 printk(KERN_INFO
"%s\n", buf
);
269 static void fb_cvt_convert_to_mode(struct fb_cvt_data
*cvt
,
270 struct fb_videomode
*mode
)
272 mode
->refresh
= cvt
->f_refresh
;
273 mode
->pixclock
= KHZ2PICOS(cvt
->pixclock
/1000);
274 mode
->left_margin
= cvt
->h_back_porch
;
275 mode
->right_margin
= cvt
->h_front_porch
;
276 mode
->hsync_len
= cvt
->hsync
;
277 mode
->upper_margin
= cvt
->v_back_porch
;
278 mode
->lower_margin
= cvt
->v_front_porch
;
279 mode
->vsync_len
= cvt
->vsync
;
281 mode
->sync
&= ~(FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
);
283 if (cvt
->flags
& FB_CVT_FLAG_REDUCED_BLANK
)
284 mode
->sync
|= FB_SYNC_HOR_HIGH_ACT
;
286 mode
->sync
|= FB_SYNC_VERT_HIGH_ACT
;
290 * fb_find_mode_cvt - calculate mode using VESA(TM) CVT
291 * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be
292 * pre-filled with the desired values
293 * @margins: add margin to calculation (1.8% of xres and yres)
294 * @rb: compute with reduced blanking (for flatpanels)
298 * @mode is filled with computed values. If interlaced, the refresh field
299 * will be filled with the field rate (2x the frame rate)
302 * Computes video timings using VESA(TM) Coordinated Video Timings
304 int fb_find_mode_cvt(struct fb_videomode
*mode
, int margins
, int rb
)
306 struct fb_cvt_data cvt
;
308 memset(&cvt
, 0, sizeof(cvt
));
311 cvt
.flags
|= FB_CVT_FLAG_MARGINS
;
314 cvt
.flags
|= FB_CVT_FLAG_REDUCED_BLANK
;
316 if (mode
->vmode
& FB_VMODE_INTERLACED
)
317 cvt
.flags
|= FB_CVT_FLAG_INTERLACED
;
319 cvt
.xres
= mode
->xres
;
320 cvt
.yres
= mode
->yres
;
321 cvt
.refresh
= mode
->refresh
;
322 cvt
.f_refresh
= cvt
.refresh
;
325 if (!cvt
.xres
|| !cvt
.yres
|| !cvt
.refresh
) {
326 printk(KERN_INFO
"fbcvt: Invalid input parameters\n");
330 if (!(cvt
.refresh
== 50 || cvt
.refresh
== 60 || cvt
.refresh
== 70 ||
331 cvt
.refresh
== 85)) {
332 printk(KERN_INFO
"fbcvt: Refresh rate not CVT "
337 cvt
.xres
&= ~(FB_CVT_CELLSIZE
- 1);
339 if (cvt
.flags
& FB_CVT_FLAG_INTERLACED
) {
344 if (cvt
.flags
& FB_CVT_FLAG_REDUCED_BLANK
) {
345 if (cvt
.refresh
!= 60) {
346 printk(KERN_INFO
"fbcvt: 60Hz refresh rate "
347 "advised for reduced blanking\n");
352 if (cvt
.flags
& FB_CVT_FLAG_MARGINS
) {
353 cvt
.h_margin
= (cvt
.xres
* 18)/1000;
354 cvt
.h_margin
&= ~(FB_CVT_CELLSIZE
- 1);
355 cvt
.v_margin
= ((cvt
.yres
/cvt
.interlace
)* 18)/1000;
358 cvt
.aspect_ratio
= fb_cvt_aspect_ratio(&cvt
);
359 cvt
.active_pixels
= cvt
.xres
+ 2 * cvt
.h_margin
;
360 cvt
.hperiod
= fb_cvt_hperiod(&cvt
);
361 cvt
.vsync
= fb_cvt_vbi_tab
[cvt
.aspect_ratio
];
362 cvt
.vtotal
= fb_cvt_vtotal(&cvt
);
363 cvt
.hblank
= fb_cvt_hblank(&cvt
);
364 cvt
.htotal
= cvt
.active_pixels
+ cvt
.hblank
;
365 cvt
.hsync
= fb_cvt_hsync(&cvt
);
366 cvt
.pixclock
= fb_cvt_pixclock(&cvt
);
367 cvt
.hfreq
= cvt
.pixclock
/cvt
.htotal
;
368 cvt
.h_back_porch
= cvt
.hblank
/2 + cvt
.h_margin
;
369 cvt
.h_front_porch
= cvt
.hblank
- cvt
.hsync
- cvt
.h_back_porch
+
371 cvt
.v_back_porch
= 3 + cvt
.v_margin
;
372 cvt
.v_front_porch
= cvt
.vtotal
- cvt
.yres
/cvt
.interlace
-
373 cvt
.v_back_porch
- cvt
.vsync
;
374 fb_cvt_print_name(&cvt
);
375 fb_cvt_convert_to_mode(&cvt
, mode
);