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@xxxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel