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