This patchset adds a library for vmalloc buffer objects and makes use of it in tinydrm.
The reason I want to move away from the cma helper, is that it restricts which drivers to PRIME import from, since cma requires the buffer to be physically continuous. Initially I looked at udl and decided to use shmem, but have later decided against it since shmem doesn't work with fbdev deferred I/O, they both use page->lru and page->mapping. This meant adding a shadow buffer for fbdev with increased complexity, so I fell back to the KISS principle.
I'm trying to make tinydrm support drivers like simpledrm and udl, so if using vmalloc blocks those use cases, please let me know.
I will do a follow up for the cma library which removes struct drm_fbdev_cma and wrappers.
Noralf.
Changes since version 1: - tinydrm: Remove dma_coerce_mask_and_coherent() calls (Matt Gattis)
Noralf Trønnes (8): drm/fb-helper: Handle function NULL argument drm: Add drm_device->fbdev pointer drm/fb-helper: Add simple init/fini functions drm/fb-helper: Add .last_close and .output_poll_changed helpers drm/gem-fb-helper: Add drm_gem_fb_debugfs_show() drm: Add vmalloc BO helper drm/tinydrm: Use drm_vmalloc_bo drm/tinydrm: Relax buffer line prefetch
Documentation/gpu/drm-kms-helpers.rst | 12 + drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fb_helper.c | 223 +++++++++++++++++- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++ drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 +++++++++++++++++++++++++ drivers/gpu/drm/tinydrm/Kconfig | 2 +- drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 134 +++-------- drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 56 +++-- drivers/gpu/drm/tinydrm/mi0283qt.c | 8 +- drivers/gpu/drm/tinydrm/mipi-dbi.c | 31 +-- drivers/gpu/drm/tinydrm/repaper.c | 21 +- drivers/gpu/drm/tinydrm/st7586.c | 14 +- include/drm/drm_device.h | 8 + include/drm/drm_fb_helper.h | 32 +++ include/drm/drm_gem_framebuffer_helper.h | 6 + include/drm/drm_vmalloc_bo_helper.h | 88 +++++++ include/drm/tinydrm/mipi-dbi.h | 2 + include/drm/tinydrm/tinydrm.h | 38 +-- 19 files changed, 811 insertions(+), 222 deletions(-) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
Make functions tolerate that the drm_fb_helper argument is NULL. This is useful for drivers that continue probing when fbdev emulation fails and not having to do this check themselves. Update docs for functions that already handles this.
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 116d1f1337c7..954cdd48de92 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -150,6 +150,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, { int err;
+ if (!fb_helper) + return 0; + mutex_lock(&fb_helper->lock); err = __drm_fb_helper_add_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock); @@ -161,7 +164,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector); /** * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init + * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL * * This functions adds all the available connectors for use with the given * fb_helper. This is a separate step to allow drivers to freely assign @@ -179,7 +182,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) struct drm_connector_list_iter conn_iter; int i, ret = 0;
- if (!drm_fbdev_emulation) + if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock); @@ -245,6 +248,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, { int err;
+ if (!fb_helper) + return 0; + mutex_lock(&fb_helper->lock); err = __drm_fb_helper_remove_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock); @@ -484,7 +490,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
/** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration - * @fb_helper: fbcon to restore + * @fb_helper: driver-allocated fbdev helper, can be NULL * * This should be called from driver's drm &drm_driver.lastclose callback * when implementing an fbcon on top of kms using this helper. This ensures that @@ -498,7 +504,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) bool do_delayed; int ret;
- if (!drm_fbdev_emulation) + if (!drm_fbdev_emulation || !fb_helper) return -ENODEV;
if (READ_ONCE(fb_helper->deferred_setup)) @@ -883,7 +889,7 @@ EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
/** * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device - * @fb_helper: driver-allocated fbdev helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * * A wrapper around unregister_framebuffer, to release the fb_info * framebuffer device. This must be called before releasing all resources for @@ -898,7 +904,7 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
/** * drm_fb_helper_fini - finialize a &struct drm_fb_helper - * @fb_helper: driver-allocated fbdev helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * * This cleans up all remaining resources associated with @fb_helper. Must be * called after drm_fb_helper_unlink_fbi() was called. @@ -937,7 +943,7 @@ EXPORT_SYMBOL(drm_fb_helper_fini);
/** * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer - * @fb_helper: driver-allocated fbdev helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * * A wrapper around unlink_framebuffer implemented by fbdev core */ @@ -1138,7 +1144,7 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
/** * drm_fb_helper_set_suspend - wrapper around fb_set_suspend - * @fb_helper: driver-allocated fbdev helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * @suspend: whether to suspend or resume * * A wrapper around fb_set_suspend implemented by fbdev core. @@ -1155,7 +1161,7 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend); /** * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also * takes the console lock - * @fb_helper: driver-allocated fbdev helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * @suspend: whether to suspend or resume * * A wrapper around fb_set_suspend() that takes the console lock. If the lock @@ -2568,7 +2574,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); /** * drm_fb_helper_hotplug_event - respond to a hotplug notification by * probing all the outputs attached to the fb - * @fb_helper: the drm_fb_helper + * @fb_helper: driver-allocated fbdev helper, can be NULL * * Scan the connectors attached to the fb_helper and try to put together a * setup after notification of a change in output configuration. @@ -2590,7 +2596,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { int err = 0;
- if (!drm_fbdev_emulation) + if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock);
On Sun, Oct 15, 2017 at 06:30:35PM +0200, Noralf Trønnes wrote:
Make functions tolerate that the drm_fb_helper argument is NULL. This is useful for drivers that continue probing when fbdev emulation fails and not having to do this check themselves. Update docs for functions that already handles this.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Is there callsites that we can now simplify? Should we add a todo.rst entry if there's too many?
Patch itself looks good.
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/drm_fb_helper.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 116d1f1337c7..954cdd48de92 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -150,6 +150,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, { int err;
- if (!fb_helper)
return 0;
- mutex_lock(&fb_helper->lock); err = __drm_fb_helper_add_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock);
@@ -161,7 +164,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector); /**
- drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
emulation helper
- @fb_helper: fbdev initialized with drm_fb_helper_init
- @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL
- This functions adds all the available connectors for use with the given
- fb_helper. This is a separate step to allow drivers to freely assign
@@ -179,7 +182,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) struct drm_connector_list_iter conn_iter; int i, ret = 0;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock);
@@ -245,6 +248,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, { int err;
- if (!fb_helper)
return 0;
- mutex_lock(&fb_helper->lock); err = __drm_fb_helper_remove_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock);
@@ -484,7 +490,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
/**
- drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
- @fb_helper: fbcon to restore
- @fb_helper: driver-allocated fbdev helper, can be NULL
- This should be called from driver's drm &drm_driver.lastclose callback
- when implementing an fbcon on top of kms using this helper. This ensures that
@@ -498,7 +504,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) bool do_delayed; int ret;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return -ENODEV;
if (READ_ONCE(fb_helper->deferred_setup))
@@ -883,7 +889,7 @@ EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
/**
- drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- A wrapper around unregister_framebuffer, to release the fb_info
- framebuffer device. This must be called before releasing all resources for
@@ -898,7 +904,7 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
/**
- drm_fb_helper_fini - finialize a &struct drm_fb_helper
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- This cleans up all remaining resources associated with @fb_helper. Must be
- called after drm_fb_helper_unlink_fbi() was called.
@@ -937,7 +943,7 @@ EXPORT_SYMBOL(drm_fb_helper_fini);
/**
- drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
- @fb_helper: driver-allocated fbdev helper
*/
- @fb_helper: driver-allocated fbdev helper, can be NULL
- A wrapper around unlink_framebuffer implemented by fbdev core
@@ -1138,7 +1144,7 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
/**
- drm_fb_helper_set_suspend - wrapper around fb_set_suspend
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- @suspend: whether to suspend or resume
- A wrapper around fb_set_suspend implemented by fbdev core.
@@ -1155,7 +1161,7 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend); /**
- drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
takes the console lock
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- @suspend: whether to suspend or resume
- A wrapper around fb_set_suspend() that takes the console lock. If the lock
@@ -2568,7 +2574,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); /**
- drm_fb_helper_hotplug_event - respond to a hotplug notification by
probing all the outputs attached to the fb
- @fb_helper: the drm_fb_helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- Scan the connectors attached to the fb_helper and try to put together a
- setup after notification of a change in output configuration.
@@ -2590,7 +2596,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { int err = 0;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock);
-- 2.14.2
Den 17.10.2017 14.38, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:35PM +0200, Noralf Trønnes wrote:
Make functions tolerate that the drm_fb_helper argument is NULL. This is useful for drivers that continue probing when fbdev emulation fails and not having to do this check themselves. Update docs for functions that already handles this.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Is there callsites that we can now simplify? Should we add a todo.rst entry if there's too many?
A quick scan didn't reveal any except for some .lastclose implementations that will be taken care of by the new lastclose helper.
Noralf.
Patch itself looks good.
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/drm_fb_helper.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 116d1f1337c7..954cdd48de92 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -150,6 +150,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, { int err;
- if (!fb_helper)
return 0;
- mutex_lock(&fb_helper->lock); err = __drm_fb_helper_add_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock);
@@ -161,7 +164,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector); /**
- drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
emulation helper
- @fb_helper: fbdev initialized with drm_fb_helper_init
- @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL
- This functions adds all the available connectors for use with the given
- fb_helper. This is a separate step to allow drivers to freely assign
@@ -179,7 +182,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) struct drm_connector_list_iter conn_iter; int i, ret = 0;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock);
@@ -245,6 +248,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, { int err;
- if (!fb_helper)
return 0;
- mutex_lock(&fb_helper->lock); err = __drm_fb_helper_remove_one_connector(fb_helper, connector); mutex_unlock(&fb_helper->lock);
@@ -484,7 +490,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
/**
- drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
- @fb_helper: fbcon to restore
- @fb_helper: driver-allocated fbdev helper, can be NULL
- This should be called from driver's drm &drm_driver.lastclose callback
- when implementing an fbcon on top of kms using this helper. This ensures that
@@ -498,7 +504,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) bool do_delayed; int ret;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return -ENODEV;
if (READ_ONCE(fb_helper->deferred_setup))
@@ -883,7 +889,7 @@ EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
/**
- drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- A wrapper around unregister_framebuffer, to release the fb_info
- framebuffer device. This must be called before releasing all resources for
@@ -898,7 +904,7 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
/**
- drm_fb_helper_fini - finialize a &struct drm_fb_helper
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- This cleans up all remaining resources associated with @fb_helper. Must be
- called after drm_fb_helper_unlink_fbi() was called.
@@ -937,7 +943,7 @@ EXPORT_SYMBOL(drm_fb_helper_fini);
/**
- drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
- @fb_helper: driver-allocated fbdev helper
*/
- @fb_helper: driver-allocated fbdev helper, can be NULL
- A wrapper around unlink_framebuffer implemented by fbdev core
@@ -1138,7 +1144,7 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
/**
- drm_fb_helper_set_suspend - wrapper around fb_set_suspend
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- @suspend: whether to suspend or resume
- A wrapper around fb_set_suspend implemented by fbdev core.
@@ -1155,7 +1161,7 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend); /**
- drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
takes the console lock
- @fb_helper: driver-allocated fbdev helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- @suspend: whether to suspend or resume
- A wrapper around fb_set_suspend() that takes the console lock. If the lock
@@ -2568,7 +2574,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); /**
- drm_fb_helper_hotplug_event - respond to a hotplug notification by
probing all the outputs attached to the fb
- @fb_helper: the drm_fb_helper
- @fb_helper: driver-allocated fbdev helper, can be NULL
- Scan the connectors attached to the fb_helper and try to put together a
- setup after notification of a change in output configuration.
@@ -2590,7 +2596,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { int err = 0;
- if (!drm_fbdev_emulation)
if (!drm_fbdev_emulation || !fb_helper) return 0;
mutex_lock(&fb_helper->lock);
-- 2.14.2
drm_fb_helper is *the* way of doing fbdev emulation so add a pointer to struct drm_device. This makes it possible to add callback helpers for .last_close and .output_poll_changed further reducing fbdev emulation footprint in drivers.
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- include/drm/drm_device.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index e21af87a2f3c..3c104b15a0c8 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -17,6 +17,7 @@ struct drm_vblank_crtc; struct drm_sg_mem; struct drm_local_map; struct drm_vma_offset_manager; +struct drm_fb_helper;
struct inode;
@@ -185,6 +186,13 @@ struct drm_device { struct drm_vma_offset_manager *vma_offset_manager; /*@} */ int switch_power_state; + + /** + * @fbdev: + * + * Optional pointer to the fbdev emulation structure. + */ + struct drm_fb_helper *fbdev; };
#endif
On Sun, Oct 15, 2017 at 06:30:36PM +0200, Noralf Trønnes wrote:
drm_fb_helper is *the* way of doing fbdev emulation so add a pointer to struct drm_device. This makes it possible to add callback helpers for .last_close and .output_poll_changed further reducing fbdev emulation footprint in drivers.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
include/drm/drm_device.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index e21af87a2f3c..3c104b15a0c8 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -17,6 +17,7 @@ struct drm_vblank_crtc; struct drm_sg_mem; struct drm_local_map; struct drm_vma_offset_manager; +struct drm_fb_helper;
struct inode;
@@ -185,6 +186,13 @@ struct drm_device { struct drm_vma_offset_manager *vma_offset_manager; /*@} */ int switch_power_state;
- /**
* @fbdev:
*
* Optional pointer to the fbdev emulation structure.
Needs more helper in here, to make it even more obvious that it's optional and not part of the core drm stuff.
Maybe call the member itself fbdev_helper_private or something like that.
With that bit of bikeshed applied to make it more over-the-top:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
*/
- struct drm_fb_helper *fbdev;
};
#endif
2.14.2
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@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 954cdd48de92..166535da9a9b 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 33fe95927742..6503f4c3e3ef 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) {
On Sun, Oct 15, 2017 at 06:30:37PM +0200, Noralf Trønnes wrote:
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@tronnes.org
So I know the saying goes that any design problem in IT can be solved by adding another layer, but layers have costs. I still feel like this here just papers over some of the evolved design issues in our fbdev helper setup/teardown logic.
Do we _really_ need all of them, can't we fold at least some of this together?
E.g. the split between initial_config and single_add_all connectors seems a bit funny. Same with prepare and init. And maybe we should just push the default bpp handling down into the helpers too (no point reinventing that wheel everywhere)?
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 954cdd48de92..166535da9a9b 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;
This definitely needs to be in one of the above functions so that you don't force other drivers to either do this themselves or use this new helper to be able to benefit from the new convenience functions you've just added.
I'd set it in prepare, that's the first point where we can set it.
- 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)
Similar here on the cleanup side, can't we simplify this a lot for everyone?
+{
- 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);
Probably good as a separate patch + using it in a bunch of drivers to show that it's the right kind of helper that's useful in a few places.
Cheers, Daniel
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 33fe95927742..6503f4c3e3ef 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.14.2
ah, yeah have to agree it was pretty tough figuring that out at first, I was pretty green with kernel space. But considering i just wrote a working linux DRM kernel driver with minimal knowledge of kernels or even really how embedded devices share memory resources... good job is all I have to say.
anyway, what's one small layer of code abstraction when we're doing it all in the name of abstracting our code to non-existance at run-time? These DRM modules are all just directing each other where to find the resources, and since everyone knows what an image is made up of, it's a fairly small overhead to encapsulate across each barrier. I'd say split it up even more... for example there could be one module that directs the interface between SPI transport layers and graphic buffers/ux, and one that directs instructions/buffers/ux back and forth from userspace to the proper transport layer and proper rendering layer (which we seem to be missing... i'm sure it could be reused in any case where two devices on a separate bus need to communicate either directly to each other or to the rendering layers). I could have been pretty happy not even having to know what DMA was to code what was missing, which was just the spi transmission instructions.
Den 17.10.2017 14.48, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:37PM +0200, Noralf Trønnes wrote:
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@tronnes.org
So I know the saying goes that any design problem in IT can be solved by adding another layer, but layers have costs. I still feel like this here just papers over some of the evolved design issues in our fbdev helper setup/teardown logic.
Do we _really_ need all of them, can't we fold at least some of this together?
E.g. the split between initial_config and single_add_all connectors seems a bit funny. Same with prepare and init. And maybe we should just push the default bpp handling down into the helpers too (no point reinventing that wheel everywhere)?
Ok, I think I'll bail out of this one, at least for now. It will be too much work and I need to focus on moving tinydrm away from the cma helper.
For those interested, here's a compilation of drm_fb_helper init/fini use in all drivers: https://gist.github.com/notro/d7fc2809736cdddb68a644b589a86ee1
I think I'll split out a patchset off of this one and do that first:
1. Add drm_device->drm_fb_helper_private Set it in drm_fb_helper_init() Clear it in drm_fb_helper_fini()
2. drm/fb-helper: Add .last_close and .output_poll_changed helpers
n. Convert drivers to use those helpers
Noralf.
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 954cdd48de92..166535da9a9b 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;
This definitely needs to be in one of the above functions so that you don't force other drivers to either do this themselves or use this new helper to be able to benefit from the new convenience functions you've just added.
I'd set it in prepare, that's the first point where we can set it.
- 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)
Similar here on the cleanup side, can't we simplify this a lot for everyone?
+{
- 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);
Probably good as a separate patch + using it in a bunch of drivers to show that it's the right kind of helper that's useful in a few places.
Cheers, Daniel
- 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 33fe95927742..6503f4c3e3ef 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.14.2
This adds helpers for the drm_driver->last_close and the drm_mode_config_funcs->output_poll_changed callbacks.
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 32 ++++++++++++++++++++++++++++++++ include/drm/drm_fb_helper.h | 10 ++++++++++ 2 files changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 166535da9a9b..897be5304135 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -226,6 +226,38 @@ void drm_fb_helper_simple_fini(struct drm_device *dev) } EXPORT_SYMBOL_GPL(drm_fb_helper_simple_fini);
+/** + * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation + * @dev: DRM device + * + * This function can be used as the &drm_driver->lastclose callback for drivers + * that only need to call drm_fb_helper_restore_fbdev_mode_unlocked(). + * + * Note: &drm_device->fbdev needs to be set. + */ +void drm_fb_helper_lastclose(struct drm_device *dev) +{ + drm_fb_helper_restore_fbdev_mode_unlocked(dev->fbdev); +} +EXPORT_SYMBOL(drm_fb_helper_lastclose); + +/** + * drm_fb_helper_output_poll_changed - DRM mode config .output_poll_changed + * helper for fbdev emulation + * @dev: DRM device + * + * This function can be used as the + * &drm_mode_config_funcs.output_poll_changed callback for drivers that only + * need to call drm_fb_helper_hotplug_event(). + * + * Note: &drm_device->fbdev needs to be set. + */ +void drm_fb_helper_output_poll_changed(struct drm_device *dev) +{ + drm_fb_helper_hotplug_event(dev->fbdev); +} +EXPORT_SYMBOL(drm_fb_helper_output_poll_changed); + #define drm_fb_helper_for_each_connector(fbh, i__) \ for (({ lockdep_assert_held(&(fbh)->lock); }), \ i__ = 0; i__ < (fbh)->connector_count; i__++) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6503f4c3e3ef..2558425e29e5 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -246,6 +246,8 @@ 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_lastclose(struct drm_device *dev); +void drm_fb_helper_output_poll_changed(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, @@ -328,6 +330,14 @@ static inline void drm_fb_helper_simple_fini(struct drm_device *dev) { }
+static inline void drm_fb_helper_lastclose(struct drm_device *dev) +{ +} + +static inline void drm_fb_helper_output_poll_changed(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)
On Sun, Oct 15, 2017 at 06:30:38PM +0200, Noralf Trønnes wrote:
This adds helpers for the drm_driver->last_close and the drm_mode_config_funcs->output_poll_changed callbacks.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Definitely want a todo entry to roll these out to all drivers, but maybe do a separate follow-up patch to add them all in one go ...
On this:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/drm_fb_helper.c | 32 ++++++++++++++++++++++++++++++++ include/drm/drm_fb_helper.h | 10 ++++++++++ 2 files changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 166535da9a9b..897be5304135 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -226,6 +226,38 @@ void drm_fb_helper_simple_fini(struct drm_device *dev) } EXPORT_SYMBOL_GPL(drm_fb_helper_simple_fini);
+/**
- drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation
- @dev: DRM device
- This function can be used as the &drm_driver->lastclose callback for drivers
- that only need to call drm_fb_helper_restore_fbdev_mode_unlocked().
- Note: &drm_device->fbdev needs to be set.
- */
+void drm_fb_helper_lastclose(struct drm_device *dev) +{
- drm_fb_helper_restore_fbdev_mode_unlocked(dev->fbdev);
+} +EXPORT_SYMBOL(drm_fb_helper_lastclose);
+/**
- drm_fb_helper_output_poll_changed - DRM mode config .output_poll_changed
helper for fbdev emulation
- @dev: DRM device
- This function can be used as the
- &drm_mode_config_funcs.output_poll_changed callback for drivers that only
- need to call drm_fb_helper_hotplug_event().
- Note: &drm_device->fbdev needs to be set.
- */
+void drm_fb_helper_output_poll_changed(struct drm_device *dev) +{
- drm_fb_helper_hotplug_event(dev->fbdev);
+} +EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
#define drm_fb_helper_for_each_connector(fbh, i__) \ for (({ lockdep_assert_held(&(fbh)->lock); }), \ i__ = 0; i__ < (fbh)->connector_count; i__++) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6503f4c3e3ef..2558425e29e5 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -246,6 +246,8 @@ 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_lastclose(struct drm_device *dev); +void drm_fb_helper_output_poll_changed(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, @@ -328,6 +330,14 @@ static inline void drm_fb_helper_simple_fini(struct drm_device *dev) { }
+static inline void drm_fb_helper_lastclose(struct drm_device *dev) +{ +}
+static inline void drm_fb_helper_output_poll_changed(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) -- 2.14.2
Add drm_gem_fb_debugfs_show() function to provide a debugfs representation of the framebuffer and GEM object(s).
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++++++++++++++++++++++++++ include/drm/drm_gem_framebuffer_helper.h | 6 ++++ 2 files changed, 51 insertions(+)
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index aa8cb9bfa499..18fdcc29427a 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -265,6 +265,51 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, } EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
+#ifdef CONFIG_DEBUG_FS +static void drm_gem_fb_describe(struct drm_framebuffer *fb, struct seq_file *m) +{ + struct drm_gem_object *obj; + unsigned int i; + + seq_printf(m, "[FB:%d] %dx%d@%4.4s\n", fb->base.id, fb->width, + fb->height, (char *)&fb->format->format); + + for (i = 0; i < fb->format->num_planes; i++) { + obj = fb->obj[i]; + seq_printf(m, "\t%u: offset=%d pitch=%d, GEM: name=%d", + i, fb->offsets[i], fb->pitches[i], obj->name); + seq_printf(m, " refcount=%d start=%08lx size=%zu%s\n", + kref_read(&obj->refcount), + drm_vma_node_start(&obj->vma_node), obj->size, + obj->import_attach ? " (imported)" : ""); + } +} + +/** + * drm_gem_fb_debugfs_show() - Helper to list GEM backed framebuffer objects + * in debugfs. + * @m: Output file + * @arg: Private data for the callback + * + * This function gives a debugfs representation of the framebuffers with their + * backing GEM objects. See drm_debugfs_create_files() for more info. + */ +int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.fb_lock); + drm_for_each_fb(fb, dev) + drm_gem_fb_describe(fb, m); + mutex_unlock(&dev->mode_config.fb_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(drm_gem_fb_debugfs_show); +#endif + /** * drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev * emulation diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index 5ca7cdc3f527..1bb73d961aba 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -28,6 +28,12 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state);
+#ifdef CONFIG_DEBUG_FS +struct seq_file; + +int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg); +#endif + struct drm_framebuffer * drm_gem_fbdev_fb_create(struct drm_device *dev, struct drm_fb_helper_surface_size *sizes,
On Sun, Oct 15, 2017 at 06:30:39PM +0200, Noralf Trønnes wrote:
Add drm_gem_fb_debugfs_show() function to provide a debugfs representation of the framebuffer and GEM object(s).
Signed-off-by: Noralf Trønnes noralf@tronnes.org
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++++++++++++++++++++++++++ include/drm/drm_gem_framebuffer_helper.h | 6 ++++ 2 files changed, 51 insertions(+)
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index aa8cb9bfa499..18fdcc29427a 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -265,6 +265,51 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, } EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
+#ifdef CONFIG_DEBUG_FS +static void drm_gem_fb_describe(struct drm_framebuffer *fb, struct seq_file *m) +{
- struct drm_gem_object *obj;
- unsigned int i;
- seq_printf(m, "[FB:%d] %dx%d@%4.4s\n", fb->base.id, fb->width,
fb->height, (char *)&fb->format->format);
- for (i = 0; i < fb->format->num_planes; i++) {
obj = fb->obj[i];
seq_printf(m, "\t%u: offset=%d pitch=%d, GEM: name=%d",
i, fb->offsets[i], fb->pitches[i], obj->name);
seq_printf(m, " refcount=%d start=%08lx size=%zu%s\n",
kref_read(&obj->refcount),
drm_vma_node_start(&obj->vma_node), obj->size,
obj->import_attach ? " (imported)" : "");
- }
+}
Given that gem is the defacto standard and that we can easily skip the gem specific stuff on non-gem drivers by checking the pointer for NULL I think we should add the framebuffer list by default. Like we already do for e.g. atomic state, see drm_atomic_debugfs_init. Probably better to split it up into a generic fb describe, which calls into a gem_obj describe, with maybe in the future the option to also call into driver callbacks to dump more driver state.
There's probably more stuff that we should list by default in debugfs, like blob properties and similar things. -Daniel
+/**
- drm_gem_fb_debugfs_show() - Helper to list GEM backed framebuffer objects
in debugfs.
- @m: Output file
- @arg: Private data for the callback
- This function gives a debugfs representation of the framebuffers with their
- backing GEM objects. See drm_debugfs_create_files() for more info.
- */
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg) +{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_framebuffer *fb;
- mutex_lock(&dev->mode_config.fb_lock);
- drm_for_each_fb(fb, dev)
drm_gem_fb_describe(fb, m);
- mutex_unlock(&dev->mode_config.fb_lock);
- return 0;
+} +EXPORT_SYMBOL_GPL(drm_gem_fb_debugfs_show); +#endif
/**
- drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
emulation
diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index 5ca7cdc3f527..1bb73d961aba 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -28,6 +28,12 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state);
+#ifdef CONFIG_DEBUG_FS +struct seq_file;
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg); +#endif
struct drm_framebuffer * drm_gem_fbdev_fb_create(struct drm_device *dev, struct drm_fb_helper_surface_size *sizes, -- 2.14.2
Den 17.10.2017 14.56, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:39PM +0200, Noralf Trønnes wrote:
Add drm_gem_fb_debugfs_show() function to provide a debugfs representation of the framebuffer and GEM object(s).
Signed-off-by: Noralf Trønnes noralf@tronnes.org
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++++++++++++++++++++++++++ include/drm/drm_gem_framebuffer_helper.h | 6 ++++ 2 files changed, 51 insertions(+)
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index aa8cb9bfa499..18fdcc29427a 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -265,6 +265,51 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, } EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
+#ifdef CONFIG_DEBUG_FS +static void drm_gem_fb_describe(struct drm_framebuffer *fb, struct seq_file *m) +{
- struct drm_gem_object *obj;
- unsigned int i;
- seq_printf(m, "[FB:%d] %dx%d@%4.4s\n", fb->base.id, fb->width,
fb->height, (char *)&fb->format->format);
- for (i = 0; i < fb->format->num_planes; i++) {
obj = fb->obj[i];
seq_printf(m, "\t%u: offset=%d pitch=%d, GEM: name=%d",
i, fb->offsets[i], fb->pitches[i], obj->name);
seq_printf(m, " refcount=%d start=%08lx size=%zu%s\n",
kref_read(&obj->refcount),
drm_vma_node_start(&obj->vma_node), obj->size,
obj->import_attach ? " (imported)" : "");
- }
+}
Given that gem is the defacto standard and that we can easily skip the gem specific stuff on non-gem drivers by checking the pointer for NULL I think we should add the framebuffer list by default. Like we already do for e.g. atomic state, see drm_atomic_debugfs_init. Probably better to split it up into a generic fb describe, which calls into a gem_obj describe, with maybe in the future the option to also call into driver callbacks to dump more driver state.
How about this:
drivers/gpu/drm/drm_gem.c:
#ifdef CONFIG_DEBUG_FS void drm_gem_print(const struct drm_gem_object *obj, struct drm_printer *p) { drm_printf(p, "name=%d refcount=%d start=%08lx size=%zu%s\n", obj->name, kref_read(&obj->refcount), drm_vma_node_start(&obj->vma_node), obj->size, obj->import_attach ? " (imported)" : ""); } #endif
drivers/gpu/drm/drm_framebuffer.c:
#ifdef CONFIG_DEBUG_FS static void drm_framebuffer_print(const struct drm_framebuffer *fb, struct drm_printer *p) { unsigned int i;
drm_printf(p, "[FB:%d] %dx%d@%4.4s refcount=%d\n", fb->base.id, fb->width, fb->height, (char *)&fb->format->format, drm_framebuffer_read_refcount(fb));
for (i = 0; i < fb->format->num_planes; i++) { drm_printf(p, "\t%u: offset=%d pitch=%d", i, fb->offsets[i], fb->pitches[i]); if (fb->obj[i]) { drm_printf(p, " GEM: "); drm_gem_print(fb->obj[i], p); } else { drm_printf(p, "\n"); } } }
static int drm_framebuffer_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct drm_printer p = drm_seq_file_printer(m); struct drm_framebuffer *fb;
mutex_lock(&dev->mode_config.fb_lock); drm_for_each_fb(fb, dev) drm_framebuffer_print(fb, &p); mutex_unlock(&dev->mode_config.fb_lock);
return 0; }
static const struct drm_info_list drm_framebuffer_debugfs_list[] = { { "framebuffer", drm_framebuffer_info, 0 }, };
int drm_framebuffer_debugfs_init(struct drm_minor *minor) { return drm_debugfs_create_files(drm_framebuffer_debugfs_list, ARRAY_SIZE(drm_framebuffer_debugfs_list), minor->debugfs_root, minor); } #endif
drivers/gpu/drm/drm_debugfs.c:
int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) { ... ret = drm_framebuffer_debugfs_init(minor); if (ret) { DRM_ERROR("Failed to create framebuffer debugfs files\n"); return ret; } ... }
Available names for the debugfs file:
$ grep -r ""fbs"" drivers/gpu/drm
$ grep -r ""fb"" drivers/gpu/drm drivers/gpu/drm/tilcdc/tilcdc_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/omapdrm/omap_debugfs.c: {"fb", fb_show, 0}, drivers/gpu/drm/tinydrm/mipi-dbi.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/arm/hdlcd_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/msm/msm_debugfs.c: { "fb", show_locked, 0, msm_fb_show }, drivers/gpu/drm/arc/arcpgu_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 },
$ grep -r ""framebuffer"" drivers/gpu/drm
$ grep -r ""framebuffers"" drivers/gpu/drm drivers/gpu/drm/tegra/drm.c: { "framebuffers", tegra_debugfs_framebuffers, 0 },
Noralf.
There's probably more stuff that we should list by default in debugfs, like blob properties and similar things. -Daniel
+/**
- drm_gem_fb_debugfs_show() - Helper to list GEM backed framebuffer objects
in debugfs.
- @m: Output file
- @arg: Private data for the callback
- This function gives a debugfs representation of the framebuffers with their
- backing GEM objects. See drm_debugfs_create_files() for more info.
- */
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg) +{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_framebuffer *fb;
- mutex_lock(&dev->mode_config.fb_lock);
- drm_for_each_fb(fb, dev)
drm_gem_fb_describe(fb, m);
- mutex_unlock(&dev->mode_config.fb_lock);
- return 0;
+} +EXPORT_SYMBOL_GPL(drm_gem_fb_debugfs_show); +#endif
- /**
- drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
emulation
diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index 5ca7cdc3f527..1bb73d961aba 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -28,6 +28,12 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state);
+#ifdef CONFIG_DEBUG_FS +struct seq_file;
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg); +#endif
- struct drm_framebuffer * drm_gem_fbdev_fb_create(struct drm_device *dev, struct drm_fb_helper_surface_size *sizes,
-- 2.14.2
On Wed, Oct 18, 2017 at 09:44:22PM +0200, Noralf Trønnes wrote:
Den 17.10.2017 14.56, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:39PM +0200, Noralf Trønnes wrote:
Add drm_gem_fb_debugfs_show() function to provide a debugfs representation of the framebuffer and GEM object(s).
Signed-off-by: Noralf Trønnes noralf@tronnes.org
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++++++++++++++++++++++++++ include/drm/drm_gem_framebuffer_helper.h | 6 ++++ 2 files changed, 51 insertions(+)
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index aa8cb9bfa499..18fdcc29427a 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -265,6 +265,51 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, } EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb); +#ifdef CONFIG_DEBUG_FS +static void drm_gem_fb_describe(struct drm_framebuffer *fb, struct seq_file *m) +{
- struct drm_gem_object *obj;
- unsigned int i;
- seq_printf(m, "[FB:%d] %dx%d@%4.4s\n", fb->base.id, fb->width,
fb->height, (char *)&fb->format->format);
- for (i = 0; i < fb->format->num_planes; i++) {
obj = fb->obj[i];
seq_printf(m, "\t%u: offset=%d pitch=%d, GEM: name=%d",
i, fb->offsets[i], fb->pitches[i], obj->name);
seq_printf(m, " refcount=%d start=%08lx size=%zu%s\n",
kref_read(&obj->refcount),
drm_vma_node_start(&obj->vma_node), obj->size,
obj->import_attach ? " (imported)" : "");
- }
+}
Given that gem is the defacto standard and that we can easily skip the gem specific stuff on non-gem drivers by checking the pointer for NULL I think we should add the framebuffer list by default. Like we already do for e.g. atomic state, see drm_atomic_debugfs_init. Probably better to split it up into a generic fb describe, which calls into a gem_obj describe, with maybe in the future the option to also call into driver callbacks to dump more driver state.
How about this:
lgtm. Whoever wants/needs to dump driver private stuff can then sprinkle the callbacks over this. -Daniel
drivers/gpu/drm/drm_gem.c:
#ifdef CONFIG_DEBUG_FS void drm_gem_print(const struct drm_gem_object *obj, struct drm_printer *p) { drm_printf(p, "name=%d refcount=%d start=%08lx size=%zu%s\n", obj->name, kref_read(&obj->refcount), drm_vma_node_start(&obj->vma_node), obj->size, obj->import_attach ? " (imported)" : ""); } #endif
drivers/gpu/drm/drm_framebuffer.c:
#ifdef CONFIG_DEBUG_FS static void drm_framebuffer_print(const struct drm_framebuffer *fb, struct drm_printer *p) { unsigned int i;
drm_printf(p, "[FB:%d] %dx%d@%4.4s refcount=%d\n", fb->base.id, fb->width, fb->height, (char *)&fb->format->format, drm_framebuffer_read_refcount(fb));
for (i = 0; i < fb->format->num_planes; i++) { drm_printf(p, "\t%u: offset=%d pitch=%d", i, fb->offsets[i], fb->pitches[i]); if (fb->obj[i]) { drm_printf(p, " GEM: "); drm_gem_print(fb->obj[i], p); } else { drm_printf(p, "\n"); } } }
static int drm_framebuffer_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct drm_printer p = drm_seq_file_printer(m); struct drm_framebuffer *fb;
mutex_lock(&dev->mode_config.fb_lock); drm_for_each_fb(fb, dev) drm_framebuffer_print(fb, &p); mutex_unlock(&dev->mode_config.fb_lock);
return 0; }
static const struct drm_info_list drm_framebuffer_debugfs_list[] = { { "framebuffer", drm_framebuffer_info, 0 }, };
int drm_framebuffer_debugfs_init(struct drm_minor *minor) { return drm_debugfs_create_files(drm_framebuffer_debugfs_list, ARRAY_SIZE(drm_framebuffer_debugfs_list), minor->debugfs_root, minor); } #endif
drivers/gpu/drm/drm_debugfs.c:
int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) { ... ret = drm_framebuffer_debugfs_init(minor); if (ret) { DRM_ERROR("Failed to create framebuffer debugfs files\n"); return ret; } ... }
Available names for the debugfs file:
$ grep -r ""fbs"" drivers/gpu/drm
$ grep -r ""fb"" drivers/gpu/drm drivers/gpu/drm/tilcdc/tilcdc_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/omapdrm/omap_debugfs.c: {"fb", fb_show, 0}, drivers/gpu/drm/tinydrm/mipi-dbi.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/arm/hdlcd_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 }, drivers/gpu/drm/msm/msm_debugfs.c: { "fb", show_locked, 0, msm_fb_show }, drivers/gpu/drm/arc/arcpgu_drv.c: { "fb", drm_fb_cma_debugfs_show, 0 },
$ grep -r ""framebuffer"" drivers/gpu/drm
$ grep -r ""framebuffers"" drivers/gpu/drm drivers/gpu/drm/tegra/drm.c: { "framebuffers", tegra_debugfs_framebuffers, 0 },
Noralf.
There's probably more stuff that we should list by default in debugfs, like blob properties and similar things. -Daniel
+/**
- drm_gem_fb_debugfs_show() - Helper to list GEM backed framebuffer objects
in debugfs.
- @m: Output file
- @arg: Private data for the callback
- This function gives a debugfs representation of the framebuffers with their
- backing GEM objects. See drm_debugfs_create_files() for more info.
- */
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg) +{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_framebuffer *fb;
- mutex_lock(&dev->mode_config.fb_lock);
- drm_for_each_fb(fb, dev)
drm_gem_fb_describe(fb, m);
- mutex_unlock(&dev->mode_config.fb_lock);
- return 0;
+} +EXPORT_SYMBOL_GPL(drm_gem_fb_debugfs_show); +#endif
- /**
- drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
emulation
diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index 5ca7cdc3f527..1bb73d961aba 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -28,6 +28,12 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state); +#ifdef CONFIG_DEBUG_FS +struct seq_file;
+int drm_gem_fb_debugfs_show(struct seq_file *m, void *arg); +#endif
- struct drm_framebuffer * drm_gem_fbdev_fb_create(struct drm_device *dev, struct drm_fb_helper_surface_size *sizes,
-- 2.14.2
Add vmalloc buffer object helper that can be useful for modesetting drivers, particularly the framebuffer flushing kind.
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 ++++++++++++++++++++++++++++++++ include/drm/drm_vmalloc_bo_helper.h | 88 +++++++++ 5 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 13dd237418cc..fd1ca10f6611 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -305,3 +305,15 @@ Framebuffer GEM Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_framebuffer_helper.c :export: + +vmalloc buffer object helper +============================ + +.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_vmalloc_bo_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c + :export: diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4d9f21831741..5d580440a259 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -145,6 +145,13 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions
+config DRM_VMALLOC_BO_HELPER + bool + depends on DRM + select DRM_KMS_HELPER + help + Choose this if you need the vmalloc buffer object helper functions + config DRM_VM bool depends on DRM && MMU diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a3fdc5a68dff..ed3eafa97a69 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,6 +39,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +drm_kms_helper-$(CONFIG_DRM_VMALLOC_BO_HELPER) += drm_vmalloc_bo_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vmalloc_bo_helper.c b/drivers/gpu/drm/drm_vmalloc_bo_helper.c new file mode 100644 index 000000000000..4015b9d1d671 --- /dev/null +++ b/drivers/gpu/drm/drm_vmalloc_bo_helper.c @@ -0,0 +1,305 @@ +/* + * DRM vmalloc buffer object helper functions + * + * Copyright (C) 2017 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-buf.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> + +/** + * DOC: overview + * + * This helper provides a simple GEM based buffer object with buffers allocated + * using vmalloc(). This is useful for modesetting drivers that do framebuffer + * flushing. It supports dumb buffers and PRIME import which can be setup using + * the DEFINE_DRM_VMALLOC_BO_FOPS() and DRM_VMALLOC_BO_DRIVER_OPS() macros. + * + * fbdev emulation can be setup using the drm_vmalloc_bo_fbdev_probe() function. + */ + +static struct drm_vmalloc_bo * +drm_vmalloc_bo_create(struct drm_device *dev, size_t size, bool backing) +{ + struct drm_vmalloc_bo *bo; + int ret; + + size = PAGE_ALIGN(size); + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return ERR_PTR(-ENOMEM); + + if (backing) { + bo->vaddr = vmalloc_user(size); + if (!bo->vaddr) { + ret = -ENOMEM; + goto error_free; + } + } + + drm_gem_private_object_init(dev, &bo->base, size); + + return bo; + +error_free: + kfree(bo); + + return ERR_PTR(ret); +} + +/** + * drm_vmalloc_bo_free_object() - Free resources associated with a vmalloc BO + * object + * @obj: GEM object to free + * + * This function frees the backing memory of the vmalloc BO object, cleans up + * the GEM object state and frees the memory used to store the object itself. + * Drivers using the vmalloc BO helpers should set this as their + * &drm_driver.gem_free_object callback. + */ +void drm_vmalloc_bo_free_object(struct drm_gem_object *obj) +{ + struct drm_vmalloc_bo *bo = to_drm_vmalloc_bo(obj); + + if (obj->import_attach) + dma_buf_vunmap(obj->import_attach->dmabuf, bo->vaddr); + else + vfree(bo->vaddr); + + drm_gem_object_release(obj); + kfree(bo); +} +EXPORT_SYMBOL(drm_vmalloc_bo_free_object); + +/** + * drm_vmalloc_bo_dumb_create() - Create a dumb vmalloc BO + * @file: DRM file structure to create the dumb buffer for + * @dev: DRM device + * @args: IOCTL data + * + * This function computes the pitch of the dumb buffer and rounds it up to an + * integer number of bytes per pixel. Drivers for hardware that doesn't have + * any additional restrictions on the pitch can directly use this function as + * their &drm_driver.dumb_create callback. + * + * For hardware with additional restrictions, drivers can adjust the fields + * set up by userspace before calling into this function. Also + * &drm_mode_create_dumb.pitch and &drm_mode_create_dumb.size can be set. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct drm_vmalloc_bo *bo; + int ret; + + if (!args->pitch) + args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + else + args->pitch = max(args->pitch, + DIV_ROUND_UP(args->width * args->bpp, 8)); + if (!args->size) + args->size = args->pitch * args->height; + else + args->size = max_t(typeof(args->size), args->size, + args->pitch * args->height); + + bo = drm_vmalloc_bo_create(dev, args->size, true); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + ret = drm_gem_handle_create(file, &bo->base, &args->handle); + /* drop reference from allocate - handle holds it now. */ + drm_gem_object_put_unlocked(&bo->base); + + return ret; +} +EXPORT_SYMBOL(drm_vmalloc_bo_dumb_create); + +const struct vm_operations_struct drm_vmalloc_bo_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; +EXPORT_SYMBOL_GPL(drm_vmalloc_bo_vm_ops); + +/** + * drm_vmalloc_bo_mmap() - Memory-map a vmalloc BO + * @filp: File object + * @vma: VMA for the area to be mapped + * + * This function implements an augmented version of the GEM DRM file mmap + * operation for vmalloc buffer objects. Drivers should use this function as + * their ->mmap handler in the DRM device file's file_operations structure. + * + * Instead of directly referencing this function, drivers should use the + * DEFINE_DRM_VMALLOC_BO_FOPS() macro. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_vmalloc_bo *bo; + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + /* Set by drm_gem_mmap() */ + vma->vm_flags &= ~VM_IO; + vma->vm_flags &= ~VM_PFNMAP; + + bo = to_drm_vmalloc_bo(vma->vm_private_data); + + return remap_vmalloc_range(vma, bo->vaddr, 0); +} +EXPORT_SYMBOL(drm_vmalloc_bo_mmap); + +/** + * drm_vmalloc_bo_prime_import_sg_table() - Produce a vmalloc BO from a dma-buf + * @dev: DRM device to import into + * @attach: dmabuf attachment + * @sgt: Scatter/gather table of pinned pages + * + * This function creates a vmalloc buffer object using the virtual address on + * the dma-buf exported by another driver. Drivers using the vmalloc BO helpers + * should set this as their &drm_driver.gem_prime_import_sg_table callback. + * + * Returns: + * A pointer to a newly created GEM object or an ERR_PTR-encoded negative + * error code on failure. + */ +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct drm_vmalloc_bo *bo; + void *vaddr; + + vaddr = dma_buf_vmap(attach->dmabuf); + if (!vaddr) { + DRM_ERROR("Failed to vmap PRIME buffer\n"); + return ERR_PTR(-ENOMEM); + } + + bo = drm_vmalloc_bo_create(dev, attach->dmabuf->size, false); + if (IS_ERR(bo)) { + dma_buf_vunmap(attach->dmabuf, vaddr); + return ERR_CAST(bo); + } + + bo->vaddr = vaddr; + + DRM_DEBUG_PRIME("size = %zu\n", attach->dmabuf->size); + + return &bo->base; +} +EXPORT_SYMBOL(drm_vmalloc_bo_prime_import_sg_table); + +static struct fb_ops drm_vmalloc_bo_fbdev_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_read = drm_fb_helper_sys_read, + .fb_write = drm_fb_helper_sys_write, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, +}; + +/** + * drm_vmalloc_bo_fbdev_probe - fbdev emulation .fb_probe helper + * @fb_helper: fbdev emulation helper structure + * @sizes: Describes fbdev size and scanout surface size + * @fb_funcs: DRM framebuffer functions + * + * This function can be used in the &drm_fb_helper_funcs.fb_probe callback to + * setup fbdev emulation. If @fb_funcs->dirty is set, fbdev deferred I/O is + * initialized. drm_fb_helper_simple_init() can be used to initialize the fbdev + * emulation. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes, + const struct drm_framebuffer_funcs *fb_funcs) +{ + struct drm_device *dev = fb_helper->dev; + struct drm_framebuffer *fb; + struct drm_vmalloc_bo *bo; + struct fb_info *fbi; + size_t size; + int ret; + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + size = sizes->surface_width * sizes->surface_height * + DIV_ROUND_UP(sizes->surface_bpp, 8); + + bo = drm_vmalloc_bo_create(dev, size, true); + if (IS_ERR(bo)) + return -ENOMEM; + + fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &bo->base, fb_funcs); + if (IS_ERR(fb)) { + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(fb); + goto err_bo_free; + } + + fb_helper->fb = fb; + + fbi = drm_fb_helper_alloc_fbi(fb_helper); + if (IS_ERR(fbi)) { + ret = PTR_ERR(fbi); + goto err_fb_free; + } + + fbi->par = fb_helper; + fbi->flags = FBINFO_VIRTFB; + fbi->fbops = &drm_vmalloc_bo_fbdev_ops; + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height); + + fbi->screen_base = bo->vaddr; + fbi->screen_size = size; + fbi->fix.smem_len = size; + + if (fb_funcs->dirty) { + ret = drm_fb_helper_defio_init(fb_helper); + if (ret) + goto err_fb_info_destroy; + } + + return 0; + +err_fb_info_destroy: + drm_fb_helper_fini(fb_helper); +err_fb_free: + drm_framebuffer_remove(fb); +err_bo_free: + drm_gem_object_put_unlocked(&bo->base); + + return ret; +} +EXPORT_SYMBOL(drm_vmalloc_bo_fbdev_probe); diff --git a/include/drm/drm_vmalloc_bo_helper.h b/include/drm/drm_vmalloc_bo_helper.h new file mode 100644 index 000000000000..0df3d15e2e4a --- /dev/null +++ b/include/drm/drm_vmalloc_bo_helper.h @@ -0,0 +1,88 @@ +#ifndef __DRM_VMALLOC_BO_HELPER_H__ +#define __DRM_VMALLOC_BO_HELPER_H__ + +#include <drm/drmP.h> +#include <drm/drm_gem.h> + +struct drm_fb_helper_surface_size; + +/** + * struct drm_vmalloc_bo - vmalloc buffer object + */ +struct drm_vmalloc_bo { + /** + * @base: + * + * Base GEM object. + */ + struct drm_gem_object base; + + /** + * @vaddr: + * + * Kernel virtual address of the buffer. + */ + void *vaddr; +}; + +static inline struct drm_vmalloc_bo * +to_drm_vmalloc_bo(struct drm_gem_object *obj) +{ + return container_of(obj, struct drm_vmalloc_bo, base); +} + +static inline void *drm_vmalloc_bo_fb_vaddr(struct drm_framebuffer *fb) +{ + return to_drm_vmalloc_bo(fb->obj[0])->vaddr; +} + +void drm_vmalloc_bo_free_object(struct drm_gem_object *obj); +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); +int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma); +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); + +int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes, + const struct drm_framebuffer_funcs *fb_funcs); + +/** + * DEFINE_DRM_VMALLOC_BO_FOPS() - Macro to generate file operations for drivers + * @name: Name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations which can be + * assigned to &drm_driver.fops for vmalloc BO drivers. + */ +#define DEFINE_DRM_VMALLOC_BO_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_vmalloc_bo_mmap,\ + } + +extern const struct vm_operations_struct drm_vmalloc_bo_vm_ops; + +/** + * DRM_VMALLOC_BO_DRIVER_OPS - Default GEM and PRIME operations + * + * This macro provides a shortcut for setting the GEM and PRIME operations in + * the &drm_driver structure for vmalloc BO drivers. + */ +#define DRM_VMALLOC_BO_DRIVER_OPS \ + .gem_free_object = drm_vmalloc_bo_free_object, \ + .gem_vm_ops = &drm_vmalloc_bo_vm_ops, \ + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ + .gem_prime_import = drm_gem_prime_import, \ + .gem_prime_import_sg_table = drm_vmalloc_bo_prime_import_sg_table, \ + .dumb_create = drm_vmalloc_bo_dumb_create + +#endif /* __DRM_VMALLOC_BO_HELPER_H__ */
On Sun, Oct 15, 2017 at 06:30:40PM +0200, Noralf Trønnes wrote:
Add vmalloc buffer object helper that can be useful for modesetting drivers, particularly the framebuffer flushing kind.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Why can't we extend the shmem stuff to provide a simple vmalloc helper? Doing a new flavour of gem for every special use-case seems like not a good idea ...
This could then also be used by stuff like udl, with fewer changes. -Daniel
Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 ++++++++++++++++++++++++++++++++ include/drm/drm_vmalloc_bo_helper.h | 88 +++++++++ 5 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 13dd237418cc..fd1ca10f6611 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -305,3 +305,15 @@ Framebuffer GEM Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_framebuffer_helper.c :export:
+vmalloc buffer object helper +============================
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :doc: overview
+.. kernel-doc:: include/drm/drm_vmalloc_bo_helper.h
- :internal:
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :export:
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4d9f21831741..5d580440a259 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -145,6 +145,13 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions
+config DRM_VMALLOC_BO_HELPER
- bool
- depends on DRM
- select DRM_KMS_HELPER
- help
Choose this if you need the vmalloc buffer object helper functions
config DRM_VM bool depends on DRM && MMU diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a3fdc5a68dff..ed3eafa97a69 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,6 +39,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +drm_kms_helper-$(CONFIG_DRM_VMALLOC_BO_HELPER) += drm_vmalloc_bo_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vmalloc_bo_helper.c b/drivers/gpu/drm/drm_vmalloc_bo_helper.c new file mode 100644 index 000000000000..4015b9d1d671 --- /dev/null +++ b/drivers/gpu/drm/drm_vmalloc_bo_helper.c @@ -0,0 +1,305 @@ +/*
- DRM vmalloc buffer object helper functions
- Copyright (C) 2017 Noralf Trønnes
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- */
+#include <linux/dma-buf.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/vmalloc.h>
+#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h>
+/**
- DOC: overview
- This helper provides a simple GEM based buffer object with buffers allocated
- using vmalloc(). This is useful for modesetting drivers that do framebuffer
- flushing. It supports dumb buffers and PRIME import which can be setup using
- the DEFINE_DRM_VMALLOC_BO_FOPS() and DRM_VMALLOC_BO_DRIVER_OPS() macros.
- fbdev emulation can be setup using the drm_vmalloc_bo_fbdev_probe() function.
- */
+static struct drm_vmalloc_bo * +drm_vmalloc_bo_create(struct drm_device *dev, size_t size, bool backing) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- size = PAGE_ALIGN(size);
- bo = kzalloc(sizeof(*bo), GFP_KERNEL);
- if (!bo)
return ERR_PTR(-ENOMEM);
- if (backing) {
bo->vaddr = vmalloc_user(size);
if (!bo->vaddr) {
ret = -ENOMEM;
goto error_free;
}
- }
- drm_gem_private_object_init(dev, &bo->base, size);
- return bo;
+error_free:
- kfree(bo);
- return ERR_PTR(ret);
+}
+/**
- drm_vmalloc_bo_free_object() - Free resources associated with a vmalloc BO
object
- @obj: GEM object to free
- This function frees the backing memory of the vmalloc BO object, cleans up
- the GEM object state and frees the memory used to store the object itself.
- Drivers using the vmalloc BO helpers should set this as their
- &drm_driver.gem_free_object callback.
- */
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj) +{
- struct drm_vmalloc_bo *bo = to_drm_vmalloc_bo(obj);
- if (obj->import_attach)
dma_buf_vunmap(obj->import_attach->dmabuf, bo->vaddr);
- else
vfree(bo->vaddr);
- drm_gem_object_release(obj);
- kfree(bo);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_free_object);
+/**
- drm_vmalloc_bo_dumb_create() - Create a dumb vmalloc BO
- @file: DRM file structure to create the dumb buffer for
- @dev: DRM device
- @args: IOCTL data
- This function computes the pitch of the dumb buffer and rounds it up to an
- integer number of bytes per pixel. Drivers for hardware that doesn't have
- any additional restrictions on the pitch can directly use this function as
- their &drm_driver.dumb_create callback.
- For hardware with additional restrictions, drivers can adjust the fields
- set up by userspace before calling into this function. Also
- &drm_mode_create_dumb.pitch and &drm_mode_create_dumb.size can be set.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct drm_vmalloc_bo *bo;
- int ret;
- if (!args->pitch)
args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- else
args->pitch = max(args->pitch,
DIV_ROUND_UP(args->width * args->bpp, 8));
- if (!args->size)
args->size = args->pitch * args->height;
- else
args->size = max_t(typeof(args->size), args->size,
args->pitch * args->height);
- bo = drm_vmalloc_bo_create(dev, args->size, true);
- if (IS_ERR(bo))
return PTR_ERR(bo);
- ret = drm_gem_handle_create(file, &bo->base, &args->handle);
- /* drop reference from allocate - handle holds it now. */
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_dumb_create);
+const struct vm_operations_struct drm_vmalloc_bo_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+}; +EXPORT_SYMBOL_GPL(drm_vmalloc_bo_vm_ops);
+/**
- drm_vmalloc_bo_mmap() - Memory-map a vmalloc BO
- @filp: File object
- @vma: VMA for the area to be mapped
- This function implements an augmented version of the GEM DRM file mmap
- operation for vmalloc buffer objects. Drivers should use this function as
- their ->mmap handler in the DRM device file's file_operations structure.
- Instead of directly referencing this function, drivers should use the
- DEFINE_DRM_VMALLOC_BO_FOPS() macro.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- ret = drm_gem_mmap(filp, vma);
- if (ret)
return ret;
- /* Set by drm_gem_mmap() */
- vma->vm_flags &= ~VM_IO;
- vma->vm_flags &= ~VM_PFNMAP;
- bo = to_drm_vmalloc_bo(vma->vm_private_data);
- return remap_vmalloc_range(vma, bo->vaddr, 0);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_mmap);
+/**
- drm_vmalloc_bo_prime_import_sg_table() - Produce a vmalloc BO from a dma-buf
- @dev: DRM device to import into
- @attach: dmabuf attachment
- @sgt: Scatter/gather table of pinned pages
- This function creates a vmalloc buffer object using the virtual address on
- the dma-buf exported by another driver. Drivers using the vmalloc BO helpers
- should set this as their &drm_driver.gem_prime_import_sg_table callback.
- Returns:
- A pointer to a newly created GEM object or an ERR_PTR-encoded negative
- error code on failure.
- */
+struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct drm_vmalloc_bo *bo;
- void *vaddr;
- vaddr = dma_buf_vmap(attach->dmabuf);
- if (!vaddr) {
DRM_ERROR("Failed to vmap PRIME buffer\n");
return ERR_PTR(-ENOMEM);
- }
- bo = drm_vmalloc_bo_create(dev, attach->dmabuf->size, false);
- if (IS_ERR(bo)) {
dma_buf_vunmap(attach->dmabuf, vaddr);
return ERR_CAST(bo);
- }
- bo->vaddr = vaddr;
- DRM_DEBUG_PRIME("size = %zu\n", attach->dmabuf->size);
- return &bo->base;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_prime_import_sg_table);
+static struct fb_ops drm_vmalloc_bo_fbdev_ops = {
- .owner = THIS_MODULE,
- DRM_FB_HELPER_DEFAULT_OPS,
- .fb_read = drm_fb_helper_sys_read,
- .fb_write = drm_fb_helper_sys_write,
- .fb_fillrect = drm_fb_helper_sys_fillrect,
- .fb_copyarea = drm_fb_helper_sys_copyarea,
- .fb_imageblit = drm_fb_helper_sys_imageblit,
+};
+/**
- drm_vmalloc_bo_fbdev_probe - fbdev emulation .fb_probe helper
- @fb_helper: fbdev emulation helper structure
- @sizes: Describes fbdev size and scanout surface size
- @fb_funcs: DRM framebuffer functions
- This function can be used in the &drm_fb_helper_funcs.fb_probe callback to
- setup fbdev emulation. If @fb_funcs->dirty is set, fbdev deferred I/O is
- initialized. drm_fb_helper_simple_init() can be used to initialize the fbdev
- emulation.
- Returns:
- Zero on success, negative error code on failure.
- */
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs)
+{
- struct drm_device *dev = fb_helper->dev;
- struct drm_framebuffer *fb;
- struct drm_vmalloc_bo *bo;
- struct fb_info *fbi;
- size_t size;
- int ret;
- DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
- size = sizes->surface_width * sizes->surface_height *
DIV_ROUND_UP(sizes->surface_bpp, 8);
- bo = drm_vmalloc_bo_create(dev, size, true);
- if (IS_ERR(bo))
return -ENOMEM;
- fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &bo->base, fb_funcs);
- if (IS_ERR(fb)) {
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
ret = PTR_ERR(fb);
goto err_bo_free;
- }
- fb_helper->fb = fb;
- fbi = drm_fb_helper_alloc_fbi(fb_helper);
- if (IS_ERR(fbi)) {
ret = PTR_ERR(fbi);
goto err_fb_free;
- }
- fbi->par = fb_helper;
- fbi->flags = FBINFO_VIRTFB;
- fbi->fbops = &drm_vmalloc_bo_fbdev_ops;
- drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
- drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
- fbi->screen_base = bo->vaddr;
- fbi->screen_size = size;
- fbi->fix.smem_len = size;
- if (fb_funcs->dirty) {
ret = drm_fb_helper_defio_init(fb_helper);
if (ret)
goto err_fb_info_destroy;
- }
- return 0;
+err_fb_info_destroy:
- drm_fb_helper_fini(fb_helper);
+err_fb_free:
- drm_framebuffer_remove(fb);
+err_bo_free:
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_fbdev_probe); diff --git a/include/drm/drm_vmalloc_bo_helper.h b/include/drm/drm_vmalloc_bo_helper.h new file mode 100644 index 000000000000..0df3d15e2e4a --- /dev/null +++ b/include/drm/drm_vmalloc_bo_helper.h @@ -0,0 +1,88 @@ +#ifndef __DRM_VMALLOC_BO_HELPER_H__ +#define __DRM_VMALLOC_BO_HELPER_H__
+#include <drm/drmP.h> +#include <drm/drm_gem.h>
+struct drm_fb_helper_surface_size;
+/**
- struct drm_vmalloc_bo - vmalloc buffer object
- */
+struct drm_vmalloc_bo {
- /**
* @base:
*
* Base GEM object.
*/
- struct drm_gem_object base;
- /**
* @vaddr:
*
* Kernel virtual address of the buffer.
*/
- void *vaddr;
+};
+static inline struct drm_vmalloc_bo * +to_drm_vmalloc_bo(struct drm_gem_object *obj) +{
- return container_of(obj, struct drm_vmalloc_bo, base);
+}
+static inline void *drm_vmalloc_bo_fb_vaddr(struct drm_framebuffer *fb) +{
- return to_drm_vmalloc_bo(fb->obj[0])->vaddr;
+}
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj); +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma); +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs);
+/**
- DEFINE_DRM_VMALLOC_BO_FOPS() - Macro to generate file operations for drivers
- @name: Name for the generated structure
- This macro autogenerates a suitable &struct file_operations which can be
- assigned to &drm_driver.fops for vmalloc BO drivers.
- */
+#define DEFINE_DRM_VMALLOC_BO_FOPS(name) \
- static const struct file_operations name = {\
.owner = THIS_MODULE,\
.open = drm_open,\
.release = drm_release,\
.unlocked_ioctl = drm_ioctl,\
.compat_ioctl = drm_compat_ioctl,\
.poll = drm_poll,\
.read = drm_read,\
.llseek = noop_llseek,\
.mmap = drm_vmalloc_bo_mmap,\
- }
+extern const struct vm_operations_struct drm_vmalloc_bo_vm_ops;
+/**
- DRM_VMALLOC_BO_DRIVER_OPS - Default GEM and PRIME operations
- This macro provides a shortcut for setting the GEM and PRIME operations in
- the &drm_driver structure for vmalloc BO drivers.
- */
+#define DRM_VMALLOC_BO_DRIVER_OPS \
- .gem_free_object = drm_vmalloc_bo_free_object, \
- .gem_vm_ops = &drm_vmalloc_bo_vm_ops, \
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
- .gem_prime_import = drm_gem_prime_import, \
- .gem_prime_import_sg_table = drm_vmalloc_bo_prime_import_sg_table, \
- .dumb_create = drm_vmalloc_bo_dumb_create
+#endif /* __DRM_VMALLOC_BO_HELPER_H__ */
2.14.2
Den 17.10.2017 14.51, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:40PM +0200, Noralf Trønnes wrote:
Add vmalloc buffer object helper that can be useful for modesetting drivers, particularly the framebuffer flushing kind.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Why can't we extend the shmem stuff to provide a simple vmalloc helper? Doing a new flavour of gem for every special use-case seems like not a good idea ...
This could then also be used by stuff like udl, with fewer changes.
This is the rationale (from the coverletter):
The reason I want to move away from the cma helper, is that it restricts which drivers to PRIME import from, since cma requires the buffer to be physically continuous. Initially I looked at udl and decided to use shmem, but have later decided against it since shmem doesn't work with fbdev deferred I/O, they both use page->lru and page->mapping. This meant adding a shadow buffer for fbdev with increased complexity, so I fell back to the KISS principle.
I'm trying to make tinydrm support drivers like simpledrm and udl, so if using vmalloc blocks those use cases, please let me know.
Noralf.
-Daniel
Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 ++++++++++++++++++++++++++++++++ include/drm/drm_vmalloc_bo_helper.h | 88 +++++++++ 5 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 13dd237418cc..fd1ca10f6611 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -305,3 +305,15 @@ Framebuffer GEM Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_framebuffer_helper.c :export:
+vmalloc buffer object helper +============================
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :doc: overview
+.. kernel-doc:: include/drm/drm_vmalloc_bo_helper.h
- :internal:
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :export:
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4d9f21831741..5d580440a259 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -145,6 +145,13 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions
+config DRM_VMALLOC_BO_HELPER
- bool
- depends on DRM
- select DRM_KMS_HELPER
- help
Choose this if you need the vmalloc buffer object helper functions
- config DRM_VM bool depends on DRM && MMU
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a3fdc5a68dff..ed3eafa97a69 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,6 +39,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +drm_kms_helper-$(CONFIG_DRM_VMALLOC_BO_HELPER) += drm_vmalloc_bo_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vmalloc_bo_helper.c b/drivers/gpu/drm/drm_vmalloc_bo_helper.c new file mode 100644 index 000000000000..4015b9d1d671 --- /dev/null +++ b/drivers/gpu/drm/drm_vmalloc_bo_helper.c @@ -0,0 +1,305 @@ +/*
- DRM vmalloc buffer object helper functions
- Copyright (C) 2017 Noralf Trønnes
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- */
+#include <linux/dma-buf.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/vmalloc.h>
+#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h>
+/**
- DOC: overview
- This helper provides a simple GEM based buffer object with buffers allocated
- using vmalloc(). This is useful for modesetting drivers that do framebuffer
- flushing. It supports dumb buffers and PRIME import which can be setup using
- the DEFINE_DRM_VMALLOC_BO_FOPS() and DRM_VMALLOC_BO_DRIVER_OPS() macros.
- fbdev emulation can be setup using the drm_vmalloc_bo_fbdev_probe() function.
- */
+static struct drm_vmalloc_bo * +drm_vmalloc_bo_create(struct drm_device *dev, size_t size, bool backing) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- size = PAGE_ALIGN(size);
- bo = kzalloc(sizeof(*bo), GFP_KERNEL);
- if (!bo)
return ERR_PTR(-ENOMEM);
- if (backing) {
bo->vaddr = vmalloc_user(size);
if (!bo->vaddr) {
ret = -ENOMEM;
goto error_free;
}
- }
- drm_gem_private_object_init(dev, &bo->base, size);
- return bo;
+error_free:
- kfree(bo);
- return ERR_PTR(ret);
+}
+/**
- drm_vmalloc_bo_free_object() - Free resources associated with a vmalloc BO
object
- @obj: GEM object to free
- This function frees the backing memory of the vmalloc BO object, cleans up
- the GEM object state and frees the memory used to store the object itself.
- Drivers using the vmalloc BO helpers should set this as their
- &drm_driver.gem_free_object callback.
- */
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj) +{
- struct drm_vmalloc_bo *bo = to_drm_vmalloc_bo(obj);
- if (obj->import_attach)
dma_buf_vunmap(obj->import_attach->dmabuf, bo->vaddr);
- else
vfree(bo->vaddr);
- drm_gem_object_release(obj);
- kfree(bo);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_free_object);
+/**
- drm_vmalloc_bo_dumb_create() - Create a dumb vmalloc BO
- @file: DRM file structure to create the dumb buffer for
- @dev: DRM device
- @args: IOCTL data
- This function computes the pitch of the dumb buffer and rounds it up to an
- integer number of bytes per pixel. Drivers for hardware that doesn't have
- any additional restrictions on the pitch can directly use this function as
- their &drm_driver.dumb_create callback.
- For hardware with additional restrictions, drivers can adjust the fields
- set up by userspace before calling into this function. Also
- &drm_mode_create_dumb.pitch and &drm_mode_create_dumb.size can be set.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct drm_vmalloc_bo *bo;
- int ret;
- if (!args->pitch)
args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- else
args->pitch = max(args->pitch,
DIV_ROUND_UP(args->width * args->bpp, 8));
- if (!args->size)
args->size = args->pitch * args->height;
- else
args->size = max_t(typeof(args->size), args->size,
args->pitch * args->height);
- bo = drm_vmalloc_bo_create(dev, args->size, true);
- if (IS_ERR(bo))
return PTR_ERR(bo);
- ret = drm_gem_handle_create(file, &bo->base, &args->handle);
- /* drop reference from allocate - handle holds it now. */
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_dumb_create);
+const struct vm_operations_struct drm_vmalloc_bo_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+}; +EXPORT_SYMBOL_GPL(drm_vmalloc_bo_vm_ops);
+/**
- drm_vmalloc_bo_mmap() - Memory-map a vmalloc BO
- @filp: File object
- @vma: VMA for the area to be mapped
- This function implements an augmented version of the GEM DRM file mmap
- operation for vmalloc buffer objects. Drivers should use this function as
- their ->mmap handler in the DRM device file's file_operations structure.
- Instead of directly referencing this function, drivers should use the
- DEFINE_DRM_VMALLOC_BO_FOPS() macro.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- ret = drm_gem_mmap(filp, vma);
- if (ret)
return ret;
- /* Set by drm_gem_mmap() */
- vma->vm_flags &= ~VM_IO;
- vma->vm_flags &= ~VM_PFNMAP;
- bo = to_drm_vmalloc_bo(vma->vm_private_data);
- return remap_vmalloc_range(vma, bo->vaddr, 0);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_mmap);
+/**
- drm_vmalloc_bo_prime_import_sg_table() - Produce a vmalloc BO from a dma-buf
- @dev: DRM device to import into
- @attach: dmabuf attachment
- @sgt: Scatter/gather table of pinned pages
- This function creates a vmalloc buffer object using the virtual address on
- the dma-buf exported by another driver. Drivers using the vmalloc BO helpers
- should set this as their &drm_driver.gem_prime_import_sg_table callback.
- Returns:
- A pointer to a newly created GEM object or an ERR_PTR-encoded negative
- error code on failure.
- */
+struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct drm_vmalloc_bo *bo;
- void *vaddr;
- vaddr = dma_buf_vmap(attach->dmabuf);
- if (!vaddr) {
DRM_ERROR("Failed to vmap PRIME buffer\n");
return ERR_PTR(-ENOMEM);
- }
- bo = drm_vmalloc_bo_create(dev, attach->dmabuf->size, false);
- if (IS_ERR(bo)) {
dma_buf_vunmap(attach->dmabuf, vaddr);
return ERR_CAST(bo);
- }
- bo->vaddr = vaddr;
- DRM_DEBUG_PRIME("size = %zu\n", attach->dmabuf->size);
- return &bo->base;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_prime_import_sg_table);
+static struct fb_ops drm_vmalloc_bo_fbdev_ops = {
- .owner = THIS_MODULE,
- DRM_FB_HELPER_DEFAULT_OPS,
- .fb_read = drm_fb_helper_sys_read,
- .fb_write = drm_fb_helper_sys_write,
- .fb_fillrect = drm_fb_helper_sys_fillrect,
- .fb_copyarea = drm_fb_helper_sys_copyarea,
- .fb_imageblit = drm_fb_helper_sys_imageblit,
+};
+/**
- drm_vmalloc_bo_fbdev_probe - fbdev emulation .fb_probe helper
- @fb_helper: fbdev emulation helper structure
- @sizes: Describes fbdev size and scanout surface size
- @fb_funcs: DRM framebuffer functions
- This function can be used in the &drm_fb_helper_funcs.fb_probe callback to
- setup fbdev emulation. If @fb_funcs->dirty is set, fbdev deferred I/O is
- initialized. drm_fb_helper_simple_init() can be used to initialize the fbdev
- emulation.
- Returns:
- Zero on success, negative error code on failure.
- */
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs)
+{
- struct drm_device *dev = fb_helper->dev;
- struct drm_framebuffer *fb;
- struct drm_vmalloc_bo *bo;
- struct fb_info *fbi;
- size_t size;
- int ret;
- DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
- size = sizes->surface_width * sizes->surface_height *
DIV_ROUND_UP(sizes->surface_bpp, 8);
- bo = drm_vmalloc_bo_create(dev, size, true);
- if (IS_ERR(bo))
return -ENOMEM;
- fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &bo->base, fb_funcs);
- if (IS_ERR(fb)) {
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
ret = PTR_ERR(fb);
goto err_bo_free;
- }
- fb_helper->fb = fb;
- fbi = drm_fb_helper_alloc_fbi(fb_helper);
- if (IS_ERR(fbi)) {
ret = PTR_ERR(fbi);
goto err_fb_free;
- }
- fbi->par = fb_helper;
- fbi->flags = FBINFO_VIRTFB;
- fbi->fbops = &drm_vmalloc_bo_fbdev_ops;
- drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
- drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
- fbi->screen_base = bo->vaddr;
- fbi->screen_size = size;
- fbi->fix.smem_len = size;
- if (fb_funcs->dirty) {
ret = drm_fb_helper_defio_init(fb_helper);
if (ret)
goto err_fb_info_destroy;
- }
- return 0;
+err_fb_info_destroy:
- drm_fb_helper_fini(fb_helper);
+err_fb_free:
- drm_framebuffer_remove(fb);
+err_bo_free:
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_fbdev_probe); diff --git a/include/drm/drm_vmalloc_bo_helper.h b/include/drm/drm_vmalloc_bo_helper.h new file mode 100644 index 000000000000..0df3d15e2e4a --- /dev/null +++ b/include/drm/drm_vmalloc_bo_helper.h @@ -0,0 +1,88 @@ +#ifndef __DRM_VMALLOC_BO_HELPER_H__ +#define __DRM_VMALLOC_BO_HELPER_H__
+#include <drm/drmP.h> +#include <drm/drm_gem.h>
+struct drm_fb_helper_surface_size;
+/**
- struct drm_vmalloc_bo - vmalloc buffer object
- */
+struct drm_vmalloc_bo {
- /**
* @base:
*
* Base GEM object.
*/
- struct drm_gem_object base;
- /**
* @vaddr:
*
* Kernel virtual address of the buffer.
*/
- void *vaddr;
+};
+static inline struct drm_vmalloc_bo * +to_drm_vmalloc_bo(struct drm_gem_object *obj) +{
- return container_of(obj, struct drm_vmalloc_bo, base);
+}
+static inline void *drm_vmalloc_bo_fb_vaddr(struct drm_framebuffer *fb) +{
- return to_drm_vmalloc_bo(fb->obj[0])->vaddr;
+}
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj); +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma); +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs);
+/**
- DEFINE_DRM_VMALLOC_BO_FOPS() - Macro to generate file operations for drivers
- @name: Name for the generated structure
- This macro autogenerates a suitable &struct file_operations which can be
- assigned to &drm_driver.fops for vmalloc BO drivers.
- */
+#define DEFINE_DRM_VMALLOC_BO_FOPS(name) \
- static const struct file_operations name = {\
.owner = THIS_MODULE,\
.open = drm_open,\
.release = drm_release,\
.unlocked_ioctl = drm_ioctl,\
.compat_ioctl = drm_compat_ioctl,\
.poll = drm_poll,\
.read = drm_read,\
.llseek = noop_llseek,\
.mmap = drm_vmalloc_bo_mmap,\
- }
+extern const struct vm_operations_struct drm_vmalloc_bo_vm_ops;
+/**
- DRM_VMALLOC_BO_DRIVER_OPS - Default GEM and PRIME operations
- This macro provides a shortcut for setting the GEM and PRIME operations in
- the &drm_driver structure for vmalloc BO drivers.
- */
+#define DRM_VMALLOC_BO_DRIVER_OPS \
- .gem_free_object = drm_vmalloc_bo_free_object, \
- .gem_vm_ops = &drm_vmalloc_bo_vm_ops, \
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
- .gem_prime_import = drm_gem_prime_import, \
- .gem_prime_import_sg_table = drm_vmalloc_bo_prime_import_sg_table, \
- .dumb_create = drm_vmalloc_bo_dumb_create
+#endif /* __DRM_VMALLOC_BO_HELPER_H__ */
2.14.2
On Wed, Oct 18, 2017 at 09:44:43PM +0200, Noralf Trønnes wrote:
Den 17.10.2017 14.51, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:40PM +0200, Noralf Trønnes wrote:
Add vmalloc buffer object helper that can be useful for modesetting drivers, particularly the framebuffer flushing kind.
Signed-off-by: Noralf Trønnes noralf@tronnes.org
Why can't we extend the shmem stuff to provide a simple vmalloc helper? Doing a new flavour of gem for every special use-case seems like not a good idea ...
This could then also be used by stuff like udl, with fewer changes.
This is the rationale (from the coverletter):
The reason I want to move away from the cma helper, is that it restricts which drivers to PRIME import from, since cma requires the buffer to be physically continuous. Initially I looked at udl and decided to use shmem, but have later decided against it since shmem doesn't work with fbdev deferred I/O, they both use page->lru and page->mapping. This meant adding a shadow buffer for fbdev with increased complexity, so I fell back to the KISS principle.
Hm, I missed that. Can you pls add the reason for why we can't just use shmem to the overview kerneldoc comment?
I'm trying to make tinydrm support drivers like simpledrm and udl, so if using vmalloc blocks those use cases, please let me know.
It shouldn't, I just wanted to know why we need something really similar to shmem (since that's also an sg list that can be vmapped). One remote concern I have is that drivers which in general want to use shmem, but also need fbdev defio support might need to do some acrobatics, and use the vmalloc gem type only for fbdev. But I think that should work out too.
I'm travelling next week and will probably not get around to do a full review of this patch here, would be good if you can find someone else meanwhile.
Thanks, Daniel
Noralf.
-Daniel
Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 ++++++++++++++++++++++++++++++++ include/drm/drm_vmalloc_bo_helper.h | 88 +++++++++ 5 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 13dd237418cc..fd1ca10f6611 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -305,3 +305,15 @@ Framebuffer GEM Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_gem_framebuffer_helper.c :export:
+vmalloc buffer object helper +============================
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :doc: overview
+.. kernel-doc:: include/drm/drm_vmalloc_bo_helper.h
- :internal:
+.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c
- :export:
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4d9f21831741..5d580440a259 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -145,6 +145,13 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +config DRM_VMALLOC_BO_HELPER
- bool
- depends on DRM
- select DRM_KMS_HELPER
- help
Choose this if you need the vmalloc buffer object helper functions
- config DRM_VM bool depends on DRM && MMU
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a3fdc5a68dff..ed3eafa97a69 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,6 +39,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +drm_kms_helper-$(CONFIG_DRM_VMALLOC_BO_HELPER) += drm_vmalloc_bo_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vmalloc_bo_helper.c b/drivers/gpu/drm/drm_vmalloc_bo_helper.c new file mode 100644 index 000000000000..4015b9d1d671 --- /dev/null +++ b/drivers/gpu/drm/drm_vmalloc_bo_helper.c @@ -0,0 +1,305 @@ +/*
- DRM vmalloc buffer object helper functions
- Copyright (C) 2017 Noralf Trønnes
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- */
+#include <linux/dma-buf.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/vmalloc.h>
+#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h>
+/**
- DOC: overview
- This helper provides a simple GEM based buffer object with buffers allocated
- using vmalloc(). This is useful for modesetting drivers that do framebuffer
- flushing. It supports dumb buffers and PRIME import which can be setup using
- the DEFINE_DRM_VMALLOC_BO_FOPS() and DRM_VMALLOC_BO_DRIVER_OPS() macros.
- fbdev emulation can be setup using the drm_vmalloc_bo_fbdev_probe() function.
- */
+static struct drm_vmalloc_bo * +drm_vmalloc_bo_create(struct drm_device *dev, size_t size, bool backing) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- size = PAGE_ALIGN(size);
- bo = kzalloc(sizeof(*bo), GFP_KERNEL);
- if (!bo)
return ERR_PTR(-ENOMEM);
- if (backing) {
bo->vaddr = vmalloc_user(size);
if (!bo->vaddr) {
ret = -ENOMEM;
goto error_free;
}
- }
- drm_gem_private_object_init(dev, &bo->base, size);
- return bo;
+error_free:
- kfree(bo);
- return ERR_PTR(ret);
+}
+/**
- drm_vmalloc_bo_free_object() - Free resources associated with a vmalloc BO
object
- @obj: GEM object to free
- This function frees the backing memory of the vmalloc BO object, cleans up
- the GEM object state and frees the memory used to store the object itself.
- Drivers using the vmalloc BO helpers should set this as their
- &drm_driver.gem_free_object callback.
- */
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj) +{
- struct drm_vmalloc_bo *bo = to_drm_vmalloc_bo(obj);
- if (obj->import_attach)
dma_buf_vunmap(obj->import_attach->dmabuf, bo->vaddr);
- else
vfree(bo->vaddr);
- drm_gem_object_release(obj);
- kfree(bo);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_free_object);
+/**
- drm_vmalloc_bo_dumb_create() - Create a dumb vmalloc BO
- @file: DRM file structure to create the dumb buffer for
- @dev: DRM device
- @args: IOCTL data
- This function computes the pitch of the dumb buffer and rounds it up to an
- integer number of bytes per pixel. Drivers for hardware that doesn't have
- any additional restrictions on the pitch can directly use this function as
- their &drm_driver.dumb_create callback.
- For hardware with additional restrictions, drivers can adjust the fields
- set up by userspace before calling into this function. Also
- &drm_mode_create_dumb.pitch and &drm_mode_create_dumb.size can be set.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct drm_vmalloc_bo *bo;
- int ret;
- if (!args->pitch)
args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- else
args->pitch = max(args->pitch,
DIV_ROUND_UP(args->width * args->bpp, 8));
- if (!args->size)
args->size = args->pitch * args->height;
- else
args->size = max_t(typeof(args->size), args->size,
args->pitch * args->height);
- bo = drm_vmalloc_bo_create(dev, args->size, true);
- if (IS_ERR(bo))
return PTR_ERR(bo);
- ret = drm_gem_handle_create(file, &bo->base, &args->handle);
- /* drop reference from allocate - handle holds it now. */
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_dumb_create);
+const struct vm_operations_struct drm_vmalloc_bo_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+}; +EXPORT_SYMBOL_GPL(drm_vmalloc_bo_vm_ops);
+/**
- drm_vmalloc_bo_mmap() - Memory-map a vmalloc BO
- @filp: File object
- @vma: VMA for the area to be mapped
- This function implements an augmented version of the GEM DRM file mmap
- operation for vmalloc buffer objects. Drivers should use this function as
- their ->mmap handler in the DRM device file's file_operations structure.
- Instead of directly referencing this function, drivers should use the
- DEFINE_DRM_VMALLOC_BO_FOPS() macro.
- Returns:
- Zero on success or a negative error code on failure.
- */
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma) +{
- struct drm_vmalloc_bo *bo;
- int ret;
- ret = drm_gem_mmap(filp, vma);
- if (ret)
return ret;
- /* Set by drm_gem_mmap() */
- vma->vm_flags &= ~VM_IO;
- vma->vm_flags &= ~VM_PFNMAP;
- bo = to_drm_vmalloc_bo(vma->vm_private_data);
- return remap_vmalloc_range(vma, bo->vaddr, 0);
+} +EXPORT_SYMBOL(drm_vmalloc_bo_mmap);
+/**
- drm_vmalloc_bo_prime_import_sg_table() - Produce a vmalloc BO from a dma-buf
- @dev: DRM device to import into
- @attach: dmabuf attachment
- @sgt: Scatter/gather table of pinned pages
- This function creates a vmalloc buffer object using the virtual address on
- the dma-buf exported by another driver. Drivers using the vmalloc BO helpers
- should set this as their &drm_driver.gem_prime_import_sg_table callback.
- Returns:
- A pointer to a newly created GEM object or an ERR_PTR-encoded negative
- error code on failure.
- */
+struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
+{
- struct drm_vmalloc_bo *bo;
- void *vaddr;
- vaddr = dma_buf_vmap(attach->dmabuf);
- if (!vaddr) {
DRM_ERROR("Failed to vmap PRIME buffer\n");
return ERR_PTR(-ENOMEM);
- }
- bo = drm_vmalloc_bo_create(dev, attach->dmabuf->size, false);
- if (IS_ERR(bo)) {
dma_buf_vunmap(attach->dmabuf, vaddr);
return ERR_CAST(bo);
- }
- bo->vaddr = vaddr;
- DRM_DEBUG_PRIME("size = %zu\n", attach->dmabuf->size);
- return &bo->base;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_prime_import_sg_table);
+static struct fb_ops drm_vmalloc_bo_fbdev_ops = {
- .owner = THIS_MODULE,
- DRM_FB_HELPER_DEFAULT_OPS,
- .fb_read = drm_fb_helper_sys_read,
- .fb_write = drm_fb_helper_sys_write,
- .fb_fillrect = drm_fb_helper_sys_fillrect,
- .fb_copyarea = drm_fb_helper_sys_copyarea,
- .fb_imageblit = drm_fb_helper_sys_imageblit,
+};
+/**
- drm_vmalloc_bo_fbdev_probe - fbdev emulation .fb_probe helper
- @fb_helper: fbdev emulation helper structure
- @sizes: Describes fbdev size and scanout surface size
- @fb_funcs: DRM framebuffer functions
- This function can be used in the &drm_fb_helper_funcs.fb_probe callback to
- setup fbdev emulation. If @fb_funcs->dirty is set, fbdev deferred I/O is
- initialized. drm_fb_helper_simple_init() can be used to initialize the fbdev
- emulation.
- Returns:
- Zero on success, negative error code on failure.
- */
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs)
+{
- struct drm_device *dev = fb_helper->dev;
- struct drm_framebuffer *fb;
- struct drm_vmalloc_bo *bo;
- struct fb_info *fbi;
- size_t size;
- int ret;
- DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
- size = sizes->surface_width * sizes->surface_height *
DIV_ROUND_UP(sizes->surface_bpp, 8);
- bo = drm_vmalloc_bo_create(dev, size, true);
- if (IS_ERR(bo))
return -ENOMEM;
- fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &bo->base, fb_funcs);
- if (IS_ERR(fb)) {
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
ret = PTR_ERR(fb);
goto err_bo_free;
- }
- fb_helper->fb = fb;
- fbi = drm_fb_helper_alloc_fbi(fb_helper);
- if (IS_ERR(fbi)) {
ret = PTR_ERR(fbi);
goto err_fb_free;
- }
- fbi->par = fb_helper;
- fbi->flags = FBINFO_VIRTFB;
- fbi->fbops = &drm_vmalloc_bo_fbdev_ops;
- drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
- drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
- fbi->screen_base = bo->vaddr;
- fbi->screen_size = size;
- fbi->fix.smem_len = size;
- if (fb_funcs->dirty) {
ret = drm_fb_helper_defio_init(fb_helper);
if (ret)
goto err_fb_info_destroy;
- }
- return 0;
+err_fb_info_destroy:
- drm_fb_helper_fini(fb_helper);
+err_fb_free:
- drm_framebuffer_remove(fb);
+err_bo_free:
- drm_gem_object_put_unlocked(&bo->base);
- return ret;
+} +EXPORT_SYMBOL(drm_vmalloc_bo_fbdev_probe); diff --git a/include/drm/drm_vmalloc_bo_helper.h b/include/drm/drm_vmalloc_bo_helper.h new file mode 100644 index 000000000000..0df3d15e2e4a --- /dev/null +++ b/include/drm/drm_vmalloc_bo_helper.h @@ -0,0 +1,88 @@ +#ifndef __DRM_VMALLOC_BO_HELPER_H__ +#define __DRM_VMALLOC_BO_HELPER_H__
+#include <drm/drmP.h> +#include <drm/drm_gem.h>
+struct drm_fb_helper_surface_size;
+/**
- struct drm_vmalloc_bo - vmalloc buffer object
- */
+struct drm_vmalloc_bo {
- /**
* @base:
*
* Base GEM object.
*/
- struct drm_gem_object base;
- /**
* @vaddr:
*
* Kernel virtual address of the buffer.
*/
- void *vaddr;
+};
+static inline struct drm_vmalloc_bo * +to_drm_vmalloc_bo(struct drm_gem_object *obj) +{
- return container_of(obj, struct drm_vmalloc_bo, base);
+}
+static inline void *drm_vmalloc_bo_fb_vaddr(struct drm_framebuffer *fb) +{
- return to_drm_vmalloc_bo(fb->obj[0])->vaddr;
+}
+void drm_vmalloc_bo_free_object(struct drm_gem_object *obj); +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
+int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma); +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
+int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes,
const struct drm_framebuffer_funcs *fb_funcs);
+/**
- DEFINE_DRM_VMALLOC_BO_FOPS() - Macro to generate file operations for drivers
- @name: Name for the generated structure
- This macro autogenerates a suitable &struct file_operations which can be
- assigned to &drm_driver.fops for vmalloc BO drivers.
- */
+#define DEFINE_DRM_VMALLOC_BO_FOPS(name) \
- static const struct file_operations name = {\
.owner = THIS_MODULE,\
.open = drm_open,\
.release = drm_release,\
.unlocked_ioctl = drm_ioctl,\
.compat_ioctl = drm_compat_ioctl,\
.poll = drm_poll,\
.read = drm_read,\
.llseek = noop_llseek,\
.mmap = drm_vmalloc_bo_mmap,\
- }
+extern const struct vm_operations_struct drm_vmalloc_bo_vm_ops;
+/**
- DRM_VMALLOC_BO_DRIVER_OPS - Default GEM and PRIME operations
- This macro provides a shortcut for setting the GEM and PRIME operations in
- the &drm_driver structure for vmalloc BO drivers.
- */
+#define DRM_VMALLOC_BO_DRIVER_OPS \
- .gem_free_object = drm_vmalloc_bo_free_object, \
- .gem_vm_ops = &drm_vmalloc_bo_vm_ops, \
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
- .gem_prime_import = drm_gem_prime_import, \
- .gem_prime_import_sg_table = drm_vmalloc_bo_prime_import_sg_table, \
- .dumb_create = drm_vmalloc_bo_dumb_create
+#endif /* __DRM_VMALLOC_BO_HELPER_H__ */
2.14.2
Use the vmalloc BO helper instead of the cma helper to be able to PRIME import from more drivers. The cma helper can only import physically continuous buffers, but tinydrm only requires the buffer to be virtually continuous.
This switch also makes it possible to use the drm_fb_helper_lastclose() helper.
Some extra includes were necessary in tinydrm-helpers.c, because it relied on includes in tinydrm.h that are now gone.
Cc: David Lechner david@lechnology.com Signed-off-by: Noralf Trønnes noralf@tronnes.org --- drivers/gpu/drm/tinydrm/Kconfig | 2 +- drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 134 +++++-------------------- drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 2 + drivers/gpu/drm/tinydrm/mi0283qt.c | 8 +- drivers/gpu/drm/tinydrm/mipi-dbi.c | 31 ++---- drivers/gpu/drm/tinydrm/repaper.c | 21 ++-- drivers/gpu/drm/tinydrm/st7586.c | 14 +-- include/drm/tinydrm/mipi-dbi.h | 2 + include/drm/tinydrm/tinydrm.h | 38 ++----- 9 files changed, 65 insertions(+), 187 deletions(-)
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index 2e790e7dced5..d47bcd6c8bfb 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -2,7 +2,7 @@ menuconfig DRM_TINYDRM tristate "Support for simple displays" depends on DRM select DRM_KMS_HELPER - select DRM_KMS_CMA_HELPER + select DRM_VMALLOC_BO_HELPER select BACKLIGHT_LCD_SUPPORT select BACKLIGHT_CLASS_DEVICE help diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 1a8a57cad431..77c5fd50af7a 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -10,7 +10,9 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> #include <drm/tinydrm/tinydrm.h> #include <linux/device.h> #include <linux/dma-buf.h> @@ -22,9 +24,11 @@ * * It is based on &drm_simple_display_pipe coupled with a &drm_connector which * has only one fixed &drm_display_mode. The framebuffers are backed by the - * cma helper and have support for framebuffer flushing (dirty). + * vmalloc BO helper and have support for framebuffer flushing (dirty). * fbdev support is also included. * + * Note: The SPI core is able to create a scatter/gather table from a vmalloc + * buffer when dealing with DMA capable SPI controllers */
/** @@ -35,94 +39,6 @@ * and registers the DRM device using devm_tinydrm_register(). */
-/** - * tinydrm_lastclose - DRM lastclose helper - * @drm: DRM device - * - * This function ensures that fbdev is restored when drm_lastclose() is called - * on the last drm_release(). Drivers can use this as their - * &drm_driver->lastclose callback. - */ -void tinydrm_lastclose(struct drm_device *drm) -{ - struct tinydrm_device *tdev = drm->dev_private; - - DRM_DEBUG_KMS("\n"); - drm_fbdev_cma_restore_mode(tdev->fbdev_cma); -} -EXPORT_SYMBOL(tinydrm_lastclose); - -/** - * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from - * another driver's scatter/gather table of pinned pages - * @drm: DRM device to import into - * @attach: DMA-BUF attachment - * @sgt: Scatter/gather table of pinned pages - * - * This function imports a scatter/gather table exported via DMA-BUF by - * another driver using drm_gem_cma_prime_import_sg_table(). It sets the - * kernel virtual address on the CMA object. Drivers should use this as their - * &drm_driver->gem_prime_import_sg_table callback if they need the virtual - * address. tinydrm_gem_cma_free_object() should be used in combination with - * this function. - * - * Returns: - * A pointer to a newly created GEM object or an ERR_PTR-encoded negative - * error code on failure. - */ -struct drm_gem_object * -tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm, - struct dma_buf_attachment *attach, - struct sg_table *sgt) -{ - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *obj; - void *vaddr; - - vaddr = dma_buf_vmap(attach->dmabuf); - if (!vaddr) { - DRM_ERROR("Failed to vmap PRIME buffer\n"); - return ERR_PTR(-ENOMEM); - } - - obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt); - if (IS_ERR(obj)) { - dma_buf_vunmap(attach->dmabuf, vaddr); - return obj; - } - - cma_obj = to_drm_gem_cma_obj(obj); - cma_obj->vaddr = vaddr; - - return obj; -} -EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table); - -/** - * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM - * object - * @gem_obj: GEM object to free - * - * This function frees the backing memory of the CMA GEM object, cleans up the - * GEM object state and frees the memory used to store the object itself using - * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel - * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers - * can use this as their &drm_driver->gem_free_object callback. - */ -void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj) -{ - if (gem_obj->import_attach) { - struct drm_gem_cma_object *cma_obj; - - cma_obj = to_drm_gem_cma_obj(gem_obj); - dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); - cma_obj->vaddr = NULL; - } - - drm_gem_cma_free_object(gem_obj); -} -EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object); - static struct drm_framebuffer * tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) @@ -211,38 +127,40 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev, } EXPORT_SYMBOL(devm_tinydrm_init);
+static int tinydrm_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) + +{ + struct tinydrm_device *tdev = helper->dev->dev_private; + + return drm_vmalloc_bo_fbdev_probe(helper, sizes, tdev->fb_funcs); +} + +static const struct drm_fb_helper_funcs tinydrm_fb_helper_funcs = { + .fb_probe = tinydrm_fbdev_probe, +}; + static int tinydrm_register(struct tinydrm_device *tdev) { struct drm_device *drm = tdev->drm; - int bpp = drm->mode_config.preferred_depth; - struct drm_fbdev_cma *fbdev; int ret;
- ret = drm_dev_register(tdev->drm, 0); + ret = drm_dev_register(drm, 0); if (ret) return ret;
- fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32, - drm->mode_config.num_connector, - tdev->fb_funcs); - if (IS_ERR(fbdev)) - DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev)); - else - tdev->fbdev_cma = fbdev; + ret = drm_fb_helper_simple_init(drm, &tinydrm_fb_helper_funcs, 0, 0); + if (ret) + DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
return 0; }
static void tinydrm_unregister(struct tinydrm_device *tdev) { - struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma; - drm_atomic_helper_shutdown(tdev->drm); - /* don't restore fbdev in lastclose, keep pipeline disabled */ - tdev->fbdev_cma = NULL; + drm_fb_helper_simple_fini(tdev->drm); drm_dev_unregister(tdev->drm); - if (fbdev_cma) - drm_fbdev_cma_fini(fbdev_cma); }
static void devm_tinydrm_register_release(void *data) @@ -312,10 +230,10 @@ int tinydrm_suspend(struct tinydrm_device *tdev) return -EINVAL; }
- drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1); + drm_fb_helper_set_suspend_unlocked(tdev->drm->fbdev, 1); state = drm_atomic_helper_suspend(tdev->drm); if (IS_ERR(state)) { - drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); + drm_fb_helper_set_suspend_unlocked(tdev->drm->fbdev, 0); return PTR_ERR(state); }
@@ -353,7 +271,7 @@ int tinydrm_resume(struct tinydrm_device *tdev) return ret; }
- drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); + drm_fb_helper_set_suspend_unlocked(tdev->drm->fbdev, 0);
return 0; } diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c index bd6cce093a85..ee9a8f305b26 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c @@ -9,10 +9,12 @@
#include <linux/backlight.h> #include <linux/dma-buf.h> +#include <linux/moduleparam.h> #include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/swab.h>
+#include <drm/drmP.h> #include <drm/tinydrm/tinydrm.h> #include <drm/tinydrm/tinydrm-helpers.h>
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index 6a83b3093254..19c8ef5cea78 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -9,6 +9,8 @@ * (at your option) any later version. */
+#include <drm/drm_fb_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> #include <drm/tinydrm/ili9341.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -132,14 +134,14 @@ static const struct drm_display_mode mi0283qt_mode = { TINYDRM_MODE(320, 240, 58, 43), };
-DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops); +DEFINE_DRM_VMALLOC_BO_FOPS(mi0283qt_fops);
static struct drm_driver mi0283qt_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &mi0283qt_fops, - TINYDRM_GEM_DRIVER_OPS, - .lastclose = tinydrm_lastclose, + DRM_VMALLOC_BO_DRIVER_OPS, + .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt", .desc = "Multi-Inno MI0283QT", diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index d43e992ab432..5f9252b05ba2 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -10,6 +10,7 @@ */
#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> #include <linux/debugfs.h> @@ -157,10 +158,10 @@ EXPORT_SYMBOL(mipi_dbi_command_buf); static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, struct drm_clip_rect *clip, bool swap) { - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); - struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; + struct drm_gem_object *obj = drm_gem_fb_get_obj(fb, 0); + struct dma_buf_attachment *import_attach = obj->import_attach; + void *src = drm_vmalloc_bo_fb_vaddr(fb); struct drm_format_name_buf format_name; - void *src = cma_obj->vaddr; int ret = 0;
if (import_attach) { @@ -199,7 +200,6 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_clip_rect *clips, unsigned int num_clips) { - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); struct tinydrm_device *tdev = fb->dev->dev_private; struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); bool swap = mipi->swap_bytes; @@ -230,7 +230,7 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, if (ret) goto out_unlock; } else { - tr = cma_obj->vaddr; + tr = drm_vmalloc_bo_fb_vaddr(fb); }
mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS, @@ -801,31 +801,12 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi, { size_t tx_size = tinydrm_spi_max_transfer_size(spi, 0); struct device *dev = &spi->dev; - int ret;
if (tx_size < 16) { DRM_ERROR("SPI transmit buffer too small: %zu\n", tx_size); return -EINVAL; }
- /* - * Even though it's not the SPI device that does DMA (the master does), - * the dma mask is necessary for the dma_alloc_wc() in - * drm_gem_cma_create(). The dma_addr returned will be a physical - * adddress which might be different from the bus address, but this is - * not a problem since the address will not be used. - * The virtual address is used in the transfer and the SPI core - * re-maps it on the SPI master device using the DMA streaming API - * (spi_map_buf()). - */ - if (!dev->coherent_dma_mask) { - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_warn(dev, "Failed to set dma mask %d\n", ret); - return ret; - } - } - mipi->spi = spi; mipi->read_commands = mipi_dbi_dcs_read_commands;
@@ -962,7 +943,7 @@ static const struct file_operations mipi_dbi_debugfs_command_fops = { };
static const struct drm_info_list mipi_dbi_debugfs_list[] = { - { "fb", drm_fb_cma_debugfs_show, 0 }, + { "fb", drm_gem_fb_debugfs_show, 0 }, };
/** diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c index 75740630c410..e449780cfd2e 100644 --- a/drivers/gpu/drm/tinydrm/repaper.c +++ b/drivers/gpu/drm/tinydrm/repaper.c @@ -26,6 +26,7 @@ #include <linux/spi/spi.h> #include <linux/thermal.h>
+#include <drm/drm_vmalloc_bo_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/tinydrm.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -526,10 +527,11 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, struct drm_clip_rect *clips, unsigned int num_clips) { - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); - struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; + struct drm_gem_object *obj = drm_gem_fb_get_obj(fb, 0); + struct dma_buf_attachment *import_attach = obj->import_attach; struct tinydrm_device *tdev = fb->dev->dev_private; struct repaper_epd *epd = epd_from_tinydrm(tdev); + void *src = drm_vmalloc_bo_fb_vaddr(fb); struct drm_clip_rect clip; u8 *buf = NULL; int ret = 0; @@ -567,7 +569,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb, goto out_unlock; }
- tinydrm_xrgb8888_to_gray8(buf, cma_obj->vaddr, fb, &clip); + tinydrm_xrgb8888_to_gray8(buf, src, fb, &clip);
if (import_attach) { ret = dma_buf_end_cpu_access(import_attach->dmabuf, @@ -887,13 +889,13 @@ static const struct drm_display_mode repaper_e2271cs021_mode = { static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00 };
-DEFINE_DRM_GEM_CMA_FOPS(repaper_fops); +DEFINE_DRM_VMALLOC_BO_FOPS(repaper_fops);
static struct drm_driver repaper_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &repaper_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_VMALLOC_BO_DRIVER_OPS, .name = "repaper", .desc = "Pervasive Displays RePaper e-ink panels", .date = "20170405", @@ -940,15 +942,6 @@ static int repaper_probe(struct spi_device *spi) model = spi_id->driver_data; }
- /* The SPI device is used to allocate dma memory */ - if (!dev->coherent_dma_mask) { - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_warn(dev, "Failed to set dma mask %d\n", ret); - return ret; - } - } - epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL); if (!epd) return -ENOMEM; diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index 0a2c60da5c0e..98657454587d 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -17,7 +17,9 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h>
+#include <drm/drm_fb_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h>
@@ -87,9 +89,9 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb, struct drm_clip_rect *clip) { - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); - struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; - void *src = cma_obj->vaddr; + struct drm_gem_object *obj = drm_gem_fb_get_obj(fb, 0); + struct dma_buf_attachment *import_attach = obj->import_attach; + void *src = drm_vmalloc_bo_fb_vaddr(fb); int ret = 0;
if (import_attach) { @@ -313,14 +315,14 @@ static const struct drm_display_mode st7586_mode = { TINYDRM_MODE(178, 128, 37, 27), };
-DEFINE_DRM_GEM_CMA_FOPS(st7586_fops); +DEFINE_DRM_VMALLOC_BO_FOPS(st7586_fops);
static struct drm_driver st7586_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &st7586_fops, - TINYDRM_GEM_DRIVER_OPS, - .lastclose = tinydrm_lastclose, + DRM_VMALLOC_BO_DRIVER_OPS, + .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586", .desc = "Sitronix ST7586", diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h index 83346ddb9dba..ff9626ea1c44 100644 --- a/include/drm/tinydrm/mipi-dbi.h +++ b/include/drm/tinydrm/mipi-dbi.h @@ -95,6 +95,8 @@ int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len); })
#ifdef CONFIG_DEBUG_FS +struct drm_minor; + int mipi_dbi_debugfs_init(struct drm_minor *minor); #else #define mipi_dbi_debugfs_init NULL diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h index 4774fe3d4273..8450d1faba66 100644 --- a/include/drm/tinydrm/tinydrm.h +++ b/include/drm/tinydrm/tinydrm.h @@ -10,16 +10,21 @@ #ifndef __LINUX_TINYDRM_H #define __LINUX_TINYDRM_H
-#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_fb_cma_helper.h> +#include <linux/mutex.h> #include <drm/drm_simple_kms_helper.h>
+struct device; +struct drm_atomic_state; +struct drm_device; +struct drm_driver; +struct drm_framebuffer_funcs; +struct drm_plane_state; + /** * struct tinydrm_device - tinydrm device * @drm: DRM device * @pipe: Display pipe structure * @dirty_lock: Serializes framebuffer flushing - * @fbdev_cma: CMA fbdev structure * @suspend_state: Atomic state when suspended * @fb_funcs: Framebuffer functions used when creating framebuffers */ @@ -27,7 +32,6 @@ struct tinydrm_device { struct drm_device *drm; struct drm_simple_display_pipe pipe; struct mutex dirty_lock; - struct drm_fbdev_cma *fbdev_cma; struct drm_atomic_state *suspend_state; const struct drm_framebuffer_funcs *fb_funcs; }; @@ -38,26 +42,6 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe) return container_of(pipe, struct tinydrm_device, pipe); }
-/** - * TINYDRM_GEM_DRIVER_OPS - default tinydrm gem operations - * - * This macro provides a shortcut for setting the tinydrm GEM operations in - * the &drm_driver structure. - */ -#define TINYDRM_GEM_DRIVER_OPS \ - .gem_free_object = tinydrm_gem_cma_free_object, \ - .gem_vm_ops = &drm_gem_cma_vm_ops, \ - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import = drm_gem_prime_import, \ - .gem_prime_export = drm_gem_prime_export, \ - .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, \ - .gem_prime_import_sg_table = tinydrm_gem_cma_prime_import_sg_table, \ - .gem_prime_vmap = drm_gem_cma_prime_vmap, \ - .gem_prime_vunmap = drm_gem_cma_prime_vunmap, \ - .gem_prime_mmap = drm_gem_cma_prime_mmap, \ - .dumb_create = drm_gem_cma_dumb_create - /** * TINYDRM_MODE - tinydrm display mode * @hd: Horizontal resolution, width @@ -81,12 +65,6 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe) .type = DRM_MODE_TYPE_DRIVER, \ .clock = 1 /* pass validation */
-void tinydrm_lastclose(struct drm_device *drm); -void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj); -struct drm_gem_object * -tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm, - struct dma_buf_attachment *attach, - struct sg_table *sgt); int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev, const struct drm_framebuffer_funcs *fb_funcs, struct drm_driver *driver);
vmalloc BO's gives us cached reads, so no need to prefetch in that case. Prefetching gives a ~20% speedup on a cma buffer using the mi0283qt driver on a Raspberry Pi 1.
Signed-off-by: Noralf Trønnes noralf@tronnes.org --- drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 54 ++++++++++++++------------ 1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c index ee9a8f305b26..bca905213cdd 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c @@ -15,6 +15,8 @@ #include <linux/swab.h>
#include <drm/drmP.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/tinydrm.h> #include <drm/tinydrm/tinydrm-helpers.h>
@@ -115,22 +117,25 @@ void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_clip_rect *clip) { size_t len = (clip->x2 - clip->x1) * sizeof(u16); + u16 *src, *buf = NULL; unsigned int x, y; - u16 *src, *buf;
/* - * The cma memory is write-combined so reads are uncached. - * Speed up by fetching one line at a time. + * Imported buffers are likely to be write-combined with uncached + * reads. Speed up by fetching one line at a time. + * prefetch_range() was tried, but didn't give any noticeable speedup + * on the Raspberry Pi 1. */ - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return; + if (drm_gem_fb_get_obj(fb, 0)->import_attach) + buf = kmalloc(len, GFP_KERNEL);
for (y = clip->y1; y < clip->y2; y++) { src = vaddr + (y * fb->pitches[0]); src += clip->x1; - memcpy(buf, src, len); - src = buf; + if (buf) { + memcpy(buf, src, len); + src = buf; + } for (x = clip->x1; x < clip->x2; x++) *dst++ = swab16(*src++); } @@ -155,19 +160,21 @@ void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, struct drm_clip_rect *clip, bool swap) { size_t len = (clip->x2 - clip->x1) * sizeof(u32); + u32 *src, *buf = NULL; unsigned int x, y; - u32 *src, *buf; u16 val16;
- buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return; + /* See tinydrm_swab16() for an explanation */ + if (drm_gem_fb_get_obj(fb, 0)->import_attach) + buf = kmalloc(len, GFP_KERNEL);
for (y = clip->y1; y < clip->y2; y++) { src = vaddr + (y * fb->pitches[0]); src += clip->x1; - memcpy(buf, src, len); - src = buf; + if (buf) { + memcpy(buf, src, len); + src = buf; + } for (x = clip->x1; x < clip->x2; x++) { val16 = ((*src & 0x00F80000) >> 8) | ((*src & 0x0000FC00) >> 5) | @@ -205,24 +212,23 @@ void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, { unsigned int len = (clip->x2 - clip->x1) * sizeof(u32); unsigned int x, y; - void *buf; + void *buf = NULL; u32 *src;
if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) return; - /* - * The cma memory is write-combined so reads are uncached. - * Speed up by fetching one line at a time. - */ - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return; + + /* See tinydrm_swab16() for an explanation */ + if (drm_gem_fb_get_obj(fb, 0)->import_attach) + buf = kmalloc(len, GFP_KERNEL);
for (y = clip->y1; y < clip->y2; y++) { src = vaddr + (y * fb->pitches[0]); src += clip->x1; - memcpy(buf, src, len); - src = buf; + if (buf) { + memcpy(buf, src, len); + src = buf; + } for (x = clip->x1; x < clip->x2; x++) { u8 r = (*src & 0x00ff0000) >> 16; u8 g = (*src & 0x0000ff00) >> 8;
Thanks for the updates! I was comparing between your code and and evdi code (eventually meant for usb displaylink), and it looks pretty similar in concept. They have some hard-coding around kernel versions for specific drm interfaces, and I can't find enough documentation to figure out if any differences matter, but thought I would pass it on to see if it is of any help: https://github.com/DisplayLink/evdi
On Sun, Oct 15, 2017 at 12:30 PM, Noralf Trønnes noralf@tronnes.org wrote:
This patchset adds a library for vmalloc buffer objects and makes use of it in tinydrm.
The reason I want to move away from the cma helper, is that it restricts which drivers to PRIME import from, since cma requires the buffer to be physically continuous. Initially I looked at udl and decided to use shmem, but have later decided against it since shmem doesn't work with fbdev deferred I/O, they both use page->lru and page->mapping. This meant adding a shadow buffer for fbdev with increased complexity, so I fell back to the KISS principle.
I'm trying to make tinydrm support drivers like simpledrm and udl, so if using vmalloc blocks those use cases, please let me know.
I will do a follow up for the cma library which removes struct drm_fbdev_cma and wrappers.
Noralf.
Changes since version 1:
- tinydrm: Remove dma_coerce_mask_and_coherent() calls (Matt Gattis)
Noralf Trønnes (8): drm/fb-helper: Handle function NULL argument drm: Add drm_device->fbdev pointer drm/fb-helper: Add simple init/fini functions drm/fb-helper: Add .last_close and .output_poll_changed helpers drm/gem-fb-helper: Add drm_gem_fb_debugfs_show() drm: Add vmalloc BO helper drm/tinydrm: Use drm_vmalloc_bo drm/tinydrm: Relax buffer line prefetch
Documentation/gpu/drm-kms-helpers.rst | 12 + drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fb_helper.c | 223 +++++++++++++++++- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 45 ++++ drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 +++++++++++++++++++++++++ drivers/gpu/drm/tinydrm/Kconfig | 2 +- drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 134 +++-------- drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 56 +++-- drivers/gpu/drm/tinydrm/mi0283qt.c | 8 +- drivers/gpu/drm/tinydrm/mipi-dbi.c | 31 +-- drivers/gpu/drm/tinydrm/repaper.c | 21 +- drivers/gpu/drm/tinydrm/st7586.c | 14 +- include/drm/drm_device.h | 8 + include/drm/drm_fb_helper.h | 32 +++ include/drm/drm_gem_framebuffer_helper.h | 6 + include/drm/drm_vmalloc_bo_helper.h | 88 +++++++ include/drm/tinydrm/mipi-dbi.h | 2 + include/drm/tinydrm/tinydrm.h | 38 +-- 19 files changed, 811 insertions(+), 222 deletions(-) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h
-- 2.14.2
dri-devel@lists.freedesktop.org