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
@ -30,6 +30,7 @@
|
||||
#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>
|
||||
@ -738,6 +739,24 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
|
||||
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)
|
||||
{
|
||||
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
|
||||
@ -753,8 +772,12 @@ static void drm_fb_helper_dirty_work(struct work_struct *work)
|
||||
spin_unlock_irqrestore(&helper->dirty_lock, flags);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2921,6 +2944,180 @@ 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
* 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.
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
struct drm_fb_helper;
|
||||
|
||||
#include <drm/drm_client.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <linux/kgdb.h>
|
||||
@ -154,6 +155,20 @@ struct drm_fb_helper_connector {
|
||||
* operations.
|
||||
*/
|
||||
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_device *dev;
|
||||
int crtc_count;
|
||||
@ -234,6 +249,12 @@ struct drm_fb_helper {
|
||||
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
|
||||
*
|
||||
@ -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_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 +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
|
||||
|
||||
static inline int
|
||||
|
Loading…
Reference in New Issue
Block a user