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