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