Commit 87e281f8 authored by Thomas Zimmermann's avatar Thomas Zimmermann Committed by Gerd Hoffmann

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: default avatarThomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: default avatarNoralf Trønnes <noralf@tronnes.org>
Link: https://patchwork.freedesktop.org/patch/315830/Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent d9b42dfa
...@@ -254,7 +254,6 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u ...@@ -254,7 +254,6 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
struct drm_device *dev = client->dev; struct drm_device *dev = client->dev;
struct drm_client_buffer *buffer; struct drm_client_buffer *buffer;
struct drm_gem_object *obj; struct drm_gem_object *obj;
void *vaddr;
int ret; int ret;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
...@@ -281,12 +280,6 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u ...@@ -281,12 +280,6 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
buffer->gem = obj; buffer->gem = obj;
vaddr = drm_client_buffer_vmap(buffer);
if (IS_ERR(vaddr)) {
ret = PTR_ERR(vaddr);
goto err_delete;
}
return buffer; return buffer;
err_delete: err_delete:
...@@ -305,7 +298,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u ...@@ -305,7 +298,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
* Client buffer mappings are not ref'counted. Each call to * Client buffer mappings are not ref'counted. Each call to
* drm_client_buffer_vmap() should be followed by a call to * drm_client_buffer_vmap() should be followed by a call to
* drm_client_buffer_vunmap(); or the client buffer should be mapped * drm_client_buffer_vunmap(); or the client buffer should be mapped
* throughout its lifetime. The latter is the default. * throughout its lifetime.
* *
* Returns: * Returns:
* The mapped memory's address * The mapped memory's address
...@@ -339,10 +332,9 @@ EXPORT_SYMBOL(drm_client_buffer_vmap); ...@@ -339,10 +332,9 @@ EXPORT_SYMBOL(drm_client_buffer_vmap);
* drm_client_buffer_vunmap - Unmap DRM client buffer * drm_client_buffer_vunmap - Unmap DRM client buffer
* @buffer: DRM client buffer * @buffer: DRM client buffer
* *
* This function removes a client buffer's memory mapping. This * This function removes a client buffer's memory mapping. Calling this
* function is only required by clients that manage their buffers * function is only required by clients that manage their buffer mappings
* by themselves. By default, DRM client buffers are mapped throughout * by themselves.
* their entire lifetime.
*/ */
void drm_client_buffer_vunmap(struct drm_client_buffer *buffer) void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
{ {
......
...@@ -403,6 +403,7 @@ static void drm_fb_helper_dirty_work(struct work_struct *work) ...@@ -403,6 +403,7 @@ static void drm_fb_helper_dirty_work(struct work_struct *work)
struct drm_clip_rect *clip = &helper->dirty_clip; struct drm_clip_rect *clip = &helper->dirty_clip;
struct drm_clip_rect clip_copy; struct drm_clip_rect clip_copy;
unsigned long flags; unsigned long flags;
void *vaddr;
spin_lock_irqsave(&helper->dirty_lock, flags); spin_lock_irqsave(&helper->dirty_lock, flags);
clip_copy = *clip; clip_copy = *clip;
...@@ -412,10 +413,18 @@ static void drm_fb_helper_dirty_work(struct work_struct *work) ...@@ -412,10 +413,18 @@ static void drm_fb_helper_dirty_work(struct work_struct *work)
/* call dirty callback only when it has been really touched */ /* call dirty callback only when it has been really touched */
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
/* Generic fbdev uses a shadow buffer */ /* Generic fbdev uses a shadow buffer */
if (helper->buffer) if (helper->buffer) {
vaddr = drm_client_buffer_vmap(helper->buffer);
if (IS_ERR(vaddr))
return;
drm_fb_helper_dirty_blit_real(helper, &clip_copy); drm_fb_helper_dirty_blit_real(helper, &clip_copy);
}
helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
if (helper->buffer)
drm_client_buffer_vunmap(helper->buffer);
} }
} }
...@@ -2178,6 +2187,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -2178,6 +2187,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
struct drm_framebuffer *fb; struct drm_framebuffer *fb;
struct fb_info *fbi; struct fb_info *fbi;
u32 format; u32 format;
void *vaddr;
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height, sizes->surface_width, sizes->surface_height,
...@@ -2200,13 +2210,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -2200,13 +2210,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fbi->fbops = &drm_fbdev_fb_ops; fbi->fbops = &drm_fbdev_fb_ops;
fbi->screen_size = fb->height * fb->pitches[0]; fbi->screen_size = fb->height * fb->pitches[0];
fbi->fix.smem_len = fbi->screen_size; fbi->fix.smem_len = fbi->screen_size;
fbi->screen_buffer = buffer->vaddr;
/* Shamelessly leak the physical address to user-space */
#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0)
fbi->fix.smem_start =
page_to_phys(virt_to_page(fbi->screen_buffer));
#endif
drm_fb_helper_fill_info(fbi, fb_helper, sizes); drm_fb_helper_fill_info(fbi, fb_helper, sizes);
if (fb->funcs->dirty) { if (fb->funcs->dirty) {
...@@ -2231,6 +2235,19 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, ...@@ -2231,6 +2235,19 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fbi->fbdefio = &drm_fbdev_defio; fbi->fbdefio = &drm_fbdev_defio;
fb_deferred_io_init(fbi); fb_deferred_io_init(fbi);
} else {
/* buffer is mapped for HW framebuffer */
vaddr = drm_client_buffer_vmap(fb_helper->buffer);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
fbi->screen_buffer = vaddr;
/* Shamelessly leak the physical address to user-space */
#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0)
fbi->fix.smem_start =
page_to_phys(virt_to_page(fbi->screen_buffer));
#endif
} }
return 0; return 0;
......
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