119757fc8STomi Valkeinen /* 219757fc8STomi Valkeinen * Generic fillrect for frame buffers with packed pixels of any depth. 319757fc8STomi Valkeinen * 419757fc8STomi Valkeinen * Copyright (C) 2000 James Simmons ([email protected]) 519757fc8STomi Valkeinen * 619757fc8STomi Valkeinen * This file is subject to the terms and conditions of the GNU General Public 719757fc8STomi Valkeinen * License. See the file COPYING in the main directory of this archive for 819757fc8STomi Valkeinen * more details. 919757fc8STomi Valkeinen * 1019757fc8STomi Valkeinen * NOTES: 1119757fc8STomi Valkeinen * 1219757fc8STomi Valkeinen * Also need to add code to deal with cards endians that are different than 1319757fc8STomi Valkeinen * the native cpu endians. I also need to deal with MSB position in the word. 1419757fc8STomi Valkeinen * 1519757fc8STomi Valkeinen */ 1619757fc8STomi Valkeinen #include <linux/module.h> 1719757fc8STomi Valkeinen #include <linux/string.h> 1819757fc8STomi Valkeinen #include <linux/fb.h> 1919757fc8STomi Valkeinen #include <asm/types.h> 2019757fc8STomi Valkeinen #include "fb_draw.h" 2119757fc8STomi Valkeinen 2219757fc8STomi Valkeinen #if BITS_PER_LONG == 32 2319757fc8STomi Valkeinen # define FB_WRITEL fb_writel 2419757fc8STomi Valkeinen # define FB_READL fb_readl 2519757fc8STomi Valkeinen #else 2619757fc8STomi Valkeinen # define FB_WRITEL fb_writeq 2719757fc8STomi Valkeinen # define FB_READL fb_readq 2819757fc8STomi Valkeinen #endif 2919757fc8STomi Valkeinen 3019757fc8STomi Valkeinen /* 3119757fc8STomi Valkeinen * Aligned pattern fill using 32/64-bit memory accesses 3219757fc8STomi Valkeinen */ 3319757fc8STomi Valkeinen 3419757fc8STomi Valkeinen static void 3519757fc8STomi Valkeinen bitfill_aligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, 3619757fc8STomi Valkeinen unsigned long pat, unsigned n, int bits, u32 bswapmask) 3719757fc8STomi Valkeinen { 3819757fc8STomi Valkeinen unsigned long first, last; 3919757fc8STomi Valkeinen 4019757fc8STomi Valkeinen if (!n) 4119757fc8STomi Valkeinen return; 4219757fc8STomi Valkeinen 4319757fc8STomi Valkeinen first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 4419757fc8STomi Valkeinen last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 4519757fc8STomi Valkeinen 4619757fc8STomi Valkeinen if (dst_idx+n <= bits) { 4719757fc8STomi Valkeinen // Single word 4819757fc8STomi Valkeinen if (last) 4919757fc8STomi Valkeinen first &= last; 5019757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 5119757fc8STomi Valkeinen } else { 5219757fc8STomi Valkeinen // Multiple destination words 5319757fc8STomi Valkeinen 5419757fc8STomi Valkeinen // Leading bits 5519757fc8STomi Valkeinen if (first!= ~0UL) { 5619757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 5719757fc8STomi Valkeinen dst++; 5819757fc8STomi Valkeinen n -= bits - dst_idx; 5919757fc8STomi Valkeinen } 6019757fc8STomi Valkeinen 6119757fc8STomi Valkeinen // Main chunk 6219757fc8STomi Valkeinen n /= bits; 6319757fc8STomi Valkeinen while (n >= 8) { 6419757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 6519757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 6619757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 6719757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 6819757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 6919757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 7019757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 7119757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 7219757fc8STomi Valkeinen n -= 8; 7319757fc8STomi Valkeinen } 7419757fc8STomi Valkeinen while (n--) 7519757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 7619757fc8STomi Valkeinen 7719757fc8STomi Valkeinen // Trailing bits 7819757fc8STomi Valkeinen if (last) 7919757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), last), dst); 8019757fc8STomi Valkeinen } 8119757fc8STomi Valkeinen } 8219757fc8STomi Valkeinen 8319757fc8STomi Valkeinen 8419757fc8STomi Valkeinen /* 8519757fc8STomi Valkeinen * Unaligned generic pattern fill using 32/64-bit memory accesses 8619757fc8STomi Valkeinen * The pattern must have been expanded to a full 32/64-bit value 8719757fc8STomi Valkeinen * Left/right are the appropriate shifts to convert to the pattern to be 8819757fc8STomi Valkeinen * used for the next 32/64-bit word 8919757fc8STomi Valkeinen */ 9019757fc8STomi Valkeinen 9119757fc8STomi Valkeinen static void 9219757fc8STomi Valkeinen bitfill_unaligned(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, 9319757fc8STomi Valkeinen unsigned long pat, int left, int right, unsigned n, int bits) 9419757fc8STomi Valkeinen { 9519757fc8STomi Valkeinen unsigned long first, last; 9619757fc8STomi Valkeinen 9719757fc8STomi Valkeinen if (!n) 9819757fc8STomi Valkeinen return; 9919757fc8STomi Valkeinen 10019757fc8STomi Valkeinen first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 10119757fc8STomi Valkeinen last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 10219757fc8STomi Valkeinen 10319757fc8STomi Valkeinen if (dst_idx+n <= bits) { 10419757fc8STomi Valkeinen // Single word 10519757fc8STomi Valkeinen if (last) 10619757fc8STomi Valkeinen first &= last; 10719757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 10819757fc8STomi Valkeinen } else { 10919757fc8STomi Valkeinen // Multiple destination words 11019757fc8STomi Valkeinen // Leading bits 11119757fc8STomi Valkeinen if (first) { 11219757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), first), dst); 11319757fc8STomi Valkeinen dst++; 11419757fc8STomi Valkeinen pat = pat << left | pat >> right; 11519757fc8STomi Valkeinen n -= bits - dst_idx; 11619757fc8STomi Valkeinen } 11719757fc8STomi Valkeinen 11819757fc8STomi Valkeinen // Main chunk 11919757fc8STomi Valkeinen n /= bits; 12019757fc8STomi Valkeinen while (n >= 4) { 12119757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 12219757fc8STomi Valkeinen pat = pat << left | pat >> right; 12319757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 12419757fc8STomi Valkeinen pat = pat << left | pat >> right; 12519757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 12619757fc8STomi Valkeinen pat = pat << left | pat >> right; 12719757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 12819757fc8STomi Valkeinen pat = pat << left | pat >> right; 12919757fc8STomi Valkeinen n -= 4; 13019757fc8STomi Valkeinen } 13119757fc8STomi Valkeinen while (n--) { 13219757fc8STomi Valkeinen FB_WRITEL(pat, dst++); 13319757fc8STomi Valkeinen pat = pat << left | pat >> right; 13419757fc8STomi Valkeinen } 13519757fc8STomi Valkeinen 13619757fc8STomi Valkeinen // Trailing bits 13719757fc8STomi Valkeinen if (last) 13819757fc8STomi Valkeinen FB_WRITEL(comp(pat, FB_READL(dst), last), dst); 13919757fc8STomi Valkeinen } 14019757fc8STomi Valkeinen } 14119757fc8STomi Valkeinen 14219757fc8STomi Valkeinen /* 14319757fc8STomi Valkeinen * Aligned pattern invert using 32/64-bit memory accesses 14419757fc8STomi Valkeinen */ 14519757fc8STomi Valkeinen static void 14619757fc8STomi Valkeinen bitfill_aligned_rev(struct fb_info *p, unsigned long __iomem *dst, 14719757fc8STomi Valkeinen int dst_idx, unsigned long pat, unsigned n, int bits, 14819757fc8STomi Valkeinen u32 bswapmask) 14919757fc8STomi Valkeinen { 15019757fc8STomi Valkeinen unsigned long val = pat, dat; 15119757fc8STomi Valkeinen unsigned long first, last; 15219757fc8STomi Valkeinen 15319757fc8STomi Valkeinen if (!n) 15419757fc8STomi Valkeinen return; 15519757fc8STomi Valkeinen 15619757fc8STomi Valkeinen first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 15719757fc8STomi Valkeinen last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 15819757fc8STomi Valkeinen 15919757fc8STomi Valkeinen if (dst_idx+n <= bits) { 16019757fc8STomi Valkeinen // Single word 16119757fc8STomi Valkeinen if (last) 16219757fc8STomi Valkeinen first &= last; 16319757fc8STomi Valkeinen dat = FB_READL(dst); 16419757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ val, dat, first), dst); 16519757fc8STomi Valkeinen } else { 16619757fc8STomi Valkeinen // Multiple destination words 16719757fc8STomi Valkeinen // Leading bits 16819757fc8STomi Valkeinen if (first!=0UL) { 16919757fc8STomi Valkeinen dat = FB_READL(dst); 17019757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ val, dat, first), dst); 17119757fc8STomi Valkeinen dst++; 17219757fc8STomi Valkeinen n -= bits - dst_idx; 17319757fc8STomi Valkeinen } 17419757fc8STomi Valkeinen 17519757fc8STomi Valkeinen // Main chunk 17619757fc8STomi Valkeinen n /= bits; 17719757fc8STomi Valkeinen while (n >= 8) { 17819757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 17919757fc8STomi Valkeinen dst++; 18019757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 18119757fc8STomi Valkeinen dst++; 18219757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 18319757fc8STomi Valkeinen dst++; 18419757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 18519757fc8STomi Valkeinen dst++; 18619757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 18719757fc8STomi Valkeinen dst++; 18819757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 18919757fc8STomi Valkeinen dst++; 19019757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 19119757fc8STomi Valkeinen dst++; 19219757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 19319757fc8STomi Valkeinen dst++; 19419757fc8STomi Valkeinen n -= 8; 19519757fc8STomi Valkeinen } 19619757fc8STomi Valkeinen while (n--) { 19719757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ val, dst); 19819757fc8STomi Valkeinen dst++; 19919757fc8STomi Valkeinen } 20019757fc8STomi Valkeinen // Trailing bits 20119757fc8STomi Valkeinen if (last) { 20219757fc8STomi Valkeinen dat = FB_READL(dst); 20319757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ val, dat, last), dst); 20419757fc8STomi Valkeinen } 20519757fc8STomi Valkeinen } 20619757fc8STomi Valkeinen } 20719757fc8STomi Valkeinen 20819757fc8STomi Valkeinen 20919757fc8STomi Valkeinen /* 21019757fc8STomi Valkeinen * Unaligned generic pattern invert using 32/64-bit memory accesses 21119757fc8STomi Valkeinen * The pattern must have been expanded to a full 32/64-bit value 21219757fc8STomi Valkeinen * Left/right are the appropriate shifts to convert to the pattern to be 21319757fc8STomi Valkeinen * used for the next 32/64-bit word 21419757fc8STomi Valkeinen */ 21519757fc8STomi Valkeinen 21619757fc8STomi Valkeinen static void 21719757fc8STomi Valkeinen bitfill_unaligned_rev(struct fb_info *p, unsigned long __iomem *dst, 21819757fc8STomi Valkeinen int dst_idx, unsigned long pat, int left, int right, 21919757fc8STomi Valkeinen unsigned n, int bits) 22019757fc8STomi Valkeinen { 22119757fc8STomi Valkeinen unsigned long first, last, dat; 22219757fc8STomi Valkeinen 22319757fc8STomi Valkeinen if (!n) 22419757fc8STomi Valkeinen return; 22519757fc8STomi Valkeinen 22619757fc8STomi Valkeinen first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 22719757fc8STomi Valkeinen last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 22819757fc8STomi Valkeinen 22919757fc8STomi Valkeinen if (dst_idx+n <= bits) { 23019757fc8STomi Valkeinen // Single word 23119757fc8STomi Valkeinen if (last) 23219757fc8STomi Valkeinen first &= last; 23319757fc8STomi Valkeinen dat = FB_READL(dst); 23419757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ pat, dat, first), dst); 23519757fc8STomi Valkeinen } else { 23619757fc8STomi Valkeinen // Multiple destination words 23719757fc8STomi Valkeinen 23819757fc8STomi Valkeinen // Leading bits 23919757fc8STomi Valkeinen if (first != 0UL) { 24019757fc8STomi Valkeinen dat = FB_READL(dst); 24119757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ pat, dat, first), dst); 24219757fc8STomi Valkeinen dst++; 24319757fc8STomi Valkeinen pat = pat << left | pat >> right; 24419757fc8STomi Valkeinen n -= bits - dst_idx; 24519757fc8STomi Valkeinen } 24619757fc8STomi Valkeinen 24719757fc8STomi Valkeinen // Main chunk 24819757fc8STomi Valkeinen n /= bits; 24919757fc8STomi Valkeinen while (n >= 4) { 25019757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ pat, dst); 25119757fc8STomi Valkeinen dst++; 25219757fc8STomi Valkeinen pat = pat << left | pat >> right; 25319757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ pat, dst); 25419757fc8STomi Valkeinen dst++; 25519757fc8STomi Valkeinen pat = pat << left | pat >> right; 25619757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ pat, dst); 25719757fc8STomi Valkeinen dst++; 25819757fc8STomi Valkeinen pat = pat << left | pat >> right; 25919757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ pat, dst); 26019757fc8STomi Valkeinen dst++; 26119757fc8STomi Valkeinen pat = pat << left | pat >> right; 26219757fc8STomi Valkeinen n -= 4; 26319757fc8STomi Valkeinen } 26419757fc8STomi Valkeinen while (n--) { 26519757fc8STomi Valkeinen FB_WRITEL(FB_READL(dst) ^ pat, dst); 26619757fc8STomi Valkeinen dst++; 26719757fc8STomi Valkeinen pat = pat << left | pat >> right; 26819757fc8STomi Valkeinen } 26919757fc8STomi Valkeinen 27019757fc8STomi Valkeinen // Trailing bits 27119757fc8STomi Valkeinen if (last) { 27219757fc8STomi Valkeinen dat = FB_READL(dst); 27319757fc8STomi Valkeinen FB_WRITEL(comp(dat ^ pat, dat, last), dst); 27419757fc8STomi Valkeinen } 27519757fc8STomi Valkeinen } 27619757fc8STomi Valkeinen } 27719757fc8STomi Valkeinen 27819757fc8STomi Valkeinen void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 27919757fc8STomi Valkeinen { 28019757fc8STomi Valkeinen unsigned long pat, pat2, fg; 28119757fc8STomi Valkeinen unsigned long width = rect->width, height = rect->height; 28219757fc8STomi Valkeinen int bits = BITS_PER_LONG, bytes = bits >> 3; 28319757fc8STomi Valkeinen u32 bpp = p->var.bits_per_pixel; 28419757fc8STomi Valkeinen unsigned long __iomem *dst; 28519757fc8STomi Valkeinen int dst_idx, left; 28619757fc8STomi Valkeinen 28719757fc8STomi Valkeinen if (p->state != FBINFO_STATE_RUNNING) 28819757fc8STomi Valkeinen return; 28919757fc8STomi Valkeinen 290*b3e88137SThomas Zimmermann if (p->flags & FBINFO_VIRTFB) 291*b3e88137SThomas Zimmermann fb_warn_once(p, "Framebuffer is not in I/O address space."); 292*b3e88137SThomas Zimmermann 29319757fc8STomi Valkeinen if (p->fix.visual == FB_VISUAL_TRUECOLOR || 29419757fc8STomi Valkeinen p->fix.visual == FB_VISUAL_DIRECTCOLOR ) 29519757fc8STomi Valkeinen fg = ((u32 *) (p->pseudo_palette))[rect->color]; 29619757fc8STomi Valkeinen else 29719757fc8STomi Valkeinen fg = rect->color; 29819757fc8STomi Valkeinen 29919757fc8STomi Valkeinen pat = pixel_to_pat(bpp, fg); 30019757fc8STomi Valkeinen 30119757fc8STomi Valkeinen dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1)); 30219757fc8STomi Valkeinen dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8; 30319757fc8STomi Valkeinen dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp; 30419757fc8STomi Valkeinen /* FIXME For now we support 1-32 bpp only */ 30519757fc8STomi Valkeinen left = bits % bpp; 30619757fc8STomi Valkeinen if (p->fbops->fb_sync) 30719757fc8STomi Valkeinen p->fbops->fb_sync(p); 30819757fc8STomi Valkeinen if (!left) { 30919757fc8STomi Valkeinen u32 bswapmask = fb_compute_bswapmask(p); 31019757fc8STomi Valkeinen void (*fill_op32)(struct fb_info *p, 31119757fc8STomi Valkeinen unsigned long __iomem *dst, int dst_idx, 31219757fc8STomi Valkeinen unsigned long pat, unsigned n, int bits, 31319757fc8STomi Valkeinen u32 bswapmask) = NULL; 31419757fc8STomi Valkeinen 31519757fc8STomi Valkeinen switch (rect->rop) { 31619757fc8STomi Valkeinen case ROP_XOR: 31719757fc8STomi Valkeinen fill_op32 = bitfill_aligned_rev; 31819757fc8STomi Valkeinen break; 31919757fc8STomi Valkeinen case ROP_COPY: 32019757fc8STomi Valkeinen fill_op32 = bitfill_aligned; 32119757fc8STomi Valkeinen break; 32219757fc8STomi Valkeinen default: 32319757fc8STomi Valkeinen printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n"); 32419757fc8STomi Valkeinen fill_op32 = bitfill_aligned; 32519757fc8STomi Valkeinen break; 32619757fc8STomi Valkeinen } 32719757fc8STomi Valkeinen while (height--) { 32819757fc8STomi Valkeinen dst += dst_idx >> (ffs(bits) - 1); 32919757fc8STomi Valkeinen dst_idx &= (bits - 1); 33019757fc8STomi Valkeinen fill_op32(p, dst, dst_idx, pat, width*bpp, bits, 33119757fc8STomi Valkeinen bswapmask); 33219757fc8STomi Valkeinen dst_idx += p->fix.line_length*8; 33319757fc8STomi Valkeinen } 33419757fc8STomi Valkeinen } else { 33519757fc8STomi Valkeinen int right, r; 33619757fc8STomi Valkeinen void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst, 33719757fc8STomi Valkeinen int dst_idx, unsigned long pat, int left, 33819757fc8STomi Valkeinen int right, unsigned n, int bits) = NULL; 33919757fc8STomi Valkeinen #ifdef __LITTLE_ENDIAN 34019757fc8STomi Valkeinen right = left; 34119757fc8STomi Valkeinen left = bpp - right; 34219757fc8STomi Valkeinen #else 34319757fc8STomi Valkeinen right = bpp - left; 34419757fc8STomi Valkeinen #endif 34519757fc8STomi Valkeinen switch (rect->rop) { 34619757fc8STomi Valkeinen case ROP_XOR: 34719757fc8STomi Valkeinen fill_op = bitfill_unaligned_rev; 34819757fc8STomi Valkeinen break; 34919757fc8STomi Valkeinen case ROP_COPY: 35019757fc8STomi Valkeinen fill_op = bitfill_unaligned; 35119757fc8STomi Valkeinen break; 35219757fc8STomi Valkeinen default: 35319757fc8STomi Valkeinen printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n"); 35419757fc8STomi Valkeinen fill_op = bitfill_unaligned; 35519757fc8STomi Valkeinen break; 35619757fc8STomi Valkeinen } 35719757fc8STomi Valkeinen while (height--) { 35819757fc8STomi Valkeinen dst += dst_idx / bits; 35919757fc8STomi Valkeinen dst_idx &= (bits - 1); 36019757fc8STomi Valkeinen r = dst_idx % bpp; 36119757fc8STomi Valkeinen /* rotate pattern to the correct start position */ 36219757fc8STomi Valkeinen pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp)); 36319757fc8STomi Valkeinen fill_op(p, dst, dst_idx, pat2, left, right, 36419757fc8STomi Valkeinen width*bpp, bits); 36519757fc8STomi Valkeinen dst_idx += p->fix.line_length*8; 36619757fc8STomi Valkeinen } 36719757fc8STomi Valkeinen } 36819757fc8STomi Valkeinen } 36919757fc8STomi Valkeinen 37019757fc8STomi Valkeinen EXPORT_SYMBOL(cfb_fillrect); 37119757fc8STomi Valkeinen 37219757fc8STomi Valkeinen MODULE_AUTHOR("James Simmons <[email protected]>"); 37319757fc8STomi Valkeinen MODULE_DESCRIPTION("Generic software accelerated fill rectangle"); 37419757fc8STomi Valkeinen MODULE_LICENSE("GPL"); 375