linux/drivers/video/cfbcopyarea.c
<<
>>
Prefs
   1/*
   2 *  Generic function for frame buffer with packed pixels of any depth.
   3 *
   4 *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
   5 *
   6 *  This file is subject to the terms and conditions of the GNU General Public
   7 *  License.  See the file COPYING in the main directory of this archive for
   8 *  more details.
   9 *
  10 * NOTES:
  11 *
  12 *  This is for cfb packed pixels. Iplan and such are incorporated in the
  13 *  drivers that need them.
  14 *
  15 *  FIXME
  16 *
  17 *  Also need to add code to deal with cards endians that are different than
  18 *  the native cpu endians. I also need to deal with MSB position in the word.
  19 *
  20 *  The two functions or copying forward and backward could be split up like
  21 *  the ones for filling, i.e. in aligned and unaligned versions. This would
  22 *  help moving some redundant computations and branches out of the loop, too.
  23 */
  24
  25#include <linux/module.h>
  26#include <linux/kernel.h>
  27#include <linux/string.h>
  28#include <linux/fb.h>
  29#include <linux/slab.h>
  30#include <asm/types.h>
  31#include <asm/io.h>
  32#include "fb_draw.h"
  33
  34#if BITS_PER_LONG == 32
  35#  define FB_WRITEL fb_writel
  36#  define FB_READL  fb_readl
  37#else
  38#  define FB_WRITEL fb_writeq
  39#  define FB_READL  fb_readq
  40#endif
  41
  42    /*
  43     *  Generic bitwise copy algorithm
  44     */
  45
  46static void
  47bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
  48                const unsigned long __iomem *src, int src_idx, int bits,
  49                unsigned n, u32 bswapmask)
  50{
  51        unsigned long first, last;
  52        int const shift = dst_idx-src_idx;
  53        int left, right;
  54
  55        first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
  56        last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
  57
  58        if (!shift) {
  59                // Same alignment for source and dest
  60
  61                if (dst_idx+n <= bits) {
  62                        // Single word
  63                        if (last)
  64                                first &= last;
  65                        FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  66                } else {
  67                        // Multiple destination words
  68
  69                        // Leading bits
  70                        if (first != ~0UL) {
  71                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
  72                                dst++;
  73                                src++;
  74                                n -= bits - dst_idx;
  75                        }
  76
  77                        // Main chunk
  78                        n /= bits;
  79                        while (n >= 8) {
  80                                FB_WRITEL(FB_READL(src++), dst++);
  81                                FB_WRITEL(FB_READL(src++), dst++);
  82                                FB_WRITEL(FB_READL(src++), dst++);
  83                                FB_WRITEL(FB_READL(src++), dst++);
  84                                FB_WRITEL(FB_READL(src++), dst++);
  85                                FB_WRITEL(FB_READL(src++), dst++);
  86                                FB_WRITEL(FB_READL(src++), dst++);
  87                                FB_WRITEL(FB_READL(src++), dst++);
  88                                n -= 8;
  89                        }
  90                        while (n--)
  91                                FB_WRITEL(FB_READL(src++), dst++);
  92
  93                        // Trailing bits
  94                        if (last)
  95                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
  96                }
  97        } else {
  98                /* Different alignment for source and dest */
  99                unsigned long d0, d1;
 100                int m;
 101
 102                right = shift & (bits - 1);
 103                left = -shift & (bits - 1);
 104                bswapmask &= shift;
 105
 106                if (dst_idx+n <= bits) {
 107                        // Single destination word
 108                        if (last)
 109                                first &= last;
 110                        d0 = FB_READL(src);
 111                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 112                        if (shift > 0) {
 113                                // Single source word
 114                                d0 >>= right;
 115                        } else if (src_idx+n <= bits) {
 116                                // Single source word
 117                                d0 <<= left;
 118                        } else {
 119                                // 2 source words
 120                                d1 = FB_READL(src + 1);
 121                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 122                                d0 = d0<<left | d1>>right;
 123                        }
 124                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 125                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 126                } else {
 127                        // Multiple destination words
 128                        /** We must always remember the last value read, because in case
 129                        SRC and DST overlap bitwise (e.g. when moving just one pixel in
 130                        1bpp), we always collect one full long for DST and that might
 131                        overlap with the current long from SRC. We store this value in
 132                        'd0'. */
 133                        d0 = FB_READL(src++);
 134                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 135                        // Leading bits
 136                        if (shift > 0) {
 137                                // Single source word
 138                                d1 = d0;
 139                                d0 >>= right;
 140                                dst++;
 141                                n -= bits - dst_idx;
 142                        } else {
 143                                // 2 source words
 144                                d1 = FB_READL(src++);
 145                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 146
 147                                d0 = d0<<left | d1>>right;
 148                                dst++;
 149                                n -= bits - dst_idx;
 150                        }
 151                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 152                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 153                        d0 = d1;
 154
 155                        // Main chunk
 156                        m = n % bits;
 157                        n /= bits;
 158                        while ((n >= 4) && !bswapmask) {
 159                                d1 = FB_READL(src++);
 160                                FB_WRITEL(d0 << left | d1 >> right, dst++);
 161                                d0 = d1;
 162                                d1 = FB_READL(src++);
 163                                FB_WRITEL(d0 << left | d1 >> right, dst++);
 164                                d0 = d1;
 165                                d1 = FB_READL(src++);
 166                                FB_WRITEL(d0 << left | d1 >> right, dst++);
 167                                d0 = d1;
 168                                d1 = FB_READL(src++);
 169                                FB_WRITEL(d0 << left | d1 >> right, dst++);
 170                                d0 = d1;
 171                                n -= 4;
 172                        }
 173                        while (n--) {
 174                                d1 = FB_READL(src++);
 175                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 176                                d0 = d0 << left | d1 >> right;
 177                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 178                                FB_WRITEL(d0, dst++);
 179                                d0 = d1;
 180                        }
 181
 182                        // Trailing bits
 183                        if (last) {
 184                                if (m <= right) {
 185                                        // Single source word
 186                                        d0 <<= left;
 187                                } else {
 188                                        // 2 source words
 189                                        d1 = FB_READL(src);
 190                                        d1 = fb_rev_pixels_in_long(d1,
 191                                                                bswapmask);
 192                                        d0 = d0<<left | d1>>right;
 193                                }
 194                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 195                                FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
 196                        }
 197                }
 198        }
 199}
 200
 201    /*
 202     *  Generic bitwise copy algorithm, operating backward
 203     */
 204
 205static void
 206bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
 207                const unsigned long __iomem *src, int src_idx, int bits,
 208                unsigned n, u32 bswapmask)
 209{
 210        unsigned long first, last;
 211        int shift;
 212
 213        dst += (n-1)/bits;
 214        src += (n-1)/bits;
 215        if ((n-1) % bits) {
 216                dst_idx += (n-1) % bits;
 217                dst += dst_idx >> (ffs(bits) - 1);
 218                dst_idx &= bits - 1;
 219                src_idx += (n-1) % bits;
 220                src += src_idx >> (ffs(bits) - 1);
 221                src_idx &= bits - 1;
 222        }
 223
 224        shift = dst_idx-src_idx;
 225
 226        first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
 227        last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
 228                                            bswapmask);
 229
 230        if (!shift) {
 231                // Same alignment for source and dest
 232
 233                if ((unsigned long)dst_idx+1 >= n) {
 234                        // Single word
 235                        if (last)
 236                                first &= last;
 237                        FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
 238                } else {
 239                        // Multiple destination words
 240
 241                        // Leading bits
 242                        if (first != ~0UL) {
 243                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
 244                                dst--;
 245                                src--;
 246                                n -= dst_idx+1;
 247                        }
 248
 249                        // Main chunk
 250                        n /= bits;
 251                        while (n >= 8) {
 252                                FB_WRITEL(FB_READL(src--), dst--);
 253                                FB_WRITEL(FB_READL(src--), dst--);
 254                                FB_WRITEL(FB_READL(src--), dst--);
 255                                FB_WRITEL(FB_READL(src--), dst--);
 256                                FB_WRITEL(FB_READL(src--), dst--);
 257                                FB_WRITEL(FB_READL(src--), dst--);
 258                                FB_WRITEL(FB_READL(src--), dst--);
 259                                FB_WRITEL(FB_READL(src--), dst--);
 260                                n -= 8;
 261                        }
 262                        while (n--)
 263                                FB_WRITEL(FB_READL(src--), dst--);
 264
 265                        // Trailing bits
 266                        if (last)
 267                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
 268                }
 269        } else {
 270                // Different alignment for source and dest
 271                unsigned long d0, d1;
 272                int m;
 273
 274                int const left = -shift & (bits-1);
 275                int const right = shift & (bits-1);
 276                bswapmask &= shift;
 277
 278                if ((unsigned long)dst_idx+1 >= n) {
 279                        // Single destination word
 280                        if (last)
 281                                first &= last;
 282                        d0 = FB_READL(src);
 283                        if (shift < 0) {
 284                                // Single source word
 285                                d0 <<= left;
 286                        } else if (1+(unsigned long)src_idx >= n) {
 287                                // Single source word
 288                                d0 >>= right;
 289                        } else {
 290                                // 2 source words
 291                                d1 = FB_READL(src - 1);
 292                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 293                                d0 = d0>>right | d1<<left;
 294                        }
 295                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 296                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 297                } else {
 298                        // Multiple destination words
 299                        /** We must always remember the last value read, because in case
 300                        SRC and DST overlap bitwise (e.g. when moving just one pixel in
 301                        1bpp), we always collect one full long for DST and that might
 302                        overlap with the current long from SRC. We store this value in
 303                        'd0'. */
 304
 305                        d0 = FB_READL(src--);
 306                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 307                        // Leading bits
 308                        if (shift < 0) {
 309                                // Single source word
 310                                d1 = d0;
 311                                d0 <<= left;
 312                        } else {
 313                                // 2 source words
 314                                d1 = FB_READL(src--);
 315                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 316                                d0 = d0>>right | d1<<left;
 317                        }
 318                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 319                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 320                        d0 = d1;
 321                        dst--;
 322                        n -= dst_idx+1;
 323
 324                        // Main chunk
 325                        m = n % bits;
 326                        n /= bits;
 327                        while ((n >= 4) && !bswapmask) {
 328                                d1 = FB_READL(src--);
 329                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 330                                d0 = d1;
 331                                d1 = FB_READL(src--);
 332                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 333                                d0 = d1;
 334                                d1 = FB_READL(src--);
 335                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 336                                d0 = d1;
 337                                d1 = FB_READL(src--);
 338                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 339                                d0 = d1;
 340                                n -= 4;
 341                        }
 342                        while (n--) {
 343                                d1 = FB_READL(src--);
 344                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 345                                d0 = d0 >> right | d1 << left;
 346                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 347                                FB_WRITEL(d0, dst--);
 348                                d0 = d1;
 349                        }
 350
 351                        // Trailing bits
 352                        if (last) {
 353                                if (m <= left) {
 354                                        // Single source word
 355                                        d0 >>= right;
 356                                } else {
 357                                        // 2 source words
 358                                        d1 = FB_READL(src);
 359                                        d1 = fb_rev_pixels_in_long(d1,
 360                                                                bswapmask);
 361                                        d0 = d0>>right | d1<<left;
 362                                }
 363                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 364                                FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
 365                        }
 366                }
 367        }
 368}
 369
 370void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
 371{
 372        u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
 373        u32 height = area->height, width = area->width;
 374        unsigned long const bits_per_line = p->fix.line_length*8u;
 375        unsigned long __iomem *dst = NULL, *src = NULL;
 376        int bits = BITS_PER_LONG, bytes = bits >> 3;
 377        int dst_idx = 0, src_idx = 0, rev_copy = 0;
 378        u32 bswapmask = fb_compute_bswapmask(p);
 379
 380        if (p->state != FBINFO_STATE_RUNNING)
 381                return;
 382
 383        /* if the beginning of the target area might overlap with the end of
 384        the source area, be have to copy the area reverse. */
 385        if ((dy == sy && dx > sx) || (dy > sy)) {
 386                dy += height;
 387                sy += height;
 388                rev_copy = 1;
 389        }
 390
 391        // split the base of the framebuffer into a long-aligned address and the
 392        // index of the first bit
 393        dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
 394        dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
 395        // add offset of source and target area
 396        dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
 397        src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
 398
 399        if (p->fbops->fb_sync)
 400                p->fbops->fb_sync(p);
 401
 402        if (rev_copy) {
 403                while (height--) {
 404                        dst_idx -= bits_per_line;
 405                        src_idx -= bits_per_line;
 406                        dst += dst_idx >> (ffs(bits) - 1);
 407                        dst_idx &= (bytes - 1);
 408                        src += src_idx >> (ffs(bits) - 1);
 409                        src_idx &= (bytes - 1);
 410                        bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
 411                                width*p->var.bits_per_pixel, bswapmask);
 412                }
 413        } else {
 414                while (height--) {
 415                        dst += dst_idx >> (ffs(bits) - 1);
 416                        dst_idx &= (bytes - 1);
 417                        src += src_idx >> (ffs(bits) - 1);
 418                        src_idx &= (bytes - 1);
 419                        bitcpy(p, dst, dst_idx, src, src_idx, bits,
 420                                width*p->var.bits_per_pixel, bswapmask);
 421                        dst_idx += bits_per_line;
 422                        src_idx += bits_per_line;
 423                }
 424        }
 425}
 426
 427EXPORT_SYMBOL(cfb_copyarea);
 428
 429MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
 430MODULE_DESCRIPTION("Generic software accelerated copyarea");
 431MODULE_LICENSE("GPL");
 432
 433