|
@@ -37,6 +37,7 @@
|
|
|
#include "psb_intel_reg.h"
|
|
|
#include "psb_intel_drv.h"
|
|
|
#include "framebuffer.h"
|
|
|
+#include "gtt.h"
|
|
|
|
|
|
#include "mdfld_output.h"
|
|
|
|
|
@@ -91,6 +92,21 @@ 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)
|
|
|
+ psb_gtt_roll(dev, psbfb->gtt, var->yoffset);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
void psbfb_suspend(struct drm_device *dev)
|
|
|
{
|
|
@@ -217,6 +233,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,
|
|
@@ -304,6 +335,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
|
|
@@ -311,11 +343,9 @@ static struct drm_framebuffer *psb_framebuffer_create
|
|
|
* and back it with a GEM object.
|
|
|
*
|
|
|
* 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;
|
|
|
/* 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)
|
|
|
return backing;
|
|
|
psb_gtt_free_range(dev, backing);
|
|
|
}
|
|
|
+ if (!force)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
/* Next try using GEM host memory */
|
|
|
backing = psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0);
|
|
|
if (backing == NULL)
|
|
@@ -359,6 +392,7 @@ static int psbfb_create(struct psb_fbdev *fbdev,
|
|
|
int size;
|
|
|
int ret;
|
|
|
struct gtt_range *backing;
|
|
|
+ int gtt_roll = 1;
|
|
|
|
|
|
mode_cmd.width = sizes->surface_width;
|
|
|
mode_cmd.height = sizes->surface_height;
|
|
@@ -368,17 +402,37 @@ static int psbfb_create(struct psb_fbdev *fbdev,
|
|
|
if (mode_cmd.bpp == 24)
|
|
|
mode_cmd.bpp = 32;
|
|
|
|
|
|
- /* HW requires pitch to be 64 byte aligned */
|
|
|
- mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.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.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 4096);
|
|
|
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 */
|
|
|
- backing = psbfb_alloc(dev, size);
|
|
|
- if (backing == NULL)
|
|
|
- return -ENOMEM;
|
|
|
+ backing = psbfb_alloc(dev, size, 0);
|
|
|
+ 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
|
|
|
+ */
|
|
|
+
|
|
|
+ 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);
|
|
|
|
|
@@ -402,11 +456,14 @@ 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) {
|
|
@@ -416,6 +473,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 = gtt_roll;
|
|
|
|
|
|
if (backing->stolen) {
|
|
|
/* Accessed stolen memory directly */
|