1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/module.h>
15#include <linux/fb.h>
16
17#undef DEBUG
18
19#define name_matches(v, s, l) \
20 ((v).name && !strncmp((s), (v).name, (l)) && strlen((v).name) == (l))
21#define res_matches(v, x, y) \
22 ((v).xres == (x) && (v).yres == (y))
23
24#ifdef DEBUG
25#define DPRINTK(fmt, args...) printk("modedb %s: " fmt, __func__ , ## args)
26#else
27#define DPRINTK(fmt, args...)
28#endif
29
30const char *fb_mode_option;
31EXPORT_SYMBOL_GPL(fb_mode_option);
32
33
34
35
36
37static const struct fb_videomode modedb[] = {
38 {
39
40 NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
41 0, FB_VMODE_NONINTERLACED
42 }, {
43
44 NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
45 0, FB_VMODE_NONINTERLACED
46 }, {
47
48 NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2,
49 0, FB_VMODE_NONINTERLACED
50 }, {
51
52 NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8,
53 0, FB_VMODE_INTERLACED
54 }, {
55
56 NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
57 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
58 }, {
59
60 NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3,
61 0, FB_VMODE_NONINTERLACED
62 }, {
63
64 NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
65 0, FB_VMODE_NONINTERLACED
66 }, {
67
68 NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
69 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
70 }, {
71
72 NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3,
73 0, FB_VMODE_NONINTERLACED
74 }, {
75
76 NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10,
77 0, FB_VMODE_INTERLACED
78 }, {
79
80 NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
81 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
82 }, {
83
84 NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6,
85 0, FB_VMODE_NONINTERLACED
86 }, {
87
88 NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6,
89 0, FB_VMODE_NONINTERLACED
90 }, {
91
92 NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8,
93 0, FB_VMODE_NONINTERLACED
94 }, {
95
96 NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5,
97 0, FB_VMODE_NONINTERLACED
98 }, {
99
100 NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
101 0, FB_VMODE_NONINTERLACED
102 }, {
103
104 NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12,
105 0, FB_VMODE_INTERLACED
106 }, {
107
108 NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6,
109 0, FB_VMODE_NONINTERLACED
110 }, {
111
112 NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3,
113 0, FB_VMODE_NONINTERLACED
114 }, {
115
116 NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10,
117 0, FB_VMODE_NONINTERLACED
118 }, {
119
120 NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3,
121 0, FB_VMODE_NONINTERLACED
122 }, {
123
124 NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3,
125 0, FB_VMODE_NONINTERLACED
126 }, {
127
128 NULL, 75, 1400, 1050, 7190, 120, 56, 23, 10, 112, 13,
129 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
130 }, {
131
132 NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
133 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
134 }, {
135
136 NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6,
137 0, FB_VMODE_NONINTERLACED
138 }, {
139
140 NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12,
141 0, FB_VMODE_NONINTERLACED
142 }, {
143
144 NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8,
145 0, FB_VMODE_NONINTERLACED
146 }, {
147
148 NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
149 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
150 }, {
151
152 NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12,
153 0, FB_VMODE_NONINTERLACED
154 }, {
155
156 NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3,
157 0, FB_VMODE_NONINTERLACED
158 }, {
159
160 NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10,
161 0, FB_VMODE_NONINTERLACED
162 }, {
163
164 NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3,
165 0, FB_VMODE_NONINTERLACED
166 }, {
167
168 NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
169 0, FB_VMODE_NONINTERLACED
170 }, {
171
172 NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19,
173 0, FB_VMODE_NONINTERLACED
174 }, {
175
176 NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
177 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
178 }, {
179
180 NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
181 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
182 }, {
183
184 NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6,
185 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
186 }, {
187
188 NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
189 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
190 }, {
191
192 NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15,
193 0, FB_VMODE_NONINTERLACED
194 }, {
195
196 NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
197 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
198 }, {
199
200 NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
201 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
202 }, {
203
204 NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3,
205 0, FB_VMODE_NONINTERLACED
206 }, {
207
208 NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3,
209 0, FB_VMODE_NONINTERLACED
210 }, {
211
212 NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1,
213 0, FB_VMODE_DOUBLE
214 }, {
215
216 NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
217 0, FB_VMODE_DOUBLE
218 }, {
219
220 NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
221 0, FB_VMODE_DOUBLE
222 }, {
223
224 NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
225 0, FB_VMODE_DOUBLE
226 }, {
227
228 NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2,
229 0, FB_VMODE_DOUBLE
230 }, {
231
232 NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3,
233 0, FB_VMODE_DOUBLE
234 }, {
235
236 NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1,
237 0, FB_VMODE_DOUBLE
238 }, {
239
240 NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2,
241 0, FB_VMODE_DOUBLE
242 }, {
243
244 NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2,
245 0, FB_VMODE_DOUBLE
246 }, {
247
248 NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3,
249 0, FB_VMODE_DOUBLE
250 }, {
251
252 NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3,
253 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
254 FB_VMODE_NONINTERLACED
255 }, {
256
257 NULL, 60, 1152, 768, 14047, 158, 26, 29, 3, 136, 6,
258 FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
259 }, {
260
261 NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5,
262 0, FB_VMODE_NONINTERLACED
263 }, {
264
265 NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3,
266 0, FB_VMODE_NONINTERLACED
267 }, {
268
269 NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5,
270 0, FB_VMODE_INTERLACED
271 }, {
272
273 NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5,
274 0, FB_VMODE_INTERLACED
275 },
276};
277
278#ifdef CONFIG_FB_MODE_HELPERS
279const struct fb_videomode vesa_modes[] = {
280
281 { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
282 FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA},
283
284 { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3,
285 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
286
287 { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3,
288 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
289
290 { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
291 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
292
293 { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
294 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
295
296 { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
297 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
298
299 { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
300 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
301
302 { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2,
303 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
304 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
305
306 { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
307 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
308 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
309
310 { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
311 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
312 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
313
314 { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
315 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
316 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
317
318 { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
319 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
320 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
321
322 { NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8,
323 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
324 FB_VMODE_INTERLACED, FB_MODE_IS_VESA },
325
326 { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
327 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
328
329 { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
330 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
331
332 { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
333 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
334 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
335
336 { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
337 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
338 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
339
340 { NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
341 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
342 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
343
344 { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
345 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
346 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
347
348 { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
349 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
350 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
351
352 { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
353 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
354 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
355
356 { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
357 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
358 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
359
360 { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
361 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
362 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
363
364 { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
365 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
366 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
367
368 { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
369 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
370 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
371
372 { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
373 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
374 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
375
376 { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
377 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
378 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
379
380 { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
381 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
382 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
383
384 { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3,
385 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
386
387 { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
388 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
389
390 { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3,
391 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
392
393 { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
394 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
395
396 { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3,
397 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
398
399 { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
400 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
401};
402EXPORT_SYMBOL(vesa_modes);
403#endif
404
405static int my_atoi(const char *name)
406{
407 int val = 0;
408
409 for (;; name++) {
410 switch (*name) {
411 case '0' ... '9':
412 val = 10*val+(*name-'0');
413 break;
414 default:
415 return val;
416 }
417 }
418}
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
434 const struct fb_videomode *mode, unsigned int bpp)
435{
436 int err = 0;
437
438 DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname",
439 mode->xres, mode->yres, bpp, mode->refresh);
440 var->xres = mode->xres;
441 var->yres = mode->yres;
442 var->xres_virtual = mode->xres;
443 var->yres_virtual = mode->yres;
444 var->xoffset = 0;
445 var->yoffset = 0;
446 var->bits_per_pixel = bpp;
447 var->activate |= FB_ACTIVATE_TEST;
448 var->pixclock = mode->pixclock;
449 var->left_margin = mode->left_margin;
450 var->right_margin = mode->right_margin;
451 var->upper_margin = mode->upper_margin;
452 var->lower_margin = mode->lower_margin;
453 var->hsync_len = mode->hsync_len;
454 var->vsync_len = mode->vsync_len;
455 var->sync = mode->sync;
456 var->vmode = mode->vmode;
457 if (info->fbops->fb_check_var)
458 err = info->fbops->fb_check_var(var, info);
459 var->activate &= ~FB_ACTIVATE_TEST;
460 return err;
461}
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505int fb_find_mode(struct fb_var_screeninfo *var,
506 struct fb_info *info, const char *mode_option,
507 const struct fb_videomode *db, unsigned int dbsize,
508 const struct fb_videomode *default_mode,
509 unsigned int default_bpp)
510{
511 int i;
512
513
514 if (!db) {
515 db = modedb;
516 dbsize = ARRAY_SIZE(modedb);
517 }
518
519 if (!default_mode)
520 default_mode = &db[0];
521
522 if (!default_bpp)
523 default_bpp = 8;
524
525
526 if (!mode_option)
527 mode_option = fb_mode_option;
528 if (mode_option) {
529 const char *name = mode_option;
530 unsigned int namelen = strlen(name);
531 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
532 unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
533 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
534 u32 best, diff, tdiff;
535
536 for (i = namelen-1; i >= 0; i--) {
537 switch (name[i]) {
538 case '@':
539 namelen = i;
540 if (!refresh_specified && !bpp_specified &&
541 !yres_specified) {
542 refresh = my_atoi(&name[i+1]);
543 refresh_specified = 1;
544 if (cvt || rb)
545 cvt = 0;
546 } else
547 goto done;
548 break;
549 case '-':
550 namelen = i;
551 if (!bpp_specified && !yres_specified) {
552 bpp = my_atoi(&name[i+1]);
553 bpp_specified = 1;
554 if (cvt || rb)
555 cvt = 0;
556 } else
557 goto done;
558 break;
559 case 'x':
560 if (!yres_specified) {
561 yres = my_atoi(&name[i+1]);
562 yres_specified = 1;
563 } else
564 goto done;
565 break;
566 case '0' ... '9':
567 break;
568 case 'M':
569 if (!yres_specified)
570 cvt = 1;
571 break;
572 case 'R':
573 if (!cvt)
574 rb = 1;
575 break;
576 case 'm':
577 if (!cvt)
578 margins = 1;
579 break;
580 case 'i':
581 if (!cvt)
582 interlace = 1;
583 break;
584 default:
585 goto done;
586 }
587 }
588 if (i < 0 && yres_specified) {
589 xres = my_atoi(name);
590 res_specified = 1;
591 }
592done:
593 if (cvt) {
594 struct fb_videomode cvt_mode;
595 int ret;
596
597 DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
598 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
599 "", (margins) ? " with margins" : "", (interlace) ?
600 " interlaced" : "");
601
602 memset(&cvt_mode, 0, sizeof(cvt_mode));
603 cvt_mode.xres = xres;
604 cvt_mode.yres = yres;
605 cvt_mode.refresh = (refresh) ? refresh : 60;
606
607 if (interlace)
608 cvt_mode.vmode |= FB_VMODE_INTERLACED;
609 else
610 cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
611
612 ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
613
614 if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
615 DPRINTK("modedb CVT: CVT mode ok\n");
616 return 1;
617 }
618
619 DPRINTK("CVT mode invalid, getting mode from database\n");
620 }
621
622 DPRINTK("Trying specified video mode%s %ix%i\n",
623 refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);
624
625 if (!refresh_specified) {
626
627
628
629
630
631
632
633 if (db != modedb &&
634 info->monspecs.vfmin && info->monspecs.vfmax &&
635 info->monspecs.hfmin && info->monspecs.hfmax &&
636 info->monspecs.dclkmax) {
637 refresh = 1000;
638 } else {
639 refresh = 60;
640 }
641 }
642
643 diff = -1;
644 best = -1;
645 for (i = 0; i < dbsize; i++) {
646 if ((name_matches(db[i], name, namelen) ||
647 (res_specified && res_matches(db[i], xres, yres))) &&
648 !fb_try_mode(var, info, &db[i], bpp)) {
649 if (refresh_specified && db[i].refresh == refresh) {
650 return 1;
651 } else {
652 if (abs(db[i].refresh - refresh) < diff) {
653 diff = abs(db[i].refresh - refresh);
654 best = i;
655 }
656 }
657 }
658 }
659 if (best != -1) {
660 fb_try_mode(var, info, &db[best], bpp);
661 return (refresh_specified) ? 2 : 1;
662 }
663
664 diff = 2 * (xres + yres);
665 best = -1;
666 DPRINTK("Trying best-fit modes\n");
667 for (i = 0; i < dbsize; i++) {
668 DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);
669 if (!fb_try_mode(var, info, &db[i], bpp)) {
670 tdiff = abs(db[i].xres - xres) +
671 abs(db[i].yres - yres);
672
673
674
675
676
677 if (xres > db[i].xres || yres > db[i].yres)
678 tdiff += xres + yres;
679
680 if (diff > tdiff) {
681 diff = tdiff;
682 best = i;
683 }
684 }
685 }
686 if (best != -1) {
687 fb_try_mode(var, info, &db[best], bpp);
688 return 5;
689 }
690 }
691
692 DPRINTK("Trying default video mode\n");
693 if (!fb_try_mode(var, info, default_mode, default_bpp))
694 return 3;
695
696 DPRINTK("Trying all modes\n");
697 for (i = 0; i < dbsize; i++)
698 if (!fb_try_mode(var, info, &db[i], default_bpp))
699 return 4;
700
701 DPRINTK("No valid mode found\n");
702 return 0;
703}
704
705
706
707
708
709
710void fb_var_to_videomode(struct fb_videomode *mode,
711 const struct fb_var_screeninfo *var)
712{
713 u32 pixclock, hfreq, htotal, vtotal;
714
715 mode->name = NULL;
716 mode->xres = var->xres;
717 mode->yres = var->yres;
718 mode->pixclock = var->pixclock;
719 mode->hsync_len = var->hsync_len;
720 mode->vsync_len = var->vsync_len;
721 mode->left_margin = var->left_margin;
722 mode->right_margin = var->right_margin;
723 mode->upper_margin = var->upper_margin;
724 mode->lower_margin = var->lower_margin;
725 mode->sync = var->sync;
726 mode->vmode = var->vmode & FB_VMODE_MASK;
727 mode->flag = FB_MODE_IS_FROM_VAR;
728 mode->refresh = 0;
729
730 if (!var->pixclock)
731 return;
732
733 pixclock = PICOS2KHZ(var->pixclock) * 1000;
734
735 htotal = var->xres + var->right_margin + var->hsync_len +
736 var->left_margin;
737 vtotal = var->yres + var->lower_margin + var->vsync_len +
738 var->upper_margin;
739
740 if (var->vmode & FB_VMODE_INTERLACED)
741 vtotal /= 2;
742 if (var->vmode & FB_VMODE_DOUBLE)
743 vtotal *= 2;
744
745 hfreq = pixclock/htotal;
746 mode->refresh = hfreq/vtotal;
747}
748
749
750
751
752
753
754void fb_videomode_to_var(struct fb_var_screeninfo *var,
755 const struct fb_videomode *mode)
756{
757 var->xres = mode->xres;
758 var->yres = mode->yres;
759 var->xres_virtual = mode->xres;
760 var->yres_virtual = mode->yres;
761 var->xoffset = 0;
762 var->yoffset = 0;
763 var->pixclock = mode->pixclock;
764 var->left_margin = mode->left_margin;
765 var->right_margin = mode->right_margin;
766 var->upper_margin = mode->upper_margin;
767 var->lower_margin = mode->lower_margin;
768 var->hsync_len = mode->hsync_len;
769 var->vsync_len = mode->vsync_len;
770 var->sync = mode->sync;
771 var->vmode = mode->vmode & FB_VMODE_MASK;
772}
773
774
775
776
777
778
779
780
781
782int fb_mode_is_equal(const struct fb_videomode *mode1,
783 const struct fb_videomode *mode2)
784{
785 return (mode1->xres == mode2->xres &&
786 mode1->yres == mode2->yres &&
787 mode1->pixclock == mode2->pixclock &&
788 mode1->hsync_len == mode2->hsync_len &&
789 mode1->vsync_len == mode2->vsync_len &&
790 mode1->left_margin == mode2->left_margin &&
791 mode1->right_margin == mode2->right_margin &&
792 mode1->upper_margin == mode2->upper_margin &&
793 mode1->lower_margin == mode2->lower_margin &&
794 mode1->sync == mode2->sync &&
795 mode1->vmode == mode2->vmode);
796}
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var,
816 struct list_head *head)
817{
818 struct list_head *pos;
819 struct fb_modelist *modelist;
820 struct fb_videomode *mode, *best = NULL;
821 u32 diff = -1;
822
823 list_for_each(pos, head) {
824 u32 d;
825
826 modelist = list_entry(pos, struct fb_modelist, list);
827 mode = &modelist->mode;
828
829 if (mode->xres >= var->xres && mode->yres >= var->yres) {
830 d = (mode->xres - var->xres) +
831 (mode->yres - var->yres);
832 if (diff > d) {
833 diff = d;
834 best = mode;
835 } else if (diff == d && best &&
836 mode->refresh > best->refresh)
837 best = mode;
838 }
839 }
840 return best;
841}
842
843
844
845
846
847
848
849
850
851
852
853const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
854 struct list_head *head)
855{
856 struct list_head *pos;
857 struct fb_modelist *modelist;
858 struct fb_videomode *cmode, *best = NULL;
859 u32 diff = -1, diff_refresh = -1;
860
861 list_for_each(pos, head) {
862 u32 d;
863
864 modelist = list_entry(pos, struct fb_modelist, list);
865 cmode = &modelist->mode;
866
867 d = abs(cmode->xres - mode->xres) +
868 abs(cmode->yres - mode->yres);
869 if (diff > d) {
870 diff = d;
871 best = cmode;
872 } else if (diff == d) {
873 d = abs(cmode->refresh - mode->refresh);
874 if (diff_refresh > d) {
875 diff_refresh = d;
876 best = cmode;
877 }
878 }
879 }
880
881 return best;
882}
883
884
885
886
887
888
889
890
891
892const struct fb_videomode *fb_match_mode(const struct fb_var_screeninfo *var,
893 struct list_head *head)
894{
895 struct list_head *pos;
896 struct fb_modelist *modelist;
897 struct fb_videomode *m, mode;
898
899 fb_var_to_videomode(&mode, var);
900 list_for_each(pos, head) {
901 modelist = list_entry(pos, struct fb_modelist, list);
902 m = &modelist->mode;
903 if (fb_mode_is_equal(m, &mode))
904 return m;
905 }
906 return NULL;
907}
908
909
910
911
912
913
914
915
916
917int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
918{
919 struct list_head *pos;
920 struct fb_modelist *modelist;
921 struct fb_videomode *m;
922 int found = 0;
923
924 list_for_each(pos, head) {
925 modelist = list_entry(pos, struct fb_modelist, list);
926 m = &modelist->mode;
927 if (fb_mode_is_equal(m, mode)) {
928 found = 1;
929 break;
930 }
931 }
932 if (!found) {
933 modelist = kmalloc(sizeof(struct fb_modelist),
934 GFP_KERNEL);
935
936 if (!modelist)
937 return -ENOMEM;
938 modelist->mode = *mode;
939 list_add(&modelist->list, head);
940 }
941 return 0;
942}
943
944
945
946
947
948
949
950
951
952void fb_delete_videomode(const struct fb_videomode *mode,
953 struct list_head *head)
954{
955 struct list_head *pos, *n;
956 struct fb_modelist *modelist;
957 struct fb_videomode *m;
958
959 list_for_each_safe(pos, n, head) {
960 modelist = list_entry(pos, struct fb_modelist, list);
961 m = &modelist->mode;
962 if (fb_mode_is_equal(m, mode)) {
963 list_del(pos);
964 kfree(pos);
965 }
966 }
967}
968
969
970
971
972
973void fb_destroy_modelist(struct list_head *head)
974{
975 struct list_head *pos, *n;
976
977 list_for_each_safe(pos, n, head) {
978 list_del(pos);
979 kfree(pos);
980 }
981}
982EXPORT_SYMBOL_GPL(fb_destroy_modelist);
983
984
985
986
987
988
989
990void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num,
991 struct list_head *head)
992{
993 int i;
994
995 INIT_LIST_HEAD(head);
996
997 for (i = 0; i < num; i++) {
998 if (fb_add_videomode(&modedb[i], head))
999 return;
1000 }
1001}
1002
1003const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs,
1004 struct list_head *head)
1005{
1006 struct list_head *pos;
1007 struct fb_modelist *modelist;
1008 const struct fb_videomode *m, *m1 = NULL, *md = NULL, *best = NULL;
1009 int first = 0;
1010
1011 if (!head->prev || !head->next || list_empty(head))
1012 goto finished;
1013
1014
1015 list_for_each(pos, head) {
1016 modelist = list_entry(pos, struct fb_modelist, list);
1017 m = &modelist->mode;
1018
1019 if (!first) {
1020 m1 = m;
1021 first = 1;
1022 }
1023
1024 if (m->flag & FB_MODE_IS_FIRST) {
1025 md = m;
1026 break;
1027 }
1028 }
1029
1030
1031 if (specs->misc & FB_MISC_1ST_DETAIL) {
1032 best = md;
1033 goto finished;
1034 }
1035
1036
1037 if (specs->max_x && specs->max_y) {
1038 struct fb_var_screeninfo var;
1039
1040 memset(&var, 0, sizeof(struct fb_var_screeninfo));
1041 var.xres = (specs->max_x * 7200)/254;
1042 var.yres = (specs->max_y * 7200)/254;
1043 m = fb_find_best_mode(&var, head);
1044 if (m) {
1045 best = m;
1046 goto finished;
1047 }
1048 }
1049
1050
1051 if (md) {
1052 best = md;
1053 goto finished;
1054 }
1055
1056
1057 best = m1;
1058finished:
1059 return best;
1060}
1061EXPORT_SYMBOL(fb_find_best_display);
1062
1063EXPORT_SYMBOL(fb_videomode_to_var);
1064EXPORT_SYMBOL(fb_var_to_videomode);
1065EXPORT_SYMBOL(fb_mode_is_equal);
1066EXPORT_SYMBOL(fb_add_videomode);
1067EXPORT_SYMBOL(fb_match_mode);
1068EXPORT_SYMBOL(fb_find_best_mode);
1069EXPORT_SYMBOL(fb_find_nearest_mode);
1070EXPORT_SYMBOL(fb_videomode_to_modelist);
1071EXPORT_SYMBOL(fb_find_mode);
1072