[PATCH] drm: Add Drm driver for Rockchip Socs

Rob Clark robdclark at gmail.com
Thu Jul 3 14:19:36 PDT 2014


On Thu, Jul 3, 2014 at 7:08 AM, mark yao <mark.yao at rock-chips.com> wrote:
> From: mark <mark.yao at rock-chips.com>
>
> This patch is a DRM Driver for Rockchip Socs and now only add the
> framework of Rockchips Socs, but we will add Rockchips Socs soon.

just a very quick skim over this..  I expect I'll have some more
comments when I see a usage of the subdrv stuff.

This is not an exhaustive review, and would be good to get review
comments from some others as well.

> Signed-off-by: mark <mark.yao at rock-chips.com>
> ---
>  drivers/gpu/drm/Kconfig                           |    2 +
>  drivers/gpu/drm/Makefile                          |    1 +
>  drivers/gpu/drm/rockchip/Kconfig                  |   35 +
>  drivers/gpu/drm/rockchip/Makefile                 |   14 +
>  drivers/gpu/drm/rockchip/rockchip_drm_buf.c       |  191 ++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_buf.h       |   39 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_connector.c |  261 ++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_connector.h |   24 +
>  drivers/gpu/drm/rockchip/rockchip_drm_core.c      |  163 +++++
>  drivers/gpu/drm/rockchip/rockchip_drm_crtc.c      |  515 ++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_crtc.h      |   42 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.c    |  290 ++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.h    |   32 +
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c       |  618 +++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h       |  319 +++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_encoder.c   |  206 ++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_encoder.h   |   30 +
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c        |  333 ++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.h        |   39 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c     |  380 +++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h     |   26 +
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c       |  738 +++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.h       |  198 ++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_iommu.c     |  149 +++++
>  drivers/gpu/drm/rockchip/rockchip_drm_iommu.h     |   76 +++
>  drivers/gpu/drm/rockchip/rockchip_drm_plane.c     |  290 ++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_plane.h     |   30 +
>  include/uapi/drm/rockchip_drm.h                   |  155 +++++
>  28 files changed, 5196 insertions(+)
>  create mode 100644 drivers/gpu/drm/rockchip/Kconfig
>  create mode 100644 drivers/gpu/drm/rockchip/Makefile
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_buf.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_buf.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_core.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_crtc.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_crtc.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_encoder.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_encoder.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_iommu.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_iommu.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_plane.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_plane.h
>  create mode 100644 include/uapi/drm/rockchip_drm.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index f512004..5951c2c 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -170,6 +170,8 @@ config DRM_SAVAGE
>
>  source "drivers/gpu/drm/exynos/Kconfig"
>
> +source "drivers/gpu/drm/rockchip/Kconfig"
> +
>  source "drivers/gpu/drm/vmwgfx/Kconfig"
>
>  source "drivers/gpu/drm/gma500/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index dd2ba42..40babd2 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
>  obj-$(CONFIG_DRM_VIA)  +=via/
>  obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
>  obj-$(CONFIG_DRM_EXYNOS) +=exynos/
> +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
>  obj-$(CONFIG_DRM_GMA500) += gma500/
>  obj-$(CONFIG_DRM_UDL) += udl/
>  obj-$(CONFIG_DRM_AST) += ast/
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> new file mode 100644
> index 0000000..9350316
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -0,0 +1,35 @@
> +config DRM_ROCKCHIP
> +       tristate "DRM Support for Rockchip "
> +       depends on DRM
> +       select DRM_KMS_HELPER
> +       select DRM_KMS_FB_HELPER
> +       select FB_CFB_FILLRECT
> +       select FB_CFB_COPYAREA
> +       select FB_CFB_IMAGEBLIT
> +       select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> +       select VIDEOMODE_HELPERS
> +       select OF
> +       help
> +         Choose this option if you have a  ROCKCHIP soc chipset.
> +         support will be included for lcd controller and display interface.
> +         use dma buffer and iommu to get continus Continuous memory.
> +         If M is selected the module will be called rockchipdrm.
> +
> +config DRM_ROCKCHIP_IOMMU
> +       bool "ROCKCHIP DRM IOMMU Support"
> +       depends on DRM_ROCKCHIP && ARM_DMA_USE_IOMMU
> +       help
> +         Choose this option if you want to use IOMMU feature for DRM.
> +         support will be included for rk3288 lcd controller.
> +         the IOMMU takes care of mapping device-visible virtual addresses
> +         to physical addresses.
> +
> +
> +config DRM_ROCKCHIP_DMABUF
> +       bool "ROCKCHIP DRM DMABUF"
> +       depends on DRM_ROCKCHIP
> +       help
> +         Choose this option if you want to use DMABUF feature for DRM.
> +         device drivers need dma buffers, we should use dma mapping APIs,
> +         if DRM_ROCKCHIP_IOMMU is not support, CMA should work behind
> +         kernel DMA mapping.

btw, is there a good reason to let the user disable dmabuf (or iommu
for that matter)?

> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> new file mode 100644
> index 0000000..6768319
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -0,0 +1,14 @@
> +#
> +# Makefile for the drm device driver.  This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_encoder.o \
> +               rockchip_drm_crtc.o rockchip_drm_fbdev.o rockchip_drm_fb.o \
> +               rockchip_drm_buf.o rockchip_drm_gem.o rockchip_drm_core.o \
> +               rockchip_drm_plane.o
> +
> +rockchipdrm-$(CONFIG_DRM_ROCKCHIP_IOMMU) += rockchip_drm_iommu.o
> +rockchipdrm-$(CONFIG_DRM_ROCKCHIP_DMABUF) += rockchip_drm_dmabuf.o
> +
> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_buf.c b/drivers/gpu/drm/rockchip/rockchip_drm_buf.c
> new file mode 100644
> index 0000000..513d7c1
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_buf.c
> @@ -0,0 +1,191 @@
> +/* rockchip_drm_buf.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_buf.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_buf.h"
> +#include "rockchip_drm_iommu.h"
> +
> +static int lowlevel_buffer_allocate(struct drm_device *dev,
> +               unsigned int flags, struct rockchip_drm_gem_buf *buf)
> +{
> +       int ret = 0;
> +       enum dma_attr attr;
> +       unsigned int nr_pages;
> +
> +       if (buf->dma_addr) {
> +               DRM_DEBUG_KMS("already allocated.\n");
> +               return 0;
> +       }
> +
> +       init_dma_attrs(&buf->dma_attrs);
> +
> +       /*
> +        * if ROCKCHIP_BO_CONTIG, fully physically contiguous memory
> +        * region will be allocated else physically contiguous
> +        * as possible.
> +        */
> +       if (!(flags & ROCKCHIP_BO_NONCONTIG))
> +               dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs);
> +
> +       /*
> +        * if ROCKCHIP_BO_WC or ROCKCHIP_BO_NONCACHABLE, writecombine mapping
> +        * else cachable mapping.
> +        */
> +       if (flags & ROCKCHIP_BO_WC || !(flags & ROCKCHIP_BO_CACHABLE))
> +               attr = DMA_ATTR_WRITE_COMBINE;
> +       else
> +               attr = DMA_ATTR_NON_CONSISTENT;
> +
> +       dma_set_attr(attr, &buf->dma_attrs);
> +       dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs);
> +
> +       nr_pages = buf->size >> PAGE_SHIFT;
> +
> +       if (!is_drm_iommu_supported(dev)) {
> +               dma_addr_t start_addr;
> +               unsigned int i = 0;
> +
> +               buf->pages = drm_calloc_large(nr_pages, sizeof(struct page *));
> +               if (!buf->pages) {
> +                       DRM_ERROR("failed to allocate pages.\n");
> +                       return -ENOMEM;
> +               }
> +
> +               buf->kvaddr = (void __iomem *)dma_alloc_attrs(dev->dev,
> +                                       buf->size,
> +                                       &buf->dma_addr, GFP_KERNEL,
> +                                       &buf->dma_attrs);
> +               if (!buf->kvaddr) {
> +                       DRM_ERROR("failed to allocate buffer.\n");
> +                       ret = -ENOMEM;
> +                       goto err_free;
> +               }
> +
> +               start_addr = buf->dma_addr;
> +               while (i < nr_pages) {
> +                       buf->pages[i] = phys_to_page(start_addr);
> +                       start_addr += PAGE_SIZE;
> +                       i++;
> +               }
> +       } else {
> +
> +               buf->pages = dma_alloc_attrs(dev->dev, buf->size,
> +                                       &buf->dma_addr, GFP_KERNEL,
> +                                       &buf->dma_attrs);
> +               if (!buf->pages) {
> +                       DRM_ERROR("failed to allocate buffer.\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages);
> +       if (IS_ERR(buf->sgt)) {
> +               DRM_ERROR("failed to get sg table.\n");
> +               ret = PTR_ERR(buf->sgt);
> +               goto err_free_attrs;
> +       }
> +
> +       DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
> +                       (unsigned long)buf->dma_addr,
> +                       buf->size);
> +
> +       return ret;
> +
> +err_free_attrs:
> +       dma_free_attrs(dev->dev, buf->size, buf->pages,
> +                       (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
> +       buf->dma_addr = (dma_addr_t)NULL;
> +err_free:
> +       if (!is_drm_iommu_supported(dev))
> +               drm_free_large(buf->pages);
> +
> +       return ret;
> +}
> +
> +static void lowlevel_buffer_deallocate(struct drm_device *dev,
> +               unsigned int flags, struct rockchip_drm_gem_buf *buf)
> +{
> +       if (!buf->dma_addr) {
> +               DRM_DEBUG_KMS("dma_addr is invalid.\n");
> +               return;
> +       }
> +
> +       DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
> +                       (unsigned long)buf->dma_addr,
> +                       buf->size);
> +
> +       sg_free_table(buf->sgt);
> +
> +       kfree(buf->sgt);
> +       buf->sgt = NULL;
> +
> +       if (!is_drm_iommu_supported(dev)) {
> +               dma_free_attrs(dev->dev, buf->size, buf->kvaddr,
> +                               (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
> +               drm_free_large(buf->pages);
> +       } else
> +               dma_free_attrs(dev->dev, buf->size, buf->pages,
> +                               (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
> +
> +       buf->dma_addr = (dma_addr_t)NULL;
> +}
> +
> +struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
> +                                               unsigned int size)
> +{
> +       struct rockchip_drm_gem_buf *buffer;
> +
> +       DRM_DEBUG_KMS("desired size = 0x%x\n", size);
> +
> +       buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
> +       if (!buffer)
> +               return NULL;
> +
> +       buffer->size = size;
> +
> +       return buffer;
> +}
> +
> +void rockchip_drm_fini_buf(struct drm_device *dev,
> +                               struct rockchip_drm_gem_buf *buffer)
> +{
> +       kfree(buffer);
> +       buffer = NULL;
> +}
> +
> +int rockchip_drm_alloc_buf(struct drm_device *dev,
> +               struct rockchip_drm_gem_buf *buf, unsigned int flags)
> +{
> +       /*
> +        * allocate memory region and set the memory information
> +        * to vaddr and dma_addr of a buffer object.
> +        */
> +       if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +void rockchip_drm_free_buf(struct drm_device *dev,
> +               unsigned int flags, struct rockchip_drm_gem_buf *buffer)
> +{
> +       lowlevel_buffer_deallocate(dev, flags, buffer);
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_buf.h b/drivers/gpu/drm/rockchip/rockchip_drm_buf.h
> new file mode 100644
> index 0000000..a4e5d00
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_buf.h
> @@ -0,0 +1,39 @@
> +/* rockchip_drm_buf.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_buf.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_BUF_H_
> +#define _ROCKCHIP_DRM_BUF_H_
> +
> +/* create and initialize buffer object. */
> +struct rockchip_drm_gem_buf *rockchip_drm_init_buf(struct drm_device *dev,
> +                                               unsigned int size);
> +
> +/* destroy buffer object. */
> +void rockchip_drm_fini_buf(struct drm_device *dev,
> +                               struct rockchip_drm_gem_buf *buffer);
> +
> +/* allocate physical memory region and setup sgt. */
> +int rockchip_drm_alloc_buf(struct drm_device *dev,
> +                               struct rockchip_drm_gem_buf *buf,
> +                               unsigned int flags);
> +
> +/* release physical memory region, and sgt. */
> +void rockchip_drm_free_buf(struct drm_device *dev,
> +                               unsigned int flags,
> +                               struct rockchip_drm_gem_buf *buffer);
> +
> +#endif /* _ROCKCHIP_DRM_BUF_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.c b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
> new file mode 100644
> index 0000000..3005788
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
> @@ -0,0 +1,261 @@
> +/* rockchip_drm_connector.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_connector.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include <drm/rockchip_drm.h>
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_encoder.h"
> +#include "rockchip_drm_connector.h"
> +
> +#define to_rockchip_connector(x) \
> +               container_of(x, struct rockchip_drm_connector, drm_connector)
> +
> +struct rockchip_drm_connector {
> +       struct drm_connector drm_connector;
> +       struct rockchip_drm_display *display;
> +       uint32_t encoder_id;
> +};
> +
> +static int rockchip_drm_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct rockchip_drm_connector *rockchip_connector =
> +                                       to_rockchip_connector(connector);
> +       struct rockchip_drm_display *display = rockchip_connector->display;
> +       struct edid *edid = NULL;
> +       unsigned int count = 0;
> +       int ret;
> +
> +       /*
> +        * if get_edid() exists then get_edid() callback of hdmi side
> +        * is called to get edid data through i2c interface else
> +        * get timing from the FIMD driver(display controller).
> +        *
> +        * P.S. in case of lcd panel, count is always 1 if success
> +        * because lcd panel has only one mode.
> +        */
> +       if (display->ops->get_edid) {
> +               edid = display->ops->get_edid(display, connector);
> +               if (IS_ERR_OR_NULL(edid)) {
> +                       ret = PTR_ERR(edid);
> +                       edid = NULL;
> +                       DRM_ERROR("Panel operation get_edid failed %d\n", ret);
> +                       goto out;
> +               }
> +
> +               count = drm_add_edid_modes(connector, edid);
> +               if (!count) {
> +                       DRM_ERROR("Add edid modes failed %d\n", count);
> +                       goto out;
> +               }
> +
> +               drm_mode_connector_update_edid_property(connector, edid);
> +       } else {
> +               struct rockchip_drm_panel_info *panel;
> +               struct drm_display_mode *mode =
> +                                       drm_mode_create(connector->dev);
> +               if (!mode) {
> +                       DRM_ERROR("failed to create a new display mode.\n");
> +                       return 0;
> +               }
> +
> +               if (display->ops->get_panel)
> +                       panel = display->ops->get_panel(display);
> +               else {
> +                       drm_mode_destroy(connector->dev, mode);
> +                       return 0;
> +               }
> +
> +               drm_display_mode_from_videomode(&panel->vm, mode);
> +               mode->width_mm = panel->width_mm;
> +               mode->height_mm = panel->height_mm;
> +               connector->display_info.width_mm = mode->width_mm;
> +               connector->display_info.height_mm = mode->height_mm;
> +
> +               mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +               drm_mode_set_name(mode);
> +               drm_mode_probed_add(connector, mode);
> +
> +               count = 1;
> +       }
> +
> +out:
> +       kfree(edid);
> +       return count;
> +}
> +
> +static int rockchip_drm_connector_mode_valid(struct drm_connector *connector,
> +                                           struct drm_display_mode *mode)
> +{
> +       struct rockchip_drm_connector *rockchip_connector =
> +                                       to_rockchip_connector(connector);
> +       struct rockchip_drm_display *display = rockchip_connector->display;
> +       int ret = MODE_BAD;
> +
> +       if (display->ops->check_mode)
> +               if (!display->ops->check_mode(display, mode))
> +                       ret = MODE_OK;
> +
> +       return ret;
> +}
> +
> +static struct drm_encoder *rockchip_drm_best_encoder(
> +               struct drm_connector *connector)
> +{
> +       struct drm_device *dev = connector->dev;
> +       struct rockchip_drm_connector *rockchip_connector =
> +                                       to_rockchip_connector(connector);
> +       struct drm_mode_object *obj;
> +       struct drm_encoder *encoder;
> +
> +       obj = drm_mode_object_find(dev, rockchip_connector->encoder_id,
> +                                  DRM_MODE_OBJECT_ENCODER);
> +       if (!obj) {
> +               DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
> +                               rockchip_connector->encoder_id);
> +               return NULL;
> +       }
> +
> +       encoder = obj_to_encoder(obj);
> +
> +       return encoder;
> +}
> +
> +static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
> +       .get_modes = rockchip_drm_connector_get_modes,
> +       .mode_valid = rockchip_drm_connector_mode_valid,
> +       .best_encoder = rockchip_drm_best_encoder,
> +};
> +
> +static int rockchip_drm_connector_fill_modes(struct drm_connector *connector,
> +                       unsigned int max_width, unsigned int max_height)
> +{
> +       struct rockchip_drm_connector *rockchip_connector =
> +                                       to_rockchip_connector(connector);
> +       struct rockchip_drm_display *display = rockchip_connector->display;
> +       unsigned int width, height;
> +
> +       width = max_width;
> +       height = max_height;
> +
> +       /*
> +        * if specific driver want to find desired_mode using maxmum
> +        * resolution then get max width and height from that driver.
> +        */
> +       if (display->ops->get_max_resol)
> +               display->ops->get_max_resol(display, &width, &height);
> +
> +       return drm_helper_probe_single_connector_modes(connector, width,
> +                                                       height);
> +}
> +
> +/* get detection status of display device. */
> +static enum drm_connector_status
> +rockchip_drm_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct rockchip_drm_connector *rockchip_connector =
> +                                       to_rockchip_connector(connector);
> +       struct rockchip_drm_display *display = rockchip_connector->display;
> +       enum drm_connector_status status = connector_status_disconnected;
> +
> +       if (display->ops->is_connected) {
> +               if (display->ops->is_connected(display))
> +                       status = connector_status_connected;
> +               else
> +                       status = connector_status_disconnected;
> +       }
> +
> +       return status;
> +}
> +
> +static void rockchip_drm_connector_destroy(struct drm_connector *connector)
> +{
> +       struct rockchip_drm_connector *rockchip_connector =
> +               to_rockchip_connector(connector);
> +
> +       drm_sysfs_connector_remove(connector);
> +       drm_connector_cleanup(connector);
> +       kfree(rockchip_connector);
> +}
> +
> +static struct drm_connector_funcs rockchip_connector_funcs = {
> +       .dpms = drm_helper_connector_dpms,
> +       .fill_modes = rockchip_drm_connector_fill_modes,
> +       .detect = rockchip_drm_connector_detect,
> +       .destroy = rockchip_drm_connector_destroy,
> +};
> +
> +struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
> +                                                  struct drm_encoder *encoder)
> +{
> +       struct rockchip_drm_connector *rockchip_connector;
> +       struct rockchip_drm_display *display =
> +                               rockchip_drm_get_display(encoder);
> +       struct drm_connector *connector;
> +       int type;
> +       int err;
> +
> +       rockchip_connector = kzalloc(sizeof(*rockchip_connector), GFP_KERNEL);
> +       if (!rockchip_connector)
> +               return NULL;
> +
> +       connector = &rockchip_connector->drm_connector;
> +
> +       switch (display->type) {
> +       case ROCKCHIP_DISPLAY_TYPE_HDMI:
> +               type = DRM_MODE_CONNECTOR_HDMIA;
> +               connector->interlace_allowed = true;
> +               connector->polled = DRM_CONNECTOR_POLL_HPD;
> +               break;
> +       case ROCKCHIP_DISPLAY_TYPE_LCD:
> +               type = DRM_MODE_CONNECTOR_LVDS;
> +               break;
> +       default:
> +               type = DRM_MODE_CONNECTOR_Unknown;
> +               break;
> +       }
> +
> +       drm_connector_init(dev, connector, &rockchip_connector_funcs, type);
> +       drm_connector_helper_add(connector, &rockchip_connector_helper_funcs);
> +
> +       err = drm_sysfs_connector_add(connector);
> +       if (err)
> +               goto err_connector;
> +
> +       rockchip_connector->encoder_id = encoder->base.id;
> +       rockchip_connector->display = display;
> +       connector->dpms = DRM_MODE_DPMS_OFF;
> +       connector->encoder = encoder;
> +
> +       err = drm_mode_connector_attach_encoder(connector, encoder);
> +       if (err) {
> +               DRM_ERROR("failed to attach a connector to a encoder\n");
> +               goto err_sysfs;
> +       }
> +
> +       DRM_DEBUG_KMS("connector has been created\n");
> +
> +       return connector;
> +
> +err_sysfs:
> +       drm_sysfs_connector_remove(connector);
> +err_connector:
> +       drm_connector_cleanup(connector);
> +       kfree(rockchip_connector);
> +       return NULL;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.h b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
> new file mode 100644
> index 0000000..6235aba
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
> @@ -0,0 +1,24 @@
> +/* rockchip_drm_connector.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_connector.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_CONNECTOR_H_
> +#define _ROCKCHIP_DRM_CONNECTOR_H_
> +
> +struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
> +                                               struct drm_encoder *encoder);
> +
> +#endif /* _ROCKCHIP_DRM_CONNECTOR_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_core.c b/drivers/gpu/drm/rockchip/rockchip_drm_core.c
> new file mode 100644
> index 0000000..ebdd3d0
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_core.c
> @@ -0,0 +1,163 @@
> +/* rockchip_drm_core.c
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_core.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_crtc.h"
> +#include "rockchip_drm_encoder.h"
> +#include "rockchip_drm_fbdev.h"
> +
> +static LIST_HEAD(rockchip_drm_subdrv_list);
> +
> +int rockchip_drm_create_enc_conn(struct drm_device *dev,
> +                                       struct rockchip_drm_display *display)
> +{
> +       struct drm_encoder *encoder;
> +       int ret;
> +       unsigned long possible_crtcs = 0;
> +
> +       ret = rockchip_drm_crtc_get_pipe_from_type(dev, display->type);
> +       if (ret < 0)
> +               return ret;
> +
> +       possible_crtcs |= 1 << ret;
> +
> +       /* create and initialize a encoder for this sub driver. */
> +       encoder = rockchip_drm_encoder_create(dev, display, possible_crtcs);
> +       if (!encoder) {
> +               DRM_ERROR("failed to create encoder\n");
> +               return -EFAULT;
> +       }
> +
> +       display->encoder = encoder;
> +
> +       ret = display->ops->create_connector(display, encoder);
> +       if (ret) {
> +               DRM_ERROR("failed to create connector ret = %d\n", ret);
> +               goto err_destroy_encoder;
> +       }
> +
> +       return 0;
> +
> +err_destroy_encoder:
> +       encoder->funcs->destroy(encoder);
> +       return ret;
> +}
> +
> +int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *subdrv)

hmm..  this does look like it at least could be partially replaced
with the 'componentised device support'

see http://lists.freedesktop.org/archives/dri-devel/2014-January/051249.html

> +{
> +       if (!subdrv)
> +               return -EINVAL;
> +
> +       list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_register);
> +
> +int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *subdrv)
> +{
> +       if (!subdrv)
> +               return -EINVAL;
> +
> +       list_del(&subdrv->list);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_unregister);
> +
> +int rockchip_drm_device_subdrv_probe(struct drm_device *dev)
> +{
> +       struct rockchip_drm_subdrv *subdrv, *n;
> +       int err;
> +
> +       if (!dev)
> +               return -EINVAL;
> +
> +       list_for_each_entry_safe(subdrv, n, &rockchip_drm_subdrv_list, list) {
> +               if (subdrv->probe) {
> +                       subdrv->drm_dev = dev;
> +
> +                       /*
> +                        * this probe callback would be called by sub driver
> +                        * after setting of all resources to this sub driver,
> +                        * such as clock, irq and register map are done.
> +                        */
> +                       err = subdrv->probe(dev, subdrv->dev);
> +                       if (err) {
> +                               DRM_DEBUG("rockchip drm subdrv probe fail.\n");
> +                               list_del(&subdrv->list);
> +                               continue;
> +                       }
> +               }
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_device_subdrv_probe);
> +
> +int rockchip_drm_device_subdrv_remove(struct drm_device *dev)
> +{
> +       struct rockchip_drm_subdrv *subdrv;
> +
> +       if (!dev) {
> +               WARN(1, "Unexpected drm device unregister!\n");
> +               return -EINVAL;
> +       }
> +
> +       list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
> +               if (subdrv->remove)
> +                       subdrv->remove(dev, subdrv->dev);
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_device_subdrv_remove);
> +
> +int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
> +{
> +       struct rockchip_drm_subdrv *subdrv;
> +       int ret;
> +
> +       list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
> +               if (subdrv->open) {
> +                       ret = subdrv->open(dev, subdrv->dev, file);
> +                       if (ret)
> +                               goto err;
> +               }
> +       }
> +
> +       return 0;
> +
> +err:
> +       list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
> +               if (subdrv->close)
> +                       subdrv->close(dev, subdrv->dev, file);
> +       }
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_open);
> +
> +void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
> +{
> +       struct rockchip_drm_subdrv *subdrv;
> +
> +       list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
> +               if (subdrv->close)
> +                       subdrv->close(dev, subdrv->dev, file);
> +       }
> +}
> +EXPORT_SYMBOL_GPL(rockchip_drm_subdrv_close);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_crtc.c b/drivers/gpu/drm/rockchip/rockchip_drm_crtc.c
> new file mode 100644
> index 0000000..2400940
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_crtc.c
> @@ -0,0 +1,515 @@
> +/* rockchip_drm_crtc.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_crtc.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_crtc.h"
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_encoder.h"
> +#include "rockchip_drm_plane.h"
> +
> +#define to_rockchip_crtc(x)    container_of(x, struct rockchip_drm_crtc,\
> +                               drm_crtc)
> +
> +enum rockchip_crtc_mode {
> +       /* normal mode */
> +       CRTC_MODE_NORMAL,
> +       /* The private plane of crtc is blank */
> +       CRTC_MODE_BLANK,
> +};
> +
> +/*
> + * Rockchip specific crtc structure.
> + *
> + * @drm_crtc: crtc object.
> + * @drm_plane: pointer of private plane object for this crtc
> + * @manager: the manager associated with this crtc
> + * @pipe: a crtc index created at load() with a new crtc object creation
> + *      and the crtc object would be set to private->crtc array
> + *      to get a crtc object corresponding to this pipe from private->crtc
> + *      array when irq interrupt occurred. the reason of using this pipe is that
> + *      drm framework doesn't support multiple irq yet.
> + *      we can refer to the crtc to current hardware interrupt occurred through
> + *      this pipe value.
> + * @dpms: store the crtc dpms value
> + * @mode: store the crtc mode value
> + */
> +struct rockchip_drm_crtc {
> +       struct drm_crtc drm_crtc;
> +       struct drm_plane *plane;
> +       struct rockchip_drm_manager *manager;
> +       wait_queue_head_t pending_flip_queue;
> +       enum rockchip_crtc_mode mode;
> +       atomic_t pending_flip;
> +       unsigned int pipe;
> +       unsigned int dpms;
> +};
> +
> +static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +
> +       DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
> +
> +       if (rockchip_crtc->dpms == mode) {
> +               DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
> +               return;
> +       }
> +
> +       if (mode > DRM_MODE_DPMS_ON) {
> +               /* wait for the completion of page flip. */
> +               wait_event(rockchip_crtc->pending_flip_queue,
> +                       atomic_read(&rockchip_crtc->pending_flip) == 0);
> +               drm_vblank_off(crtc->dev, rockchip_crtc->pipe);
> +       }
> +
> +       if (manager->ops->dpms)
> +               manager->ops->dpms(manager, mode);
> +
> +       rockchip_crtc->dpms = mode;
> +}
> +
> +static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       /* drm framework doesn't check NULL. */
> +}
> +
> +static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +
> +       rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> +
> +       rockchip_plane_commit(rockchip_crtc->plane);
> +
> +       if (manager->ops->commit)
> +               manager->ops->commit(manager);
> +
> +       rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_ON);
> +}
> +
> +static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> +                               const struct drm_display_mode *mode,
> +                               struct drm_display_mode *adjusted_mode)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +
> +       if (manager->ops->mode_fixup)
> +               return manager->ops->mode_fixup(manager, mode, adjusted_mode);
> +
> +       return true;
> +}
> +
> +static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
> +                       struct drm_display_mode *mode,
> +                       struct drm_display_mode *adjusted_mode, int x, int y,
> +                       struct drm_framebuffer *old_fb)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +       struct drm_plane *plane = rockchip_crtc->plane;
> +       unsigned int crtc_w;
> +       unsigned int crtc_h;
> +       int ret;
> +
> +       /*
> +        * copy the mode data adjusted by mode_fixup() into crtc->mode
> +        * so that hardware can be seet to proper mode.
> +        */
> +       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
> +
> +       crtc_w = crtc->primary->fb->width - x;
> +       crtc_h = crtc->primary->fb->height - y;
> +
> +       if (manager->ops->mode_set)
> +               manager->ops->mode_set(manager, &crtc->mode);
> +
> +       ret = rockchip_plane_mode_set(plane, crtc, crtc->primary->fb,
> +                                       0, 0, crtc_w, crtc_h,
> +                                       x, y, crtc_w, crtc_h);
> +       if (ret)
> +               return ret;
> +
> +       plane->crtc = crtc;
> +       plane->fb = crtc->primary->fb;
> +       drm_framebuffer_reference(plane->fb);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_commit(struct drm_crtc *crtc,
> +                               int x, int y, struct drm_framebuffer *old_fb)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct drm_plane *plane = rockchip_crtc->plane;
> +       unsigned int crtc_w;
> +       unsigned int crtc_h;
> +       int ret;
> +
> +       /* when framebuffer changing is requested, crtc's dpms should be on */
> +       if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
> +               DRM_ERROR("failed framebuffer changing request.\n");
> +               return -EPERM;
> +       }
> +
> +       crtc_w = crtc->primary->fb->width - x;
> +       crtc_h = crtc->primary->fb->height - y;
> +
> +       ret = rockchip_plane_mode_set(plane, crtc, crtc->primary->fb,
> +                               0, 0, crtc_w, crtc_h, x, y, crtc_w, crtc_h);
> +       if (ret)
> +               return ret;
> +
> +       rockchip_drm_crtc_commit(crtc);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> +                                         struct drm_framebuffer *old_fb)
> +{
> +       return rockchip_drm_crtc_mode_set_commit(crtc, x, y, old_fb);
> +}
> +
> +static void rockchip_drm_crtc_disable(struct drm_crtc *crtc)
> +{
> +       struct drm_plane *plane;
> +       int ret;
> +
> +       rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
> +
> +       drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
> +               if (plane->crtc != crtc)
> +                       continue;
> +
> +               ret = plane->funcs->disable_plane(plane);
> +               if (ret)
> +                       DRM_ERROR("Failed to disable plane %d\n", ret);
> +       }
> +}
> +
> +static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
> +       .dpms = rockchip_drm_crtc_dpms,
> +       .prepare = rockchip_drm_crtc_prepare,
> +       .commit = rockchip_drm_crtc_commit,
> +       .mode_fixup = rockchip_drm_crtc_mode_fixup,
> +       .mode_set = rockchip_drm_crtc_mode_set,
> +       .mode_set_base = rockchip_drm_crtc_mode_set_base,
> +       .disable = rockchip_drm_crtc_disable,
> +};
> +
> +static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
> +                                    struct drm_framebuffer *fb,
> +                                    struct drm_pending_vblank_event *event,
> +                                    uint32_t page_flip_flags)
> +{
> +       struct drm_device *dev = crtc->dev;
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct drm_framebuffer *old_fb = crtc->primary->fb;
> +       int ret = -EINVAL;
> +
> +       /* when the page flip is requested, crtc's dpms should be on */
> +       if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
> +               DRM_ERROR("failed page flip request.\n");
> +               return -EINVAL;
> +       }
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       if (event) {
> +               /*
> +                * the pipe from user always is 0 so we can set pipe number
> +                * of current owner to event.
> +                */
> +               event->pipe = rockchip_crtc->pipe;
> +
> +               ret = drm_vblank_get(dev, rockchip_crtc->pipe);
> +               if (ret) {
> +                       DRM_DEBUG("failed to acquire vblank counter\n");
> +
> +                       goto out;
> +               }
> +
> +               spin_lock_irq(&dev->event_lock);
> +               list_add_tail(&event->base.link,
> +                               &dev_priv->pageflip_event_list);

you don't need a list.  Return -EBUSY if userspace tries to queue a
flip when there is already one pending

> +               atomic_set(&rockchip_crtc->pending_flip, 1);
> +               spin_unlock_irq(&dev->event_lock);
> +
> +               crtc->primary->fb = fb;
> +               ret = rockchip_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,
> +                                                   NULL);

so.. 'event' could be NULL if userspace doesn't ask for an event.  So
you probably don't want to ignore the pageflip if !event

> +               if (ret) {
> +                       crtc->primary->fb = old_fb;
> +
> +                       spin_lock_irq(&dev->event_lock);
> +                       drm_vblank_put(dev, rockchip_crtc->pipe);
> +                       list_del(&event->base.link);
> +                       spin_unlock_irq(&dev->event_lock);
> +
> +                       goto out;
> +               }
> +       }
> +out:
> +       mutex_unlock(&dev->struct_mutex);
> +       return ret;
> +}
> +
> +static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +       struct rockchip_drm_private *private = crtc->dev->dev_private;
> +
> +       private->crtc[rockchip_crtc->pipe] = NULL;
> +
> +       drm_crtc_cleanup(crtc);
> +       kfree(rockchip_crtc);
> +}
> +
> +static int rockchip_drm_crtc_set_property(struct drm_crtc *crtc,
> +                                       struct drm_property *property,
> +                                       uint64_t val)
> +{
> +       struct drm_device *dev = crtc->dev;
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
> +
> +       if (property == dev_priv->crtc_mode_property) {
> +               enum rockchip_crtc_mode mode = val;
> +
> +               if (mode == rockchip_crtc->mode)
> +                       return 0;
> +
> +               rockchip_crtc->mode = mode;
> +
> +               switch (mode) {
> +               case CRTC_MODE_NORMAL:
> +                       rockchip_drm_crtc_commit(crtc);
> +                       break;
> +               case CRTC_MODE_BLANK:
> +                       rockchip_plane_dpms(rockchip_crtc->plane,
> +                                         DRM_MODE_DPMS_OFF);
> +                       break;
> +               default:
> +                       break;
> +               }
> +
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static struct drm_crtc_funcs rockchip_crtc_funcs = {
> +       .set_config = drm_crtc_helper_set_config,
> +       .page_flip = rockchip_drm_crtc_page_flip,
> +       .destroy = rockchip_drm_crtc_destroy,
> +       .set_property = rockchip_drm_crtc_set_property,
> +};
> +
> +static const struct drm_prop_enum_list mode_names[] = {
> +       { CRTC_MODE_NORMAL, "normal" },
> +       { CRTC_MODE_BLANK, "blank" },
> +};
> +
> +static void rockchip_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
> +{
> +       struct drm_device *dev = crtc->dev;
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct drm_property *prop;
> +
> +       prop = dev_priv->crtc_mode_property;
> +       if (!prop) {
> +               prop = drm_property_create_enum(dev, 0, "mode", mode_names,
> +                                               ARRAY_SIZE(mode_names));

ok, this looks a bit sketchy.  If you need a custom property to do
what the DPMS property on the connector does, it really seems like you
are probably doing something wrong.

Also, for driver custom properties, they need to be documented in
Documentation/DocBook/drm.tmpl.

But I'd really prefer if new drivers started out without any custom
properties, and then added properties as follow-on patches to make
sure userspace facing interfaces get properly reviewed.

> +               if (!prop)
> +                       return;
> +
> +               dev_priv->crtc_mode_property = prop;
> +       }
> +
> +       drm_object_attach_property(&crtc->base, prop, 0);
> +}
> +
> +int rockchip_drm_crtc_create(struct rockchip_drm_manager *manager)
> +{
> +       struct rockchip_drm_crtc *rockchip_crtc;
> +       struct rockchip_drm_private *private = manager->drm_dev->dev_private;
> +       struct drm_crtc *crtc;
> +
> +       rockchip_crtc = kzalloc(sizeof(*rockchip_crtc), GFP_KERNEL);
> +       if (!rockchip_crtc)
> +               return -ENOMEM;
> +
> +       init_waitqueue_head(&rockchip_crtc->pending_flip_queue);
> +       atomic_set(&rockchip_crtc->pending_flip, 0);
> +
> +       rockchip_crtc->dpms = DRM_MODE_DPMS_OFF;
> +       rockchip_crtc->manager = manager;
> +       rockchip_crtc->pipe = manager->pipe;
> +       rockchip_crtc->plane = rockchip_plane_init(manager->drm_dev,
> +                               1 << manager->pipe, true);
> +       if (!rockchip_crtc->plane) {
> +               kfree(rockchip_crtc);
> +               return -ENOMEM;
> +       }
> +
> +       manager->crtc = &rockchip_crtc->drm_crtc;
> +       crtc = &rockchip_crtc->drm_crtc;
> +
> +       private->crtc[manager->pipe] = crtc;
> +
> +       drm_crtc_init(manager->drm_dev, crtc, &rockchip_crtc_funcs);
> +       drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
> +
> +       rockchip_drm_crtc_attach_mode_property(crtc);
> +
> +       return 0;
> +}
> +
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct rockchip_drm_crtc *rockchip_crtc =
> +               to_rockchip_crtc(private->crtc[pipe]);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +
> +       if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
> +               return -EPERM;
> +
> +       if (manager->ops->enable_vblank)
> +               manager->ops->enable_vblank(manager);

if mgr doesn't support vblank, maybe you should return -EINVAL?

> +
> +       return 0;
> +}
> +
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct rockchip_drm_crtc *rockchip_crtc =
> +               to_rockchip_crtc(private->crtc[pipe]);
> +       struct rockchip_drm_manager *manager = rockchip_crtc->manager;
> +
> +       if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
> +               return;
> +
> +       if (manager->ops->disable_vblank)
> +               manager->ops->disable_vblank(manager);
> +}
> +
> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
> +{
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct drm_pending_vblank_event *e, *t;
> +       struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
> +       struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(drm_crtc);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +
> +       list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
> +                       base.link) {
> +               /* if event's pipe isn't same as crtc then ignore it. */
> +               if (pipe != e->pipe)
> +                       continue;
> +
> +               list_del(&e->base.link);
> +               drm_send_vblank_event(dev, -1, e);
> +               drm_vblank_put(dev, pipe);
> +               atomic_set(&rockchip_crtc->pending_flip, 0);
> +               wake_up(&rockchip_crtc->pending_flip_queue);
> +       }
> +
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +void rockchip_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
> +                       struct rockchip_drm_overlay *overlay)
> +{
> +       struct rockchip_drm_manager *manager = to_rockchip_crtc(crtc)->manager;
> +
> +       if (manager->ops->win_mode_set)
> +               manager->ops->win_mode_set(manager, overlay);
> +}
> +
> +void rockchip_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos)
> +{
> +       struct rockchip_drm_manager *manager = to_rockchip_crtc(crtc)->manager;
> +
> +       if (manager->ops->win_commit)
> +               manager->ops->win_commit(manager, zpos);
> +}
> +
> +void rockchip_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos)
> +{
> +       struct rockchip_drm_manager *manager = to_rockchip_crtc(crtc)->manager;
> +
> +       if (manager->ops->win_enable)
> +               manager->ops->win_enable(manager, zpos);
> +}
> +
> +void rockchip_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos)
> +{
> +       struct rockchip_drm_manager *manager = to_rockchip_crtc(crtc)->manager;
> +
> +       if (manager->ops->win_disable)
> +               manager->ops->win_disable(manager, zpos);
> +}
> +
> +void rockchip_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
> +{
> +       struct rockchip_drm_manager *manager;
> +       struct drm_device *dev = fb->dev;
> +       struct drm_crtc *crtc;
> +
> +       /*
> +        * make sure that overlay data are updated to real hardware
> +        * for all encoders.
> +        */
> +       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> +               manager = to_rockchip_crtc(crtc)->manager;
> +
> +               /*
> +                * wait for vblank interrupt
> +                * - this makes sure that overlay data are updated to
> +                *     real hardware.
> +                */
> +               if (manager->ops->wait_for_vblank)
> +                       manager->ops->wait_for_vblank(manager);
> +       }
> +}
> +
> +int rockchip_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
> +                                       unsigned int out_type)
> +{
> +       struct drm_crtc *crtc;
> +
> +       list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
> +               struct rockchip_drm_crtc *rockchip_crtc;
> +
> +               rockchip_crtc = to_rockchip_crtc(crtc);
> +               if (rockchip_crtc->manager->type == out_type)
> +                       return rockchip_crtc->manager->pipe;
> +       }
> +
> +       return -EPERM;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_crtc.h b/drivers/gpu/drm/rockchip/rockchip_drm_crtc.h
> new file mode 100644
> index 0000000..bbf7214
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_crtc.h
> @@ -0,0 +1,42 @@
> +/* rockchip_drm_crtc.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_crtc.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_CRTC_H_
> +#define _ROCKCHIP_DRM_CRTC_H_
> +
> +struct drm_device;
> +struct drm_crtc;
> +struct rockchip_drm_manager;
> +struct rockchip_drm_overlay;
> +
> +int rockchip_drm_crtc_create(struct rockchip_drm_manager *manager);
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
> +
> +void rockchip_drm_crtc_plane_mode_set(struct drm_crtc *crtc,
> +                       struct rockchip_drm_overlay *overlay);
> +void rockchip_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
> +void rockchip_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
> +void rockchip_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
> +
> +/* This function gets pipe value to crtc device matched with out_type. */
> +int rockchip_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
> +                                       unsigned int out_type);
> +
> +#endif /* _ROCKCHIP_DRM_CRTC_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.c b/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.c
> new file mode 100644
> index 0000000..d3e237d
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.c
> @@ -0,0 +1,290 @@
> +/* rockchip_drm_dmabuf.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_dmabuf.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/dma-buf.h>
> +#include <drm/drmP.h>
> +#include <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_dmabuf.h"
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +
> +
> +struct rockchip_drm_dmabuf_attachment {
> +       struct sg_table sgt;
> +       enum dma_data_direction dir;
> +       bool is_mapped;
> +};
> +
> +static struct rockchip_drm_gem_obj *dma_buf_to_obj(struct dma_buf *buf)
> +{
> +       return to_rockchip_gem_obj(buf->priv);
> +}
> +
> +static int rockchip_gem_attach_dma_buf(struct dma_buf *dmabuf,
> +                                       struct device *dev,
> +                                       struct dma_buf_attachment *attach)
> +{
> +       struct rockchip_drm_dmabuf_attachment *rockchip_attach;
> +
> +       rockchip_attach = kzalloc(sizeof(*rockchip_attach), GFP_KERNEL);
> +       if (!rockchip_attach)
> +               return -ENOMEM;
> +
> +       rockchip_attach->dir = DMA_NONE;
> +       attach->priv = rockchip_attach;
> +
> +       return 0;
> +}
> +
> +static void rockchip_gem_detach_dma_buf(struct dma_buf *dmabuf,
> +                                       struct dma_buf_attachment *attach)
> +{
> +       struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
> +       struct sg_table *sgt;
> +
> +       if (!rockchip_attach)
> +               return;
> +
> +       sgt = &rockchip_attach->sgt;
> +
> +       if (rockchip_attach->dir != DMA_NONE)
> +               dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
> +                               rockchip_attach->dir);
> +
> +       sg_free_table(sgt);
> +       kfree(rockchip_attach);
> +       attach->priv = NULL;
> +}
> +
> +static struct sg_table *
> +               rockchip_gem_map_dma_buf(struct dma_buf_attachment *attach,
> +                                       enum dma_data_direction dir)
> +{
> +       struct rockchip_drm_dmabuf_attachment *rockchip_attach = attach->priv;
> +       struct rockchip_drm_gem_obj *gem_obj = dma_buf_to_obj(attach->dmabuf);
> +       struct drm_device *dev = gem_obj->base.dev;
> +       struct rockchip_drm_gem_buf *buf;
> +       struct scatterlist *rd, *wr;
> +       struct sg_table *sgt = NULL;
> +       unsigned int i;
> +       int nents, ret;
> +
> +       /* just return current sgt if already requested. */
> +       if (rockchip_attach->dir == dir && rockchip_attach->is_mapped)
> +               return &rockchip_attach->sgt;
> +
> +       buf = gem_obj->buffer;
> +       if (!buf) {
> +               DRM_ERROR("buffer is null.\n");
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       sgt = &rockchip_attach->sgt;
> +
> +       ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL);
> +       if (ret) {
> +               DRM_ERROR("failed to alloc sgt.\n");
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       rd = buf->sgt->sgl;
> +       wr = sgt->sgl;
> +       for (i = 0; i < sgt->orig_nents; ++i) {
> +               sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
> +               rd = sg_next(rd);
> +               wr = sg_next(wr);
> +       }
> +
> +       if (dir != DMA_NONE) {
> +               nents = dma_map_sg(attach->dev, sgt->sgl,
> +                                       sgt->orig_nents, dir);
> +               if (!nents) {
> +                       DRM_ERROR("failed to map sgl with iommu.\n");
> +                       sg_free_table(sgt);
> +                       sgt = ERR_PTR(-EIO);
> +                       goto err_unlock;
> +               }
> +       }
> +
> +       rockchip_attach->is_mapped = true;
> +       rockchip_attach->dir = dir;
> +       attach->priv = rockchip_attach;
> +
> +       DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size);
> +
> +err_unlock:
> +       mutex_unlock(&dev->struct_mutex);
> +       return sgt;
> +}
> +
> +static void rockchip_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
> +                                               struct sg_table *sgt,
> +                                               enum dma_data_direction dir)
> +{
> +       /* Nothing to do. */
> +}
> +
> +static void *rockchip_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
> +                                               unsigned long page_num)
> +{
> +       /* TODO */
> +
> +       return NULL;
> +}
> +
> +static void rockchip_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
> +                                               unsigned long page_num,
> +                                               void *addr)
> +{
> +       /* TODO */
> +}
> +
> +static void *rockchip_gem_dmabuf_kmap(struct dma_buf *dma_buf,
> +                                       unsigned long page_num)
> +{
> +       /* TODO */
> +
> +       return NULL;
> +}
> +
> +static void rockchip_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
> +                                       unsigned long page_num, void *addr)
> +{
> +       /* TODO */
> +}
> +
> +static int rockchip_gem_dmabuf_mmap(struct dma_buf *dma_buf,
> +       struct vm_area_struct *vma)
> +{
> +       return -ENOTTY;
> +}
> +
> +static struct dma_buf_ops rockchip_dmabuf_ops = {
> +       .attach = rockchip_gem_attach_dma_buf,
> +       .detach = rockchip_gem_detach_dma_buf,
> +       .map_dma_buf = rockchip_gem_map_dma_buf,
> +       .unmap_dma_buf = rockchip_gem_unmap_dma_buf,
> +       .kmap = rockchip_gem_dmabuf_kmap,
> +       .kmap_atomic = rockchip_gem_dmabuf_kmap_atomic,
> +       .kunmap = rockchip_gem_dmabuf_kunmap,
> +       .kunmap_atomic = rockchip_gem_dmabuf_kunmap_atomic,
> +       .mmap = rockchip_gem_dmabuf_mmap,
> +       .release = drm_gem_dmabuf_release,
> +};

Hmm, you probably should have a look at the prime-helpers (ie.
drm_gem_prime_export(), etc).. I guess the exynos dmabuf code
pre-dates the helpers, if that was the example you are working off of.
At least for drm/msm, they made my dmabuf implementation somewhat more
simple.

> +struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
> +                               struct drm_gem_object *obj, int flags)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj =
> +                                               to_rockchip_gem_obj(obj);
> +
> +       return dma_buf_export(obj, &rockchip_dmabuf_ops,
> +                               rockchip_gem_obj->base.size, flags);
> +}
> +
> +struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
> +                               struct dma_buf *dma_buf)
> +{
> +       struct dma_buf_attachment *attach;
> +       struct sg_table *sgt;
> +       struct scatterlist *sgl;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct rockchip_drm_gem_buf *buffer;
> +       int ret;
> +
> +       /* is this one of own objects? */
> +       if (dma_buf->ops == &rockchip_dmabuf_ops) {
> +               struct drm_gem_object *obj;
> +
> +               obj = dma_buf->priv;
> +
> +               /* is it from our device? */
> +               if (obj->dev == drm_dev) {
> +                       /*
> +                        * Importing dmabuf exported from out own gem increases
> +                        * refcount on gem itself instead of f_count of dmabuf.
> +                        */
> +                       drm_gem_object_reference(obj);
> +                       return obj;
> +               }
> +       }
> +
> +       attach = dma_buf_attach(dma_buf, drm_dev->dev);
> +       if (IS_ERR(attach))
> +               return ERR_PTR(-EINVAL);
> +
> +       get_dma_buf(dma_buf);
> +
> +       sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> +       if (IS_ERR(sgt)) {
> +               ret = PTR_ERR(sgt);
> +               goto err_buf_detach;
> +       }
> +
> +       buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
> +       if (!buffer) {
> +               ret = -ENOMEM;
> +               goto err_unmap_attach;
> +       }
> +
> +       rockchip_gem_obj = rockchip_drm_gem_init(drm_dev, dma_buf->size);
> +       if (!rockchip_gem_obj) {
> +               ret = -ENOMEM;
> +               goto err_free_buffer;
> +       }
> +
> +       sgl = sgt->sgl;
> +
> +       buffer->size = dma_buf->size;
> +       buffer->dma_addr = sg_dma_address(sgl);
> +
> +       if (sgt->nents == 1) {
> +               /* always physically continuous memory if sgt->nents is 1. */
> +               rockchip_gem_obj->flags |= ROCKCHIP_BO_CONTIG;
> +       } else {
> +               /*
> +                * this case could be CONTIG or NONCONTIG type but for now
> +                * sets NONCONTIG.
> +                * TODO. we have to find a way that exporter can notify
> +                * the type of its own buffer to importer.
> +                */
> +               rockchip_gem_obj->flags |= ROCKCHIP_BO_NONCONTIG;
> +       }
> +
> +       rockchip_gem_obj->buffer = buffer;
> +       buffer->sgt = sgt;
> +       rockchip_gem_obj->base.import_attach = attach;
> +
> +       DRM_DEBUG_PRIME("dma_addr = %pad, size = 0x%lx\n", &buffer->dma_addr,
> +                                                               buffer->size);
> +
> +       return &rockchip_gem_obj->base;
> +
> +err_free_buffer:
> +       kfree(buffer);
> +       buffer = NULL;
> +err_unmap_attach:
> +       dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
> +err_buf_detach:
> +       dma_buf_detach(dma_buf, attach);
> +       dma_buf_put(dma_buf);
> +
> +       return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.h b/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.h
> new file mode 100644
> index 0000000..93c1c77
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_dmabuf.h
> @@ -0,0 +1,32 @@
> +/* rockchip_drm_dmabuf.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_dmabuf.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_DMABUF_H_
> +#define _ROCKCHIP_DRM_DMABUF_H_
> +
> +#ifdef CONFIG_DRM_ROCKCHIP_DMABUF
> +struct dma_buf *rockchip_dmabuf_prime_export(struct drm_device *drm_dev,
> +                               struct drm_gem_object *obj, int flags);
> +
> +struct drm_gem_object *rockchip_dmabuf_prime_import(struct drm_device *drm_dev,
> +                                               struct dma_buf *dma_buf);
> +#else
> +#define rockchip_dmabuf_prime_export           NULL
> +#define rockchip_dmabuf_prime_import           NULL
> +#endif
> +
> +#endif /* _ROCKCHIP_DRM_DMABUF_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> new file mode 100644
> index 0000000..33136ba
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -0,0 +1,618 @@
> +/* rockchip_drm_drv.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_drv.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/pm_runtime.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/component.h>
> +
> +#include <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_crtc.h"
> +#include "rockchip_drm_encoder.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_plane.h"
> +#include "rockchip_drm_dmabuf.h"
> +#include "rockchip_drm_iommu.h"
> +
> +#define DRIVER_NAME    "rockchip"
> +#define DRIVER_DESC    "rockchip Soc DRM"
> +#define DRIVER_DATE    "20140623"
> +#define DRIVER_MAJOR   1
> +#define DRIVER_MINOR   0
> +
> +#define VBLANK_OFF_DELAY       50000
> +
> +static struct platform_device *rockchip_drm_pdev;
> +
> +static DEFINE_MUTEX(drm_component_lock);
> +static LIST_HEAD(drm_component_list);
> +
> +struct component_dev {
> +       struct list_head list;
> +       struct device *crtc_dev;
> +       struct device *conn_dev;
> +       enum rockchip_drm_output_type out_type;
> +       unsigned int dev_type_flag;
> +};
> +
> +static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
> +{
> +       struct rockchip_drm_private *private;
> +       int ret;
> +       int nr;
> +
> +       private = kzalloc(sizeof(struct rockchip_drm_private), GFP_KERNEL);
> +       if (!private)
> +               return -ENOMEM;
> +
> +       INIT_LIST_HEAD(&private->pageflip_event_list);
> +       dev_set_drvdata(dev->dev, dev);
> +       dev->dev_private = (void *)private;
> +
> +       /*
> +        * create mapping to manage iommu table and set a pointer to iommu
> +        * mapping structure to iommu_mapping of private data.
> +        * also this iommu_mapping can be used to check if iommu is supported
> +        * or not.
> +        */
> +       ret = drm_create_iommu_mapping(dev);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to create iommu mapping.\n");
> +               goto err_free_private;
> +       }
> +
> +       drm_mode_config_init(dev);
> +
> +       rockchip_drm_mode_config_init(dev);
> +
> +       for (nr = 0; nr < MAX_PLANE; nr++) {
> +               struct drm_plane *plane;
> +               unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
> +
> +               plane = rockchip_plane_init(dev, possible_crtcs, false);
> +               if (!plane)
> +                       goto err_mode_config_cleanup;
> +       }
> +
> +       /* init kms poll for handling hpd */
> +       drm_kms_helper_poll_init(dev);
> +
> +       ret = drm_vblank_init(dev, MAX_CRTC);
> +       if (ret)
> +               goto err_mode_config_cleanup;
> +
> +       /* setup possible_clones. */
> +       rockchip_drm_encoder_setup(dev);
> +
> +       drm_vblank_offdelay = VBLANK_OFF_DELAY;
> +
> +       platform_set_drvdata(dev->platformdev, dev);
> +
> +       /* Try to bind all sub drivers. */
> +       ret = component_bind_all(dev->dev, dev);
> +       if (ret)
> +               goto err_cleanup_vblank;
> +
> +       /* Probe non kms sub drivers and virtual display driver. */
> +       ret = rockchip_drm_device_subdrv_probe(dev);
> +       if (ret)
> +               goto err_unbind_all;
> +
> +       /* force connectors detection */
> +       drm_helper_hpd_irq_event(dev);
> +
> +       return 0;
> +
> +err_unbind_all:
> +       component_unbind_all(dev->dev, dev);
> +err_cleanup_vblank:
> +       drm_vblank_cleanup(dev);
> +err_mode_config_cleanup:
> +       drm_mode_config_cleanup(dev);
> +       drm_release_iommu_mapping(dev);
> +err_free_private:
> +       kfree(private);
> +
> +       return ret;
> +}
> +
> +static int rockchip_drm_unload(struct drm_device *dev)
> +{
> +       rockchip_drm_device_subdrv_remove(dev);
> +
> +       rockchip_drm_fbdev_fini(dev);
> +       drm_vblank_cleanup(dev);
> +       drm_kms_helper_poll_fini(dev);
> +       drm_mode_config_cleanup(dev);
> +
> +       drm_release_iommu_mapping(dev);
> +       kfree(dev->dev_private);
> +
> +       component_unbind_all(dev->dev, dev);
> +       dev->dev_private = NULL;
> +
> +       return 0;
> +}
> +
> +static const struct file_operations rockchip_drm_gem_fops = {
> +       .mmap = rockchip_drm_gem_mmap_buffer,
> +};
> +
> +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
> +{
> +       struct drm_connector *connector;
> +
> +       drm_modeset_lock_all(dev);
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               int old_dpms = connector->dpms;
> +
> +               if (connector->funcs->dpms)
> +                       connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
> +
> +               /* Set the old mode back to the connector for resume */
> +               connector->dpms = old_dpms;
> +       }
> +       drm_modeset_unlock_all(dev);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_resume(struct drm_device *dev)
> +{
> +       struct drm_connector *connector;
> +
> +       drm_modeset_lock_all(dev);
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->funcs->dpms)
> +                       connector->funcs->dpms(connector, connector->dpms);
> +       }
> +       drm_modeset_unlock_all(dev);
> +
> +       drm_helper_resume_force_mode(dev);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
> +{
> +       struct drm_rockchip_file_private *file_priv;
> +       struct file *anon_filp;
> +       int ret;
> +
> +       file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
> +       if (!file_priv)
> +               return -ENOMEM;
> +
> +       file->driver_priv = file_priv;
> +
> +       ret = rockchip_drm_subdrv_open(dev, file);
> +       if (ret)
> +               goto err_file_priv_free;
> +
> +       anon_filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
> +                                       NULL, 0);
> +       if (IS_ERR(anon_filp)) {
> +               ret = PTR_ERR(anon_filp);
> +               goto err_subdrv_close;
> +       }
> +
> +       anon_filp->f_mode = FMODE_READ | FMODE_WRITE;
> +       file_priv->anon_filp = anon_filp;
> +
> +       return ret;
> +
> +err_subdrv_close:
> +       rockchip_drm_subdrv_close(dev, file);
> +
> +err_file_priv_free:
> +       kfree(file_priv);
> +       file->driver_priv = NULL;
> +       return ret;
> +}
> +
> +static void rockchip_drm_preclose(struct drm_device *dev,
> +                                       struct drm_file *file)
> +{
> +       rockchip_drm_subdrv_close(dev, file);
> +}
> +
> +static void rockchip_drm_postclose(struct drm_device *dev,
> +                                       struct drm_file *file)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct drm_rockchip_file_private *file_priv;
> +       struct drm_pending_vblank_event *v, *vt;
> +       struct drm_pending_event *e, *et;
> +       unsigned long flags;
> +
> +       if (!file->driver_priv)
> +               return;
> +
> +       /* Release all events not unhandled by page flip handler. */
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +       list_for_each_entry_safe(v, vt, &private->pageflip_event_list,
> +                       base.link) {
> +               if (v->base.file_priv == file) {
> +                       list_del(&v->base.link);
> +                       drm_vblank_put(dev, v->pipe);
> +                       v->base.destroy(&v->base);
> +               }
> +       }
> +
> +       /* Release all events handled by page flip handler but not freed. */
> +       list_for_each_entry_safe(e, et, &file->event_list, link) {
> +               list_del(&e->link);
> +               e->destroy(e);
> +       }
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +       file_priv = file->driver_priv;
> +       if (file_priv->anon_filp)
> +               fput(file_priv->anon_filp);
> +
> +       kfree(file->driver_priv);
> +       file->driver_priv = NULL;
> +}
> +
> +static void rockchip_drm_lastclose(struct drm_device *dev)
> +{
> +       rockchip_drm_fbdev_restore_mode(dev);
> +}
> +
> +static const struct vm_operations_struct rockchip_drm_gem_vm_ops = {
> +       .fault = rockchip_drm_gem_fault,
> +       .open = drm_gem_vm_open,
> +       .close = drm_gem_vm_close,
> +};
> +
> +static const struct drm_ioctl_desc rockchip_ioctls[] = {
> +       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
> +                       DRM_UNLOCKED | DRM_AUTH),
> +       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
> +                       rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
> +                       DRM_AUTH),
> +       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP,
> +                       rockchip_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
> +       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET,
> +                       rockchip_drm_gem_get_ioctl, DRM_UNLOCKED),
> +};
> +
> +static const struct file_operations rockchip_drm_driver_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .mmap = rockchip_drm_gem_mmap,
> +       .poll = drm_poll,
> +       .read = drm_read,
> +       .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> +       .compat_ioctl = drm_compat_ioctl,
> +#endif
> +       .release = drm_release,
> +};
> +
> +static struct drm_driver rockchip_drm_driver = {
> +       .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
> +       .load                   = rockchip_drm_load,
> +       .unload                 = rockchip_drm_unload,
> +       .suspend                = rockchip_drm_suspend,
> +       .resume                 = rockchip_drm_resume,
> +       .open                   = rockchip_drm_open,
> +       .preclose               = rockchip_drm_preclose,
> +       .lastclose              = rockchip_drm_lastclose,
> +       .postclose              = rockchip_drm_postclose,
> +       .get_vblank_counter     = drm_vblank_count,
> +       .enable_vblank          = rockchip_drm_crtc_enable_vblank,
> +       .disable_vblank         = rockchip_drm_crtc_disable_vblank,
> +       .gem_free_object        = rockchip_drm_gem_free_object,
> +       .gem_vm_ops             = &rockchip_drm_gem_vm_ops,
> +       .dumb_create            = rockchip_drm_gem_dumb_create,
> +       .dumb_map_offset        = rockchip_drm_gem_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       = rockchip_dmabuf_prime_export,
> +       .gem_prime_import       = rockchip_dmabuf_prime_import,
> +       .ioctls                 = rockchip_ioctls,
> +       .num_ioctls             = ARRAY_SIZE(rockchip_ioctls),
> +       .fops                   = &rockchip_drm_driver_fops,
> +       .name   = DRIVER_NAME,
> +       .desc   = DRIVER_DESC,
> +       .date   = DRIVER_DATE,
> +       .major  = DRIVER_MAJOR,
> +       .minor  = DRIVER_MINOR,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rockchip_drm_sys_suspend(struct device *dev)
> +{
> +       struct drm_device *drm_dev = dev_get_drvdata(dev);
> +       pm_message_t message;
> +
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       message.event = PM_EVENT_SUSPEND;
> +
> +       return rockchip_drm_suspend(drm_dev, message);
> +}
> +
> +static int rockchip_drm_sys_resume(struct device *dev)
> +{
> +       struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       return rockchip_drm_resume(drm_dev);
> +}
> +#endif
> +
> +static const struct dev_pm_ops rockchip_drm_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
> +                                       rockchip_drm_sys_resume)
> +};
> +
> +int rockchip_drm_component_add(struct device *dev,
> +                               enum rockchip_drm_device_type dev_type,
> +                               enum rockchip_drm_output_type out_type)
> +{
> +       struct component_dev *cdev;
> +
> +       if (dev_type != ROCKCHIP_DEVICE_TYPE_CRTC &&
> +                       dev_type != ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
> +               DRM_ERROR("invalid device type.\n");
> +               return -EINVAL;
> +       }
> +
> +       mutex_lock(&drm_component_lock);
> +
> +       /*
> +        * Make sure to check if there is a component which has two device
> +        * objects, for connector and for encoder/connector.
> +        * It should make sure that crtc and encoder/connector drivers are
> +        * ready before rockchip drm core binds them.
> +        */
> +       list_for_each_entry(cdev, &drm_component_list, list) {
> +               if (cdev->out_type == out_type) {
> +                       /*
> +                        * If crtc and encoder/connector device objects are
> +                        * added already just return.
> +                        */
> +                       if (cdev->dev_type_flag == (ROCKCHIP_DEVICE_TYPE_CRTC |
> +                                       ROCKCHIP_DEVICE_TYPE_CONNECTOR)) {
> +                               mutex_unlock(&drm_component_lock);
> +                               return 0;
> +                       }
> +
> +                       if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
> +                               cdev->crtc_dev = dev;
> +                               cdev->dev_type_flag |= dev_type;
> +                       }
> +
> +                       if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
> +                               cdev->conn_dev = dev;
> +                               cdev->dev_type_flag |= dev_type;
> +                       }
> +
> +                       mutex_unlock(&drm_component_lock);
> +                       return 0;
> +               }
> +       }
> +
> +       mutex_unlock(&drm_component_lock);
> +
> +       cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
> +       if (!cdev)
> +               return -ENOMEM;
> +
> +       if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC)
> +               cdev->crtc_dev = dev;
> +       if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR)
> +               cdev->conn_dev = dev;
> +
> +       cdev->out_type = out_type;
> +       cdev->dev_type_flag = dev_type;
> +
> +       mutex_lock(&drm_component_lock);
> +       list_add_tail(&cdev->list, &drm_component_list);
> +       mutex_unlock(&drm_component_lock);
> +
> +       return 0;
> +}
> +
> +void rockchip_drm_component_del(struct device *dev,
> +                               enum rockchip_drm_device_type dev_type)
> +{
> +       struct component_dev *cdev, *next;
> +
> +       mutex_lock(&drm_component_lock);
> +
> +       list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
> +               if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
> +                       if (cdev->crtc_dev == dev) {
> +                               cdev->crtc_dev = NULL;
> +                               cdev->dev_type_flag &= ~dev_type;
> +                       }
> +               }
> +
> +               if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
> +                       if (cdev->conn_dev == dev) {
> +                               cdev->conn_dev = NULL;
> +                               cdev->dev_type_flag &= ~dev_type;
> +                       }
> +               }
> +
> +               /*
> +                * Release cdev object only in case that both of crtc and
> +                * encoder/connector device objects are NULL.
> +                */
> +               if (!cdev->crtc_dev && !cdev->conn_dev) {
> +                       list_del(&cdev->list);
> +                       kfree(cdev);
> +               }
> +
> +               break;
> +       }
> +
> +       mutex_unlock(&drm_component_lock);
> +}
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       return dev == (struct device *)data;
> +}
> +
> +static int rockchip_drm_add_components(struct device *dev, struct master *m)
> +{
> +       struct component_dev *cdev;
> +       unsigned int attach_cnt = 0;
> +
> +       mutex_lock(&drm_component_lock);
> +
> +       list_for_each_entry(cdev, &drm_component_list, list) {

hmm.. ok.. so you are kinda using the componentised device support.
But really the intention behind that was to get *rid* of global lists
like drm_component_list.  Also, since you only add things that are
already in drm_component_list, it is sort of defeating the point.

> +               int ret;
> +
> +               /*
> +                * Add components to master only in case that crtc and
> +                * encoder/connector device objects exist.
> +                */
> +               if (!cdev->crtc_dev || !cdev->conn_dev)
> +                       continue;
> +
> +               attach_cnt++;
> +
> +               mutex_unlock(&drm_component_lock);
> +
> +               /*
> +                * lcdc and dp modules have same device object so add
> +                * only crtc device object in this case.
> +                *
> +                * TODO. if dp module follows driver-model driver then
> +                * below codes can be removed.
> +                */
> +               if (cdev->crtc_dev == cdev->conn_dev) {
> +                       ret = component_master_add_child(m, compare_of,
> +                                       cdev->crtc_dev);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       goto out_lock;
> +               }
> +
> +               /*
> +                * Do not chage below call order.
> +                * crtc device first should be added to master because
> +                * connector/encoder need pipe number of crtc when they
> +                * are created.
> +                */
> +               ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
> +               ret |= component_master_add_child(m, compare_of,
> +                                                       cdev->conn_dev);
> +               if (ret < 0)
> +                       return ret;
> +
> +out_lock:
> +               mutex_lock(&drm_component_lock);
> +       }
> +
> +       mutex_unlock(&drm_component_lock);
> +
> +       return attach_cnt ? 0 : -ENODEV;
> +}
> +
> +static int rockchip_drm_bind(struct device *dev)
> +{
> +       return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
> +}
> +
> +static void rockchip_drm_unbind(struct device *dev)
> +{
> +       drm_put_dev(dev_get_drvdata(dev));
> +}
> +
> +static const struct component_master_ops rockchip_drm_ops = {
> +       .add_components = rockchip_drm_add_components,
> +       .bind = rockchip_drm_bind,
> +       .unbind = rockchip_drm_unbind,
> +};
> +
> +static int rockchip_drm_platform_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +
> +       pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +       rockchip_drm_driver.num_ioctls = ARRAY_SIZE(rockchip_ioctls);
> +
> +       ret = component_master_add(&pdev->dev, &rockchip_drm_ops);
> +       if (ret < 0)
> +               DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_platform_remove(struct platform_device *pdev)
> +{
> +       component_master_del(&pdev->dev, &rockchip_drm_ops);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver rockchip_drm_platform_driver = {
> +       .probe = rockchip_drm_platform_probe,
> +       .remove = rockchip_drm_platform_remove,
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "rockchip-drm",
> +               .pm = &rockchip_drm_pm_ops,
> +       },
> +};
> +
> +static int rockchip_drm_init(void)
> +{
> +       int ret;
> +
> +       rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
> +                                                               NULL, 0);
> +       if (IS_ERR(rockchip_drm_pdev))
> +               return PTR_ERR(rockchip_drm_pdev);
> +
> +       ret = platform_driver_register(&rockchip_drm_platform_driver);
> +       if (ret)
> +               goto err_unregister_pd;
> +
> +       return 0;
> +
> +err_unregister_pd:
> +       platform_device_unregister(rockchip_drm_pdev);
> +
> +       return ret;
> +}
> +
> +static void rockchip_drm_exit(void)
> +{
> +       platform_device_unregister(rockchip_drm_pdev);
> +       platform_driver_unregister(&rockchip_drm_platform_driver);
> +}
> +
> +module_init(rockchip_drm_init);
> +module_exit(rockchip_drm_exit);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> new file mode 100644
> index 0000000..6340452
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -0,0 +1,319 @@
> +/* rockchip_drm_drv.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_drv.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_DRV_H_
> +#define _ROCKCHIP_DRM_DRV_H_
> +
> +#include <linux/module.h>
> +
> +#define MAX_CRTC       3
> +#define MAX_PLANE      5
> +#define MAX_FB_BUFFER  4
> +#define DEFAULT_ZPOS   -1
> +
> +#define _wait_for(COND, MS) ({ \
> +       unsigned long timeout__ = jiffies + msecs_to_jiffies(MS);       \
> +       int ret__ = 0;                                                  \
> +       while (!(COND)) {                                               \
> +               if (time_after(jiffies, timeout__)) {                   \
> +                       ret__ = -ETIMEDOUT;                             \
> +                       break;                                          \
> +               }                                                       \
> +       }                                                               \
> +       ret__;                                                          \
> +})
> +
> +#define wait_for(COND, MS) _wait_for(COND, MS)
> +
> +struct drm_device;
> +struct drm_connector;
> +struct rockchip_drm_overlay;
> +struct rockchip_drm_manager;
> +
> +extern unsigned int drm_vblank_offdelay;
> +
> +/* This enumerates device type. */
> +enum rockchip_drm_device_type {
> +       ROCKCHIP_DEVICE_TYPE_NONE,
> +       ROCKCHIP_DEVICE_TYPE_CRTC,
> +       ROCKCHIP_DEVICE_TYPE_CONNECTOR,
> +};
> +
> +/* this enumerates display type. */
> +enum rockchip_drm_output_type {
> +       ROCKCHIP_DISPLAY_TYPE_NONE,
> +       /* RGB or CPU Interface. */
> +       ROCKCHIP_DISPLAY_TYPE_LCD,
> +       /* HDMI Interface. */
> +       ROCKCHIP_DISPLAY_TYPE_HDMI,
> +       /* Virtual Display Interface. */
> +       ROCKCHIP_DISPLAY_TYPE_VIDI,
> +};
> +
> +/*
> + * Rockchip drm common overlay structure.
> + *
> + * @fb_x: offset x on a framebuffer to be displayed.
> + *      - the unit is screen coordinates.
> + * @fb_y: offset y on a framebuffer to be displayed.
> + *      - the unit is screen coordinates.
> + * @fb_width: width of a framebuffer.
> + * @fb_height: height of a framebuffer.
> + * @src_width: width of a partial image to be displayed from framebuffer.
> + * @src_height: height of a partial image to be displayed from framebuffer.
> + * @crtc_x: offset x on hardware screen.
> + * @crtc_y: offset y on hardware screen.
> + * @crtc_width: window width to be displayed (hardware screen).
> + * @crtc_height: window height to be displayed (hardware screen).
> + * @mode_width: width of screen mode.
> + * @mode_height: height of screen mode.
> + * @refresh: refresh rate.
> + * @scan_flag: interlace or progressive way.
> + *      (it could be DRM_MODE_FLAG_*)
> + * @bpp: pixel size.(in bit)
> + * @pixel_format: fourcc pixel format of this overlay
> + * @dma_addr: array of bus(accessed by dma) address to the memory region
> + *          allocated for a overlay.
> + * @zpos: order of overlay layer(z position).
> + * @default_win: a window to be enabled.
> + * @color_key: color key on or off.
> + * @index_color: if using color key feature then this value would be used
> + *             as index color.
> + * @local_path: in case of lcd type, local path mode on or off.
> + * @transparency: transparency on or off.
> + * @activated: activated or not.
> + *
> + * this structure is common to rockchip SoC and its contents would be copied
> + * to hardware specific overlay info.
> + */
> +struct rockchip_drm_overlay {
> +       unsigned int fb_x;
> +       unsigned int fb_y;
> +       unsigned int fb_width;
> +       unsigned int fb_height;
> +       unsigned int src_width;
> +       unsigned int src_height;
> +       unsigned int crtc_x;
> +       unsigned int crtc_y;
> +       unsigned int crtc_width;
> +       unsigned int crtc_height;
> +       unsigned int mode_width;
> +       unsigned int mode_height;
> +       unsigned int refresh;
> +       unsigned int scan_flag;
> +       unsigned int bpp;
> +       unsigned int pitch;
> +       uint32_t pixel_format;
> +       dma_addr_t dma_addr[MAX_FB_BUFFER];
> +       int zpos;
> +
> +       bool default_win;
> +       bool color_key;
> +       unsigned int index_color;
> +       bool local_path;
> +       bool transparency;
> +       bool activated;
> +};
> +
> +/*
> + * Rockchip DRM Display Structure.
> + *     - this structure is common to analog tv, digital tv and lcd panel.
> + *
> + * @remove: cleans up the display for removal
> + * @mode_fixup: fix mode data comparing to hw specific display mode.
> + * @mode_set: convert drm_display_mode to hw specific display mode and
> + *            would be called by encoder->mode_set().
> + * @check_mode: check if mode is valid or not.
> + * @dpms: display device on or off.
> + * @commit: apply changes to hw
> + */
> +struct rockchip_drm_display;
> +struct rockchip_drm_display_ops {
> +       int (*create_connector)(struct rockchip_drm_display *display,
> +                               struct drm_encoder *encoder);
> +       void (*remove)(struct rockchip_drm_display *display);
> +       void (*mode_fixup)(struct rockchip_drm_display *display,
> +                               struct drm_connector *connector,
> +                               const struct drm_display_mode *mode,
> +                               struct drm_display_mode *adjusted_mode);
> +       void (*mode_set)(struct rockchip_drm_display *display,
> +                               struct drm_display_mode *mode);
> +       int (*check_mode)(struct rockchip_drm_display *display,
> +                               struct drm_display_mode *mode);
> +       void (*dpms)(struct rockchip_drm_display *display, int mode);
> +       void (*commit)(struct rockchip_drm_display *display);
> +};
> +
> +/*
> + * Rockchip drm display structure, maps 1:1 with an encoder/connector
> + *
> + * @list: the list entry for this manager
> + * @type: one of ROCKCHIP_DISPLAY_TYPE_LCD and HDMI.
> + * @encoder: encoder object this display maps to
> + * @connector: connector object this display maps to
> + * @ops: pointer to callbacks for rockchip drm specific functionality
> + * @ctx: A pointer to the display's implementation specific context
> + */
> +struct rockchip_drm_display {
> +       struct list_head list;
> +       enum rockchip_drm_output_type type;
> +       struct drm_encoder *encoder;
> +       struct drm_connector *connector;
> +       struct rockchip_drm_display_ops *ops;
> +       void *ctx;
> +};
> +
> +/*
> + * Rockchip drm manager ops
> + *
> + * @dpms: control device power.
> + * @mode_fixup: fix mode data before applying it
> + * @mode_set: set the given mode to the manager
> + * @commit: set current hw specific display mode to hw.
> + * @enable_vblank: specific driver callback for enabling vblank interrupt.
> + * @disable_vblank: specific driver callback for disabling vblank interrupt.
> + * @wait_for_vblank: wait for vblank interrupt to make sure that
> + *      hardware overlay is updated.
> + * @win_mode_set: copy drm overlay info to hw specific overlay info.
> + * @win_commit: apply hardware specific overlay data to registers.
> + * @win_enable: enable hardware specific overlay.
> + * @win_disable: disable hardware specific overlay.
> + */
> +struct rockchip_drm_manager_ops {
> +       void (*dpms)(struct rockchip_drm_manager *mgr, int mode);
> +       bool (*mode_fixup)(struct rockchip_drm_manager *mgr,
> +                               const struct drm_display_mode *mode,
> +                               struct drm_display_mode *adjusted_mode);
> +       void (*mode_set)(struct rockchip_drm_manager *mgr,
> +                               const struct drm_display_mode *mode);
> +       void (*commit)(struct rockchip_drm_manager *mgr);
> +       int (*enable_vblank)(struct rockchip_drm_manager *mgr);
> +       void (*disable_vblank)(struct rockchip_drm_manager *mgr);
> +       void (*wait_for_vblank)(struct rockchip_drm_manager *mgr);
> +       void (*win_mode_set)(struct rockchip_drm_manager *mgr,
> +                               struct rockchip_drm_overlay *overlay);
> +       void (*win_commit)(struct rockchip_drm_manager *mgr, int zpos);
> +       void (*win_enable)(struct rockchip_drm_manager *mgr, int zpos);
> +       void (*win_disable)(struct rockchip_drm_manager *mgr, int zpos);
> +};
> +
> +/*
> + * Rockchip drm common manager structure, maps 1:1 with a crtc
> + *
> + * @list: the list entry for this manager
> + * @type: one of ROCKCHIP_DISPLAY_TYPE_LCD and HDMI.
> + * @drm_dev: pointer to the drm device
> + * @crtc: crtc object.
> + * @pipe: the pipe number for this crtc/manager
> + * @ops: pointer to callbacks for rockchip drm specific functionality
> + * @ctx: A pointer to the manager's implementation specific context
> + */
> +struct rockchip_drm_manager {
> +       struct list_head list;
> +       enum rockchip_drm_output_type type;
> +       struct drm_device *drm_dev;
> +       struct drm_crtc *crtc;
> +       int pipe;
> +       struct rockchip_drm_manager_ops *ops;
> +       void *ctx;
> +};
> +
> +struct drm_rockchip_file_private {
> +       struct file *anon_filp;
> +};
> +
> +/*
> + * Rockchip drm private structure.
> + *
> + * @da_start: start address to device address space.
> + *      with iommu, device address space starts from this address
> + *      otherwise default one.
> + * @da_space_size: size of device address space.
> + *      if 0 then default value is used for it.
> + * @pipe: the pipe number for this crtc/manager.
> + */
> +struct rockchip_drm_private {
> +       struct drm_fb_helper *fb_helper;
> +
> +       /* list head for new event to be added. */
> +       struct list_head pageflip_event_list;
> +
> +       /*
> +        * created crtc object would be contained at this array and
> +        * this array is used to be aware of which crtc did it request vblank.
> +        */
> +       struct drm_crtc *crtc[MAX_CRTC];
> +       struct drm_property *plane_zpos_property;
> +       struct drm_property *crtc_mode_property;
> +
> +       unsigned long da_start;
> +       unsigned long da_space_size;
> +
> +       unsigned int pipe;
> +};
> +
> +/*
> + * Rockchip drm sub driver structure.
> + *
> + * @list: sub driver has its own list object to register to rockchip drm driver.
> + * @dev: pointer to device object for subdrv device driver.
> + * @drm_dev: pointer to drm_device and this pointer would be set
> + *      when sub driver calls rockchip_drm_subdrv_register().
> + * @manager: subdrv has its own manager to control a hardware appropriately
> + *     and we can access a hardware drawing on this manager.
> + * @probe: this callback would be called by rockchip drm driver after
> + *     subdrv is registered to it.
> + * @remove: this callback is used to release resources created
> + *     by probe callback.
> + * @open: this would be called with drm device file open.
> + * @close: this would be called with drm device file close.
> + */
> +struct rockchip_drm_subdrv {
> +       struct list_head list;
> +       struct device *dev;
> +       struct drm_device *drm_dev;
> +
> +       int (*probe)(struct drm_device *drm_dev, struct device *dev);
> +       void (*remove)(struct drm_device *drm_dev, struct device *dev);
> +       int (*open)(struct drm_device *drm_dev, struct device *dev,
> +                       struct drm_file *file);
> +       void (*close)(struct drm_device *drm_dev, struct device *dev,
> +                       struct drm_file *file);
> +};
> +
> + /* This function would be called by non kms drivers. */
> +int rockchip_drm_subdrv_register(struct rockchip_drm_subdrv *drm_subdrv);
> +
> +/* this function removes subdrv list from rockchip drm driver */
> +int rockchip_drm_subdrv_unregister(struct rockchip_drm_subdrv *drm_subdrv);
> +
> +int rockchip_drm_device_subdrv_probe(struct drm_device *dev);
> +int rockchip_drm_device_subdrv_remove(struct drm_device *dev);
> +int rockchip_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
> +void rockchip_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
> +
> +/* This function creates a encoder and a connector, and initializes them. */
> +int rockchip_drm_create_enc_conn(struct drm_device *dev,
> +                               struct rockchip_drm_display *display);
> +
> +int rockchip_drm_component_add(struct device *dev,
> +                               enum rockchip_drm_device_type dev_type,
> +                               enum rockchip_drm_output_type out_type);
> +
> +void rockchip_drm_component_del(struct device *dev,
> +                               enum rockchip_drm_device_type dev_type);
> +#endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_encoder.c b/drivers/gpu/drm/rockchip/rockchip_drm_encoder.c
> new file mode 100644
> index 0000000..adc82ed
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_encoder.c
> @@ -0,0 +1,206 @@
> +/* rockchip_drm_encoder.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_encoder.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_encoder.h"
> +
> +#define to_rockchip_encoder(x) container_of(x, struct rockchip_drm_encoder,\
> +                               drm_encoder)
> +
> +/*
> + * rockchip specific encoder structure.
> + *
> + * @drm_encoder: encoder object.
> + * @display: the display structure that maps to this encoder
> + */
> +struct rockchip_drm_encoder {
> +       struct drm_encoder drm_encoder;
> +       struct rockchip_drm_display *display;
> +};
> +
> +static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +       struct rockchip_drm_display *display = rockchip_encoder->display;
> +
> +       DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
> +
> +       if (display->ops->dpms)
> +               display->ops->dpms(display, mode);
> +}
> +
> +static bool
> +rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
> +                              const struct drm_display_mode *mode,
> +                              struct drm_display_mode *adjusted_mode)
> +{
> +       struct drm_device *dev = encoder->dev;
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +       struct rockchip_drm_display *display = rockchip_encoder->display;
> +       struct drm_connector *connector;
> +
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->encoder != encoder)
> +                       continue;
> +
> +               if (display->ops->mode_fixup)
> +                       display->ops->mode_fixup(display, connector, mode,
> +                                       adjusted_mode);
> +       }
> +
> +       return true;
> +}
> +
> +static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
> +                                       struct drm_display_mode *mode,
> +                                       struct drm_display_mode *adjusted_mode)
> +{
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +       struct rockchip_drm_display *display = rockchip_encoder->display;
> +
> +       if (display->ops->mode_set)
> +               display->ops->mode_set(display, adjusted_mode);
> +}
> +
> +static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
> +{
> +       /* drm framework doesn't check NULL. */
> +}
> +
> +static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
> +{
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +       struct rockchip_drm_display *display = rockchip_encoder->display;
> +
> +       if (display->ops->dpms)
> +               display->ops->dpms(display, DRM_MODE_DPMS_ON);
> +
> +       if (display->ops->commit)
> +               display->ops->commit(display);
> +}
> +
> +static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct drm_plane *plane;
> +       struct drm_device *dev = encoder->dev;
> +
> +       rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> +
> +       /* all planes connected to this encoder should be also disabled. */
> +       drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
> +               if (plane->crtc == encoder->crtc)
> +                       plane->funcs->disable_plane(plane);
> +       }
> +}
> +
> +static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
> +       .dpms = rockchip_drm_encoder_dpms,
> +       .mode_fixup = rockchip_drm_encoder_mode_fixup,
> +       .mode_set = rockchip_drm_encoder_mode_set,
> +       .prepare = rockchip_drm_encoder_prepare,
> +       .commit = rockchip_drm_encoder_commit,
> +       .disable = rockchip_drm_encoder_disable,
> +};
> +
> +static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
> +{
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +
> +       drm_encoder_cleanup(encoder);
> +       kfree(rockchip_encoder);
> +}
> +
> +static struct drm_encoder_funcs rockchip_encoder_funcs = {
> +       .destroy = rockchip_drm_encoder_destroy,
> +};
> +
> +static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
> +{
> +       struct drm_encoder *clone;
> +       struct drm_device *dev = encoder->dev;
> +       struct rockchip_drm_encoder *rockchip_encoder =
> +                                               to_rockchip_encoder(encoder);
> +       struct rockchip_drm_display *display = rockchip_encoder->display;
> +       unsigned int clone_mask = 0;
> +       int cnt = 0;
> +
> +       list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
> +               switch (display->type) {
> +               case ROCKCHIP_DISPLAY_TYPE_LCD:
> +               case ROCKCHIP_DISPLAY_TYPE_HDMI:
> +                       clone_mask |= (1 << (cnt++));
> +                       break;
> +               default:
> +                       continue;
> +               }
> +       }
> +
> +       return clone_mask;
> +}
> +
> +void rockchip_drm_encoder_setup(struct drm_device *dev)
> +{
> +       struct drm_encoder *encoder;
> +
> +       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
> +               encoder->possible_clones =
> +                               rockchip_drm_encoder_clones(encoder);
> +}
> +
> +struct drm_encoder *rockchip_drm_encoder_create(struct drm_device *dev,
> +                                       struct rockchip_drm_display *display,
> +                                       unsigned long possible_crtcs)
> +{
> +       struct drm_encoder *encoder;
> +       struct rockchip_drm_encoder *rockchip_encoder;
> +
> +       if (!possible_crtcs)
> +               return NULL;
> +
> +       rockchip_encoder = kzalloc(sizeof(*rockchip_encoder), GFP_KERNEL);
> +       if (!rockchip_encoder)
> +               return NULL;
> +
> +       rockchip_encoder->display = display;
> +       encoder = &rockchip_encoder->drm_encoder;
> +       encoder->possible_crtcs = possible_crtcs;
> +
> +       DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +       drm_encoder_init(dev, encoder, &rockchip_encoder_funcs,
> +                       DRM_MODE_ENCODER_TMDS);
> +
> +       drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
> +
> +       DRM_DEBUG_KMS("encoder has been created\n");
> +
> +       return encoder;
> +}
> +
> +struct rockchip_drm_display *
> +               rockchip_drm_get_display(struct drm_encoder *encoder)
> +{
> +       return to_rockchip_encoder(encoder)->display;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_encoder.h b/drivers/gpu/drm/rockchip/rockchip_drm_encoder.h
> new file mode 100644
> index 0000000..38b7dc0
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_encoder.h
> @@ -0,0 +1,30 @@
> +/* rockchip_drm_encoder.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_encoder.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_ENCODER_H_
> +#define _ROCKCHIP_DRM_ENCODER_H_
> +
> +struct rockchip_drm_manager;
> +
> +void rockchip_drm_encoder_setup(struct drm_device *dev);
> +struct drm_encoder *rockchip_drm_encoder_create(struct drm_device *dev,
> +                       struct rockchip_drm_display *mgr,
> +                       unsigned long possible_crtcs);
> +struct rockchip_drm_display *
> +               rockchip_drm_get_display(struct drm_encoder *encoder);
> +
> +#endif /* _ROCKCHIP_DRM_ENCODER_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> new file mode 100644
> index 0000000..ca27d74
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -0,0 +1,333 @@
> +/* rockchip_drm_fb.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_fb.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <uapi/drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_iommu.h"
> +#include "rockchip_drm_crtc.h"
> +
> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> +
> +/*
> + * rockchip specific framebuffer structure.
> + *
> + * @fb: drm framebuffer obejct.
> + * @buf_cnt: a buffer count to drm framebuffer.
> + * @rockchip_gem_obj: array of rockchip specific
> + *                    gem object containing a gem object.
> + */
> +struct rockchip_drm_fb {
> +       struct drm_framebuffer fb;
> +       unsigned int buf_cnt;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj[MAX_FB_BUFFER];
> +};
> +
> +static int check_fb_gem_memory_type(struct drm_device *drm_dev,
> +                               struct rockchip_drm_gem_obj *rockchip_gem_obj)
> +{
> +       unsigned int flags;
> +
> +       /*
> +        * if rockchip drm driver supports iommu then framebuffer can use
> +        * all the buffer types.
> +        */
> +       if (is_drm_iommu_supported(drm_dev))
> +               return 0;
> +
> +       flags = rockchip_gem_obj->flags;
> +
> +       /*
> +        * without iommu support, not support physically non-continuous memory
> +        * for framebuffer.
> +        */
> +       if (IS_NONCONTIG_BUFFER(flags)) {
> +               DRM_ERROR("cannot use this gem memory type for fb.\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +       unsigned int i;
> +
> +       /* make sure that overlay data are updated before relesing fb. */
> +       rockchip_drm_crtc_complete_scanout(fb);
> +
> +       drm_framebuffer_cleanup(fb);
> +
> +       for (i = 0; i < ARRAY_SIZE(rockchip_fb->rockchip_gem_obj); i++) {
> +               struct drm_gem_object *obj;
> +
> +               if (rockchip_fb->rockchip_gem_obj[i] == NULL)
> +                       continue;
> +
> +               obj = &rockchip_fb->rockchip_gem_obj[i]->base;
> +               drm_gem_object_unreference_unlocked(obj);
> +       }
> +
> +       kfree(rockchip_fb);
> +       rockchip_fb = NULL;
> +}
> +
> +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> +                                       struct drm_file *file_priv,
> +                                       unsigned int *handle)
> +{
> +       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +
> +       /* This fb should have only one gem object. */
> +       if (WARN_ON(rockchip_fb->buf_cnt != 1))
> +               return -EINVAL;
> +
> +       return drm_gem_handle_create(file_priv,
> +                       &rockchip_fb->rockchip_gem_obj[0]->base, handle);
> +}
> +
> +static int rockchip_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 rockchip_drm_fb_funcs = {
> +       .destroy = rockchip_drm_fb_destroy,
> +       .create_handle = rockchip_drm_fb_create_handle,
> +       .dirty = rockchip_drm_fb_dirty,
> +};
> +
> +void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
> +                                               unsigned int cnt)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +
> +       rockchip_fb = to_rockchip_fb(fb);
> +
> +       rockchip_fb->buf_cnt = cnt;
> +}
> +
> +unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +
> +       rockchip_fb = to_rockchip_fb(fb);
> +
> +       return rockchip_fb->buf_cnt;
> +}
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> +                           struct drm_mode_fb_cmd2 *mode_cmd,
> +                           struct drm_gem_object *obj)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       int ret;
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
> +       if (ret < 0) {
> +               DRM_ERROR("cannot use this gem memory type for fb.\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
> +       if (!rockchip_fb)
> +               return ERR_PTR(-ENOMEM);
> +
> +       drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
> +       rockchip_fb->rockchip_gem_obj[0] = rockchip_gem_obj;
> +
> +       ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
> +                                       &rockchip_drm_fb_funcs);
> +       if (ret) {
> +               DRM_ERROR("failed to initialize framebuffer\n");
> +               return ERR_PTR(ret);
> +       }
> +
> +       return &rockchip_fb->fb;
> +}
> +
> +static u32 rockchip_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +       unsigned int cnt = 0;
> +
> +       if (mode_cmd->pixel_format != DRM_FORMAT_NV12)
> +               return drm_format_num_planes(mode_cmd->pixel_format);
> +
> +       while (cnt != MAX_FB_BUFFER) {
> +               if (!mode_cmd->handles[cnt])
> +                       break;
> +               cnt++;
> +       }
> +
> +       /*
> +        * check if NV12 or NV12M.
> +        *
> +        * NV12
> +        * handles[0] = base1, offsets[0] = 0
> +        * handles[1] = base1, offsets[1] = Y_size
> +        *
> +        * NV12M
> +        * handles[0] = base1, offsets[0] = 0
> +        * handles[1] = base2, offsets[1] = 0
> +        */
> +       if (cnt == 2) {
> +               /*
> +                * in case of NV12 format, offsets[1] is not 0 and
> +                * handles[0] is same as handles[1].
> +                */
> +               if (mode_cmd->offsets[1] &&
> +                       mode_cmd->handles[0] == mode_cmd->handles[1])
> +                       cnt = 1;
> +       }
> +
> +       return cnt;
> +}
> +
> +static struct drm_framebuffer *
> +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> +                     struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +       struct drm_gem_object *obj;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct rockchip_drm_fb *rockchip_fb;
> +       int i, ret;
> +
> +       rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
> +       if (!rockchip_fb)
> +               return ERR_PTR(-ENOMEM);
> +
> +       obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object\n");
> +               ret = -ENOENT;
> +               goto err_free;
> +       }
> +
> +       drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
> +       rockchip_fb->rockchip_gem_obj[0] = to_rockchip_gem_obj(obj);
> +       rockchip_fb->buf_cnt = rockchip_drm_format_num_buffers(mode_cmd);
> +
> +       DRM_DEBUG_KMS("buf_cnt = %d\n", rockchip_fb->buf_cnt);
> +
> +       for (i = 1; i < rockchip_fb->buf_cnt; i++) {
> +               obj = drm_gem_object_lookup(dev, file_priv,
> +                               mode_cmd->handles[i]);
> +               if (!obj) {
> +                       DRM_ERROR("failed to lookup gem object\n");
> +                       ret = -ENOENT;
> +                       rockchip_fb->buf_cnt = i;
> +                       goto err_unreference;
> +               }
> +
> +               rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +               rockchip_fb->rockchip_gem_obj[i] = rockchip_gem_obj;
> +
> +               ret = check_fb_gem_memory_type(dev, rockchip_gem_obj);
> +               if (ret < 0) {
> +                       DRM_ERROR("cannot use this gem memory type for fb.\n");
> +                       goto err_unreference;
> +               }
> +       }
> +
> +       ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
> +                                       &rockchip_drm_fb_funcs);
> +       if (ret) {
> +               DRM_ERROR("failed to init framebuffer.\n");
> +               goto err_unreference;
> +       }
> +
> +       return &rockchip_fb->fb;
> +
> +err_unreference:
> +       for (i = 0; i < rockchip_fb->buf_cnt; i++) {
> +               struct drm_gem_object *obj;
> +
> +               obj = &rockchip_fb->rockchip_gem_obj[i]->base;
> +               if (obj)
> +                       drm_gem_object_unreference_unlocked(obj);
> +       }
> +err_free:
> +       kfree(rockchip_fb);
> +       return ERR_PTR(ret);
> +}
> +
> +struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
> +                                               int index)
> +{
> +       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +       struct rockchip_drm_gem_buf *buffer;
> +
> +       if (index >= MAX_FB_BUFFER)
> +               return NULL;
> +
> +       buffer = rockchip_fb->rockchip_gem_obj[index]->buffer;
> +       if (!buffer)
> +               return NULL;
> +
> +       DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr);
> +
> +       return buffer;
> +}
> +
> +static void rockchip_drm_output_poll_changed(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct drm_fb_helper *fb_helper = private->fb_helper;
> +
> +       if (fb_helper)
> +               drm_fb_helper_hotplug_event(fb_helper);
> +       else
> +               rockchip_drm_fbdev_init(dev);
> +}
> +
> +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
> +       .fb_create = rockchip_user_fb_create,
> +       .output_poll_changed = rockchip_drm_output_poll_changed,
> +};
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev)
> +{
> +       dev->mode_config.min_width = 0;
> +       dev->mode_config.min_height = 0;
> +
> +       /*
> +        * set max width and height as default value(4096x4096).
> +        * this value would be used to check framebuffer size limitation
> +        * at drm_mode_addfb().
> +        */
> +       dev->mode_config.max_width = 4096;
> +       dev->mode_config.max_height = 4096;
> +
> +       dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> new file mode 100644
> index 0000000..1639ab20
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> @@ -0,0 +1,39 @@
> +/* rockchip_drm_fb.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_fb.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FB_H_
> +#define _ROCKCHIP_DRM_FB_H_
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> +                           struct drm_mode_fb_cmd2 *mode_cmd,
> +                           struct drm_gem_object *obj);
> +
> +/* get memory information of a drm framebuffer */
> +struct rockchip_drm_gem_buf *rockchip_drm_fb_buffer(struct drm_framebuffer *fb,
> +                                                int index);
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev);
> +
> +/* set a buffer count to drm framebuffer. */
> +void rockchip_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
> +                                               unsigned int cnt);
> +
> +/* get a buffer count to drm framebuffer. */
> +unsigned int rockchip_drm_fb_get_buf_cnt(struct drm_framebuffer *fb);
> +
> +#endif /* _ROCKCHIP_DRM_FB_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> new file mode 100644
> index 0000000..49f41a9
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> @@ -0,0 +1,380 @@
> +/* rockchip_drm_fbdev.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_fbdev.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_iommu.h"
> +
> +#define MAX_CONNECTOR          4
> +#define PREFERRED_BPP          32
> +
> +#define to_rockchip_fbdev(x)   container_of(x, struct rockchip_drm_fbdev,\
> +                               drm_fb_helper)
> +
> +struct rockchip_drm_fbdev {
> +       struct drm_fb_helper drm_fb_helper;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +};
> +
> +static int rockchip_drm_fb_mmap(struct fb_info *info,
> +                       struct vm_area_struct *vma)
> +{
> +       struct drm_fb_helper *helper = info->par;
> +       struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(helper);
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj =
> +                                       rockchip_fbd->rockchip_gem_obj;
> +       struct rockchip_drm_gem_buf *buffer = rockchip_gem_obj->buffer;
> +       unsigned long vm_size;
> +       int ret;
> +
> +       vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
> +
> +       vm_size = vma->vm_end - vma->vm_start;
> +
> +       if (vm_size > buffer->size)
> +               return -EINVAL;
> +
> +       ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
> +               buffer->dma_addr, buffer->size, &buffer->dma_attrs);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to mmap.\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static struct fb_ops rockchip_drm_fb_ops = {
> +       .owner = THIS_MODULE,
> +       .fb_mmap = rockchip_drm_fb_mmap,
> +       .fb_fillrect = cfb_fillrect,
> +       .fb_copyarea = cfb_copyarea,
> +       .fb_imageblit = cfb_imageblit,
> +       .fb_check_var = drm_fb_helper_check_var,
> +       .fb_set_par = drm_fb_helper_set_par,
> +       .fb_blank = drm_fb_helper_blank,
> +       .fb_pan_display = drm_fb_helper_pan_display,
> +       .fb_setcmap = drm_fb_helper_setcmap,
> +};
> +
> +static int rockchip_drm_fbdev_update(struct drm_fb_helper *helper,
> +                                    struct drm_framebuffer *fb)
> +{
> +       struct fb_info *fbi = helper->fbdev;
> +       struct drm_device *dev = helper->dev;
> +       struct rockchip_drm_gem_buf *buffer;
> +       unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
> +       unsigned long offset;
> +
> +       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
> +       drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
> +
> +       /* RGB formats use only one buffer */
> +       buffer = rockchip_drm_fb_buffer(fb, 0);
> +       if (!buffer) {
> +               DRM_DEBUG_KMS("buffer is null.\n");
> +               return -EFAULT;
> +       }
> +
> +       /* map pages with kernel virtual space. */
> +       if (!buffer->kvaddr) {
> +               if (is_drm_iommu_supported(dev)) {
> +                       unsigned int nr_pages = buffer->size >> PAGE_SHIFT;
> +
> +                       buffer->kvaddr = (void __iomem *) vmap(buffer->pages,
> +                                       nr_pages, VM_MAP,
> +                                       pgprot_writecombine(PAGE_KERNEL));
> +               } else {
> +                       phys_addr_t dma_addr = buffer->dma_addr;
> +
> +                       if (dma_addr)
> +                               buffer->kvaddr =
> +                                       (void __iomem *)phys_to_virt(dma_addr);
> +                       else
> +                               buffer->kvaddr = (void __iomem *)NULL;
> +               }
> +               if (!buffer->kvaddr) {
> +                       DRM_ERROR("failed to map pages to kernel space.\n");
> +                       return -EIO;
> +               }
> +       }
> +
> +       /* buffer count to framebuffer always is 1 at booting time. */
> +       rockchip_drm_fb_set_buf_cnt(fb, 1);
> +
> +       offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
> +       offset += fbi->var.yoffset * fb->pitches[0];
> +
> +       fbi->screen_base = buffer->kvaddr + offset;
> +       fbi->screen_size = size;
> +       fbi->fix.smem_len = size;
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
> +                                   struct drm_fb_helper_surface_size *sizes)
> +{
> +       struct rockchip_drm_fbdev *rockchip_fbdev = to_rockchip_fbdev(helper);
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_device *dev = helper->dev;
> +       struct fb_info *fbi;
> +       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
> +       struct platform_device *pdev = dev->platformdev;
> +       unsigned long size;
> +       int ret;
> +
> +       DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
> +                       sizes->surface_width, sizes->surface_height,
> +                       sizes->surface_bpp);
> +
> +       mode_cmd.width = sizes->surface_width;
> +       mode_cmd.height = sizes->surface_height;
> +       mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
> +       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> +                                                         sizes->surface_depth);
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       fbi = framebuffer_alloc(0, &pdev->dev);
> +       if (!fbi) {
> +               DRM_ERROR("failed to allocate fb info.\n");
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       size = mode_cmd.pitches[0] * mode_cmd.height;
> +
> +       rockchip_gem_obj = rockchip_drm_gem_create(dev,
> +                                               ROCKCHIP_BO_CONTIG, size);
> +       /*
> +        * If physically contiguous memory allocation fails and if IOMMU is
> +        * supported then try to get buffer from non physically contiguous
> +        * memory area.
> +        */
> +       if (IS_ERR(rockchip_gem_obj) && is_drm_iommu_supported(dev)) {
> +               dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
> +               rockchip_gem_obj = rockchip_drm_gem_create(dev,
> +                                       ROCKCHIP_BO_NONCONTIG, size);
> +       }
> +
> +       if (IS_ERR(rockchip_gem_obj)) {
> +               ret = PTR_ERR(rockchip_gem_obj);
> +               goto err_release_framebuffer;
> +       }
> +
> +       rockchip_fbdev->rockchip_gem_obj = rockchip_gem_obj;
> +
> +       helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
> +                       &rockchip_gem_obj->base);
> +       if (IS_ERR(helper->fb)) {
> +               DRM_ERROR("failed to create drm framebuffer.\n");
> +               ret = PTR_ERR(helper->fb);
> +               goto err_destroy_gem;
> +       }
> +
> +       helper->fbdev = fbi;
> +
> +       fbi->par = helper;
> +       fbi->flags = FBINFO_FLAG_DEFAULT;
> +       fbi->fbops = &rockchip_drm_fb_ops;
> +
> +       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> +       if (ret) {
> +               DRM_ERROR("failed to allocate cmap.\n");
> +               goto err_destroy_framebuffer;
> +       }
> +
> +       ret = rockchip_drm_fbdev_update(helper, helper->fb);
> +       if (ret < 0)
> +               goto err_dealloc_cmap;
> +
> +       mutex_unlock(&dev->struct_mutex);
> +       return ret;
> +
> +err_dealloc_cmap:
> +       fb_dealloc_cmap(&fbi->cmap);
> +err_destroy_framebuffer:
> +       drm_framebuffer_cleanup(helper->fb);
> +err_destroy_gem:
> +       rockchip_drm_gem_destroy(rockchip_gem_obj);
> +err_release_framebuffer:
> +       framebuffer_release(fbi);
> +
> +/*
> + * if failed, all resources allocated above would be released by
> + * drm_mode_config_cleanup() when drm_load() had been called prior
> + * to any specific driver such as lcdc or hdmi driver.
> + */
> +out:
> +       mutex_unlock(&dev->struct_mutex);
> +       return ret;
> +}
> +
> +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
> +       .fb_probe = rockchip_drm_fbdev_create,
> +};
> +
> +static bool rockchip_drm_fbdev_is_anything_connected(struct drm_device *dev)
> +{
> +       struct drm_connector *connector;
> +       bool ret = false;
> +
> +       mutex_lock(&dev->mode_config.mutex);
> +       list_for_each_entry(connector,
> +                       &dev->mode_config.connector_list, head) {
> +               if (connector->status != connector_status_connected)
> +                       continue;
> +
> +               ret = true;
> +               break;
> +       }
> +       mutex_unlock(&dev->mode_config.mutex);
> +
> +       return ret;
> +}
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev)
> +{
> +       struct rockchip_drm_fbdev *fbdev;
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct drm_fb_helper *helper;
> +       unsigned int num_crtc;
> +       int ret;
> +
> +       if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
> +               return 0;
> +
> +       if (!rockchip_drm_fbdev_is_anything_connected(dev))
> +               return 0;

well, this is at least *different* from how the other drivers operate.
I guess it avoids getting stuck with a 1024x768 fb console if nothing
connected at boot.  But it also means the fbdev device won't show up.
Which doesn't seem desirable.

> +
> +       fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
> +       if (!fbdev)
> +               return -ENOMEM;
> +
> +       private->fb_helper = helper = &fbdev->drm_fb_helper;
> +       helper->funcs = &rockchip_drm_fb_helper_funcs;
> +
> +       num_crtc = dev->mode_config.num_crtc;
> +
> +       ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to initialize drm fb helper.\n");
> +               goto err_init;
> +       }
> +
> +       ret = drm_fb_helper_single_add_all_connectors(helper);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to register drm_fb_helper_connector.\n");
> +               goto err_setup;
> +       }
> +
> +       /* disable all the possible outputs/crtcs before entering KMS mode */
> +       drm_helper_disable_unused_functions(dev);
> +
> +       ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to set up hw configuration.\n");
> +               goto err_setup;
> +       }
> +
> +       return 0;
> +
> +err_setup:
> +       drm_fb_helper_fini(helper);
> +
> +err_init:
> +       private->fb_helper = NULL;
> +       kfree(fbdev);
> +
> +       return ret;
> +}
> +
> +static void rockchip_drm_fbdev_destroy(struct drm_device *dev,
> +                                     struct drm_fb_helper *fb_helper)
> +{
> +       struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(fb_helper);
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj =
> +                                               rockchip_fbd->rockchip_gem_obj;
> +       struct drm_framebuffer *fb;
> +
> +       if (is_drm_iommu_supported(dev) && rockchip_gem_obj->buffer->kvaddr)
> +               vunmap(rockchip_gem_obj->buffer->kvaddr);
> +
> +       /* release drm framebuffer and real buffer */
> +       if (fb_helper->fb && fb_helper->fb->funcs) {
> +               fb = fb_helper->fb;
> +               if (fb) {
> +                       drm_framebuffer_unregister_private(fb);
> +                       drm_framebuffer_remove(fb);
> +               }
> +       }
> +
> +       /* release linux framebuffer */
> +       if (fb_helper->fbdev) {
> +               struct fb_info *info;
> +               int ret;
> +
> +               info = fb_helper->fbdev;
> +               ret = unregister_framebuffer(info);
> +               if (ret < 0)
> +                       DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
> +
> +               if (info->cmap.len)
> +                       fb_dealloc_cmap(&info->cmap);
> +
> +               framebuffer_release(info);
> +       }
> +
> +       drm_fb_helper_fini(fb_helper);
> +}
> +
> +void rockchip_drm_fbdev_fini(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct rockchip_drm_fbdev *fbdev;
> +
> +       if (!private || !private->fb_helper)
> +               return;
> +
> +       fbdev = to_rockchip_fbdev(private->fb_helper);
> +
> +       if (fbdev->rockchip_gem_obj)
> +               rockchip_drm_gem_destroy(fbdev->rockchip_gem_obj);
> +
> +       rockchip_drm_fbdev_destroy(dev, private->fb_helper);
> +       kfree(fbdev);
> +       private->fb_helper = NULL;
> +}
> +
> +void rockchip_drm_fbdev_restore_mode(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +
> +       if (!private || !private->fb_helper)
> +               return;
> +
> +       drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> new file mode 100644
> index 0000000..d4bda37
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> @@ -0,0 +1,26 @@
> +/* rockchip_drm_fbdev.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_fbdev.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FBDEV_H_
> +#define _ROCKCHIP_DRM_FBDEV_H_
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev);
> +int rockchip_drm_fbdev_reinit(struct drm_device *dev);
> +void rockchip_drm_fbdev_fini(struct drm_device *dev);
> +void rockchip_drm_fbdev_restore_mode(struct drm_device *dev);
> +
> +#endif /* _ROCKCHIP_DRM_FBDEV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> new file mode 100644
> index 0000000..96116d9
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> @@ -0,0 +1,738 @@
> +/* rockchip_drm_gem.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_gem.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
> +
> +#include <linux/shmem_fs.h>
> +#include <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_buf.h"
> +#include "rockchip_drm_iommu.h"
> +
> +static unsigned int convert_to_vm_err_msg(int msg)
> +{
> +       unsigned int out_msg;
> +
> +       switch (msg) {
> +       case 0:
> +       case -ERESTARTSYS:
> +       case -EINTR:
> +               out_msg = VM_FAULT_NOPAGE;
> +               break;
> +
> +       case -ENOMEM:
> +               out_msg = VM_FAULT_OOM;
> +               break;
> +       default:
> +               out_msg = VM_FAULT_SIGBUS;
> +               break;
> +       }
> +
> +       return out_msg;
> +}
> +
> +static int check_gem_flags(unsigned int flags)
> +{
> +       if (flags & ~(ROCKCHIP_BO_MASK)) {
> +               DRM_ERROR("invalid flags.\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static void update_vm_cache_attr(struct rockchip_drm_gem_obj *obj,
> +                                       struct vm_area_struct *vma)
> +{
> +       DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
> +
> +       /* non-cachable as default. */
> +       if (obj->flags & ROCKCHIP_BO_CACHABLE)
> +               vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
> +       else if (obj->flags & ROCKCHIP_BO_WC)
> +               vma->vm_page_prot =
> +                       pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +       else
> +               vma->vm_page_prot =
> +                       pgprot_noncached(vm_get_page_prot(vma->vm_flags));
> +}
> +
> +static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
> +{
> +       /* TODO */
> +
> +       return roundup(size, PAGE_SIZE);
> +}
> +
> +static int rockchip_drm_gem_map_buf(struct drm_gem_object *obj,
> +                                       struct vm_area_struct *vma,
> +                                       unsigned long f_vaddr,
> +                                       pgoff_t page_offset)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj =
> +                                               to_rockchip_gem_obj(obj);
> +       struct rockchip_drm_gem_buf *buf = rockchip_gem_obj->buffer;
> +       struct scatterlist *sgl;
> +       unsigned long pfn;
> +       int i;
> +
> +       if (!buf->sgt)
> +               return -EINTR;
> +
> +       if (page_offset >= (buf->size >> PAGE_SHIFT)) {
> +               DRM_ERROR("invalid page offset\n");
> +               return -EINVAL;
> +       }
> +
> +       sgl = buf->sgt->sgl;
> +       for_each_sg(buf->sgt->sgl, sgl, buf->sgt->nents, i) {
> +               if (page_offset < (sgl->length >> PAGE_SHIFT))
> +                       break;
> +               page_offset -= (sgl->length >> PAGE_SHIFT);
> +       }
> +
> +       pfn = __phys_to_pfn(sg_phys(sgl)) + page_offset;
> +
> +       return vm_insert_mixed(vma, f_vaddr, pfn);
> +}
> +
> +static int rockchip_drm_gem_handle_create(struct drm_gem_object *obj,
> +                                       struct drm_file *file_priv,
> +                                       unsigned int *handle)
> +{
> +       int ret;
> +
> +       /*
> +        * allocate a id of idr table where the obj is registered
> +        * and handle has the id what user can see.
> +        */
> +       ret = drm_gem_handle_create(file_priv, obj, handle);
> +       if (ret)
> +               return ret;
> +
> +       DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
> +
> +       /* drop reference from allocate - handle holds it now. */
> +       drm_gem_object_unreference_unlocked(obj);
> +
> +       return 0;
> +}
> +
> +void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj)
> +{
> +       struct drm_gem_object *obj;
> +       struct rockchip_drm_gem_buf *buf;
> +
> +       obj = &rockchip_gem_obj->base;
> +       buf = rockchip_gem_obj->buffer;
> +
> +       DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count);
> +
> +       /*
> +        * do not release memory region from exporter.
> +        *
> +        * the region will be released by exporter
> +        * once dmabuf's refcount becomes 0.
> +        */
> +       if (obj->import_attach)
> +               goto out;
> +
> +       rockchip_drm_free_buf(obj->dev, rockchip_gem_obj->flags, buf);
> +
> +out:
> +       rockchip_drm_fini_buf(obj->dev, buf);
> +       rockchip_gem_obj->buffer = NULL;
> +
> +       drm_gem_free_mmap_offset(obj);
> +
> +       /* release file pointer to gem object. */
> +       drm_gem_object_release(obj);
> +
> +       kfree(rockchip_gem_obj);
> +       rockchip_gem_obj = NULL;
> +}
> +
> +unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
> +                                               unsigned int gem_handle,
> +                                               struct drm_file *file_priv)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_gem_object *obj;
> +
> +       obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               return 0;
> +       }
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       drm_gem_object_unreference_unlocked(obj);
> +
> +       return rockchip_gem_obj->buffer->size;
> +}
> +
> +
> +struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
> +                                                     unsigned long size)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       rockchip_gem_obj = kzalloc(sizeof(*rockchip_gem_obj), GFP_KERNEL);
> +       if (!rockchip_gem_obj)
> +               return NULL;
> +
> +       rockchip_gem_obj->size = size;
> +       obj = &rockchip_gem_obj->base;
> +
> +       ret = drm_gem_object_init(dev, obj, size);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to initialize gem object\n");
> +               kfree(rockchip_gem_obj);
> +               return NULL;
> +       }
> +
> +       DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
> +
> +       return rockchip_gem_obj;
> +}
> +
> +struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
> +                                               unsigned int flags,
> +                                               unsigned long size)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct rockchip_drm_gem_buf *buf;
> +       int ret;
> +
> +       if (!size) {
> +               DRM_ERROR("invalid size.\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       size = roundup_gem_size(size, flags);
> +
> +       ret = check_gem_flags(flags);
> +       if (ret < 0)
> +               return ERR_PTR(ret);
> +
> +       buf = rockchip_drm_init_buf(dev, size);
> +       if (!buf)
> +               return ERR_PTR(-ENOMEM);
> +
> +       rockchip_gem_obj = rockchip_drm_gem_init(dev, size);
> +       if (!rockchip_gem_obj) {
> +               ret = -ENOMEM;
> +               goto err_fini_buf;
> +       }
> +
> +       rockchip_gem_obj->buffer = buf;
> +
> +       /* set memory type and cache attribute from user side. */
> +       rockchip_gem_obj->flags = flags;
> +
> +       ret = rockchip_drm_alloc_buf(dev, buf, flags);
> +       if (ret < 0)
> +               goto err_gem_fini;
> +
> +       return rockchip_gem_obj;
> +
> +err_gem_fini:
> +       drm_gem_object_release(&rockchip_gem_obj->base);
> +       kfree(rockchip_gem_obj);
> +err_fini_buf:
> +       rockchip_drm_fini_buf(dev, buf);
> +       return ERR_PTR(ret);
> +}
> +
> +int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
> +                               struct drm_file *file_priv)
> +{
> +       struct drm_rockchip_gem_create *args = data;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       int ret;
> +
> +       rockchip_gem_obj = rockchip_drm_gem_create(dev,
> +                                       args->flags, args->size);
> +       if (IS_ERR(rockchip_gem_obj))
> +               return PTR_ERR(rockchip_gem_obj);
> +
> +       ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base,
> +                                       file_priv, &args->handle);
> +       if (ret) {
> +               rockchip_drm_gem_destroy(rockchip_gem_obj);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
> +                                       unsigned int gem_handle,
> +                                       struct drm_file *filp)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_gem_object *obj;
> +
> +       obj = drm_gem_object_lookup(dev, filp, gem_handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       return &rockchip_gem_obj->buffer->dma_addr;
> +}
> +
> +void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
> +                                       unsigned int gem_handle,
> +                                       struct drm_file *filp)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_gem_object *obj;
> +
> +       obj = drm_gem_object_lookup(dev, filp, gem_handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               return;
> +       }
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       drm_gem_object_unreference_unlocked(obj);
> +
> +       /*
> +        * decrease obj->refcount one more time because we has already
> +        * increased it at rockchip_drm_gem_get_dma_addr().
> +        */
> +       drm_gem_object_unreference_unlocked(obj);
> +}
> +
> +int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
> +                                   struct drm_file *file_priv)
> +{
> +       struct drm_rockchip_gem_map_off *args = data;
> +
> +       DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
> +                       args->handle, (unsigned long)args->offset);
> +
> +       if (!(dev->driver->driver_features & DRIVER_GEM)) {
> +               DRM_ERROR("does not support GEM.\n");
> +               return -ENODEV;
> +       }
> +
> +       return rockchip_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
> +                       &args->offset);
> +}
> +
> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
> +                                     struct vm_area_struct *vma)
> +{
> +       struct drm_gem_object *obj = filp->private_data;
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj =
> +                                               to_rockchip_gem_obj(obj);
> +       struct drm_device *drm_dev = obj->dev;
> +       struct rockchip_drm_gem_buf *buffer;
> +       unsigned long vm_size;
> +       int ret;
> +
> +       WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
> +
> +       vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
> +       vma->vm_private_data = obj;
> +       vma->vm_ops = drm_dev->driver->gem_vm_ops;
> +
> +       update_vm_cache_attr(rockchip_gem_obj, vma);
> +
> +       vm_size = vma->vm_end - vma->vm_start;
> +
> +       /*
> +        * a buffer contains information to physically continuous memory
> +        * allocated by user request or at framebuffer creation.
> +        */
> +       buffer = rockchip_gem_obj->buffer;
> +
> +       /* check if user-requested size is valid. */
> +       if (vm_size > buffer->size)
> +               return -EINVAL;
> +
> +       ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
> +                               buffer->dma_addr, buffer->size,
> +                               &buffer->dma_attrs);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to mmap.\n");
> +               return ret;
> +       }
> +
> +       /*
> +        * take a reference to this mapping of the object. And this reference
> +        * is unreferenced by the corresponding vm_close call.
> +        */
> +       drm_gem_object_reference(obj);
> +
> +       drm_vm_open_locked(drm_dev, vma);
> +
> +       return 0;
> +}
> +
> +int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
> +                             struct drm_file *file_priv)
> +{
> +       struct drm_rockchip_file_private *rockchip_file_priv;
> +       struct drm_rockchip_gem_mmap *args = data;
> +       struct drm_gem_object *obj;
> +       struct file *anon_filp;
> +       unsigned long addr;
> +
> +       if (!(dev->driver->driver_features & DRIVER_GEM)) {
> +               DRM_ERROR("does not support GEM.\n");
> +               return -ENODEV;
> +       }
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               mutex_unlock(&dev->struct_mutex);
> +               return -EINVAL;
> +       }
> +
> +       rockchip_file_priv = file_priv->driver_priv;
> +       anon_filp = rockchip_file_priv->anon_filp;
> +       anon_filp->private_data = obj;
> +
> +       addr = vm_mmap(anon_filp, 0, args->size, PROT_READ | PROT_WRITE,
> +                       MAP_SHARED, 0);
> +
> +       drm_gem_object_unreference(obj);
> +
> +       if (IS_ERR_VALUE(addr)) {
> +               mutex_unlock(&dev->struct_mutex);
> +               return (int)addr;
> +       }
> +
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       args->mapped = addr;
> +
> +       DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
> +
> +       return 0;
> +}
> +
> +int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
> +                                     struct drm_file *file_priv)
> +{      struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_rockchip_gem_info *args = data;
> +       struct drm_gem_object *obj;
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               mutex_unlock(&dev->struct_mutex);
> +               return -EINVAL;
> +       }
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       args->flags = rockchip_gem_obj->flags;
> +       args->size = rockchip_gem_obj->size;
> +
> +       drm_gem_object_unreference(obj);
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       return 0;
> +}
> +
> +struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma)

I guess this get/put_vma stuff is just copy/pasta from exynos userptr
stuff.  Please drop it, since it isn't needed.

> +{
> +       struct vm_area_struct *vma_copy;
> +
> +       vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
> +       if (!vma_copy)
> +               return NULL;
> +
> +       if (vma->vm_ops && vma->vm_ops->open)
> +               vma->vm_ops->open(vma);
> +
> +       if (vma->vm_file)
> +               get_file(vma->vm_file);
> +
> +       memcpy(vma_copy, vma, sizeof(*vma));
> +
> +       vma_copy->vm_mm = NULL;
> +       vma_copy->vm_next = NULL;
> +       vma_copy->vm_prev = NULL;
> +
> +       return vma_copy;
> +}
> +
> +void rockchip_gem_put_vma(struct vm_area_struct *vma)
> +{
> +       if (!vma)
> +               return;
> +
> +       if (vma->vm_ops && vma->vm_ops->close)
> +               vma->vm_ops->close(vma);
> +
> +       if (vma->vm_file)
> +               fput(vma->vm_file);
> +
> +       kfree(vma);
> +}
> +
> +int rockchip_gem_get_pages_from_userptr(unsigned long start,
> +                                               unsigned int npages,
> +                                               struct page **pages,
> +                                               struct vm_area_struct *vma)
> +{
> +       int get_npages;
> +
> +       /* the memory region mmaped with VM_PFNMAP. */
> +       if (vma_is_io(vma)) {
> +               unsigned int i;
> +
> +               for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
> +                       unsigned long pfn;
> +                       int ret = follow_pfn(vma, start, &pfn);
> +
> +                       if (ret)
> +                               return ret;
> +
> +                       pages[i] = pfn_to_page(pfn);
> +               }
> +
> +               if (i != npages) {
> +                       DRM_ERROR("failed to get user_pages.\n");
> +                       return -EINVAL;
> +               }
> +
> +               return 0;
> +       }
> +
> +       get_npages = get_user_pages(current, current->mm, start,
> +                                       npages, 1, 1, pages, NULL);
> +       get_npages = max(get_npages, 0);
> +       if (get_npages != npages) {
> +               DRM_ERROR("failed to get user_pages.\n");
> +               while (get_npages)
> +                       put_page(pages[--get_npages]);
> +               return -EFAULT;
> +       }
> +
> +       return 0;
> +}
> +
> +void rockchip_gem_put_pages_to_userptr(struct page **pages,
> +                                       unsigned int npages,
> +                                       struct vm_area_struct *vma)
> +{
> +       if (!vma_is_io(vma)) {
> +               unsigned int i;
> +
> +               for (i = 0; i < npages; i++) {
> +                       set_page_dirty_lock(pages[i]);
> +
> +                       /*
> +                        * undo the reference we took when populating
> +                        * the table.
> +                        */
> +                       put_page(pages[i]);
> +               }
> +       }
> +}
> +
> +int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
> +                               struct sg_table *sgt,
> +                               enum dma_data_direction dir)
> +{
> +       int nents;
> +
> +       mutex_lock(&drm_dev->struct_mutex);
> +
> +       nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
> +       if (!nents) {
> +               DRM_ERROR("failed to map sgl with dma.\n");
> +               mutex_unlock(&drm_dev->struct_mutex);
> +               return nents;
> +       }
> +
> +       mutex_unlock(&drm_dev->struct_mutex);
> +
> +       return 0;
> +}
> +
> +void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
> +                               struct sg_table *sgt,
> +                               enum dma_data_direction dir)
> +{
> +       dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
> +}
> +
> +void rockchip_drm_gem_free_object(struct drm_gem_object *obj)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct rockchip_drm_gem_buf *buf;
> +
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +       buf = rockchip_gem_obj->buffer;
> +
> +       if (obj->import_attach)
> +               drm_prime_gem_destroy(obj, buf->sgt);
> +
> +       rockchip_drm_gem_destroy(to_rockchip_gem_obj(obj));
> +}
> +
> +int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
> +                              struct drm_device *dev,
> +                              struct drm_mode_create_dumb *args)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       int ret;
> +
> +       /*
> +        * allocate memory to be used for framebuffer.
> +        * - this callback would be called by user application
> +        * with DRM_IOCTL_MODE_CREATE_DUMB command.
> +        */
> +
> +       args->pitch = args->width * ((args->bpp + 7) / 8);
> +       args->size = args->pitch * args->height;
> +
> +       if (is_drm_iommu_supported(dev)) {
> +               rockchip_gem_obj = rockchip_drm_gem_create(dev,
> +                       ROCKCHIP_BO_NONCONTIG | ROCKCHIP_BO_WC,
> +                       args->size);
> +       } else {
> +               rockchip_gem_obj = rockchip_drm_gem_create(dev,
> +                       ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_WC,
> +                       args->size);
> +       }
> +
> +       if (IS_ERR(rockchip_gem_obj)) {
> +               dev_warn(dev->dev, "FB allocation failed.\n");
> +               return PTR_ERR(rockchip_gem_obj);
> +       }
> +
> +       ret = rockchip_drm_gem_handle_create(&rockchip_gem_obj->base,
> +                                       file_priv, &args->handle);
> +       if (ret) {
> +               rockchip_drm_gem_destroy(rockchip_gem_obj);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> +                                  struct drm_device *dev, uint32_t handle,
> +                                  uint64_t *offset)
> +{
> +       struct drm_gem_object *obj;
> +       int ret = 0;
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       /*
> +        * get offset of memory allocated for drm framebuffer.
> +        * - this callback would be called by user application
> +        * with DRM_IOCTL_MODE_MAP_DUMB command.
> +        */
> +
> +       obj = drm_gem_object_lookup(dev, file_priv, handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       ret = drm_gem_create_mmap_offset(obj);
> +       if (ret)
> +               goto out;
> +
> +       *offset = drm_vma_node_offset_addr(&obj->vma_node);
> +       DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
> +
> +out:
> +       drm_gem_object_unreference(obj);
> +unlock:
> +       mutex_unlock(&dev->struct_mutex);
> +       return ret;
> +}
> +
> +int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +       struct drm_gem_object *obj = vma->vm_private_data;
> +       struct drm_device *dev = obj->dev;
> +       unsigned long f_vaddr;
> +       pgoff_t page_offset;
> +       int ret;
> +
> +       page_offset = ((unsigned long)vmf->virtual_address -
> +                       vma->vm_start) >> PAGE_SHIFT;
> +       f_vaddr = (unsigned long)vmf->virtual_address;
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       ret = rockchip_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
> +       if (ret < 0)
> +               DRM_ERROR("failed to map a buffer with user.\n");
> +
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       return convert_to_vm_err_msg(ret);
> +}
> +
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +       struct rockchip_drm_gem_obj *rockchip_gem_obj;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       /* set vm_area_struct. */
> +       ret = drm_gem_mmap(filp, vma);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to mmap.\n");
> +               return ret;
> +       }
> +
> +       obj = vma->vm_private_data;
> +       rockchip_gem_obj = to_rockchip_gem_obj(obj);
> +
> +       ret = check_gem_flags(rockchip_gem_obj->flags);
> +       if (ret < 0) {
> +               drm_gem_vm_close(vma);
> +               drm_gem_free_mmap_offset(obj);
> +               return ret;
> +       }
> +
> +       vma->vm_flags &= ~VM_PFNMAP;
> +       vma->vm_flags |= VM_MIXEDMAP;
> +
> +       update_vm_cache_attr(rockchip_gem_obj, vma);
> +
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> new file mode 100644
> index 0000000..e322c42
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> @@ -0,0 +1,198 @@
> +/* rockchip_drm_gem.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_gem.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_GEM_H_
> +#define _ROCKCHIP_DRM_GEM_H_
> +
> +#define to_rockchip_gem_obj(x) container_of(x,\
> +                       struct rockchip_drm_gem_obj, base)
> +
> +#define IS_NONCONTIG_BUFFER(f)         (f & ROCKCHIP_BO_NONCONTIG)
> +
> +/*
> + * rockchip drm gem buffer structure.
> + *
> + * @kvaddr: kernel virtual address to allocated memory region.
> + * *userptr: user space address.
> + * @dma_addr: bus address(accessed by dma) to allocated memory region.
> + *        - this address could be physical address without IOMMU and
> + *        device address with IOMMU.
> + * @write: whether pages will be written to by the caller.
> + * @pages: Array of backing pages.
> + * @sgt: sg table to transfer page data.
> + * @size: size of allocated memory region.
> + * @pfnmap: indicate whether memory region from userptr is mmaped with
> + * VM_PFNMAP or not.
> + */
> +struct rockchip_drm_gem_buf {
> +       void __iomem *kvaddr;
> +       unsigned long userptr;
> +       dma_addr_t dma_addr;
> +       struct dma_attrs dma_attrs;
> +       unsigned int write;
> +       struct page **pages;
> +       struct sg_table *sgt;
> +       unsigned long size;
> +       bool pfnmap;
> +};
> +
> +/*
> + * rockchip drm buffer structure.
> + *
> + * @base: a gem object.
> + *      - a new handle to this gem object would be created
> + *      by drm_gem_handle_create().
> + * @buffer: a pointer to rockchip_drm_gem_buffer object.
> + *      - contain the information to memory region allocated
> + *      by user request or at framebuffer creation.
> + *      continuous memory region allocated by user request
> + *      or at framebuffer creation.
> + * @size: size requested from user, in bytes and this size is aligned
> + *      in page unit.
> + * @vma: a pointer to vm_area.
> + * @flags: indicate memory type to allocated buffer and cache attruibute.
> + *
> + * P.S. this object would be transferred to user as kms_bo.handle so
> + *      user can access the buffer through kms_bo.handle.
> + */
> +struct rockchip_drm_gem_obj {
> +       struct drm_gem_object base;
> +       struct rockchip_drm_gem_buf *buffer;
> +       unsigned long size;
> +       struct vm_area_struct *vma;
> +       unsigned int flags;
> +};
> +
> +struct page **rockchip_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
> +
> +/* destroy a buffer with gem object */
> +void rockchip_drm_gem_destroy(struct rockchip_drm_gem_obj *rockchip_gem_obj);
> +
> +/* create a private gem object and initialize it. */
> +struct rockchip_drm_gem_obj *rockchip_drm_gem_init(struct drm_device *dev,
> +                                                     unsigned long size);
> +
> +/* create a new buffer with gem object */
> +struct rockchip_drm_gem_obj *rockchip_drm_gem_create(struct drm_device *dev,
> +                                               unsigned int flags,
> +                                               unsigned long size);
> +
> +/*
> + * request gem object creation and buffer allocation as the size
> + * that it is calculated with framebuffer information such as width,
> + * height and bpp.
> + */
> +int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
> +                               struct drm_file *file_priv);
> +
> +/*
> + * get dma address from gem handle and this function could be used for
> + * other drivers such as 2d/3d acceleration drivers.
> + * with this function call, gem object reference count would be increased.
> + */
> +dma_addr_t *rockchip_drm_gem_get_dma_addr(struct drm_device *dev,
> +                                       unsigned int gem_handle,
> +                                       struct drm_file *filp);
> +
> +/*
> + * put dma address from gem handle and this function could be used for
> + * other drivers such as 2d/3d acceleration drivers.
> + * with this function call, gem object reference count would be decreased.
> + */
> +void rockchip_drm_gem_put_dma_addr(struct drm_device *dev,
> +                                       unsigned int gem_handle,
> +                                       struct drm_file *filp);
> +
> +/* get buffer offset to map to user space. */
> +int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
> +                                   struct drm_file *file_priv);
> +
> +/*
> + * mmap the physically continuous memory that a gem object contains
> + * to user space.
> + */
> +int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
> +                             struct drm_file *file_priv);
> +
> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
> +                                     struct vm_area_struct *vma);
> +
> +/* map user space allocated by malloc to pages. */
> +int rockchip_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
> +                                     struct drm_file *file_priv);

really.. please, no userptr!

> +/* get buffer information to memory region allocated by gem. */
> +int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
> +                                     struct drm_file *file_priv);
> +
> +/* get buffer size to gem handle. */
> +unsigned long rockchip_drm_gem_get_size(struct drm_device *dev,
> +                                               unsigned int gem_handle,
> +                                               struct drm_file *file_priv);
> +
> +/* free gem object. */
> +void rockchip_drm_gem_free_object(struct drm_gem_object *gem_obj);
> +
> +/* create memory region for drm framebuffer. */
> +int rockchip_drm_gem_dumb_create(struct drm_file *file_priv,
> +                              struct drm_device *dev,
> +                              struct drm_mode_create_dumb *args);
> +
> +/* map memory region for drm framebuffer to user space. */
> +int rockchip_drm_gem_dumb_map_offset(struct drm_file *file_priv,
> +                                  struct drm_device *dev, uint32_t handle,
> +                                  uint64_t *offset);
> +
> +/* page fault handler and mmap fault address(virtual) to physical memory. */
> +int rockchip_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
> +
> +/* set vm_flags and we can change the vm attribute to other one at here. */
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +static inline int vma_is_io(struct vm_area_struct *vma)
> +{
> +       return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
> +}
> +
> +/* get a copy of a virtual memory region. */
> +struct vm_area_struct *rockchip_gem_get_vma(struct vm_area_struct *vma);
> +
> +/* release a userspace virtual memory area. */
> +void rockchip_gem_put_vma(struct vm_area_struct *vma);
> +
> +/* get pages from user space. */
> +int rockchip_gem_get_pages_from_userptr(unsigned long start,
> +                                               unsigned int npages,
> +                                               struct page **pages,
> +                                               struct vm_area_struct *vma);
> +
> +/* drop the reference to pages. */
> +void rockchip_gem_put_pages_to_userptr(struct page **pages,
> +                                       unsigned int npages,
> +                                       struct vm_area_struct *vma);
> +
> +/* map sgt with dma region. */
> +int rockchip_gem_map_sgt_with_dma(struct drm_device *drm_dev,
> +                               struct sg_table *sgt,
> +                               enum dma_data_direction dir);
> +
> +/* unmap sgt from dma region. */
> +void rockchip_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
> +                               struct sg_table *sgt,
> +                               enum dma_data_direction dir);
> +
> +#endif
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_iommu.c b/drivers/gpu/drm/rockchip/rockchip_drm_iommu.c
> new file mode 100644
> index 0000000..ffc3170
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_iommu.c
> @@ -0,0 +1,149 @@
> +/* rockchip_drm_iommu.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_iommu.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drmP.h>
> +#include <drm/rockchip_drm.h>
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/iommu.h>
> +#include <linux/kref.h>
> +
> +#include <asm/dma-iommu.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_iommu.h"
> +
> +/*
> + * drm_create_iommu_mapping - create a mapping structure
> + *
> + * @drm_dev: DRM device
> + */
> +int drm_create_iommu_mapping(struct drm_device *drm_dev)
> +{
> +       struct dma_iommu_mapping *mapping = NULL;
> +       struct rockchip_drm_private *priv = drm_dev->dev_private;
> +       struct device *dev = drm_dev->dev;
> +
> +       if (!priv->da_start)
> +               priv->da_start = ROCKCHIP_DEV_ADDR_START;
> +       if (!priv->da_space_size)
> +               priv->da_space_size = ROCKCHIP_DEV_ADDR_SIZE;
> +
> +       mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start,
> +                                               priv->da_space_size);
> +

using arm_iommu_* APIs directly looks a bit strange..

is there a reason for not using iommu_* API's directly?

> +       if (IS_ERR(mapping))
> +               return PTR_ERR(mapping);
> +
> +       dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
> +                                       GFP_KERNEL);
> +       if (!dev->dma_parms)
> +               goto error;
> +
> +       dma_set_max_seg_size(dev, 0xffffffffu);
> +       dev->archdata.mapping = mapping;
> +
> +       return 0;
> +error:
> +       arm_iommu_release_mapping(mapping);
> +       return -ENOMEM;
> +}
> +
> +/*
> + * drm_release_iommu_mapping - release iommu mapping structure
> + *
> + * @drm_dev: DRM device
> + *
> + * if mapping->kref becomes 0 then all things related to iommu mapping
> + * will be released
> + */
> +void drm_release_iommu_mapping(struct drm_device *drm_dev)
> +{
> +       struct device *dev = drm_dev->dev;
> +
> +       arm_iommu_release_mapping(dev->archdata.mapping);
> +}
> +
> +/*
> + * drm_iommu_attach_device- attach device to iommu mapping
> + *
> + * @drm_dev: DRM device
> + * @subdrv_dev: device to be attach
> + *
> + * This function should be called by sub drivers to attach it to iommu
> + * mapping.
> + */
> +int drm_iommu_attach_device(struct drm_device *drm_dev,
> +                               struct device *subdrv_dev)
> +{
> +       struct device *dev = drm_dev->dev;
> +       int ret;
> +
> +       if (!dev->archdata.mapping) {
> +               DRM_ERROR("iommu_mapping is null.\n");
> +               return -EFAULT;
> +       }
> +
> +       subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev,
> +                                       sizeof(*subdrv_dev->dma_parms),
> +                                       GFP_KERNEL);
> +       if (!subdrv_dev->dma_parms)
> +               return -ENOMEM;
> +
> +       dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
> +
> +       ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping);
> +       if (ret < 0) {
> +               DRM_DEBUG_KMS("failed iommu attach.\n");
> +               return ret;
> +       }
> +
> +       /*
> +        * Set dma_ops to drm_device just one time.
> +        *
> +        * The dma mapping api needs device object and the api is used
> +        * to allocate physial memory and map it with iommu table.
> +        * If iommu attach succeeded, the sub driver would have dma_ops
> +        * for iommu and also all sub drivers have same dma_ops.
> +        */
> +       if (!dev->archdata.dma_ops)
> +               dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops;
> +
> +       return 0;
> +}
> +
> +/*
> + * drm_iommu_detach_device -detach device address space mapping from device
> + *
> + * @drm_dev: DRM device
> + * @subdrv_dev: device to be detached
> + *
> + * This function should be called by sub drivers to detach it from iommu
> + * mapping
> + */
> +void drm_iommu_detach_device(struct drm_device *drm_dev,
> +                               struct device *subdrv_dev)
> +{
> +       struct device *dev = drm_dev->dev;
> +       struct dma_iommu_mapping *mapping = dev->archdata.mapping;
> +
> +       if (!mapping || !mapping->domain)
> +               return;
> +
> +       iommu_detach_device(mapping->domain, subdrv_dev);
> +       drm_release_iommu_mapping(drm_dev);
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_iommu.h b/drivers/gpu/drm/rockchip/rockchip_drm_iommu.h
> new file mode 100644
> index 0000000..4fb4050
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_iommu.h
> @@ -0,0 +1,76 @@
> +/* rockchip_drm_iommu.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_iommu.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_IOMMU_H_
> +#define _ROCKCHIP_DRM_IOMMU_H_
> +
> +#define ROCKCHIP_DEV_ADDR_START        0x20000000
> +#define ROCKCHIP_DEV_ADDR_SIZE 0x40000000
> +
> +#ifdef CONFIG_DRM_ROCKCHIP_IOMMU
> +
> +int drm_create_iommu_mapping(struct drm_device *drm_dev);
> +
> +void drm_release_iommu_mapping(struct drm_device *drm_dev);
> +
> +int drm_iommu_attach_device(struct drm_device *drm_dev,
> +                               struct device *subdrv_dev);
> +
> +void drm_iommu_detach_device(struct drm_device *dev_dev,
> +                               struct device *subdrv_dev);
> +
> +static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
> +{
> +#ifdef CONFIG_ARM_DMA_USE_IOMMU
> +       struct device *dev = drm_dev->dev;
> +
> +       return dev->archdata.mapping ? true : false;
> +#else
> +       return false;
> +#endif
> +}
> +
> +#else
> +
> +struct dma_iommu_mapping;
> +static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
> +{
> +       return 0;
> +}
> +
> +static inline void drm_release_iommu_mapping(struct drm_device *drm_dev)
> +{
> +}
> +
> +static inline int drm_iommu_attach_device(struct drm_device *drm_dev,
> +                                               struct device *subdrv_dev)
> +{
> +       return 0;
> +}
> +
> +static inline void drm_iommu_detach_device(struct drm_device *drm_dev,
> +                                               struct device *subdrv_dev)
> +{
> +}
> +
> +static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
> +{
> +       return false;
> +}
> +
> +#endif
> +#endif /* _ROCKCHIP_DRM_IOMMU_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_plane.c b/drivers/gpu/drm/rockchip/rockchip_drm_plane.c
> new file mode 100644
> index 0000000..230a35b
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_plane.c
> @@ -0,0 +1,290 @@
> +/* rockchip_drm_plane.c
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_plane.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drmP.h>
> +
> +#include <drm/rockchip_drm.h>
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_crtc.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_plane.h"
> +
> +#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
> +
> +struct rockchip_plane {
> +       struct rockchip_drm_overlay overlay;
> +       struct drm_plane base;
> +       bool enabled;
> +};
> +
> +static const uint32_t formats[] = {
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_NV12,
> +       DRM_FORMAT_NV12MT,
> +};
> +
> +/*
> + * This function is to get X or Y size shown via screen. This needs length and
> + * start position of CRTC.
> + *
> + *      <--- length --->
> + * CRTC ----------------
> + *      ^ start        ^ end
> + *
> + * There are six cases from a to f.
> + *
> + *             <----- SCREEN ----->
> + *             0                 last
> + *   ----------|------------------|----------
> + * CRTCs
> + * a -------
> + *        b -------
> + *        c --------------------------
> + *                 d --------
> + *                           e -------
> + *                                  f -------
> + */
> +static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
> +{
> +       int end = start + length;
> +       int size = 0;
> +
> +       if (start <= 0) {
> +               if (end > 0)
> +                       size = min_t(unsigned, end, last);
> +       } else if (start <= last) {
> +               size = min_t(unsigned, last - start, length);
> +       }
> +
> +       return size;
> +}
> +
> +int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
> +                         struct drm_framebuffer *fb, int crtc_x, int crtc_y,
> +                         unsigned int crtc_w, unsigned int crtc_h,
> +                         uint32_t src_x, uint32_t src_y,
> +                         uint32_t src_w, uint32_t src_h)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
> +       unsigned int actual_w;
> +       unsigned int actual_h;
> +       int nr;
> +       int i;
> +
> +       nr = rockchip_drm_fb_get_buf_cnt(fb);
> +       for (i = 0; i < nr; i++) {
> +               struct rockchip_drm_gem_buf *buffer =
> +                                       rockchip_drm_fb_buffer(fb, i);
> +
> +               if (!buffer) {
> +                       DRM_DEBUG_KMS("buffer is null\n");
> +                       return -EFAULT;
> +               }
> +
> +               overlay->dma_addr[i] = buffer->dma_addr;
> +
> +               DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
> +                               i, (unsigned long)overlay->dma_addr[i]);
> +       }
> +
> +       actual_w = rockchip_plane_get_size(crtc_x,
> +                                       crtc_w, crtc->mode.hdisplay);
> +       actual_h = rockchip_plane_get_size(crtc_y,
> +                                       crtc_h, crtc->mode.vdisplay);
> +
> +       if (crtc_x < 0) {
> +               if (actual_w)
> +                       src_x -= crtc_x;
> +               crtc_x = 0;
> +       }
> +
> +       if (crtc_y < 0) {
> +               if (actual_h)
> +                       src_y -= crtc_y;
> +               crtc_y = 0;
> +       }
> +
> +       /* set drm framebuffer data. */
> +       overlay->fb_x = src_x;
> +       overlay->fb_y = src_y;
> +       overlay->fb_width = fb->width;
> +       overlay->fb_height = fb->height;
> +       overlay->src_width = src_w;
> +       overlay->src_height = src_h;
> +       overlay->bpp = fb->bits_per_pixel;
> +       overlay->pitch = fb->pitches[0];
> +       overlay->pixel_format = fb->pixel_format;
> +
> +       /* set overlay range to be displayed. */
> +       overlay->crtc_x = crtc_x;
> +       overlay->crtc_y = crtc_y;
> +       overlay->crtc_width = actual_w;
> +       overlay->crtc_height = actual_h;
> +
> +       /* set drm mode data. */
> +       overlay->mode_width = crtc->mode.hdisplay;
> +       overlay->mode_height = crtc->mode.vdisplay;
> +       overlay->refresh = crtc->mode.vrefresh;
> +       overlay->scan_flag = crtc->mode.flags;
> +
> +       DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
> +                       overlay->crtc_x, overlay->crtc_y,
> +                       overlay->crtc_width, overlay->crtc_height);
> +
> +       rockchip_drm_crtc_plane_mode_set(crtc, overlay);
> +
> +       return 0;
> +}
> +
> +void rockchip_plane_commit(struct drm_plane *plane)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
> +
> +       rockchip_drm_crtc_plane_commit(plane->crtc, overlay->zpos);
> +}
> +
> +void rockchip_plane_dpms(struct drm_plane *plane, int mode)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       struct rockchip_drm_overlay *overlay = &rockchip_plane->overlay;
> +
> +       if (mode == DRM_MODE_DPMS_ON) {
> +               if (rockchip_plane->enabled)
> +                       return;
> +
> +               rockchip_drm_crtc_plane_enable(plane->crtc, overlay->zpos);
> +               rockchip_plane->enabled = true;
> +       } else {
> +               if (!rockchip_plane->enabled)
> +                       return;
> +
> +               rockchip_drm_crtc_plane_disable(plane->crtc, overlay->zpos);
> +               rockchip_plane->enabled = false;
> +       }
> +}
> +
> +static int
> +rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> +                    struct drm_framebuffer *fb, int crtc_x, int crtc_y,
> +                    unsigned int crtc_w, unsigned int crtc_h,
> +                    uint32_t src_x, uint32_t src_y,
> +                    uint32_t src_w, uint32_t src_h)
> +{
> +       int ret;
> +
> +       ret = rockchip_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
> +                       crtc_w, crtc_h, src_x >> 16, src_y >> 16,
> +                       src_w >> 16, src_h >> 16);
> +       if (ret < 0)
> +               return ret;
> +
> +       plane->crtc = crtc;
> +
> +       rockchip_plane_commit(plane);
> +       rockchip_plane_dpms(plane, DRM_MODE_DPMS_ON);
> +
> +       return 0;
> +}
> +
> +static int rockchip_disable_plane(struct drm_plane *plane)
> +{
> +       rockchip_plane_dpms(plane, DRM_MODE_DPMS_OFF);
> +
> +       return 0;
> +}
> +
> +static void rockchip_plane_destroy(struct drm_plane *plane)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +
> +       rockchip_disable_plane(plane);
> +       drm_plane_cleanup(plane);
> +       kfree(rockchip_plane);
> +}
> +
> +static int rockchip_plane_set_property(struct drm_plane *plane,
> +                                    struct drm_property *property,
> +                                    uint64_t val)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +
> +       if (property == dev_priv->plane_zpos_property) {
> +               rockchip_plane->overlay.zpos = val;
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static struct drm_plane_funcs rockchip_plane_funcs = {
> +       .update_plane = rockchip_update_plane,
> +       .disable_plane = rockchip_disable_plane,
> +       .destroy = rockchip_plane_destroy,
> +       .set_property = rockchip_plane_set_property,
> +};
> +
> +static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct drm_property *prop;
> +
> +       prop = dev_priv->plane_zpos_property;
> +       if (!prop) {
> +               prop = drm_property_create_range(dev, 0, "zpos", 0,
> +                                                MAX_PLANE - 1);
> +               if (!prop)
> +                       return;
> +
> +               dev_priv->plane_zpos_property = prop;
> +       }
> +
> +       drm_object_attach_property(&plane->base, prop, 0);
> +}
> +
> +struct drm_plane *rockchip_plane_init(struct drm_device *dev,
> +                                   unsigned long possible_crtcs, bool priv)
> +{
> +       struct rockchip_plane *rockchip_plane;
> +       int err;
> +
> +       rockchip_plane = kzalloc(sizeof(struct rockchip_plane), GFP_KERNEL);
> +       if (!rockchip_plane)
> +               return NULL;
> +
> +       err = drm_plane_init(dev, &rockchip_plane->base, possible_crtcs,
> +                             &rockchip_plane_funcs, formats,
> +                             ARRAY_SIZE(formats), priv);
> +       if (err) {
> +               DRM_ERROR("failed to initialize plane\n");
> +               kfree(rockchip_plane);
> +               return NULL;
> +       }
> +
> +       if (priv)
> +               rockchip_plane->overlay.zpos = DEFAULT_ZPOS;
> +       else
> +               rockchip_plane_attach_zpos_property(&rockchip_plane->base);
> +
> +       return &rockchip_plane->base;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_plane.h b/drivers/gpu/drm/rockchip/rockchip_drm_plane.h
> new file mode 100644
> index 0000000..3832496
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_plane.h
> @@ -0,0 +1,30 @@
> +/* rockchip_drm_plane.h
> + *
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:mark yao <mark.yao at rock-chips.com>
> + *
> + * based on exynos_drm_plane.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_PLANE_H_
> +#define _ROCKCHIP_DRM_PLANE_H_
> +
> +int rockchip_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
> +                         struct drm_framebuffer *fb, int crtc_x, int crtc_y,
> +                         unsigned int crtc_w, unsigned int crtc_h,
> +                         uint32_t src_x, uint32_t src_y,
> +                         uint32_t src_w, uint32_t src_h);
> +void rockchip_plane_commit(struct drm_plane *plane);
> +void rockchip_plane_dpms(struct drm_plane *plane, int mode);
> +struct drm_plane *rockchip_plane_init(struct drm_device *dev,
> +                                   unsigned long possible_crtcs, bool priv);
> +#endif /* _ROCKCHIP_DRM_PLANE_H_ */
> diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
> new file mode 100644
> index 0000000..7f705f4
> --- /dev/null
> +++ b/include/uapi/drm/rockchip_drm.h
> @@ -0,0 +1,155 @@
> +/* rockchip_drm.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * Authors:
> + *       mark yao<yzq at rock-chips.com>
> + *
> + * base on exynos_drm.h
> + *
> + * 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.
> + */
> +
> +#ifndef _UAPI_ROCKCHIP_DRM_H_
> +#define _UAPI_ROCKCHIP_DRM_H_
> +
> +#include <drm/drm.h>
> +
> +/**
> + * User-desired buffer creation information structure.
> + *
> + * @size: user-desired memory allocation size.
> + *     - this size value would be page-aligned internally.
> + * @flags: user request for setting memory type or cache attributes.
> + * @handle: returned a handle to created gem object.
> + *     - this handle will be set by gem module of kernel side.
> + */
> +struct drm_rockchip_gem_create {
> +       uint64_t size;
> +       unsigned int flags;
> +       unsigned int handle;
> +};
> +
> +/**
> + * A structure for getting buffer offset.
> + *
> + * @handle: a pointer to gem object created.
> + * @pad: just padding to be 64-bit aligned.
> + * @offset: relatived offset value of the memory region allocated.
> + *     - this value should be set by user.
> + */
> +struct drm_rockchip_gem_map_off {
> +       unsigned int handle;
> +       unsigned int pad;
> +       uint64_t offset;
> +};
> +
> +/**
> + * A structure for mapping buffer.
> + *
> + * @handle: a handle to gem object created.
> + * @pad: just padding to be 64-bit aligned.
> + * @size: memory size to be mapped.
> + * @mapped: having user virtual address mmaped.
> + *      - this variable would be filled by rockchip gem module
> + *      of kernel side with user virtual address which is allocated
> + *      by do_mmap().
> + */
> +struct drm_rockchip_gem_mmap {
> +       unsigned int handle;
> +       unsigned int pad;
> +       uint64_t size;
> +       uint64_t mapped;
> +};
> +
> +/**
> + * A structure to gem information.
> + *
> + * @handle: a handle to gem object created.
> + * @flags: flag value including memory type and cache attribute and
> + *      this value would be set by driver.
> + * @size: size to memory region allocated by gem and this size would
> + *      be set by driver.
> + */
> +struct drm_rockchip_gem_info {
> +       unsigned int handle;
> +       unsigned int flags;
> +       uint64_t size;
> +};
> +
> +/* memory type definitions. */
> +enum e_drm_rockchip_gem_mem_type {
> +       /* Physically Continuous memory and used as default. */
> +       ROCKCHIP_BO_CONTIG = 0 << 0,
> +       /* Physically Non-Continuous memory. */
> +       ROCKCHIP_BO_NONCONTIG = 1 << 0,
> +       /* non-cachable mapping and used as default. */
> +       ROCKCHIP_BO_NONCACHABLE = 0 << 1,
> +       /* cachable mapping. */
> +       ROCKCHIP_BO_CACHABLE = 1 << 1,
> +       /* write-combine mapping. */
> +       ROCKCHIP_BO_WC = 1 << 2,
> +       ROCKCHIP_BO_MASK = ROCKCHIP_BO_NONCONTIG | ROCKCHIP_BO_CACHABLE |
> +                                       ROCKCHIP_BO_WC
> +};

for userspace facing interfaces, I think you should stick with #define's

BR,
-R

> +
> +enum drm_rockchip_ops_id {
> +       ROCKCHIP_DRM_OPS_SRC,
> +       ROCKCHIP_DRM_OPS_DST,
> +       ROCKCHIP_DRM_OPS_MAX,
> +};
> +
> +struct drm_rockchip_sz {
> +       __u32 hsize;
> +       __u32 vsize;
> +};
> +
> +struct drm_rockchip_pos {
> +       __u32 x;
> +       __u32 y;
> +       __u32 w;
> +       __u32 h;
> +};
> +
> +enum drm_rockchip_flip {
> +       ROCKCHIP_DRM_FLIP_NONE = (0 << 0),
> +       ROCKCHIP_DRM_FLIP_VERTICAL = (1 << 0),
> +       ROCKCHIP_DRM_FLIP_HORIZONTAL = (1 << 1),
> +       ROCKCHIP_DRM_FLIP_BOTH = ROCKCHIP_DRM_FLIP_VERTICAL |
> +                       ROCKCHIP_DRM_FLIP_HORIZONTAL,
> +};
> +
> +enum drm_rockchip_degree {
> +       ROCKCHIP_DRM_DEGREE_0,
> +       ROCKCHIP_DRM_DEGREE_90,
> +       ROCKCHIP_DRM_DEGREE_180,
> +       ROCKCHIP_DRM_DEGREE_270,
> +};
> +
> +enum drm_rockchip_planer {
> +       ROCKCHIP_DRM_PLANAR_Y,
> +       ROCKCHIP_DRM_PLANAR_CB,
> +       ROCKCHIP_DRM_PLANAR_CR,
> +       ROCKCHIP_DRM_PLANAR_MAX,
> +};
> +
> +#define DRM_ROCKCHIP_GEM_CREATE                0x00
> +#define DRM_ROCKCHIP_GEM_MAP_OFFSET    0x01
> +#define DRM_ROCKCHIP_GEM_MMAP          0x02
> +/* Reserved 0x03 ~ 0x05 for rockchip specific gem ioctl */
> +#define DRM_ROCKCHIP_GEM_GET           0x04
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_CREATE          DRM_IOWR(DRM_COMMAND_BASE + \
> +               DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET      DRM_IOWR(DRM_COMMAND_BASE + \
> +               DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_MMAP    DRM_IOWR(DRM_COMMAND_BASE + \
> +               DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_GET     DRM_IOWR(DRM_COMMAND_BASE + \
> +               DRM_ROCKCHIP_GEM_GET,   struct drm_rockchip_gem_info)
> +#endif /* _UAPI_ROCKCHIP_DRM_H_ */
> --
> 1.7.9.5
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


More information about the dri-devel mailing list