[PATCH RFC 2/8] drm: hisilicon: Add new DRM driver for hisilicon Soc
From: Xinwei Kong
Date: Tue Sep 15 2015 - 05:43:33 EST
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@xxxxxxxxxx>
Signed-off-by: Xinwei Kong <kong.kongxinwei@xxxxxxxxxxxxx>
Signed-off-by: Andy Green <andy.green@xxxxxxxxxx>
Signed-off-by: Jiwen Qi <qijiwen@xxxxxxxxxxxxx>
Signed-off-by: Yu Gong <gongyu@xxxxxxxxxxxxx>
---
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@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@xxxxxxxxxx>");
+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@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@xxxxxxxxxx>");
+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@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@xxxxxxxxxx>");
+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@xxxxxxxxxx
+ *
+ * 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@xxxxxxxxxx
+ *
+ * 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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/