diff options
author | Rob Clark <rob@ti.com> | 2012-01-16 13:51:15 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-02-08 17:14:11 -0500 |
commit | ae43d7ca4047b126adedcf7028c1ff99ed18703c (patch) | |
tree | b3052ce49c59e91f0400e6afe8c7b924793934b7 | |
parent | ff4f38765d0e3bcc7d992d07cb27c37adb9b16ba (diff) |
staging: drm/omap: drm API update: addfb2
Update to reflect changes in:
"drm: add an fb creation ioctl that takes a pixel format v5"
Signed-off-by: Rob Clark <rob@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/staging/omapdrm/omap_drv.h | 30 | ||||
-rw-r--r-- | drivers/staging/omapdrm/omap_fb.c | 103 | ||||
-rw-r--r-- | drivers/staging/omapdrm/omap_fbdev.c | 55 |
3 files changed, 143 insertions, 45 deletions
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index 76c42515ecc5..4ad2ae55ec43 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/types.h> | 25 | #include <linux/types.h> |
26 | #include <drm/drmP.h> | 26 | #include <drm/drmP.h> |
27 | #include <drm/drm_crtc_helper.h> | ||
27 | #include "omap_drm.h" | 28 | #include "omap_drm.h" |
28 | #include "omap_priv.h" | 29 | #include "omap_priv.h" |
29 | 30 | ||
@@ -80,9 +81,9 @@ void omap_connector_flush(struct drm_connector *connector, | |||
80 | int x, int y, int w, int h); | 81 | int x, int y, int w, int h); |
81 | 82 | ||
82 | struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, | 83 | struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, |
83 | struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); | 84 | struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd); |
84 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, | 85 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, |
85 | struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); | 86 | struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); |
86 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); | 87 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); |
87 | int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, | 88 | int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, |
88 | void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); | 89 | void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); |
@@ -132,4 +133,29 @@ static inline int align_pitch(int pitch, int width, int bpp) | |||
132 | return ALIGN(pitch, 8 * bytespp); | 133 | return ALIGN(pitch, 8 * bytespp); |
133 | } | 134 | } |
134 | 135 | ||
136 | /* should these be made into common util helpers? | ||
137 | */ | ||
138 | |||
139 | static inline int objects_lookup(struct drm_device *dev, | ||
140 | struct drm_file *filp, uint32_t pixel_format, | ||
141 | struct drm_gem_object **bos, uint32_t *handles) | ||
142 | { | ||
143 | int i, n = drm_format_num_planes(pixel_format); | ||
144 | |||
145 | for (i = 0; i < n; i++) { | ||
146 | bos[i] = drm_gem_object_lookup(dev, filp, handles[i]); | ||
147 | if (!bos[i]) { | ||
148 | goto fail; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | return 0; | ||
153 | |||
154 | fail: | ||
155 | while (--i > 0) { | ||
156 | drm_gem_object_unreference_unlocked(bos[i]); | ||
157 | } | ||
158 | return -ENOENT; | ||
159 | } | ||
160 | |||
135 | #endif /* __OMAP_DRV_H__ */ | 161 | #endif /* __OMAP_DRV_H__ */ |
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 0b50c5b3b564..805a18e559c4 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c | |||
@@ -22,11 +22,43 @@ | |||
22 | #include "drm_crtc.h" | 22 | #include "drm_crtc.h" |
23 | #include "drm_crtc_helper.h" | 23 | #include "drm_crtc_helper.h" |
24 | 24 | ||
25 | |||
26 | /* | 25 | /* |
27 | * framebuffer funcs | 26 | * framebuffer funcs |
28 | */ | 27 | */ |
29 | 28 | ||
29 | /* per-format info: */ | ||
30 | struct format { | ||
31 | enum omap_color_mode dss_format; | ||
32 | uint32_t pixel_format; | ||
33 | struct { | ||
34 | int stride_bpp; /* this times width is stride */ | ||
35 | int sub_y; /* sub-sample in y dimension */ | ||
36 | } planes[4]; | ||
37 | bool yuv; | ||
38 | }; | ||
39 | |||
40 | static const struct format formats[] = { | ||
41 | /* 16bpp [A]RGB: */ | ||
42 | { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ | ||
43 | { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ | ||
44 | { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ | ||
45 | { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ | ||
46 | { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ | ||
47 | { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ | ||
48 | { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ | ||
49 | /* 24bpp RGB: */ | ||
50 | { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ | ||
51 | /* 32bpp [A]RGB: */ | ||
52 | { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ | ||
53 | { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ | ||
54 | { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ | ||
55 | { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ | ||
56 | /* YUV: */ | ||
57 | { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, | ||
58 | { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, | ||
59 | { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, | ||
60 | }; | ||
61 | |||
30 | #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) | 62 | #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) |
31 | 63 | ||
32 | struct omap_framebuffer { | 64 | struct omap_framebuffer { |
@@ -34,6 +66,7 @@ struct omap_framebuffer { | |||
34 | struct drm_gem_object *bo; | 66 | struct drm_gem_object *bo; |
35 | int size; | 67 | int size; |
36 | dma_addr_t paddr; | 68 | dma_addr_t paddr; |
69 | const struct format *format; | ||
37 | }; | 70 | }; |
38 | 71 | ||
39 | static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, | 72 | static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, |
@@ -91,7 +124,7 @@ int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, | |||
91 | int bpp = fb->bits_per_pixel / 8; | 124 | int bpp = fb->bits_per_pixel / 8; |
92 | unsigned long offset; | 125 | unsigned long offset; |
93 | 126 | ||
94 | offset = (x * bpp) + (y * fb->pitch); | 127 | offset = (x * bpp) + (y * fb->pitches[0]); |
95 | 128 | ||
96 | if (vaddr) { | 129 | if (vaddr) { |
97 | void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); | 130 | void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); |
@@ -105,7 +138,7 @@ int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, | |||
105 | } | 138 | } |
106 | 139 | ||
107 | *paddr = omap_fb->paddr + offset; | 140 | *paddr = omap_fb->paddr + offset; |
108 | *screen_width = fb->pitch / bpp; | 141 | *screen_width = fb->pitches[0] / bpp; |
109 | 142 | ||
110 | return omap_fb->size - offset; | 143 | return omap_fb->size - offset; |
111 | } | 144 | } |
@@ -171,39 +204,61 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb, | |||
171 | } | 204 | } |
172 | 205 | ||
173 | struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, | 206 | struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, |
174 | struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd) | 207 | struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd) |
175 | { | 208 | { |
176 | struct drm_gem_object *bo; | 209 | struct drm_gem_object *bos[4]; |
177 | struct drm_framebuffer *fb; | 210 | struct drm_framebuffer *fb; |
178 | bo = drm_gem_object_lookup(dev, file, mode_cmd->handle); | 211 | int ret; |
179 | if (!bo) { | 212 | |
180 | return ERR_PTR(-ENOENT); | 213 | ret = objects_lookup(dev, file, mode_cmd->pixel_format, |
181 | } | 214 | bos, mode_cmd->handles); |
182 | fb = omap_framebuffer_init(dev, mode_cmd, bo); | 215 | if (ret) |
183 | if (!fb) { | 216 | return ERR_PTR(ret); |
184 | return ERR_PTR(-ENOMEM); | 217 | |
218 | fb = omap_framebuffer_init(dev, mode_cmd, bos); | ||
219 | if (IS_ERR(fb)) { | ||
220 | int i, n = drm_format_num_planes(mode_cmd->pixel_format); | ||
221 | for (i = 0; i < n; i++) | ||
222 | drm_gem_object_unreference_unlocked(bos[i]); | ||
223 | return fb; | ||
185 | } | 224 | } |
186 | return fb; | 225 | return fb; |
187 | } | 226 | } |
188 | 227 | ||
189 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, | 228 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, |
190 | struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo) | 229 | struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) |
191 | { | 230 | { |
192 | struct omap_framebuffer *omap_fb; | 231 | struct omap_framebuffer *omap_fb; |
193 | struct drm_framebuffer *fb = NULL; | 232 | struct drm_framebuffer *fb = NULL; |
194 | int size, ret; | 233 | const struct format *format = NULL; |
234 | int i, size, ret; | ||
195 | 235 | ||
196 | DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)", | 236 | DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", |
197 | dev, mode_cmd, mode_cmd->width, mode_cmd->height, | 237 | dev, mode_cmd, mode_cmd->width, mode_cmd->height, |
198 | mode_cmd->bpp); | 238 | (char *)&mode_cmd->pixel_format); |
239 | |||
240 | for (i = 0; i < ARRAY_SIZE(formats); i++) { | ||
241 | if (formats[i].pixel_format == mode_cmd->pixel_format) { | ||
242 | format = &formats[i]; | ||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | if (!format) { | ||
248 | dev_err(dev->dev, "unsupported pixel format: %4.4s\n", | ||
249 | (char *)&mode_cmd->pixel_format); | ||
250 | ret = -EINVAL; | ||
251 | goto fail; | ||
252 | } | ||
199 | 253 | ||
200 | /* in case someone tries to feed us a completely bogus stride: */ | 254 | /* in case someone tries to feed us a completely bogus stride: */ |
201 | mode_cmd->pitch = align_pitch(mode_cmd->pitch, | 255 | mode_cmd->pitches[0] = align_pitch(mode_cmd->pitches[0], |
202 | mode_cmd->width, mode_cmd->bpp); | 256 | mode_cmd->width, format->planes[0].stride_bpp); |
203 | 257 | ||
204 | omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); | 258 | omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); |
205 | if (!omap_fb) { | 259 | if (!omap_fb) { |
206 | dev_err(dev->dev, "could not allocate fb\n"); | 260 | dev_err(dev->dev, "could not allocate fb\n"); |
261 | ret = -ENOMEM; | ||
207 | goto fail; | 262 | goto fail; |
208 | } | 263 | } |
209 | 264 | ||
@@ -216,17 +271,19 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, | |||
216 | 271 | ||
217 | DBG("create: FB ID: %d (%p)", fb->base.id, fb); | 272 | DBG("create: FB ID: %d (%p)", fb->base.id, fb); |
218 | 273 | ||
219 | size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height); | 274 | size = PAGE_ALIGN(mode_cmd->pitches[0] * mode_cmd->height); |
220 | 275 | ||
221 | if (size > bo->size) { | 276 | if (size > bos[0]->size) { |
222 | dev_err(dev->dev, "provided buffer object is too small!\n"); | 277 | dev_err(dev->dev, "provided buffer object is too small!\n"); |
278 | ret = -EINVAL; | ||
223 | goto fail; | 279 | goto fail; |
224 | } | 280 | } |
225 | 281 | ||
226 | omap_fb->bo = bo; | 282 | omap_fb->bo = bos[0]; |
227 | omap_fb->size = size; | 283 | omap_fb->size = size; |
228 | 284 | ||
229 | if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { | 285 | ret = omap_gem_get_paddr(bos[0], &omap_fb->paddr, true); |
286 | if (ret) { | ||
230 | dev_err(dev->dev, "could not map (paddr)!\n"); | 287 | dev_err(dev->dev, "could not map (paddr)!\n"); |
231 | goto fail; | 288 | goto fail; |
232 | } | 289 | } |
@@ -239,5 +296,5 @@ fail: | |||
239 | if (fb) { | 296 | if (fb) { |
240 | omap_framebuffer_destroy(fb); | 297 | omap_framebuffer_destroy(fb); |
241 | } | 298 | } |
242 | return NULL; | 299 | return ERR_PTR(ret); |
243 | } | 300 | } |
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 093ae2f87b20..ba4530697fe8 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c | |||
@@ -129,10 +129,8 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, | |||
129 | struct drm_framebuffer *fb = NULL; | 129 | struct drm_framebuffer *fb = NULL; |
130 | union omap_gem_size gsize; | 130 | union omap_gem_size gsize; |
131 | struct fb_info *fbi = NULL; | 131 | struct fb_info *fbi = NULL; |
132 | struct drm_mode_fb_cmd mode_cmd = {0}; | 132 | struct drm_mode_fb_cmd2 mode_cmd = {0}; |
133 | dma_addr_t paddr; | 133 | dma_addr_t paddr; |
134 | void __iomem *vaddr; | ||
135 | int size, screen_width; | ||
136 | int ret; | 134 | int ret; |
137 | 135 | ||
138 | /* only doing ARGB32 since this is what is needed to alpha-blend | 136 | /* only doing ARGB32 since this is what is needed to alpha-blend |
@@ -145,36 +143,56 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, | |||
145 | sizes->surface_height, sizes->surface_bpp, | 143 | sizes->surface_height, sizes->surface_bpp, |
146 | sizes->fb_width, sizes->fb_height); | 144 | sizes->fb_width, sizes->fb_height); |
147 | 145 | ||
146 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | ||
147 | sizes->surface_depth); | ||
148 | |||
148 | mode_cmd.width = sizes->surface_width; | 149 | mode_cmd.width = sizes->surface_width; |
149 | mode_cmd.height = sizes->surface_height; | 150 | mode_cmd.height = sizes->surface_height; |
150 | 151 | ||
151 | mode_cmd.bpp = sizes->surface_bpp; | 152 | mode_cmd.pitches[0] = align_pitch( |
152 | mode_cmd.depth = sizes->surface_depth; | 153 | mode_cmd.width * ((sizes->surface_bpp + 7) / 8), |
153 | 154 | mode_cmd.width, sizes->surface_bpp); | |
154 | mode_cmd.pitch = align_pitch( | ||
155 | mode_cmd.width * ((mode_cmd.bpp + 7) / 8), | ||
156 | mode_cmd.width, mode_cmd.bpp); | ||
157 | 155 | ||
158 | fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled; | 156 | fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled; |
159 | if (fbdev->ywrap_enabled) { | 157 | if (fbdev->ywrap_enabled) { |
160 | /* need to align pitch to page size if using DMM scrolling */ | 158 | /* need to align pitch to page size if using DMM scrolling */ |
161 | mode_cmd.pitch = ALIGN(mode_cmd.pitch, PAGE_SIZE); | 159 | mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE); |
162 | } | 160 | } |
163 | 161 | ||
164 | /* allocate backing bo */ | 162 | /* allocate backing bo */ |
165 | gsize = (union omap_gem_size){ | 163 | gsize = (union omap_gem_size){ |
166 | .bytes = PAGE_ALIGN(mode_cmd.pitch * mode_cmd.height), | 164 | .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height), |
167 | }; | 165 | }; |
168 | DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); | 166 | DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); |
169 | fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); | 167 | fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); |
170 | if (!fbdev->bo) { | 168 | if (!fbdev->bo) { |
171 | dev_err(dev->dev, "failed to allocate buffer object\n"); | 169 | dev_err(dev->dev, "failed to allocate buffer object\n"); |
170 | ret = -ENOMEM; | ||
172 | goto fail; | 171 | goto fail; |
173 | } | 172 | } |
174 | 173 | ||
175 | fb = omap_framebuffer_init(dev, &mode_cmd, fbdev->bo); | 174 | fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo); |
176 | if (!fb) { | 175 | if (IS_ERR(fb)) { |
177 | dev_err(dev->dev, "failed to allocate fb\n"); | 176 | dev_err(dev->dev, "failed to allocate fb\n"); |
177 | /* note: if fb creation failed, we can't rely on fb destroy | ||
178 | * to unref the bo: | ||
179 | */ | ||
180 | drm_gem_object_unreference(fbdev->bo); | ||
181 | ret = PTR_ERR(fb); | ||
182 | goto fail; | ||
183 | } | ||
184 | |||
185 | /* note: this keeps the bo pinned.. which is perhaps not ideal, | ||
186 | * but is needed as long as we use fb_mmap() to mmap to userspace | ||
187 | * (since this happens using fix.smem_start). Possibly we could | ||
188 | * implement our own mmap using GEM mmap support to avoid this | ||
189 | * (non-tiled buffer doesn't need to be pinned for fbcon to write | ||
190 | * to it). Then we just need to be sure that we are able to re- | ||
191 | * pin it in case of an opps. | ||
192 | */ | ||
193 | ret = omap_gem_get_paddr(fbdev->bo, &paddr, true); | ||
194 | if (ret) { | ||
195 | dev_err(dev->dev, "could not map (paddr)!\n"); | ||
178 | ret = -ENOMEM; | 196 | ret = -ENOMEM; |
179 | goto fail; | 197 | goto fail; |
180 | } | 198 | } |
@@ -206,18 +224,15 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, | |||
206 | goto fail_unlock; | 224 | goto fail_unlock; |
207 | } | 225 | } |
208 | 226 | ||
209 | drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); | 227 | drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); |
210 | drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); | 228 | drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); |
211 | 229 | ||
212 | size = omap_framebuffer_get_buffer(fb, 0, 0, | ||
213 | &vaddr, &paddr, &screen_width); | ||
214 | |||
215 | dev->mode_config.fb_base = paddr; | 230 | dev->mode_config.fb_base = paddr; |
216 | 231 | ||
217 | fbi->screen_base = vaddr; | 232 | fbi->screen_base = omap_gem_vaddr(fbdev->bo); |
218 | fbi->screen_size = size; | 233 | fbi->screen_size = fbdev->bo->size; |
219 | fbi->fix.smem_start = paddr; | 234 | fbi->fix.smem_start = paddr; |
220 | fbi->fix.smem_len = size; | 235 | fbi->fix.smem_len = fbdev->bo->size; |
221 | 236 | ||
222 | /* if we have DMM, then we can use it for scrolling by just | 237 | /* if we have DMM, then we can use it for scrolling by just |
223 | * shuffling pages around in DMM rather than doing sw blit. | 238 | * shuffling pages around in DMM rather than doing sw blit. |