Skip to content

Commit d536540

Browse files
committed
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 <[email protected]> Reviewed-by: Daniel Vetter <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent c76f0f7 commit d536540

File tree

2 files changed

+229
-1
lines changed

2 files changed

+229
-1
lines changed

drivers/gpu/drm/drm_fb_helper.c

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3131

3232
#include <linux/console.h>
33+
#include <linux/dma-buf.h>
3334
#include <linux/kernel.h>
3435
#include <linux/sysrq.h>
3536
#include <linux/slab.h>
@@ -738,6 +739,24 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
738739
console_unlock();
739740
}
740741

742+
static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
743+
struct drm_clip_rect *clip)
744+
{
745+
struct drm_framebuffer *fb = fb_helper->fb;
746+
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
747+
size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp;
748+
void *src = fb_helper->fbdev->screen_buffer + offset;
749+
void *dst = fb_helper->buffer->vaddr + offset;
750+
size_t len = (clip->x2 - clip->x1) * cpp;
751+
unsigned int y;
752+
753+
for (y = clip->y1; y < clip->y2; y++) {
754+
memcpy(dst, src, len);
755+
src += fb->pitches[0];
756+
dst += fb->pitches[0];
757+
}
758+
}
759+
741760
static void drm_fb_helper_dirty_work(struct work_struct *work)
742761
{
743762
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)
753772
spin_unlock_irqrestore(&helper->dirty_lock, flags);
754773

755774
/* call dirty callback only when it has been really touched */
756-
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
775+
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
776+
/* Generic fbdev uses a shadow buffer */
777+
if (helper->buffer)
778+
drm_fb_helper_dirty_blit_real(helper, &clip_copy);
757779
helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
780+
}
758781
}
759782

760783
/**
@@ -2921,6 +2944,180 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
29212944
}
29222945
EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
29232946

2947+
/* @user: 1=userspace, 0=fbcon */
2948+
static int drm_fbdev_fb_open(struct fb_info *info, int user)
2949+
{
2950+
struct drm_fb_helper *fb_helper = info->par;
2951+
2952+
if (!try_module_get(fb_helper->dev->driver->fops->owner))
2953+
return -ENODEV;
2954+
2955+
return 0;
2956+
}
2957+
2958+
static int drm_fbdev_fb_release(struct fb_info *info, int user)
2959+
{
2960+
struct drm_fb_helper *fb_helper = info->par;
2961+
2962+
module_put(fb_helper->dev->driver->fops->owner);
2963+
2964+
return 0;
2965+
}
2966+
2967+
/*
2968+
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
2969+
* unregister_framebuffer() or fb_release().
2970+
*/
2971+
static void drm_fbdev_fb_destroy(struct fb_info *info)
2972+
{
2973+
struct drm_fb_helper *fb_helper = info->par;
2974+
struct fb_info *fbi = fb_helper->fbdev;
2975+
struct fb_ops *fbops = NULL;
2976+
void *shadow = NULL;
2977+
2978+
if (fbi->fbdefio) {
2979+
fb_deferred_io_cleanup(fbi);
2980+
shadow = fbi->screen_buffer;
2981+
fbops = fbi->fbops;
2982+
}
2983+
2984+
drm_fb_helper_fini(fb_helper);
2985+
2986+
if (shadow) {
2987+
vfree(shadow);
2988+
kfree(fbops);
2989+
}
2990+
2991+
drm_client_framebuffer_delete(fb_helper->buffer);
2992+
/*
2993+
* FIXME:
2994+
* Remove conditional when all CMA drivers have been moved over to using
2995+
* drm_fbdev_generic_setup().
2996+
*/
2997+
if (fb_helper->client.funcs) {
2998+
drm_client_release(&fb_helper->client);
2999+
kfree(fb_helper);
3000+
}
3001+
}
3002+
3003+
static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
3004+
{
3005+
struct drm_fb_helper *fb_helper = info->par;
3006+
3007+
if (fb_helper->dev->driver->gem_prime_mmap)
3008+
return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
3009+
else
3010+
return -ENODEV;
3011+
}
3012+
3013+
static struct fb_ops drm_fbdev_fb_ops = {
3014+
.owner = THIS_MODULE,
3015+
DRM_FB_HELPER_DEFAULT_OPS,
3016+
.fb_open = drm_fbdev_fb_open,
3017+
.fb_release = drm_fbdev_fb_release,
3018+
.fb_destroy = drm_fbdev_fb_destroy,
3019+
.fb_mmap = drm_fbdev_fb_mmap,
3020+
.fb_read = drm_fb_helper_sys_read,
3021+
.fb_write = drm_fb_helper_sys_write,
3022+
.fb_fillrect = drm_fb_helper_sys_fillrect,
3023+
.fb_copyarea = drm_fb_helper_sys_copyarea,
3024+
.fb_imageblit = drm_fb_helper_sys_imageblit,
3025+
};
3026+
3027+
static struct fb_deferred_io drm_fbdev_defio = {
3028+
.delay = HZ / 20,
3029+
.deferred_io = drm_fb_helper_deferred_io,
3030+
};
3031+
3032+
/**
3033+
* drm_fb_helper_generic_probe - Generic fbdev emulation probe helper
3034+
* @fb_helper: fbdev helper structure
3035+
* @sizes: describes fbdev size and scanout surface size
3036+
*
3037+
* This function uses the client API to crate a framebuffer backed by a dumb buffer.
3038+
*
3039+
* The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect,
3040+
* fb_copyarea, fb_imageblit.
3041+
*
3042+
* Returns:
3043+
* Zero on success or negative error code on failure.
3044+
*/
3045+
int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
3046+
struct drm_fb_helper_surface_size *sizes)
3047+
{
3048+
struct drm_client_dev *client = &fb_helper->client;
3049+
struct drm_client_buffer *buffer;
3050+
struct drm_framebuffer *fb;
3051+
struct fb_info *fbi;
3052+
u32 format;
3053+
int ret;
3054+
3055+
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
3056+
sizes->surface_width, sizes->surface_height,
3057+
sizes->surface_bpp);
3058+
3059+
format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
3060+
buffer = drm_client_framebuffer_create(client, sizes->surface_width,
3061+
sizes->surface_height, format);
3062+
if (IS_ERR(buffer))
3063+
return PTR_ERR(buffer);
3064+
3065+
fb_helper->buffer = buffer;
3066+
fb_helper->fb = buffer->fb;
3067+
fb = buffer->fb;
3068+
3069+
fbi = drm_fb_helper_alloc_fbi(fb_helper);
3070+
if (IS_ERR(fbi)) {
3071+
ret = PTR_ERR(fbi);
3072+
goto err_free_buffer;
3073+
}
3074+
3075+
fbi->par = fb_helper;
3076+
fbi->fbops = &drm_fbdev_fb_ops;
3077+
fbi->screen_size = fb->height * fb->pitches[0];
3078+
fbi->fix.smem_len = fbi->screen_size;
3079+
fbi->screen_buffer = buffer->vaddr;
3080+
strcpy(fbi->fix.id, "DRM emulated");
3081+
3082+
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
3083+
drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
3084+
3085+
if (fb->funcs->dirty) {
3086+
struct fb_ops *fbops;
3087+
void *shadow;
3088+
3089+
/*
3090+
* fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
3091+
* instance version is necessary.
3092+
*/
3093+
fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
3094+
shadow = vzalloc(fbi->screen_size);
3095+
if (!fbops || !shadow) {
3096+
kfree(fbops);
3097+
vfree(shadow);
3098+
ret = -ENOMEM;
3099+
goto err_fb_info_destroy;
3100+
}
3101+
3102+
*fbops = *fbi->fbops;
3103+
fbi->fbops = fbops;
3104+
fbi->screen_buffer = shadow;
3105+
fbi->fbdefio = &drm_fbdev_defio;
3106+
3107+
fb_deferred_io_init(fbi);
3108+
}
3109+
3110+
return 0;
3111+
3112+
err_fb_info_destroy:
3113+
drm_fb_helper_fini(fb_helper);
3114+
err_free_buffer:
3115+
drm_client_framebuffer_delete(buffer);
3116+
3117+
return ret;
3118+
}
3119+
EXPORT_SYMBOL(drm_fb_helper_generic_probe);
3120+
29243121
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
29253122
* but the module doesn't depend on any fb console symbols. At least
29263123
* attempt to load fbcon to avoid leaving the system without a usable console.

include/drm/drm_fb_helper.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
struct drm_fb_helper;
3434

35+
#include <drm/drm_client.h>
3536
#include <drm/drm_crtc.h>
3637
#include <drm/drm_device.h>
3738
#include <linux/kgdb.h>
@@ -154,6 +155,20 @@ struct drm_fb_helper_connector {
154155
* operations.
155156
*/
156157
struct drm_fb_helper {
158+
/**
159+
* @client:
160+
*
161+
* DRM client used by the generic fbdev emulation.
162+
*/
163+
struct drm_client_dev client;
164+
165+
/**
166+
* @buffer:
167+
*
168+
* Framebuffer used by the generic fbdev emulation.
169+
*/
170+
struct drm_client_buffer *buffer;
171+
157172
struct drm_framebuffer *fb;
158173
struct drm_device *dev;
159174
int crtc_count;
@@ -234,6 +249,12 @@ struct drm_fb_helper {
234249
int preferred_bpp;
235250
};
236251

252+
static inline struct drm_fb_helper *
253+
drm_fb_helper_from_client(struct drm_client_dev *client)
254+
{
255+
return container_of(client, struct drm_fb_helper, client);
256+
}
257+
237258
/**
238259
* define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers
239260
*
@@ -330,6 +351,9 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
330351

331352
void drm_fb_helper_lastclose(struct drm_device *dev);
332353
void drm_fb_helper_output_poll_changed(struct drm_device *dev);
354+
355+
int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
356+
struct drm_fb_helper_surface_size *sizes);
333357
#else
334358
static inline void drm_fb_helper_prepare(struct drm_device *dev,
335359
struct drm_fb_helper *helper,
@@ -564,6 +588,13 @@ static inline void drm_fb_helper_output_poll_changed(struct drm_device *dev)
564588
{
565589
}
566590

591+
static inline int
592+
drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
593+
struct drm_fb_helper_surface_size *sizes)
594+
{
595+
return 0;
596+
}
597+
567598
#endif
568599

569600
static inline int

0 commit comments

Comments
 (0)