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 devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html