gma500: gtt based hardware scrolling console
authorAlan Cox <alan@linux.intel.com>
Tue, 29 Nov 2011 22:27:22 +0000 (22:27 +0000)
committerDave Airlie <airlied@redhat.com>
Tue, 6 Dec 2011 09:55:39 +0000 (09:55 +0000)
Add support for GTT based scrolling. Instead of pushing bits around we simply
use the GTT to change the mappings. This provides us with a very fast way to
scroll the display providing we have enough memory to allocate on 4K line
boundaries. In practice this seems to be the case except for very big displays
such as HDMI, and the usual configurations are netbooks/tablets.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/gtt.c
drivers/gpu/drm/gma500/gtt.h

index 867a047..652f1ec 100644 (file)
@@ -38,6 +38,7 @@
 #include "psb_intel_reg.h"
 #include "psb_intel_drv.h"
 #include "framebuffer.h"
+#include "gtt.h"
 
 static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb);
 static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb,
@@ -90,6 +91,25 @@ static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green,
        return 0;
 }
 
+static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct psb_fbdev *fbdev = info->par;
+       struct psb_framebuffer *psbfb = &fbdev->pfb;
+       struct drm_device *dev = psbfb->base.dev;
+
+       /*
+        *      We have to poke our nose in here. The core fb code assumes
+        *      panning is part of the hardware that can be invoked before
+        *      the actual fb is mapped. In our case that isn't quite true.
+        */
+       if (psbfb->gtt->npage) {
+               /* GTT roll shifts in 4K pages, we need to shift the right
+                  number of pages */
+               int pages = info->fix.line_length >> 12;
+               psb_gtt_roll(dev, psbfb->gtt, var->yoffset * pages);
+       }
+        return 0;
+}
 
 void psbfb_suspend(struct drm_device *dev)
 {
@@ -216,6 +236,21 @@ static struct fb_ops psbfb_ops = {
        .fb_ioctl = psbfb_ioctl,
 };
 
+static struct fb_ops psbfb_roll_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = drm_fb_helper_check_var,
+       .fb_set_par = drm_fb_helper_set_par,
+       .fb_blank = drm_fb_helper_blank,
+       .fb_setcolreg = psbfb_setcolreg,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+       .fb_pan_display = psbfb_pan,
+       .fb_mmap = psbfb_mmap,
+       .fb_sync = psbfb_sync,
+       .fb_ioctl = psbfb_ioctl,
+};
+
 static struct fb_ops psbfb_unaccel_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
@@ -306,6 +341,7 @@ static struct drm_framebuffer *psb_framebuffer_create
  *     psbfb_alloc             -       allocate frame buffer memory
  *     @dev: the DRM device
  *     @aligned_size: space needed
+ *     @force: fall back to GEM buffers if need be
  *
  *     Allocate the frame buffer. In the usual case we get a GTT range that
  *     is stolen memory backed and life is simple. If there isn't sufficient
@@ -349,6 +385,7 @@ static int psbfb_create(struct psb_fbdev *fbdev,
        int ret;
        struct gtt_range *backing;
        u32 bpp, depth;
+       int gtt_roll = 1;
 
        mode_cmd.width = sizes->surface_width;
        mode_cmd.height = sizes->surface_height;
@@ -358,17 +395,38 @@ static int psbfb_create(struct psb_fbdev *fbdev,
        if (bpp == 24)
                bpp = 32;
 
-       /* HW requires pitch to be 64 byte aligned */
-       mode_cmd.pitches[0] =  ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64);
+       /* Acceleration via the GTT requires pitch to be 4096 byte aligned 
+          (ie 1024 or 2048 pixels in normal use) */
+       mode_cmd.pitches[0] =  ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096);
        depth = sizes->surface_depth;
 
        size = mode_cmd.pitches[0] * mode_cmd.height;
        size = ALIGN(size, PAGE_SIZE);
 
-       /* Allocate the framebuffer in the GTT with stolen page backing */
+       /* Try and allocate with the alignment we need */
        backing = psbfb_alloc(dev, size);
-       if (backing == NULL)
-               return -ENOMEM;
+       if (backing == NULL) {
+               /*
+                *      We couldn't get the space we wanted, fall back to the
+                *      display engine requirement instead.  The HW requires
+                *      the pitch to be 64 byte aligned
+                *
+                *      FIXME: We could try alignments in a loop so that we can still
+                *      accelerate power of two font sizes.
+                */
+
+               gtt_roll = 0;   /* Don't use GTT accelerated scrolling */
+
+               mode_cmd.pitches[0] =  ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64);
+
+               size = mode_cmd.pitches[0] * mode_cmd.height;
+               size = ALIGN(size, PAGE_SIZE);
+
+               /* Allocate the framebuffer in the GTT with stolen page backing */
+               backing = psbfb_alloc(dev, size);
+               if (backing == NULL)
+                       return -ENOMEM;
+       }
 
        mutex_lock(&dev->struct_mutex);
 
@@ -394,11 +452,13 @@ static int psbfb_create(struct psb_fbdev *fbdev,
        strcpy(info->fix.id, "psbfb");
 
        info->flags = FBINFO_DEFAULT;
-       /* No 2D engine */
-       if (!dev_priv->ops->accel_2d)
-               info->fbops = &psbfb_unaccel_ops;
-       else
+       if (gtt_roll) { /* GTT rolling seems best */
+               info->fbops = &psbfb_roll_ops;
+               info->flags |= FBINFO_HWACCEL_YPAN;
+        } else if (dev_priv->ops->accel_2d)    /* 2D engine */
                info->fbops = &psbfb_ops;
+       else    /* Software */
+               info->fbops = &psbfb_unaccel_ops;
 
        ret = fb_alloc_cmap(&info->cmap, 256, 0);
        if (ret) {
@@ -408,6 +468,8 @@ static int psbfb_create(struct psb_fbdev *fbdev,
 
        info->fix.smem_start = dev->mode_config.fb_base;
        info->fix.smem_len = size;
+       info->fix.ywrapstep = gtt_roll;
+       info->fix.ypanstep = 0;
 
        /* Accessed stolen memory directly */
        info->screen_base = (char *)dev_priv->vram_addr +
index a246239..e770bd1 100644 (file)
@@ -95,12 +95,17 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r)
        set_pages_array_uc(pages, r->npage);
 
        /* Write our page entries into the GTT itself */
-       for (i = 0; i < r->npage; i++) {
-               pte = psb_gtt_mask_pte(page_to_pfn(*pages++), 0/*type*/);
+       for (i = r->roll; i < r->npage; i++) {
+               pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
+               iowrite32(pte, gtt_slot++);
+       }
+       for (i = 0; i < r->roll; i++) {
+               pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
                iowrite32(pte, gtt_slot++);
        }
        /* Make sure all the entries are set before we return */
        ioread32(gtt_slot - 1);
+
        return 0;
 }
 
@@ -113,7 +118,6 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r)
  *     page table entries with the dummy page. This is protected via the gtt
  *     mutex which the caller must hold.
  */
-
 static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
 {
        struct drm_psb_private *dev_priv = dev->dev_private;
@@ -132,6 +136,46 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
 }
 
 /**
+ *     psb_gtt_roll    -       set scrolling position
+ *     @dev: our DRM device
+ *     @r: the gtt mapping we are using
+ *     @roll: roll offset
+ *
+ *     Roll an existing pinned mapping by moving the pages through the GTT.
+ *     This allows us to implement hardware scrolling on the consoles without
+ *     a 2D engine
+ */
+void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll)
+{
+       u32 *gtt_slot, pte;
+       int i;
+
+       if (roll >= r->npage) {
+               WARN_ON(1);
+               return;
+       }
+
+       r->roll = roll;
+
+       /* Not currently in the GTT - no worry we will write the mapping at
+          the right position when it gets pinned */
+       if (!r->stolen && !r->in_gart)
+               return;
+
+       gtt_slot = psb_gtt_entry(dev, r);
+
+       for (i = r->roll; i < r->npage; i++) {
+               pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
+               iowrite32(pte, gtt_slot++);
+       }
+       for (i = 0; i < r->roll; i++) {
+               pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
+               iowrite32(pte, gtt_slot++);
+       }
+       ioread32(gtt_slot - 1);
+}
+
+/**
  *     psb_gtt_attach_pages    -       attach and pin GEM pages
  *     @gt: the gtt range
  *
@@ -302,6 +346,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
        gt->resource.name = name;
        gt->stolen = backed;
        gt->in_gart = backed;
+       gt->roll = 0;
        /* Ensure this is set for non GEM objects */
        gt->gem.dev = dev;
        ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
index e0e1cb6..aa17423 100644 (file)
@@ -49,6 +49,7 @@ struct gtt_range {
        bool mmapping;                  /* Is mmappable */
        struct page **pages;            /* Backing pages if present */
        int npage;                      /* Number of backing pages */
+       int roll;                       /* Roll applied to the GTT entries */
 };
 
 extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
@@ -57,5 +58,7 @@ extern void psb_gtt_kref_put(struct gtt_range *gt);
 extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt);
 extern int psb_gtt_pin(struct gtt_range *gt);
 extern void psb_gtt_unpin(struct gtt_range *gt);
+extern void psb_gtt_roll(struct drm_device *dev,
+                                       struct gtt_range *gt, int roll);
 
 #endif