</para>
<sect1><title>Frame Buffer Memory</title>
-!Edrivers/video/fbdev/fbmem.c
+!Edrivers/video/fbdev/core/fbmem.c
</sect1>
<!--
<sect1><title>Frame Buffer Console</title>
</sect1>
-->
<sect1><title>Frame Buffer Colormap</title>
-!Edrivers/video/fbdev/fbcmap.c
+!Edrivers/video/fbdev/core/fbcmap.c
</sect1>
<!-- FIXME:
drivers/video/fbgen.c has no docs, which stuffs up the sgml. Comment
</sect1>
KAO -->
<sect1><title>Frame Buffer Video Mode Database</title>
-!Idrivers/video/fbdev/modedb.c
-!Edrivers/video/fbdev/modedb.c
+!Idrivers/video/fbdev/core/modedb.c
+!Edrivers/video/fbdev/core/modedb.c
</sect1>
<sect1><title>Frame Buffer Macintosh Video Mode Database</title>
!Edrivers/video/fbdev/macmodes.c
# Each configuration option enables a list of files.
-obj-y += fb_notify.o
-obj-$(CONFIG_FB) += fb.o
-fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
- modedb.o fbcvt.o
-fb-objs := $(fb-y)
+obj-y += core/
obj-$(CONFIG_EXYNOS_VIDEO) += exynos/
-obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
-obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
-obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
-obj-$(CONFIG_FB_SYS_FILLRECT) += sysfillrect.o
-obj-$(CONFIG_FB_SYS_COPYAREA) += syscopyarea.o
-obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
-obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o
-obj-$(CONFIG_FB_SVGALIB) += svgalib.o
obj-$(CONFIG_FB_MACMODES) += macmodes.o
-obj-$(CONFIG_FB_DDC) += fb_ddc.o
-obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o
# Hardware specific drivers go first
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
-#include "../fb_draw.h"
+#include "../core/fb_draw.h"
#include <asm/io.h>
+++ /dev/null
-/*
- * Generic function for frame buffer with packed pixels of any depth.
- *
- * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- * NOTES:
- *
- * This is for cfb packed pixels. Iplan and such are incorporated in the
- * drivers that need them.
- *
- * FIXME
- *
- * Also need to add code to deal with cards endians that are different than
- * the native cpu endians. I also need to deal with MSB position in the word.
- *
- * The two functions or copying forward and backward could be split up like
- * the ones for filling, i.e. in aligned and unaligned versions. This would
- * help moving some redundant computations and branches out of the loop, too.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-#include <asm/io.h>
-#include "fb_draw.h"
-
-#if BITS_PER_LONG == 32
-# define FB_WRITEL fb_writel
-# define FB_READL fb_readl
-#else
-# define FB_WRITEL fb_writeq
-# define FB_READL fb_readq
-#endif
-
- /*
- * Generic bitwise copy algorithm
- */
-
-static void
-bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
- const unsigned long __iomem *src, unsigned src_idx, int bits,
- unsigned n, u32 bswapmask)
-{
- unsigned long first, last;
- int const shift = dst_idx-src_idx;
-
-#if 0
- /*
- * If you suspect bug in this function, compare it with this simple
- * memmove implementation.
- */
- fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
- (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
- return;
-#endif
-
- first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
- last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
- if (!shift) {
- // Same alignment for source and dest
-
- if (dst_idx+n <= bits) {
- // Single word
- if (last)
- first &= last;
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
- } else {
- // Multiple destination words
-
- // Leading bits
- if (first != ~0UL) {
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
- dst++;
- src++;
- n -= bits - dst_idx;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 8) {
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- FB_WRITEL(FB_READL(src++), dst++);
- n -= 8;
- }
- while (n--)
- FB_WRITEL(FB_READL(src++), dst++);
-
- // Trailing bits
- if (last)
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
- }
- } else {
- /* Different alignment for source and dest */
- unsigned long d0, d1;
- int m;
-
- int const left = shift & (bits - 1);
- int const right = -shift & (bits - 1);
-
- if (dst_idx+n <= bits) {
- // Single destination word
- if (last)
- first &= last;
- d0 = FB_READL(src);
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- if (shift > 0) {
- // Single source word
- d0 <<= left;
- } else if (src_idx+n <= bits) {
- // Single source word
- d0 >>= right;
- } else {
- // 2 source words
- d1 = FB_READL(src + 1);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 >> right | d1 << left;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
- } else {
- // Multiple destination words
- /** We must always remember the last value read, because in case
- SRC and DST overlap bitwise (e.g. when moving just one pixel in
- 1bpp), we always collect one full long for DST and that might
- overlap with the current long from SRC. We store this value in
- 'd0'. */
- d0 = FB_READL(src++);
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- // Leading bits
- if (shift > 0) {
- // Single source word
- d1 = d0;
- d0 <<= left;
- n -= bits - dst_idx;
- } else {
- // 2 source words
- d1 = FB_READL(src++);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
-
- d0 = d0 >> right | d1 << left;
- n -= bits - dst_idx;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
- d0 = d1;
- dst++;
-
- // Main chunk
- m = n % bits;
- n /= bits;
- while ((n >= 4) && !bswapmask) {
- d1 = FB_READL(src++);
- FB_WRITEL(d0 >> right | d1 << left, dst++);
- d0 = d1;
- d1 = FB_READL(src++);
- FB_WRITEL(d0 >> right | d1 << left, dst++);
- d0 = d1;
- d1 = FB_READL(src++);
- FB_WRITEL(d0 >> right | d1 << left, dst++);
- d0 = d1;
- d1 = FB_READL(src++);
- FB_WRITEL(d0 >> right | d1 << left, dst++);
- d0 = d1;
- n -= 4;
- }
- while (n--) {
- d1 = FB_READL(src++);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 >> right | d1 << left;
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(d0, dst++);
- d0 = d1;
- }
-
- // Trailing bits
- if (m) {
- if (m <= bits - right) {
- // Single source word
- d0 >>= right;
- } else {
- // 2 source words
- d1 = FB_READL(src);
- d1 = fb_rev_pixels_in_long(d1,
- bswapmask);
- d0 = d0 >> right | d1 << left;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
- }
- }
- }
-}
-
- /*
- * Generic bitwise copy algorithm, operating backward
- */
-
-static void
-bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
- const unsigned long __iomem *src, unsigned src_idx, int bits,
- unsigned n, u32 bswapmask)
-{
- unsigned long first, last;
- int shift;
-
-#if 0
- /*
- * If you suspect bug in this function, compare it with this simple
- * memmove implementation.
- */
- fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
- (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
- return;
-#endif
-
- dst += (dst_idx + n - 1) / bits;
- src += (src_idx + n - 1) / bits;
- dst_idx = (dst_idx + n - 1) % bits;
- src_idx = (src_idx + n - 1) % bits;
-
- shift = dst_idx-src_idx;
-
- first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
- last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
-
- if (!shift) {
- // Same alignment for source and dest
-
- if ((unsigned long)dst_idx+1 >= n) {
- // Single word
- if (first)
- last &= first;
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
- } else {
- // Multiple destination words
-
- // Leading bits
- if (first) {
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
- dst--;
- src--;
- n -= dst_idx+1;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 8) {
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- FB_WRITEL(FB_READL(src--), dst--);
- n -= 8;
- }
- while (n--)
- FB_WRITEL(FB_READL(src--), dst--);
-
- // Trailing bits
- if (last != -1UL)
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
- }
- } else {
- // Different alignment for source and dest
- unsigned long d0, d1;
- int m;
-
- int const left = shift & (bits-1);
- int const right = -shift & (bits-1);
-
- if ((unsigned long)dst_idx+1 >= n) {
- // Single destination word
- if (first)
- last &= first;
- d0 = FB_READL(src);
- if (shift < 0) {
- // Single source word
- d0 >>= right;
- } else if (1+(unsigned long)src_idx >= n) {
- // Single source word
- d0 <<= left;
- } else {
- // 2 source words
- d1 = FB_READL(src - 1);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 << left | d1 >> right;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
- } else {
- // Multiple destination words
- /** We must always remember the last value read, because in case
- SRC and DST overlap bitwise (e.g. when moving just one pixel in
- 1bpp), we always collect one full long for DST and that might
- overlap with the current long from SRC. We store this value in
- 'd0'. */
-
- d0 = FB_READL(src--);
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- // Leading bits
- if (shift < 0) {
- // Single source word
- d1 = d0;
- d0 >>= right;
- } else {
- // 2 source words
- d1 = FB_READL(src--);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 << left | d1 >> right;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
- d0 = d1;
- dst--;
- n -= dst_idx+1;
-
- // Main chunk
- m = n % bits;
- n /= bits;
- while ((n >= 4) && !bswapmask) {
- d1 = FB_READL(src--);
- FB_WRITEL(d0 << left | d1 >> right, dst--);
- d0 = d1;
- d1 = FB_READL(src--);
- FB_WRITEL(d0 << left | d1 >> right, dst--);
- d0 = d1;
- d1 = FB_READL(src--);
- FB_WRITEL(d0 << left | d1 >> right, dst--);
- d0 = d1;
- d1 = FB_READL(src--);
- FB_WRITEL(d0 << left | d1 >> right, dst--);
- d0 = d1;
- n -= 4;
- }
- while (n--) {
- d1 = FB_READL(src--);
- d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 << left | d1 >> right;
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(d0, dst--);
- d0 = d1;
- }
-
- // Trailing bits
- if (m) {
- if (m <= bits - left) {
- // Single source word
- d0 <<= left;
- } else {
- // 2 source words
- d1 = FB_READL(src);
- d1 = fb_rev_pixels_in_long(d1,
- bswapmask);
- d0 = d0 << left | d1 >> right;
- }
- d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
- }
- }
- }
-}
-
-void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
-{
- u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
- u32 height = area->height, width = area->width;
- unsigned long const bits_per_line = p->fix.line_length*8u;
- unsigned long __iomem *base = NULL;
- int bits = BITS_PER_LONG, bytes = bits >> 3;
- unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
- u32 bswapmask = fb_compute_bswapmask(p);
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- /* if the beginning of the target area might overlap with the end of
- the source area, be have to copy the area reverse. */
- if ((dy == sy && dx > sx) || (dy > sy)) {
- dy += height;
- sy += height;
- rev_copy = 1;
- }
-
- // split the base of the framebuffer into a long-aligned address and the
- // index of the first bit
- base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
- dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
- // add offset of source and target area
- dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
- src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
-
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
-
- if (rev_copy) {
- while (height--) {
- dst_idx -= bits_per_line;
- src_idx -= bits_per_line;
- bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
- base + (src_idx / bits), src_idx % bits, bits,
- width*p->var.bits_per_pixel, bswapmask);
- }
- } else {
- while (height--) {
- bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
- base + (src_idx / bits), src_idx % bits, bits,
- width*p->var.bits_per_pixel, bswapmask);
- dst_idx += bits_per_line;
- src_idx += bits_per_line;
- }
- }
-}
-
-EXPORT_SYMBOL(cfb_copyarea);
-
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated copyarea");
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-/*
- * Generic fillrect for frame buffers with packed pixels of any depth.
- *
- * Copyright (C) 2000 James Simmons (jsimmons@linux-fbdev.org)
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- * NOTES:
- *
- * Also need to add code to deal with cards endians that are different than
- * the native cpu endians. I also need to deal with MSB position in the word.
- *
- */
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-#include "fb_draw.h"
-
-#if BITS_PER_LONG == 32
-# define FB_WRITEL fb_writel
-# define FB_READL fb_readl
-#else
-# define FB_WRITEL fb_writeq
-# define FB_READL fb_readq
-#endif
-
- /*
- * Aligned pattern fill using 32/64-bit memory accesses
- */
-
-static void
-bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- unsigned long pat, unsigned n, int bits, u32 bswapmask)
-{
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
- last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
- if (dst_idx+n <= bits) {
- // Single word
- if (last)
- first &= last;
- FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
- } else {
- // Multiple destination words
-
- // Leading bits
- if (first!= ~0UL) {
- FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
- dst++;
- n -= bits - dst_idx;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 8) {
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- FB_WRITEL(pat, dst++);
- n -= 8;
- }
- while (n--)
- FB_WRITEL(pat, dst++);
-
- // Trailing bits
- if (last)
- FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
- }
-}
-
-
- /*
- * Unaligned generic pattern fill using 32/64-bit memory accesses
- * The pattern must have been expanded to a full 32/64-bit value
- * Left/right are the appropriate shifts to convert to the pattern to be
- * used for the next 32/64-bit word
- */
-
-static void
-bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- unsigned long pat, int left, int right, unsigned n, int bits)
-{
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- // Single word
- if (last)
- first &= last;
- FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
- } else {
- // Multiple destination words
- // Leading bits
- if (first) {
- FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
- dst++;
- pat = pat << left | pat >> right;
- n -= bits - dst_idx;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 4) {
- FB_WRITEL(pat, dst++);
- pat = pat << left | pat >> right;
- FB_WRITEL(pat, dst++);
- pat = pat << left | pat >> right;
- FB_WRITEL(pat, dst++);
- pat = pat << left | pat >> right;
- FB_WRITEL(pat, dst++);
- pat = pat << left | pat >> right;
- n -= 4;
- }
- while (n--) {
- FB_WRITEL(pat, dst++);
- pat = pat << left | pat >> right;
- }
-
- // Trailing bits
- if (last)
- FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
- }
-}
-
- /*
- * Aligned pattern invert using 32/64-bit memory accesses
- */
-static void
-bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst,
- int dst_idx, unsigned long pat, unsigned n, int bits,
- u32 bswapmask)
-{
- unsigned long val = pat, dat;
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
- last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
-
- if (dst_idx+n <= bits) {
- // Single word
- if (last)
- first &= last;
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ val, dat, first), dst);
- } else {
- // Multiple destination words
- // Leading bits
- if (first!=0UL) {
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ val, dat, first), dst);
- dst++;
- n -= bits - dst_idx;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 8) {
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- n -= 8;
- }
- while (n--) {
- FB_WRITEL(FB_READL(dst) ^ val, dst);
- dst++;
- }
- // Trailing bits
- if (last) {
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ val, dat, last), dst);
- }
- }
-}
-
-
- /*
- * Unaligned generic pattern invert using 32/64-bit memory accesses
- * The pattern must have been expanded to a full 32/64-bit value
- * Left/right are the appropriate shifts to convert to the pattern to be
- * used for the next 32/64-bit word
- */
-
-static void
-bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst,
- int dst_idx, unsigned long pat, int left, int right,
- unsigned n, int bits)
-{
- unsigned long first, last, dat;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- // Single word
- if (last)
- first &= last;
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ pat, dat, first), dst);
- } else {
- // Multiple destination words
-
- // Leading bits
- if (first != 0UL) {
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ pat, dat, first), dst);
- dst++;
- pat = pat << left | pat >> right;
- n -= bits - dst_idx;
- }
-
- // Main chunk
- n /= bits;
- while (n >= 4) {
- FB_WRITEL(FB_READL(dst) ^ pat, dst);
- dst++;
- pat = pat << left | pat >> right;
- FB_WRITEL(FB_READL(dst) ^ pat, dst);
- dst++;
- pat = pat << left | pat >> right;
- FB_WRITEL(FB_READL(dst) ^ pat, dst);
- dst++;
- pat = pat << left | pat >> right;
- FB_WRITEL(FB_READL(dst) ^ pat, dst);
- dst++;
- pat = pat << left | pat >> right;
- n -= 4;
- }
- while (n--) {
- FB_WRITEL(FB_READL(dst) ^ pat, dst);
- dst++;
- pat = pat << left | pat >> right;
- }
-
- // Trailing bits
- if (last) {
- dat = FB_READL(dst);
- FB_WRITEL(comp(dat ^ pat, dat, last), dst);
- }
- }
-}
-
-void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
-{
- unsigned long pat, pat2, fg;
- unsigned long width = rect->width, height = rect->height;
- int bits = BITS_PER_LONG, bytes = bits >> 3;
- u32 bpp = p->var.bits_per_pixel;
- unsigned long __iomem *dst;
- int dst_idx, left;
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR )
- fg = ((u32 *) (p->pseudo_palette))[rect->color];
- else
- fg = rect->color;
-
- pat = pixel_to_pat(bpp, fg);
-
- dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
- dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
- dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
- /* FIXME For now we support 1-32 bpp only */
- left = bits % bpp;
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
- if (!left) {
- u32 bswapmask = fb_compute_bswapmask(p);
- void (*fill_op32)(struct fb_info *p,
- unsigned long __iomem *dst, int dst_idx,
- unsigned long pat, unsigned n, int bits,
- u32 bswapmask) = NULL;
-
- switch (rect->rop) {
- case ROP_XOR:
- fill_op32 = bitfill_aligned_rev;
- break;
- case ROP_COPY:
- fill_op32 = bitfill_aligned;
- break;
- default:
- printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
- fill_op32 = bitfill_aligned;
- break;
- }
- while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bits - 1);
- fill_op32(p, dst, dst_idx, pat, width*bpp, bits,
- bswapmask);
- dst_idx += p->fix.line_length*8;
- }
- } else {
- int right, r;
- void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,
- int dst_idx, unsigned long pat, int left,
- int right, unsigned n, int bits) = NULL;
-#ifdef __LITTLE_ENDIAN
- right = left;
- left = bpp - right;
-#else
- right = bpp - left;
-#endif
- switch (rect->rop) {
- case ROP_XOR:
- fill_op = bitfill_unaligned_rev;
- break;
- case ROP_COPY:
- fill_op = bitfill_unaligned;
- break;
- default:
- printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
- fill_op = bitfill_unaligned;
- break;
- }
- while (height--) {
- dst += dst_idx / bits;
- dst_idx &= (bits - 1);
- r = dst_idx % bpp;
- /* rotate pattern to the correct start position */
- pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
- fill_op(p, dst, dst_idx, pat2, left, right,
- width*bpp, bits);
- dst_idx += p->fix.line_length*8;
- }
- }
-}
-
-EXPORT_SYMBOL(cfb_fillrect);
-
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Generic BitBLT function for frame buffer with packed pixels of any depth.
- *
- * Copyright (C) June 1999 James Simmons
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- * NOTES:
- *
- * This function copys a image from system memory to video memory. The
- * image can be a bitmap where each 0 represents the background color and
- * each 1 represents the foreground color. Great for font handling. It can
- * also be a color image. This is determined by image_depth. The color image
- * must be laid out exactly in the same format as the framebuffer. Yes I know
- * their are cards with hardware that coverts images of various depths to the
- * framebuffer depth. But not every card has this. All images must be rounded
- * up to the nearest byte. For example a bitmap 12 bits wide must be two
- * bytes width.
- *
- * Tony:
- * Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds
- * up the code significantly.
- *
- * Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
- * still processed a bit at a time.
- *
- * Also need to add code to deal with cards endians that are different than
- * the native cpu endians. I also need to deal with MSB position in the word.
- */
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-#include "fb_draw.h"
-
-#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-static const u32 cfb_tab8_be[] = {
- 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
- 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
- 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
- 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
-};
-
-static const u32 cfb_tab8_le[] = {
- 0x00000000,0xff000000,0x00ff0000,0xffff0000,
- 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
- 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
- 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
-};
-
-static const u32 cfb_tab16_be[] = {
- 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
-};
-
-static const u32 cfb_tab16_le[] = {
- 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
-};
-
-static const u32 cfb_tab32[] = {
- 0x00000000, 0xffffffff
-};
-
-#define FB_WRITEL fb_writel
-#define FB_READL fb_readl
-
-static inline void color_imageblit(const struct fb_image *image,
- struct fb_info *p, u8 __iomem *dst1,
- u32 start_index,
- u32 pitch_index)
-{
- /* Draw the penguin */
- u32 __iomem *dst, *dst2;
- u32 color = 0, val, shift;
- int i, n, bpp = p->var.bits_per_pixel;
- u32 null_bits = 32 - bpp;
- u32 *palette = (u32 *) p->pseudo_palette;
- const u8 *src = image->data;
- u32 bswapmask = fb_compute_bswapmask(p);
-
- dst2 = (u32 __iomem *) dst1;
- for (i = image->height; i--; ) {
- n = image->width;
- dst = (u32 __iomem *) dst1;
- shift = 0;
- val = 0;
-
- if (start_index) {
- u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
- start_index, bswapmask);
- val = FB_READL(dst) & start_mask;
- shift = start_index;
- }
- while (n--) {
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR )
- color = palette[*src];
- else
- color = *src;
- color <<= FB_LEFT_POS(p, bpp);
- val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
- if (shift >= null_bits) {
- FB_WRITEL(val, dst++);
-
- val = (shift == null_bits) ? 0 :
- FB_SHIFT_LOW(p, color, 32 - shift);
- }
- shift += bpp;
- shift &= (32 - 1);
- src++;
- }
- if (shift) {
- u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
- bswapmask);
-
- FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
- }
- dst1 += p->fix.line_length;
- if (pitch_index) {
- dst2 += p->fix.line_length;
- dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
-
- start_index += pitch_index;
- start_index &= 32 - 1;
- }
- }
-}
-
-static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
- u8 __iomem *dst1, u32 fgcolor,
- u32 bgcolor,
- u32 start_index,
- u32 pitch_index)
-{
- u32 shift, color = 0, bpp = p->var.bits_per_pixel;
- u32 __iomem *dst, *dst2;
- u32 val, pitch = p->fix.line_length;
- u32 null_bits = 32 - bpp;
- u32 spitch = (image->width+7)/8;
- const u8 *src = image->data, *s;
- u32 i, j, l;
- u32 bswapmask = fb_compute_bswapmask(p);
-
- dst2 = (u32 __iomem *) dst1;
- fgcolor <<= FB_LEFT_POS(p, bpp);
- bgcolor <<= FB_LEFT_POS(p, bpp);
-
- for (i = image->height; i--; ) {
- shift = val = 0;
- l = 8;
- j = image->width;
- dst = (u32 __iomem *) dst1;
- s = src;
-
- /* write leading bits */
- if (start_index) {
- u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
- start_index, bswapmask);
- val = FB_READL(dst) & start_mask;
- shift = start_index;
- }
-
- while (j--) {
- l--;
- color = (*s & (1 << l)) ? fgcolor : bgcolor;
- val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
-
- /* Did the bitshift spill bits to the next long? */
- if (shift >= null_bits) {
- FB_WRITEL(val, dst++);
- val = (shift == null_bits) ? 0 :
- FB_SHIFT_LOW(p, color, 32 - shift);
- }
- shift += bpp;
- shift &= (32 - 1);
- if (!l) { l = 8; s++; }
- }
-
- /* write trailing bits */
- if (shift) {
- u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
- bswapmask);
-
- FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
- }
-
- dst1 += pitch;
- src += spitch;
- if (pitch_index) {
- dst2 += pitch;
- dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
- start_index += pitch_index;
- start_index &= 32 - 1;
- }
-
- }
-}
-
-/*
- * fast_imageblit - optimized monochrome color expansion
- *
- * Only if: bits_per_pixel == 8, 16, or 32
- * image->width is divisible by pixel/dword (ppw);
- * fix->line_legth is divisible by 4;
- * beginning and end of a scanline is dword aligned
- */
-static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
- u8 __iomem *dst1, u32 fgcolor,
- u32 bgcolor)
-{
- u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
- u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
- u32 bit_mask, end_mask, eorx, shift;
- const char *s = image->data, *src;
- u32 __iomem *dst;
- const u32 *tab = NULL;
- int i, j, k;
-
- switch (bpp) {
- case 8:
- tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
- break;
- case 16:
- tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
- break;
- case 32:
- default:
- tab = cfb_tab32;
- break;
- }
-
- for (i = ppw-1; i--; ) {
- fgx <<= bpp;
- bgx <<= bpp;
- fgx |= fgcolor;
- bgx |= bgcolor;
- }
-
- bit_mask = (1 << ppw) - 1;
- eorx = fgx ^ bgx;
- k = image->width/ppw;
-
- for (i = image->height; i--; ) {
- dst = (u32 __iomem *) dst1, shift = 8; src = s;
-
- for (j = k; j--; ) {
- shift -= ppw;
- end_mask = tab[(*src >> shift) & bit_mask];
- FB_WRITEL((end_mask & eorx)^bgx, dst++);
- if (!shift) { shift = 8; src++; }
- }
- dst1 += p->fix.line_length;
- s += spitch;
- }
-}
-
-void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
-{
- u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
- u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
- u32 width = image->width;
- u32 dx = image->dx, dy = image->dy;
- u8 __iomem *dst1;
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
- start_index = bitstart & (32 - 1);
- pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
-
- bitstart /= 8;
- bitstart &= ~(bpl - 1);
- dst1 = p->screen_base + bitstart;
-
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
-
- if (image->depth == 1) {
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
- fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
- bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
- } else {
- fgcolor = image->fg_color;
- bgcolor = image->bg_color;
- }
-
- if (32 % bpp == 0 && !start_index && !pitch_index &&
- ((width & (32/bpp-1)) == 0) &&
- bpp >= 8 && bpp <= 32)
- fast_imageblit(image, p, dst1, fgcolor, bgcolor);
- else
- slow_imageblit(image, p, dst1, fgcolor, bgcolor,
- start_index, pitch_index);
- } else
- color_imageblit(image, p, dst1, start_index, pitch_index);
-}
-
-EXPORT_SYMBOL(cfb_imageblit);
-
-MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
-MODULE_DESCRIPTION("Generic software accelerated imaging drawing");
-MODULE_LICENSE("GPL");
-
--- /dev/null
+obj-y += fb_notify.o
+obj-$(CONFIG_FB) += fb.o
+fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
+ modedb.o fbcvt.o
+fb-objs := $(fb-y)
+
+obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
+obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
+obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
+obj-$(CONFIG_FB_SYS_FILLRECT) += sysfillrect.o
+obj-$(CONFIG_FB_SYS_COPYAREA) += syscopyarea.o
+obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
+obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o
+obj-$(CONFIG_FB_SVGALIB) += svgalib.o
+obj-$(CONFIG_FB_DDC) += fb_ddc.o
+obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
--- /dev/null
+/*
+ * Generic function for frame buffer with packed pixels of any depth.
+ *
+ * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * NOTES:
+ *
+ * This is for cfb packed pixels. Iplan and such are incorporated in the
+ * drivers that need them.
+ *
+ * FIXME
+ *
+ * Also need to add code to deal with cards endians that are different than
+ * the native cpu endians. I also need to deal with MSB position in the word.
+ *
+ * The two functions or copying forward and backward could be split up like
+ * the ones for filling, i.e. in aligned and unaligned versions. This would
+ * help moving some redundant computations and branches out of the loop, too.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include "fb_draw.h"
+
+#if BITS_PER_LONG == 32
+# define FB_WRITEL fb_writel
+# define FB_READL fb_readl
+#else
+# define FB_WRITEL fb_writeq
+# define FB_READL fb_readq
+#endif
+
+ /*
+ * Generic bitwise copy algorithm
+ */
+
+static void
+bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
+ unsigned n, u32 bswapmask)
+{
+ unsigned long first, last;
+ int const shift = dst_idx-src_idx;
+
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
+
+ first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
+ last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
+
+ if (!shift) {
+ // Same alignment for source and dest
+
+ if (dst_idx+n <= bits) {
+ // Single word
+ if (last)
+ first &= last;
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
+ } else {
+ // Multiple destination words
+
+ // Leading bits
+ if (first != ~0UL) {
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
+ dst++;
+ src++;
+ n -= bits - dst_idx;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 8) {
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ FB_WRITEL(FB_READL(src++), dst++);
+ n -= 8;
+ }
+ while (n--)
+ FB_WRITEL(FB_READL(src++), dst++);
+
+ // Trailing bits
+ if (last)
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
+ }
+ } else {
+ /* Different alignment for source and dest */
+ unsigned long d0, d1;
+ int m;
+
+ int const left = shift & (bits - 1);
+ int const right = -shift & (bits - 1);
+
+ if (dst_idx+n <= bits) {
+ // Single destination word
+ if (last)
+ first &= last;
+ d0 = FB_READL(src);
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ if (shift > 0) {
+ // Single source word
+ d0 <<= left;
+ } else if (src_idx+n <= bits) {
+ // Single source word
+ d0 >>= right;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src + 1);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+ d0 = d0 >> right | d1 << left;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+ } else {
+ // Multiple destination words
+ /** We must always remember the last value read, because in case
+ SRC and DST overlap bitwise (e.g. when moving just one pixel in
+ 1bpp), we always collect one full long for DST and that might
+ overlap with the current long from SRC. We store this value in
+ 'd0'. */
+ d0 = FB_READL(src++);
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ // Leading bits
+ if (shift > 0) {
+ // Single source word
+ d1 = d0;
+ d0 <<= left;
+ n -= bits - dst_idx;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src++);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+
+ d0 = d0 >> right | d1 << left;
+ n -= bits - dst_idx;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+ d0 = d1;
+ dst++;
+
+ // Main chunk
+ m = n % bits;
+ n /= bits;
+ while ((n >= 4) && !bswapmask) {
+ d1 = FB_READL(src++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
+ d0 = d1;
+ d1 = FB_READL(src++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
+ d0 = d1;
+ d1 = FB_READL(src++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
+ d0 = d1;
+ d1 = FB_READL(src++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
+ d0 = d1;
+ n -= 4;
+ }
+ while (n--) {
+ d1 = FB_READL(src++);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+ d0 = d0 >> right | d1 << left;
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(d0, dst++);
+ d0 = d1;
+ }
+
+ // Trailing bits
+ if (m) {
+ if (m <= bits - right) {
+ // Single source word
+ d0 >>= right;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src);
+ d1 = fb_rev_pixels_in_long(d1,
+ bswapmask);
+ d0 = d0 >> right | d1 << left;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
+ }
+ }
+ }
+}
+
+ /*
+ * Generic bitwise copy algorithm, operating backward
+ */
+
+static void
+bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
+ unsigned n, u32 bswapmask)
+{
+ unsigned long first, last;
+ int shift;
+
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
+
+ dst += (dst_idx + n - 1) / bits;
+ src += (src_idx + n - 1) / bits;
+ dst_idx = (dst_idx + n - 1) % bits;
+ src_idx = (src_idx + n - 1) % bits;
+
+ shift = dst_idx-src_idx;
+
+ first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
+ last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
+
+ if (!shift) {
+ // Same alignment for source and dest
+
+ if ((unsigned long)dst_idx+1 >= n) {
+ // Single word
+ if (first)
+ last &= first;
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
+ } else {
+ // Multiple destination words
+
+ // Leading bits
+ if (first) {
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
+ dst--;
+ src--;
+ n -= dst_idx+1;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 8) {
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ FB_WRITEL(FB_READL(src--), dst--);
+ n -= 8;
+ }
+ while (n--)
+ FB_WRITEL(FB_READL(src--), dst--);
+
+ // Trailing bits
+ if (last != -1UL)
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
+ }
+ } else {
+ // Different alignment for source and dest
+ unsigned long d0, d1;
+ int m;
+
+ int const left = shift & (bits-1);
+ int const right = -shift & (bits-1);
+
+ if ((unsigned long)dst_idx+1 >= n) {
+ // Single destination word
+ if (first)
+ last &= first;
+ d0 = FB_READL(src);
+ if (shift < 0) {
+ // Single source word
+ d0 >>= right;
+ } else if (1+(unsigned long)src_idx >= n) {
+ // Single source word
+ d0 <<= left;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src - 1);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+ d0 = d0 << left | d1 >> right;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
+ } else {
+ // Multiple destination words
+ /** We must always remember the last value read, because in case
+ SRC and DST overlap bitwise (e.g. when moving just one pixel in
+ 1bpp), we always collect one full long for DST and that might
+ overlap with the current long from SRC. We store this value in
+ 'd0'. */
+
+ d0 = FB_READL(src--);
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ // Leading bits
+ if (shift < 0) {
+ // Single source word
+ d1 = d0;
+ d0 >>= right;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src--);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+ d0 = d0 << left | d1 >> right;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+ d0 = d1;
+ dst--;
+ n -= dst_idx+1;
+
+ // Main chunk
+ m = n % bits;
+ n /= bits;
+ while ((n >= 4) && !bswapmask) {
+ d1 = FB_READL(src--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
+ d0 = d1;
+ d1 = FB_READL(src--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
+ d0 = d1;
+ d1 = FB_READL(src--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
+ d0 = d1;
+ d1 = FB_READL(src--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
+ d0 = d1;
+ n -= 4;
+ }
+ while (n--) {
+ d1 = FB_READL(src--);
+ d1 = fb_rev_pixels_in_long(d1, bswapmask);
+ d0 = d0 << left | d1 >> right;
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(d0, dst--);
+ d0 = d1;
+ }
+
+ // Trailing bits
+ if (m) {
+ if (m <= bits - left) {
+ // Single source word
+ d0 <<= left;
+ } else {
+ // 2 source words
+ d1 = FB_READL(src);
+ d1 = fb_rev_pixels_in_long(d1,
+ bswapmask);
+ d0 = d0 << left | d1 >> right;
+ }
+ d0 = fb_rev_pixels_in_long(d0, bswapmask);
+ FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
+ }
+ }
+ }
+}
+
+void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+ u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
+ u32 height = area->height, width = area->width;
+ unsigned long const bits_per_line = p->fix.line_length*8u;
+ unsigned long __iomem *base = NULL;
+ int bits = BITS_PER_LONG, bytes = bits >> 3;
+ unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
+ u32 bswapmask = fb_compute_bswapmask(p);
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ /* if the beginning of the target area might overlap with the end of
+ the source area, be have to copy the area reverse. */
+ if ((dy == sy && dx > sx) || (dy > sy)) {
+ dy += height;
+ sy += height;
+ rev_copy = 1;
+ }
+
+ // split the base of the framebuffer into a long-aligned address and the
+ // index of the first bit
+ base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
+ dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
+ // add offset of source and target area
+ dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
+ src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
+
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+
+ if (rev_copy) {
+ while (height--) {
+ dst_idx -= bits_per_line;
+ src_idx -= bits_per_line;
+ bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
+ width*p->var.bits_per_pixel, bswapmask);
+ }
+ } else {
+ while (height--) {
+ bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
+ width*p->var.bits_per_pixel, bswapmask);
+ dst_idx += bits_per_line;
+ src_idx += bits_per_line;
+ }
+ }
+}
+
+EXPORT_SYMBOL(cfb_copyarea);
+
+MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
+MODULE_DESCRIPTION("Generic software accelerated copyarea");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+ * Generic fillrect for frame buffers with packed pixels of any depth.
+ *
+ * Copyright (C) 2000 James Simmons (jsimmons@linux-fbdev.org)
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * NOTES:
+ *
+ * Also need to add code to deal with cards endians that are different than
+ * the native cpu endians. I also need to deal with MSB position in the word.
+ *
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+#include "fb_draw.h"
+
+#if BITS_PER_LONG == 32
+# define FB_WRITEL fb_writel
+# define FB_READL fb_readl
+#else
+# define FB_WRITEL fb_writeq
+# define FB_READL fb_readq
+#endif
+
+ /*
+ * Aligned pattern fill using 32/64-bit memory accesses
+ */
+
+static void
+bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
+ unsigned long pat, unsigned n, int bits, u32 bswapmask)
+{
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
+ last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
+
+ if (dst_idx+n <= bits) {
+ // Single word
+ if (last)
+ first &= last;
+ FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
+ } else {
+ // Multiple destination words
+
+ // Leading bits
+ if (first!= ~0UL) {
+ FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
+ dst++;
+ n -= bits - dst_idx;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 8) {
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ FB_WRITEL(pat, dst++);
+ n -= 8;
+ }
+ while (n--)
+ FB_WRITEL(pat, dst++);
+
+ // Trailing bits
+ if (last)
+ FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
+ }
+}
+
+
+ /*
+ * Unaligned generic pattern fill using 32/64-bit memory accesses
+ * The pattern must have been expanded to a full 32/64-bit value
+ * Left/right are the appropriate shifts to convert to the pattern to be
+ * used for the next 32/64-bit word
+ */
+
+static void
+bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
+ unsigned long pat, int left, int right, unsigned n, int bits)
+{
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ // Single word
+ if (last)
+ first &= last;
+ FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
+ } else {
+ // Multiple destination words
+ // Leading bits
+ if (first) {
+ FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ n -= bits - dst_idx;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 4) {
+ FB_WRITEL(pat, dst++);
+ pat = pat << left | pat >> right;
+ FB_WRITEL(pat, dst++);
+ pat = pat << left | pat >> right;
+ FB_WRITEL(pat, dst++);
+ pat = pat << left | pat >> right;
+ FB_WRITEL(pat, dst++);
+ pat = pat << left | pat >> right;
+ n -= 4;
+ }
+ while (n--) {
+ FB_WRITEL(pat, dst++);
+ pat = pat << left | pat >> right;
+ }
+
+ // Trailing bits
+ if (last)
+ FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
+ }
+}
+
+ /*
+ * Aligned pattern invert using 32/64-bit memory accesses
+ */
+static void
+bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst,
+ int dst_idx, unsigned long pat, unsigned n, int bits,
+ u32 bswapmask)
+{
+ unsigned long val = pat, dat;
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
+ last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
+
+ if (dst_idx+n <= bits) {
+ // Single word
+ if (last)
+ first &= last;
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ val, dat, first), dst);
+ } else {
+ // Multiple destination words
+ // Leading bits
+ if (first!=0UL) {
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ val, dat, first), dst);
+ dst++;
+ n -= bits - dst_idx;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 8) {
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ n -= 8;
+ }
+ while (n--) {
+ FB_WRITEL(FB_READL(dst) ^ val, dst);
+ dst++;
+ }
+ // Trailing bits
+ if (last) {
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ val, dat, last), dst);
+ }
+ }
+}
+
+
+ /*
+ * Unaligned generic pattern invert using 32/64-bit memory accesses
+ * The pattern must have been expanded to a full 32/64-bit value
+ * Left/right are the appropriate shifts to convert to the pattern to be
+ * used for the next 32/64-bit word
+ */
+
+static void
+bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst,
+ int dst_idx, unsigned long pat, int left, int right,
+ unsigned n, int bits)
+{
+ unsigned long first, last, dat;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ // Single word
+ if (last)
+ first &= last;
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ pat, dat, first), dst);
+ } else {
+ // Multiple destination words
+
+ // Leading bits
+ if (first != 0UL) {
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ pat, dat, first), dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ n -= bits - dst_idx;
+ }
+
+ // Main chunk
+ n /= bits;
+ while (n >= 4) {
+ FB_WRITEL(FB_READL(dst) ^ pat, dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ FB_WRITEL(FB_READL(dst) ^ pat, dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ FB_WRITEL(FB_READL(dst) ^ pat, dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ FB_WRITEL(FB_READL(dst) ^ pat, dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ n -= 4;
+ }
+ while (n--) {
+ FB_WRITEL(FB_READL(dst) ^ pat, dst);
+ dst++;
+ pat = pat << left | pat >> right;
+ }
+
+ // Trailing bits
+ if (last) {
+ dat = FB_READL(dst);
+ FB_WRITEL(comp(dat ^ pat, dat, last), dst);
+ }
+ }
+}
+
+void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+ unsigned long pat, pat2, fg;
+ unsigned long width = rect->width, height = rect->height;
+ int bits = BITS_PER_LONG, bytes = bits >> 3;
+ u32 bpp = p->var.bits_per_pixel;
+ unsigned long __iomem *dst;
+ int dst_idx, left;
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+ fg = ((u32 *) (p->pseudo_palette))[rect->color];
+ else
+ fg = rect->color;
+
+ pat = pixel_to_pat(bpp, fg);
+
+ dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
+ dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
+ dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
+ /* FIXME For now we support 1-32 bpp only */
+ left = bits % bpp;
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+ if (!left) {
+ u32 bswapmask = fb_compute_bswapmask(p);
+ void (*fill_op32)(struct fb_info *p,
+ unsigned long __iomem *dst, int dst_idx,
+ unsigned long pat, unsigned n, int bits,
+ u32 bswapmask) = NULL;
+
+ switch (rect->rop) {
+ case ROP_XOR:
+ fill_op32 = bitfill_aligned_rev;
+ break;
+ case ROP_COPY:
+ fill_op32 = bitfill_aligned;
+ break;
+ default:
+ printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
+ fill_op32 = bitfill_aligned;
+ break;
+ }
+ while (height--) {
+ dst += dst_idx >> (ffs(bits) - 1);
+ dst_idx &= (bits - 1);
+ fill_op32(p, dst, dst_idx, pat, width*bpp, bits,
+ bswapmask);
+ dst_idx += p->fix.line_length*8;
+ }
+ } else {
+ int right, r;
+ void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,
+ int dst_idx, unsigned long pat, int left,
+ int right, unsigned n, int bits) = NULL;
+#ifdef __LITTLE_ENDIAN
+ right = left;
+ left = bpp - right;
+#else
+ right = bpp - left;
+#endif
+ switch (rect->rop) {
+ case ROP_XOR:
+ fill_op = bitfill_unaligned_rev;
+ break;
+ case ROP_COPY:
+ fill_op = bitfill_unaligned;
+ break;
+ default:
+ printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
+ fill_op = bitfill_unaligned;
+ break;
+ }
+ while (height--) {
+ dst += dst_idx / bits;
+ dst_idx &= (bits - 1);
+ r = dst_idx % bpp;
+ /* rotate pattern to the correct start position */
+ pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
+ fill_op(p, dst, dst_idx, pat2, left, right,
+ width*bpp, bits);
+ dst_idx += p->fix.line_length*8;
+ }
+ }
+}
+
+EXPORT_SYMBOL(cfb_fillrect);
+
+MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
+MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Generic BitBLT function for frame buffer with packed pixels of any depth.
+ *
+ * Copyright (C) June 1999 James Simmons
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * NOTES:
+ *
+ * This function copys a image from system memory to video memory. The
+ * image can be a bitmap where each 0 represents the background color and
+ * each 1 represents the foreground color. Great for font handling. It can
+ * also be a color image. This is determined by image_depth. The color image
+ * must be laid out exactly in the same format as the framebuffer. Yes I know
+ * their are cards with hardware that coverts images of various depths to the
+ * framebuffer depth. But not every card has this. All images must be rounded
+ * up to the nearest byte. For example a bitmap 12 bits wide must be two
+ * bytes width.
+ *
+ * Tony:
+ * Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API. This speeds
+ * up the code significantly.
+ *
+ * Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
+ * still processed a bit at a time.
+ *
+ * Also need to add code to deal with cards endians that are different than
+ * the native cpu endians. I also need to deal with MSB position in the word.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+#include "fb_draw.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+static const u32 cfb_tab8_be[] = {
+ 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+ 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+ 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+ 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+};
+
+static const u32 cfb_tab8_le[] = {
+ 0x00000000,0xff000000,0x00ff0000,0xffff0000,
+ 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+ 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+ 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+};
+
+static const u32 cfb_tab16_be[] = {
+ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+};
+
+static const u32 cfb_tab16_le[] = {
+ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+};
+
+static const u32 cfb_tab32[] = {
+ 0x00000000, 0xffffffff
+};
+
+#define FB_WRITEL fb_writel
+#define FB_READL fb_readl
+
+static inline void color_imageblit(const struct fb_image *image,
+ struct fb_info *p, u8 __iomem *dst1,
+ u32 start_index,
+ u32 pitch_index)
+{
+ /* Draw the penguin */
+ u32 __iomem *dst, *dst2;
+ u32 color = 0, val, shift;
+ int i, n, bpp = p->var.bits_per_pixel;
+ u32 null_bits = 32 - bpp;
+ u32 *palette = (u32 *) p->pseudo_palette;
+ const u8 *src = image->data;
+ u32 bswapmask = fb_compute_bswapmask(p);
+
+ dst2 = (u32 __iomem *) dst1;
+ for (i = image->height; i--; ) {
+ n = image->width;
+ dst = (u32 __iomem *) dst1;
+ shift = 0;
+ val = 0;
+
+ if (start_index) {
+ u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
+ start_index, bswapmask);
+ val = FB_READL(dst) & start_mask;
+ shift = start_index;
+ }
+ while (n--) {
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+ color = palette[*src];
+ else
+ color = *src;
+ color <<= FB_LEFT_POS(p, bpp);
+ val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
+ if (shift >= null_bits) {
+ FB_WRITEL(val, dst++);
+
+ val = (shift == null_bits) ? 0 :
+ FB_SHIFT_LOW(p, color, 32 - shift);
+ }
+ shift += bpp;
+ shift &= (32 - 1);
+ src++;
+ }
+ if (shift) {
+ u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
+ bswapmask);
+
+ FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
+ }
+ dst1 += p->fix.line_length;
+ if (pitch_index) {
+ dst2 += p->fix.line_length;
+ dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
+
+ start_index += pitch_index;
+ start_index &= 32 - 1;
+ }
+ }
+}
+
+static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p,
+ u8 __iomem *dst1, u32 fgcolor,
+ u32 bgcolor,
+ u32 start_index,
+ u32 pitch_index)
+{
+ u32 shift, color = 0, bpp = p->var.bits_per_pixel;
+ u32 __iomem *dst, *dst2;
+ u32 val, pitch = p->fix.line_length;
+ u32 null_bits = 32 - bpp;
+ u32 spitch = (image->width+7)/8;
+ const u8 *src = image->data, *s;
+ u32 i, j, l;
+ u32 bswapmask = fb_compute_bswapmask(p);
+
+ dst2 = (u32 __iomem *) dst1;
+ fgcolor <<= FB_LEFT_POS(p, bpp);
+ bgcolor <<= FB_LEFT_POS(p, bpp);
+
+ for (i = image->height; i--; ) {
+ shift = val = 0;
+ l = 8;
+ j = image->width;
+ dst = (u32 __iomem *) dst1;
+ s = src;
+
+ /* write leading bits */
+ if (start_index) {
+ u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
+ start_index, bswapmask);
+ val = FB_READL(dst) & start_mask;
+ shift = start_index;
+ }
+
+ while (j--) {
+ l--;
+ color = (*s & (1 << l)) ? fgcolor : bgcolor;
+ val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
+
+ /* Did the bitshift spill bits to the next long? */
+ if (shift >= null_bits) {
+ FB_WRITEL(val, dst++);
+ val = (shift == null_bits) ? 0 :
+ FB_SHIFT_LOW(p, color, 32 - shift);
+ }
+ shift += bpp;
+ shift &= (32 - 1);
+ if (!l) { l = 8; s++; }
+ }
+
+ /* write trailing bits */
+ if (shift) {
+ u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
+ bswapmask);
+
+ FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
+ }
+
+ dst1 += pitch;
+ src += spitch;
+ if (pitch_index) {
+ dst2 += pitch;
+ dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
+ start_index += pitch_index;
+ start_index &= 32 - 1;
+ }
+
+ }
+}
+
+/*
+ * fast_imageblit - optimized monochrome color expansion
+ *
+ * Only if: bits_per_pixel == 8, 16, or 32
+ * image->width is divisible by pixel/dword (ppw);
+ * fix->line_legth is divisible by 4;
+ * beginning and end of a scanline is dword aligned
+ */
+static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p,
+ u8 __iomem *dst1, u32 fgcolor,
+ u32 bgcolor)
+{
+ u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
+ u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
+ u32 bit_mask, end_mask, eorx, shift;
+ const char *s = image->data, *src;
+ u32 __iomem *dst;
+ const u32 *tab = NULL;
+ int i, j, k;
+
+ switch (bpp) {
+ case 8:
+ tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
+ break;
+ case 16:
+ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
+ break;
+ case 32:
+ default:
+ tab = cfb_tab32;
+ break;
+ }
+
+ for (i = ppw-1; i--; ) {
+ fgx <<= bpp;
+ bgx <<= bpp;
+ fgx |= fgcolor;
+ bgx |= bgcolor;
+ }
+
+ bit_mask = (1 << ppw) - 1;
+ eorx = fgx ^ bgx;
+ k = image->width/ppw;
+
+ for (i = image->height; i--; ) {
+ dst = (u32 __iomem *) dst1, shift = 8; src = s;
+
+ for (j = k; j--; ) {
+ shift -= ppw;
+ end_mask = tab[(*src >> shift) & bit_mask];
+ FB_WRITEL((end_mask & eorx)^bgx, dst++);
+ if (!shift) { shift = 8; src++; }
+ }
+ dst1 += p->fix.line_length;
+ s += spitch;
+ }
+}
+
+void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+ u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
+ u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
+ u32 width = image->width;
+ u32 dx = image->dx, dy = image->dy;
+ u8 __iomem *dst1;
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
+ start_index = bitstart & (32 - 1);
+ pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
+
+ bitstart /= 8;
+ bitstart &= ~(bpl - 1);
+ dst1 = p->screen_base + bitstart;
+
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+
+ if (image->depth == 1) {
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
+ bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
+ } else {
+ fgcolor = image->fg_color;
+ bgcolor = image->bg_color;
+ }
+
+ if (32 % bpp == 0 && !start_index && !pitch_index &&
+ ((width & (32/bpp-1)) == 0) &&
+ bpp >= 8 && bpp <= 32)
+ fast_imageblit(image, p, dst1, fgcolor, bgcolor);
+ else
+ slow_imageblit(image, p, dst1, fgcolor, bgcolor,
+ start_index, pitch_index);
+ } else
+ color_imageblit(image, p, dst1, start_index, pitch_index);
+}
+
+EXPORT_SYMBOL(cfb_imageblit);
+
+MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
+MODULE_DESCRIPTION("Generic software accelerated imaging drawing");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+ * drivers/video/fb_ddc.c - DDC/EDID read support.
+ *
+ * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/slab.h>
+
+#include "../edid.h"
+
+#define DDC_ADDR 0x50
+
+static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter)
+{
+ unsigned char start = 0x0;
+ unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = buf,
+ }
+ };
+
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_warn(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+
+unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ algo_data->setscl(algo_data->data, 1);
+
+ for (i = 0; i < 3; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 1);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = fb_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 1);
+ algo_data->setscl(algo_data->data, 1);
+
+ adapter->class |= I2C_CLASS_DDC;
+ return edid;
+}
+
+EXPORT_SYMBOL_GPL(fb_ddc_read);
+
+MODULE_AUTHOR("Dennis Munsie <dmunsie@cecropia.com>");
+MODULE_DESCRIPTION("DDC/EDID reading support");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/drivers/video/fb_defio.c
+ *
+ * Copyright (C) 2006 Jaya Kumar
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/list.h>
+
+/* to support deferred IO */
+#include <linux/rmap.h>
+#include <linux/pagemap.h>
+
+static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
+{
+ void *screen_base = (void __force *) info->screen_base;
+ struct page *page;
+
+ if (is_vmalloc_addr(screen_base + offs))
+ page = vmalloc_to_page(screen_base + offs);
+ else
+ page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
+
+ return page;
+}
+
+/* this is to find and return the vmalloc-ed fb pages */
+static int fb_deferred_io_fault(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ unsigned long offset;
+ struct page *page;
+ struct fb_info *info = vma->vm_private_data;
+
+ offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= info->fix.smem_len)
+ return VM_FAULT_SIGBUS;
+
+ page = fb_deferred_io_page(info, offset);
+ if (!page)
+ return VM_FAULT_SIGBUS;
+
+ get_page(page);
+
+ if (vma->vm_file)
+ page->mapping = vma->vm_file->f_mapping;
+ else
+ printk(KERN_ERR "no mapping available\n");
+
+ BUG_ON(!page->mapping);
+ page->index = vmf->pgoff;
+
+ vmf->page = page;
+ return 0;
+}
+
+int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct fb_info *info = file->private_data;
+ struct inode *inode = file_inode(file);
+ int err = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (err)
+ return err;
+
+ /* Skip if deferred io is compiled-in but disabled on this fbdev */
+ if (!info->fbdefio)
+ return 0;
+
+ mutex_lock(&inode->i_mutex);
+ /* Kill off the delayed work */
+ cancel_delayed_work_sync(&info->deferred_work);
+
+ /* Run it immediately */
+ err = schedule_delayed_work(&info->deferred_work, 0);
+ mutex_unlock(&inode->i_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
+
+/* vm_ops->page_mkwrite handler */
+static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ struct page *page = vmf->page;
+ struct fb_info *info = vma->vm_private_data;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct page *cur;
+
+ /* this is a callback we get when userspace first tries to
+ write to the page. we schedule a workqueue. that workqueue
+ will eventually mkclean the touched pages and execute the
+ deferred framebuffer IO. then if userspace touches a page
+ again, we repeat the same scheme */
+
+ file_update_time(vma->vm_file);
+
+ /* protect against the workqueue changing the page list */
+ mutex_lock(&fbdefio->lock);
+
+ /* first write in this cycle, notify the driver */
+ if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
+ fbdefio->first_io(info);
+
+ /*
+ * We want the page to remain locked from ->page_mkwrite until
+ * the PTE is marked dirty to avoid page_mkclean() being called
+ * before the PTE is updated, which would leave the page ignored
+ * by defio.
+ * Do this by locking the page here and informing the caller
+ * about it with VM_FAULT_LOCKED.
+ */
+ lock_page(page);
+
+ /* we loop through the pagelist before adding in order
+ to keep the pagelist sorted */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+ /* this check is to catch the case where a new
+ process could start writing to the same page
+ through a new pte. this new access can cause the
+ mkwrite even when the original ps's pte is marked
+ writable */
+ if (unlikely(cur == page))
+ goto page_already_added;
+ else if (cur->index > page->index)
+ break;
+ }
+
+ list_add_tail(&page->lru, &cur->lru);
+
+page_already_added:
+ mutex_unlock(&fbdefio->lock);
+
+ /* come back after delay to process the deferred IO */
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+ return VM_FAULT_LOCKED;
+}
+
+static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+ .fault = fb_deferred_io_fault,
+ .page_mkwrite = fb_deferred_io_mkwrite,
+};
+
+static int fb_deferred_io_set_page_dirty(struct page *page)
+{
+ if (!PageDirty(page))
+ SetPageDirty(page);
+ return 0;
+}
+
+static const struct address_space_operations fb_deferred_io_aops = {
+ .set_page_dirty = fb_deferred_io_set_page_dirty,
+};
+
+static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ vma->vm_ops = &fb_deferred_io_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ if (!(info->flags & FBINFO_VIRTFB))
+ vma->vm_flags |= VM_IO;
+ vma->vm_private_data = info;
+ return 0;
+}
+
+/* workqueue callback */
+static void fb_deferred_io_work(struct work_struct *work)
+{
+ struct fb_info *info = container_of(work, struct fb_info,
+ deferred_work.work);
+ struct list_head *node, *next;
+ struct page *cur;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ /* here we mkclean the pages, then do all deferred IO */
+ mutex_lock(&fbdefio->lock);
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+ lock_page(cur);
+ page_mkclean(cur);
+ unlock_page(cur);
+ }
+
+ /* driver's callback with pagelist */
+ fbdefio->deferred_io(info, &fbdefio->pagelist);
+
+ /* clear the list */
+ list_for_each_safe(node, next, &fbdefio->pagelist) {
+ list_del(node);
+ }
+ mutex_unlock(&fbdefio->lock);
+}
+
+void fb_deferred_io_init(struct fb_info *info)
+{
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ BUG_ON(!fbdefio);
+ mutex_init(&fbdefio->lock);
+ info->fbops->fb_mmap = fb_deferred_io_mmap;
+ INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+ INIT_LIST_HEAD(&fbdefio->pagelist);
+ if (fbdefio->delay == 0) /* set a default of 1 s */
+ fbdefio->delay = HZ;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+
+void fb_deferred_io_open(struct fb_info *info,
+ struct inode *inode,
+ struct file *file)
+{
+ file->f_mapping->a_ops = &fb_deferred_io_aops;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_open);
+
+void fb_deferred_io_cleanup(struct fb_info *info)
+{
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct page *page;
+ int i;
+
+ BUG_ON(!fbdefio);
+ cancel_delayed_work_sync(&info->deferred_work);
+
+ /* clear out the mapping that we setup */
+ for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
+ page = fb_deferred_io_page(info, i);
+ page->mapping = NULL;
+ }
+
+ info->fbops->fb_mmap = NULL;
+ mutex_destroy(&fbdefio->lock);
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+#ifndef _FB_DRAW_H
+#define _FB_DRAW_H
+
+#include <asm/types.h>
+#include <linux/fb.h>
+#include <linux/bug.h>
+
+ /*
+ * Compose two values, using a bitmask as decision value
+ * This is equivalent to (a & mask) | (b & ~mask)
+ */
+
+static inline unsigned long
+comp(unsigned long a, unsigned long b, unsigned long mask)
+{
+ return ((a ^ b) & mask) ^ b;
+}
+
+ /*
+ * Create a pattern with the given pixel's color
+ */
+
+#if BITS_PER_LONG == 64
+static inline unsigned long
+pixel_to_pat( u32 bpp, u32 pixel)
+{
+ switch (bpp) {
+ case 1:
+ return 0xfffffffffffffffful*pixel;
+ case 2:
+ return 0x5555555555555555ul*pixel;
+ case 4:
+ return 0x1111111111111111ul*pixel;
+ case 8:
+ return 0x0101010101010101ul*pixel;
+ case 12:
+ return 0x1001001001001001ul*pixel;
+ case 16:
+ return 0x0001000100010001ul*pixel;
+ case 24:
+ return 0x0001000001000001ul*pixel;
+ case 32:
+ return 0x0000000100000001ul*pixel;
+ default:
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
+ }
+}
+#else
+static inline unsigned long
+pixel_to_pat( u32 bpp, u32 pixel)
+{
+ switch (bpp) {
+ case 1:
+ return 0xfffffffful*pixel;
+ case 2:
+ return 0x55555555ul*pixel;
+ case 4:
+ return 0x11111111ul*pixel;
+ case 8:
+ return 0x01010101ul*pixel;
+ case 12:
+ return 0x01001001ul*pixel;
+ case 16:
+ return 0x00010001ul*pixel;
+ case 24:
+ return 0x01000001ul*pixel;
+ case 32:
+ return 0x00000001ul*pixel;
+ default:
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
+ }
+}
+#endif
+
+#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
+#if BITS_PER_LONG == 64
+#define REV_PIXELS_MASK1 0x5555555555555555ul
+#define REV_PIXELS_MASK2 0x3333333333333333ul
+#define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful
+#else
+#define REV_PIXELS_MASK1 0x55555555ul
+#define REV_PIXELS_MASK2 0x33333333ul
+#define REV_PIXELS_MASK4 0x0f0f0f0ful
+#endif
+
+static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
+ u32 bswapmask)
+{
+ if (bswapmask & 1)
+ val = comp(val >> 1, val << 1, REV_PIXELS_MASK1);
+ if (bswapmask & 2)
+ val = comp(val >> 2, val << 2, REV_PIXELS_MASK2);
+ if (bswapmask & 3)
+ val = comp(val >> 4, val << 4, REV_PIXELS_MASK4);
+ return val;
+}
+
+static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index,
+ u32 bswapmask)
+{
+ u32 mask;
+
+ if (!bswapmask) {
+ mask = FB_SHIFT_HIGH(p, ~(u32)0, index);
+ } else {
+ mask = 0xff << FB_LEFT_POS(p, 8);
+ mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
+ mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
+#if defined(__i386__) || defined(__x86_64__)
+ /* Shift argument is limited to 0 - 31 on x86 based CPU's */
+ if(index + bswapmask < 32)
+#endif
+ mask |= FB_SHIFT_HIGH(p, ~(u32)0,
+ (index + bswapmask) & ~(bswapmask));
+ }
+ return mask;
+}
+
+static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p,
+ u32 index,
+ u32 bswapmask)
+{
+ unsigned long mask;
+
+ if (!bswapmask) {
+ mask = FB_SHIFT_HIGH(p, ~0UL, index);
+ } else {
+ mask = 0xff << FB_LEFT_POS(p, 8);
+ mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
+ mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
+#if defined(__i386__) || defined(__x86_64__)
+ /* Shift argument is limited to 0 - 31 on x86 based CPU's */
+ if(index + bswapmask < BITS_PER_LONG)
+#endif
+ mask |= FB_SHIFT_HIGH(p, ~0UL,
+ (index + bswapmask) & ~(bswapmask));
+ }
+ return mask;
+}
+
+
+static inline u32 fb_compute_bswapmask(struct fb_info *info)
+{
+ u32 bswapmask = 0;
+ unsigned bpp = info->var.bits_per_pixel;
+
+ if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) {
+ /*
+ * Reversed order of pixel layout in bytes
+ * works only for 1, 2 and 4 bpp
+ */
+ bswapmask = 7 - bpp + 1;
+ }
+ return bswapmask;
+}
+
+#else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
+
+static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
+ u32 bswapmask)
+{
+ return val;
+}
+
+#define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i))
+#define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i))
+#define fb_compute_bswapmask(...) 0
+
+#endif /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
+
+#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
+#define _cpu_to_le_long(x) __cpu_to_le_long(x)
+#define __cpu_to_le_long(x) cpu_to_le##x
+
+#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
+#define _le_long_to_cpu(x) __le_long_to_cpu(x)
+#define __le_long_to_cpu(x) le##x##_to_cpu
+
+static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
+{
+ return (word << shift) | (word >> (x - shift));
+}
+
+#endif /* FB_DRAW_H */
--- /dev/null
+/*
+ * linux/drivers/video/fb_notify.c
+ *
+ * Copyright (C) 2006 Antonino Daplas <adaplas@pol.net>
+ *
+ * 2001 - Documented with DocBook
+ * - Brad Douglas <brad@neruo.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#include <linux/export.h>
+
+static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
+
+/**
+ * fb_register_client - register a client notifier
+ * @nb: notifier block to callback on events
+ */
+int fb_register_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&fb_notifier_list, nb);
+}
+EXPORT_SYMBOL(fb_register_client);
+
+/**
+ * fb_unregister_client - unregister a client notifier
+ * @nb: notifier block to callback on events
+ */
+int fb_unregister_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
+}
+EXPORT_SYMBOL(fb_unregister_client);
+
+/**
+ * fb_notifier_call_chain - notify clients of fb_events
+ *
+ */
+int fb_notifier_call_chain(unsigned long val, void *v)
+{
+ return blocking_notifier_call_chain(&fb_notifier_list, val, v);
+}
+EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
--- /dev/null
+/*
+ * linux/drivers/video/fb_sys_read.c - Generic file operations where
+ * framebuffer is in system RAM
+ *
+ * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *src;
+ int err = 0;
+ unsigned long total_size;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p >= total_size)
+ return 0;
+
+ if (count >= total_size)
+ count = total_size;
+
+ if (count + p > total_size)
+ count = total_size - p;
+
+ src = (void __force *)(info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ if (copy_to_user(buf, src, count))
+ err = -EFAULT;
+
+ if (!err)
+ *ppos += count;
+
+ return (err) ? err : count;
+}
+EXPORT_SYMBOL_GPL(fb_sys_read);
+
+ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *dst;
+ int err = 0;
+ unsigned long total_size;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ dst = (void __force *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ if (copy_from_user(dst, buf, count))
+ err = -EFAULT;
+
+ if (!err)
+ *ppos += count;
+
+ return (err) ? err : count;
+}
+EXPORT_SYMBOL_GPL(fb_sys_write);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic file read (fb in system RAM)");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/drivers/video/fbcmap.c -- Colormap handling for frame buffer devices
+ *
+ * Created 15 Jun 1997 by Geert Uytterhoeven
+ *
+ * 2001 - Documented with DocBook
+ * - Brad Douglas <brad@neruo.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+static u16 red2[] __read_mostly = {
+ 0x0000, 0xaaaa
+};
+static u16 green2[] __read_mostly = {
+ 0x0000, 0xaaaa
+};
+static u16 blue2[] __read_mostly = {
+ 0x0000, 0xaaaa
+};
+
+static u16 red4[] __read_mostly = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+static u16 green4[] __read_mostly = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+static u16 blue4[] __read_mostly = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+
+static u16 red8[] __read_mostly = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
+};
+static u16 green8[] __read_mostly = {
+ 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa
+};
+static u16 blue8[] __read_mostly = {
+ 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
+};
+
+static u16 red16[] __read_mostly = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
+};
+static u16 green16[] __read_mostly = {
+ 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa,
+ 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
+};
+static u16 blue16[] __read_mostly = {
+ 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
+ 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
+};
+
+static const struct fb_cmap default_2_colors = {
+ .len=2, .red=red2, .green=green2, .blue=blue2
+};
+static const struct fb_cmap default_8_colors = {
+ .len=8, .red=red8, .green=green8, .blue=blue8
+};
+static const struct fb_cmap default_4_colors = {
+ .len=4, .red=red4, .green=green4, .blue=blue4
+};
+static const struct fb_cmap default_16_colors = {
+ .len=16, .red=red16, .green=green16, .blue=blue16
+};
+
+
+
+/**
+ * fb_alloc_cmap - allocate a colormap
+ * @cmap: frame buffer colormap structure
+ * @len: length of @cmap
+ * @transp: boolean, 1 if there is transparency, 0 otherwise
+ * @flags: flags for kmalloc memory allocation
+ *
+ * Allocates memory for a colormap @cmap. @len is the
+ * number of entries in the palette.
+ *
+ * Returns negative errno on error, or zero on success.
+ *
+ */
+
+int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
+{
+ int size = len * sizeof(u16);
+ int ret = -ENOMEM;
+
+ if (cmap->len != len) {
+ fb_dealloc_cmap(cmap);
+ if (!len)
+ return 0;
+
+ cmap->red = kmalloc(size, flags);
+ if (!cmap->red)
+ goto fail;
+ cmap->green = kmalloc(size, flags);
+ if (!cmap->green)
+ goto fail;
+ cmap->blue = kmalloc(size, flags);
+ if (!cmap->blue)
+ goto fail;
+ if (transp) {
+ cmap->transp = kmalloc(size, flags);
+ if (!cmap->transp)
+ goto fail;
+ } else {
+ cmap->transp = NULL;
+ }
+ }
+ cmap->start = 0;
+ cmap->len = len;
+ ret = fb_copy_cmap(fb_default_cmap(len), cmap);
+ if (ret)
+ goto fail;
+ return 0;
+
+fail:
+ fb_dealloc_cmap(cmap);
+ return ret;
+}
+
+int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
+{
+ return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
+}
+
+/**
+ * fb_dealloc_cmap - deallocate a colormap
+ * @cmap: frame buffer colormap structure
+ *
+ * Deallocates a colormap that was previously allocated with
+ * fb_alloc_cmap().
+ *
+ */
+
+void fb_dealloc_cmap(struct fb_cmap *cmap)
+{
+ kfree(cmap->red);
+ kfree(cmap->green);
+ kfree(cmap->blue);
+ kfree(cmap->transp);
+
+ cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
+ cmap->len = 0;
+}
+
+/**
+ * fb_copy_cmap - copy a colormap
+ * @from: frame buffer colormap structure
+ * @to: frame buffer colormap structure
+ *
+ * Copy contents of colormap from @from to @to.
+ */
+
+int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
+{
+ int tooff = 0, fromoff = 0;
+ int size;
+
+ if (to->start > from->start)
+ fromoff = to->start - from->start;
+ else
+ tooff = from->start - to->start;
+ size = to->len - tooff;
+ if (size > (int) (from->len - fromoff))
+ size = from->len - fromoff;
+ if (size <= 0)
+ return -EINVAL;
+ size *= sizeof(u16);
+
+ memcpy(to->red+tooff, from->red+fromoff, size);
+ memcpy(to->green+tooff, from->green+fromoff, size);
+ memcpy(to->blue+tooff, from->blue+fromoff, size);
+ if (from->transp && to->transp)
+ memcpy(to->transp+tooff, from->transp+fromoff, size);
+ return 0;
+}
+
+int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
+{
+ int tooff = 0, fromoff = 0;
+ int size;
+
+ if (to->start > from->start)
+ fromoff = to->start - from->start;
+ else
+ tooff = from->start - to->start;
+ size = to->len - tooff;
+ if (size > (int) (from->len - fromoff))
+ size = from->len - fromoff;
+ if (size <= 0)
+ return -EINVAL;
+ size *= sizeof(u16);
+
+ if (copy_to_user(to->red+tooff, from->red+fromoff, size))
+ return -EFAULT;
+ if (copy_to_user(to->green+tooff, from->green+fromoff, size))
+ return -EFAULT;
+ if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
+ return -EFAULT;
+ if (from->transp && to->transp)
+ if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * fb_set_cmap - set the colormap
+ * @cmap: frame buffer colormap structure
+ * @info: frame buffer info structure
+ *
+ * Sets the colormap @cmap for a screen of device @info.
+ *
+ * Returns negative errno on error, or zero on success.
+ *
+ */
+
+int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ int i, start, rc = 0;
+ u16 *red, *green, *blue, *transp;
+ u_int hred, hgreen, hblue, htransp = 0xffff;
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+
+ if (start < 0 || (!info->fbops->fb_setcolreg &&
+ !info->fbops->fb_setcmap))
+ return -EINVAL;
+ if (info->fbops->fb_setcmap) {
+ rc = info->fbops->fb_setcmap(cmap, info);
+ } else {
+ for (i = 0; i < cmap->len; i++) {
+ hred = *red++;
+ hgreen = *green++;
+ hblue = *blue++;
+ if (transp)
+ htransp = *transp++;
+ if (info->fbops->fb_setcolreg(start++,
+ hred, hgreen, hblue,
+ htransp, info))
+ break;
+ }
+ }
+ if (rc == 0)
+ fb_copy_cmap(cmap, &info->cmap);
+
+ return rc;
+}
+
+int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
+{
+ int rc, size = cmap->len * sizeof(u16);
+ struct fb_cmap umap;
+
+ if (size < 0 || size < cmap->len)
+ return -E2BIG;
+
+ memset(&umap, 0, sizeof(struct fb_cmap));
+ rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
+ GFP_KERNEL);
+ if (rc)
+ return rc;
+ if (copy_from_user(umap.red, cmap->red, size) ||
+ copy_from_user(umap.green, cmap->green, size) ||
+ copy_from_user(umap.blue, cmap->blue, size) ||
+ (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
+ rc = -EFAULT;
+ goto out;
+ }
+ umap.start = cmap->start;
+ if (!lock_fb_info(info)) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ rc = fb_set_cmap(&umap, info);
+ unlock_fb_info(info);
+out:
+ fb_dealloc_cmap(&umap);
+ return rc;
+}
+
+/**
+ * fb_default_cmap - get default colormap
+ * @len: size of palette for a depth
+ *
+ * Gets the default colormap for a specific screen depth. @len
+ * is the size of the palette for a particular screen depth.
+ *
+ * Returns pointer to a frame buffer colormap structure.
+ *
+ */
+
+const struct fb_cmap *fb_default_cmap(int len)
+{
+ if (len <= 2)
+ return &default_2_colors;
+ if (len <= 4)
+ return &default_4_colors;
+ if (len <= 8)
+ return &default_8_colors;
+ return &default_16_colors;
+}
+
+
+/**
+ * fb_invert_cmaps - invert all defaults colormaps
+ *
+ * Invert all default colormaps.
+ *
+ */
+
+void fb_invert_cmaps(void)
+{
+ u_int i;
+
+ for (i = 0; i < ARRAY_SIZE(red2); i++) {
+ red2[i] = ~red2[i];
+ green2[i] = ~green2[i];
+ blue2[i] = ~blue2[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(red4); i++) {
+ red4[i] = ~red4[i];
+ green4[i] = ~green4[i];
+ blue4[i] = ~blue4[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(red8); i++) {
+ red8[i] = ~red8[i];
+ green8[i] = ~green8[i];
+ blue8[i] = ~blue8[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(red16); i++) {
+ red16[i] = ~red16[i];
+ green16[i] = ~green16[i];
+ blue16[i] = ~blue16[i];
+ }
+}
+
+
+ /*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(fb_alloc_cmap);
+EXPORT_SYMBOL(fb_dealloc_cmap);
+EXPORT_SYMBOL(fb_copy_cmap);
+EXPORT_SYMBOL(fb_set_cmap);
+EXPORT_SYMBOL(fb_default_cmap);
+EXPORT_SYMBOL(fb_invert_cmaps);
--- /dev/null
+/*
+ * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings
+ *
+ * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
+ *
+ * Based from the VESA(TM) Coordinated Video Timing Generator by
+ * Graham Loveridge April 9, 2003 available at
+ * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#define FB_CVT_CELLSIZE 8
+#define FB_CVT_GTF_C 40
+#define FB_CVT_GTF_J 20
+#define FB_CVT_GTF_K 128
+#define FB_CVT_GTF_M 600
+#define FB_CVT_MIN_VSYNC_BP 550
+#define FB_CVT_MIN_VPORCH 3
+#define FB_CVT_MIN_BPORCH 6
+
+#define FB_CVT_RB_MIN_VBLANK 460
+#define FB_CVT_RB_HBLANK 160
+#define FB_CVT_RB_V_FPORCH 3
+
+#define FB_CVT_FLAG_REDUCED_BLANK 1
+#define FB_CVT_FLAG_MARGINS 2
+#define FB_CVT_FLAG_INTERLACED 4
+
+struct fb_cvt_data {
+ u32 xres;
+ u32 yres;
+ u32 refresh;
+ u32 f_refresh;
+ u32 pixclock;
+ u32 hperiod;
+ u32 hblank;
+ u32 hfreq;
+ u32 htotal;
+ u32 vtotal;
+ u32 vsync;
+ u32 hsync;
+ u32 h_front_porch;
+ u32 h_back_porch;
+ u32 v_front_porch;
+ u32 v_back_porch;
+ u32 h_margin;
+ u32 v_margin;
+ u32 interlace;
+ u32 aspect_ratio;
+ u32 active_pixels;
+ u32 flags;
+ u32 status;
+};
+
+static const unsigned char fb_cvt_vbi_tab[] = {
+ 4, /* 4:3 */
+ 5, /* 16:9 */
+ 6, /* 16:10 */
+ 7, /* 5:4 */
+ 7, /* 15:9 */
+ 8, /* reserved */
+ 9, /* reserved */
+ 10 /* custom */
+};
+
+/* returns hperiod * 1000 */
+static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
+{
+ u32 num = 1000000000/cvt->f_refresh;
+ u32 den;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
+ num -= FB_CVT_RB_MIN_VBLANK * 1000;
+ den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin);
+ } else {
+ num -= FB_CVT_MIN_VSYNC_BP * 1000;
+ den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2
+ + FB_CVT_MIN_VPORCH + cvt->interlace/2);
+ }
+
+ return 2 * (num/den);
+}
+
+/* returns ideal duty cycle * 1000 */
+static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt)
+{
+ u32 c_prime = (FB_CVT_GTF_C - FB_CVT_GTF_J) *
+ (FB_CVT_GTF_K) + 256 * FB_CVT_GTF_J;
+ u32 m_prime = (FB_CVT_GTF_K * FB_CVT_GTF_M);
+ u32 h_period_est = cvt->hperiod;
+
+ return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256;
+}
+
+static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
+{
+ u32 hblank = 0;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
+ hblank = FB_CVT_RB_HBLANK;
+ else {
+ u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt);
+ u32 active_pixels = cvt->active_pixels;
+
+ if (ideal_duty_cycle < 20000)
+ hblank = (active_pixels * 20000)/
+ (100000 - 20000);
+ else {
+ hblank = (active_pixels * ideal_duty_cycle)/
+ (100000 - ideal_duty_cycle);
+ }
+ }
+
+ hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
+
+ return hblank;
+}
+
+static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
+{
+ u32 hsync;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
+ hsync = 32;
+ else
+ hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
+
+ hsync &= ~(FB_CVT_CELLSIZE - 1);
+ return hsync;
+}
+
+static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
+{
+ u32 vbi_lines, min_vbi_lines, act_vbi_lines;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
+ vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1;
+ min_vbi_lines = FB_CVT_RB_V_FPORCH + cvt->vsync +
+ FB_CVT_MIN_BPORCH;
+
+ } else {
+ vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
+ FB_CVT_MIN_VPORCH;
+ min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
+ FB_CVT_MIN_VPORCH;
+ }
+
+ if (vbi_lines < min_vbi_lines)
+ act_vbi_lines = min_vbi_lines;
+ else
+ act_vbi_lines = vbi_lines;
+
+ return act_vbi_lines;
+}
+
+static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
+{
+ u32 vtotal = cvt->yres/cvt->interlace;
+
+ vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
+ vtotal |= cvt->interlace/2;
+
+ return vtotal;
+}
+
+static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
+{
+ u32 pixclock;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
+ pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
+ else
+ pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
+
+ pixclock /= 250;
+ pixclock *= 250;
+ pixclock *= 1000;
+
+ return pixclock;
+}
+
+static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
+{
+ u32 xres = cvt->xres;
+ u32 yres = cvt->yres;
+ u32 aspect = -1;
+
+ if (xres == (yres * 4)/3 && !((yres * 4) % 3))
+ aspect = 0;
+ else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
+ aspect = 1;
+ else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
+ aspect = 2;
+ else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
+ aspect = 3;
+ else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
+ aspect = 4;
+ else {
+ printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
+ "standard\n");
+ aspect = 7;
+ cvt->status = 1;
+ }
+
+ return aspect;
+}
+
+static void fb_cvt_print_name(struct fb_cvt_data *cvt)
+{
+ u32 pixcount, pixcount_mod;
+ int cnt = 255, offset = 0, read = 0;
+ u8 *buf = kzalloc(256, GFP_KERNEL);
+
+ if (!buf)
+ return;
+
+ pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000;
+ pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000;
+ pixcount_mod /= 1000;
+
+ read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ",
+ cvt->xres, cvt->yres, cvt->refresh);
+ offset += read;
+ cnt -= read;
+
+ if (cvt->status)
+ snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega "
+ "Pixel Image\n", pixcount, pixcount_mod);
+ else {
+ if (pixcount) {
+ read = snprintf(buf+offset, cnt, "%d", pixcount);
+ cnt -= read;
+ offset += read;
+ }
+
+ read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod);
+ cnt -= read;
+ offset += read;
+
+ if (cvt->aspect_ratio == 0)
+ read = snprintf(buf+offset, cnt, "3");
+ else if (cvt->aspect_ratio == 3)
+ read = snprintf(buf+offset, cnt, "4");
+ else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4)
+ read = snprintf(buf+offset, cnt, "9");
+ else if (cvt->aspect_ratio == 2)
+ read = snprintf(buf+offset, cnt, "A");
+ else
+ read = 0;
+ cnt -= read;
+ offset += read;
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
+ read = snprintf(buf+offset, cnt, "-R");
+ cnt -= read;
+ offset += read;
+ }
+ }
+
+ printk(KERN_INFO "%s\n", buf);
+ kfree(buf);
+}
+
+static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt,
+ struct fb_videomode *mode)
+{
+ mode->refresh = cvt->f_refresh;
+ mode->pixclock = KHZ2PICOS(cvt->pixclock/1000);
+ mode->left_margin = cvt->h_back_porch;
+ mode->right_margin = cvt->h_front_porch;
+ mode->hsync_len = cvt->hsync;
+ mode->upper_margin = cvt->v_back_porch;
+ mode->lower_margin = cvt->v_front_porch;
+ mode->vsync_len = cvt->vsync;
+
+ mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);
+
+ if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
+ mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ else
+ mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+}
+
+/*
+ * fb_find_mode_cvt - calculate mode using VESA(TM) CVT
+ * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be
+ * pre-filled with the desired values
+ * @margins: add margin to calculation (1.8% of xres and yres)
+ * @rb: compute with reduced blanking (for flatpanels)
+ *
+ * RETURNS:
+ * 0 for success
+ * @mode is filled with computed values. If interlaced, the refresh field
+ * will be filled with the field rate (2x the frame rate)
+ *
+ * DESCRIPTION:
+ * Computes video timings using VESA(TM) Coordinated Video Timings
+ */
+int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
+{
+ struct fb_cvt_data cvt;
+
+ memset(&cvt, 0, sizeof(cvt));
+
+ if (margins)
+ cvt.flags |= FB_CVT_FLAG_MARGINS;
+
+ if (rb)
+ cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK;
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ cvt.flags |= FB_CVT_FLAG_INTERLACED;
+
+ cvt.xres = mode->xres;
+ cvt.yres = mode->yres;
+ cvt.refresh = mode->refresh;
+ cvt.f_refresh = cvt.refresh;
+ cvt.interlace = 1;
+
+ if (!cvt.xres || !cvt.yres || !cvt.refresh) {
+ printk(KERN_INFO "fbcvt: Invalid input parameters\n");
+ return 1;
+ }
+
+ if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 ||
+ cvt.refresh == 85)) {
+ printk(KERN_INFO "fbcvt: Refresh rate not CVT "
+ "standard\n");
+ cvt.status = 1;
+ }
+
+ cvt.xres &= ~(FB_CVT_CELLSIZE - 1);
+
+ if (cvt.flags & FB_CVT_FLAG_INTERLACED) {
+ cvt.interlace = 2;
+ cvt.f_refresh *= 2;
+ }
+
+ if (cvt.flags & FB_CVT_FLAG_REDUCED_BLANK) {
+ if (cvt.refresh != 60) {
+ printk(KERN_INFO "fbcvt: 60Hz refresh rate "
+ "advised for reduced blanking\n");
+ cvt.status = 1;
+ }
+ }
+
+ if (cvt.flags & FB_CVT_FLAG_MARGINS) {
+ cvt.h_margin = (cvt.xres * 18)/1000;
+ cvt.h_margin &= ~(FB_CVT_CELLSIZE - 1);
+ cvt.v_margin = ((cvt.yres/cvt.interlace)* 18)/1000;
+ }
+
+ cvt.aspect_ratio = fb_cvt_aspect_ratio(&cvt);
+ cvt.active_pixels = cvt.xres + 2 * cvt.h_margin;
+ cvt.hperiod = fb_cvt_hperiod(&cvt);
+ cvt.vsync = fb_cvt_vbi_tab[cvt.aspect_ratio];
+ cvt.vtotal = fb_cvt_vtotal(&cvt);
+ cvt.hblank = fb_cvt_hblank(&cvt);
+ cvt.htotal = cvt.active_pixels + cvt.hblank;
+ cvt.hsync = fb_cvt_hsync(&cvt);
+ cvt.pixclock = fb_cvt_pixclock(&cvt);
+ cvt.hfreq = cvt.pixclock/cvt.htotal;
+ cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin;
+ cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch +
+ 2 * cvt.h_margin;
+ cvt.v_back_porch = 3 + cvt.v_margin;
+ cvt.v_front_porch = cvt.vtotal - cvt.yres/cvt.interlace -
+ cvt.v_back_porch - cvt.vsync;
+ fb_cvt_print_name(&cvt);
+ fb_cvt_convert_to_mode(&cvt, mode);
+
+ return 0;
+}
--- /dev/null
+/*
+ * linux/drivers/video/fbmem.c
+ *
+ * Copyright (C) 1994 Martin Schaller
+ *
+ * 2001 - Documented with DocBook
+ * - Brad Douglas <brad@neruo.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+
+#include <linux/compat.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/vt.h>
+#include <linux/init.h>
+#include <linux/linux_logo.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/console.h>
+#include <linux/kmod.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/efi.h>
+#include <linux/fb.h>
+
+#include <asm/fb.h>
+
+
+ /*
+ * Frame buffer device initialization and setup routines
+ */
+
+#define FBPIXMAPSIZE (1024 * 8)
+
+static DEFINE_MUTEX(registration_lock);
+
+struct fb_info *registered_fb[FB_MAX] __read_mostly;
+EXPORT_SYMBOL(registered_fb);
+
+int num_registered_fb __read_mostly;
+EXPORT_SYMBOL(num_registered_fb);
+
+static struct fb_info *get_fb_info(unsigned int idx)
+{
+ struct fb_info *fb_info;
+
+ if (idx >= FB_MAX)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(®istration_lock);
+ fb_info = registered_fb[idx];
+ if (fb_info)
+ atomic_inc(&fb_info->count);
+ mutex_unlock(®istration_lock);
+
+ return fb_info;
+}
+
+static void put_fb_info(struct fb_info *fb_info)
+{
+ if (!atomic_dec_and_test(&fb_info->count))
+ return;
+ if (fb_info->fbops->fb_destroy)
+ fb_info->fbops->fb_destroy(fb_info);
+}
+
+int lock_fb_info(struct fb_info *info)
+{
+ mutex_lock(&info->lock);
+ if (!info->fbops) {
+ mutex_unlock(&info->lock);
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(lock_fb_info);
+
+/*
+ * Helpers
+ */
+
+int fb_get_color_depth(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix)
+{
+ int depth = 0;
+
+ if (fix->visual == FB_VISUAL_MONO01 ||
+ fix->visual == FB_VISUAL_MONO10)
+ depth = 1;
+ else {
+ if (var->green.length == var->blue.length &&
+ var->green.length == var->red.length &&
+ var->green.offset == var->blue.offset &&
+ var->green.offset == var->red.offset)
+ depth = var->green.length;
+ else
+ depth = var->green.length + var->red.length +
+ var->blue.length;
+ }
+
+ return depth;
+}
+EXPORT_SYMBOL(fb_get_color_depth);
+
+/*
+ * Data padding functions.
+ */
+void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
+{
+ __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
+}
+EXPORT_SYMBOL(fb_pad_aligned_buffer);
+
+void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
+ u32 shift_high, u32 shift_low, u32 mod)
+{
+ u8 mask = (u8) (0xfff << shift_high), tmp;
+ int i, j;
+
+ for (i = height; i--; ) {
+ for (j = 0; j < idx; j++) {
+ tmp = dst[j];
+ tmp &= mask;
+ tmp |= *src >> shift_low;
+ dst[j] = tmp;
+ tmp = *src << shift_high;
+ dst[j+1] = tmp;
+ src++;
+ }
+ tmp = dst[idx];
+ tmp &= mask;
+ tmp |= *src >> shift_low;
+ dst[idx] = tmp;
+ if (shift_high < mod) {
+ tmp = *src << shift_high;
+ dst[idx+1] = tmp;
+ }
+ src++;
+ dst += d_pitch;
+ }
+}
+EXPORT_SYMBOL(fb_pad_unaligned_buffer);
+
+/*
+ * we need to lock this section since fb_cursor
+ * may use fb_imageblit()
+ */
+char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
+{
+ u32 align = buf->buf_align - 1, offset;
+ char *addr = buf->addr;
+
+ /* If IO mapped, we need to sync before access, no sharing of
+ * the pixmap is done
+ */
+ if (buf->flags & FB_PIXMAP_IO) {
+ if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
+ info->fbops->fb_sync(info);
+ return addr;
+ }
+
+ /* See if we fit in the remaining pixmap space */
+ offset = buf->offset + align;
+ offset &= ~align;
+ if (offset + size > buf->size) {
+ /* We do not fit. In order to be able to re-use the buffer,
+ * we must ensure no asynchronous DMA'ing or whatever operation
+ * is in progress, we sync for that.
+ */
+ if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
+ info->fbops->fb_sync(info);
+ offset = 0;
+ }
+ buf->offset = offset + size;
+ addr += offset;
+
+ return addr;
+}
+EXPORT_SYMBOL(fb_get_buffer_offset);
+
+#ifdef CONFIG_LOGO
+
+static inline unsigned safe_shift(unsigned d, int n)
+{
+ return n < 0 ? d >> -n : d << n;
+}
+
+static void fb_set_logocmap(struct fb_info *info,
+ const struct linux_logo *logo)
+{
+ struct fb_cmap palette_cmap;
+ u16 palette_green[16];
+ u16 palette_blue[16];
+ u16 palette_red[16];
+ int i, j, n;
+ const unsigned char *clut = logo->clut;
+
+ palette_cmap.start = 0;
+ palette_cmap.len = 16;
+ palette_cmap.red = palette_red;
+ palette_cmap.green = palette_green;
+ palette_cmap.blue = palette_blue;
+ palette_cmap.transp = NULL;
+
+ for (i = 0; i < logo->clutsize; i += n) {
+ n = logo->clutsize - i;
+ /* palette_cmap provides space for only 16 colors at once */
+ if (n > 16)
+ n = 16;
+ palette_cmap.start = 32 + i;
+ palette_cmap.len = n;
+ for (j = 0; j < n; ++j) {
+ palette_cmap.red[j] = clut[0] << 8 | clut[0];
+ palette_cmap.green[j] = clut[1] << 8 | clut[1];
+ palette_cmap.blue[j] = clut[2] << 8 | clut[2];
+ clut += 3;
+ }
+ fb_set_cmap(&palette_cmap, info);
+ }
+}
+
+static void fb_set_logo_truepalette(struct fb_info *info,
+ const struct linux_logo *logo,
+ u32 *palette)
+{
+ static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
+ unsigned char redmask, greenmask, bluemask;
+ int redshift, greenshift, blueshift;
+ int i;
+ const unsigned char *clut = logo->clut;
+
+ /*
+ * We have to create a temporary palette since console palette is only
+ * 16 colors long.
+ */
+ /* Bug: Doesn't obey msb_right ... (who needs that?) */
+ redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
+ greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
+ bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
+ redshift = info->var.red.offset - (8 - info->var.red.length);
+ greenshift = info->var.green.offset - (8 - info->var.green.length);
+ blueshift = info->var.blue.offset - (8 - info->var.blue.length);
+
+ for ( i = 0; i < logo->clutsize; i++) {
+ palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
+ safe_shift((clut[1] & greenmask), greenshift) |
+ safe_shift((clut[2] & bluemask), blueshift));
+ clut += 3;
+ }
+}
+
+static void fb_set_logo_directpalette(struct fb_info *info,
+ const struct linux_logo *logo,
+ u32 *palette)
+{
+ int redshift, greenshift, blueshift;
+ int i;
+
+ redshift = info->var.red.offset;
+ greenshift = info->var.green.offset;
+ blueshift = info->var.blue.offset;
+
+ for (i = 32; i < 32 + logo->clutsize; i++)
+ palette[i] = i << redshift | i << greenshift | i << blueshift;
+}
+
+static void fb_set_logo(struct fb_info *info,
+ const struct linux_logo *logo, u8 *dst,
+ int depth)
+{
+ int i, j, k;
+ const u8 *src = logo->data;
+ u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
+ u8 fg = 1, d;
+
+ switch (fb_get_color_depth(&info->var, &info->fix)) {
+ case 1:
+ fg = 1;
+ break;
+ case 2:
+ fg = 3;
+ break;
+ default:
+ fg = 7;
+ break;
+ }
+
+ if (info->fix.visual == FB_VISUAL_MONO01 ||
+ info->fix.visual == FB_VISUAL_MONO10)
+ fg = ~((u8) (0xfff << info->var.green.length));
+
+ switch (depth) {
+ case 4:
+ for (i = 0; i < logo->height; i++)
+ for (j = 0; j < logo->width; src++) {
+ *dst++ = *src >> 4;
+ j++;
+ if (j < logo->width) {
+ *dst++ = *src & 0x0f;
+ j++;
+ }
+ }
+ break;
+ case 1:
+ for (i = 0; i < logo->height; i++) {
+ for (j = 0; j < logo->width; src++) {
+ d = *src ^ xor;
+ for (k = 7; k >= 0; k--) {
+ *dst++ = ((d >> k) & 1) ? fg : 0;
+ j++;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
+ * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
+ * the visual format and color depth of the framebuffer, the DAC, the
+ * pseudo_palette, and the logo data will be adjusted accordingly.
+ *
+ * Case 1 - linux_logo_clut224:
+ * Color exceeds the number of console colors (16), thus we set the hardware DAC
+ * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
+ *
+ * For visuals that require color info from the pseudo_palette, we also construct
+ * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
+ * will be set.
+ *
+ * Case 2 - linux_logo_vga16:
+ * The number of colors just matches the console colors, thus there is no need
+ * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
+ * each byte contains color information for two pixels (upper and lower nibble).
+ * To be consistent with fb_imageblit() usage, we therefore separate the two
+ * nibbles into separate bytes. The "depth" flag will be set to 4.
+ *
+ * Case 3 - linux_logo_mono:
+ * This is similar with Case 2. Each byte contains information for 8 pixels.
+ * We isolate each bit and expand each into a byte. The "depth" flag will
+ * be set to 1.
+ */
+static struct logo_data {
+ int depth;
+ int needs_directpalette;
+ int needs_truepalette;
+ int needs_cmapreset;
+ const struct linux_logo *logo;
+} fb_logo __read_mostly;
+
+static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
+{
+ u32 size = width * height, i;
+
+ out += size - 1;
+
+ for (i = size; i--; )
+ *out-- = *in++;
+}
+
+static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
+{
+ int i, j, h = height - 1;
+
+ for (i = 0; i < height; i++)
+ for (j = 0; j < width; j++)
+ out[height * j + h - i] = *in++;
+}
+
+static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
+{
+ int i, j, w = width - 1;
+
+ for (i = 0; i < height; i++)
+ for (j = 0; j < width; j++)
+ out[height * (w - j) + i] = *in++;
+}
+
+static void fb_rotate_logo(struct fb_info *info, u8 *dst,
+ struct fb_image *image, int rotate)
+{
+ u32 tmp;
+
+ if (rotate == FB_ROTATE_UD) {
+ fb_rotate_logo_ud(image->data, dst, image->width,
+ image->height);
+ image->dx = info->var.xres - image->width - image->dx;
+ image->dy = info->var.yres - image->height - image->dy;
+ } else if (rotate == FB_ROTATE_CW) {
+ fb_rotate_logo_cw(image->data, dst, image->width,
+ image->height);
+ tmp = image->width;
+ image->width = image->height;
+ image->height = tmp;
+ tmp = image->dy;
+ image->dy = image->dx;
+ image->dx = info->var.xres - image->width - tmp;
+ } else if (rotate == FB_ROTATE_CCW) {
+ fb_rotate_logo_ccw(image->data, dst, image->width,
+ image->height);
+ tmp = image->width;
+ image->width = image->height;
+ image->height = tmp;
+ tmp = image->dx;
+ image->dx = image->dy;
+ image->dy = info->var.yres - image->height - tmp;
+ }
+
+ image->data = dst;
+}
+
+static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
+ int rotate, unsigned int num)
+{
+ unsigned int x;
+
+ if (rotate == FB_ROTATE_UR) {
+ for (x = 0;
+ x < num && image->dx + image->width <= info->var.xres;
+ x++) {
+ info->fbops->fb_imageblit(info, image);
+ image->dx += image->width + 8;
+ }
+ } else if (rotate == FB_ROTATE_UD) {
+ for (x = 0; x < num && image->dx >= 0; x++) {
+ info->fbops->fb_imageblit(info, image);
+ image->dx -= image->width + 8;
+ }
+ } else if (rotate == FB_ROTATE_CW) {
+ for (x = 0;
+ x < num && image->dy + image->height <= info->var.yres;
+ x++) {
+ info->fbops->fb_imageblit(info, image);
+ image->dy += image->height + 8;
+ }
+ } else if (rotate == FB_ROTATE_CCW) {
+ for (x = 0; x < num && image->dy >= 0; x++) {
+ info->fbops->fb_imageblit(info, image);
+ image->dy -= image->height + 8;
+ }
+ }
+}
+
+static int fb_show_logo_line(struct fb_info *info, int rotate,
+ const struct linux_logo *logo, int y,
+ unsigned int n)
+{
+ u32 *palette = NULL, *saved_pseudo_palette = NULL;
+ unsigned char *logo_new = NULL, *logo_rotate = NULL;
+ struct fb_image image;
+
+ /* Return if the frame buffer is not mapped or suspended */
+ if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
+ info->flags & FBINFO_MODULE)
+ return 0;
+
+ image.depth = 8;
+ image.data = logo->data;
+
+ if (fb_logo.needs_cmapreset)
+ fb_set_logocmap(info, logo);
+
+ if (fb_logo.needs_truepalette ||
+ fb_logo.needs_directpalette) {
+ palette = kmalloc(256 * 4, GFP_KERNEL);
+ if (palette == NULL)
+ return 0;
+
+ if (fb_logo.needs_truepalette)
+ fb_set_logo_truepalette(info, logo, palette);
+ else
+ fb_set_logo_directpalette(info, logo, palette);
+
+ saved_pseudo_palette = info->pseudo_palette;
+ info->pseudo_palette = palette;
+ }
+
+ if (fb_logo.depth <= 4) {
+ logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);
+ if (logo_new == NULL) {
+ kfree(palette);
+ if (saved_pseudo_palette)
+ info->pseudo_palette = saved_pseudo_palette;
+ return 0;
+ }
+ image.data = logo_new;
+ fb_set_logo(info, logo, logo_new, fb_logo.depth);
+ }
+
+ image.dx = 0;
+ image.dy = y;
+ image.width = logo->width;
+ image.height = logo->height;
+
+ if (rotate) {
+ logo_rotate = kmalloc(logo->width *
+ logo->height, GFP_KERNEL);
+ if (logo_rotate)
+ fb_rotate_logo(info, logo_rotate, &image, rotate);
+ }
+
+ fb_do_show_logo(info, &image, rotate, n);
+
+ kfree(palette);
+ if (saved_pseudo_palette != NULL)
+ info->pseudo_palette = saved_pseudo_palette;
+ kfree(logo_new);
+ kfree(logo_rotate);
+ return logo->height;
+}
+
+
+#ifdef CONFIG_FB_LOGO_EXTRA
+
+#define FB_LOGO_EX_NUM_MAX 10
+static struct logo_data_extra {
+ const struct linux_logo *logo;
+ unsigned int n;
+} fb_logo_ex[FB_LOGO_EX_NUM_MAX];
+static unsigned int fb_logo_ex_num;
+
+void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
+{
+ if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
+ return;
+
+ fb_logo_ex[fb_logo_ex_num].logo = logo;
+ fb_logo_ex[fb_logo_ex_num].n = n;
+ fb_logo_ex_num++;
+}
+
+static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
+ unsigned int yres)
+{
+ unsigned int i;
+
+ /* FIXME: logo_ex supports only truecolor fb. */
+ if (info->fix.visual != FB_VISUAL_TRUECOLOR)
+ fb_logo_ex_num = 0;
+
+ for (i = 0; i < fb_logo_ex_num; i++) {
+ if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
+ fb_logo_ex[i].logo = NULL;
+ continue;
+ }
+ height += fb_logo_ex[i].logo->height;
+ if (height > yres) {
+ height -= fb_logo_ex[i].logo->height;
+ fb_logo_ex_num = i;
+ break;
+ }
+ }
+ return height;
+}
+
+static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
+{
+ unsigned int i;
+
+ for (i = 0; i < fb_logo_ex_num; i++)
+ y += fb_show_logo_line(info, rotate,
+ fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
+
+ return y;
+}
+
+#else /* !CONFIG_FB_LOGO_EXTRA */
+
+static inline int fb_prepare_extra_logos(struct fb_info *info,
+ unsigned int height,
+ unsigned int yres)
+{
+ return height;
+}
+
+static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
+{
+ return y;
+}
+
+#endif /* CONFIG_FB_LOGO_EXTRA */
+
+
+int fb_prepare_logo(struct fb_info *info, int rotate)
+{
+ int depth = fb_get_color_depth(&info->var, &info->fix);
+ unsigned int yres;
+
+ memset(&fb_logo, 0, sizeof(struct logo_data));
+
+ if (info->flags & FBINFO_MISC_TILEBLITTING ||
+ info->flags & FBINFO_MODULE)
+ return 0;
+
+ if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ depth = info->var.blue.length;
+ if (info->var.red.length < depth)
+ depth = info->var.red.length;
+ if (info->var.green.length < depth)
+ depth = info->var.green.length;
+ }
+
+ if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
+ /* assume console colormap */
+ depth = 4;
+ }
+
+ /* Return if no suitable logo was found */
+ fb_logo.logo = fb_find_logo(depth);
+
+ if (!fb_logo.logo) {
+ return 0;
+ }
+
+ if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
+ yres = info->var.yres;
+ else
+ yres = info->var.xres;
+
+ if (fb_logo.logo->height > yres) {
+ fb_logo.logo = NULL;
+ return 0;
+ }
+
+ /* What depth we asked for might be different from what we get */
+ if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
+ fb_logo.depth = 8;
+ else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
+ fb_logo.depth = 4;
+ else
+ fb_logo.depth = 1;
+
+
+ if (fb_logo.depth > 4 && depth > 4) {
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ fb_logo.needs_truepalette = 1;
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ fb_logo.needs_directpalette = 1;
+ fb_logo.needs_cmapreset = 1;
+ break;
+ case FB_VISUAL_PSEUDOCOLOR:
+ fb_logo.needs_cmapreset = 1;
+ break;
+ }
+ }
+
+ return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);
+}
+
+int fb_show_logo(struct fb_info *info, int rotate)
+{
+ int y;
+
+ y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
+ num_online_cpus());
+ y = fb_show_extra_logos(info, y, rotate);
+
+ return y;
+}
+#else
+int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
+int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
+#endif /* CONFIG_LOGO */
+EXPORT_SYMBOL(fb_show_logo);
+
+static void *fb_seq_start(struct seq_file *m, loff_t *pos)
+{
+ mutex_lock(®istration_lock);
+ return (*pos < FB_MAX) ? pos : NULL;
+}
+
+static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ (*pos)++;
+ return (*pos < FB_MAX) ? pos : NULL;
+}
+
+static void fb_seq_stop(struct seq_file *m, void *v)
+{
+ mutex_unlock(®istration_lock);
+}
+
+static int fb_seq_show(struct seq_file *m, void *v)
+{
+ int i = *(loff_t *)v;
+ struct fb_info *fi = registered_fb[i];
+
+ if (fi)
+ seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
+ return 0;
+}
+
+static const struct seq_operations proc_fb_seq_ops = {
+ .start = fb_seq_start,
+ .next = fb_seq_next,
+ .stop = fb_seq_stop,
+ .show = fb_seq_show,
+};
+
+static int proc_fb_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_fb_seq_ops);
+}
+
+static const struct file_operations fb_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = proc_fb_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*
+ * We hold a reference to the fb_info in file->private_data,
+ * but if the current registered fb has changed, we don't
+ * actually want to use it.
+ *
+ * So look up the fb_info using the inode minor number,
+ * and just verify it against the reference we have.
+ */
+static struct fb_info *file_fb_info(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ int fbidx = iminor(inode);
+ struct fb_info *info = registered_fb[fbidx];
+
+ if (info != file->private_data)
+ info = NULL;
+ return info;
+}
+
+static ssize_t
+fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ struct fb_info *info = file_fb_info(file);
+ u8 *buffer, *dst;
+ u8 __iomem *src;
+ int c, cnt = 0, err = 0;
+ unsigned long total_size;
+
+ if (!info || ! info->screen_base)
+ return -ENODEV;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ if (info->fbops->fb_read)
+ return info->fbops->fb_read(info, buf, count, ppos);
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p >= total_size)
+ return 0;
+
+ if (count >= total_size)
+ count = total_size;
+
+ if (count + p > total_size)
+ count = total_size - p;
+
+ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
+ GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ src = (u8 __iomem *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ while (count) {
+ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ dst = buffer;
+ fb_memcpy_fromfb(dst, src, c);
+ dst += c;
+ src += c;
+
+ if (copy_to_user(buf, buffer, c)) {
+ err = -EFAULT;
+ break;
+ }
+ *ppos += c;
+ buf += c;
+ cnt += c;
+ count -= c;
+ }
+
+ kfree(buffer);
+
+ return (err) ? err : cnt;
+}
+
+static ssize_t
+fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ struct fb_info *info = file_fb_info(file);
+ u8 *buffer, *src;
+ u8 __iomem *dst;
+ int c, cnt = 0, err = 0;
+ unsigned long total_size;
+
+ if (!info || !info->screen_base)
+ return -ENODEV;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ if (info->fbops->fb_write)
+ return info->fbops->fb_write(info, buf, count, ppos);
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
+ GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ dst = (u8 __iomem *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ while (count) {
+ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ src = buffer;
+
+ if (copy_from_user(src, buf, c)) {
+ err = -EFAULT;
+ break;
+ }
+
+ fb_memcpy_tofb(dst, src, c);
+ dst += c;
+ src += c;
+ *ppos += c;
+ buf += c;
+ cnt += c;
+ count -= c;
+ }
+
+ kfree(buffer);
+
+ return (cnt) ? cnt : err;
+}
+
+int
+fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ unsigned int yres = info->var.yres;
+ int err = 0;
+
+ if (var->yoffset > 0) {
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
+ err = -EINVAL;
+ else
+ yres = 0;
+ } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
+ err = -EINVAL;
+ }
+
+ if (var->xoffset > 0 && (!fix->xpanstep ||
+ (var->xoffset % fix->xpanstep)))
+ err = -EINVAL;
+
+ if (err || !info->fbops->fb_pan_display ||
+ var->yoffset > info->var.yres_virtual - yres ||
+ var->xoffset > info->var.xres_virtual - info->var.xres)
+ return -EINVAL;
+
+ if ((err = info->fbops->fb_pan_display(var, info)))
+ return err;
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+EXPORT_SYMBOL(fb_pan_display);
+
+static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
+ u32 activate)
+{
+ struct fb_event event;
+ struct fb_blit_caps caps, fbcaps;
+ int err = 0;
+
+ memset(&caps, 0, sizeof(caps));
+ memset(&fbcaps, 0, sizeof(fbcaps));
+ caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
+ event.info = info;
+ event.data = ∩︀
+ fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
+ info->fbops->fb_get_caps(info, &fbcaps, var);
+
+ if (((fbcaps.x ^ caps.x) & caps.x) ||
+ ((fbcaps.y ^ caps.y) & caps.y) ||
+ (fbcaps.len < caps.len))
+ err = -EINVAL;
+
+ return err;
+}
+
+int
+fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+ int flags = info->flags;
+ int ret = 0;
+
+ if (var->activate & FB_ACTIVATE_INV_MODE) {
+ struct fb_videomode mode1, mode2;
+
+ fb_var_to_videomode(&mode1, var);
+ fb_var_to_videomode(&mode2, &info->var);
+ /* make sure we don't delete the videomode of current var */
+ ret = fb_mode_is_equal(&mode1, &mode2);
+
+ if (!ret) {
+ struct fb_event event;
+
+ event.info = info;
+ event.data = &mode1;
+ ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
+ }
+
+ if (!ret)
+ fb_delete_videomode(&mode1, &info->modelist);
+
+
+ ret = (ret) ? -EINVAL : 0;
+ goto done;
+ }
+
+ if ((var->activate & FB_ACTIVATE_FORCE) ||
+ memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
+ u32 activate = var->activate;
+
+ /* When using FOURCC mode, make sure the red, green, blue and
+ * transp fields are set to 0.
+ */
+ if ((info->fix.capabilities & FB_CAP_FOURCC) &&
+ var->grayscale > 1) {
+ if (var->red.offset || var->green.offset ||
+ var->blue.offset || var->transp.offset ||
+ var->red.length || var->green.length ||
+ var->blue.length || var->transp.length ||
+ var->red.msb_right || var->green.msb_right ||
+ var->blue.msb_right || var->transp.msb_right)
+ return -EINVAL;
+ }
+
+ if (!info->fbops->fb_check_var) {
+ *var = info->var;
+ goto done;
+ }
+
+ ret = info->fbops->fb_check_var(var, info);
+
+ if (ret)
+ goto done;
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ struct fb_var_screeninfo old_var;
+ struct fb_videomode mode;
+
+ if (info->fbops->fb_get_caps) {
+ ret = fb_check_caps(info, var, activate);
+
+ if (ret)
+ goto done;
+ }
+
+ old_var = info->var;
+ info->var = *var;
+
+ if (info->fbops->fb_set_par) {
+ ret = info->fbops->fb_set_par(info);
+
+ if (ret) {
+ info->var = old_var;
+ printk(KERN_WARNING "detected "
+ "fb_set_par error, "
+ "error code: %d\n", ret);
+ goto done;
+ }
+ }
+
+ fb_pan_display(info, &info->var);
+ fb_set_cmap(&info->cmap, info);
+ fb_var_to_videomode(&mode, &info->var);
+
+ if (info->modelist.prev && info->modelist.next &&
+ !list_empty(&info->modelist))
+ ret = fb_add_videomode(&mode, &info->modelist);
+
+ if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
+ struct fb_event event;
+ int evnt = (activate & FB_ACTIVATE_ALL) ?
+ FB_EVENT_MODE_CHANGE_ALL :
+ FB_EVENT_MODE_CHANGE;
+
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ event.info = info;
+ event.data = &mode;
+ fb_notifier_call_chain(evnt, &event);
+ }
+ }
+ }
+
+ done:
+ return ret;
+}
+EXPORT_SYMBOL(fb_set_var);
+
+int
+fb_blank(struct fb_info *info, int blank)
+{
+ struct fb_event event;
+ int ret = -EINVAL, early_ret;
+
+ if (blank > FB_BLANK_POWERDOWN)
+ blank = FB_BLANK_POWERDOWN;
+
+ event.info = info;
+ event.data = ␣
+
+ early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);
+
+ if (info->fbops->fb_blank)
+ ret = info->fbops->fb_blank(blank, info);
+
+ if (!ret)
+ fb_notifier_call_chain(FB_EVENT_BLANK, &event);
+ else {
+ /*
+ * if fb_blank is failed then revert effects of
+ * the early blank event.
+ */
+ if (!early_ret)
+ fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(fb_blank);
+
+static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct fb_ops *fb;
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ struct fb_con2fbmap con2fb;
+ struct fb_cmap cmap_from;
+ struct fb_cmap_user cmap;
+ struct fb_event event;
+ void __user *argp = (void __user *)arg;
+ long ret = 0;
+
+ switch (cmd) {
+ case FBIOGET_VSCREENINFO:
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ var = info->var;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
+ break;
+ case FBIOPUT_VSCREENINFO:
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ console_lock();
+ if (!lock_fb_info(info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+ info->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ unlock_fb_info(info);
+ console_unlock();
+ if (!ret && copy_to_user(argp, &var, sizeof(var)))
+ ret = -EFAULT;
+ break;
+ case FBIOGET_FSCREENINFO:
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fix = info->fix;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
+ break;
+ case FBIOPUTCMAP:
+ if (copy_from_user(&cmap, argp, sizeof(cmap)))
+ return -EFAULT;
+ ret = fb_set_user_cmap(&cmap, info);
+ break;
+ case FBIOGETCMAP:
+ if (copy_from_user(&cmap, argp, sizeof(cmap)))
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ cmap_from = info->cmap;
+ unlock_fb_info(info);
+ ret = fb_cmap_to_user(&cmap_from, &cmap);
+ break;
+ case FBIOPAN_DISPLAY:
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ console_lock();
+ if (!lock_fb_info(info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+ ret = fb_pan_display(info, &var);
+ unlock_fb_info(info);
+ console_unlock();
+ if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
+ return -EFAULT;
+ break;
+ case FBIO_CURSOR:
+ ret = -EINVAL;
+ break;
+ case FBIOGET_CON2FBMAP:
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ con2fb.framebuffer = -1;
+ event.data = &con2fb;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ event.info = info;
+ fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
+ unlock_fb_info(info);
+ ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
+ break;
+ case FBIOPUT_CON2FBMAP:
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+ return -EINVAL;
+ if (!registered_fb[con2fb.framebuffer])
+ request_module("fb%d", con2fb.framebuffer);
+ if (!registered_fb[con2fb.framebuffer]) {
+ ret = -EINVAL;
+ break;
+ }
+ event.data = &con2fb;
+ console_lock();
+ if (!lock_fb_info(info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+ event.info = info;
+ ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
+ unlock_fb_info(info);
+ console_unlock();
+ break;
+ case FBIOBLANK:
+ console_lock();
+ if (!lock_fb_info(info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+ info->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_blank(info, arg);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ unlock_fb_info(info);
+ console_unlock();
+ break;
+ default:
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fb = info->fbops;
+ if (fb->fb_ioctl)
+ ret = fb->fb_ioctl(info, cmd, arg);
+ else
+ ret = -ENOTTY;
+ unlock_fb_info(info);
+ }
+ return ret;
+}
+
+static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct fb_info *info = file_fb_info(file);
+
+ if (!info)
+ return -ENODEV;
+ return do_fb_ioctl(info, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+struct fb_fix_screeninfo32 {
+ char id[16];
+ compat_caddr_t smem_start;
+ u32 smem_len;
+ u32 type;
+ u32 type_aux;
+ u32 visual;
+ u16 xpanstep;
+ u16 ypanstep;
+ u16 ywrapstep;
+ u32 line_length;
+ compat_caddr_t mmio_start;
+ u32 mmio_len;
+ u32 accel;
+ u16 reserved[3];
+};
+
+struct fb_cmap32 {
+ u32 start;
+ u32 len;
+ compat_caddr_t red;
+ compat_caddr_t green;
+ compat_caddr_t blue;
+ compat_caddr_t transp;
+};
+
+static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct fb_cmap_user __user *cmap;
+ struct fb_cmap32 __user *cmap32;
+ __u32 data;
+ int err;
+
+ cmap = compat_alloc_user_space(sizeof(*cmap));
+ cmap32 = compat_ptr(arg);
+
+ if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32)))
+ return -EFAULT;
+
+ if (get_user(data, &cmap32->red) ||
+ put_user(compat_ptr(data), &cmap->red) ||
+ get_user(data, &cmap32->green) ||
+ put_user(compat_ptr(data), &cmap->green) ||
+ get_user(data, &cmap32->blue) ||
+ put_user(compat_ptr(data), &cmap->blue) ||
+ get_user(data, &cmap32->transp) ||
+ put_user(compat_ptr(data), &cmap->transp))
+ return -EFAULT;
+
+ err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
+
+ if (!err) {
+ if (copy_in_user(&cmap32->start,
+ &cmap->start,
+ 2 * sizeof(__u32)))
+ err = -EFAULT;
+ }
+ return err;
+}
+
+static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
+ struct fb_fix_screeninfo32 __user *fix32)
+{
+ __u32 data;
+ int err;
+
+ err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
+
+ data = (__u32) (unsigned long) fix->smem_start;
+ err |= put_user(data, &fix32->smem_start);
+
+ err |= put_user(fix->smem_len, &fix32->smem_len);
+ err |= put_user(fix->type, &fix32->type);
+ err |= put_user(fix->type_aux, &fix32->type_aux);
+ err |= put_user(fix->visual, &fix32->visual);
+ err |= put_user(fix->xpanstep, &fix32->xpanstep);
+ err |= put_user(fix->ypanstep, &fix32->ypanstep);
+ err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
+ err |= put_user(fix->line_length, &fix32->line_length);
+
+ data = (__u32) (unsigned long) fix->mmio_start;
+ err |= put_user(data, &fix32->mmio_start);
+
+ err |= put_user(fix->mmio_len, &fix32->mmio_len);
+ err |= put_user(fix->accel, &fix32->accel);
+ err |= copy_to_user(fix32->reserved, fix->reserved,
+ sizeof(fix->reserved));
+
+ if (err)
+ return -EFAULT;
+ return 0;
+}
+
+static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ mm_segment_t old_fs;
+ struct fb_fix_screeninfo fix;
+ struct fb_fix_screeninfo32 __user *fix32;
+ int err;
+
+ fix32 = compat_ptr(arg);
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
+ set_fs(old_fs);
+
+ if (!err)
+ err = do_fscreeninfo_to_user(&fix, fix32);
+
+ return err;
+}
+
+static long fb_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct fb_info *info = file_fb_info(file);
+ struct fb_ops *fb;
+ long ret = -ENOIOCTLCMD;
+
+ if (!info)
+ return -ENODEV;
+ fb = info->fbops;
+ switch(cmd) {
+ case FBIOGET_VSCREENINFO:
+ case FBIOPUT_VSCREENINFO:
+ case FBIOPAN_DISPLAY:
+ case FBIOGET_CON2FBMAP:
+ case FBIOPUT_CON2FBMAP:
+ arg = (unsigned long) compat_ptr(arg);
+ case FBIOBLANK:
+ ret = do_fb_ioctl(info, cmd, arg);
+ break;
+
+ case FBIOGET_FSCREENINFO:
+ ret = fb_get_fscreeninfo(info, cmd, arg);
+ break;
+
+ case FBIOGETCMAP:
+ case FBIOPUTCMAP:
+ ret = fb_getput_cmap(info, cmd, arg);
+ break;
+
+ default:
+ if (fb->fb_compat_ioctl)
+ ret = fb->fb_compat_ioctl(info, cmd, arg);
+ break;
+ }
+ return ret;
+}
+#endif
+
+static int
+fb_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct fb_info *info = file_fb_info(file);
+ struct fb_ops *fb;
+ unsigned long mmio_pgoff;
+ unsigned long start;
+ u32 len;
+
+ if (!info)
+ return -ENODEV;
+ fb = info->fbops;
+ if (!fb)
+ return -ENODEV;
+ mutex_lock(&info->mm_lock);
+ if (fb->fb_mmap) {
+ int res;
+ res = fb->fb_mmap(info, vma);
+ mutex_unlock(&info->mm_lock);
+ return res;
+ }
+
+ /*
+ * Ugh. This can be either the frame buffer mapping, or
+ * if pgoff points past it, the mmio mapping.
+ */
+ start = info->fix.smem_start;
+ len = info->fix.smem_len;
+ mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
+ if (vma->vm_pgoff >= mmio_pgoff) {
+ if (info->var.accel_flags) {
+ mutex_unlock(&info->mm_lock);
+ return -EINVAL;
+ }
+
+ vma->vm_pgoff -= mmio_pgoff;
+ start = info->fix.mmio_start;
+ len = info->fix.mmio_len;
+ }
+ mutex_unlock(&info->mm_lock);
+
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ fb_pgprotect(file, vma, start);
+
+ return vm_iomap_memory(vma, start, len);
+}
+
+static int
+fb_open(struct inode *inode, struct file *file)
+__acquires(&info->lock)
+__releases(&info->lock)
+{
+ int fbidx = iminor(inode);
+ struct fb_info *info;
+ int res = 0;
+
+ info = get_fb_info(fbidx);
+ if (!info) {
+ request_module("fb%d", fbidx);
+ info = get_fb_info(fbidx);
+ if (!info)
+ return -ENODEV;
+ }
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ mutex_lock(&info->lock);
+ if (!try_module_get(info->fbops->owner)) {
+ res = -ENODEV;
+ goto out;
+ }
+ file->private_data = info;
+ if (info->fbops->fb_open) {
+ res = info->fbops->fb_open(info,1);
+ if (res)
+ module_put(info->fbops->owner);
+ }
+#ifdef CONFIG_FB_DEFERRED_IO
+ if (info->fbdefio)
+ fb_deferred_io_open(info, inode, file);
+#endif
+out:
+ mutex_unlock(&info->lock);
+ if (res)
+ put_fb_info(info);
+ return res;
+}
+
+static int
+fb_release(struct inode *inode, struct file *file)
+__acquires(&info->lock)
+__releases(&info->lock)
+{
+ struct fb_info * const info = file->private_data;
+
+ mutex_lock(&info->lock);
+ if (info->fbops->fb_release)
+ info->fbops->fb_release(info,1);
+ module_put(info->fbops->owner);
+ mutex_unlock(&info->lock);
+ put_fb_info(info);
+ return 0;
+}
+
+static const struct file_operations fb_fops = {
+ .owner = THIS_MODULE,
+ .read = fb_read,
+ .write = fb_write,
+ .unlocked_ioctl = fb_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = fb_compat_ioctl,
+#endif
+ .mmap = fb_mmap,
+ .open = fb_open,
+ .release = fb_release,
+#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
+ .get_unmapped_area = get_fb_unmapped_area,
+#endif
+#ifdef CONFIG_FB_DEFERRED_IO
+ .fsync = fb_deferred_io_fsync,
+#endif
+ .llseek = default_llseek,
+};
+
+struct class *fb_class;
+EXPORT_SYMBOL(fb_class);
+
+static int fb_check_foreignness(struct fb_info *fi)
+{
+ const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
+
+ fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
+
+#ifdef __BIG_ENDIAN
+ fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
+#else
+ fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
+#endif /* __BIG_ENDIAN */
+
+ if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
+ pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
+ "support this framebuffer\n", fi->fix.id);
+ return -ENOSYS;
+ } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
+ pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
+ "support this framebuffer\n", fi->fix.id);
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
+{
+ /* is the generic aperture base the same as the HW one */
+ if (gen->base == hw->base)
+ return true;
+ /* is the generic aperture base inside the hw base->hw base+size */
+ if (gen->base > hw->base && gen->base < hw->base + hw->size)
+ return true;
+ return false;
+}
+
+static bool fb_do_apertures_overlap(struct apertures_struct *gena,
+ struct apertures_struct *hwa)
+{
+ int i, j;
+ if (!hwa || !gena)
+ return false;
+
+ for (i = 0; i < hwa->count; ++i) {
+ struct aperture *h = &hwa->ranges[i];
+ for (j = 0; j < gena->count; ++j) {
+ struct aperture *g = &gena->ranges[j];
+ printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
+ (unsigned long long)g->base,
+ (unsigned long long)g->size,
+ (unsigned long long)h->base,
+ (unsigned long long)h->size);
+ if (apertures_overlap(g, h))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int do_unregister_framebuffer(struct fb_info *fb_info);
+
+#define VGA_FB_PHYS 0xA0000
+static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
+ const char *name, bool primary)
+{
+ int i, ret;
+
+ /* check all firmware fbs and kick off if the base addr overlaps */
+ for (i = 0 ; i < FB_MAX; i++) {
+ struct apertures_struct *gen_aper;
+ if (!registered_fb[i])
+ continue;
+
+ if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
+ continue;
+
+ gen_aper = registered_fb[i]->apertures;
+ if (fb_do_apertures_overlap(gen_aper, a) ||
+ (primary && gen_aper && gen_aper->count &&
+ gen_aper->ranges[0].base == VGA_FB_PHYS)) {
+
+ printk(KERN_INFO "fb: switching to %s from %s\n",
+ name, registered_fb[i]->fix.id);
+ ret = do_unregister_framebuffer(registered_fb[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int do_register_framebuffer(struct fb_info *fb_info)
+{
+ int i, ret;
+ struct fb_event event;
+ struct fb_videomode mode;
+
+ if (fb_check_foreignness(fb_info))
+ return -ENOSYS;
+
+ ret = do_remove_conflicting_framebuffers(fb_info->apertures,
+ fb_info->fix.id,
+ fb_is_primary_device(fb_info));
+ if (ret)
+ return ret;
+
+ if (num_registered_fb == FB_MAX)
+ return -ENXIO;
+
+ num_registered_fb++;
+ for (i = 0 ; i < FB_MAX; i++)
+ if (!registered_fb[i])
+ break;
+ fb_info->node = i;
+ atomic_set(&fb_info->count, 1);
+ mutex_init(&fb_info->lock);
+ mutex_init(&fb_info->mm_lock);
+
+ fb_info->dev = device_create(fb_class, fb_info->device,
+ MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
+ if (IS_ERR(fb_info->dev)) {
+ /* Not fatal */
+ printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
+ fb_info->dev = NULL;
+ } else
+ fb_init_device(fb_info);
+
+ if (fb_info->pixmap.addr == NULL) {
+ fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
+ if (fb_info->pixmap.addr) {
+ fb_info->pixmap.size = FBPIXMAPSIZE;
+ fb_info->pixmap.buf_align = 1;
+ fb_info->pixmap.scan_align = 1;
+ fb_info->pixmap.access_align = 32;
+ fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
+ }
+ }
+ fb_info->pixmap.offset = 0;
+
+ if (!fb_info->pixmap.blit_x)
+ fb_info->pixmap.blit_x = ~(u32)0;
+
+ if (!fb_info->pixmap.blit_y)
+ fb_info->pixmap.blit_y = ~(u32)0;
+
+ if (!fb_info->modelist.prev || !fb_info->modelist.next)
+ INIT_LIST_HEAD(&fb_info->modelist);
+
+ if (fb_info->skip_vt_switch)
+ pm_vt_switch_required(fb_info->dev, false);
+ else
+ pm_vt_switch_required(fb_info->dev, true);
+
+ fb_var_to_videomode(&mode, &fb_info->var);
+ fb_add_videomode(&mode, &fb_info->modelist);
+ registered_fb[i] = fb_info;
+
+ event.info = fb_info;
+ console_lock();
+ if (!lock_fb_info(fb_info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+
+ fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
+ unlock_fb_info(fb_info);
+ console_unlock();
+ return 0;
+}
+
+static int do_unregister_framebuffer(struct fb_info *fb_info)
+{
+ struct fb_event event;
+ int i, ret = 0;
+
+ i = fb_info->node;
+ if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
+ return -EINVAL;
+
+ console_lock();
+ if (!lock_fb_info(fb_info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+
+ event.info = fb_info;
+ ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
+ unlock_fb_info(fb_info);
+ console_unlock();
+
+ if (ret)
+ return -EINVAL;
+
+ pm_vt_switch_unregister(fb_info->dev);
+
+ unlink_framebuffer(fb_info);
+ if (fb_info->pixmap.addr &&
+ (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
+ kfree(fb_info->pixmap.addr);
+ fb_destroy_modelist(&fb_info->modelist);
+ registered_fb[i] = NULL;
+ num_registered_fb--;
+ fb_cleanup_device(fb_info);
+ event.info = fb_info;
+ console_lock();
+ fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
+ console_unlock();
+
+ /* this may free fb info */
+ put_fb_info(fb_info);
+ return 0;
+}
+
+int unlink_framebuffer(struct fb_info *fb_info)
+{
+ int i;
+
+ i = fb_info->node;
+ if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
+ return -EINVAL;
+
+ if (fb_info->dev) {
+ device_destroy(fb_class, MKDEV(FB_MAJOR, i));
+ fb_info->dev = NULL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(unlink_framebuffer);
+
+int remove_conflicting_framebuffers(struct apertures_struct *a,
+ const char *name, bool primary)
+{
+ int ret;
+
+ mutex_lock(®istration_lock);
+ ret = do_remove_conflicting_framebuffers(a, name, primary);
+ mutex_unlock(®istration_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(remove_conflicting_framebuffers);
+
+/**
+ * register_framebuffer - registers a frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Registers a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ */
+int
+register_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+
+ mutex_lock(®istration_lock);
+ ret = do_register_framebuffer(fb_info);
+ mutex_unlock(®istration_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(register_framebuffer);
+
+/**
+ * unregister_framebuffer - releases a frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Unregisters a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ * This function will also notify the framebuffer console
+ * to release the driver.
+ *
+ * This is meant to be called within a driver's module_exit()
+ * function. If this is called outside module_exit(), ensure
+ * that the driver implements fb_open() and fb_release() to
+ * check that no processes are using the device.
+ */
+int
+unregister_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+
+ mutex_lock(®istration_lock);
+ ret = do_unregister_framebuffer(fb_info);
+ mutex_unlock(®istration_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(unregister_framebuffer);
+
+/**
+ * fb_set_suspend - low level driver signals suspend
+ * @info: framebuffer affected
+ * @state: 0 = resuming, !=0 = suspending
+ *
+ * This is meant to be used by low level drivers to
+ * signal suspend/resume to the core & clients.
+ * It must be called with the console semaphore held
+ */
+void fb_set_suspend(struct fb_info *info, int state)
+{
+ struct fb_event event;
+
+ event.info = info;
+ if (state) {
+ fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
+ info->state = FBINFO_STATE_SUSPENDED;
+ } else {
+ info->state = FBINFO_STATE_RUNNING;
+ fb_notifier_call_chain(FB_EVENT_RESUME, &event);
+ }
+}
+EXPORT_SYMBOL(fb_set_suspend);
+
+/**
+ * fbmem_init - init frame buffer subsystem
+ *
+ * Initialize the frame buffer subsystem.
+ *
+ * NOTE: This function is _only_ to be called by drivers/char/mem.c.
+ *
+ */
+
+static int __init
+fbmem_init(void)
+{
+ proc_create("fb", 0, NULL, &fb_proc_fops);
+
+ if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
+ printk("unable to get major %d for fb devs\n", FB_MAJOR);
+
+ fb_class = class_create(THIS_MODULE, "graphics");
+ if (IS_ERR(fb_class)) {
+ printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
+ fb_class = NULL;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+module_init(fbmem_init);
+static void __exit
+fbmem_exit(void)
+{
+ remove_proc_entry("fb", NULL);
+ class_destroy(fb_class);
+ unregister_chrdev(FB_MAJOR, "fb");
+}
+
+module_exit(fbmem_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Framebuffer base");
+#else
+subsys_initcall(fbmem_init);
+#endif
+
+int fb_new_modelist(struct fb_info *info)
+{
+ struct fb_event event;
+ struct fb_var_screeninfo var = info->var;
+ struct list_head *pos, *n;
+ struct fb_modelist *modelist;
+ struct fb_videomode *m, mode;
+ int err = 1;
+
+ list_for_each_safe(pos, n, &info->modelist) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ m = &modelist->mode;
+ fb_videomode_to_var(&var, m);
+ var.activate = FB_ACTIVATE_TEST;
+ err = fb_set_var(info, &var);
+ fb_var_to_videomode(&mode, &var);
+ if (err || !fb_mode_is_equal(m, &mode)) {
+ list_del(pos);
+ kfree(pos);
+ }
+ }
+
+ err = 1;
+
+ if (!list_empty(&info->modelist)) {
+ event.info = info;
+ err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
+ }
+
+ return err;
+}
+
+static char *video_options[FB_MAX] __read_mostly;
+static int ofonly __read_mostly;
+
+/**
+ * fb_get_options - get kernel boot parameters
+ * @name: framebuffer name as it would appear in
+ * the boot parameter line
+ * (video=<name>:<options>)
+ * @option: the option will be stored here
+ *
+ * NOTE: Needed to maintain backwards compatibility
+ */
+int fb_get_options(const char *name, char **option)
+{
+ char *opt, *options = NULL;
+ int retval = 0;
+ int name_len = strlen(name), i;
+
+ if (name_len && ofonly && strncmp(name, "offb", 4))
+ retval = 1;
+
+ if (name_len && !retval) {
+ for (i = 0; i < FB_MAX; i++) {
+ if (video_options[i] == NULL)
+ continue;
+ if (!video_options[i][0])
+ continue;
+ opt = video_options[i];
+ if (!strncmp(name, opt, name_len) &&
+ opt[name_len] == ':')
+ options = opt + name_len + 1;
+ }
+ }
+ /* No match, pass global option */
+ if (!options && option && fb_mode_option)
+ options = kstrdup(fb_mode_option, GFP_KERNEL);
+ if (options && !strncmp(options, "off", 3))
+ retval = 1;
+
+ if (option)
+ *option = options;
+
+ return retval;
+}
+EXPORT_SYMBOL(fb_get_options);
+
+#ifndef MODULE
+/**
+ * video_setup - process command line options
+ * @options: string of options
+ *
+ * Process command line options for frame buffer subsystem.
+ *
+ * NOTE: This function is a __setup and __init function.
+ * It only stores the options. Drivers have to call
+ * fb_get_options() as necessary.
+ *
+ * Returns zero.
+ *
+ */
+static int __init video_setup(char *options)
+{
+ int i, global = 0;
+
+ if (!options || !*options)
+ global = 1;
+
+ if (!global && !strncmp(options, "ofonly", 6)) {
+ ofonly = 1;
+ global = 1;
+ }
+
+ if (!global && !strchr(options, ':')) {
+ fb_mode_option = options;
+ global = 1;
+ }
+
+ if (!global) {
+ for (i = 0; i < FB_MAX; i++) {
+ if (video_options[i] == NULL) {
+ video_options[i] = options;
+ break;
+ }
+
+ }
+ }
+
+ return 1;
+}
+__setup("video=", video_setup);
+#endif
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/drivers/video/fbmon.c
+ *
+ * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
+ *
+ * Credits:
+ *
+ * The EDID Parser is a conglomeration from the following sources:
+ *
+ * 1. SciTech SNAP Graphics Architecture
+ * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
+ *
+ * 2. XFree86 4.3.0, interpret_edid.c
+ * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
+ *
+ * 3. John Fremlin <vii@users.sourceforge.net> and
+ * Ani Joshi <ajoshi@unixbox.com>
+ *
+ * Generalized Timing Formula is derived from:
+ *
+ * GTF Spreadsheet by Andy Morrish (1/5/97)
+ * available at http://www.vesa.org
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <video/edid.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+#ifdef CONFIG_PPC_OF
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#endif
+#include "../edid.h"
+
+/*
+ * EDID parser
+ */
+
+#undef DEBUG /* define this for verbose EDID parsing output */
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(fmt,## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define FBMON_FIX_HEADER 1
+#define FBMON_FIX_INPUT 2
+#define FBMON_FIX_TIMINGS 3
+
+#ifdef CONFIG_FB_MODE_HELPERS
+struct broken_edid {
+ u8 manufacturer[4];
+ u32 model;
+ u32 fix;
+};
+
+static const struct broken_edid brokendb[] = {
+ /* DEC FR-PCXAV-YZ */
+ {
+ .manufacturer = "DEC",
+ .model = 0x073a,
+ .fix = FBMON_FIX_HEADER,
+ },
+ /* ViewSonic PF775a */
+ {
+ .manufacturer = "VSC",
+ .model = 0x5a44,
+ .fix = FBMON_FIX_INPUT,
+ },
+ /* Sharp UXGA? */
+ {
+ .manufacturer = "SHP",
+ .model = 0x138e,
+ .fix = FBMON_FIX_TIMINGS,
+ },
+};
+
+static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00
+};
+
+static void copy_string(unsigned char *c, unsigned char *s)
+{
+ int i;
+ c = c + 5;
+ for (i = 0; (i < 13 && *c != 0x0A); i++)
+ *(s++) = *(c++);
+ *s = 0;
+ while (i-- && (*--s == 0x20)) *s = 0;
+}
+
+static int edid_is_serial_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xff) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_ascii_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfe) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_limits_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfd) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_monitor_block(unsigned char *block)
+{
+ if ((block[0] == 0x00) && (block[1] == 0x00) &&
+ (block[2] == 0x00) && (block[3] == 0xfc) &&
+ (block[4] == 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int edid_is_timing_block(unsigned char *block)
+{
+ if ((block[0] != 0x00) || (block[1] != 0x00) ||
+ (block[2] != 0x00) || (block[4] != 0x00))
+ return 1;
+ else
+ return 0;
+}
+
+static int check_edid(unsigned char *edid)
+{
+ unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
+ unsigned char *b;
+ u32 model;
+ int i, fix = 0, ret = 0;
+
+ manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
+ manufacturer[1] = ((block[0] & 0x03) << 3) +
+ ((block[1] & 0xe0) >> 5) + '@';
+ manufacturer[2] = (block[1] & 0x1f) + '@';
+ manufacturer[3] = 0;
+ model = block[2] + (block[3] << 8);
+
+ for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
+ if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
+ brokendb[i].model == model) {
+ fix = brokendb[i].fix;
+ break;
+ }
+ }
+
+ switch (fix) {
+ case FBMON_FIX_HEADER:
+ for (i = 0; i < 8; i++) {
+ if (edid[i] != edid_v1_header[i]) {
+ ret = fix;
+ break;
+ }
+ }
+ break;
+ case FBMON_FIX_INPUT:
+ b = edid + EDID_STRUCT_DISPLAY;
+ /* Only if display is GTF capable will
+ the input type be reset to analog */
+ if (b[4] & 0x01 && b[0] & 0x80)
+ ret = fix;
+ break;
+ case FBMON_FIX_TIMINGS:
+ b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ ret = fix;
+
+ for (i = 0; i < 4; i++) {
+ if (edid_is_limits_block(b)) {
+ ret = 0;
+ break;
+ }
+
+ b += DETAILED_TIMING_DESCRIPTION_SIZE;
+ }
+
+ break;
+ }
+
+ if (ret)
+ printk("fbmon: The EDID Block of "
+ "Manufacturer: %s Model: 0x%x is known to "
+ "be broken,\n", manufacturer, model);
+
+ return ret;
+}
+
+static void fix_edid(unsigned char *edid, int fix)
+{
+ int i;
+ unsigned char *b, csum = 0;
+
+ switch (fix) {
+ case FBMON_FIX_HEADER:
+ printk("fbmon: trying a header reconstruct\n");
+ memcpy(edid, edid_v1_header, 8);
+ break;
+ case FBMON_FIX_INPUT:
+ printk("fbmon: trying to fix input type\n");
+ b = edid + EDID_STRUCT_DISPLAY;
+ b[0] &= ~0x80;
+ edid[127] += 0x80;
+ break;
+ case FBMON_FIX_TIMINGS:
+ printk("fbmon: trying to fix monitor timings\n");
+ b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++) {
+ if (!(edid_is_serial_block(b) ||
+ edid_is_ascii_block(b) ||
+ edid_is_monitor_block(b) ||
+ edid_is_timing_block(b))) {
+ b[0] = 0x00;
+ b[1] = 0x00;
+ b[2] = 0x00;
+ b[3] = 0xfd;
+ b[4] = 0x00;
+ b[5] = 60; /* vfmin */
+ b[6] = 60; /* vfmax */
+ b[7] = 30; /* hfmin */
+ b[8] = 75; /* hfmax */
+ b[9] = 17; /* pixclock - 170 MHz*/
+ b[10] = 0; /* GTF */
+ break;
+ }
+
+ b += DETAILED_TIMING_DESCRIPTION_SIZE;
+ }
+
+ for (i = 0; i < EDID_LENGTH - 1; i++)
+ csum += edid[i];
+
+ edid[127] = 256 - csum;
+ break;
+ }
+}
+
+static int edid_checksum(unsigned char *edid)
+{
+ unsigned char csum = 0, all_null = 0;
+ int i, err = 0, fix = check_edid(edid);
+
+ if (fix)
+ fix_edid(edid, fix);
+
+ for (i = 0; i < EDID_LENGTH; i++) {
+ csum += edid[i];
+ all_null |= edid[i];
+ }
+
+ if (csum == 0x00 && all_null) {
+ /* checksum passed, everything's good */
+ err = 1;
+ }
+
+ return err;
+}
+
+static int edid_check_header(unsigned char *edid)
+{
+ int i, err = 1, fix = check_edid(edid);
+
+ if (fix)
+ fix_edid(edid, fix);
+
+ for (i = 0; i < 8; i++) {
+ if (edid[i] != edid_v1_header[i])
+ err = 0;
+ }
+
+ return err;
+}
+
+static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs)
+{
+ specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
+ specs->manufacturer[1] = ((block[0] & 0x03) << 3) +
+ ((block[1] & 0xe0) >> 5) + '@';
+ specs->manufacturer[2] = (block[1] & 0x1f) + '@';
+ specs->manufacturer[3] = 0;
+ specs->model = block[2] + (block[3] << 8);
+ specs->serial = block[4] + (block[5] << 8) +
+ (block[6] << 16) + (block[7] << 24);
+ specs->year = block[9] + 1990;
+ specs->week = block[8];
+ DPRINTK(" Manufacturer: %s\n", specs->manufacturer);
+ DPRINTK(" Model: %x\n", specs->model);
+ DPRINTK(" Serial#: %u\n", specs->serial);
+ DPRINTK(" Year: %u Week %u\n", specs->year, specs->week);
+}
+
+static void get_dpms_capabilities(unsigned char flags,
+ struct fb_monspecs *specs)
+{
+ specs->dpms = 0;
+ if (flags & DPMS_ACTIVE_OFF)
+ specs->dpms |= FB_DPMS_ACTIVE_OFF;
+ if (flags & DPMS_SUSPEND)
+ specs->dpms |= FB_DPMS_SUSPEND;
+ if (flags & DPMS_STANDBY)
+ specs->dpms |= FB_DPMS_STANDBY;
+ DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n",
+ (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
+ (flags & DPMS_SUSPEND) ? "yes" : "no",
+ (flags & DPMS_STANDBY) ? "yes" : "no");
+}
+
+static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
+{
+ int tmp;
+
+ DPRINTK(" Chroma\n");
+ /* Chromaticity data */
+ tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.redx = tmp/1024;
+ DPRINTK(" RedX: 0.%03d ", specs->chroma.redx);
+
+ tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.redy = tmp/1024;
+ DPRINTK("RedY: 0.%03d\n", specs->chroma.redy);
+
+ tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.greenx = tmp/1024;
+ DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx);
+
+ tmp = (block[5] & 3) | (block[0xa] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.greeny = tmp/1024;
+ DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny);
+
+ tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.bluex = tmp/1024;
+ DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex);
+
+ tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.bluey = tmp/1024;
+ DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey);
+
+ tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.whitex = tmp/1024;
+ DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex);
+
+ tmp = (block[6] & 3) | (block[0xe] << 2);
+ tmp *= 1000;
+ tmp += 512;
+ specs->chroma.whitey = tmp/1024;
+ DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey);
+}
+
+static void calc_mode_timings(int xres, int yres, int refresh,
+ struct fb_videomode *mode)
+{
+ struct fb_var_screeninfo *var;
+
+ var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
+
+ if (var) {
+ var->xres = xres;
+ var->yres = yres;
+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
+ refresh, var, NULL);
+ mode->xres = xres;
+ mode->yres = yres;
+ mode->pixclock = var->pixclock;
+ mode->refresh = refresh;
+ mode->left_margin = var->left_margin;
+ mode->right_margin = var->right_margin;
+ mode->upper_margin = var->upper_margin;
+ mode->lower_margin = var->lower_margin;
+ mode->hsync_len = var->hsync_len;
+ mode->vsync_len = var->vsync_len;
+ mode->vmode = 0;
+ mode->sync = 0;
+ kfree(var);
+ }
+}
+
+static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
+{
+ int num = 0;
+ unsigned char c;
+
+ c = block[0];
+ if (c&0x80) {
+ calc_mode_timings(720, 400, 70, &mode[num]);
+ mode[num++].flag = FB_MODE_IS_CALCULATED;
+ DPRINTK(" 720x400@70Hz\n");
+ }
+ if (c&0x40) {
+ calc_mode_timings(720, 400, 88, &mode[num]);
+ mode[num++].flag = FB_MODE_IS_CALCULATED;
+ DPRINTK(" 720x400@88Hz\n");
+ }
+ if (c&0x20) {
+ mode[num++] = vesa_modes[3];
+ DPRINTK(" 640x480@60Hz\n");
+ }
+ if (c&0x10) {
+ calc_mode_timings(640, 480, 67, &mode[num]);
+ mode[num++].flag = FB_MODE_IS_CALCULATED;
+ DPRINTK(" 640x480@67Hz\n");
+ }
+ if (c&0x08) {
+ mode[num++] = vesa_modes[4];
+ DPRINTK(" 640x480@72Hz\n");
+ }
+ if (c&0x04) {
+ mode[num++] = vesa_modes[5];
+ DPRINTK(" 640x480@75Hz\n");
+ }
+ if (c&0x02) {
+ mode[num++] = vesa_modes[7];
+ DPRINTK(" 800x600@56Hz\n");
+ }
+ if (c&0x01) {
+ mode[num++] = vesa_modes[8];
+ DPRINTK(" 800x600@60Hz\n");
+ }
+
+ c = block[1];
+ if (c&0x80) {
+ mode[num++] = vesa_modes[9];
+ DPRINTK(" 800x600@72Hz\n");
+ }
+ if (c&0x40) {
+ mode[num++] = vesa_modes[10];
+ DPRINTK(" 800x600@75Hz\n");
+ }
+ if (c&0x20) {
+ calc_mode_timings(832, 624, 75, &mode[num]);
+ mode[num++].flag = FB_MODE_IS_CALCULATED;
+ DPRINTK(" 832x624@75Hz\n");
+ }
+ if (c&0x10) {
+ mode[num++] = vesa_modes[12];
+ DPRINTK(" 1024x768@87Hz Interlaced\n");
+ }
+ if (c&0x08) {
+ mode[num++] = vesa_modes[13];
+ DPRINTK(" 1024x768@60Hz\n");
+ }
+ if (c&0x04) {
+ mode[num++] = vesa_modes[14];
+ DPRINTK(" 1024x768@70Hz\n");
+ }
+ if (c&0x02) {
+ mode[num++] = vesa_modes[15];
+ DPRINTK(" 1024x768@75Hz\n");
+ }
+ if (c&0x01) {
+ mode[num++] = vesa_modes[21];
+ DPRINTK(" 1280x1024@75Hz\n");
+ }
+ c = block[2];
+ if (c&0x80) {
+ mode[num++] = vesa_modes[17];
+ DPRINTK(" 1152x870@75Hz\n");
+ }
+ DPRINTK(" Manufacturer's mask: %x\n",c&0x7F);
+ return num;
+}
+
+static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
+ int ver, int rev)
+{
+ int xres, yres = 0, refresh, ratio, i;
+
+ xres = (block[0] + 31) * 8;
+ if (xres <= 256)
+ return 0;
+
+ ratio = (block[1] & 0xc0) >> 6;
+ switch (ratio) {
+ case 0:
+ /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
+ if (ver < 1 || (ver == 1 && rev < 3))
+ yres = xres;
+ else
+ yres = (xres * 10)/16;
+ break;
+ case 1:
+ yres = (xres * 3)/4;
+ break;
+ case 2:
+ yres = (xres * 4)/5;
+ break;
+ case 3:
+ yres = (xres * 9)/16;
+ break;
+ }
+ refresh = (block[1] & 0x3f) + 60;
+
+ DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh);
+ for (i = 0; i < VESA_MODEDB_SIZE; i++) {
+ if (vesa_modes[i].xres == xres &&
+ vesa_modes[i].yres == yres &&
+ vesa_modes[i].refresh == refresh) {
+ *mode = vesa_modes[i];
+ mode->flag |= FB_MODE_IS_STANDARD;
+ return 1;
+ }
+ }
+ calc_mode_timings(xres, yres, refresh, mode);
+ return 1;
+}
+
+static int get_dst_timing(unsigned char *block,
+ struct fb_videomode *mode, int ver, int rev)
+{
+ int j, num = 0;
+
+ for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
+ num += get_std_timing(block, &mode[num], ver, rev);
+
+ return num;
+}
+
+static void get_detailed_timing(unsigned char *block,
+ struct fb_videomode *mode)
+{
+ mode->xres = H_ACTIVE;
+ mode->yres = V_ACTIVE;
+ mode->pixclock = PIXEL_CLOCK;
+ mode->pixclock /= 1000;
+ mode->pixclock = KHZ2PICOS(mode->pixclock);
+ mode->right_margin = H_SYNC_OFFSET;
+ mode->left_margin = (H_ACTIVE + H_BLANKING) -
+ (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+ mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+ V_SYNC_WIDTH;
+ mode->lower_margin = V_SYNC_OFFSET;
+ mode->hsync_len = H_SYNC_WIDTH;
+ mode->vsync_len = V_SYNC_WIDTH;
+ if (HSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (VSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+ (V_ACTIVE + V_BLANKING));
+ if (INTERLACED) {
+ mode->yres *= 2;
+ mode->upper_margin *= 2;
+ mode->lower_margin *= 2;
+ mode->vsync_len *= 2;
+ mode->vmode |= FB_VMODE_INTERLACED;
+ }
+ mode->flag = FB_MODE_IS_DETAILED;
+
+ DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
+ DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+ H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+ DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+ V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+ DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
+ (VSYNC_POSITIVE) ? "+" : "-");
+}
+
+/**
+ * fb_create_modedb - create video mode database
+ * @edid: EDID data
+ * @dbsize: database size
+ *
+ * RETURNS: struct fb_videomode, @dbsize contains length of database
+ *
+ * DESCRIPTION:
+ * This function builds a mode database using the contents of the EDID
+ * data
+ */
+static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
+{
+ struct fb_videomode *mode, *m;
+ unsigned char *block;
+ int num = 0, i, first = 1;
+ int ver, rev;
+
+ ver = edid[EDID_STRUCT_VERSION];
+ rev = edid[EDID_STRUCT_REVISION];
+
+ mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
+ if (mode == NULL)
+ return NULL;
+
+ if (edid == NULL || !edid_checksum(edid) ||
+ !edid_check_header(edid)) {
+ kfree(mode);
+ return NULL;
+ }
+
+ *dbsize = 0;
+
+ DPRINTK(" Detailed Timings\n");
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (!(block[0] == 0x00 && block[1] == 0x00)) {
+ get_detailed_timing(block, &mode[num]);
+ if (first) {
+ mode[num].flag |= FB_MODE_IS_FIRST;
+ first = 0;
+ }
+ num++;
+ }
+ }
+
+ DPRINTK(" Supported VESA Modes\n");
+ block = edid + ESTABLISHED_TIMING_1;
+ num += get_est_timing(block, &mode[num]);
+
+ DPRINTK(" Standard Timings\n");
+ block = edid + STD_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
+ num += get_std_timing(block, &mode[num], ver, rev);
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
+ num += get_dst_timing(block + 5, &mode[num], ver, rev);
+ }
+
+ /* Yikes, EDID data is totally useless */
+ if (!num) {
+ kfree(mode);
+ return NULL;
+ }
+
+ *dbsize = num;
+ m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL);
+ if (!m)
+ return mode;
+ memmove(m, mode, num * sizeof(struct fb_videomode));
+ kfree(mode);
+ return m;
+}
+
+/**
+ * fb_destroy_modedb - destroys mode database
+ * @modedb: mode database to destroy
+ *
+ * DESCRIPTION:
+ * Destroy mode database created by fb_create_modedb
+ */
+void fb_destroy_modedb(struct fb_videomode *modedb)
+{
+ kfree(modedb);
+}
+
+static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
+{
+ int i, retval = 1;
+ unsigned char *block;
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+
+ DPRINTK(" Monitor Operating Limits: ");
+
+ for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (edid_is_limits_block(block)) {
+ specs->hfmin = H_MIN_RATE * 1000;
+ specs->hfmax = H_MAX_RATE * 1000;
+ specs->vfmin = V_MIN_RATE;
+ specs->vfmax = V_MAX_RATE;
+ specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
+ specs->gtf = (GTF_SUPPORT) ? 1 : 0;
+ retval = 0;
+ DPRINTK("From EDID\n");
+ break;
+ }
+ }
+
+ /* estimate monitor limits based on modes supported */
+ if (retval) {
+ struct fb_videomode *modes, *mode;
+ int num_modes, hz, hscan, pixclock;
+ int vtotal, htotal;
+
+ modes = fb_create_modedb(edid, &num_modes);
+ if (!modes) {
+ DPRINTK("None Available\n");
+ return 1;
+ }
+
+ retval = 0;
+ for (i = 0; i < num_modes; i++) {
+ mode = &modes[i];
+ pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
+ htotal = mode->xres + mode->right_margin + mode->hsync_len
+ + mode->left_margin;
+ vtotal = mode->yres + mode->lower_margin + mode->vsync_len
+ + mode->upper_margin;
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+
+ if (mode->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hscan = (pixclock + htotal / 2) / htotal;
+ hscan = (hscan + 500) / 1000 * 1000;
+ hz = (hscan + vtotal / 2) / vtotal;
+
+ if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
+ specs->dclkmax = pixclock;
+
+ if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
+ specs->dclkmin = pixclock;
+
+ if (specs->hfmax == 0 || specs->hfmax < hscan)
+ specs->hfmax = hscan;
+
+ if (specs->hfmin == 0 || specs->hfmin > hscan)
+ specs->hfmin = hscan;
+
+ if (specs->vfmax == 0 || specs->vfmax < hz)
+ specs->vfmax = hz;
+
+ if (specs->vfmin == 0 || specs->vfmin > hz)
+ specs->vfmin = hz;
+ }
+ DPRINTK("Extrapolated\n");
+ fb_destroy_modedb(modes);
+ }
+ DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
+ specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
+ specs->vfmax, specs->dclkmax/1000000);
+ return retval;
+}
+
+static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
+{
+ unsigned char c, *block;
+
+ block = edid + EDID_STRUCT_DISPLAY;
+
+ fb_get_monitor_limits(edid, specs);
+
+ c = block[0] & 0x80;
+ specs->input = 0;
+ if (c) {
+ specs->input |= FB_DISP_DDI;
+ DPRINTK(" Digital Display Input");
+ } else {
+ DPRINTK(" Analog Display Input: Input Voltage - ");
+ switch ((block[0] & 0x60) >> 5) {
+ case 0:
+ DPRINTK("0.700V/0.300V");
+ specs->input |= FB_DISP_ANA_700_300;
+ break;
+ case 1:
+ DPRINTK("0.714V/0.286V");
+ specs->input |= FB_DISP_ANA_714_286;
+ break;
+ case 2:
+ DPRINTK("1.000V/0.400V");
+ specs->input |= FB_DISP_ANA_1000_400;
+ break;
+ case 3:
+ DPRINTK("0.700V/0.000V");
+ specs->input |= FB_DISP_ANA_700_000;
+ break;
+ }
+ }
+ DPRINTK("\n Sync: ");
+ c = block[0] & 0x10;
+ if (c)
+ DPRINTK(" Configurable signal level\n");
+ c = block[0] & 0x0f;
+ specs->signal = 0;
+ if (c & 0x10) {
+ DPRINTK("Blank to Blank ");
+ specs->signal |= FB_SIGNAL_BLANK_BLANK;
+ }
+ if (c & 0x08) {
+ DPRINTK("Separate ");
+ specs->signal |= FB_SIGNAL_SEPARATE;
+ }
+ if (c & 0x04) {
+ DPRINTK("Composite ");
+ specs->signal |= FB_SIGNAL_COMPOSITE;
+ }
+ if (c & 0x02) {
+ DPRINTK("Sync on Green ");
+ specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
+ }
+ if (c & 0x01) {
+ DPRINTK("Serration on ");
+ specs->signal |= FB_SIGNAL_SERRATION_ON;
+ }
+ DPRINTK("\n");
+ specs->max_x = block[1];
+ specs->max_y = block[2];
+ DPRINTK(" Max H-size in cm: ");
+ if (specs->max_x)
+ DPRINTK("%d\n", specs->max_x);
+ else
+ DPRINTK("variable\n");
+ DPRINTK(" Max V-size in cm: ");
+ if (specs->max_y)
+ DPRINTK("%d\n", specs->max_y);
+ else
+ DPRINTK("variable\n");
+
+ c = block[3];
+ specs->gamma = c+100;
+ DPRINTK(" Gamma: ");
+ DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
+
+ get_dpms_capabilities(block[4], specs);
+
+ switch ((block[4] & 0x18) >> 3) {
+ case 0:
+ DPRINTK(" Monochrome/Grayscale\n");
+ specs->input |= FB_DISP_MONO;
+ break;
+ case 1:
+ DPRINTK(" RGB Color Display\n");
+ specs->input |= FB_DISP_RGB;
+ break;
+ case 2:
+ DPRINTK(" Non-RGB Multicolor Display\n");
+ specs->input |= FB_DISP_MULTI;
+ break;
+ default:
+ DPRINTK(" Unknown\n");
+ specs->input |= FB_DISP_UNKNOWN;
+ break;
+ }
+
+ get_chroma(block, specs);
+
+ specs->misc = 0;
+ c = block[4] & 0x7;
+ if (c & 0x04) {
+ DPRINTK(" Default color format is primary\n");
+ specs->misc |= FB_MISC_PRIM_COLOR;
+ }
+ if (c & 0x02) {
+ DPRINTK(" First DETAILED Timing is preferred\n");
+ specs->misc |= FB_MISC_1ST_DETAIL;
+ }
+ if (c & 0x01) {
+ printk(" Display is GTF capable\n");
+ specs->gtf = 1;
+ }
+}
+
+int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
+{
+ int i;
+ unsigned char *block;
+
+ if (edid == NULL || var == NULL)
+ return 1;
+
+ if (!(edid_checksum(edid)))
+ return 1;
+
+ if (!(edid_check_header(edid)))
+ return 1;
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+
+ for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (edid_is_timing_block(block)) {
+ var->xres = var->xres_virtual = H_ACTIVE;
+ var->yres = var->yres_virtual = V_ACTIVE;
+ var->height = var->width = 0;
+ var->right_margin = H_SYNC_OFFSET;
+ var->left_margin = (H_ACTIVE + H_BLANKING) -
+ (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+ var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+ V_SYNC_WIDTH;
+ var->lower_margin = V_SYNC_OFFSET;
+ var->hsync_len = H_SYNC_WIDTH;
+ var->vsync_len = V_SYNC_WIDTH;
+ var->pixclock = PIXEL_CLOCK;
+ var->pixclock /= 1000;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+
+ if (HSYNC_POSITIVE)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (VSYNC_POSITIVE)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
+{
+ unsigned char *block;
+ int i, found = 0;
+
+ if (edid == NULL)
+ return;
+
+ if (!(edid_checksum(edid)))
+ return;
+
+ if (!(edid_check_header(edid)))
+ return;
+
+ memset(specs, 0, sizeof(struct fb_monspecs));
+
+ specs->version = edid[EDID_STRUCT_VERSION];
+ specs->revision = edid[EDID_STRUCT_REVISION];
+
+ DPRINTK("========================================\n");
+ DPRINTK("Display Information (EDID)\n");
+ DPRINTK("========================================\n");
+ DPRINTK(" EDID Version %d.%d\n", (int) specs->version,
+ (int) specs->revision);
+
+ parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (edid_is_serial_block(block)) {
+ copy_string(block, specs->serial_no);
+ DPRINTK(" Serial Number: %s\n", specs->serial_no);
+ } else if (edid_is_ascii_block(block)) {
+ copy_string(block, specs->ascii);
+ DPRINTK(" ASCII Block: %s\n", specs->ascii);
+ } else if (edid_is_monitor_block(block)) {
+ copy_string(block, specs->monitor);
+ DPRINTK(" Monitor Name: %s\n", specs->monitor);
+ }
+ }
+
+ DPRINTK(" Display Characteristics:\n");
+ get_monspecs(edid, specs);
+
+ specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
+
+ /*
+ * Workaround for buggy EDIDs that sets that the first
+ * detailed timing is preferred but has not detailed
+ * timing specified
+ */
+ for (i = 0; i < specs->modedb_len; i++) {
+ if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ specs->misc &= ~FB_MISC_1ST_DETAIL;
+
+ DPRINTK("========================================\n");
+}
+
+/**
+ * fb_edid_add_monspecs() - add monitor video modes from E-EDID data
+ * @edid: 128 byte array with an E-EDID block
+ * @spacs: monitor specs to be extended
+ */
+void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
+{
+ unsigned char *block;
+ struct fb_videomode *m;
+ int num = 0, i;
+ u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
+ u8 pos = 4, svd_n = 0;
+
+ if (!edid)
+ return;
+
+ if (!edid_checksum(edid))
+ return;
+
+ if (edid[0] != 0x2 ||
+ edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
+ return;
+
+ DPRINTK(" Short Video Descriptors\n");
+
+ while (pos < edid[2]) {
+ u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
+ pr_debug("Data block %u of %u bytes\n", type, len);
+ if (type == 2)
+ for (i = pos; i < pos + len; i++) {
+ u8 idx = edid[pos + i] & 0x7f;
+ svd[svd_n++] = idx;
+ pr_debug("N%sative mode #%d\n",
+ edid[pos + i] & 0x80 ? "" : "on-n", idx);
+ }
+ pos += len + 1;
+ }
+
+ block = edid + edid[2];
+
+ DPRINTK(" Extended Detailed Timings\n");
+
+ for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE;
+ i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
+ if (PIXEL_CLOCK)
+ edt[num++] = block - edid;
+
+ /* Yikes, EDID data is totally useless */
+ if (!(num + svd_n))
+ return;
+
+ m = kzalloc((specs->modedb_len + num + svd_n) *
+ sizeof(struct fb_videomode), GFP_KERNEL);
+
+ if (!m)
+ return;
+
+ memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode));
+
+ for (i = specs->modedb_len; i < specs->modedb_len + num; i++) {
+ get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]);
+ if (i == specs->modedb_len)
+ m[i].flag |= FB_MODE_IS_FIRST;
+ pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh);
+ }
+
+ for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) {
+ int idx = svd[i - specs->modedb_len - num];
+ if (!idx || idx > 63) {
+ pr_warning("Reserved SVD code %d\n", idx);
+ } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) {
+ pr_warning("Unimplemented SVD code %d\n", idx);
+ } else {
+ memcpy(&m[i], cea_modes + idx, sizeof(m[i]));
+ pr_debug("Adding SVD #%d: %ux%u@%u\n", idx,
+ m[i].xres, m[i].yres, m[i].refresh);
+ }
+ }
+
+ kfree(specs->modedb);
+ specs->modedb = m;
+ specs->modedb_len = specs->modedb_len + num + svd_n;
+}
+
+/*
+ * VESA Generalized Timing Formula (GTF)
+ */
+
+#define FLYBACK 550
+#define V_FRONTPORCH 1
+#define H_OFFSET 40
+#define H_SCALEFACTOR 20
+#define H_BLANKSCALE 128
+#define H_GRADIENT 600
+#define C_VAL 30
+#define M_VAL 300
+
+struct __fb_timings {
+ u32 dclk;
+ u32 hfreq;
+ u32 vfreq;
+ u32 hactive;
+ u32 vactive;
+ u32 hblank;
+ u32 vblank;
+ u32 htotal;
+ u32 vtotal;
+};
+
+/**
+ * fb_get_vblank - get vertical blank time
+ * @hfreq: horizontal freq
+ *
+ * DESCRIPTION:
+ * vblank = right_margin + vsync_len + left_margin
+ *
+ * given: right_margin = 1 (V_FRONTPORCH)
+ * vsync_len = 3
+ * flyback = 550
+ *
+ * flyback * hfreq
+ * left_margin = --------------- - vsync_len
+ * 1000000
+ */
+static u32 fb_get_vblank(u32 hfreq)
+{
+ u32 vblank;
+
+ vblank = (hfreq * FLYBACK)/1000;
+ vblank = (vblank + 500)/1000;
+ return (vblank + V_FRONTPORCH);
+}
+
+/**
+ * fb_get_hblank_by_freq - get horizontal blank time given hfreq
+ * @hfreq: horizontal freq
+ * @xres: horizontal resolution in pixels
+ *
+ * DESCRIPTION:
+ *
+ * xres * duty_cycle
+ * hblank = ------------------
+ * 100 - duty_cycle
+ *
+ * duty cycle = percent of htotal assigned to inactive display
+ * duty cycle = C - (M/Hfreq)
+ *
+ * where: C = ((offset - scale factor) * blank_scale)
+ * -------------------------------------- + scale factor
+ * 256
+ * M = blank_scale * gradient
+ *
+ */
+static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
+{
+ u32 c_val, m_val, duty_cycle, hblank;
+
+ c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
+ H_SCALEFACTOR) * 1000;
+ m_val = (H_BLANKSCALE * H_GRADIENT)/256;
+ m_val = (m_val * 1000000)/hfreq;
+ duty_cycle = c_val - m_val;
+ hblank = (xres * duty_cycle)/(100000 - duty_cycle);
+ return (hblank);
+}
+
+/**
+ * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock
+ * @dclk: pixelclock in Hz
+ * @xres: horizontal resolution in pixels
+ *
+ * DESCRIPTION:
+ *
+ * xres * duty_cycle
+ * hblank = ------------------
+ * 100 - duty_cycle
+ *
+ * duty cycle = percent of htotal assigned to inactive display
+ * duty cycle = C - (M * h_period)
+ *
+ * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100
+ * -----------------------------------------------
+ * 2 * M
+ * M = 300;
+ * C = 30;
+
+ */
+static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres)
+{
+ u32 duty_cycle, h_period, hblank;
+
+ dclk /= 1000;
+ h_period = 100 - C_VAL;
+ h_period *= h_period;
+ h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk);
+ h_period *= 10000;
+
+ h_period = int_sqrt(h_period);
+ h_period -= (100 - C_VAL) * 100;
+ h_period *= 1000;
+ h_period /= 2 * M_VAL;
+
+ duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100;
+ hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8;
+ hblank &= ~15;
+ return (hblank);
+}
+
+/**
+ * fb_get_hfreq - estimate hsync
+ * @vfreq: vertical refresh rate
+ * @yres: vertical resolution
+ *
+ * DESCRIPTION:
+ *
+ * (yres + front_port) * vfreq * 1000000
+ * hfreq = -------------------------------------
+ * (1000000 - (vfreq * FLYBACK)
+ *
+ */
+
+static u32 fb_get_hfreq(u32 vfreq, u32 yres)
+{
+ u32 divisor, hfreq;
+
+ divisor = (1000000 - (vfreq * FLYBACK))/1000;
+ hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
+ return (hfreq/divisor);
+}
+
+static void fb_timings_vfreq(struct __fb_timings *timings)
+{
+ timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
+ timings->vblank = fb_get_vblank(timings->hfreq);
+ timings->vtotal = timings->vactive + timings->vblank;
+ timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
+ timings->hactive);
+ timings->htotal = timings->hactive + timings->hblank;
+ timings->dclk = timings->htotal * timings->hfreq;
+}
+
+static void fb_timings_hfreq(struct __fb_timings *timings)
+{
+ timings->vblank = fb_get_vblank(timings->hfreq);
+ timings->vtotal = timings->vactive + timings->vblank;
+ timings->vfreq = timings->hfreq/timings->vtotal;
+ timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
+ timings->hactive);
+ timings->htotal = timings->hactive + timings->hblank;
+ timings->dclk = timings->htotal * timings->hfreq;
+}
+
+static void fb_timings_dclk(struct __fb_timings *timings)
+{
+ timings->hblank = fb_get_hblank_by_dclk(timings->dclk,
+ timings->hactive);
+ timings->htotal = timings->hactive + timings->hblank;
+ timings->hfreq = timings->dclk/timings->htotal;
+ timings->vblank = fb_get_vblank(timings->hfreq);
+ timings->vtotal = timings->vactive + timings->vblank;
+ timings->vfreq = timings->hfreq/timings->vtotal;
+}
+
+/*
+ * fb_get_mode - calculates video mode using VESA GTF
+ * @flags: if: 0 - maximize vertical refresh rate
+ * 1 - vrefresh-driven calculation;
+ * 2 - hscan-driven calculation;
+ * 3 - pixelclock-driven calculation;
+ * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
+ * @var: pointer to fb_var_screeninfo
+ * @info: pointer to fb_info
+ *
+ * DESCRIPTION:
+ * Calculates video mode based on monitor specs using VESA GTF.
+ * The GTF is best for VESA GTF compliant monitors but is
+ * specifically formulated to work for older monitors as well.
+ *
+ * If @flag==0, the function will attempt to maximize the
+ * refresh rate. Otherwise, it will calculate timings based on
+ * the flag and accompanying value.
+ *
+ * If FB_IGNOREMON bit is set in @flags, monitor specs will be
+ * ignored and @var will be filled with the calculated timings.
+ *
+ * All calculations are based on the VESA GTF Spreadsheet
+ * available at VESA's public ftp (http://www.vesa.org).
+ *
+ * NOTES:
+ * The timings generated by the GTF will be different from VESA
+ * DMT. It might be a good idea to keep a table of standard
+ * VESA modes as well. The GTF may also not work for some displays,
+ * such as, and especially, analog TV.
+ *
+ * REQUIRES:
+ * A valid info->monspecs, otherwise 'safe numbers' will be used.
+ */
+int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct __fb_timings *timings;
+ u32 interlace = 1, dscan = 1;
+ u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
+
+
+ timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL);
+
+ if (!timings)
+ return -ENOMEM;
+
+ /*
+ * If monspecs are invalid, use values that are enough
+ * for 640x480@60
+ */
+ if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax ||
+ !info->monspecs.dclkmax ||
+ info->monspecs.hfmax < info->monspecs.hfmin ||
+ info->monspecs.vfmax < info->monspecs.vfmin ||
+ info->monspecs.dclkmax < info->monspecs.dclkmin) {
+ hfmin = 29000; hfmax = 30000;
+ vfmin = 60; vfmax = 60;
+ dclkmin = 0; dclkmax = 25000000;
+ } else {
+ hfmin = info->monspecs.hfmin;
+ hfmax = info->monspecs.hfmax;
+ vfmin = info->monspecs.vfmin;
+ vfmax = info->monspecs.vfmax;
+ dclkmin = info->monspecs.dclkmin;
+ dclkmax = info->monspecs.dclkmax;
+ }
+
+ timings->hactive = var->xres;
+ timings->vactive = var->yres;
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ timings->vactive /= 2;
+ interlace = 2;
+ }
+ if (var->vmode & FB_VMODE_DOUBLE) {
+ timings->vactive *= 2;
+ dscan = 2;
+ }
+
+ switch (flags & ~FB_IGNOREMON) {
+ case FB_MAXTIMINGS: /* maximize refresh rate */
+ timings->hfreq = hfmax;
+ fb_timings_hfreq(timings);
+ if (timings->vfreq > vfmax) {
+ timings->vfreq = vfmax;
+ fb_timings_vfreq(timings);
+ }
+ if (timings->dclk > dclkmax) {
+ timings->dclk = dclkmax;
+ fb_timings_dclk(timings);
+ }
+ break;
+ case FB_VSYNCTIMINGS: /* vrefresh driven */
+ timings->vfreq = val;
+ fb_timings_vfreq(timings);
+ break;
+ case FB_HSYNCTIMINGS: /* hsync driven */
+ timings->hfreq = val;
+ fb_timings_hfreq(timings);
+ break;
+ case FB_DCLKTIMINGS: /* pixelclock driven */
+ timings->dclk = PICOS2KHZ(val) * 1000;
+ fb_timings_dclk(timings);
+ break;
+ default:
+ err = -EINVAL;
+
+ }
+
+ if (err || (!(flags & FB_IGNOREMON) &&
+ (timings->vfreq < vfmin || timings->vfreq > vfmax ||
+ timings->hfreq < hfmin || timings->hfreq > hfmax ||
+ timings->dclk < dclkmin || timings->dclk > dclkmax))) {
+ err = -EINVAL;
+ } else {
+ var->pixclock = KHZ2PICOS(timings->dclk/1000);
+ var->hsync_len = (timings->htotal * 8)/100;
+ var->right_margin = (timings->hblank/2) - var->hsync_len;
+ var->left_margin = timings->hblank - var->right_margin -
+ var->hsync_len;
+ var->vsync_len = (3 * interlace)/dscan;
+ var->lower_margin = (1 * interlace)/dscan;
+ var->upper_margin = (timings->vblank * interlace)/dscan -
+ (var->vsync_len + var->lower_margin);
+ }
+
+ kfree(timings);
+ return err;
+}
+
+#ifdef CONFIG_VIDEOMODE_HELPERS
+int fb_videomode_from_videomode(const struct videomode *vm,
+ struct fb_videomode *fbmode)
+{
+ unsigned int htotal, vtotal;
+
+ fbmode->xres = vm->hactive;
+ fbmode->left_margin = vm->hback_porch;
+ fbmode->right_margin = vm->hfront_porch;
+ fbmode->hsync_len = vm->hsync_len;
+
+ fbmode->yres = vm->vactive;
+ fbmode->upper_margin = vm->vback_porch;
+ fbmode->lower_margin = vm->vfront_porch;
+ fbmode->vsync_len = vm->vsync_len;
+
+ /* prevent division by zero in KHZ2PICOS macro */
+ fbmode->pixclock = vm->pixelclock ?
+ KHZ2PICOS(vm->pixelclock / 1000) : 0;
+
+ fbmode->sync = 0;
+ fbmode->vmode = 0;
+ if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (vm->flags & DISPLAY_FLAGS_INTERLACED)
+ fbmode->vmode |= FB_VMODE_INTERLACED;
+ if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN)
+ fbmode->vmode |= FB_VMODE_DOUBLE;
+ fbmode->flag = 0;
+
+ htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
+ vm->hsync_len;
+ vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
+ vm->vsync_len;
+ /* prevent division by zero */
+ if (htotal && vtotal) {
+ fbmode->refresh = vm->pixelclock / (htotal * vtotal);
+ /* a mode must have htotal and vtotal != 0 or it is invalid */
+ } else {
+ fbmode->refresh = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+
+#ifdef CONFIG_OF
+static inline void dump_fb_videomode(const struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
+ m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+/**
+ * of_get_fb_videomode - get a fb_videomode from devicetree
+ * @np: device_node with the timing specification
+ * @fb: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings ond
+ * work with that instead.
+ */
+int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
+ int index)
+{
+ struct videomode vm;
+ int ret;
+
+ ret = of_get_videomode(np, &vm, index);
+ if (ret)
+ return ret;
+
+ fb_videomode_from_videomode(&vm, fb);
+
+ pr_debug("%s: got %dx%d display mode from %s\n",
+ of_node_full_name(np), vm.hactive, vm.vactive, np->name);
+ dump_fb_videomode(fb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_fb_videomode);
+#endif /* CONFIG_OF */
+#endif /* CONFIG_VIDEOMODE_HELPERS */
+
+#else
+int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
+{
+ return 1;
+}
+void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
+{
+ specs = NULL;
+}
+void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
+{
+}
+void fb_destroy_modedb(struct fb_videomode *modedb)
+{
+}
+int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_FB_MODE_HELPERS */
+
+/*
+ * fb_validate_mode - validates var against monitor capabilities
+ * @var: pointer to fb_var_screeninfo
+ * @info: pointer to fb_info
+ *
+ * DESCRIPTION:
+ * Validates video mode against monitor capabilities specified in
+ * info->monspecs.
+ *
+ * REQUIRES:
+ * A valid info->monspecs.
+ */
+int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 hfreq, vfreq, htotal, vtotal, pixclock;
+ u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
+
+ /*
+ * If monspecs are invalid, use values that are enough
+ * for 640x480@60
+ */
+ if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
+ !info->monspecs.dclkmax ||
+ info->monspecs.hfmax < info->monspecs.hfmin ||
+ info->monspecs.vfmax < info->monspecs.vfmin ||
+ info->monspecs.dclkmax < info->monspecs.dclkmin) {
+ hfmin = 29000; hfmax = 30000;
+ vfmin = 60; vfmax = 60;
+ dclkmin = 0; dclkmax = 25000000;
+ } else {
+ hfmin = info->monspecs.hfmin;
+ hfmax = info->monspecs.hfmax;
+ vfmin = info->monspecs.vfmin;
+ vfmax = info->monspecs.vfmax;
+ dclkmin = info->monspecs.dclkmin;
+ dclkmax = info->monspecs.dclkmax;
+ }
+
+ if (!var->pixclock)
+ return -EINVAL;
+ pixclock = PICOS2KHZ(var->pixclock) * 1000;
+
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+
+ if (var->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hfreq = pixclock/htotal;
+ hfreq = (hfreq + 500) / 1000 * 1000;
+
+ vfreq = hfreq/vtotal;
+
+ return (vfreq < vfmin || vfreq > vfmax ||
+ hfreq < hfmin || hfreq > hfmax ||
+ pixclock < dclkmin || pixclock > dclkmax) ?
+ -EINVAL : 0;
+}
+
+#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
+
+/*
+ * We need to ensure that the EDID block is only returned for
+ * the primary graphics adapter.
+ */
+
+const unsigned char *fb_firmware_edid(struct device *device)
+{
+ struct pci_dev *dev = NULL;
+ struct resource *res = NULL;
+ unsigned char *edid = NULL;
+
+ if (device)
+ dev = to_pci_dev(device);
+
+ if (dev)
+ res = &dev->resource[PCI_ROM_RESOURCE];
+
+ if (res && res->flags & IORESOURCE_ROM_SHADOW)
+ edid = edid_info.dummy;
+
+ return edid;
+}
+#else
+const unsigned char *fb_firmware_edid(struct device *device)
+{
+ return NULL;
+}
+#endif
+EXPORT_SYMBOL(fb_firmware_edid);
+
+EXPORT_SYMBOL(fb_parse_edid);
+EXPORT_SYMBOL(fb_edid_to_monspecs);
+EXPORT_SYMBOL(fb_edid_add_monspecs);
+EXPORT_SYMBOL(fb_get_mode);
+EXPORT_SYMBOL(fb_validate_mode);
+EXPORT_SYMBOL(fb_destroy_modedb);
--- /dev/null
+/*
+ * fbsysfs.c - framebuffer device class and attributes
+ *
+ * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
+ *
+ * This program is free software you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Note: currently there's only stubs for framebuffer_alloc and
+ * framebuffer_release here. The reson for that is that until all drivers
+ * are converted to use it a sysfsification will open OOPSable races.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/module.h>
+
+#define FB_SYSFS_FLAG_ATTR 1
+
+/**
+ * framebuffer_alloc - creates a new frame buffer info structure
+ *
+ * @size: size of driver private data, can be zero
+ * @dev: pointer to the device for this fb, this can be NULL
+ *
+ * Creates a new frame buffer info structure. Also reserves @size bytes
+ * for driver private data (info->par). info->par (if any) will be
+ * aligned to sizeof(long).
+ *
+ * Returns the new structure, or NULL if an error occurred.
+ *
+ */
+struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
+{
+#define BYTES_PER_LONG (BITS_PER_LONG/8)
+#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
+ int fb_info_size = sizeof(struct fb_info);
+ struct fb_info *info;
+ char *p;
+
+ if (size)
+ fb_info_size += PADDING;
+
+ p = kzalloc(fb_info_size + size, GFP_KERNEL);
+
+ if (!p)
+ return NULL;
+
+ info = (struct fb_info *) p;
+
+ if (size)
+ info->par = p + fb_info_size;
+
+ info->device = dev;
+
+#ifdef CONFIG_FB_BACKLIGHT
+ mutex_init(&info->bl_curve_mutex);
+#endif
+
+ return info;
+#undef PADDING
+#undef BYTES_PER_LONG
+}
+EXPORT_SYMBOL(framebuffer_alloc);
+
+/**
+ * framebuffer_release - marks the structure available for freeing
+ *
+ * @info: frame buffer info structure
+ *
+ * Drop the reference count of the device embedded in the
+ * framebuffer info structure.
+ *
+ */
+void framebuffer_release(struct fb_info *info)
+{
+ if (!info)
+ return;
+ kfree(info->apertures);
+ kfree(info);
+}
+EXPORT_SYMBOL(framebuffer_release);
+
+static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
+{
+ int err;
+
+ var->activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ fb_info->flags |= FBINFO_MISC_USEREVENT;
+ err = fb_set_var(fb_info, var);
+ fb_info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ if (err)
+ return err;
+ return 0;
+}
+
+static int mode_string(char *buf, unsigned int offset,
+ const struct fb_videomode *mode)
+{
+ char m = 'U';
+ char v = 'p';
+
+ if (mode->flag & FB_MODE_IS_DETAILED)
+ m = 'D';
+ if (mode->flag & FB_MODE_IS_VESA)
+ m = 'V';
+ if (mode->flag & FB_MODE_IS_STANDARD)
+ m = 'S';
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ v = 'i';
+ if (mode->vmode & FB_VMODE_DOUBLE)
+ v = 'd';
+
+ return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
+ m, mode->xres, mode->yres, v, mode->refresh);
+}
+
+static ssize_t store_mode(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ char mstr[100];
+ struct fb_var_screeninfo var;
+ struct fb_modelist *modelist;
+ struct fb_videomode *mode;
+ struct list_head *pos;
+ size_t i;
+ int err;
+
+ memset(&var, 0, sizeof(var));
+
+ list_for_each(pos, &fb_info->modelist) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ mode = &modelist->mode;
+ i = mode_string(mstr, 0, mode);
+ if (strncmp(mstr, buf, max(count, i)) == 0) {
+
+ var = fb_info->var;
+ fb_videomode_to_var(&var, mode);
+ if ((err = activate(fb_info, &var)))
+ return err;
+ fb_info->mode = mode;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+
+static ssize_t show_mode(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+
+ if (!fb_info->mode)
+ return 0;
+
+ return mode_string(buf, 0, fb_info->mode);
+}
+
+static ssize_t store_modes(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ LIST_HEAD(old_list);
+ int i = count / sizeof(struct fb_videomode);
+
+ if (i * sizeof(struct fb_videomode) != count)
+ return -EINVAL;
+
+ console_lock();
+ if (!lock_fb_info(fb_info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+
+ list_splice(&fb_info->modelist, &old_list);
+ fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
+ &fb_info->modelist);
+ if (fb_new_modelist(fb_info)) {
+ fb_destroy_modelist(&fb_info->modelist);
+ list_splice(&old_list, &fb_info->modelist);
+ } else
+ fb_destroy_modelist(&old_list);
+
+ unlock_fb_info(fb_info);
+ console_unlock();
+
+ return 0;
+}
+
+static ssize_t show_modes(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ unsigned int i;
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ const struct fb_videomode *mode;
+
+ i = 0;
+ list_for_each(pos, &fb_info->modelist) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ mode = &modelist->mode;
+ i += mode_string(buf, i, mode);
+ }
+ return i;
+}
+
+static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fb_var_screeninfo var;
+ char ** last = NULL;
+ int err;
+
+ var = fb_info->var;
+ var.bits_per_pixel = simple_strtoul(buf, last, 0);
+ if ((err = activate(fb_info, &var)))
+ return err;
+ return count;
+}
+
+static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
+}
+
+static ssize_t store_rotate(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fb_var_screeninfo var;
+ char **last = NULL;
+ int err;
+
+ var = fb_info->var;
+ var.rotate = simple_strtoul(buf, last, 0);
+
+ if ((err = activate(fb_info, &var)))
+ return err;
+
+ return count;
+}
+
+
+static ssize_t show_rotate(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
+}
+
+static ssize_t store_virtual(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fb_var_screeninfo var;
+ char *last = NULL;
+ int err;
+
+ var = fb_info->var;
+ var.xres_virtual = simple_strtoul(buf, &last, 0);
+ last++;
+ if (last - buf >= count)
+ return -EINVAL;
+ var.yres_virtual = simple_strtoul(last, &last, 0);
+
+ if ((err = activate(fb_info, &var)))
+ return err;
+ return count;
+}
+
+static ssize_t show_virtual(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
+ fb_info->var.yres_virtual);
+}
+
+static ssize_t show_stride(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
+}
+
+static ssize_t store_blank(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ char *last = NULL;
+ int err;
+
+ console_lock();
+ fb_info->flags |= FBINFO_MISC_USEREVENT;
+ err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
+ fb_info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t show_blank(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+// struct fb_info *fb_info = dev_get_drvdata(device);
+ return 0;
+}
+
+static ssize_t store_console(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+// struct fb_info *fb_info = dev_get_drvdata(device);
+ return 0;
+}
+
+static ssize_t show_console(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+// struct fb_info *fb_info = dev_get_drvdata(device);
+ return 0;
+}
+
+static ssize_t store_cursor(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+// struct fb_info *fb_info = dev_get_drvdata(device);
+ return 0;
+}
+
+static ssize_t show_cursor(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+// struct fb_info *fb_info = dev_get_drvdata(device);
+ return 0;
+}
+
+static ssize_t store_pan(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fb_var_screeninfo var;
+ char *last = NULL;
+ int err;
+
+ var = fb_info->var;
+ var.xoffset = simple_strtoul(buf, &last, 0);
+ last++;
+ if (last - buf >= count)
+ return -EINVAL;
+ var.yoffset = simple_strtoul(last, &last, 0);
+
+ console_lock();
+ err = fb_pan_display(fb_info, &var);
+ console_unlock();
+
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t show_pan(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
+ fb_info->var.yoffset);
+}
+
+static ssize_t show_name(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
+}
+
+static ssize_t store_fbstate(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ u32 state;
+ char *last = NULL;
+
+ state = simple_strtoul(buf, &last, 0);
+
+ console_lock();
+ if (!lock_fb_info(fb_info)) {
+ console_unlock();
+ return -ENODEV;
+ }
+
+ fb_set_suspend(fb_info, (int)state);
+
+ unlock_fb_info(fb_info);
+ console_unlock();
+
+ return count;
+}
+
+static ssize_t show_fbstate(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
+}
+
+#ifdef CONFIG_FB_BACKLIGHT
+static ssize_t store_bl_curve(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ u8 tmp_curve[FB_BACKLIGHT_LEVELS];
+ unsigned int i;
+
+ /* Some drivers don't use framebuffer_alloc(), but those also
+ * don't have backlights.
+ */
+ if (!fb_info || !fb_info->bl_dev)
+ return -ENODEV;
+
+ if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
+ return -EINVAL;
+
+ for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
+ if (sscanf(&buf[i * 24],
+ "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
+ &tmp_curve[i * 8 + 0],
+ &tmp_curve[i * 8 + 1],
+ &tmp_curve[i * 8 + 2],
+ &tmp_curve[i * 8 + 3],
+ &tmp_curve[i * 8 + 4],
+ &tmp_curve[i * 8 + 5],
+ &tmp_curve[i * 8 + 6],
+ &tmp_curve[i * 8 + 7]) != 8)
+ return -EINVAL;
+
+ /* If there has been an error in the input data, we won't
+ * reach this loop.
+ */
+ mutex_lock(&fb_info->bl_curve_mutex);
+ for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
+ fb_info->bl_curve[i] = tmp_curve[i];
+ mutex_unlock(&fb_info->bl_curve_mutex);
+
+ return count;
+}
+
+static ssize_t show_bl_curve(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ ssize_t len = 0;
+ unsigned int i;
+
+ /* Some drivers don't use framebuffer_alloc(), but those also
+ * don't have backlights.
+ */
+ if (!fb_info || !fb_info->bl_dev)
+ return -ENODEV;
+
+ mutex_lock(&fb_info->bl_curve_mutex);
+ for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
+ len += snprintf(&buf[len], PAGE_SIZE,
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ fb_info->bl_curve[i + 0],
+ fb_info->bl_curve[i + 1],
+ fb_info->bl_curve[i + 2],
+ fb_info->bl_curve[i + 3],
+ fb_info->bl_curve[i + 4],
+ fb_info->bl_curve[i + 5],
+ fb_info->bl_curve[i + 6],
+ fb_info->bl_curve[i + 7]);
+ mutex_unlock(&fb_info->bl_curve_mutex);
+
+ return len;
+}
+#endif
+
+/* When cmap is added back in it should be a binary attribute
+ * not a text one. Consideration should also be given to converting
+ * fbdev to use configfs instead of sysfs */
+static struct device_attribute device_attrs[] = {
+ __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
+ __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
+ __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
+ __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
+ __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
+ __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
+ __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
+ __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(stride, S_IRUGO, show_stride, NULL),
+ __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
+ __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
+#ifdef CONFIG_FB_BACKLIGHT
+ __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
+#endif
+};
+
+int fb_init_device(struct fb_info *fb_info)
+{
+ int i, error = 0;
+
+ dev_set_drvdata(fb_info->dev, fb_info);
+
+ fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
+
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
+ error = device_create_file(fb_info->dev, &device_attrs[i]);
+
+ if (error)
+ break;
+ }
+
+ if (error) {
+ while (--i >= 0)
+ device_remove_file(fb_info->dev, &device_attrs[i]);
+ fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
+ }
+
+ return 0;
+}
+
+void fb_cleanup_device(struct fb_info *fb_info)
+{
+ unsigned int i;
+
+ if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
+ device_remove_file(fb_info->dev, &device_attrs[i]);
+
+ fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
+ }
+}
+
+#ifdef CONFIG_FB_BACKLIGHT
+/* This function generates a linear backlight curve
+ *
+ * 0: off
+ * 1-7: min
+ * 8-127: linear from min to max
+ */
+void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
+{
+ unsigned int i, flat, count, range = (max - min);
+
+ mutex_lock(&fb_info->bl_curve_mutex);
+
+ fb_info->bl_curve[0] = off;
+
+ for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
+ fb_info->bl_curve[flat] = min;
+
+ count = FB_BACKLIGHT_LEVELS * 15 / 16;
+ for (i = 0; i < count; ++i)
+ fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
+
+ mutex_unlock(&fb_info->bl_curve_mutex);
+}
+EXPORT_SYMBOL_GPL(fb_bl_default_curve);
+#endif
--- /dev/null
+/*
+ * linux/drivers/video/modedb.c -- Standard video mode database management
+ *
+ * Copyright (C) 1999 Geert Uytterhoeven
+ *
+ * 2001 - Documented with DocBook
+ * - Brad Douglas <brad@neruo.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+
+#undef DEBUG
+
+#define name_matches(v, s, l) \
+ ((v).name && !strncmp((s), (v).name, (l)) && strlen((v).name) == (l))
+#define res_matches(v, x, y) \
+ ((v).xres == (x) && (v).yres == (y))
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk("modedb %s: " fmt, __func__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+const char *fb_mode_option;
+EXPORT_SYMBOL_GPL(fb_mode_option);
+
+/*
+ * Standard video mode definitions (taken from XFree86)
+ */
+
+static const struct fb_videomode modedb[] = {
+
+ /* 640x400 @ 70 Hz, 31.5 kHz hsync */
+ { NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 640x480 @ 60 Hz, 31.5 kHz hsync */
+ { NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 800x600 @ 56 Hz, 35.15 kHz hsync */
+ { NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
+ { NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8, 0,
+ FB_VMODE_INTERLACED },
+
+ /* 640x400 @ 85 Hz, 37.86 kHz hsync */
+ { NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED },
+
+ /* 640x480 @ 72 Hz, 36.5 kHz hsync */
+ { NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 640x480 @ 75 Hz, 37.50 kHz hsync */
+ { NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 800x600 @ 60 Hz, 37.8 kHz hsync */
+ { NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 640x480 @ 85 Hz, 43.27 kHz hsync */
+ { NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
+ { NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10, 0,
+ FB_VMODE_INTERLACED },
+ /* 800x600 @ 72 Hz, 48.0 kHz hsync */
+ { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 60 Hz, 48.4 kHz hsync */
+ { NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 640x480 @ 100 Hz, 53.01 kHz hsync */
+ { NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 60 Hz, 53.5 kHz hsync */
+ { NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 800x600 @ 85 Hz, 55.84 kHz hsync */
+ { NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 70 Hz, 56.5 kHz hsync */
+ { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
+ { NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12, 0,
+ FB_VMODE_INTERLACED },
+
+ /* 800x600 @ 100 Hz, 64.02 kHz hsync */
+ { NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 76 Hz, 62.5 kHz hsync */
+ { NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 70 Hz, 62.4 kHz hsync */
+ { NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
+ { NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1400x1050 @ 60Hz, 63.9 kHz hsync */
+ { NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
+ { NULL, 75, 1400, 1050, 7190, 120, 56, 23, 10, 112, 13,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
+ { NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 85 Hz, 70.24 kHz hsync */
+ { NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 78 Hz, 70.8 kHz hsync */
+ { NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
+ { NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1600x1200 @ 60Hz, 75.00 kHz hsync */
+ { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 84 Hz, 76.0 kHz hsync */
+ { NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
+ { NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1024x768 @ 100Hz, 80.21 kHz hsync */
+ { NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
+ { NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
+ { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x864 @ 100 Hz, 89.62 kHz hsync */
+ { NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
+ { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
+ { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */
+ { NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
+ { NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
+ { NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1800x1440 @ 64Hz, 96.15 kHz hsync */
+ { NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1800x1440 @ 70Hz, 104.52 kHz hsync */
+ { NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 512x384 @ 78 Hz, 31.50 kHz hsync */
+ { NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 512x384 @ 85 Hz, 34.38 kHz hsync */
+ { NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
+ { NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
+ { NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 320x240 @ 72 Hz, 36.5 kHz hsync */
+ { NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
+ { NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 400x300 @ 60 Hz, 37.8 kHz hsync */
+ { NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 400x300 @ 72 Hz, 48.0 kHz hsync */
+ { NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
+ { NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 480x300 @ 60 Hz, 37.8 kHz hsync */
+ { NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 480x300 @ 63 Hz, 39.6 kHz hsync */
+ { NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 480x300 @ 72 Hz, 48.0 kHz hsync */
+ { NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, 0,
+ FB_VMODE_DOUBLE },
+
+ /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */
+ { NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1152x768, 60 Hz, PowerBook G4 Titanium I and II */
+ { NULL, 60, 1152, 768, 14047, 158, 26, 29, 3, 136, 6,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1366x768, 60 Hz, 47.403 kHz hsync, WXGA 16:9 aspect ratio */
+ { NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 1280x800, 60 Hz, 47.403 kHz hsync, WXGA 16:10 aspect ratio */
+ { NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3, 0,
+ FB_VMODE_NONINTERLACED },
+
+ /* 720x576i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
+ { NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5, 0,
+ FB_VMODE_INTERLACED },
+
+ /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
+ { NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, 0,
+ FB_VMODE_INTERLACED },
+
+ /* 864x480 @ 60 Hz, 35.15 kHz hsync */
+ { NULL, 60, 864, 480, 27777, 1, 1, 1, 1, 0, 0,
+ 0, FB_VMODE_NONINTERLACED },
+};
+
+#ifdef CONFIG_FB_MODE_HELPERS
+const struct fb_videomode cea_modes[64] = {
+ /* #1: 640x480p@59.94/60Hz */
+ [1] = {
+ NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #3: 720x480p@59.94/60Hz */
+ [3] = {
+ NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #5: 1920x1080i@59.94/60Hz */
+ [5] = {
+ NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED, 0,
+ },
+ /* #7: 720(1440)x480iH@59.94/60Hz */
+ [7] = {
+ NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_INTERLACED, 0,
+ },
+ /* #9: 720(1440)x240pH@59.94/60Hz */
+ [9] = {
+ NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #18: 720x576pH@50Hz */
+ [18] = {
+ NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #19: 1280x720p@50Hz */
+ [19] = {
+ NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #20: 1920x1080i@50Hz */
+ [20] = {
+ NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED, 0,
+ },
+ /* #32: 1920x1080p@23.98/24Hz */
+ [32] = {
+ NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+ /* #35: (2880)x480p4x@59.94/60Hz */
+ [35] = {
+ NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
+ FB_VMODE_NONINTERLACED, 0,
+ },
+};
+
+const struct fb_videomode vesa_modes[] = {
+ /* 0 640x350-85 VESA */
+ { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
+ FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA},
+ /* 1 640x400-85 VESA */
+ { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 2 720x400-85 VESA */
+ { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 3 640x480-60 VESA */
+ { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 4 640x480-72 VESA */
+ { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 5 640x480-75 VESA */
+ { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 6 640x480-85 VESA */
+ { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 7 800x600-56 VESA */
+ { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 8 800x600-60 VESA */
+ { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 9 800x600-72 VESA */
+ { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 10 800x600-75 VESA */
+ { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 11 800x600-85 VESA */
+ { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 12 1024x768i-43 VESA */
+ { NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED, FB_MODE_IS_VESA },
+ /* 13 1024x768-60 VESA */
+ { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 14 1024x768-70 VESA */
+ { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 15 1024x768-75 VESA */
+ { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 16 1024x768-85 VESA */
+ { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 17 1152x864-75 VESA */
+ { NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 18 1280x960-60 VESA */
+ { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 19 1280x960-85 VESA */
+ { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 20 1280x1024-60 VESA */
+ { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 21 1280x1024-75 VESA */
+ { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 22 1280x1024-85 VESA */
+ { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 23 1600x1200-60 VESA */
+ { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 24 1600x1200-65 VESA */
+ { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 25 1600x1200-70 VESA */
+ { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 26 1600x1200-75 VESA */
+ { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 27 1600x1200-85 VESA */
+ { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 28 1792x1344-60 VESA */
+ { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 29 1792x1344-75 VESA */
+ { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 30 1856x1392-60 VESA */
+ { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 31 1856x1392-75 VESA */
+ { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 32 1920x1440-60 VESA */
+ { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 33 1920x1440-75 VESA */
+ { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+};
+EXPORT_SYMBOL(vesa_modes);
+#endif /* CONFIG_FB_MODE_HELPERS */
+
+/**
+ * fb_try_mode - test a video mode
+ * @var: frame buffer user defined part of display
+ * @info: frame buffer info structure
+ * @mode: frame buffer video mode structure
+ * @bpp: color depth in bits per pixel
+ *
+ * Tries a video mode to test it's validity for device @info.
+ *
+ * Returns 1 on success.
+ *
+ */
+
+static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
+ const struct fb_videomode *mode, unsigned int bpp)
+{
+ int err = 0;
+
+ DPRINTK("Trying mode %s %dx%d-%d@%d\n",
+ mode->name ? mode->name : "noname",
+ mode->xres, mode->yres, bpp, mode->refresh);
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = mode->xres;
+ var->yres_virtual = mode->yres;
+ var->xoffset = 0;
+ var->yoffset = 0;
+ var->bits_per_pixel = bpp;
+ var->activate |= FB_ACTIVATE_TEST;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = mode->vmode;
+ if (info->fbops->fb_check_var)
+ err = info->fbops->fb_check_var(var, info);
+ var->activate &= ~FB_ACTIVATE_TEST;
+ return err;
+}
+
+/**
+ * fb_find_mode - finds a valid video mode
+ * @var: frame buffer user defined part of display
+ * @info: frame buffer info structure
+ * @mode_option: string video mode to find
+ * @db: video mode database
+ * @dbsize: size of @db
+ * @default_mode: default video mode to fall back to
+ * @default_bpp: default color depth in bits per pixel
+ *
+ * Finds a suitable video mode, starting with the specified mode
+ * in @mode_option with fallback to @default_mode. If
+ * @default_mode fails, all modes in the video mode database will
+ * be tried.
+ *
+ * Valid mode specifiers for @mode_option:
+ *
+ * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
+ * <name>[-<bpp>][@<refresh>]
+ *
+ * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
+ * <name> a string.
+ *
+ * If 'M' is present after yres (and before refresh/bpp if present),
+ * the function will compute the timings using VESA(tm) Coordinated
+ * Video Timings (CVT). If 'R' is present after 'M', will compute with
+ * reduced blanking (for flatpanels). If 'i' is present, compute
+ * interlaced mode. If 'm' is present, add margins equal to 1.8%
+ * of xres rounded down to 8 pixels, and 1.8% of yres. The char
+ * 'i' and 'm' must be after 'M' and 'R'. Example:
+ *
+ * 1024x768MR-8@60m - Reduced blank with margins at 60Hz.
+ *
+ * NOTE: The passed struct @var is _not_ cleared! This allows you
+ * to supply values for e.g. the grayscale and accel_flags fields.
+ *
+ * Returns zero for failure, 1 if using specified @mode_option,
+ * 2 if using specified @mode_option with an ignored refresh rate,
+ * 3 if default mode is used, 4 if fall back to any valid mode.
+ *
+ */
+
+int fb_find_mode(struct fb_var_screeninfo *var,
+ struct fb_info *info, const char *mode_option,
+ const struct fb_videomode *db, unsigned int dbsize,
+ const struct fb_videomode *default_mode,
+ unsigned int default_bpp)
+{
+ int i;
+
+ /* Set up defaults */
+ if (!db) {
+ db = modedb;
+ dbsize = ARRAY_SIZE(modedb);
+ }
+
+ if (!default_mode)
+ default_mode = &db[0];
+
+ if (!default_bpp)
+ default_bpp = 8;
+
+ /* Did the user specify a video mode? */
+ if (!mode_option)
+ mode_option = fb_mode_option;
+ if (mode_option) {
+ const char *name = mode_option;
+ unsigned int namelen = strlen(name);
+ int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
+ unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
+ int yres_specified = 0, cvt = 0, rb = 0, interlace = 0;
+ int margins = 0;
+ u32 best, diff, tdiff;
+
+ for (i = namelen-1; i >= 0; i--) {
+ switch (name[i]) {
+ case '@':
+ namelen = i;
+ if (!refresh_specified && !bpp_specified &&
+ !yres_specified) {
+ refresh = simple_strtol(&name[i+1], NULL,
+ 10);
+ refresh_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case '-':
+ namelen = i;
+ if (!bpp_specified && !yres_specified) {
+ bpp = simple_strtol(&name[i+1], NULL,
+ 10);
+ bpp_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case 'x':
+ if (!yres_specified) {
+ yres = simple_strtol(&name[i+1], NULL,
+ 10);
+ yres_specified = 1;
+ } else
+ goto done;
+ break;
+ case '0' ... '9':
+ break;
+ case 'M':
+ if (!yres_specified)
+ cvt = 1;
+ break;
+ case 'R':
+ if (!cvt)
+ rb = 1;
+ break;
+ case 'm':
+ if (!cvt)
+ margins = 1;
+ break;
+ case 'i':
+ if (!cvt)
+ interlace = 1;
+ break;
+ default:
+ goto done;
+ }
+ }
+ if (i < 0 && yres_specified) {
+ xres = simple_strtol(name, NULL, 10);
+ res_specified = 1;
+ }
+done:
+ if (cvt) {
+ struct fb_videomode cvt_mode;
+ int ret;
+
+ DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
+ (refresh) ? refresh : 60,
+ (rb) ? " reduced blanking" : "",
+ (margins) ? " with margins" : "",
+ (interlace) ? " interlaced" : "");
+
+ memset(&cvt_mode, 0, sizeof(cvt_mode));
+ cvt_mode.xres = xres;
+ cvt_mode.yres = yres;
+ cvt_mode.refresh = (refresh) ? refresh : 60;
+
+ if (interlace)
+ cvt_mode.vmode |= FB_VMODE_INTERLACED;
+ else
+ cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
+
+ ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
+
+ if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
+ DPRINTK("modedb CVT: CVT mode ok\n");
+ return 1;
+ }
+
+ DPRINTK("CVT mode invalid, getting mode from database\n");
+ }
+
+ DPRINTK("Trying specified video mode%s %ix%i\n",
+ refresh_specified ? "" : " (ignoring refresh rate)",
+ xres, yres);
+
+ if (!refresh_specified) {
+ /*
+ * If the caller has provided a custom mode database and
+ * a valid monspecs structure, we look for the mode with
+ * the highest refresh rate. Otherwise we play it safe
+ * it and try to find a mode with a refresh rate closest
+ * to the standard 60 Hz.
+ */
+ if (db != modedb &&
+ info->monspecs.vfmin && info->monspecs.vfmax &&
+ info->monspecs.hfmin && info->monspecs.hfmax &&
+ info->monspecs.dclkmax) {
+ refresh = 1000;
+ } else {
+ refresh = 60;
+ }
+ }
+
+ diff = -1;
+ best = -1;
+ for (i = 0; i < dbsize; i++) {
+ if ((name_matches(db[i], name, namelen) ||
+ (res_specified && res_matches(db[i], xres, yres))) &&
+ !fb_try_mode(var, info, &db[i], bpp)) {
+ if (refresh_specified && db[i].refresh == refresh)
+ return 1;
+
+ if (abs(db[i].refresh - refresh) < diff) {
+ diff = abs(db[i].refresh - refresh);
+ best = i;
+ }
+ }
+ }
+ if (best != -1) {
+ fb_try_mode(var, info, &db[best], bpp);
+ return (refresh_specified) ? 2 : 1;
+ }
+
+ diff = 2 * (xres + yres);
+ best = -1;
+ DPRINTK("Trying best-fit modes\n");
+ for (i = 0; i < dbsize; i++) {
+ DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);
+ if (!fb_try_mode(var, info, &db[i], bpp)) {
+ tdiff = abs(db[i].xres - xres) +
+ abs(db[i].yres - yres);
+
+ /*
+ * Penalize modes with resolutions smaller
+ * than requested.
+ */
+ if (xres > db[i].xres || yres > db[i].yres)
+ tdiff += xres + yres;
+
+ if (diff > tdiff) {
+ diff = tdiff;
+ best = i;
+ }
+ }
+ }
+ if (best != -1) {
+ fb_try_mode(var, info, &db[best], bpp);
+ return 5;
+ }
+ }
+
+ DPRINTK("Trying default video mode\n");
+ if (!fb_try_mode(var, info, default_mode, default_bpp))
+ return 3;
+
+ DPRINTK("Trying all modes\n");
+ for (i = 0; i < dbsize; i++)
+ if (!fb_try_mode(var, info, &db[i], default_bpp))
+ return 4;
+
+ DPRINTK("No valid mode found\n");
+ return 0;
+}
+
+/**
+ * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
+ * @mode: pointer to struct fb_videomode
+ * @var: pointer to struct fb_var_screeninfo
+ */
+void fb_var_to_videomode(struct fb_videomode *mode,
+ const struct fb_var_screeninfo *var)
+{
+ u32 pixclock, hfreq, htotal, vtotal;
+
+ mode->name = NULL;
+ mode->xres = var->xres;
+ mode->yres = var->yres;
+ mode->pixclock = var->pixclock;
+ mode->hsync_len = var->hsync_len;
+ mode->vsync_len = var->vsync_len;
+ mode->left_margin = var->left_margin;
+ mode->right_margin = var->right_margin;
+ mode->upper_margin = var->upper_margin;
+ mode->lower_margin = var->lower_margin;
+ mode->sync = var->sync;
+ mode->vmode = var->vmode & FB_VMODE_MASK;
+ mode->flag = FB_MODE_IS_FROM_VAR;
+ mode->refresh = 0;
+
+ if (!var->pixclock)
+ return;
+
+ pixclock = PICOS2KHZ(var->pixclock) * 1000;
+
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+
+ if (var->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hfreq = pixclock/htotal;
+ mode->refresh = hfreq/vtotal;
+}
+
+/**
+ * fb_videomode_to_var - convert fb_videomode to fb_var_screeninfo
+ * @var: pointer to struct fb_var_screeninfo
+ * @mode: pointer to struct fb_videomode
+ */
+void fb_videomode_to_var(struct fb_var_screeninfo *var,
+ const struct fb_videomode *mode)
+{
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = mode->xres;
+ var->yres_virtual = mode->yres;
+ var->xoffset = 0;
+ var->yoffset = 0;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = mode->vmode & FB_VMODE_MASK;
+}
+
+/**
+ * fb_mode_is_equal - compare 2 videomodes
+ * @mode1: first videomode
+ * @mode2: second videomode
+ *
+ * RETURNS:
+ * 1 if equal, 0 if not
+ */
+int fb_mode_is_equal(const struct fb_videomode *mode1,
+ const struct fb_videomode *mode2)
+{
+ return (mode1->xres == mode2->xres &&
+ mode1->yres == mode2->yres &&
+ mode1->pixclock == mode2->pixclock &&
+ mode1->hsync_len == mode2->hsync_len &&
+ mode1->vsync_len == mode2->vsync_len &&
+ mode1->left_margin == mode2->left_margin &&
+ mode1->right_margin == mode2->right_margin &&
+ mode1->upper_margin == mode2->upper_margin &&
+ mode1->lower_margin == mode2->lower_margin &&
+ mode1->sync == mode2->sync &&
+ mode1->vmode == mode2->vmode);
+}
+
+/**
+ * fb_find_best_mode - find best matching videomode
+ * @var: pointer to struct fb_var_screeninfo
+ * @head: pointer to struct list_head of modelist
+ *
+ * RETURNS:
+ * struct fb_videomode, NULL if none found
+ *
+ * IMPORTANT:
+ * This function assumes that all modelist entries in
+ * info->modelist are valid.
+ *
+ * NOTES:
+ * Finds best matching videomode which has an equal or greater dimension than
+ * var->xres and var->yres. If more than 1 videomode is found, will return
+ * the videomode with the highest refresh rate
+ */
+const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var,
+ struct list_head *head)
+{
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ struct fb_videomode *mode, *best = NULL;
+ u32 diff = -1;
+
+ list_for_each(pos, head) {
+ u32 d;
+
+ modelist = list_entry(pos, struct fb_modelist, list);
+ mode = &modelist->mode;
+
+ if (mode->xres >= var->xres && mode->yres >= var->yres) {
+ d = (mode->xres - var->xres) +
+ (mode->yres - var->yres);
+ if (diff > d) {
+ diff = d;
+ best = mode;
+ } else if (diff == d && best &&
+ mode->refresh > best->refresh)
+ best = mode;
+ }
+ }
+ return best;
+}
+
+/**
+ * fb_find_nearest_mode - find closest videomode
+ *
+ * @mode: pointer to struct fb_videomode
+ * @head: pointer to modelist
+ *
+ * Finds best matching videomode, smaller or greater in dimension.
+ * If more than 1 videomode is found, will return the videomode with
+ * the closest refresh rate.
+ */
+const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
+ struct list_head *head)
+{
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ struct fb_videomode *cmode, *best = NULL;
+ u32 diff = -1, diff_refresh = -1;
+
+ list_for_each(pos, head) {
+ u32 d;
+
+ modelist = list_entry(pos, struct fb_modelist, list);
+ cmode = &modelist->mode;
+
+ d = abs(cmode->xres - mode->xres) +
+ abs(cmode->yres - mode->yres);
+ if (diff > d) {
+ diff = d;
+ diff_refresh = abs(cmode->refresh - mode->refresh);
+ best = cmode;
+ } else if (diff == d) {
+ d = abs(cmode->refresh - mode->refresh);
+ if (diff_refresh > d) {
+ diff_refresh = d;
+ best = cmode;
+ }
+ }
+ }
+
+ return best;
+}
+
+/**
+ * fb_match_mode - find a videomode which exactly matches the timings in var
+ * @var: pointer to struct fb_var_screeninfo
+ * @head: pointer to struct list_head of modelist
+ *
+ * RETURNS:
+ * struct fb_videomode, NULL if none found
+ */
+const struct fb_videomode *fb_match_mode(const struct fb_var_screeninfo *var,
+ struct list_head *head)
+{
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ struct fb_videomode *m, mode;
+
+ fb_var_to_videomode(&mode, var);
+ list_for_each(pos, head) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ m = &modelist->mode;
+ if (fb_mode_is_equal(m, &mode))
+ return m;
+ }
+ return NULL;
+}
+
+/**
+ * fb_add_videomode - adds videomode entry to modelist
+ * @mode: videomode to add
+ * @head: struct list_head of modelist
+ *
+ * NOTES:
+ * Will only add unmatched mode entries
+ */
+int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
+{
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ struct fb_videomode *m;
+ int found = 0;
+
+ list_for_each(pos, head) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ m = &modelist->mode;
+ if (fb_mode_is_equal(m, mode)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ modelist = kmalloc(sizeof(struct fb_modelist),
+ GFP_KERNEL);
+
+ if (!modelist)
+ return -ENOMEM;
+ modelist->mode = *mode;
+ list_add(&modelist->list, head);
+ }
+ return 0;
+}
+
+/**
+ * fb_delete_videomode - removed videomode entry from modelist
+ * @mode: videomode to remove
+ * @head: struct list_head of modelist
+ *
+ * NOTES:
+ * Will remove all matching mode entries
+ */
+void fb_delete_videomode(const struct fb_videomode *mode,
+ struct list_head *head)
+{
+ struct list_head *pos, *n;
+ struct fb_modelist *modelist;
+ struct fb_videomode *m;
+
+ list_for_each_safe(pos, n, head) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ m = &modelist->mode;
+ if (fb_mode_is_equal(m, mode)) {
+ list_del(pos);
+ kfree(pos);
+ }
+ }
+}
+
+/**
+ * fb_destroy_modelist - destroy modelist
+ * @head: struct list_head of modelist
+ */
+void fb_destroy_modelist(struct list_head *head)
+{
+ struct list_head *pos, *n;
+
+ list_for_each_safe(pos, n, head) {
+ list_del(pos);
+ kfree(pos);
+ }
+}
+EXPORT_SYMBOL_GPL(fb_destroy_modelist);
+
+/**
+ * fb_videomode_to_modelist - convert mode array to mode list
+ * @modedb: array of struct fb_videomode
+ * @num: number of entries in array
+ * @head: struct list_head of modelist
+ */
+void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num,
+ struct list_head *head)
+{
+ int i;
+
+ INIT_LIST_HEAD(head);
+
+ for (i = 0; i < num; i++) {
+ if (fb_add_videomode(&modedb[i], head))
+ return;
+ }
+}
+
+const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs,
+ struct list_head *head)
+{
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+ const struct fb_videomode *m, *m1 = NULL, *md = NULL, *best = NULL;
+ int first = 0;
+
+ if (!head->prev || !head->next || list_empty(head))
+ goto finished;
+
+ /* get the first detailed mode and the very first mode */
+ list_for_each(pos, head) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ m = &modelist->mode;
+
+ if (!first) {
+ m1 = m;
+ first = 1;
+ }
+
+ if (m->flag & FB_MODE_IS_FIRST) {
+ md = m;
+ break;
+ }
+ }
+
+ /* first detailed timing is preferred */
+ if (specs->misc & FB_MISC_1ST_DETAIL) {
+ best = md;
+ goto finished;
+ }
+
+ /* find best mode based on display width and height */
+ if (specs->max_x && specs->max_y) {
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(struct fb_var_screeninfo));
+ var.xres = (specs->max_x * 7200)/254;
+ var.yres = (specs->max_y * 7200)/254;
+ m = fb_find_best_mode(&var, head);
+ if (m) {
+ best = m;
+ goto finished;
+ }
+ }
+
+ /* use first detailed mode */
+ if (md) {
+ best = md;
+ goto finished;
+ }
+
+ /* last resort, use the very first mode */
+ best = m1;
+finished:
+ return best;
+}
+EXPORT_SYMBOL(fb_find_best_display);
+
+EXPORT_SYMBOL(fb_videomode_to_var);
+EXPORT_SYMBOL(fb_var_to_videomode);
+EXPORT_SYMBOL(fb_mode_is_equal);
+EXPORT_SYMBOL(fb_add_videomode);
+EXPORT_SYMBOL(fb_match_mode);
+EXPORT_SYMBOL(fb_find_best_mode);
+EXPORT_SYMBOL(fb_find_nearest_mode);
+EXPORT_SYMBOL(fb_videomode_to_modelist);
+EXPORT_SYMBOL(fb_find_mode);
+EXPORT_SYMBOL(fb_find_mode_cvt);
--- /dev/null
+/*
+ * Common utility functions for VGA-based graphics cards.
+ *
+ * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Some parts are based on David Boucher's viafb (http://davesdomain.org.uk/viafb/)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/svga.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+
+/* Write a CRT register value spread across multiple registers */
+void svga_wcrt_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
+{
+ u8 regval, bitval, bitnum;
+
+ while (regset->regnum != VGA_REGSET_END_VAL) {
+ regval = vga_rcrt(regbase, regset->regnum);
+ bitnum = regset->lowbit;
+ while (bitnum <= regset->highbit) {
+ bitval = 1 << bitnum;
+ regval = regval & ~bitval;
+ if (value & 1) regval = regval | bitval;
+ bitnum ++;
+ value = value >> 1;
+ }
+ vga_wcrt(regbase, regset->regnum, regval);
+ regset ++;
+ }
+}
+
+/* Write a sequencer register value spread across multiple registers */
+void svga_wseq_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
+{
+ u8 regval, bitval, bitnum;
+
+ while (regset->regnum != VGA_REGSET_END_VAL) {
+ regval = vga_rseq(regbase, regset->regnum);
+ bitnum = regset->lowbit;
+ while (bitnum <= regset->highbit) {
+ bitval = 1 << bitnum;
+ regval = regval & ~bitval;
+ if (value & 1) regval = regval | bitval;
+ bitnum ++;
+ value = value >> 1;
+ }
+ vga_wseq(regbase, regset->regnum, regval);
+ regset ++;
+ }
+}
+
+static unsigned int svga_regset_size(const struct vga_regset *regset)
+{
+ u8 count = 0;
+
+ while (regset->regnum != VGA_REGSET_END_VAL) {
+ count += regset->highbit - regset->lowbit + 1;
+ regset ++;
+ }
+ return 1 << count;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Set graphics controller registers to sane values */
+void svga_set_default_gfx_regs(void __iomem *regbase)
+{
+ /* All standard GFX registers (GR00 - GR08) */
+ vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0x00);
+ vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0x00);
+ vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0x00);
+ vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0x00);
+ vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0x00);
+ vga_wgfx(regbase, VGA_GFX_MODE, 0x00);
+/* vga_wgfx(regbase, VGA_GFX_MODE, 0x20); */
+/* vga_wgfx(regbase, VGA_GFX_MODE, 0x40); */
+ vga_wgfx(regbase, VGA_GFX_MISC, 0x05);
+/* vga_wgfx(regbase, VGA_GFX_MISC, 0x01); */
+ vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x0F);
+ vga_wgfx(regbase, VGA_GFX_BIT_MASK, 0xFF);
+}
+
+/* Set attribute controller registers to sane values */
+void svga_set_default_atc_regs(void __iomem *regbase)
+{
+ u8 count;
+
+ vga_r(regbase, 0x3DA);
+ vga_w(regbase, VGA_ATT_W, 0x00);
+
+ /* All standard ATC registers (AR00 - AR14) */
+ for (count = 0; count <= 0xF; count ++)
+ svga_wattr(regbase, count, count);
+
+ svga_wattr(regbase, VGA_ATC_MODE, 0x01);
+/* svga_wattr(regbase, VGA_ATC_MODE, 0x41); */
+ svga_wattr(regbase, VGA_ATC_OVERSCAN, 0x00);
+ svga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 0x0F);
+ svga_wattr(regbase, VGA_ATC_PEL, 0x00);
+ svga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0x00);
+
+ vga_r(regbase, 0x3DA);
+ vga_w(regbase, VGA_ATT_W, 0x20);
+}
+
+/* Set sequencer registers to sane values */
+void svga_set_default_seq_regs(void __iomem *regbase)
+{
+ /* Standard sequencer registers (SR01 - SR04), SR00 is not set */
+ vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, VGA_SR01_CHAR_CLK_8DOTS);
+ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, VGA_SR02_ALL_PLANES);
+ vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
+/* vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE | VGA_SR04_CHN_4M); */
+ vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE);
+}
+
+/* Set CRTC registers to sane values */
+void svga_set_default_crt_regs(void __iomem *regbase)
+{
+ /* Standard CRT registers CR03 CR08 CR09 CR14 CR17 */
+ svga_wcrt_mask(regbase, 0x03, 0x80, 0x80); /* Enable vertical retrace EVRA */
+ vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
+ svga_wcrt_mask(regbase, VGA_CRTC_MAX_SCAN, 0, 0x1F);
+ vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
+ vga_wcrt(regbase, VGA_CRTC_MODE, 0xE3);
+}
+
+void svga_set_textmode_vga_regs(void __iomem *regbase)
+{
+ /* svga_wseq_mask(regbase, 0x1, 0x00, 0x01); */ /* Switch 8/9 pixel per char */
+ vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM);
+ vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x03);
+
+ vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, 0x0f); /* 0x4f */
+ vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0x1f);
+ svga_wcrt_mask(regbase, VGA_CRTC_MODE, 0x23, 0x7f);
+
+ vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0x0d);
+ vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 0x0e);
+ vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0x00);
+ vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0x00);
+
+ vga_wgfx(regbase, VGA_GFX_MODE, 0x10); /* Odd/even memory mode */
+ vga_wgfx(regbase, VGA_GFX_MISC, 0x0E); /* Misc graphics register - text mode enable */
+ vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x00);
+
+ vga_r(regbase, 0x3DA);
+ vga_w(regbase, VGA_ATT_W, 0x00);
+
+ svga_wattr(regbase, 0x10, 0x0C); /* Attribute Mode Control Register - text mode, blinking and line graphics */
+ svga_wattr(regbase, 0x13, 0x08); /* Horizontal Pixel Panning Register */
+
+ vga_r(regbase, 0x3DA);
+ vga_w(regbase, VGA_ATT_W, 0x20);
+}
+
+#if 0
+void svga_dump_var(struct fb_var_screeninfo *var, int node)
+{
+ pr_debug("fb%d: var.vmode : 0x%X\n", node, var->vmode);
+ pr_debug("fb%d: var.xres : %d\n", node, var->xres);
+ pr_debug("fb%d: var.yres : %d\n", node, var->yres);
+ pr_debug("fb%d: var.bits_per_pixel: %d\n", node, var->bits_per_pixel);
+ pr_debug("fb%d: var.xres_virtual : %d\n", node, var->xres_virtual);
+ pr_debug("fb%d: var.yres_virtual : %d\n", node, var->yres_virtual);
+ pr_debug("fb%d: var.left_margin : %d\n", node, var->left_margin);
+ pr_debug("fb%d: var.right_margin : %d\n", node, var->right_margin);
+ pr_debug("fb%d: var.upper_margin : %d\n", node, var->upper_margin);
+ pr_debug("fb%d: var.lower_margin : %d\n", node, var->lower_margin);
+ pr_debug("fb%d: var.hsync_len : %d\n", node, var->hsync_len);
+ pr_debug("fb%d: var.vsync_len : %d\n", node, var->vsync_len);
+ pr_debug("fb%d: var.sync : 0x%X\n", node, var->sync);
+ pr_debug("fb%d: var.pixclock : %d\n\n", node, var->pixclock);
+}
+#endif /* 0 */
+
+
+/* ------------------------------------------------------------------------- */
+
+
+void svga_settile(struct fb_info *info, struct fb_tilemap *map)
+{
+ const u8 *font = map->data;
+ u8 __iomem *fb = (u8 __iomem *)info->screen_base;
+ int i, c;
+
+ if ((map->width != 8) || (map->height != 16) ||
+ (map->depth != 1) || (map->length != 256)) {
+ fb_err(info, "unsupported font parameters: width %d, height %d, depth %d, length %d\n",
+ map->width, map->height, map->depth, map->length);
+ return;
+ }
+
+ fb += 2;
+ for (c = 0; c < map->length; c++) {
+ for (i = 0; i < map->height; i++) {
+ fb_writeb(font[i], fb + i * 4);
+// fb[i * 4] = font[i];
+ }
+ fb += 128;
+ font += map->height;
+ }
+}
+
+/* Copy area in text (tileblit) mode */
+void svga_tilecopy(struct fb_info *info, struct fb_tilearea *area)
+{
+ int dx, dy;
+ /* colstride is halved in this function because u16 are used */
+ int colstride = 1 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
+ int rowstride = colstride * (info->var.xres_virtual / 8);
+ u16 __iomem *fb = (u16 __iomem *) info->screen_base;
+ u16 __iomem *src, *dst;
+
+ if ((area->sy > area->dy) ||
+ ((area->sy == area->dy) && (area->sx > area->dx))) {
+ src = fb + area->sx * colstride + area->sy * rowstride;
+ dst = fb + area->dx * colstride + area->dy * rowstride;
+ } else {
+ src = fb + (area->sx + area->width - 1) * colstride
+ + (area->sy + area->height - 1) * rowstride;
+ dst = fb + (area->dx + area->width - 1) * colstride
+ + (area->dy + area->height - 1) * rowstride;
+
+ colstride = -colstride;
+ rowstride = -rowstride;
+ }
+
+ for (dy = 0; dy < area->height; dy++) {
+ u16 __iomem *src2 = src;
+ u16 __iomem *dst2 = dst;
+ for (dx = 0; dx < area->width; dx++) {
+ fb_writew(fb_readw(src2), dst2);
+// *dst2 = *src2;
+ src2 += colstride;
+ dst2 += colstride;
+ }
+ src += rowstride;
+ dst += rowstride;
+ }
+}
+
+/* Fill area in text (tileblit) mode */
+void svga_tilefill(struct fb_info *info, struct fb_tilerect *rect)
+{
+ int dx, dy;
+ int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
+ int rowstride = colstride * (info->var.xres_virtual / 8);
+ int attr = (0x0F & rect->bg) << 4 | (0x0F & rect->fg);
+ u8 __iomem *fb = (u8 __iomem *)info->screen_base;
+ fb += rect->sx * colstride + rect->sy * rowstride;
+
+ for (dy = 0; dy < rect->height; dy++) {
+ u8 __iomem *fb2 = fb;
+ for (dx = 0; dx < rect->width; dx++) {
+ fb_writeb(rect->index, fb2);
+ fb_writeb(attr, fb2 + 1);
+ fb2 += colstride;
+ }
+ fb += rowstride;
+ }
+}
+
+/* Write text in text (tileblit) mode */
+void svga_tileblit(struct fb_info *info, struct fb_tileblit *blit)
+{
+ int dx, dy, i;
+ int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
+ int rowstride = colstride * (info->var.xres_virtual / 8);
+ int attr = (0x0F & blit->bg) << 4 | (0x0F & blit->fg);
+ u8 __iomem *fb = (u8 __iomem *)info->screen_base;
+ fb += blit->sx * colstride + blit->sy * rowstride;
+
+ i=0;
+ for (dy=0; dy < blit->height; dy ++) {
+ u8 __iomem *fb2 = fb;
+ for (dx = 0; dx < blit->width; dx ++) {
+ fb_writeb(blit->indices[i], fb2);
+ fb_writeb(attr, fb2 + 1);
+ fb2 += colstride;
+ i ++;
+ if (i == blit->length) return;
+ }
+ fb += rowstride;
+ }
+
+}
+
+/* Set cursor in text (tileblit) mode */
+void svga_tilecursor(void __iomem *regbase, struct fb_info *info, struct fb_tilecursor *cursor)
+{
+ u8 cs = 0x0d;
+ u8 ce = 0x0e;
+ u16 pos = cursor->sx + (info->var.xoffset / 8)
+ + (cursor->sy + (info->var.yoffset / 16))
+ * (info->var.xres_virtual / 8);
+
+ if (! cursor -> mode)
+ return;
+
+ svga_wcrt_mask(regbase, 0x0A, 0x20, 0x20); /* disable cursor */
+
+ if (cursor -> shape == FB_TILE_CURSOR_NONE)
+ return;
+
+ switch (cursor -> shape) {
+ case FB_TILE_CURSOR_UNDERLINE:
+ cs = 0x0d;
+ break;
+ case FB_TILE_CURSOR_LOWER_THIRD:
+ cs = 0x09;
+ break;
+ case FB_TILE_CURSOR_LOWER_HALF:
+ cs = 0x07;
+ break;
+ case FB_TILE_CURSOR_TWO_THIRDS:
+ cs = 0x05;
+ break;
+ case FB_TILE_CURSOR_BLOCK:
+ cs = 0x01;
+ break;
+ }
+
+ /* set cursor position */
+ vga_wcrt(regbase, 0x0E, pos >> 8);
+ vga_wcrt(regbase, 0x0F, pos & 0xFF);
+
+ vga_wcrt(regbase, 0x0B, ce); /* set cursor end */
+ vga_wcrt(regbase, 0x0A, cs); /* set cursor start and enable it */
+}
+
+int svga_get_tilemax(struct fb_info *info)
+{
+ return 256;
+}
+
+/* Get capabilities of accelerator based on the mode */
+
+void svga_get_caps(struct fb_info *info, struct fb_blit_caps *caps,
+ struct fb_var_screeninfo *var)
+{
+ if (var->bits_per_pixel == 0) {
+ /* can only support 256 8x16 bitmap */
+ caps->x = 1 << (8 - 1);
+ caps->y = 1 << (16 - 1);
+ caps->len = 256;
+ } else {
+ caps->x = (var->bits_per_pixel == 4) ? 1 << (8 - 1) : ~(u32)0;
+ caps->y = ~(u32)0;
+ caps->len = ~(u32)0;
+ }
+}
+EXPORT_SYMBOL(svga_get_caps);
+
+/* ------------------------------------------------------------------------- */
+
+
+/*
+ * Compute PLL settings (M, N, R)
+ * F_VCO = (F_BASE * M) / N
+ * F_OUT = F_VCO / (2^R)
+ */
+
+static inline u32 abs_diff(u32 a, u32 b)
+{
+ return (a > b) ? (a - b) : (b - a);
+}
+
+int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u16 *r, int node)
+{
+ u16 am, an, ar;
+ u32 f_vco, f_current, delta_current, delta_best;
+
+ pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int) f_wanted);
+
+ ar = pll->r_max;
+ f_vco = f_wanted << ar;
+
+ /* overflow check */
+ if ((f_vco >> ar) != f_wanted)
+ return -EINVAL;
+
+ /* It is usually better to have greater VCO clock
+ because of better frequency stability.
+ So first try r_max, then r smaller. */
+ while ((ar > pll->r_min) && (f_vco > pll->f_vco_max)) {
+ ar--;
+ f_vco = f_vco >> 1;
+ }
+
+ /* VCO bounds check */
+ if ((f_vco < pll->f_vco_min) || (f_vco > pll->f_vco_max))
+ return -EINVAL;
+
+ delta_best = 0xFFFFFFFF;
+ *m = 0;
+ *n = 0;
+ *r = ar;
+
+ am = pll->m_min;
+ an = pll->n_min;
+
+ while ((am <= pll->m_max) && (an <= pll->n_max)) {
+ f_current = (pll->f_base * am) / an;
+ delta_current = abs_diff (f_current, f_vco);
+
+ if (delta_current < delta_best) {
+ delta_best = delta_current;
+ *m = am;
+ *n = an;
+ }
+
+ if (f_current <= f_vco) {
+ am ++;
+ } else {
+ an ++;
+ }
+ }
+
+ f_current = (pll->f_base * *m) / *n;
+ pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int) (f_current >> ar), (int) f_current);
+ pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int) *m, (unsigned int) *n, (unsigned int) *r);
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Check CRT timing values */
+int svga_check_timings(const struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node)
+{
+ u32 value;
+
+ var->xres = (var->xres+7)&~7;
+ var->left_margin = (var->left_margin+7)&~7;
+ var->right_margin = (var->right_margin+7)&~7;
+ var->hsync_len = (var->hsync_len+7)&~7;
+
+ /* Check horizontal total */
+ value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+ if (((value / 8) - 5) >= svga_regset_size (tm->h_total_regs))
+ return -EINVAL;
+
+ /* Check horizontal display and blank start */
+ value = var->xres;
+ if (((value / 8) - 1) >= svga_regset_size (tm->h_display_regs))
+ return -EINVAL;
+ if (((value / 8) - 1) >= svga_regset_size (tm->h_blank_start_regs))
+ return -EINVAL;
+
+ /* Check horizontal sync start */
+ value = var->xres + var->right_margin;
+ if (((value / 8) - 1) >= svga_regset_size (tm->h_sync_start_regs))
+ return -EINVAL;
+
+ /* Check horizontal blank end (or length) */
+ value = var->left_margin + var->right_margin + var->hsync_len;
+ if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_blank_end_regs)))
+ return -EINVAL;
+
+ /* Check horizontal sync end (or length) */
+ value = var->hsync_len;
+ if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_sync_end_regs)))
+ return -EINVAL;
+
+ /* Check vertical total */
+ value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+ if ((value - 1) >= svga_regset_size(tm->v_total_regs))
+ return -EINVAL;
+
+ /* Check vertical display and blank start */
+ value = var->yres;
+ if ((value - 1) >= svga_regset_size(tm->v_display_regs))
+ return -EINVAL;
+ if ((value - 1) >= svga_regset_size(tm->v_blank_start_regs))
+ return -EINVAL;
+
+ /* Check vertical sync start */
+ value = var->yres + var->lower_margin;
+ if ((value - 1) >= svga_regset_size(tm->v_sync_start_regs))
+ return -EINVAL;
+
+ /* Check vertical blank end (or length) */
+ value = var->upper_margin + var->lower_margin + var->vsync_len;
+ if ((value == 0) || (value >= svga_regset_size (tm->v_blank_end_regs)))
+ return -EINVAL;
+
+ /* Check vertical sync end (or length) */
+ value = var->vsync_len;
+ if ((value == 0) || (value >= svga_regset_size (tm->v_sync_end_regs)))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Set CRT timing registers */
+void svga_set_timings(void __iomem *regbase, const struct svga_timing_regs *tm,
+ struct fb_var_screeninfo *var,
+ u32 hmul, u32 hdiv, u32 vmul, u32 vdiv, u32 hborder, int node)
+{
+ u8 regval;
+ u32 value;
+
+ value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal total : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_total_regs, (value / 8) - 5);
+
+ value = var->xres;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal display : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_display_regs, (value / 8) - 1);
+
+ value = var->xres;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal blank start: %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_blank_start_regs, (value / 8) - 1 + hborder);
+
+ value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal blank end : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_blank_end_regs, (value / 8) - 1 - hborder);
+
+ value = var->xres + var->right_margin;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal sync start : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_sync_start_regs, (value / 8));
+
+ value = var->xres + var->right_margin + var->hsync_len;
+ value = (value * hmul) / hdiv;
+ pr_debug("fb%d: horizontal sync end : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->h_sync_end_regs, (value / 8));
+
+ value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical total : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_total_regs, value - 2);
+
+ value = var->yres;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical display : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_display_regs, value - 1);
+
+ value = var->yres;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical blank start : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_blank_start_regs, value);
+
+ value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical blank end : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_blank_end_regs, value - 2);
+
+ value = var->yres + var->lower_margin;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical sync start : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_sync_start_regs, value);
+
+ value = var->yres + var->lower_margin + var->vsync_len;
+ value = (value * vmul) / vdiv;
+ pr_debug("fb%d: vertical sync end : %d\n", node, value);
+ svga_wcrt_multi(regbase, tm->v_sync_end_regs, value);
+
+ /* Set horizontal and vertical sync pulse polarity in misc register */
+
+ regval = vga_r(regbase, VGA_MIS_R);
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT) {
+ pr_debug("fb%d: positive horizontal sync\n", node);
+ regval = regval & ~0x80;
+ } else {
+ pr_debug("fb%d: negative horizontal sync\n", node);
+ regval = regval | 0x80;
+ }
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT) {
+ pr_debug("fb%d: positive vertical sync\n", node);
+ regval = regval & ~0x40;
+ } else {
+ pr_debug("fb%d: negative vertical sync\n\n", node);
+ regval = regval | 0x40;
+ }
+ vga_w(regbase, VGA_MIS_W, regval);
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+static inline int match_format(const struct svga_fb_format *frm,
+ struct fb_var_screeninfo *var)
+{
+ int i = 0;
+ int stored = -EINVAL;
+
+ while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL)
+ {
+ if ((var->bits_per_pixel == frm->bits_per_pixel) &&
+ (var->red.length <= frm->red.length) &&
+ (var->green.length <= frm->green.length) &&
+ (var->blue.length <= frm->blue.length) &&
+ (var->transp.length <= frm->transp.length) &&
+ (var->nonstd == frm->nonstd))
+ return i;
+ if (var->bits_per_pixel == frm->bits_per_pixel)
+ stored = i;
+ i++;
+ frm++;
+ }
+ return stored;
+}
+
+int svga_match_format(const struct svga_fb_format *frm,
+ struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix)
+{
+ int i = match_format(frm, var);
+
+ if (i >= 0) {
+ var->bits_per_pixel = frm[i].bits_per_pixel;
+ var->red = frm[i].red;
+ var->green = frm[i].green;
+ var->blue = frm[i].blue;
+ var->transp = frm[i].transp;
+ var->nonstd = frm[i].nonstd;
+ if (fix != NULL) {
+ fix->type = frm[i].type;
+ fix->type_aux = frm[i].type_aux;
+ fix->visual = frm[i].visual;
+ fix->xpanstep = frm[i].xpanstep;
+ }
+ }
+
+ return i;
+}
+
+
+EXPORT_SYMBOL(svga_wcrt_multi);
+EXPORT_SYMBOL(svga_wseq_multi);
+
+EXPORT_SYMBOL(svga_set_default_gfx_regs);
+EXPORT_SYMBOL(svga_set_default_atc_regs);
+EXPORT_SYMBOL(svga_set_default_seq_regs);
+EXPORT_SYMBOL(svga_set_default_crt_regs);
+EXPORT_SYMBOL(svga_set_textmode_vga_regs);
+
+EXPORT_SYMBOL(svga_settile);
+EXPORT_SYMBOL(svga_tilecopy);
+EXPORT_SYMBOL(svga_tilefill);
+EXPORT_SYMBOL(svga_tileblit);
+EXPORT_SYMBOL(svga_tilecursor);
+EXPORT_SYMBOL(svga_get_tilemax);
+
+EXPORT_SYMBOL(svga_compute_pll);
+EXPORT_SYMBOL(svga_check_timings);
+EXPORT_SYMBOL(svga_set_timings);
+EXPORT_SYMBOL(svga_match_format);
+
+MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>");
+MODULE_DESCRIPTION("Common utility functions for VGA-based graphics cards");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Generic Bit Block Transfer for frame buffers located in system RAM with
+ * packed pixels of any depth.
+ *
+ * Based almost entirely from cfbcopyarea.c (which is based almost entirely
+ * on Geert Uytterhoeven's copyarea routine)
+ *
+ * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include "fb_draw.h"
+
+ /*
+ * Generic bitwise copy algorithm
+ */
+
+static void
+bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx,
+ const unsigned long *src, int src_idx, int bits, unsigned n)
+{
+ unsigned long first, last;
+ int const shift = dst_idx-src_idx;
+ int left, right;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (!shift) {
+ /* Same alignment for source and dest */
+ if (dst_idx+n <= bits) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(*src, *dst, first);
+ } else {
+ /* Multiple destination words */
+ /* Leading bits */
+ if (first != ~0UL) {
+ *dst = comp(*src, *dst, first);
+ dst++;
+ src++;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 8) {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ n -= 8;
+ }
+ while (n--)
+ *dst++ = *src++;
+
+ /* Trailing bits */
+ if (last)
+ *dst = comp(*src, *dst, last);
+ }
+ } else {
+ unsigned long d0, d1;
+ int m;
+
+ /* Different alignment for source and dest */
+ right = shift & (bits - 1);
+ left = -shift & (bits - 1);
+
+ if (dst_idx+n <= bits) {
+ /* Single destination word */
+ if (last)
+ first &= last;
+ if (shift > 0) {
+ /* Single source word */
+ *dst = comp(*src >> right, *dst, first);
+ } else if (src_idx+n <= bits) {
+ /* Single source word */
+ *dst = comp(*src << left, *dst, first);
+ } else {
+ /* 2 source words */
+ d0 = *src++;
+ d1 = *src;
+ *dst = comp(d0 << left | d1 >> right, *dst,
+ first);
+ }
+ } else {
+ /* Multiple destination words */
+ /** We must always remember the last value read,
+ because in case SRC and DST overlap bitwise (e.g.
+ when moving just one pixel in 1bpp), we always
+ collect one full long for DST and that might
+ overlap with the current long from SRC. We store
+ this value in 'd0'. */
+ d0 = *src++;
+ /* Leading bits */
+ if (shift > 0) {
+ /* Single source word */
+ *dst = comp(d0 >> right, *dst, first);
+ dst++;
+ n -= bits - dst_idx;
+ } else {
+ /* 2 source words */
+ d1 = *src++;
+ *dst = comp(d0 << left | *dst >> right, *dst, first);
+ d0 = d1;
+ dst++;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ m = n % bits;
+ n /= bits;
+ while (n >= 4) {
+ d1 = *src++;
+ *dst++ = d0 << left | d1 >> right;
+ d0 = d1;
+ d1 = *src++;
+ *dst++ = d0 << left | d1 >> right;
+ d0 = d1;
+ d1 = *src++;
+ *dst++ = d0 << left | d1 >> right;
+ d0 = d1;
+ d1 = *src++;
+ *dst++ = d0 << left | d1 >> right;
+ d0 = d1;
+ n -= 4;
+ }
+ while (n--) {
+ d1 = *src++;
+ *dst++ = d0 << left | d1 >> right;
+ d0 = d1;
+ }
+
+ /* Trailing bits */
+ if (last) {
+ if (m <= right) {
+ /* Single source word */
+ *dst = comp(d0 << left, *dst, last);
+ } else {
+ /* 2 source words */
+ d1 = *src;
+ *dst = comp(d0 << left | d1 >> right,
+ *dst, last);
+ }
+ }
+ }
+ }
+}
+
+ /*
+ * Generic bitwise copy algorithm, operating backward
+ */
+
+static void
+bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
+ const unsigned long *src, int src_idx, int bits, unsigned n)
+{
+ unsigned long first, last;
+ int shift;
+
+ dst += (n-1)/bits;
+ src += (n-1)/bits;
+ if ((n-1) % bits) {
+ dst_idx += (n-1) % bits;
+ dst += dst_idx >> (ffs(bits) - 1);
+ dst_idx &= bits - 1;
+ src_idx += (n-1) % bits;
+ src += src_idx >> (ffs(bits) - 1);
+ src_idx &= bits - 1;
+ }
+
+ shift = dst_idx-src_idx;
+
+ first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx);
+ last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits)));
+
+ if (!shift) {
+ /* Same alignment for source and dest */
+ if ((unsigned long)dst_idx+1 >= n) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(*src, *dst, first);
+ } else {
+ /* Multiple destination words */
+
+ /* Leading bits */
+ if (first != ~0UL) {
+ *dst = comp(*src, *dst, first);
+ dst--;
+ src--;
+ n -= dst_idx+1;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 8) {
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ *dst-- = *src--;
+ n -= 8;
+ }
+ while (n--)
+ *dst-- = *src--;
+ /* Trailing bits */
+ if (last)
+ *dst = comp(*src, *dst, last);
+ }
+ } else {
+ /* Different alignment for source and dest */
+
+ int const left = -shift & (bits-1);
+ int const right = shift & (bits-1);
+
+ if ((unsigned long)dst_idx+1 >= n) {
+ /* Single destination word */
+ if (last)
+ first &= last;
+ if (shift < 0) {
+ /* Single source word */
+ *dst = comp(*src << left, *dst, first);
+ } else if (1+(unsigned long)src_idx >= n) {
+ /* Single source word */
+ *dst = comp(*src >> right, *dst, first);
+ } else {
+ /* 2 source words */
+ *dst = comp(*src >> right | *(src-1) << left,
+ *dst, first);
+ }
+ } else {
+ /* Multiple destination words */
+ /** We must always remember the last value read,
+ because in case SRC and DST overlap bitwise (e.g.
+ when moving just one pixel in 1bpp), we always
+ collect one full long for DST and that might
+ overlap with the current long from SRC. We store
+ this value in 'd0'. */
+ unsigned long d0, d1;
+ int m;
+
+ d0 = *src--;
+ /* Leading bits */
+ if (shift < 0) {
+ /* Single source word */
+ *dst = comp(d0 << left, *dst, first);
+ } else {
+ /* 2 source words */
+ d1 = *src--;
+ *dst = comp(d0 >> right | d1 << left, *dst,
+ first);
+ d0 = d1;
+ }
+ dst--;
+ n -= dst_idx+1;
+
+ /* Main chunk */
+ m = n % bits;
+ n /= bits;
+ while (n >= 4) {
+ d1 = *src--;
+ *dst-- = d0 >> right | d1 << left;
+ d0 = d1;
+ d1 = *src--;
+ *dst-- = d0 >> right | d1 << left;
+ d0 = d1;
+ d1 = *src--;
+ *dst-- = d0 >> right | d1 << left;
+ d0 = d1;
+ d1 = *src--;
+ *dst-- = d0 >> right | d1 << left;
+ d0 = d1;
+ n -= 4;
+ }
+ while (n--) {
+ d1 = *src--;
+ *dst-- = d0 >> right | d1 << left;
+ d0 = d1;
+ }
+
+ /* Trailing bits */
+ if (last) {
+ if (m <= left) {
+ /* Single source word */
+ *dst = comp(d0 >> right, *dst, last);
+ } else {
+ /* 2 source words */
+ d1 = *src;
+ *dst = comp(d0 >> right | d1 << left,
+ *dst, last);
+ }
+ }
+ }
+ }
+}
+
+void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+ u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
+ u32 height = area->height, width = area->width;
+ unsigned long const bits_per_line = p->fix.line_length*8u;
+ unsigned long *dst = NULL, *src = NULL;
+ int bits = BITS_PER_LONG, bytes = bits >> 3;
+ int dst_idx = 0, src_idx = 0, rev_copy = 0;
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ /* if the beginning of the target area might overlap with the end of
+ the source area, be have to copy the area reverse. */
+ if ((dy == sy && dx > sx) || (dy > sy)) {
+ dy += height;
+ sy += height;
+ rev_copy = 1;
+ }
+
+ /* split the base of the framebuffer into a long-aligned address and
+ the index of the first bit */
+ dst = src = (unsigned long *)((unsigned long)p->screen_base &
+ ~(bytes-1));
+ dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
+ /* add offset of source and target area */
+ dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
+ src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
+
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+
+ if (rev_copy) {
+ while (height--) {
+ dst_idx -= bits_per_line;
+ src_idx -= bits_per_line;
+ dst += dst_idx >> (ffs(bits) - 1);
+ dst_idx &= (bytes - 1);
+ src += src_idx >> (ffs(bits) - 1);
+ src_idx &= (bytes - 1);
+ bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
+ width*p->var.bits_per_pixel);
+ }
+ } else {
+ while (height--) {
+ dst += dst_idx >> (ffs(bits) - 1);
+ dst_idx &= (bytes - 1);
+ src += src_idx >> (ffs(bits) - 1);
+ src_idx &= (bytes - 1);
+ bitcpy(p, dst, dst_idx, src, src_idx, bits,
+ width*p->var.bits_per_pixel);
+ dst_idx += bits_per_line;
+ src_idx += bits_per_line;
+ }
+ }
+}
+
+EXPORT_SYMBOL(sys_copyarea);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+ * Generic fillrect for frame buffers in system RAM with packed pixels of
+ * any depth.
+ *
+ * Based almost entirely from cfbfillrect.c (which is based almost entirely
+ * on Geert Uytterhoeven's fillrect routine)
+ *
+ * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+#include "fb_draw.h"
+
+ /*
+ * Aligned pattern fill using 32/64-bit memory accesses
+ */
+
+static void
+bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
+ unsigned long pat, unsigned n, int bits)
+{
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(pat, *dst, first);
+ } else {
+ /* Multiple destination words */
+
+ /* Leading bits */
+ if (first!= ~0UL) {
+ *dst = comp(pat, *dst, first);
+ dst++;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 8) {
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ *dst++ = pat;
+ n -= 8;
+ }
+ while (n--)
+ *dst++ = pat;
+ /* Trailing bits */
+ if (last)
+ *dst = comp(pat, *dst, last);
+ }
+}
+
+
+ /*
+ * Unaligned generic pattern fill using 32/64-bit memory accesses
+ * The pattern must have been expanded to a full 32/64-bit value
+ * Left/right are the appropriate shifts to convert to the pattern to be
+ * used for the next 32/64-bit word
+ */
+
+static void
+bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
+ unsigned long pat, int left, int right, unsigned n, int bits)
+{
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(pat, *dst, first);
+ } else {
+ /* Multiple destination words */
+ /* Leading bits */
+ if (first) {
+ *dst = comp(pat, *dst, first);
+ dst++;
+ pat = pat << left | pat >> right;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 4) {
+ *dst++ = pat;
+ pat = pat << left | pat >> right;
+ *dst++ = pat;
+ pat = pat << left | pat >> right;
+ *dst++ = pat;
+ pat = pat << left | pat >> right;
+ *dst++ = pat;
+ pat = pat << left | pat >> right;
+ n -= 4;
+ }
+ while (n--) {
+ *dst++ = pat;
+ pat = pat << left | pat >> right;
+ }
+
+ /* Trailing bits */
+ if (last)
+ *dst = comp(pat, *dst, last);
+ }
+}
+
+ /*
+ * Aligned pattern invert using 32/64-bit memory accesses
+ */
+static void
+bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
+ unsigned long pat, unsigned n, int bits)
+{
+ unsigned long val = pat;
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(*dst ^ val, *dst, first);
+ } else {
+ /* Multiple destination words */
+ /* Leading bits */
+ if (first!=0UL) {
+ *dst = comp(*dst ^ val, *dst, first);
+ dst++;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 8) {
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ *dst++ ^= val;
+ n -= 8;
+ }
+ while (n--)
+ *dst++ ^= val;
+ /* Trailing bits */
+ if (last)
+ *dst = comp(*dst ^ val, *dst, last);
+ }
+}
+
+
+ /*
+ * Unaligned generic pattern invert using 32/64-bit memory accesses
+ * The pattern must have been expanded to a full 32/64-bit value
+ * Left/right are the appropriate shifts to convert to the pattern to be
+ * used for the next 32/64-bit word
+ */
+
+static void
+bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
+ unsigned long pat, int left, int right, unsigned n,
+ int bits)
+{
+ unsigned long first, last;
+
+ if (!n)
+ return;
+
+ first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
+ last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
+
+ if (dst_idx+n <= bits) {
+ /* Single word */
+ if (last)
+ first &= last;
+ *dst = comp(*dst ^ pat, *dst, first);
+ } else {
+ /* Multiple destination words */
+
+ /* Leading bits */
+ if (first != 0UL) {
+ *dst = comp(*dst ^ pat, *dst, first);
+ dst++;
+ pat = pat << left | pat >> right;
+ n -= bits - dst_idx;
+ }
+
+ /* Main chunk */
+ n /= bits;
+ while (n >= 4) {
+ *dst++ ^= pat;
+ pat = pat << left | pat >> right;
+ *dst++ ^= pat;
+ pat = pat << left | pat >> right;
+ *dst++ ^= pat;
+ pat = pat << left | pat >> right;
+ *dst++ ^= pat;
+ pat = pat << left | pat >> right;
+ n -= 4;
+ }
+ while (n--) {
+ *dst ^= pat;
+ pat = pat << left | pat >> right;
+ }
+
+ /* Trailing bits */
+ if (last)
+ *dst = comp(*dst ^ pat, *dst, last);
+ }
+}
+
+void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+ unsigned long pat, pat2, fg;
+ unsigned long width = rect->width, height = rect->height;
+ int bits = BITS_PER_LONG, bytes = bits >> 3;
+ u32 bpp = p->var.bits_per_pixel;
+ unsigned long *dst;
+ int dst_idx, left;
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+ fg = ((u32 *) (p->pseudo_palette))[rect->color];
+ else
+ fg = rect->color;
+
+ pat = pixel_to_pat( bpp, fg);
+
+ dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
+ dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
+ dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
+ /* FIXME For now we support 1-32 bpp only */
+ left = bits % bpp;
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+ if (!left) {
+ void (*fill_op32)(struct fb_info *p, unsigned long *dst,
+ int dst_idx, unsigned long pat, unsigned n,
+ int bits) = NULL;
+
+ switch (rect->rop) {
+ case ROP_XOR:
+ fill_op32 = bitfill_aligned_rev;
+ break;
+ case ROP_COPY:
+ fill_op32 = bitfill_aligned;
+ break;
+ default:
+ printk( KERN_ERR "cfb_fillrect(): unknown rop, "
+ "defaulting to ROP_COPY\n");
+ fill_op32 = bitfill_aligned;
+ break;
+ }
+ while (height--) {
+ dst += dst_idx >> (ffs(bits) - 1);
+ dst_idx &= (bits - 1);
+ fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
+ dst_idx += p->fix.line_length*8;
+ }
+ } else {
+ int right, r;
+ void (*fill_op)(struct fb_info *p, unsigned long *dst,
+ int dst_idx, unsigned long pat, int left,
+ int right, unsigned n, int bits) = NULL;
+#ifdef __LITTLE_ENDIAN
+ right = left;
+ left = bpp - right;
+#else
+ right = bpp - left;
+#endif
+ switch (rect->rop) {
+ case ROP_XOR:
+ fill_op = bitfill_unaligned_rev;
+ break;
+ case ROP_COPY:
+ fill_op = bitfill_unaligned;
+ break;
+ default:
+ printk(KERN_ERR "sys_fillrect(): unknown rop, "
+ "defaulting to ROP_COPY\n");
+ fill_op = bitfill_unaligned;
+ break;
+ }
+ while (height--) {
+ dst += dst_idx / bits;
+ dst_idx &= (bits - 1);
+ r = dst_idx % bpp;
+ /* rotate pattern to the correct start position */
+ pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
+ fill_op(p, dst, dst_idx, pat2, left, right,
+ width*bpp, bits);
+ dst_idx += p->fix.line_length*8;
+ }
+ }
+}
+
+EXPORT_SYMBOL(sys_fillrect);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Generic 1-bit or 8-bit source to 1-32 bit destination expansion
+ * for frame buffer located in system RAM with packed pixels of any depth.
+ *
+ * Based almost entirely on cfbimgblt.c
+ *
+ * Copyright (C) April 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+static const u32 cfb_tab8_be[] = {
+ 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+ 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+ 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+ 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+};
+
+static const u32 cfb_tab8_le[] = {
+ 0x00000000,0xff000000,0x00ff0000,0xffff0000,
+ 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+ 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+ 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+};
+
+static const u32 cfb_tab16_be[] = {
+ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+};
+
+static const u32 cfb_tab16_le[] = {
+ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+};
+
+static const u32 cfb_tab32[] = {
+ 0x00000000, 0xffffffff
+};
+
+static void color_imageblit(const struct fb_image *image, struct fb_info *p,
+ void *dst1, u32 start_index, u32 pitch_index)
+{
+ /* Draw the penguin */
+ u32 *dst, *dst2;
+ u32 color = 0, val, shift;
+ int i, n, bpp = p->var.bits_per_pixel;
+ u32 null_bits = 32 - bpp;
+ u32 *palette = (u32 *) p->pseudo_palette;
+ const u8 *src = image->data;
+
+ dst2 = dst1;
+ for (i = image->height; i--; ) {
+ n = image->width;
+ dst = dst1;
+ shift = 0;
+ val = 0;
+
+ if (start_index) {
+ u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
+ start_index));
+ val = *dst & start_mask;
+ shift = start_index;
+ }
+ while (n--) {
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+ color = palette[*src];
+ else
+ color = *src;
+ color <<= FB_LEFT_POS(p, bpp);
+ val |= FB_SHIFT_HIGH(p, color, shift);
+ if (shift >= null_bits) {
+ *dst++ = val;
+
+ val = (shift == null_bits) ? 0 :
+ FB_SHIFT_LOW(p, color, 32 - shift);
+ }
+ shift += bpp;
+ shift &= (32 - 1);
+ src++;
+ }
+ if (shift) {
+ u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
+
+ *dst &= end_mask;
+ *dst |= val;
+ }
+ dst1 += p->fix.line_length;
+ if (pitch_index) {
+ dst2 += p->fix.line_length;
+ dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
+
+ start_index += pitch_index;
+ start_index &= 32 - 1;
+ }
+ }
+}
+
+static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
+ void *dst1, u32 fgcolor, u32 bgcolor,
+ u32 start_index, u32 pitch_index)
+{
+ u32 shift, color = 0, bpp = p->var.bits_per_pixel;
+ u32 *dst, *dst2;
+ u32 val, pitch = p->fix.line_length;
+ u32 null_bits = 32 - bpp;
+ u32 spitch = (image->width+7)/8;
+ const u8 *src = image->data, *s;
+ u32 i, j, l;
+
+ dst2 = dst1;
+ fgcolor <<= FB_LEFT_POS(p, bpp);
+ bgcolor <<= FB_LEFT_POS(p, bpp);
+
+ for (i = image->height; i--; ) {
+ shift = val = 0;
+ l = 8;
+ j = image->width;
+ dst = dst1;
+ s = src;
+
+ /* write leading bits */
+ if (start_index) {
+ u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
+ start_index));
+ val = *dst & start_mask;
+ shift = start_index;
+ }
+
+ while (j--) {
+ l--;
+ color = (*s & (1 << l)) ? fgcolor : bgcolor;
+ val |= FB_SHIFT_HIGH(p, color, shift);
+
+ /* Did the bitshift spill bits to the next long? */
+ if (shift >= null_bits) {
+ *dst++ = val;
+ val = (shift == null_bits) ? 0 :
+ FB_SHIFT_LOW(p, color, 32 - shift);
+ }
+ shift += bpp;
+ shift &= (32 - 1);
+ if (!l) { l = 8; s++; }
+ }
+
+ /* write trailing bits */
+ if (shift) {
+ u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
+
+ *dst &= end_mask;
+ *dst |= val;
+ }
+
+ dst1 += pitch;
+ src += spitch;
+ if (pitch_index) {
+ dst2 += pitch;
+ dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
+ start_index += pitch_index;
+ start_index &= 32 - 1;
+ }
+
+ }
+}
+
+/*
+ * fast_imageblit - optimized monochrome color expansion
+ *
+ * Only if: bits_per_pixel == 8, 16, or 32
+ * image->width is divisible by pixel/dword (ppw);
+ * fix->line_legth is divisible by 4;
+ * beginning and end of a scanline is dword aligned
+ */
+static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
+ void *dst1, u32 fgcolor, u32 bgcolor)
+{
+ u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
+ u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
+ u32 bit_mask, end_mask, eorx, shift;
+ const char *s = image->data, *src;
+ u32 *dst;
+ const u32 *tab = NULL;
+ int i, j, k;
+
+ switch (bpp) {
+ case 8:
+ tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
+ break;
+ case 16:
+ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
+ break;
+ case 32:
+ default:
+ tab = cfb_tab32;
+ break;
+ }
+
+ for (i = ppw-1; i--; ) {
+ fgx <<= bpp;
+ bgx <<= bpp;
+ fgx |= fgcolor;
+ bgx |= bgcolor;
+ }
+
+ bit_mask = (1 << ppw) - 1;
+ eorx = fgx ^ bgx;
+ k = image->width/ppw;
+
+ for (i = image->height; i--; ) {
+ dst = dst1;
+ shift = 8;
+ src = s;
+
+ for (j = k; j--; ) {
+ shift -= ppw;
+ end_mask = tab[(*src >> shift) & bit_mask];
+ *dst++ = (end_mask & eorx) ^ bgx;
+ if (!shift) {
+ shift = 8;
+ src++;
+ }
+ }
+ dst1 += p->fix.line_length;
+ s += spitch;
+ }
+}
+
+void sys_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+ u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
+ u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
+ u32 width = image->width;
+ u32 dx = image->dx, dy = image->dy;
+ void *dst1;
+
+ if (p->state != FBINFO_STATE_RUNNING)
+ return;
+
+ bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
+ start_index = bitstart & (32 - 1);
+ pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
+
+ bitstart /= 8;
+ bitstart &= ~(bpl - 1);
+ dst1 = (void __force *)p->screen_base + bitstart;
+
+ if (p->fbops->fb_sync)
+ p->fbops->fb_sync(p);
+
+ if (image->depth == 1) {
+ if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+ p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
+ bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
+ } else {
+ fgcolor = image->fg_color;
+ bgcolor = image->bg_color;
+ }
+
+ if (32 % bpp == 0 && !start_index && !pitch_index &&
+ ((width & (32/bpp-1)) == 0) &&
+ bpp >= 8 && bpp <= 32)
+ fast_imageblit(image, p, dst1, fgcolor, bgcolor);
+ else
+ slow_imageblit(image, p, dst1, fgcolor, bgcolor,
+ start_index, pitch_index);
+ } else
+ color_imageblit(image, p, dst1, start_index, pitch_index);
+}
+
+EXPORT_SYMBOL(sys_imageblit);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)");
+MODULE_LICENSE("GPL");
+
+++ /dev/null
-/*
- * drivers/video/fb_ddc.c - DDC/EDID read support.
- *
- * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/fb.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/slab.h>
-
-#include "edid.h"
-
-#define DDC_ADDR 0x50
-
-static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter)
-{
- unsigned char start = 0x0;
- unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
- struct i2c_msg msgs[] = {
- {
- .addr = DDC_ADDR,
- .flags = 0,
- .len = 1,
- .buf = &start,
- }, {
- .addr = DDC_ADDR,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- .buf = buf,
- }
- };
-
- if (!buf) {
- dev_warn(&adapter->dev, "unable to allocate memory for EDID "
- "block.\n");
- return NULL;
- }
-
- if (i2c_transfer(adapter, msgs, 2) == 2)
- return buf;
-
- dev_warn(&adapter->dev, "unable to read EDID block.\n");
- kfree(buf);
- return NULL;
-}
-
-unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
-{
- struct i2c_algo_bit_data *algo_data = adapter->algo_data;
- unsigned char *edid = NULL;
- int i, j;
-
- algo_data->setscl(algo_data->data, 1);
-
- for (i = 0; i < 3; i++) {
- /* For some old monitors we need the
- * following process to initialize/stop DDC
- */
- algo_data->setsda(algo_data->data, 1);
- msleep(13);
-
- algo_data->setscl(algo_data->data, 1);
- for (j = 0; j < 5; j++) {
- msleep(10);
- if (algo_data->getscl(algo_data->data))
- break;
- }
- if (j == 5)
- continue;
-
- algo_data->setsda(algo_data->data, 0);
- msleep(15);
- algo_data->setscl(algo_data->data, 0);
- msleep(15);
- algo_data->setsda(algo_data->data, 1);
- msleep(15);
-
- /* Do the real work */
- edid = fb_do_probe_ddc_edid(adapter);
- algo_data->setsda(algo_data->data, 0);
- algo_data->setscl(algo_data->data, 0);
- msleep(15);
-
- algo_data->setscl(algo_data->data, 1);
- for (j = 0; j < 10; j++) {
- msleep(10);
- if (algo_data->getscl(algo_data->data))
- break;
- }
-
- algo_data->setsda(algo_data->data, 1);
- msleep(15);
- algo_data->setscl(algo_data->data, 0);
- algo_data->setsda(algo_data->data, 0);
- if (edid)
- break;
- }
- /* Release the DDC lines when done or the Apple Cinema HD display
- * will switch off
- */
- algo_data->setsda(algo_data->data, 1);
- algo_data->setscl(algo_data->data, 1);
-
- adapter->class |= I2C_CLASS_DDC;
- return edid;
-}
-
-EXPORT_SYMBOL_GPL(fb_ddc_read);
-
-MODULE_AUTHOR("Dennis Munsie <dmunsie@cecropia.com>");
-MODULE_DESCRIPTION("DDC/EDID reading support");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * linux/drivers/video/fb_defio.c
- *
- * Copyright (C) 2006 Jaya Kumar
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/fb.h>
-#include <linux/list.h>
-
-/* to support deferred IO */
-#include <linux/rmap.h>
-#include <linux/pagemap.h>
-
-static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
-{
- void *screen_base = (void __force *) info->screen_base;
- struct page *page;
-
- if (is_vmalloc_addr(screen_base + offs))
- page = vmalloc_to_page(screen_base + offs);
- else
- page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
-
- return page;
-}
-
-/* this is to find and return the vmalloc-ed fb pages */
-static int fb_deferred_io_fault(struct vm_area_struct *vma,
- struct vm_fault *vmf)
-{
- unsigned long offset;
- struct page *page;
- struct fb_info *info = vma->vm_private_data;
-
- offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= info->fix.smem_len)
- return VM_FAULT_SIGBUS;
-
- page = fb_deferred_io_page(info, offset);
- if (!page)
- return VM_FAULT_SIGBUS;
-
- get_page(page);
-
- if (vma->vm_file)
- page->mapping = vma->vm_file->f_mapping;
- else
- printk(KERN_ERR "no mapping available\n");
-
- BUG_ON(!page->mapping);
- page->index = vmf->pgoff;
-
- vmf->page = page;
- return 0;
-}
-
-int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)
-{
- struct fb_info *info = file->private_data;
- struct inode *inode = file_inode(file);
- int err = filemap_write_and_wait_range(inode->i_mapping, start, end);
- if (err)
- return err;
-
- /* Skip if deferred io is compiled-in but disabled on this fbdev */
- if (!info->fbdefio)
- return 0;
-
- mutex_lock(&inode->i_mutex);
- /* Kill off the delayed work */
- cancel_delayed_work_sync(&info->deferred_work);
-
- /* Run it immediately */
- err = schedule_delayed_work(&info->deferred_work, 0);
- mutex_unlock(&inode->i_mutex);
- return err;
-}
-EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
-
-/* vm_ops->page_mkwrite handler */
-static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
- struct vm_fault *vmf)
-{
- struct page *page = vmf->page;
- struct fb_info *info = vma->vm_private_data;
- struct fb_deferred_io *fbdefio = info->fbdefio;
- struct page *cur;
-
- /* this is a callback we get when userspace first tries to
- write to the page. we schedule a workqueue. that workqueue
- will eventually mkclean the touched pages and execute the
- deferred framebuffer IO. then if userspace touches a page
- again, we repeat the same scheme */
-
- file_update_time(vma->vm_file);
-
- /* protect against the workqueue changing the page list */
- mutex_lock(&fbdefio->lock);
-
- /* first write in this cycle, notify the driver */
- if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
- fbdefio->first_io(info);
-
- /*
- * We want the page to remain locked from ->page_mkwrite until
- * the PTE is marked dirty to avoid page_mkclean() being called
- * before the PTE is updated, which would leave the page ignored
- * by defio.
- * Do this by locking the page here and informing the caller
- * about it with VM_FAULT_LOCKED.
- */
- lock_page(page);
-
- /* we loop through the pagelist before adding in order
- to keep the pagelist sorted */
- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
- /* this check is to catch the case where a new
- process could start writing to the same page
- through a new pte. this new access can cause the
- mkwrite even when the original ps's pte is marked
- writable */
- if (unlikely(cur == page))
- goto page_already_added;
- else if (cur->index > page->index)
- break;
- }
-
- list_add_tail(&page->lru, &cur->lru);
-
-page_already_added:
- mutex_unlock(&fbdefio->lock);
-
- /* come back after delay to process the deferred IO */
- schedule_delayed_work(&info->deferred_work, fbdefio->delay);
- return VM_FAULT_LOCKED;
-}
-
-static const struct vm_operations_struct fb_deferred_io_vm_ops = {
- .fault = fb_deferred_io_fault,
- .page_mkwrite = fb_deferred_io_mkwrite,
-};
-
-static int fb_deferred_io_set_page_dirty(struct page *page)
-{
- if (!PageDirty(page))
- SetPageDirty(page);
- return 0;
-}
-
-static const struct address_space_operations fb_deferred_io_aops = {
- .set_page_dirty = fb_deferred_io_set_page_dirty,
-};
-
-static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
-{
- vma->vm_ops = &fb_deferred_io_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- if (!(info->flags & FBINFO_VIRTFB))
- vma->vm_flags |= VM_IO;
- vma->vm_private_data = info;
- return 0;
-}
-
-/* workqueue callback */
-static void fb_deferred_io_work(struct work_struct *work)
-{
- struct fb_info *info = container_of(work, struct fb_info,
- deferred_work.work);
- struct list_head *node, *next;
- struct page *cur;
- struct fb_deferred_io *fbdefio = info->fbdefio;
-
- /* here we mkclean the pages, then do all deferred IO */
- mutex_lock(&fbdefio->lock);
- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
- lock_page(cur);
- page_mkclean(cur);
- unlock_page(cur);
- }
-
- /* driver's callback with pagelist */
- fbdefio->deferred_io(info, &fbdefio->pagelist);
-
- /* clear the list */
- list_for_each_safe(node, next, &fbdefio->pagelist) {
- list_del(node);
- }
- mutex_unlock(&fbdefio->lock);
-}
-
-void fb_deferred_io_init(struct fb_info *info)
-{
- struct fb_deferred_io *fbdefio = info->fbdefio;
-
- BUG_ON(!fbdefio);
- mutex_init(&fbdefio->lock);
- info->fbops->fb_mmap = fb_deferred_io_mmap;
- INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
- INIT_LIST_HEAD(&fbdefio->pagelist);
- if (fbdefio->delay == 0) /* set a default of 1 s */
- fbdefio->delay = HZ;
-}
-EXPORT_SYMBOL_GPL(fb_deferred_io_init);
-
-void fb_deferred_io_open(struct fb_info *info,
- struct inode *inode,
- struct file *file)
-{
- file->f_mapping->a_ops = &fb_deferred_io_aops;
-}
-EXPORT_SYMBOL_GPL(fb_deferred_io_open);
-
-void fb_deferred_io_cleanup(struct fb_info *info)
-{
- struct fb_deferred_io *fbdefio = info->fbdefio;
- struct page *page;
- int i;
-
- BUG_ON(!fbdefio);
- cancel_delayed_work_sync(&info->deferred_work);
-
- /* clear out the mapping that we setup */
- for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
- page = fb_deferred_io_page(info, i);
- page->mapping = NULL;
- }
-
- info->fbops->fb_mmap = NULL;
- mutex_destroy(&fbdefio->lock);
-}
-EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-#ifndef _FB_DRAW_H
-#define _FB_DRAW_H
-
-#include <asm/types.h>
-#include <linux/fb.h>
-#include <linux/bug.h>
-
- /*
- * Compose two values, using a bitmask as decision value
- * This is equivalent to (a & mask) | (b & ~mask)
- */
-
-static inline unsigned long
-comp(unsigned long a, unsigned long b, unsigned long mask)
-{
- return ((a ^ b) & mask) ^ b;
-}
-
- /*
- * Create a pattern with the given pixel's color
- */
-
-#if BITS_PER_LONG == 64
-static inline unsigned long
-pixel_to_pat( u32 bpp, u32 pixel)
-{
- switch (bpp) {
- case 1:
- return 0xfffffffffffffffful*pixel;
- case 2:
- return 0x5555555555555555ul*pixel;
- case 4:
- return 0x1111111111111111ul*pixel;
- case 8:
- return 0x0101010101010101ul*pixel;
- case 12:
- return 0x1001001001001001ul*pixel;
- case 16:
- return 0x0001000100010001ul*pixel;
- case 24:
- return 0x0001000001000001ul*pixel;
- case 32:
- return 0x0000000100000001ul*pixel;
- default:
- WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
- return 0;
- }
-}
-#else
-static inline unsigned long
-pixel_to_pat( u32 bpp, u32 pixel)
-{
- switch (bpp) {
- case 1:
- return 0xfffffffful*pixel;
- case 2:
- return 0x55555555ul*pixel;
- case 4:
- return 0x11111111ul*pixel;
- case 8:
- return 0x01010101ul*pixel;
- case 12:
- return 0x01001001ul*pixel;
- case 16:
- return 0x00010001ul*pixel;
- case 24:
- return 0x01000001ul*pixel;
- case 32:
- return 0x00000001ul*pixel;
- default:
- WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
- return 0;
- }
-}
-#endif
-
-#ifdef CONFIG_FB_CFB_REV_PIXELS_IN_BYTE
-#if BITS_PER_LONG == 64
-#define REV_PIXELS_MASK1 0x5555555555555555ul
-#define REV_PIXELS_MASK2 0x3333333333333333ul
-#define REV_PIXELS_MASK4 0x0f0f0f0f0f0f0f0ful
-#else
-#define REV_PIXELS_MASK1 0x55555555ul
-#define REV_PIXELS_MASK2 0x33333333ul
-#define REV_PIXELS_MASK4 0x0f0f0f0ful
-#endif
-
-static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
- u32 bswapmask)
-{
- if (bswapmask & 1)
- val = comp(val >> 1, val << 1, REV_PIXELS_MASK1);
- if (bswapmask & 2)
- val = comp(val >> 2, val << 2, REV_PIXELS_MASK2);
- if (bswapmask & 3)
- val = comp(val >> 4, val << 4, REV_PIXELS_MASK4);
- return val;
-}
-
-static inline u32 fb_shifted_pixels_mask_u32(struct fb_info *p, u32 index,
- u32 bswapmask)
-{
- u32 mask;
-
- if (!bswapmask) {
- mask = FB_SHIFT_HIGH(p, ~(u32)0, index);
- } else {
- mask = 0xff << FB_LEFT_POS(p, 8);
- mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
- mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
-#if defined(__i386__) || defined(__x86_64__)
- /* Shift argument is limited to 0 - 31 on x86 based CPU's */
- if(index + bswapmask < 32)
-#endif
- mask |= FB_SHIFT_HIGH(p, ~(u32)0,
- (index + bswapmask) & ~(bswapmask));
- }
- return mask;
-}
-
-static inline unsigned long fb_shifted_pixels_mask_long(struct fb_info *p,
- u32 index,
- u32 bswapmask)
-{
- unsigned long mask;
-
- if (!bswapmask) {
- mask = FB_SHIFT_HIGH(p, ~0UL, index);
- } else {
- mask = 0xff << FB_LEFT_POS(p, 8);
- mask = FB_SHIFT_LOW(p, mask, index & (bswapmask)) & mask;
- mask = FB_SHIFT_HIGH(p, mask, index & ~(bswapmask));
-#if defined(__i386__) || defined(__x86_64__)
- /* Shift argument is limited to 0 - 31 on x86 based CPU's */
- if(index + bswapmask < BITS_PER_LONG)
-#endif
- mask |= FB_SHIFT_HIGH(p, ~0UL,
- (index + bswapmask) & ~(bswapmask));
- }
- return mask;
-}
-
-
-static inline u32 fb_compute_bswapmask(struct fb_info *info)
-{
- u32 bswapmask = 0;
- unsigned bpp = info->var.bits_per_pixel;
-
- if ((bpp < 8) && (info->var.nonstd & FB_NONSTD_REV_PIX_IN_B)) {
- /*
- * Reversed order of pixel layout in bytes
- * works only for 1, 2 and 4 bpp
- */
- bswapmask = 7 - bpp + 1;
- }
- return bswapmask;
-}
-
-#else /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
-
-static inline unsigned long fb_rev_pixels_in_long(unsigned long val,
- u32 bswapmask)
-{
- return val;
-}
-
-#define fb_shifted_pixels_mask_u32(p, i, b) FB_SHIFT_HIGH((p), ~(u32)0, (i))
-#define fb_shifted_pixels_mask_long(p, i, b) FB_SHIFT_HIGH((p), ~0UL, (i))
-#define fb_compute_bswapmask(...) 0
-
-#endif /* CONFIG_FB_CFB_REV_PIXELS_IN_BYTE */
-
-#define cpu_to_le_long _cpu_to_le_long(BITS_PER_LONG)
-#define _cpu_to_le_long(x) __cpu_to_le_long(x)
-#define __cpu_to_le_long(x) cpu_to_le##x
-
-#define le_long_to_cpu _le_long_to_cpu(BITS_PER_LONG)
-#define _le_long_to_cpu(x) __le_long_to_cpu(x)
-#define __le_long_to_cpu(x) le##x##_to_cpu
-
-static inline unsigned long rolx(unsigned long word, unsigned int shift, unsigned int x)
-{
- return (word << shift) | (word >> (x - shift));
-}
-
-#endif /* FB_DRAW_H */
+++ /dev/null
-/*
- * linux/drivers/video/fb_notify.c
- *
- * Copyright (C) 2006 Antonino Daplas <adaplas@pol.net>
- *
- * 2001 - Documented with DocBook
- * - Brad Douglas <brad@neruo.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-#include <linux/fb.h>
-#include <linux/notifier.h>
-#include <linux/export.h>
-
-static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
-
-/**
- * fb_register_client - register a client notifier
- * @nb: notifier block to callback on events
- */
-int fb_register_client(struct notifier_block *nb)
-{
- return blocking_notifier_chain_register(&fb_notifier_list, nb);
-}
-EXPORT_SYMBOL(fb_register_client);
-
-/**
- * fb_unregister_client - unregister a client notifier
- * @nb: notifier block to callback on events
- */
-int fb_unregister_client(struct notifier_block *nb)
-{
- return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
-}
-EXPORT_SYMBOL(fb_unregister_client);
-
-/**
- * fb_notifier_call_chain - notify clients of fb_events
- *
- */
-int fb_notifier_call_chain(unsigned long val, void *v)
-{
- return blocking_notifier_call_chain(&fb_notifier_list, val, v);
-}
-EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
+++ /dev/null
-/*
- * linux/drivers/video/fb_sys_read.c - Generic file operations where
- * framebuffer is in system RAM
- *
- * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-#include <linux/fb.h>
-#include <linux/module.h>
-#include <linux/uaccess.h>
-
-ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
- loff_t *ppos)
-{
- unsigned long p = *ppos;
- void *src;
- int err = 0;
- unsigned long total_size;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p >= total_size)
- return 0;
-
- if (count >= total_size)
- count = total_size;
-
- if (count + p > total_size)
- count = total_size - p;
-
- src = (void __force *)(info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- if (copy_to_user(buf, src, count))
- err = -EFAULT;
-
- if (!err)
- *ppos += count;
-
- return (err) ? err : count;
-}
-EXPORT_SYMBOL_GPL(fb_sys_read);
-
-ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- unsigned long p = *ppos;
- void *dst;
- int err = 0;
- unsigned long total_size;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p > total_size)
- return -EFBIG;
-
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
-
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
-
- count = total_size - p;
- }
-
- dst = (void __force *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- if (copy_from_user(dst, buf, count))
- err = -EFAULT;
-
- if (!err)
- *ppos += count;
-
- return (err) ? err : count;
-}
-EXPORT_SYMBOL_GPL(fb_sys_write);
-
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("Generic file read (fb in system RAM)");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * linux/drivers/video/fbcmap.c -- Colormap handling for frame buffer devices
- *
- * Created 15 Jun 1997 by Geert Uytterhoeven
- *
- * 2001 - Documented with DocBook
- * - Brad Douglas <brad@neruo.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/fb.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-static u16 red2[] __read_mostly = {
- 0x0000, 0xaaaa
-};
-static u16 green2[] __read_mostly = {
- 0x0000, 0xaaaa
-};
-static u16 blue2[] __read_mostly = {
- 0x0000, 0xaaaa
-};
-
-static u16 red4[] __read_mostly = {
- 0x0000, 0xaaaa, 0x5555, 0xffff
-};
-static u16 green4[] __read_mostly = {
- 0x0000, 0xaaaa, 0x5555, 0xffff
-};
-static u16 blue4[] __read_mostly = {
- 0x0000, 0xaaaa, 0x5555, 0xffff
-};
-
-static u16 red8[] __read_mostly = {
- 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
-};
-static u16 green8[] __read_mostly = {
- 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa
-};
-static u16 blue8[] __read_mostly = {
- 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
-};
-
-static u16 red16[] __read_mostly = {
- 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
- 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
-};
-static u16 green16[] __read_mostly = {
- 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0x5555, 0xaaaa,
- 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
-};
-static u16 blue16[] __read_mostly = {
- 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
- 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
-};
-
-static const struct fb_cmap default_2_colors = {
- .len=2, .red=red2, .green=green2, .blue=blue2
-};
-static const struct fb_cmap default_8_colors = {
- .len=8, .red=red8, .green=green8, .blue=blue8
-};
-static const struct fb_cmap default_4_colors = {
- .len=4, .red=red4, .green=green4, .blue=blue4
-};
-static const struct fb_cmap default_16_colors = {
- .len=16, .red=red16, .green=green16, .blue=blue16
-};
-
-
-
-/**
- * fb_alloc_cmap - allocate a colormap
- * @cmap: frame buffer colormap structure
- * @len: length of @cmap
- * @transp: boolean, 1 if there is transparency, 0 otherwise
- * @flags: flags for kmalloc memory allocation
- *
- * Allocates memory for a colormap @cmap. @len is the
- * number of entries in the palette.
- *
- * Returns negative errno on error, or zero on success.
- *
- */
-
-int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
-{
- int size = len * sizeof(u16);
- int ret = -ENOMEM;
-
- if (cmap->len != len) {
- fb_dealloc_cmap(cmap);
- if (!len)
- return 0;
-
- cmap->red = kmalloc(size, flags);
- if (!cmap->red)
- goto fail;
- cmap->green = kmalloc(size, flags);
- if (!cmap->green)
- goto fail;
- cmap->blue = kmalloc(size, flags);
- if (!cmap->blue)
- goto fail;
- if (transp) {
- cmap->transp = kmalloc(size, flags);
- if (!cmap->transp)
- goto fail;
- } else {
- cmap->transp = NULL;
- }
- }
- cmap->start = 0;
- cmap->len = len;
- ret = fb_copy_cmap(fb_default_cmap(len), cmap);
- if (ret)
- goto fail;
- return 0;
-
-fail:
- fb_dealloc_cmap(cmap);
- return ret;
-}
-
-int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
-{
- return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
-}
-
-/**
- * fb_dealloc_cmap - deallocate a colormap
- * @cmap: frame buffer colormap structure
- *
- * Deallocates a colormap that was previously allocated with
- * fb_alloc_cmap().
- *
- */
-
-void fb_dealloc_cmap(struct fb_cmap *cmap)
-{
- kfree(cmap->red);
- kfree(cmap->green);
- kfree(cmap->blue);
- kfree(cmap->transp);
-
- cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
- cmap->len = 0;
-}
-
-/**
- * fb_copy_cmap - copy a colormap
- * @from: frame buffer colormap structure
- * @to: frame buffer colormap structure
- *
- * Copy contents of colormap from @from to @to.
- */
-
-int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
-{
- int tooff = 0, fromoff = 0;
- int size;
-
- if (to->start > from->start)
- fromoff = to->start - from->start;
- else
- tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
- return -EINVAL;
- size *= sizeof(u16);
-
- memcpy(to->red+tooff, from->red+fromoff, size);
- memcpy(to->green+tooff, from->green+fromoff, size);
- memcpy(to->blue+tooff, from->blue+fromoff, size);
- if (from->transp && to->transp)
- memcpy(to->transp+tooff, from->transp+fromoff, size);
- return 0;
-}
-
-int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
-{
- int tooff = 0, fromoff = 0;
- int size;
-
- if (to->start > from->start)
- fromoff = to->start - from->start;
- else
- tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
- return -EINVAL;
- size *= sizeof(u16);
-
- if (copy_to_user(to->red+tooff, from->red+fromoff, size))
- return -EFAULT;
- if (copy_to_user(to->green+tooff, from->green+fromoff, size))
- return -EFAULT;
- if (copy_to_user(to->blue+tooff, from->blue+fromoff, size))
- return -EFAULT;
- if (from->transp && to->transp)
- if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
- return -EFAULT;
- return 0;
-}
-
-/**
- * fb_set_cmap - set the colormap
- * @cmap: frame buffer colormap structure
- * @info: frame buffer info structure
- *
- * Sets the colormap @cmap for a screen of device @info.
- *
- * Returns negative errno on error, or zero on success.
- *
- */
-
-int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
-{
- int i, start, rc = 0;
- u16 *red, *green, *blue, *transp;
- u_int hred, hgreen, hblue, htransp = 0xffff;
-
- red = cmap->red;
- green = cmap->green;
- blue = cmap->blue;
- transp = cmap->transp;
- start = cmap->start;
-
- if (start < 0 || (!info->fbops->fb_setcolreg &&
- !info->fbops->fb_setcmap))
- return -EINVAL;
- if (info->fbops->fb_setcmap) {
- rc = info->fbops->fb_setcmap(cmap, info);
- } else {
- for (i = 0; i < cmap->len; i++) {
- hred = *red++;
- hgreen = *green++;
- hblue = *blue++;
- if (transp)
- htransp = *transp++;
- if (info->fbops->fb_setcolreg(start++,
- hred, hgreen, hblue,
- htransp, info))
- break;
- }
- }
- if (rc == 0)
- fb_copy_cmap(cmap, &info->cmap);
-
- return rc;
-}
-
-int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
-{
- int rc, size = cmap->len * sizeof(u16);
- struct fb_cmap umap;
-
- if (size < 0 || size < cmap->len)
- return -E2BIG;
-
- memset(&umap, 0, sizeof(struct fb_cmap));
- rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
- GFP_KERNEL);
- if (rc)
- return rc;
- if (copy_from_user(umap.red, cmap->red, size) ||
- copy_from_user(umap.green, cmap->green, size) ||
- copy_from_user(umap.blue, cmap->blue, size) ||
- (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) {
- rc = -EFAULT;
- goto out;
- }
- umap.start = cmap->start;
- if (!lock_fb_info(info)) {
- rc = -ENODEV;
- goto out;
- }
-
- rc = fb_set_cmap(&umap, info);
- unlock_fb_info(info);
-out:
- fb_dealloc_cmap(&umap);
- return rc;
-}
-
-/**
- * fb_default_cmap - get default colormap
- * @len: size of palette for a depth
- *
- * Gets the default colormap for a specific screen depth. @len
- * is the size of the palette for a particular screen depth.
- *
- * Returns pointer to a frame buffer colormap structure.
- *
- */
-
-const struct fb_cmap *fb_default_cmap(int len)
-{
- if (len <= 2)
- return &default_2_colors;
- if (len <= 4)
- return &default_4_colors;
- if (len <= 8)
- return &default_8_colors;
- return &default_16_colors;
-}
-
-
-/**
- * fb_invert_cmaps - invert all defaults colormaps
- *
- * Invert all default colormaps.
- *
- */
-
-void fb_invert_cmaps(void)
-{
- u_int i;
-
- for (i = 0; i < ARRAY_SIZE(red2); i++) {
- red2[i] = ~red2[i];
- green2[i] = ~green2[i];
- blue2[i] = ~blue2[i];
- }
- for (i = 0; i < ARRAY_SIZE(red4); i++) {
- red4[i] = ~red4[i];
- green4[i] = ~green4[i];
- blue4[i] = ~blue4[i];
- }
- for (i = 0; i < ARRAY_SIZE(red8); i++) {
- red8[i] = ~red8[i];
- green8[i] = ~green8[i];
- blue8[i] = ~blue8[i];
- }
- for (i = 0; i < ARRAY_SIZE(red16); i++) {
- red16[i] = ~red16[i];
- green16[i] = ~green16[i];
- blue16[i] = ~blue16[i];
- }
-}
-
-
- /*
- * Visible symbols for modules
- */
-
-EXPORT_SYMBOL(fb_alloc_cmap);
-EXPORT_SYMBOL(fb_dealloc_cmap);
-EXPORT_SYMBOL(fb_copy_cmap);
-EXPORT_SYMBOL(fb_set_cmap);
-EXPORT_SYMBOL(fb_default_cmap);
-EXPORT_SYMBOL(fb_invert_cmaps);
+++ /dev/null
-/*
- * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings
- *
- * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
- *
- * Based from the VESA(TM) Coordinated Video Timing Generator by
- * Graham Loveridge April 9, 2003 available at
- * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-#include <linux/fb.h>
-#include <linux/slab.h>
-
-#define FB_CVT_CELLSIZE 8
-#define FB_CVT_GTF_C 40
-#define FB_CVT_GTF_J 20
-#define FB_CVT_GTF_K 128
-#define FB_CVT_GTF_M 600
-#define FB_CVT_MIN_VSYNC_BP 550
-#define FB_CVT_MIN_VPORCH 3
-#define FB_CVT_MIN_BPORCH 6
-
-#define FB_CVT_RB_MIN_VBLANK 460
-#define FB_CVT_RB_HBLANK 160
-#define FB_CVT_RB_V_FPORCH 3
-
-#define FB_CVT_FLAG_REDUCED_BLANK 1
-#define FB_CVT_FLAG_MARGINS 2
-#define FB_CVT_FLAG_INTERLACED 4
-
-struct fb_cvt_data {
- u32 xres;
- u32 yres;
- u32 refresh;
- u32 f_refresh;
- u32 pixclock;
- u32 hperiod;
- u32 hblank;
- u32 hfreq;
- u32 htotal;
- u32 vtotal;
- u32 vsync;
- u32 hsync;
- u32 h_front_porch;
- u32 h_back_porch;
- u32 v_front_porch;
- u32 v_back_porch;
- u32 h_margin;
- u32 v_margin;
- u32 interlace;
- u32 aspect_ratio;
- u32 active_pixels;
- u32 flags;
- u32 status;
-};
-
-static const unsigned char fb_cvt_vbi_tab[] = {
- 4, /* 4:3 */
- 5, /* 16:9 */
- 6, /* 16:10 */
- 7, /* 5:4 */
- 7, /* 15:9 */
- 8, /* reserved */
- 9, /* reserved */
- 10 /* custom */
-};
-
-/* returns hperiod * 1000 */
-static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
-{
- u32 num = 1000000000/cvt->f_refresh;
- u32 den;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
- num -= FB_CVT_RB_MIN_VBLANK * 1000;
- den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin);
- } else {
- num -= FB_CVT_MIN_VSYNC_BP * 1000;
- den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2
- + FB_CVT_MIN_VPORCH + cvt->interlace/2);
- }
-
- return 2 * (num/den);
-}
-
-/* returns ideal duty cycle * 1000 */
-static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt)
-{
- u32 c_prime = (FB_CVT_GTF_C - FB_CVT_GTF_J) *
- (FB_CVT_GTF_K) + 256 * FB_CVT_GTF_J;
- u32 m_prime = (FB_CVT_GTF_K * FB_CVT_GTF_M);
- u32 h_period_est = cvt->hperiod;
-
- return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256;
-}
-
-static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
-{
- u32 hblank = 0;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
- hblank = FB_CVT_RB_HBLANK;
- else {
- u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt);
- u32 active_pixels = cvt->active_pixels;
-
- if (ideal_duty_cycle < 20000)
- hblank = (active_pixels * 20000)/
- (100000 - 20000);
- else {
- hblank = (active_pixels * ideal_duty_cycle)/
- (100000 - ideal_duty_cycle);
- }
- }
-
- hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
-
- return hblank;
-}
-
-static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
-{
- u32 hsync;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
- hsync = 32;
- else
- hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
-
- hsync &= ~(FB_CVT_CELLSIZE - 1);
- return hsync;
-}
-
-static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
-{
- u32 vbi_lines, min_vbi_lines, act_vbi_lines;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
- vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1;
- min_vbi_lines = FB_CVT_RB_V_FPORCH + cvt->vsync +
- FB_CVT_MIN_BPORCH;
-
- } else {
- vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
- FB_CVT_MIN_VPORCH;
- min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
- FB_CVT_MIN_VPORCH;
- }
-
- if (vbi_lines < min_vbi_lines)
- act_vbi_lines = min_vbi_lines;
- else
- act_vbi_lines = vbi_lines;
-
- return act_vbi_lines;
-}
-
-static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
-{
- u32 vtotal = cvt->yres/cvt->interlace;
-
- vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
- vtotal |= cvt->interlace/2;
-
- return vtotal;
-}
-
-static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
-{
- u32 pixclock;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
- pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
- else
- pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
-
- pixclock /= 250;
- pixclock *= 250;
- pixclock *= 1000;
-
- return pixclock;
-}
-
-static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
-{
- u32 xres = cvt->xres;
- u32 yres = cvt->yres;
- u32 aspect = -1;
-
- if (xres == (yres * 4)/3 && !((yres * 4) % 3))
- aspect = 0;
- else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
- aspect = 1;
- else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
- aspect = 2;
- else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
- aspect = 3;
- else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
- aspect = 4;
- else {
- printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
- "standard\n");
- aspect = 7;
- cvt->status = 1;
- }
-
- return aspect;
-}
-
-static void fb_cvt_print_name(struct fb_cvt_data *cvt)
-{
- u32 pixcount, pixcount_mod;
- int cnt = 255, offset = 0, read = 0;
- u8 *buf = kzalloc(256, GFP_KERNEL);
-
- if (!buf)
- return;
-
- pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000;
- pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000;
- pixcount_mod /= 1000;
-
- read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ",
- cvt->xres, cvt->yres, cvt->refresh);
- offset += read;
- cnt -= read;
-
- if (cvt->status)
- snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega "
- "Pixel Image\n", pixcount, pixcount_mod);
- else {
- if (pixcount) {
- read = snprintf(buf+offset, cnt, "%d", pixcount);
- cnt -= read;
- offset += read;
- }
-
- read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod);
- cnt -= read;
- offset += read;
-
- if (cvt->aspect_ratio == 0)
- read = snprintf(buf+offset, cnt, "3");
- else if (cvt->aspect_ratio == 3)
- read = snprintf(buf+offset, cnt, "4");
- else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4)
- read = snprintf(buf+offset, cnt, "9");
- else if (cvt->aspect_ratio == 2)
- read = snprintf(buf+offset, cnt, "A");
- else
- read = 0;
- cnt -= read;
- offset += read;
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
- read = snprintf(buf+offset, cnt, "-R");
- cnt -= read;
- offset += read;
- }
- }
-
- printk(KERN_INFO "%s\n", buf);
- kfree(buf);
-}
-
-static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt,
- struct fb_videomode *mode)
-{
- mode->refresh = cvt->f_refresh;
- mode->pixclock = KHZ2PICOS(cvt->pixclock/1000);
- mode->left_margin = cvt->h_back_porch;
- mode->right_margin = cvt->h_front_porch;
- mode->hsync_len = cvt->hsync;
- mode->upper_margin = cvt->v_back_porch;
- mode->lower_margin = cvt->v_front_porch;
- mode->vsync_len = cvt->vsync;
-
- mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);
-
- if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
- mode->sync |= FB_SYNC_HOR_HIGH_ACT;
- else
- mode->sync |= FB_SYNC_VERT_HIGH_ACT;
-}
-
-/*
- * fb_find_mode_cvt - calculate mode using VESA(TM) CVT
- * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be
- * pre-filled with the desired values
- * @margins: add margin to calculation (1.8% of xres and yres)
- * @rb: compute with reduced blanking (for flatpanels)
- *
- * RETURNS:
- * 0 for success
- * @mode is filled with computed values. If interlaced, the refresh field
- * will be filled with the field rate (2x the frame rate)
- *
- * DESCRIPTION:
- * Computes video timings using VESA(TM) Coordinated Video Timings
- */
-int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
-{
- struct fb_cvt_data cvt;
-
- memset(&cvt, 0, sizeof(cvt));
-
- if (margins)
- cvt.flags |= FB_CVT_FLAG_MARGINS;
-
- if (rb)
- cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK;
-
- if (mode->vmode & FB_VMODE_INTERLACED)
- cvt.flags |= FB_CVT_FLAG_INTERLACED;
-
- cvt.xres = mode->xres;
- cvt.yres = mode->yres;
- cvt.refresh = mode->refresh;
- cvt.f_refresh = cvt.refresh;
- cvt.interlace = 1;
-
- if (!cvt.xres || !cvt.yres || !cvt.refresh) {
- printk(KERN_INFO "fbcvt: Invalid input parameters\n");
- return 1;
- }
-
- if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 ||
- cvt.refresh == 85)) {
- printk(KERN_INFO "fbcvt: Refresh rate not CVT "
- "standard\n");
- cvt.status = 1;
- }
-
- cvt.xres &= ~(FB_CVT_CELLSIZE - 1);
-
- if (cvt.flags & FB_CVT_FLAG_INTERLACED) {
- cvt.interlace = 2;
- cvt.f_refresh *= 2;
- }
-
- if (cvt.flags & FB_CVT_FLAG_REDUCED_BLANK) {
- if (cvt.refresh != 60) {
- printk(KERN_INFO "fbcvt: 60Hz refresh rate "
- "advised for reduced blanking\n");
- cvt.status = 1;
- }
- }
-
- if (cvt.flags & FB_CVT_FLAG_MARGINS) {
- cvt.h_margin = (cvt.xres * 18)/1000;
- cvt.h_margin &= ~(FB_CVT_CELLSIZE - 1);
- cvt.v_margin = ((cvt.yres/cvt.interlace)* 18)/1000;
- }
-
- cvt.aspect_ratio = fb_cvt_aspect_ratio(&cvt);
- cvt.active_pixels = cvt.xres + 2 * cvt.h_margin;
- cvt.hperiod = fb_cvt_hperiod(&cvt);
- cvt.vsync = fb_cvt_vbi_tab[cvt.aspect_ratio];
- cvt.vtotal = fb_cvt_vtotal(&cvt);
- cvt.hblank = fb_cvt_hblank(&cvt);
- cvt.htotal = cvt.active_pixels + cvt.hblank;
- cvt.hsync = fb_cvt_hsync(&cvt);
- cvt.pixclock = fb_cvt_pixclock(&cvt);
- cvt.hfreq = cvt.pixclock/cvt.htotal;
- cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin;
- cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch +
- 2 * cvt.h_margin;
- cvt.v_back_porch = 3 + cvt.v_margin;
- cvt.v_front_porch = cvt.vtotal - cvt.yres/cvt.interlace -
- cvt.v_back_porch - cvt.vsync;
- fb_cvt_print_name(&cvt);
- fb_cvt_convert_to_mode(&cvt, mode);
-
- return 0;
-}
+++ /dev/null
-/*
- * linux/drivers/video/fbmem.c
- *
- * Copyright (C) 1994 Martin Schaller
- *
- * 2001 - Documented with DocBook
- * - Brad Douglas <brad@neruo.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-
-#include <linux/compat.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/vt.h>
-#include <linux/init.h>
-#include <linux/linux_logo.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/console.h>
-#include <linux/kmod.h>
-#include <linux/err.h>
-#include <linux/device.h>
-#include <linux/efi.h>
-#include <linux/fb.h>
-
-#include <asm/fb.h>
-
-
- /*
- * Frame buffer device initialization and setup routines
- */
-
-#define FBPIXMAPSIZE (1024 * 8)
-
-static DEFINE_MUTEX(registration_lock);
-
-struct fb_info *registered_fb[FB_MAX] __read_mostly;
-EXPORT_SYMBOL(registered_fb);
-
-int num_registered_fb __read_mostly;
-EXPORT_SYMBOL(num_registered_fb);
-
-static struct fb_info *get_fb_info(unsigned int idx)
-{
- struct fb_info *fb_info;
-
- if (idx >= FB_MAX)
- return ERR_PTR(-ENODEV);
-
- mutex_lock(®istration_lock);
- fb_info = registered_fb[idx];
- if (fb_info)
- atomic_inc(&fb_info->count);
- mutex_unlock(®istration_lock);
-
- return fb_info;
-}
-
-static void put_fb_info(struct fb_info *fb_info)
-{
- if (!atomic_dec_and_test(&fb_info->count))
- return;
- if (fb_info->fbops->fb_destroy)
- fb_info->fbops->fb_destroy(fb_info);
-}
-
-int lock_fb_info(struct fb_info *info)
-{
- mutex_lock(&info->lock);
- if (!info->fbops) {
- mutex_unlock(&info->lock);
- return 0;
- }
- return 1;
-}
-EXPORT_SYMBOL(lock_fb_info);
-
-/*
- * Helpers
- */
-
-int fb_get_color_depth(struct fb_var_screeninfo *var,
- struct fb_fix_screeninfo *fix)
-{
- int depth = 0;
-
- if (fix->visual == FB_VISUAL_MONO01 ||
- fix->visual == FB_VISUAL_MONO10)
- depth = 1;
- else {
- if (var->green.length == var->blue.length &&
- var->green.length == var->red.length &&
- var->green.offset == var->blue.offset &&
- var->green.offset == var->red.offset)
- depth = var->green.length;
- else
- depth = var->green.length + var->red.length +
- var->blue.length;
- }
-
- return depth;
-}
-EXPORT_SYMBOL(fb_get_color_depth);
-
-/*
- * Data padding functions.
- */
-void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
-{
- __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
-}
-EXPORT_SYMBOL(fb_pad_aligned_buffer);
-
-void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
- u32 shift_high, u32 shift_low, u32 mod)
-{
- u8 mask = (u8) (0xfff << shift_high), tmp;
- int i, j;
-
- for (i = height; i--; ) {
- for (j = 0; j < idx; j++) {
- tmp = dst[j];
- tmp &= mask;
- tmp |= *src >> shift_low;
- dst[j] = tmp;
- tmp = *src << shift_high;
- dst[j+1] = tmp;
- src++;
- }
- tmp = dst[idx];
- tmp &= mask;
- tmp |= *src >> shift_low;
- dst[idx] = tmp;
- if (shift_high < mod) {
- tmp = *src << shift_high;
- dst[idx+1] = tmp;
- }
- src++;
- dst += d_pitch;
- }
-}
-EXPORT_SYMBOL(fb_pad_unaligned_buffer);
-
-/*
- * we need to lock this section since fb_cursor
- * may use fb_imageblit()
- */
-char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
-{
- u32 align = buf->buf_align - 1, offset;
- char *addr = buf->addr;
-
- /* If IO mapped, we need to sync before access, no sharing of
- * the pixmap is done
- */
- if (buf->flags & FB_PIXMAP_IO) {
- if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
- info->fbops->fb_sync(info);
- return addr;
- }
-
- /* See if we fit in the remaining pixmap space */
- offset = buf->offset + align;
- offset &= ~align;
- if (offset + size > buf->size) {
- /* We do not fit. In order to be able to re-use the buffer,
- * we must ensure no asynchronous DMA'ing or whatever operation
- * is in progress, we sync for that.
- */
- if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
- info->fbops->fb_sync(info);
- offset = 0;
- }
- buf->offset = offset + size;
- addr += offset;
-
- return addr;
-}
-EXPORT_SYMBOL(fb_get_buffer_offset);
-
-#ifdef CONFIG_LOGO
-
-static inline unsigned safe_shift(unsigned d, int n)
-{
- return n < 0 ? d >> -n : d << n;
-}
-
-static void fb_set_logocmap(struct fb_info *info,
- const struct linux_logo *logo)
-{
- struct fb_cmap palette_cmap;
- u16 palette_green[16];
- u16 palette_blue[16];
- u16 palette_red[16];
- int i, j, n;
- const unsigned char *clut = logo->clut;
-
- palette_cmap.start = 0;
- palette_cmap.len = 16;
- palette_cmap.red = palette_red;
- palette_cmap.green = palette_green;
- palette_cmap.blue = palette_blue;
- palette_cmap.transp = NULL;
-
- for (i = 0; i < logo->clutsize; i += n) {
- n = logo->clutsize - i;
- /* palette_cmap provides space for only 16 colors at once */
- if (n > 16)
- n = 16;
- palette_cmap.start = 32 + i;
- palette_cmap.len = n;
- for (j = 0; j < n; ++j) {
- palette_cmap.red[j] = clut[0] << 8 | clut[0];
- palette_cmap.green[j] = clut[1] << 8 | clut[1];
- palette_cmap.blue[j] = clut[2] << 8 | clut[2];
- clut += 3;
- }
- fb_set_cmap(&palette_cmap, info);
- }
-}
-
-static void fb_set_logo_truepalette(struct fb_info *info,
- const struct linux_logo *logo,
- u32 *palette)
-{
- static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
- unsigned char redmask, greenmask, bluemask;
- int redshift, greenshift, blueshift;
- int i;
- const unsigned char *clut = logo->clut;
-
- /*
- * We have to create a temporary palette since console palette is only
- * 16 colors long.
- */
- /* Bug: Doesn't obey msb_right ... (who needs that?) */
- redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
- greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
- bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
- redshift = info->var.red.offset - (8 - info->var.red.length);
- greenshift = info->var.green.offset - (8 - info->var.green.length);
- blueshift = info->var.blue.offset - (8 - info->var.blue.length);
-
- for ( i = 0; i < logo->clutsize; i++) {
- palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
- safe_shift((clut[1] & greenmask), greenshift) |
- safe_shift((clut[2] & bluemask), blueshift));
- clut += 3;
- }
-}
-
-static void fb_set_logo_directpalette(struct fb_info *info,
- const struct linux_logo *logo,
- u32 *palette)
-{
- int redshift, greenshift, blueshift;
- int i;
-
- redshift = info->var.red.offset;
- greenshift = info->var.green.offset;
- blueshift = info->var.blue.offset;
-
- for (i = 32; i < 32 + logo->clutsize; i++)
- palette[i] = i << redshift | i << greenshift | i << blueshift;
-}
-
-static void fb_set_logo(struct fb_info *info,
- const struct linux_logo *logo, u8 *dst,
- int depth)
-{
- int i, j, k;
- const u8 *src = logo->data;
- u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
- u8 fg = 1, d;
-
- switch (fb_get_color_depth(&info->var, &info->fix)) {
- case 1:
- fg = 1;
- break;
- case 2:
- fg = 3;
- break;
- default:
- fg = 7;
- break;
- }
-
- if (info->fix.visual == FB_VISUAL_MONO01 ||
- info->fix.visual == FB_VISUAL_MONO10)
- fg = ~((u8) (0xfff << info->var.green.length));
-
- switch (depth) {
- case 4:
- for (i = 0; i < logo->height; i++)
- for (j = 0; j < logo->width; src++) {
- *dst++ = *src >> 4;
- j++;
- if (j < logo->width) {
- *dst++ = *src & 0x0f;
- j++;
- }
- }
- break;
- case 1:
- for (i = 0; i < logo->height; i++) {
- for (j = 0; j < logo->width; src++) {
- d = *src ^ xor;
- for (k = 7; k >= 0; k--) {
- *dst++ = ((d >> k) & 1) ? fg : 0;
- j++;
- }
- }
- }
- break;
- }
-}
-
-/*
- * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
- * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
- * the visual format and color depth of the framebuffer, the DAC, the
- * pseudo_palette, and the logo data will be adjusted accordingly.
- *
- * Case 1 - linux_logo_clut224:
- * Color exceeds the number of console colors (16), thus we set the hardware DAC
- * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
- *
- * For visuals that require color info from the pseudo_palette, we also construct
- * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
- * will be set.
- *
- * Case 2 - linux_logo_vga16:
- * The number of colors just matches the console colors, thus there is no need
- * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
- * each byte contains color information for two pixels (upper and lower nibble).
- * To be consistent with fb_imageblit() usage, we therefore separate the two
- * nibbles into separate bytes. The "depth" flag will be set to 4.
- *
- * Case 3 - linux_logo_mono:
- * This is similar with Case 2. Each byte contains information for 8 pixels.
- * We isolate each bit and expand each into a byte. The "depth" flag will
- * be set to 1.
- */
-static struct logo_data {
- int depth;
- int needs_directpalette;
- int needs_truepalette;
- int needs_cmapreset;
- const struct linux_logo *logo;
-} fb_logo __read_mostly;
-
-static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
-{
- u32 size = width * height, i;
-
- out += size - 1;
-
- for (i = size; i--; )
- *out-- = *in++;
-}
-
-static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
-{
- int i, j, h = height - 1;
-
- for (i = 0; i < height; i++)
- for (j = 0; j < width; j++)
- out[height * j + h - i] = *in++;
-}
-
-static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
-{
- int i, j, w = width - 1;
-
- for (i = 0; i < height; i++)
- for (j = 0; j < width; j++)
- out[height * (w - j) + i] = *in++;
-}
-
-static void fb_rotate_logo(struct fb_info *info, u8 *dst,
- struct fb_image *image, int rotate)
-{
- u32 tmp;
-
- if (rotate == FB_ROTATE_UD) {
- fb_rotate_logo_ud(image->data, dst, image->width,
- image->height);
- image->dx = info->var.xres - image->width - image->dx;
- image->dy = info->var.yres - image->height - image->dy;
- } else if (rotate == FB_ROTATE_CW) {
- fb_rotate_logo_cw(image->data, dst, image->width,
- image->height);
- tmp = image->width;
- image->width = image->height;
- image->height = tmp;
- tmp = image->dy;
- image->dy = image->dx;
- image->dx = info->var.xres - image->width - tmp;
- } else if (rotate == FB_ROTATE_CCW) {
- fb_rotate_logo_ccw(image->data, dst, image->width,
- image->height);
- tmp = image->width;
- image->width = image->height;
- image->height = tmp;
- tmp = image->dx;
- image->dx = image->dy;
- image->dy = info->var.yres - image->height - tmp;
- }
-
- image->data = dst;
-}
-
-static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
- int rotate, unsigned int num)
-{
- unsigned int x;
-
- if (rotate == FB_ROTATE_UR) {
- for (x = 0;
- x < num && image->dx + image->width <= info->var.xres;
- x++) {
- info->fbops->fb_imageblit(info, image);
- image->dx += image->width + 8;
- }
- } else if (rotate == FB_ROTATE_UD) {
- for (x = 0; x < num && image->dx >= 0; x++) {
- info->fbops->fb_imageblit(info, image);
- image->dx -= image->width + 8;
- }
- } else if (rotate == FB_ROTATE_CW) {
- for (x = 0;
- x < num && image->dy + image->height <= info->var.yres;
- x++) {
- info->fbops->fb_imageblit(info, image);
- image->dy += image->height + 8;
- }
- } else if (rotate == FB_ROTATE_CCW) {
- for (x = 0; x < num && image->dy >= 0; x++) {
- info->fbops->fb_imageblit(info, image);
- image->dy -= image->height + 8;
- }
- }
-}
-
-static int fb_show_logo_line(struct fb_info *info, int rotate,
- const struct linux_logo *logo, int y,
- unsigned int n)
-{
- u32 *palette = NULL, *saved_pseudo_palette = NULL;
- unsigned char *logo_new = NULL, *logo_rotate = NULL;
- struct fb_image image;
-
- /* Return if the frame buffer is not mapped or suspended */
- if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
- info->flags & FBINFO_MODULE)
- return 0;
-
- image.depth = 8;
- image.data = logo->data;
-
- if (fb_logo.needs_cmapreset)
- fb_set_logocmap(info, logo);
-
- if (fb_logo.needs_truepalette ||
- fb_logo.needs_directpalette) {
- palette = kmalloc(256 * 4, GFP_KERNEL);
- if (palette == NULL)
- return 0;
-
- if (fb_logo.needs_truepalette)
- fb_set_logo_truepalette(info, logo, palette);
- else
- fb_set_logo_directpalette(info, logo, palette);
-
- saved_pseudo_palette = info->pseudo_palette;
- info->pseudo_palette = palette;
- }
-
- if (fb_logo.depth <= 4) {
- logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL);
- if (logo_new == NULL) {
- kfree(palette);
- if (saved_pseudo_palette)
- info->pseudo_palette = saved_pseudo_palette;
- return 0;
- }
- image.data = logo_new;
- fb_set_logo(info, logo, logo_new, fb_logo.depth);
- }
-
- image.dx = 0;
- image.dy = y;
- image.width = logo->width;
- image.height = logo->height;
-
- if (rotate) {
- logo_rotate = kmalloc(logo->width *
- logo->height, GFP_KERNEL);
- if (logo_rotate)
- fb_rotate_logo(info, logo_rotate, &image, rotate);
- }
-
- fb_do_show_logo(info, &image, rotate, n);
-
- kfree(palette);
- if (saved_pseudo_palette != NULL)
- info->pseudo_palette = saved_pseudo_palette;
- kfree(logo_new);
- kfree(logo_rotate);
- return logo->height;
-}
-
-
-#ifdef CONFIG_FB_LOGO_EXTRA
-
-#define FB_LOGO_EX_NUM_MAX 10
-static struct logo_data_extra {
- const struct linux_logo *logo;
- unsigned int n;
-} fb_logo_ex[FB_LOGO_EX_NUM_MAX];
-static unsigned int fb_logo_ex_num;
-
-void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
-{
- if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
- return;
-
- fb_logo_ex[fb_logo_ex_num].logo = logo;
- fb_logo_ex[fb_logo_ex_num].n = n;
- fb_logo_ex_num++;
-}
-
-static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
- unsigned int yres)
-{
- unsigned int i;
-
- /* FIXME: logo_ex supports only truecolor fb. */
- if (info->fix.visual != FB_VISUAL_TRUECOLOR)
- fb_logo_ex_num = 0;
-
- for (i = 0; i < fb_logo_ex_num; i++) {
- if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
- fb_logo_ex[i].logo = NULL;
- continue;
- }
- height += fb_logo_ex[i].logo->height;
- if (height > yres) {
- height -= fb_logo_ex[i].logo->height;
- fb_logo_ex_num = i;
- break;
- }
- }
- return height;
-}
-
-static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
-{
- unsigned int i;
-
- for (i = 0; i < fb_logo_ex_num; i++)
- y += fb_show_logo_line(info, rotate,
- fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
-
- return y;
-}
-
-#else /* !CONFIG_FB_LOGO_EXTRA */
-
-static inline int fb_prepare_extra_logos(struct fb_info *info,
- unsigned int height,
- unsigned int yres)
-{
- return height;
-}
-
-static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
-{
- return y;
-}
-
-#endif /* CONFIG_FB_LOGO_EXTRA */
-
-
-int fb_prepare_logo(struct fb_info *info, int rotate)
-{
- int depth = fb_get_color_depth(&info->var, &info->fix);
- unsigned int yres;
-
- memset(&fb_logo, 0, sizeof(struct logo_data));
-
- if (info->flags & FBINFO_MISC_TILEBLITTING ||
- info->flags & FBINFO_MODULE)
- return 0;
-
- if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
- depth = info->var.blue.length;
- if (info->var.red.length < depth)
- depth = info->var.red.length;
- if (info->var.green.length < depth)
- depth = info->var.green.length;
- }
-
- if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
- /* assume console colormap */
- depth = 4;
- }
-
- /* Return if no suitable logo was found */
- fb_logo.logo = fb_find_logo(depth);
-
- if (!fb_logo.logo) {
- return 0;
- }
-
- if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
- yres = info->var.yres;
- else
- yres = info->var.xres;
-
- if (fb_logo.logo->height > yres) {
- fb_logo.logo = NULL;
- return 0;
- }
-
- /* What depth we asked for might be different from what we get */
- if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
- fb_logo.depth = 8;
- else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
- fb_logo.depth = 4;
- else
- fb_logo.depth = 1;
-
-
- if (fb_logo.depth > 4 && depth > 4) {
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
- fb_logo.needs_truepalette = 1;
- break;
- case FB_VISUAL_DIRECTCOLOR:
- fb_logo.needs_directpalette = 1;
- fb_logo.needs_cmapreset = 1;
- break;
- case FB_VISUAL_PSEUDOCOLOR:
- fb_logo.needs_cmapreset = 1;
- break;
- }
- }
-
- return fb_prepare_extra_logos(info, fb_logo.logo->height, yres);
-}
-
-int fb_show_logo(struct fb_info *info, int rotate)
-{
- int y;
-
- y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
- num_online_cpus());
- y = fb_show_extra_logos(info, y, rotate);
-
- return y;
-}
-#else
-int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
-int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
-#endif /* CONFIG_LOGO */
-EXPORT_SYMBOL(fb_show_logo);
-
-static void *fb_seq_start(struct seq_file *m, loff_t *pos)
-{
- mutex_lock(®istration_lock);
- return (*pos < FB_MAX) ? pos : NULL;
-}
-
-static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
-{
- (*pos)++;
- return (*pos < FB_MAX) ? pos : NULL;
-}
-
-static void fb_seq_stop(struct seq_file *m, void *v)
-{
- mutex_unlock(®istration_lock);
-}
-
-static int fb_seq_show(struct seq_file *m, void *v)
-{
- int i = *(loff_t *)v;
- struct fb_info *fi = registered_fb[i];
-
- if (fi)
- seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
- return 0;
-}
-
-static const struct seq_operations proc_fb_seq_ops = {
- .start = fb_seq_start,
- .next = fb_seq_next,
- .stop = fb_seq_stop,
- .show = fb_seq_show,
-};
-
-static int proc_fb_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &proc_fb_seq_ops);
-}
-
-static const struct file_operations fb_proc_fops = {
- .owner = THIS_MODULE,
- .open = proc_fb_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
-/*
- * We hold a reference to the fb_info in file->private_data,
- * but if the current registered fb has changed, we don't
- * actually want to use it.
- *
- * So look up the fb_info using the inode minor number,
- * and just verify it against the reference we have.
- */
-static struct fb_info *file_fb_info(struct file *file)
-{
- struct inode *inode = file_inode(file);
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
-
- if (info != file->private_data)
- info = NULL;
- return info;
-}
-
-static ssize_t
-fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
- unsigned long p = *ppos;
- struct fb_info *info = file_fb_info(file);
- u8 *buffer, *dst;
- u8 __iomem *src;
- int c, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || ! info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_read)
- return info->fbops->fb_read(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p >= total_size)
- return 0;
-
- if (count >= total_size)
- count = total_size;
-
- if (count + p > total_size)
- count = total_size - p;
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- src = (u8 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- dst = buffer;
- fb_memcpy_fromfb(dst, src, c);
- dst += c;
- src += c;
-
- if (copy_to_user(buf, buffer, c)) {
- err = -EFAULT;
- break;
- }
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (err) ? err : cnt;
-}
-
-static ssize_t
-fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
- unsigned long p = *ppos;
- struct fb_info *info = file_fb_info(file);
- u8 *buffer, *src;
- u8 __iomem *dst;
- int c, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || !info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_write)
- return info->fbops->fb_write(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p > total_size)
- return -EFBIG;
-
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
-
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
-
- count = total_size - p;
- }
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- dst = (u8 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- src = buffer;
-
- if (copy_from_user(src, buf, c)) {
- err = -EFAULT;
- break;
- }
-
- fb_memcpy_tofb(dst, src, c);
- dst += c;
- src += c;
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (cnt) ? cnt : err;
-}
-
-int
-fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
-{
- struct fb_fix_screeninfo *fix = &info->fix;
- unsigned int yres = info->var.yres;
- int err = 0;
-
- if (var->yoffset > 0) {
- if (var->vmode & FB_VMODE_YWRAP) {
- if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
- err = -EINVAL;
- else
- yres = 0;
- } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
- err = -EINVAL;
- }
-
- if (var->xoffset > 0 && (!fix->xpanstep ||
- (var->xoffset % fix->xpanstep)))
- err = -EINVAL;
-
- if (err || !info->fbops->fb_pan_display ||
- var->yoffset > info->var.yres_virtual - yres ||
- var->xoffset > info->var.xres_virtual - info->var.xres)
- return -EINVAL;
-
- if ((err = info->fbops->fb_pan_display(var, info)))
- return err;
- info->var.xoffset = var->xoffset;
- info->var.yoffset = var->yoffset;
- if (var->vmode & FB_VMODE_YWRAP)
- info->var.vmode |= FB_VMODE_YWRAP;
- else
- info->var.vmode &= ~FB_VMODE_YWRAP;
- return 0;
-}
-EXPORT_SYMBOL(fb_pan_display);
-
-static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
- u32 activate)
-{
- struct fb_event event;
- struct fb_blit_caps caps, fbcaps;
- int err = 0;
-
- memset(&caps, 0, sizeof(caps));
- memset(&fbcaps, 0, sizeof(fbcaps));
- caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
- event.info = info;
- event.data = ∩︀
- fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
- info->fbops->fb_get_caps(info, &fbcaps, var);
-
- if (((fbcaps.x ^ caps.x) & caps.x) ||
- ((fbcaps.y ^ caps.y) & caps.y) ||
- (fbcaps.len < caps.len))
- err = -EINVAL;
-
- return err;
-}
-
-int
-fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
-{
- int flags = info->flags;
- int ret = 0;
-
- if (var->activate & FB_ACTIVATE_INV_MODE) {
- struct fb_videomode mode1, mode2;
-
- fb_var_to_videomode(&mode1, var);
- fb_var_to_videomode(&mode2, &info->var);
- /* make sure we don't delete the videomode of current var */
- ret = fb_mode_is_equal(&mode1, &mode2);
-
- if (!ret) {
- struct fb_event event;
-
- event.info = info;
- event.data = &mode1;
- ret = fb_notifier_call_chain(FB_EVENT_MODE_DELETE, &event);
- }
-
- if (!ret)
- fb_delete_videomode(&mode1, &info->modelist);
-
-
- ret = (ret) ? -EINVAL : 0;
- goto done;
- }
-
- if ((var->activate & FB_ACTIVATE_FORCE) ||
- memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
- u32 activate = var->activate;
-
- /* When using FOURCC mode, make sure the red, green, blue and
- * transp fields are set to 0.
- */
- if ((info->fix.capabilities & FB_CAP_FOURCC) &&
- var->grayscale > 1) {
- if (var->red.offset || var->green.offset ||
- var->blue.offset || var->transp.offset ||
- var->red.length || var->green.length ||
- var->blue.length || var->transp.length ||
- var->red.msb_right || var->green.msb_right ||
- var->blue.msb_right || var->transp.msb_right)
- return -EINVAL;
- }
-
- if (!info->fbops->fb_check_var) {
- *var = info->var;
- goto done;
- }
-
- ret = info->fbops->fb_check_var(var, info);
-
- if (ret)
- goto done;
-
- if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
- struct fb_var_screeninfo old_var;
- struct fb_videomode mode;
-
- if (info->fbops->fb_get_caps) {
- ret = fb_check_caps(info, var, activate);
-
- if (ret)
- goto done;
- }
-
- old_var = info->var;
- info->var = *var;
-
- if (info->fbops->fb_set_par) {
- ret = info->fbops->fb_set_par(info);
-
- if (ret) {
- info->var = old_var;
- printk(KERN_WARNING "detected "
- "fb_set_par error, "
- "error code: %d\n", ret);
- goto done;
- }
- }
-
- fb_pan_display(info, &info->var);
- fb_set_cmap(&info->cmap, info);
- fb_var_to_videomode(&mode, &info->var);
-
- if (info->modelist.prev && info->modelist.next &&
- !list_empty(&info->modelist))
- ret = fb_add_videomode(&mode, &info->modelist);
-
- if (!ret && (flags & FBINFO_MISC_USEREVENT)) {
- struct fb_event event;
- int evnt = (activate & FB_ACTIVATE_ALL) ?
- FB_EVENT_MODE_CHANGE_ALL :
- FB_EVENT_MODE_CHANGE;
-
- info->flags &= ~FBINFO_MISC_USEREVENT;
- event.info = info;
- event.data = &mode;
- fb_notifier_call_chain(evnt, &event);
- }
- }
- }
-
- done:
- return ret;
-}
-EXPORT_SYMBOL(fb_set_var);
-
-int
-fb_blank(struct fb_info *info, int blank)
-{
- struct fb_event event;
- int ret = -EINVAL, early_ret;
-
- if (blank > FB_BLANK_POWERDOWN)
- blank = FB_BLANK_POWERDOWN;
-
- event.info = info;
- event.data = ␣
-
- early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);
-
- if (info->fbops->fb_blank)
- ret = info->fbops->fb_blank(blank, info);
-
- if (!ret)
- fb_notifier_call_chain(FB_EVENT_BLANK, &event);
- else {
- /*
- * if fb_blank is failed then revert effects of
- * the early blank event.
- */
- if (!early_ret)
- fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
- }
-
- return ret;
-}
-EXPORT_SYMBOL(fb_blank);
-
-static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
-{
- struct fb_ops *fb;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_con2fbmap con2fb;
- struct fb_cmap cmap_from;
- struct fb_cmap_user cmap;
- struct fb_event event;
- void __user *argp = (void __user *)arg;
- long ret = 0;
-
- switch (cmd) {
- case FBIOGET_VSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- var = info->var;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
- break;
- case FBIOPUT_VSCREENINFO:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- console_lock();
- if (!lock_fb_info(info)) {
- console_unlock();
- return -ENODEV;
- }
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_set_var(info, &var);
- info->flags &= ~FBINFO_MISC_USEREVENT;
- unlock_fb_info(info);
- console_unlock();
- if (!ret && copy_to_user(argp, &var, sizeof(var)))
- ret = -EFAULT;
- break;
- case FBIOGET_FSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- fix = info->fix;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
- break;
- case FBIOPUTCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- ret = fb_set_user_cmap(&cmap, info);
- break;
- case FBIOGETCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- cmap_from = info->cmap;
- unlock_fb_info(info);
- ret = fb_cmap_to_user(&cmap_from, &cmap);
- break;
- case FBIOPAN_DISPLAY:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- console_lock();
- if (!lock_fb_info(info)) {
- console_unlock();
- return -ENODEV;
- }
- ret = fb_pan_display(info, &var);
- unlock_fb_info(info);
- console_unlock();
- if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
- return -EFAULT;
- break;
- case FBIO_CURSOR:
- ret = -EINVAL;
- break;
- case FBIOGET_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- con2fb.framebuffer = -1;
- event.data = &con2fb;
- if (!lock_fb_info(info))
- return -ENODEV;
- event.info = info;
- fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
- break;
- case FBIOPUT_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
- return -EINVAL;
- if (!registered_fb[con2fb.framebuffer])
- request_module("fb%d", con2fb.framebuffer);
- if (!registered_fb[con2fb.framebuffer]) {
- ret = -EINVAL;
- break;
- }
- event.data = &con2fb;
- console_lock();
- if (!lock_fb_info(info)) {
- console_unlock();
- return -ENODEV;
- }
- event.info = info;
- ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- console_unlock();
- break;
- case FBIOBLANK:
- console_lock();
- if (!lock_fb_info(info)) {
- console_unlock();
- return -ENODEV;
- }
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_blank(info, arg);
- info->flags &= ~FBINFO_MISC_USEREVENT;
- unlock_fb_info(info);
- console_unlock();
- break;
- default:
- if (!lock_fb_info(info))
- return -ENODEV;
- fb = info->fbops;
- if (fb->fb_ioctl)
- ret = fb->fb_ioctl(info, cmd, arg);
- else
- ret = -ENOTTY;
- unlock_fb_info(info);
- }
- return ret;
-}
-
-static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct fb_info *info = file_fb_info(file);
-
- if (!info)
- return -ENODEV;
- return do_fb_ioctl(info, cmd, arg);
-}
-
-#ifdef CONFIG_COMPAT
-struct fb_fix_screeninfo32 {
- char id[16];
- compat_caddr_t smem_start;
- u32 smem_len;
- u32 type;
- u32 type_aux;
- u32 visual;
- u16 xpanstep;
- u16 ypanstep;
- u16 ywrapstep;
- u32 line_length;
- compat_caddr_t mmio_start;
- u32 mmio_len;
- u32 accel;
- u16 reserved[3];
-};
-
-struct fb_cmap32 {
- u32 start;
- u32 len;
- compat_caddr_t red;
- compat_caddr_t green;
- compat_caddr_t blue;
- compat_caddr_t transp;
-};
-
-static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
-{
- struct fb_cmap_user __user *cmap;
- struct fb_cmap32 __user *cmap32;
- __u32 data;
- int err;
-
- cmap = compat_alloc_user_space(sizeof(*cmap));
- cmap32 = compat_ptr(arg);
-
- if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32)))
- return -EFAULT;
-
- if (get_user(data, &cmap32->red) ||
- put_user(compat_ptr(data), &cmap->red) ||
- get_user(data, &cmap32->green) ||
- put_user(compat_ptr(data), &cmap->green) ||
- get_user(data, &cmap32->blue) ||
- put_user(compat_ptr(data), &cmap->blue) ||
- get_user(data, &cmap32->transp) ||
- put_user(compat_ptr(data), &cmap->transp))
- return -EFAULT;
-
- err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
-
- if (!err) {
- if (copy_in_user(&cmap32->start,
- &cmap->start,
- 2 * sizeof(__u32)))
- err = -EFAULT;
- }
- return err;
-}
-
-static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
- struct fb_fix_screeninfo32 __user *fix32)
-{
- __u32 data;
- int err;
-
- err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
-
- data = (__u32) (unsigned long) fix->smem_start;
- err |= put_user(data, &fix32->smem_start);
-
- err |= put_user(fix->smem_len, &fix32->smem_len);
- err |= put_user(fix->type, &fix32->type);
- err |= put_user(fix->type_aux, &fix32->type_aux);
- err |= put_user(fix->visual, &fix32->visual);
- err |= put_user(fix->xpanstep, &fix32->xpanstep);
- err |= put_user(fix->ypanstep, &fix32->ypanstep);
- err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
- err |= put_user(fix->line_length, &fix32->line_length);
-
- data = (__u32) (unsigned long) fix->mmio_start;
- err |= put_user(data, &fix32->mmio_start);
-
- err |= put_user(fix->mmio_len, &fix32->mmio_len);
- err |= put_user(fix->accel, &fix32->accel);
- err |= copy_to_user(fix32->reserved, fix->reserved,
- sizeof(fix->reserved));
-
- if (err)
- return -EFAULT;
- return 0;
-}
-
-static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
-{
- mm_segment_t old_fs;
- struct fb_fix_screeninfo fix;
- struct fb_fix_screeninfo32 __user *fix32;
- int err;
-
- fix32 = compat_ptr(arg);
-
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
- set_fs(old_fs);
-
- if (!err)
- err = do_fscreeninfo_to_user(&fix, fix32);
-
- return err;
-}
-
-static long fb_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct fb_info *info = file_fb_info(file);
- struct fb_ops *fb;
- long ret = -ENOIOCTLCMD;
-
- if (!info)
- return -ENODEV;
- fb = info->fbops;
- switch(cmd) {
- case FBIOGET_VSCREENINFO:
- case FBIOPUT_VSCREENINFO:
- case FBIOPAN_DISPLAY:
- case FBIOGET_CON2FBMAP:
- case FBIOPUT_CON2FBMAP:
- arg = (unsigned long) compat_ptr(arg);
- case FBIOBLANK:
- ret = do_fb_ioctl(info, cmd, arg);
- break;
-
- case FBIOGET_FSCREENINFO:
- ret = fb_get_fscreeninfo(info, cmd, arg);
- break;
-
- case FBIOGETCMAP:
- case FBIOPUTCMAP:
- ret = fb_getput_cmap(info, cmd, arg);
- break;
-
- default:
- if (fb->fb_compat_ioctl)
- ret = fb->fb_compat_ioctl(info, cmd, arg);
- break;
- }
- return ret;
-}
-#endif
-
-static int
-fb_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct fb_info *info = file_fb_info(file);
- struct fb_ops *fb;
- unsigned long mmio_pgoff;
- unsigned long start;
- u32 len;
-
- if (!info)
- return -ENODEV;
- fb = info->fbops;
- if (!fb)
- return -ENODEV;
- mutex_lock(&info->mm_lock);
- if (fb->fb_mmap) {
- int res;
- res = fb->fb_mmap(info, vma);
- mutex_unlock(&info->mm_lock);
- return res;
- }
-
- /*
- * Ugh. This can be either the frame buffer mapping, or
- * if pgoff points past it, the mmio mapping.
- */
- start = info->fix.smem_start;
- len = info->fix.smem_len;
- mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
- if (vma->vm_pgoff >= mmio_pgoff) {
- if (info->var.accel_flags) {
- mutex_unlock(&info->mm_lock);
- return -EINVAL;
- }
-
- vma->vm_pgoff -= mmio_pgoff;
- start = info->fix.mmio_start;
- len = info->fix.mmio_len;
- }
- mutex_unlock(&info->mm_lock);
-
- vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
- fb_pgprotect(file, vma, start);
-
- return vm_iomap_memory(vma, start, len);
-}
-
-static int
-fb_open(struct inode *inode, struct file *file)
-__acquires(&info->lock)
-__releases(&info->lock)
-{
- int fbidx = iminor(inode);
- struct fb_info *info;
- int res = 0;
-
- info = get_fb_info(fbidx);
- if (!info) {
- request_module("fb%d", fbidx);
- info = get_fb_info(fbidx);
- if (!info)
- return -ENODEV;
- }
- if (IS_ERR(info))
- return PTR_ERR(info);
-
- mutex_lock(&info->lock);
- if (!try_module_get(info->fbops->owner)) {
- res = -ENODEV;
- goto out;
- }
- file->private_data = info;
- if (info->fbops->fb_open) {
- res = info->fbops->fb_open(info,1);
- if (res)
- module_put(info->fbops->owner);
- }
-#ifdef CONFIG_FB_DEFERRED_IO
- if (info->fbdefio)
- fb_deferred_io_open(info, inode, file);
-#endif
-out:
- mutex_unlock(&info->lock);
- if (res)
- put_fb_info(info);
- return res;
-}
-
-static int
-fb_release(struct inode *inode, struct file *file)
-__acquires(&info->lock)
-__releases(&info->lock)
-{
- struct fb_info * const info = file->private_data;
-
- mutex_lock(&info->lock);
- if (info->fbops->fb_release)
- info->fbops->fb_release(info,1);
- module_put(info->fbops->owner);
- mutex_unlock(&info->lock);
- put_fb_info(info);
- return 0;
-}
-
-static const struct file_operations fb_fops = {
- .owner = THIS_MODULE,
- .read = fb_read,
- .write = fb_write,
- .unlocked_ioctl = fb_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = fb_compat_ioctl,
-#endif
- .mmap = fb_mmap,
- .open = fb_open,
- .release = fb_release,
-#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
- .get_unmapped_area = get_fb_unmapped_area,
-#endif
-#ifdef CONFIG_FB_DEFERRED_IO
- .fsync = fb_deferred_io_fsync,
-#endif
- .llseek = default_llseek,
-};
-
-struct class *fb_class;
-EXPORT_SYMBOL(fb_class);
-
-static int fb_check_foreignness(struct fb_info *fi)
-{
- const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
-
- fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
-
-#ifdef __BIG_ENDIAN
- fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
-#else
- fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
-#endif /* __BIG_ENDIAN */
-
- if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
- pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
- "support this framebuffer\n", fi->fix.id);
- return -ENOSYS;
- } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
- pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
- "support this framebuffer\n", fi->fix.id);
- return -ENOSYS;
- }
-
- return 0;
-}
-
-static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
-{
- /* is the generic aperture base the same as the HW one */
- if (gen->base == hw->base)
- return true;
- /* is the generic aperture base inside the hw base->hw base+size */
- if (gen->base > hw->base && gen->base < hw->base + hw->size)
- return true;
- return false;
-}
-
-static bool fb_do_apertures_overlap(struct apertures_struct *gena,
- struct apertures_struct *hwa)
-{
- int i, j;
- if (!hwa || !gena)
- return false;
-
- for (i = 0; i < hwa->count; ++i) {
- struct aperture *h = &hwa->ranges[i];
- for (j = 0; j < gena->count; ++j) {
- struct aperture *g = &gena->ranges[j];
- printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
- (unsigned long long)g->base,
- (unsigned long long)g->size,
- (unsigned long long)h->base,
- (unsigned long long)h->size);
- if (apertures_overlap(g, h))
- return true;
- }
- }
-
- return false;
-}
-
-static int do_unregister_framebuffer(struct fb_info *fb_info);
-
-#define VGA_FB_PHYS 0xA0000
-static int do_remove_conflicting_framebuffers(struct apertures_struct *a,
- const char *name, bool primary)
-{
- int i, ret;
-
- /* check all firmware fbs and kick off if the base addr overlaps */
- for (i = 0 ; i < FB_MAX; i++) {
- struct apertures_struct *gen_aper;
- if (!registered_fb[i])
- continue;
-
- if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
- continue;
-
- gen_aper = registered_fb[i]->apertures;
- if (fb_do_apertures_overlap(gen_aper, a) ||
- (primary && gen_aper && gen_aper->count &&
- gen_aper->ranges[0].base == VGA_FB_PHYS)) {
-
- printk(KERN_INFO "fb: switching to %s from %s\n",
- name, registered_fb[i]->fix.id);
- ret = do_unregister_framebuffer(registered_fb[i]);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
-static int do_register_framebuffer(struct fb_info *fb_info)
-{
- int i, ret;
- struct fb_event event;
- struct fb_videomode mode;
-
- if (fb_check_foreignness(fb_info))
- return -ENOSYS;
-
- ret = do_remove_conflicting_framebuffers(fb_info->apertures,
- fb_info->fix.id,
- fb_is_primary_device(fb_info));
- if (ret)
- return ret;
-
- if (num_registered_fb == FB_MAX)
- return -ENXIO;
-
- num_registered_fb++;
- for (i = 0 ; i < FB_MAX; i++)
- if (!registered_fb[i])
- break;
- fb_info->node = i;
- atomic_set(&fb_info->count, 1);
- mutex_init(&fb_info->lock);
- mutex_init(&fb_info->mm_lock);
-
- fb_info->dev = device_create(fb_class, fb_info->device,
- MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
- if (IS_ERR(fb_info->dev)) {
- /* Not fatal */
- printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
- fb_info->dev = NULL;
- } else
- fb_init_device(fb_info);
-
- if (fb_info->pixmap.addr == NULL) {
- fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
- if (fb_info->pixmap.addr) {
- fb_info->pixmap.size = FBPIXMAPSIZE;
- fb_info->pixmap.buf_align = 1;
- fb_info->pixmap.scan_align = 1;
- fb_info->pixmap.access_align = 32;
- fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- }
- }
- fb_info->pixmap.offset = 0;
-
- if (!fb_info->pixmap.blit_x)
- fb_info->pixmap.blit_x = ~(u32)0;
-
- if (!fb_info->pixmap.blit_y)
- fb_info->pixmap.blit_y = ~(u32)0;
-
- if (!fb_info->modelist.prev || !fb_info->modelist.next)
- INIT_LIST_HEAD(&fb_info->modelist);
-
- if (fb_info->skip_vt_switch)
- pm_vt_switch_required(fb_info->dev, false);
- else
- pm_vt_switch_required(fb_info->dev, true);
-
- fb_var_to_videomode(&mode, &fb_info->var);
- fb_add_videomode(&mode, &fb_info->modelist);
- registered_fb[i] = fb_info;
-
- event.info = fb_info;
- console_lock();
- if (!lock_fb_info(fb_info)) {
- console_unlock();
- return -ENODEV;
- }
-
- fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
- unlock_fb_info(fb_info);
- console_unlock();
- return 0;
-}
-
-static int do_unregister_framebuffer(struct fb_info *fb_info)
-{
- struct fb_event event;
- int i, ret = 0;
-
- i = fb_info->node;
- if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
- return -EINVAL;
-
- console_lock();
- if (!lock_fb_info(fb_info)) {
- console_unlock();
- return -ENODEV;
- }
-
- event.info = fb_info;
- ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
- unlock_fb_info(fb_info);
- console_unlock();
-
- if (ret)
- return -EINVAL;
-
- pm_vt_switch_unregister(fb_info->dev);
-
- unlink_framebuffer(fb_info);
- if (fb_info->pixmap.addr &&
- (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
- kfree(fb_info->pixmap.addr);
- fb_destroy_modelist(&fb_info->modelist);
- registered_fb[i] = NULL;
- num_registered_fb--;
- fb_cleanup_device(fb_info);
- event.info = fb_info;
- console_lock();
- fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
- console_unlock();
-
- /* this may free fb info */
- put_fb_info(fb_info);
- return 0;
-}
-
-int unlink_framebuffer(struct fb_info *fb_info)
-{
- int i;
-
- i = fb_info->node;
- if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
- return -EINVAL;
-
- if (fb_info->dev) {
- device_destroy(fb_class, MKDEV(FB_MAJOR, i));
- fb_info->dev = NULL;
- }
- return 0;
-}
-EXPORT_SYMBOL(unlink_framebuffer);
-
-int remove_conflicting_framebuffers(struct apertures_struct *a,
- const char *name, bool primary)
-{
- int ret;
-
- mutex_lock(®istration_lock);
- ret = do_remove_conflicting_framebuffers(a, name, primary);
- mutex_unlock(®istration_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(remove_conflicting_framebuffers);
-
-/**
- * register_framebuffer - registers a frame buffer device
- * @fb_info: frame buffer info structure
- *
- * Registers a frame buffer device @fb_info.
- *
- * Returns negative errno on error, or zero for success.
- *
- */
-int
-register_framebuffer(struct fb_info *fb_info)
-{
- int ret;
-
- mutex_lock(®istration_lock);
- ret = do_register_framebuffer(fb_info);
- mutex_unlock(®istration_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(register_framebuffer);
-
-/**
- * unregister_framebuffer - releases a frame buffer device
- * @fb_info: frame buffer info structure
- *
- * Unregisters a frame buffer device @fb_info.
- *
- * Returns negative errno on error, or zero for success.
- *
- * This function will also notify the framebuffer console
- * to release the driver.
- *
- * This is meant to be called within a driver's module_exit()
- * function. If this is called outside module_exit(), ensure
- * that the driver implements fb_open() and fb_release() to
- * check that no processes are using the device.
- */
-int
-unregister_framebuffer(struct fb_info *fb_info)
-{
- int ret;
-
- mutex_lock(®istration_lock);
- ret = do_unregister_framebuffer(fb_info);
- mutex_unlock(®istration_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(unregister_framebuffer);
-
-/**
- * fb_set_suspend - low level driver signals suspend
- * @info: framebuffer affected
- * @state: 0 = resuming, !=0 = suspending
- *
- * This is meant to be used by low level drivers to
- * signal suspend/resume to the core & clients.
- * It must be called with the console semaphore held
- */
-void fb_set_suspend(struct fb_info *info, int state)
-{
- struct fb_event event;
-
- event.info = info;
- if (state) {
- fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
- info->state = FBINFO_STATE_SUSPENDED;
- } else {
- info->state = FBINFO_STATE_RUNNING;
- fb_notifier_call_chain(FB_EVENT_RESUME, &event);
- }
-}
-EXPORT_SYMBOL(fb_set_suspend);
-
-/**
- * fbmem_init - init frame buffer subsystem
- *
- * Initialize the frame buffer subsystem.
- *
- * NOTE: This function is _only_ to be called by drivers/char/mem.c.
- *
- */
-
-static int __init
-fbmem_init(void)
-{
- proc_create("fb", 0, NULL, &fb_proc_fops);
-
- if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
- printk("unable to get major %d for fb devs\n", FB_MAJOR);
-
- fb_class = class_create(THIS_MODULE, "graphics");
- if (IS_ERR(fb_class)) {
- printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
- fb_class = NULL;
- }
- return 0;
-}
-
-#ifdef MODULE
-module_init(fbmem_init);
-static void __exit
-fbmem_exit(void)
-{
- remove_proc_entry("fb", NULL);
- class_destroy(fb_class);
- unregister_chrdev(FB_MAJOR, "fb");
-}
-
-module_exit(fbmem_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Framebuffer base");
-#else
-subsys_initcall(fbmem_init);
-#endif
-
-int fb_new_modelist(struct fb_info *info)
-{
- struct fb_event event;
- struct fb_var_screeninfo var = info->var;
- struct list_head *pos, *n;
- struct fb_modelist *modelist;
- struct fb_videomode *m, mode;
- int err = 1;
-
- list_for_each_safe(pos, n, &info->modelist) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- fb_videomode_to_var(&var, m);
- var.activate = FB_ACTIVATE_TEST;
- err = fb_set_var(info, &var);
- fb_var_to_videomode(&mode, &var);
- if (err || !fb_mode_is_equal(m, &mode)) {
- list_del(pos);
- kfree(pos);
- }
- }
-
- err = 1;
-
- if (!list_empty(&info->modelist)) {
- event.info = info;
- err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
- }
-
- return err;
-}
-
-static char *video_options[FB_MAX] __read_mostly;
-static int ofonly __read_mostly;
-
-/**
- * fb_get_options - get kernel boot parameters
- * @name: framebuffer name as it would appear in
- * the boot parameter line
- * (video=<name>:<options>)
- * @option: the option will be stored here
- *
- * NOTE: Needed to maintain backwards compatibility
- */
-int fb_get_options(const char *name, char **option)
-{
- char *opt, *options = NULL;
- int retval = 0;
- int name_len = strlen(name), i;
-
- if (name_len && ofonly && strncmp(name, "offb", 4))
- retval = 1;
-
- if (name_len && !retval) {
- for (i = 0; i < FB_MAX; i++) {
- if (video_options[i] == NULL)
- continue;
- if (!video_options[i][0])
- continue;
- opt = video_options[i];
- if (!strncmp(name, opt, name_len) &&
- opt[name_len] == ':')
- options = opt + name_len + 1;
- }
- }
- /* No match, pass global option */
- if (!options && option && fb_mode_option)
- options = kstrdup(fb_mode_option, GFP_KERNEL);
- if (options && !strncmp(options, "off", 3))
- retval = 1;
-
- if (option)
- *option = options;
-
- return retval;
-}
-EXPORT_SYMBOL(fb_get_options);
-
-#ifndef MODULE
-/**
- * video_setup - process command line options
- * @options: string of options
- *
- * Process command line options for frame buffer subsystem.
- *
- * NOTE: This function is a __setup and __init function.
- * It only stores the options. Drivers have to call
- * fb_get_options() as necessary.
- *
- * Returns zero.
- *
- */
-static int __init video_setup(char *options)
-{
- int i, global = 0;
-
- if (!options || !*options)
- global = 1;
-
- if (!global && !strncmp(options, "ofonly", 6)) {
- ofonly = 1;
- global = 1;
- }
-
- if (!global && !strchr(options, ':')) {
- fb_mode_option = options;
- global = 1;
- }
-
- if (!global) {
- for (i = 0; i < FB_MAX; i++) {
- if (video_options[i] == NULL) {
- video_options[i] = options;
- break;
- }
-
- }
- }
-
- return 1;
-}
-__setup("video=", video_setup);
-#endif
-
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * linux/drivers/video/fbmon.c
- *
- * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
- *
- * Credits:
- *
- * The EDID Parser is a conglomeration from the following sources:
- *
- * 1. SciTech SNAP Graphics Architecture
- * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
- *
- * 2. XFree86 4.3.0, interpret_edid.c
- * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
- *
- * 3. John Fremlin <vii@users.sourceforge.net> and
- * Ani Joshi <ajoshi@unixbox.com>
- *
- * Generalized Timing Formula is derived from:
- *
- * GTF Spreadsheet by Andy Morrish (1/5/97)
- * available at http://www.vesa.org
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-#include <linux/fb.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <video/edid.h>
-#include <video/of_videomode.h>
-#include <video/videomode.h>
-#ifdef CONFIG_PPC_OF
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#endif
-#include "edid.h"
-
-/*
- * EDID parser
- */
-
-#undef DEBUG /* define this for verbose EDID parsing output */
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(fmt,## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-#define FBMON_FIX_HEADER 1
-#define FBMON_FIX_INPUT 2
-#define FBMON_FIX_TIMINGS 3
-
-#ifdef CONFIG_FB_MODE_HELPERS
-struct broken_edid {
- u8 manufacturer[4];
- u32 model;
- u32 fix;
-};
-
-static const struct broken_edid brokendb[] = {
- /* DEC FR-PCXAV-YZ */
- {
- .manufacturer = "DEC",
- .model = 0x073a,
- .fix = FBMON_FIX_HEADER,
- },
- /* ViewSonic PF775a */
- {
- .manufacturer = "VSC",
- .model = 0x5a44,
- .fix = FBMON_FIX_INPUT,
- },
- /* Sharp UXGA? */
- {
- .manufacturer = "SHP",
- .model = 0x138e,
- .fix = FBMON_FIX_TIMINGS,
- },
-};
-
-static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0x00
-};
-
-static void copy_string(unsigned char *c, unsigned char *s)
-{
- int i;
- c = c + 5;
- for (i = 0; (i < 13 && *c != 0x0A); i++)
- *(s++) = *(c++);
- *s = 0;
- while (i-- && (*--s == 0x20)) *s = 0;
-}
-
-static int edid_is_serial_block(unsigned char *block)
-{
- if ((block[0] == 0x00) && (block[1] == 0x00) &&
- (block[2] == 0x00) && (block[3] == 0xff) &&
- (block[4] == 0x00))
- return 1;
- else
- return 0;
-}
-
-static int edid_is_ascii_block(unsigned char *block)
-{
- if ((block[0] == 0x00) && (block[1] == 0x00) &&
- (block[2] == 0x00) && (block[3] == 0xfe) &&
- (block[4] == 0x00))
- return 1;
- else
- return 0;
-}
-
-static int edid_is_limits_block(unsigned char *block)
-{
- if ((block[0] == 0x00) && (block[1] == 0x00) &&
- (block[2] == 0x00) && (block[3] == 0xfd) &&
- (block[4] == 0x00))
- return 1;
- else
- return 0;
-}
-
-static int edid_is_monitor_block(unsigned char *block)
-{
- if ((block[0] == 0x00) && (block[1] == 0x00) &&
- (block[2] == 0x00) && (block[3] == 0xfc) &&
- (block[4] == 0x00))
- return 1;
- else
- return 0;
-}
-
-static int edid_is_timing_block(unsigned char *block)
-{
- if ((block[0] != 0x00) || (block[1] != 0x00) ||
- (block[2] != 0x00) || (block[4] != 0x00))
- return 1;
- else
- return 0;
-}
-
-static int check_edid(unsigned char *edid)
-{
- unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
- unsigned char *b;
- u32 model;
- int i, fix = 0, ret = 0;
-
- manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
- manufacturer[1] = ((block[0] & 0x03) << 3) +
- ((block[1] & 0xe0) >> 5) + '@';
- manufacturer[2] = (block[1] & 0x1f) + '@';
- manufacturer[3] = 0;
- model = block[2] + (block[3] << 8);
-
- for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
- if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
- brokendb[i].model == model) {
- fix = brokendb[i].fix;
- break;
- }
- }
-
- switch (fix) {
- case FBMON_FIX_HEADER:
- for (i = 0; i < 8; i++) {
- if (edid[i] != edid_v1_header[i]) {
- ret = fix;
- break;
- }
- }
- break;
- case FBMON_FIX_INPUT:
- b = edid + EDID_STRUCT_DISPLAY;
- /* Only if display is GTF capable will
- the input type be reset to analog */
- if (b[4] & 0x01 && b[0] & 0x80)
- ret = fix;
- break;
- case FBMON_FIX_TIMINGS:
- b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
- ret = fix;
-
- for (i = 0; i < 4; i++) {
- if (edid_is_limits_block(b)) {
- ret = 0;
- break;
- }
-
- b += DETAILED_TIMING_DESCRIPTION_SIZE;
- }
-
- break;
- }
-
- if (ret)
- printk("fbmon: The EDID Block of "
- "Manufacturer: %s Model: 0x%x is known to "
- "be broken,\n", manufacturer, model);
-
- return ret;
-}
-
-static void fix_edid(unsigned char *edid, int fix)
-{
- int i;
- unsigned char *b, csum = 0;
-
- switch (fix) {
- case FBMON_FIX_HEADER:
- printk("fbmon: trying a header reconstruct\n");
- memcpy(edid, edid_v1_header, 8);
- break;
- case FBMON_FIX_INPUT:
- printk("fbmon: trying to fix input type\n");
- b = edid + EDID_STRUCT_DISPLAY;
- b[0] &= ~0x80;
- edid[127] += 0x80;
- break;
- case FBMON_FIX_TIMINGS:
- printk("fbmon: trying to fix monitor timings\n");
- b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
- for (i = 0; i < 4; i++) {
- if (!(edid_is_serial_block(b) ||
- edid_is_ascii_block(b) ||
- edid_is_monitor_block(b) ||
- edid_is_timing_block(b))) {
- b[0] = 0x00;
- b[1] = 0x00;
- b[2] = 0x00;
- b[3] = 0xfd;
- b[4] = 0x00;
- b[5] = 60; /* vfmin */
- b[6] = 60; /* vfmax */
- b[7] = 30; /* hfmin */
- b[8] = 75; /* hfmax */
- b[9] = 17; /* pixclock - 170 MHz*/
- b[10] = 0; /* GTF */
- break;
- }
-
- b += DETAILED_TIMING_DESCRIPTION_SIZE;
- }
-
- for (i = 0; i < EDID_LENGTH - 1; i++)
- csum += edid[i];
-
- edid[127] = 256 - csum;
- break;
- }
-}
-
-static int edid_checksum(unsigned char *edid)
-{
- unsigned char csum = 0, all_null = 0;
- int i, err = 0, fix = check_edid(edid);
-
- if (fix)
- fix_edid(edid, fix);
-
- for (i = 0; i < EDID_LENGTH; i++) {
- csum += edid[i];
- all_null |= edid[i];
- }
-
- if (csum == 0x00 && all_null) {
- /* checksum passed, everything's good */
- err = 1;
- }
-
- return err;
-}
-
-static int edid_check_header(unsigned char *edid)
-{
- int i, err = 1, fix = check_edid(edid);
-
- if (fix)
- fix_edid(edid, fix);
-
- for (i = 0; i < 8; i++) {
- if (edid[i] != edid_v1_header[i])
- err = 0;
- }
-
- return err;
-}
-
-static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs)
-{
- specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
- specs->manufacturer[1] = ((block[0] & 0x03) << 3) +
- ((block[1] & 0xe0) >> 5) + '@';
- specs->manufacturer[2] = (block[1] & 0x1f) + '@';
- specs->manufacturer[3] = 0;
- specs->model = block[2] + (block[3] << 8);
- specs->serial = block[4] + (block[5] << 8) +
- (block[6] << 16) + (block[7] << 24);
- specs->year = block[9] + 1990;
- specs->week = block[8];
- DPRINTK(" Manufacturer: %s\n", specs->manufacturer);
- DPRINTK(" Model: %x\n", specs->model);
- DPRINTK(" Serial#: %u\n", specs->serial);
- DPRINTK(" Year: %u Week %u\n", specs->year, specs->week);
-}
-
-static void get_dpms_capabilities(unsigned char flags,
- struct fb_monspecs *specs)
-{
- specs->dpms = 0;
- if (flags & DPMS_ACTIVE_OFF)
- specs->dpms |= FB_DPMS_ACTIVE_OFF;
- if (flags & DPMS_SUSPEND)
- specs->dpms |= FB_DPMS_SUSPEND;
- if (flags & DPMS_STANDBY)
- specs->dpms |= FB_DPMS_STANDBY;
- DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n",
- (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
- (flags & DPMS_SUSPEND) ? "yes" : "no",
- (flags & DPMS_STANDBY) ? "yes" : "no");
-}
-
-static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
-{
- int tmp;
-
- DPRINTK(" Chroma\n");
- /* Chromaticity data */
- tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.redx = tmp/1024;
- DPRINTK(" RedX: 0.%03d ", specs->chroma.redx);
-
- tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.redy = tmp/1024;
- DPRINTK("RedY: 0.%03d\n", specs->chroma.redy);
-
- tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.greenx = tmp/1024;
- DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx);
-
- tmp = (block[5] & 3) | (block[0xa] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.greeny = tmp/1024;
- DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny);
-
- tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.bluex = tmp/1024;
- DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex);
-
- tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.bluey = tmp/1024;
- DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey);
-
- tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.whitex = tmp/1024;
- DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex);
-
- tmp = (block[6] & 3) | (block[0xe] << 2);
- tmp *= 1000;
- tmp += 512;
- specs->chroma.whitey = tmp/1024;
- DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey);
-}
-
-static void calc_mode_timings(int xres, int yres, int refresh,
- struct fb_videomode *mode)
-{
- struct fb_var_screeninfo *var;
-
- var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
-
- if (var) {
- var->xres = xres;
- var->yres = yres;
- fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
- refresh, var, NULL);
- mode->xres = xres;
- mode->yres = yres;
- mode->pixclock = var->pixclock;
- mode->refresh = refresh;
- mode->left_margin = var->left_margin;
- mode->right_margin = var->right_margin;
- mode->upper_margin = var->upper_margin;
- mode->lower_margin = var->lower_margin;
- mode->hsync_len = var->hsync_len;
- mode->vsync_len = var->vsync_len;
- mode->vmode = 0;
- mode->sync = 0;
- kfree(var);
- }
-}
-
-static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
-{
- int num = 0;
- unsigned char c;
-
- c = block[0];
- if (c&0x80) {
- calc_mode_timings(720, 400, 70, &mode[num]);
- mode[num++].flag = FB_MODE_IS_CALCULATED;
- DPRINTK(" 720x400@70Hz\n");
- }
- if (c&0x40) {
- calc_mode_timings(720, 400, 88, &mode[num]);
- mode[num++].flag = FB_MODE_IS_CALCULATED;
- DPRINTK(" 720x400@88Hz\n");
- }
- if (c&0x20) {
- mode[num++] = vesa_modes[3];
- DPRINTK(" 640x480@60Hz\n");
- }
- if (c&0x10) {
- calc_mode_timings(640, 480, 67, &mode[num]);
- mode[num++].flag = FB_MODE_IS_CALCULATED;
- DPRINTK(" 640x480@67Hz\n");
- }
- if (c&0x08) {
- mode[num++] = vesa_modes[4];
- DPRINTK(" 640x480@72Hz\n");
- }
- if (c&0x04) {
- mode[num++] = vesa_modes[5];
- DPRINTK(" 640x480@75Hz\n");
- }
- if (c&0x02) {
- mode[num++] = vesa_modes[7];
- DPRINTK(" 800x600@56Hz\n");
- }
- if (c&0x01) {
- mode[num++] = vesa_modes[8];
- DPRINTK(" 800x600@60Hz\n");
- }
-
- c = block[1];
- if (c&0x80) {
- mode[num++] = vesa_modes[9];
- DPRINTK(" 800x600@72Hz\n");
- }
- if (c&0x40) {
- mode[num++] = vesa_modes[10];
- DPRINTK(" 800x600@75Hz\n");
- }
- if (c&0x20) {
- calc_mode_timings(832, 624, 75, &mode[num]);
- mode[num++].flag = FB_MODE_IS_CALCULATED;
- DPRINTK(" 832x624@75Hz\n");
- }
- if (c&0x10) {
- mode[num++] = vesa_modes[12];
- DPRINTK(" 1024x768@87Hz Interlaced\n");
- }
- if (c&0x08) {
- mode[num++] = vesa_modes[13];
- DPRINTK(" 1024x768@60Hz\n");
- }
- if (c&0x04) {
- mode[num++] = vesa_modes[14];
- DPRINTK(" 1024x768@70Hz\n");
- }
- if (c&0x02) {
- mode[num++] = vesa_modes[15];
- DPRINTK(" 1024x768@75Hz\n");
- }
- if (c&0x01) {
- mode[num++] = vesa_modes[21];
- DPRINTK(" 1280x1024@75Hz\n");
- }
- c = block[2];
- if (c&0x80) {
- mode[num++] = vesa_modes[17];
- DPRINTK(" 1152x870@75Hz\n");
- }
- DPRINTK(" Manufacturer's mask: %x\n",c&0x7F);
- return num;
-}
-
-static int get_std_timing(unsigned char *block, struct fb_videomode *mode,
- int ver, int rev)
-{
- int xres, yres = 0, refresh, ratio, i;
-
- xres = (block[0] + 31) * 8;
- if (xres <= 256)
- return 0;
-
- ratio = (block[1] & 0xc0) >> 6;
- switch (ratio) {
- case 0:
- /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */
- if (ver < 1 || (ver == 1 && rev < 3))
- yres = xres;
- else
- yres = (xres * 10)/16;
- break;
- case 1:
- yres = (xres * 3)/4;
- break;
- case 2:
- yres = (xres * 4)/5;
- break;
- case 3:
- yres = (xres * 9)/16;
- break;
- }
- refresh = (block[1] & 0x3f) + 60;
-
- DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh);
- for (i = 0; i < VESA_MODEDB_SIZE; i++) {
- if (vesa_modes[i].xres == xres &&
- vesa_modes[i].yres == yres &&
- vesa_modes[i].refresh == refresh) {
- *mode = vesa_modes[i];
- mode->flag |= FB_MODE_IS_STANDARD;
- return 1;
- }
- }
- calc_mode_timings(xres, yres, refresh, mode);
- return 1;
-}
-
-static int get_dst_timing(unsigned char *block,
- struct fb_videomode *mode, int ver, int rev)
-{
- int j, num = 0;
-
- for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
- num += get_std_timing(block, &mode[num], ver, rev);
-
- return num;
-}
-
-static void get_detailed_timing(unsigned char *block,
- struct fb_videomode *mode)
-{
- mode->xres = H_ACTIVE;
- mode->yres = V_ACTIVE;
- mode->pixclock = PIXEL_CLOCK;
- mode->pixclock /= 1000;
- mode->pixclock = KHZ2PICOS(mode->pixclock);
- mode->right_margin = H_SYNC_OFFSET;
- mode->left_margin = (H_ACTIVE + H_BLANKING) -
- (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
- mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
- V_SYNC_WIDTH;
- mode->lower_margin = V_SYNC_OFFSET;
- mode->hsync_len = H_SYNC_WIDTH;
- mode->vsync_len = V_SYNC_WIDTH;
- if (HSYNC_POSITIVE)
- mode->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (VSYNC_POSITIVE)
- mode->sync |= FB_SYNC_VERT_HIGH_ACT;
- mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
- (V_ACTIVE + V_BLANKING));
- if (INTERLACED) {
- mode->yres *= 2;
- mode->upper_margin *= 2;
- mode->lower_margin *= 2;
- mode->vsync_len *= 2;
- mode->vmode |= FB_VMODE_INTERLACED;
- }
- mode->flag = FB_MODE_IS_DETAILED;
-
- DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
- DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
- H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
- DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
- V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
- DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
- (VSYNC_POSITIVE) ? "+" : "-");
-}
-
-/**
- * fb_create_modedb - create video mode database
- * @edid: EDID data
- * @dbsize: database size
- *
- * RETURNS: struct fb_videomode, @dbsize contains length of database
- *
- * DESCRIPTION:
- * This function builds a mode database using the contents of the EDID
- * data
- */
-static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
-{
- struct fb_videomode *mode, *m;
- unsigned char *block;
- int num = 0, i, first = 1;
- int ver, rev;
-
- ver = edid[EDID_STRUCT_VERSION];
- rev = edid[EDID_STRUCT_REVISION];
-
- mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
- if (mode == NULL)
- return NULL;
-
- if (edid == NULL || !edid_checksum(edid) ||
- !edid_check_header(edid)) {
- kfree(mode);
- return NULL;
- }
-
- *dbsize = 0;
-
- DPRINTK(" Detailed Timings\n");
- block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
- for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
- if (!(block[0] == 0x00 && block[1] == 0x00)) {
- get_detailed_timing(block, &mode[num]);
- if (first) {
- mode[num].flag |= FB_MODE_IS_FIRST;
- first = 0;
- }
- num++;
- }
- }
-
- DPRINTK(" Supported VESA Modes\n");
- block = edid + ESTABLISHED_TIMING_1;
- num += get_est_timing(block, &mode[num]);
-
- DPRINTK(" Standard Timings\n");
- block = edid + STD_TIMING_DESCRIPTIONS_START;
- for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
- num += get_std_timing(block, &mode[num], ver, rev);
-
- block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
- for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
- if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
- num += get_dst_timing(block + 5, &mode[num], ver, rev);
- }
-
- /* Yikes, EDID data is totally useless */
- if (!num) {
- kfree(mode);
- return NULL;
- }
-
- *dbsize = num;
- m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL);
- if (!m)
- return mode;
- memmove(m, mode, num * sizeof(struct fb_videomode));
- kfree(mode);
- return m;
-}
-
-/**
- * fb_destroy_modedb - destroys mode database
- * @modedb: mode database to destroy
- *
- * DESCRIPTION:
- * Destroy mode database created by fb_create_modedb
- */
-void fb_destroy_modedb(struct fb_videomode *modedb)
-{
- kfree(modedb);
-}
-
-static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
-{
- int i, retval = 1;
- unsigned char *block;
-
- block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
-
- DPRINTK(" Monitor Operating Limits: ");
-
- for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
- if (edid_is_limits_block(block)) {
- specs->hfmin = H_MIN_RATE * 1000;
- specs->hfmax = H_MAX_RATE * 1000;
- specs->vfmin = V_MIN_RATE;
- specs->vfmax = V_MAX_RATE;
- specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
- specs->gtf = (GTF_SUPPORT) ? 1 : 0;
- retval = 0;
- DPRINTK("From EDID\n");
- break;
- }
- }
-
- /* estimate monitor limits based on modes supported */
- if (retval) {
- struct fb_videomode *modes, *mode;
- int num_modes, hz, hscan, pixclock;
- int vtotal, htotal;
-
- modes = fb_create_modedb(edid, &num_modes);
- if (!modes) {
- DPRINTK("None Available\n");
- return 1;
- }
-
- retval = 0;
- for (i = 0; i < num_modes; i++) {
- mode = &modes[i];
- pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
- htotal = mode->xres + mode->right_margin + mode->hsync_len
- + mode->left_margin;
- vtotal = mode->yres + mode->lower_margin + mode->vsync_len
- + mode->upper_margin;
-
- if (mode->vmode & FB_VMODE_INTERLACED)
- vtotal /= 2;
-
- if (mode->vmode & FB_VMODE_DOUBLE)
- vtotal *= 2;
-
- hscan = (pixclock + htotal / 2) / htotal;
- hscan = (hscan + 500) / 1000 * 1000;
- hz = (hscan + vtotal / 2) / vtotal;
-
- if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
- specs->dclkmax = pixclock;
-
- if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
- specs->dclkmin = pixclock;
-
- if (specs->hfmax == 0 || specs->hfmax < hscan)
- specs->hfmax = hscan;
-
- if (specs->hfmin == 0 || specs->hfmin > hscan)
- specs->hfmin = hscan;
-
- if (specs->vfmax == 0 || specs->vfmax < hz)
- specs->vfmax = hz;
-
- if (specs->vfmin == 0 || specs->vfmin > hz)
- specs->vfmin = hz;
- }
- DPRINTK("Extrapolated\n");
- fb_destroy_modedb(modes);
- }
- DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
- specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
- specs->vfmax, specs->dclkmax/1000000);
- return retval;
-}
-
-static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
- unsigned char c, *block;
-
- block = edid + EDID_STRUCT_DISPLAY;
-
- fb_get_monitor_limits(edid, specs);
-
- c = block[0] & 0x80;
- specs->input = 0;
- if (c) {
- specs->input |= FB_DISP_DDI;
- DPRINTK(" Digital Display Input");
- } else {
- DPRINTK(" Analog Display Input: Input Voltage - ");
- switch ((block[0] & 0x60) >> 5) {
- case 0:
- DPRINTK("0.700V/0.300V");
- specs->input |= FB_DISP_ANA_700_300;
- break;
- case 1:
- DPRINTK("0.714V/0.286V");
- specs->input |= FB_DISP_ANA_714_286;
- break;
- case 2:
- DPRINTK("1.000V/0.400V");
- specs->input |= FB_DISP_ANA_1000_400;
- break;
- case 3:
- DPRINTK("0.700V/0.000V");
- specs->input |= FB_DISP_ANA_700_000;
- break;
- }
- }
- DPRINTK("\n Sync: ");
- c = block[0] & 0x10;
- if (c)
- DPRINTK(" Configurable signal level\n");
- c = block[0] & 0x0f;
- specs->signal = 0;
- if (c & 0x10) {
- DPRINTK("Blank to Blank ");
- specs->signal |= FB_SIGNAL_BLANK_BLANK;
- }
- if (c & 0x08) {
- DPRINTK("Separate ");
- specs->signal |= FB_SIGNAL_SEPARATE;
- }
- if (c & 0x04) {
- DPRINTK("Composite ");
- specs->signal |= FB_SIGNAL_COMPOSITE;
- }
- if (c & 0x02) {
- DPRINTK("Sync on Green ");
- specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
- }
- if (c & 0x01) {
- DPRINTK("Serration on ");
- specs->signal |= FB_SIGNAL_SERRATION_ON;
- }
- DPRINTK("\n");
- specs->max_x = block[1];
- specs->max_y = block[2];
- DPRINTK(" Max H-size in cm: ");
- if (specs->max_x)
- DPRINTK("%d\n", specs->max_x);
- else
- DPRINTK("variable\n");
- DPRINTK(" Max V-size in cm: ");
- if (specs->max_y)
- DPRINTK("%d\n", specs->max_y);
- else
- DPRINTK("variable\n");
-
- c = block[3];
- specs->gamma = c+100;
- DPRINTK(" Gamma: ");
- DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
-
- get_dpms_capabilities(block[4], specs);
-
- switch ((block[4] & 0x18) >> 3) {
- case 0:
- DPRINTK(" Monochrome/Grayscale\n");
- specs->input |= FB_DISP_MONO;
- break;
- case 1:
- DPRINTK(" RGB Color Display\n");
- specs->input |= FB_DISP_RGB;
- break;
- case 2:
- DPRINTK(" Non-RGB Multicolor Display\n");
- specs->input |= FB_DISP_MULTI;
- break;
- default:
- DPRINTK(" Unknown\n");
- specs->input |= FB_DISP_UNKNOWN;
- break;
- }
-
- get_chroma(block, specs);
-
- specs->misc = 0;
- c = block[4] & 0x7;
- if (c & 0x04) {
- DPRINTK(" Default color format is primary\n");
- specs->misc |= FB_MISC_PRIM_COLOR;
- }
- if (c & 0x02) {
- DPRINTK(" First DETAILED Timing is preferred\n");
- specs->misc |= FB_MISC_1ST_DETAIL;
- }
- if (c & 0x01) {
- printk(" Display is GTF capable\n");
- specs->gtf = 1;
- }
-}
-
-int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
-{
- int i;
- unsigned char *block;
-
- if (edid == NULL || var == NULL)
- return 1;
-
- if (!(edid_checksum(edid)))
- return 1;
-
- if (!(edid_check_header(edid)))
- return 1;
-
- block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
-
- for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
- if (edid_is_timing_block(block)) {
- var->xres = var->xres_virtual = H_ACTIVE;
- var->yres = var->yres_virtual = V_ACTIVE;
- var->height = var->width = 0;
- var->right_margin = H_SYNC_OFFSET;
- var->left_margin = (H_ACTIVE + H_BLANKING) -
- (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
- var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
- V_SYNC_WIDTH;
- var->lower_margin = V_SYNC_OFFSET;
- var->hsync_len = H_SYNC_WIDTH;
- var->vsync_len = V_SYNC_WIDTH;
- var->pixclock = PIXEL_CLOCK;
- var->pixclock /= 1000;
- var->pixclock = KHZ2PICOS(var->pixclock);
-
- if (HSYNC_POSITIVE)
- var->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (VSYNC_POSITIVE)
- var->sync |= FB_SYNC_VERT_HIGH_ACT;
- return 0;
- }
- }
- return 1;
-}
-
-void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
- unsigned char *block;
- int i, found = 0;
-
- if (edid == NULL)
- return;
-
- if (!(edid_checksum(edid)))
- return;
-
- if (!(edid_check_header(edid)))
- return;
-
- memset(specs, 0, sizeof(struct fb_monspecs));
-
- specs->version = edid[EDID_STRUCT_VERSION];
- specs->revision = edid[EDID_STRUCT_REVISION];
-
- DPRINTK("========================================\n");
- DPRINTK("Display Information (EDID)\n");
- DPRINTK("========================================\n");
- DPRINTK(" EDID Version %d.%d\n", (int) specs->version,
- (int) specs->revision);
-
- parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
-
- block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
- for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
- if (edid_is_serial_block(block)) {
- copy_string(block, specs->serial_no);
- DPRINTK(" Serial Number: %s\n", specs->serial_no);
- } else if (edid_is_ascii_block(block)) {
- copy_string(block, specs->ascii);
- DPRINTK(" ASCII Block: %s\n", specs->ascii);
- } else if (edid_is_monitor_block(block)) {
- copy_string(block, specs->monitor);
- DPRINTK(" Monitor Name: %s\n", specs->monitor);
- }
- }
-
- DPRINTK(" Display Characteristics:\n");
- get_monspecs(edid, specs);
-
- specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
-
- /*
- * Workaround for buggy EDIDs that sets that the first
- * detailed timing is preferred but has not detailed
- * timing specified
- */
- for (i = 0; i < specs->modedb_len; i++) {
- if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) {
- found = 1;
- break;
- }
- }
-
- if (!found)
- specs->misc &= ~FB_MISC_1ST_DETAIL;
-
- DPRINTK("========================================\n");
-}
-
-/**
- * fb_edid_add_monspecs() - add monitor video modes from E-EDID data
- * @edid: 128 byte array with an E-EDID block
- * @spacs: monitor specs to be extended
- */
-void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
- unsigned char *block;
- struct fb_videomode *m;
- int num = 0, i;
- u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE];
- u8 pos = 4, svd_n = 0;
-
- if (!edid)
- return;
-
- if (!edid_checksum(edid))
- return;
-
- if (edid[0] != 0x2 ||
- edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE)
- return;
-
- DPRINTK(" Short Video Descriptors\n");
-
- while (pos < edid[2]) {
- u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
- pr_debug("Data block %u of %u bytes\n", type, len);
- if (type == 2)
- for (i = pos; i < pos + len; i++) {
- u8 idx = edid[pos + i] & 0x7f;
- svd[svd_n++] = idx;
- pr_debug("N%sative mode #%d\n",
- edid[pos + i] & 0x80 ? "" : "on-n", idx);
- }
- pos += len + 1;
- }
-
- block = edid + edid[2];
-
- DPRINTK(" Extended Detailed Timings\n");
-
- for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE;
- i++, block += DETAILED_TIMING_DESCRIPTION_SIZE)
- if (PIXEL_CLOCK)
- edt[num++] = block - edid;
-
- /* Yikes, EDID data is totally useless */
- if (!(num + svd_n))
- return;
-
- m = kzalloc((specs->modedb_len + num + svd_n) *
- sizeof(struct fb_videomode), GFP_KERNEL);
-
- if (!m)
- return;
-
- memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode));
-
- for (i = specs->modedb_len; i < specs->modedb_len + num; i++) {
- get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]);
- if (i == specs->modedb_len)
- m[i].flag |= FB_MODE_IS_FIRST;
- pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh);
- }
-
- for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) {
- int idx = svd[i - specs->modedb_len - num];
- if (!idx || idx > 63) {
- pr_warning("Reserved SVD code %d\n", idx);
- } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) {
- pr_warning("Unimplemented SVD code %d\n", idx);
- } else {
- memcpy(&m[i], cea_modes + idx, sizeof(m[i]));
- pr_debug("Adding SVD #%d: %ux%u@%u\n", idx,
- m[i].xres, m[i].yres, m[i].refresh);
- }
- }
-
- kfree(specs->modedb);
- specs->modedb = m;
- specs->modedb_len = specs->modedb_len + num + svd_n;
-}
-
-/*
- * VESA Generalized Timing Formula (GTF)
- */
-
-#define FLYBACK 550
-#define V_FRONTPORCH 1
-#define H_OFFSET 40
-#define H_SCALEFACTOR 20
-#define H_BLANKSCALE 128
-#define H_GRADIENT 600
-#define C_VAL 30
-#define M_VAL 300
-
-struct __fb_timings {
- u32 dclk;
- u32 hfreq;
- u32 vfreq;
- u32 hactive;
- u32 vactive;
- u32 hblank;
- u32 vblank;
- u32 htotal;
- u32 vtotal;
-};
-
-/**
- * fb_get_vblank - get vertical blank time
- * @hfreq: horizontal freq
- *
- * DESCRIPTION:
- * vblank = right_margin + vsync_len + left_margin
- *
- * given: right_margin = 1 (V_FRONTPORCH)
- * vsync_len = 3
- * flyback = 550
- *
- * flyback * hfreq
- * left_margin = --------------- - vsync_len
- * 1000000
- */
-static u32 fb_get_vblank(u32 hfreq)
-{
- u32 vblank;
-
- vblank = (hfreq * FLYBACK)/1000;
- vblank = (vblank + 500)/1000;
- return (vblank + V_FRONTPORCH);
-}
-
-/**
- * fb_get_hblank_by_freq - get horizontal blank time given hfreq
- * @hfreq: horizontal freq
- * @xres: horizontal resolution in pixels
- *
- * DESCRIPTION:
- *
- * xres * duty_cycle
- * hblank = ------------------
- * 100 - duty_cycle
- *
- * duty cycle = percent of htotal assigned to inactive display
- * duty cycle = C - (M/Hfreq)
- *
- * where: C = ((offset - scale factor) * blank_scale)
- * -------------------------------------- + scale factor
- * 256
- * M = blank_scale * gradient
- *
- */
-static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
-{
- u32 c_val, m_val, duty_cycle, hblank;
-
- c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
- H_SCALEFACTOR) * 1000;
- m_val = (H_BLANKSCALE * H_GRADIENT)/256;
- m_val = (m_val * 1000000)/hfreq;
- duty_cycle = c_val - m_val;
- hblank = (xres * duty_cycle)/(100000 - duty_cycle);
- return (hblank);
-}
-
-/**
- * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock
- * @dclk: pixelclock in Hz
- * @xres: horizontal resolution in pixels
- *
- * DESCRIPTION:
- *
- * xres * duty_cycle
- * hblank = ------------------
- * 100 - duty_cycle
- *
- * duty cycle = percent of htotal assigned to inactive display
- * duty cycle = C - (M * h_period)
- *
- * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100
- * -----------------------------------------------
- * 2 * M
- * M = 300;
- * C = 30;
-
- */
-static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres)
-{
- u32 duty_cycle, h_period, hblank;
-
- dclk /= 1000;
- h_period = 100 - C_VAL;
- h_period *= h_period;
- h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk);
- h_period *= 10000;
-
- h_period = int_sqrt(h_period);
- h_period -= (100 - C_VAL) * 100;
- h_period *= 1000;
- h_period /= 2 * M_VAL;
-
- duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100;
- hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8;
- hblank &= ~15;
- return (hblank);
-}
-
-/**
- * fb_get_hfreq - estimate hsync
- * @vfreq: vertical refresh rate
- * @yres: vertical resolution
- *
- * DESCRIPTION:
- *
- * (yres + front_port) * vfreq * 1000000
- * hfreq = -------------------------------------
- * (1000000 - (vfreq * FLYBACK)
- *
- */
-
-static u32 fb_get_hfreq(u32 vfreq, u32 yres)
-{
- u32 divisor, hfreq;
-
- divisor = (1000000 - (vfreq * FLYBACK))/1000;
- hfreq = (yres + V_FRONTPORCH) * vfreq * 1000;
- return (hfreq/divisor);
-}
-
-static void fb_timings_vfreq(struct __fb_timings *timings)
-{
- timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
- timings->vblank = fb_get_vblank(timings->hfreq);
- timings->vtotal = timings->vactive + timings->vblank;
- timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
- timings->hactive);
- timings->htotal = timings->hactive + timings->hblank;
- timings->dclk = timings->htotal * timings->hfreq;
-}
-
-static void fb_timings_hfreq(struct __fb_timings *timings)
-{
- timings->vblank = fb_get_vblank(timings->hfreq);
- timings->vtotal = timings->vactive + timings->vblank;
- timings->vfreq = timings->hfreq/timings->vtotal;
- timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
- timings->hactive);
- timings->htotal = timings->hactive + timings->hblank;
- timings->dclk = timings->htotal * timings->hfreq;
-}
-
-static void fb_timings_dclk(struct __fb_timings *timings)
-{
- timings->hblank = fb_get_hblank_by_dclk(timings->dclk,
- timings->hactive);
- timings->htotal = timings->hactive + timings->hblank;
- timings->hfreq = timings->dclk/timings->htotal;
- timings->vblank = fb_get_vblank(timings->hfreq);
- timings->vtotal = timings->vactive + timings->vblank;
- timings->vfreq = timings->hfreq/timings->vtotal;
-}
-
-/*
- * fb_get_mode - calculates video mode using VESA GTF
- * @flags: if: 0 - maximize vertical refresh rate
- * 1 - vrefresh-driven calculation;
- * 2 - hscan-driven calculation;
- * 3 - pixelclock-driven calculation;
- * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
- * @var: pointer to fb_var_screeninfo
- * @info: pointer to fb_info
- *
- * DESCRIPTION:
- * Calculates video mode based on monitor specs using VESA GTF.
- * The GTF is best for VESA GTF compliant monitors but is
- * specifically formulated to work for older monitors as well.
- *
- * If @flag==0, the function will attempt to maximize the
- * refresh rate. Otherwise, it will calculate timings based on
- * the flag and accompanying value.
- *
- * If FB_IGNOREMON bit is set in @flags, monitor specs will be
- * ignored and @var will be filled with the calculated timings.
- *
- * All calculations are based on the VESA GTF Spreadsheet
- * available at VESA's public ftp (http://www.vesa.org).
- *
- * NOTES:
- * The timings generated by the GTF will be different from VESA
- * DMT. It might be a good idea to keep a table of standard
- * VESA modes as well. The GTF may also not work for some displays,
- * such as, and especially, analog TV.
- *
- * REQUIRES:
- * A valid info->monspecs, otherwise 'safe numbers' will be used.
- */
-int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
-{
- struct __fb_timings *timings;
- u32 interlace = 1, dscan = 1;
- u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
-
-
- timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL);
-
- if (!timings)
- return -ENOMEM;
-
- /*
- * If monspecs are invalid, use values that are enough
- * for 640x480@60
- */
- if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax ||
- !info->monspecs.dclkmax ||
- info->monspecs.hfmax < info->monspecs.hfmin ||
- info->monspecs.vfmax < info->monspecs.vfmin ||
- info->monspecs.dclkmax < info->monspecs.dclkmin) {
- hfmin = 29000; hfmax = 30000;
- vfmin = 60; vfmax = 60;
- dclkmin = 0; dclkmax = 25000000;
- } else {
- hfmin = info->monspecs.hfmin;
- hfmax = info->monspecs.hfmax;
- vfmin = info->monspecs.vfmin;
- vfmax = info->monspecs.vfmax;
- dclkmin = info->monspecs.dclkmin;
- dclkmax = info->monspecs.dclkmax;
- }
-
- timings->hactive = var->xres;
- timings->vactive = var->yres;
- if (var->vmode & FB_VMODE_INTERLACED) {
- timings->vactive /= 2;
- interlace = 2;
- }
- if (var->vmode & FB_VMODE_DOUBLE) {
- timings->vactive *= 2;
- dscan = 2;
- }
-
- switch (flags & ~FB_IGNOREMON) {
- case FB_MAXTIMINGS: /* maximize refresh rate */
- timings->hfreq = hfmax;
- fb_timings_hfreq(timings);
- if (timings->vfreq > vfmax) {
- timings->vfreq = vfmax;
- fb_timings_vfreq(timings);
- }
- if (timings->dclk > dclkmax) {
- timings->dclk = dclkmax;
- fb_timings_dclk(timings);
- }
- break;
- case FB_VSYNCTIMINGS: /* vrefresh driven */
- timings->vfreq = val;
- fb_timings_vfreq(timings);
- break;
- case FB_HSYNCTIMINGS: /* hsync driven */
- timings->hfreq = val;
- fb_timings_hfreq(timings);
- break;
- case FB_DCLKTIMINGS: /* pixelclock driven */
- timings->dclk = PICOS2KHZ(val) * 1000;
- fb_timings_dclk(timings);
- break;
- default:
- err = -EINVAL;
-
- }
-
- if (err || (!(flags & FB_IGNOREMON) &&
- (timings->vfreq < vfmin || timings->vfreq > vfmax ||
- timings->hfreq < hfmin || timings->hfreq > hfmax ||
- timings->dclk < dclkmin || timings->dclk > dclkmax))) {
- err = -EINVAL;
- } else {
- var->pixclock = KHZ2PICOS(timings->dclk/1000);
- var->hsync_len = (timings->htotal * 8)/100;
- var->right_margin = (timings->hblank/2) - var->hsync_len;
- var->left_margin = timings->hblank - var->right_margin -
- var->hsync_len;
- var->vsync_len = (3 * interlace)/dscan;
- var->lower_margin = (1 * interlace)/dscan;
- var->upper_margin = (timings->vblank * interlace)/dscan -
- (var->vsync_len + var->lower_margin);
- }
-
- kfree(timings);
- return err;
-}
-
-#ifdef CONFIG_VIDEOMODE_HELPERS
-int fb_videomode_from_videomode(const struct videomode *vm,
- struct fb_videomode *fbmode)
-{
- unsigned int htotal, vtotal;
-
- fbmode->xres = vm->hactive;
- fbmode->left_margin = vm->hback_porch;
- fbmode->right_margin = vm->hfront_porch;
- fbmode->hsync_len = vm->hsync_len;
-
- fbmode->yres = vm->vactive;
- fbmode->upper_margin = vm->vback_porch;
- fbmode->lower_margin = vm->vfront_porch;
- fbmode->vsync_len = vm->vsync_len;
-
- /* prevent division by zero in KHZ2PICOS macro */
- fbmode->pixclock = vm->pixelclock ?
- KHZ2PICOS(vm->pixelclock / 1000) : 0;
-
- fbmode->sync = 0;
- fbmode->vmode = 0;
- if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
- fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
- fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
- if (vm->flags & DISPLAY_FLAGS_INTERLACED)
- fbmode->vmode |= FB_VMODE_INTERLACED;
- if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN)
- fbmode->vmode |= FB_VMODE_DOUBLE;
- fbmode->flag = 0;
-
- htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
- vm->hsync_len;
- vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
- vm->vsync_len;
- /* prevent division by zero */
- if (htotal && vtotal) {
- fbmode->refresh = vm->pixelclock / (htotal * vtotal);
- /* a mode must have htotal and vtotal != 0 or it is invalid */
- } else {
- fbmode->refresh = 0;
- return -EINVAL;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
-
-#ifdef CONFIG_OF
-static inline void dump_fb_videomode(const struct fb_videomode *m)
-{
- pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
- m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
- m->right_margin, m->upper_margin, m->lower_margin,
- m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
-}
-
-/**
- * of_get_fb_videomode - get a fb_videomode from devicetree
- * @np: device_node with the timing specification
- * @fb: will be set to the return value
- * @index: index into the list of display timings in devicetree
- *
- * DESCRIPTION:
- * This function is expensive and should only be used, if only one mode is to be
- * read from DT. To get multiple modes start with of_get_display_timings ond
- * work with that instead.
- */
-int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
- int index)
-{
- struct videomode vm;
- int ret;
-
- ret = of_get_videomode(np, &vm, index);
- if (ret)
- return ret;
-
- fb_videomode_from_videomode(&vm, fb);
-
- pr_debug("%s: got %dx%d display mode from %s\n",
- of_node_full_name(np), vm.hactive, vm.vactive, np->name);
- dump_fb_videomode(fb);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_get_fb_videomode);
-#endif /* CONFIG_OF */
-#endif /* CONFIG_VIDEOMODE_HELPERS */
-
-#else
-int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
-{
- return 1;
-}
-void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
- specs = NULL;
-}
-void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
-{
-}
-void fb_destroy_modedb(struct fb_videomode *modedb)
-{
-}
-int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- return -EINVAL;
-}
-#endif /* CONFIG_FB_MODE_HELPERS */
-
-/*
- * fb_validate_mode - validates var against monitor capabilities
- * @var: pointer to fb_var_screeninfo
- * @info: pointer to fb_info
- *
- * DESCRIPTION:
- * Validates video mode against monitor capabilities specified in
- * info->monspecs.
- *
- * REQUIRES:
- * A valid info->monspecs.
- */
-int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
-{
- u32 hfreq, vfreq, htotal, vtotal, pixclock;
- u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
-
- /*
- * If monspecs are invalid, use values that are enough
- * for 640x480@60
- */
- if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
- !info->monspecs.dclkmax ||
- info->monspecs.hfmax < info->monspecs.hfmin ||
- info->monspecs.vfmax < info->monspecs.vfmin ||
- info->monspecs.dclkmax < info->monspecs.dclkmin) {
- hfmin = 29000; hfmax = 30000;
- vfmin = 60; vfmax = 60;
- dclkmin = 0; dclkmax = 25000000;
- } else {
- hfmin = info->monspecs.hfmin;
- hfmax = info->monspecs.hfmax;
- vfmin = info->monspecs.vfmin;
- vfmax = info->monspecs.vfmax;
- dclkmin = info->monspecs.dclkmin;
- dclkmax = info->monspecs.dclkmax;
- }
-
- if (!var->pixclock)
- return -EINVAL;
- pixclock = PICOS2KHZ(var->pixclock) * 1000;
-
- htotal = var->xres + var->right_margin + var->hsync_len +
- var->left_margin;
- vtotal = var->yres + var->lower_margin + var->vsync_len +
- var->upper_margin;
-
- if (var->vmode & FB_VMODE_INTERLACED)
- vtotal /= 2;
- if (var->vmode & FB_VMODE_DOUBLE)
- vtotal *= 2;
-
- hfreq = pixclock/htotal;
- hfreq = (hfreq + 500) / 1000 * 1000;
-
- vfreq = hfreq/vtotal;
-
- return (vfreq < vfmin || vfreq > vfmax ||
- hfreq < hfmin || hfreq > hfmax ||
- pixclock < dclkmin || pixclock > dclkmax) ?
- -EINVAL : 0;
-}
-
-#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
-
-/*
- * We need to ensure that the EDID block is only returned for
- * the primary graphics adapter.
- */
-
-const unsigned char *fb_firmware_edid(struct device *device)
-{
- struct pci_dev *dev = NULL;
- struct resource *res = NULL;
- unsigned char *edid = NULL;
-
- if (device)
- dev = to_pci_dev(device);
-
- if (dev)
- res = &dev->resource[PCI_ROM_RESOURCE];
-
- if (res && res->flags & IORESOURCE_ROM_SHADOW)
- edid = edid_info.dummy;
-
- return edid;
-}
-#else
-const unsigned char *fb_firmware_edid(struct device *device)
-{
- return NULL;
-}
-#endif
-EXPORT_SYMBOL(fb_firmware_edid);
-
-EXPORT_SYMBOL(fb_parse_edid);
-EXPORT_SYMBOL(fb_edid_to_monspecs);
-EXPORT_SYMBOL(fb_edid_add_monspecs);
-EXPORT_SYMBOL(fb_get_mode);
-EXPORT_SYMBOL(fb_validate_mode);
-EXPORT_SYMBOL(fb_destroy_modedb);
+++ /dev/null
-/*
- * fbsysfs.c - framebuffer device class and attributes
- *
- * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
- *
- * This program is free software you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-/*
- * Note: currently there's only stubs for framebuffer_alloc and
- * framebuffer_release here. The reson for that is that until all drivers
- * are converted to use it a sysfsification will open OOPSable races.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-#include <linux/console.h>
-#include <linux/module.h>
-
-#define FB_SYSFS_FLAG_ATTR 1
-
-/**
- * framebuffer_alloc - creates a new frame buffer info structure
- *
- * @size: size of driver private data, can be zero
- * @dev: pointer to the device for this fb, this can be NULL
- *
- * Creates a new frame buffer info structure. Also reserves @size bytes
- * for driver private data (info->par). info->par (if any) will be
- * aligned to sizeof(long).
- *
- * Returns the new structure, or NULL if an error occurred.
- *
- */
-struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
-{
-#define BYTES_PER_LONG (BITS_PER_LONG/8)
-#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
- int fb_info_size = sizeof(struct fb_info);
- struct fb_info *info;
- char *p;
-
- if (size)
- fb_info_size += PADDING;
-
- p = kzalloc(fb_info_size + size, GFP_KERNEL);
-
- if (!p)
- return NULL;
-
- info = (struct fb_info *) p;
-
- if (size)
- info->par = p + fb_info_size;
-
- info->device = dev;
-
-#ifdef CONFIG_FB_BACKLIGHT
- mutex_init(&info->bl_curve_mutex);
-#endif
-
- return info;
-#undef PADDING
-#undef BYTES_PER_LONG
-}
-EXPORT_SYMBOL(framebuffer_alloc);
-
-/**
- * framebuffer_release - marks the structure available for freeing
- *
- * @info: frame buffer info structure
- *
- * Drop the reference count of the device embedded in the
- * framebuffer info structure.
- *
- */
-void framebuffer_release(struct fb_info *info)
-{
- if (!info)
- return;
- kfree(info->apertures);
- kfree(info);
-}
-EXPORT_SYMBOL(framebuffer_release);
-
-static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
-{
- int err;
-
- var->activate |= FB_ACTIVATE_FORCE;
- console_lock();
- fb_info->flags |= FBINFO_MISC_USEREVENT;
- err = fb_set_var(fb_info, var);
- fb_info->flags &= ~FBINFO_MISC_USEREVENT;
- console_unlock();
- if (err)
- return err;
- return 0;
-}
-
-static int mode_string(char *buf, unsigned int offset,
- const struct fb_videomode *mode)
-{
- char m = 'U';
- char v = 'p';
-
- if (mode->flag & FB_MODE_IS_DETAILED)
- m = 'D';
- if (mode->flag & FB_MODE_IS_VESA)
- m = 'V';
- if (mode->flag & FB_MODE_IS_STANDARD)
- m = 'S';
-
- if (mode->vmode & FB_VMODE_INTERLACED)
- v = 'i';
- if (mode->vmode & FB_VMODE_DOUBLE)
- v = 'd';
-
- return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
- m, mode->xres, mode->yres, v, mode->refresh);
-}
-
-static ssize_t store_mode(struct device *device, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- char mstr[100];
- struct fb_var_screeninfo var;
- struct fb_modelist *modelist;
- struct fb_videomode *mode;
- struct list_head *pos;
- size_t i;
- int err;
-
- memset(&var, 0, sizeof(var));
-
- list_for_each(pos, &fb_info->modelist) {
- modelist = list_entry(pos, struct fb_modelist, list);
- mode = &modelist->mode;
- i = mode_string(mstr, 0, mode);
- if (strncmp(mstr, buf, max(count, i)) == 0) {
-
- var = fb_info->var;
- fb_videomode_to_var(&var, mode);
- if ((err = activate(fb_info, &var)))
- return err;
- fb_info->mode = mode;
- return count;
- }
- }
- return -EINVAL;
-}
-
-static ssize_t show_mode(struct device *device, struct device_attribute *attr,
- char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
-
- if (!fb_info->mode)
- return 0;
-
- return mode_string(buf, 0, fb_info->mode);
-}
-
-static ssize_t store_modes(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- LIST_HEAD(old_list);
- int i = count / sizeof(struct fb_videomode);
-
- if (i * sizeof(struct fb_videomode) != count)
- return -EINVAL;
-
- console_lock();
- if (!lock_fb_info(fb_info)) {
- console_unlock();
- return -ENODEV;
- }
-
- list_splice(&fb_info->modelist, &old_list);
- fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
- &fb_info->modelist);
- if (fb_new_modelist(fb_info)) {
- fb_destroy_modelist(&fb_info->modelist);
- list_splice(&old_list, &fb_info->modelist);
- } else
- fb_destroy_modelist(&old_list);
-
- unlock_fb_info(fb_info);
- console_unlock();
-
- return 0;
-}
-
-static ssize_t show_modes(struct device *device, struct device_attribute *attr,
- char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- unsigned int i;
- struct list_head *pos;
- struct fb_modelist *modelist;
- const struct fb_videomode *mode;
-
- i = 0;
- list_for_each(pos, &fb_info->modelist) {
- modelist = list_entry(pos, struct fb_modelist, list);
- mode = &modelist->mode;
- i += mode_string(buf, i, mode);
- }
- return i;
-}
-
-static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- struct fb_var_screeninfo var;
- char ** last = NULL;
- int err;
-
- var = fb_info->var;
- var.bits_per_pixel = simple_strtoul(buf, last, 0);
- if ((err = activate(fb_info, &var)))
- return err;
- return count;
-}
-
-static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
- char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
-}
-
-static ssize_t store_rotate(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- struct fb_var_screeninfo var;
- char **last = NULL;
- int err;
-
- var = fb_info->var;
- var.rotate = simple_strtoul(buf, last, 0);
-
- if ((err = activate(fb_info, &var)))
- return err;
-
- return count;
-}
-
-
-static ssize_t show_rotate(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
-}
-
-static ssize_t store_virtual(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- struct fb_var_screeninfo var;
- char *last = NULL;
- int err;
-
- var = fb_info->var;
- var.xres_virtual = simple_strtoul(buf, &last, 0);
- last++;
- if (last - buf >= count)
- return -EINVAL;
- var.yres_virtual = simple_strtoul(last, &last, 0);
-
- if ((err = activate(fb_info, &var)))
- return err;
- return count;
-}
-
-static ssize_t show_virtual(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
- fb_info->var.yres_virtual);
-}
-
-static ssize_t show_stride(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
-}
-
-static ssize_t store_blank(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- char *last = NULL;
- int err;
-
- console_lock();
- fb_info->flags |= FBINFO_MISC_USEREVENT;
- err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
- fb_info->flags &= ~FBINFO_MISC_USEREVENT;
- console_unlock();
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t show_blank(struct device *device,
- struct device_attribute *attr, char *buf)
-{
-// struct fb_info *fb_info = dev_get_drvdata(device);
- return 0;
-}
-
-static ssize_t store_console(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
-// struct fb_info *fb_info = dev_get_drvdata(device);
- return 0;
-}
-
-static ssize_t show_console(struct device *device,
- struct device_attribute *attr, char *buf)
-{
-// struct fb_info *fb_info = dev_get_drvdata(device);
- return 0;
-}
-
-static ssize_t store_cursor(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
-// struct fb_info *fb_info = dev_get_drvdata(device);
- return 0;
-}
-
-static ssize_t show_cursor(struct device *device,
- struct device_attribute *attr, char *buf)
-{
-// struct fb_info *fb_info = dev_get_drvdata(device);
- return 0;
-}
-
-static ssize_t store_pan(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- struct fb_var_screeninfo var;
- char *last = NULL;
- int err;
-
- var = fb_info->var;
- var.xoffset = simple_strtoul(buf, &last, 0);
- last++;
- if (last - buf >= count)
- return -EINVAL;
- var.yoffset = simple_strtoul(last, &last, 0);
-
- console_lock();
- err = fb_pan_display(fb_info, &var);
- console_unlock();
-
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t show_pan(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
- fb_info->var.yoffset);
-}
-
-static ssize_t show_name(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
-
- return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
-}
-
-static ssize_t store_fbstate(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- u32 state;
- char *last = NULL;
-
- state = simple_strtoul(buf, &last, 0);
-
- console_lock();
- if (!lock_fb_info(fb_info)) {
- console_unlock();
- return -ENODEV;
- }
-
- fb_set_suspend(fb_info, (int)state);
-
- unlock_fb_info(fb_info);
- console_unlock();
-
- return count;
-}
-
-static ssize_t show_fbstate(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
-}
-
-#ifdef CONFIG_FB_BACKLIGHT
-static ssize_t store_bl_curve(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- u8 tmp_curve[FB_BACKLIGHT_LEVELS];
- unsigned int i;
-
- /* Some drivers don't use framebuffer_alloc(), but those also
- * don't have backlights.
- */
- if (!fb_info || !fb_info->bl_dev)
- return -ENODEV;
-
- if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
- return -EINVAL;
-
- for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
- if (sscanf(&buf[i * 24],
- "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
- &tmp_curve[i * 8 + 0],
- &tmp_curve[i * 8 + 1],
- &tmp_curve[i * 8 + 2],
- &tmp_curve[i * 8 + 3],
- &tmp_curve[i * 8 + 4],
- &tmp_curve[i * 8 + 5],
- &tmp_curve[i * 8 + 6],
- &tmp_curve[i * 8 + 7]) != 8)
- return -EINVAL;
-
- /* If there has been an error in the input data, we won't
- * reach this loop.
- */
- mutex_lock(&fb_info->bl_curve_mutex);
- for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
- fb_info->bl_curve[i] = tmp_curve[i];
- mutex_unlock(&fb_info->bl_curve_mutex);
-
- return count;
-}
-
-static ssize_t show_bl_curve(struct device *device,
- struct device_attribute *attr, char *buf)
-{
- struct fb_info *fb_info = dev_get_drvdata(device);
- ssize_t len = 0;
- unsigned int i;
-
- /* Some drivers don't use framebuffer_alloc(), but those also
- * don't have backlights.
- */
- if (!fb_info || !fb_info->bl_dev)
- return -ENODEV;
-
- mutex_lock(&fb_info->bl_curve_mutex);
- for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
- len += snprintf(&buf[len], PAGE_SIZE,
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- fb_info->bl_curve[i + 0],
- fb_info->bl_curve[i + 1],
- fb_info->bl_curve[i + 2],
- fb_info->bl_curve[i + 3],
- fb_info->bl_curve[i + 4],
- fb_info->bl_curve[i + 5],
- fb_info->bl_curve[i + 6],
- fb_info->bl_curve[i + 7]);
- mutex_unlock(&fb_info->bl_curve_mutex);
-
- return len;
-}
-#endif
-
-/* When cmap is added back in it should be a binary attribute
- * not a text one. Consideration should also be given to converting
- * fbdev to use configfs instead of sysfs */
-static struct device_attribute device_attrs[] = {
- __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
- __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
- __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
- __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
- __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
- __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
- __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
- __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(stride, S_IRUGO, show_stride, NULL),
- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
-#ifdef CONFIG_FB_BACKLIGHT
- __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
-#endif
-};
-
-int fb_init_device(struct fb_info *fb_info)
-{
- int i, error = 0;
-
- dev_set_drvdata(fb_info->dev, fb_info);
-
- fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
-
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(fb_info->dev, &device_attrs[i]);
-
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(fb_info->dev, &device_attrs[i]);
- fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
- }
-
- return 0;
-}
-
-void fb_cleanup_device(struct fb_info *fb_info)
-{
- unsigned int i;
-
- if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(fb_info->dev, &device_attrs[i]);
-
- fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
- }
-}
-
-#ifdef CONFIG_FB_BACKLIGHT
-/* This function generates a linear backlight curve
- *
- * 0: off
- * 1-7: min
- * 8-127: linear from min to max
- */
-void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
-{
- unsigned int i, flat, count, range = (max - min);
-
- mutex_lock(&fb_info->bl_curve_mutex);
-
- fb_info->bl_curve[0] = off;
-
- for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
- fb_info->bl_curve[flat] = min;
-
- count = FB_BACKLIGHT_LEVELS * 15 / 16;
- for (i = 0; i < count; ++i)
- fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
-
- mutex_unlock(&fb_info->bl_curve_mutex);
-}
-EXPORT_SYMBOL_GPL(fb_bl_default_curve);
-#endif
+++ /dev/null
-/*
- * linux/drivers/video/modedb.c -- Standard video mode database management
- *
- * Copyright (C) 1999 Geert Uytterhoeven
- *
- * 2001 - Documented with DocBook
- * - Brad Douglas <brad@neruo.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-#include <linux/kernel.h>
-
-#undef DEBUG
-
-#define name_matches(v, s, l) \
- ((v).name && !strncmp((s), (v).name, (l)) && strlen((v).name) == (l))
-#define res_matches(v, x, y) \
- ((v).xres == (x) && (v).yres == (y))
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk("modedb %s: " fmt, __func__ , ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-const char *fb_mode_option;
-EXPORT_SYMBOL_GPL(fb_mode_option);
-
-/*
- * Standard video mode definitions (taken from XFree86)
- */
-
-static const struct fb_videomode modedb[] = {
-
- /* 640x400 @ 70 Hz, 31.5 kHz hsync */
- { NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 640x480 @ 60 Hz, 31.5 kHz hsync */
- { NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 800x600 @ 56 Hz, 35.15 kHz hsync */
- { NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
- { NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8, 0,
- FB_VMODE_INTERLACED },
-
- /* 640x400 @ 85 Hz, 37.86 kHz hsync */
- { NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED },
-
- /* 640x480 @ 72 Hz, 36.5 kHz hsync */
- { NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 640x480 @ 75 Hz, 37.50 kHz hsync */
- { NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 800x600 @ 60 Hz, 37.8 kHz hsync */
- { NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 640x480 @ 85 Hz, 43.27 kHz hsync */
- { NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
- { NULL, 89, 1152, 864, 15384, 96, 16, 110, 1, 216, 10, 0,
- FB_VMODE_INTERLACED },
- /* 800x600 @ 72 Hz, 48.0 kHz hsync */
- { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 60 Hz, 48.4 kHz hsync */
- { NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 640x480 @ 100 Hz, 53.01 kHz hsync */
- { NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 60 Hz, 53.5 kHz hsync */
- { NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 800x600 @ 85 Hz, 55.84 kHz hsync */
- { NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 70 Hz, 56.5 kHz hsync */
- { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
- { NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12, 0,
- FB_VMODE_INTERLACED },
-
- /* 800x600 @ 100 Hz, 64.02 kHz hsync */
- { NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 76 Hz, 62.5 kHz hsync */
- { NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 70 Hz, 62.4 kHz hsync */
- { NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
- { NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1400x1050 @ 60Hz, 63.9 kHz hsync */
- { NULL, 60, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
- { NULL, 75, 1400, 1050, 7190, 120, 56, 23, 10, 112, 13,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
- { NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 85 Hz, 70.24 kHz hsync */
- { NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 78 Hz, 70.8 kHz hsync */
- { NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
- { NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1600x1200 @ 60Hz, 75.00 kHz hsync */
- { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 84 Hz, 76.0 kHz hsync */
- { NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
- { NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1024x768 @ 100Hz, 80.21 kHz hsync */
- { NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
- { NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
- { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x864 @ 100 Hz, 89.62 kHz hsync */
- { NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
- { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
- { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1680x1050 @ 60 Hz, 65.191 kHz hsync */
- { NULL, 60, 1680, 1050, 6848, 280, 104, 30, 3, 176, 6,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
- { NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
- { NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1800x1440 @ 64Hz, 96.15 kHz hsync */
- { NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1800x1440 @ 70Hz, 104.52 kHz hsync */
- { NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 512x384 @ 78 Hz, 31.50 kHz hsync */
- { NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 512x384 @ 85 Hz, 34.38 kHz hsync */
- { NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
- { NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0,
- FB_VMODE_DOUBLE },
-
- /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
- { NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1, 0,
- FB_VMODE_DOUBLE },
-
- /* 320x240 @ 72 Hz, 36.5 kHz hsync */
- { NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0,
- FB_VMODE_DOUBLE },
-
- /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
- { NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1, 0,
- FB_VMODE_DOUBLE },
-
- /* 400x300 @ 60 Hz, 37.8 kHz hsync */
- { NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2, 0,
- FB_VMODE_DOUBLE },
-
- /* 400x300 @ 72 Hz, 48.0 kHz hsync */
- { NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3, 0,
- FB_VMODE_DOUBLE },
-
- /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
- { NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1, 0,
- FB_VMODE_DOUBLE },
-
- /* 480x300 @ 60 Hz, 37.8 kHz hsync */
- { NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2, 0,
- FB_VMODE_DOUBLE },
-
- /* 480x300 @ 63 Hz, 39.6 kHz hsync */
- { NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2, 0,
- FB_VMODE_DOUBLE },
-
- /* 480x300 @ 72 Hz, 48.0 kHz hsync */
- { NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3, 0,
- FB_VMODE_DOUBLE },
-
- /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */
- { NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1152x768, 60 Hz, PowerBook G4 Titanium I and II */
- { NULL, 60, 1152, 768, 14047, 158, 26, 29, 3, 136, 6,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED },
-
- /* 1366x768, 60 Hz, 47.403 kHz hsync, WXGA 16:9 aspect ratio */
- { NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 1280x800, 60 Hz, 47.403 kHz hsync, WXGA 16:10 aspect ratio */
- { NULL, 60, 1280, 800, 12048, 200, 64, 24, 1, 136, 3, 0,
- FB_VMODE_NONINTERLACED },
-
- /* 720x576i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
- { NULL, 50, 720, 576, 74074, 64, 16, 39, 5, 64, 5, 0,
- FB_VMODE_INTERLACED },
-
- /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */
- { NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, 0,
- FB_VMODE_INTERLACED },
-
- /* 864x480 @ 60 Hz, 35.15 kHz hsync */
- { NULL, 60, 864, 480, 27777, 1, 1, 1, 1, 0, 0,
- 0, FB_VMODE_NONINTERLACED },
-};
-
-#ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
- /* #1: 640x480p@59.94/60Hz */
- [1] = {
- NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #3: 720x480p@59.94/60Hz */
- [3] = {
- NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #5: 1920x1080i@59.94/60Hz */
- [5] = {
- NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
- },
- /* #7: 720(1440)x480iH@59.94/60Hz */
- [7] = {
- NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
- FB_VMODE_INTERLACED, 0,
- },
- /* #9: 720(1440)x240pH@59.94/60Hz */
- [9] = {
- NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #18: 720x576pH@50Hz */
- [18] = {
- NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #19: 1280x720p@50Hz */
- [19] = {
- NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #20: 1920x1080i@50Hz */
- [20] = {
- NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
- },
- /* #32: 1920x1080p@23.98/24Hz */
- [32] = {
- NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
- },
- /* #35: (2880)x480p4x@59.94/60Hz */
- [35] = {
- NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
- },
-};
-
-const struct fb_videomode vesa_modes[] = {
- /* 0 640x350-85 VESA */
- { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
- FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA},
- /* 1 640x400-85 VESA */
- { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 2 720x400-85 VESA */
- { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 3 640x480-60 VESA */
- { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 4 640x480-72 VESA */
- { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 5 640x480-75 VESA */
- { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 6 640x480-85 VESA */
- { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 7 800x600-56 VESA */
- { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 8 800x600-60 VESA */
- { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 9 800x600-72 VESA */
- { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 10 800x600-75 VESA */
- { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 11 800x600-85 VESA */
- { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 12 1024x768i-43 VESA */
- { NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, FB_MODE_IS_VESA },
- /* 13 1024x768-60 VESA */
- { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 14 1024x768-70 VESA */
- { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
- 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 15 1024x768-75 VESA */
- { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 16 1024x768-85 VESA */
- { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 17 1152x864-75 VESA */
- { NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 18 1280x960-60 VESA */
- { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 19 1280x960-85 VESA */
- { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 20 1280x1024-60 VESA */
- { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 21 1280x1024-75 VESA */
- { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 22 1280x1024-85 VESA */
- { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 23 1600x1200-60 VESA */
- { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 24 1600x1200-65 VESA */
- { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 25 1600x1200-70 VESA */
- { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 26 1600x1200-75 VESA */
- { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 27 1600x1200-85 VESA */
- { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 28 1792x1344-60 VESA */
- { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 29 1792x1344-75 VESA */
- { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 30 1856x1392-60 VESA */
- { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 31 1856x1392-75 VESA */
- { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 32 1920x1440-60 VESA */
- { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
- /* 33 1920x1440-75 VESA */
- { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
-};
-EXPORT_SYMBOL(vesa_modes);
-#endif /* CONFIG_FB_MODE_HELPERS */
-
-/**
- * fb_try_mode - test a video mode
- * @var: frame buffer user defined part of display
- * @info: frame buffer info structure
- * @mode: frame buffer video mode structure
- * @bpp: color depth in bits per pixel
- *
- * Tries a video mode to test it's validity for device @info.
- *
- * Returns 1 on success.
- *
- */
-
-static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
- const struct fb_videomode *mode, unsigned int bpp)
-{
- int err = 0;
-
- DPRINTK("Trying mode %s %dx%d-%d@%d\n",
- mode->name ? mode->name : "noname",
- mode->xres, mode->yres, bpp, mode->refresh);
- var->xres = mode->xres;
- var->yres = mode->yres;
- var->xres_virtual = mode->xres;
- var->yres_virtual = mode->yres;
- var->xoffset = 0;
- var->yoffset = 0;
- var->bits_per_pixel = bpp;
- var->activate |= FB_ACTIVATE_TEST;
- var->pixclock = mode->pixclock;
- var->left_margin = mode->left_margin;
- var->right_margin = mode->right_margin;
- var->upper_margin = mode->upper_margin;
- var->lower_margin = mode->lower_margin;
- var->hsync_len = mode->hsync_len;
- var->vsync_len = mode->vsync_len;
- var->sync = mode->sync;
- var->vmode = mode->vmode;
- if (info->fbops->fb_check_var)
- err = info->fbops->fb_check_var(var, info);
- var->activate &= ~FB_ACTIVATE_TEST;
- return err;
-}
-
-/**
- * fb_find_mode - finds a valid video mode
- * @var: frame buffer user defined part of display
- * @info: frame buffer info structure
- * @mode_option: string video mode to find
- * @db: video mode database
- * @dbsize: size of @db
- * @default_mode: default video mode to fall back to
- * @default_bpp: default color depth in bits per pixel
- *
- * Finds a suitable video mode, starting with the specified mode
- * in @mode_option with fallback to @default_mode. If
- * @default_mode fails, all modes in the video mode database will
- * be tried.
- *
- * Valid mode specifiers for @mode_option:
- *
- * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or
- * <name>[-<bpp>][@<refresh>]
- *
- * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
- * <name> a string.
- *
- * If 'M' is present after yres (and before refresh/bpp if present),
- * the function will compute the timings using VESA(tm) Coordinated
- * Video Timings (CVT). If 'R' is present after 'M', will compute with
- * reduced blanking (for flatpanels). If 'i' is present, compute
- * interlaced mode. If 'm' is present, add margins equal to 1.8%
- * of xres rounded down to 8 pixels, and 1.8% of yres. The char
- * 'i' and 'm' must be after 'M' and 'R'. Example:
- *
- * 1024x768MR-8@60m - Reduced blank with margins at 60Hz.
- *
- * NOTE: The passed struct @var is _not_ cleared! This allows you
- * to supply values for e.g. the grayscale and accel_flags fields.
- *
- * Returns zero for failure, 1 if using specified @mode_option,
- * 2 if using specified @mode_option with an ignored refresh rate,
- * 3 if default mode is used, 4 if fall back to any valid mode.
- *
- */
-
-int fb_find_mode(struct fb_var_screeninfo *var,
- struct fb_info *info, const char *mode_option,
- const struct fb_videomode *db, unsigned int dbsize,
- const struct fb_videomode *default_mode,
- unsigned int default_bpp)
-{
- int i;
-
- /* Set up defaults */
- if (!db) {
- db = modedb;
- dbsize = ARRAY_SIZE(modedb);
- }
-
- if (!default_mode)
- default_mode = &db[0];
-
- if (!default_bpp)
- default_bpp = 8;
-
- /* Did the user specify a video mode? */
- if (!mode_option)
- mode_option = fb_mode_option;
- if (mode_option) {
- const char *name = mode_option;
- unsigned int namelen = strlen(name);
- int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
- unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;
- int yres_specified = 0, cvt = 0, rb = 0, interlace = 0;
- int margins = 0;
- u32 best, diff, tdiff;
-
- for (i = namelen-1; i >= 0; i--) {
- switch (name[i]) {
- case '@':
- namelen = i;
- if (!refresh_specified && !bpp_specified &&
- !yres_specified) {
- refresh = simple_strtol(&name[i+1], NULL,
- 10);
- refresh_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case '-':
- namelen = i;
- if (!bpp_specified && !yres_specified) {
- bpp = simple_strtol(&name[i+1], NULL,
- 10);
- bpp_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case 'x':
- if (!yres_specified) {
- yres = simple_strtol(&name[i+1], NULL,
- 10);
- yres_specified = 1;
- } else
- goto done;
- break;
- case '0' ... '9':
- break;
- case 'M':
- if (!yres_specified)
- cvt = 1;
- break;
- case 'R':
- if (!cvt)
- rb = 1;
- break;
- case 'm':
- if (!cvt)
- margins = 1;
- break;
- case 'i':
- if (!cvt)
- interlace = 1;
- break;
- default:
- goto done;
- }
- }
- if (i < 0 && yres_specified) {
- xres = simple_strtol(name, NULL, 10);
- res_specified = 1;
- }
-done:
- if (cvt) {
- struct fb_videomode cvt_mode;
- int ret;
-
- DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,
- (refresh) ? refresh : 60,
- (rb) ? " reduced blanking" : "",
- (margins) ? " with margins" : "",
- (interlace) ? " interlaced" : "");
-
- memset(&cvt_mode, 0, sizeof(cvt_mode));
- cvt_mode.xres = xres;
- cvt_mode.yres = yres;
- cvt_mode.refresh = (refresh) ? refresh : 60;
-
- if (interlace)
- cvt_mode.vmode |= FB_VMODE_INTERLACED;
- else
- cvt_mode.vmode &= ~FB_VMODE_INTERLACED;
-
- ret = fb_find_mode_cvt(&cvt_mode, margins, rb);
-
- if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {
- DPRINTK("modedb CVT: CVT mode ok\n");
- return 1;
- }
-
- DPRINTK("CVT mode invalid, getting mode from database\n");
- }
-
- DPRINTK("Trying specified video mode%s %ix%i\n",
- refresh_specified ? "" : " (ignoring refresh rate)",
- xres, yres);
-
- if (!refresh_specified) {
- /*
- * If the caller has provided a custom mode database and
- * a valid monspecs structure, we look for the mode with
- * the highest refresh rate. Otherwise we play it safe
- * it and try to find a mode with a refresh rate closest
- * to the standard 60 Hz.
- */
- if (db != modedb &&
- info->monspecs.vfmin && info->monspecs.vfmax &&
- info->monspecs.hfmin && info->monspecs.hfmax &&
- info->monspecs.dclkmax) {
- refresh = 1000;
- } else {
- refresh = 60;
- }
- }
-
- diff = -1;
- best = -1;
- for (i = 0; i < dbsize; i++) {
- if ((name_matches(db[i], name, namelen) ||
- (res_specified && res_matches(db[i], xres, yres))) &&
- !fb_try_mode(var, info, &db[i], bpp)) {
- if (refresh_specified && db[i].refresh == refresh)
- return 1;
-
- if (abs(db[i].refresh - refresh) < diff) {
- diff = abs(db[i].refresh - refresh);
- best = i;
- }
- }
- }
- if (best != -1) {
- fb_try_mode(var, info, &db[best], bpp);
- return (refresh_specified) ? 2 : 1;
- }
-
- diff = 2 * (xres + yres);
- best = -1;
- DPRINTK("Trying best-fit modes\n");
- for (i = 0; i < dbsize; i++) {
- DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);
- if (!fb_try_mode(var, info, &db[i], bpp)) {
- tdiff = abs(db[i].xres - xres) +
- abs(db[i].yres - yres);
-
- /*
- * Penalize modes with resolutions smaller
- * than requested.
- */
- if (xres > db[i].xres || yres > db[i].yres)
- tdiff += xres + yres;
-
- if (diff > tdiff) {
- diff = tdiff;
- best = i;
- }
- }
- }
- if (best != -1) {
- fb_try_mode(var, info, &db[best], bpp);
- return 5;
- }
- }
-
- DPRINTK("Trying default video mode\n");
- if (!fb_try_mode(var, info, default_mode, default_bpp))
- return 3;
-
- DPRINTK("Trying all modes\n");
- for (i = 0; i < dbsize; i++)
- if (!fb_try_mode(var, info, &db[i], default_bpp))
- return 4;
-
- DPRINTK("No valid mode found\n");
- return 0;
-}
-
-/**
- * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
- * @mode: pointer to struct fb_videomode
- * @var: pointer to struct fb_var_screeninfo
- */
-void fb_var_to_videomode(struct fb_videomode *mode,
- const struct fb_var_screeninfo *var)
-{
- u32 pixclock, hfreq, htotal, vtotal;
-
- mode->name = NULL;
- mode->xres = var->xres;
- mode->yres = var->yres;
- mode->pixclock = var->pixclock;
- mode->hsync_len = var->hsync_len;
- mode->vsync_len = var->vsync_len;
- mode->left_margin = var->left_margin;
- mode->right_margin = var->right_margin;
- mode->upper_margin = var->upper_margin;
- mode->lower_margin = var->lower_margin;
- mode->sync = var->sync;
- mode->vmode = var->vmode & FB_VMODE_MASK;
- mode->flag = FB_MODE_IS_FROM_VAR;
- mode->refresh = 0;
-
- if (!var->pixclock)
- return;
-
- pixclock = PICOS2KHZ(var->pixclock) * 1000;
-
- htotal = var->xres + var->right_margin + var->hsync_len +
- var->left_margin;
- vtotal = var->yres + var->lower_margin + var->vsync_len +
- var->upper_margin;
-
- if (var->vmode & FB_VMODE_INTERLACED)
- vtotal /= 2;
- if (var->vmode & FB_VMODE_DOUBLE)
- vtotal *= 2;
-
- hfreq = pixclock/htotal;
- mode->refresh = hfreq/vtotal;
-}
-
-/**
- * fb_videomode_to_var - convert fb_videomode to fb_var_screeninfo
- * @var: pointer to struct fb_var_screeninfo
- * @mode: pointer to struct fb_videomode
- */
-void fb_videomode_to_var(struct fb_var_screeninfo *var,
- const struct fb_videomode *mode)
-{
- var->xres = mode->xres;
- var->yres = mode->yres;
- var->xres_virtual = mode->xres;
- var->yres_virtual = mode->yres;
- var->xoffset = 0;
- var->yoffset = 0;
- var->pixclock = mode->pixclock;
- var->left_margin = mode->left_margin;
- var->right_margin = mode->right_margin;
- var->upper_margin = mode->upper_margin;
- var->lower_margin = mode->lower_margin;
- var->hsync_len = mode->hsync_len;
- var->vsync_len = mode->vsync_len;
- var->sync = mode->sync;
- var->vmode = mode->vmode & FB_VMODE_MASK;
-}
-
-/**
- * fb_mode_is_equal - compare 2 videomodes
- * @mode1: first videomode
- * @mode2: second videomode
- *
- * RETURNS:
- * 1 if equal, 0 if not
- */
-int fb_mode_is_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
-{
- return (mode1->xres == mode2->xres &&
- mode1->yres == mode2->yres &&
- mode1->pixclock == mode2->pixclock &&
- mode1->hsync_len == mode2->hsync_len &&
- mode1->vsync_len == mode2->vsync_len &&
- mode1->left_margin == mode2->left_margin &&
- mode1->right_margin == mode2->right_margin &&
- mode1->upper_margin == mode2->upper_margin &&
- mode1->lower_margin == mode2->lower_margin &&
- mode1->sync == mode2->sync &&
- mode1->vmode == mode2->vmode);
-}
-
-/**
- * fb_find_best_mode - find best matching videomode
- * @var: pointer to struct fb_var_screeninfo
- * @head: pointer to struct list_head of modelist
- *
- * RETURNS:
- * struct fb_videomode, NULL if none found
- *
- * IMPORTANT:
- * This function assumes that all modelist entries in
- * info->modelist are valid.
- *
- * NOTES:
- * Finds best matching videomode which has an equal or greater dimension than
- * var->xres and var->yres. If more than 1 videomode is found, will return
- * the videomode with the highest refresh rate
- */
-const struct fb_videomode *fb_find_best_mode(const struct fb_var_screeninfo *var,
- struct list_head *head)
-{
- struct list_head *pos;
- struct fb_modelist *modelist;
- struct fb_videomode *mode, *best = NULL;
- u32 diff = -1;
-
- list_for_each(pos, head) {
- u32 d;
-
- modelist = list_entry(pos, struct fb_modelist, list);
- mode = &modelist->mode;
-
- if (mode->xres >= var->xres && mode->yres >= var->yres) {
- d = (mode->xres - var->xres) +
- (mode->yres - var->yres);
- if (diff > d) {
- diff = d;
- best = mode;
- } else if (diff == d && best &&
- mode->refresh > best->refresh)
- best = mode;
- }
- }
- return best;
-}
-
-/**
- * fb_find_nearest_mode - find closest videomode
- *
- * @mode: pointer to struct fb_videomode
- * @head: pointer to modelist
- *
- * Finds best matching videomode, smaller or greater in dimension.
- * If more than 1 videomode is found, will return the videomode with
- * the closest refresh rate.
- */
-const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode,
- struct list_head *head)
-{
- struct list_head *pos;
- struct fb_modelist *modelist;
- struct fb_videomode *cmode, *best = NULL;
- u32 diff = -1, diff_refresh = -1;
-
- list_for_each(pos, head) {
- u32 d;
-
- modelist = list_entry(pos, struct fb_modelist, list);
- cmode = &modelist->mode;
-
- d = abs(cmode->xres - mode->xres) +
- abs(cmode->yres - mode->yres);
- if (diff > d) {
- diff = d;
- diff_refresh = abs(cmode->refresh - mode->refresh);
- best = cmode;
- } else if (diff == d) {
- d = abs(cmode->refresh - mode->refresh);
- if (diff_refresh > d) {
- diff_refresh = d;
- best = cmode;
- }
- }
- }
-
- return best;
-}
-
-/**
- * fb_match_mode - find a videomode which exactly matches the timings in var
- * @var: pointer to struct fb_var_screeninfo
- * @head: pointer to struct list_head of modelist
- *
- * RETURNS:
- * struct fb_videomode, NULL if none found
- */
-const struct fb_videomode *fb_match_mode(const struct fb_var_screeninfo *var,
- struct list_head *head)
-{
- struct list_head *pos;
- struct fb_modelist *modelist;
- struct fb_videomode *m, mode;
-
- fb_var_to_videomode(&mode, var);
- list_for_each(pos, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- if (fb_mode_is_equal(m, &mode))
- return m;
- }
- return NULL;
-}
-
-/**
- * fb_add_videomode - adds videomode entry to modelist
- * @mode: videomode to add
- * @head: struct list_head of modelist
- *
- * NOTES:
- * Will only add unmatched mode entries
- */
-int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
-{
- struct list_head *pos;
- struct fb_modelist *modelist;
- struct fb_videomode *m;
- int found = 0;
-
- list_for_each(pos, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- if (fb_mode_is_equal(m, mode)) {
- found = 1;
- break;
- }
- }
- if (!found) {
- modelist = kmalloc(sizeof(struct fb_modelist),
- GFP_KERNEL);
-
- if (!modelist)
- return -ENOMEM;
- modelist->mode = *mode;
- list_add(&modelist->list, head);
- }
- return 0;
-}
-
-/**
- * fb_delete_videomode - removed videomode entry from modelist
- * @mode: videomode to remove
- * @head: struct list_head of modelist
- *
- * NOTES:
- * Will remove all matching mode entries
- */
-void fb_delete_videomode(const struct fb_videomode *mode,
- struct list_head *head)
-{
- struct list_head *pos, *n;
- struct fb_modelist *modelist;
- struct fb_videomode *m;
-
- list_for_each_safe(pos, n, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- if (fb_mode_is_equal(m, mode)) {
- list_del(pos);
- kfree(pos);
- }
- }
-}
-
-/**
- * fb_destroy_modelist - destroy modelist
- * @head: struct list_head of modelist
- */
-void fb_destroy_modelist(struct list_head *head)
-{
- struct list_head *pos, *n;
-
- list_for_each_safe(pos, n, head) {
- list_del(pos);
- kfree(pos);
- }
-}
-EXPORT_SYMBOL_GPL(fb_destroy_modelist);
-
-/**
- * fb_videomode_to_modelist - convert mode array to mode list
- * @modedb: array of struct fb_videomode
- * @num: number of entries in array
- * @head: struct list_head of modelist
- */
-void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num,
- struct list_head *head)
-{
- int i;
-
- INIT_LIST_HEAD(head);
-
- for (i = 0; i < num; i++) {
- if (fb_add_videomode(&modedb[i], head))
- return;
- }
-}
-
-const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs,
- struct list_head *head)
-{
- struct list_head *pos;
- struct fb_modelist *modelist;
- const struct fb_videomode *m, *m1 = NULL, *md = NULL, *best = NULL;
- int first = 0;
-
- if (!head->prev || !head->next || list_empty(head))
- goto finished;
-
- /* get the first detailed mode and the very first mode */
- list_for_each(pos, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
-
- if (!first) {
- m1 = m;
- first = 1;
- }
-
- if (m->flag & FB_MODE_IS_FIRST) {
- md = m;
- break;
- }
- }
-
- /* first detailed timing is preferred */
- if (specs->misc & FB_MISC_1ST_DETAIL) {
- best = md;
- goto finished;
- }
-
- /* find best mode based on display width and height */
- if (specs->max_x && specs->max_y) {
- struct fb_var_screeninfo var;
-
- memset(&var, 0, sizeof(struct fb_var_screeninfo));
- var.xres = (specs->max_x * 7200)/254;
- var.yres = (specs->max_y * 7200)/254;
- m = fb_find_best_mode(&var, head);
- if (m) {
- best = m;
- goto finished;
- }
- }
-
- /* use first detailed mode */
- if (md) {
- best = md;
- goto finished;
- }
-
- /* last resort, use the very first mode */
- best = m1;
-finished:
- return best;
-}
-EXPORT_SYMBOL(fb_find_best_display);
-
-EXPORT_SYMBOL(fb_videomode_to_var);
-EXPORT_SYMBOL(fb_var_to_videomode);
-EXPORT_SYMBOL(fb_mode_is_equal);
-EXPORT_SYMBOL(fb_add_videomode);
-EXPORT_SYMBOL(fb_match_mode);
-EXPORT_SYMBOL(fb_find_best_mode);
-EXPORT_SYMBOL(fb_find_nearest_mode);
-EXPORT_SYMBOL(fb_videomode_to_modelist);
-EXPORT_SYMBOL(fb_find_mode);
-EXPORT_SYMBOL(fb_find_mode_cvt);
+++ /dev/null
-/*
- * Common utility functions for VGA-based graphics cards.
- *
- * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- * Some parts are based on David Boucher's viafb (http://davesdomain.org.uk/viafb/)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <linux/svga.h>
-#include <asm/types.h>
-#include <asm/io.h>
-
-
-/* Write a CRT register value spread across multiple registers */
-void svga_wcrt_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
-{
- u8 regval, bitval, bitnum;
-
- while (regset->regnum != VGA_REGSET_END_VAL) {
- regval = vga_rcrt(regbase, regset->regnum);
- bitnum = regset->lowbit;
- while (bitnum <= regset->highbit) {
- bitval = 1 << bitnum;
- regval = regval & ~bitval;
- if (value & 1) regval = regval | bitval;
- bitnum ++;
- value = value >> 1;
- }
- vga_wcrt(regbase, regset->regnum, regval);
- regset ++;
- }
-}
-
-/* Write a sequencer register value spread across multiple registers */
-void svga_wseq_multi(void __iomem *regbase, const struct vga_regset *regset, u32 value)
-{
- u8 regval, bitval, bitnum;
-
- while (regset->regnum != VGA_REGSET_END_VAL) {
- regval = vga_rseq(regbase, regset->regnum);
- bitnum = regset->lowbit;
- while (bitnum <= regset->highbit) {
- bitval = 1 << bitnum;
- regval = regval & ~bitval;
- if (value & 1) regval = regval | bitval;
- bitnum ++;
- value = value >> 1;
- }
- vga_wseq(regbase, regset->regnum, regval);
- regset ++;
- }
-}
-
-static unsigned int svga_regset_size(const struct vga_regset *regset)
-{
- u8 count = 0;
-
- while (regset->regnum != VGA_REGSET_END_VAL) {
- count += regset->highbit - regset->lowbit + 1;
- regset ++;
- }
- return 1 << count;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-/* Set graphics controller registers to sane values */
-void svga_set_default_gfx_regs(void __iomem *regbase)
-{
- /* All standard GFX registers (GR00 - GR08) */
- vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0x00);
- vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0x00);
- vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0x00);
- vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0x00);
- vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0x00);
- vga_wgfx(regbase, VGA_GFX_MODE, 0x00);
-/* vga_wgfx(regbase, VGA_GFX_MODE, 0x20); */
-/* vga_wgfx(regbase, VGA_GFX_MODE, 0x40); */
- vga_wgfx(regbase, VGA_GFX_MISC, 0x05);
-/* vga_wgfx(regbase, VGA_GFX_MISC, 0x01); */
- vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x0F);
- vga_wgfx(regbase, VGA_GFX_BIT_MASK, 0xFF);
-}
-
-/* Set attribute controller registers to sane values */
-void svga_set_default_atc_regs(void __iomem *regbase)
-{
- u8 count;
-
- vga_r(regbase, 0x3DA);
- vga_w(regbase, VGA_ATT_W, 0x00);
-
- /* All standard ATC registers (AR00 - AR14) */
- for (count = 0; count <= 0xF; count ++)
- svga_wattr(regbase, count, count);
-
- svga_wattr(regbase, VGA_ATC_MODE, 0x01);
-/* svga_wattr(regbase, VGA_ATC_MODE, 0x41); */
- svga_wattr(regbase, VGA_ATC_OVERSCAN, 0x00);
- svga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 0x0F);
- svga_wattr(regbase, VGA_ATC_PEL, 0x00);
- svga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0x00);
-
- vga_r(regbase, 0x3DA);
- vga_w(regbase, VGA_ATT_W, 0x20);
-}
-
-/* Set sequencer registers to sane values */
-void svga_set_default_seq_regs(void __iomem *regbase)
-{
- /* Standard sequencer registers (SR01 - SR04), SR00 is not set */
- vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, VGA_SR01_CHAR_CLK_8DOTS);
- vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, VGA_SR02_ALL_PLANES);
- vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
-/* vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE | VGA_SR04_CHN_4M); */
- vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE);
-}
-
-/* Set CRTC registers to sane values */
-void svga_set_default_crt_regs(void __iomem *regbase)
-{
- /* Standard CRT registers CR03 CR08 CR09 CR14 CR17 */
- svga_wcrt_mask(regbase, 0x03, 0x80, 0x80); /* Enable vertical retrace EVRA */
- vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
- svga_wcrt_mask(regbase, VGA_CRTC_MAX_SCAN, 0, 0x1F);
- vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
- vga_wcrt(regbase, VGA_CRTC_MODE, 0xE3);
-}
-
-void svga_set_textmode_vga_regs(void __iomem *regbase)
-{
- /* svga_wseq_mask(regbase, 0x1, 0x00, 0x01); */ /* Switch 8/9 pixel per char */
- vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM);
- vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x03);
-
- vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, 0x0f); /* 0x4f */
- vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0x1f);
- svga_wcrt_mask(regbase, VGA_CRTC_MODE, 0x23, 0x7f);
-
- vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0x0d);
- vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 0x0e);
- vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0x00);
- vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0x00);
-
- vga_wgfx(regbase, VGA_GFX_MODE, 0x10); /* Odd/even memory mode */
- vga_wgfx(regbase, VGA_GFX_MISC, 0x0E); /* Misc graphics register - text mode enable */
- vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 0x00);
-
- vga_r(regbase, 0x3DA);
- vga_w(regbase, VGA_ATT_W, 0x00);
-
- svga_wattr(regbase, 0x10, 0x0C); /* Attribute Mode Control Register - text mode, blinking and line graphics */
- svga_wattr(regbase, 0x13, 0x08); /* Horizontal Pixel Panning Register */
-
- vga_r(regbase, 0x3DA);
- vga_w(regbase, VGA_ATT_W, 0x20);
-}
-
-#if 0
-void svga_dump_var(struct fb_var_screeninfo *var, int node)
-{
- pr_debug("fb%d: var.vmode : 0x%X\n", node, var->vmode);
- pr_debug("fb%d: var.xres : %d\n", node, var->xres);
- pr_debug("fb%d: var.yres : %d\n", node, var->yres);
- pr_debug("fb%d: var.bits_per_pixel: %d\n", node, var->bits_per_pixel);
- pr_debug("fb%d: var.xres_virtual : %d\n", node, var->xres_virtual);
- pr_debug("fb%d: var.yres_virtual : %d\n", node, var->yres_virtual);
- pr_debug("fb%d: var.left_margin : %d\n", node, var->left_margin);
- pr_debug("fb%d: var.right_margin : %d\n", node, var->right_margin);
- pr_debug("fb%d: var.upper_margin : %d\n", node, var->upper_margin);
- pr_debug("fb%d: var.lower_margin : %d\n", node, var->lower_margin);
- pr_debug("fb%d: var.hsync_len : %d\n", node, var->hsync_len);
- pr_debug("fb%d: var.vsync_len : %d\n", node, var->vsync_len);
- pr_debug("fb%d: var.sync : 0x%X\n", node, var->sync);
- pr_debug("fb%d: var.pixclock : %d\n\n", node, var->pixclock);
-}
-#endif /* 0 */
-
-
-/* ------------------------------------------------------------------------- */
-
-
-void svga_settile(struct fb_info *info, struct fb_tilemap *map)
-{
- const u8 *font = map->data;
- u8 __iomem *fb = (u8 __iomem *)info->screen_base;
- int i, c;
-
- if ((map->width != 8) || (map->height != 16) ||
- (map->depth != 1) || (map->length != 256)) {
- fb_err(info, "unsupported font parameters: width %d, height %d, depth %d, length %d\n",
- map->width, map->height, map->depth, map->length);
- return;
- }
-
- fb += 2;
- for (c = 0; c < map->length; c++) {
- for (i = 0; i < map->height; i++) {
- fb_writeb(font[i], fb + i * 4);
-// fb[i * 4] = font[i];
- }
- fb += 128;
- font += map->height;
- }
-}
-
-/* Copy area in text (tileblit) mode */
-void svga_tilecopy(struct fb_info *info, struct fb_tilearea *area)
-{
- int dx, dy;
- /* colstride is halved in this function because u16 are used */
- int colstride = 1 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
- int rowstride = colstride * (info->var.xres_virtual / 8);
- u16 __iomem *fb = (u16 __iomem *) info->screen_base;
- u16 __iomem *src, *dst;
-
- if ((area->sy > area->dy) ||
- ((area->sy == area->dy) && (area->sx > area->dx))) {
- src = fb + area->sx * colstride + area->sy * rowstride;
- dst = fb + area->dx * colstride + area->dy * rowstride;
- } else {
- src = fb + (area->sx + area->width - 1) * colstride
- + (area->sy + area->height - 1) * rowstride;
- dst = fb + (area->dx + area->width - 1) * colstride
- + (area->dy + area->height - 1) * rowstride;
-
- colstride = -colstride;
- rowstride = -rowstride;
- }
-
- for (dy = 0; dy < area->height; dy++) {
- u16 __iomem *src2 = src;
- u16 __iomem *dst2 = dst;
- for (dx = 0; dx < area->width; dx++) {
- fb_writew(fb_readw(src2), dst2);
-// *dst2 = *src2;
- src2 += colstride;
- dst2 += colstride;
- }
- src += rowstride;
- dst += rowstride;
- }
-}
-
-/* Fill area in text (tileblit) mode */
-void svga_tilefill(struct fb_info *info, struct fb_tilerect *rect)
-{
- int dx, dy;
- int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
- int rowstride = colstride * (info->var.xres_virtual / 8);
- int attr = (0x0F & rect->bg) << 4 | (0x0F & rect->fg);
- u8 __iomem *fb = (u8 __iomem *)info->screen_base;
- fb += rect->sx * colstride + rect->sy * rowstride;
-
- for (dy = 0; dy < rect->height; dy++) {
- u8 __iomem *fb2 = fb;
- for (dx = 0; dx < rect->width; dx++) {
- fb_writeb(rect->index, fb2);
- fb_writeb(attr, fb2 + 1);
- fb2 += colstride;
- }
- fb += rowstride;
- }
-}
-
-/* Write text in text (tileblit) mode */
-void svga_tileblit(struct fb_info *info, struct fb_tileblit *blit)
-{
- int dx, dy, i;
- int colstride = 2 << (info->fix.type_aux & FB_AUX_TEXT_SVGA_MASK);
- int rowstride = colstride * (info->var.xres_virtual / 8);
- int attr = (0x0F & blit->bg) << 4 | (0x0F & blit->fg);
- u8 __iomem *fb = (u8 __iomem *)info->screen_base;
- fb += blit->sx * colstride + blit->sy * rowstride;
-
- i=0;
- for (dy=0; dy < blit->height; dy ++) {
- u8 __iomem *fb2 = fb;
- for (dx = 0; dx < blit->width; dx ++) {
- fb_writeb(blit->indices[i], fb2);
- fb_writeb(attr, fb2 + 1);
- fb2 += colstride;
- i ++;
- if (i == blit->length) return;
- }
- fb += rowstride;
- }
-
-}
-
-/* Set cursor in text (tileblit) mode */
-void svga_tilecursor(void __iomem *regbase, struct fb_info *info, struct fb_tilecursor *cursor)
-{
- u8 cs = 0x0d;
- u8 ce = 0x0e;
- u16 pos = cursor->sx + (info->var.xoffset / 8)
- + (cursor->sy + (info->var.yoffset / 16))
- * (info->var.xres_virtual / 8);
-
- if (! cursor -> mode)
- return;
-
- svga_wcrt_mask(regbase, 0x0A, 0x20, 0x20); /* disable cursor */
-
- if (cursor -> shape == FB_TILE_CURSOR_NONE)
- return;
-
- switch (cursor -> shape) {
- case FB_TILE_CURSOR_UNDERLINE:
- cs = 0x0d;
- break;
- case FB_TILE_CURSOR_LOWER_THIRD:
- cs = 0x09;
- break;
- case FB_TILE_CURSOR_LOWER_HALF:
- cs = 0x07;
- break;
- case FB_TILE_CURSOR_TWO_THIRDS:
- cs = 0x05;
- break;
- case FB_TILE_CURSOR_BLOCK:
- cs = 0x01;
- break;
- }
-
- /* set cursor position */
- vga_wcrt(regbase, 0x0E, pos >> 8);
- vga_wcrt(regbase, 0x0F, pos & 0xFF);
-
- vga_wcrt(regbase, 0x0B, ce); /* set cursor end */
- vga_wcrt(regbase, 0x0A, cs); /* set cursor start and enable it */
-}
-
-int svga_get_tilemax(struct fb_info *info)
-{
- return 256;
-}
-
-/* Get capabilities of accelerator based on the mode */
-
-void svga_get_caps(struct fb_info *info, struct fb_blit_caps *caps,
- struct fb_var_screeninfo *var)
-{
- if (var->bits_per_pixel == 0) {
- /* can only support 256 8x16 bitmap */
- caps->x = 1 << (8 - 1);
- caps->y = 1 << (16 - 1);
- caps->len = 256;
- } else {
- caps->x = (var->bits_per_pixel == 4) ? 1 << (8 - 1) : ~(u32)0;
- caps->y = ~(u32)0;
- caps->len = ~(u32)0;
- }
-}
-EXPORT_SYMBOL(svga_get_caps);
-
-/* ------------------------------------------------------------------------- */
-
-
-/*
- * Compute PLL settings (M, N, R)
- * F_VCO = (F_BASE * M) / N
- * F_OUT = F_VCO / (2^R)
- */
-
-static inline u32 abs_diff(u32 a, u32 b)
-{
- return (a > b) ? (a - b) : (b - a);
-}
-
-int svga_compute_pll(const struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u16 *r, int node)
-{
- u16 am, an, ar;
- u32 f_vco, f_current, delta_current, delta_best;
-
- pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int) f_wanted);
-
- ar = pll->r_max;
- f_vco = f_wanted << ar;
-
- /* overflow check */
- if ((f_vco >> ar) != f_wanted)
- return -EINVAL;
-
- /* It is usually better to have greater VCO clock
- because of better frequency stability.
- So first try r_max, then r smaller. */
- while ((ar > pll->r_min) && (f_vco > pll->f_vco_max)) {
- ar--;
- f_vco = f_vco >> 1;
- }
-
- /* VCO bounds check */
- if ((f_vco < pll->f_vco_min) || (f_vco > pll->f_vco_max))
- return -EINVAL;
-
- delta_best = 0xFFFFFFFF;
- *m = 0;
- *n = 0;
- *r = ar;
-
- am = pll->m_min;
- an = pll->n_min;
-
- while ((am <= pll->m_max) && (an <= pll->n_max)) {
- f_current = (pll->f_base * am) / an;
- delta_current = abs_diff (f_current, f_vco);
-
- if (delta_current < delta_best) {
- delta_best = delta_current;
- *m = am;
- *n = an;
- }
-
- if (f_current <= f_vco) {
- am ++;
- } else {
- an ++;
- }
- }
-
- f_current = (pll->f_base * *m) / *n;
- pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int) (f_current >> ar), (int) f_current);
- pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int) *m, (unsigned int) *n, (unsigned int) *r);
- return 0;
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-/* Check CRT timing values */
-int svga_check_timings(const struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node)
-{
- u32 value;
-
- var->xres = (var->xres+7)&~7;
- var->left_margin = (var->left_margin+7)&~7;
- var->right_margin = (var->right_margin+7)&~7;
- var->hsync_len = (var->hsync_len+7)&~7;
-
- /* Check horizontal total */
- value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
- if (((value / 8) - 5) >= svga_regset_size (tm->h_total_regs))
- return -EINVAL;
-
- /* Check horizontal display and blank start */
- value = var->xres;
- if (((value / 8) - 1) >= svga_regset_size (tm->h_display_regs))
- return -EINVAL;
- if (((value / 8) - 1) >= svga_regset_size (tm->h_blank_start_regs))
- return -EINVAL;
-
- /* Check horizontal sync start */
- value = var->xres + var->right_margin;
- if (((value / 8) - 1) >= svga_regset_size (tm->h_sync_start_regs))
- return -EINVAL;
-
- /* Check horizontal blank end (or length) */
- value = var->left_margin + var->right_margin + var->hsync_len;
- if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_blank_end_regs)))
- return -EINVAL;
-
- /* Check horizontal sync end (or length) */
- value = var->hsync_len;
- if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_sync_end_regs)))
- return -EINVAL;
-
- /* Check vertical total */
- value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
- if ((value - 1) >= svga_regset_size(tm->v_total_regs))
- return -EINVAL;
-
- /* Check vertical display and blank start */
- value = var->yres;
- if ((value - 1) >= svga_regset_size(tm->v_display_regs))
- return -EINVAL;
- if ((value - 1) >= svga_regset_size(tm->v_blank_start_regs))
- return -EINVAL;
-
- /* Check vertical sync start */
- value = var->yres + var->lower_margin;
- if ((value - 1) >= svga_regset_size(tm->v_sync_start_regs))
- return -EINVAL;
-
- /* Check vertical blank end (or length) */
- value = var->upper_margin + var->lower_margin + var->vsync_len;
- if ((value == 0) || (value >= svga_regset_size (tm->v_blank_end_regs)))
- return -EINVAL;
-
- /* Check vertical sync end (or length) */
- value = var->vsync_len;
- if ((value == 0) || (value >= svga_regset_size (tm->v_sync_end_regs)))
- return -EINVAL;
-
- return 0;
-}
-
-/* Set CRT timing registers */
-void svga_set_timings(void __iomem *regbase, const struct svga_timing_regs *tm,
- struct fb_var_screeninfo *var,
- u32 hmul, u32 hdiv, u32 vmul, u32 vdiv, u32 hborder, int node)
-{
- u8 regval;
- u32 value;
-
- value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal total : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_total_regs, (value / 8) - 5);
-
- value = var->xres;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal display : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_display_regs, (value / 8) - 1);
-
- value = var->xres;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal blank start: %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_blank_start_regs, (value / 8) - 1 + hborder);
-
- value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal blank end : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_blank_end_regs, (value / 8) - 1 - hborder);
-
- value = var->xres + var->right_margin;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal sync start : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_sync_start_regs, (value / 8));
-
- value = var->xres + var->right_margin + var->hsync_len;
- value = (value * hmul) / hdiv;
- pr_debug("fb%d: horizontal sync end : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->h_sync_end_regs, (value / 8));
-
- value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical total : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_total_regs, value - 2);
-
- value = var->yres;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical display : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_display_regs, value - 1);
-
- value = var->yres;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical blank start : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_blank_start_regs, value);
-
- value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical blank end : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_blank_end_regs, value - 2);
-
- value = var->yres + var->lower_margin;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical sync start : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_sync_start_regs, value);
-
- value = var->yres + var->lower_margin + var->vsync_len;
- value = (value * vmul) / vdiv;
- pr_debug("fb%d: vertical sync end : %d\n", node, value);
- svga_wcrt_multi(regbase, tm->v_sync_end_regs, value);
-
- /* Set horizontal and vertical sync pulse polarity in misc register */
-
- regval = vga_r(regbase, VGA_MIS_R);
- if (var->sync & FB_SYNC_HOR_HIGH_ACT) {
- pr_debug("fb%d: positive horizontal sync\n", node);
- regval = regval & ~0x80;
- } else {
- pr_debug("fb%d: negative horizontal sync\n", node);
- regval = regval | 0x80;
- }
- if (var->sync & FB_SYNC_VERT_HIGH_ACT) {
- pr_debug("fb%d: positive vertical sync\n", node);
- regval = regval & ~0x40;
- } else {
- pr_debug("fb%d: negative vertical sync\n\n", node);
- regval = regval | 0x40;
- }
- vga_w(regbase, VGA_MIS_W, regval);
-}
-
-
-/* ------------------------------------------------------------------------- */
-
-
-static inline int match_format(const struct svga_fb_format *frm,
- struct fb_var_screeninfo *var)
-{
- int i = 0;
- int stored = -EINVAL;
-
- while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL)
- {
- if ((var->bits_per_pixel == frm->bits_per_pixel) &&
- (var->red.length <= frm->red.length) &&
- (var->green.length <= frm->green.length) &&
- (var->blue.length <= frm->blue.length) &&
- (var->transp.length <= frm->transp.length) &&
- (var->nonstd == frm->nonstd))
- return i;
- if (var->bits_per_pixel == frm->bits_per_pixel)
- stored = i;
- i++;
- frm++;
- }
- return stored;
-}
-
-int svga_match_format(const struct svga_fb_format *frm,
- struct fb_var_screeninfo *var,
- struct fb_fix_screeninfo *fix)
-{
- int i = match_format(frm, var);
-
- if (i >= 0) {
- var->bits_per_pixel = frm[i].bits_per_pixel;
- var->red = frm[i].red;
- var->green = frm[i].green;
- var->blue = frm[i].blue;
- var->transp = frm[i].transp;
- var->nonstd = frm[i].nonstd;
- if (fix != NULL) {
- fix->type = frm[i].type;
- fix->type_aux = frm[i].type_aux;
- fix->visual = frm[i].visual;
- fix->xpanstep = frm[i].xpanstep;
- }
- }
-
- return i;
-}
-
-
-EXPORT_SYMBOL(svga_wcrt_multi);
-EXPORT_SYMBOL(svga_wseq_multi);
-
-EXPORT_SYMBOL(svga_set_default_gfx_regs);
-EXPORT_SYMBOL(svga_set_default_atc_regs);
-EXPORT_SYMBOL(svga_set_default_seq_regs);
-EXPORT_SYMBOL(svga_set_default_crt_regs);
-EXPORT_SYMBOL(svga_set_textmode_vga_regs);
-
-EXPORT_SYMBOL(svga_settile);
-EXPORT_SYMBOL(svga_tilecopy);
-EXPORT_SYMBOL(svga_tilefill);
-EXPORT_SYMBOL(svga_tileblit);
-EXPORT_SYMBOL(svga_tilecursor);
-EXPORT_SYMBOL(svga_get_tilemax);
-
-EXPORT_SYMBOL(svga_compute_pll);
-EXPORT_SYMBOL(svga_check_timings);
-EXPORT_SYMBOL(svga_set_timings);
-EXPORT_SYMBOL(svga_match_format);
-
-MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>");
-MODULE_DESCRIPTION("Common utility functions for VGA-based graphics cards");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Generic Bit Block Transfer for frame buffers located in system RAM with
- * packed pixels of any depth.
- *
- * Based almost entirely from cfbcopyarea.c (which is based almost entirely
- * on Geert Uytterhoeven's copyarea routine)
- *
- * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-#include <asm/io.h>
-#include "fb_draw.h"
-
- /*
- * Generic bitwise copy algorithm
- */
-
-static void
-bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx,
- const unsigned long *src, int src_idx, int bits, unsigned n)
-{
- unsigned long first, last;
- int const shift = dst_idx-src_idx;
- int left, right;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (!shift) {
- /* Same alignment for source and dest */
- if (dst_idx+n <= bits) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(*src, *dst, first);
- } else {
- /* Multiple destination words */
- /* Leading bits */
- if (first != ~0UL) {
- *dst = comp(*src, *dst, first);
- dst++;
- src++;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 8) {
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
- n -= 8;
- }
- while (n--)
- *dst++ = *src++;
-
- /* Trailing bits */
- if (last)
- *dst = comp(*src, *dst, last);
- }
- } else {
- unsigned long d0, d1;
- int m;
-
- /* Different alignment for source and dest */
- right = shift & (bits - 1);
- left = -shift & (bits - 1);
-
- if (dst_idx+n <= bits) {
- /* Single destination word */
- if (last)
- first &= last;
- if (shift > 0) {
- /* Single source word */
- *dst = comp(*src >> right, *dst, first);
- } else if (src_idx+n <= bits) {
- /* Single source word */
- *dst = comp(*src << left, *dst, first);
- } else {
- /* 2 source words */
- d0 = *src++;
- d1 = *src;
- *dst = comp(d0 << left | d1 >> right, *dst,
- first);
- }
- } else {
- /* Multiple destination words */
- /** We must always remember the last value read,
- because in case SRC and DST overlap bitwise (e.g.
- when moving just one pixel in 1bpp), we always
- collect one full long for DST and that might
- overlap with the current long from SRC. We store
- this value in 'd0'. */
- d0 = *src++;
- /* Leading bits */
- if (shift > 0) {
- /* Single source word */
- *dst = comp(d0 >> right, *dst, first);
- dst++;
- n -= bits - dst_idx;
- } else {
- /* 2 source words */
- d1 = *src++;
- *dst = comp(d0 << left | *dst >> right, *dst, first);
- d0 = d1;
- dst++;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- m = n % bits;
- n /= bits;
- while (n >= 4) {
- d1 = *src++;
- *dst++ = d0 << left | d1 >> right;
- d0 = d1;
- d1 = *src++;
- *dst++ = d0 << left | d1 >> right;
- d0 = d1;
- d1 = *src++;
- *dst++ = d0 << left | d1 >> right;
- d0 = d1;
- d1 = *src++;
- *dst++ = d0 << left | d1 >> right;
- d0 = d1;
- n -= 4;
- }
- while (n--) {
- d1 = *src++;
- *dst++ = d0 << left | d1 >> right;
- d0 = d1;
- }
-
- /* Trailing bits */
- if (last) {
- if (m <= right) {
- /* Single source word */
- *dst = comp(d0 << left, *dst, last);
- } else {
- /* 2 source words */
- d1 = *src;
- *dst = comp(d0 << left | d1 >> right,
- *dst, last);
- }
- }
- }
- }
-}
-
- /*
- * Generic bitwise copy algorithm, operating backward
- */
-
-static void
-bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
- const unsigned long *src, int src_idx, int bits, unsigned n)
-{
- unsigned long first, last;
- int shift;
-
- dst += (n-1)/bits;
- src += (n-1)/bits;
- if ((n-1) % bits) {
- dst_idx += (n-1) % bits;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= bits - 1;
- src_idx += (n-1) % bits;
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= bits - 1;
- }
-
- shift = dst_idx-src_idx;
-
- first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx);
- last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits)));
-
- if (!shift) {
- /* Same alignment for source and dest */
- if ((unsigned long)dst_idx+1 >= n) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(*src, *dst, first);
- } else {
- /* Multiple destination words */
-
- /* Leading bits */
- if (first != ~0UL) {
- *dst = comp(*src, *dst, first);
- dst--;
- src--;
- n -= dst_idx+1;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 8) {
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- *dst-- = *src--;
- n -= 8;
- }
- while (n--)
- *dst-- = *src--;
- /* Trailing bits */
- if (last)
- *dst = comp(*src, *dst, last);
- }
- } else {
- /* Different alignment for source and dest */
-
- int const left = -shift & (bits-1);
- int const right = shift & (bits-1);
-
- if ((unsigned long)dst_idx+1 >= n) {
- /* Single destination word */
- if (last)
- first &= last;
- if (shift < 0) {
- /* Single source word */
- *dst = comp(*src << left, *dst, first);
- } else if (1+(unsigned long)src_idx >= n) {
- /* Single source word */
- *dst = comp(*src >> right, *dst, first);
- } else {
- /* 2 source words */
- *dst = comp(*src >> right | *(src-1) << left,
- *dst, first);
- }
- } else {
- /* Multiple destination words */
- /** We must always remember the last value read,
- because in case SRC and DST overlap bitwise (e.g.
- when moving just one pixel in 1bpp), we always
- collect one full long for DST and that might
- overlap with the current long from SRC. We store
- this value in 'd0'. */
- unsigned long d0, d1;
- int m;
-
- d0 = *src--;
- /* Leading bits */
- if (shift < 0) {
- /* Single source word */
- *dst = comp(d0 << left, *dst, first);
- } else {
- /* 2 source words */
- d1 = *src--;
- *dst = comp(d0 >> right | d1 << left, *dst,
- first);
- d0 = d1;
- }
- dst--;
- n -= dst_idx+1;
-
- /* Main chunk */
- m = n % bits;
- n /= bits;
- while (n >= 4) {
- d1 = *src--;
- *dst-- = d0 >> right | d1 << left;
- d0 = d1;
- d1 = *src--;
- *dst-- = d0 >> right | d1 << left;
- d0 = d1;
- d1 = *src--;
- *dst-- = d0 >> right | d1 << left;
- d0 = d1;
- d1 = *src--;
- *dst-- = d0 >> right | d1 << left;
- d0 = d1;
- n -= 4;
- }
- while (n--) {
- d1 = *src--;
- *dst-- = d0 >> right | d1 << left;
- d0 = d1;
- }
-
- /* Trailing bits */
- if (last) {
- if (m <= left) {
- /* Single source word */
- *dst = comp(d0 >> right, *dst, last);
- } else {
- /* 2 source words */
- d1 = *src;
- *dst = comp(d0 >> right | d1 << left,
- *dst, last);
- }
- }
- }
- }
-}
-
-void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
-{
- u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
- u32 height = area->height, width = area->width;
- unsigned long const bits_per_line = p->fix.line_length*8u;
- unsigned long *dst = NULL, *src = NULL;
- int bits = BITS_PER_LONG, bytes = bits >> 3;
- int dst_idx = 0, src_idx = 0, rev_copy = 0;
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- /* if the beginning of the target area might overlap with the end of
- the source area, be have to copy the area reverse. */
- if ((dy == sy && dx > sx) || (dy > sy)) {
- dy += height;
- sy += height;
- rev_copy = 1;
- }
-
- /* split the base of the framebuffer into a long-aligned address and
- the index of the first bit */
- dst = src = (unsigned long *)((unsigned long)p->screen_base &
- ~(bytes-1));
- dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
- /* add offset of source and target area */
- dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
- src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
-
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
-
- if (rev_copy) {
- while (height--) {
- dst_idx -= bits_per_line;
- src_idx -= bits_per_line;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
- width*p->var.bits_per_pixel);
- }
- } else {
- while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy(p, dst, dst_idx, src, src_idx, bits,
- width*p->var.bits_per_pixel);
- dst_idx += bits_per_line;
- src_idx += bits_per_line;
- }
- }
-}
-
-EXPORT_SYMBOL(sys_copyarea);
-
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-/*
- * Generic fillrect for frame buffers in system RAM with packed pixels of
- * any depth.
- *
- * Based almost entirely from cfbfillrect.c (which is based almost entirely
- * on Geert Uytterhoeven's fillrect routine)
- *
- * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- */
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-#include "fb_draw.h"
-
- /*
- * Aligned pattern fill using 32/64-bit memory accesses
- */
-
-static void
-bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
- unsigned long pat, unsigned n, int bits)
-{
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(pat, *dst, first);
- } else {
- /* Multiple destination words */
-
- /* Leading bits */
- if (first!= ~0UL) {
- *dst = comp(pat, *dst, first);
- dst++;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 8) {
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- *dst++ = pat;
- n -= 8;
- }
- while (n--)
- *dst++ = pat;
- /* Trailing bits */
- if (last)
- *dst = comp(pat, *dst, last);
- }
-}
-
-
- /*
- * Unaligned generic pattern fill using 32/64-bit memory accesses
- * The pattern must have been expanded to a full 32/64-bit value
- * Left/right are the appropriate shifts to convert to the pattern to be
- * used for the next 32/64-bit word
- */
-
-static void
-bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
- unsigned long pat, int left, int right, unsigned n, int bits)
-{
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(pat, *dst, first);
- } else {
- /* Multiple destination words */
- /* Leading bits */
- if (first) {
- *dst = comp(pat, *dst, first);
- dst++;
- pat = pat << left | pat >> right;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 4) {
- *dst++ = pat;
- pat = pat << left | pat >> right;
- *dst++ = pat;
- pat = pat << left | pat >> right;
- *dst++ = pat;
- pat = pat << left | pat >> right;
- *dst++ = pat;
- pat = pat << left | pat >> right;
- n -= 4;
- }
- while (n--) {
- *dst++ = pat;
- pat = pat << left | pat >> right;
- }
-
- /* Trailing bits */
- if (last)
- *dst = comp(pat, *dst, last);
- }
-}
-
- /*
- * Aligned pattern invert using 32/64-bit memory accesses
- */
-static void
-bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
- unsigned long pat, unsigned n, int bits)
-{
- unsigned long val = pat;
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(*dst ^ val, *dst, first);
- } else {
- /* Multiple destination words */
- /* Leading bits */
- if (first!=0UL) {
- *dst = comp(*dst ^ val, *dst, first);
- dst++;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 8) {
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- *dst++ ^= val;
- n -= 8;
- }
- while (n--)
- *dst++ ^= val;
- /* Trailing bits */
- if (last)
- *dst = comp(*dst ^ val, *dst, last);
- }
-}
-
-
- /*
- * Unaligned generic pattern invert using 32/64-bit memory accesses
- * The pattern must have been expanded to a full 32/64-bit value
- * Left/right are the appropriate shifts to convert to the pattern to be
- * used for the next 32/64-bit word
- */
-
-static void
-bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
- unsigned long pat, int left, int right, unsigned n,
- int bits)
-{
- unsigned long first, last;
-
- if (!n)
- return;
-
- first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
- last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
-
- if (dst_idx+n <= bits) {
- /* Single word */
- if (last)
- first &= last;
- *dst = comp(*dst ^ pat, *dst, first);
- } else {
- /* Multiple destination words */
-
- /* Leading bits */
- if (first != 0UL) {
- *dst = comp(*dst ^ pat, *dst, first);
- dst++;
- pat = pat << left | pat >> right;
- n -= bits - dst_idx;
- }
-
- /* Main chunk */
- n /= bits;
- while (n >= 4) {
- *dst++ ^= pat;
- pat = pat << left | pat >> right;
- *dst++ ^= pat;
- pat = pat << left | pat >> right;
- *dst++ ^= pat;
- pat = pat << left | pat >> right;
- *dst++ ^= pat;
- pat = pat << left | pat >> right;
- n -= 4;
- }
- while (n--) {
- *dst ^= pat;
- pat = pat << left | pat >> right;
- }
-
- /* Trailing bits */
- if (last)
- *dst = comp(*dst ^ pat, *dst, last);
- }
-}
-
-void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
-{
- unsigned long pat, pat2, fg;
- unsigned long width = rect->width, height = rect->height;
- int bits = BITS_PER_LONG, bytes = bits >> 3;
- u32 bpp = p->var.bits_per_pixel;
- unsigned long *dst;
- int dst_idx, left;
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR )
- fg = ((u32 *) (p->pseudo_palette))[rect->color];
- else
- fg = rect->color;
-
- pat = pixel_to_pat( bpp, fg);
-
- dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
- dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
- dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
- /* FIXME For now we support 1-32 bpp only */
- left = bits % bpp;
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
- if (!left) {
- void (*fill_op32)(struct fb_info *p, unsigned long *dst,
- int dst_idx, unsigned long pat, unsigned n,
- int bits) = NULL;
-
- switch (rect->rop) {
- case ROP_XOR:
- fill_op32 = bitfill_aligned_rev;
- break;
- case ROP_COPY:
- fill_op32 = bitfill_aligned;
- break;
- default:
- printk( KERN_ERR "cfb_fillrect(): unknown rop, "
- "defaulting to ROP_COPY\n");
- fill_op32 = bitfill_aligned;
- break;
- }
- while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bits - 1);
- fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
- dst_idx += p->fix.line_length*8;
- }
- } else {
- int right, r;
- void (*fill_op)(struct fb_info *p, unsigned long *dst,
- int dst_idx, unsigned long pat, int left,
- int right, unsigned n, int bits) = NULL;
-#ifdef __LITTLE_ENDIAN
- right = left;
- left = bpp - right;
-#else
- right = bpp - left;
-#endif
- switch (rect->rop) {
- case ROP_XOR:
- fill_op = bitfill_unaligned_rev;
- break;
- case ROP_COPY:
- fill_op = bitfill_unaligned;
- break;
- default:
- printk(KERN_ERR "sys_fillrect(): unknown rop, "
- "defaulting to ROP_COPY\n");
- fill_op = bitfill_unaligned;
- break;
- }
- while (height--) {
- dst += dst_idx / bits;
- dst_idx &= (bits - 1);
- r = dst_idx % bpp;
- /* rotate pattern to the correct start position */
- pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
- fill_op(p, dst, dst_idx, pat2, left, right,
- width*bpp, bits);
- dst_idx += p->fix.line_length*8;
- }
- }
-}
-
-EXPORT_SYMBOL(sys_fillrect);
-
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Generic 1-bit or 8-bit source to 1-32 bit destination expansion
- * for frame buffer located in system RAM with packed pixels of any depth.
- *
- * Based almost entirely on cfbimgblt.c
- *
- * Copyright (C) April 2007 Antonino Daplas <adaplas@pol.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- */
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-#include <asm/types.h>
-
-#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-static const u32 cfb_tab8_be[] = {
- 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
- 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
- 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
- 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
-};
-
-static const u32 cfb_tab8_le[] = {
- 0x00000000,0xff000000,0x00ff0000,0xffff0000,
- 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
- 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
- 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
-};
-
-static const u32 cfb_tab16_be[] = {
- 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
-};
-
-static const u32 cfb_tab16_le[] = {
- 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
-};
-
-static const u32 cfb_tab32[] = {
- 0x00000000, 0xffffffff
-};
-
-static void color_imageblit(const struct fb_image *image, struct fb_info *p,
- void *dst1, u32 start_index, u32 pitch_index)
-{
- /* Draw the penguin */
- u32 *dst, *dst2;
- u32 color = 0, val, shift;
- int i, n, bpp = p->var.bits_per_pixel;
- u32 null_bits = 32 - bpp;
- u32 *palette = (u32 *) p->pseudo_palette;
- const u8 *src = image->data;
-
- dst2 = dst1;
- for (i = image->height; i--; ) {
- n = image->width;
- dst = dst1;
- shift = 0;
- val = 0;
-
- if (start_index) {
- u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
- start_index));
- val = *dst & start_mask;
- shift = start_index;
- }
- while (n--) {
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR )
- color = palette[*src];
- else
- color = *src;
- color <<= FB_LEFT_POS(p, bpp);
- val |= FB_SHIFT_HIGH(p, color, shift);
- if (shift >= null_bits) {
- *dst++ = val;
-
- val = (shift == null_bits) ? 0 :
- FB_SHIFT_LOW(p, color, 32 - shift);
- }
- shift += bpp;
- shift &= (32 - 1);
- src++;
- }
- if (shift) {
- u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
-
- *dst &= end_mask;
- *dst |= val;
- }
- dst1 += p->fix.line_length;
- if (pitch_index) {
- dst2 += p->fix.line_length;
- dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
-
- start_index += pitch_index;
- start_index &= 32 - 1;
- }
- }
-}
-
-static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
- void *dst1, u32 fgcolor, u32 bgcolor,
- u32 start_index, u32 pitch_index)
-{
- u32 shift, color = 0, bpp = p->var.bits_per_pixel;
- u32 *dst, *dst2;
- u32 val, pitch = p->fix.line_length;
- u32 null_bits = 32 - bpp;
- u32 spitch = (image->width+7)/8;
- const u8 *src = image->data, *s;
- u32 i, j, l;
-
- dst2 = dst1;
- fgcolor <<= FB_LEFT_POS(p, bpp);
- bgcolor <<= FB_LEFT_POS(p, bpp);
-
- for (i = image->height; i--; ) {
- shift = val = 0;
- l = 8;
- j = image->width;
- dst = dst1;
- s = src;
-
- /* write leading bits */
- if (start_index) {
- u32 start_mask = ~(FB_SHIFT_HIGH(p, ~(u32)0,
- start_index));
- val = *dst & start_mask;
- shift = start_index;
- }
-
- while (j--) {
- l--;
- color = (*s & (1 << l)) ? fgcolor : bgcolor;
- val |= FB_SHIFT_HIGH(p, color, shift);
-
- /* Did the bitshift spill bits to the next long? */
- if (shift >= null_bits) {
- *dst++ = val;
- val = (shift == null_bits) ? 0 :
- FB_SHIFT_LOW(p, color, 32 - shift);
- }
- shift += bpp;
- shift &= (32 - 1);
- if (!l) { l = 8; s++; }
- }
-
- /* write trailing bits */
- if (shift) {
- u32 end_mask = FB_SHIFT_HIGH(p, ~(u32)0, shift);
-
- *dst &= end_mask;
- *dst |= val;
- }
-
- dst1 += pitch;
- src += spitch;
- if (pitch_index) {
- dst2 += pitch;
- dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
- start_index += pitch_index;
- start_index &= 32 - 1;
- }
-
- }
-}
-
-/*
- * fast_imageblit - optimized monochrome color expansion
- *
- * Only if: bits_per_pixel == 8, 16, or 32
- * image->width is divisible by pixel/dword (ppw);
- * fix->line_legth is divisible by 4;
- * beginning and end of a scanline is dword aligned
- */
-static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
- void *dst1, u32 fgcolor, u32 bgcolor)
-{
- u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
- u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
- u32 bit_mask, end_mask, eorx, shift;
- const char *s = image->data, *src;
- u32 *dst;
- const u32 *tab = NULL;
- int i, j, k;
-
- switch (bpp) {
- case 8:
- tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
- break;
- case 16:
- tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
- break;
- case 32:
- default:
- tab = cfb_tab32;
- break;
- }
-
- for (i = ppw-1; i--; ) {
- fgx <<= bpp;
- bgx <<= bpp;
- fgx |= fgcolor;
- bgx |= bgcolor;
- }
-
- bit_mask = (1 << ppw) - 1;
- eorx = fgx ^ bgx;
- k = image->width/ppw;
-
- for (i = image->height; i--; ) {
- dst = dst1;
- shift = 8;
- src = s;
-
- for (j = k; j--; ) {
- shift -= ppw;
- end_mask = tab[(*src >> shift) & bit_mask];
- *dst++ = (end_mask & eorx) ^ bgx;
- if (!shift) {
- shift = 8;
- src++;
- }
- }
- dst1 += p->fix.line_length;
- s += spitch;
- }
-}
-
-void sys_imageblit(struct fb_info *p, const struct fb_image *image)
-{
- u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
- u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
- u32 width = image->width;
- u32 dx = image->dx, dy = image->dy;
- void *dst1;
-
- if (p->state != FBINFO_STATE_RUNNING)
- return;
-
- bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
- start_index = bitstart & (32 - 1);
- pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
-
- bitstart /= 8;
- bitstart &= ~(bpl - 1);
- dst1 = (void __force *)p->screen_base + bitstart;
-
- if (p->fbops->fb_sync)
- p->fbops->fb_sync(p);
-
- if (image->depth == 1) {
- if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
- p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
- fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
- bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
- } else {
- fgcolor = image->fg_color;
- bgcolor = image->bg_color;
- }
-
- if (32 % bpp == 0 && !start_index && !pitch_index &&
- ((width & (32/bpp-1)) == 0) &&
- bpp >= 8 && bpp <= 32)
- fast_imageblit(image, p, dst1, fgcolor, bgcolor);
- else
- slow_imageblit(image, p, dst1, fgcolor, bgcolor,
- start_index, pitch_index);
- } else
- color_imageblit(image, p, dst1, start_index, pitch_index);
-}
-
-EXPORT_SYMBOL(sys_imageblit);
-
-MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
-MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)");
-MODULE_LICENSE("GPL");
-
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
-#include "fb_draw.h"
+#include "core/fb_draw.h"
#define GE_COMMAND_OFF 0x00
#define GE_DEPTH_OFF 0x04