1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 |
5 | * |
6 | * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> |
7 | * |
8 | * Version: 1.65 2002/08/14 |
9 | * |
10 | * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org> |
11 | * |
12 | * Contributors: "menion?" <menion@mindless.com> |
13 | * Betatesting, fixes, ideas |
14 | * |
15 | * "Kurt Garloff" <garloff@suse.de> |
16 | * Betatesting, fixes, ideas, videomodes, videomodes timmings |
17 | * |
18 | * "Tom Rini" <trini@kernel.crashing.org> |
19 | * MTRR stuff, PPC cleanups, betatesting, fixes, ideas |
20 | * |
21 | * "Bibek Sahu" <scorpio@dodds.net> |
22 | * Access device through readb|w|l and write b|w|l |
23 | * Extensive debugging stuff |
24 | * |
25 | * "Daniel Haun" <haund@usa.net> |
26 | * Testing, hardware cursor fixes |
27 | * |
28 | * "Scott Wood" <sawst46+@pitt.edu> |
29 | * Fixes |
30 | * |
31 | * "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de> |
32 | * Betatesting |
33 | * |
34 | * "Kelly French" <targon@hazmat.com> |
35 | * "Fernando Herrera" <fherrera@eurielec.etsit.upm.es> |
36 | * Betatesting, bug reporting |
37 | * |
38 | * "Pablo Bianucci" <pbian@pccp.com.ar> |
39 | * Fixes, ideas, betatesting |
40 | * |
41 | * "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es> |
42 | * Fixes, enhandcements, ideas, betatesting |
43 | * |
44 | * "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp> |
45 | * PPC betatesting, PPC support, backward compatibility |
46 | * |
47 | * "Paul Womar" <Paul@pwomar.demon.co.uk> |
48 | * "Owen Waller" <O.Waller@ee.qub.ac.uk> |
49 | * PPC betatesting |
50 | * |
51 | * "Thomas Pornin" <pornin@bolet.ens.fr> |
52 | * Alpha betatesting |
53 | * |
54 | * "Pieter van Leuven" <pvl@iae.nl> |
55 | * "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de> |
56 | * G100 testing |
57 | * |
58 | * "H. Peter Arvin" <hpa@transmeta.com> |
59 | * Ideas |
60 | * |
61 | * "Cort Dougan" <cort@cs.nmt.edu> |
62 | * CHRP fixes and PReP cleanup |
63 | * |
64 | * "Mark Vojkovich" <mvojkovi@ucsd.edu> |
65 | * G400 support |
66 | * |
67 | * (following author is not in any relation with this code, but his code |
68 | * is included in this driver) |
69 | * |
70 | * Based on framebuffer driver for VBE 2.0 compliant graphic boards |
71 | * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> |
72 | * |
73 | * (following author is not in any relation with this code, but his ideas |
74 | * were used when writing this driver) |
75 | * |
76 | * FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk> |
77 | * |
78 | */ |
79 | |
80 | #include "matroxfb_accel.h" |
81 | #include "matroxfb_DAC1064.h" |
82 | #include "matroxfb_Ti3026.h" |
83 | #include "matroxfb_misc.h" |
84 | |
85 | #define curr_ydstorg(x) ((x)->curr.ydstorg.pixels) |
86 | |
87 | #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) |
88 | |
89 | static inline void matrox_cfb4_pal(u_int32_t* pal) { |
90 | unsigned int i; |
91 | |
92 | for (i = 0; i < 16; i++) { |
93 | pal[i] = i * 0x11111111U; |
94 | } |
95 | } |
96 | |
97 | static inline void matrox_cfb8_pal(u_int32_t* pal) { |
98 | unsigned int i; |
99 | |
100 | for (i = 0; i < 16; i++) { |
101 | pal[i] = i * 0x01010101U; |
102 | } |
103 | } |
104 | |
105 | static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area); |
106 | static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect); |
107 | static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image); |
108 | static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect); |
109 | static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area); |
110 | |
111 | void matrox_cfbX_init(struct matrox_fb_info *minfo) |
112 | { |
113 | u_int32_t maccess; |
114 | u_int32_t mpitch; |
115 | u_int32_t mopmode; |
116 | int accel; |
117 | |
118 | DBG(__func__) |
119 | |
120 | mpitch = minfo->fbcon.var.xres_virtual; |
121 | |
122 | minfo->fbops.fb_copyarea = cfb_copyarea; |
123 | minfo->fbops.fb_fillrect = cfb_fillrect; |
124 | minfo->fbops.fb_imageblit = cfb_imageblit; |
125 | minfo->fbops.fb_cursor = NULL; |
126 | |
127 | accel = (minfo->fbcon.var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; |
128 | |
129 | switch (minfo->fbcon.var.bits_per_pixel) { |
130 | case 4: maccess = 0x00000000; /* accelerate as 8bpp video */ |
131 | mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */ |
132 | mopmode = M_OPMODE_4BPP; |
133 | matrox_cfb4_pal(pal: minfo->cmap); |
134 | if (accel && !(mpitch & 1)) { |
135 | minfo->fbops.fb_copyarea = matroxfb_cfb4_copyarea; |
136 | minfo->fbops.fb_fillrect = matroxfb_cfb4_fillrect; |
137 | } |
138 | break; |
139 | case 8: maccess = 0x00000000; |
140 | mopmode = M_OPMODE_8BPP; |
141 | matrox_cfb8_pal(pal: minfo->cmap); |
142 | if (accel) { |
143 | minfo->fbops.fb_copyarea = matroxfb_copyarea; |
144 | minfo->fbops.fb_fillrect = matroxfb_fillrect; |
145 | minfo->fbops.fb_imageblit = matroxfb_imageblit; |
146 | } |
147 | break; |
148 | case 16: if (minfo->fbcon.var.green.length == 5) |
149 | maccess = 0xC0000001; |
150 | else |
151 | maccess = 0x40000001; |
152 | mopmode = M_OPMODE_16BPP; |
153 | if (accel) { |
154 | minfo->fbops.fb_copyarea = matroxfb_copyarea; |
155 | minfo->fbops.fb_fillrect = matroxfb_fillrect; |
156 | minfo->fbops.fb_imageblit = matroxfb_imageblit; |
157 | } |
158 | break; |
159 | case 24: maccess = 0x00000003; |
160 | mopmode = M_OPMODE_24BPP; |
161 | if (accel) { |
162 | minfo->fbops.fb_copyarea = matroxfb_copyarea; |
163 | minfo->fbops.fb_fillrect = matroxfb_fillrect; |
164 | minfo->fbops.fb_imageblit = matroxfb_imageblit; |
165 | } |
166 | break; |
167 | case 32: maccess = 0x00000002; |
168 | mopmode = M_OPMODE_32BPP; |
169 | if (accel) { |
170 | minfo->fbops.fb_copyarea = matroxfb_copyarea; |
171 | minfo->fbops.fb_fillrect = matroxfb_fillrect; |
172 | minfo->fbops.fb_imageblit = matroxfb_imageblit; |
173 | } |
174 | break; |
175 | default: maccess = 0x00000000; |
176 | mopmode = 0x00000000; |
177 | break; /* turn off acceleration!!! */ |
178 | } |
179 | mga_fifo(8); |
180 | mga_outl(M_PITCH, mpitch); |
181 | mga_outl(M_YDSTORG, curr_ydstorg(minfo)); |
182 | if (minfo->capable.plnwt) |
183 | mga_outl(M_PLNWT, -1); |
184 | if (minfo->capable.srcorg) { |
185 | mga_outl(M_SRCORG, 0); |
186 | mga_outl(M_DSTORG, 0); |
187 | } |
188 | mga_outl(M_OPMODE, mopmode); |
189 | mga_outl(M_CXBNDRY, 0xFFFF0000); |
190 | mga_outl(M_YTOP, 0); |
191 | mga_outl(M_YBOT, 0x01FFFFFF); |
192 | mga_outl(M_MACCESS, maccess); |
193 | minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; |
194 | if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC; |
195 | minfo->accel.m_opmode = mopmode; |
196 | minfo->accel.m_access = maccess; |
197 | minfo->accel.m_pitch = mpitch; |
198 | } |
199 | |
200 | EXPORT_SYMBOL(matrox_cfbX_init); |
201 | |
202 | static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo) |
203 | { |
204 | mga_outl(M_MACCESS, minfo->accel.m_access); |
205 | mga_outl(M_PITCH, minfo->accel.m_pitch); |
206 | } |
207 | |
208 | static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy, |
209 | int sx, int dy, int dx, int height, int width) |
210 | { |
211 | int start, end; |
212 | CRITFLAGS |
213 | |
214 | DBG(__func__) |
215 | |
216 | CRITBEGIN |
217 | |
218 | if ((dy < sy) || ((dy == sy) && (dx <= sx))) { |
219 | mga_fifo(4); |
220 | matrox_accel_restore_maccess(minfo); |
221 | mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | |
222 | M_DWG_BFCOL | M_DWG_REPLACE); |
223 | mga_outl(M_AR5, vxres); |
224 | width--; |
225 | start = sy*vxres+sx+curr_ydstorg(minfo); |
226 | end = start+width; |
227 | } else { |
228 | mga_fifo(5); |
229 | matrox_accel_restore_maccess(minfo); |
230 | mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); |
231 | mga_outl(M_SGN, 5); |
232 | mga_outl(M_AR5, -vxres); |
233 | width--; |
234 | end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); |
235 | start = end+width; |
236 | dy += height-1; |
237 | } |
238 | mga_fifo(6); |
239 | matrox_accel_restore_maccess(minfo); |
240 | mga_outl(M_AR0, end); |
241 | mga_outl(M_AR3, start); |
242 | mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); |
243 | mga_ydstlen(dy, height); |
244 | WaitTillIdle(); |
245 | |
246 | CRITEND |
247 | } |
248 | |
249 | static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres, |
250 | int sy, int sx, int dy, int dx, int height, |
251 | int width) |
252 | { |
253 | int start, end; |
254 | CRITFLAGS |
255 | |
256 | DBG(__func__) |
257 | |
258 | CRITBEGIN |
259 | |
260 | if ((dy < sy) || ((dy == sy) && (dx <= sx))) { |
261 | mga_fifo(4); |
262 | matrox_accel_restore_maccess(minfo); |
263 | mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | |
264 | M_DWG_BFCOL | M_DWG_REPLACE); |
265 | mga_outl(M_AR5, vxres); |
266 | width--; |
267 | start = sy*vxres+sx+curr_ydstorg(minfo); |
268 | end = start+width; |
269 | } else { |
270 | mga_fifo(5); |
271 | matrox_accel_restore_maccess(minfo); |
272 | mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); |
273 | mga_outl(M_SGN, 5); |
274 | mga_outl(M_AR5, -vxres); |
275 | width--; |
276 | end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); |
277 | start = end+width; |
278 | dy += height-1; |
279 | } |
280 | mga_fifo(7); |
281 | matrox_accel_restore_maccess(minfo); |
282 | mga_outl(M_AR0, end); |
283 | mga_outl(M_AR3, start); |
284 | mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); |
285 | mga_outl(M_YDST, dy*vxres >> 5); |
286 | mga_outl(M_LEN | M_EXEC, height); |
287 | WaitTillIdle(); |
288 | |
289 | CRITEND |
290 | } |
291 | |
292 | static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) { |
293 | struct matrox_fb_info *minfo = info2minfo(info); |
294 | |
295 | if ((area->sx | area->dx | area->width) & 1) |
296 | cfb_copyarea(info, area); |
297 | else |
298 | matrox_accel_bmove_lin(minfo, vxres: minfo->fbcon.var.xres_virtual >> 1, sy: area->sy, sx: area->sx >> 1, dy: area->dy, dx: area->dx >> 1, height: area->height, width: area->width >> 1); |
299 | } |
300 | |
301 | static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) { |
302 | struct matrox_fb_info *minfo = info2minfo(info); |
303 | |
304 | matrox_accel_bmove(minfo, vxres: minfo->fbcon.var.xres_virtual, sy: area->sy, sx: area->sx, dy: area->dy, dx: area->dx, height: area->height, width: area->width); |
305 | } |
306 | |
307 | static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color, |
308 | int sy, int sx, int height, int width) |
309 | { |
310 | CRITFLAGS |
311 | |
312 | DBG(__func__) |
313 | |
314 | CRITBEGIN |
315 | |
316 | mga_fifo(7); |
317 | matrox_accel_restore_maccess(minfo); |
318 | mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE); |
319 | mga_outl(M_FCOL, color); |
320 | mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); |
321 | mga_ydstlen(sy, height); |
322 | WaitTillIdle(); |
323 | |
324 | CRITEND |
325 | } |
326 | |
327 | static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { |
328 | struct matrox_fb_info *minfo = info2minfo(info); |
329 | |
330 | switch (rect->rop) { |
331 | case ROP_COPY: |
332 | matroxfb_accel_clear(minfo, color: ((u_int32_t *)info->pseudo_palette)[rect->color], sy: rect->dy, sx: rect->dx, height: rect->height, width: rect->width); |
333 | break; |
334 | } |
335 | } |
336 | |
337 | static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx, |
338 | int sy, int sx, int height, int width) |
339 | { |
340 | int whattodo; |
341 | CRITFLAGS |
342 | |
343 | DBG(__func__) |
344 | |
345 | CRITBEGIN |
346 | |
347 | whattodo = 0; |
348 | if (sx & 1) { |
349 | sx ++; |
350 | if (!width) return; |
351 | width --; |
352 | whattodo = 1; |
353 | } |
354 | if (width & 1) { |
355 | whattodo |= 2; |
356 | } |
357 | width >>= 1; |
358 | sx >>= 1; |
359 | if (width) { |
360 | mga_fifo(7); |
361 | matrox_accel_restore_maccess(minfo); |
362 | mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2); |
363 | mga_outl(M_FCOL, bgx); |
364 | mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); |
365 | mga_outl(M_YDST, sy * minfo->fbcon.var.xres_virtual >> 6); |
366 | mga_outl(M_LEN | M_EXEC, height); |
367 | WaitTillIdle(); |
368 | } |
369 | if (whattodo) { |
370 | u_int32_t step = minfo->fbcon.var.xres_virtual >> 1; |
371 | vaddr_t vbase = minfo->video.vbase; |
372 | if (whattodo & 1) { |
373 | unsigned int uaddr = sy * step + sx - 1; |
374 | u_int32_t loop; |
375 | u_int8_t bgx2 = bgx & 0xF0; |
376 | for (loop = height; loop > 0; loop --) { |
377 | mga_writeb(va: vbase, offs: uaddr, value: (mga_readb(va: vbase, offs: uaddr) & 0x0F) | bgx2); |
378 | uaddr += step; |
379 | } |
380 | } |
381 | if (whattodo & 2) { |
382 | unsigned int uaddr = sy * step + sx + width; |
383 | u_int32_t loop; |
384 | u_int8_t bgx2 = bgx & 0x0F; |
385 | for (loop = height; loop > 0; loop --) { |
386 | mga_writeb(va: vbase, offs: uaddr, value: (mga_readb(va: vbase, offs: uaddr) & 0xF0) | bgx2); |
387 | uaddr += step; |
388 | } |
389 | } |
390 | } |
391 | |
392 | CRITEND |
393 | } |
394 | |
395 | static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { |
396 | struct matrox_fb_info *minfo = info2minfo(info); |
397 | |
398 | switch (rect->rop) { |
399 | case ROP_COPY: |
400 | matroxfb_cfb4_clear(minfo, bgx: ((u_int32_t *)info->pseudo_palette)[rect->color], sy: rect->dy, sx: rect->dx, height: rect->height, width: rect->width); |
401 | break; |
402 | } |
403 | } |
404 | |
405 | static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx, |
406 | u_int32_t bgx, const u_int8_t *chardata, |
407 | int width, int height, int yy, int xx) |
408 | { |
409 | u_int32_t step; |
410 | u_int32_t ydstlen; |
411 | u_int32_t xlen; |
412 | u_int32_t ar0; |
413 | u_int32_t charcell; |
414 | u_int32_t fxbndry; |
415 | vaddr_t mmio; |
416 | int easy; |
417 | CRITFLAGS |
418 | |
419 | DBG_HEAVY(__func__); |
420 | |
421 | step = (width + 7) >> 3; |
422 | charcell = height * step; |
423 | xlen = (charcell + 3) & ~3; |
424 | ydstlen = (yy << 16) | height; |
425 | if (width == step << 3) { |
426 | ar0 = height * width - 1; |
427 | easy = 1; |
428 | } else { |
429 | ar0 = width - 1; |
430 | easy = 0; |
431 | } |
432 | |
433 | CRITBEGIN |
434 | |
435 | mga_fifo(5); |
436 | matrox_accel_restore_maccess(minfo); |
437 | if (easy) |
438 | mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE); |
439 | else |
440 | mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE); |
441 | mga_outl(M_FCOL, fgx); |
442 | mga_outl(M_BCOL, bgx); |
443 | fxbndry = ((xx + width - 1) << 16) | xx; |
444 | mmio = minfo->mmio.vbase; |
445 | |
446 | mga_fifo(8); |
447 | matrox_accel_restore_maccess(minfo); |
448 | mga_writel(va: mmio, M_FXBNDRY, value: fxbndry); |
449 | mga_writel(va: mmio, M_AR0, value: ar0); |
450 | mga_writel(va: mmio, M_AR3, value: 0); |
451 | if (easy) { |
452 | mga_writel(va: mmio, M_YDSTLEN | M_EXEC, value: ydstlen); |
453 | mga_memcpy_toio(va: mmio, src: chardata, len: xlen); |
454 | } else { |
455 | mga_writel(va: mmio, M_AR5, value: 0); |
456 | mga_writel(va: mmio, M_YDSTLEN | M_EXEC, value: ydstlen); |
457 | if ((step & 3) == 0) { |
458 | /* Great. Source has 32bit aligned lines, so we can feed them |
459 | directly to the accelerator. */ |
460 | mga_memcpy_toio(va: mmio, src: chardata, len: charcell); |
461 | } else if (step == 1) { |
462 | /* Special case for 1..8bit widths */ |
463 | while (height--) { |
464 | #if defined(__BIG_ENDIAN) |
465 | fb_writel((*chardata) << 24, mmio.vaddr); |
466 | #else |
467 | fb_writel(b: *chardata, addr: mmio.vaddr); |
468 | #endif |
469 | chardata++; |
470 | } |
471 | } else if (step == 2) { |
472 | /* Special case for 9..15bit widths */ |
473 | while (height--) { |
474 | #if defined(__BIG_ENDIAN) |
475 | fb_writel((*(u_int16_t*)chardata) << 16, mmio.vaddr); |
476 | #else |
477 | fb_writel(b: *(u_int16_t*)chardata, addr: mmio.vaddr); |
478 | #endif |
479 | chardata += 2; |
480 | } |
481 | } else { |
482 | /* Tell... well, why bother... */ |
483 | while (height--) { |
484 | size_t i; |
485 | |
486 | for (i = 0; i < step; i += 4) { |
487 | /* Hope that there are at least three readable bytes beyond the end of bitmap */ |
488 | fb_writel(get_unaligned((u_int32_t*)(chardata + i)),addr: mmio.vaddr); |
489 | } |
490 | chardata += step; |
491 | } |
492 | } |
493 | } |
494 | WaitTillIdle(); |
495 | CRITEND |
496 | } |
497 | |
498 | |
499 | static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) { |
500 | struct matrox_fb_info *minfo = info2minfo(info); |
501 | |
502 | DBG_HEAVY(__func__); |
503 | |
504 | if (image->depth == 1) { |
505 | u_int32_t fgx, bgx; |
506 | |
507 | fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; |
508 | bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color]; |
509 | matroxfb_1bpp_imageblit(minfo, fgx, bgx, chardata: image->data, width: image->width, height: image->height, yy: image->dy, xx: image->dx); |
510 | } else { |
511 | /* Danger! image->depth is useless: logo painting code always |
512 | passes framebuffer color depth here, although logo data are |
513 | always 8bpp and info->pseudo_palette is changed to contain |
514 | logo palette to be used (but only for true/direct-color... sic...). |
515 | So do it completely in software... */ |
516 | cfb_imageblit(info, image); |
517 | } |
518 | } |
519 | |
520 | MODULE_LICENSE("GPL" ); |
521 | |