[PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers

Thomas Zimmermann tzimmermann at suse.de
Thu Mar 3 20:58:36 UTC 2022


Provide struct drm_driver.dumb_create_fbdev, a callback hook for
fbdev dumb buffers. Wire up fbdev and client helpers to use the new
interface, if present.

This acknowledges the fact that fbdev buffers are different. The most
significant difference to regular GEM BOs is in mmap semantics. Fbdev
userspace treats the pages as video memory, which makes it impossible
to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
the BO permanently or install an intermediate shadow buffer for mmap.

So far, fbdev memory came from dumb buffers and DRM drivers had no
means of detecting this without reimplementing a good part of the fbdev
code. With the new callback, drivers can perma-pin fbdev buffer objects
if needed.

Several drivers also require damage handling, which fbdev implements
with its deferred I/O helpers. The new callback allows a driver's memory
manager to set up a suitable mmap.

Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de>
---
 drivers/gpu/drm/drm_client.c        | 14 +++++++----
 drivers/gpu/drm/drm_crtc_internal.h |  3 +++
 drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
 drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
 include/drm/drm_client.h            |  3 ++-
 include/drm/drm_drv.h               | 17 ++++++++++++++
 6 files changed, 84 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index af3b7395bf69..c964064651d5 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
 }
 
 static struct drm_client_buffer *
-drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			 bool fbdev)
 {
 	const struct drm_format_info *info = drm_format_info(format);
 	struct drm_mode_create_dumb dumb_args = { };
@@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
 	dumb_args.width = width;
 	dumb_args.height = height;
 	dumb_args.bpp = info->cpp[0] * 8;
-	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
+	if (fbdev)
+		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
+	else
+		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
 	if (ret)
 		goto err_delete;
 
@@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * @width: Framebuffer width
  * @height: Framebuffer height
  * @format: Buffer format
+ * @fbdev: True if the client provides an fbdev device, or false otherwise
  *
  * This function creates a &drm_client_buffer which consists of a
  * &drm_framebuffer backed by a dumb buffer.
@@ -411,12 +416,13 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * Pointer to a client buffer or an error pointer on failure.
  */
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev)
 {
 	struct drm_client_buffer *buffer;
 	int ret;
 
-	buffer = drm_client_buffer_create(client, width, height, format);
+	buffer = drm_client_buffer_create(client, width, height, format, fbdev);
 	if (IS_ERR(buffer))
 		return buffer;
 
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 63279e984342..34e532953b5a 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -101,6 +101,9 @@ int drm_mode_getresources(struct drm_device *dev,
 int drm_mode_create_dumb(struct drm_device *dev,
 			 struct drm_mode_create_dumb *args,
 			 struct drm_file *file_priv);
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv);
 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
 			  struct drm_file *file_priv);
 
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index ad17fa21cebb..a605b4c4e4a3 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -57,14 +57,10 @@
  * a hardware-specific ioctl to allocate suitable buffer objects.
  */
 
-int drm_mode_create_dumb(struct drm_device *dev,
-			 struct drm_mode_create_dumb *args,
-			 struct drm_file *file_priv)
+static int drm_mode_sanitize_args(struct drm_mode_create_dumb *args)
 {
 	u32 cpp, stride, size;
 
-	if (!dev->driver->dumb_create)
-		return -ENOSYS;
 	if (!args->width || !args->height || !args->bpp)
 		return -EINVAL;
 
@@ -93,6 +89,21 @@ int drm_mode_create_dumb(struct drm_device *dev,
 	args->pitch = 0;
 	args->size = 0;
 
+	return 0;
+}
+
+int drm_mode_create_dumb(struct drm_device *dev,
+			 struct drm_mode_create_dumb *args,
+			 struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
 	return dev->driver->dumb_create(file_priv, dev, args);
 }
 
@@ -102,6 +113,21 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
 	return drm_mode_create_dumb(dev, data, file_priv);
 }
 
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create_fbdev)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
+	return dev->driver->dumb_create_fbdev(file_priv, dev, args);
+}
+
 /**
  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 660ec5038c4e..9d1140a789f4 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -655,6 +655,15 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_framebuffer *fb = fb_helper->fb;
 
+	/*
+	 * If the driver provides an fbdev create function, it is expected
+	 * to provide its own implementation of deferred I/O. Shadow
+	 * buffering can still be enabled with the prefer_shadow_fbdev
+	 * flag.
+	 */
+	if (dev->driver->dumb_create_fbdev)
+		return dev->mode_config.prefer_shadow_fbdev;
+
 	return dev->mode_config.prefer_shadow_fbdev ||
 	       dev->mode_config.prefer_shadow ||
 	       fb->funcs->dirty;
@@ -2130,6 +2139,7 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
 static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
 	struct drm_fb_helper *fb_helper = info->par;
+	struct drm_device *dev = fb_helper->dev;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
 		/*
@@ -2137,14 +2147,20 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 		 * deferred I/O.
 		 */
 		return fb_deferred_io_mmap(info, vma);
-	else if (fb_helper->dev->driver->gem_prime_mmap)
+	else if (!dev->driver->dumb_create_fbdev && drm_fbdev_use_deferred_io(fb_helper))
+		/*
+		 * Drivers without fbdev dumb-create helper rely on fbdev's
+		 * deferred I/O.
+		 */
+		return fb_deferred_io_mmap(info, vma);
+	else if (dev->driver->gem_prime_mmap)
 		/*
 		 * Either directly mmap'ed or with deferred I/O; drivers
 		 * without shadow buffer handle mmap themselves.
 		 */
-		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
-	else
-		return -ENODEV;
+		return dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
+
+	return -ENODEV;
 }
 
 static bool drm_fbdev_use_iomem(struct fb_info *info)
@@ -2395,7 +2411,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 
 	format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
 	buffer = drm_client_framebuffer_create(client, sizes->surface_width,
-					       sizes->surface_height, format);
+					       sizes->surface_height, format, true);
 	if (IS_ERR(buffer))
 		return PTR_ERR(buffer);
 
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 4fc8018eddda..8d80d5594bea 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -153,7 +153,8 @@ struct drm_client_buffer {
 };
 
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev);
 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
 int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect);
 int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index f6159acb8856..da4a095de1e7 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -379,6 +379,23 @@ struct drm_driver {
 	int (*dumb_create)(struct drm_file *file_priv,
 			   struct drm_device *dev,
 			   struct drm_mode_create_dumb *args);
+	/**
+	 * @dumb_create_fbdev:
+	 *
+	 * This creates a new dumb buffer for use with DRM fbdev clients and
+	 * returns the resulting buffer handle. This handle can then be wrapped
+	 * up into a framebuffer modeset object.
+	 *
+	 * This is an interface for kernel clients and never called via ioctl.
+	 *
+	 * See @dumb_create for more information.
+	 *
+	 * Returns:
+	 * Zero on success, negative errno on failure.
+	 */
+	int (*dumb_create_fbdev)(struct drm_file *file_priv,
+				 struct drm_device *dev,
+				 struct drm_mode_create_dumb *args);
 	/**
 	 * @dumb_map_offset:
 	 *
-- 
2.35.1



More information about the dri-devel mailing list