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