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