Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / video / fbdev / core / cfbimgblt.c
1 /*
2  *  Generic BitBLT function for frame buffer with packed pixels of any depth.
3  *
4  *      Copyright (C)  June 1999 James Simmons
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 function copys a image from system memory to video memory. The
13  *  image can be a bitmap where each 0 represents the background color and
14  *  each 1 represents the foreground color. Great for font handling. It can
15  *  also be a color image. This is determined by image_depth. The color image
16  *  must be laid out exactly in the same format as the framebuffer. Yes I know
17  *  their are cards with hardware that coverts images of various depths to the
18  *  framebuffer depth. But not every card has this. All images must be rounded
19  *  up to the nearest byte. For example a bitmap 12 bits wide must be two 
20  *  bytes width. 
21  *
22  *  Tony: 
23  *  Incorporate mask tables similar to fbcon-cfb*.c in 2.4 API.  This speeds 
24  *  up the code significantly.
25  *  
26  *  Code for depths not multiples of BITS_PER_LONG is still kludgy, which is
27  *  still processed a bit at a time.   
28  *
29  *  Also need to add code to deal with cards endians that are different than
30  *  the native cpu endians. I also need to deal with MSB position in the word.
31  *  Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013:
32  *  - Provide optimized versions of fast_imageblit for 16 and 32bpp that are
33  *    significantly faster than the previous implementation.
34  *  - Simplify the fast/slow_imageblit selection code, avoiding integer
35  *    divides.
36  */
37 #include <linux/module.h>
38 #include <linux/string.h>
39 #include <linux/fb.h>
40 #include <asm/types.h>
41 #include "fb_draw.h"
42
43 #define DEBUG
44
45 #ifdef DEBUG
46 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__func__,## args)
47 #else
48 #define DPRINTK(fmt, args...)
49 #endif
50
51 static const u32 cfb_tab8_be[] = {
52     0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
53     0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
54     0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
55     0xffff0000,0xffff00ff,0xffffff00,0xffffffff
56 };
57
58 static const u32 cfb_tab8_le[] = {
59     0x00000000,0xff000000,0x00ff0000,0xffff0000,
60     0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
61     0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
62     0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
63 };
64
65 static const u32 cfb_tab16_be[] = {
66     0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
67 };
68
69 static const u32 cfb_tab16_le[] = {
70     0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
71 };
72
73 static const u32 cfb_tab32[] = {
74         0x00000000, 0xffffffff
75 };
76
77 #define FB_WRITEL fb_writel
78 #define FB_READL  fb_readl
79
80 static inline void color_imageblit(const struct fb_image *image, 
81                                    struct fb_info *p, u8 __iomem *dst1, 
82                                    u32 start_index,
83                                    u32 pitch_index)
84 {
85         /* Draw the penguin */
86         u32 __iomem *dst, *dst2;
87         u32 color = 0, val, shift;
88         int i, n, bpp = p->var.bits_per_pixel;
89         u32 null_bits = 32 - bpp;
90         u32 *palette = (u32 *) p->pseudo_palette;
91         const u8 *src = image->data;
92         u32 bswapmask = fb_compute_bswapmask(p);
93
94         dst2 = (u32 __iomem *) dst1;
95         for (i = image->height; i--; ) {
96                 n = image->width;
97                 dst = (u32 __iomem *) dst1;
98                 shift = 0;
99                 val = 0;
100                 
101                 if (start_index) {
102                         u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
103                                                 start_index, bswapmask);
104                         val = FB_READL(dst) & start_mask;
105                         shift = start_index;
106                 }
107                 while (n--) {
108                         if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
109                             p->fix.visual == FB_VISUAL_DIRECTCOLOR )
110                                 color = palette[*src];
111                         else
112                                 color = *src;
113                         color <<= FB_LEFT_POS(p, bpp);
114                         val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
115                         if (shift >= null_bits) {
116                                 FB_WRITEL(val, dst++);
117         
118                                 val = (shift == null_bits) ? 0 : 
119                                         FB_SHIFT_LOW(p, color, 32 - shift);
120                         }
121                         shift += bpp;
122                         shift &= (32 - 1);
123                         src++;
124                 }
125                 if (shift) {
126                         u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
127                                                 bswapmask);
128
129                         FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
130                 }
131                 dst1 += p->fix.line_length;
132                 if (pitch_index) {
133                         dst2 += p->fix.line_length;
134                         dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
135
136                         start_index += pitch_index;
137                         start_index &= 32 - 1;
138                 }
139         }
140 }
141
142 static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p, 
143                                   u8 __iomem *dst1, u32 fgcolor,
144                                   u32 bgcolor, 
145                                   u32 start_index,
146                                   u32 pitch_index)
147 {
148         u32 shift, color = 0, bpp = p->var.bits_per_pixel;
149         u32 __iomem *dst, *dst2;
150         u32 val, pitch = p->fix.line_length;
151         u32 null_bits = 32 - bpp;
152         u32 spitch = (image->width+7)/8;
153         const u8 *src = image->data, *s;
154         u32 i, j, l;
155         u32 bswapmask = fb_compute_bswapmask(p);
156
157         dst2 = (u32 __iomem *) dst1;
158         fgcolor <<= FB_LEFT_POS(p, bpp);
159         bgcolor <<= FB_LEFT_POS(p, bpp);
160
161         for (i = image->height; i--; ) {
162                 shift = val = 0;
163                 l = 8;
164                 j = image->width;
165                 dst = (u32 __iomem *) dst1;
166                 s = src;
167
168                 /* write leading bits */
169                 if (start_index) {
170                         u32 start_mask = ~fb_shifted_pixels_mask_u32(p,
171                                                 start_index, bswapmask);
172                         val = FB_READL(dst) & start_mask;
173                         shift = start_index;
174                 }
175
176                 while (j--) {
177                         l--;
178                         color = (*s & (1 << l)) ? fgcolor : bgcolor;
179                         val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask);
180                         
181                         /* Did the bitshift spill bits to the next long? */
182                         if (shift >= null_bits) {
183                                 FB_WRITEL(val, dst++);
184                                 val = (shift == null_bits) ? 0 :
185                                         FB_SHIFT_LOW(p, color, 32 - shift);
186                         }
187                         shift += bpp;
188                         shift &= (32 - 1);
189                         if (!l) { l = 8; s++; }
190                 }
191
192                 /* write trailing bits */
193                 if (shift) {
194                         u32 end_mask = fb_shifted_pixels_mask_u32(p, shift,
195                                                 bswapmask);
196
197                         FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
198                 }
199                 
200                 dst1 += pitch;
201                 src += spitch;  
202                 if (pitch_index) {
203                         dst2 += pitch;
204                         dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1));
205                         start_index += pitch_index;
206                         start_index &= 32 - 1;
207                 }
208                 
209         }
210 }
211
212 /*
213  * fast_imageblit - optimized monochrome color expansion
214  *
215  * Only if:  bits_per_pixel == 8, 16, or 32
216  *           image->width is divisible by pixel/dword (ppw);
217  *           fix->line_legth is divisible by 4;
218  *           beginning and end of a scanline is dword aligned
219  */
220 static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p, 
221                                   u8 __iomem *dst1, u32 fgcolor, 
222                                   u32 bgcolor) 
223 {
224         u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
225         u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
226         u32 bit_mask, end_mask, eorx, shift;
227         const char *s = image->data, *src;
228         u32 __iomem *dst;
229         const u32 *tab = NULL;
230         int i, j, k;
231
232         switch (bpp) {
233         case 8:
234                 tab = fb_be_math(p) ? cfb_tab8_be : cfb_tab8_le;
235                 break;
236         case 16:
237                 tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
238                 break;
239         case 32:
240         default:
241                 tab = cfb_tab32;
242                 break;
243         }
244
245         for (i = ppw-1; i--; ) {
246                 fgx <<= bpp;
247                 bgx <<= bpp;
248                 fgx |= fgcolor;
249                 bgx |= bgcolor;
250         }
251         
252         bit_mask = (1 << ppw) - 1;
253         eorx = fgx ^ bgx;
254         k = image->width/ppw;
255
256         for (i = image->height; i--; ) {
257                 dst = (u32 __iomem *) dst1, shift = 8; src = s;
258                 
259                 for (j = k; j--; ) {
260                         shift -= ppw;
261                         end_mask = tab[(*src >> shift) & bit_mask];
262                         FB_WRITEL((end_mask & eorx)^bgx, dst++);
263                         if (!shift) { shift = 8; src++; }               
264                 }
265                 dst1 += p->fix.line_length;
266                 s += spitch;
267         }
268 }       
269         
270 /*
271  * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded
272  * into the code, main loop unrolled.
273  */
274
275 static inline void fast_imageblit16(const struct fb_image *image,
276                                     struct fb_info *p, u8 __iomem * dst1,
277                                     u32 fgcolor, u32 bgcolor)
278 {
279         u32 fgx = fgcolor, bgx = bgcolor;
280         u32 spitch = (image->width + 7) / 8;
281         u32 end_mask, eorx;
282         const char *s = image->data, *src;
283         u32 __iomem *dst;
284         const u32 *tab = NULL;
285         int i, j, k;
286
287         tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
288
289         fgx <<= 16;
290         bgx <<= 16;
291         fgx |= fgcolor;
292         bgx |= bgcolor;
293
294         eorx = fgx ^ bgx;
295         k = image->width / 2;
296
297         for (i = image->height; i--;) {
298                 dst = (u32 __iomem *) dst1;
299                 src = s;
300
301                 j = k;
302                 while (j >= 4) {
303                         u8 bits = *src;
304                         end_mask = tab[(bits >> 6) & 3];
305                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
306                         end_mask = tab[(bits >> 4) & 3];
307                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
308                         end_mask = tab[(bits >> 2) & 3];
309                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
310                         end_mask = tab[bits & 3];
311                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
312                         src++;
313                         j -= 4;
314                 }
315                 if (j != 0) {
316                         u8 bits = *src;
317                         end_mask = tab[(bits >> 6) & 3];
318                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
319                         if (j >= 2) {
320                                 end_mask = tab[(bits >> 4) & 3];
321                                 FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
322                                 if (j == 3) {
323                                         end_mask = tab[(bits >> 2) & 3];
324                                         FB_WRITEL((end_mask & eorx) ^ bgx, dst);
325                                 }
326                         }
327                 }
328                 dst1 += p->fix.line_length;
329                 s += spitch;
330         }
331 }
332
333 /*
334  * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded
335  * into the code, main loop unrolled.
336  */
337
338 static inline void fast_imageblit32(const struct fb_image *image,
339                                     struct fb_info *p, u8 __iomem * dst1,
340                                     u32 fgcolor, u32 bgcolor)
341 {
342         u32 fgx = fgcolor, bgx = bgcolor;
343         u32 spitch = (image->width + 7) / 8;
344         u32 end_mask, eorx;
345         const char *s = image->data, *src;
346         u32 __iomem *dst;
347         const u32 *tab = NULL;
348         int i, j, k;
349
350         tab = cfb_tab32;
351
352         eorx = fgx ^ bgx;
353         k = image->width;
354
355         for (i = image->height; i--;) {
356                 dst = (u32 __iomem *) dst1;
357                 src = s;
358
359                 j = k;
360                 while (j >= 8) {
361                         u8 bits = *src;
362                         end_mask = tab[(bits >> 7) & 1];
363                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
364                         end_mask = tab[(bits >> 6) & 1];
365                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
366                         end_mask = tab[(bits >> 5) & 1];
367                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
368                         end_mask = tab[(bits >> 4) & 1];
369                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
370                         end_mask = tab[(bits >> 3) & 1];
371                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
372                         end_mask = tab[(bits >> 2) & 1];
373                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
374                         end_mask = tab[(bits >> 1) & 1];
375                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
376                         end_mask = tab[bits & 1];
377                         FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
378                         src++;
379                         j -= 8;
380                 }
381                 if (j != 0) {
382                         u32 bits = (u32) * src;
383                         while (j > 1) {
384                                 end_mask = tab[(bits >> 7) & 1];
385                                 FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
386                                 bits <<= 1;
387                                 j--;
388                         }
389                         end_mask = tab[(bits >> 7) & 1];
390                         FB_WRITEL((end_mask & eorx) ^ bgx, dst);
391                 }
392                 dst1 += p->fix.line_length;
393                 s += spitch;
394         }
395 }
396
397 void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
398 {
399         u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
400         u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
401         u32 width = image->width;
402         u32 dx = image->dx, dy = image->dy;
403         u8 __iomem *dst1;
404
405         if (p->state != FBINFO_STATE_RUNNING)
406                 return;
407
408         bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
409         start_index = bitstart & (32 - 1);
410         pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
411
412         bitstart /= 8;
413         bitstart &= ~(bpl - 1);
414         dst1 = p->screen_base + bitstart;
415
416         if (p->fbops->fb_sync)
417                 p->fbops->fb_sync(p);
418
419         if (image->depth == 1) {
420                 if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
421                     p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
422                         fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
423                         bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
424                 } else {
425                         fgcolor = image->fg_color;
426                         bgcolor = image->bg_color;
427                 }       
428                 
429                 if (!start_index && !pitch_index) {
430                         if (bpp == 32)
431                                 fast_imageblit32(image, p, dst1, fgcolor,
432                                                  bgcolor);
433                         else if (bpp == 16 && (width & 1) == 0)
434                                 fast_imageblit16(image, p, dst1, fgcolor,
435                                                  bgcolor);
436                         else if (bpp == 8 && (width & 3) == 0)
437                                 fast_imageblit(image, p, dst1, fgcolor,
438                                                bgcolor);
439                         else
440                                 slow_imageblit(image, p, dst1, fgcolor,
441                                                bgcolor,
442                                                start_index, pitch_index);
443                 } else
444                         slow_imageblit(image, p, dst1, fgcolor, bgcolor,
445                                         start_index, pitch_index);
446         } else
447                 color_imageblit(image, p, dst1, start_index, pitch_index);
448 }
449
450 EXPORT_SYMBOL(cfb_imageblit);
451
452 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
453 MODULE_DESCRIPTION("Generic software accelerated imaging drawing");
454 MODULE_LICENSE("GPL");
455