[PATCH 5/9] drm/fb-helper: Add generic fbdev emulation .fb_probe function

Noralf Trønnes noralf at tronnes.org
Fri May 25 12:42:02 UTC 2018


Den 24.05.2018 11.16, skrev Daniel Vetter:
> On Wed, May 23, 2018 at 04:34:07PM +0200, Noralf Trønnes wrote:
>> This is the first step in getting generic fbdev emulation.
>> A drm_fb_helper_funcs.fb_probe function is added which uses the
>> DRM client API to get a framebuffer backed by a dumb buffer.
>>
>> A transitional hack for tinydrm is needed in order to switch over all
>> CMA helper drivers in a later patch. This hack will be removed when
>> tinydrm moves to vmalloc buffers.
>>
>> Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
>> ---
>>   drivers/gpu/drm/drm_fb_helper.c | 164 ++++++++++++++++++++++++++++++++++++++++
>>   include/drm/drm_fb_helper.h     |  26 +++++++
>>   2 files changed, 190 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 2ee1eaa66188..444c2b4040ea 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -30,11 +30,13 @@
>>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>   
>>   #include <linux/console.h>
>> +#include <linux/dma-buf.h>
>>   #include <linux/kernel.h>
>>   #include <linux/sysrq.h>
>>   #include <linux/slab.h>
>>   #include <linux/module.h>
>>   #include <drm/drmP.h>
>> +#include <drm/drm_client.h>
>>   #include <drm/drm_crtc.h>
>>   #include <drm/drm_fb_helper.h>
>>   #include <drm/drm_crtc_helper.h>
>> @@ -2928,6 +2930,168 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
>>   }
>>   EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
>>   
>> +/* @user: 1=userspace, 0=fbcon */
>> +static int drm_fbdev_fb_open(struct fb_info *info, int user)
>> +{
>> +	struct drm_fb_helper *fb_helper = info->par;
>> +
>> +	if (!try_module_get(fb_helper->dev->driver->fops->owner))
>> +		return -ENODEV;
>> +
>> +	return 0;
>> +}
>> +
>> +static int drm_fbdev_fb_release(struct fb_info *info, int user)
>> +{
>> +	struct drm_fb_helper *fb_helper = info->par;
>> +
>> +	module_put(fb_helper->dev->driver->fops->owner);
>> +
>> +	return 0;
>> +}
> Hm, I thought earlier versions of your patch series had this separately,
> for everyone. What's the reasons for merging this into the fb_probe
> implementation.

This is only necessary when struct fb_ops is defined in a library where
the owner field is the library module and not the driver module.
I realised that with this generic version it's highly unlikely that we
get another library that defines struct fb_ops, so it's only needed here.

Noralf.

>> +
>> +/*
>> + * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
>> + * unregister_framebuffer() or fb_release().
>> + */
>> +static void drm_fbdev_fb_destroy(struct fb_info *info)
>> +{
>> +	struct drm_fb_helper *fb_helper = info->par;
>> +	struct fb_ops *fbops = NULL;
>> +
>> +	DRM_DEBUG("\n");
>> +
>> +	if (fb_helper->fbdev->fbdefio) {
>> +		fb_deferred_io_cleanup(fb_helper->fbdev);
>> +		fbops = fb_helper->fbdev->fbops;
>> +	}
>> +
>> +	drm_fb_helper_fini(fb_helper);
>> +	drm_client_framebuffer_delete(fb_helper->buffer);
>> +	drm_client_free(fb_helper->client);
>> +	kfree(fb_helper);
>> +	kfree(fbops);
>> +}
> Hm, if we go with the idea that drm_clients could auto-unregister through
> a callback, then I expect this will lead to some control inversion. But we
> can fix that later on.
>
>> +
>> +static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
>> +{
>> +	struct drm_fb_helper *fb_helper = info->par;
>> +
>> +	if (fb_helper->dev->driver->gem_prime_mmap)
>> +		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
>> +	else
>> +		return -ENODEV;
>> +}
>> +
>> +static struct fb_ops drm_fbdev_fb_ops = {
>> +	/* No need to set owner, this module is already pinned by the driver. */
> I'd still set it, means less thinking since more obviously correct.
>
>> +	DRM_FB_HELPER_DEFAULT_OPS,
>> +	.fb_open	= drm_fbdev_fb_open,
>> +	.fb_release	= drm_fbdev_fb_release,
>> +	.fb_destroy	= drm_fbdev_fb_destroy,
>> +	.fb_mmap	= drm_fbdev_fb_mmap,
>> +	.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,
> Hm, some drivers require the cfb versions of these. In practice I guess
> there's not much of a difference really, at least on x86 and arm.
>
> We might want to document that though.
>
>> +};
>> +
>> +static struct fb_deferred_io drm_fbdev_defio = {
>> +	.delay		= HZ / 20,
>> +	.deferred_io	= drm_fb_helper_deferred_io,
>> +};
>> +
>> +/*
>> + * TODO: Remove this when tinydrm is converted to vmalloc buffers.
>> + */
>> +static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info,
>> +					  struct vm_area_struct *vma)
>> +{
>> +	fb_deferred_io_mmap(info, vma);
>> +	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
>> +
>> +	return 0;
>> +}
>> +
> Needs kerneldoc here.
>
>> +int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
>> +				struct drm_fb_helper_surface_size *sizes)
>> +{
>> +	struct drm_client_dev *client = fb_helper->client;
>> +	struct drm_client_buffer *buffer;
>> +	struct drm_framebuffer *fb;
>> +	struct fb_info *fbi;
>> +	u32 format;
>> +	int ret;
>> +
>> +	DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
>> +		      sizes->surface_width, sizes->surface_height,
>> +		      sizes->surface_bpp);
>> +
>> +	format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
>> +	buffer = drm_client_framebuffer_create(client, sizes->surface_width,
>> +					       sizes->surface_height, format);
>> +	if (IS_ERR(buffer))
>> +		return PTR_ERR(buffer);
>> +
>> +	fb_helper->buffer = buffer;
>> +	fb_helper->fb = buffer->fb;
>> +	fb = buffer->fb;
>> +
>> +	fbi = drm_fb_helper_alloc_fbi(fb_helper);
>> +	if (IS_ERR(fbi)) {
>> +		ret = PTR_ERR(fbi);
>> +		goto err_free_buffer;
>> +	}
>> +
>> +	fbi->par = fb_helper;
>> +	fbi->fbops = &drm_fbdev_fb_ops;
>> +	fbi->screen_size = fb->height * fb->pitches[0];
>> +	fbi->fix.smem_len = fbi->screen_size;
>> +	fbi->screen_buffer = buffer->vaddr;
>> +	strcpy(fbi->fix.id, "DRM emulated");
>> +
>> +	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);
>> +
>> +	if (fb->funcs->dirty) {
>> +		struct fb_ops *fbops;
>> +
>> +		/*
>> +		 * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
>> +		 * instance version is necessary.
>> +		 */
>> +		fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
>> +		if (!fbops) {
>> +			ret = -ENOMEM;
>> +			goto err_fb_info_destroy;
>> +		}
>> +
>> +		*fbops = *fbi->fbops;
>> +		fbi->fbops = fbops;
>> +
>> +		fbi->fbdefio = &drm_fbdev_defio;
>> +
>> +		/* TODO: Remove this when tinydrm is converted to vmalloc buffers. */
>> +		fbi->fix.smem_start = page_to_phys(virt_to_page(buffer->vaddr));
>> +
>> +		fb_deferred_io_init(fbi);
>> +
>> +		/* TODO: Remove this when tinydrm is converted to vmalloc buffers. */
>> +		fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap;
>> +	}
> Ugh. Yeah defio and generic allocator through dumb buffers don't mix well.
> The only true generic solution for this would be to give userspace (and
> only userspace, for fbcon we can intercept everything) a staging buffer,
> and then upload things using the dirty callback.
>
> But that introduces another copy step, so isn't cool.
>
> I think a check for is_vmalloc_addr and if that's false, not doing any of
> the defio mmap setup would be good. Until we have a better idea. And yes
> that would need to be done after tinydrm is moved over.
>
>> +
>> +	return 0;
>> +
>> +err_fb_info_destroy:
>> +	drm_fb_helper_fini(fb_helper);
>> +err_free_buffer:
>> +	drm_client_framebuffer_delete(buffer);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(drm_fb_helper_generic_probe);
>> +
>>   /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
>>    * but the module doesn't depend on any fb console symbols.  At least
>>    * attempt to load fbcon to avoid leaving the system without a usable console.
>> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
>> index b069433e7fc1..bb38469a9502 100644
>> --- a/include/drm/drm_fb_helper.h
>> +++ b/include/drm/drm_fb_helper.h
>> @@ -30,6 +30,8 @@
>>   #ifndef DRM_FB_HELPER_H
>>   #define DRM_FB_HELPER_H
>>   
>> +struct drm_client_buffer;
>> +struct drm_client_dev;
>>   struct drm_fb_helper;
>>   
>>   #include <drm/drm_crtc.h>
>> @@ -232,6 +234,20 @@ struct drm_fb_helper {
>>   	 * See also: @deferred_setup
>>   	 */
>>   	int preferred_bpp;
>> +
>> +	/**
>> +	 * @client:
>> +	 *
>> +	 * DRM client used by the generic fbdev emulation.
>> +	 */
>> +	struct drm_client_dev *client;
>> +
>> +	/**
>> +	 * @buffer:
>> +	 *
>> +	 * Framebuffer used by the generic fbdev emulation.
>> +	 */
>> +	struct drm_client_buffer *buffer;
>>   };
>>   
>>   /**
>> @@ -330,6 +346,9 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
>>   
>>   void drm_fb_helper_lastclose(struct drm_device *dev);
>>   void drm_fb_helper_output_poll_changed(struct drm_device *dev);
>> +
>> +int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
>> +				struct drm_fb_helper_surface_size *sizes);
>>   #else
>>   static inline void drm_fb_helper_prepare(struct drm_device *dev,
>>   					struct drm_fb_helper *helper,
>> @@ -564,6 +583,13 @@ static inline void drm_fb_helper_output_poll_changed(struct drm_device *dev)
>>   {
>>   }
>>   
>> +static inline int
>> +drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
>> +			    struct drm_fb_helper_surface_size *sizes)
>> +{
>> +	return 0;
>> +}
> Ok, I think this patch looks ok. With the missing kerneldoc added (which
> also explains the current limitations) this is
>
> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
>
>> +
>>   #endif
>>   
>>   static inline int
>> -- 
>> 2.15.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel



More information about the dri-devel mailing list