unsigned int height;
        unsigned long full_offset;      /* start of fullscreen DDR fb */
        unsigned long fb_offset;        /* start of actual DDR fb */
+       unsigned long pan_offset;
 };
 
 struct ps3fb_res_table {
        base = frame * yres * line_length;
 
        ps3fb_sync_image(info->device, base + par->full_offset,
-                        base + par->fb_offset, base, par->width, par->height,
-                        line_length);
+                        base + par->fb_offset, base + par->pan_offset,
+                        par->width, par->height, line_length);
 
 out:
        release_console_sem();
        if (!mode)
                return -EINVAL;
 
-       /*
-        *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
-        *  as FB_VMODE_SMOOTH_XPAN is only used internally
-        */
+       /* Virtual screen */
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
 
-       if (var->vmode & FB_VMODE_CONUPDATE) {
-               var->vmode |= FB_VMODE_YWRAP;
-               var->xoffset = info->var.xoffset;
-               var->yoffset = info->var.yoffset;
-       }
-
-       /* Virtual screen and panning are not supported */
-       if (var->xres_virtual > var->xres || var->yres_virtual > var->yres ||
-           var->xoffset || var->yoffset) {
+       if (var->xres_virtual > line_length / BPP) {
                dev_dbg(info->device,
-                       "Virtual screen and panning are not supported\n");
+                       "Horizontal virtual screen size too large\n");
                return -EINVAL;
        }
 
-       var->xres_virtual = var->xres;
-       var->yres_virtual = var->yres;
+       if (var->xoffset + var->xres > var->xres_virtual ||
+           var->yoffset + var->yres > var->yres_virtual) {
+               dev_dbg(info->device, "panning out-of-range\n");
+               return -EINVAL;
+       }
 
        /* We support ARGB8888 only */
        if (var->bits_per_pixel > 32 || var->grayscale ||
        }
 
        /* Memory limit */
-       if (var->yres * line_length > ps3fb.xdr_size) {
+       if (var->yres_virtual * line_length > ps3fb.xdr_size) {
                dev_dbg(info->device, "Not enough memory\n");
                return -ENOMEM;
        }
 static int ps3fb_set_par(struct fb_info *info)
 {
        struct ps3fb_par *par = info->par;
-       unsigned int mode, lines, maxlines;
+       unsigned int mode, line_length, lines, maxlines;
        int i;
        unsigned long offset, dst;
 
                info->var.xres, info->var.xres_virtual,
                info->var.yres, info->var.yres_virtual, info->var.pixclock);
 
-       mode = ps3fb_find_mode(&info->var, &info->fix.line_length);
+       mode = ps3fb_find_mode(&info->var, &line_length);
        if (!mode)
                return -EINVAL;
 
 
        info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea);
        info->fix.smem_len = ps3fb.xdr_size;
+       info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
+       info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
+       info->fix.line_length = line_length;
+
        info->screen_base = (char __iomem *)ps3fb.xdr_ea;
 
        par->num_frames = info->fix.smem_len/
        offset = VP_OFF(i);
        par->fb_offset = GPU_ALIGN_UP(offset);
        par->full_offset = par->fb_offset - offset;
+       par->pan_offset = info->var.yoffset * line_length +
+                         info->var.xoffset * BPP;
 
        if (par->new_mode_id != par->mode_id) {
                if (ps3av_set_video_mode(par->new_mode_id)) {
        lines = ps3fb_res[i].yres * par->num_frames;
        if (par->full_offset)
                lines++;
-       maxlines = ps3fb.xdr_size / info->fix.line_length;
-       for (dst = 0; lines; dst += maxlines * info->fix.line_length) {
+       maxlines = ps3fb.xdr_size / line_length;
+       for (dst = 0; lines; dst += maxlines * line_length) {
                unsigned int l = min(lines, maxlines);
                ps3fb_sync_image(info->device, 0, dst, 0, ps3fb_res[i].xres, l,
-                                info->fix.line_length);
+                                line_length);
                lines -= l;
        }
 
        return 0;
 }
 
+static int ps3fb_pan_display(struct fb_var_screeninfo *var,
+                            struct fb_info *info)
+{
+       struct ps3fb_par *par = info->par;
+
+       par->pan_offset = var->yoffset * info->fix.line_length +
+                         var->xoffset * BPP;
+       return 0;
+}
+
     /*
      *  As we have a virtual frame buffer, we need our own mmap function
      */
        .fb_check_var   = ps3fb_check_var,
        .fb_set_par     = ps3fb_set_par,
        .fb_setcolreg   = ps3fb_setcolreg,
+       .fb_pan_display = ps3fb_pan_display,
        .fb_fillrect    = sys_fillrect,
        .fb_copyarea    = sys_copyarea,
        .fb_imageblit   = sys_imageblit,
        info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea);
        info->fix.smem_len = ps3fb.xdr_size;
        info->pseudo_palette = par->pseudo_palette;
-       info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST;
+       info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
+                     FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
 
        retval = fb_alloc_cmap(&info->cmap, 256, 0);
        if (retval < 0)