Branch data Line data Source code
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.elo.utfsm.cl/~elo212/docs/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 : : #include <linux/slab.h>
17 : :
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
26 : :
27 : : #define FB_CVT_RB_MIN_VBLANK 460
28 : : #define FB_CVT_RB_HBLANK 160
29 : : #define FB_CVT_RB_V_FPORCH 3
30 : :
31 : : #define FB_CVT_FLAG_REDUCED_BLANK 1
32 : : #define FB_CVT_FLAG_MARGINS 2
33 : : #define FB_CVT_FLAG_INTERLACED 4
34 : :
35 : : struct fb_cvt_data {
36 : : u32 xres;
37 : : u32 yres;
38 : : u32 refresh;
39 : : u32 f_refresh;
40 : : u32 pixclock;
41 : : u32 hperiod;
42 : : u32 hblank;
43 : : u32 hfreq;
44 : : u32 htotal;
45 : : u32 vtotal;
46 : : u32 vsync;
47 : : u32 hsync;
48 : : u32 h_front_porch;
49 : : u32 h_back_porch;
50 : : u32 v_front_porch;
51 : : u32 v_back_porch;
52 : : u32 h_margin;
53 : : u32 v_margin;
54 : : u32 interlace;
55 : : u32 aspect_ratio;
56 : : u32 active_pixels;
57 : : u32 flags;
58 : : u32 status;
59 : : };
60 : :
61 : : static const unsigned char fb_cvt_vbi_tab[] = {
62 : : 4, /* 4:3 */
63 : : 5, /* 16:9 */
64 : : 6, /* 16:10 */
65 : : 7, /* 5:4 */
66 : : 7, /* 15:9 */
67 : : 8, /* reserved */
68 : : 9, /* reserved */
69 : : 10 /* custom */
70 : : };
71 : :
72 : : /* returns hperiod * 1000 */
73 : 0 : static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
74 : : {
75 : 0 : u32 num = 1000000000/cvt->f_refresh;
76 : : u32 den;
77 : :
78 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
79 : 0 : num -= FB_CVT_RB_MIN_VBLANK * 1000;
80 : 0 : den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin);
81 : : } else {
82 : 0 : num -= FB_CVT_MIN_VSYNC_BP * 1000;
83 : 0 : den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2
84 : 0 : + FB_CVT_MIN_VPORCH + cvt->interlace/2);
85 : : }
86 : :
87 : 0 : return 2 * (num/den);
88 : : }
89 : :
90 : : /* returns ideal duty cycle * 1000 */
91 : : static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt)
92 : : {
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 : 0 : u32 h_period_est = cvt->hperiod;
97 : :
98 : 0 : return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256;
99 : : }
100 : :
101 : 0 : static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
102 : : {
103 : : u32 hblank = 0;
104 : :
105 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
106 : : hblank = FB_CVT_RB_HBLANK;
107 : : else {
108 : : u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt);
109 : 0 : u32 active_pixels = cvt->active_pixels;
110 : :
111 [ # # ]: 0 : if (ideal_duty_cycle < 20000)
112 : 0 : hblank = (active_pixels * 20000)/
113 : : (100000 - 20000);
114 : : else {
115 : 0 : hblank = (active_pixels * ideal_duty_cycle)/
116 : 0 : (100000 - ideal_duty_cycle);
117 : : }
118 : : }
119 : :
120 : 0 : hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
121 : :
122 : 0 : return hblank;
123 : : }
124 : :
125 : : static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
126 : : {
127 : : u32 hsync;
128 : :
129 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
130 : : hsync = 32;
131 : : else
132 : 0 : hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
133 : :
134 : 0 : hsync &= ~(FB_CVT_CELLSIZE - 1);
135 : : return hsync;
136 : : }
137 : :
138 : : static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
139 : : {
140 : : u32 vbi_lines, min_vbi_lines, act_vbi_lines;
141 : :
142 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
143 : 0 : vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1;
144 : 0 : min_vbi_lines = FB_CVT_RB_V_FPORCH + cvt->vsync +
145 : : FB_CVT_MIN_BPORCH;
146 : :
147 : : } else {
148 : 0 : vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
149 : : FB_CVT_MIN_VPORCH;
150 : 0 : min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
151 : : FB_CVT_MIN_VPORCH;
152 : : }
153 : :
154 [ # # ]: 0 : if (vbi_lines < min_vbi_lines)
155 : : act_vbi_lines = min_vbi_lines;
156 : : else
157 : : act_vbi_lines = vbi_lines;
158 : :
159 : : return act_vbi_lines;
160 : : }
161 : :
162 : 0 : static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
163 : : {
164 : 0 : u32 vtotal = cvt->yres/cvt->interlace;
165 : :
166 : 0 : vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
167 : 0 : vtotal |= cvt->interlace/2;
168 : :
169 : 0 : return vtotal;
170 : : }
171 : :
172 : : static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
173 : : {
174 : : u32 pixclock;
175 : :
176 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
177 : 0 : pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
178 : : else
179 : 0 : pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
180 : :
181 : 0 : pixclock /= 250;
182 : 0 : pixclock *= 250;
183 : 0 : pixclock *= 1000;
184 : :
185 : : return pixclock;
186 : : }
187 : :
188 : 0 : static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
189 : : {
190 : 0 : u32 xres = cvt->xres;
191 : 0 : u32 yres = cvt->yres;
192 : : u32 aspect = -1;
193 : :
194 [ # # # # ]: 0 : if (xres == (yres * 4)/3 && !((yres * 4) % 3))
195 : : aspect = 0;
196 [ # # # # ]: 0 : else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
197 : : aspect = 1;
198 [ # # # # ]: 0 : else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
199 : : aspect = 2;
200 [ # # # # ]: 0 : else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
201 : : aspect = 3;
202 [ # # # # ]: 0 : else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
203 : : aspect = 4;
204 : : else {
205 : 0 : printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
206 : : "standard\n");
207 : : aspect = 7;
208 : 0 : cvt->status = 1;
209 : : }
210 : :
211 : 0 : return aspect;
212 : : }
213 : :
214 : 0 : static void fb_cvt_print_name(struct fb_cvt_data *cvt)
215 : : {
216 : : u32 pixcount, pixcount_mod;
217 : : int cnt = 255, offset = 0, read = 0;
218 : 0 : u8 *buf = kzalloc(256, GFP_KERNEL);
219 : :
220 [ # # ]: 0 : if (!buf)
221 : 0 : return;
222 : :
223 : 0 : pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000;
224 : 0 : pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000;
225 : 0 : pixcount_mod /= 1000;
226 : :
227 : 0 : read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ",
228 : : cvt->xres, cvt->yres, cvt->refresh);
229 : : offset += read;
230 : 0 : cnt -= read;
231 : :
232 [ # # ]: 0 : if (cvt->status)
233 : 0 : snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega "
234 : : "Pixel Image\n", pixcount, pixcount_mod);
235 : : else {
236 [ # # ]: 0 : if (pixcount) {
237 : 0 : read = snprintf(buf+offset, cnt, "%d", pixcount);
238 : 0 : cnt -= read;
239 : 0 : offset += read;
240 : : }
241 : :
242 : 0 : read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod);
243 : 0 : cnt -= read;
244 : 0 : offset += read;
245 : :
246 [ # # ]: 0 : if (cvt->aspect_ratio == 0)
247 : 0 : read = snprintf(buf+offset, cnt, "3");
248 [ # # ]: 0 : else if (cvt->aspect_ratio == 3)
249 : 0 : read = snprintf(buf+offset, cnt, "4");
250 [ # # ]: 0 : else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4)
251 : 0 : read = snprintf(buf+offset, cnt, "9");
252 [ # # ]: 0 : else if (cvt->aspect_ratio == 2)
253 : 0 : read = snprintf(buf+offset, cnt, "A");
254 : : else
255 : : read = 0;
256 : 0 : cnt -= read;
257 : 0 : offset += read;
258 : :
259 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
260 : 0 : read = snprintf(buf+offset, cnt, "-R");
261 : : cnt -= read;
262 : : offset += read;
263 : : }
264 : : }
265 : :
266 : 0 : printk(KERN_INFO "%s\n", buf);
267 : 0 : kfree(buf);
268 : : }
269 : :
270 : 0 : static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt,
271 : : struct fb_videomode *mode)
272 : : {
273 : 0 : mode->refresh = cvt->f_refresh;
274 : 0 : mode->pixclock = KHZ2PICOS(cvt->pixclock/1000);
275 : 0 : mode->left_margin = cvt->h_back_porch;
276 : 0 : mode->right_margin = cvt->h_front_porch;
277 : 0 : mode->hsync_len = cvt->hsync;
278 : 0 : mode->upper_margin = cvt->v_back_porch;
279 : 0 : mode->lower_margin = cvt->v_front_porch;
280 : 0 : mode->vsync_len = cvt->vsync;
281 : :
282 : 0 : mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);
283 : :
284 [ # # ]: 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
285 : 0 : mode->sync |= FB_SYNC_HOR_HIGH_ACT;
286 : : else
287 : 0 : mode->sync |= FB_SYNC_VERT_HIGH_ACT;
288 : 0 : }
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 : : */
305 : 0 : int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
306 : : {
307 : : struct fb_cvt_data cvt;
308 : :
309 : 0 : memset(&cvt, 0, sizeof(cvt));
310 : :
311 [ # # ]: 0 : if (margins)
312 : 0 : cvt.flags |= FB_CVT_FLAG_MARGINS;
313 : :
314 [ # # ]: 0 : if (rb)
315 : 0 : cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK;
316 : :
317 [ # # ]: 0 : if (mode->vmode & FB_VMODE_INTERLACED)
318 : 0 : cvt.flags |= FB_CVT_FLAG_INTERLACED;
319 : :
320 : 0 : cvt.xres = mode->xres;
321 : 0 : cvt.yres = mode->yres;
322 : 0 : cvt.refresh = mode->refresh;
323 : 0 : cvt.f_refresh = cvt.refresh;
324 : 0 : cvt.interlace = 1;
325 : :
326 [ # # # # : 0 : if (!cvt.xres || !cvt.yres || !cvt.refresh) {
# # ]
327 : 0 : printk(KERN_INFO "fbcvt: Invalid input parameters\n");
328 : 0 : return 1;
329 : : }
330 : :
331 [ # # # # : 0 : if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 ||
# # ]
332 : : cvt.refresh == 85)) {
333 : 0 : printk(KERN_INFO "fbcvt: Refresh rate not CVT "
334 : : "standard\n");
335 : 0 : cvt.status = 1;
336 : : }
337 : :
338 : 0 : cvt.xres &= ~(FB_CVT_CELLSIZE - 1);
339 : :
340 [ # # ]: 0 : if (cvt.flags & FB_CVT_FLAG_INTERLACED) {
341 : 0 : cvt.interlace = 2;
342 : 0 : cvt.f_refresh *= 2;
343 : : }
344 : :
345 [ # # ]: 0 : if (cvt.flags & FB_CVT_FLAG_REDUCED_BLANK) {
346 [ # # ]: 0 : if (cvt.refresh != 60) {
347 : 0 : printk(KERN_INFO "fbcvt: 60Hz refresh rate "
348 : : "advised for reduced blanking\n");
349 : 0 : cvt.status = 1;
350 : : }
351 : : }
352 : :
353 [ # # ]: 0 : if (cvt.flags & FB_CVT_FLAG_MARGINS) {
354 : 0 : cvt.h_margin = (cvt.xres * 18)/1000;
355 : 0 : cvt.h_margin &= ~(FB_CVT_CELLSIZE - 1);
356 : 0 : cvt.v_margin = ((cvt.yres/cvt.interlace)* 18)/1000;
357 : : }
358 : :
359 : 0 : cvt.aspect_ratio = fb_cvt_aspect_ratio(&cvt);
360 : 0 : cvt.active_pixels = cvt.xres + 2 * cvt.h_margin;
361 : 0 : cvt.hperiod = fb_cvt_hperiod(&cvt);
362 : 0 : cvt.vsync = fb_cvt_vbi_tab[cvt.aspect_ratio];
363 : 0 : cvt.vtotal = fb_cvt_vtotal(&cvt);
364 : 0 : cvt.hblank = fb_cvt_hblank(&cvt);
365 : 0 : cvt.htotal = cvt.active_pixels + cvt.hblank;
366 : 0 : cvt.hsync = fb_cvt_hsync(&cvt);
367 : 0 : cvt.pixclock = fb_cvt_pixclock(&cvt);
368 : 0 : cvt.hfreq = cvt.pixclock/cvt.htotal;
369 : 0 : cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin;
370 : 0 : cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch +
371 : : 2 * cvt.h_margin;
372 : 0 : cvt.v_front_porch = 3 + cvt.v_margin;
373 : 0 : cvt.v_back_porch = cvt.vtotal - cvt.yres/cvt.interlace -
374 : 0 : cvt.v_front_porch - cvt.vsync;
375 : 0 : fb_cvt_print_name(&cvt);
376 : 0 : fb_cvt_convert_to_mode(&cvt, mode);
377 : :
378 : 0 : return 0;
379 : : }
|