[PATCH 3/8] drm/fb-helper: Add simple init/fini functions

Noralf Trønnes noralf at tronnes.org
Wed Oct 4 18:06:30 UTC 2017


This adds some simple init/fini helpers for drivers that don't need
anything special in their fbdev emulation setup.

Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
 drivers/gpu/drm/drm_fb_helper.c | 163 ++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_fb_helper.h     |  22 ++++++
 2 files changed, 185 insertions(+)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 98aa6b5..ca8a961 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -105,6 +105,127 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
  * mmap page writes.
  */
 
+/**
+ * drm_fb_helper_simple_init - Simple fbdev emulation setup
+ * @dev: DRM device
+ * @funcs: Pointer to structure of functions associated with this helper
+ * @bpp: Bits per pixel value to use for the framebuffer configuration.
+ *       @dev->mode_config.preferred_depth is used if this is zero.
+ * @max_conn: Max connector count. @dev->mode_config.num_connector is used if
+ *            this is zero.
+ *
+ * Simple fbdev emulation initialization. This function allocates a
+ * &drm_fb_helper structure attaches it to &drm_device->fbdev and calls:
+ * drm_fb_helper_prepare(), drm_fb_helper_init(),
+ * drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config().
+ *
+ * If fbdev emulation is disabled, this function does nothing. In that case
+ * &drm_device->fbdev will be NULL. All drm_fb_helper entry function can handle
+ * a NULL argument.
+ *
+ * fbdev deferred I/O users should use the drm_fb_helper_defio_init() function
+ * in their &drm_fb_helper_funcs.fb_probe callback.
+ *
+ * Returns:
+ * Zero on success or if fbdev emulation is disabled, negative error code on
+ * failure.
+ */
+int drm_fb_helper_simple_init(struct drm_device *dev,
+			      const struct drm_fb_helper_funcs *funcs,
+			      unsigned int bpp, int max_conn)
+{
+	struct drm_fb_helper *fb_helper;
+	int ret;
+
+	if (!drm_fbdev_emulation)
+		return 0;
+
+	if (!bpp)
+		bpp = dev->mode_config.preferred_depth;
+
+	if (!max_conn)
+		max_conn = dev->mode_config.num_connector;
+
+	fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
+	if (!fb_helper)
+		return -ENOMEM;
+
+	drm_fb_helper_prepare(dev, fb_helper, funcs);
+
+	ret = drm_fb_helper_init(dev, fb_helper, max_conn);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev->dev, "Failed to initialize fb helper.\n");
+		goto err_drm_fb_helper_free;
+	}
+
+	ret = drm_fb_helper_single_add_all_connectors(fb_helper);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev->dev, "Failed to add connectors.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	ret = drm_fb_helper_initial_config(fb_helper, bpp);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev->dev, "Failed to set initial hw config.\n");
+		goto err_drm_fb_helper_fini;
+	}
+
+	dev->fbdev = fb_helper;
+
+	return 0;
+
+err_drm_fb_helper_fini:
+	drm_fb_helper_fini(fb_helper);
+err_drm_fb_helper_free:
+	kfree(fb_helper);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(drm_fb_helper_simple_init);
+
+/**
+ * drm_fb_helper_simple_fini - Simple fbdev cleanup
+ * @dev: DRM device
+ *
+ * Simple fbdev emulation cleanup. This function unregisters fbdev if it is not
+ * done, cleans up deferred IO if necessary, removes framebuffer, finalizes
+ * @fb_helper and frees the structure.
+ */
+void drm_fb_helper_simple_fini(struct drm_device *dev)
+{
+	struct drm_fb_helper *fb_helper = dev->fbdev;
+	struct fb_info *info;
+	struct fb_ops *fbops;
+
+	if (!fb_helper)
+		return;
+
+	dev->fbdev = NULL;
+	info = fb_helper->fbdev;
+
+	/* Make sure it hasn't been unregistered already */
+	if (info && info->dev)
+		drm_fb_helper_unregister_fbi(fb_helper);
+
+	if (info && info->fbdefio) {
+		fb_deferred_io_cleanup(info);
+		kfree(info->fbdefio);
+		fbops = info->fbops;
+	}
+
+	drm_fb_helper_fini(fb_helper);
+
+	if (info && info->fbdefio)
+		kfree(fbops);
+
+	if (fb_helper->fb)
+		drm_framebuffer_remove(fb_helper->fb);
+
+	kfree(fb_helper);
+}
+EXPORT_SYMBOL_GPL(drm_fb_helper_simple_fini);
+
 #define drm_fb_helper_for_each_connector(fbh, i__) \
 	for (({ lockdep_assert_held(&(fbh)->lock); }), \
 	     i__ = 0; i__ < (fbh)->connector_count; i__++)
@@ -954,6 +1075,48 @@ void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
 
+/**
+ * drm_fb_helper_defio_init - fbdev deferred I/O initialization
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * This function allocates &fb_deferred_io, sets callback to
+ * drm_fb_helper_deferred_io(), delay to 50ms and calls fb_deferred_io_init().
+ *
+ * NOTE: A copy of &fb_ops is made and assigned to &info->fbops. This is done
+ * because fb_deferred_io_cleanup() clears &fbops->fb_mmap and would thereby
+ * affect other instances of that &fb_ops. This copy is freed in
+ * drm_fb_helper_simple_fini().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
+{
+	struct fb_info *info = fb_helper->fbdev;
+	struct fb_deferred_io *fbdefio;
+	struct fb_ops *fbops;
+
+	fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL);
+	fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
+	if (!fbdefio || !fbops) {
+		kfree(fbdefio);
+		kfree(fbops);
+		return -ENOMEM;
+	}
+
+	info->fbdefio = fbdefio;
+	fbdefio->delay = msecs_to_jiffies(50);
+	fbdefio->deferred_io = drm_fb_helper_deferred_io;
+
+	*fbops = *info->fbops;
+	info->fbops = fbops;
+
+	fb_deferred_io_init(info);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_defio_init);
+
 static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y,
 				u32 width, u32 height)
 {
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 33fe959..6503f4c 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -242,6 +242,10 @@ struct drm_fb_helper {
 	.fb_ioctl	= drm_fb_helper_ioctl
 
 #ifdef CONFIG_DRM_FBDEV_EMULATION
+int drm_fb_helper_simple_init(struct drm_device *dev,
+			      const struct drm_fb_helper_funcs *funcs,
+			      unsigned int bpp, int max_conn);
+void drm_fb_helper_simple_fini(struct drm_device *dev);
 void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
 			   const struct drm_fb_helper_funcs *funcs);
 int drm_fb_helper_init(struct drm_device *dev,
@@ -265,6 +269,7 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 
 void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper);
 
+int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper);
 void drm_fb_helper_deferred_io(struct fb_info *info,
 			       struct list_head *pagelist);
 
@@ -311,6 +316,18 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
 int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
 				       struct drm_connector *connector);
 #else
+static inline int
+drm_fb_helper_simple_init(struct drm_device *dev,
+			  const struct drm_fb_helper_funcs *funcs,
+			  unsigned int bpp, int max_conn)
+{
+	return 0;
+}
+
+static inline void drm_fb_helper_simple_fini(struct drm_device *dev)
+{
+}
+
 static inline void drm_fb_helper_prepare(struct drm_device *dev,
 					struct drm_fb_helper *helper,
 					const struct drm_fb_helper_funcs *funcs)
@@ -393,6 +410,11 @@ static inline void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
 {
 }
 
+static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
+{
+	return -ENODEV;
+}
+
 static inline void drm_fb_helper_deferred_io(struct fb_info *info,
 					     struct list_head *pagelist)
 {
-- 
2.7.4



More information about the dri-devel mailing list