aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbcvt.c
diff options
context:
space:
mode:
authorAntonino A. Daplas <adaplas@gmail.com>2005-09-09 16:09:58 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-09-09 17:03:39 -0400
commit96fe6a2109db29cd15b90a093c16e6cb4b19371a (patch)
treebd3c424f3f43e6b5a2ba6c235c7c25b3ba197f4c /drivers/video/fbcvt.c
parent5251bffc9b4ca699993c79166adf02faf1bbc043 (diff)
[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>
Diffstat (limited to 'drivers/video/fbcvt.c')
-rw-r--r--drivers/video/fbcvt.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/drivers/video/fbcvt.c b/drivers/video/fbcvt.c
new file mode 100644
index 00000000000..cfa61b512de
--- /dev/null
+++ b/drivers/video/fbcvt.c
@@ -0,0 +1,380 @@
1/*
2 * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
5 *
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
9 *
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
12 * for more details.
13 *
14 */
15#include <linux/fb.h>
16
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
25
26#define FB_CVT_RB_MIN_VBLANK 460
27#define FB_CVT_RB_HBLANK 160
28#define FB_CVT_RB_V_FPORCH 3
29
30#define FB_CVT_FLAG_REDUCED_BLANK 1
31#define FB_CVT_FLAG_MARGINS 2
32#define FB_CVT_FLAG_INTERLACED 4
33
34struct fb_cvt_data {
35 u32 xres;
36 u32 yres;
37 u32 refresh;
38 u32 f_refresh;
39 u32 pixclock;
40 u32 hperiod;
41 u32 hblank;
42 u32 hfreq;
43 u32 htotal;
44 u32 vtotal;
45 u32 vsync;
46 u32 hsync;
47 u32 h_front_porch;
48 u32 h_back_porch;
49 u32 v_front_porch;
50 u32 v_back_porch;
51 u32 h_margin;
52 u32 v_margin;
53 u32 interlace;
54 u32 aspect_ratio;
55 u32 active_pixels;
56 u32 flags;
57 u32 status;
58};
59
60static int fb_cvt_vbi_tab[] = {
61 4, /* 4:3 */
62 5, /* 16:9 */
63 6, /* 16:10 */
64 7, /* 5:4 */
65 7, /* 15:9 */
66 8, /* reserved */
67 9, /* reserved */
68 10 /* custom */
69};
70
71/* returns hperiod * 1000 */
72static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
73{
74 u32 num = 1000000000/cvt->f_refresh;
75 u32 den;
76
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);
80 } else {
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);
84 }
85
86 return 2 * (num/den);
87}
88
89/* returns ideal duty cycle * 1000 */
90static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt)
91{
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;
96
97 return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256;
98}
99
100static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
101{
102 u32 hblank = 0;
103
104 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
105 hblank = FB_CVT_RB_HBLANK;
106 else {
107 u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt);
108 u32 active_pixels = cvt->active_pixels;
109
110 if (ideal_duty_cycle < 20000)
111 hblank = (active_pixels * 20000)/
112 (100000 - 20000);
113 else {
114 hblank = (active_pixels * ideal_duty_cycle)/
115 (100000 - ideal_duty_cycle);
116 }
117 }
118
119 hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
120
121 return hblank;
122}
123
124static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
125{
126 u32 hsync;
127
128 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
129 hsync = 32;
130 else
131 hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
132
133 hsync &= ~(FB_CVT_CELLSIZE - 1);
134 return hsync;
135}
136
137static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
138{
139 u32 vbi_lines, min_vbi_lines, act_vbi_lines;
140
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 +
144 FB_CVT_MIN_BPORCH;
145
146 } else {
147 vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
148 FB_CVT_MIN_VPORCH;
149 min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
150 FB_CVT_MIN_VPORCH;
151 }
152
153 if (vbi_lines < min_vbi_lines)
154 act_vbi_lines = min_vbi_lines;
155 else
156 act_vbi_lines = vbi_lines;
157
158 return act_vbi_lines;
159}
160
161static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
162{
163 u32 vtotal = cvt->yres/cvt->interlace;
164
165 vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
166 vtotal |= cvt->interlace/2;
167
168 return vtotal;
169}
170
171static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
172{
173 u32 pixclock;
174
175 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
176 pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
177 else
178 pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
179
180 pixclock /= 250;
181 pixclock *= 250;
182 pixclock *= 1000;
183
184 return pixclock;
185}
186
187static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
188{
189 u32 xres = cvt->xres;
190 u32 yres = cvt->yres;
191 u32 aspect = -1;
192
193 if (xres == (yres * 4)/3 && !((yres * 4) % 3))
194 aspect = 0;
195 else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
196 aspect = 1;
197 else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
198 aspect = 2;
199 else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
200 aspect = 3;
201 else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
202 aspect = 4;
203 else {
204 printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
205 "standard\n");
206 aspect = 7;
207 cvt->status = 1;
208 }
209
210 return aspect;
211}
212
213static void fb_cvt_print_name(struct fb_cvt_data *cvt)
214{
215 u32 pixcount, pixcount_mod;
216 int cnt = 255, offset = 0, read = 0;
217 u8 *buf = kmalloc(256, GFP_KERNEL);
218
219 if (!buf)
220 return;
221
222 memset(buf, 0, 256);
223 pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000;
224 pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000;
225 pixcount_mod /= 1000;
226
227 read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ",
228 cvt->xres, cvt->yres, cvt->refresh);
229 offset += read;
230 cnt -= read;
231
232 if (cvt->status)
233 snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega "
234 "Pixel Image\n", pixcount, pixcount_mod);
235 else {
236 if (pixcount) {
237 read = snprintf(buf+offset, cnt, "%d", pixcount);
238 cnt -= read;
239 offset += read;
240 }
241
242 read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod);
243 cnt -= read;
244 offset += read;
245
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");
254 else
255 read = 0;
256 cnt -= read;
257 offset += read;
258
259 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
260 read = snprintf(buf+offset, cnt, "-R");
261 cnt -= read;
262 offset += read;
263 }
264 }
265
266 printk(KERN_INFO "%s\n", buf);
267 kfree(buf);
268}
269
270static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt,
271 struct fb_videomode *mode)
272{
273 mode->refresh = cvt->f_refresh;
274 mode->pixclock = KHZ2PICOS(cvt->pixclock/1000);
275 mode->left_margin = cvt->h_front_porch;
276 mode->right_margin = cvt->h_back_porch;
277 mode->hsync_len = cvt->hsync;
278 mode->upper_margin = cvt->v_front_porch;
279 mode->lower_margin = cvt->v_back_porch;
280 mode->vsync_len = cvt->vsync;
281
282 mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);
283
284 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
285 mode->sync |= FB_SYNC_HOR_HIGH_ACT;
286 else
287 mode->sync |= FB_SYNC_VERT_HIGH_ACT;
288}
289
290/*
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)
296 *
297 * RETURNS:
298 * 0 for success
299 * @mode is filled with computed values. If interlaced, the refresh field
300 * will be filled with the field rate (2x the frame rate)
301 *
302 * DESCRIPTION:
303 * Computes video timings using VESA(TM) Coordinated Video Timings
304 */
305int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
306{
307 struct fb_cvt_data cvt;
308
309 memset(&cvt, 0, sizeof(cvt));
310
311 if (margins)
312 cvt.flags |= FB_CVT_FLAG_MARGINS;
313
314 if (rb)
315 cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK;
316
317 if (mode->vmode & FB_VMODE_INTERLACED)
318 cvt.flags |= FB_CVT_FLAG_INTERLACED;
319
320 cvt.xres = mode->xres;
321 cvt.yres = mode->yres;
322 cvt.refresh = mode->refresh;
323 cvt.f_refresh = cvt.refresh;
324 cvt.interlace = 1;
325
326 if (!cvt.xres || !cvt.yres || !cvt.refresh) {
327 printk(KERN_INFO "fbcvt: Invalid input parameters\n");
328 return 1;
329 }
330
331 if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 ||
332 cvt.refresh == 85)) {
333 printk(KERN_INFO "fbcvt: Refresh rate not CVT "
334 "standard\n");
335 cvt.status = 1;
336 }
337
338 cvt.xres &= ~(FB_CVT_CELLSIZE - 1);
339
340 if (cvt.flags & FB_CVT_FLAG_INTERLACED) {
341 cvt.interlace = 2;
342 cvt.f_refresh *= 2;
343 }
344
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");
349 cvt.status = 1;
350 }
351 }
352
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;
357 }
358
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 +
371 2 * cvt.h_margin;
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);
377
378 return 0;
379}
380EXPORT_SYMBOL(fb_find_mode_cvt);