Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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, and the usual configurations are netbooks/tablets.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Alan Cox and committed by
Dave Airlie
a6ba582d 9242fe23

+122 -12
+71 -9
drivers/gpu/drm/gma500/framebuffer.c
··· 38 38 #include "psb_intel_reg.h" 39 39 #include "psb_intel_drv.h" 40 40 #include "framebuffer.h" 41 + #include "gtt.h" 41 42 42 43 static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb); 43 44 static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, ··· 91 90 return 0; 92 91 } 93 92 93 + static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) 94 + { 95 + struct psb_fbdev *fbdev = info->par; 96 + struct psb_framebuffer *psbfb = &fbdev->pfb; 97 + struct drm_device *dev = psbfb->base.dev; 98 + 99 + /* 100 + * We have to poke our nose in here. The core fb code assumes 101 + * panning is part of the hardware that can be invoked before 102 + * the actual fb is mapped. In our case that isn't quite true. 103 + */ 104 + if (psbfb->gtt->npage) { 105 + /* GTT roll shifts in 4K pages, we need to shift the right 106 + number of pages */ 107 + int pages = info->fix.line_length >> 12; 108 + psb_gtt_roll(dev, psbfb->gtt, var->yoffset * pages); 109 + } 110 + return 0; 111 + } 94 112 95 113 void psbfb_suspend(struct drm_device *dev) 96 114 { ··· 236 216 .fb_ioctl = psbfb_ioctl, 237 217 }; 238 218 219 + static struct fb_ops psbfb_roll_ops = { 220 + .owner = THIS_MODULE, 221 + .fb_check_var = drm_fb_helper_check_var, 222 + .fb_set_par = drm_fb_helper_set_par, 223 + .fb_blank = drm_fb_helper_blank, 224 + .fb_setcolreg = psbfb_setcolreg, 225 + .fb_fillrect = cfb_fillrect, 226 + .fb_copyarea = cfb_copyarea, 227 + .fb_imageblit = cfb_imageblit, 228 + .fb_pan_display = psbfb_pan, 229 + .fb_mmap = psbfb_mmap, 230 + .fb_sync = psbfb_sync, 231 + .fb_ioctl = psbfb_ioctl, 232 + }; 233 + 239 234 static struct fb_ops psbfb_unaccel_ops = { 240 235 .owner = THIS_MODULE, 241 236 .fb_check_var = drm_fb_helper_check_var, ··· 341 306 * psbfb_alloc - allocate frame buffer memory 342 307 * @dev: the DRM device 343 308 * @aligned_size: space needed 309 + * @force: fall back to GEM buffers if need be 344 310 * 345 311 * Allocate the frame buffer. In the usual case we get a GTT range that 346 312 * is stolen memory backed and life is simple. If there isn't sufficient ··· 385 349 int ret; 386 350 struct gtt_range *backing; 387 351 u32 bpp, depth; 352 + int gtt_roll = 1; 388 353 389 354 mode_cmd.width = sizes->surface_width; 390 355 mode_cmd.height = sizes->surface_height; ··· 395 358 if (bpp == 24) 396 359 bpp = 32; 397 360 398 - /* HW requires pitch to be 64 byte aligned */ 399 - mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); 361 + /* Acceleration via the GTT requires pitch to be 4096 byte aligned 362 + (ie 1024 or 2048 pixels in normal use) */ 363 + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096); 400 364 depth = sizes->surface_depth; 401 365 402 366 size = mode_cmd.pitches[0] * mode_cmd.height; 403 367 size = ALIGN(size, PAGE_SIZE); 404 368 405 - /* Allocate the framebuffer in the GTT with stolen page backing */ 369 + /* Try and allocate with the alignment we need */ 406 370 backing = psbfb_alloc(dev, size); 407 - if (backing == NULL) 408 - return -ENOMEM; 371 + if (backing == NULL) { 372 + /* 373 + * We couldn't get the space we wanted, fall back to the 374 + * display engine requirement instead. The HW requires 375 + * the pitch to be 64 byte aligned 376 + * 377 + * FIXME: We could try alignments in a loop so that we can still 378 + * accelerate power of two font sizes. 379 + */ 380 + 381 + gtt_roll = 0; /* Don't use GTT accelerated scrolling */ 382 + 383 + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); 384 + 385 + size = mode_cmd.pitches[0] * mode_cmd.height; 386 + size = ALIGN(size, PAGE_SIZE); 387 + 388 + /* Allocate the framebuffer in the GTT with stolen page backing */ 389 + backing = psbfb_alloc(dev, size); 390 + if (backing == NULL) 391 + return -ENOMEM; 392 + } 409 393 410 394 mutex_lock(&dev->struct_mutex); 411 395 ··· 452 394 strcpy(info->fix.id, "psbfb"); 453 395 454 396 info->flags = FBINFO_DEFAULT; 455 - /* No 2D engine */ 456 - if (!dev_priv->ops->accel_2d) 457 - info->fbops = &psbfb_unaccel_ops; 458 - else 397 + if (gtt_roll) { /* GTT rolling seems best */ 398 + info->fbops = &psbfb_roll_ops; 399 + info->flags |= FBINFO_HWACCEL_YPAN; 400 + } else if (dev_priv->ops->accel_2d) /* 2D engine */ 459 401 info->fbops = &psbfb_ops; 402 + else /* Software */ 403 + info->fbops = &psbfb_unaccel_ops; 460 404 461 405 ret = fb_alloc_cmap(&info->cmap, 256, 0); 462 406 if (ret) { ··· 468 408 469 409 info->fix.smem_start = dev->mode_config.fb_base; 470 410 info->fix.smem_len = size; 411 + info->fix.ywrapstep = gtt_roll; 412 + info->fix.ypanstep = 0; 471 413 472 414 /* Accessed stolen memory directly */ 473 415 info->screen_base = (char *)dev_priv->vram_addr +
+48 -3
drivers/gpu/drm/gma500/gtt.c
··· 95 95 set_pages_array_uc(pages, r->npage); 96 96 97 97 /* Write our page entries into the GTT itself */ 98 - for (i = 0; i < r->npage; i++) { 99 - pte = psb_gtt_mask_pte(page_to_pfn(*pages++), 0/*type*/); 98 + for (i = r->roll; i < r->npage; i++) { 99 + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); 100 + iowrite32(pte, gtt_slot++); 101 + } 102 + for (i = 0; i < r->roll; i++) { 103 + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); 100 104 iowrite32(pte, gtt_slot++); 101 105 } 102 106 /* Make sure all the entries are set before we return */ 103 107 ioread32(gtt_slot - 1); 108 + 104 109 return 0; 105 110 } 106 111 ··· 118 113 * page table entries with the dummy page. This is protected via the gtt 119 114 * mutex which the caller must hold. 120 115 */ 121 - 122 116 static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) 123 117 { 124 118 struct drm_psb_private *dev_priv = dev->dev_private; ··· 133 129 iowrite32(pte, gtt_slot++); 134 130 ioread32(gtt_slot - 1); 135 131 set_pages_array_wb(r->pages, r->npage); 132 + } 133 + 134 + /** 135 + * psb_gtt_roll - set scrolling position 136 + * @dev: our DRM device 137 + * @r: the gtt mapping we are using 138 + * @roll: roll offset 139 + * 140 + * Roll an existing pinned mapping by moving the pages through the GTT. 141 + * This allows us to implement hardware scrolling on the consoles without 142 + * a 2D engine 143 + */ 144 + void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) 145 + { 146 + u32 *gtt_slot, pte; 147 + int i; 148 + 149 + if (roll >= r->npage) { 150 + WARN_ON(1); 151 + return; 152 + } 153 + 154 + r->roll = roll; 155 + 156 + /* Not currently in the GTT - no worry we will write the mapping at 157 + the right position when it gets pinned */ 158 + if (!r->stolen && !r->in_gart) 159 + return; 160 + 161 + gtt_slot = psb_gtt_entry(dev, r); 162 + 163 + for (i = r->roll; i < r->npage; i++) { 164 + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); 165 + iowrite32(pte, gtt_slot++); 166 + } 167 + for (i = 0; i < r->roll; i++) { 168 + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); 169 + iowrite32(pte, gtt_slot++); 170 + } 171 + ioread32(gtt_slot - 1); 136 172 } 137 173 138 174 /** ··· 346 302 gt->resource.name = name; 347 303 gt->stolen = backed; 348 304 gt->in_gart = backed; 305 + gt->roll = 0; 349 306 /* Ensure this is set for non GEM objects */ 350 307 gt->gem.dev = dev; 351 308 ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
+3
drivers/gpu/drm/gma500/gtt.h
··· 49 49 bool mmapping; /* Is mmappable */ 50 50 struct page **pages; /* Backing pages if present */ 51 51 int npage; /* Number of backing pages */ 52 + int roll; /* Roll applied to the GTT entries */ 52 53 }; 53 54 54 55 extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, ··· 58 57 extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt); 59 58 extern int psb_gtt_pin(struct gtt_range *gt); 60 59 extern void psb_gtt_unpin(struct gtt_range *gt); 60 + extern void psb_gtt_roll(struct drm_device *dev, 61 + struct gtt_range *gt, int roll); 61 62 62 63 #endif