blob: e5ff62e9cfb81c1952823c99ea4b4be17c8e9761 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Generic fillrect for frame buffers with packed pixels of any depth.
3 *
4 * Copyright (C) 2000 James Simmons (jsimmons@linux-fbdev.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 * The code for depths like 24 that don't have integer number of pixels per
13 * long is broken and needs to be fixed. For now I turned these types of
14 * mode off.
15 *
16 * Also need to add code to deal with cards endians that are different than
17 * the native cpu endians. I also need to deal with MSB position in the word.
18 *
19 */
20#include <linux/config.h>
21#include <linux/module.h>
22#include <linux/string.h>
23#include <linux/fb.h>
24#include <asm/types.h>
25
26#if BITS_PER_LONG == 32
27# define FB_WRITEL fb_writel
28# define FB_READL fb_readl
29#else
30# define FB_WRITEL fb_writeq
31# define FB_READL fb_readq
32#endif
33
34 /*
35 * Compose two values, using a bitmask as decision value
36 * This is equivalent to (a & mask) | (b & ~mask)
37 */
38
39static inline unsigned long
40comp(unsigned long a, unsigned long b, unsigned long mask)
41{
42 return ((a ^ b) & mask) ^ b;
43}
44
45 /*
46 * Create a pattern with the given pixel's color
47 */
48
49#if BITS_PER_LONG == 64
50static inline unsigned long
51pixel_to_pat( u32 bpp, u32 pixel)
52{
53 switch (bpp) {
54 case 1:
55 return 0xfffffffffffffffful*pixel;
56 case 2:
57 return 0x5555555555555555ul*pixel;
58 case 4:
59 return 0x1111111111111111ul*pixel;
60 case 8:
61 return 0x0101010101010101ul*pixel;
62 case 12:
63 return 0x0001001001001001ul*pixel;
64 case 16:
65 return 0x0001000100010001ul*pixel;
66 case 24:
67 return 0x0000000001000001ul*pixel;
68 case 32:
69 return 0x0000000100000001ul*pixel;
70 default:
71 panic("pixel_to_pat(): unsupported pixelformat\n");
72 }
73}
74#else
75static inline unsigned long
76pixel_to_pat( u32 bpp, u32 pixel)
77{
78 switch (bpp) {
79 case 1:
80 return 0xfffffffful*pixel;
81 case 2:
82 return 0x55555555ul*pixel;
83 case 4:
84 return 0x11111111ul*pixel;
85 case 8:
86 return 0x01010101ul*pixel;
87 case 12:
88 return 0x00001001ul*pixel;
89 case 16:
90 return 0x00010001ul*pixel;
91 case 24:
92 return 0x00000001ul*pixel;
93 case 32:
94 return 0x00000001ul*pixel;
95 default:
96 panic("pixel_to_pat(): unsupported pixelformat\n");
97 }
98}
99#endif
100
101 /*
102 * Aligned pattern fill using 32/64-bit memory accesses
103 */
104
105static void
106bitfill_aligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
107{
108 unsigned long first, last;
109
110 if (!n)
111 return;
112
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -0800113 first = FB_SHIFT_HIGH(~0UL, dst_idx);
114 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
116 if (dst_idx+n <= bits) {
117 // Single word
118 if (last)
119 first &= last;
120 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
121 } else {
122 // Multiple destination words
123
124 // Leading bits
125 if (first!= ~0UL) {
126 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
127 dst++;
128 n -= bits - dst_idx;
129 }
130
131 // Main chunk
132 n /= bits;
133 while (n >= 8) {
134 FB_WRITEL(pat, dst++);
135 FB_WRITEL(pat, dst++);
136 FB_WRITEL(pat, dst++);
137 FB_WRITEL(pat, dst++);
138 FB_WRITEL(pat, dst++);
139 FB_WRITEL(pat, dst++);
140 FB_WRITEL(pat, dst++);
141 FB_WRITEL(pat, dst++);
142 n -= 8;
143 }
144 while (n--)
145 FB_WRITEL(pat, dst++);
146
147 // Trailing bits
148 if (last)
149 FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
150 }
151}
152
153
154 /*
155 * Unaligned generic pattern fill using 32/64-bit memory accesses
156 * The pattern must have been expanded to a full 32/64-bit value
157 * Left/right are the appropriate shifts to convert to the pattern to be
158 * used for the next 32/64-bit word
159 */
160
161static void
162bitfill_unaligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
163 int left, int right, unsigned n, int bits)
164{
165 unsigned long first, last;
166
167 if (!n)
168 return;
169
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -0800170 first = FB_SHIFT_HIGH(~0UL, dst_idx);
171 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
173 if (dst_idx+n <= bits) {
174 // Single word
175 if (last)
176 first &= last;
177 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
178 } else {
179 // Multiple destination words
180 // Leading bits
181 if (first) {
182 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
183 dst++;
184 pat = pat << left | pat >> right;
185 n -= bits - dst_idx;
186 }
187
188 // Main chunk
189 n /= bits;
190 while (n >= 4) {
191 FB_WRITEL(pat, dst++);
192 pat = pat << left | pat >> right;
193 FB_WRITEL(pat, dst++);
194 pat = pat << left | pat >> right;
195 FB_WRITEL(pat, dst++);
196 pat = pat << left | pat >> right;
197 FB_WRITEL(pat, dst++);
198 pat = pat << left | pat >> right;
199 n -= 4;
200 }
201 while (n--) {
202 FB_WRITEL(pat, dst++);
203 pat = pat << left | pat >> right;
204 }
205
206 // Trailing bits
207 if (last)
208 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
209 }
210}
211
212 /*
213 * Aligned pattern invert using 32/64-bit memory accesses
214 */
215static void
216bitfill_aligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
217{
218 unsigned long val = pat, dat;
219 unsigned long first, last;
220
221 if (!n)
222 return;
223
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -0800224 first = FB_SHIFT_HIGH(~0UL, dst_idx);
225 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
227 if (dst_idx+n <= bits) {
228 // Single word
229 if (last)
230 first &= last;
231 dat = FB_READL(dst);
232 FB_WRITEL(comp(dat ^ val, dat, first), dst);
233 } else {
234 // Multiple destination words
235 // Leading bits
236 if (first!=0UL) {
237 dat = FB_READL(dst);
238 FB_WRITEL(comp(dat ^ val, dat, first), dst);
239 dst++;
240 n -= bits - dst_idx;
241 }
242
243 // Main chunk
244 n /= bits;
245 while (n >= 8) {
246 FB_WRITEL(FB_READL(dst) ^ val, dst);
247 dst++;
248 FB_WRITEL(FB_READL(dst) ^ val, dst);
249 dst++;
250 FB_WRITEL(FB_READL(dst) ^ val, dst);
251 dst++;
252 FB_WRITEL(FB_READL(dst) ^ val, dst);
253 dst++;
254 FB_WRITEL(FB_READL(dst) ^ val, dst);
255 dst++;
256 FB_WRITEL(FB_READL(dst) ^ val, dst);
257 dst++;
258 FB_WRITEL(FB_READL(dst) ^ val, dst);
259 dst++;
260 FB_WRITEL(FB_READL(dst) ^ val, dst);
261 dst++;
262 n -= 8;
263 }
264 while (n--) {
265 FB_WRITEL(FB_READL(dst) ^ val, dst);
266 dst++;
267 }
268 // Trailing bits
269 if (last) {
270 dat = FB_READL(dst);
271 FB_WRITEL(comp(dat ^ val, dat, last), dst);
272 }
273 }
274}
275
276
277 /*
278 * Unaligned generic pattern invert using 32/64-bit memory accesses
279 * The pattern must have been expanded to a full 32/64-bit value
280 * Left/right are the appropriate shifts to convert to the pattern to be
281 * used for the next 32/64-bit word
282 */
283
284static void
285bitfill_unaligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
286 int left, int right, unsigned n, int bits)
287{
288 unsigned long first, last, dat;
289
290 if (!n)
291 return;
292
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -0800293 first = FB_SHIFT_HIGH(~0UL, dst_idx);
294 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 if (dst_idx+n <= bits) {
297 // Single word
298 if (last)
299 first &= last;
300 dat = FB_READL(dst);
301 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
302 } else {
303 // Multiple destination words
304
305 // Leading bits
306 if (first != 0UL) {
307 dat = FB_READL(dst);
308 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
309 dst++;
310 pat = pat << left | pat >> right;
311 n -= bits - dst_idx;
312 }
313
314 // Main chunk
315 n /= bits;
316 while (n >= 4) {
317 FB_WRITEL(FB_READL(dst) ^ pat, dst);
318 dst++;
319 pat = pat << left | pat >> right;
320 FB_WRITEL(FB_READL(dst) ^ pat, dst);
321 dst++;
322 pat = pat << left | pat >> right;
323 FB_WRITEL(FB_READL(dst) ^ pat, dst);
324 dst++;
325 pat = pat << left | pat >> right;
326 FB_WRITEL(FB_READL(dst) ^ pat, dst);
327 dst++;
328 pat = pat << left | pat >> right;
329 n -= 4;
330 }
331 while (n--) {
332 FB_WRITEL(FB_READL(dst) ^ pat, dst);
333 dst++;
334 pat = pat << left | pat >> right;
335 }
336
337 // Trailing bits
338 if (last) {
339 dat = FB_READL(dst);
340 FB_WRITEL(comp(dat ^ pat, dat, last), dst);
341 }
342 }
343}
344
345void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
346{
Antonino A. Daplasb4d8aea2005-11-07 01:00:39 -0800347 unsigned long pat, fg;
348 unsigned long width = rect->width, height = rect->height;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 int bits = BITS_PER_LONG, bytes = bits >> 3;
350 u32 bpp = p->var.bits_per_pixel;
351 unsigned long __iomem *dst;
352 int dst_idx, left;
353
354 if (p->state != FBINFO_STATE_RUNNING)
355 return;
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
358 p->fix.visual == FB_VISUAL_DIRECTCOLOR )
359 fg = ((u32 *) (p->pseudo_palette))[rect->color];
360 else
361 fg = rect->color;
362
363 pat = pixel_to_pat( bpp, fg);
364
365 dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
366 dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
367 dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
368 /* FIXME For now we support 1-32 bpp only */
369 left = bits % bpp;
370 if (p->fbops->fb_sync)
371 p->fbops->fb_sync(p);
372 if (!left) {
373 void (*fill_op32)(unsigned long __iomem *dst, int dst_idx,
374 unsigned long pat, unsigned n, int bits) = NULL;
375
376 switch (rect->rop) {
377 case ROP_XOR:
378 fill_op32 = bitfill_aligned_rev;
379 break;
380 case ROP_COPY:
381 fill_op32 = bitfill_aligned;
382 break;
383 default:
384 printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
385 fill_op32 = bitfill_aligned;
386 break;
387 }
388 while (height--) {
389 dst += dst_idx >> (ffs(bits) - 1);
390 dst_idx &= (bits - 1);
391 fill_op32(dst, dst_idx, pat, width*bpp, bits);
392 dst_idx += p->fix.line_length*8;
393 }
394 } else {
395 int right;
396 int r;
397 int rot = (left-dst_idx) % bpp;
398 void (*fill_op)(unsigned long __iomem *dst, int dst_idx,
399 unsigned long pat, int left, int right,
400 unsigned n, int bits) = NULL;
401
402 /* rotate pattern to correct start position */
403 pat = pat << rot | pat >> (bpp-rot);
404
405 right = bpp-left;
406 switch (rect->rop) {
407 case ROP_XOR:
408 fill_op = bitfill_unaligned_rev;
409 break;
410 case ROP_COPY:
411 fill_op = bitfill_unaligned;
412 break;
413 default:
414 printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
415 fill_op = bitfill_unaligned;
416 break;
417 }
418 while (height--) {
419 dst += dst_idx >> (ffs(bits) - 1);
420 dst_idx &= (bits - 1);
421 fill_op(dst, dst_idx, pat, left, right,
422 width*bpp, bits);
423 r = (p->fix.line_length*8) % bpp;
424 pat = pat << (bpp-r) | pat >> r;
425 dst_idx += p->fix.line_length*8;
426 }
427 }
428}
429
430EXPORT_SYMBOL(cfb_fillrect);
431
432MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
433MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
434MODULE_LICENSE("GPL");