vserver 1.9.5.x5
[linux-2.6.git] / drivers / video / aty / radeon_accel.c
index 82614b4..dc8598d 100644 (file)
@@ -4,6 +4,41 @@
  * "ACCEL_MMIO" ifdef branches in XFree86
  * --dte
  */
+
+static void radeon_fixup_offset(struct radeonfb_info *rinfo)
+{
+       u32 local_base;
+
+       /* *** Ugly workaround *** */
+       /*
+        * On some platforms, the video memory is mapped at 0 in radeon chip space
+        * (like PPCs) by the firmware. X will always move it up so that it's seen
+        * by the chip to be at the same address as the PCI BAR.
+        * That means that when switching back from X, there is a mismatch between
+        * the offsets programmed into the engine. This means that potentially,
+        * accel operations done before radeonfb has a chance to re-init the engine
+        * will have incorrect offsets, and potentially trash system memory !
+        *
+        * The correct fix is for fbcon to never call any accel op before the engine
+        * has properly been re-initialized (by a call to set_var), but this is a
+        * complex fix. This workaround in the meantime, called before every accel
+        * operation, makes sure the offsets are in sync.
+        */
+
+       radeon_fifo_wait (1);
+       local_base = INREG(MC_FB_LOCATION) << 16;
+       if (local_base == rinfo->fb_local_base)
+               return;
+
+       rinfo->fb_local_base = local_base;
+
+       radeon_fifo_wait (3);
+       OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
+                                    (rinfo->fb_local_base >> 10));
+       OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+       OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+}
+
 static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo, 
                                   const struct fb_fillrect *region)
 {
@@ -33,13 +68,15 @@ void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region)
   
        if (info->state != FBINFO_STATE_RUNNING)
                return;
-       if (radeon_accel_disabled()) {
+       if (info->flags & FBINFO_HWACCEL_DISABLED) {
                cfb_fillrect(info, region);
                return;
        }
 
-       vxres = info->var.xres;
-       vyres = info->var.yres;
+       radeon_fixup_offset(rinfo);
+
+       vxres = info->var.xres_virtual;
+       vyres = info->var.yres_virtual;
 
        memcpy(&modded, region, sizeof(struct fb_fillrect));
 
@@ -71,9 +108,10 @@ static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo,
        radeon_fifo_wait(3);
        OUTREG(DP_GUI_MASTER_CNTL,
                rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */
+               | GMC_BRUSH_NONE
                | GMC_SRC_DSTCOLOR
                | ROP3_S 
-               | DP_SRC_RECT );
+               | DP_SRC_SOURCE_MEMORY );
        OUTREG(DP_WRITE_MSK, 0xffffffff);
        OUTREG(DP_CNTL, (xdir>=0 ? DST_X_LEFT_TO_RIGHT : 0)
                        | (ydir>=0 ? DST_Y_TOP_TO_BOTTOM : 0));
@@ -99,13 +137,15 @@ void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
   
        if (info->state != FBINFO_STATE_RUNNING)
                return;
-       if (radeon_accel_disabled()) {
+       if (info->flags & FBINFO_HWACCEL_DISABLED) {
                cfb_copyarea(info, area);
                return;
        }
 
-       vxres = info->var.xres;
-       vyres = info->var.yres;
+       radeon_fixup_offset(rinfo);
+
+       vxres = info->var.xres_virtual;
+       vyres = info->var.yres_virtual;
 
        if(!modded.width || !modded.height ||
           modded.sx >= vxres || modded.sy >= vyres ||