[6/7] drm/hisilicon:Add fbdev

Submitted by lijianhua on Feb. 29, 2016, 12:58 a.m.

Details

Message ID 1456707521-59036-7-git-send-email-jueying0518@gmail.com
State New
Headers show
Series "Add DRM driver for Hisilicon hi1710" ( rev: 1 ) in DRI devel

Not browsing as part of any series.

Commit Message

lijianhua Feb. 29, 2016, 12:58 a.m.
Add fbdev.

Signed-off-by: lijianhua <jueying0518@gmail.com>
---
 drivers/gpu/drm/hisilicon/hibmc/Makefile          |   2 +-
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c   |   5 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h   |   2 +
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 290 ++++++++++++++++++++++
 4 files changed, 298 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c

Patch hide | download patch | download mbox

diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile
index 6ab59b1..1ca1d49 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/Makefile
+++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile
@@ -1,5 +1,5 @@ 
 ccflags-y := -Iinclude/drm
-hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_hw.o
+hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_fbdev.o hibmc_drm_hw.o
 
 obj-$(CONFIG_DRM_HISI_HIBMC)	+=hibmc-drm.o
 #obj-y	+= hibmc-drm.o
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 673a8cd..7439f62 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -250,6 +250,7 @@  static int hibmc_unload(struct drm_device *dev)
 {
 	struct hibmc_private *hiprivate = dev->dev_private;
 
+	hibmc_fbdev_fini(hiprivate);
 	hibmc_kms_fini(hiprivate);
 	hibmc_hw_fini(dev);
 	dev->dev_private = NULL;
@@ -283,6 +284,10 @@  static int hibmc_load(struct drm_device *dev, unsigned long flags)
 	/* reset all the states of crtc/plane/encoder/connector */
 	drm_mode_config_reset(dev);
 
+	ret = hibmc_fbdev_init(hiprivate);
+	if (ret)
+		goto err;
+
 	return 0;
 
 err:
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
index 1f6b25c..e863e1a 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h
@@ -50,5 +50,7 @@  int hibmc_plane_init(struct drm_device *dev);
 int hibmc_crtc_init(struct drm_device *dev);
 int hibmc_encoder_init(struct drm_device *dev);
 int hibmc_connector_init(struct drm_device *dev);
+int hibmc_fbdev_init(struct hibmc_private *hiprivate);
+void hibmc_fbdev_fini(struct hibmc_private *hiprivate);
 
 #endif
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
new file mode 100644
index 0000000..416a6c6
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c
@@ -0,0 +1,290 @@ 
+/*
+ * Copyright (c) 2016 Huawei Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "hibmc_drm_drv.h"
+
+/* ---------------------------------------------------------------------- */
+
+void hibmc_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct hibmc_private *hiprivate =
+		container_of(fb, struct hibmc_private, fbdev.fb.fb);
+	struct drm_gem_object *base = &hiprivate->fbdev.fb.obj->base;
+
+	if (hiprivate->fbdev.fb.obj)
+		drm_gem_object_unreference_unlocked(base);
+	drm_framebuffer_cleanup(fb);
+	kfree(fb);
+}
+
+static struct drm_framebuffer_funcs hibmc_drm_fb_funcs = {
+	.destroy	= hibmc_drm_fb_destroy,
+};
+
+int hibmc_drm_fb_init(struct drm_device *dev,
+		      struct hibmc_framebuffer *gfb,
+		      struct drm_mode_fb_cmd2 *mode_cmd,
+		      struct drm_gem_cma_object *obj)
+{
+	int ret;
+
+	drm_helper_mode_fill_fb_struct(&gfb->fb, mode_cmd);
+	gfb->obj = obj;
+	gfb->is_fbdev_fb = true;
+	ret = drm_framebuffer_init(dev, &gfb->fb, &hibmc_drm_fb_funcs);
+	if (ret) {
+		DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+struct drm_gem_cma_object *hibmc_drm_gem_create_object(
+						struct hibmc_private *hiprivate,
+						size_t size)
+{
+	struct drm_gem_cma_object *cma_obj;
+	struct drm_gem_object *gem_obj;
+	struct drm_device *drm = hiprivate->dev;
+	int ret;
+
+	cma_obj = devm_kzalloc(drm->dev, sizeof(*cma_obj), GFP_KERNEL);
+	if (!cma_obj)
+		return ERR_PTR(-ENOMEM);
+
+	gem_obj = &cma_obj->base;
+
+	ret = drm_gem_object_init(drm, gem_obj, size);
+	if (ret)
+		return ERR_PTR(ret);
+
+	cma_obj->vaddr = hiprivate->fb_map;
+	cma_obj->paddr = hiprivate->fb_base;
+	return cma_obj;
+}
+
+void hibmc_drm_gem_free_object(struct drm_gem_object *gem_obj)
+{
+	struct drm_gem_cma_object *cma_obj;
+
+	cma_obj = to_drm_gem_cma_obj(gem_obj);
+	drm_gem_object_release(gem_obj);
+}
+
+static struct fb_ops hibmc_drm_fb_ops = {
+	.owner = THIS_MODULE,
+	.fb_check_var = drm_fb_helper_check_var,
+	.fb_set_par = drm_fb_helper_set_par,
+	.fb_fillrect = sys_fillrect,
+	.fb_copyarea = sys_copyarea,
+	.fb_imageblit = sys_imageblit,
+	.fb_pan_display = drm_fb_helper_pan_display,
+	.fb_blank = drm_fb_helper_blank,
+	.fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int hibmc_drm_fb_probe(struct drm_fb_helper *helper,
+			      struct drm_fb_helper_surface_size *sizes)
+{
+	struct hibmc_private *hiprivate =
+		container_of(helper, struct hibmc_private, fbdev.helper);
+	struct drm_device *dev = hiprivate->dev;
+	struct fb_info *info;
+	struct drm_framebuffer *fb;
+	struct drm_mode_fb_cmd2 mode_cmd;
+	struct device *device = &dev->pdev->dev;
+	struct drm_gem_cma_object *obj = NULL;
+	int ret = 0;
+	size_t size;
+	unsigned int bytes_per_pixel;
+	unsigned long offset;
+
+	DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n",
+			 sizes->surface_width, sizes->surface_height,
+			 sizes->surface_bpp);
+	sizes->surface_depth = 32;
+
+	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+							  sizes->surface_depth);
+
+	size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE);
+
+	obj = hibmc_drm_gem_create_object(hiprivate, size);
+	if (IS_ERR(obj)) {
+		DRM_ERROR("can't allocate gem object\n");
+		return -ENOMEM;
+	}
+
+	/* init fb device */
+	info = framebuffer_alloc(0, device);
+	if (!info) {
+		DRM_ERROR("can't allocate framebuffer\n");
+		goto err_drm_gem_cma_free_object;
+	}
+
+	ret = hibmc_drm_fb_init(hiprivate->dev,
+				&hiprivate->fbdev.fb, &mode_cmd, obj);
+	if (ret) {
+		DRM_ERROR("framebuffer init error\n");
+		goto err_drm_gem_cma_free_object;
+	}
+
+	/* setup helper */
+	fb = &hiprivate->fbdev.fb.fb;
+	hiprivate->fbdev.helper.fb = fb;
+	hiprivate->fbdev.helper.fbdev = info;
+
+	strcpy(info->fix.id, "hibmcdrmfb");
+	info->par = &hiprivate->fbdev.helper;
+	info->flags = FBINFO_DEFAULT;
+	info->fbops = &hibmc_drm_fb_ops;
+
+	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+	drm_fb_helper_fill_var(info, &hiprivate->fbdev.helper, sizes->fb_width,
+			       sizes->fb_height);
+
+	offset = info->var.xoffset * bytes_per_pixel;
+	offset += info->var.yoffset * fb->pitches[0];
+
+	dev->mode_config.fb_base = (resource_size_t)obj->paddr;
+	info->screen_base = obj->vaddr + offset;
+	info->fix.smem_start = (unsigned long)(obj->paddr + offset);
+	info->screen_size = size;
+	info->fix.smem_len = size;
+
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret) {
+		DRM_ERROR("%s: can't allocate color map\n", info->fix.id);
+		goto err_hisi_drm_fb_destroy;
+	}
+
+	return 0;
+
+err_hisi_drm_fb_destroy:
+	drm_framebuffer_unregister_private(fb);
+err_drm_gem_cma_free_object:
+	hibmc_drm_gem_free_object(&obj->base);
+	return ret;
+}
+
+static int hibmc_fbdev_destroy(struct hibmc_private *hiprivate)
+{
+	struct hibmc_framebuffer *gfb = &hiprivate->fbdev.fb;
+	struct fb_info *info;
+
+	DRM_DEBUG_DRIVER("hibmc_fbdev_destroy\n");
+
+	if (hiprivate->fbdev.helper.fbdev) {
+		info = hiprivate->fbdev.helper.fbdev;
+
+		unregister_framebuffer(info);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+		framebuffer_release(info);
+	}
+
+	drm_framebuffer_unregister_private(&gfb->fb);
+	if (gfb->obj) {
+		drm_gem_object_unreference_unlocked(&gfb->obj->base);
+		gfb->obj = NULL;
+	}
+
+	drm_framebuffer_cleanup(&gfb->fb);
+	drm_fb_helper_fini(&hiprivate->fbdev.helper);
+
+	return 0;
+}
+
+static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = {
+	.fb_probe = hibmc_drm_fb_probe,
+};
+
+int hibmc_fbdev_init(struct hibmc_private *hiprivate)
+{
+	int ret;
+	struct fb_var_screeninfo *var;
+	struct fb_fix_screeninfo *fix;
+
+	drm_fb_helper_prepare(hiprivate->dev, &hiprivate->fbdev.helper,
+			      &hibmc_fbdev_helper_funcs);
+
+	/* Now just one crtc and one channel */
+	ret = drm_fb_helper_init(hiprivate->dev,
+				 &hiprivate->fbdev.helper, 1, 1);
+	if (ret)
+		return ret;
+
+	ret = drm_fb_helper_single_add_all_connectors(&hiprivate->fbdev.helper);
+	if (ret)
+		goto fini;
+
+	drm_helper_disable_unused_functions(hiprivate->dev);
+
+	ret = drm_fb_helper_initial_config(&hiprivate->fbdev.helper, 16);
+	if (ret)
+		goto fini;
+
+	hiprivate->fbdev.initialized = true;
+
+	var = &hiprivate->fbdev.helper.fbdev->var;
+	fix = &hiprivate->fbdev.helper.fbdev->fix;
+
+	DRM_DEBUG("Member of info->var is :\n"
+		 "xres=%d\n"
+		 "yres=%d\n"
+		 "xres_virtual=%d\n"
+		 "yres_virtual=%d\n"
+		 "xoffset=%d\n"
+		 "yoffset=%d\n"
+		 "bits_per_pixel=%d\n"
+		 "...\n", var->xres, var->yres, var->xres_virtual,
+		 var->yres_virtual, var->xoffset, var->yoffset,
+		 var->bits_per_pixel);
+	DRM_DEBUG("Member of info->fix is :\n"
+		 "smem_start=%lx\n"
+		 "smem_len=%d\n"
+		 "type=%d\n"
+		 "type_aux=%d\n"
+		 "visual=%d\n"
+		 "xpanstep=%d\n"
+		 "ypanstep=%d\n"
+		 "ywrapstep=%d\n"
+		 "line_length=%d\n"
+		 "accel=%d\n"
+		 "capabilities=%d\n"
+		 "...\n", fix->smem_start, fix->smem_len, fix->type,
+		 fix->type_aux, fix->visual, fix->xpanstep,
+		 fix->ypanstep, fix->ywrapstep, fix->line_length,
+		 fix->accel, fix->capabilities);
+
+	return 0;
+
+fini:
+	drm_fb_helper_fini(&hiprivate->fbdev.helper);
+	return ret;
+}
+
+void hibmc_fbdev_fini(struct hibmc_private *hiprivate)
+{
+	if (!hiprivate->fbdev.initialized)
+		return;
+
+	hibmc_fbdev_destroy(hiprivate);
+	hiprivate->fbdev.initialized = false;
+}