[PATCH] drm: Add Drm driver for Rockchip Socs

From: mark yao
Date: Thu Jul 03 2014 - 07:20:29 EST


From: mark <mark.yao@xxxxxxxxxxxxxx>

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.

Signed-off-by: mark <mark.yao@xxxxxxxxxxxxxx>
---
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.
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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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)
+{
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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);
+ 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);
+ 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));
+ 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);
+
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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,
+};
+
+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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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) {
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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;
+
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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)
+{
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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);
+
+/* 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@xxxxxxxxxxxxxx>
+ *
+ * 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);
+
+ 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxx>
+ *
+ * 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
+};
+
+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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/