[PATCH RFC 2/8] drm: hisilicon: Add new DRM driver for hisilicon Soc

Xinwei Kong kong.kongxinwei at hisilicon.com
Tue Sep 15 02:37:08 PDT 2015


This patch creates this driver itself and register all the sub-components
which is from DTS inode, this driver uses components framework mechanism
to bind all the sub-components.

This patch also introduces a memory manager for hisilison drm. As cma
framebuffer helpers can no more be used.

Signed-off-by: Xinliang Liu <xinliang.liu at linaro.org>
Signed-off-by: Xinwei Kong <kong.kongxinwei at hisilicon.com>
Signed-off-by: Andy Green <andy.green at linaro.org>
Signed-off-by: Jiwen Qi <qijiwen at hisilicon.com>
Signed-off-by: Yu Gong <gongyu at hisilicon.com>
---
 arch/arm64/configs/defconfig             |   5 +
 drivers/gpu/drm/Kconfig                  |   2 +
 drivers/gpu/drm/Makefile                 |   1 +
 drivers/gpu/drm/hisilicon/Kconfig        |   9 ++
 drivers/gpu/drm/hisilicon/Makefile       |   7 ++
 drivers/gpu/drm/hisilicon/hisi_ade.c     | 166 +++++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hisi_drm_drv.c | 206 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hisi_drm_dsi.c | 131 ++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hisi_drm_fb.c  | 156 +++++++++++++++++++++++
 drivers/gpu/drm/hisilicon/hisi_drm_fb.h  |  26 ++++
 10 files changed, 709 insertions(+)
 create mode 100644 drivers/gpu/drm/hisilicon/Kconfig
 create mode 100644 drivers/gpu/drm/hisilicon/Makefile
 create mode 100644 drivers/gpu/drm/hisilicon/hisi_ade.c
 create mode 100644 drivers/gpu/drm/hisilicon/hisi_drm_drv.c
 create mode 100644 drivers/gpu/drm/hisilicon/hisi_drm_dsi.c
 create mode 100644 drivers/gpu/drm/hisilicon/hisi_drm_fb.c
 create mode 100644 drivers/gpu/drm/hisilicon/hisi_drm_fb.h

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 4e17e7e..c2ea280 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -146,6 +146,8 @@ CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_SYSCON=y
 CONFIG_LEDS_TRIGGERS=y
 CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+
+
 CONFIG_LEDS_TRIGGER_CPU=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_EFI=y
@@ -199,3 +201,6 @@ CONFIG_CRYPTO_GHASH_ARM64_CE=y
 CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
 CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
 CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
+CONFIG_DRM=y
+CONFIG_DRM_HISI=y
+# CONFIG_DRM_HISI_FBDEV is not set
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c46ca31..31ee120 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -240,3 +240,5 @@ source "drivers/gpu/drm/sti/Kconfig"
 source "drivers/gpu/drm/amd/amdkfd/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/hisilicon/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 5713d05..47936d4 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_IMX) += imx/
+obj-$(CONFIG_DRM_HISI) += hisilicon/
 obj-y			+= i2c/
 obj-y			+= panel/
 obj-y			+= bridge/
diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig
new file mode 100644
index 0000000..60b42e4
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/Kconfig
@@ -0,0 +1,9 @@
+config DRM_HISI
+	tristate "DRM Support for Hisilicon Terminal SoCs Platform"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	help
+	  Choose this option if you have a hisilicon terminal chipset.
+	  If M is selected the module will be called hisi-drm.
+
diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile
new file mode 100644
index 0000000..3f042fd
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/Makefile
@@ -0,0 +1,7 @@
+hisi-drm-y := hisi_drm_drv.o \
+	      hisi_ade.o \
+	      hisi_drm_dsi.o \
+	      hisi_drm_fb.o \
+
+obj-$(CONFIG_DRM_HISI)	+= hisi-drm.o
+
diff --git a/drivers/gpu/drm/hisilicon/hisi_ade.c b/drivers/gpu/drm/hisilicon/hisi_ade.c
new file mode 100644
index 0000000..9b58d20
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hisi_ade.c
@@ -0,0 +1,166 @@
+/*
+ * Hisilicon Terminal SoCs drm driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author: Xinwei Kong <kong.kongxinwei at hisilicon.com> for hisilicon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+
+#include <drm/drm_gem_cma_helper.h>
+
+struct ade_hardware_context {
+	void __iomem  *base;
+	void __iomem  *media_base;
+
+	int irq;
+	u32 ade_core_rate;
+	u32 media_noc_rate;
+
+	struct clk *ade_core_clk;
+	struct clk *media_noc_clk;
+	struct clk *ade_pix_clk;
+	bool power_on;
+};
+
+struct hisi_ade {
+	struct ade_hardware_context ctx;
+};
+
+static int ade_dts_parse(struct platform_device *pdev,
+			 struct ade_hardware_context *ctx)
+{
+	struct resource *res;
+	struct device *dev;
+	struct device_node *np;
+	int ret;
+
+	dev = &pdev->dev;
+	np  = dev->of_node;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ade_base");
+	ctx->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctx->base)) {
+		DRM_ERROR("failed to remap ade io base\n");
+		return  PTR_ERR(ctx->base);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "media_base");
+	ctx->media_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctx->media_base)) {
+		DRM_ERROR("failed to remap media io base\n");
+		return PTR_ERR(ctx->media_base);
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		DRM_ERROR("failed to parse the irq\n");
+		return -ENODEV;
+	}
+
+	ctx->ade_core_clk = devm_clk_get(&pdev->dev, "clk_ade_core");
+	if (!ctx->ade_core_clk) {
+		DRM_ERROR("failed to parse the ade core clock\n");
+		return -ENODEV;
+	}
+	ctx->media_noc_clk = devm_clk_get(&pdev->dev,
+					"aclk_codec_jpeg_src");
+	if (!ctx->media_noc_clk) {
+		DRM_ERROR("failed to parse the codec jpeg\n");
+	    return -ENODEV;
+	}
+	ctx->ade_pix_clk = devm_clk_get(&pdev->dev, "clk_ade_pix");
+	if (!ctx->ade_pix_clk) {
+		DRM_ERROR("failed to parse the ade pixel src\n");
+	    return -ENODEV;
+	}
+
+	ret = of_property_read_u32(np, "ade_core_clk_rate",
+				   &ctx->ade_core_rate);
+	if (ret) {
+		DRM_ERROR("failed to parse the ade core clk rate\n");
+	    return -ENODEV;
+	}
+	ret = of_property_read_u32(np, "media_noc_clk_rate",
+				   &ctx->media_noc_rate);
+	if (ret) {
+		DRM_ERROR("failed to parse the media noc clk rate\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ade_bind(struct device *dev, struct device *master, void *data)
+{
+	return 0;
+}
+
+static void ade_unbind(struct device *dev, struct device *master,
+		       void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops ade_ops = {
+	.bind	= ade_bind,
+	.unbind	= ade_unbind,
+};
+
+static int ade_probe(struct platform_device *pdev)
+{
+	struct hisi_ade *ade;
+	int ret;
+
+	ade = devm_kzalloc(&pdev->dev, sizeof(*ade), GFP_KERNEL);
+	if (!ade) {
+		DRM_ERROR("failed to alloc hisi_ade\n");
+		return -ENOMEM;
+	}
+
+	ret = ade_dts_parse(pdev, &ade->ctx);
+	if (ret) {
+		DRM_ERROR("failed to parse ade dts!\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, ade);
+
+	return component_add(&pdev->dev, &ade_ops);
+}
+
+static int ade_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &ade_ops);
+
+	return 0;
+}
+
+static const struct of_device_id ade_of_match[] = {
+	{ .compatible = "hisilicon,hi6220-ade" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ade_of_match);
+
+static struct platform_driver ade_driver = {
+	.probe = ade_probe,
+	.remove = ade_remove,
+	.driver = {
+		   .name = "hisi-ade",
+		   .owner = THIS_MODULE,
+		   .of_match_table = ade_of_match,
+	},
+};
+
+module_platform_driver(ade_driver);
+
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei at hisilicon.com>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang at huawei.com>");
+MODULE_DESCRIPTION("hisilicon SoC DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_drv.c b/drivers/gpu/drm/hisilicon/hisi_drm_drv.c
new file mode 100644
index 0000000..0983ad7
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hisi_drm_drv.c
@@ -0,0 +1,206 @@
+/*
+ * Hisilicon Terminal SoCs drm driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author: Xinwei Kong <kong.kongxinwei at hisilicon.com> for hisilicon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/of_platform.h>
+#include <linux/component.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "hisi_drm_fb.h"
+
+#define DRIVER_NAME	"hisi-drm"
+
+static int hisi_drm_unload(struct drm_device *dev)
+{
+	drm_vblank_cleanup(dev);
+	drm_mode_config_cleanup(dev);
+	dev->dev_private = NULL;
+
+	return 0;
+}
+
+static const struct drm_mode_config_funcs hisi_drm_mode_config_funcs = {
+	.fb_create = hisi_drm_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void hisi_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+
+	dev->mode_config.funcs = &hisi_drm_mode_config_funcs;
+}
+
+static int hisi_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	int ret;
+
+	/* debug setting
+	drm_debug = DRM_UT_DRIVER|DRM_UT_KMS; */
+
+	/* dev->mode_config initialization */
+	drm_mode_config_init(dev);
+	hisi_drm_mode_config_init(dev);
+
+	/* only support one crtc now */
+	ret = drm_vblank_init(dev, 1);
+	if (ret)
+		goto out_err;
+
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		goto out_err;
+
+	return 0;
+
+out_err:
+	hisi_drm_unload(dev);
+	return ret;
+}
+
+static const struct file_operations hisi_drm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= drm_open,
+	.release	= drm_release,
+	.unlocked_ioctl	= drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= drm_compat_ioctl,
+#endif
+	.poll		= drm_poll,
+	.read		= drm_read,
+	.llseek		= no_llseek,
+	.mmap		= drm_gem_cma_mmap,
+};
+
+static struct dma_buf *hisi_drm_gem_prime_export(struct drm_device *dev,
+						 struct drm_gem_object *obj,
+						 int flags)
+{
+	/* we want to be able to write in mmapped buffer */
+	flags |= O_RDWR;
+	return drm_gem_prime_export(dev, obj, flags);
+}
+
+static struct drm_driver hisi_drm_driver = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
+				| DRIVER_HAVE_IRQ,
+	.load			= hisi_drm_load,
+	.unload                 = hisi_drm_unload,
+	.fops			= &hisi_drm_fops,
+	.set_busid		= drm_platform_set_busid,
+
+	.gem_free_object	= drm_gem_cma_free_object,
+	.gem_vm_ops		= &drm_gem_cma_vm_ops,
+	.dumb_create		= drm_gem_cma_dumb_create,
+	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
+	.dumb_destroy		= drm_gem_dumb_destroy,
+
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_export	= hisi_drm_gem_prime_export,
+	.gem_prime_import	= drm_gem_prime_import,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
+
+	.name			= "hisi",
+	.desc			= "Hisilicon Terminal SoCs DRM Driver",
+	.date			= "20150830",
+	.major			= 1,
+	.minor			= 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int hisi_drm_bind(struct device *dev)
+{
+	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	return drm_platform_init(&hisi_drm_driver, to_platform_device(dev));
+}
+
+static void hisi_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops hisi_drm_ops = {
+	.bind = hisi_drm_bind,
+	.unbind = hisi_drm_unbind,
+};
+
+static int hisi_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct device_node *child_np;
+	struct component_match *match = NULL;
+
+	of_platform_populate(node, NULL, NULL, dev);
+
+	child_np = of_get_next_available_child(node, NULL);
+	while (child_np) {
+		component_match_add(dev, &match, compare_of, child_np);
+		of_node_put(child_np);
+		child_np = of_get_next_available_child(node, child_np);
+	}
+
+	return component_master_add_with_match(dev, &hisi_drm_ops, match);
+
+	return 0;
+}
+
+static int hisi_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &hisi_drm_ops);
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id hisi_drm_dt_ids[] = {
+	{ .compatible = "hisilicon,display-subsystem", },
+	{ /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, hisi_drm_dt_ids);
+
+static struct platform_driver hisi_drm_platform_driver = {
+	.probe = hisi_drm_platform_probe,
+	.remove = hisi_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME,
+		.of_match_table = hisi_drm_dt_ids,
+	},
+};
+
+module_platform_driver(hisi_drm_platform_driver);
+
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei at hisilicon.com>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang at huawei.com>");
+MODULE_DESCRIPTION("hisilicon SoC DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c
new file mode 100644
index 0000000..a8dbaad
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hisi_drm_dsi.c
@@ -0,0 +1,131 @@
+/*
+ * Hisilicon Terminal SoCs drm driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author: Xinwei Kong <kong.kongxinwei at hisilicon.com> for hisilicon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_encoder_slave.h>
+
+#define DSI_24BITS_1               (5)
+
+struct hisi_dsi {
+	u32 lanes;
+	u32 format;
+	u32 date_enable_pol;
+	u32 mode_flags;
+	u8 color_mode;
+	void *ctx;
+};
+
+struct hisi_dsi_context {
+	struct hisi_dsi dsi;
+	struct clk *dsi_cfg_clk;
+	struct drm_device *dev;
+
+	void __iomem *base;
+	int nominal_pixel_clk_kHz;
+};
+
+static int hisi_dsi_bind(struct device *dev, struct device *master,
+			 void *data)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+static void hisi_dsi_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops hisi_dsi_ops = {
+	.bind	= hisi_dsi_bind,
+	.unbind	= hisi_dsi_unbind,
+};
+
+static int hisi_dsi_probe(struct platform_device *pdev)
+{
+	struct hisi_dsi_context *ctx;
+	struct hisi_dsi *dsi;
+	struct resource *res;
+	struct device_node *slave_node;
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		DRM_ERROR("failed to allocate hisi dsi context.\n");
+		ret = -ENOMEM;
+	}
+
+	ctx->dsi_cfg_clk = devm_clk_get(&pdev->dev, "pclk_dsi");
+	if (IS_ERR(ctx->dsi_cfg_clk)) {
+		DRM_ERROR("failed to parse the dsi config clock\n");
+		ret = PTR_ERR(ctx->dsi_cfg_clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctx->base)) {
+		DRM_ERROR("failed to remap dsi io region\n");
+		ret = PTR_ERR(ctx->base);
+	}
+
+	slave_node = of_parse_phandle(np, "encoder-slave", 0);
+	if (!slave_node) {
+		DRM_ERROR("failed to parse the slave encoder node\n");
+		return -EINVAL;
+	}
+
+	dsi = &ctx->dsi;
+	dsi->ctx = ctx;
+	dsi->lanes = 3;
+	dsi->date_enable_pol = 0;
+	dsi->color_mode = DSI_24BITS_1;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+
+	return component_add(&pdev->dev, &hisi_dsi_ops);
+}
+
+static int hisi_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &hisi_dsi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id hisi_dsi_of_match[] = {
+	{.compatible = "hisilicon,hi6220-dsi"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, hisi_dsi_of_match);
+
+static struct platform_driver hisi_dsi_driver = {
+	.probe = hisi_dsi_probe,
+	.remove = hisi_dsi_remove,
+	.driver = {
+		.name = "hisi-dsi",
+		.owner = THIS_MODULE,
+		.of_match_table = hisi_dsi_of_match,
+	},
+};
+
+module_platform_driver(hisi_dsi_driver);
+
+MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei at hisilicon.com>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang at huawei.com>");
+MODULE_DESCRIPTION("hisilicon SoC DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_fb.c b/drivers/gpu/drm/hisilicon/hisi_drm_fb.c
new file mode 100644
index 0000000..5dace8b
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hisi_drm_fb.c
@@ -0,0 +1,156 @@
+/*
+ * Hisilicon Terminal SoCs drm fbdev driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author: z.liuxinliang at huawei.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "hisi_drm_fb.h"
+
+struct hisi_drm_fb *to_hisi_drm_fb(struct drm_framebuffer *fb)
+{
+	return container_of(fb, struct hisi_drm_fb, fb);
+}
+
+void hisi_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	struct hisi_drm_fb *hisi_fb = to_hisi_drm_fb(fb);
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if (hisi_fb->obj[i])
+			drm_gem_object_unreference_unlocked
+				(&hisi_fb->obj[i]->base);
+	}
+
+	drm_framebuffer_cleanup(fb);
+	kfree(hisi_fb);
+}
+
+static int hisi_drm_fb_create_handle(struct drm_framebuffer *fb,
+				     struct drm_file *file_priv,
+				     unsigned int *handle)
+{
+	struct hisi_drm_fb *hisi_fb = to_hisi_drm_fb(fb);
+
+	return drm_gem_handle_create(file_priv,
+			&hisi_fb->obj[0]->base, handle);
+}
+
+static int hisi_drm_fb_dirty(struct drm_framebuffer *fb,
+			     struct drm_file *file_priv,
+			     unsigned flags,
+			     unsigned color,
+			     struct drm_clip_rect *clips,
+			     unsigned num_clips)
+{
+	/* TODO */
+	return 0;
+}
+
+static struct drm_framebuffer_funcs hisi_drm_fb_funcs = {
+	.destroy	= hisi_drm_fb_destroy,
+	.create_handle	= hisi_drm_fb_create_handle,
+	.dirty		= hisi_drm_fb_dirty,
+};
+
+struct hisi_drm_fb *hisi_drm_fb_alloc(struct drm_device *dev,
+				      struct drm_mode_fb_cmd2 *mode_cmd,
+				      struct drm_gem_cma_object **obj,
+				      unsigned int num_planes, bool is_fbdev_fb)
+{
+	struct hisi_drm_fb *hisi_fb;
+	int ret;
+	int i;
+
+	hisi_fb = kzalloc(sizeof(*hisi_fb), GFP_KERNEL);
+	if (!hisi_fb)
+		return ERR_PTR(-ENOMEM);
+
+	hisi_fb->is_fbdev_fb = is_fbdev_fb;
+	drm_helper_mode_fill_fb_struct(&hisi_fb->fb, mode_cmd);
+
+	for (i = 0; i < num_planes; i++)
+		hisi_fb->obj[i] = obj[i];
+
+	ret = drm_framebuffer_init(dev, &hisi_fb->fb, &hisi_drm_fb_funcs);
+	if (ret) {
+		DRM_ERROR("Failed to initialize framebuffer: %d\n", ret);
+		kfree(hisi_fb);
+		return ERR_PTR(ret);
+	}
+
+	return hisi_fb;
+}
+
+/**
+ * hisi_drm_fb_create() - (struct drm_mode_config_funcs *)->fb_create callback
+ *function
+ * If your hardware has special alignment or pitch requirements these should be
+ * checked before calling this function.
+ */
+
+struct drm_framebuffer *hisi_drm_fb_create(struct drm_device *dev,
+					   struct drm_file *file_priv,
+					   struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct hisi_drm_fb *hisi_fb;
+	struct drm_gem_cma_object *objs[4];
+	struct drm_gem_object *obj;
+	unsigned int hsub;
+	unsigned int vsub;
+	int ret;
+	int i;
+
+	/* TODO: Need to use ion heaps to create frame buffer?? */
+
+	hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+	vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+
+	for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
+		unsigned int width = mode_cmd->width / (i ? hsub : 1);
+		unsigned int height = mode_cmd->height / (i ? vsub : 1);
+		unsigned int min_size;
+
+		obj = drm_gem_object_lookup(dev, file_priv,
+					    mode_cmd->handles[i]);
+		if (!obj) {
+			DRM_ERROR("Failed to lookup GEM object\n");
+			ret = -ENXIO;
+			goto err_gem_object_unreference;
+		}
+
+		min_size = (height - 1) * mode_cmd->pitches[i]
+		     + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
+		     + mode_cmd->offsets[i];
+
+		if (obj->size < min_size) {
+			drm_gem_object_unreference_unlocked(obj);
+			ret = -EINVAL;
+			goto err_gem_object_unreference;
+		}
+		objs[i] = to_drm_gem_cma_obj(obj);
+	}
+
+	hisi_fb = hisi_drm_fb_alloc(dev, mode_cmd, objs, i, false);
+	if (IS_ERR(hisi_fb)) {
+		ret = PTR_ERR(hisi_fb);
+		goto err_gem_object_unreference;
+	}
+
+	return &hisi_fb->fb;
+
+err_gem_object_unreference:
+	for (i--; i >= 0; i--)
+		drm_gem_object_unreference_unlocked(&objs[i]->base);
+	return ERR_PTR(ret);
+}
+
diff --git a/drivers/gpu/drm/hisilicon/hisi_drm_fb.h b/drivers/gpu/drm/hisilicon/hisi_drm_fb.h
new file mode 100644
index 0000000..1db1289
--- /dev/null
+++ b/drivers/gpu/drm/hisilicon/hisi_drm_fb.h
@@ -0,0 +1,26 @@
+/*
+ * Hisilicon Terminal SoCs drm fbdev driver
+ *
+ * Copyright (c) 2014-2015 Hisilicon Limited.
+ * Author: z.liuxinliang at huawei.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __HISI_DRM_FB_H__
+#define __HISI_DRM_FB_H__
+
+struct hisi_drm_fb {
+	struct drm_framebuffer		fb;
+	struct drm_gem_cma_object	*obj[4];
+	bool is_fbdev_fb;
+};
+
+struct drm_framebuffer *hisi_drm_fb_create(struct drm_device *dev,
+					   struct drm_file *file_priv,
+					   struct drm_mode_fb_cmd2 *mode_cmd);
+
+#endif /* __HISI_DRM_FB_H__ */
-- 
1.9.1





More information about the linux-arm-kernel mailing list