linux/drivers/video/fbdev/i810/i810_accel.c
<<
>>
Prefs
   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
  19static 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
  31extern void flush_cache(void);
  32
  33/************************************************************/
  34
  35/* BLT Engine Routines */
  36static 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 */     
  59static 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 */
  90static 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 */ 
 118static 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 */
 135static 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 */
 161static 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 */
 195static 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 */
 235static 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
 256static 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 */
 284static 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
 298void 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        
 326void 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
 367void 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
 405int 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
 415void 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 */
 435void 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