1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/mb862xx/mb862xxfb_accel.c |
4 | * |
5 | * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver acceleration support |
6 | * |
7 | * (C) 2007 Alexander Shishkin <virtuoso@slind.org> |
8 | * (C) 2009 Valentin Sitdikov <v.sitdikov@gmail.com> |
9 | * (C) 2009 Siemens AG |
10 | */ |
11 | #include <linux/fb.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/module.h> |
16 | #include <linux/pci.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #include "mb862xxfb.h" |
20 | #include "mb862xx_reg.h" |
21 | #include "mb862xxfb_accel.h" |
22 | |
23 | static void mb862xxfb_write_fifo(u32 count, u32 *data, struct fb_info *info) |
24 | { |
25 | struct mb862xxfb_par *par = info->par; |
26 | static u32 free; |
27 | |
28 | u32 total = 0; |
29 | while (total < count) { |
30 | if (free) { |
31 | outreg(geo, GDC_GEO_REG_INPUT_FIFO, data[total]); |
32 | total++; |
33 | free--; |
34 | } else { |
35 | free = (u32) inreg(draw, GDC_REG_FIFO_COUNT); |
36 | } |
37 | } |
38 | } |
39 | |
40 | static void mb86290fb_copyarea(struct fb_info *info, |
41 | const struct fb_copyarea *area) |
42 | { |
43 | __u32 cmd[6]; |
44 | |
45 | cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; |
46 | /* Set raster operation */ |
47 | cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); |
48 | cmd[2] = GDC_TYPE_BLTCOPYP << 24; |
49 | |
50 | if (area->sx >= area->dx && area->sy >= area->dy) |
51 | cmd[2] |= GDC_CMD_BLTCOPY_TOP_LEFT << 16; |
52 | else if (area->sx >= area->dx && area->sy <= area->dy) |
53 | cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_LEFT << 16; |
54 | else if (area->sx <= area->dx && area->sy >= area->dy) |
55 | cmd[2] |= GDC_CMD_BLTCOPY_TOP_RIGHT << 16; |
56 | else |
57 | cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_RIGHT << 16; |
58 | |
59 | cmd[3] = (area->sy << 16) | area->sx; |
60 | cmd[4] = (area->dy << 16) | area->dx; |
61 | cmd[5] = (area->height << 16) | area->width; |
62 | mb862xxfb_write_fifo(count: 6, data: cmd, info); |
63 | } |
64 | |
65 | /* |
66 | * Fill in the cmd array /GDC FIFO commands/ to draw a 1bit image. |
67 | * Make sure cmd has enough room! |
68 | */ |
69 | static void mb86290fb_imageblit1(u32 *cmd, u16 step, u16 dx, u16 dy, |
70 | u16 width, u16 height, u32 fgcolor, |
71 | u32 bgcolor, const struct fb_image *image, |
72 | struct fb_info *info) |
73 | { |
74 | int i; |
75 | unsigned const char *line; |
76 | u16 bytes; |
77 | |
78 | /* set colors and raster operation regs */ |
79 | cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; |
80 | /* Set raster operation */ |
81 | cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); |
82 | cmd[2] = |
83 | (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16); |
84 | cmd[3] = fgcolor; |
85 | cmd[4] = |
86 | (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_BACK_COLOR << 16); |
87 | cmd[5] = bgcolor; |
88 | |
89 | i = 0; |
90 | line = image->data; |
91 | bytes = (image->width + 7) >> 3; |
92 | |
93 | /* and the image */ |
94 | cmd[6] = (GDC_TYPE_DRAWBITMAPP << 24) | |
95 | (GDC_CMD_BITMAP << 16) | (2 + (step * height)); |
96 | cmd[7] = (dy << 16) | dx; |
97 | cmd[8] = (height << 16) | width; |
98 | |
99 | while (i < height) { |
100 | memcpy(&cmd[9 + i * step], line, step << 2); |
101 | #ifdef __LITTLE_ENDIAN |
102 | { |
103 | int k = 0; |
104 | for (k = 0; k < step; k++) |
105 | cmd[9 + i * step + k] = |
106 | cpu_to_be32(cmd[9 + i * step + k]); |
107 | } |
108 | #endif |
109 | line += bytes; |
110 | i++; |
111 | } |
112 | } |
113 | |
114 | /* |
115 | * Fill in the cmd array /GDC FIFO commands/ to draw a 8bit image. |
116 | * Make sure cmd has enough room! |
117 | */ |
118 | static void mb86290fb_imageblit8(u32 *cmd, u16 step, u16 dx, u16 dy, |
119 | u16 width, u16 height, u32 fgcolor, |
120 | u32 bgcolor, const struct fb_image *image, |
121 | struct fb_info *info) |
122 | { |
123 | int i, j; |
124 | unsigned const char *line, *ptr; |
125 | u16 bytes; |
126 | |
127 | cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) | |
128 | (GDC_CMD_BLT_DRAW << 16) | (2 + (height * step)); |
129 | cmd[1] = (dy << 16) | dx; |
130 | cmd[2] = (height << 16) | width; |
131 | |
132 | i = 0; |
133 | line = image->data; |
134 | bytes = image->width; |
135 | |
136 | while (i < height) { |
137 | ptr = line; |
138 | for (j = 0; j < step; j++) { |
139 | cmd[3 + i * step + j] = |
140 | (((u32 *) (info->pseudo_palette))[*ptr]) & 0xffff; |
141 | ptr++; |
142 | cmd[3 + i * step + j] |= |
143 | ((((u32 *) (info-> |
144 | pseudo_palette))[*ptr]) & 0xffff) << 16; |
145 | ptr++; |
146 | } |
147 | |
148 | line += bytes; |
149 | i++; |
150 | } |
151 | } |
152 | |
153 | /* |
154 | * Fill in the cmd array /GDC FIFO commands/ to draw a 16bit image. |
155 | * Make sure cmd has enough room! |
156 | */ |
157 | static void mb86290fb_imageblit16(u32 *cmd, u16 step, u16 dx, u16 dy, |
158 | u16 width, u16 height, u32 fgcolor, |
159 | u32 bgcolor, const struct fb_image *image, |
160 | struct fb_info *info) |
161 | { |
162 | int i; |
163 | unsigned const char *line; |
164 | u16 bytes; |
165 | |
166 | i = 0; |
167 | line = image->data; |
168 | bytes = image->width << 1; |
169 | |
170 | cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) | |
171 | (GDC_CMD_BLT_DRAW << 16) | (2 + step * height); |
172 | cmd[1] = (dy << 16) | dx; |
173 | cmd[2] = (height << 16) | width; |
174 | |
175 | while (i < height) { |
176 | memcpy(&cmd[3 + i * step], line, step); |
177 | line += bytes; |
178 | i++; |
179 | } |
180 | } |
181 | |
182 | static void mb86290fb_imageblit(struct fb_info *info, |
183 | const struct fb_image *image) |
184 | { |
185 | u32 *cmd = NULL; |
186 | void (*cmdfn) (u32 *, u16, u16, u16, u16, u16, u32, u32, |
187 | const struct fb_image *, struct fb_info *) = NULL; |
188 | u32 cmdlen; |
189 | u32 fgcolor = 0, bgcolor = 0; |
190 | u16 step; |
191 | |
192 | u16 width = image->width, height = image->height; |
193 | u16 dx = image->dx, dy = image->dy; |
194 | int x2, y2, vxres, vyres; |
195 | |
196 | x2 = image->dx + image->width; |
197 | y2 = image->dy + image->height; |
198 | vxres = info->var.xres_virtual; |
199 | vyres = info->var.yres_virtual; |
200 | x2 = min(x2, vxres); |
201 | y2 = min(y2, vyres); |
202 | width = x2 - dx; |
203 | height = y2 - dy; |
204 | |
205 | switch (image->depth) { |
206 | case 1: |
207 | step = (width + 31) >> 5; |
208 | cmdlen = 9 + height * step; |
209 | cmdfn = mb86290fb_imageblit1; |
210 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || |
211 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) { |
212 | fgcolor = |
213 | ((u32 *) (info->pseudo_palette))[image->fg_color]; |
214 | bgcolor = |
215 | ((u32 *) (info->pseudo_palette))[image->bg_color]; |
216 | } else { |
217 | fgcolor = image->fg_color; |
218 | bgcolor = image->bg_color; |
219 | } |
220 | |
221 | break; |
222 | |
223 | case 8: |
224 | step = (width + 1) >> 1; |
225 | cmdlen = 3 + height * step; |
226 | cmdfn = mb86290fb_imageblit8; |
227 | break; |
228 | |
229 | case 16: |
230 | step = (width + 1) >> 1; |
231 | cmdlen = 3 + height * step; |
232 | cmdfn = mb86290fb_imageblit16; |
233 | break; |
234 | |
235 | default: |
236 | cfb_imageblit(info, image); |
237 | return; |
238 | } |
239 | |
240 | cmd = kmalloc_array(n: cmdlen, size: 4, GFP_DMA); |
241 | if (!cmd) |
242 | return cfb_imageblit(info, image); |
243 | cmdfn(cmd, step, dx, dy, width, height, fgcolor, bgcolor, image, info); |
244 | mb862xxfb_write_fifo(count: cmdlen, data: cmd, info); |
245 | kfree(objp: cmd); |
246 | } |
247 | |
248 | static void mb86290fb_fillrect(struct fb_info *info, |
249 | const struct fb_fillrect *rect) |
250 | { |
251 | |
252 | u32 x2, y2, vxres, vyres, height, width, fg; |
253 | u32 cmd[7]; |
254 | |
255 | vxres = info->var.xres_virtual; |
256 | vyres = info->var.yres_virtual; |
257 | |
258 | if (!rect->width || !rect->height || rect->dx > vxres |
259 | || rect->dy > vyres) |
260 | return; |
261 | |
262 | /* We could use hardware clipping but on many cards you get around |
263 | * hardware clipping by writing to framebuffer directly. */ |
264 | x2 = rect->dx + rect->width; |
265 | y2 = rect->dy + rect->height; |
266 | x2 = min(x2, vxres); |
267 | y2 = min(y2, vyres); |
268 | width = x2 - rect->dx; |
269 | height = y2 - rect->dy; |
270 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || |
271 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) |
272 | fg = ((u32 *) (info->pseudo_palette))[rect->color]; |
273 | else |
274 | fg = rect->color; |
275 | |
276 | switch (rect->rop) { |
277 | |
278 | case ROP_XOR: |
279 | /* Set raster operation */ |
280 | cmd[1] = (2 << 7) | (GDC_ROP_XOR << 9); |
281 | break; |
282 | |
283 | case ROP_COPY: |
284 | /* Set raster operation */ |
285 | cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); |
286 | break; |
287 | |
288 | } |
289 | |
290 | cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; |
291 | /* cmd[1] set earlier */ |
292 | cmd[2] = |
293 | (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16); |
294 | cmd[3] = fg; |
295 | cmd[4] = (GDC_TYPE_DRAWRECTP << 24) | (GDC_CMD_BLT_FILL << 16); |
296 | cmd[5] = (rect->dy << 16) | (rect->dx); |
297 | cmd[6] = (height << 16) | width; |
298 | |
299 | mb862xxfb_write_fifo(count: 7, data: cmd, info); |
300 | } |
301 | |
302 | void mb862xxfb_init_accel(struct fb_info *info, struct fb_ops *fbops, int xres) |
303 | { |
304 | struct mb862xxfb_par *par = info->par; |
305 | |
306 | if (info->var.bits_per_pixel == 32) { |
307 | fbops->fb_fillrect = cfb_fillrect; |
308 | fbops->fb_copyarea = cfb_copyarea; |
309 | fbops->fb_imageblit = cfb_imageblit; |
310 | } else { |
311 | outreg(disp, GC_L0EM, 3); |
312 | fbops->fb_fillrect = mb86290fb_fillrect; |
313 | fbops->fb_copyarea = mb86290fb_copyarea; |
314 | fbops->fb_imageblit = mb86290fb_imageblit; |
315 | } |
316 | outreg(draw, GDC_REG_DRAW_BASE, 0); |
317 | outreg(draw, GDC_REG_MODE_MISC, 0x8000); |
318 | outreg(draw, GDC_REG_X_RESOLUTION, xres); |
319 | |
320 | info->flags |= |
321 | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | |
322 | FBINFO_HWACCEL_IMAGEBLIT; |
323 | info->fix.accel = 0xff; /*FIXME: add right define */ |
324 | } |
325 | |
326 | MODULE_LICENSE("GPL v2" ); |
327 | |