aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/core
diff options
context:
space:
mode:
authorKukjin Kim <kgene.kim@samsung.com>2014-05-30 13:36:49 -0400
committerKukjin Kim <kgene.kim@samsung.com>2014-05-30 13:36:49 -0400
commitfced6dee29f6fb143fe16ea90331176ff77e6120 (patch)
tree5b6e57e7a757adc2a6518ce291a4d2914397b917 /drivers/video/fbdev/core
parentbfed1074f213051e94648bfad0d0611a16d81366 (diff)
parentbe1f7c8d7e2bc8b8c76846aa6f276e8d2ef8975a (diff)
Merge branch 'v3.16-next/cleanup-samsung' into v3.16-next/platform-exynos
Diffstat (limited to 'drivers/video/fbdev/core')
-rw-r--r--drivers/video/fbdev/core/Makefile16
-rw-r--r--drivers/video/fbdev/core/cfbcopyarea.c434
-rw-r--r--drivers/video/fbdev/core/cfbfillrect.c371
-rw-r--r--drivers/video/fbdev/core/cfbimgblt.c313
-rw-r--r--drivers/video/fbdev/core/fb_ddc.c119
-rw-r--r--drivers/video/fbdev/core/fb_defio.c245
-rw-r--r--drivers/video/fbdev/core/fb_draw.h186
-rw-r--r--drivers/video/fbdev/core/fb_notify.c47
-rw-r--r--drivers/video/fbdev/core/fb_sys_fops.c104
-rw-r--r--drivers/video/fbdev/core/fbcmap.c362
-rw-r--r--drivers/video/fbdev/core/fbcvt.c379
-rw-r--r--drivers/video/fbdev/core/fbmem.c2002
-rw-r--r--drivers/video/fbdev/core/fbmon.c1592
-rw-r--r--drivers/video/fbdev/core/fbsysfs.c586
-rw-r--r--drivers/video/fbdev/core/modedb.c1137
-rw-r--r--drivers/video/fbdev/core/svgalib.c672
-rw-r--r--drivers/video/fbdev/core/syscopyarea.c377
-rw-r--r--drivers/video/fbdev/core/sysfillrect.c335
-rw-r--r--drivers/video/fbdev/core/sysimgblt.c288
19 files changed, 9565 insertions, 0 deletions
diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile
new file mode 100644
index 000000000000..fa306538dac2
--- /dev/null
+++ b/drivers/video/fbdev/core/Makefile
@@ -0,0 +1,16 @@
1obj-y += fb_notify.o
2obj-$(CONFIG_FB) += fb.o
3fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
4 modedb.o fbcvt.o
5fb-objs := $(fb-y)
6
7obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
8obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
9obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
10obj-$(CONFIG_FB_SYS_FILLRECT) += sysfillrect.o
11obj-$(CONFIG_FB_SYS_COPYAREA) += syscopyarea.o
12obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
13obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o
14obj-$(CONFIG_FB_SVGALIB) += svgalib.o
15obj-$(CONFIG_FB_DDC) += fb_ddc.o
16obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
diff --git a/drivers/video/fbdev/core/cfbcopyarea.c b/drivers/video/fbdev/core/cfbcopyarea.c
new file mode 100644
index 000000000000..bcb57235fcc7
--- /dev/null
+++ b/drivers/video/fbdev/core/cfbcopyarea.c
@@ -0,0 +1,434 @@
1/*
2 * Generic function for frame buffer with packed pixels of any depth.
3 *
4 * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 *
10 * NOTES:
11 *
12 * This is for cfb packed pixels. Iplan and such are incorporated in the
13 * drivers that need them.
14 *
15 * FIXME
16 *
17 * Also need to add code to deal with cards endians that are different than
18 * the native cpu endians. I also need to deal with MSB position in the word.
19 *
20 * The two functions or copying forward and backward could be split up like
21 * the ones for filling, i.e. in aligned and unaligned versions. This would
22 * help moving some redundant computations and branches out of the loop, too.
23 */
24
25#include <linux/module.h>
26#include <linux/kernel.h>
27#include <linux/string.h>
28#include <linux/fb.h>
29#include <asm/types.h>
30#include <asm/io.h>
31#include "fb_draw.h"
32
33#if BITS_PER_LONG == 32
34# define FB_WRITEL fb_writel
35# define FB_READL fb_readl
36#else
37# define FB_WRITEL fb_writeq
38# define FB_READL fb_readq
39#endif
40
41 /*
42 * Generic bitwise copy algorithm
43 */
44
45static void
46bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
47 const unsigned long __iomem *src, unsigned src_idx, int bits,
48 unsigned n, u32 bswapmask)
49{
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
52
53#if 0
54 /*
55 * If you suspect bug in this function, compare it with this simple
56 * memmove implementation.
57 */
58 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
59 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
60 return;
61#endif
62
63 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
64 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
65
66 if (!shift) {
67 // Same alignment for source and dest
68
69 if (dst_idx+n <= bits) {
70 // Single word
71 if (last)
72 first &= last;
73 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
74 } else {
75 // Multiple destination words
76
77 // Leading bits
78 if (first != ~0UL) {
79 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
80 dst++;
81 src++;
82 n -= bits - dst_idx;
83 }
84
85 // Main chunk
86 n /= bits;
87 while (n >= 8) {
88 FB_WRITEL(FB_READL(src++), dst++);
89 FB_WRITEL(FB_READL(src++), dst++);
90 FB_WRITEL(FB_READL(src++), dst++);
91 FB_WRITEL(FB_READL(src++), dst++);
92 FB_WRITEL(FB_READL(src++), dst++);
93 FB_WRITEL(FB_READL(src++), dst++);
94 FB_WRITEL(FB_READL(src++), dst++);
95 FB_WRITEL(FB_READL(src++), dst++);
96 n -= 8;
97 }
98 while (n--)
99 FB_WRITEL(FB_READL(src++), dst++);
100
101 // Trailing bits
102 if (last)
103 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
104 }
105 } else {
106 /* Different alignment for source and dest */
107 unsigned long d0, d1;
108 int m;
109
110 int const left = shift & (bits - 1);
111 int const right = -shift & (bits - 1);
112
113 if (dst_idx+n <= bits) {
114 // Single destination word
115 if (last)
116 first &= last;
117 d0 = FB_READL(src);
118 d0 = fb_rev_pixels_in_long(d0, bswapmask);
119 if (shift > 0) {
120 // Single source word
121 d0 <<= left;
122 } else if (src_idx+n <= bits) {
123 // Single source word
124 d0 >>= right;
125 } else {
126 // 2 source words
127 d1 = FB_READL(src + 1);
128 d1 = fb_rev_pixels_in_long(d1, bswapmask);
129 d0 = d0 >> right | d1 << left;
130 }
131 d0 = fb_rev_pixels_in_long(d0, bswapmask);
132 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
133 } else {
134 // Multiple destination words
135 /** We must always remember the last value read, because in case
136 SRC and DST overlap bitwise (e.g. when moving just one pixel in
137 1bpp), we always collect one full long for DST and that might
138 overlap with the current long from SRC. We store this value in
139 'd0'. */
140 d0 = FB_READL(src++);
141 d0 = fb_rev_pixels_in_long(d0, bswapmask);
142 // Leading bits
143 if (shift > 0) {
144 // Single source word
145 d1 = d0;
146 d0 <<= left;
147 n -= bits - dst_idx;
148 } else {
149 // 2 source words
150 d1 = FB_READL(src++);
151 d1 = fb_rev_pixels_in_long(d1, bswapmask);
152
153 d0 = d0 >> right | d1 << left;
154 n -= bits - dst_idx;
155 }
156 d0 = fb_rev_pixels_in_long(d0, bswapmask);
157 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
158 d0 = d1;
159 dst++;
160
161 // Main chunk
162 m = n % bits;
163 n /= bits;
164 while ((n >= 4) && !bswapmask) {
165 d1 = FB_READL(src++);
166 FB_WRITEL(d0 >> right | d1 << left, dst++);
167 d0 = d1;
168 d1 = FB_READL(src++);
169 FB_WRITEL(d0 >> right | d1 << left, dst++);
170 d0 = d1;
171 d1 = FB_READL(src++);
172 FB_WRITEL(d0 >> right | d1 << left, dst++);
173 d0 = d1;
174 d1 = FB_READL(src++);
175 FB_WRITEL(d0 >> right | d1 << left, dst++);
176 d0 = d1;
177 n -= 4;
178 }
179 while (n--) {
180 d1 = FB_READL(src++);
181 d1 = fb_rev_pixels_in_long(d1, bswapmask);
182 d0 = d0 >> right | d1 << left;
183 d0 = fb_rev_pixels_in_long(d0, bswapmask);
184 FB_WRITEL(d0, dst++);
185 d0 = d1;
186 }
187
188 // Trailing bits
189 if (m) {
190 if (m <= bits - right) {
191 // Single source word
192 d0 >>= right;
193 } else {
194 // 2 source words
195 d1 = FB_READL(src);
196 d1 = fb_rev_pixels_in_long(d1,
197 bswapmask);
198 d0 = d0 >> right | d1 << left;
199 }
200 d0 = fb_rev_pixels_in_long(d0, bswapmask);
201 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
202 }
203 }
204 }
205}
206
207 /*
208 * Generic bitwise copy algorithm, operating backward
209 */
210
211static void
212bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
213 const unsigned long __iomem *src, unsigned src_idx, int bits,
214 unsigned n, u32 bswapmask)
215{
216 unsigned long first, last;
217 int shift;
218
219#if 0
220 /*
221 * If you suspect bug in this function, compare it with this simple
222 * memmove implementation.
223 */
224 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
225 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
226 return;
227#endif
228
229 dst += (dst_idx + n - 1) / bits;
230 src += (src_idx + n - 1) / bits;
231 dst_idx = (dst_idx + n - 1) % bits;
232 src_idx = (src_idx + n - 1) % bits;
233
234 shift = dst_idx-src_idx;
235
236 first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
237 last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
238
239 if (!shift) {
240 // Same alignment for source and dest
241
242 if ((unsigned long)dst_idx+1 >= n) {
243 // Single word
244 if (first)
245 last &= first;
246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
247 } else {
248 // Multiple destination words
249
250 // Leading bits
251 if (first) {
252 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
253 dst--;
254 src--;
255 n -= dst_idx+1;
256 }
257
258 // Main chunk
259 n /= bits;
260 while (n >= 8) {
261 FB_WRITEL(FB_READL(src--), dst--);
262 FB_WRITEL(FB_READL(src--), dst--);
263 FB_WRITEL(FB_READL(src--), dst--);
264 FB_WRITEL(FB_READL(src--), dst--);
265 FB_WRITEL(FB_READL(src--), dst--);
266 FB_WRITEL(FB_READL(src--), dst--);
267 FB_WRITEL(FB_READL(src--), dst--);
268 FB_WRITEL(FB_READL(src--), dst--);
269 n -= 8;
270 }
271 while (n--)
272 FB_WRITEL(FB_READL(src--), dst--);
273
274 // Trailing bits
275 if (last != -1UL)
276 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
277 }
278 } else {
279 // Different alignment for source and dest
280 unsigned long d0, d1;
281 int m;
282
283 int const left = shift & (bits-1);
284 int const right = -shift & (bits-1);
285
286 if ((unsigned long)dst_idx+1 >= n) {
287 // Single destination word
288 if (first)
289 last &= first;
290 d0 = FB_READL(src);
291 if (shift < 0) {
292 // Single source word
293 d0 >>= right;
294 } else if (1+(unsigned long)src_idx >= n) {
295 // Single source word
296 d0 <<= left;
297 } else {
298 // 2 source words
299 d1 = FB_READL(src - 1);
300 d1 = fb_rev_pixels_in_long(d1, bswapmask);
301 d0 = d0 << left | d1 >> right;
302 }
303 d0 = fb_rev_pixels_in_long(d0, bswapmask);
304 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
305 } else {
306 // Multiple destination words
307 /** We must always remember the last value read, because in case
308 SRC and DST overlap bitwise (e.g. when moving just one pixel in
309 1bpp), we always collect one full long for DST and that might
310 overlap with the current long from SRC. We store this value in
311 'd0'. */
312
313 d0 = FB_READL(src--);
314 d0 = fb_rev_pixels_in_long(d0, bswapmask);
315 // Leading bits
316 if (shift < 0) {
317 // Single source word
318 d1 = d0;
319 d0 >>= right;
320 } else {
321 // 2 source words
322 d1 = FB_READL(src--);
323 d1 = fb_rev_pixels_in_long(d1, bswapmask);
324 d0 = d0 << left | d1 >> right;
325 }
326 d0 = fb_rev_pixels_in_long(d0, bswapmask);
327 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
328 d0 = d1;
329 dst--;
330 n -= dst_idx+1;
331
332 // Main chunk
333 m = n % bits;
334 n /= bits;
335 while ((n >= 4) && !bswapmask) {
336 d1 = FB_READL(src--);
337 FB_WRITEL(d0 << left | d1 >> right, dst--);
338 d0 = d1;
339 d1 = FB_READL(src--);
340 FB_WRITEL(d0 << left | d1 >> right, dst--);
341 d0 = d1;
342 d1 = FB_READL(src--);
343 FB_WRITEL(d0 << left | d1 >> right, dst--);
344 d0 = d1;
345 d1 = FB_READL(src--);
346 FB_WRITEL(d0 << left | d1 >> right, dst--);
347 d0 = d1;
348 n -= 4;
349 }
350 while (n--) {
351 d1 = FB_READL(src--);
352 d1 = fb_rev_pixels_in_long(d1, bswapmask);
353 d0 = d0 << left | d1 >> right;
354 d0 = fb_rev_pixels_in_long(d0, bswapmask);
355 FB_WRITEL(d0, dst--);
356 d0 = d1;
357 }
358
359 // Trailing bits
360 if (m) {
361 if (m <= bits - left) {
362 // Single source word
363 d0 <<= left;
364 } else {
365 // 2 source words
366 d1 = FB_READL(src);
367 d1 = fb_rev_pixels_in_long(d1,
368 bswapmask);
369 d0 = d0 << left | d1 >> right;
370 }
371 d0 = fb_rev_pixels_in_long(d0, bswapmask);
372 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
373 }
374 }
375 }
376}
377
378void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
379{
380 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
381 u32 height = area->height, width = area->width;
382 unsigned long const bits_per_line = p->fix.line_length*8u;
383 unsigned long __iomem *base = NULL;
384 int bits = BITS_PER_LONG, bytes = bits >> 3;
385 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
386 u32 bswapmask = fb_compute_bswapmask(p);
387
388 if (p->state != FBINFO_STATE_RUNNING)
389 return;
390
391 /* if the beginning of the target area might overlap with the end of
392 the source area, be have to copy the area reverse. */
393 if ((dy == sy && dx > sx) || (dy > sy)) {
394 dy += height;
395 sy += height;
396 rev_copy = 1;
397 }
398
399 // split the base of the framebuffer into a long-aligned address and the
400 // index of the first bit
401 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
402 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
403 // add offset of source and target area
404 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
405 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
406
407 if (p->fbops->fb_sync)
408 p->fbops->fb_sync(p);
409
410 if (rev_copy) {
411 while (height--) {
412 dst_idx -= bits_per_line;
413 src_idx -= bits_per_line;
414 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
415 base + (src_idx / bits), src_idx % bits, bits,
416 width*p->var.bits_per_pixel, bswapmask);
417 }
418 } else {
419 while (height--) {
420 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
421 base + (src_idx / bits), src_idx % bits, bits,
422 width*p->var.bits_per_pixel, bswapmask);
423 dst_idx += bits_per_line;
424 src_idx += bits_per_line;
425 }
426 }
427}
428
429EXPORT_SYMBOL(cfb_copyarea);
430
431MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
432MODULE_DESCRIPTION("Generic software accelerated copyarea");
433MODULE_LICENSE("GPL");
434
diff --git a/drivers/video/fbdev/core/cfbfillrect.c b/drivers/video/fbdev/core/cfbfillrect.c
new file mode 100644
index 000000000000..ba9f58b2a5e8
--- /dev/null
+++ b/drivers/video/fbdev/core/cfbfillrect.c
@@ -0,0 +1,371 @@
1/*
2 * Generic fillrect for frame buffers with packed pixels of any depth.
3 *
4 * Copyright (C) 2000 James Simmons (jsimmons@linux-fbdev.org)
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 *
10 * NOTES:
11 *
12 * Also need to add code to deal with cards endians that are different than
13 * the native cpu endians. I also need to deal with MSB position in the word.
14 *
15 */
16#include <linux/module.h>
17#include <linux/string.h>
18#include <linux/fb.h>
19#include <asm/types.h>
20#include "fb_draw.h"
21
22#if BITS_PER_LONG == 32
23# define FB_WRITEL fb_writel
24# define FB_READL fb_readl
25#else
26# define FB_WRITEL fb_writeq
27# define FB_READL fb_readq
28#endif
29
30 /*
31 * Aligned pattern fill using 32/64-bit memory accesses
32 */
33
34static void
35bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
36 unsigned long pat, unsigned n, int bits, u32 bswapmask)
37{
38 unsigned long first, last;
39
40 if (!n)
41 return;
42
43 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
44 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
45
46 if (dst_idx+n <= bits) {
47 // Single word
48 if (last)
49 first &= last;
50 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
51 } else {
52 // Multiple destination words
53
54 // Leading bits
55 if (first!= ~0UL) {
56 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
57 dst++;
58 n -= bits - dst_idx;
59 }
60
61 // Main chunk
62 n /= bits;
63 while (n >= 8) {
64 FB_WRITEL(pat, dst++);
65 FB_WRITEL(pat, dst++);
66 FB_WRITEL(pat, dst++);
67 FB_WRITEL(pat, dst++);
68 FB_WRITEL(pat, dst++);
69 FB_WRITEL(pat, dst++);
70 FB_WRITEL(pat, dst++);
71 FB_WRITEL(pat, dst++);
72 n -= 8;
73 }
74 while (n--)
75 FB_WRITEL(pat, dst++);
76
77 // Trailing bits
78 if (last)
79 FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
80 }
81}
82
83
84 /*
85 * Unaligned generic pattern fill using 32/64-bit memory accesses
86 * The pattern must have been expanded to a full 32/64-bit value
87 * Left/right are the appropriate shifts to convert to the pattern to be
88 * used for the next 32/64-bit word
89 */
90
91static void
92bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
93 unsigned long pat, int left, int right, unsigned n, int bits)
94{
95 unsigned long first, last;
96
97 if (!n)
98 return;
99
100 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
101 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
102
103 if (dst_idx+n <= bits) {
104 // Single word
105 if (last)
106 first &= last;
107 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
108 } else {
109 // Multiple destination words
110 // Leading bits
111 if (first) {
112 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
113 dst++;
114 pat = pat << left | pat >> right;
115 n -= bits - dst_idx;
116 }
117
118 // Main chunk
119 n /= bits;
120 while (n >= 4) {
121 FB_WRITEL(pat, dst++);
122 pat = pat << left | pat >> right;
123 FB_WRITEL(pat, dst++);
124 pat = pat << left | pat >> right;
125 FB_WRITEL(pat, dst++);
126 pat = pat << left | pat >> right;
127 FB_WRITEL(pat, dst++);
128 pat = pat << left | pat >> right;
129 n -= 4;
130 }
131 while (n--) {
132 FB_WRITEL(pat, dst++);
133 pat = pat << left | pat >> right;
134 }
135
136 // Trailing bits
137 if (last)
138 FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
139 }
140}
141
142 /*
143 * Aligned pattern invert using 32/64-bit memory accesses
144 */
145static void
146bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst,
147 int dst_idx, unsigned long pat, unsigned n, int bits,
148 u32 bswapmask)
149{
150 unsigned long val = pat, dat;
151 unsigned long first, last;
152
153 if (!n)
154 return;
155
156 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
157 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
158
159 if (dst_idx+n <= bits) {
160 // Single word
161 if (last)
162 first &= last;
163 dat = FB_READL(dst);
164 FB_WRITEL(comp(dat ^ val, dat, first), dst);
165 } else {
166 // Multiple destination words
167 // Leading bits
168 if (first!=0UL) {
169 dat = FB_READL(dst);
170 FB_WRITEL(comp(dat ^ val, dat, first), dst);
171 dst++;
172 n -= bits - dst_idx;
173 }
174
175 // Main chunk
176 n /= bits;
177 while (n >= 8) {
178 FB_WRITEL(FB_READL(dst) ^ val, dst);
179 dst++;
180 FB_WRITEL(FB_READL(dst) ^ val, dst);
181 dst++;
182 FB_WRITEL(FB_READL(dst) ^ val, dst);
183 dst++;
184 FB_WRITEL(FB_READL(dst) ^ val, dst);
185 dst++;
186 FB_WRITEL(FB_READL(dst) ^ val, dst);
187 dst++;
188 FB_WRITEL(FB_READL(dst) ^ val, dst);
189 dst++;
190 FB_WRITEL(FB_READL(dst) ^ val, dst);
191 dst++;
192 FB_WRITEL(FB_READL(dst) ^ val, dst);
193 dst++;
194 n -= 8;
195 }
196 while (n--) {
197 FB_WRITEL(FB_READL(dst) ^ val, dst);
198 dst++;
199 }
200 // Trailing bits
201 if (last) {
202 dat = FB_READL(dst);
203 FB_WRITEL(comp(dat ^ val, dat, last), dst);
204 }
205 }
206}
207
208
209 /*
210 * Unaligned generic pattern invert using 32/64-bit memory accesses
211 * The pattern must have been expanded to a full 32/64-bit value
212 * Left/right are the appropriate shifts to convert to the pattern to be
213 * used for the next 32/64-bit word
214 */
215
216static void
217bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst,
218 int dst_idx, unsigned long pat, int left, int right,
219 unsigned n, int bits)
220{
221 unsigned long first, last, dat;
222
223 if (!n)
224 return;
225
226 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
227 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
228
229 if (dst_idx+n <= bits) {
230 // Single word
231 if (last)
232 first &= last;
233 dat = FB_READL(dst);
234 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
235 } else {
236 // Multiple destination words
237
238 // Leading bits
239 if (first != 0UL) {
240 dat = FB_READL(dst);
241 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
242 dst++;
243 pat = pat << left | pat >> right;
244 n -= bits - dst_idx;
245 }
246
247 // Main chunk
248 n /= bits;
249 while (n >= 4) {
250 FB_WRITEL(FB_READL(dst) ^ pat, dst);
251 dst++;
252 pat = pat << left | pat >> right;
253 FB_WRITEL(FB_READL(dst) ^ pat, dst);
254 dst++;
255 pat = pat << left | pat >> right;
256 FB_WRITEL(FB_READL(dst) ^ pat, dst);
257 dst++;
258 pat = pat << left | pat >> right;
259 FB_WRITEL(FB_READL(dst) ^ pat, dst);
260 dst++;
261 pat = pat << left | pat >> right;
262 n -= 4;
263 }
264 while (n--) {
265 FB_WRITEL(FB_READL(dst) ^ pat, dst);
266 dst++;
267 pat = pat << left | pat >> right;
268 }
269
270 // Trailing bits
271 if (last) {
272 dat = FB_READL(dst);
273 FB_WRITEL(comp(dat ^ pat, dat, last), dst);
274 }
275 }
276}
277
278void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
279{
280 unsigned long pat, pat2, fg;
281 unsigned long width = rect->width, height = rect->height;
282 int bits = BITS_PER_LONG, bytes = bits >> 3;
283 u32 bpp = p->var.bits_per_pixel;
284 unsigned long __iomem *dst;
285 int dst_idx, left;
286
287 if (p->state != FBINFO_STATE_RUNNING)
288 return;
289
290 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
291 p->fix.visual == FB_VISUAL_DIRECTCOLOR )
292 fg = ((u32 *) (p->pseudo_palette))[rect->color];
293 else
294 fg = rect->color;
295
296 pat = pixel_to_pat(bpp, fg);
297
298 dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
299 dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
300 dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
301 /* FIXME For now we support 1-32 bpp only */
302 left = bits % bpp;
303 if (p->fbops->fb_sync)
304 p->fbops->fb_sync(p);
305 if (!left) {
306 u32 bswapmask = fb_compute_bswapmask(p);
307 void (*fill_op32)(struct fb_info *p,
308 unsigned long __iomem *dst, int dst_idx,
309 unsigned long pat, unsigned n, int bits,
310 u32 bswapmask) = NULL;
311
312 switch (rect->rop) {
313 case ROP_XOR:
314 fill_op32 = bitfill_aligned_rev;
315 break;
316 case ROP_COPY:
317 fill_op32 = bitfill_aligned;
318 break;
319 default:
320 printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
321 fill_op32 = bitfill_aligned;
322 break;
323 }
324 while (height--) {
325 dst += dst_idx >> (ffs(bits) - 1);
326 dst_idx &= (bits - 1);
327 fill_op32(p, dst, dst_idx, pat, width*bpp, bits,
328 bswapmask);
329 dst_idx += p->fix.line_length*8;
330 }
331 } else {
332 int right, r;
333 void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,
334 int dst_idx, unsigned long pat, int left,
335 int right, unsigned n, int bits) = NULL;
336#ifdef __LITTLE_ENDIAN
337 right = left;
338 left = bpp - right;
339#else
340 right = bpp - left;
341#endif
342 switch (rect->rop) {
343 case ROP_XOR:
344 fill_op = bitfill_unaligned_rev;
345 break;
346 case ROP_COPY:
347 fill_op = bitfill_unaligned;
348 break;
349 default:
350 printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
351 fill_op = bitfill_unaligned;
352 break;
353 }
354 while (height--) {
355 dst += dst_idx / bits;
356 dst_idx &= (bits - 1);
357 r = dst_idx % bpp;
358 /* rotate pattern to the correct start position */
359 pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
360 fill_op(p, dst, dst_idx, pat2, left, right,
361 width*bpp, bits);
362 dst_idx += p->fix.line_length*8;
363 }
364 }
365}
366
367EXPORT_SYMBOL(cfb_fillrect);
368
369MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
370MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
371MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c
new file mode 100644
index 000000000000..a2bb276a8b24
--- /dev/null
+++ b/drivers/video/fbdev/core/cfbimgblt.c
@@ -0,0 +1,313 @@
1/*
2 * Generic BitBLT function for frame buffer with packed pixels of any depth.
3 *
4 * Copyright (C) June 1999 James Simmons
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 *
10 * NOTES:
11 *
12 * This function copys a image from system memory to video memory. The
13 * image can be a bitmap where each 0 represents the background color and
14 * each 1 represents the foreground color. Great for font handling. It can
15 * also be a color image. This is determined by image_depth. The color image
16 * must be laid out exactly in the same format as the framebuffer. Yes I know
17 * their are cards with hardware that coverts images of various depths to the
18 * framebuffer depth. But not every card has this. All images must be rounded
19 * up to the nearest byte. For example a bitmap 12 bits wide must be two
20 * bytes width.
21 *
22 * Tony:
23 * Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds
24 * up the code significantly.
25 *
26 * Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
27 * still processed a bit at a time.
28 *
29 * Also need to add code to deal with cards endians that are different than
30 * the native cpu endians. I also need to deal with MSB position in the word.
31 */
32#include <linux/module.h>
33#include <linux/string.h>
34#include <linux/fb.h>
35#include <asm/types.h>
36#include "fb_draw.h"
37
38#define DEBUG
39
40#ifdef DEBUG
41#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
42#else
43#define DPRINTK(fmt, args...)
44#endif
45
46static const u32 cfb_tab8_be[] = {
47 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
48 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
49 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
50 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
51};
52
53static const u32 cfb_tab8_le[] = {
54 0x00000000,0xff000000,0x00ff0000,0xffff0000,
55 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
56 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
57 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
58};
59
60static const u32 cfb_tab16_be[] = {
61 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
62};
63
64static const u32 cfb_tab16_le[] = {
65 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
66};
67
68static const u32 cfb_tab32[] = {
69 0x00000000, 0xffffffff
70};
71
72#define FB_WRITEL fb_writel
73#define FB_READL fb_readl
74
75static inline void color_imageblit(const struct fb_image *image,
76 struct fb_info *p, u8 __iomem *dst1,
77 u32 start_index,
78 u32 pitch_index)
79{
80 /* Draw the penguin */
81 u32 __iomem *dst, *dst2;
82 u32 color = 0, val, shift;
83 int i, n, bpp = p->var.bits_per_pixel;
84 u32 null_bits = 32 - bpp;
85 u32 *palette = (u32 *) p->pseudo_palette;
86 const u8 *src = image->data;
87 u32 bswapmask = fb_compute_bswapmask(p);
88
89 dst2 = (u32 __iomem *) dst1;
90 for (i = image->height; i--; ) {
91 n = image->width;
92 dst = (u32 __iomem *) dst1;
93 shift = 0;
94 val = 0;
95
96 if (start_index) {
97 u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
98 start_index, bswapmask);
99 val = FB_READL(dst) & start_mask;
100 shift = start_index;
101 }
102 while (n--) {
103 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
104 p->fix.visual == FB_VISUAL_DIRECTCOLOR )
105 color = palette[*src];
106 else
107 color = *src;
108 color <<= FB_LEFT_POS(p, bpp);
109 val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
110 if (shift >= null_bits) {
111 FB_WRITEL(val, dst++);
112
113 val = (shift == null_bits) ? 0 :
114 FB_SHIFT_LOW(p, color, 32 - shift);
115 }
116 shift += bpp;
117 shift &= (32 - 1);
118 src++;
119 }
120 if (shift) {
121 u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
122 bswapmask);
123
124 FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
125 }
126 dst1 += p->fix.line_length;
127 if (pitch_index) {
128 dst2 += p->fix.line_length;
129 dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
130
131 start_index += pitch_index;
132 start_index &= 32 - 1;
133 }
134 }
135}
136
137static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
138 u8 __iomem *dst1, u32 fgcolor,
139 u32 bgcolor,
140 u32 start_index,
141 u32 pitch_index)
142{
143 u32 shift, color = 0, bpp = p->var.bits_per_pixel;
144 u32 __iomem *dst, *dst2;
145 u32 val, pitch = p->fix.line_length;
146 u32 null_bits = 32 - bpp;
147 u32 spitch = (image->width+7)/8;
148 const u8 *src = image->data, *s;
149 u32 i, j, l;
150 u32 bswapmask = fb_compute_bswapmask(p);
151
152 dst2 = (u32 __iomem *) dst1;
153 fgcolor <<= FB_LEFT_POS(p, bpp);
154 bgcolor <<= FB_LEFT_POS(p, bpp);
155
156 for (i = image->height; i--; ) {
157 shift = val = 0;
158 l = 8;
159 j = image->width;
160 dst = (u32 __iomem *) dst1;
161 s = src;
162
163 /* write leading bits */
164 if (start_index) {
165 u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
166 start_index, bswapmask);
167 val = FB_READL(dst) & start_mask;
168 shift = start_index;
169 }
170
171 while (j--) {
172 l--;
173 color = (*s & (1 << l)) ? fgcolor : bgcolor;
174 val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
175
176 /* Did the bitshift spill bits to the next long? */
177 if (shift >= null_bits) {
178 FB_WRITEL(val, dst++);
179 val = (shift == null_bits) ? 0 :
180 FB_SHIFT_LOW(p, color, 32 - shift);
181 }
182 shift += bpp;
183 shift &= (32 - 1);
184 if (!l) { l = 8; s++; }
185 }
186
187 /* write trailing bits */
188 if (shift) {
189 u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
190 bswapmask);
191
192 FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
193 }
194
195 dst1 += pitch;
196 src += spitch;
197 if (pitch_index) {
198 dst2 += pitch;
199 dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
200 start_index += pitch_index;
201 start_index &= 32 - 1;
202 }
203
204 }
205}
206
207/*
208 * fast_imageblit - optimized monochrome color expansion
209 *
210 * Only if: bits_per_pixel == 8, 16, or 32
211 * image->width is divisible by pixel/dword (ppw);
212 * fix->line_legth is divisible by 4;
213 * beginning and end of a scanline is dword aligned
214 */
215static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
216 u8 __iomem *dst1, u32 fgcolor,
217 u32 bgcolor)
218{
219 u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
220 u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
221 u32 bit_mask, end_mask, eorx, shift;
222 const char *s = image->data, *src;
223 u32 __iomem *dst;
224 const u32 *tab = NULL;
225 int i, j, k;
226
227 switch (bpp) {
228 case 8:
229 tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
230 break;
231 case 16:
232 tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
233 break;
234 case 32:
235 default:
236 tab = cfb_tab32;
237 break;
238 }
239
240 for (i = ppw-1; i--; ) {
241 fgx <<= bpp;
242 bgx <<= bpp;
243 fgx |= fgcolor;
244 bgx |= bgcolor;
245 }
246
247 bit_mask = (1 << ppw) - 1;
248 eorx = fgx ^ bgx;
249 k = image->width/ppw;
250
251 for (i = image->height; i--; ) {
252 dst = (u32 __iomem *) dst1, shift = 8; src = s;
253
254 for (j = k; j--; ) {
255 shift -= ppw;
256 end_mask = tab[(*src >> shift) & bit_mask];
257 FB_WRITEL((end_mask & eorx)^bgx, dst++);
258 if (!shift) { shift = 8; src++; }
259 }
260 dst1 += p->fix.line_length;
261 s += spitch;
262 }
263}
264
265void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
266{
267 u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
268 u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
269 u32 width = image->width;
270 u32 dx = image->dx, dy = image->dy;
271 u8 __iomem *dst1;
272
273 if (p->state != FBINFO_STATE_RUNNING)
274 return;
275
276 bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
277 start_index = bitstart & (32 - 1);
278 pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
279
280 bitstart /= 8;
281 bitstart &= ~(bpl - 1);
282 dst1 = p->screen_base + bitstart;
283
284 if (p->fbops->fb_sync)
285 p->fbops->fb_sync(p);
286
287 if (image->depth == 1) {
288 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
289 p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
290 fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
291 bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
292 } else {
293 fgcolor = image->fg_color;
294 bgcolor = image->bg_color;
295 }
296
297 if (32 % bpp == 0 && !start_index && !pitch_index &&
298 ((width & (32/bpp-1)) == 0) &&
299 bpp >= 8 && bpp <= 32)
300 fast_imageblit(image, p, dst1, fgcolor, bgcolor);
301 else
302 slow_imageblit(image, p, dst1, fgcolor, bgcolor,
303 start_index, pitch_index);
304 } else
305 color_imageblit(image, p, dst1, start_index, pitch_index);
306}
307
308EXPORT_SYMBOL(cfb_imageblit);
309
310MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
311MODULE_DESCRIPTION("Generic software accelerated imaging drawing");
312MODULE_LICENSE("GPL");
313
diff --git a/drivers/video/fbdev/core/fb_ddc.c b/drivers/video/fbdev/core/fb_ddc.c
new file mode 100644
index 000000000000..94322ccfedde
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_ddc.c
@@ -0,0 +1,119 @@
1/*
2 * drivers/video/fb_ddc.c - DDC/EDID read support.
3 *
4 * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/delay.h>
12#include <linux/device.h>
13#include <linux/module.h>
14#include <linux/fb.h>
15#include <linux/i2c-algo-bit.h>
16#include <linux/slab.h>
17
18#include "../edid.h"
19
20#define DDC_ADDR 0x50
21
22static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter)
23{
24 unsigned char start = 0x0;
25 unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
26 struct i2c_msg msgs[] = {
27 {
28 .addr = DDC_ADDR,
29 .flags = 0,
30 .len = 1,
31 .buf = &start,
32 }, {
33 .addr = DDC_ADDR,
34 .flags = I2C_M_RD,
35 .len = EDID_LENGTH,
36 .buf = buf,
37 }
38 };
39
40 if (!buf) {
41 dev_warn(&adapter->dev, "unable to allocate memory for EDID "
42 "block.\n");
43 return NULL;
44 }
45
46 if (i2c_transfer(adapter, msgs, 2) == 2)
47 return buf;
48
49 dev_warn(&adapter->dev, "unable to read EDID block.\n");
50 kfree(buf);
51 return NULL;
52}
53
54unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
55{
56 struct i2c_algo_bit_data *algo_data = adapter->algo_data;
57 unsigned char *edid = NULL;
58 int i, j;
59
60 algo_data->setscl(algo_data->data, 1);
61
62 for (i = 0; i < 3; i++) {
63 /* For some old monitors we need the
64 * following process to initialize/stop DDC
65 */
66 algo_data->setsda(algo_data->data, 1);
67 msleep(13);
68
69 algo_data->setscl(algo_data->data, 1);
70 for (j = 0; j < 5; j++) {
71 msleep(10);
72 if (algo_data->getscl(algo_data->data))
73 break;
74 }
75 if (j == 5)
76 continue;
77
78 algo_data->setsda(algo_data->data, 0);
79 msleep(15);
80 algo_data->setscl(algo_data->data, 0);
81 msleep(15);
82 algo_data->setsda(algo_data->data, 1);
83 msleep(15);
84
85 /* Do the real work */
86 edid = fb_do_probe_ddc_edid(adapter);
87 algo_data->setsda(algo_data->data, 0);
88 algo_data->setscl(algo_data->data, 0);
89 msleep(15);
90
91 algo_data->setscl(algo_data->data, 1);
92 for (j = 0; j < 10; j++) {
93 msleep(10);
94 if (algo_data->getscl(algo_data->data))
95 break;
96 }
97
98 algo_data->setsda(algo_data->data, 1);
99 msleep(15);
100 algo_data->setscl(algo_data->data, 0);
101 algo_data->setsda(algo_data->data, 0);
102 if (edid)
103 break;
104 }
105 /* Release the DDC lines when done or the Apple Cinema HD display
106 * will switch off
107 */
108 algo_data->setsda(algo_data->data, 1);
109 algo_data->setscl(algo_data->data, 1);
110
111 adapter->class |= I2C_CLASS_DDC;
112 return edid;
113}
114
115EXPORT_SYMBOL_GPL(fb_ddc_read);
116
117MODULE_AUTHOR("Dennis Munsie <dmunsie@cecropia.com>");
118MODULE_DESCRIPTION("DDC/EDID reading support");
119MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
new file mode 100644
index 000000000000..900aa4ecd617
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -0,0 +1,245 @@
1/*
2 * linux/drivers/video/fb_defio.c
3 *
4 * Copyright (C) 2006 Jaya Kumar
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/string.h>
15#include <linux/mm.h>
16#include <linux/vmalloc.h>
17#include <linux/delay.h>
18#include <linux/interrupt.h>
19#include <linux/fb.h>
20#include <linux/list.h>
21
22/* to support deferred IO */
23#include <linux/rmap.h>
24#include <linux/pagemap.h>
25
26static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
27{
28 void *screen_base = (void __force *) info->screen_base;
29 struct page *page;
30
31 if (is_vmalloc_addr(screen_base + offs))
32 page = vmalloc_to_page(screen_base + offs);
33 else
34 page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
35
36 return page;
37}
38
39/* this is to find and return the vmalloc-ed fb pages */
40static int fb_deferred_io_fault(struct vm_area_struct *vma,
41 struct vm_fault *vmf)
42{
43 unsigned long offset;
44 struct page *page;
45 struct fb_info *info = vma->vm_private_data;
46
47 offset = vmf->pgoff << PAGE_SHIFT;
48 if (offset >= info->fix.smem_len)
49 return VM_FAULT_SIGBUS;
50
51 page = fb_deferred_io_page(info, offset);
52 if (!page)
53 return VM_FAULT_SIGBUS;
54
55 get_page(page);
56
57 if (vma->vm_file)
58 page->mapping = vma->vm_file->f_mapping;
59 else
60 printk(KERN_ERR "no mapping available\n");
61
62 BUG_ON(!page->mapping);
63 page->index = vmf->pgoff;
64
65 vmf->page = page;
66 return 0;
67}
68
69int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
70{
71 struct fb_info *info = file->private_data;
72 struct inode *inode = file_inode(file);
73 int err = filemap_write_and_wait_range(inode->i_mapping, start, end);
74 if (err)
75 return err;
76
77 /* Skip if deferred io is compiled-in but disabled on this fbdev */
78 if (!info->fbdefio)
79 return 0;
80
81 mutex_lock(&inode->i_mutex);
82 /* Kill off the delayed work */
83 cancel_delayed_work_sync(&info->deferred_work);
84
85 /* Run it immediately */
86 err = schedule_delayed_work(&info->deferred_work, 0);
87 mutex_unlock(&inode->i_mutex);
88 return err;
89}
90EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
91
92/* vm_ops->page_mkwrite handler */
93static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
94 struct vm_fault *vmf)
95{
96 struct page *page = vmf->page;
97 struct fb_info *info = vma->vm_private_data;
98 struct fb_deferred_io *fbdefio = info->fbdefio;
99 struct page *cur;
100
101 /* this is a callback we get when userspace first tries to
102 write to the page. we schedule a workqueue. that workqueue
103 will eventually mkclean the touched pages and execute the
104 deferred framebuffer IO. then if userspace touches a page
105 again, we repeat the same scheme */
106
107 file_update_time(vma->vm_file);
108
109 /* protect against the workqueue changing the page list */
110 mutex_lock(&fbdefio->lock);
111
112 /* first write in this cycle, notify the driver */
113 if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
114 fbdefio->first_io(info);
115
116 /*
117 * We want the page to remain locked from ->page_mkwrite until
118 * the PTE is marked dirty to avoid page_mkclean() being called
119 * before the PTE is updated, which would leave the page ignored
120 * by defio.
121 * Do this by locking the page here and informing the caller
122 * about it with VM_FAULT_LOCKED.
123 */
124 lock_page(page);
125
126 /* we loop through the pagelist before adding in order
127 to keep the pagelist sorted */
128 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
129 /* this check is to catch the case where a new
130 process could start writing to the same page
131 through a new pte. this new access can cause the
132 mkwrite even when the original ps's pte is marked
133 writable */
134 if (unlikely(cur == page))
135 goto page_already_added;
136 else if (cur->index > page->index)
137 break;
138 }
139
140 list_add_tail(&page->lru, &cur->lru);
141
142page_already_added:
143 mutex_unlock(&fbdefio->lock);
144
145 /* come back after delay to process the deferred IO */
146 schedule_delayed_work(&info->deferred_work, fbdefio->delay);
147 return VM_FAULT_LOCKED;
148}
149
150static const struct vm_operations_struct fb_deferred_io_vm_ops = {
151 .fault = fb_deferred_io_fault,
152 .page_mkwrite = fb_deferred_io_mkwrite,
153};
154
155static int fb_deferred_io_set_page_dirty(struct page *page)
156{
157 if (!PageDirty(page))
158 SetPageDirty(page);
159 return 0;
160}
161
162static const struct address_space_operations fb_deferred_io_aops = {
163 .set_page_dirty = fb_deferred_io_set_page_dirty,
164};
165
166static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
167{
168 vma->vm_ops = &fb_deferred_io_vm_ops;
169 vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
170 if (!(info->flags & FBINFO_VIRTFB))
171 vma->vm_flags |= VM_IO;
172 vma->vm_private_data = info;
173 return 0;
174}
175
176/* workqueue callback */
177static void fb_deferred_io_work(struct work_struct *work)
178{
179 struct fb_info *info = container_of(work, struct fb_info,
180 deferred_work.work);
181 struct list_head *node, *next;
182 struct page *cur;
183 struct fb_deferred_io *fbdefio = info->fbdefio;
184
185 /* here we mkclean the pages, then do all deferred IO */
186 mutex_lock(&fbdefio->lock);
187 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
188 lock_page(cur);
189 page_mkclean(cur);
190 unlock_page(cur);
191 }
192
193 /* driver's callback with pagelist */
194 fbdefio->deferred_io(info, &fbdefio->pagelist);
195
196 /* clear the list */
197 list_for_each_safe(node, next, &fbdefio->pagelist) {
198 list_del(node);
199 }
200 mutex_unlock(&fbdefio->lock);
201}
202
203void fb_deferred_io_init(struct fb_info *info)
204{
205 struct fb_deferred_io *fbdefio = info->fbdefio;
206
207 BUG_ON(!fbdefio);
208 mutex_init(&fbdefio->lock);
209 info->fbops->fb_mmap = fb_deferred_io_mmap;
210 INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
211 INIT_LIST_HEAD(&fbdefio->pagelist);
212 if (fbdefio->delay == 0) /* set a default of 1 s */
213 fbdefio->delay = HZ;
214}
215EXPORT_SYMBOL_GPL(fb_deferred_io_init);
216
217void fb_deferred_io_open(struct fb_info *info,
218 struct inode *inode,
219 struct file *file)
220{
221 file->f_mapping->a_ops = &fb_deferred_io_aops;
222}
223EXPORT_SYMBOL_GPL(fb_deferred_io_open);
224
225void fb_deferred_io_cleanup(struct fb_info *info)
226{
227 struct fb_deferred_io *fbdefio = info->fbdefio;
228 struct page *page;
229 int i;
230
231 BUG_ON(!fbdefio);
232 cancel_delayed_work_sync(&info->deferred_work);
233
234 /* clear out the mapping that we setup */
235 for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
236 page = fb_deferred_io_page(info, i);
237 page->mapping = NULL;
238 }
239
240 info->fbops->fb_mmap = NULL;
241 mutex_destroy(&fbdefio->lock);
242}
243EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
244
245MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/fb_draw.h b/drivers/video/fbdev/core/fb_draw.h
new file mode 100644
index 000000000000..624ee115f129
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_draw.h
@@ -0,0 +1,186 @@
1#ifndef _FB_DRAW_H
2#define _FB_DRAW_H
3
4#include <asm/types.h>
5#include <linux/fb.h>
6#include <linux/bug.h>
7
8 /*
9 * Compose two values, using a bitmask as decision value
10 * This is equivalent to (a & mask) | (b & ~mask)
11 */
12
13static inline unsigned long
14comp(unsigned long a, unsigned long b, unsigned long mask)
15{
16 return ((a ^ b) & mask) ^ b;
17}
18
19 /*
20 * Create a pattern with the given pixel's color
21 */
22
23#if BITS_PER_LONG == 64
24static inline unsigned long
25pixel_to_pat( u32 bpp, u32 pixel)
26{
27 switch (bpp) {
28 case 1:
29 return 0xfffffffffffffffful*pixel;
30 case 2:
31 return 0x5555555555555555ul*pixel;
32 case 4:
33 return 0x1111111111111111ul*pixel;
34 case 8:
35 return 0x0101010101010101ul*pixel;
36 case 12:
37 return 0x1001001001001001ul*pixel;
38 case 16:
39 return 0x0001000100010001ul*pixel;
40 case 24:
41 return 0x0001000001000001ul*pixel;
42 case 32:
43 return 0x0000000100000001ul*pixel;
44 default:
45 WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
46 return 0;
47 }
48}
49#else
50static inline unsigned long
51pixel_to_pat( u32 bpp, u32 pixel)
52{
53 switch (bpp) {
54 case 1:
55 return 0xfffffffful*pixel;
56 case 2:
57 return 0x55555555ul*pixel;
58 case 4:
59 return 0x11111111ul*pixel;
60 case 8:
61 return 0x01010101ul*pixel;
62 case 12:
63 return 0x01001001ul*pixel;
64 case 16:
65 return 0x00010001ul*pixel;
66 case 24:
67 return 0x01000001ul*pixel;
68 case 32:
69 return 0x00000001ul*pixel;
70 default:
71 WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
72 return 0;
73 }
74}
75#endif
76
77#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
78#if BITS_PER_LONG == 64
79#define REV_PIXELS_MASK1 0x5555555555555555ul
80#define REV_PIXELS_MASK2 0x3333333333333333ul
81#define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful
82#else
83#define REV_PIXELS_MASK1 0x55555555ul
84#define REV_PIXELS_MASK2 0x33333333ul
85#define REV_PIXELS_MASK4 0x0f0f0f0ful
86#endif
87
88static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
89 u32 bswapmask)
90{
91 if (bswapmask & 1)
92 val = comp(val >> 1, val << 1, REV_PIXELS_MASK1);
93 if (bswapmask & 2)
94 val = comp(val >> 2, val << 2, REV_PIXELS_MASK2);
95 if (bswapmask & 3)
96 val = comp(val >> 4, val << 4, REV_PIXELS_MASK4);
97 return val;
98}
99
100static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index,
101 u32 bswapmask)
102{
103 u32 mask;
104
105 if (!bswapmask) {
106 mask = FB_SHIFT_HIGH(p, ~(u32)0, index);
107 } else {
108 mask = 0xff << FB_LEFT_POS(p, 8);
109 mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
110 mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
111#if defined(__i386__) || defined(__x86_64__)
112 /* Shift argument is limited to 0 - 31 on x86 based CPU's */
113 if(index + bswapmask < 32)
114#endif
115 mask |= FB_SHIFT_HIGH(p, ~(u32)0,
116 (index + bswapmask) & ~(bswapmask));
117 }
118 return mask;
119}
120
121static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p,
122 u32 index,
123 u32 bswapmask)
124{
125 unsigned long mask;
126
127 if (!bswapmask) {
128 mask = FB_SHIFT_HIGH(p, ~0UL, index);
129 } else {
130 mask = 0xff << FB_LEFT_POS(p, 8);
131 mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
132 mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
133#if defined(__i386__) || defined(__x86_64__)
134 /* Shift argument is limited to 0 - 31 on x86 based CPU's */
135 if(index + bswapmask < BITS_PER_LONG)
136#endif
137 mask |= FB_SHIFT_HIGH(p, ~0UL,
138 (index + bswapmask) & ~(bswapmask));
139 }
140 return mask;
141}
142
143
144static inline u32 fb_compute_bswapmask(struct fb_info *info)
145{
146 u32 bswapmask = 0;
147 unsigned bpp = info->var.bits_per_pixel;
148
149 if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) {
150 /*
151 * Reversed order of pixel layout in bytes
152 * works only for 1, 2 and 4 bpp
153 */
154 bswapmask = 7 - bpp + 1;
155 }
156 return bswapmask;
157}
158
159#else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
160
161static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
162 u32 bswapmask)
163{
164 return val;
165}
166
167#define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i))
168#define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i))
169#define fb_compute_bswapmask(...) 0
170
171#endif /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
172
173#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
174#define _cpu_to_le_long(x) __cpu_to_le_long(x)
175#define __cpu_to_le_long(x) cpu_to_le##x
176
177#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
178#define _le_long_to_cpu(x) __le_long_to_cpu(x)
179#define __le_long_to_cpu(x) le##x##_to_cpu
180
181static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
182{
183 return (word << shift) | (word >> (x - shift));
184}
185
186#endif /* FB_DRAW_H */
diff --git a/drivers/video/fbdev/core/fb_notify.c b/drivers/video/fbdev/core/fb_notify.c
new file mode 100644
index 000000000000..74c2da528884
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_notify.c
@@ -0,0 +1,47 @@
1/*
2 * linux/drivers/video/fb_notify.c
3 *
4 * Copyright (C) 2006 Antonino Daplas <adaplas@pol.net>
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
11 * for more details.
12 */
13#include <linux/fb.h>
14#include <linux/notifier.h>
15#include <linux/export.h>
16
17static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
18
19/**
20 * fb_register_client - register a client notifier
21 * @nb: notifier block to callback on events
22 */
23int fb_register_client(struct notifier_block *nb)
24{
25 return blocking_notifier_chain_register(&fb_notifier_list, nb);
26}
27EXPORT_SYMBOL(fb_register_client);
28
29/**
30 * fb_unregister_client - unregister a client notifier
31 * @nb: notifier block to callback on events
32 */
33int fb_unregister_client(struct notifier_block *nb)
34{
35 return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
36}
37EXPORT_SYMBOL(fb_unregister_client);
38
39/**
40 * fb_notifier_call_chain - notify clients of fb_events
41 *
42 */
43int fb_notifier_call_chain(unsigned long val, void *v)
44{
45 return blocking_notifier_call_chain(&fb_notifier_list, val, v);
46}
47EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
diff --git a/drivers/video/fbdev/core/fb_sys_fops.c b/drivers/video/fbdev/core/fb_sys_fops.c
new file mode 100644
index 000000000000..ff275d7f3eaf
--- /dev/null
+++ b/drivers/video/fbdev/core/fb_sys_fops.c
@@ -0,0 +1,104 @@
1/*
2 * linux/drivers/video/fb_sys_read.c - Generic file operations where
3 * framebuffer is in system RAM
4 *
5 * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file COPYING in the main directory of this archive
9 * for more details.
10 *
11 */
12#include <linux/fb.h>
13#include <linux/module.h>
14#include <linux/uaccess.h>
15
16ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
17 loff_t *ppos)
18{
19 unsigned long p = *ppos;
20 void *src;
21 int err = 0;
22 unsigned long total_size;
23
24 if (info->state != FBINFO_STATE_RUNNING)
25 return -EPERM;
26
27 total_size = info->screen_size;
28
29 if (total_size == 0)
30 total_size = info->fix.smem_len;
31
32 if (p >= total_size)
33 return 0;
34
35 if (count >= total_size)
36 count = total_size;
37
38 if (count + p > total_size)
39 count = total_size - p;
40
41 src = (void __force *)(info->screen_base + p);
42
43 if (info->fbops->fb_sync)
44 info->fbops->fb_sync(info);
45
46 if (copy_to_user(buf, src, count))
47 err = -EFAULT;
48
49 if (!err)
50 *ppos += count;
51
52 return (err) ? err : count;
53}
54EXPORT_SYMBOL_GPL(fb_sys_read);
55
56ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
57 size_t count, loff_t *ppos)
58{
59 unsigned long p = *ppos;
60 void *dst;
61 int err = 0;
62 unsigned long total_size;
63
64 if (info->state != FBINFO_STATE_RUNNING)
65 return -EPERM;
66
67 total_size = info->screen_size;
68
69 if (total_size == 0)
70 total_size = info->fix.smem_len;
71
72 if (p > total_size)
73 return -EFBIG;
74
75 if (count > total_size) {
76 err = -EFBIG;
77 count = total_size;
78 }
79
80 if (count + p > total_size) {
81 if (!err)
82 err = -ENOSPC;
83
84 count = total_size - p;
85 }
86
87 dst = (void __force *) (info->screen_base + p);
88
89 if (info->fbops->fb_sync)
90 info->fbops->fb_sync(info);
91
92 if (copy_from_user(dst, buf, count))
93 err = -EFAULT;
94
95 if (!err)
96 *ppos += count;
97
98 return (err) ? err : count;
99}
100EXPORT_SYMBOL_GPL(fb_sys_write);
101
102MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
103MODULE_DESCRIPTION("Generic file read (fb in system RAM)");
104MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
new file mode 100644
index 000000000000..f89245b8ba8e
--- /dev/null
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -0,0 +1,362 @@
1/*
2 * linux/drivers/video/fbcmap.c -- Colormap handling for frame buffer devices
3 *
4 * Created 15 Jun 1997 by 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/string.h>
15#include <linux/module.h>
16#include <linux/fb.h>
17#include <linux/slab.h>
18#include <linux/uaccess.h>
19
20static u16 red2[] __read_mostly = {
21 0x0000, 0xaaaa
22};
23static u16 green2[] __read_mostly = {
24 0x0000, 0xaaaa
25};
26static u16 blue2[] __read_mostly = {
27 0x0000, 0xaaaa
28};
29
30static u16 red4[] __read_mostly = {
31 0x0000, 0xaaaa, 0x5555, 0xffff
32};
33static u16 green4[] __read_mostly = {
34 0x0000, 0xaaaa, 0x5555, 0xffff
35};
36static u16 blue4[] __read_mostly = {
37 0x0000, 0xaaaa, 0x5555, 0xffff
38};
39
40static u16 red8[] __read_mostly = {
41 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
42};
43static u16 green8[] __read_mostly = {
44 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa
45};
46static u16 blue8[] __read_mostly = {
47 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
48};
49
50static u16 red16[] __read_mostly = {
51 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
52 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
53};
54static u16 green16[] __read_mostly = {
55 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa,
56 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
57};
58static u16 blue16[] __read_mostly = {
59 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
60 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
61};
62
63static const struct fb_cmap default_2_colors = {
64 .len=2, .red=red2, .green=green2, .blue=blue2
65};
66static const struct fb_cmap default_8_colors = {
67 .len=8, .red=red8, .green=green8, .blue=blue8
68};
69static const struct fb_cmap default_4_colors = {
70 .len=4, .red=red4, .green=green4, .blue=blue4
71};
72static const struct fb_cmap default_16_colors = {
73 .len=16, .red=red16, .green=green16, .blue=blue16
74};
75
76
77
78/**
79 * fb_alloc_cmap - allocate a colormap
80 * @cmap: frame buffer colormap structure
81 * @len: length of @cmap
82 * @transp: boolean, 1 if there is transparency, 0 otherwise
83 * @flags: flags for kmalloc memory allocation
84 *
85 * Allocates memory for a colormap @cmap. @len is the
86 * number of entries in the palette.
87 *
88 * Returns negative errno on error, or zero on success.
89 *
90 */
91
92int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
93{
94 int size = len * sizeof(u16);
95 int ret = -ENOMEM;
96
97 if (cmap->len != len) {
98 fb_dealloc_cmap(cmap);
99 if (!len)
100 return 0;
101
102 cmap->red = kmalloc(size, flags);
103 if (!cmap->red)
104 goto fail;
105 cmap->green = kmalloc(size, flags);
106 if (!cmap->green)
107 goto fail;
108 cmap->blue = kmalloc(size, flags);
109 if (!cmap->blue)
110 goto fail;
111 if (transp) {
112 cmap->transp = kmalloc(size, flags);
113 if (!cmap->transp)
114 goto fail;
115 } else {
116 cmap->transp = NULL;
117 }
118 }
119 cmap->start = 0;
120 cmap->len = len;
121 ret = fb_copy_cmap(fb_default_cmap(len), cmap);
122 if (ret)
123 goto fail;
124 return 0;
125
126fail:
127 fb_dealloc_cmap(cmap);
128 return ret;
129}
130
131int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
132{
133 return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
134}
135
136/**
137 * fb_dealloc_cmap - deallocate a colormap
138 * @cmap: frame buffer colormap structure
139 *
140 * Deallocates a colormap that was previously allocated with
141 * fb_alloc_cmap().
142 *
143 */
144
145void fb_dealloc_cmap(struct fb_cmap *cmap)
146{
147 kfree(cmap->red);
148 kfree(cmap->green);
149 kfree(cmap->blue);
150 kfree(cmap->transp);
151
152 cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
153 cmap->len = 0;
154}
155
156/**
157 * fb_copy_cmap - copy a colormap
158 * @from: frame buffer colormap structure
159 * @to: frame buffer colormap structure
160 *
161 * Copy contents of colormap from @from to @to.
162 */
163
164int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
165{
166 int tooff = 0, fromoff = 0;
167 int size;
168
169 if (to->start > from->start)
170 fromoff = to->start - from->start;
171 else
172 tooff = from->start - to->start;
173 size = to->len - tooff;
174 if (size > (int) (from->len - fromoff))
175 size = from->len - fromoff;
176 if (size <= 0)
177 return -EINVAL;
178 size *= sizeof(u16);
179
180 memcpy(to->red+tooff, from->red+fromoff, size);
181 memcpy(to->green+tooff, from->green+fromoff, size);
182 memcpy(to->blue+tooff, from->blue+fromoff, size);
183 if (from->transp && to->transp)
184 memcpy(to->transp+tooff, from->transp+fromoff, size);
185 return 0;
186}
187
188int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
189{
190 int tooff = 0, fromoff = 0;
191 int size;
192
193 if (to->start > from->start)
194 fromoff = to->start - from->start;
195 else
196 tooff = from->start - to->start;
197 size = to->len - tooff;
198 if (size > (int) (from->len - fromoff))
199 size = from->len - fromoff;
200 if (size <= 0)
201 return -EINVAL;
202 size *= sizeof(u16);
203
204 if (copy_to_user(to->red+tooff, from->red+fromoff, size))
205 return -EFAULT;
206 if (copy_to_user(to->green+tooff, from->green+fromoff, size))
207 return -EFAULT;
208 if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
209 return -EFAULT;
210 if (from->transp && to->transp)
211 if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
212 return -EFAULT;
213 return 0;
214}
215
216/**
217 * fb_set_cmap - set the colormap
218 * @cmap: frame buffer colormap structure
219 * @info: frame buffer info structure
220 *
221 * Sets the colormap @cmap for a screen of device @info.
222 *
223 * Returns negative errno on error, or zero on success.
224 *
225 */
226
227int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
228{
229 int i, start, rc = 0;
230 u16 *red, *green, *blue, *transp;
231 u_int hred, hgreen, hblue, htransp = 0xffff;
232
233 red = cmap->red;
234 green = cmap->green;
235 blue = cmap->blue;
236 transp = cmap->transp;
237 start = cmap->start;
238
239 if (start < 0 || (!info->fbops->fb_setcolreg &&
240 !info->fbops->fb_setcmap))
241 return -EINVAL;
242 if (info->fbops->fb_setcmap) {
243 rc = info->fbops->fb_setcmap(cmap, info);
244 } else {
245 for (i = 0; i < cmap->len; i++) {
246 hred = *red++;
247 hgreen = *green++;
248 hblue = *blue++;
249 if (transp)
250 htransp = *transp++;
251 if (info->fbops->fb_setcolreg(start++,
252 hred, hgreen, hblue,
253 htransp, info))
254 break;
255 }
256 }
257 if (rc == 0)
258 fb_copy_cmap(cmap, &info->cmap);
259
260 return rc;
261}
262
263int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
264{
265 int rc, size = cmap->len * sizeof(u16);
266 struct fb_cmap umap;
267
268 if (size < 0 || size < cmap->len)
269 return -E2BIG;
270
271 memset(&umap, 0, sizeof(struct fb_cmap));
272 rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
273 GFP_KERNEL);
274 if (rc)
275 return rc;
276 if (copy_from_user(umap.red, cmap->red, size) ||
277 copy_from_user(umap.green, cmap->green, size) ||
278 copy_from_user(umap.blue, cmap->blue, size) ||
279 (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
280 rc = -EFAULT;
281 goto out;
282 }
283 umap.start = cmap->start;
284 if (!lock_fb_info(info)) {
285 rc = -ENODEV;
286 goto out;
287 }
288
289 rc = fb_set_cmap(&umap, info);
290 unlock_fb_info(info);
291out:
292 fb_dealloc_cmap(&umap);
293 return rc;
294}
295
296/**
297 * fb_default_cmap - get default colormap
298 * @len: size of palette for a depth
299 *
300 * Gets the default colormap for a specific screen depth. @len
301 * is the size of the palette for a particular screen depth.
302 *
303 * Returns pointer to a frame buffer colormap structure.
304 *
305 */
306
307const struct fb_cmap *fb_default_cmap(int len)
308{
309 if (len <= 2)
310 return &default_2_colors;
311 if (len <= 4)
312 return &default_4_colors;
313 if (len <= 8)
314 return &default_8_colors;
315 return &default_16_colors;
316}
317
318
319/**
320 * fb_invert_cmaps - invert all defaults colormaps
321 *
322 * Invert all default colormaps.
323 *
324 */
325
326void fb_invert_cmaps(void)
327{
328 u_int i;
329
330 for (i = 0; i < ARRAY_SIZE(red2); i++) {
331 red2[i] = ~red2[i];
332 green2[i] = ~green2[i];
333 blue2[i] = ~blue2[i];
334 }
335 for (i = 0; i < ARRAY_SIZE(red4); i++) {
336 red4[i] = ~red4[i];
337 green4[i] = ~green4[i];
338 blue4[i] = ~blue4[i];
339 }
340 for (i = 0; i < ARRAY_SIZE(red8); i++) {
341 red8[i] = ~red8[i];
342 green8[i] = ~green8[i];
343 blue8[i] = ~blue8[i];
344 }
345 for (i = 0; i < ARRAY_SIZE(red16); i++) {
346 red16[i] = ~red16[i];
347 green16[i] = ~green16[i];
348 blue16[i] = ~blue16[i];
349 }
350}
351
352
353 /*
354 * Visible symbols for modules
355 */
356
357EXPORT_SYMBOL(fb_alloc_cmap);
358EXPORT_SYMBOL(fb_dealloc_cmap);
359EXPORT_SYMBOL(fb_copy_cmap);
360EXPORT_SYMBOL(fb_set_cmap);
361EXPORT_SYMBOL(fb_default_cmap);
362EXPORT_SYMBOL(fb_invert_cmaps);
diff --git a/drivers/video/fbdev/core/fbcvt.c b/drivers/video/fbdev/core/fbcvt.c
new file mode 100644
index 000000000000..7cb715dfc0e1
--- /dev/null
+++ b/drivers/video/fbdev/core/fbcvt.c
@@ -0,0 +1,379 @@
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
35struct 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
61static 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 */
73static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
74{
75 u32 num = 1000000000/cvt->f_refresh;
76 u32 den;
77
78 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
79 num -= FB_CVT_RB_MIN_VBLANK * 1000;
80 den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin);
81 } else {
82 num -= FB_CVT_MIN_VSYNC_BP * 1000;
83 den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2
84 + FB_CVT_MIN_VPORCH + cvt->interlace/2);
85 }
86
87 return 2 * (num/den);
88}
89
90/* returns ideal duty cycle * 1000 */
91static 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 u32 h_period_est = cvt->hperiod;
97
98 return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256;
99}
100
101static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
102{
103 u32 hblank = 0;
104
105 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 u32 active_pixels = cvt->active_pixels;
110
111 if (ideal_duty_cycle < 20000)
112 hblank = (active_pixels * 20000)/
113 (100000 - 20000);
114 else {
115 hblank = (active_pixels * ideal_duty_cycle)/
116 (100000 - ideal_duty_cycle);
117 }
118 }
119
120 hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
121
122 return hblank;
123}
124
125static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
126{
127 u32 hsync;
128
129 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
130 hsync = 32;
131 else
132 hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
133
134 hsync &= ~(FB_CVT_CELLSIZE - 1);
135 return hsync;
136}
137
138static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
139{
140 u32 vbi_lines, min_vbi_lines, act_vbi_lines;
141
142 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
143 vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1;
144 min_vbi_lines = FB_CVT_RB_V_FPORCH + cvt->vsync +
145 FB_CVT_MIN_BPORCH;
146
147 } else {
148 vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
149 FB_CVT_MIN_VPORCH;
150 min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
151 FB_CVT_MIN_VPORCH;
152 }
153
154 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
162static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
163{
164 u32 vtotal = cvt->yres/cvt->interlace;
165
166 vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
167 vtotal |= cvt->interlace/2;
168
169 return vtotal;
170}
171
172static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
173{
174 u32 pixclock;
175
176 if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
177 pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
178 else
179 pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
180
181 pixclock /= 250;
182 pixclock *= 250;
183 pixclock *= 1000;
184
185 return pixclock;
186}
187
188static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
189{
190 u32 xres = cvt->xres;
191 u32 yres = cvt->yres;
192 u32 aspect = -1;
193
194 if (xres == (yres * 4)/3 && !((yres * 4) % 3))
195 aspect = 0;
196 else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
197 aspect = 1;
198 else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
199 aspect = 2;
200 else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
201 aspect = 3;
202 else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
203 aspect = 4;
204 else {
205 printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
206 "standard\n");
207 aspect = 7;
208 cvt->status = 1;
209 }
210
211 return aspect;
212}
213
214static 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 u8 *buf = kzalloc(256, GFP_KERNEL);
219
220 if (!buf)
221 return;
222
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_back_porch;
276 mode->right_margin = cvt->h_front_porch;
277 mode->hsync_len = cvt->hsync;
278 mode->upper_margin = cvt->v_back_porch;
279 mode->lower_margin = cvt->v_front_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}
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
new file mode 100644
index 000000000000..b6d5008f361f
--- /dev/null
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -0,0 +1,2002 @@
1/*
2 * linux/drivers/video/fbmem.c
3 *
4 * Copyright (C) 1994 Martin Schaller
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
11 * for more details.
12 */
13
14#include <linux/module.h>
15
16#include <linux/compat.h>
17#include <linux/types.h>
18#include <linux/errno.h>
19#include <linux/kernel.h>
20#include <linux/major.h>
21#include <linux/slab.h>
22#include <linux/mm.h>
23#include <linux/mman.h>
24#include <linux/vt.h>
25#include <linux/init.h>
26#include <linux/linux_logo.h>
27#include <linux/proc_fs.h>
28#include <linux/seq_file.h>
29#include <linux/console.h>
30#include <linux/kmod.h>
31#include <linux/err.h>
32#include <linux/device.h>
33#include <linux/efi.h>
34#include <linux/fb.h>
35
36#include <asm/fb.h>
37
38
39 /*
40 * Frame buffer device initialization and setup routines
41 */
42
43#define FBPIXMAPSIZE (1024 * 8)
44
45static DEFINE_MUTEX(registration_lock);
46
47struct fb_info *registered_fb[FB_MAX] __read_mostly;
48EXPORT_SYMBOL(registered_fb);
49
50int num_registered_fb __read_mostly;
51EXPORT_SYMBOL(num_registered_fb);
52
53static struct fb_info *get_fb_info(unsigned int idx)
54{
55 struct fb_info *fb_info;
56
57 if (idx >= FB_MAX)
58 return ERR_PTR(-ENODEV);
59
60 mutex_lock(&registration_lock);
61 fb_info = registered_fb[idx];
62 if (fb_info)
63 atomic_inc(&fb_info->count);
64 mutex_unlock(&registration_lock);
65
66 return fb_info;
67}
68
69static void put_fb_info(struct fb_info *fb_info)
70{
71 if (!atomic_dec_and_test(&fb_info->count))
72 return;
73 if (fb_info->fbops->fb_destroy)
74 fb_info->fbops->fb_destroy(fb_info);
75}
76
77int lock_fb_info(struct fb_info *info)
78{
79 mutex_lock(&info->lock);
80 if (!info->fbops) {
81 mutex_unlock(&info->lock);
82 return 0;
83 }
84 return 1;
85}
86EXPORT_SYMBOL(lock_fb_info);
87
88/*
89 * Helpers
90 */
91
92int fb_get_color_depth(struct fb_var_screeninfo *var,
93 struct fb_fix_screeninfo *fix)
94{
95 int depth = 0;
96
97 if (fix->visual == FB_VISUAL_MONO01 ||
98 fix->visual == FB_VISUAL_MONO10)
99 depth = 1;
100 else {
101 if (var->green.length == var->blue.length &&
102 var->green.length == var->red.length &&
103 var->green.offset == var->blue.offset &&
104 var->green.offset == var->red.offset)
105 depth = var->green.length;
106 else
107 depth = var->green.length + var->red.length +
108 var->blue.length;
109 }
110
111 return depth;
112}
113EXPORT_SYMBOL(fb_get_color_depth);
114
115/*
116 * Data padding functions.
117 */
118void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
119{
120 __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
121}
122EXPORT_SYMBOL(fb_pad_aligned_buffer);
123
124void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
125 u32 shift_high, u32 shift_low, u32 mod)
126{
127 u8 mask = (u8) (0xfff << shift_high), tmp;
128 int i, j;
129
130 for (i = height; i--; ) {
131 for (j = 0; j < idx; j++) {
132 tmp = dst[j];
133 tmp &= mask;
134 tmp |= *src >> shift_low;
135 dst[j] = tmp;
136 tmp = *src << shift_high;
137 dst[j+1] = tmp;
138 src++;
139 }
140 tmp = dst[idx];
141 tmp &= mask;
142 tmp |= *src >> shift_low;
143 dst[idx] = tmp;
144 if (shift_high < mod) {
145 tmp = *src << shift_high;
146 dst[idx+1] = tmp;
147 }
148 src++;
149 dst += d_pitch;
150 }
151}
152EXPORT_SYMBOL(fb_pad_unaligned_buffer);
153
154/*
155 * we need to lock this section since fb_cursor
156 * may use fb_imageblit()
157 */
158char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
159{
160 u32 align = buf->buf_align - 1, offset;
161 char *addr = buf->addr;
162
163 /* If IO mapped, we need to sync before access, no sharing of
164 * the pixmap is done
165 */
166 if (buf->flags & FB_PIXMAP_IO) {
167 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
168 info->fbops->fb_sync(info);
169 return addr;
170 }
171
172 /* See if we fit in the remaining pixmap space */
173 offset = buf->offset + align;
174 offset &= ~align;
175 if (offset + size > buf->size) {
176 /* We do not fit. In order to be able to re-use the buffer,
177 * we must ensure no asynchronous DMA'ing or whatever operation
178 * is in progress, we sync for that.
179 */
180 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
181 info->fbops->fb_sync(info);
182 offset = 0;
183 }
184 buf->offset = offset + size;
185 addr += offset;
186
187 return addr;
188}
189EXPORT_SYMBOL(fb_get_buffer_offset);
190
191#ifdef CONFIG_LOGO
192
193static inline unsigned safe_shift(unsigned d, int n)
194{
195 return n < 0 ? d >> -n : d << n;
196}
197
198static void fb_set_logocmap(struct fb_info *info,
199 const struct linux_logo *logo)
200{
201 struct fb_cmap palette_cmap;
202 u16 palette_green[16];
203 u16 palette_blue[16];
204 u16 palette_red[16];
205 int i, j, n;
206 const unsigned char *clut = logo->clut;
207
208 palette_cmap.start = 0;
209 palette_cmap.len = 16;
210 palette_cmap.red = palette_red;
211 palette_cmap.green = palette_green;
212 palette_cmap.blue = palette_blue;
213 palette_cmap.transp = NULL;
214
215 for (i = 0; i < logo->clutsize; i += n) {
216 n = logo->clutsize - i;
217 /* palette_cmap provides space for only 16 colors at once */
218 if (n > 16)
219 n = 16;
220 palette_cmap.start = 32 + i;
221 palette_cmap.len = n;
222 for (j = 0; j < n; ++j) {
223 palette_cmap.red[j] = clut[0] << 8 | clut[0];
224 palette_cmap.green[j] = clut[1] << 8 | clut[1];
225 palette_cmap.blue[j] = clut[2] << 8 | clut[2];
226 clut += 3;
227 }
228 fb_set_cmap(&palette_cmap, info);
229 }
230}
231
232static void fb_set_logo_truepalette(struct fb_info *info,
233 const struct linux_logo *logo,
234 u32 *palette)
235{
236 static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
237 unsigned char redmask, greenmask, bluemask;
238 int redshift, greenshift, blueshift;
239 int i;
240 const unsigned char *clut = logo->clut;
241
242 /*
243 * We have to create a temporary palette since console palette is only
244 * 16 colors long.
245 */
246 /* Bug: Doesn't obey msb_right ... (who needs that?) */
247 redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
248 greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
249 bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
250 redshift = info->var.red.offset - (8 - info->var.red.length);
251 greenshift = info->var.green.offset - (8 - info->var.green.length);
252 blueshift = info->var.blue.offset - (8 - info->var.blue.length);
253
254 for ( i = 0; i < logo->clutsize; i++) {
255 palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
256 safe_shift((clut[1] & greenmask), greenshift) |
257 safe_shift((clut[2] & bluemask), blueshift));
258 clut += 3;
259 }
260}
261
262static void fb_set_logo_directpalette(struct fb_info *info,
263 const struct linux_logo *logo,
264 u32 *palette)
265{
266 int redshift, greenshift, blueshift;
267 int i;
268
269 redshift = info->var.red.offset;
270 greenshift = info->var.green.offset;
271 blueshift = info->var.blue.offset;
272
273 for (i = 32; i < 32 + logo->clutsize; i++)
274 palette[i] = i << redshift | i << greenshift | i << blueshift;
275}
276
277static void fb_set_logo(struct fb_info *info,
278 const struct linux_logo *logo, u8 *dst,
279 int depth)
280{
281 int i, j, k;
282 const u8 *src = logo->data;
283 u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
284 u8 fg = 1, d;
285
286 switch (fb_get_color_depth(&info->var, &info->fix)) {
287 case 1:
288 fg = 1;
289 break;
290 case 2:
291 fg = 3;
292 break;
293 default:
294 fg = 7;
295 break;
296 }
297
298 if (info->fix.visual == FB_VISUAL_MONO01 ||
299 info->fix.visual == FB_VISUAL_MONO10)
300 fg = ~((u8) (0xfff << info->var.green.length));
301
302 switch (depth) {
303 case 4:
304 for (i = 0; i < logo->height; i++)
305 for (j = 0; j < logo->width; src++) {
306 *dst++ = *src >> 4;
307 j++;
308 if (j < logo->width) {
309 *dst++ = *src & 0x0f;
310 j++;
311 }
312 }
313 break;
314 case 1:
315 for (i = 0; i < logo->height; i++) {
316 for (j = 0; j < logo->width; src++) {
317 d = *src ^ xor;
318 for (k = 7; k >= 0; k--) {
319 *dst++ = ((d >> k) & 1) ? fg : 0;
320 j++;
321 }
322 }
323 }
324 break;
325 }
326}
327
328/*
329 * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
330 * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
331 * the visual format and color depth of the framebuffer, the DAC, the
332 * pseudo_palette, and the logo data will be adjusted accordingly.
333 *
334 * Case 1 - linux_logo_clut224:
335 * Color exceeds the number of console colors (16), thus we set the hardware DAC
336 * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
337 *
338 * For visuals that require color info from the pseudo_palette, we also construct
339 * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
340 * will be set.
341 *
342 * Case 2 - linux_logo_vga16:
343 * The number of colors just matches the console colors, thus there is no need
344 * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
345 * each byte contains color information for two pixels (upper and lower nibble).
346 * To be consistent with fb_imageblit() usage, we therefore separate the two
347 * nibbles into separate bytes. The "depth" flag will be set to 4.
348 *
349 * Case 3 - linux_logo_mono:
350 * This is similar with Case 2. Each byte contains information for 8 pixels.
351 * We isolate each bit and expand each into a byte. The "depth" flag will
352 * be set to 1.
353 */
354static struct logo_data {
355 int depth;
356 int needs_directpalette;
357 int needs_truepalette;
358 int needs_cmapreset;
359 const struct linux_logo *logo;
360} fb_logo __read_mostly;
361
362static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
363{
364 u32 size = width * height, i;
365
366 out += size - 1;
367
368 for (i = size; i--; )
369 *out-- = *in++;
370}
371
372static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
373{
374 int i, j, h = height - 1;
375
376 for (i = 0; i < height; i++)
377 for (j = 0; j < width; j++)
378 out[height * j + h - i] = *in++;
379}
380
381static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
382{
383 int i, j, w = width - 1;
384
385 for (i = 0; i < height; i++)
386 for (j = 0; j < width; j++)
387 out[height * (w - j) + i] = *in++;
388}
389
390static void fb_rotate_logo(struct fb_info *info, u8 *dst,
391 struct fb_image *image, int rotate)
392{
393 u32 tmp;
394
395 if (rotate == FB_ROTATE_UD) {
396 fb_rotate_logo_ud(image->data, dst, image->width,
397 image->height);
398 image->dx = info->var.xres - image->width - image->dx;
399 image->dy = info->var.yres - image->height - image->dy;
400 } else if (rotate == FB_ROTATE_CW) {
401 fb_rotate_logo_cw(image->data, dst, image->width,
402 image->height);
403 tmp = image->width;
404 image->width = image->height;
405 image->height = tmp;
406 tmp = image->dy;
407 image->dy = image->dx;
408 image->dx = info->var.xres - image->width - tmp;
409 } else if (rotate == FB_ROTATE_CCW) {
410 fb_rotate_logo_ccw(image->data, dst, image->width,
411 image->height);
412 tmp = image->width;
413 image->width = image->height;
414 image->height = tmp;
415 tmp = image->dx;
416 image->dx = image->dy;
417 image->dy = info->var.yres - image->height - tmp;
418 }
419
420 image->data = dst;
421}
422
423static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
424 int rotate, unsigned int num)
425{
426 unsigned int x;
427
428 if (rotate == FB_ROTATE_UR) {
429 for (x = 0;
430 x < num && image->dx + image->width <= info->var.xres;
431 x++) {
432 info->fbops->fb_imageblit(info, image);
433 image->dx += image->width + 8;
434 }
435 } else if (rotate == FB_ROTATE_UD) {
436 for (x = 0; x < num && image->dx >= 0; x++) {
437 info->fbops->fb_imageblit(info, image);
438 image->dx -= image->width + 8;
439 }
440 } else if (rotate == FB_ROTATE_CW) {
441 for (x = 0;
442 x < num && image->dy + image->height <= info->var.yres;
443 x++) {
444 info->fbops->fb_imageblit(info, image);
445 image->dy += image->height + 8;
446 }
447 } else if (rotate == FB_ROTATE_CCW) {
448 for (x = 0; x < num && image->dy >= 0; x++) {
449 info->fbops->fb_imageblit(info, image);
450 image->dy -= image->height + 8;
451 }
452 }
453}
454
455static int fb_show_logo_line(struct fb_info *info, int rotate,
456 const struct linux_logo *logo, int y,
457 unsigned int n)
458{
459 u32 *palette = NULL, *saved_pseudo_palette = NULL;
460 unsigned char *logo_new = NULL, *logo_rotate = NULL;
461 struct fb_image image;
462
463 /* Return if the frame buffer is not mapped or suspended */
464 if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
465 info->flags & FBINFO_MODULE)
466 return 0;
467
468 image.depth = 8;
469 image.data = logo->data;
470
471 if (fb_logo.needs_cmapreset)
472 fb_set_logocmap(info, logo);
473
474 if (fb_logo.needs_truepalette ||
475 fb_logo.needs_directpalette) {
476 palette = kmalloc(256 * 4, GFP_KERNEL);
477 if (palette == NULL)
478 return 0;
479
480 if (fb_logo.needs_truepalette)
481 fb_set_logo_truepalette(info, logo, palette);
482 else
483 fb_set_logo_directpalette(info, logo, palette);
484
485 saved_pseudo_palette = info->pseudo_palette;
486 info->pseudo_palette = palette;
487 }
488
489 if (fb_logo.depth <= 4) {
490 logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);
491 if (logo_new == NULL) {
492 kfree(palette);
493 if (saved_pseudo_palette)
494 info->pseudo_palette = saved_pseudo_palette;
495 return 0;
496 }
497 image.data = logo_new;
498 fb_set_logo(info, logo, logo_new, fb_logo.depth);
499 }
500
501 image.dx = 0;
502 image.dy = y;
503 image.width = logo->width;
504 image.height = logo->height;
505
506 if (rotate) {
507 logo_rotate = kmalloc(logo->width *
508 logo->height, GFP_KERNEL);
509 if (logo_rotate)
510 fb_rotate_logo(info, logo_rotate, &image, rotate);
511 }
512
513 fb_do_show_logo(info, &image, rotate, n);
514
515 kfree(palette);
516 if (saved_pseudo_palette != NULL)
517 info->pseudo_palette = saved_pseudo_palette;
518 kfree(logo_new);
519 kfree(logo_rotate);
520 return logo->height;
521}
522
523
524#ifdef CONFIG_FB_LOGO_EXTRA
525
526#define FB_LOGO_EX_NUM_MAX 10
527static struct logo_data_extra {
528 const struct linux_logo *logo;
529 unsigned int n;
530} fb_logo_ex[FB_LOGO_EX_NUM_MAX];
531static unsigned int fb_logo_ex_num;
532
533void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
534{
535 if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
536 return;
537
538 fb_logo_ex[fb_logo_ex_num].logo = logo;
539 fb_logo_ex[fb_logo_ex_num].n = n;
540 fb_logo_ex_num++;
541}
542
543static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
544 unsigned int yres)
545{
546 unsigned int i;
547
548 /* FIXME: logo_ex supports only truecolor fb. */
549 if (info->fix.visual != FB_VISUAL_TRUECOLOR)
550 fb_logo_ex_num = 0;
551
552 for (i = 0; i < fb_logo_ex_num; i++) {
553 if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
554 fb_logo_ex[i].logo = NULL;
555 continue;
556 }
557 height += fb_logo_ex[i].logo->height;
558 if (height > yres) {
559 height -= fb_logo_ex[i].logo->height;
560 fb_logo_ex_num = i;
561 break;
562 }
563 }
564 return height;
565}
566
567static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
568{
569 unsigned int i;
570
571 for (i = 0; i < fb_logo_ex_num; i++)
572 y += fb_show_logo_line(info, rotate,
573 fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
574
575 return y;
576}
577
578#else /* !CONFIG_FB_LOGO_EXTRA */
579
580static inline int fb_prepare_extra_logos(struct fb_info *info,
581 unsigned int height,
582 unsigned int yres)
583{
584 return height;
585}
586
587static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
588{
589 return y;
590}
591
592#endif /* CONFIG_FB_LOGO_EXTRA */
593
594
595int fb_prepare_logo(struct fb_info *info, int rotate)
596{
597 int depth = fb_get_color_depth(&info->var, &info->fix);
598 unsigned int yres;
599
600 memset(&fb_logo, 0, sizeof(struct logo_data));
601
602 if (info->flags & FBINFO_MISC_TILEBLITTING ||
603 info->flags & FBINFO_MODULE)
604 return 0;
605
606 if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
607 depth = info->var.blue.length;
608 if (info->var.red.length < depth)
609 depth = info->var.red.length;
610 if (info->var.green.length < depth)
611 depth = info->var.green.length;
612 }
613
614 if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
615 /* assume console colormap */
616 depth = 4;
617 }
618
619 /* Return if no suitable logo was found */
620 fb_logo.logo = fb_find_logo(depth);
621
622 if (!fb_logo.logo) {
623 return 0;
624 }
625
626 if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
627 yres = info->var.yres;
628 else
629 yres = info->var.xres;
630
631 if (fb_logo.logo->height > yres) {
632 fb_logo.logo = NULL;
633 return 0;
634 }
635
636 /* What depth we asked for might be different from what we get */
637 if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
638 fb_logo.depth = 8;
639 else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
640 fb_logo.depth = 4;
641 else
642 fb_logo.depth = 1;
643
644
645 if (fb_logo.depth > 4 && depth > 4) {
646 switch (info->fix.visual) {
647 case FB_VISUAL_TRUECOLOR:
648 fb_logo.needs_truepalette = 1;
649 break;
650 case FB_VISUAL_DIRECTCOLOR:
651 fb_logo.needs_directpalette = 1;
652 fb_logo.needs_cmapreset = 1;
653 break;
654 case FB_VISUAL_PSEUDOCOLOR:
655 fb_logo.needs_cmapreset = 1;
656 break;
657 }
658 }
659
660 return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);
661}
662
663int fb_show_logo(struct fb_info *info, int rotate)
664{
665 int y;
666
667 y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
668 num_online_cpus());
669 y = fb_show_extra_logos(info, y, rotate);
670
671 return y;
672}
673#else
674int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
675int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
676#endif /* CONFIG_LOGO */
677EXPORT_SYMBOL(fb_show_logo);
678
679static void *fb_seq_start(struct seq_file *m, loff_t *pos)
680{
681 mutex_lock(&registration_lock);
682 return (*pos < FB_MAX) ? pos : NULL;
683}
684
685static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
686{
687 (*pos)++;
688 return (*pos < FB_MAX) ? pos : NULL;
689}
690
691static void fb_seq_stop(struct seq_file *m, void *v)
692{
693 mutex_unlock(&registration_lock);
694}
695
696static int fb_seq_show(struct seq_file *m, void *v)
697{
698 int i = *(loff_t *)v;
699 struct fb_info *fi = registered_fb[i];
700
701 if (fi)
702 seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
703 return 0;
704}
705
706static const struct seq_operations proc_fb_seq_ops = {
707 .start = fb_seq_start,
708 .next = fb_seq_next,
709 .stop = fb_seq_stop,
710 .show = fb_seq_show,
711};
712
713static int proc_fb_open(struct inode *inode, struct file *file)
714{
715 return seq_open(file, &proc_fb_seq_ops);
716}
717
718static const struct file_operations fb_proc_fops = {
719 .owner = THIS_MODULE,
720 .open = proc_fb_open,
721 .read = seq_read,
722 .llseek = seq_lseek,
723 .release = seq_release,
724};
725
726/*
727 * We hold a reference to the fb_info in file->private_data,
728 * but if the current registered fb has changed, we don't
729 * actually want to use it.
730 *
731 * So look up the fb_info using the inode minor number,
732 * and just verify it against the reference we have.
733 */
734static struct fb_info *file_fb_info(struct file *file)
735{
736 struct inode *inode = file_inode(file);
737 int fbidx = iminor(inode);
738 struct fb_info *info = registered_fb[fbidx];
739
740 if (info != file->private_data)
741 info = NULL;
742 return info;
743}
744
745static ssize_t
746fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
747{
748 unsigned long p = *ppos;
749 struct fb_info *info = file_fb_info(file);
750 u8 *buffer, *dst;
751 u8 __iomem *src;
752 int c, cnt = 0, err = 0;
753 unsigned long total_size;
754
755 if (!info || ! info->screen_base)
756 return -ENODEV;
757
758 if (info->state != FBINFO_STATE_RUNNING)
759 return -EPERM;
760
761 if (info->fbops->fb_read)
762 return info->fbops->fb_read(info, buf, count, ppos);
763
764 total_size = info->screen_size;
765
766 if (total_size == 0)
767 total_size = info->fix.smem_len;
768
769 if (p >= total_size)
770 return 0;
771
772 if (count >= total_size)
773 count = total_size;
774
775 if (count + p > total_size)
776 count = total_size - p;
777
778 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
779 GFP_KERNEL);
780 if (!buffer)
781 return -ENOMEM;
782
783 src = (u8 __iomem *) (info->screen_base + p);
784
785 if (info->fbops->fb_sync)
786 info->fbops->fb_sync(info);
787
788 while (count) {
789 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
790 dst = buffer;
791 fb_memcpy_fromfb(dst, src, c);
792 dst += c;
793 src += c;
794
795 if (copy_to_user(buf, buffer, c)) {
796 err = -EFAULT;
797 break;
798 }
799 *ppos += c;
800 buf += c;
801 cnt += c;
802 count -= c;
803 }
804
805 kfree(buffer);
806
807 return (err) ? err : cnt;
808}
809
810static ssize_t
811fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
812{
813 unsigned long p = *ppos;
814 struct fb_info *info = file_fb_info(file);
815 u8 *buffer, *src;
816 u8 __iomem *dst;
817 int c, cnt = 0, err = 0;
818 unsigned long total_size;
819
820 if (!info || !info->screen_base)
821 return -ENODEV;
822
823 if (info->state != FBINFO_STATE_RUNNING)
824 return -EPERM;
825
826 if (info->fbops->fb_write)
827 return info->fbops->fb_write(info, buf, count, ppos);
828
829 total_size = info->screen_size;
830
831 if (total_size == 0)
832 total_size = info->fix.smem_len;
833
834 if (p > total_size)
835 return -EFBIG;
836
837 if (count > total_size) {
838 err = -EFBIG;
839 count = total_size;
840 }
841
842 if (count + p > total_size) {
843 if (!err)
844 err = -ENOSPC;
845
846 count = total_size - p;
847 }
848
849 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
850 GFP_KERNEL);
851 if (!buffer)
852 return -ENOMEM;
853
854 dst = (u8 __iomem *) (info->screen_base + p);
855
856 if (info->fbops->fb_sync)
857 info->fbops->fb_sync(info);
858
859 while (count) {
860 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
861 src = buffer;
862
863 if (copy_from_user(src, buf, c)) {
864 err = -EFAULT;
865 break;
866 }
867
868 fb_memcpy_tofb(dst, src, c);
869 dst += c;
870 src += c;
871 *ppos += c;
872 buf += c;
873 cnt += c;
874 count -= c;
875 }
876
877 kfree(buffer);
878
879 return (cnt) ? cnt : err;
880}
881
882int
883fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
884{
885 struct fb_fix_screeninfo *fix = &info->fix;
886 unsigned int yres = info->var.yres;
887 int err = 0;
888
889 if (var->yoffset > 0) {
890 if (var->vmode & FB_VMODE_YWRAP) {
891 if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
892 err = -EINVAL;
893 else
894 yres = 0;
895 } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
896 err = -EINVAL;
897 }
898
899 if (var->xoffset > 0 && (!fix->xpanstep ||
900 (var->xoffset % fix->xpanstep)))
901 err = -EINVAL;
902
903 if (err || !info->fbops->fb_pan_display ||
904 var->yoffset > info->var.yres_virtual - yres ||
905 var->xoffset > info->var.xres_virtual - info->var.xres)
906 return -EINVAL;
907
908 if ((err = info->fbops->fb_pan_display(var, info)))
909 return err;
910 info->var.xoffset = var->xoffset;
911 info->var.yoffset = var->yoffset;
912 if (var->vmode & FB_VMODE_YWRAP)
913 info->var.vmode |= FB_VMODE_YWRAP;
914 else
915 info->var.vmode &= ~FB_VMODE_YWRAP;
916 return 0;
917}
918EXPORT_SYMBOL(fb_pan_display);
919
920static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
921 u32 activate)
922{
923 struct fb_event event;
924 struct fb_blit_caps caps, fbcaps;
925 int err = 0;
926
927 memset(&caps, 0, sizeof(caps));
928 memset(&fbcaps, 0, sizeof(fbcaps));
929 caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
930 event.info = info;
931 event.data = &caps;
932 fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
933 info->fbops->fb_get_caps(info, &fbcaps, var);
934
935 if (((fbcaps.x ^ caps.x) & caps.x) ||
936 ((fbcaps.y ^ caps.y) & caps.y) ||
937 (fbcaps.len < caps.len))
938 err = -EINVAL;
939
940 return err;
941}
942
943int
944fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
945{
946 int flags = info->flags;
947 int ret = 0;
948
949 if (var->activate & FB_ACTIVATE_INV_MODE) {
950 struct fb_videomode mode1, mode2;
951
952 fb_var_to_videomode(&mode1, var);
953 fb_var_to_videomode(&mode2, &info->var);
954 /* make sure we don't delete the videomode of current var */
955 ret = fb_mode_is_equal(&mode1, &mode2);
956
957 if (!ret) {
958 struct fb_event event;
959
960 event.info = info;
961 event.data = &mode1;
962 ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
963 }
964
965 if (!ret)
966 fb_delete_videomode(&mode1, &info->modelist);
967
968
969 ret = (ret) ? -EINVAL : 0;
970 goto done;
971 }
972
973 if ((var->activate & FB_ACTIVATE_FORCE) ||
974 memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
975 u32 activate = var->activate;
976
977 /* When using FOURCC mode, make sure the red, green, blue and
978 * transp fields are set to 0.
979 */
980 if ((info->fix.capabilities & FB_CAP_FOURCC) &&
981 var->grayscale > 1) {
982 if (var->red.offset || var->green.offset ||
983 var->blue.offset || var->transp.offset ||
984 var->red.length || var->green.length ||
985 var->blue.length || var->transp.length ||
986 var->red.msb_right || var->green.msb_right ||
987 var->blue.msb_right || var->transp.msb_right)
988 return -EINVAL;
989 }
990
991 if (!info->fbops->fb_check_var) {
992 *var = info->var;
993 goto done;
994 }
995
996 ret = info->fbops->fb_check_var(var, info);
997
998 if (ret)
999 goto done;
1000
1001 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
1002 struct fb_var_screeninfo old_var;
1003 struct fb_videomode mode;
1004
1005 if (info->fbops->fb_get_caps) {
1006 ret = fb_check_caps(info, var, activate);
1007
1008 if (ret)
1009 goto done;
1010 }
1011
1012 old_var = info->var;
1013 info->var = *var;
1014
1015 if (info->fbops->fb_set_par) {
1016 ret = info->fbops->fb_set_par(info);
1017
1018 if (ret) {
1019 info->var = old_var;
1020 printk(KERN_WARNING "detected "
1021 "fb_set_par error, "
1022 "error code: %d\n", ret);
1023 goto done;
1024 }
1025 }
1026
1027 fb_pan_display(info, &info->var);
1028 fb_set_cmap(&info->cmap, info);
1029 fb_var_to_videomode(&mode, &info->var);
1030
1031 if (info->modelist.prev && info->modelist.next &&
1032 !list_empty(&info->modelist))
1033 ret = fb_add_videomode(&mode, &info->modelist);
1034
1035 if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
1036 struct fb_event event;
1037 int evnt = (activate & FB_ACTIVATE_ALL) ?
1038 FB_EVENT_MODE_CHANGE_ALL :
1039 FB_EVENT_MODE_CHANGE;
1040
1041 info->flags &= ~FBINFO_MISC_USEREVENT;
1042 event.info = info;
1043 event.data = &mode;
1044 fb_notifier_call_chain(evnt, &event);
1045 }
1046 }
1047 }
1048
1049 done:
1050 return ret;
1051}
1052EXPORT_SYMBOL(fb_set_var);
1053
1054int
1055fb_blank(struct fb_info *info, int blank)
1056{
1057 struct fb_event event;
1058 int ret = -EINVAL, early_ret;
1059
1060 if (blank > FB_BLANK_POWERDOWN)
1061 blank = FB_BLANK_POWERDOWN;
1062
1063 event.info = info;
1064 event.data = &blank;
1065
1066 early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);
1067
1068 if (info->fbops->fb_blank)
1069 ret = info->fbops->fb_blank(blank, info);
1070
1071 if (!ret)
1072 fb_notifier_call_chain(FB_EVENT_BLANK, &event);
1073 else {
1074 /*
1075 * if fb_blank is failed then revert effects of
1076 * the early blank event.
1077 */
1078 if (!early_ret)
1079 fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
1080 }
1081
1082 return ret;
1083}
1084EXPORT_SYMBOL(fb_blank);
1085
1086static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
1087 unsigned long arg)
1088{
1089 struct fb_ops *fb;
1090 struct fb_var_screeninfo var;
1091 struct fb_fix_screeninfo fix;
1092 struct fb_con2fbmap con2fb;
1093 struct fb_cmap cmap_from;
1094 struct fb_cmap_user cmap;
1095 struct fb_event event;
1096 void __user *argp = (void __user *)arg;
1097 long ret = 0;
1098
1099 switch (cmd) {
1100 case FBIOGET_VSCREENINFO:
1101 if (!lock_fb_info(info))
1102 return -ENODEV;
1103 var = info->var;
1104 unlock_fb_info(info);
1105
1106 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
1107 break;
1108 case FBIOPUT_VSCREENINFO:
1109 if (copy_from_user(&var, argp, sizeof(var)))
1110 return -EFAULT;
1111 console_lock();
1112 if (!lock_fb_info(info)) {
1113 console_unlock();
1114 return -ENODEV;
1115 }
1116 info->flags |= FBINFO_MISC_USEREVENT;
1117 ret = fb_set_var(info, &var);
1118 info->flags &= ~FBINFO_MISC_USEREVENT;
1119 unlock_fb_info(info);
1120 console_unlock();
1121 if (!ret && copy_to_user(argp, &var, sizeof(var)))
1122 ret = -EFAULT;
1123 break;
1124 case FBIOGET_FSCREENINFO:
1125 if (!lock_fb_info(info))
1126 return -ENODEV;
1127 fix = info->fix;
1128 unlock_fb_info(info);
1129
1130 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
1131 break;
1132 case FBIOPUTCMAP:
1133 if (copy_from_user(&cmap, argp, sizeof(cmap)))
1134 return -EFAULT;
1135 ret = fb_set_user_cmap(&cmap, info);
1136 break;
1137 case FBIOGETCMAP:
1138 if (copy_from_user(&cmap, argp, sizeof(cmap)))
1139 return -EFAULT;
1140 if (!lock_fb_info(info))
1141 return -ENODEV;
1142 cmap_from = info->cmap;
1143 unlock_fb_info(info);
1144 ret = fb_cmap_to_user(&cmap_from, &cmap);
1145 break;
1146 case FBIOPAN_DISPLAY:
1147 if (copy_from_user(&var, argp, sizeof(var)))
1148 return -EFAULT;
1149 console_lock();
1150 if (!lock_fb_info(info)) {
1151 console_unlock();
1152 return -ENODEV;
1153 }
1154 ret = fb_pan_display(info, &var);
1155 unlock_fb_info(info);
1156 console_unlock();
1157 if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
1158 return -EFAULT;
1159 break;
1160 case FBIO_CURSOR:
1161 ret = -EINVAL;
1162 break;
1163 case FBIOGET_CON2FBMAP:
1164 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1165 return -EFAULT;
1166 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1167 return -EINVAL;
1168 con2fb.framebuffer = -1;
1169 event.data = &con2fb;
1170 if (!lock_fb_info(info))
1171 return -ENODEV;
1172 event.info = info;
1173 fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
1174 unlock_fb_info(info);
1175 ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
1176 break;
1177 case FBIOPUT_CON2FBMAP:
1178 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1179 return -EFAULT;
1180 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
1181 return -EINVAL;
1182 if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
1183 return -EINVAL;
1184 if (!registered_fb[con2fb.framebuffer])
1185 request_module("fb%d", con2fb.framebuffer);
1186 if (!registered_fb[con2fb.framebuffer]) {
1187 ret = -EINVAL;
1188 break;
1189 }
1190 event.data = &con2fb;
1191 console_lock();
1192 if (!lock_fb_info(info)) {
1193 console_unlock();
1194 return -ENODEV;
1195 }
1196 event.info = info;
1197 ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
1198 unlock_fb_info(info);
1199 console_unlock();
1200 break;
1201 case FBIOBLANK:
1202 console_lock();
1203 if (!lock_fb_info(info)) {
1204 console_unlock();
1205 return -ENODEV;
1206 }
1207 info->flags |= FBINFO_MISC_USEREVENT;
1208 ret = fb_blank(info, arg);
1209 info->flags &= ~FBINFO_MISC_USEREVENT;
1210 unlock_fb_info(info);
1211 console_unlock();
1212 break;
1213 default:
1214 if (!lock_fb_info(info))
1215 return -ENODEV;
1216 fb = info->fbops;
1217 if (fb->fb_ioctl)
1218 ret = fb->fb_ioctl(info, cmd, arg);
1219 else
1220 ret = -ENOTTY;
1221 unlock_fb_info(info);
1222 }
1223 return ret;
1224}
1225
1226static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1227{
1228 struct fb_info *info = file_fb_info(file);
1229
1230 if (!info)
1231 return -ENODEV;
1232 return do_fb_ioctl(info, cmd, arg);
1233}
1234
1235#ifdef CONFIG_COMPAT
1236struct fb_fix_screeninfo32 {
1237 char id[16];
1238 compat_caddr_t smem_start;
1239 u32 smem_len;
1240 u32 type;
1241 u32 type_aux;
1242 u32 visual;
1243 u16 xpanstep;
1244 u16 ypanstep;
1245 u16 ywrapstep;
1246 u32 line_length;
1247 compat_caddr_t mmio_start;
1248 u32 mmio_len;
1249 u32 accel;
1250 u16 reserved[3];
1251};
1252
1253struct fb_cmap32 {
1254 u32 start;
1255 u32 len;
1256 compat_caddr_t red;
1257 compat_caddr_t green;
1258 compat_caddr_t blue;
1259 compat_caddr_t transp;
1260};
1261
1262static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
1263 unsigned long arg)
1264{
1265 struct fb_cmap_user __user *cmap;
1266 struct fb_cmap32 __user *cmap32;
1267 __u32 data;
1268 int err;
1269
1270 cmap = compat_alloc_user_space(sizeof(*cmap));
1271 cmap32 = compat_ptr(arg);
1272
1273 if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32)))
1274 return -EFAULT;
1275
1276 if (get_user(data, &cmap32->red) ||
1277 put_user(compat_ptr(data), &cmap->red) ||
1278 get_user(data, &cmap32->green) ||
1279 put_user(compat_ptr(data), &cmap->green) ||
1280 get_user(data, &cmap32->blue) ||
1281 put_user(compat_ptr(data), &cmap->blue) ||
1282 get_user(data, &cmap32->transp) ||
1283 put_user(compat_ptr(data), &cmap->transp))
1284 return -EFAULT;
1285
1286 err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
1287
1288 if (!err) {
1289 if (copy_in_user(&cmap32->start,
1290 &cmap->start,
1291 2 * sizeof(__u32)))
1292 err = -EFAULT;
1293 }
1294 return err;
1295}
1296
1297static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
1298 struct fb_fix_screeninfo32 __user *fix32)
1299{
1300 __u32 data;
1301 int err;
1302
1303 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
1304
1305 data = (__u32) (unsigned long) fix->smem_start;
1306 err |= put_user(data, &fix32->smem_start);
1307
1308 err |= put_user(fix->smem_len, &fix32->smem_len);
1309 err |= put_user(fix->type, &fix32->type);
1310 err |= put_user(fix->type_aux, &fix32->type_aux);
1311 err |= put_user(fix->visual, &fix32->visual);
1312 err |= put_user(fix->xpanstep, &fix32->xpanstep);
1313 err |= put_user(fix->ypanstep, &fix32->ypanstep);
1314 err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
1315 err |= put_user(fix->line_length, &fix32->line_length);
1316
1317 data = (__u32) (unsigned long) fix->mmio_start;
1318 err |= put_user(data, &fix32->mmio_start);
1319
1320 err |= put_user(fix->mmio_len, &fix32->mmio_len);
1321 err |= put_user(fix->accel, &fix32->accel);
1322 err |= copy_to_user(fix32->reserved, fix->reserved,
1323 sizeof(fix->reserved));
1324
1325 if (err)
1326 return -EFAULT;
1327 return 0;
1328}
1329
1330static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
1331 unsigned long arg)
1332{
1333 mm_segment_t old_fs;
1334 struct fb_fix_screeninfo fix;
1335 struct fb_fix_screeninfo32 __user *fix32;
1336 int err;
1337
1338 fix32 = compat_ptr(arg);
1339
1340 old_fs = get_fs();
1341 set_fs(KERNEL_DS);
1342 err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
1343 set_fs(old_fs);
1344
1345 if (!err)
1346 err = do_fscreeninfo_to_user(&fix, fix32);
1347
1348 return err;
1349}
1350
1351static long fb_compat_ioctl(struct file *file, unsigned int cmd,
1352 unsigned long arg)
1353{
1354 struct fb_info *info = file_fb_info(file);
1355 struct fb_ops *fb;
1356 long ret = -ENOIOCTLCMD;
1357
1358 if (!info)
1359 return -ENODEV;
1360 fb = info->fbops;
1361 switch(cmd) {
1362 case FBIOGET_VSCREENINFO:
1363 case FBIOPUT_VSCREENINFO:
1364 case FBIOPAN_DISPLAY:
1365 case FBIOGET_CON2FBMAP:
1366 case FBIOPUT_CON2FBMAP:
1367 arg = (unsigned long) compat_ptr(arg);
1368 case FBIOBLANK:
1369 ret = do_fb_ioctl(info, cmd, arg);
1370 break;
1371
1372 case FBIOGET_FSCREENINFO:
1373 ret = fb_get_fscreeninfo(info, cmd, arg);
1374 break;
1375
1376 case FBIOGETCMAP:
1377 case FBIOPUTCMAP:
1378 ret = fb_getput_cmap(info, cmd, arg);
1379 break;
1380
1381 default:
1382 if (fb->fb_compat_ioctl)
1383 ret = fb->fb_compat_ioctl(info, cmd, arg);
1384 break;
1385 }
1386 return ret;
1387}
1388#endif
1389
1390static int
1391fb_mmap(struct file *file, struct vm_area_struct * vma)
1392{
1393 struct fb_info *info = file_fb_info(file);
1394 struct fb_ops *fb;
1395 unsigned long mmio_pgoff;
1396 unsigned long start;
1397 u32 len;
1398
1399 if (!info)
1400 return -ENODEV;
1401 fb = info->fbops;
1402 if (!fb)
1403 return -ENODEV;
1404 mutex_lock(&info->mm_lock);
1405 if (fb->fb_mmap) {
1406 int res;
1407 res = fb->fb_mmap(info, vma);
1408 mutex_unlock(&info->mm_lock);
1409 return res;
1410 }
1411
1412 /*
1413 * Ugh. This can be either the frame buffer mapping, or
1414 * if pgoff points past it, the mmio mapping.
1415 */
1416 start = info->fix.smem_start;
1417 len = info->fix.smem_len;
1418 mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
1419 if (vma->vm_pgoff >= mmio_pgoff) {
1420 if (info->var.accel_flags) {
1421 mutex_unlock(&info->mm_lock);
1422 return -EINVAL;
1423 }
1424
1425 vma->vm_pgoff -= mmio_pgoff;
1426 start = info->fix.mmio_start;
1427 len = info->fix.mmio_len;
1428 }
1429 mutex_unlock(&info->mm_lock);
1430
1431 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
1432 fb_pgprotect(file, vma, start);
1433
1434 return vm_iomap_memory(vma, start, len);
1435}
1436
1437static int
1438fb_open(struct inode *inode, struct file *file)
1439__acquires(&info->lock)
1440__releases(&info->lock)
1441{
1442 int fbidx = iminor(inode);
1443 struct fb_info *info;
1444 int res = 0;
1445
1446 info = get_fb_info(fbidx);
1447 if (!info) {
1448 request_module("fb%d", fbidx);
1449 info = get_fb_info(fbidx);
1450 if (!info)
1451 return -ENODEV;
1452 }
1453 if (IS_ERR(info))
1454 return PTR_ERR(info);
1455
1456 mutex_lock(&info->lock);
1457 if (!try_module_get(info->fbops->owner)) {
1458 res = -ENODEV;
1459 goto out;
1460 }
1461 file->private_data = info;
1462 if (info->fbops->fb_open) {
1463 res = info->fbops->fb_open(info,1);
1464 if (res)
1465 module_put(info->fbops->owner);
1466 }
1467#ifdef CONFIG_FB_DEFERRED_IO
1468 if (info->fbdefio)
1469 fb_deferred_io_open(info, inode, file);
1470#endif
1471out:
1472 mutex_unlock(&info->lock);
1473 if (res)
1474 put_fb_info(info);
1475 return res;
1476}
1477
1478static int
1479fb_release(struct inode *inode, struct file *file)
1480__acquires(&info->lock)
1481__releases(&info->lock)
1482{
1483 struct fb_info * const info = file->private_data;
1484
1485 mutex_lock(&info->lock);
1486 if (info->fbops->fb_release)
1487 info->fbops->fb_release(info,1);
1488 module_put(info->fbops->owner);
1489 mutex_unlock(&info->lock);
1490 put_fb_info(info);
1491 return 0;
1492}
1493
1494static const struct file_operations fb_fops = {
1495 .owner = THIS_MODULE,
1496 .read = fb_read,
1497 .write = fb_write,
1498 .unlocked_ioctl = fb_ioctl,
1499#ifdef CONFIG_COMPAT
1500 .compat_ioctl = fb_compat_ioctl,
1501#endif
1502 .mmap = fb_mmap,
1503 .open = fb_open,
1504 .release = fb_release,
1505#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1506 .get_unmapped_area = get_fb_unmapped_area,
1507#endif
1508#ifdef CONFIG_FB_DEFERRED_IO
1509 .fsync = fb_deferred_io_fsync,
1510#endif
1511 .llseek = default_llseek,
1512};
1513
1514struct class *fb_class;
1515EXPORT_SYMBOL(fb_class);
1516
1517static int fb_check_foreignness(struct fb_info *fi)
1518{
1519 const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
1520
1521 fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
1522
1523#ifdef __BIG_ENDIAN
1524 fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
1525#else
1526 fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
1527#endif /* __BIG_ENDIAN */
1528
1529 if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
1530 pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
1531 "support this framebuffer\n", fi->fix.id);
1532 return -ENOSYS;
1533 } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
1534 pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
1535 "support this framebuffer\n", fi->fix.id);
1536 return -ENOSYS;
1537 }
1538
1539 return 0;
1540}
1541
1542static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
1543{
1544 /* is the generic aperture base the same as the HW one */
1545 if (gen->base == hw->base)
1546 return true;
1547 /* is the generic aperture base inside the hw base->hw base+size */
1548 if (gen->base > hw->base && gen->base < hw->base + hw->size)
1549 return true;
1550 return false;
1551}
1552
1553static bool fb_do_apertures_overlap(struct apertures_struct *gena,
1554 struct apertures_struct *hwa)
1555{
1556 int i, j;
1557 if (!hwa || !gena)
1558 return false;
1559
1560 for (i = 0; i < hwa->count; ++i) {
1561 struct aperture *h = &hwa->ranges[i];
1562 for (j = 0; j < gena->count; ++j) {
1563 struct aperture *g = &gena->ranges[j];
1564 printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
1565 (unsigned long long)g->base,
1566 (unsigned long long)g->size,
1567 (unsigned long long)h->base,
1568 (unsigned long long)h->size);
1569 if (apertures_overlap(g, h))
1570 return true;
1571 }
1572 }
1573
1574 return false;
1575}
1576
1577static int do_unregister_framebuffer(struct fb_info *fb_info);
1578
1579#define VGA_FB_PHYS 0xA0000
1580static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
1581 const char *name, bool primary)
1582{
1583 int i, ret;
1584
1585 /* check all firmware fbs and kick off if the base addr overlaps */
1586 for (i = 0 ; i < FB_MAX; i++) {
1587 struct apertures_struct *gen_aper;
1588 if (!registered_fb[i])
1589 continue;
1590
1591 if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
1592 continue;
1593
1594 gen_aper = registered_fb[i]->apertures;
1595 if (fb_do_apertures_overlap(gen_aper, a) ||
1596 (primary && gen_aper && gen_aper->count &&
1597 gen_aper->ranges[0].base == VGA_FB_PHYS)) {
1598
1599 printk(KERN_INFO "fb: switching to %s from %s\n",
1600 name, registered_fb[i]->fix.id);
1601 ret = do_unregister_framebuffer(registered_fb[i]);
1602 if (ret)
1603 return ret;
1604 }
1605 }
1606
1607 return 0;
1608}
1609
1610static int do_register_framebuffer(struct fb_info *fb_info)
1611{
1612 int i, ret;
1613 struct fb_event event;
1614 struct fb_videomode mode;
1615
1616 if (fb_check_foreignness(fb_info))
1617 return -ENOSYS;
1618
1619 ret = do_remove_conflicting_framebuffers(fb_info->apertures,
1620 fb_info->fix.id,
1621 fb_is_primary_device(fb_info));
1622 if (ret)
1623 return ret;
1624
1625 if (num_registered_fb == FB_MAX)
1626 return -ENXIO;
1627
1628 num_registered_fb++;
1629 for (i = 0 ; i < FB_MAX; i++)
1630 if (!registered_fb[i])
1631 break;
1632 fb_info->node = i;
1633 atomic_set(&fb_info->count, 1);
1634 mutex_init(&fb_info->lock);
1635 mutex_init(&fb_info->mm_lock);
1636
1637 fb_info->dev = device_create(fb_class, fb_info->device,
1638 MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
1639 if (IS_ERR(fb_info->dev)) {
1640 /* Not fatal */
1641 printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1642 fb_info->dev = NULL;
1643 } else
1644 fb_init_device(fb_info);
1645
1646 if (fb_info->pixmap.addr == NULL) {
1647 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
1648 if (fb_info->pixmap.addr) {
1649 fb_info->pixmap.size = FBPIXMAPSIZE;
1650 fb_info->pixmap.buf_align = 1;
1651 fb_info->pixmap.scan_align = 1;
1652 fb_info->pixmap.access_align = 32;
1653 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
1654 }
1655 }
1656 fb_info->pixmap.offset = 0;
1657
1658 if (!fb_info->pixmap.blit_x)
1659 fb_info->pixmap.blit_x = ~(u32)0;
1660
1661 if (!fb_info->pixmap.blit_y)
1662 fb_info->pixmap.blit_y = ~(u32)0;
1663
1664 if (!fb_info->modelist.prev || !fb_info->modelist.next)
1665 INIT_LIST_HEAD(&fb_info->modelist);
1666
1667 if (fb_info->skip_vt_switch)
1668 pm_vt_switch_required(fb_info->dev, false);
1669 else
1670 pm_vt_switch_required(fb_info->dev, true);
1671
1672 fb_var_to_videomode(&mode, &fb_info->var);
1673 fb_add_videomode(&mode, &fb_info->modelist);
1674 registered_fb[i] = fb_info;
1675
1676 event.info = fb_info;
1677 console_lock();
1678 if (!lock_fb_info(fb_info)) {
1679 console_unlock();
1680 return -ENODEV;
1681 }
1682
1683 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
1684 unlock_fb_info(fb_info);
1685 console_unlock();
1686 return 0;
1687}
1688
1689static int do_unregister_framebuffer(struct fb_info *fb_info)
1690{
1691 struct fb_event event;
1692 int i, ret = 0;
1693
1694 i = fb_info->node;
1695 if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
1696 return -EINVAL;
1697
1698 console_lock();
1699 if (!lock_fb_info(fb_info)) {
1700 console_unlock();
1701 return -ENODEV;
1702 }
1703
1704 event.info = fb_info;
1705 ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
1706 unlock_fb_info(fb_info);
1707 console_unlock();
1708
1709 if (ret)
1710 return -EINVAL;
1711
1712 pm_vt_switch_unregister(fb_info->dev);
1713
1714 unlink_framebuffer(fb_info);
1715 if (fb_info->pixmap.addr &&
1716 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1717 kfree(fb_info->pixmap.addr);
1718 fb_destroy_modelist(&fb_info->modelist);
1719 registered_fb[i] = NULL;
1720 num_registered_fb--;
1721 fb_cleanup_device(fb_info);
1722 event.info = fb_info;
1723 console_lock();
1724 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1725 console_unlock();
1726
1727 /* this may free fb info */
1728 put_fb_info(fb_info);
1729 return 0;
1730}
1731
1732int unlink_framebuffer(struct fb_info *fb_info)
1733{
1734 int i;
1735
1736 i = fb_info->node;
1737 if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
1738 return -EINVAL;
1739
1740 if (fb_info->dev) {
1741 device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1742 fb_info->dev = NULL;
1743 }
1744 return 0;
1745}
1746EXPORT_SYMBOL(unlink_framebuffer);
1747
1748int remove_conflicting_framebuffers(struct apertures_struct *a,
1749 const char *name, bool primary)
1750{
1751 int ret;
1752
1753 mutex_lock(&registration_lock);
1754 ret = do_remove_conflicting_framebuffers(a, name, primary);
1755 mutex_unlock(&registration_lock);
1756
1757 return ret;
1758}
1759EXPORT_SYMBOL(remove_conflicting_framebuffers);
1760
1761/**
1762 * register_framebuffer - registers a frame buffer device
1763 * @fb_info: frame buffer info structure
1764 *
1765 * Registers a frame buffer device @fb_info.
1766 *
1767 * Returns negative errno on error, or zero for success.
1768 *
1769 */
1770int
1771register_framebuffer(struct fb_info *fb_info)
1772{
1773 int ret;
1774
1775 mutex_lock(&registration_lock);
1776 ret = do_register_framebuffer(fb_info);
1777 mutex_unlock(&registration_lock);
1778
1779 return ret;
1780}
1781EXPORT_SYMBOL(register_framebuffer);
1782
1783/**
1784 * unregister_framebuffer - releases a frame buffer device
1785 * @fb_info: frame buffer info structure
1786 *
1787 * Unregisters a frame buffer device @fb_info.
1788 *
1789 * Returns negative errno on error, or zero for success.
1790 *
1791 * This function will also notify the framebuffer console
1792 * to release the driver.
1793 *
1794 * This is meant to be called within a driver's module_exit()
1795 * function. If this is called outside module_exit(), ensure
1796 * that the driver implements fb_open() and fb_release() to
1797 * check that no processes are using the device.
1798 */
1799int
1800unregister_framebuffer(struct fb_info *fb_info)
1801{
1802 int ret;
1803
1804 mutex_lock(&registration_lock);
1805 ret = do_unregister_framebuffer(fb_info);
1806 mutex_unlock(&registration_lock);
1807
1808 return ret;
1809}
1810EXPORT_SYMBOL(unregister_framebuffer);
1811
1812/**
1813 * fb_set_suspend - low level driver signals suspend
1814 * @info: framebuffer affected
1815 * @state: 0 = resuming, !=0 = suspending
1816 *
1817 * This is meant to be used by low level drivers to
1818 * signal suspend/resume to the core & clients.
1819 * It must be called with the console semaphore held
1820 */
1821void fb_set_suspend(struct fb_info *info, int state)
1822{
1823 struct fb_event event;
1824
1825 event.info = info;
1826 if (state) {
1827 fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
1828 info->state = FBINFO_STATE_SUSPENDED;
1829 } else {
1830 info->state = FBINFO_STATE_RUNNING;
1831 fb_notifier_call_chain(FB_EVENT_RESUME, &event);
1832 }
1833}
1834EXPORT_SYMBOL(fb_set_suspend);
1835
1836/**
1837 * fbmem_init - init frame buffer subsystem
1838 *
1839 * Initialize the frame buffer subsystem.
1840 *
1841 * NOTE: This function is _only_ to be called by drivers/char/mem.c.
1842 *
1843 */
1844
1845static int __init
1846fbmem_init(void)
1847{
1848 proc_create("fb", 0, NULL, &fb_proc_fops);
1849
1850 if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
1851 printk("unable to get major %d for fb devs\n", FB_MAJOR);
1852
1853 fb_class = class_create(THIS_MODULE, "graphics");
1854 if (IS_ERR(fb_class)) {
1855 printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
1856 fb_class = NULL;
1857 }
1858 return 0;
1859}
1860
1861#ifdef MODULE
1862module_init(fbmem_init);
1863static void __exit
1864fbmem_exit(void)
1865{
1866 remove_proc_entry("fb", NULL);
1867 class_destroy(fb_class);
1868 unregister_chrdev(FB_MAJOR, "fb");
1869}
1870
1871module_exit(fbmem_exit);
1872MODULE_LICENSE("GPL");
1873MODULE_DESCRIPTION("Framebuffer base");
1874#else
1875subsys_initcall(fbmem_init);
1876#endif
1877
1878int fb_new_modelist(struct fb_info *info)
1879{
1880 struct fb_event event;
1881 struct fb_var_screeninfo var = info->var;
1882 struct list_head *pos, *n;
1883 struct fb_modelist *modelist;
1884 struct fb_videomode *m, mode;
1885 int err = 1;
1886
1887 list_for_each_safe(pos, n, &info->modelist) {
1888 modelist = list_entry(pos, struct fb_modelist, list);
1889 m = &modelist->mode;
1890 fb_videomode_to_var(&var, m);
1891 var.activate = FB_ACTIVATE_TEST;
1892 err = fb_set_var(info, &var);
1893 fb_var_to_videomode(&mode, &var);
1894 if (err || !fb_mode_is_equal(m, &mode)) {
1895 list_del(pos);
1896 kfree(pos);
1897 }
1898 }
1899
1900 err = 1;
1901
1902 if (!list_empty(&info->modelist)) {
1903 event.info = info;
1904 err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
1905 }
1906
1907 return err;
1908}
1909
1910static char *video_options[FB_MAX] __read_mostly;
1911static int ofonly __read_mostly;
1912
1913/**
1914 * fb_get_options - get kernel boot parameters
1915 * @name: framebuffer name as it would appear in
1916 * the boot parameter line
1917 * (video=<name>:<options>)
1918 * @option: the option will be stored here
1919 *
1920 * NOTE: Needed to maintain backwards compatibility
1921 */
1922int fb_get_options(const char *name, char **option)
1923{
1924 char *opt, *options = NULL;
1925 int retval = 0;
1926 int name_len = strlen(name), i;
1927
1928 if (name_len && ofonly && strncmp(name, "offb", 4))
1929 retval = 1;
1930
1931 if (name_len && !retval) {
1932 for (i = 0; i < FB_MAX; i++) {
1933 if (video_options[i] == NULL)
1934 continue;
1935 if (!video_options[i][0])
1936 continue;
1937 opt = video_options[i];
1938 if (!strncmp(name, opt, name_len) &&
1939 opt[name_len] == ':')
1940 options = opt + name_len + 1;
1941 }
1942 }
1943 /* No match, pass global option */
1944 if (!options && option && fb_mode_option)
1945 options = kstrdup(fb_mode_option, GFP_KERNEL);
1946 if (options && !strncmp(options, "off", 3))
1947 retval = 1;
1948
1949 if (option)
1950 *option = options;
1951
1952 return retval;
1953}
1954EXPORT_SYMBOL(fb_get_options);
1955
1956#ifndef MODULE
1957/**
1958 * video_setup - process command line options
1959 * @options: string of options
1960 *
1961 * Process command line options for frame buffer subsystem.
1962 *
1963 * NOTE: This function is a __setup and __init function.
1964 * It only stores the options. Drivers have to call
1965 * fb_get_options() as necessary.
1966 *
1967 * Returns zero.
1968 *
1969 */
1970static int __init video_setup(char *options)
1971{
1972 int i, global = 0;
1973
1974 if (!options || !*options)
1975 global = 1;
1976
1977 if (!global && !strncmp(options, "ofonly", 6)) {
1978 ofonly = 1;
1979 global = 1;
1980 }
1981
1982 if (!global && !strchr(options, ':')) {
1983 fb_mode_option = options;
1984 global = 1;
1985 }
1986
1987 if (!global) {
1988 for (i = 0; i < FB_MAX; i++) {
1989 if (video_options[i] == NULL) {
1990 video_options[i] = options;
1991 break;
1992 }
1993
1994 }
1995 }
1996
1997 return 1;
1998}
1999__setup("video=", video_setup);
2000#endif
2001
2002MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
new file mode 100644
index 000000000000..c204ebe6187e
--- /dev/null
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -0,0 +1,1592 @@
1/*
2 * linux/drivers/video/fbmon.c
3 *
4 * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
5 *
6 * Credits:
7 *
8 * The EDID Parser is a conglomeration from the following sources:
9 *
10 * 1. SciTech SNAP Graphics Architecture
11 * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
12 *
13 * 2. XFree86 4.3.0, interpret_edid.c
14 * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
15 *
16 * 3. John Fremlin <vii@users.sourceforge.net> and
17 * Ani Joshi <ajoshi@unixbox.com>
18 *
19 * Generalized Timing Formula is derived from:
20 *
21 * GTF Spreadsheet by Andy Morrish (1/5/97)
22 * available at http://www.vesa.org
23 *
24 * This file is subject to the terms and conditions of the GNU General Public
25 * License. See the file COPYING in the main directory of this archive
26 * for more details.
27 *
28 */
29#include <linux/fb.h>
30#include <linux/module.h>
31#include <linux/pci.h>
32#include <linux/slab.h>
33#include <video/edid.h>
34#include <video/of_videomode.h>
35#include <video/videomode.h>
36#ifdef CONFIG_PPC_OF
37#include <asm/prom.h>
38#include <asm/pci-bridge.h>
39#endif
40#include "../edid.h"
41
42/*
43 * EDID parser
44 */
45
46#undef DEBUG /* define this for verbose EDID parsing output */
47
48#ifdef DEBUG
49#define DPRINTK(fmt, args...) printk(fmt,## args)
50#else
51#define DPRINTK(fmt, args...)
52#endif
53
54#define FBMON_FIX_HEADER 1
55#define FBMON_FIX_INPUT 2
56#define FBMON_FIX_TIMINGS 3
57
58#ifdef CONFIG_FB_MODE_HELPERS
59struct broken_edid {
60 u8 manufacturer[4];
61 u32 model;
62 u32 fix;
63};
64
65static const struct broken_edid brokendb[] = {
66 /* DEC FR-PCXAV-YZ */
67 {
68 .manufacturer = "DEC",
69 .model = 0x073a,
70 .fix = FBMON_FIX_HEADER,
71 },
72 /* ViewSonic PF775a */
73 {
74 .manufacturer = "VSC",
75 .model = 0x5a44,
76 .fix = FBMON_FIX_INPUT,
77 },
78 /* Sharp UXGA? */
79 {
80 .manufacturer = "SHP",
81 .model = 0x138e,
82 .fix = FBMON_FIX_TIMINGS,
83 },
84};
85
86static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
87 0xff, 0xff, 0xff, 0x00
88};
89
90static void copy_string(unsigned char *c, unsigned char *s)
91{
92 int i;
93 c = c + 5;
94 for (i = 0; (i < 13 && *c != 0x0A); i++)
95 *(s++) = *(c++);
96 *s = 0;
97 while (i-- && (*--s == 0x20)) *s = 0;
98}
99
100static int edid_is_serial_block(unsigned char *block)
101{
102 if ((block[0] == 0x00) && (block[1] == 0x00) &&
103 (block[2] == 0x00) && (block[3] == 0xff) &&
104 (block[4] == 0x00))
105 return 1;
106 else
107 return 0;
108}
109
110static int edid_is_ascii_block(unsigned char *block)
111{
112 if ((block[0] == 0x00) && (block[1] == 0x00) &&
113 (block[2] == 0x00) && (block[3] == 0xfe) &&
114 (block[4] == 0x00))
115 return 1;
116 else
117 return 0;
118}
119
120static int edid_is_limits_block(unsigned char *block)
121{
122 if ((block[0] == 0x00) && (block[1] == 0x00) &&
123 (block[2] == 0x00) && (block[3] == 0xfd) &&
124 (block[4] == 0x00))
125 return 1;
126 else
127 return 0;
128}
129
130static int edid_is_monitor_block(unsigned char *block)
131{
132 if ((block[0] == 0x00) && (block[1] == 0x00) &&
133 (block[2] == 0x00) && (block[3] == 0xfc) &&
134 (block[4] == 0x00))
135 return 1;
136 else
137 return 0;
138}
139
140static int edid_is_timing_block(unsigned char *block)
141{
142 if ((block[0] != 0x00) || (block[1] != 0x00) ||
143 (block[2] != 0x00) || (block[4] != 0x00))
144 return 1;
145 else
146 return 0;
147}
148
149static int check_edid(unsigned char *edid)
150{
151 unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
152 unsigned char *b;
153 u32 model;
154 int i, fix = 0, ret = 0;
155
156 manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
157 manufacturer[1] = ((block[0] & 0x03) << 3) +
158 ((block[1] & 0xe0) >> 5) + '@';
159 manufacturer[2] = (block[1] & 0x1f) + '@';
160 manufacturer[3] = 0;
161 model = block[2] + (block[3] << 8);
162
163 for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
164 if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
165 brokendb[i].model == model) {
166 fix = brokendb[i].fix;
167 break;
168 }
169 }
170
171 switch (fix) {
172 case FBMON_FIX_HEADER:
173 for (i = 0; i < 8; i++) {
174 if (edid[i] != edid_v1_header[i]) {
175 ret = fix;
176 break;
177 }
178 }
179 break;
180 case FBMON_FIX_INPUT:
181 b = edid + EDID_STRUCT_DISPLAY;
182 /* Only if display is GTF capable will
183 the input type be reset to analog */
184 if (b[4] & 0x01 && b[0] & 0x80)
185 ret = fix;
186 break;
187 case FBMON_FIX_TIMINGS:
188 b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
189 ret = fix;
190
191 for (i = 0; i < 4; i++) {
192 if (edid_is_limits_block(b)) {
193 ret = 0;
194 break;
195 }
196
197 b += DETAILED_TIMING_DESCRIPTION_SIZE;
198 }
199
200 break;
201 }
202
203 if (ret)
204 printk("fbmon: The EDID Block of "
205 "Manufacturer: %s Model: 0x%x is known to "
206 "be broken,\n", manufacturer, model);
207
208 return ret;
209}
210
211static void fix_edid(unsigned char *edid, int fix)
212{
213 int i;
214 unsigned char *b, csum = 0;
215
216 switch (fix) {
217 case FBMON_FIX_HEADER:
218 printk("fbmon: trying a header reconstruct\n");
219 memcpy(edid, edid_v1_header, 8);
220 break;
221 case FBMON_FIX_INPUT:
222 printk("fbmon: trying to fix input type\n");
223 b = edid + EDID_STRUCT_DISPLAY;
224 b[0] &= ~0x80;
225 edid[127] += 0x80;
226 break;
227 case FBMON_FIX_TIMINGS:
228 printk("fbmon: trying to fix monitor timings\n");
229 b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
230 for (i = 0; i < 4; i++) {
231 if (!(edid_is_serial_block(b) ||
232 edid_is_ascii_block(b) ||
233 edid_is_monitor_block(b) ||
234 edid_is_timing_block(b))) {
235 b[0] = 0x00;
236 b[1] = 0x00;
237 b[2] = 0x00;
238 b[3] = 0xfd;
239 b[4] = 0x00;
240 b[5] = 60; /* vfmin */
241 b[6] = 60; /* vfmax */
242 b[7] = 30; /* hfmin */
243 b[8] = 75; /* hfmax */
244 b[9] = 17; /* pixclock - 170 MHz*/
245 b[10] = 0; /* GTF */
246 break;
247 }
248
249 b += DETAILED_TIMING_DESCRIPTION_SIZE;
250 }
251
252 for (i = 0; i < EDID_LENGTH - 1; i++)
253 csum += edid[i];
254
255 edid[127] = 256 - csum;
256 break;
257 }
258}
259
260static int edid_checksum(unsigned char *edid)
261{
262 unsigned char csum = 0, all_null = 0;
263 int i, err = 0, fix = check_edid(edid);
264
265 if (fix)
266 fix_edid(edid, fix);
267
268 for (i = 0; i < EDID_LENGTH; i++) {
269 csum += edid[i];
270 all_null |= edid[i];
271 }
272
273 if (csum == 0x00 && all_null) {
274 /* checksum passed, everything's good */
275 err = 1;
276 }
277
278 return err;
279}
280
281static int edid_check_header(unsigned char *edid)
282{
283 int i, err = 1, fix = check_edid(edid);
284
285 if (fix)
286 fix_edid(edid, fix);
287
288 for (i = 0; i < 8; i++) {
289 if (edid[i] != edid_v1_header[i])
290 err = 0;
291 }
292
293 return err;
294}
295
296static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs)
297{
298 specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
299 specs->manufacturer[1] = ((block[0] & 0x03) << 3) +
300 ((block[1] & 0xe0) >> 5) + '@';
301 specs->manufacturer[2] = (block[1] & 0x1f) + '@';
302 specs->manufacturer[3] = 0;
303 specs->model = block[2] + (block[3] << 8);
304 specs->serial = block[4] + (block[5] << 8) +
305 (block[6] << 16) + (block[7] << 24);
306 specs->year = block[9] + 1990;
307 specs->week = block[8];
308 DPRINTK(" Manufacturer: %s\n", specs->manufacturer);
309 DPRINTK(" Model: %x\n", specs->model);
310 DPRINTK(" Serial#: %u\n", specs->serial);
311 DPRINTK(" Year: %u Week %u\n", specs->year, specs->week);
312}
313
314static void get_dpms_capabilities(unsigned char flags,
315 struct fb_monspecs *specs)
316{
317 specs->dpms = 0;
318 if (flags & DPMS_ACTIVE_OFF)
319 specs->dpms |= FB_DPMS_ACTIVE_OFF;
320 if (flags & DPMS_SUSPEND)
321 specs->dpms |= FB_DPMS_SUSPEND;
322 if (flags & DPMS_STANDBY)
323 specs->dpms |= FB_DPMS_STANDBY;
324 DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n",
325 (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
326 (flags & DPMS_SUSPEND) ? "yes" : "no",
327 (flags & DPMS_STANDBY) ? "yes" : "no");
328}
329
330static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
331{
332 int tmp;
333
334 DPRINTK(" Chroma\n");
335 /* Chromaticity data */
336 tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2);
337 tmp *= 1000;
338 tmp += 512;
339 specs->chroma.redx = tmp/1024;
340 DPRINTK(" RedX: 0.%03d ", specs->chroma.redx);
341
342 tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2);
343 tmp *= 1000;
344 tmp += 512;
345 specs->chroma.redy = tmp/1024;
346 DPRINTK("RedY: 0.%03d\n", specs->chroma.redy);
347
348 tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2);
349 tmp *= 1000;
350 tmp += 512;
351 specs->chroma.greenx = tmp/1024;
352 DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx);
353
354 tmp = (block[5] & 3) | (block[0xa] << 2);
355 tmp *= 1000;
356 tmp += 512;
357 specs->chroma.greeny = tmp/1024;
358 DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny);
359
360 tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2);
361 tmp *= 1000;
362 tmp += 512;
363 specs->chroma.bluex = tmp/1024;
364 DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex);
365
366 tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2);
367 tmp *= 1000;
368 tmp += 512;
369 specs->chroma.bluey = tmp/1024;
370 DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey);
371
372 tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2);
373 tmp *= 1000;
374 tmp += 512;
375 specs->chroma.whitex = tmp/1024;
376 DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex);
377
378 tmp = (block[6] & 3) | (block[0xe] << 2);
379 tmp *= 1000;
380 tmp += 512;
381 specs->chroma.whitey = tmp/1024;
382 DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey);
383}
384
385static void calc_mode_timings(int xres, int yres, int refresh,
386 struct fb_videomode *mode)
387{
388 struct fb_var_screeninfo *var;
389
390 var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
391
392 if (var) {
393 var->xres = xres;
394 var->yres = yres;
395 fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
396 refresh, var, NULL);
397 mode->xres = xres;
398 mode->yres = yres;
399 mode->pixclock = var->pixclock;
400 mode->refresh = refresh;
401 mode->left_margin = var->left_margin;
402 mode->right_margin = var->right_margin;
403 mode->upper_margin = var->upper_margin;
404 mode->lower_margin = var->lower_margin;
405 mode->hsync_len = var->hsync_len;
406 mode->vsync_len = var->vsync_len;
407 mode->vmode = 0;
408 mode->sync = 0;
409 kfree(var);
410 }
411}
412
413static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
414{
415 int num = 0;
416 unsigned char c;
417
418 c = block[0];
419 if (c&0x80) {
420 calc_mode_timings(720, 400, 70, &mode[num]);
421 mode[num++].flag = FB_MODE_IS_CALCULATED;
422 DPRINTK(" 720x400@70Hz\n");
423 }
424 if (c&0x40) {
425 calc_mode_timings(720, 400, 88, &mode[num]);
426 mode[num++].flag = FB_MODE_IS_CALCULATED;
427 DPRINTK(" 720x400@88Hz\n");
428 }
429 if (c&0x20) {
430 mode[num++] = vesa_modes[3];
431 DPRINTK(" 640x480@60Hz\n");
432 }
433 if (c&0x10) {
434 calc_mode_timings(640, 480, 67, &mode[num]);
435 mode[num++].flag = FB_MODE_IS_CALCULATED;
436 DPRINTK(" 640x480@67Hz\n");
437 }
438 if (c&0x08) {
439 mode[num++] = vesa_modes[4];
440 DPRINTK(" 640x480@72Hz\n");
441 }
442 if (c&0x04) {
443 mode[num++] = vesa_modes[5];
444 DPRINTK(" 640x480@75Hz\n");
445 }
446 if (c&0x02) {
447 mode[num++] = vesa_modes[7];
448 DPRINTK(" 800x600@56Hz\n");
449 }
450 if (c&0x01) {
451 mode[num++] = vesa_modes[8];
452 DPRINTK(" 800x600@60Hz\n");
453 }
454
455 c = block[1];
456 if (c&0x80) {
457 mode[num++] = vesa_modes[9];
458 DPRINTK(" 800x600@72Hz\n");
459 }
460 if (c&0x40) {
461 mode[num++] = vesa_modes[10];
462 DPRINTK(" 800x600@75Hz\n");
463 }
464 if (c&0x20) {
465 calc_mode_timings(832, 624, 75, &mode[num]);
466 mode[num++].flag = FB_MODE_IS_CALCULATED;
467 DPRINTK(" 832x624@75Hz\n");
468 }
469 if (c&0x10) {
470 mode[num++] = vesa_modes[12];
471 DPRINTK(" 1024x768@87Hz Interlaced\n");
472 }
473 if (c&0x08) {
474 mode[num++] = vesa_modes[13];
475 DPRINTK(" 1024x768@60Hz\n");
476 }
477 if (c&0x04) {
478 mode[num++] = vesa_modes[14];
479 DPRINTK(" 1024x768@70Hz\n");
480 }
481 if (c&0x02) {
482 mode[num++] = vesa_modes[15];
483 DPRINTK(" 1024x768@75Hz\n");
484 }
485 if (c&0x01) {
486 mode[num++] = vesa_modes[21];
487 DPRINTK(" 1280x1024@75Hz\n");
488 }
489 c = block[2];
490 if (c&0x80) {
491 mode[num++] = vesa_modes[17];
492 DPRINTK(" 1152x870@75Hz\n");
493 }
494 DPRINTK(" Manufacturer's mask: %x\n",c&0x7F);
495 return num;
496}
497
498static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
499 int ver, int rev)
500{
501 int xres, yres = 0, refresh, ratio, i;
502
503 xres = (block[0] + 31) * 8;
504 if (xres <= 256)
505 return 0;
506
507 ratio = (block[1] & 0xc0) >> 6;
508 switch (ratio) {
509 case 0:
510 /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
511 if (ver < 1 || (ver == 1 && rev < 3))
512 yres = xres;
513 else
514 yres = (xres * 10)/16;
515 break;
516 case 1:
517 yres = (xres * 3)/4;
518 break;
519 case 2:
520 yres = (xres * 4)/5;
521 break;
522 case 3:
523 yres = (xres * 9)/16;
524 break;
525 }
526 refresh = (block[1] & 0x3f) + 60;
527
528 DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh);
529 for (i = 0; i < VESA_MODEDB_SIZE; i++) {
530 if (vesa_modes[i].xres == xres &&
531 vesa_modes[i].yres == yres &&
532 vesa_modes[i].refresh == refresh) {
533 *mode = vesa_modes[i];
534 mode->flag |= FB_MODE_IS_STANDARD;
535 return 1;
536 }
537 }
538 calc_mode_timings(xres, yres, refresh, mode);
539 return 1;
540}
541
542static int get_dst_timing(unsigned char *block,
543 struct fb_videomode *mode, int ver, int rev)
544{
545 int j, num = 0;
546
547 for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
548 num += get_std_timing(block, &mode[num], ver, rev);
549
550 return num;
551}
552
553static void get_detailed_timing(unsigned char *block,
554 struct fb_videomode *mode)
555{
556 mode->xres = H_ACTIVE;
557 mode->yres = V_ACTIVE;
558 mode->pixclock = PIXEL_CLOCK;
559 mode->pixclock /= 1000;
560 mode->pixclock = KHZ2PICOS(mode->pixclock);
561 mode->right_margin = H_SYNC_OFFSET;
562 mode->left_margin = (H_ACTIVE + H_BLANKING) -
563 (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
564 mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
565 V_SYNC_WIDTH;
566 mode->lower_margin = V_SYNC_OFFSET;
567 mode->hsync_len = H_SYNC_WIDTH;
568 mode->vsync_len = V_SYNC_WIDTH;
569 if (HSYNC_POSITIVE)
570 mode->sync |= FB_SYNC_HOR_HIGH_ACT;
571 if (VSYNC_POSITIVE)
572 mode->sync |= FB_SYNC_VERT_HIGH_ACT;
573 mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
574 (V_ACTIVE + V_BLANKING));
575 if (INTERLACED) {
576 mode->yres *= 2;
577 mode->upper_margin *= 2;
578 mode->lower_margin *= 2;
579 mode->vsync_len *= 2;
580 mode->vmode |= FB_VMODE_INTERLACED;
581 }
582 mode->flag = FB_MODE_IS_DETAILED;
583
584 DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
585 DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
586 H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
587 DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
588 V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
589 DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
590 (VSYNC_POSITIVE) ? "+" : "-");
591}
592
593/**
594 * fb_create_modedb - create video mode database
595 * @edid: EDID data
596 * @dbsize: database size
597 *
598 * RETURNS: struct fb_videomode, @dbsize contains length of database
599 *
600 * DESCRIPTION:
601 * This function builds a mode database using the contents of the EDID
602 * data
603 */
604static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
605{
606 struct fb_videomode *mode, *m;
607 unsigned char *block;
608 int num = 0, i, first = 1;
609 int ver, rev;
610
611 ver = edid[EDID_STRUCT_VERSION];
612 rev = edid[EDID_STRUCT_REVISION];
613
614 mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
615 if (mode == NULL)
616 return NULL;
617
618 if (edid == NULL || !edid_checksum(edid) ||
619 !edid_check_header(edid)) {
620 kfree(mode);
621 return NULL;
622 }
623
624 *dbsize = 0;
625
626 DPRINTK(" Detailed Timings\n");
627 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
628 for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
629 if (!(block[0] == 0x00 && block[1] == 0x00)) {
630 get_detailed_timing(block, &mode[num]);
631 if (first) {
632 mode[num].flag |= FB_MODE_IS_FIRST;
633 first = 0;
634 }
635 num++;
636 }
637 }
638
639 DPRINTK(" Supported VESA Modes\n");
640 block = edid + ESTABLISHED_TIMING_1;
641 num += get_est_timing(block, &mode[num]);
642
643 DPRINTK(" Standard Timings\n");
644 block = edid + STD_TIMING_DESCRIPTIONS_START;
645 for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
646 num += get_std_timing(block, &mode[num], ver, rev);
647
648 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
649 for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
650 if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
651 num += get_dst_timing(block + 5, &mode[num], ver, rev);
652 }
653
654 /* Yikes, EDID data is totally useless */
655 if (!num) {
656 kfree(mode);
657 return NULL;
658 }
659
660 *dbsize = num;
661 m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL);
662 if (!m)
663 return mode;
664 memmove(m, mode, num * sizeof(struct fb_videomode));
665 kfree(mode);
666 return m;
667}
668
669/**
670 * fb_destroy_modedb - destroys mode database
671 * @modedb: mode database to destroy
672 *
673 * DESCRIPTION:
674 * Destroy mode database created by fb_create_modedb
675 */
676void fb_destroy_modedb(struct fb_videomode *modedb)
677{
678 kfree(modedb);
679}
680
681static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
682{
683 int i, retval = 1;
684 unsigned char *block;
685
686 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
687
688 DPRINTK(" Monitor Operating Limits: ");
689
690 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
691 if (edid_is_limits_block(block)) {
692 specs->hfmin = H_MIN_RATE * 1000;
693 specs->hfmax = H_MAX_RATE * 1000;
694 specs->vfmin = V_MIN_RATE;
695 specs->vfmax = V_MAX_RATE;
696 specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
697 specs->gtf = (GTF_SUPPORT) ? 1 : 0;
698 retval = 0;
699 DPRINTK("From EDID\n");
700 break;
701 }
702 }
703
704 /* estimate monitor limits based on modes supported */
705 if (retval) {
706 struct fb_videomode *modes, *mode;
707 int num_modes, hz, hscan, pixclock;
708 int vtotal, htotal;
709
710 modes = fb_create_modedb(edid, &num_modes);
711 if (!modes) {
712 DPRINTK("None Available\n");
713 return 1;
714 }
715
716 retval = 0;
717 for (i = 0; i < num_modes; i++) {
718 mode = &modes[i];
719 pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
720 htotal = mode->xres + mode->right_margin + mode->hsync_len
721 + mode->left_margin;
722 vtotal = mode->yres + mode->lower_margin + mode->vsync_len
723 + mode->upper_margin;
724
725 if (mode->vmode & FB_VMODE_INTERLACED)
726 vtotal /= 2;
727
728 if (mode->vmode & FB_VMODE_DOUBLE)
729 vtotal *= 2;
730
731 hscan = (pixclock + htotal / 2) / htotal;
732 hscan = (hscan + 500) / 1000 * 1000;
733 hz = (hscan + vtotal / 2) / vtotal;
734
735 if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
736 specs->dclkmax = pixclock;
737
738 if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
739 specs->dclkmin = pixclock;
740
741 if (specs->hfmax == 0 || specs->hfmax < hscan)
742 specs->hfmax = hscan;
743
744 if (specs->hfmin == 0 || specs->hfmin > hscan)
745 specs->hfmin = hscan;
746
747 if (specs->vfmax == 0 || specs->vfmax < hz)
748 specs->vfmax = hz;
749
750 if (specs->vfmin == 0 || specs->vfmin > hz)
751 specs->vfmin = hz;
752 }
753 DPRINTK("Extrapolated\n");
754 fb_destroy_modedb(modes);
755 }
756 DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
757 specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
758 specs->vfmax, specs->dclkmax/1000000);
759 return retval;
760}
761
762static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
763{
764 unsigned char c, *block;
765
766 block = edid + EDID_STRUCT_DISPLAY;
767
768 fb_get_monitor_limits(edid, specs);
769
770 c = block[0] & 0x80;
771 specs->input = 0;
772 if (c) {
773 specs->input |= FB_DISP_DDI;
774 DPRINTK(" Digital Display Input");
775 } else {
776 DPRINTK(" Analog Display Input: Input Voltage - ");
777 switch ((block[0] & 0x60) >> 5) {
778 case 0:
779 DPRINTK("0.700V/0.300V");
780 specs->input |= FB_DISP_ANA_700_300;
781 break;
782 case 1:
783 DPRINTK("0.714V/0.286V");
784 specs->input |= FB_DISP_ANA_714_286;
785 break;
786 case 2:
787 DPRINTK("1.000V/0.400V");
788 specs->input |= FB_DISP_ANA_1000_400;
789 break;
790 case 3:
791 DPRINTK("0.700V/0.000V");
792 specs->input |= FB_DISP_ANA_700_000;
793 break;
794 }
795 }
796 DPRINTK("\n Sync: ");
797 c = block[0] & 0x10;
798 if (c)
799 DPRINTK(" Configurable signal level\n");
800 c = block[0] & 0x0f;
801 specs->signal = 0;
802 if (c & 0x10) {
803 DPRINTK("Blank to Blank ");
804 specs->signal |= FB_SIGNAL_BLANK_BLANK;
805 }
806 if (c & 0x08) {
807 DPRINTK("Separate ");
808 specs->signal |= FB_SIGNAL_SEPARATE;
809 }
810 if (c & 0x04) {
811 DPRINTK("Composite ");
812 specs->signal |= FB_SIGNAL_COMPOSITE;
813 }
814 if (c & 0x02) {
815 DPRINTK("Sync on Green ");
816 specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
817 }
818 if (c & 0x01) {
819 DPRINTK("Serration on ");
820 specs->signal |= FB_SIGNAL_SERRATION_ON;
821 }
822 DPRINTK("\n");
823 specs->max_x = block[1];
824 specs->max_y = block[2];
825 DPRINTK(" Max H-size in cm: ");
826 if (specs->max_x)
827 DPRINTK("%d\n", specs->max_x);
828 else
829 DPRINTK("variable\n");
830 DPRINTK(" Max V-size in cm: ");
831 if (specs->max_y)
832 DPRINTK("%d\n", specs->max_y);
833 else
834 DPRINTK("variable\n");
835
836 c = block[3];
837 specs->gamma = c+100;
838 DPRINTK(" Gamma: ");
839 DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
840
841 get_dpms_capabilities(block[4], specs);
842
843 switch ((block[4] & 0x18) >> 3) {
844 case 0:
845 DPRINTK(" Monochrome/Grayscale\n");
846 specs->input |= FB_DISP_MONO;
847 break;
848 case 1:
849 DPRINTK(" RGB Color Display\n");
850 specs->input |= FB_DISP_RGB;
851 break;
852 case 2:
853 DPRINTK(" Non-RGB Multicolor Display\n");
854 specs->input |= FB_DISP_MULTI;
855 break;
856 default:
857 DPRINTK(" Unknown\n");
858 specs->input |= FB_DISP_UNKNOWN;
859 break;
860 }
861
862 get_chroma(block, specs);
863
864 specs->misc = 0;
865 c = block[4] & 0x7;
866 if (c & 0x04) {
867 DPRINTK(" Default color format is primary\n");
868 specs->misc |= FB_MISC_PRIM_COLOR;
869 }
870 if (c & 0x02) {
871 DPRINTK(" First DETAILED Timing is preferred\n");
872 specs->misc |= FB_MISC_1ST_DETAIL;
873 }
874 if (c & 0x01) {
875 printk(" Display is GTF capable\n");
876 specs->gtf = 1;
877 }
878}
879
880int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
881{
882 int i;
883 unsigned char *block;
884
885 if (edid == NULL || var == NULL)
886 return 1;
887
888 if (!(edid_checksum(edid)))
889 return 1;
890
891 if (!(edid_check_header(edid)))
892 return 1;
893
894 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
895
896 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
897 if (edid_is_timing_block(block)) {
898 var->xres = var->xres_virtual = H_ACTIVE;
899 var->yres = var->yres_virtual = V_ACTIVE;
900 var->height = var->width = 0;
901 var->right_margin = H_SYNC_OFFSET;
902 var->left_margin = (H_ACTIVE + H_BLANKING) -
903 (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
904 var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
905 V_SYNC_WIDTH;
906 var->lower_margin = V_SYNC_OFFSET;
907 var->hsync_len = H_SYNC_WIDTH;
908 var->vsync_len = V_SYNC_WIDTH;
909 var->pixclock = PIXEL_CLOCK;
910 var->pixclock /= 1000;
911 var->pixclock = KHZ2PICOS(var->pixclock);
912
913 if (HSYNC_POSITIVE)
914 var->sync |= FB_SYNC_HOR_HIGH_ACT;
915 if (VSYNC_POSITIVE)
916 var->sync |= FB_SYNC_VERT_HIGH_ACT;
917 return 0;
918 }
919 }
920 return 1;
921}
922
923void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
924{
925 unsigned char *block;
926 int i, found = 0;
927
928 if (edid == NULL)
929 return;
930
931 if (!(edid_checksum(edid)))
932 return;
933
934 if (!(edid_check_header(edid)))
935 return;
936
937 memset(specs, 0, sizeof(struct fb_monspecs));
938
939 specs->version = edid[EDID_STRUCT_VERSION];
940 specs->revision = edid[EDID_STRUCT_REVISION];
941
942 DPRINTK("========================================\n");
943 DPRINTK("Display Information (EDID)\n");
944 DPRINTK("========================================\n");
945 DPRINTK(" EDID Version %d.%d\n", (int) specs->version,
946 (int) specs->revision);
947
948 parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
949
950 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
951 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
952 if (edid_is_serial_block(block)) {
953 copy_string(block, specs->serial_no);
954 DPRINTK(" Serial Number: %s\n", specs->serial_no);
955 } else if (edid_is_ascii_block(block)) {
956 copy_string(block, specs->ascii);
957 DPRINTK(" ASCII Block: %s\n", specs->ascii);
958 } else if (edid_is_monitor_block(block)) {
959 copy_string(block, specs->monitor);
960 DPRINTK(" Monitor Name: %s\n", specs->monitor);
961 }
962 }
963
964 DPRINTK(" Display Characteristics:\n");
965 get_monspecs(edid, specs);
966
967 specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
968
969 /*
970 * Workaround for buggy EDIDs that sets that the first
971 * detailed timing is preferred but has not detailed
972 * timing specified
973 */
974 for (i = 0; i < specs->modedb_len; i++) {
975 if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) {
976 found = 1;
977 break;
978 }
979 }
980
981 if (!found)
982 specs->misc &= ~FB_MISC_1ST_DETAIL;
983
984 DPRINTK("========================================\n");
985}
986
987/**
988 * fb_edid_add_monspecs() - add monitor video modes from E-EDID data
989 * @edid: 128 byte array with an E-EDID block
990 * @spacs: monitor specs to be extended
991 */
992void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
993{
994 unsigned char *block;
995 struct fb_videomode *m;
996 int num = 0, i;
997 u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
998 u8 pos = 4, svd_n = 0;
999
1000 if (!edid)
1001 return;
1002
1003 if (!edid_checksum(edid))
1004 return;
1005
1006 if (edid[0] != 0x2 ||
1007 edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
1008 return;
1009
1010 DPRINTK(" Short Video Descriptors\n");
1011
1012 while (pos < edid[2]) {
1013 u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
1014 pr_debug("Data block %u of %u bytes\n", type, len);
1015 if (type == 2)
1016 for (i = pos; i < pos + len; i++) {
1017 u8 idx = edid[pos + i] & 0x7f;
1018 svd[svd_n++] = idx;
1019 pr_debug("N%sative mode #%d\n",
1020 edid[pos + i] & 0x80 ? "" : "on-n", idx);
1021 }
1022 pos += len + 1;
1023 }
1024
1025 block = edid + edid[2];
1026
1027 DPRINTK(" Extended Detailed Timings\n");
1028
1029 for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE;
1030 i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
1031 if (PIXEL_CLOCK)
1032 edt[num++] = block - edid;
1033
1034 /* Yikes, EDID data is totally useless */
1035 if (!(num + svd_n))
1036 return;
1037
1038 m = kzalloc((specs->modedb_len + num + svd_n) *
1039 sizeof(struct fb_videomode), GFP_KERNEL);
1040
1041 if (!m)
1042 return;
1043
1044 memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode));
1045
1046 for (i = specs->modedb_len; i < specs->modedb_len + num; i++) {
1047 get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]);
1048 if (i == specs->modedb_len)
1049 m[i].flag |= FB_MODE_IS_FIRST;
1050 pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh);
1051 }
1052
1053 for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) {
1054 int idx = svd[i - specs->modedb_len - num];
1055 if (!idx || idx > 63) {
1056 pr_warning("Reserved SVD code %d\n", idx);
1057 } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) {
1058 pr_warning("Unimplemented SVD code %d\n", idx);
1059 } else {
1060 memcpy(&m[i], cea_modes + idx, sizeof(m[i]));
1061 pr_debug("Adding SVD #%d: %ux%u@%u\n", idx,
1062 m[i].xres, m[i].yres, m[i].refresh);
1063 }
1064 }
1065
1066 kfree(specs->modedb);
1067 specs->modedb = m;
1068 specs->modedb_len = specs->modedb_len + num + svd_n;
1069}
1070
1071/*
1072 * VESA Generalized Timing Formula (GTF)
1073 */
1074
1075#define FLYBACK 550
1076#define V_FRONTPORCH 1
1077#define H_OFFSET 40
1078#define H_SCALEFACTOR 20
1079#define H_BLANKSCALE 128
1080#define H_GRADIENT 600
1081#define C_VAL 30
1082#define M_VAL 300
1083
1084struct __fb_timings {
1085 u32 dclk;
1086 u32 hfreq;
1087 u32 vfreq;
1088 u32 hactive;
1089 u32 vactive;
1090 u32 hblank;
1091 u32 vblank;
1092 u32 htotal;
1093 u32 vtotal;
1094};
1095
1096/**
1097 * fb_get_vblank - get vertical blank time
1098 * @hfreq: horizontal freq
1099 *
1100 * DESCRIPTION:
1101 * vblank = right_margin + vsync_len + left_margin
1102 *
1103 * given: right_margin = 1 (V_FRONTPORCH)
1104 * vsync_len = 3
1105 * flyback = 550
1106 *
1107 * flyback * hfreq
1108 * left_margin = --------------- - vsync_len
1109 * 1000000
1110 */
1111static u32 fb_get_vblank(u32 hfreq)
1112{
1113 u32 vblank;
1114
1115 vblank = (hfreq * FLYBACK)/1000;
1116 vblank = (vblank + 500)/1000;
1117 return (vblank + V_FRONTPORCH);
1118}
1119
1120/**
1121 * fb_get_hblank_by_freq - get horizontal blank time given hfreq
1122 * @hfreq: horizontal freq
1123 * @xres: horizontal resolution in pixels
1124 *
1125 * DESCRIPTION:
1126 *
1127 * xres * duty_cycle
1128 * hblank = ------------------
1129 * 100 - duty_cycle
1130 *
1131 * duty cycle = percent of htotal assigned to inactive display
1132 * duty cycle = C - (M/Hfreq)
1133 *
1134 * where: C = ((offset - scale factor) * blank_scale)
1135 * -------------------------------------- + scale factor
1136 * 256
1137 * M = blank_scale * gradient
1138 *
1139 */
1140static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
1141{
1142 u32 c_val, m_val, duty_cycle, hblank;
1143
1144 c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
1145 H_SCALEFACTOR) * 1000;
1146 m_val = (H_BLANKSCALE * H_GRADIENT)/256;
1147 m_val = (m_val * 1000000)/hfreq;
1148 duty_cycle = c_val - m_val;
1149 hblank = (xres * duty_cycle)/(100000 - duty_cycle);
1150 return (hblank);
1151}
1152
1153/**
1154 * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock
1155 * @dclk: pixelclock in Hz
1156 * @xres: horizontal resolution in pixels
1157 *
1158 * DESCRIPTION:
1159 *
1160 * xres * duty_cycle
1161 * hblank = ------------------
1162 * 100 - duty_cycle
1163 *
1164 * duty cycle = percent of htotal assigned to inactive display
1165 * duty cycle = C - (M * h_period)
1166 *
1167 * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100
1168 * -----------------------------------------------
1169 * 2 * M
1170 * M = 300;
1171 * C = 30;
1172
1173 */
1174static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres)
1175{
1176 u32 duty_cycle, h_period, hblank;
1177
1178 dclk /= 1000;
1179 h_period = 100 - C_VAL;
1180 h_period *= h_period;
1181 h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk);
1182 h_period *= 10000;
1183
1184 h_period = int_sqrt(h_period);
1185 h_period -= (100 - C_VAL) * 100;
1186 h_period *= 1000;
1187 h_period /= 2 * M_VAL;
1188
1189 duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100;
1190 hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8;
1191 hblank &= ~15;
1192 return (hblank);
1193}
1194
1195/**
1196 * fb_get_hfreq - estimate hsync
1197 * @vfreq: vertical refresh rate
1198 * @yres: vertical resolution
1199 *
1200 * DESCRIPTION:
1201 *
1202 * (yres + front_port) * vfreq * 1000000
1203 * hfreq = -------------------------------------
1204 * (1000000 - (vfreq * FLYBACK)
1205 *
1206 */
1207
1208static u32 fb_get_hfreq(u32 vfreq, u32 yres)
1209{
1210 u32 divisor, hfreq;
1211
1212 divisor = (1000000 - (vfreq * FLYBACK))/1000;
1213 hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
1214 return (hfreq/divisor);
1215}
1216
1217static void fb_timings_vfreq(struct __fb_timings *timings)
1218{
1219 timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
1220 timings->vblank = fb_get_vblank(timings->hfreq);
1221 timings->vtotal = timings->vactive + timings->vblank;
1222 timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
1223 timings->hactive);
1224 timings->htotal = timings->hactive + timings->hblank;
1225 timings->dclk = timings->htotal * timings->hfreq;
1226}
1227
1228static void fb_timings_hfreq(struct __fb_timings *timings)
1229{
1230 timings->vblank = fb_get_vblank(timings->hfreq);
1231 timings->vtotal = timings->vactive + timings->vblank;
1232 timings->vfreq = timings->hfreq/timings->vtotal;
1233 timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
1234 timings->hactive);
1235 timings->htotal = timings->hactive + timings->hblank;
1236 timings->dclk = timings->htotal * timings->hfreq;
1237}
1238
1239static void fb_timings_dclk(struct __fb_timings *timings)
1240{
1241 timings->hblank = fb_get_hblank_by_dclk(timings->dclk,
1242 timings->hactive);
1243 timings->htotal = timings->hactive + timings->hblank;
1244 timings->hfreq = timings->dclk/timings->htotal;
1245 timings->vblank = fb_get_vblank(timings->hfreq);
1246 timings->vtotal = timings->vactive + timings->vblank;
1247 timings->vfreq = timings->hfreq/timings->vtotal;
1248}
1249
1250/*
1251 * fb_get_mode - calculates video mode using VESA GTF
1252 * @flags: if: 0 - maximize vertical refresh rate
1253 * 1 - vrefresh-driven calculation;
1254 * 2 - hscan-driven calculation;
1255 * 3 - pixelclock-driven calculation;
1256 * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
1257 * @var: pointer to fb_var_screeninfo
1258 * @info: pointer to fb_info
1259 *
1260 * DESCRIPTION:
1261 * Calculates video mode based on monitor specs using VESA GTF.
1262 * The GTF is best for VESA GTF compliant monitors but is
1263 * specifically formulated to work for older monitors as well.
1264 *
1265 * If @flag==0, the function will attempt to maximize the
1266 * refresh rate. Otherwise, it will calculate timings based on
1267 * the flag and accompanying value.
1268 *
1269 * If FB_IGNOREMON bit is set in @flags, monitor specs will be
1270 * ignored and @var will be filled with the calculated timings.
1271 *
1272 * All calculations are based on the VESA GTF Spreadsheet
1273 * available at VESA's public ftp (http://www.vesa.org).
1274 *
1275 * NOTES:
1276 * The timings generated by the GTF will be different from VESA
1277 * DMT. It might be a good idea to keep a table of standard
1278 * VESA modes as well. The GTF may also not work for some displays,
1279 * such as, and especially, analog TV.
1280 *
1281 * REQUIRES:
1282 * A valid info->monspecs, otherwise 'safe numbers' will be used.
1283 */
1284int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
1285{
1286 struct __fb_timings *timings;
1287 u32 interlace = 1, dscan = 1;
1288 u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
1289
1290
1291 timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL);
1292
1293 if (!timings)
1294 return -ENOMEM;
1295
1296 /*
1297 * If monspecs are invalid, use values that are enough
1298 * for 640x480@60
1299 */
1300 if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax ||
1301 !info->monspecs.dclkmax ||
1302 info->monspecs.hfmax < info->monspecs.hfmin ||
1303 info->monspecs.vfmax < info->monspecs.vfmin ||
1304 info->monspecs.dclkmax < info->monspecs.dclkmin) {
1305 hfmin = 29000; hfmax = 30000;
1306 vfmin = 60; vfmax = 60;
1307 dclkmin = 0; dclkmax = 25000000;
1308 } else {
1309 hfmin = info->monspecs.hfmin;
1310 hfmax = info->monspecs.hfmax;
1311 vfmin = info->monspecs.vfmin;
1312 vfmax = info->monspecs.vfmax;
1313 dclkmin = info->monspecs.dclkmin;
1314 dclkmax = info->monspecs.dclkmax;
1315 }
1316
1317 timings->hactive = var->xres;
1318 timings->vactive = var->yres;
1319 if (var->vmode & FB_VMODE_INTERLACED) {
1320 timings->vactive /= 2;
1321 interlace = 2;
1322 }
1323 if (var->vmode & FB_VMODE_DOUBLE) {
1324 timings->vactive *= 2;
1325 dscan = 2;
1326 }
1327
1328 switch (flags & ~FB_IGNOREMON) {
1329 case FB_MAXTIMINGS: /* maximize refresh rate */
1330 timings->hfreq = hfmax;
1331 fb_timings_hfreq(timings);
1332 if (timings->vfreq > vfmax) {
1333 timings->vfreq = vfmax;
1334 fb_timings_vfreq(timings);
1335 }
1336 if (timings->dclk > dclkmax) {
1337 timings->dclk = dclkmax;
1338 fb_timings_dclk(timings);
1339 }
1340 break;
1341 case FB_VSYNCTIMINGS: /* vrefresh driven */
1342 timings->vfreq = val;
1343 fb_timings_vfreq(timings);
1344 break;
1345 case FB_HSYNCTIMINGS: /* hsync driven */
1346 timings->hfreq = val;
1347 fb_timings_hfreq(timings);
1348 break;
1349 case FB_DCLKTIMINGS: /* pixelclock driven */
1350 timings->dclk = PICOS2KHZ(val) * 1000;
1351 fb_timings_dclk(timings);
1352 break;
1353 default:
1354 err = -EINVAL;
1355
1356 }
1357
1358 if (err || (!(flags & FB_IGNOREMON) &&
1359 (timings->vfreq < vfmin || timings->vfreq > vfmax ||
1360 timings->hfreq < hfmin || timings->hfreq > hfmax ||
1361 timings->dclk < dclkmin || timings->dclk > dclkmax))) {
1362 err = -EINVAL;
1363 } else {
1364 var->pixclock = KHZ2PICOS(timings->dclk/1000);
1365 var->hsync_len = (timings->htotal * 8)/100;
1366 var->right_margin = (timings->hblank/2) - var->hsync_len;
1367 var->left_margin = timings->hblank - var->right_margin -
1368 var->hsync_len;
1369 var->vsync_len = (3 * interlace)/dscan;
1370 var->lower_margin = (1 * interlace)/dscan;
1371 var->upper_margin = (timings->vblank * interlace)/dscan -
1372 (var->vsync_len + var->lower_margin);
1373 }
1374
1375 kfree(timings);
1376 return err;
1377}
1378
1379#ifdef CONFIG_VIDEOMODE_HELPERS
1380int fb_videomode_from_videomode(const struct videomode *vm,
1381 struct fb_videomode *fbmode)
1382{
1383 unsigned int htotal, vtotal;
1384
1385 fbmode->xres = vm->hactive;
1386 fbmode->left_margin = vm->hback_porch;
1387 fbmode->right_margin = vm->hfront_porch;
1388 fbmode->hsync_len = vm->hsync_len;
1389
1390 fbmode->yres = vm->vactive;
1391 fbmode->upper_margin = vm->vback_porch;
1392 fbmode->lower_margin = vm->vfront_porch;
1393 fbmode->vsync_len = vm->vsync_len;
1394
1395 /* prevent division by zero in KHZ2PICOS macro */
1396 fbmode->pixclock = vm->pixelclock ?
1397 KHZ2PICOS(vm->pixelclock / 1000) : 0;
1398
1399 fbmode->sync = 0;
1400 fbmode->vmode = 0;
1401 if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
1402 fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
1403 if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
1404 fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
1405 if (vm->flags & DISPLAY_FLAGS_INTERLACED)
1406 fbmode->vmode |= FB_VMODE_INTERLACED;
1407 if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN)
1408 fbmode->vmode |= FB_VMODE_DOUBLE;
1409 fbmode->flag = 0;
1410
1411 htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
1412 vm->hsync_len;
1413 vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
1414 vm->vsync_len;
1415 /* prevent division by zero */
1416 if (htotal && vtotal) {
1417 fbmode->refresh = vm->pixelclock / (htotal * vtotal);
1418 /* a mode must have htotal and vtotal != 0 or it is invalid */
1419 } else {
1420 fbmode->refresh = 0;
1421 return -EINVAL;
1422 }
1423
1424 return 0;
1425}
1426EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
1427
1428#ifdef CONFIG_OF
1429static inline void dump_fb_videomode(const struct fb_videomode *m)
1430{
1431 pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
1432 m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
1433 m->right_margin, m->upper_margin, m->lower_margin,
1434 m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
1435}
1436
1437/**
1438 * of_get_fb_videomode - get a fb_videomode from devicetree
1439 * @np: device_node with the timing specification
1440 * @fb: will be set to the return value
1441 * @index: index into the list of display timings in devicetree
1442 *
1443 * DESCRIPTION:
1444 * This function is expensive and should only be used, if only one mode is to be
1445 * read from DT. To get multiple modes start with of_get_display_timings ond
1446 * work with that instead.
1447 */
1448int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
1449 int index)
1450{
1451 struct videomode vm;
1452 int ret;
1453
1454 ret = of_get_videomode(np, &vm, index);
1455 if (ret)
1456 return ret;
1457
1458 fb_videomode_from_videomode(&vm, fb);
1459
1460 pr_debug("%s: got %dx%d display mode from %s\n",
1461 of_node_full_name(np), vm.hactive, vm.vactive, np->name);
1462 dump_fb_videomode(fb);
1463
1464 return 0;
1465}
1466EXPORT_SYMBOL_GPL(of_get_fb_videomode);
1467#endif /* CONFIG_OF */
1468#endif /* CONFIG_VIDEOMODE_HELPERS */
1469
1470#else
1471int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
1472{
1473 return 1;
1474}
1475void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
1476{
1477 specs = NULL;
1478}
1479void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
1480{
1481}
1482void fb_destroy_modedb(struct fb_videomode *modedb)
1483{
1484}
1485int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
1486 struct fb_info *info)
1487{
1488 return -EINVAL;
1489}
1490#endif /* CONFIG_FB_MODE_HELPERS */
1491
1492/*
1493 * fb_validate_mode - validates var against monitor capabilities
1494 * @var: pointer to fb_var_screeninfo
1495 * @info: pointer to fb_info
1496 *
1497 * DESCRIPTION:
1498 * Validates video mode against monitor capabilities specified in
1499 * info->monspecs.
1500 *
1501 * REQUIRES:
1502 * A valid info->monspecs.
1503 */
1504int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
1505{
1506 u32 hfreq, vfreq, htotal, vtotal, pixclock;
1507 u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
1508
1509 /*
1510 * If monspecs are invalid, use values that are enough
1511 * for 640x480@60
1512 */
1513 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
1514 !info->monspecs.dclkmax ||
1515 info->monspecs.hfmax < info->monspecs.hfmin ||
1516 info->monspecs.vfmax < info->monspecs.vfmin ||
1517 info->monspecs.dclkmax < info->monspecs.dclkmin) {
1518 hfmin = 29000; hfmax = 30000;
1519 vfmin = 60; vfmax = 60;
1520 dclkmin = 0; dclkmax = 25000000;
1521 } else {
1522 hfmin = info->monspecs.hfmin;
1523 hfmax = info->monspecs.hfmax;
1524 vfmin = info->monspecs.vfmin;
1525 vfmax = info->monspecs.vfmax;
1526 dclkmin = info->monspecs.dclkmin;
1527 dclkmax = info->monspecs.dclkmax;
1528 }
1529
1530 if (!var->pixclock)
1531 return -EINVAL;
1532 pixclock = PICOS2KHZ(var->pixclock) * 1000;
1533
1534 htotal = var->xres + var->right_margin + var->hsync_len +
1535 var->left_margin;
1536 vtotal = var->yres + var->lower_margin + var->vsync_len +
1537 var->upper_margin;
1538
1539 if (var->vmode & FB_VMODE_INTERLACED)
1540 vtotal /= 2;
1541 if (var->vmode & FB_VMODE_DOUBLE)
1542 vtotal *= 2;
1543
1544 hfreq = pixclock/htotal;
1545 hfreq = (hfreq + 500) / 1000 * 1000;
1546
1547 vfreq = hfreq/vtotal;
1548
1549 return (vfreq < vfmin || vfreq > vfmax ||
1550 hfreq < hfmin || hfreq > hfmax ||
1551 pixclock < dclkmin || pixclock > dclkmax) ?
1552 -EINVAL : 0;
1553}
1554
1555#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
1556
1557/*
1558 * We need to ensure that the EDID block is only returned for
1559 * the primary graphics adapter.
1560 */
1561
1562const unsigned char *fb_firmware_edid(struct device *device)
1563{
1564 struct pci_dev *dev = NULL;
1565 struct resource *res = NULL;
1566 unsigned char *edid = NULL;
1567
1568 if (device)
1569 dev = to_pci_dev(device);
1570
1571 if (dev)
1572 res = &dev->resource[PCI_ROM_RESOURCE];
1573
1574 if (res && res->flags & IORESOURCE_ROM_SHADOW)
1575 edid = edid_info.dummy;
1576
1577 return edid;
1578}
1579#else
1580const unsigned char *fb_firmware_edid(struct device *device)
1581{
1582 return NULL;
1583}
1584#endif
1585EXPORT_SYMBOL(fb_firmware_edid);
1586
1587EXPORT_SYMBOL(fb_parse_edid);
1588EXPORT_SYMBOL(fb_edid_to_monspecs);
1589EXPORT_SYMBOL(fb_edid_add_monspecs);
1590EXPORT_SYMBOL(fb_get_mode);
1591EXPORT_SYMBOL(fb_validate_mode);
1592EXPORT_SYMBOL(fb_destroy_modedb);
diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c
new file mode 100644
index 000000000000..53444ac19fe0
--- /dev/null
+++ b/drivers/video/fbdev/core/fbsysfs.c
@@ -0,0 +1,586 @@
1/*
2 * fbsysfs.c - framebuffer device class and attributes
3 *
4 * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
5 *
6 * This program is free software you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12/*
13 * Note: currently there's only stubs for framebuffer_alloc and
14 * framebuffer_release here. The reson for that is that until all drivers
15 * are converted to use it a sysfsification will open OOPSable races.
16 */
17
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/fb.h>
21#include <linux/console.h>
22#include <linux/module.h>
23
24#define FB_SYSFS_FLAG_ATTR 1
25
26/**
27 * framebuffer_alloc - creates a new frame buffer info structure
28 *
29 * @size: size of driver private data, can be zero
30 * @dev: pointer to the device for this fb, this can be NULL
31 *
32 * Creates a new frame buffer info structure. Also reserves @size bytes
33 * for driver private data (info->par). info->par (if any) will be
34 * aligned to sizeof(long).
35 *
36 * Returns the new structure, or NULL if an error occurred.
37 *
38 */
39struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
40{
41#define BYTES_PER_LONG (BITS_PER_LONG/8)
42#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
43 int fb_info_size = sizeof(struct fb_info);
44 struct fb_info *info;
45 char *p;
46
47 if (size)
48 fb_info_size += PADDING;
49
50 p = kzalloc(fb_info_size + size, GFP_KERNEL);
51
52 if (!p)
53 return NULL;
54
55 info = (struct fb_info *) p;
56
57 if (size)
58 info->par = p + fb_info_size;
59
60 info->device = dev;
61
62#ifdef CONFIG_FB_BACKLIGHT
63 mutex_init(&info->bl_curve_mutex);
64#endif
65
66 return info;
67#undef PADDING
68#undef BYTES_PER_LONG
69}
70EXPORT_SYMBOL(framebuffer_alloc);
71
72/**
73 * framebuffer_release - marks the structure available for freeing
74 *
75 * @info: frame buffer info structure
76 *
77 * Drop the reference count of the device embedded in the
78 * framebuffer info structure.
79 *
80 */
81void framebuffer_release(struct fb_info *info)
82{
83 if (!info)
84 return;
85 kfree(info->apertures);
86 kfree(info);
87}
88EXPORT_SYMBOL(framebuffer_release);
89
90static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
91{
92 int err;
93
94 var->activate |= FB_ACTIVATE_FORCE;
95 console_lock();
96 fb_info->flags |= FBINFO_MISC_USEREVENT;
97 err = fb_set_var(fb_info, var);
98 fb_info->flags &= ~FBINFO_MISC_USEREVENT;
99 console_unlock();
100 if (err)
101 return err;
102 return 0;
103}
104
105static int mode_string(char *buf, unsigned int offset,
106 const struct fb_videomode *mode)
107{
108 char m = 'U';
109 char v = 'p';
110
111 if (mode->flag & FB_MODE_IS_DETAILED)
112 m = 'D';
113 if (mode->flag & FB_MODE_IS_VESA)
114 m = 'V';
115 if (mode->flag & FB_MODE_IS_STANDARD)
116 m = 'S';
117
118 if (mode->vmode & FB_VMODE_INTERLACED)
119 v = 'i';
120 if (mode->vmode & FB_VMODE_DOUBLE)
121 v = 'd';
122
123 return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
124 m, mode->xres, mode->yres, v, mode->refresh);
125}
126
127static ssize_t store_mode(struct device *device, struct device_attribute *attr,
128 const char *buf, size_t count)
129{
130 struct fb_info *fb_info = dev_get_drvdata(device);
131 char mstr[100];
132 struct fb_var_screeninfo var;
133 struct fb_modelist *modelist;
134 struct fb_videomode *mode;
135 struct list_head *pos;
136 size_t i;
137 int err;
138
139 memset(&var, 0, sizeof(var));
140
141 list_for_each(pos, &fb_info->modelist) {
142 modelist = list_entry(pos, struct fb_modelist, list);
143 mode = &modelist->mode;
144 i = mode_string(mstr, 0, mode);
145 if (strncmp(mstr, buf, max(count, i)) == 0) {
146
147 var = fb_info->var;
148 fb_videomode_to_var(&var, mode);
149 if ((err = activate(fb_info, &var)))
150 return err;
151 fb_info->mode = mode;
152 return count;
153 }
154 }
155 return -EINVAL;
156}
157
158static ssize_t show_mode(struct device *device, struct device_attribute *attr,
159 char *buf)
160{
161 struct fb_info *fb_info = dev_get_drvdata(device);
162
163 if (!fb_info->mode)
164 return 0;
165
166 return mode_string(buf, 0, fb_info->mode);
167}
168
169static ssize_t store_modes(struct device *device,
170 struct device_attribute *attr,
171 const char *buf, size_t count)
172{
173 struct fb_info *fb_info = dev_get_drvdata(device);
174 LIST_HEAD(old_list);
175 int i = count / sizeof(struct fb_videomode);
176
177 if (i * sizeof(struct fb_videomode) != count)
178 return -EINVAL;
179
180 console_lock();
181 if (!lock_fb_info(fb_info)) {
182 console_unlock();
183 return -ENODEV;
184 }
185
186 list_splice(&fb_info->modelist, &old_list);
187 fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
188 &fb_info->modelist);
189 if (fb_new_modelist(fb_info)) {
190 fb_destroy_modelist(&fb_info->modelist);
191 list_splice(&old_list, &fb_info->modelist);
192 } else
193 fb_destroy_modelist(&old_list);
194
195 unlock_fb_info(fb_info);
196 console_unlock();
197
198 return 0;
199}
200
201static ssize_t show_modes(struct device *device, struct device_attribute *attr,
202 char *buf)
203{
204 struct fb_info *fb_info = dev_get_drvdata(device);
205 unsigned int i;
206 struct list_head *pos;
207 struct fb_modelist *modelist;
208 const struct fb_videomode *mode;
209
210 i = 0;
211 list_for_each(pos, &fb_info->modelist) {
212 modelist = list_entry(pos, struct fb_modelist, list);
213 mode = &modelist->mode;
214 i += mode_string(buf, i, mode);
215 }
216 return i;
217}
218
219static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
220 const char *buf, size_t count)
221{
222 struct fb_info *fb_info = dev_get_drvdata(device);
223 struct fb_var_screeninfo var;
224 char ** last = NULL;
225 int err;
226
227 var = fb_info->var;
228 var.bits_per_pixel = simple_strtoul(buf, last, 0);
229 if ((err = activate(fb_info, &var)))
230 return err;
231 return count;
232}
233
234static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
235 char *buf)
236{
237 struct fb_info *fb_info = dev_get_drvdata(device);
238 return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
239}
240
241static ssize_t store_rotate(struct device *device,
242 struct device_attribute *attr,
243 const char *buf, size_t count)
244{
245 struct fb_info *fb_info = dev_get_drvdata(device);
246 struct fb_var_screeninfo var;
247 char **last = NULL;
248 int err;
249
250 var = fb_info->var;
251 var.rotate = simple_strtoul(buf, last, 0);
252
253 if ((err = activate(fb_info, &var)))
254 return err;
255
256 return count;
257}
258
259
260static ssize_t show_rotate(struct device *device,
261 struct device_attribute *attr, char *buf)
262{
263 struct fb_info *fb_info = dev_get_drvdata(device);
264
265 return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
266}
267
268static ssize_t store_virtual(struct device *device,
269 struct device_attribute *attr,
270 const char *buf, size_t count)
271{
272 struct fb_info *fb_info = dev_get_drvdata(device);
273 struct fb_var_screeninfo var;
274 char *last = NULL;
275 int err;
276
277 var = fb_info->var;
278 var.xres_virtual = simple_strtoul(buf, &last, 0);
279 last++;
280 if (last - buf >= count)
281 return -EINVAL;
282 var.yres_virtual = simple_strtoul(last, &last, 0);
283
284 if ((err = activate(fb_info, &var)))
285 return err;
286 return count;
287}
288
289static ssize_t show_virtual(struct device *device,
290 struct device_attribute *attr, char *buf)
291{
292 struct fb_info *fb_info = dev_get_drvdata(device);
293 return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
294 fb_info->var.yres_virtual);
295}
296
297static ssize_t show_stride(struct device *device,
298 struct device_attribute *attr, char *buf)
299{
300 struct fb_info *fb_info = dev_get_drvdata(device);
301 return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
302}
303
304static ssize_t store_blank(struct device *device,
305 struct device_attribute *attr,
306 const char *buf, size_t count)
307{
308 struct fb_info *fb_info = dev_get_drvdata(device);
309 char *last = NULL;
310 int err;
311
312 console_lock();
313 fb_info->flags |= FBINFO_MISC_USEREVENT;
314 err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
315 fb_info->flags &= ~FBINFO_MISC_USEREVENT;
316 console_unlock();
317 if (err < 0)
318 return err;
319 return count;
320}
321
322static ssize_t show_blank(struct device *device,
323 struct device_attribute *attr, char *buf)
324{
325// struct fb_info *fb_info = dev_get_drvdata(device);
326 return 0;
327}
328
329static ssize_t store_console(struct device *device,
330 struct device_attribute *attr,
331 const char *buf, size_t count)
332{
333// struct fb_info *fb_info = dev_get_drvdata(device);
334 return 0;
335}
336
337static ssize_t show_console(struct device *device,
338 struct device_attribute *attr, char *buf)
339{
340// struct fb_info *fb_info = dev_get_drvdata(device);
341 return 0;
342}
343
344static ssize_t store_cursor(struct device *device,
345 struct device_attribute *attr,
346 const char *buf, size_t count)
347{
348// struct fb_info *fb_info = dev_get_drvdata(device);
349 return 0;
350}
351
352static ssize_t show_cursor(struct device *device,
353 struct device_attribute *attr, char *buf)
354{
355// struct fb_info *fb_info = dev_get_drvdata(device);
356 return 0;
357}
358
359static ssize_t store_pan(struct device *device,
360 struct device_attribute *attr,
361 const char *buf, size_t count)
362{
363 struct fb_info *fb_info = dev_get_drvdata(device);
364 struct fb_var_screeninfo var;
365 char *last = NULL;
366 int err;
367
368 var = fb_info->var;
369 var.xoffset = simple_strtoul(buf, &last, 0);
370 last++;
371 if (last - buf >= count)
372 return -EINVAL;
373 var.yoffset = simple_strtoul(last, &last, 0);
374
375 console_lock();
376 err = fb_pan_display(fb_info, &var);
377 console_unlock();
378
379 if (err < 0)
380 return err;
381 return count;
382}
383
384static ssize_t show_pan(struct device *device,
385 struct device_attribute *attr, char *buf)
386{
387 struct fb_info *fb_info = dev_get_drvdata(device);
388 return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
389 fb_info->var.yoffset);
390}
391
392static ssize_t show_name(struct device *device,
393 struct device_attribute *attr, char *buf)
394{
395 struct fb_info *fb_info = dev_get_drvdata(device);
396
397 return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
398}
399
400static ssize_t store_fbstate(struct device *device,
401 struct device_attribute *attr,
402 const char *buf, size_t count)
403{
404 struct fb_info *fb_info = dev_get_drvdata(device);
405 u32 state;
406 char *last = NULL;
407
408 state = simple_strtoul(buf, &last, 0);
409
410 console_lock();
411 if (!lock_fb_info(fb_info)) {
412 console_unlock();
413 return -ENODEV;
414 }
415
416 fb_set_suspend(fb_info, (int)state);
417
418 unlock_fb_info(fb_info);
419 console_unlock();
420
421 return count;
422}
423
424static ssize_t show_fbstate(struct device *device,
425 struct device_attribute *attr, char *buf)
426{
427 struct fb_info *fb_info = dev_get_drvdata(device);
428 return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
429}
430
431#ifdef CONFIG_FB_BACKLIGHT
432static ssize_t store_bl_curve(struct device *device,
433 struct device_attribute *attr,
434 const char *buf, size_t count)
435{
436 struct fb_info *fb_info = dev_get_drvdata(device);
437 u8 tmp_curve[FB_BACKLIGHT_LEVELS];
438 unsigned int i;
439
440 /* Some drivers don't use framebuffer_alloc(), but those also
441 * don't have backlights.
442 */
443 if (!fb_info || !fb_info->bl_dev)
444 return -ENODEV;
445
446 if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
447 return -EINVAL;
448
449 for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
450 if (sscanf(&buf[i * 24],
451 "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
452 &tmp_curve[i * 8 + 0],
453 &tmp_curve[i * 8 + 1],
454 &tmp_curve[i * 8 + 2],
455 &tmp_curve[i * 8 + 3],
456 &tmp_curve[i * 8 + 4],
457 &tmp_curve[i * 8 + 5],
458 &tmp_curve[i * 8 + 6],
459 &tmp_curve[i * 8 + 7]) != 8)
460 return -EINVAL;
461
462 /* If there has been an error in the input data, we won't
463 * reach this loop.
464 */
465 mutex_lock(&fb_info->bl_curve_mutex);
466 for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
467 fb_info->bl_curve[i] = tmp_curve[i];
468 mutex_unlock(&fb_info->bl_curve_mutex);
469
470 return count;
471}
472
473static ssize_t show_bl_curve(struct device *device,
474 struct device_attribute *attr, char *buf)
475{
476 struct fb_info *fb_info = dev_get_drvdata(device);
477 ssize_t len = 0;
478 unsigned int i;
479
480 /* Some drivers don't use framebuffer_alloc(), but those also
481 * don't have backlights.
482 */
483 if (!fb_info || !fb_info->bl_dev)
484 return -ENODEV;
485
486 mutex_lock(&fb_info->bl_curve_mutex);
487 for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
488 len += snprintf(&buf[len], PAGE_SIZE,
489 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
490 fb_info->bl_curve[i + 0],
491 fb_info->bl_curve[i + 1],
492 fb_info->bl_curve[i + 2],
493 fb_info->bl_curve[i + 3],
494 fb_info->bl_curve[i + 4],
495 fb_info->bl_curve[i + 5],
496 fb_info->bl_curve[i + 6],
497 fb_info->bl_curve[i + 7]);
498 mutex_unlock(&fb_info->bl_curve_mutex);
499
500 return len;
501}
502#endif
503
504/* When cmap is added back in it should be a binary attribute
505 * not a text one. Consideration should also be given to converting
506 * fbdev to use configfs instead of sysfs */
507static struct device_attribute device_attrs[] = {
508 __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
509 __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
510 __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
511 __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
512 __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
513 __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
514 __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
515 __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
516 __ATTR(name, S_IRUGO, show_name, NULL),
517 __ATTR(stride, S_IRUGO, show_stride, NULL),
518 __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
519 __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
520#ifdef CONFIG_FB_BACKLIGHT
521 __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
522#endif
523};
524
525int fb_init_device(struct fb_info *fb_info)
526{
527 int i, error = 0;
528
529 dev_set_drvdata(fb_info->dev, fb_info);
530
531 fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
532
533 for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
534 error = device_create_file(fb_info->dev, &device_attrs[i]);
535
536 if (error)
537 break;
538 }
539
540 if (error) {
541 while (--i >= 0)
542 device_remove_file(fb_info->dev, &device_attrs[i]);
543 fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
544 }
545
546 return 0;
547}
548
549void fb_cleanup_device(struct fb_info *fb_info)
550{
551 unsigned int i;
552
553 if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
554 for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
555 device_remove_file(fb_info->dev, &device_attrs[i]);
556
557 fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
558 }
559}
560
561#ifdef CONFIG_FB_BACKLIGHT
562/* This function generates a linear backlight curve
563 *
564 * 0: off
565 * 1-7: min
566 * 8-127: linear from min to max
567 */
568void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
569{
570 unsigned int i, flat, count, range = (max - min);
571
572 mutex_lock(&fb_info->bl_curve_mutex);
573
574 fb_info->bl_curve[0] = off;
575
576 for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
577 fb_info->bl_curve[flat] = min;
578
579 count = FB_BACKLIGHT_LEVELS * 15 / 16;
580 for (i = 0; i < count; ++i)
581 fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
582
583 mutex_unlock(&fb_info->bl_curve_mutex);
584}
585EXPORT_SYMBOL_GPL(fb_bl_default_curve);
586#endif
diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c
new file mode 100644
index 000000000000..a9a907c440d7
--- /dev/null
+++ b/drivers/video/fbdev/core/modedb.c
@@ -0,0 +1,1137 @@
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/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 * Standard video mode definitions (taken from XFree86)
37 */
38
39static const struct fb_videomode modedb[] = {
40
41 /* 640x400 @ 70 Hz, 31.5 kHz hsync */
42 { NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, 0,
43 FB_VMODE_NONINTERLACED },
44
45 /* 640x480 @ 60 Hz, 31.5 kHz hsync */
46 { NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, 0,
47 FB_VMODE_NONINTERLACED },
48
49 /* 800x600 @ 56 Hz, 35.15 kHz hsync */
50 { NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, 0,
51 FB_VMODE_NONINTERLACED },
52
53 /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
54 { NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8, 0,
55 FB_VMODE_INTERLACED },
56
57 /* 640x400 @ 85 Hz, 37.86 kHz hsync */
58 { NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
59 FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED },
60
61 /* 640x480 @ 72 Hz, 36.5 kHz hsync */
62 { NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3, 0,
63 FB_VMODE_NONINTERLACED },
64
65 /* 640x480 @ 75 Hz, 37.50 kHz hsync */
66 { NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0,
67 FB_VMODE_NONINTERLACED },
68
69 /* 800x600 @ 60 Hz, 37.8 kHz hsync */
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 /* 640x480 @ 85 Hz, 43.27 kHz hsync */
75 { NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3, 0,
76 FB_VMODE_NONINTERLACED },
77
78 /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
79 { NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10, 0,
80 FB_VMODE_INTERLACED },
81 /* 800x600 @ 72 Hz, 48.0 kHz hsync */
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 /* 1024x768 @ 60 Hz, 48.4 kHz hsync */
87 { NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6, 0,
88 FB_VMODE_NONINTERLACED },
89
90 /* 640x480 @ 100 Hz, 53.01 kHz hsync */
91 { NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6, 0,
92 FB_VMODE_NONINTERLACED },
93
94 /* 1152x864 @ 60 Hz, 53.5 kHz hsync */
95 { NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8, 0,
96 FB_VMODE_NONINTERLACED },
97
98 /* 800x600 @ 85 Hz, 55.84 kHz hsync */
99 { NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5, 0,
100 FB_VMODE_NONINTERLACED },
101
102 /* 1024x768 @ 70 Hz, 56.5 kHz hsync */
103 { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0,
104 FB_VMODE_NONINTERLACED },
105
106 /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
107 { NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12, 0,
108 FB_VMODE_INTERLACED },
109
110 /* 800x600 @ 100 Hz, 64.02 kHz hsync */
111 { NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6, 0,
112 FB_VMODE_NONINTERLACED },
113
114 /* 1024x768 @ 76 Hz, 62.5 kHz hsync */
115 { NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3, 0,
116 FB_VMODE_NONINTERLACED },
117
118 /* 1152x864 @ 70 Hz, 62.4 kHz hsync */
119 { NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10, 0,
120 FB_VMODE_NONINTERLACED },
121
122 /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
123 { NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0,
124 FB_VMODE_NONINTERLACED },
125
126 /* 1400x1050 @ 60Hz, 63.9 kHz hsync */
127 { NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3, 0,
128 FB_VMODE_NONINTERLACED },
129
130 /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
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 /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
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 /* 1024x768 @ 85 Hz, 70.24 kHz hsync */
141 { NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6, 0,
142 FB_VMODE_NONINTERLACED },
143
144 /* 1152x864 @ 78 Hz, 70.8 kHz hsync */
145 { NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12, 0,
146 FB_VMODE_NONINTERLACED },
147
148 /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
149 { NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8, 0,
150 FB_VMODE_NONINTERLACED },
151
152 /* 1600x1200 @ 60Hz, 75.00 kHz hsync */
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 /* 1152x864 @ 84 Hz, 76.0 kHz hsync */
158 { NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12, 0,
159 FB_VMODE_NONINTERLACED },
160
161 /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
162 { NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3, 0,
163 FB_VMODE_NONINTERLACED },
164
165 /* 1024x768 @ 100Hz, 80.21 kHz hsync */
166 { NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10, 0,
167 FB_VMODE_NONINTERLACED },
168
169 /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
170 { NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3, 0,
171 FB_VMODE_NONINTERLACED },
172
173 /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
174 { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, 0,
175 FB_VMODE_NONINTERLACED },
176
177 /* 1152x864 @ 100 Hz, 89.62 kHz hsync */
178 { NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19, 0,
179 FB_VMODE_NONINTERLACED },
180
181 /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
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 /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
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 /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */
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 /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
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 /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
202 { NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15, 0,
203 FB_VMODE_NONINTERLACED },
204
205 /* 1800x1440 @ 64Hz, 96.15 kHz hsync */
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 /* 1800x1440 @ 70Hz, 104.52 kHz hsync */
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 /* 512x384 @ 78 Hz, 31.50 kHz hsync */
216 { NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0,
217 FB_VMODE_NONINTERLACED },
218
219 /* 512x384 @ 85 Hz, 34.38 kHz hsync */
220 { NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3, 0,
221 FB_VMODE_NONINTERLACED },
222
223 /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
224 { NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0,
225 FB_VMODE_DOUBLE },
226
227 /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
228 { NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1, 0,
229 FB_VMODE_DOUBLE },
230
231 /* 320x240 @ 72 Hz, 36.5 kHz hsync */
232 { NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0,
233 FB_VMODE_DOUBLE },
234
235 /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
236 { NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1, 0,
237 FB_VMODE_DOUBLE },
238
239 /* 400x300 @ 60 Hz, 37.8 kHz hsync */
240 { NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2, 0,
241 FB_VMODE_DOUBLE },
242
243 /* 400x300 @ 72 Hz, 48.0 kHz hsync */
244 { NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3, 0,
245 FB_VMODE_DOUBLE },
246
247 /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
248 { NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1, 0,
249 FB_VMODE_DOUBLE },
250
251 /* 480x300 @ 60 Hz, 37.8 kHz hsync */
252 { NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2, 0,
253 FB_VMODE_DOUBLE },
254
255 /* 480x300 @ 63 Hz, 39.6 kHz hsync */
256 { NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2, 0,
257 FB_VMODE_DOUBLE },
258
259 /* 480x300 @ 72 Hz, 48.0 kHz hsync */
260 { NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, 0,
261 FB_VMODE_DOUBLE },
262
263 /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */
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 /* 1152x768, 60 Hz, PowerBook G4 Titanium I and II */
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 /* 1366x768, 60 Hz, 47.403 kHz hsync, WXGA 16:9 aspect ratio */
274 { NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5, 0,
275 FB_VMODE_NONINTERLACED },
276
277 /* 1280x800, 60 Hz, 47.403 kHz hsync, WXGA 16:10 aspect ratio */
278 { NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3, 0,
279 FB_VMODE_NONINTERLACED },
280
281 /* 720x576i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
282 { NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5, 0,
283 FB_VMODE_INTERLACED },
284
285 /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
286 { NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, 0,
287 FB_VMODE_INTERLACED },
288
289 /* 864x480 @ 60 Hz, 35.15 kHz hsync */
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 /* #1: 640x480p@59.94/60Hz */
297 [1] = {
298 NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
299 FB_VMODE_NONINTERLACED, 0,
300 },
301 /* #3: 720x480p@59.94/60Hz */
302 [3] = {
303 NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
304 FB_VMODE_NONINTERLACED, 0,
305 },
306 /* #5: 1920x1080i@59.94/60Hz */
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 /* #7: 720(1440)x480iH@59.94/60Hz */
313 [7] = {
314 NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
315 FB_VMODE_INTERLACED, 0,
316 },
317 /* #9: 720(1440)x240pH@59.94/60Hz */
318 [9] = {
319 NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
320 FB_VMODE_NONINTERLACED, 0,
321 },
322 /* #18: 720x576pH@50Hz */
323 [18] = {
324 NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
325 FB_VMODE_NONINTERLACED, 0,
326 },
327 /* #19: 1280x720p@50Hz */
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 /* #20: 1920x1080i@50Hz */
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 /* #32: 1920x1080p@23.98/24Hz */
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 /* #35: (2880)x480p4x@59.94/60Hz */
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 /* 0 640x350-85 VESA */
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 /* 1 640x400-85 VESA */
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 /* 2 720x400-85 VESA */
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 /* 3 640x480-60 VESA */
363 { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
364 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
365 /* 4 640x480-72 VESA */
366 { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
367 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
368 /* 5 640x480-75 VESA */
369 { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
370 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
371 /* 6 640x480-85 VESA */
372 { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
373 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
374 /* 7 800x600-56 VESA */
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 /* 8 800x600-60 VESA */
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 /* 9 800x600-72 VESA */
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 /* 10 800x600-75 VESA */
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 /* 11 800x600-85 VESA */
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 /* 12 1024x768i-43 VESA */
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 /* 13 1024x768-60 VESA */
399 { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
400 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
401 /* 14 1024x768-70 VESA */
402 { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
403 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
404 /* 15 1024x768-75 VESA */
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 /* 16 1024x768-85 VESA */
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 /* 17 1152x864-75 VESA */
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 /* 18 1280x960-60 VESA */
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 /* 19 1280x960-85 VESA */
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 /* 20 1280x1024-60 VESA */
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 /* 21 1280x1024-75 VESA */
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 /* 22 1280x1024-85 VESA */
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 /* 23 1600x1200-60 VESA */
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 /* 24 1600x1200-65 VESA */
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 /* 25 1600x1200-70 VESA */
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 /* 26 1600x1200-75 VESA */
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 /* 27 1600x1200-85 VESA */
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 /* 28 1792x1344-60 VESA */
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 /* 29 1792x1344-75 VESA */
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 /* 30 1856x1392-60 VESA */
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 /* 31 1856x1392-75 VESA */
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 /* 32 1920x1440-60 VESA */
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 /* 33 1920x1440-75 VESA */
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 /* CONFIG_FB_MODE_HELPERS */
477
478/**
479 * fb_try_mode - test a video mode
480 * @var: frame buffer user defined part of display
481 * @info: frame buffer info structure
482 * @mode: frame buffer video mode structure
483 * @bpp: color depth in bits per pixel
484 *
485 * Tries a video mode to test it's validity for device @info.
486 *
487 * Returns 1 on success.
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 * fb_find_mode - finds a valid video mode
524 * @var: frame buffer user defined part of display
525 * @info: frame buffer info structure
526 * @mode_option: string video mode to find
527 * @db: video mode database
528 * @dbsize: size of @db
529 * @default_mode: default video mode to fall back to
530 * @default_bpp: default color depth in bits per pixel
531 *
532 * Finds a suitable video mode, starting with the specified mode
533 * in @mode_option with fallback to @default_mode. If
534 * @default_mode fails, all modes in the video mode database will
535 * be tried.
536 *
537 * Valid mode specifiers for @mode_option:
538 *
539 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
540 * <name>[-<bpp>][@<refresh>]
541 *
542 * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
543 * <name> a string.
544 *
545 * If 'M' is present after yres (and before refresh/bpp if present),
546 * the function will compute the timings using VESA(tm) Coordinated
547 * Video Timings (CVT). If 'R' is present after 'M', will compute with
548 * reduced blanking (for flatpanels). If 'i' is present, compute
549 * interlaced mode. If 'm' is present, add margins equal to 1.8%
550 * of xres rounded down to 8 pixels, and 1.8% of yres. The char
551 * 'i' and 'm' must be after 'M' and 'R'. Example:
552 *
553 * 1024x768MR-8@60m - Reduced blank with margins at 60Hz.
554 *
555 * NOTE: The passed struct @var is _not_ cleared! This allows you
556 * to supply values for e.g. the grayscale and accel_flags fields.
557 *
558 * Returns zero for failure, 1 if using specified @mode_option,
559 * 2 if using specified @mode_option with an ignored refresh rate,
560 * 3 if default mode is used, 4 if fall back to any valid mode.
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 /* Set up defaults */
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 /* Did the user specify a video mode? */
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 * If the caller has provided a custom mode database and
693 * a valid monspecs structure, we look for the mode with
694 * the highest refresh rate. Otherwise we play it safe
695 * it and try to find a mode with a refresh rate closest
696 * to the standard 60 Hz.
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 * Penalize modes with resolutions smaller
739 * than requested.
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 * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
771 * @mode: pointer to struct fb_videomode
772 * @var: pointer to struct fb_var_screeninfo
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 * fb_videomode_to_var - convert fb_videomode to fb_var_screeninfo
815 * @var: pointer to struct fb_var_screeninfo
816 * @mode: pointer to struct fb_videomode
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 * fb_mode_is_equal - compare 2 videomodes
840 * @mode1: first videomode
841 * @mode2: second videomode
842 *
843 * RETURNS:
844 * 1 if equal, 0 if not
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 * fb_find_best_mode - find best matching videomode
864 * @var: pointer to struct fb_var_screeninfo
865 * @head: pointer to struct list_head of modelist
866 *
867 * RETURNS:
868 * struct fb_videomode, NULL if none found
869 *
870 * IMPORTANT:
871 * This function assumes that all modelist entries in
872 * info->modelist are valid.
873 *
874 * NOTES:
875 * Finds best matching videomode which has an equal or greater dimension than
876 * var->xres and var->yres. If more than 1 videomode is found, will return
877 * the videomode with the highest refresh rate
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 * fb_find_nearest_mode - find closest videomode
909 *
910 * @mode: pointer to struct fb_videomode
911 * @head: pointer to modelist
912 *
913 * Finds best matching videomode, smaller or greater in dimension.
914 * If more than 1 videomode is found, will return the videomode with
915 * the closest refresh rate.
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 * fb_match_mode - find a videomode which exactly matches the timings in var
951 * @var: pointer to struct fb_var_screeninfo
952 * @head: pointer to struct list_head of modelist
953 *
954 * RETURNS:
955 * struct fb_videomode, NULL if none found
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 * fb_add_videomode - adds videomode entry to modelist
976 * @mode: videomode to add
977 * @head: struct list_head of modelist
978 *
979 * NOTES:
980 * Will only add unmatched mode entries
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 * fb_delete_videomode - removed videomode entry from modelist
1011 * @mode: videomode to remove
1012 * @head: struct list_head of modelist
1013 *
1014 * NOTES:
1015 * Will remove all matching mode entries
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 * fb_destroy_modelist - destroy modelist
1036 * @head: struct list_head of modelist
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 * fb_videomode_to_modelist - convert mode array to mode list
1051 * @modedb: array of struct fb_videomode
1052 * @num: number of entries in array
1053 * @head: struct list_head of modelist
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 /* get the first detailed mode and the very first mode */
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 /* first detailed timing is preferred */
1096 if (specs->misc & FB_MISC_1ST_DETAIL) {
1097 best = md;
1098 goto finished;
1099 }
1100
1101 /* find best mode based on display width and height */
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 /* use first detailed mode */
1116 if (md) {
1117 best = md;
1118 goto finished;
1119 }
1120
1121 /* last resort, use the very first mode */
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);
diff --git a/drivers/video/fbdev/core/svgalib.c b/drivers/video/fbdev/core/svgalib.c
new file mode 100644
index 000000000000..9e01322fabe3
--- /dev/null
+++ b/drivers/video/fbdev/core/svgalib.c
@@ -0,0 +1,672 @@
1/*
2 * Common utility functions for VGA-based graphics cards.
3 *
4 * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 *
10 * Some parts are based on David Boucher's viafb (http://davesdomain.org.uk/viafb/)
11 */
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/string.h>
16#include <linux/fb.h>
17#include <linux/svga.h>
18#include <asm/types.h>
19#include <asm/io.h>
20
21
22/* Write a CRT register value spread across multiple registers */
23void svga_wcrt_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
24{
25 u8 regval, bitval, bitnum;
26
27 while (regset->regnum != VGA_REGSET_END_VAL) {
28 regval = vga_rcrt(regbase, regset->regnum);
29 bitnum = regset->lowbit;
30 while (bitnum <= regset->highbit) {
31 bitval = 1 << bitnum;
32 regval = regval & ~bitval;
33 if (value & 1) regval = regval | bitval;
34 bitnum ++;
35 value = value >> 1;
36 }
37 vga_wcrt(regbase, regset->regnum, regval);
38 regset ++;
39 }
40}
41
42/* Write a sequencer register value spread across multiple registers */
43void svga_wseq_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
44{
45 u8 regval, bitval, bitnum;
46
47 while (regset->regnum != VGA_REGSET_END_VAL) {
48 regval = vga_rseq(regbase, regset->regnum);
49 bitnum = regset->lowbit;
50 while (bitnum <= regset->highbit) {
51 bitval = 1 << bitnum;
52 regval = regval & ~bitval;
53 if (value & 1) regval = regval | bitval;
54 bitnum ++;
55 value = value >> 1;
56 }
57 vga_wseq(regbase, regset->regnum, regval);
58 regset ++;
59 }
60}
61
62static unsigned int svga_regset_size(const struct vga_regset *regset)
63{
64 u8 count = 0;
65
66 while (regset->regnum != VGA_REGSET_END_VAL) {
67 count += regset->highbit - regset->lowbit + 1;
68 regset ++;
69 }
70 return 1 << count;
71}
72
73
74/* ------------------------------------------------------------------------- */
75
76
77/* Set graphics controller registers to sane values */
78void svga_set_default_gfx_regs(void __iomem *regbase)
79{
80 /* All standard GFX registers (GR00 - GR08) */
81 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0x00);
82 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0x00);
83 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0x00);
84 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0x00);
85 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0x00);
86 vga_wgfx(regbase, VGA_GFX_MODE, 0x00);
87/* vga_wgfx(regbase, VGA_GFX_MODE, 0x20); */
88/* vga_wgfx(regbase, VGA_GFX_MODE, 0x40); */
89 vga_wgfx(regbase, VGA_GFX_MISC, 0x05);
90/* vga_wgfx(regbase, VGA_GFX_MISC, 0x01); */
91 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x0F);
92 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 0xFF);
93}
94
95/* Set attribute controller registers to sane values */
96void svga_set_default_atc_regs(void __iomem *regbase)
97{
98 u8 count;
99
100 vga_r(regbase, 0x3DA);
101 vga_w(regbase, VGA_ATT_W, 0x00);
102
103 /* All standard ATC registers (AR00 - AR14) */
104 for (count = 0; count <= 0xF; count ++)
105 svga_wattr(regbase, count, count);
106
107 svga_wattr(regbase, VGA_ATC_MODE, 0x01);
108/* svga_wattr(regbase, VGA_ATC_MODE, 0x41); */
109 svga_wattr(regbase, VGA_ATC_OVERSCAN, 0x00);
110 svga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 0x0F);
111 svga_wattr(regbase, VGA_ATC_PEL, 0x00);
112 svga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0x00);
113
114 vga_r(regbase, 0x3DA);
115 vga_w(regbase, VGA_ATT_W, 0x20);
116}
117
118/* Set sequencer registers to sane values */
119void svga_set_default_seq_regs(void __iomem *regbase)
120{
121 /* Standard sequencer registers (SR01 - SR04), SR00 is not set */
122 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, VGA_SR01_CHAR_CLK_8DOTS);
123 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, VGA_SR02_ALL_PLANES);
124 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
125/* vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE | VGA_SR04_CHN_4M); */
126 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE);
127}
128
129/* Set CRTC registers to sane values */
130void svga_set_default_crt_regs(void __iomem *regbase)
131{
132 /* Standard CRT registers CR03 CR08 CR09 CR14 CR17 */
133 svga_wcrt_mask(regbase, 0x03, 0x80, 0x80); /* Enable vertical retrace EVRA */
134 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
135 svga_wcrt_mask(regbase, VGA_CRTC_MAX_SCAN, 0, 0x1F);
136 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
137 vga_wcrt(regbase, VGA_CRTC_MODE, 0xE3);
138}
139
140void svga_set_textmode_vga_regs(void __iomem *regbase)
141{
142 /* svga_wseq_mask(regbase, 0x1, 0x00, 0x01); */ /* Switch 8/9 pixel per char */
143 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM);
144 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x03);
145
146 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, 0x0f); /* 0x4f */
147 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0x1f);
148 svga_wcrt_mask(regbase, VGA_CRTC_MODE, 0x23, 0x7f);
149
150 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0x0d);
151 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 0x0e);
152 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0x00);
153 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0x00);
154
155 vga_wgfx(regbase, VGA_GFX_MODE, 0x10); /* Odd/even memory mode */
156 vga_wgfx(regbase, VGA_GFX_MISC, 0x0E); /* Misc graphics register - text mode enable */
157 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x00);
158
159 vga_r(regbase, 0x3DA);
160 vga_w(regbase, VGA_ATT_W, 0x00);
161
162 svga_wattr(regbase, 0x10, 0x0C); /* Attribute Mode Control Register - text mode, blinking and line graphics */
163 svga_wattr(regbase, 0x13, 0x08); /* Horizontal Pixel Panning Register */
164
165 vga_r(regbase, 0x3DA);
166 vga_w(regbase, VGA_ATT_W, 0x20);
167}
168
169#if 0
170void svga_dump_var(struct fb_var_screeninfo *var, int node)
171{
172 pr_debug("fb%d: var.vmode : 0x%X\n", node, var->vmode);
173 pr_debug("fb%d: var.xres : %d\n", node, var->xres);
174 pr_debug("fb%d: var.yres : %d\n", node, var->yres);
175 pr_debug("fb%d: var.bits_per_pixel: %d\n", node, var->bits_per_pixel);
176 pr_debug("fb%d: var.xres_virtual : %d\n", node, var->xres_virtual);
177 pr_debug("fb%d: var.yres_virtual : %d\n", node, var->yres_virtual);
178 pr_debug("fb%d: var.left_margin : %d\n", node, var->left_margin);
179 pr_debug("fb%d: var.right_margin : %d\n", node, var->right_margin);
180 pr_debug("fb%d: var.upper_margin : %d\n", node, var->upper_margin);
181 pr_debug("fb%d: var.lower_margin : %d\n", node, var->lower_margin);
182 pr_debug("fb%d: var.hsync_len : %d\n", node, var->hsync_len);
183 pr_debug("fb%d: var.vsync_len : %d\n", node, var->vsync_len);
184 pr_debug("fb%d: var.sync : 0x%X\n", node, var->sync);
185 pr_debug("fb%d: var.pixclock : %d\n\n", node, var->pixclock);
186}
187#endif /* 0 */
188
189
190/* ------------------------------------------------------------------------- */
191
192
193void svga_settile(struct fb_info *info, struct fb_tilemap *map)
194{
195 const u8 *font = map->data;
196 u8 __iomem *fb = (u8 __iomem *)info->screen_base;
197 int i, c;
198
199 if ((map->width != 8) || (map->height != 16) ||
200 (map->depth != 1) || (map->length != 256)) {
201 fb_err(info, "unsupported font parameters: width %d, height %d, depth %d, length %d\n",
202 map->width, map->height, map->depth, map->length);
203 return;
204 }
205
206 fb += 2;
207 for (c = 0; c < map->length; c++) {
208 for (i = 0; i < map->height; i++) {
209 fb_writeb(font[i], fb + i * 4);
210// fb[i * 4] = font[i];
211 }
212 fb += 128;
213 font += map->height;
214 }
215}
216
217/* Copy area in text (tileblit) mode */
218void svga_tilecopy(struct fb_info *info, struct fb_tilearea *area)
219{
220 int dx, dy;
221 /* colstride is halved in this function because u16 are used */
222 int colstride = 1 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
223 int rowstride = colstride * (info->var.xres_virtual / 8);
224 u16 __iomem *fb = (u16 __iomem *) info->screen_base;
225 u16 __iomem *src, *dst;
226
227 if ((area->sy > area->dy) ||
228 ((area->sy == area->dy) && (area->sx > area->dx))) {
229 src = fb + area->sx * colstride + area->sy * rowstride;
230 dst = fb + area->dx * colstride + area->dy * rowstride;
231 } else {
232 src = fb + (area->sx + area->width - 1) * colstride
233 + (area->sy + area->height - 1) * rowstride;
234 dst = fb + (area->dx + area->width - 1) * colstride
235 + (area->dy + area->height - 1) * rowstride;
236
237 colstride = -colstride;
238 rowstride = -rowstride;
239 }
240
241 for (dy = 0; dy < area->height; dy++) {
242 u16 __iomem *src2 = src;
243 u16 __iomem *dst2 = dst;
244 for (dx = 0; dx < area->width; dx++) {
245 fb_writew(fb_readw(src2), dst2);
246// *dst2 = *src2;
247 src2 += colstride;
248 dst2 += colstride;
249 }
250 src += rowstride;
251 dst += rowstride;
252 }
253}
254
255/* Fill area in text (tileblit) mode */
256void svga_tilefill(struct fb_info *info, struct fb_tilerect *rect)
257{
258 int dx, dy;
259 int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
260 int rowstride = colstride * (info->var.xres_virtual / 8);
261 int attr = (0x0F & rect->bg) << 4 | (0x0F & rect->fg);
262 u8 __iomem *fb = (u8 __iomem *)info->screen_base;
263 fb += rect->sx * colstride + rect->sy * rowstride;
264
265 for (dy = 0; dy < rect->height; dy++) {
266 u8 __iomem *fb2 = fb;
267 for (dx = 0; dx < rect->width; dx++) {
268 fb_writeb(rect->index, fb2);
269 fb_writeb(attr, fb2 + 1);
270 fb2 += colstride;
271 }
272 fb += rowstride;
273 }
274}
275
276/* Write text in text (tileblit) mode */
277void svga_tileblit(struct fb_info *info, struct fb_tileblit *blit)
278{
279 int dx, dy, i;
280 int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
281 int rowstride = colstride * (info->var.xres_virtual / 8);
282 int attr = (0x0F & blit->bg) << 4 | (0x0F & blit->fg);
283 u8 __iomem *fb = (u8 __iomem *)info->screen_base;
284 fb += blit->sx * colstride + blit->sy * rowstride;
285
286 i=0;
287 for (dy=0; dy < blit->height; dy ++) {
288 u8 __iomem *fb2 = fb;
289 for (dx = 0; dx < blit->width; dx ++) {
290 fb_writeb(blit->indices[i], fb2);
291 fb_writeb(attr, fb2 + 1);
292 fb2 += colstride;
293 i ++;
294 if (i == blit->length) return;
295 }
296 fb += rowstride;
297 }
298
299}
300
301/* Set cursor in text (tileblit) mode */
302void svga_tilecursor(void __iomem *regbase, struct fb_info *info, struct fb_tilecursor *cursor)
303{
304 u8 cs = 0x0d;
305 u8 ce = 0x0e;
306 u16 pos = cursor->sx + (info->var.xoffset / 8)
307 + (cursor->sy + (info->var.yoffset / 16))
308 * (info->var.xres_virtual / 8);
309
310 if (! cursor -> mode)
311 return;
312
313 svga_wcrt_mask(regbase, 0x0A, 0x20, 0x20); /* disable cursor */
314
315 if (cursor -> shape == FB_TILE_CURSOR_NONE)
316 return;
317
318 switch (cursor -> shape) {
319 case FB_TILE_CURSOR_UNDERLINE:
320 cs = 0x0d;
321 break;
322 case FB_TILE_CURSOR_LOWER_THIRD:
323 cs = 0x09;
324 break;
325 case FB_TILE_CURSOR_LOWER_HALF:
326 cs = 0x07;
327 break;
328 case FB_TILE_CURSOR_TWO_THIRDS:
329 cs = 0x05;
330 break;
331 case FB_TILE_CURSOR_BLOCK:
332 cs = 0x01;
333 break;
334 }
335
336 /* set cursor position */
337 vga_wcrt(regbase, 0x0E, pos >> 8);
338 vga_wcrt(regbase, 0x0F, pos & 0xFF);
339
340 vga_wcrt(regbase, 0x0B, ce); /* set cursor end */
341 vga_wcrt(regbase, 0x0A, cs); /* set cursor start and enable it */
342}
343
344int svga_get_tilemax(struct fb_info *info)
345{
346 return 256;
347}
348
349/* Get capabilities of accelerator based on the mode */
350
351void svga_get_caps(struct fb_info *info, struct fb_blit_caps *caps,
352 struct fb_var_screeninfo *var)
353{
354 if (var->bits_per_pixel == 0) {
355 /* can only support 256 8x16 bitmap */
356 caps->x = 1 << (8 - 1);
357 caps->y = 1 << (16 - 1);
358 caps->len = 256;
359 } else {
360 caps->x = (var->bits_per_pixel == 4) ? 1 << (8 - 1) : ~(u32)0;
361 caps->y = ~(u32)0;
362 caps->len = ~(u32)0;
363 }
364}
365EXPORT_SYMBOL(svga_get_caps);
366
367/* ------------------------------------------------------------------------- */
368
369
370/*
371 * Compute PLL settings (M, N, R)
372 * F_VCO = (F_BASE * M) / N
373 * F_OUT = F_VCO / (2^R)
374 */
375
376static inline u32 abs_diff(u32 a, u32 b)
377{
378 return (a > b) ? (a - b) : (b - a);
379}
380
381int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u16 *r, int node)
382{
383 u16 am, an, ar;
384 u32 f_vco, f_current, delta_current, delta_best;
385
386 pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int) f_wanted);
387
388 ar = pll->r_max;
389 f_vco = f_wanted << ar;
390
391 /* overflow check */
392 if ((f_vco >> ar) != f_wanted)
393 return -EINVAL;
394
395 /* It is usually better to have greater VCO clock
396 because of better frequency stability.
397 So first try r_max, then r smaller. */
398 while ((ar > pll->r_min) && (f_vco > pll->f_vco_max)) {
399 ar--;
400 f_vco = f_vco >> 1;
401 }
402
403 /* VCO bounds check */
404 if ((f_vco < pll->f_vco_min) || (f_vco > pll->f_vco_max))
405 return -EINVAL;
406
407 delta_best = 0xFFFFFFFF;
408 *m = 0;
409 *n = 0;
410 *r = ar;
411
412 am = pll->m_min;
413 an = pll->n_min;
414
415 while ((am <= pll->m_max) && (an <= pll->n_max)) {
416 f_current = (pll->f_base * am) / an;
417 delta_current = abs_diff (f_current, f_vco);
418
419 if (delta_current < delta_best) {
420 delta_best = delta_current;
421 *m = am;
422 *n = an;
423 }
424
425 if (f_current <= f_vco) {
426 am ++;
427 } else {
428 an ++;
429 }
430 }
431
432 f_current = (pll->f_base * *m) / *n;
433 pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int) (f_current >> ar), (int) f_current);
434 pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int) *m, (unsigned int) *n, (unsigned int) *r);
435 return 0;
436}
437
438
439/* ------------------------------------------------------------------------- */
440
441
442/* Check CRT timing values */
443int svga_check_timings(const struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node)
444{
445 u32 value;
446
447 var->xres = (var->xres+7)&~7;
448 var->left_margin = (var->left_margin+7)&~7;
449 var->right_margin = (var->right_margin+7)&~7;
450 var->hsync_len = (var->hsync_len+7)&~7;
451
452 /* Check horizontal total */
453 value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
454 if (((value / 8) - 5) >= svga_regset_size (tm->h_total_regs))
455 return -EINVAL;
456
457 /* Check horizontal display and blank start */
458 value = var->xres;
459 if (((value / 8) - 1) >= svga_regset_size (tm->h_display_regs))
460 return -EINVAL;
461 if (((value / 8) - 1) >= svga_regset_size (tm->h_blank_start_regs))
462 return -EINVAL;
463
464 /* Check horizontal sync start */
465 value = var->xres + var->right_margin;
466 if (((value / 8) - 1) >= svga_regset_size (tm->h_sync_start_regs))
467 return -EINVAL;
468
469 /* Check horizontal blank end (or length) */
470 value = var->left_margin + var->right_margin + var->hsync_len;
471 if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_blank_end_regs)))
472 return -EINVAL;
473
474 /* Check horizontal sync end (or length) */
475 value = var->hsync_len;
476 if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_sync_end_regs)))
477 return -EINVAL;
478
479 /* Check vertical total */
480 value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
481 if ((value - 1) >= svga_regset_size(tm->v_total_regs))
482 return -EINVAL;
483
484 /* Check vertical display and blank start */
485 value = var->yres;
486 if ((value - 1) >= svga_regset_size(tm->v_display_regs))
487 return -EINVAL;
488 if ((value - 1) >= svga_regset_size(tm->v_blank_start_regs))
489 return -EINVAL;
490
491 /* Check vertical sync start */
492 value = var->yres + var->lower_margin;
493 if ((value - 1) >= svga_regset_size(tm->v_sync_start_regs))
494 return -EINVAL;
495
496 /* Check vertical blank end (or length) */
497 value = var->upper_margin + var->lower_margin + var->vsync_len;
498 if ((value == 0) || (value >= svga_regset_size (tm->v_blank_end_regs)))
499 return -EINVAL;
500
501 /* Check vertical sync end (or length) */
502 value = var->vsync_len;
503 if ((value == 0) || (value >= svga_regset_size (tm->v_sync_end_regs)))
504 return -EINVAL;
505
506 return 0;
507}
508
509/* Set CRT timing registers */
510void svga_set_timings(void __iomem *regbase, const struct svga_timing_regs *tm,
511 struct fb_var_screeninfo *var,
512 u32 hmul, u32 hdiv, u32 vmul, u32 vdiv, u32 hborder, int node)
513{
514 u8 regval;
515 u32 value;
516
517 value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
518 value = (value * hmul) / hdiv;
519 pr_debug("fb%d: horizontal total : %d\n", node, value);
520 svga_wcrt_multi(regbase, tm->h_total_regs, (value / 8) - 5);
521
522 value = var->xres;
523 value = (value * hmul) / hdiv;
524 pr_debug("fb%d: horizontal display : %d\n", node, value);
525 svga_wcrt_multi(regbase, tm->h_display_regs, (value / 8) - 1);
526
527 value = var->xres;
528 value = (value * hmul) / hdiv;
529 pr_debug("fb%d: horizontal blank start: %d\n", node, value);
530 svga_wcrt_multi(regbase, tm->h_blank_start_regs, (value / 8) - 1 + hborder);
531
532 value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
533 value = (value * hmul) / hdiv;
534 pr_debug("fb%d: horizontal blank end : %d\n", node, value);
535 svga_wcrt_multi(regbase, tm->h_blank_end_regs, (value / 8) - 1 - hborder);
536
537 value = var->xres + var->right_margin;
538 value = (value * hmul) / hdiv;
539 pr_debug("fb%d: horizontal sync start : %d\n", node, value);
540 svga_wcrt_multi(regbase, tm->h_sync_start_regs, (value / 8));
541
542 value = var->xres + var->right_margin + var->hsync_len;
543 value = (value * hmul) / hdiv;
544 pr_debug("fb%d: horizontal sync end : %d\n", node, value);
545 svga_wcrt_multi(regbase, tm->h_sync_end_regs, (value / 8));
546
547 value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
548 value = (value * vmul) / vdiv;
549 pr_debug("fb%d: vertical total : %d\n", node, value);
550 svga_wcrt_multi(regbase, tm->v_total_regs, value - 2);
551
552 value = var->yres;
553 value = (value * vmul) / vdiv;
554 pr_debug("fb%d: vertical display : %d\n", node, value);
555 svga_wcrt_multi(regbase, tm->v_display_regs, value - 1);
556
557 value = var->yres;
558 value = (value * vmul) / vdiv;
559 pr_debug("fb%d: vertical blank start : %d\n", node, value);
560 svga_wcrt_multi(regbase, tm->v_blank_start_regs, value);
561
562 value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
563 value = (value * vmul) / vdiv;
564 pr_debug("fb%d: vertical blank end : %d\n", node, value);
565 svga_wcrt_multi(regbase, tm->v_blank_end_regs, value - 2);
566
567 value = var->yres + var->lower_margin;
568 value = (value * vmul) / vdiv;
569 pr_debug("fb%d: vertical sync start : %d\n", node, value);
570 svga_wcrt_multi(regbase, tm->v_sync_start_regs, value);
571
572 value = var->yres + var->lower_margin + var->vsync_len;
573 value = (value * vmul) / vdiv;
574 pr_debug("fb%d: vertical sync end : %d\n", node, value);
575 svga_wcrt_multi(regbase, tm->v_sync_end_regs, value);
576
577 /* Set horizontal and vertical sync pulse polarity in misc register */
578
579 regval = vga_r(regbase, VGA_MIS_R);
580 if (var->sync & FB_SYNC_HOR_HIGH_ACT) {
581 pr_debug("fb%d: positive horizontal sync\n", node);
582 regval = regval & ~0x80;
583 } else {
584 pr_debug("fb%d: negative horizontal sync\n", node);
585 regval = regval | 0x80;
586 }
587 if (var->sync & FB_SYNC_VERT_HIGH_ACT) {
588 pr_debug("fb%d: positive vertical sync\n", node);
589 regval = regval & ~0x40;
590 } else {
591 pr_debug("fb%d: negative vertical sync\n\n", node);
592 regval = regval | 0x40;
593 }
594 vga_w(regbase, VGA_MIS_W, regval);
595}
596
597
598/* ------------------------------------------------------------------------- */
599
600
601static inline int match_format(const struct svga_fb_format *frm,
602 struct fb_var_screeninfo *var)
603{
604 int i = 0;
605 int stored = -EINVAL;
606
607 while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL)
608 {
609 if ((var->bits_per_pixel == frm->bits_per_pixel) &&
610 (var->red.length <= frm->red.length) &&
611 (var->green.length <= frm->green.length) &&
612 (var->blue.length <= frm->blue.length) &&
613 (var->transp.length <= frm->transp.length) &&
614 (var->nonstd == frm->nonstd))
615 return i;
616 if (var->bits_per_pixel == frm->bits_per_pixel)
617 stored = i;
618 i++;
619 frm++;
620 }
621 return stored;
622}
623
624int svga_match_format(const struct svga_fb_format *frm,
625 struct fb_var_screeninfo *var,
626 struct fb_fix_screeninfo *fix)
627{
628 int i = match_format(frm, var);
629
630 if (i >= 0) {
631 var->bits_per_pixel = frm[i].bits_per_pixel;
632 var->red = frm[i].red;
633 var->green = frm[i].green;
634 var->blue = frm[i].blue;
635 var->transp = frm[i].transp;
636 var->nonstd = frm[i].nonstd;
637 if (fix != NULL) {
638 fix->type = frm[i].type;
639 fix->type_aux = frm[i].type_aux;
640 fix->visual = frm[i].visual;
641 fix->xpanstep = frm[i].xpanstep;
642 }
643 }
644
645 return i;
646}
647
648
649EXPORT_SYMBOL(svga_wcrt_multi);
650EXPORT_SYMBOL(svga_wseq_multi);
651
652EXPORT_SYMBOL(svga_set_default_gfx_regs);
653EXPORT_SYMBOL(svga_set_default_atc_regs);
654EXPORT_SYMBOL(svga_set_default_seq_regs);
655EXPORT_SYMBOL(svga_set_default_crt_regs);
656EXPORT_SYMBOL(svga_set_textmode_vga_regs);
657
658EXPORT_SYMBOL(svga_settile);
659EXPORT_SYMBOL(svga_tilecopy);
660EXPORT_SYMBOL(svga_tilefill);
661EXPORT_SYMBOL(svga_tileblit);
662EXPORT_SYMBOL(svga_tilecursor);
663EXPORT_SYMBOL(svga_get_tilemax);
664
665EXPORT_SYMBOL(svga_compute_pll);
666EXPORT_SYMBOL(svga_check_timings);
667EXPORT_SYMBOL(svga_set_timings);
668EXPORT_SYMBOL(svga_match_format);
669
670MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>");
671MODULE_DESCRIPTION("Common utility functions for VGA-based graphics cards");
672MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c
new file mode 100644
index 000000000000..844a32fd38ed
--- /dev/null
+++ b/drivers/video/fbdev/core/syscopyarea.c
@@ -0,0 +1,377 @@
1/*
2 * Generic Bit Block Transfer for frame buffers located in system RAM with
3 * packed pixels of any depth.
4 *
5 * Based almost entirely from cfbcopyarea.c (which is based almost entirely
6 * on Geert Uytterhoeven's copyarea routine)
7 *
8 * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
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 for
12 * more details.
13 *
14 */
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/string.h>
18#include <linux/fb.h>
19#include <asm/types.h>
20#include <asm/io.h>
21#include "fb_draw.h"
22
23 /*
24 * Generic bitwise copy algorithm
25 */
26
27static void
28bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx,
29 const unsigned long *src, int src_idx, int bits, unsigned n)
30{
31 unsigned long first, last;
32 int const shift = dst_idx-src_idx;
33 int left, right;
34
35 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
36 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
37
38 if (!shift) {
39 /* Same alignment for source and dest */
40 if (dst_idx+n <= bits) {
41 /* Single word */
42 if (last)
43 first &= last;
44 *dst = comp(*src, *dst, first);
45 } else {
46 /* Multiple destination words */
47 /* Leading bits */
48 if (first != ~0UL) {
49 *dst = comp(*src, *dst, first);
50 dst++;
51 src++;
52 n -= bits - dst_idx;
53 }
54
55 /* Main chunk */
56 n /= bits;
57 while (n >= 8) {
58 *dst++ = *src++;
59 *dst++ = *src++;
60 *dst++ = *src++;
61 *dst++ = *src++;
62 *dst++ = *src++;
63 *dst++ = *src++;
64 *dst++ = *src++;
65 *dst++ = *src++;
66 n -= 8;
67 }
68 while (n--)
69 *dst++ = *src++;
70
71 /* Trailing bits */
72 if (last)
73 *dst = comp(*src, *dst, last);
74 }
75 } else {
76 unsigned long d0, d1;
77 int m;
78
79 /* Different alignment for source and dest */
80 right = shift & (bits - 1);
81 left = -shift & (bits - 1);
82
83 if (dst_idx+n <= bits) {
84 /* Single destination word */
85 if (last)
86 first &= last;
87 if (shift > 0) {
88 /* Single source word */
89 *dst = comp(*src >> right, *dst, first);
90 } else if (src_idx+n <= bits) {
91 /* Single source word */
92 *dst = comp(*src << left, *dst, first);
93 } else {
94 /* 2 source words */
95 d0 = *src++;
96 d1 = *src;
97 *dst = comp(d0 << left | d1 >> right, *dst,
98 first);
99 }
100 } else {
101 /* Multiple destination words */
102 /** We must always remember the last value read,
103 because in case SRC and DST overlap bitwise (e.g.
104 when moving just one pixel in 1bpp), we always
105 collect one full long for DST and that might
106 overlap with the current long from SRC. We store
107 this value in 'd0'. */
108 d0 = *src++;
109 /* Leading bits */
110 if (shift > 0) {
111 /* Single source word */
112 *dst = comp(d0 >> right, *dst, first);
113 dst++;
114 n -= bits - dst_idx;
115 } else {
116 /* 2 source words */
117 d1 = *src++;
118 *dst = comp(d0 << left | *dst >> right, *dst, first);
119 d0 = d1;
120 dst++;
121 n -= bits - dst_idx;
122 }
123
124 /* Main chunk */
125 m = n % bits;
126 n /= bits;
127 while (n >= 4) {
128 d1 = *src++;
129 *dst++ = d0 << left | d1 >> right;
130 d0 = d1;
131 d1 = *src++;
132 *dst++ = d0 << left | d1 >> right;
133 d0 = d1;
134 d1 = *src++;
135 *dst++ = d0 << left | d1 >> right;
136 d0 = d1;
137 d1 = *src++;
138 *dst++ = d0 << left | d1 >> right;
139 d0 = d1;
140 n -= 4;
141 }
142 while (n--) {
143 d1 = *src++;
144 *dst++ = d0 << left | d1 >> right;
145 d0 = d1;
146 }
147
148 /* Trailing bits */
149 if (last) {
150 if (m <= right) {
151 /* Single source word */
152 *dst = comp(d0 << left, *dst, last);
153 } else {
154 /* 2 source words */
155 d1 = *src;
156 *dst = comp(d0 << left | d1 >> right,
157 *dst, last);
158 }
159 }
160 }
161 }
162}
163
164 /*
165 * Generic bitwise copy algorithm, operating backward
166 */
167
168static void
169bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
170 const unsigned long *src, int src_idx, int bits, unsigned n)
171{
172 unsigned long first, last;
173 int shift;
174
175 dst += (n-1)/bits;
176 src += (n-1)/bits;
177 if ((n-1) % bits) {
178 dst_idx += (n-1) % bits;
179 dst += dst_idx >> (ffs(bits) - 1);
180 dst_idx &= bits - 1;
181 src_idx += (n-1) % bits;
182 src += src_idx >> (ffs(bits) - 1);
183 src_idx &= bits - 1;
184 }
185
186 shift = dst_idx-src_idx;
187
188 first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx);
189 last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits)));
190
191 if (!shift) {
192 /* Same alignment for source and dest */
193 if ((unsigned long)dst_idx+1 >= n) {
194 /* Single word */
195 if (last)
196 first &= last;
197 *dst = comp(*src, *dst, first);
198 } else {
199 /* Multiple destination words */
200
201 /* Leading bits */
202 if (first != ~0UL) {
203 *dst = comp(*src, *dst, first);
204 dst--;
205 src--;
206 n -= dst_idx+1;
207 }
208
209 /* Main chunk */
210 n /= bits;
211 while (n >= 8) {
212 *dst-- = *src--;
213 *dst-- = *src--;
214 *dst-- = *src--;
215 *dst-- = *src--;
216 *dst-- = *src--;
217 *dst-- = *src--;
218 *dst-- = *src--;
219 *dst-- = *src--;
220 n -= 8;
221 }
222 while (n--)
223 *dst-- = *src--;
224 /* Trailing bits */
225 if (last)
226 *dst = comp(*src, *dst, last);
227 }
228 } else {
229 /* Different alignment for source and dest */
230
231 int const left = -shift & (bits-1);
232 int const right = shift & (bits-1);
233
234 if ((unsigned long)dst_idx+1 >= n) {
235 /* Single destination word */
236 if (last)
237 first &= last;
238 if (shift < 0) {
239 /* Single source word */
240 *dst = comp(*src << left, *dst, first);
241 } else if (1+(unsigned long)src_idx >= n) {
242 /* Single source word */
243 *dst = comp(*src >> right, *dst, first);
244 } else {
245 /* 2 source words */
246 *dst = comp(*src >> right | *(src-1) << left,
247 *dst, first);
248 }
249 } else {
250 /* Multiple destination words */
251 /** We must always remember the last value read,
252 because in case SRC and DST overlap bitwise (e.g.
253 when moving just one pixel in 1bpp), we always
254 collect one full long for DST and that might
255 overlap with the current long from SRC. We store
256 this value in 'd0'. */
257 unsigned long d0, d1;
258 int m;
259
260 d0 = *src--;
261 /* Leading bits */
262 if (shift < 0) {
263 /* Single source word */
264 *dst = comp(d0 << left, *dst, first);
265 } else {
266 /* 2 source words */
267 d1 = *src--;
268 *dst = comp(d0 >> right | d1 << left, *dst,
269 first);
270 d0 = d1;
271 }
272 dst--;
273 n -= dst_idx+1;
274
275 /* Main chunk */
276 m = n % bits;
277 n /= bits;
278 while (n >= 4) {
279 d1 = *src--;
280 *dst-- = d0 >> right | d1 << left;
281 d0 = d1;
282 d1 = *src--;
283 *dst-- = d0 >> right | d1 << left;
284 d0 = d1;
285 d1 = *src--;
286 *dst-- = d0 >> right | d1 << left;
287 d0 = d1;
288 d1 = *src--;
289 *dst-- = d0 >> right | d1 << left;
290 d0 = d1;
291 n -= 4;
292 }
293 while (n--) {
294 d1 = *src--;
295 *dst-- = d0 >> right | d1 << left;
296 d0 = d1;
297 }
298
299 /* Trailing bits */
300 if (last) {
301 if (m <= left) {
302 /* Single source word */
303 *dst = comp(d0 >> right, *dst, last);
304 } else {
305 /* 2 source words */
306 d1 = *src;
307 *dst = comp(d0 >> right | d1 << left,
308 *dst, last);
309 }
310 }
311 }
312 }
313}
314
315void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
316{
317 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
318 u32 height = area->height, width = area->width;
319 unsigned long const bits_per_line = p->fix.line_length*8u;
320 unsigned long *dst = NULL, *src = NULL;
321 int bits = BITS_PER_LONG, bytes = bits >> 3;
322 int dst_idx = 0, src_idx = 0, rev_copy = 0;
323
324 if (p->state != FBINFO_STATE_RUNNING)
325 return;
326
327 /* if the beginning of the target area might overlap with the end of
328 the source area, be have to copy the area reverse. */
329 if ((dy == sy && dx > sx) || (dy > sy)) {
330 dy += height;
331 sy += height;
332 rev_copy = 1;
333 }
334
335 /* split the base of the framebuffer into a long-aligned address and
336 the index of the first bit */
337 dst = src = (unsigned long *)((unsigned long)p->screen_base &
338 ~(bytes-1));
339 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
340 /* add offset of source and target area */
341 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
342 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
343
344 if (p->fbops->fb_sync)
345 p->fbops->fb_sync(p);
346
347 if (rev_copy) {
348 while (height--) {
349 dst_idx -= bits_per_line;
350 src_idx -= bits_per_line;
351 dst += dst_idx >> (ffs(bits) - 1);
352 dst_idx &= (bytes - 1);
353 src += src_idx >> (ffs(bits) - 1);
354 src_idx &= (bytes - 1);
355 bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
356 width*p->var.bits_per_pixel);
357 }
358 } else {
359 while (height--) {
360 dst += dst_idx >> (ffs(bits) - 1);
361 dst_idx &= (bytes - 1);
362 src += src_idx >> (ffs(bits) - 1);
363 src_idx &= (bytes - 1);
364 bitcpy(p, dst, dst_idx, src, src_idx, bits,
365 width*p->var.bits_per_pixel);
366 dst_idx += bits_per_line;
367 src_idx += bits_per_line;
368 }
369 }
370}
371
372EXPORT_SYMBOL(sys_copyarea);
373
374MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
375MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
376MODULE_LICENSE("GPL");
377
diff --git a/drivers/video/fbdev/core/sysfillrect.c b/drivers/video/fbdev/core/sysfillrect.c
new file mode 100644
index 000000000000..33ee3d34f9d2
--- /dev/null
+++ b/drivers/video/fbdev/core/sysfillrect.c
@@ -0,0 +1,335 @@
1/*
2 * Generic fillrect for frame buffers in system RAM with packed pixels of
3 * any depth.
4 *
5 * Based almost entirely from cfbfillrect.c (which is based almost entirely
6 * on Geert Uytterhoeven's fillrect routine)
7 *
8 * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
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 for
12 * more details.
13 */
14#include <linux/module.h>
15#include <linux/string.h>
16#include <linux/fb.h>
17#include <asm/types.h>
18#include "fb_draw.h"
19
20 /*
21 * Aligned pattern fill using 32/64-bit memory accesses
22 */
23
24static void
25bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
26 unsigned long pat, unsigned n, int bits)
27{
28 unsigned long first, last;
29
30 if (!n)
31 return;
32
33 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
34 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
35
36 if (dst_idx+n <= bits) {
37 /* Single word */
38 if (last)
39 first &= last;
40 *dst = comp(pat, *dst, first);
41 } else {
42 /* Multiple destination words */
43
44 /* Leading bits */
45 if (first!= ~0UL) {
46 *dst = comp(pat, *dst, first);
47 dst++;
48 n -= bits - dst_idx;
49 }
50
51 /* Main chunk */
52 n /= bits;
53 while (n >= 8) {
54 *dst++ = pat;
55 *dst++ = pat;
56 *dst++ = pat;
57 *dst++ = pat;
58 *dst++ = pat;
59 *dst++ = pat;
60 *dst++ = pat;
61 *dst++ = pat;
62 n -= 8;
63 }
64 while (n--)
65 *dst++ = pat;
66 /* Trailing bits */
67 if (last)
68 *dst = comp(pat, *dst, last);
69 }
70}
71
72
73 /*
74 * Unaligned generic pattern fill using 32/64-bit memory accesses
75 * The pattern must have been expanded to a full 32/64-bit value
76 * Left/right are the appropriate shifts to convert to the pattern to be
77 * used for the next 32/64-bit word
78 */
79
80static void
81bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
82 unsigned long pat, int left, int right, unsigned n, int bits)
83{
84 unsigned long first, last;
85
86 if (!n)
87 return;
88
89 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
90 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
91
92 if (dst_idx+n <= bits) {
93 /* Single word */
94 if (last)
95 first &= last;
96 *dst = comp(pat, *dst, first);
97 } else {
98 /* Multiple destination words */
99 /* Leading bits */
100 if (first) {
101 *dst = comp(pat, *dst, first);
102 dst++;
103 pat = pat << left | pat >> right;
104 n -= bits - dst_idx;
105 }
106
107 /* Main chunk */
108 n /= bits;
109 while (n >= 4) {
110 *dst++ = pat;
111 pat = pat << left | pat >> right;
112 *dst++ = pat;
113 pat = pat << left | pat >> right;
114 *dst++ = pat;
115 pat = pat << left | pat >> right;
116 *dst++ = pat;
117 pat = pat << left | pat >> right;
118 n -= 4;
119 }
120 while (n--) {
121 *dst++ = pat;
122 pat = pat << left | pat >> right;
123 }
124
125 /* Trailing bits */
126 if (last)
127 *dst = comp(pat, *dst, last);
128 }
129}
130
131 /*
132 * Aligned pattern invert using 32/64-bit memory accesses
133 */
134static void
135bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
136 unsigned long pat, unsigned n, int bits)
137{
138 unsigned long val = pat;
139 unsigned long first, last;
140
141 if (!n)
142 return;
143
144 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
145 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
146
147 if (dst_idx+n <= bits) {
148 /* Single word */
149 if (last)
150 first &= last;
151 *dst = comp(*dst ^ val, *dst, first);
152 } else {
153 /* Multiple destination words */
154 /* Leading bits */
155 if (first!=0UL) {
156 *dst = comp(*dst ^ val, *dst, first);
157 dst++;
158 n -= bits - dst_idx;
159 }
160
161 /* Main chunk */
162 n /= bits;
163 while (n >= 8) {
164 *dst++ ^= val;
165 *dst++ ^= val;
166 *dst++ ^= val;
167 *dst++ ^= val;
168 *dst++ ^= val;
169 *dst++ ^= val;
170 *dst++ ^= val;
171 *dst++ ^= val;
172 n -= 8;
173 }
174 while (n--)
175 *dst++ ^= val;
176 /* Trailing bits */
177 if (last)
178 *dst = comp(*dst ^ val, *dst, last);
179 }
180}
181
182
183 /*
184 * Unaligned generic pattern invert using 32/64-bit memory accesses
185 * The pattern must have been expanded to a full 32/64-bit value
186 * Left/right are the appropriate shifts to convert to the pattern to be
187 * used for the next 32/64-bit word
188 */
189
190static void
191bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
192 unsigned long pat, int left, int right, unsigned n,
193 int bits)
194{
195 unsigned long first, last;
196
197 if (!n)
198 return;
199
200 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
201 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
202
203 if (dst_idx+n <= bits) {
204 /* Single word */
205 if (last)
206 first &= last;
207 *dst = comp(*dst ^ pat, *dst, first);
208 } else {
209 /* Multiple destination words */
210
211 /* Leading bits */
212 if (first != 0UL) {
213 *dst = comp(*dst ^ pat, *dst, first);
214 dst++;
215 pat = pat << left | pat >> right;
216 n -= bits - dst_idx;
217 }
218
219 /* Main chunk */
220 n /= bits;
221 while (n >= 4) {
222 *dst++ ^= pat;
223 pat = pat << left | pat >> right;
224 *dst++ ^= pat;
225 pat = pat << left | pat >> right;
226 *dst++ ^= pat;
227 pat = pat << left | pat >> right;
228 *dst++ ^= pat;
229 pat = pat << left | pat >> right;
230 n -= 4;
231 }
232 while (n--) {
233 *dst ^= pat;
234 pat = pat << left | pat >> right;
235 }
236
237 /* Trailing bits */
238 if (last)
239 *dst = comp(*dst ^ pat, *dst, last);
240 }
241}
242
243void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
244{
245 unsigned long pat, pat2, fg;
246 unsigned long width = rect->width, height = rect->height;
247 int bits = BITS_PER_LONG, bytes = bits >> 3;
248 u32 bpp = p->var.bits_per_pixel;
249 unsigned long *dst;
250 int dst_idx, left;
251
252 if (p->state != FBINFO_STATE_RUNNING)
253 return;
254
255 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
256 p->fix.visual == FB_VISUAL_DIRECTCOLOR )
257 fg = ((u32 *) (p->pseudo_palette))[rect->color];
258 else
259 fg = rect->color;
260
261 pat = pixel_to_pat( bpp, fg);
262
263 dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
264 dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
265 dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
266 /* FIXME For now we support 1-32 bpp only */
267 left = bits % bpp;
268 if (p->fbops->fb_sync)
269 p->fbops->fb_sync(p);
270 if (!left) {
271 void (*fill_op32)(struct fb_info *p, unsigned long *dst,
272 int dst_idx, unsigned long pat, unsigned n,
273 int bits) = NULL;
274
275 switch (rect->rop) {
276 case ROP_XOR:
277 fill_op32 = bitfill_aligned_rev;
278 break;
279 case ROP_COPY:
280 fill_op32 = bitfill_aligned;
281 break;
282 default:
283 printk( KERN_ERR "cfb_fillrect(): unknown rop, "
284 "defaulting to ROP_COPY\n");
285 fill_op32 = bitfill_aligned;
286 break;
287 }
288 while (height--) {
289 dst += dst_idx >> (ffs(bits) - 1);
290 dst_idx &= (bits - 1);
291 fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
292 dst_idx += p->fix.line_length*8;
293 }
294 } else {
295 int right, r;
296 void (*fill_op)(struct fb_info *p, unsigned long *dst,
297 int dst_idx, unsigned long pat, int left,
298 int right, unsigned n, int bits) = NULL;
299#ifdef __LITTLE_ENDIAN
300 right = left;
301 left = bpp - right;
302#else
303 right = bpp - left;
304#endif
305 switch (rect->rop) {
306 case ROP_XOR:
307 fill_op = bitfill_unaligned_rev;
308 break;
309 case ROP_COPY:
310 fill_op = bitfill_unaligned;
311 break;
312 default:
313 printk(KERN_ERR "sys_fillrect(): unknown rop, "
314 "defaulting to ROP_COPY\n");
315 fill_op = bitfill_unaligned;
316 break;
317 }
318 while (height--) {
319 dst += dst_idx / bits;
320 dst_idx &= (bits - 1);
321 r = dst_idx % bpp;
322 /* rotate pattern to the correct start position */
323 pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
324 fill_op(p, dst, dst_idx, pat2, left, right,
325 width*bpp, bits);
326 dst_idx += p->fix.line_length*8;
327 }
328 }
329}
330
331EXPORT_SYMBOL(sys_fillrect);
332
333MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
334MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
335MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/sysimgblt.c b/drivers/video/fbdev/core/sysimgblt.c
new file mode 100644
index 000000000000..a4d05b1b17d7
--- /dev/null
+++ b/drivers/video/fbdev/core/sysimgblt.c
@@ -0,0 +1,288 @@
1/*
2 * Generic 1-bit or 8-bit source to 1-32 bit destination expansion
3 * for frame buffer located in system RAM with packed pixels of any depth.
4 *
5 * Based almost entirely on cfbimgblt.c
6 *
7 * Copyright (C) April 2007 Antonino Daplas <adaplas@pol.net>
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#include <linux/module.h>
14#include <linux/string.h>
15#include <linux/fb.h>
16#include <asm/types.h>
17
18#define DEBUG
19
20#ifdef DEBUG
21#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
22#else
23#define DPRINTK(fmt, args...)
24#endif
25
26static const u32 cfb_tab8_be[] = {
27 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
28 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
29 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
30 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
31};
32
33static const u32 cfb_tab8_le[] = {
34 0x00000000,0xff000000,0x00ff0000,0xffff0000,
35 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
36 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
37 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
38};
39
40static const u32 cfb_tab16_be[] = {
41 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
42};
43
44static const u32 cfb_tab16_le[] = {
45 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
46};
47
48static const u32 cfb_tab32[] = {
49 0x00000000, 0xffffffff
50};
51
52static void color_imageblit(const struct fb_image *image, struct fb_info *p,
53 void *dst1, u32 start_index, u32 pitch_index)
54{
55 /* Draw the penguin */
56 u32 *dst, *dst2;
57 u32 color = 0, val, shift;
58 int i, n, bpp = p->var.bits_per_pixel;
59 u32 null_bits = 32 - bpp;
60 u32 *palette = (u32 *) p->pseudo_palette;
61 const u8 *src = image->data;
62
63 dst2 = dst1;
64 for (i = image->height; i--; ) {
65 n = image->width;
66 dst = dst1;
67 shift = 0;
68 val = 0;
69
70 if (start_index) {
71 u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
72 start_index));
73 val = *dst & start_mask;
74 shift = start_index;
75 }
76 while (n--) {
77 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
78 p->fix.visual == FB_VISUAL_DIRECTCOLOR )
79 color = palette[*src];
80 else
81 color = *src;
82 color <<= FB_LEFT_POS(p, bpp);
83 val |= FB_SHIFT_HIGH(p, color, shift);
84 if (shift >= null_bits) {
85 *dst++ = val;
86
87 val = (shift == null_bits) ? 0 :
88 FB_SHIFT_LOW(p, color, 32 - shift);
89 }
90 shift += bpp;
91 shift &= (32 - 1);
92 src++;
93 }
94 if (shift) {
95 u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
96
97 *dst &= end_mask;
98 *dst |= val;
99 }
100 dst1 += p->fix.line_length;
101 if (pitch_index) {
102 dst2 += p->fix.line_length;
103 dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
104
105 start_index += pitch_index;
106 start_index &= 32 - 1;
107 }
108 }
109}
110
111static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
112 void *dst1, u32 fgcolor, u32 bgcolor,
113 u32 start_index, u32 pitch_index)
114{
115 u32 shift, color = 0, bpp = p->var.bits_per_pixel;
116 u32 *dst, *dst2;
117 u32 val, pitch = p->fix.line_length;
118 u32 null_bits = 32 - bpp;
119 u32 spitch = (image->width+7)/8;
120 const u8 *src = image->data, *s;
121 u32 i, j, l;
122
123 dst2 = dst1;
124 fgcolor <<= FB_LEFT_POS(p, bpp);
125 bgcolor <<= FB_LEFT_POS(p, bpp);
126
127 for (i = image->height; i--; ) {
128 shift = val = 0;
129 l = 8;
130 j = image->width;
131 dst = dst1;
132 s = src;
133
134 /* write leading bits */
135 if (start_index) {
136 u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
137 start_index));
138 val = *dst & start_mask;
139 shift = start_index;
140 }
141
142 while (j--) {
143 l--;
144 color = (*s & (1 << l)) ? fgcolor : bgcolor;
145 val |= FB_SHIFT_HIGH(p, color, shift);
146
147 /* Did the bitshift spill bits to the next long? */
148 if (shift >= null_bits) {
149 *dst++ = val;
150 val = (shift == null_bits) ? 0 :
151 FB_SHIFT_LOW(p, color, 32 - shift);
152 }
153 shift += bpp;
154 shift &= (32 - 1);
155 if (!l) { l = 8; s++; }
156 }
157
158 /* write trailing bits */
159 if (shift) {
160 u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
161
162 *dst &= end_mask;
163 *dst |= val;
164 }
165
166 dst1 += pitch;
167 src += spitch;
168 if (pitch_index) {
169 dst2 += pitch;
170 dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
171 start_index += pitch_index;
172 start_index &= 32 - 1;
173 }
174
175 }
176}
177
178/*
179 * fast_imageblit - optimized monochrome color expansion
180 *
181 * Only if: bits_per_pixel == 8, 16, or 32
182 * image->width is divisible by pixel/dword (ppw);
183 * fix->line_legth is divisible by 4;
184 * beginning and end of a scanline is dword aligned
185 */
186static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
187 void *dst1, u32 fgcolor, u32 bgcolor)
188{
189 u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
190 u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
191 u32 bit_mask, end_mask, eorx, shift;
192 const char *s = image->data, *src;
193 u32 *dst;
194 const u32 *tab = NULL;
195 int i, j, k;
196
197 switch (bpp) {
198 case 8:
199 tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
200 break;
201 case 16:
202 tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
203 break;
204 case 32:
205 default:
206 tab = cfb_tab32;
207 break;
208 }
209
210 for (i = ppw-1; i--; ) {
211 fgx <<= bpp;
212 bgx <<= bpp;
213 fgx |= fgcolor;
214 bgx |= bgcolor;
215 }
216
217 bit_mask = (1 << ppw) - 1;
218 eorx = fgx ^ bgx;
219 k = image->width/ppw;
220
221 for (i = image->height; i--; ) {
222 dst = dst1;
223 shift = 8;
224 src = s;
225
226 for (j = k; j--; ) {
227 shift -= ppw;
228 end_mask = tab[(*src >> shift) & bit_mask];
229 *dst++ = (end_mask & eorx) ^ bgx;
230 if (!shift) {
231 shift = 8;
232 src++;
233 }
234 }
235 dst1 += p->fix.line_length;
236 s += spitch;
237 }
238}
239
240void sys_imageblit(struct fb_info *p, const struct fb_image *image)
241{
242 u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
243 u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
244 u32 width = image->width;
245 u32 dx = image->dx, dy = image->dy;
246 void *dst1;
247
248 if (p->state != FBINFO_STATE_RUNNING)
249 return;
250
251 bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
252 start_index = bitstart & (32 - 1);
253 pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
254
255 bitstart /= 8;
256 bitstart &= ~(bpl - 1);
257 dst1 = (void __force *)p->screen_base + bitstart;
258
259 if (p->fbops->fb_sync)
260 p->fbops->fb_sync(p);
261
262 if (image->depth == 1) {
263 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
264 p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
265 fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
266 bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
267 } else {
268 fgcolor = image->fg_color;
269 bgcolor = image->bg_color;
270 }
271
272 if (32 % bpp == 0 && !start_index && !pitch_index &&
273 ((width & (32/bpp-1)) == 0) &&
274 bpp >= 8 && bpp <= 32)
275 fast_imageblit(image, p, dst1, fgcolor, bgcolor);
276 else
277 slow_imageblit(image, p, dst1, fgcolor, bgcolor,
278 start_index, pitch_index);
279 } else
280 color_imageblit(image, p, dst1, start_index, pitch_index);
281}
282
283EXPORT_SYMBOL(sys_imageblit);
284
285MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
286MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)");
287MODULE_LICENSE("GPL");
288