Commit ef86de58 authored by Alan Cox's avatar Alan Cox Committed by Greg Kroah-Hartman

staging: gma500: gtt based hardware scrolling console

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. It works out nicely on the usual configurations are netbooks and
tablets.
Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c7e285be
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "psb_intel_reg.h" #include "psb_intel_reg.h"
#include "psb_intel_drv.h" #include "psb_intel_drv.h"
#include "framebuffer.h" #include "framebuffer.h"
#include "gtt.h"
#include "mdfld_output.h" #include "mdfld_output.h"
...@@ -91,6 +92,21 @@ static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, ...@@ -91,6 +92,21 @@ static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 0; 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)
psb_gtt_roll(dev, psbfb->gtt, var->yoffset);
return 0;
}
void psbfb_suspend(struct drm_device *dev) void psbfb_suspend(struct drm_device *dev)
{ {
...@@ -217,6 +233,21 @@ static struct fb_ops psbfb_ops = { ...@@ -217,6 +233,21 @@ static struct fb_ops psbfb_ops = {
.fb_ioctl = psbfb_ioctl, .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 = { static struct fb_ops psbfb_unaccel_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var, .fb_check_var = drm_fb_helper_check_var,
...@@ -304,6 +335,7 @@ static struct drm_framebuffer *psb_framebuffer_create ...@@ -304,6 +335,7 @@ static struct drm_framebuffer *psb_framebuffer_create
* psbfb_alloc - allocate frame buffer memory * psbfb_alloc - allocate frame buffer memory
* @dev: the DRM device * @dev: the DRM device
* @aligned_size: space needed * @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 * 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 * is stolen memory backed and life is simple. If there isn't sufficient
...@@ -311,11 +343,9 @@ static struct drm_framebuffer *psb_framebuffer_create ...@@ -311,11 +343,9 @@ static struct drm_framebuffer *psb_framebuffer_create
* and back it with a GEM object. * and back it with a GEM object.
* *
* In this case the GEM object has no handle. * In this case the GEM object has no handle.
*
* FIXME: console speed up - allocate twice the space if room and use
* hardware scrolling for acceleration.
*/ */
static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) static struct gtt_range *psbfb_alloc(struct drm_device *dev,
int aligned_size, int force)
{ {
struct gtt_range *backing; struct gtt_range *backing;
/* Begin by trying to use stolen memory backing */ /* Begin by trying to use stolen memory backing */
...@@ -326,6 +356,9 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) ...@@ -326,6 +356,9 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size)
return backing; return backing;
psb_gtt_free_range(dev, backing); psb_gtt_free_range(dev, backing);
} }
if (!force)
return NULL;
/* Next try using GEM host memory */ /* Next try using GEM host memory */
backing = psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0); backing = psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0);
if (backing == NULL) if (backing == NULL)
...@@ -359,6 +392,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, ...@@ -359,6 +392,7 @@ static int psbfb_create(struct psb_fbdev *fbdev,
int size; int size;
int ret; int ret;
struct gtt_range *backing; struct gtt_range *backing;
int gtt_roll = 1;
mode_cmd.width = sizes->surface_width; mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height; mode_cmd.height = sizes->surface_height;
...@@ -368,17 +402,37 @@ static int psbfb_create(struct psb_fbdev *fbdev, ...@@ -368,17 +402,37 @@ static int psbfb_create(struct psb_fbdev *fbdev,
if (mode_cmd.bpp == 24) if (mode_cmd.bpp == 24)
mode_cmd.bpp = 32; mode_cmd.bpp = 32;
/* HW requires pitch to be 64 byte aligned */ /* Acceleration via the GTT requires pitch to be 4096 byte aligned
mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 64); (ie 1024 or 2048 pixels in normal use) */
mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 4096);
mode_cmd.depth = sizes->surface_depth; mode_cmd.depth = sizes->surface_depth;
size = mode_cmd.pitch * mode_cmd.height; size = mode_cmd.pitch * mode_cmd.height;
size = ALIGN(size, PAGE_SIZE); size = ALIGN(size, PAGE_SIZE);
/* Allocate the framebuffer in the GTT with stolen page backing */ /* Allocate the framebuffer in the GTT with stolen page backing */
backing = psbfb_alloc(dev, size); backing = psbfb_alloc(dev, size, 0);
if (backing == NULL) if (backing == NULL) {
return -ENOMEM; /*
* 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
*/
gtt_roll = 0; /* Don't use GTT accelerated scrolling */
mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 64);
mode_cmd.depth = sizes->surface_depth;
size = mode_cmd.pitch * mode_cmd.height;
size = ALIGN(size, PAGE_SIZE);
/* Allocate the framebuffer in the GTT with stolen page
backing when there is room */
backing = psbfb_alloc(dev, size, 1);
if (backing == NULL)
return -ENOMEM;
}
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -402,11 +456,14 @@ static int psbfb_create(struct psb_fbdev *fbdev, ...@@ -402,11 +456,14 @@ static int psbfb_create(struct psb_fbdev *fbdev,
strcpy(info->fix.id, "psbfb"); strcpy(info->fix.id, "psbfb");
info->flags = FBINFO_DEFAULT; info->flags = FBINFO_DEFAULT;
/* No 2D engine */ if (gtt_roll) { /* GTT rolling seems best */
if (!dev_priv->ops->accel_2d) info->fbops = &psbfb_roll_ops;
info->fbops = &psbfb_unaccel_ops; info->flags |= FBINFO_HWACCEL_YPAN;
else }
else if (dev_priv->ops->accel_2d) /* 2D engine */
info->fbops = &psbfb_ops; info->fbops = &psbfb_ops;
else /* Software */
info->fbops = &psbfb_unaccel_ops;
ret = fb_alloc_cmap(&info->cmap, 256, 0); ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret) { if (ret) {
...@@ -416,6 +473,8 @@ static int psbfb_create(struct psb_fbdev *fbdev, ...@@ -416,6 +473,8 @@ static int psbfb_create(struct psb_fbdev *fbdev,
info->fix.smem_start = dev->mode_config.fb_base; info->fix.smem_start = dev->mode_config.fb_base;
info->fix.smem_len = size; info->fix.smem_len = size;
info->fix.ywrapstep = gtt_roll;
info->fix.ypanstep = gtt_roll;
if (backing->stolen) { if (backing->stolen) {
/* Accessed stolen memory directly */ /* Accessed stolen memory directly */
......
...@@ -95,12 +95,17 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) ...@@ -95,12 +95,17 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r)
set_pages_array_uc(pages, r->npage); set_pages_array_uc(pages, r->npage);
/* Write our page entries into the GTT itself */ /* Write our page entries into the GTT itself */
for (i = 0; i < r->npage; i++) { for (i = r->roll; i < r->npage; i++) {
pte = psb_gtt_mask_pte(page_to_pfn(*pages++), 0/*type*/); 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++); iowrite32(pte, gtt_slot++);
} }
/* Make sure all the entries are set before we return */ /* Make sure all the entries are set before we return */
ioread32(gtt_slot - 1); ioread32(gtt_slot - 1);
return 0; return 0;
} }
...@@ -113,7 +118,6 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) ...@@ -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 * page table entries with the dummy page. This is protected via the gtt
* mutex which the caller must hold. * mutex which the caller must hold.
*/ */
static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
{ {
struct drm_psb_private *dev_priv = dev->dev_private; struct drm_psb_private *dev_priv = dev->dev_private;
...@@ -131,6 +135,46 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) ...@@ -131,6 +135,46 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
set_pages_array_wb(r->pages, r->npage); set_pages_array_wb(r->pages, r->npage);
} }
/**
* 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 * psb_gtt_attach_pages - attach and pin GEM pages
* @gt: the gtt range * @gt: the gtt range
...@@ -302,6 +346,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, ...@@ -302,6 +346,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
gt->resource.name = name; gt->resource.name = name;
gt->stolen = backed; gt->stolen = backed;
gt->in_gart = backed; gt->in_gart = backed;
gt->roll = 0;
/* Ensure this is set for non GEM objects */ /* Ensure this is set for non GEM objects */
gt->gem.dev = dev; gt->gem.dev = dev;
ret = allocate_resource(dev_priv->gtt_mem, &gt->resource, ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
......
...@@ -49,6 +49,7 @@ struct gtt_range { ...@@ -49,6 +49,7 @@ struct gtt_range {
bool mmapping; /* Is mmappable */ bool mmapping; /* Is mmappable */
struct page **pages; /* Backing pages if present */ struct page **pages; /* Backing pages if present */
int npage; /* Number of backing pages */ 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, 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); ...@@ -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 void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt);
extern int psb_gtt_pin(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_unpin(struct gtt_range *gt);
extern void psb_gtt_roll(struct drm_device *dev,
struct gtt_range *gt, int roll);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment