blob: 9d4a5735522e0df041e25d9229ca162016679f97 [file] [log] [blame]
/*
* 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:
*
* The code for depths like 24 that don't have integer number of pixels per
* long is broken and needs to be fixed. For now I turned these types of
* mode off.
*
* 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/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fb.h>
#include <asm/types.h>
#if BITS_PER_LONG == 32
#define FB_WRITEL fb_writel
#define FB_READL fb_readl
#define BYTES_PER_LONG 4
#define SHIFT_PER_LONG 5
#else
#define FB_WRITEL fb_writeq
#define FB_READL fb_readq
#define BYTES_PER_LONG 8
#define SHIFT_PER_LONG 6
#endif
#define EXP1(x) 0xffffffffU*x
#define EXP2(x) 0x55555555U*x
#define EXP4(x) 0x11111111U*0x ## x
typedef u32 pixel_t;
static const u32 bpp1tab[2] = {
EXP1(0), EXP1(1)
};
static const u32 bpp2tab[4] = {
EXP2(0), EXP2(1), EXP2(2), EXP2(3)
};
static const u32 bpp4tab[16] = {
EXP4(0), EXP4(1), EXP4(2), EXP4(3), EXP4(4), EXP4(5), EXP4(6), EXP4(7),
EXP4(8), EXP4(9), EXP4(a), EXP4(b), EXP4(c), EXP4(d), EXP4(e), EXP4(f)
};
/*
* 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;
}
static inline u32 pixel_to_pat32(const struct fb_info *p, pixel_t pixel)
{
u32 pat = pixel;
switch (p->var.bits_per_pixel) {
case 1:
pat = bpp1tab[pat];
break;
case 2:
pat = bpp2tab[pat];
break;
case 4:
pat = bpp4tab[pat];
break;
case 8:
pat |= pat << 8;
// Fall through
case 16:
pat |= pat << 16;
// Fall through
case 32:
break;
}
return pat;
}
/*
* Expand a pixel value to a generic 32/64-bit pattern and rotate it to
* the correct start position
*/
static inline unsigned long pixel_to_pat(const struct fb_info *p, pixel_t pixel, int left)
{
unsigned long pat = pixel;
u32 bpp = p->var.bits_per_pixel;
int i;
/* expand pixel value */
for (i = bpp; i < BITS_PER_LONG; i *= 2)
pat |= pat << i;
/* rotate pattern to correct start position */
pat = pat << left | pat >> (bpp-left);
return pat;
}
/*
* Unaligned 32-bit pattern fill using 32/64-bit memory accesses
*/
void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
{
unsigned long val = pat;
unsigned long first, last;
if (!n)
return;
#if BITS_PER_LONG == 64
val |= val << 32;
#endif
first = ~0UL >> dst_idx;
last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
if (dst_idx+n <= BITS_PER_LONG) {
// Single word
if (last)
first &= last;
FB_WRITEL(comp(val, FB_READL(dst), first), dst);
} else {
// Multiple destination words
// Leading bits
if (first) {
FB_WRITEL(comp(val, FB_READL(dst), first), dst);
dst++;
n -= BITS_PER_LONG-dst_idx;
}
// Main chunk
n /= BITS_PER_LONG;
while (n >= 8) {
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
FB_WRITEL(val, dst++);
n -= 8;
}
while (n--)
FB_WRITEL(val, dst++);
// Trailing bits
if (last)
FB_WRITEL(comp(val, FB_READL(dst), first), 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
*/
void bitfill(unsigned long *dst, int dst_idx, unsigned long pat, int left,
int right, u32 n)
{
unsigned long first, last;
if (!n)
return;
first = ~0UL >> dst_idx;
last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
if (dst_idx+n <= BITS_PER_LONG) {
// 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_PER_LONG-dst_idx;
}
// Main chunk
n /= BITS_PER_LONG;
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), first), dst);
}
}
void bitfill32_rev(unsigned long *dst, int dst_idx, u32 pat, u32 n)
{
unsigned long val = pat, dat;
unsigned long first, last;
if (!n)
return;
#if BITS_PER_LONG == 64
val |= val << 32;
#endif
first = ~0UL >> dst_idx;
last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
if (dst_idx+n <= BITS_PER_LONG) {
// 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) {
dat = FB_READL(dst);
FB_WRITEL(comp(dat ^ val, dat, first), dst);
dst++;
n -= BITS_PER_LONG-dst_idx;
}
// Main chunk
n /= BITS_PER_LONG;
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, first), 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
*/
void bitfill_rev(unsigned long *dst, int dst_idx, unsigned long pat, int left,
int right, u32 n)
{
unsigned long first, last, dat;
if (!n)
return;
first = ~0UL >> dst_idx;
last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
if (dst_idx+n <= BITS_PER_LONG) {
// 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) {
dat = FB_READL(dst);
FB_WRITEL(comp(dat ^ pat, dat, first), dst);
dst++;
pat = pat << left | pat >> right;
n -= BITS_PER_LONG-dst_idx;
}
// Main chunk
n /= BITS_PER_LONG;
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, first), dst);
}
}
}
void cfb_fillrect(struct fb_info *p, struct fb_fillrect *rect)
{
unsigned long height, fg;
unsigned long x2, y2, vxres, vyres;
unsigned long *dst;
int dst_idx, left;
u32 bpp = p->var.bits_per_pixel;
/* We want rotation but lack hardware to do it for us. */
if (!p->fbops->fb_rotate && p->var.rotate) {
}
vxres = p->var.xres_virtual;
vyres = p->var.yres_virtual;
if (!rect->width || !rect->height || rect->dx > vxres || rect->dy > vyres)
return;
/* We could use hardware clipping but on many cards you get around
* hardware clipping by writing to framebuffer directly. */
x2 = rect->dx + rect->width;
y2 = rect->dy + rect->height;
x2 = x2 < vxres ? x2 : vxres;
y2 = y2 < vyres ? y2 : vyres;
rect->width = x2 - rect->dx;
height = y2 - rect->dy;
if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
p->fix.visual == FB_VISUAL_DIRECTCOLOR )
fg = ((u32 *) (p->pseudo_palette))[rect->color];
else
fg = rect->color;
dst = (unsigned long *)((unsigned long)p->screen_base & ~(BYTES_PER_LONG-1));
dst_idx = ((unsigned long)p->screen_base & (BYTES_PER_LONG-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_PER_LONG % bpp;
if (!left) {
u32 pat = pixel_to_pat32(p, fg);
void (*fill_op32)(unsigned long *dst, int dst_idx, u32 pat, u32 n) = NULL;
switch (rect->rop) {
case ROP_XOR:
fill_op32 = bitfill32_rev;
break;
case ROP_COPY:
default:
fill_op32 = bitfill32;
break;
}
while (height--) {
dst += dst_idx >> SHIFT_PER_LONG;
dst_idx &= (BITS_PER_LONG-1);
fill_op32(dst, dst_idx, pat, rect->width*bpp);
dst_idx += p->fix.line_length*8;
}
} else {
unsigned long pat = pixel_to_pat(p, fg, (left-dst_idx) % bpp);
int right = bpp-left;
int r;
void (*fill_op)(unsigned long *dst, int dst_idx, unsigned long pat,
int left, int right, u32 n) = NULL;
switch (rect->rop) {
case ROP_XOR:
fill_op = bitfill_rev;
break;
case ROP_COPY:
default:
fill_op = bitfill;
break;
}
while (height--) {
dst += dst_idx >> SHIFT_PER_LONG;
dst_idx &= (BITS_PER_LONG-1);
fill_op(dst, dst_idx, pat, left, right, rect->width*bpp);
r = (p->fix.line_length*8) % bpp;
pat = pat << (bpp-r) | pat >> r;
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");