Create a simple fbdev device during SimpleDRM setup so legacy user-space
and fbcon can use it.

Signed-off-by: David Herrmann <dh.herrmann at gmail.com>
---
 drivers/gpu/drm/simpledrm/Makefile          |   1 +
 drivers/gpu/drm/simpledrm/simpledrm.h       |   8 ++
 drivers/gpu/drm/simpledrm/simpledrm_drv.c   |  13 +++
 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 143 ++++++++++++++++++++++++++++
 drivers/gpu/drm/simpledrm/simpledrm_kms.c   |  55 ++++++-----
 5 files changed, 196 insertions(+), 24 deletions(-)
 create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c

diff --git a/drivers/gpu/drm/simpledrm/Makefile 
b/drivers/gpu/drm/simpledrm/Makefile
index d7b179d..e368d14 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -1,6 +1,7 @@
 simpledrm-y := \
        simpledrm_damage.o \
        simpledrm_drv.o \
+       simpledrm_fbdev.o \
        simpledrm_gem.o \
        simpledrm_kms.o \
        simpledrm_of.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h 
b/drivers/gpu/drm/simpledrm/simpledrm.h
index ed6d725..d7a2045 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -19,6 +19,7 @@
 #include <linux/mutex.h>

 struct clk;
+struct drm_fb_helper;
 struct regulator;
 struct simplefb_format;

@@ -49,6 +50,7 @@ struct sdrm_device {
        atomic_t n_used;
        struct drm_device *ddev;
        struct sdrm_hw *hw;
+       struct drm_fb_helper *fbdev;

        size_t n_clks;
        size_t n_regulators;
@@ -66,6 +68,9 @@ void sdrm_of_unbind(struct sdrm_device *sdrm);
 int sdrm_kms_bind(struct sdrm_device *sdrm);
 void sdrm_kms_unbind(struct sdrm_device *sdrm);

+void sdrm_fbdev_bind(struct sdrm_device *sdrm);
+void sdrm_fbdev_unbind(struct sdrm_device *sdrm);
+
 void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height);

 struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size);
@@ -80,4 +85,7 @@ int sdrm_dumb_map_offset(struct drm_file *dfile,
                         uint32_t handle,
                         uint64_t *offset);

+struct sdrm_fb *sdrm_fb_new(struct sdrm_bo *bo,
+                           const struct drm_mode_fb_cmd2 *cmd);
+
 #endif /* __SDRM_SIMPLEDRM_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c 
b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
index d569120..6aefe49 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c
@@ -9,6 +9,7 @@

 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
 #include <linux/atomic.h>
 #include <linux/err.h>
 #include <linux/io.h>
@@ -210,6 +211,7 @@ error:
 static void sdrm_device_unbind(struct sdrm_device *sdrm)
 {
        if (sdrm) {
+               sdrm_fbdev_unbind(sdrm);
                sdrm_kms_unbind(sdrm);
                sdrm_hw_unbind(sdrm->hw);
                sdrm_of_unbind(sdrm);
@@ -232,6 +234,8 @@ static int sdrm_device_bind(struct sdrm_device *sdrm)
        if (r < 0)
                goto error;

+       sdrm_fbdev_bind(sdrm);
+
        return 0;

 error:
@@ -253,6 +257,14 @@ static void sdrm_device_release(struct sdrm_device *sdrm)
        }
 }

+static void sdrm_device_lastclose(struct drm_device *ddev)
+{
+       struct sdrm_device *sdrm = ddev->dev_private;
+
+       if (sdrm->fbdev)
+               drm_fb_helper_restore_fbdev_mode_unlocked(sdrm->fbdev);
+}
+
 static int sdrm_fop_open(struct inode *inode, struct file *file)
 {
        struct drm_device *ddev;
@@ -411,6 +423,7 @@ static const struct file_operations sdrm_drm_fops = {
 static struct drm_driver sdrm_drm_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
        .fops = &sdrm_drm_fops,
+       .lastclose = sdrm_device_lastclose,

        .gem_free_object = sdrm_bo_free,

diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c 
b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
new file mode 100644
index 0000000..17e4e83
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012-2016 Red Hat, Inc.
+ * Copyright (C) 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "simpledrm.h"
+
+static int sdrm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+       return -ENODEV;
+}
+
+static struct fb_ops sdrm_fbdev_ops = {
+       .owner          = THIS_MODULE,
+       .fb_fillrect    = drm_fb_helper_sys_fillrect,
+       .fb_copyarea    = drm_fb_helper_sys_copyarea,
+       .fb_imageblit   = drm_fb_helper_sys_imageblit,
+       .fb_check_var   = drm_fb_helper_check_var,
+       .fb_set_par     = drm_fb_helper_set_par,
+       .fb_setcmap     = drm_fb_helper_setcmap,
+       .fb_mmap        = sdrm_fbdev_mmap,
+};
+
+static int sdrm_fbdev_probe(struct drm_fb_helper *fbdev,
+                           struct drm_fb_helper_surface_size *sizes)
+{
+       struct sdrm_device *sdrm = fbdev->dev->dev_private;
+       struct drm_mode_fb_cmd2 cmd = {
+               .width = sdrm->hw->width,
+               .height = sdrm->hw->height,
+               .pitches[0] = sdrm->hw->stride,
+               .pixel_format = sdrm->hw->format,
+       };
+       struct fb_info *fbi;
+       struct sdrm_bo *bo;
+       struct sdrm_fb *fb;
+       int r;
+
+       fbi = drm_fb_helper_alloc_fbi(fbdev);
+       if (IS_ERR(fbi))
+               return PTR_ERR(fbi);
+
+       bo = sdrm_bo_new(sdrm->ddev,
+                        PAGE_ALIGN(sdrm->hw->height * sdrm->hw->stride));
+       if (!bo) {
+               r = -ENOMEM;
+               goto error;
+       }
+
+       fb = sdrm_fb_new(bo, &cmd);
+       drm_gem_object_unreference_unlocked(&bo->base);
+       if (IS_ERR(fb)) {
+               r = PTR_ERR(fb);
+               goto error;
+       }
+
+       fbdev->fb = &fb->base;
+       fbi->par = fbdev;
+       fbi->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
+       fbi->fbops = &sdrm_fbdev_ops;
+
+       drm_fb_helper_fill_fix(fbi, fb->base.pitches[0], fb->base.depth);
+       drm_fb_helper_fill_var(fbi, fbdev, fb->base.width, fb->base.height);
+
+       strncpy(fbi->fix.id, "simpledrmfb", sizeof(fbi->fix.id) - 1);
+       fbi->screen_base = bo->vmapping;
+       fbi->fix.smem_len = bo->base.size;
+
+       return 0;
+
+error:
+       drm_fb_helper_release_fbi(fbdev);
+       return r;
+}
+
+static const struct drm_fb_helper_funcs sdrm_fbdev_funcs = {
+       .fb_probe = sdrm_fbdev_probe,
+};
+
+void sdrm_fbdev_bind(struct sdrm_device *sdrm)
+{
+       struct drm_fb_helper *fbdev;
+       int r;
+
+       fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+       if (!fbdev)
+               return;
+
+       drm_fb_helper_prepare(sdrm->ddev, fbdev, &sdrm_fbdev_funcs);
+
+       r = drm_fb_helper_init(sdrm->ddev, fbdev, 1, 1);
+       if (r < 0)
+               goto error;
+
+       r = drm_fb_helper_single_add_all_connectors(fbdev);
+       if (r < 0)
+               goto error;
+
+       r = drm_fb_helper_initial_config(fbdev,
+                               sdrm->ddev->mode_config.preferred_depth);
+       if (r < 0)
+               goto error;
+
+       if (!fbdev->fbdev)
+               goto error;
+
+       sdrm->fbdev = fbdev;
+       return;
+
+error:
+       drm_fb_helper_fini(fbdev);
+       kfree(fbdev);
+}
+
+void sdrm_fbdev_unbind(struct sdrm_device *sdrm)
+{
+       struct drm_fb_helper *fbdev = sdrm->fbdev;
+
+       if (!fbdev)
+               return;
+
+       sdrm->fbdev = NULL;
+       drm_fb_helper_unregister_fbi(fbdev);
+       cancel_work_sync(&fbdev->dirty_work);
+       drm_fb_helper_release_fbi(fbdev);
+       drm_framebuffer_unreference(fbdev->fb);
+       fbdev->fb = NULL;
+       drm_fb_helper_fini(fbdev);
+       kfree(fbdev);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c 
b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
index 00101c9..8f67fe5 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm_kms.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -169,53 +169,60 @@ static const struct drm_framebuffer_funcs sdrm_fb_ops = {
        .destroy                = sdrm_fb_destroy,
 };

-static struct drm_framebuffer *
-sdrm_fb_create(struct drm_device *ddev,
-              struct drm_file *dfile,
-              const struct drm_mode_fb_cmd2 *cmd)
+struct sdrm_fb *sdrm_fb_new(struct sdrm_bo *bo,
+                           const struct drm_mode_fb_cmd2 *cmd)
 {
-       struct drm_gem_object *dobj;
-       struct sdrm_fb *fb = NULL;
-       struct sdrm_bo *bo;
+       struct sdrm_fb *fb;
        int r;

        if (cmd->flags)
                return ERR_PTR(-EINVAL);

-       dobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
-       if (!dobj)
-               return ERR_PTR(-EINVAL);
-
-       bo = container_of(dobj, struct sdrm_bo, base);
-
        r = sdrm_bo_vmap(bo);
        if (r < 0)
-               goto error;
+               return ERR_PTR(r);

        fb = kzalloc(sizeof(*fb), GFP_KERNEL);
-       if (!fb) {
-               r = -ENOMEM;
-               goto error;
-       }
+       if (!fb)
+               return ERR_PTR(r);

+       drm_gem_object_reference(&bo->base);
        fb->bo = bo;
        drm_helper_mode_fill_fb_struct(&fb->base, cmd);

-       r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
+       r = drm_framebuffer_init(bo->base.dev, &fb->base, &sdrm_fb_ops);
        if (r < 0)
                goto error;

-       DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id,
-                     drm_get_format_name(fb->base.pixel_format));
-
-       return &fb->base;
+       return fb;

 error:
+       drm_gem_object_unreference_unlocked(&bo->base);
        kfree(fb);
-       drm_gem_object_unreference_unlocked(dobj);
        return ERR_PTR(r);
 }

+static struct drm_framebuffer *
+sdrm_fb_create(struct drm_device *ddev,
+              struct drm_file *dfile,
+              const struct drm_mode_fb_cmd2 *cmd)
+{
+       struct drm_gem_object *dobj;
+       struct sdrm_fb *fb;
+
+       dobj = drm_gem_object_lookup(dfile, cmd->handles[0]);
+       if (!dobj)
+               return ERR_PTR(-EINVAL);
+
+       fb = sdrm_fb_new(container_of(dobj, struct sdrm_bo, base), cmd);
+       drm_gem_object_unreference_unlocked(dobj);
+
+       if (IS_ERR(fb))
+               return ERR_CAST(fb);
+
+       return &fb->base;
+}
+
 static const struct drm_mode_config_funcs sdrm_mode_config_ops = {
        .fb_create              = sdrm_fb_create,
        .atomic_check           = drm_atomic_helper_check,
-- 
2.9.3

Reply via email to