File: /usr/src/linux/drivers/video/modedb.c

1     /*
2      *  linux/drivers/video/modedb.c -- Standard video mode database management
3      *
4      *	Copyright (C) 1999 Geert Uytterhoeven
5      *
6      *	2001 - Documented with DocBook
7      *	- Brad Douglas <brad@neruo.com>
8      *
9      *  This file is subject to the terms and conditions of the GNU General Public
10      *  License. See the file COPYING in the main directory of this archive for
11      *  more details.
12      */
13     
14     #include <linux/module.h>
15     #include <linux/tty.h>
16     #include <linux/fb.h>
17     #include <linux/console_struct.h>
18     #include <linux/sched.h>
19     
20     
21     #undef DEBUG
22     
23     #define name_matches(v, s, l) \
24         ((v).name && !strncmp((s), (v).name, (l)) && strlen((v).name) == (l))
25     #define res_matches(v, x, y) \
26         ((v).xres == (x) && (v).yres == (y))
27     
28     #ifdef DEBUG
29     #define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
30     #else
31     #define DPRINTK(fmt, args...)
32     #endif
33     
34     
35     const char *global_mode_option = NULL;
36     
37     
38         /*
39          *  Standard video mode definitions (taken from XFree86)
40          */
41     
42     #define DEFAULT_MODEDB_INDEX	0
43     
44     static const struct fb_videomode modedb[] __initdata = {
45         {
46     	/* 640x400 @ 70 Hz, 31.5 kHz hsync */
47     	NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
48     	0, FB_VMODE_NONINTERLACED
49         }, {
50     	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
51     	NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
52     	0, FB_VMODE_NONINTERLACED
53         }, {
54     	/* 800x600 @ 56 Hz, 35.15 kHz hsync */
55     	NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2,
56     	0, FB_VMODE_NONINTERLACED
57         }, {
58     	/* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
59     	NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8,
60     	0, FB_VMODE_INTERLACED
61         }, {
62     	/* 640x400 @ 85 Hz, 37.86 kHz hsync */
63     	NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
64     	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
65         }, {
66     	/* 640x480 @ 72 Hz, 36.5 kHz hsync */
67     	NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3,
68     	0, FB_VMODE_NONINTERLACED
69         }, {
70     	/* 640x480 @ 75 Hz, 37.50 kHz hsync */
71     	NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
72     	0, FB_VMODE_NONINTERLACED
73         }, {
74     	/* 800x600 @ 60 Hz, 37.8 kHz hsync */
75     	NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
76     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
77         }, {
78     	/* 640x480 @ 85 Hz, 43.27 kHz hsync */
79     	NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3,
80     	0, FB_VMODE_NONINTERLACED
81         }, {
82     	/* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
83     	NULL, 69, 1152, 864, 15384, 96, 16, 110, 1, 216, 10,
84     	0, FB_VMODE_INTERLACED
85         }, {
86     	/* 800x600 @ 72 Hz, 48.0 kHz hsync */
87     	NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
88     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
89         }, {
90     	/* 1024x768 @ 60 Hz, 48.4 kHz hsync */
91     	NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6,
92     	0, FB_VMODE_NONINTERLACED
93         }, {
94     	/* 640x480 @ 100 Hz, 53.01 kHz hsync */
95     	NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6,
96     	0, FB_VMODE_NONINTERLACED
97         }, {
98     	/* 1152x864 @ 60 Hz, 53.5 kHz hsync */
99     	NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8,
100     	0, FB_VMODE_NONINTERLACED
101         }, {
102     	/* 800x600 @ 85 Hz, 55.84 kHz hsync */
103     	NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5,
104     	0, FB_VMODE_NONINTERLACED
105         }, {
106     	/* 1024x768 @ 70 Hz, 56.5 kHz hsync */
107     	NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
108     	0, FB_VMODE_NONINTERLACED
109         }, {
110     	/* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
111     	NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12,
112     	0, FB_VMODE_INTERLACED
113         }, {
114     	/* 800x600 @ 100 Hz, 64.02 kHz hsync */
115     	NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6,
116     	0, FB_VMODE_NONINTERLACED
117         }, {
118     	/* 1024x768 @ 76 Hz, 62.5 kHz hsync */
119     	NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3,
120     	0, FB_VMODE_NONINTERLACED
121         }, {
122     	/* 1152x864 @ 70 Hz, 62.4 kHz hsync */
123     	NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10,
124     	0, FB_VMODE_NONINTERLACED
125         }, {
126     	/* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
127     	NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3,
128     	0, FB_VMODE_NONINTERLACED
129         }, {
130     	/* 1024x768 @ 85 Hz, 70.24 kHz hsync */
131     	NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6,
132     	0, FB_VMODE_NONINTERLACED
133         }, {
134     	/* 1152x864 @ 78 Hz, 70.8 kHz hsync */
135     	NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12,
136     	0, FB_VMODE_NONINTERLACED
137         }, {
138     	/* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
139     	NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8,
140     	0, FB_VMODE_NONINTERLACED
141         }, {
142     	/* 1600x1200 @ 60Hz, 75.00 kHz hsync */
143     	NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
144     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
145         }, {
146     	/* 1152x864 @ 84 Hz, 76.0 kHz hsync */
147     	NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12,
148     	0, FB_VMODE_NONINTERLACED
149         }, {
150     	/* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
151     	NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3,
152     	0, FB_VMODE_NONINTERLACED
153         }, {
154     	/* 1024x768 @ 100Hz, 80.21 kHz hsync */
155     	NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10,
156     	0, FB_VMODE_NONINTERLACED
157         }, {
158     	/* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
159     	NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3,
160     	0, FB_VMODE_NONINTERLACED
161         }, {
162     	/* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
163     	NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
164     	0, FB_VMODE_NONINTERLACED
165         }, {
166     	/* 1152x864 @ 100 Hz, 89.62 kHz hsync */
167     	NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19,
168     	0, FB_VMODE_NONINTERLACED
169         }, {
170     	/* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
171     	NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
172     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
173         }, {
174     	/* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
175     	NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
176     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
177         }, {
178     	/* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
179     	NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
180     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
181         }, {
182     	/* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
183     	NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15,
184     	0, FB_VMODE_NONINTERLACED
185         }, {
186     	/* 1800x1440 @ 64Hz, 96.15 kHz hsync  */
187     	NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
188     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
189         }, {
190     	/* 1800x1440 @ 70Hz, 104.52 kHz hsync  */
191     	NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
192     	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
193         }, {
194     	/* 512x384 @ 78 Hz, 31.50 kHz hsync */
195     	NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3,
196     	0, FB_VMODE_NONINTERLACED
197         }, {
198     	/* 512x384 @ 85 Hz, 34.38 kHz hsync */
199     	NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3,
200     	0, FB_VMODE_NONINTERLACED
201         }, {
202     	/* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
203     	NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1,
204     	0, FB_VMODE_DOUBLE
205         }, {
206     	/* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
207     	NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
208     	0, FB_VMODE_DOUBLE
209         }, {
210     	/* 320x240 @ 72 Hz, 36.5 kHz hsync */
211     	NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
212     	0, FB_VMODE_DOUBLE
213         }, {
214     	/* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
215     	NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
216     	0, FB_VMODE_DOUBLE
217         }, {
218     	/* 400x300 @ 60 Hz, 37.8 kHz hsync */
219     	NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2,
220     	0, FB_VMODE_DOUBLE
221         }, {
222     	/* 400x300 @ 72 Hz, 48.0 kHz hsync */
223     	NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3,
224     	0, FB_VMODE_DOUBLE
225         }, {
226     	/* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
227     	NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1,
228     	0, FB_VMODE_DOUBLE
229         }, {
230     	/* 480x300 @ 60 Hz, 37.8 kHz hsync */
231     	NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2,
232     	0, FB_VMODE_DOUBLE
233         }, {
234     	/* 480x300 @ 63 Hz, 39.6 kHz hsync */
235     	NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2,
236     	0, FB_VMODE_DOUBLE
237         }, {
238     	/* 480x300 @ 72 Hz, 48.0 kHz hsync */
239     	NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3,
240     	0, FB_VMODE_DOUBLE
241         },
242     };
243     
244     
245     static int __init my_atoi(const char *name)
246     {
247         int val = 0;
248     
249         for (;; name++) {
250     	switch (*name) {
251     	    case '0'...'9':
252     		val = 10*val+(*name-'0');
253     		break;
254     	    default:
255     		return val;
256     	}
257         }
258     }
259     
260     static int PROC_CONSOLE(const struct fb_info *info)
261     {
262     	int fgc;
263     	
264     	if (info->display_fg != NULL)
265     		fgc = info->display_fg->vc_num;
266     	else
267     		return -1;
268     		
269     	if (!current->tty)
270     		return fgc;
271     
272     	if (current->tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
273     		/* XXX Should report error here? */
274     		return fgc;
275     
276     	if (MINOR(current->tty->device) < 1)
277     		return fgc;
278     
279     	return MINOR(current->tty->device) - 1;
280     }
281     
282     
283     /**
284      *	__fb_try_mode - test a video mode
285      *	@var: frame buffer user defined part of display
286      *	@info: frame buffer info structure
287      *	@mode: frame buffer video mode structure
288      *	@bpp: color depth in bits per pixel
289      *
290      *	Tries a video mode to test it's validity for device @info.
291      *
292      *	Returns 1 on success.
293      *
294      */
295     
296     int __fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
297     		  const struct fb_videomode *mode, unsigned int bpp)
298     {
299         int err;
300     
301         DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname",
302     	    mode->xres, mode->yres, bpp, mode->refresh);
303         var->xres = mode->xres;
304         var->yres = mode->yres;
305         var->xres_virtual = mode->xres;
306         var->yres_virtual = mode->yres;
307         var->xoffset = 0;
308         var->yoffset = 0;
309         var->bits_per_pixel = bpp;
310         var->activate |= FB_ACTIVATE_TEST;
311         var->pixclock = mode->pixclock;
312         var->left_margin = mode->left_margin;
313         var->right_margin = mode->right_margin;
314         var->upper_margin = mode->upper_margin;
315         var->lower_margin = mode->lower_margin;
316         var->hsync_len = mode->hsync_len;
317         var->vsync_len = mode->vsync_len;
318         var->sync = mode->sync;
319         var->vmode = mode->vmode;
320         err = info->fbops->fb_set_var(var, PROC_CONSOLE(info), info);
321         var->activate &= ~FB_ACTIVATE_TEST;
322         return !err;
323     }
324     
325     
326     /**
327      *	fb_find_mode - finds a valid video mode
328      *	@var: frame buffer user defined part of display
329      *	@info: frame buffer info structure
330      *	@mode_option: string video mode to find
331      *	@db: video mode database
332      *	@dbsize: size of @db
333      *	@default_mode: default video mode to fall back to
334      *	@default_bpp: default color depth in bits per pixel
335      *
336      *	Finds a suitable video mode, starting with the specified mode
337      *	in @mode_option with fallback to @default_mode.  If
338      *	@default_mode fails, all modes in the video mode database will
339      *	be tried.
340      *
341      *	Valid mode specifiers for @mode_option:
342      *
343      *	<xres>x<yres>[-<bpp>][@<refresh>] or
344      *	<name>[-<bpp>][@<refresh>]
345      *
346      *	with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
347      *	<name> a string.
348      *
349      *	NOTE: The passed struct @var is _not_ cleared!  This allows you
350      *	to supply values for e.g. the grayscale and accel_flags fields.
351      *
352      *	Returns zero for failure, 1 if using specified @mode_option,
353      *	2 if using specified @mode_option with an ignored refresh rate,
354      *	3 if default mode is used, 4 if fall back to any valid mode.
355      *
356      */
357     
358     int __init fb_find_mode(struct fb_var_screeninfo *var,
359     			struct fb_info *info, const char *mode_option,
360     			const struct fb_videomode *db, unsigned int dbsize,
361     			const struct fb_videomode *default_mode,
362     			unsigned int default_bpp)
363     {
364         int i, j;
365     
366         /* Set up defaults */
367         if (!db) {
368     	db = modedb;
369     	dbsize = sizeof(modedb)/sizeof(*modedb);
370         }
371         if (!default_mode)
372     	default_mode = &modedb[DEFAULT_MODEDB_INDEX];
373         if (!default_bpp)
374     	default_bpp = 8;
375     
376         /* Did the user specify a video mode? */
377         if (mode_option || (mode_option = global_mode_option)) {
378     	const char *name = mode_option;
379     	unsigned int namelen = strlen(name);
380     	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
381     	unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
382     	int yres_specified = 0;
383     
384     	for (i = namelen-1; i >= 0; i--) {
385     	    switch (name[i]) {
386     		case '@':
387     		    namelen = i;
388     		    if (!refresh_specified && !bpp_specified &&
389     			!yres_specified) {
390     			refresh = my_atoi(&name[i+1]);
391     			refresh_specified = 1;
392     		    } else
393     			goto done;
394     		    break;
395     		case '-':
396     		    namelen = i;
397     		    if (!bpp_specified && !yres_specified) {
398     			bpp = my_atoi(&name[i+1]);
399     			bpp_specified = 1;
400     		    } else
401     			goto done;
402     		    break;
403     		case 'x':
404     		    if (!yres_specified) {
405     			yres = my_atoi(&name[i+1]);
406     			yres_specified = 1;
407     		    } else
408     			goto done;
409     		    break;
410     		case '0'...'9':
411     		    break;
412     		default:
413     		    goto done;
414     	    }
415     	}
416     	if (i < 0 && yres_specified) {
417     	    xres = my_atoi(name);
418     	    res_specified = 1;
419     	}
420     done:
421     	for (i = refresh_specified; i >= 0; i--) {
422     	    DPRINTK("Trying specified video mode%s\n",
423     		    i ? "" : " (ignoring refresh rate)");
424     	    for (j = 0; j < dbsize; j++)
425     		if ((name_matches(db[j], name, namelen) ||
426     		     (res_specified && res_matches(db[j], xres, yres))) &&
427     		    (!i || db[j].refresh == refresh) &&
428     		    __fb_try_mode(var, info, &db[j], bpp))
429     		    return 2-i;
430     	}
431         }
432     
433         DPRINTK("Trying default video mode\n");
434         if (__fb_try_mode(var, info, default_mode, default_bpp))
435     	return 3;
436     
437         DPRINTK("Trying all modes\n");
438         for (i = 0; i < dbsize; i++)
439     	if (__fb_try_mode(var, info, &db[i], default_bpp))
440     	    return 4;
441     
442         DPRINTK("No valid mode found\n");
443         return 0;
444     }
445     
446     EXPORT_SYMBOL(__fb_try_mode);
447