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 <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, int dst_idx,
  47                const unsigned long __iomem *src, int src_idx, int bits,
  48                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(p, dst_idx, bswapmask);
  55        last = ~fb_shifted_pixels_mask_long(p, (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(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
 206                const unsigned long __iomem *src, int src_idx, int bits,
 207                unsigned n, u32 bswapmask)
 208{
 209        unsigned long first, last;
 210        int shift;
 211
 212        dst += (n-1)/bits;
 213        src += (n-1)/bits;
 214        if ((n-1) % bits) {
 215                dst_idx += (n-1) % bits;
 216                dst += dst_idx >> (ffs(bits) - 1);
 217                dst_idx &= bits - 1;
 218                src_idx += (n-1) % bits;
 219                src += src_idx >> (ffs(bits) - 1);
 220                src_idx &= bits - 1;
 221        }
 222
 223        shift = dst_idx-src_idx;
 224
 225        first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
 226        last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
 227                                            bswapmask);
 228
 229        if (!shift) {
 230                // Same alignment for source and dest
 231
 232                if ((unsigned long)dst_idx+1 >= n) {
 233                        // Single word
 234                        if (last)
 235                                first &= last;
 236                        FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
 237                } else {
 238                        // Multiple destination words
 239
 240                        // Leading bits
 241                        if (first != ~0UL) {
 242                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
 243                                dst--;
 244                                src--;
 245                                n -= dst_idx+1;
 246                        }
 247
 248                        // Main chunk
 249                        n /= bits;
 250                        while (n >= 8) {
 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                                FB_WRITEL(FB_READL(src--), dst--);
 258                                FB_WRITEL(FB_READL(src--), dst--);
 259                                n -= 8;
 260                        }
 261                        while (n--)
 262                                FB_WRITEL(FB_READL(src--), dst--);
 263
 264                        // Trailing bits
 265                        if (last)
 266                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
 267                }
 268        } else {
 269                // Different alignment for source and dest
 270                unsigned long d0, d1;
 271                int m;
 272
 273                int const left = -shift & (bits-1);
 274                int const right = shift & (bits-1);
 275                bswapmask &= shift;
 276
 277                if ((unsigned long)dst_idx+1 >= n) {
 278                        // Single destination word
 279                        if (last)
 280                                first &= last;
 281                        d0 = FB_READL(src);
 282                        if (shift < 0) {
 283                                // Single source word
 284                                d0 <<= left;
 285                        } else if (1+(unsigned long)src_idx >= n) {
 286                                // Single source word
 287                                d0 >>= right;
 288                        } else {
 289                                // 2 source words
 290                                d1 = FB_READL(src - 1);
 291                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 292                                d0 = d0>>right | d1<<left;
 293                        }
 294                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 295                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 296                } else {
 297                        // Multiple destination words
 298                        /** We must always remember the last value read, because in case
 299                        SRC and DST overlap bitwise (e.g. when moving just one pixel in
 300                        1bpp), we always collect one full long for DST and that might
 301                        overlap with the current long from SRC. We store this value in
 302                        'd0'. */
 303
 304                        d0 = FB_READL(src--);
 305                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 306                        // Leading bits
 307                        if (shift < 0) {
 308                                // Single source word
 309                                d1 = d0;
 310                                d0 <<= left;
 311                        } else {
 312                                // 2 source words
 313                                d1 = FB_READL(src--);
 314                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 315                                d0 = d0>>right | d1<<left;
 316                        }
 317                        d0 = fb_rev_pixels_in_long(d0, bswapmask);
 318                        FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
 319                        d0 = d1;
 320                        dst--;
 321                        n -= dst_idx+1;
 322
 323                        // Main chunk
 324                        m = n % bits;
 325                        n /= bits;
 326                        while ((n >= 4) && !bswapmask) {
 327                                d1 = FB_READL(src--);
 328                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 329                                d0 = d1;
 330                                d1 = FB_READL(src--);
 331                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 332                                d0 = d1;
 333                                d1 = FB_READL(src--);
 334                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 335                                d0 = d1;
 336                                d1 = FB_READL(src--);
 337                                FB_WRITEL(d0 >> right | d1 << left, dst--);
 338                                d0 = d1;
 339                                n -= 4;
 340                        }
 341                        while (n--) {
 342                                d1 = FB_READL(src--);
 343                                d1 = fb_rev_pixels_in_long(d1, bswapmask);
 344                                d0 = d0 >> right | d1 << left;
 345                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 346                                FB_WRITEL(d0, dst--);
 347                                d0 = d1;
 348                        }
 349
 350                        // Trailing bits
 351                        if (last) {
 352                                if (m <= left) {
 353                                        // Single source word
 354                                        d0 >>= right;
 355                                } else {
 356                                        // 2 source words
 357                                        d1 = FB_READL(src);
 358                                        d1 = fb_rev_pixels_in_long(d1,
 359                                                                bswapmask);
 360                                        d0 = d0>>right | d1<<left;
 361                                }
 362                                d0 = fb_rev_pixels_in_long(d0, bswapmask);
 363                                FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
 364                        }
 365                }
 366        }
 367}
 368
 369void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
 370{
 371        u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
 372        u32 height = area->height, width = area->width;
 373        unsigned long const bits_per_line = p->fix.line_length*8u;
 374        unsigned long __iomem *dst = NULL, *src = NULL;
 375        int bits = BITS_PER_LONG, bytes = bits >> 3;
 376        int dst_idx = 0, src_idx = 0, rev_copy = 0;
 377        u32 bswapmask = fb_compute_bswapmask(p);
 378
 379        if (p->state != FBINFO_STATE_RUNNING)
 380                return;
 381
 382        /* if the beginning of the target area might overlap with the end of
 383        the source area, be have to copy the area reverse. */
 384        if ((dy == sy && dx > sx) || (dy > sy)) {
 385                dy += height;
 386                sy += height;
 387                rev_copy = 1;
 388        }
 389
 390        // split the base of the framebuffer into a long-aligned address and the
 391        // index of the first bit
 392        dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
 393        dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
 394        // add offset of source and target area
 395        dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
 396        src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
 397
 398        if (p->fbops->fb_sync)
 399                p->fbops->fb_sync(p);
 400
 401        if (rev_copy) {
 402                while (height--) {
 403                        dst_idx -= bits_per_line;
 404                        src_idx -= bits_per_line;
 405                        dst += dst_idx >> (ffs(bits) - 1);
 406                        dst_idx &= (bytes - 1);
 407                        src += src_idx >> (ffs(bits) - 1);
 408                        src_idx &= (bytes - 1);
 409                        bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
 410                                width*p->var.bits_per_pixel, bswapmask);
 411                }
 412        } else {
 413                while (height--) {
 414                        dst += dst_idx >> (ffs(bits) - 1);
 415                        dst_idx &= (bytes - 1);
 416                        src += src_idx >> (ffs(bits) - 1);
 417                        src_idx &= (bytes - 1);
 418                        bitcpy(p, dst, dst_idx, src, src_idx, bits,
 419                                width*p->var.bits_per_pixel, bswapmask);
 420                        dst_idx += bits_per_line;
 421                        src_idx += bits_per_line;
 422                }
 423        }
 424}
 425
 426EXPORT_SYMBOL(cfb_copyarea);
 427
 428MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
 429MODULE_DESCRIPTION("Generic software accelerated copyarea");
 430MODULE_LICENSE("GPL");
 431
 432