diff options
Diffstat (limited to 'drivers/video/i810/i810_accel.c')
-rw-r--r-- | drivers/video/i810/i810_accel.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/video/i810/i810_accel.c b/drivers/video/i810/i810_accel.c new file mode 100644 index 000000000000..64cd1c827cf0 --- /dev/null +++ b/drivers/video/i810/i810_accel.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /*-*- linux-c -*- | ||
2 | * linux/drivers/video/i810_accel.c -- Hardware Acceleration | ||
3 | * | ||
4 | * Copyright (C) 2001 Antonino Daplas<adaplas@pol.net> | ||
5 | * All Rights Reserved | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file COPYING in the main directory of this archive for | ||
9 | * more details. | ||
10 | */ | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/fb.h> | ||
14 | |||
15 | #include "i810_regs.h" | ||
16 | #include "i810.h" | ||
17 | |||
18 | static u32 i810fb_rop[] = { | ||
19 | COLOR_COPY_ROP, /* ROP_COPY */ | ||
20 | XOR_ROP /* ROP_XOR */ | ||
21 | }; | ||
22 | |||
23 | /* Macros */ | ||
24 | #define PUT_RING(n) { \ | ||
25 | i810_writel(par->cur_tail, par->iring.virtual, n); \ | ||
26 | par->cur_tail += 4; \ | ||
27 | par->cur_tail &= RING_SIZE_MASK; \ | ||
28 | } | ||
29 | |||
30 | extern void flush_cache(void); | ||
31 | |||
32 | /************************************************************/ | ||
33 | |||
34 | /* BLT Engine Routines */ | ||
35 | static inline void i810_report_error(u8 __iomem *mmio) | ||
36 | { | ||
37 | printk("IIR : 0x%04x\n" | ||
38 | "EIR : 0x%04x\n" | ||
39 | "PGTBL_ER: 0x%04x\n" | ||
40 | "IPEIR : 0x%04x\n" | ||
41 | "IPEHR : 0x%04x\n", | ||
42 | i810_readw(IIR, mmio), | ||
43 | i810_readb(EIR, mmio), | ||
44 | i810_readl(PGTBL_ER, mmio), | ||
45 | i810_readl(IPEIR, mmio), | ||
46 | i810_readl(IPEHR, mmio)); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * wait_for_space - check ring buffer free space | ||
51 | * @space: amount of ringbuffer space needed in bytes | ||
52 | * @par: pointer to i810fb_par structure | ||
53 | * | ||
54 | * DESCRIPTION: | ||
55 | * The function waits until a free space from the ringbuffer | ||
56 | * is available | ||
57 | */ | ||
58 | static inline int wait_for_space(struct fb_info *info, u32 space) | ||
59 | { | ||
60 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
61 | u32 head, count = WAIT_COUNT, tail; | ||
62 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
63 | |||
64 | tail = par->cur_tail; | ||
65 | while (count--) { | ||
66 | head = i810_readl(IRING + 4, mmio) & RBUFFER_HEAD_MASK; | ||
67 | if ((tail == head) || | ||
68 | (tail > head && | ||
69 | (par->iring.size - tail + head) >= space) || | ||
70 | (tail < head && (head - tail) >= space)) { | ||
71 | return 0; | ||
72 | } | ||
73 | } | ||
74 | printk("ringbuffer lockup!!!\n"); | ||
75 | i810_report_error(mmio); | ||
76 | par->dev_flags |= LOCKUP; | ||
77 | info->pixmap.scan_align = 1; | ||
78 | return 1; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * wait_for_engine_idle - waits for all hardware engines to finish | ||
83 | * @par: pointer to i810fb_par structure | ||
84 | * | ||
85 | * DESCRIPTION: | ||
86 | * This waits for lring(0), iring(1), and batch(3), etc to finish and | ||
87 | * waits until ringbuffer is empty. | ||
88 | */ | ||
89 | static inline int wait_for_engine_idle(struct fb_info *info) | ||
90 | { | ||
91 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
92 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
93 | int count = WAIT_COUNT; | ||
94 | |||
95 | if (wait_for_space(info, par->iring.size)) /* flush */ | ||
96 | return 1; | ||
97 | |||
98 | while((i810_readw(INSTDONE, mmio) & 0x7B) != 0x7B && --count); | ||
99 | if (count) return 0; | ||
100 | |||
101 | printk("accel engine lockup!!!\n"); | ||
102 | printk("INSTDONE: 0x%04x\n", i810_readl(INSTDONE, mmio)); | ||
103 | i810_report_error(mmio); | ||
104 | par->dev_flags |= LOCKUP; | ||
105 | info->pixmap.scan_align = 1; | ||
106 | return 1; | ||
107 | } | ||
108 | |||
109 | /* begin_iring - prepares the ringbuffer | ||
110 | * @space: length of sequence in dwords | ||
111 | * @par: pointer to i810fb_par structure | ||
112 | * | ||
113 | * DESCRIPTION: | ||
114 | * Checks/waits for sufficent space in ringbuffer of size | ||
115 | * space. Returns the tail of the buffer | ||
116 | */ | ||
117 | static inline u32 begin_iring(struct fb_info *info, u32 space) | ||
118 | { | ||
119 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
120 | |||
121 | if (par->dev_flags & ALWAYS_SYNC) | ||
122 | wait_for_engine_idle(info); | ||
123 | return wait_for_space(info, space); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * end_iring - advances the buffer | ||
128 | * @par: pointer to i810fb_par structure | ||
129 | * | ||
130 | * DESCRIPTION: | ||
131 | * This advances the tail of the ringbuffer, effectively | ||
132 | * beginning the execution of the graphics instruction sequence. | ||
133 | */ | ||
134 | static inline void end_iring(struct i810fb_par *par) | ||
135 | { | ||
136 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
137 | |||
138 | i810_writel(IRING, mmio, par->cur_tail); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * source_copy_blit - BLIT transfer operation | ||
143 | * @dwidth: width of rectangular graphics data | ||
144 | * @dheight: height of rectangular graphics data | ||
145 | * @dpitch: bytes per line of destination buffer | ||
146 | * @xdir: direction of copy (left to right or right to left) | ||
147 | * @src: address of first pixel to read from | ||
148 | * @dest: address of first pixel to write to | ||
149 | * @from: source address | ||
150 | * @where: destination address | ||
151 | * @rop: raster operation | ||
152 | * @blit_bpp: pixel format which can be different from the | ||
153 | * framebuffer's pixelformat | ||
154 | * @par: pointer to i810fb_par structure | ||
155 | * | ||
156 | * DESCRIPTION: | ||
157 | * This is a BLIT operation typically used when doing | ||
158 | * a 'Copy and Paste' | ||
159 | */ | ||
160 | static inline void source_copy_blit(int dwidth, int dheight, int dpitch, | ||
161 | int xdir, int src, int dest, int rop, | ||
162 | int blit_bpp, struct fb_info *info) | ||
163 | { | ||
164 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
165 | |||
166 | if (begin_iring(info, 24 + IRING_PAD)) return; | ||
167 | |||
168 | PUT_RING(BLIT | SOURCE_COPY_BLIT | 4); | ||
169 | PUT_RING(xdir | rop << 16 | dpitch | DYN_COLOR_EN | blit_bpp); | ||
170 | PUT_RING(dheight << 16 | dwidth); | ||
171 | PUT_RING(dest); | ||
172 | PUT_RING(dpitch); | ||
173 | PUT_RING(src); | ||
174 | |||
175 | end_iring(par); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * color_blit - solid color BLIT operation | ||
180 | * @width: width of destination | ||
181 | * @height: height of destination | ||
182 | * @pitch: pixels per line of the buffer | ||
183 | * @dest: address of first pixel to write to | ||
184 | * @where: destination | ||
185 | * @rop: raster operation | ||
186 | * @what: color to transfer | ||
187 | * @blit_bpp: pixel format which can be different from the | ||
188 | * framebuffer's pixelformat | ||
189 | * @par: pointer to i810fb_par structure | ||
190 | * | ||
191 | * DESCRIPTION: | ||
192 | * A BLIT operation which can be used for color fill/rectangular fill | ||
193 | */ | ||
194 | static inline void color_blit(int width, int height, int pitch, int dest, | ||
195 | int rop, int what, int blit_bpp, | ||
196 | struct fb_info *info) | ||
197 | { | ||
198 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
199 | |||
200 | if (begin_iring(info, 24 + IRING_PAD)) return; | ||
201 | |||
202 | PUT_RING(BLIT | COLOR_BLT | 3); | ||
203 | PUT_RING(rop << 16 | pitch | SOLIDPATTERN | DYN_COLOR_EN | blit_bpp); | ||
204 | PUT_RING(height << 16 | width); | ||
205 | PUT_RING(dest); | ||
206 | PUT_RING(what); | ||
207 | PUT_RING(NOP); | ||
208 | |||
209 | end_iring(par); | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * mono_src_copy_imm_blit - color expand from system memory to framebuffer | ||
214 | * @dwidth: width of destination | ||
215 | * @dheight: height of destination | ||
216 | * @dpitch: pixels per line of the buffer | ||
217 | * @dsize: size of bitmap in double words | ||
218 | * @dest: address of first byte of pixel; | ||
219 | * @rop: raster operation | ||
220 | * @blit_bpp: pixelformat to use which can be different from the | ||
221 | * framebuffer's pixelformat | ||
222 | * @src: address of image data | ||
223 | * @bg: backgound color | ||
224 | * @fg: forground color | ||
225 | * @par: pointer to i810fb_par structure | ||
226 | * | ||
227 | * DESCRIPTION: | ||
228 | * A color expand operation where the source data is placed in the | ||
229 | * ringbuffer itself. Useful for drawing text. | ||
230 | * | ||
231 | * REQUIREMENT: | ||
232 | * The end of a scanline must be padded to the next word. | ||
233 | */ | ||
234 | static inline void mono_src_copy_imm_blit(int dwidth, int dheight, int dpitch, | ||
235 | int dsize, int blit_bpp, int rop, | ||
236 | int dest, const u32 *src, int bg, | ||
237 | int fg, struct fb_info *info) | ||
238 | { | ||
239 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
240 | |||
241 | if (begin_iring(info, 24 + (dsize << 2) + IRING_PAD)) return; | ||
242 | |||
243 | PUT_RING(BLIT | MONO_SOURCE_COPY_IMMEDIATE | (4 + dsize)); | ||
244 | PUT_RING(DYN_COLOR_EN | blit_bpp | rop << 16 | dpitch); | ||
245 | PUT_RING(dheight << 16 | dwidth); | ||
246 | PUT_RING(dest); | ||
247 | PUT_RING(bg); | ||
248 | PUT_RING(fg); | ||
249 | while (dsize--) | ||
250 | PUT_RING(*src++); | ||
251 | |||
252 | end_iring(par); | ||
253 | } | ||
254 | |||
255 | static inline void load_front(int offset, struct fb_info *info) | ||
256 | { | ||
257 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
258 | |||
259 | if (begin_iring(info, 8 + IRING_PAD)) return; | ||
260 | |||
261 | PUT_RING(PARSER | FLUSH); | ||
262 | PUT_RING(NOP); | ||
263 | |||
264 | end_iring(par); | ||
265 | |||
266 | if (begin_iring(info, 8 + IRING_PAD)) return; | ||
267 | |||
268 | PUT_RING(PARSER | FRONT_BUFFER | ((par->pitch >> 3) << 8)); | ||
269 | PUT_RING((par->fb.offset << 12) + offset); | ||
270 | |||
271 | end_iring(par); | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * i810fb_iring_enable - enables/disables the ringbuffer | ||
276 | * @mode: enable or disable | ||
277 | * @par: pointer to i810fb_par structure | ||
278 | * | ||
279 | * DESCRIPTION: | ||
280 | * Enables or disables the ringbuffer, effectively enabling or | ||
281 | * disabling the instruction/acceleration engine. | ||
282 | */ | ||
283 | static inline void i810fb_iring_enable(struct i810fb_par *par, u32 mode) | ||
284 | { | ||
285 | u32 tmp; | ||
286 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
287 | |||
288 | tmp = i810_readl(IRING + 12, mmio); | ||
289 | if (mode == OFF) | ||
290 | tmp &= ~1; | ||
291 | else | ||
292 | tmp |= 1; | ||
293 | flush_cache(); | ||
294 | i810_writel(IRING + 12, mmio, tmp); | ||
295 | } | ||
296 | |||
297 | void i810fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
298 | { | ||
299 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
300 | u32 dx, dy, width, height, dest, rop = 0, color = 0; | ||
301 | |||
302 | if (!info->var.accel_flags || par->dev_flags & LOCKUP || | ||
303 | par->depth == 4) | ||
304 | return cfb_fillrect(info, rect); | ||
305 | |||
306 | if (par->depth == 1) | ||
307 | color = rect->color; | ||
308 | else | ||
309 | color = ((u32 *) (info->pseudo_palette))[rect->color]; | ||
310 | |||
311 | rop = i810fb_rop[rect->rop]; | ||
312 | |||
313 | dx = rect->dx * par->depth; | ||
314 | width = rect->width * par->depth; | ||
315 | dy = rect->dy; | ||
316 | height = rect->height; | ||
317 | |||
318 | dest = info->fix.smem_start + (dy * info->fix.line_length) + dx; | ||
319 | color_blit(width, height, info->fix.line_length, dest, rop, color, | ||
320 | par->blit_bpp, info); | ||
321 | } | ||
322 | |||
323 | void i810fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) | ||
324 | { | ||
325 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
326 | u32 sx, sy, dx, dy, pitch, width, height, src, dest, xdir; | ||
327 | |||
328 | if (!info->var.accel_flags || par->dev_flags & LOCKUP || | ||
329 | par->depth == 4) | ||
330 | return cfb_copyarea(info, region); | ||
331 | |||
332 | dx = region->dx * par->depth; | ||
333 | sx = region->sx * par->depth; | ||
334 | width = region->width * par->depth; | ||
335 | sy = region->sy; | ||
336 | dy = region->dy; | ||
337 | height = region->height; | ||
338 | |||
339 | if (dx <= sx) { | ||
340 | xdir = INCREMENT; | ||
341 | } | ||
342 | else { | ||
343 | xdir = DECREMENT; | ||
344 | sx += width - 1; | ||
345 | dx += width - 1; | ||
346 | } | ||
347 | if (dy <= sy) { | ||
348 | pitch = info->fix.line_length; | ||
349 | } | ||
350 | else { | ||
351 | pitch = (-(info->fix.line_length)) & 0xFFFF; | ||
352 | sy += height - 1; | ||
353 | dy += height - 1; | ||
354 | } | ||
355 | src = info->fix.smem_start + (sy * info->fix.line_length) + sx; | ||
356 | dest = info->fix.smem_start + (dy * info->fix.line_length) + dx; | ||
357 | |||
358 | source_copy_blit(width, height, pitch, xdir, src, dest, | ||
359 | PAT_COPY_ROP, par->blit_bpp, info); | ||
360 | } | ||
361 | |||
362 | void i810fb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
363 | { | ||
364 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
365 | u32 fg = 0, bg = 0, size, dst; | ||
366 | |||
367 | if (!info->var.accel_flags || par->dev_flags & LOCKUP || | ||
368 | par->depth == 4 || image->depth != 1) | ||
369 | return cfb_imageblit(info, image); | ||
370 | |||
371 | switch (info->var.bits_per_pixel) { | ||
372 | case 8: | ||
373 | fg = image->fg_color; | ||
374 | bg = image->bg_color; | ||
375 | break; | ||
376 | case 16: | ||
377 | case 24: | ||
378 | fg = ((u32 *)(info->pseudo_palette))[image->fg_color]; | ||
379 | bg = ((u32 *)(info->pseudo_palette))[image->bg_color]; | ||
380 | break; | ||
381 | } | ||
382 | |||
383 | dst = info->fix.smem_start + (image->dy * info->fix.line_length) + | ||
384 | (image->dx * par->depth); | ||
385 | |||
386 | size = (image->width+7)/8 + 1; | ||
387 | size &= ~1; | ||
388 | size *= image->height; | ||
389 | size += 7; | ||
390 | size &= ~7; | ||
391 | mono_src_copy_imm_blit(image->width * par->depth, | ||
392 | image->height, info->fix.line_length, | ||
393 | size/4, par->blit_bpp, | ||
394 | PAT_COPY_ROP, dst, (u32 *) image->data, | ||
395 | bg, fg, info); | ||
396 | } | ||
397 | |||
398 | int i810fb_sync(struct fb_info *info) | ||
399 | { | ||
400 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
401 | |||
402 | if (!info->var.accel_flags || par->dev_flags & LOCKUP) | ||
403 | return 0; | ||
404 | |||
405 | return wait_for_engine_idle(info); | ||
406 | } | ||
407 | |||
408 | void i810fb_load_front(u32 offset, struct fb_info *info) | ||
409 | { | ||
410 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
411 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
412 | |||
413 | if (!info->var.accel_flags || par->dev_flags & LOCKUP) | ||
414 | i810_writel(DPLYBASE, mmio, par->fb.physical + offset); | ||
415 | else | ||
416 | load_front(offset, info); | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * i810fb_init_ringbuffer - initialize the ringbuffer | ||
421 | * @par: pointer to i810fb_par structure | ||
422 | * | ||
423 | * DESCRIPTION: | ||
424 | * Initializes the ringbuffer by telling the device the | ||
425 | * size and location of the ringbuffer. It also sets | ||
426 | * the head and tail pointers = 0 | ||
427 | */ | ||
428 | void i810fb_init_ringbuffer(struct fb_info *info) | ||
429 | { | ||
430 | struct i810fb_par *par = (struct i810fb_par *) info->par; | ||
431 | u32 tmp1, tmp2; | ||
432 | u8 __iomem *mmio = par->mmio_start_virtual; | ||
433 | |||
434 | wait_for_engine_idle(info); | ||
435 | i810fb_iring_enable(par, OFF); | ||
436 | i810_writel(IRING, mmio, 0); | ||
437 | i810_writel(IRING + 4, mmio, 0); | ||
438 | par->cur_tail = 0; | ||
439 | |||
440 | tmp2 = i810_readl(IRING + 8, mmio) & ~RBUFFER_START_MASK; | ||
441 | tmp1 = par->iring.physical; | ||
442 | i810_writel(IRING + 8, mmio, tmp2 | tmp1); | ||
443 | |||
444 | tmp1 = i810_readl(IRING + 12, mmio); | ||
445 | tmp1 &= ~RBUFFER_SIZE_MASK; | ||
446 | tmp2 = (par->iring.size - I810_PAGESIZE) & RBUFFER_SIZE_MASK; | ||
447 | i810_writel(IRING + 12, mmio, tmp1 | tmp2); | ||
448 | i810fb_iring_enable(par, ON); | ||
449 | } | ||