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

drm/fb-helper: Map DRM client buffer only when required

This patch changes DRM clients to not map the buffer by default. The
buffer, like any buffer object, should be mapped and unmapped when
needed.

An unmapped buffer object can be evicted to system memory and does
not consume video ram until displayed. This allows to use generic fbdev
emulation with drivers for low-memory devices, such as ast and mgag200.

This change affects the generic framebuffer console. HW-based consoles
map their console buffer once and keep it mapped. Userspace can mmap this
buffer into its address space. The shadow-buffered framebuffer console
only needs the buffer object to be mapped during updates. While not being
updated from the shadow buffer, the buffer object can remain unmapped.
Userspace will always mmap the shadow buffer.

v2:
* change DRM client to not map buffer by default
* manually map client buffer for fbdev with HW framebuffer

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Noralf Trønnes <noralf@tronnes.org>
Link: https://patchwork.freedesktop.org/patch/315830/
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

authored by

Thomas Zimmermann and committed by
Gerd Hoffmann
87e281f8 d9b42dfa

+29 -20
+4 -12
drivers/gpu/drm/drm_client.c
··· 254 254 struct drm_device *dev = client->dev; 255 255 struct drm_client_buffer *buffer; 256 256 struct drm_gem_object *obj; 257 - void *vaddr; 258 257 int ret; 259 258 260 259 buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); ··· 280 281 281 282 buffer->gem = obj; 282 283 283 - vaddr = drm_client_buffer_vmap(buffer); 284 - if (IS_ERR(vaddr)) { 285 - ret = PTR_ERR(vaddr); 286 - goto err_delete; 287 - } 288 - 289 284 return buffer; 290 285 291 286 err_delete: ··· 298 305 * Client buffer mappings are not ref'counted. Each call to 299 306 * drm_client_buffer_vmap() should be followed by a call to 300 307 * drm_client_buffer_vunmap(); or the client buffer should be mapped 301 - * throughout its lifetime. The latter is the default. 308 + * throughout its lifetime. 302 309 * 303 310 * Returns: 304 311 * The mapped memory's address ··· 332 339 * drm_client_buffer_vunmap - Unmap DRM client buffer 333 340 * @buffer: DRM client buffer 334 341 * 335 - * This function removes a client buffer's memory mapping. This 336 - * function is only required by clients that manage their buffers 337 - * by themselves. By default, DRM client buffers are mapped throughout 338 - * their entire lifetime. 342 + * This function removes a client buffer's memory mapping. Calling this 343 + * function is only required by clients that manage their buffer mappings 344 + * by themselves. 339 345 */ 340 346 void drm_client_buffer_vunmap(struct drm_client_buffer *buffer) 341 347 {
+25 -8
drivers/gpu/drm/drm_fb_helper.c
··· 403 403 struct drm_clip_rect *clip = &helper->dirty_clip; 404 404 struct drm_clip_rect clip_copy; 405 405 unsigned long flags; 406 + void *vaddr; 406 407 407 408 spin_lock_irqsave(&helper->dirty_lock, flags); 408 409 clip_copy = *clip; ··· 413 412 414 413 /* call dirty callback only when it has been really touched */ 415 414 if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { 415 + 416 416 /* Generic fbdev uses a shadow buffer */ 417 - if (helper->buffer) 417 + if (helper->buffer) { 418 + vaddr = drm_client_buffer_vmap(helper->buffer); 419 + if (IS_ERR(vaddr)) 420 + return; 418 421 drm_fb_helper_dirty_blit_real(helper, &clip_copy); 422 + } 419 423 helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); 424 + 425 + if (helper->buffer) 426 + drm_client_buffer_vunmap(helper->buffer); 420 427 } 421 428 } 422 429 ··· 2187 2178 struct drm_framebuffer *fb; 2188 2179 struct fb_info *fbi; 2189 2180 u32 format; 2181 + void *vaddr; 2190 2182 2191 2183 DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", 2192 2184 sizes->surface_width, sizes->surface_height, ··· 2210 2200 fbi->fbops = &drm_fbdev_fb_ops; 2211 2201 fbi->screen_size = fb->height * fb->pitches[0]; 2212 2202 fbi->fix.smem_len = fbi->screen_size; 2213 - fbi->screen_buffer = buffer->vaddr; 2214 - /* Shamelessly leak the physical address to user-space */ 2215 - #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 2216 - if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) 2217 - fbi->fix.smem_start = 2218 - page_to_phys(virt_to_page(fbi->screen_buffer)); 2219 - #endif 2203 + 2220 2204 drm_fb_helper_fill_info(fbi, fb_helper, sizes); 2221 2205 2222 2206 if (fb->funcs->dirty) { ··· 2235 2231 fbi->fbdefio = &drm_fbdev_defio; 2236 2232 2237 2233 fb_deferred_io_init(fbi); 2234 + } else { 2235 + /* buffer is mapped for HW framebuffer */ 2236 + vaddr = drm_client_buffer_vmap(fb_helper->buffer); 2237 + if (IS_ERR(vaddr)) 2238 + return PTR_ERR(vaddr); 2239 + 2240 + fbi->screen_buffer = vaddr; 2241 + /* Shamelessly leak the physical address to user-space */ 2242 + #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 2243 + if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) 2244 + fbi->fix.smem_start = 2245 + page_to_phys(virt_to_page(fbi->screen_buffer)); 2246 + #endif 2238 2247 } 2239 2248 2240 2249 return 0;