forked from mirrors/linux
		
	drm/fb-helper: Add generic fbdev emulation .fb_probe function
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. Signed-off-by: Noralf Trønnes <noralf@tronnes.org> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-3-noralf@tronnes.org
This commit is contained in:
		
							parent
							
								
									c76f0f7cb5
								
							
						
					
					
						commit
						d536540f30
					
				
					 2 changed files with 229 additions and 1 deletions
				
			
		| 
						 | 
					@ -30,6 +30,7 @@
 | 
				
			||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
					#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/console.h>
 | 
					#include <linux/console.h>
 | 
				
			||||||
 | 
					#include <linux/dma-buf.h>
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/sysrq.h>
 | 
					#include <linux/sysrq.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
| 
						 | 
					@ -738,6 +739,24 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
 | 
				
			||||||
	console_unlock();
 | 
						console_unlock();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
 | 
				
			||||||
 | 
										  struct drm_clip_rect *clip)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_framebuffer *fb = fb_helper->fb;
 | 
				
			||||||
 | 
						unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
 | 
				
			||||||
 | 
						size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp;
 | 
				
			||||||
 | 
						void *src = fb_helper->fbdev->screen_buffer + offset;
 | 
				
			||||||
 | 
						void *dst = fb_helper->buffer->vaddr + offset;
 | 
				
			||||||
 | 
						size_t len = (clip->x2 - clip->x1) * cpp;
 | 
				
			||||||
 | 
						unsigned int y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (y = clip->y1; y < clip->y2; y++) {
 | 
				
			||||||
 | 
							memcpy(dst, src, len);
 | 
				
			||||||
 | 
							src += fb->pitches[0];
 | 
				
			||||||
 | 
							dst += fb->pitches[0];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void drm_fb_helper_dirty_work(struct work_struct *work)
 | 
					static void drm_fb_helper_dirty_work(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
 | 
						struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
 | 
				
			||||||
| 
						 | 
					@ -753,9 +772,13 @@ static void drm_fb_helper_dirty_work(struct work_struct *work)
 | 
				
			||||||
	spin_unlock_irqrestore(&helper->dirty_lock, flags);
 | 
						spin_unlock_irqrestore(&helper->dirty_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* call dirty callback only when it has been really touched */
 | 
						/* call dirty callback only when it has been really touched */
 | 
				
			||||||
	if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
 | 
						if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
 | 
				
			||||||
 | 
							/* Generic fbdev uses a shadow buffer */
 | 
				
			||||||
 | 
							if (helper->buffer)
 | 
				
			||||||
 | 
								drm_fb_helper_dirty_blit_real(helper, &clip_copy);
 | 
				
			||||||
		helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
 | 
							helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * drm_fb_helper_prepare - setup a drm_fb_helper structure
 | 
					 * drm_fb_helper_prepare - setup a drm_fb_helper structure
 | 
				
			||||||
| 
						 | 
					@ -2921,6 +2944,180 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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_info *fbi = fb_helper->fbdev;
 | 
				
			||||||
 | 
						struct fb_ops *fbops = NULL;
 | 
				
			||||||
 | 
						void *shadow = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fbi->fbdefio) {
 | 
				
			||||||
 | 
							fb_deferred_io_cleanup(fbi);
 | 
				
			||||||
 | 
							shadow = fbi->screen_buffer;
 | 
				
			||||||
 | 
							fbops = fbi->fbops;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_fb_helper_fini(fb_helper);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (shadow) {
 | 
				
			||||||
 | 
							vfree(shadow);
 | 
				
			||||||
 | 
							kfree(fbops);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_client_framebuffer_delete(fb_helper->buffer);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * FIXME:
 | 
				
			||||||
 | 
						 * Remove conditional when all CMA drivers have been moved over to using
 | 
				
			||||||
 | 
						 * drm_fbdev_generic_setup().
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (fb_helper->client.funcs) {
 | 
				
			||||||
 | 
							drm_client_release(&fb_helper->client);
 | 
				
			||||||
 | 
							kfree(fb_helper);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 = {
 | 
				
			||||||
 | 
						.owner		= THIS_MODULE,
 | 
				
			||||||
 | 
						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,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct fb_deferred_io drm_fbdev_defio = {
 | 
				
			||||||
 | 
						.delay		= HZ / 20,
 | 
				
			||||||
 | 
						.deferred_io	= drm_fb_helper_deferred_io,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * drm_fb_helper_generic_probe - Generic fbdev emulation probe helper
 | 
				
			||||||
 | 
					 * @fb_helper: fbdev helper structure
 | 
				
			||||||
 | 
					 * @sizes: describes fbdev size and scanout surface size
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function uses the client API to crate a framebuffer backed by a dumb buffer.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect,
 | 
				
			||||||
 | 
					 * fb_copyarea, fb_imageblit.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns:
 | 
				
			||||||
 | 
					 * Zero on success or negative error code on failure.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
							void *shadow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
 | 
				
			||||||
 | 
							 * instance version is necessary.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
 | 
				
			||||||
 | 
							shadow = vzalloc(fbi->screen_size);
 | 
				
			||||||
 | 
							if (!fbops || !shadow) {
 | 
				
			||||||
 | 
								kfree(fbops);
 | 
				
			||||||
 | 
								vfree(shadow);
 | 
				
			||||||
 | 
								ret = -ENOMEM;
 | 
				
			||||||
 | 
								goto err_fb_info_destroy;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*fbops = *fbi->fbops;
 | 
				
			||||||
 | 
							fbi->fbops = fbops;
 | 
				
			||||||
 | 
							fbi->screen_buffer = shadow;
 | 
				
			||||||
 | 
							fbi->fbdefio = &drm_fbdev_defio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fb_deferred_io_init(fbi);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
					/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
 | 
				
			||||||
 * but the module doesn't depend on any fb console symbols.  At least
 | 
					 * 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.
 | 
					 * attempt to load fbcon to avoid leaving the system without a usable console.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct drm_fb_helper;
 | 
					struct drm_fb_helper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <drm/drm_client.h>
 | 
				
			||||||
#include <drm/drm_crtc.h>
 | 
					#include <drm/drm_crtc.h>
 | 
				
			||||||
#include <drm/drm_device.h>
 | 
					#include <drm/drm_device.h>
 | 
				
			||||||
#include <linux/kgdb.h>
 | 
					#include <linux/kgdb.h>
 | 
				
			||||||
| 
						 | 
					@ -154,6 +155,20 @@ struct drm_fb_helper_connector {
 | 
				
			||||||
 * operations.
 | 
					 * operations.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct drm_fb_helper {
 | 
					struct drm_fb_helper {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct drm_framebuffer *fb;
 | 
						struct drm_framebuffer *fb;
 | 
				
			||||||
	struct drm_device *dev;
 | 
						struct drm_device *dev;
 | 
				
			||||||
	int crtc_count;
 | 
						int crtc_count;
 | 
				
			||||||
| 
						 | 
					@ -234,6 +249,12 @@ struct drm_fb_helper {
 | 
				
			||||||
	int preferred_bpp;
 | 
						int preferred_bpp;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct drm_fb_helper *
 | 
				
			||||||
 | 
					drm_fb_helper_from_client(struct drm_client_dev *client)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return container_of(client, struct drm_fb_helper, client);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers
 | 
					 * define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -330,6 +351,9 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void drm_fb_helper_lastclose(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_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
 | 
					#else
 | 
				
			||||||
static inline void drm_fb_helper_prepare(struct drm_device *dev,
 | 
					static inline void drm_fb_helper_prepare(struct drm_device *dev,
 | 
				
			||||||
					struct drm_fb_helper *helper,
 | 
										struct drm_fb_helper *helper,
 | 
				
			||||||
| 
						 | 
					@ -564,6 +588,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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int
 | 
					static inline int
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue