First draft 68/54268/1
authorAndrii Sokolenko <a.sokolenko@samsung.com>
Wed, 9 Dec 2015 15:35:55 +0000 (17:35 +0200)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 14 Dec 2015 11:19:57 +0000 (20:19 +0900)
Signed-off-by: Andrii Sokolenko <a.sokolenko@samsung.com>
Change-Id: Ic0835f7a939c1bc13dd0dc62772b8382989c4dbc

12 files changed:
COPYING [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
packaging/libtdm-sprd.manifest [new file with mode: 0644]
packaging/libtdm-sprd.spec [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/tdm_sprd.c [new file with mode: 0644]
src/tdm_sprd.h [new file with mode: 0644]
src/tdm_sprd_display.c [new file with mode: 0644]
src/tdm_sprd_format.c [new file with mode: 0644]
src/tdm_sprd_pp.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..f737ba1
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,21 @@
+Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..af437a6
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..30d679f
--- /dev/null
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+autoreconf --force --install --verbose "$srcdir"
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..ddb7f06
--- /dev/null
@@ -0,0 +1,55 @@
+AC_PREREQ([2.60])
+AC_INIT([libtdm-sprd],
+        [1.0.0],
+        [https://www.tizen.org],
+        [libtdm-sprd])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_SRCDIR([Makefile.am])
+AM_INIT_AUTOMAKE([1.10 foreign dist-bzip2])
+AM_MAINTAINER_MODE([enable])
+
+# Check for programs
+AC_PROG_CC
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+AC_FUNC_ALLOCA
+
+# Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT([disable-static])
+
+# Enable quiet compiles on automake 1.11.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+PKG_CHECK_MODULES(TDM_SPRD, libtdm libtbm libdrm libdrm_sprd)
+PKG_CHECK_MODULES(UDEV, libudev, [udev=yes], [udev=no])
+if test x"$udev" = xyes; then
+       AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection])
+       TDM_SPRD_CFLAGS="$TDM_SPRD_CFLAGS $UDEV_CFLAGS"
+       TDM_SPRD_LIBS="$TDM_SPRD_LIBS $UDEV_LIBS"
+fi
+
+AC_SUBST(TDM_SPRD_CFLAGS)
+AC_SUBST(TDM_SPRD_LIBS)
+
+# set the dir for the tbm module
+DEFAULT_TDM_MODULE_PATH="${libdir}/tdm"
+AC_ARG_WITH(tdm-module-path, AS_HELP_STRING([--with-tdm-module-path=PATH], [tdm module dir]),
+                               [ TDM_MODULE_PATH="$withval" ],
+                               [ TDM_MODULE_PATH="${DEFAULT_TDM_MODULE_PATH}" ])
+AC_SUBST(TDM_MODULE_PATH)
+
+# For enumerating devices in test case
+AC_OUTPUT([
+       Makefile
+       src/Makefile])
+
+echo ""
+echo "$PACKAGE_STRING will be compiled with:"
+echo ""
+echo "TDM_SPRD_CFLAGS : $TDM_SPRD_CFLAGS"
+echo "TDM_SPRD_LIBS   : $TDM_SPRD_LIBS"
+echo "TDM_MODULE_DIR  : $TDM_MODULE_PATH"
+echo ""
diff --git a/packaging/libtdm-sprd.manifest b/packaging/libtdm-sprd.manifest
new file mode 100644 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
diff --git a/packaging/libtdm-sprd.spec b/packaging/libtdm-sprd.spec
new file mode 100644 (file)
index 0000000..72262d1
--- /dev/null
@@ -0,0 +1,42 @@
+Name:           libtdm-sprd
+Version:        1.0.0
+Release:        0
+Summary:        Tizen Display Manager Spreadtrum Back-End Library
+Group:          Development/Libraries
+License:        MIT
+Source0:        %{name}-%{version}.tar.gz
+Source1001:     %{name}.manifest
+BuildRequires: pkgconfig(libdrm)
+BuildRequires: pkgconfig(libdrm_sprd)
+BuildRequires: pkgconfig(libudev)
+BuildRequires: pkgconfig(libtdm)
+
+%description
+Back-End library of Tizen Display Manager Spreadtrum : libtdm-mgr SPRD library
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+%reconfigure --prefix=%{_prefix} --libdir=%{_libdir}  --disable-static \
+             CFLAGS="${CFLAGS} -Wall -Werror" \
+             LDFLAGS="${LDFLAGS} -Wl,--hash-style=both -Wl,--as-needed"
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+%post
+if [ -f %{_libdir}/tdm/libtdm-default.so ]; then
+    rm -rf %{_libdir}/tdm/libtdm-default.so
+fi
+ln -s libtdm-sprd.so %{_libdir}/tdm/libtdm-default.so
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%license COPYING
+%defattr(-,root,root,-)
+%{_libdir}/tdm/libtdm-sprd.so
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..269f8ff
--- /dev/null
@@ -0,0 +1,14 @@
+AM_CFLAGS = \
+       $(TDM_SPRD_CFLAGS) \
+       -I$(top_srcdir)/src
+
+libtdm_sprd_la_LTLIBRARIES = libtdm-sprd.la
+libtdm_sprd_ladir = $(TDM_MODULE_PATH)
+libtdm_sprd_la_LDFLAGS = -module -avoid-version
+libtdm_sprd_la_LIBADD = $(TDM_SPRD_LIBS) -ldl -ldrm_sprd -ldrm
+
+libtdm_sprd_la_SOURCES = \
+       tdm_sprd_format.c \
+       tdm_sprd_display.c \
+       tdm_sprd_pp.c \
+       tdm_sprd.c
diff --git a/src/tdm_sprd.c b/src/tdm_sprd.c
new file mode 100644 (file)
index 0000000..1a7be3a
--- /dev/null
@@ -0,0 +1,318 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_UDEV
+#include <libudev.h>
+#endif
+
+#include "sprd_drmif.h"
+#include "tdm_sprd.h"
+#include <tdm_helper.h>
+
+#define SPRD_DRM_NAME "sprd"
+
+static tdm_func_display sprd_func_display =
+{
+    sprd_display_get_capabilitiy,
+    sprd_display_get_pp_capability,
+    NULL,  //display_get_capture_capability
+    sprd_display_get_outputs,
+    sprd_display_get_fd,
+    sprd_display_handle_events,
+    sprd_display_create_pp,
+    sprd_output_get_capability,
+    sprd_output_get_layers,
+    sprd_output_set_property,
+    sprd_output_get_property,
+    sprd_output_wait_vblank,
+    sprd_output_set_vblank_handler,
+    sprd_output_commit,
+    sprd_output_set_commit_handler,
+    sprd_output_set_dpms,
+    sprd_output_get_dpms,
+    sprd_output_set_mode,
+    sprd_output_get_mode,
+    NULL,   //output_create_capture
+    sprd_layer_get_capability,
+    sprd_layer_set_property,
+    sprd_layer_get_property,
+    sprd_layer_set_info,
+    sprd_layer_get_info,
+    sprd_layer_set_buffer,
+    sprd_layer_unset_buffer,
+    NULL,    //layer_create_capture
+};
+
+static tdm_func_pp sprd_func_pp =
+{
+    sprd_pp_destroy,
+    sprd_pp_set_info,
+    sprd_pp_attach,
+    sprd_pp_commit,
+    sprd_pp_set_done_handler,
+};
+
+static tdm_sprd_data *sprd_data;
+
+static int
+_tdm_sprd_open_drm(void)
+{
+    int fd = -1;
+
+    fd = drmOpen(SPRD_DRM_NAME, NULL);
+    if (fd < 0)
+    {
+        TDM_ERR("Cannot open '%s' drm", SPRD_DRM_NAME);
+    }
+
+#ifdef HAVE_UDEV
+    if (fd < 0)
+    {
+        struct udev *udev;
+        struct udev_enumerate *e;
+        struct udev_list_entry *entry;
+        struct udev_device *device, *drm_device, *device_parent;
+        const char *filename;
+
+        TDM_WRN("Cannot open drm device.. search by udev");
+        udev = udev_new();
+        if (!udev)
+        {
+            TDM_ERR("fail to initialize udev context\n");
+            goto close_l;
+        }
+
+        /* Will try to find sys path /sprd-drm/drm/card0 */
+        e = udev_enumerate_new(udev);
+        udev_enumerate_add_match_subsystem(e, "drm");
+        udev_enumerate_add_match_sysname(e, "card[0-9]*");
+        udev_enumerate_scan_devices(e);
+
+        drm_device = NULL;
+        udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e))
+        {
+            device = udev_device_new_from_syspath(udev_enumerate_get_udev(e),
+                                                  udev_list_entry_get_name
+                                                  (entry));
+            device_parent = udev_device_get_parent(device);
+            /* Not need unref device_parent. device_parent and device have same refcnt */
+            if (device_parent)
+            {
+                if (strcmp(udev_device_get_sysname(device_parent), "sprd-drm") == 0)
+                {
+                    drm_device = device;
+                    TDM_DBG("Found drm device: '%s' (%s)\n",
+                            udev_device_get_syspath(drm_device),
+                            udev_device_get_sysname(device_parent));
+                    break;
+                }
+            }
+            udev_device_unref(device);
+        }
+
+        if (drm_device == NULL)
+        {
+            TDM_ERR("fail to find drm device\n");
+            udev_enumerate_unref(e);
+            udev_unref(udev);
+            goto close_l;
+        }
+
+        filename = udev_device_get_devnode(drm_device);
+
+        fd = open(filename, O_RDWR | O_CLOEXEC);
+        if (fd < 0)
+        {
+            TDM_ERR("Cannot open drm device(%s)\n", filename);
+        }
+        udev_device_unref(drm_device);
+        udev_enumerate_unref(e);
+        udev_unref(udev);
+    }
+close_l:
+#endif
+    return fd;
+}
+#if 0
+static int
+_tdm_sprd_drm_user_handler(struct drm_event *event)
+{
+    struct drm_sprd_ipp_event *ipp;
+
+    if (event->type != DRM_EXYNOS_IPP_EVENT)
+        return -1;
+
+    TDM_DBG("got ipp event");
+
+    ipp = (struct drm_sprd_ipp_event *)event;
+
+    tdm_sprd_pp_handler(ipp->prop_id, ipp->buf_id, ipp->tv_sec, ipp->tv_usec,
+                          (void *)(unsigned long)ipp->user_data);
+
+    return 0;
+}
+#endif
+void
+tdm_sprd_deinit(tdm_backend_data *bdata)
+{
+    if (sprd_data != bdata)
+        return;
+
+    TDM_INFO("deinit");
+#if 0
+    drmRemoveUserHandler(tdm_helper_drm_fd, _tdm_sprd_drm_user_handler);
+#endif
+    tdm_sprd_display_destroy_output_list(sprd_data);
+
+    if (sprd_data->plane_res)
+        drmModeFreePlaneResources(sprd_data->plane_res);
+    if (sprd_data->mode_res)
+        drmModeFreeResources(sprd_data->mode_res);
+    if (sprd_data->drm_fd >= 0)
+    {
+        if (sprd_data->sprd_drm_dev)
+            sprd_device_destroy(sprd_data->sprd_drm_dev);
+        close(sprd_data->drm_fd);
+    }
+    free(sprd_data);
+    sprd_data = NULL;
+}
+
+tdm_backend_data*
+tdm_sprd_init(tdm_display *dpy, tdm_error *error)
+{
+    tdm_error ret;
+
+    if (!dpy)
+    {
+        TDM_ERR("display is null");
+        if (error)
+            *error = TDM_ERROR_INVALID_PARAMETER;
+        return NULL;
+    }
+
+    if (sprd_data)
+    {
+        TDM_ERR("failed_l: init twice");
+        if (error)
+            *error = TDM_ERROR_BAD_REQUEST;
+        return NULL;
+    }
+
+    sprd_data = calloc(1, sizeof(tdm_sprd_data));
+    if (!sprd_data)
+    {
+        TDM_ERR("alloc failed_l");
+        if (error)
+            *error = TDM_ERROR_OUT_OF_MEMORY;
+        return NULL;
+    }
+
+    LIST_INITHEAD(&sprd_data->output_list);
+    LIST_INITHEAD(&sprd_data->buffer_list);
+
+    ret = tdm_backend_register_func_display(dpy, &sprd_func_display);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_l;
+
+    ret = tdm_backend_register_func_pp(dpy, &sprd_func_pp);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_l;
+
+    sprd_data->dpy = dpy;
+    sprd_data->fb_fd = -1;
+    sprd_data->drm_fd = -1;
+#if 0
+    if (tdm_helper_drm_fd >= 0)
+    {
+        sprd_data->drm_fd = tdm_helper_drm_fd;
+        drmAddUserHandler(tdm_helper_drm_fd, _tdm_sprd_drm_user_handler);
+    }
+#endif
+    if (sprd_data->drm_fd < 0)
+        sprd_data->drm_fd = _tdm_sprd_open_drm();
+
+    if (sprd_data->drm_fd < 0)
+    {
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_l;
+    }
+    if ((sprd_data->sprd_drm_dev = sprd_device_create(sprd_data->drm_fd)) == NULL)
+    {
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_l;
+    }
+
+#if 0
+    if (drmSetClientCap(sprd_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0)
+        TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed_l");
+#endif
+    sprd_data->mode_res = drmModeGetResources(sprd_data->drm_fd);
+    if (!sprd_data->mode_res)
+    {
+        TDM_ERR("no drm resource: %m");
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_l;
+    }
+
+    sprd_data->plane_res = drmModeGetPlaneResources(sprd_data->drm_fd);
+    if (!sprd_data->plane_res)
+    {
+        TDM_ERR("no drm plane resource: %m");
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_l;
+    }
+
+    if (sprd_data->plane_res->count_planes <= 0)
+    {
+        TDM_ERR("no drm plane resource");
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_l;
+    }
+
+    ret = tdm_sprd_display_get_property(sprd_data, sprd_data->plane_res->planes[0],
+                                          DRM_MODE_OBJECT_PLANE, "zpos", NULL,
+                                          &sprd_data->is_immutable_zpos);
+    if (ret == TDM_ERROR_NONE)
+    {
+        sprd_data->has_zpos_info = 1;
+        if (sprd_data->is_immutable_zpos)
+            TDM_DBG("plane has immutable zpos info");
+    }
+    else
+        TDM_DBG("plane doesn't have zpos info");
+
+    ret = tdm_sprd_display_create_output_list(sprd_data);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_l;
+
+    ret = tdm_sprd_display_create_layer_list(sprd_data);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_l;
+
+    if (error)
+        *error = TDM_ERROR_NONE;
+
+    TDM_INFO("init success!");
+
+    return (tdm_backend_data*)sprd_data;
+failed_l:
+    if (error)
+        *error = ret;
+
+    tdm_sprd_deinit(sprd_data);
+
+    TDM_ERR("init failed!");
+    return NULL;
+}
+
+tdm_backend_module tdm_backend_module_data =
+{
+    "sprd",
+    "Samsung",
+    TDM_BACKEND_ABI_VERSION,
+    tdm_sprd_init,
+    tdm_sprd_deinit
+};
+
diff --git a/src/tdm_sprd.h b/src/tdm_sprd.h
new file mode 100644 (file)
index 0000000..04e04b0
--- /dev/null
@@ -0,0 +1,130 @@
+#ifndef _TDM_SPRD_H_
+#define _TDM_SPRD_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+#include <tdm_backend.h>
+#include <tdm_log.h>
+#include <tdm_list.h>
+
+/* sprd backend functions (display) */
+tdm_error    sprd_display_get_capabilitiy(tdm_backend_data *bdata, tdm_caps_display *caps);
+tdm_error    sprd_display_get_pp_capability(tdm_backend_data *bdata, tdm_caps_pp *caps);
+tdm_output** sprd_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error);
+tdm_error    sprd_display_get_fd(tdm_backend_data *bdata, int *fd);
+tdm_error    sprd_display_handle_events(tdm_backend_data *bdata);
+tdm_pp*      sprd_display_create_pp(tdm_backend_data *bdata, tdm_error *error);
+tdm_error    sprd_output_get_capability(tdm_output *output, tdm_caps_output *caps);
+tdm_layer**  sprd_output_get_layers(tdm_output *output, int *count, tdm_error *error);
+tdm_error    sprd_output_set_property(tdm_output *output, unsigned int id, tdm_value value);
+tdm_error    sprd_output_get_property(tdm_output *output, unsigned int id, tdm_value *value);
+tdm_error    sprd_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data);
+tdm_error    sprd_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func);
+tdm_error    sprd_output_commit(tdm_output *output, int sync, void *user_data);
+tdm_error    sprd_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func);
+tdm_error    sprd_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value);
+tdm_error    sprd_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value);
+tdm_error    sprd_output_set_mode(tdm_output *output, tdm_output_mode *mode);
+tdm_error    sprd_output_get_mode(tdm_output *output, const tdm_output_mode **mode);
+tdm_error    sprd_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps);
+tdm_error    sprd_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value);
+tdm_error    sprd_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value);
+tdm_error    sprd_layer_set_info(tdm_layer *layer, tdm_info_layer *info);
+tdm_error    sprd_layer_get_info(tdm_layer *layer, tdm_info_layer *info);
+tdm_error    sprd_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer);
+tdm_error    sprd_layer_unset_buffer(tdm_layer *layer);
+void         sprd_pp_destroy(tdm_pp *pp);
+tdm_error    sprd_pp_set_info(tdm_pp *pp, tdm_info_pp *info);
+tdm_error    sprd_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst);
+tdm_error    sprd_pp_commit(tdm_pp *pp);
+tdm_error    sprd_pp_set_done_handler(tdm_pp *pp, tdm_pp_done_handler func, void *user_data);
+
+
+/* sprd module internal macros, structures, functions */
+#define NEVER_GET_HERE() TDM_ERR("** NEVER GET HERE **")
+
+#define C(b,m)              (((b) >> (m)) & 0xFF)
+#define B(c,s)              ((((unsigned int)(c)) & 0xff) << (s))
+#define FOURCC(a,b,c,d)     (B(d,24) | B(c,16) | B(b,8) | B(a,0))
+#define FOURCC_STR(id)      C(id,0), C(id,8), C(id,16), C(id,24)
+
+#define IS_RGB(format)      (format == TBM_FORMAT_XRGB8888 || format == TBM_FORMAT_ARGB8888 || \
+                             format == TBM_FORMAT_XBGR8888 || format == TBM_FORMAT_ABGR8888)
+
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define SWAP(a, b)  ({int t; t = a; a = b; b = t;})
+#define ROUNDUP(x)  (ceil (floor ((float)(height) / 4)))
+
+#define ALIGN_TO_16B(x)    ((((x) + (1 <<  4) - 1) >>  4) <<  4)
+#define ALIGN_TO_32B(x)    ((((x) + (1 <<  5) - 1) >>  5) <<  5)
+#define ALIGN_TO_128B(x)   ((((x) + (1 <<  7) - 1) >>  7) <<  7)
+#define ALIGN_TO_2KB(x)    ((((x) + (1 << 11) - 1) >> 11) << 11)
+#define ALIGN_TO_8KB(x)    ((((x) + (1 << 13) - 1) >> 13) << 13)
+#define ALIGN_TO_64KB(x)   ((((x) + (1 << 16) - 1) >> 16) << 16)
+
+#define RETURN_VAL_IF_FAIL(cond, val) {\
+    if (!(cond)) {\
+        TDM_ERR("'%s' failed", #cond);\
+        return val;\
+    }\
+}
+
+typedef struct _tdm_sprd_data
+{
+    tdm_display *dpy;
+    int drm_fd;
+    int fb_fd;
+    void * sprd_drm_dev;
+
+    /* If true, it means that the device has many planes for one crtc. If false,
+     * planes are dedicated to specific crtc.
+     */
+    int has_zpos_info;
+
+    /* If has_zpos_info is false and is_immutable_zpos is true, it means that
+     * planes are dedicated to specific crtc.
+     */
+    int is_immutable_zpos;
+
+    drmModeResPtr mode_res;
+    drmModePlaneResPtr plane_res;
+
+    struct list_head output_list;
+    struct list_head buffer_list;
+} tdm_sprd_data;
+
+uint32_t     tdm_sprd_format_to_drm_format(tbm_format format);
+tbm_format   tdm_sprd_format_to_tbm_format(uint32_t format);
+
+tdm_error    tdm_sprd_display_create_output_list(tdm_sprd_data *sprd_data);
+void         tdm_sprd_display_destroy_output_list(tdm_sprd_data *sprd_data);
+tdm_error    tdm_sprd_display_create_layer_list(tdm_sprd_data *sprd_data);
+tdm_error    tdm_sprd_display_set_property(tdm_sprd_data *sprd_data,
+                                             unsigned int obj_id, unsigned int obj_type,
+                                             const char *name, unsigned int value);
+tdm_error    tdm_sprd_display_get_property(tdm_sprd_data *sprd_data,
+                                             unsigned int obj_id, unsigned int obj_type,
+                                             const char *name, unsigned int *value, int *is_immutable);
+
+tdm_error    tdm_sprd_pp_get_capability(tdm_sprd_data *sprd_data, tdm_caps_pp *caps);
+tdm_pp*      tdm_sprd_pp_create(tdm_sprd_data *sprd_data, tdm_error *error);
+void         tdm_sprd_pp_handler(unsigned int prop_id, unsigned int *buf_idx,
+                                   unsigned int tv_sec, unsigned int tv_usec, void *data);
+
+#endif /* _TDM_SPRD_H_ */
diff --git a/src/tdm_sprd_display.c b/src/tdm_sprd_display.c
new file mode 100644 (file)
index 0000000..9104f0b
--- /dev/null
@@ -0,0 +1,1851 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <drm_fourcc.h>
+#include <tdm_helper.h>
+#include "tdm_sprd.h"
+
+#define MIN_WIDTH   32
+#define LAYER_COUNT_PER_OUTPUT   2
+
+
+typedef struct _tdm_sprd_output_data tdm_sprd_output_data;
+typedef struct _tdm_sprd_layer_data tdm_sprd_layer_data;
+typedef struct _tdm_sprd_vblank_data tdm_sprd_vblank_data;
+
+typedef enum
+{
+    VBLANK_TYPE_WAIT,
+    VBLANK_TYPE_COMMIT,
+} vblank_type;
+
+typedef struct _tdm_sprd_display_buffer
+{
+    struct list_head link;
+
+    unsigned int fb_id;
+    tbm_surface_h buffer;
+    int width;
+} tdm_sprd_display_buffer;
+
+struct _tdm_sprd_vblank_data
+{
+    vblank_type type;
+    tdm_sprd_output_data *output_data;
+    void *user_data;
+};
+
+struct _tdm_sprd_output_data
+{
+    struct list_head link;
+
+    /* data which are fixed at initializing */
+    tdm_sprd_data *sprd_data;
+    uint32_t connector_id;
+    uint32_t encoder_id;
+    uint32_t crtc_id;
+    uint32_t pipe;
+    uint32_t dpms_prop_id;
+    int count_modes;
+    drmModeModeInfoPtr drm_modes;
+    tdm_output_mode *output_modes;
+    tdm_output_type connector_type;
+    unsigned int connector_type_id;
+    struct list_head layer_list;
+    tdm_sprd_layer_data *primary_layer;
+
+    /* not fixed data below */
+    tdm_output_vblank_handler vblank_func;
+    tdm_output_commit_handler commit_func;
+
+    tdm_output_conn_status status;
+
+    int mode_changed;
+    tdm_output_mode *current_mode;
+
+    int waiting_vblank_event;
+};
+
+struct _tdm_sprd_layer_data
+{
+    struct list_head link;
+
+    /* data which are fixed at initializing */
+    tdm_sprd_data *sprd_data;
+    tdm_sprd_output_data *output_data;
+    uint32_t plane_id;
+    tdm_layer_capability capabilities;
+    int zpos;
+
+    /* not fixed data below */
+    tdm_info_layer info;
+    int info_changed;
+
+    tdm_sprd_display_buffer *display_buffer;
+    int display_buffer_changed;
+};
+
+typedef struct _Drm_Event_Context
+{
+    void (*vblank_handler)(int fd, unsigned int sequence, unsigned int tv_sec,
+                           unsigned int tv_usec, void *user_data);
+    void (*pp_handler)(int fd, unsigned int  prop_id, unsigned int *buf_idx,
+                       unsigned int  tv_sec, unsigned int  tv_usec, void *user_data);
+} Drm_Event_Context;
+
+static tdm_error
+check_hw_restriction(unsigned int crtc_w, unsigned int buf_w,
+                     unsigned int src_x, unsigned int src_w, unsigned int dst_x, unsigned int dst_w,
+                     unsigned int *new_src_x, unsigned int *new_src_w,
+                     unsigned int *new_dst_x, unsigned int *new_dst_w)
+{
+    int start, end, diff;
+    int virtual_screen;
+
+    *new_src_x = src_x;
+    *new_src_w = src_w;
+    *new_dst_x = dst_x;
+    *new_dst_w = dst_w;
+
+    if (buf_w < MIN_WIDTH || buf_w % 2)
+    {
+        TDM_ERR("buf_w(%d) not 2's multiple or less than %d", buf_w, MIN_WIDTH);
+        return TDM_ERROR_BAD_REQUEST;
+    }
+
+    if (src_x > dst_x || ((dst_x - src_x) + buf_w) > crtc_w)
+        virtual_screen = 1;
+    else
+        virtual_screen = 0;
+
+    start = (dst_x < 0) ? 0 : dst_x;
+    end = ((dst_x + dst_w) > crtc_w) ? crtc_w : (dst_x + dst_w);
+
+    /* check window minimun width */
+    if ((end - start) < MIN_WIDTH)
+    {
+        TDM_ERR("visible_w(%d) less than %d", end-start, MIN_WIDTH);
+        return TDM_ERROR_BAD_REQUEST;
+    }
+
+    if (!virtual_screen)
+    {
+        /* Pagewidth of window (= 8 byte align / bytes-per-pixel ) */
+        if ((end - start) % 2)
+            end--;
+    }
+    else
+    {
+        /* You should align the sum of PAGEWIDTH_F and OFFSIZE_F double-word (8 byte) boundary. */
+        if (end % 2)
+            end--;
+    }
+
+    *new_dst_x = start;
+    *new_dst_w = end - start;
+    *new_src_w = *new_dst_w;
+    diff = start - dst_x;
+    *new_src_x += diff;
+
+    RETURN_VAL_IF_FAIL(*new_src_w > 0, TDM_ERROR_BAD_REQUEST);
+    RETURN_VAL_IF_FAIL(*new_dst_w > 0, TDM_ERROR_BAD_REQUEST);
+
+    if (src_x != *new_src_x || src_w != *new_src_w || dst_x != *new_dst_x || dst_w != *new_dst_w)
+        TDM_DBG("=> buf_w(%d) src(%d,%d) dst(%d,%d), virt(%d) start(%d) end(%d)",
+                buf_w, *new_src_x, *new_src_w, *new_dst_x, *new_dst_w, virtual_screen, start, end);
+
+    return TDM_ERROR_NONE;
+}
+
+static drmModeModeInfoPtr
+_tdm_sprd_display_get_mode(tdm_sprd_output_data *output_data)
+{
+    int i;
+
+    if (!output_data->current_mode)
+    {
+        TDM_ERR("no output_data->current_mode");
+        return NULL;
+    }
+
+    for (i = 0; i < output_data->count_modes; i++)
+    {
+        drmModeModeInfoPtr drm_mode = &output_data->drm_modes[i];
+        if ((drm_mode->hdisplay == output_data->current_mode->width) &&
+            (drm_mode->vdisplay == output_data->current_mode->height) &&
+            (drm_mode->vrefresh == output_data->current_mode->refresh) &&
+            (drm_mode->flags == output_data->current_mode->flags) &&
+            (drm_mode->type == output_data->current_mode->type) &&
+            !(strncmp(drm_mode->name, output_data->current_mode->name, TDM_NAME_LEN)))
+            return drm_mode;
+    }
+
+    return NULL;
+}
+
+static tdm_sprd_display_buffer*
+_tdm_sprd_display_find_buffer(tdm_sprd_data *sprd_data, tbm_surface_h buffer)
+{
+    tdm_sprd_display_buffer *display_buffer;
+
+    LIST_FOR_EACH_ENTRY(display_buffer, &sprd_data->buffer_list, link)
+    {
+        if (display_buffer->buffer == buffer)
+            return display_buffer;
+    }
+
+    return NULL;
+}
+
+static void
+_tdm_sprd_display_to_tdm_mode(drmModeModeInfoPtr drm_mode, tdm_output_mode *tdm_mode)
+{
+    tdm_mode->width = drm_mode->hdisplay;
+    tdm_mode->height = drm_mode->vdisplay;
+    tdm_mode->refresh = drm_mode->vrefresh;
+    tdm_mode->flags = drm_mode->flags;
+    tdm_mode->type = drm_mode->type;
+    snprintf(tdm_mode->name, TDM_NAME_LEN, "%s", drm_mode->name);
+}
+
+static tdm_error
+_tdm_sprd_display_get_cur_msc (int fd, int pipe, uint *msc)
+{
+    drmVBlank vbl;
+
+    vbl.request.type = DRM_VBLANK_RELATIVE;
+    if (pipe > 0)
+        vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+    vbl.request.sequence = 0;
+    if (drmWaitVBlank(fd, &vbl))
+    {
+        TDM_ERR("get vblank counter failed: %m");
+        *msc = 0;
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    *msc = vbl.reply.sequence;
+
+    return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_sprd_display_wait_vblank(int fd, int pipe, uint *target_msc, void *data)
+{
+    drmVBlank vbl;
+
+    vbl.request.type =  DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+    if (pipe > 0)
+        vbl.request.type |= DRM_VBLANK_SECONDARY;
+
+    vbl.request.sequence = *target_msc;
+    vbl.request.signal = (unsigned long)(uintptr_t)data;
+
+    if (drmWaitVBlank(fd, &vbl))
+    {
+        *target_msc = 0;
+        TDM_ERR("wait vblank failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    *target_msc = vbl.reply.sequence;
+
+    return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_sprd_display_commit_primary_layer(tdm_sprd_layer_data *layer_data)
+{
+    tdm_sprd_data *sprd_data = layer_data->sprd_data;
+    tdm_sprd_output_data *output_data = layer_data->output_data;
+
+    if (output_data->mode_changed && layer_data->display_buffer_changed)
+    {
+        drmModeModeInfoPtr mode;
+
+        if (!layer_data->display_buffer)
+        {
+            TDM_ERR("primary layer should have a buffer for modestting");
+            return TDM_ERROR_BAD_REQUEST;
+        }
+
+        output_data->mode_changed = 0;
+        layer_data->display_buffer_changed = 0;
+        layer_data->info_changed = 0;
+
+        mode = _tdm_sprd_display_get_mode(output_data);
+        if (!mode)
+        {
+            TDM_ERR("couldn't find proper mode");
+            return TDM_ERROR_BAD_REQUEST;
+        }
+
+        if (drmModeSetCrtc(sprd_data->drm_fd, output_data->crtc_id,
+                           layer_data->display_buffer->fb_id, 0, 0,
+                           &output_data->connector_id, 1, mode))
+        {
+            TDM_ERR("set crtc failed: %m");
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+
+        return TDM_ERROR_NONE;
+    }
+    else if (layer_data->display_buffer_changed)
+    {
+        layer_data->display_buffer_changed = 0;
+
+        if (!layer_data->display_buffer)
+        {
+            if (drmModeSetCrtc(sprd_data->drm_fd, output_data->crtc_id,
+                               0, 0, 0, NULL, 0, NULL))
+            {
+                TDM_ERR("unset crtc failed: %m");
+                return TDM_ERROR_OPERATION_FAILED;
+            }
+        }
+        else
+        {
+            if (drmModePageFlip(sprd_data->drm_fd, output_data->crtc_id,
+                                layer_data->display_buffer->fb_id, DRM_MODE_PAGE_FLIP_EVENT, layer_data->display_buffer))
+            {
+                TDM_ERR("pageflip failed: %m");
+                return TDM_ERROR_OPERATION_FAILED;
+            }
+        }
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_sprd_display_commit_layer(tdm_sprd_layer_data *layer_data)
+{
+    tdm_sprd_data *sprd_data = layer_data->sprd_data;
+    tdm_sprd_output_data *output_data = layer_data->output_data;
+    unsigned int new_src_x, new_src_w;
+    unsigned int new_dst_x, new_dst_w;
+    uint32_t fx, fy, fw, fh;
+    int crtc_w;
+
+    if (!layer_data->display_buffer_changed && !layer_data->info_changed)
+        return TDM_ERROR_NONE;
+
+    if (output_data->current_mode)
+        crtc_w = output_data->current_mode->width;
+    else
+    {
+        drmModeCrtcPtr crtc = drmModeGetCrtc(sprd_data->drm_fd, output_data->crtc_id);
+        if (!crtc)
+        {
+            TDM_ERR("getting crtc failed");
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+        crtc_w = crtc->width;
+        if (crtc_w == 0)
+        {
+            TDM_ERR("getting crtc width failed");
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    layer_data->display_buffer_changed = 0;
+    layer_data->info_changed = 0;
+
+    if (!layer_data->display_buffer)
+    {
+        if (drmModeSetPlane(sprd_data->drm_fd, layer_data->plane_id,
+                            output_data->crtc_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
+            TDM_ERR("unset plane(%d) filed: %m", layer_data->plane_id);
+
+        return TDM_ERROR_NONE;
+    }
+
+    /* check hw restriction*/
+    if (check_hw_restriction(crtc_w, layer_data->display_buffer->width,
+                             layer_data->info.src_config.pos.x,
+                             layer_data->info.src_config.pos.w,
+                             layer_data->info.dst_pos.x,
+                             layer_data->info.dst_pos.w,
+                             &new_src_x, &new_src_w, &new_dst_x, &new_dst_w) != TDM_ERROR_NONE)
+    {
+        TDM_WRN("not going to set plane(%d)", layer_data->plane_id);
+        return TDM_ERROR_NONE;
+    }
+
+    if (layer_data->info.src_config.pos.x != new_src_x)
+        TDM_DBG("src_x changed: %d => %d", layer_data->info.src_config.pos.x, new_src_x);
+    if (layer_data->info.src_config.pos.w != new_src_w)
+        TDM_DBG("src_w changed: %d => %d", layer_data->info.src_config.pos.w, new_src_w);
+    if (layer_data->info.dst_pos.x != new_dst_x)
+        TDM_DBG("dst_x changed: %d => %d", layer_data->info.dst_pos.x, new_dst_x);
+    if (layer_data->info.dst_pos.w != new_dst_w)
+        TDM_DBG("dst_w changed: %d => %d", layer_data->info.dst_pos.w, new_dst_w);
+
+    /* Source values are 16.16 fixed point */
+    fx = ((unsigned int)new_src_x) << 16;
+    fy = ((unsigned int)layer_data->info.src_config.pos.y) << 16;
+    fw = ((unsigned int)new_src_w) << 16;
+    fh = ((unsigned int)layer_data->info.src_config.pos.h) << 16;
+
+    if (drmModeSetPlane(sprd_data->drm_fd, layer_data->plane_id,
+                        output_data->crtc_id, layer_data->display_buffer->fb_id, 0,
+                        new_dst_x, layer_data->info.dst_pos.y,
+                        new_dst_w, layer_data->info.dst_pos.h,
+                        fx, fy, fw, fh) < 0)
+    {
+        TDM_ERR("set plane(%d) failed: %m", layer_data->plane_id);
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+static void
+_tdm_sprd_display_cb_vblank(int fd, unsigned int sequence,
+                              unsigned int tv_sec, unsigned int tv_usec,
+                              void *user_data)
+{
+    tdm_sprd_vblank_data *vblank_data = user_data;
+    tdm_sprd_output_data *output_data;
+
+    if (!vblank_data)
+    {
+        TDM_ERR("no vblank data");
+        return;
+    }
+
+    output_data = vblank_data->output_data;
+
+    switch(vblank_data->type)
+    {
+    case VBLANK_TYPE_WAIT:
+        if (output_data->vblank_func)
+            output_data->vblank_func(output_data, sequence, tv_sec, tv_usec, vblank_data->user_data);
+        break;
+    case VBLANK_TYPE_COMMIT:
+        if (output_data->commit_func)
+            output_data->commit_func(output_data, sequence, tv_sec, tv_usec, vblank_data->user_data);
+        break;
+    default:
+        return;
+    }
+}
+
+static void
+_tdm_sprd_display_cb_pp(int fd, unsigned int prop_id, unsigned int *buf_idx,
+                          unsigned int tv_sec, unsigned int tv_usec,
+                          void *user_data)
+{
+    tdm_sprd_pp_handler(prop_id, buf_idx, tv_sec, tv_usec, user_data);
+}
+
+static int
+_tdm_sprd_display_events_handle(int fd, Drm_Event_Context *evctx)
+{
+#define MAX_BUF_SIZE    1024
+
+    char buffer[MAX_BUF_SIZE];
+    unsigned int len, i;
+    struct drm_event *e;
+
+    /* The DRM read semantics guarantees that we always get only
+     * complete events. */
+    len = read(fd, buffer, sizeof buffer);
+    if (len == 0)
+    {
+        TDM_WRN("warning: the size of the drm_event is 0.");
+        return 0;
+    }
+    if (len < sizeof *e)
+    {
+        TDM_WRN("warning: the size of the drm_event is less than drm_event structure.");
+        return -1;
+    }
+#if 0
+    if (len > MAX_BUF_SIZE - sizeof(struct drm_sprd_ipp_event))
+    {
+        TDM_WRN("warning: the size of the drm_event can be over the maximum size.");
+        return -1;
+    }
+#endif
+    i = 0;
+    while (i < len)
+    {
+        e = (struct drm_event *) &buffer[i];
+        switch (e->type)
+        {
+            case DRM_EVENT_VBLANK:
+                {
+                    struct drm_event_vblank *vblank;
+
+                    if (evctx->vblank_handler == NULL)
+                        break;
+
+                    vblank = (struct drm_event_vblank *)e;
+                    TDM_DBG("******* VBLANK *******");
+                    evctx->vblank_handler (fd, vblank->sequence,
+                                           vblank->tv_sec, vblank->tv_usec,
+                                           (void *)((unsigned long)vblank->user_data));
+                    TDM_DBG("******* VBLANK *******...");
+                }
+                break;
+#if 0
+            case DRM_EXYNOS_IPP_EVENT:
+                {
+                    struct drm_sprd_ipp_event *ipp;
+
+                    if (evctx->pp_handler == NULL)
+                        break;
+
+                    ipp = (struct drm_sprd_ipp_event *)e;
+                    TDM_DBG("******* PP *******");
+                    evctx->pp_handler (fd, ipp->prop_id, ipp->buf_id,
+                                       ipp->tv_sec, ipp->tv_usec,
+                                       (void *)((unsigned long)ipp->user_data));
+                    TDM_DBG("******* PP *******...");
+                }
+#endif
+                break;
+            case DRM_EVENT_FLIP_COMPLETE:
+                /* do nothing for flip complete */
+                break;
+            default:
+                break;
+        }
+        i += e->length;
+    }
+
+    return 0;
+}
+
+static tdm_error
+_tdm_sprd_display_create_layer_list_type(tdm_sprd_data *sprd_data)
+{
+    tdm_error ret;
+    int i;
+
+    for (i = 0; i < sprd_data->plane_res->count_planes; i++)
+    {
+        tdm_sprd_output_data *output_data;
+        tdm_sprd_layer_data *layer_data;
+        drmModePlanePtr plane;
+        unsigned int type = 0;
+
+        plane = drmModeGetPlane(sprd_data->drm_fd, sprd_data->plane_res->planes[i]);
+        if (!plane)
+        {
+            TDM_ERR("no plane");
+            continue;
+        }
+
+        ret = tdm_sprd_display_get_property(sprd_data, sprd_data->plane_res->planes[i],
+                                              DRM_MODE_OBJECT_PLANE, "type", &type, NULL);
+        if (ret != TDM_ERROR_NONE)
+        {
+            TDM_ERR("plane(%d) doesn't have 'type' info", sprd_data->plane_res->planes[i]);
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        layer_data = calloc(1, sizeof(tdm_sprd_layer_data));
+        if (!layer_data)
+        {
+            TDM_ERR("alloc failed");
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+        {
+            if (plane->possible_crtcs & (1 << output_data->pipe))
+                break;
+        }
+
+        if (!output_data)
+        {
+            TDM_ERR("plane(%d) couldn't found proper output", plane->plane_id);
+            drmModeFreePlane(plane);
+            free(layer_data);
+            continue;
+        }
+
+        layer_data->sprd_data = sprd_data;
+        layer_data->output_data = output_data;
+        layer_data->plane_id = sprd_data->plane_res->planes[i];
+
+        if (type == DRM_PLANE_TYPE_CURSOR)
+        {
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_CURSOR | TDM_LAYER_CAPABILITY_GRAPHIC;
+            layer_data->zpos = 2;
+        }
+        else if (type == DRM_PLANE_TYPE_OVERLAY)
+        {
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC;
+            layer_data->zpos = 1;
+        }
+        else if (type == DRM_PLANE_TYPE_PRIMARY)
+        {
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC;
+            layer_data->zpos = 0;
+            output_data->primary_layer = layer_data;
+        }
+        else
+        {
+            drmModeFreePlane(plane);
+            free(layer_data);
+            continue;
+        }
+
+        TDM_DBG("layer_data(%p) plane_id(%d) crtc_id(%d) zpos(%d) capabilities(%x)",
+                layer_data, layer_data->plane_id, layer_data->output_data->crtc_id,
+                layer_data->zpos, layer_data->capabilities);
+
+        LIST_ADDTAIL(&layer_data->link, &output_data->layer_list);
+
+        drmModeFreePlane(plane);
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_sprd_display_create_layer_list_immutable_zpos(tdm_sprd_data *sprd_data)
+{
+    tdm_error ret;
+    int i;
+
+    for (i = 0; i < sprd_data->plane_res->count_planes; i++)
+    {
+        tdm_sprd_output_data *output_data;
+        tdm_sprd_layer_data *layer_data;
+        drmModePlanePtr plane;
+        unsigned int type = 0, zpos = 0;
+
+        plane = drmModeGetPlane(sprd_data->drm_fd, sprd_data->plane_res->planes[i]);
+        if (!plane)
+        {
+            TDM_ERR("no plane");
+            continue;
+        }
+
+        ret = tdm_sprd_display_get_property(sprd_data, sprd_data->plane_res->planes[i],
+                                              DRM_MODE_OBJECT_PLANE, "type", &type, NULL);
+        if (ret != TDM_ERROR_NONE)
+        {
+            TDM_ERR("plane(%d) doesn't have 'type' info", sprd_data->plane_res->planes[i]);
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        ret = tdm_sprd_display_get_property(sprd_data, sprd_data->plane_res->planes[i],
+                                              DRM_MODE_OBJECT_PLANE, "zpos", &zpos, NULL);
+        if (ret != TDM_ERROR_NONE)
+        {
+            TDM_ERR("plane(%d) doesn't have 'zpos' info", sprd_data->plane_res->planes[i]);
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        layer_data = calloc(1, sizeof(tdm_sprd_layer_data));
+        if (!layer_data)
+        {
+            TDM_ERR("alloc failed");
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+        {
+            if (plane->possible_crtcs & (1 << output_data->pipe))
+                break;
+        }
+
+        if (!output_data)
+        {
+            TDM_ERR("plane(%d) couldn't found proper output", plane->plane_id);
+            drmModeFreePlane(plane);
+            free(layer_data);
+            continue;
+        }
+
+        layer_data->sprd_data = sprd_data;
+        layer_data->output_data = output_data;
+        layer_data->plane_id = sprd_data->plane_res->planes[i];
+        layer_data->zpos = zpos;
+
+        if (type == DRM_PLANE_TYPE_CURSOR)
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_CURSOR | TDM_LAYER_CAPABILITY_GRAPHIC;
+        else if (type == DRM_PLANE_TYPE_OVERLAY)
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC;
+        else if (type == DRM_PLANE_TYPE_PRIMARY)
+        {
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC;
+            output_data->primary_layer = layer_data;
+        }
+        else
+        {
+            drmModeFreePlane(plane);
+            free(layer_data);
+            continue;
+        }
+
+        TDM_DBG("layer_data(%p) plane_id(%d) crtc_id(%d) zpos(%d) capabilities(%x)",
+                layer_data, layer_data->plane_id, layer_data->output_data->crtc_id,
+                layer_data->zpos, layer_data->capabilities);
+
+        LIST_ADDTAIL(&layer_data->link, &output_data->layer_list);
+
+        drmModeFreePlane(plane);
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_sprd_display_create_layer_list_not_fixed(tdm_sprd_data *sprd_data)
+{
+    int i, find_pipe = -1;
+
+    if (sprd_data->mode_res->count_connectors * LAYER_COUNT_PER_OUTPUT > sprd_data->plane_res->count_planes)
+    {
+        TDM_ERR("not enough layers");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    for (i = 0; i < sprd_data->plane_res->count_planes; i++)
+    {
+        tdm_sprd_output_data *output_data;
+        tdm_sprd_layer_data *layer_data;
+        drmModePlanePtr plane;
+
+        plane = drmModeGetPlane(sprd_data->drm_fd, sprd_data->plane_res->planes[i]);
+        if (!plane)
+        {
+            TDM_ERR("no plane");
+            continue;
+        }
+
+        layer_data = calloc(1, sizeof(tdm_sprd_layer_data));
+        if (!layer_data)
+        {
+            TDM_ERR("alloc failed");
+            drmModeFreePlane(plane);
+            continue;
+        }
+
+        /* TODO
+         * Currently, kernel doesn give us the correct device infomation.
+         * Primary connector type is invalid. plane's count is not correct.
+         * So we need to fix all of them with kernel.
+         * Temporarily we dedicate only 2 plane to each output.
+         * First plane is primary layer. Second plane's zpos is 1.
+         */
+        if (i % LAYER_COUNT_PER_OUTPUT == 0)
+            find_pipe++;
+
+        LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+        {
+            if (output_data->pipe == find_pipe)
+                break;
+        }
+
+        if (i == sprd_data->mode_res->count_connectors * LAYER_COUNT_PER_OUTPUT)
+        {
+            TDM_DBG("no more plane(%d) need for outputs", plane->plane_id);
+            drmModeFreePlane(plane);
+            free(layer_data);
+            return TDM_ERROR_NONE;
+        }
+
+        layer_data->sprd_data = sprd_data;
+        layer_data->output_data = output_data;
+        layer_data->plane_id = sprd_data->plane_res->planes[i];
+
+        layer_data->zpos = i % 2;
+        if (layer_data->zpos == 0)
+        {
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC;
+            output_data->primary_layer = layer_data;
+        }
+        else
+        {
+            tdm_error ret;
+
+            layer_data->capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC;
+
+            ret = tdm_sprd_display_set_property(sprd_data, layer_data->plane_id,
+                                                  DRM_MODE_OBJECT_PLANE, "zpos", layer_data->zpos);
+            if (ret != TDM_ERROR_NONE)
+            {
+                drmModeFreePlane(plane);
+                free(layer_data);
+                return TDM_ERROR_OPERATION_FAILED;
+            }
+        }
+
+        TDM_DBG("layer_data(%p) plane_id(%d) crtc_id(%d) zpos(%d) capabilities(%x)",
+                layer_data, layer_data->plane_id, layer_data->output_data->crtc_id,
+                layer_data->zpos, layer_data->capabilities);
+
+        LIST_ADDTAIL(&layer_data->link, &output_data->layer_list);
+
+        drmModeFreePlane(plane);
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_sprd_display_create_layer_list(tdm_sprd_data *sprd_data)
+{
+    tdm_sprd_output_data *output_data;
+    tdm_error ret;
+
+    if (!sprd_data->has_zpos_info)
+        ret = _tdm_sprd_display_create_layer_list_type(sprd_data);
+    else if (sprd_data->is_immutable_zpos)
+        ret = _tdm_sprd_display_create_layer_list_immutable_zpos(sprd_data);
+    else
+        ret = _tdm_sprd_display_create_layer_list_not_fixed(sprd_data);
+
+    if (ret != TDM_ERROR_NONE)
+        return ret;
+
+    LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+    {
+        if (!output_data->primary_layer)
+        {
+            TDM_ERR("output(%d) no primary layer", output_data->pipe);
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+void
+tdm_sprd_display_destroy_output_list(tdm_sprd_data *sprd_data)
+{
+    tdm_sprd_output_data *o = NULL, *oo = NULL;
+
+    if (LIST_IS_EMPTY(&sprd_data->output_list))
+        return;
+
+    LIST_FOR_EACH_ENTRY_SAFE(o, oo, &sprd_data->output_list, link)
+    {
+        LIST_DEL(&o->link);
+        if (!LIST_IS_EMPTY(&o->layer_list))
+        {
+            tdm_sprd_layer_data *l = NULL, *ll = NULL;
+            LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link)
+            {
+                LIST_DEL(&l->link);
+                free(l);
+            }
+        }
+        free(o->drm_modes);
+        free(o->output_modes);
+        free(o);
+    }
+}
+
+tdm_error
+tdm_sprd_display_create_output_list(tdm_sprd_data *sprd_data)
+{
+    tdm_sprd_output_data *output_data;
+    int i;
+    tdm_error ret;
+    int allocated = 0;
+
+    RETURN_VAL_IF_FAIL(LIST_IS_EMPTY(&sprd_data->output_list), TDM_ERROR_OPERATION_FAILED);
+
+    for (i = 0; i < sprd_data->mode_res->count_connectors; i++)
+    {
+        drmModeConnectorPtr connector;
+        drmModeEncoderPtr encoder;
+        int crtc_id = 0, c, j;
+
+        connector = drmModeGetConnector(sprd_data->drm_fd, sprd_data->mode_res->connectors[i]);
+        if (!connector)
+        {
+            TDM_ERR("no connector");
+            ret = TDM_ERROR_OPERATION_FAILED;
+            goto failed_create;
+        }
+
+        if (connector->count_encoders != 1)
+        {
+            TDM_ERR("too many encoders: %d", connector->count_encoders);
+            drmModeFreeConnector(connector);
+            ret = TDM_ERROR_OPERATION_FAILED;
+            goto failed_create;
+        }
+
+        encoder = drmModeGetEncoder(sprd_data->drm_fd, connector->encoders[0]);
+        if (!encoder)
+        {
+            TDM_ERR("no encoder");
+            drmModeFreeConnector(connector);
+            ret = TDM_ERROR_OPERATION_FAILED;
+            goto failed_create;
+        }
+
+        for (c = 0; c < sprd_data->mode_res->count_crtcs; c++)
+        {
+            if (allocated & (1 << c))
+                continue;
+
+            if ((encoder->possible_crtcs & (1 << c)) == 0)
+                continue;
+
+            crtc_id = sprd_data->mode_res->crtcs[c];
+            allocated |= (1 << c);
+            break;
+        }
+
+        if (crtc_id == 0)
+        {
+            TDM_ERR("no possible crtc");
+            drmModeFreeConnector(connector);
+            ret = TDM_ERROR_OPERATION_FAILED;
+            goto failed_create;
+        }
+
+        output_data = calloc(1, sizeof(tdm_sprd_output_data));
+        if (!output_data)
+        {
+            TDM_ERR("alloc failed");
+            drmModeFreeConnector(connector);
+            drmModeFreeEncoder(encoder);
+            ret = TDM_ERROR_OUT_OF_MEMORY;
+            goto failed_create;
+        }
+
+        LIST_INITHEAD(&output_data->layer_list);
+
+        output_data->sprd_data = sprd_data;
+        output_data->connector_id = sprd_data->mode_res->connectors[i];
+        output_data->encoder_id = encoder->encoder_id;
+        output_data->crtc_id = crtc_id;
+        output_data->pipe = c;
+        output_data->connector_type = connector->connector_type;
+        output_data->connector_type_id = connector->connector_type_id;
+
+        if (connector->connection == DRM_MODE_CONNECTED)
+            output_data->status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
+        else
+            output_data->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+        for (j = 0; j < connector->count_props; j++)
+        {
+            drmModePropertyPtr prop = drmModeGetProperty(sprd_data->drm_fd, connector->props[i]);
+            if (!prop)
+                continue;
+            if (!strcmp(prop->name, "DPMS"))
+            {
+                output_data->dpms_prop_id = connector->props[i];
+                drmModeFreeProperty(prop);
+                break;
+            }
+            drmModeFreeProperty(prop);
+        }
+
+        output_data->count_modes = connector->count_modes;
+        output_data->drm_modes = calloc(connector->count_modes, sizeof(drmModeModeInfo));
+        if (!output_data->drm_modes)
+        {
+            TDM_ERR("alloc failed");
+            free(output_data);
+            drmModeFreeConnector(connector);
+            drmModeFreeEncoder(encoder);
+            ret = TDM_ERROR_OUT_OF_MEMORY;
+            goto failed_create;
+        }
+        output_data->output_modes = calloc(connector->count_modes, sizeof(tdm_output_mode));
+        if (!output_data->output_modes)
+        {
+            TDM_ERR("alloc failed");
+            free(output_data);
+            free(output_data->drm_modes);
+            drmModeFreeConnector(connector);
+            drmModeFreeEncoder(encoder);
+            ret = TDM_ERROR_OUT_OF_MEMORY;
+            goto failed_create;
+        }
+        for (j = 0; j < connector->count_modes; j++)
+        {
+            output_data->drm_modes[j] = connector->modes[j];
+            _tdm_sprd_display_to_tdm_mode(&output_data->drm_modes[j], &output_data->output_modes[j]);
+        }
+
+        LIST_ADDTAIL(&output_data->link, &sprd_data->output_list);
+
+        TDM_DBG("output_data(%p) connector_id(%d:%d:%d-%d) encoder_id(%d) crtc_id(%d) pipe(%d) dpms_id(%d)",
+                output_data, output_data->connector_id, output_data->status, output_data->connector_type,
+                output_data->connector_type_id, output_data->encoder_id, output_data->crtc_id,
+                output_data->pipe, output_data->dpms_prop_id);
+
+        drmModeFreeEncoder(encoder);
+        drmModeFreeConnector(connector);
+    }
+
+    TDM_DBG("output count: %d", sprd_data->mode_res->count_connectors);
+
+    return TDM_ERROR_NONE;
+failed_create:
+    tdm_sprd_display_destroy_output_list(sprd_data);
+    return ret;
+}
+
+tdm_error
+tdm_sprd_display_set_property(tdm_sprd_data *sprd_data,
+                                unsigned int obj_id, unsigned int obj_type,
+                                const char *name, unsigned int value)
+{
+    drmModeObjectPropertiesPtr props = NULL;
+    unsigned int i;
+
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, obj_id, obj_type);
+    if (!props)
+    {
+        TDM_ERR("drmModeObjectGetProperties failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+    for (i = 0; i < props->count_props; i++)
+    {
+        drmModePropertyPtr prop = drmModeGetProperty(sprd_data->drm_fd, props->props[i]);
+        int ret;
+        if (!prop)
+        {
+            TDM_ERR("drmModeGetProperty failed: %m");
+            drmModeFreeObjectProperties(props);
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+        if (!strcmp(prop->name, name))
+        {
+            ret = drmModeObjectSetProperty(sprd_data->drm_fd, obj_id, obj_type, prop->prop_id, value);
+            if (ret < 0)
+            {
+                TDM_ERR("drmModeObjectSetProperty failed: %m");
+                drmModeFreeProperty(prop);
+                drmModeFreeObjectProperties(props);
+                return TDM_ERROR_OPERATION_FAILED;
+            }
+            drmModeFreeProperty(prop);
+            drmModeFreeObjectProperties(props);
+            return TDM_ERROR_NONE;
+        }
+        drmModeFreeProperty(prop);
+    }
+
+    TDM_ERR("not found '%s' property", name);
+
+    drmModeFreeObjectProperties(props);
+    /* TODO
+    * kernel info error
+    * it must be changed to 'return TDM_ERROR_OPERATION_FAILED' after kernel fix.
+    */
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_sprd_display_get_property(tdm_sprd_data *sprd_data,
+                                unsigned int obj_id, unsigned int obj_type,
+                                const char *name, unsigned int *value, int *is_immutable)
+{
+    drmModeObjectPropertiesPtr props = NULL;
+    int i;
+
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, obj_id, obj_type);
+    if (!props)
+        return TDM_ERROR_OPERATION_FAILED;
+
+    for (i = 0; i < props->count_props; i++)
+    {
+        drmModePropertyPtr prop = drmModeGetProperty(sprd_data->drm_fd, props->props[i]);
+
+        if (!prop)
+            continue;
+
+        if (!strcmp(prop->name, name))
+        {
+            if (is_immutable)
+                *is_immutable = prop->flags & DRM_MODE_PROP_IMMUTABLE;
+            if (value)
+                *value = (unsigned int)props->prop_values[i];
+            drmModeFreeProperty(prop);
+            drmModeFreeObjectProperties(props);
+            return TDM_ERROR_NONE;
+        }
+
+        drmModeFreeProperty(prop);
+    }
+    drmModeFreeObjectProperties(props);
+    TDM_DBG("coundn't find '%s' property", name);
+    return TDM_ERROR_OPERATION_FAILED;
+}
+
+tdm_error
+sprd_display_get_capabilitiy(tdm_backend_data *bdata, tdm_caps_display *caps)
+{
+    RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER);
+
+    caps->max_layer_count = -1; /* not defined */
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_display_get_pp_capability(tdm_backend_data *bdata, tdm_caps_pp *caps)
+{
+    return tdm_sprd_pp_get_capability(bdata, caps);
+}
+
+tdm_output**
+sprd_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error)
+{
+    tdm_sprd_data *sprd_data = bdata;
+    tdm_sprd_output_data *output_data;
+    tdm_output **outputs;
+    tdm_error ret;
+    int i;
+
+    RETURN_VAL_IF_FAIL(sprd_data, NULL);
+    RETURN_VAL_IF_FAIL(count, NULL);
+
+    *count = 0;
+    LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+        (*count)++;
+
+    if (*count == 0)
+    {
+        ret = TDM_ERROR_NONE;
+        goto failed_get;
+    }
+
+    /* will be freed in frontend */
+    outputs = calloc(*count, sizeof(tdm_sprd_output_data*));
+    if (!outputs)
+    {
+        TDM_ERR("failed: alloc memory");
+        *count = 0;
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        goto failed_get;
+    }
+
+    i = 0;
+    LIST_FOR_EACH_ENTRY(output_data, &sprd_data->output_list, link)
+        outputs[i++] = output_data;
+
+    if (error)
+        *error = TDM_ERROR_NONE;
+
+    return outputs;
+failed_get:
+    if (error)
+        *error = ret;
+    return NULL;
+}
+
+tdm_error
+sprd_display_get_fd(tdm_backend_data *bdata, int *fd)
+{
+    tdm_sprd_data *sprd_data = bdata;
+
+    RETURN_VAL_IF_FAIL(sprd_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(fd, TDM_ERROR_INVALID_PARAMETER);
+
+    *fd = sprd_data->drm_fd;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_display_handle_events(tdm_backend_data *bdata)
+{
+    tdm_sprd_data *sprd_data = bdata;
+    Drm_Event_Context ctx;
+
+    RETURN_VAL_IF_FAIL(sprd_data, TDM_ERROR_INVALID_PARAMETER);
+
+    memset(&ctx, 0, sizeof(Drm_Event_Context));
+
+    ctx.vblank_handler = _tdm_sprd_display_cb_vblank;
+    ctx.pp_handler = _tdm_sprd_display_cb_pp;
+
+    _tdm_sprd_display_events_handle(sprd_data->drm_fd, &ctx);
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_pp*
+sprd_display_create_pp(tdm_backend_data *bdata, tdm_error *error)
+{
+    tdm_sprd_data *sprd_data = bdata;
+
+    RETURN_VAL_IF_FAIL(sprd_data, NULL);
+
+    return tdm_sprd_pp_create(sprd_data, error);
+}
+
+tdm_error
+sprd_output_get_capability(tdm_output *output, tdm_caps_output *caps)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    drmModeConnectorPtr connector = NULL;
+    drmModeCrtcPtr crtc = NULL;
+    drmModeObjectPropertiesPtr props = NULL;
+    int i;
+    tdm_error ret;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER);
+
+    memset(caps, 0, sizeof(tdm_caps_output));
+
+    sprd_data = output_data->sprd_data;
+
+    caps->status = output_data->status;
+    caps->type = output_data->connector_type;
+    caps->type_id = output_data->connector_type_id;
+
+    connector = drmModeGetConnector(sprd_data->drm_fd, output_data->connector_id);
+    RETURN_VAL_IF_FAIL(connector, TDM_ERROR_OPERATION_FAILED);
+
+    caps->mode_count = connector->count_modes;
+    caps->modes = calloc(1, sizeof(tdm_output_mode*) * caps->mode_count);
+    if (!caps->modes)
+    {
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        TDM_ERR("alloc failed\n");
+        goto failed_get;
+    }
+    for (i = 0; i < caps->mode_count; i++)
+        caps->modes[i] = output_data->output_modes[i];
+
+    caps->mmWidth = connector->mmWidth;
+    caps->mmHeight = connector->mmHeight;
+    caps->subpixel = connector->subpixel;
+
+    caps->min_w = sprd_data->mode_res->min_width;
+    caps->min_h = sprd_data->mode_res->min_height;
+    caps->max_w = sprd_data->mode_res->max_width;
+    caps->max_h = sprd_data->mode_res->max_height;
+    caps->preferred_align = -1;
+
+    crtc = drmModeGetCrtc(sprd_data->drm_fd, output_data->crtc_id);
+    if (!crtc)
+    {
+        ret = TDM_ERROR_OPERATION_FAILED;
+        TDM_ERR("get crtc failed: %m\n");
+        goto failed_get;
+    }
+
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, output_data->crtc_id, DRM_MODE_OBJECT_CRTC);
+    if (!props)
+    {
+        ret = TDM_ERROR_OPERATION_FAILED;
+        TDM_ERR("get crtc properties failed: %m\n");
+        goto failed_get;
+    }
+
+    caps->prop_count = props->count_props;
+    caps->props = calloc(1, sizeof(tdm_prop) * caps->prop_count);
+    if (!caps->props)
+    {
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        TDM_ERR("alloc failed\n");
+        goto failed_get;
+    }
+
+    for (i = 0; i < caps->prop_count; i++)
+    {
+        drmModePropertyPtr prop = drmModeGetProperty(sprd_data->drm_fd, props->props[i]);
+        if (!prop)
+            continue;
+        snprintf(caps->props[i].name, TDM_NAME_LEN, "%s", prop->name);
+        caps->props[i].id = props->props[i];
+        drmModeFreeProperty(prop);
+    }
+
+    drmModeFreeObjectProperties(props);
+    drmModeFreeCrtc(crtc);
+    drmModeFreeConnector(connector);
+
+    return TDM_ERROR_NONE;
+failed_get:
+    drmModeFreeCrtc(crtc);
+    drmModeFreeObjectProperties(props);
+    drmModeFreeConnector(connector);
+    free(caps->modes);
+    free(caps->props);
+    memset(caps, 0, sizeof(tdm_caps_output));
+    return ret;
+}
+
+tdm_layer**
+sprd_output_get_layers(tdm_output *output,  int *count, tdm_error *error)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_layer_data *layer_data;
+    tdm_layer **layers;
+    tdm_error ret;
+    int i;
+
+    RETURN_VAL_IF_FAIL(output_data, NULL);
+    RETURN_VAL_IF_FAIL(count, NULL);
+
+    *count = 0;
+    LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link)
+        (*count)++;
+
+    if (*count == 0)
+    {
+        ret = TDM_ERROR_NONE;
+        goto failed_get;
+    }
+
+    /* will be freed in frontend */
+    layers = calloc(*count, sizeof(tdm_sprd_layer_data*));
+    if (!layers)
+    {
+        TDM_ERR("failed: alloc memory");
+        *count = 0;
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        goto failed_get;
+    }
+
+    i = 0;
+    LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link)
+        layers[i++] = layer_data;
+
+    if (error)
+        *error = TDM_ERROR_NONE;
+
+    return layers;
+failed_get:
+    if (error)
+        *error = ret;
+    return NULL;
+}
+
+tdm_error
+sprd_output_set_property(tdm_output *output, unsigned int id, tdm_value value)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    int ret;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(output_data->crtc_id > 0, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = output_data->sprd_data;
+    ret = drmModeObjectSetProperty(sprd_data->drm_fd,
+                                   output_data->crtc_id, DRM_MODE_OBJECT_CRTC,
+                                   id, value.u32);
+    if (ret < 0)
+    {
+        TDM_ERR("set property failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_get_property(tdm_output *output, unsigned int id, tdm_value *value)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    drmModeObjectPropertiesPtr props;
+    int i;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(output_data->crtc_id > 0, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(value, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = output_data->sprd_data;
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, output_data->crtc_id, DRM_MODE_OBJECT_CRTC);
+    if (props == NULL)
+    {
+        TDM_ERR("get property failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    for (i = 0; i < props->count_props; i++)
+        if (props->props[i] == id)
+        {
+            (*value).u32 = (uint)props->prop_values[i];
+            break;
+        }
+
+    drmModeFreeObjectProperties(props);
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    tdm_sprd_vblank_data *vblank_data;
+    uint target_msc;
+    tdm_error ret;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+    vblank_data = calloc(1, sizeof(tdm_sprd_vblank_data));
+    if (!vblank_data)
+    {
+        TDM_ERR("alloc failed");
+        return TDM_ERROR_OUT_OF_MEMORY;
+    }
+
+    sprd_data = output_data->sprd_data;
+
+    ret = _tdm_sprd_display_get_cur_msc(sprd_data->drm_fd, output_data->pipe, &target_msc);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_vblank;
+
+    target_msc++;
+
+    vblank_data->type = VBLANK_TYPE_WAIT;
+    vblank_data->output_data = output_data;
+    vblank_data->user_data = user_data;
+
+    ret = _tdm_sprd_display_wait_vblank(sprd_data->drm_fd, output_data->pipe, &target_msc, vblank_data);
+    if (ret != TDM_ERROR_NONE)
+        goto failed_vblank;
+
+    return TDM_ERROR_NONE;
+failed_vblank:
+    free(vblank_data);
+    return ret;
+}
+
+tdm_error
+sprd_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func)
+{
+    tdm_sprd_output_data *output_data = output;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER);
+
+    output_data->vblank_func = func;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_commit(tdm_output *output, int sync, void *user_data)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    tdm_sprd_layer_data *layer_data;
+    tdm_error ret;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = output_data->sprd_data;
+
+    LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link)
+    {
+        if (layer_data == output_data->primary_layer)
+        {
+            ret = _tdm_sprd_display_commit_primary_layer(layer_data);
+            if (ret != TDM_ERROR_NONE)
+                return ret;
+        }
+        else
+        {
+            ret = _tdm_sprd_display_commit_layer(layer_data);
+            if (ret != TDM_ERROR_NONE)
+                return ret;
+        }
+    }
+
+    if (tdm_helper_drm_fd == -1)
+    {
+        tdm_sprd_vblank_data *vblank_data = calloc(1, sizeof(tdm_sprd_vblank_data));
+        uint target_msc;
+
+        if (!vblank_data)
+        {
+            TDM_ERR("alloc failed");
+            return TDM_ERROR_OUT_OF_MEMORY;
+        }
+
+        ret = _tdm_sprd_display_get_cur_msc(sprd_data->drm_fd, output_data->pipe, &target_msc);
+        if (ret != TDM_ERROR_NONE)
+        {
+            free(vblank_data);
+            return ret;
+        }
+
+        target_msc++;
+
+        vblank_data->type = VBLANK_TYPE_COMMIT;
+        vblank_data->output_data = output_data;
+        vblank_data->user_data = user_data;
+
+        ret = _tdm_sprd_display_wait_vblank(sprd_data->drm_fd, output_data->pipe, &target_msc, vblank_data);
+        if (ret != TDM_ERROR_NONE)
+        {
+            free(vblank_data);
+            return ret;
+        }
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func)
+{
+    tdm_sprd_output_data *output_data = output;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER);
+
+    output_data->commit_func = func;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    int ret;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = output_data->sprd_data;
+    ret = drmModeObjectSetProperty(sprd_data->drm_fd,
+                                   output_data->connector_id, DRM_MODE_OBJECT_CONNECTOR,
+                                   output_data->dpms_prop_id, dpms_value);
+    if (ret < 0)
+    {
+        TDM_ERR("set dpms failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value)
+{
+    tdm_sprd_output_data *output_data = output;
+    tdm_sprd_data *sprd_data;
+    drmModeObjectPropertiesPtr props;
+    int i;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(dpms_value, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = output_data->sprd_data;
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, output_data->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+    if (props == NULL)
+    {
+        TDM_ERR("get property failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    for (i = 0; i < props->count_props; i++)
+        if (props->props[i] == output_data->dpms_prop_id)
+        {
+            *dpms_value = (uint)props->prop_values[i];
+            break;
+        }
+
+    drmModeFreeObjectProperties(props);
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_set_mode(tdm_output *output, tdm_output_mode *mode)
+{
+    tdm_sprd_output_data *output_data = output;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER);
+
+    output_data->current_mode = mode;
+    output_data->mode_changed = 1;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_output_get_mode(tdm_output *output, const tdm_output_mode **mode)
+{
+    tdm_sprd_output_data *output_data = output;
+
+    RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER);
+
+    *mode = output_data->current_mode;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+    tdm_sprd_data *sprd_data;
+    drmModePlanePtr plane = NULL;
+    drmModeObjectPropertiesPtr props = NULL;
+    int i;
+    tdm_error ret;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER);
+
+    memset(caps, 0, sizeof(tdm_caps_layer));
+
+    sprd_data = layer_data->sprd_data;
+    plane = drmModeGetPlane(sprd_data->drm_fd, layer_data->plane_id);
+    if (!plane)
+    {
+        TDM_ERR("get plane failed: %m");
+        ret = TDM_ERROR_OPERATION_FAILED;
+        goto failed_get;
+    }
+
+    caps->capabilities = layer_data->capabilities;
+    caps->zpos = layer_data->zpos;  /* if VIDEO layer, zpos is -1 */
+
+    caps->format_count = plane->count_formats;
+    caps->formats = calloc(1, sizeof(tbm_format) * caps->format_count);
+    if (!caps->formats)
+    {
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        TDM_ERR("alloc failed\n");
+        goto failed_get;
+    }
+
+    for (i = 0; i < caps->format_count; i++)
+    {
+        /* TODO: kernel reports wrong formats */
+        if (plane->formats[i] != DRM_FORMAT_XRGB8888 && plane->formats[i] != DRM_FORMAT_ARGB8888)
+           continue;
+        caps->formats[i] = tdm_sprd_format_to_tbm_format(plane->formats[i]);
+    }
+
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, layer_data->plane_id, DRM_MODE_OBJECT_PLANE);
+    if (!props)
+    {
+        ret = TDM_ERROR_OPERATION_FAILED;
+        TDM_ERR("get plane properties failed: %m\n");
+        goto failed_get;
+    }
+
+    caps->props = calloc(1, sizeof(tdm_prop) * props->count_props);
+    if (!caps->props)
+    {
+        ret = TDM_ERROR_OUT_OF_MEMORY;
+        TDM_ERR("alloc failed\n");
+        goto failed_get;
+    }
+
+    caps->prop_count = 0;
+    for (i = 0; i < props->count_props; i++)
+    {
+        drmModePropertyPtr prop = drmModeGetProperty(sprd_data->drm_fd, props->props[i]);
+        if (!prop)
+            continue;
+        if (!strncmp(prop->name, "type", TDM_NAME_LEN))
+            continue;
+        if (!strncmp(prop->name, "zpos", TDM_NAME_LEN))
+            continue;
+        snprintf(caps->props[i].name, TDM_NAME_LEN, "%s", prop->name);
+        caps->props[i].id = props->props[i];
+        caps->prop_count++;
+        drmModeFreeProperty(prop);
+    }
+
+    drmModeFreeObjectProperties(props);
+    drmModeFreePlane(plane);
+
+    return TDM_ERROR_NONE;
+failed_get:
+    drmModeFreeObjectProperties(props);
+    drmModeFreePlane(plane);
+    free(caps->formats);
+    free(caps->props);
+    memset(caps, 0, sizeof(tdm_caps_layer));
+    return ret;
+}
+
+tdm_error
+sprd_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+    tdm_sprd_data *sprd_data;
+    int ret;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(layer_data->plane_id > 0, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = layer_data->sprd_data;
+    ret = drmModeObjectSetProperty(sprd_data->drm_fd,
+                                   layer_data->plane_id, DRM_MODE_OBJECT_PLANE,
+                                   id, value.u32);
+    if (ret < 0)
+    {
+        TDM_ERR("set property failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+    tdm_sprd_data *sprd_data;
+    drmModeObjectPropertiesPtr props;
+    int i;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(layer_data->plane_id > 0, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(value, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = layer_data->sprd_data;
+    props = drmModeObjectGetProperties(sprd_data->drm_fd, layer_data->plane_id,
+                                       DRM_MODE_OBJECT_PLANE);
+    if (props == NULL)
+    {
+        TDM_ERR("get property failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    for (i = 0; i < props->count_props; i++)
+        if (props->props[i] == id)
+        {
+            (*value).u32 = (uint)props->prop_values[i];
+            break;
+        }
+
+    drmModeFreeObjectProperties(props);
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_set_info(tdm_layer *layer, tdm_info_layer *info)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER);
+
+    layer_data->info = *info;
+    layer_data->info_changed = 1;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_get_info(tdm_layer *layer, tdm_info_layer *info)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER);
+
+    *info = layer_data->info;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+    tdm_sprd_data *sprd_data;
+    tdm_sprd_display_buffer *display_buffer;
+    int ret, i, count;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(buffer, TDM_ERROR_INVALID_PARAMETER);
+
+    sprd_data = layer_data->sprd_data;
+
+    display_buffer = _tdm_sprd_display_find_buffer(sprd_data, buffer);
+    if (!display_buffer)
+    {
+        display_buffer = calloc(1, sizeof(tdm_sprd_display_buffer));
+        if (!display_buffer)
+        {
+            TDM_ERR("alloc failed");
+            return TDM_ERROR_OUT_OF_MEMORY;
+        }
+
+        LIST_ADDTAIL(&display_buffer->link, &sprd_data->buffer_list);
+    }
+
+    if (display_buffer->fb_id == 0)
+    {
+        unsigned int width;
+        unsigned int height;
+        unsigned int format;
+        unsigned int handles[4] = {0,};
+        unsigned int pitches[4] = {0,};
+        unsigned int offsets[4] = {0,};
+        unsigned int size;
+
+        width = tbm_surface_get_width(buffer);
+        height = tbm_surface_get_height(buffer);
+        format = tbm_surface_get_format(buffer);
+        count = tbm_surface_internal_get_num_bos(buffer);
+        for (i = 0; i < count; i++)
+        {
+            tbm_bo bo = tbm_surface_internal_get_bo(buffer, i);
+            handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+        }
+        count = tbm_surface_internal_get_num_planes(format);
+        for (i = 0; i < count; i++)
+            tbm_surface_internal_get_plane_data(buffer, i, &size, &offsets[i], &pitches[i]);
+
+        ret = drmModeAddFB2(sprd_data->drm_fd, width, height, format,
+                            handles, pitches, offsets, &display_buffer->fb_id, 0);
+        if (ret < 0)
+        {
+            TDM_ERR("add fb failed: %m");
+            return TDM_ERROR_OPERATION_FAILED;
+        }
+
+        if (IS_RGB(format))
+            display_buffer->width = pitches[0] >> 2;
+        else
+            display_buffer->width = pitches[0];
+    }
+
+    layer_data->display_buffer = display_buffer;
+    layer_data->display_buffer_changed = 1;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_layer_unset_buffer(tdm_layer *layer)
+{
+    tdm_sprd_layer_data *layer_data = layer;
+
+    RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER);
+
+    layer_data->display_buffer = NULL;
+    layer_data->display_buffer_changed = 1;
+
+    return TDM_ERROR_NONE;
+}
diff --git a/src/tdm_sprd_format.c b/src/tdm_sprd_format.c
new file mode 100644 (file)
index 0000000..c7f2abc
--- /dev/null
@@ -0,0 +1,115 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <drm_fourcc.h>
+#include <tbm_surface.h>
+
+#include "tdm_sprd.h"
+
+#ifndef TBM_FORMAT_NV12MT
+#define TBM_FORMAT_NV12MT   FOURCC('T', 'M', '1', '2')
+#endif
+
+#ifndef DRM_FORMAT_12MT
+#define DRM_FORMAT_12MT     FOURCC('T', 'M', '1', '2')
+#endif
+
+typedef struct
+{
+    tbm_format  tbm_format;
+    uint32_t    drm_format;
+} tbm_sprd_format_data;
+
+static const tbm_sprd_format_data formats[] =
+{
+    {TBM_FORMAT_C8, DRM_FORMAT_C8},
+    {TBM_FORMAT_RGB332, DRM_FORMAT_RGB332},
+    {TBM_FORMAT_BGR233, DRM_FORMAT_BGR233},
+    {TBM_FORMAT_XRGB4444, DRM_FORMAT_XRGB4444},
+    {TBM_FORMAT_XBGR4444, DRM_FORMAT_XBGR4444},
+    {TBM_FORMAT_RGBX4444, DRM_FORMAT_RGBX4444},
+    {TBM_FORMAT_BGRX4444, DRM_FORMAT_BGRX4444},
+    {TBM_FORMAT_ARGB4444, DRM_FORMAT_ARGB4444},
+    {TBM_FORMAT_ABGR4444, DRM_FORMAT_ABGR4444},
+    {TBM_FORMAT_RGBA4444, DRM_FORMAT_RGBA4444},
+    {TBM_FORMAT_BGRA4444, DRM_FORMAT_BGRA4444},
+    {TBM_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555},
+    {TBM_FORMAT_XBGR1555, DRM_FORMAT_XBGR1555},
+    {TBM_FORMAT_RGBX5551, DRM_FORMAT_RGBX5551},
+    {TBM_FORMAT_BGRX5551, DRM_FORMAT_BGRX5551},
+    {TBM_FORMAT_ARGB1555, DRM_FORMAT_ARGB1555},
+    {TBM_FORMAT_ABGR1555, DRM_FORMAT_ABGR1555},
+    {TBM_FORMAT_RGBA5551, DRM_FORMAT_RGBA5551},
+    {TBM_FORMAT_BGRA5551, DRM_FORMAT_BGRA5551},
+    {TBM_FORMAT_RGB565, DRM_FORMAT_RGB565},
+    {TBM_FORMAT_BGR565, DRM_FORMAT_BGR565},
+    {TBM_FORMAT_RGB888, DRM_FORMAT_RGB888},
+    {TBM_FORMAT_BGR888, DRM_FORMAT_BGR888},
+    {TBM_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888},
+    {TBM_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888},
+    {TBM_FORMAT_RGBX8888, DRM_FORMAT_RGBX8888},
+    {TBM_FORMAT_BGRX8888, DRM_FORMAT_BGRX8888},
+    {TBM_FORMAT_ARGB8888, DRM_FORMAT_ARGB8888},
+    {TBM_FORMAT_ABGR8888, DRM_FORMAT_ABGR8888},
+    {TBM_FORMAT_RGBA8888, DRM_FORMAT_RGBA8888},
+    {TBM_FORMAT_BGRA8888, DRM_FORMAT_BGRA8888},
+    {TBM_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010},
+    {TBM_FORMAT_XBGR2101010, DRM_FORMAT_XBGR2101010},
+    {TBM_FORMAT_RGBX1010102, DRM_FORMAT_RGBX1010102},
+    {TBM_FORMAT_BGRX1010102, DRM_FORMAT_BGRX1010102},
+    {TBM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB2101010},
+    {TBM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR2101010},
+    {TBM_FORMAT_RGBA1010102, DRM_FORMAT_RGBA1010102},
+    {TBM_FORMAT_BGRA1010102, DRM_FORMAT_BGRA1010102},
+    {TBM_FORMAT_YUYV, DRM_FORMAT_YUYV},
+    {TBM_FORMAT_YVYU, DRM_FORMAT_YVYU},
+    {TBM_FORMAT_UYVY, DRM_FORMAT_UYVY},
+    {TBM_FORMAT_VYUY, DRM_FORMAT_VYUY},
+    {TBM_FORMAT_AYUV, DRM_FORMAT_AYUV},
+    {TBM_FORMAT_NV12, DRM_FORMAT_NV12},
+    {TBM_FORMAT_NV21, DRM_FORMAT_NV21},
+    {TBM_FORMAT_NV16, DRM_FORMAT_NV16},
+    {TBM_FORMAT_NV61, DRM_FORMAT_NV61},
+    {TBM_FORMAT_YUV410, DRM_FORMAT_YUV410},
+    {TBM_FORMAT_YVU410, DRM_FORMAT_YVU410},
+    {TBM_FORMAT_YUV411, DRM_FORMAT_YUV411},
+    {TBM_FORMAT_YVU411, DRM_FORMAT_YVU411},
+    {TBM_FORMAT_YUV420, DRM_FORMAT_YUV420},
+    {TBM_FORMAT_YVU420, DRM_FORMAT_YVU420},
+    {TBM_FORMAT_YUV422, DRM_FORMAT_YUV422},
+    {TBM_FORMAT_YVU422, DRM_FORMAT_YVU422},
+    {TBM_FORMAT_YUV444, DRM_FORMAT_YUV444},
+    {TBM_FORMAT_YVU444, DRM_FORMAT_YVU444},
+    {TBM_FORMAT_NV12MT, DRM_FORMAT_12MT},
+};
+
+#define NUM_FORMATS (sizeof(formats) / sizeof(formats[0]))
+
+uint32_t
+tdm_sprd_format_to_drm_format(tbm_format format)
+{
+    int i;
+
+    for (i = 0; i < NUM_FORMATS; i++)
+        if (formats[i].tbm_format == format)
+            return formats[i].drm_format;
+
+    TDM_ERR("tbm format '%c%c%c%c' not found", FOURCC_STR(format));
+
+    return 0;
+}
+
+tbm_format
+tdm_sprd_format_to_tbm_format(uint32_t format)
+{
+    int i;
+
+    for (i = 0; i < NUM_FORMATS; i++)
+        if (formats[i].drm_format == format)
+            return formats[i].tbm_format;
+
+    TDM_ERR("drm format '%c%c%c%c' not found", FOURCC_STR(format));
+
+    return 0;
+}
diff --git a/src/tdm_sprd_pp.c b/src/tdm_sprd_pp.c
new file mode 100644 (file)
index 0000000..874f0de
--- /dev/null
@@ -0,0 +1,475 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tdm_sprd.h"
+
+typedef struct _tdm_sprd_pp_buffer
+{
+    int index;
+    tbm_surface_h src;
+    tbm_surface_h dst;
+
+    struct list_head link;
+} tdm_sprd_pp_buffer;
+
+typedef struct _tdm_sprd_pp_data
+{
+    tdm_sprd_data *sprd_data;
+
+    unsigned int prop_id;
+
+    tdm_info_pp info;
+    int info_changed;
+
+    struct list_head pending_buffer_list;
+    struct list_head buffer_list;
+
+    tdm_pp_done_handler done_func;
+    void *done_user_data;
+
+    int startd;
+    int first_event;
+
+    struct list_head link;
+} tdm_sprd_pp_data;
+
+#if 0
+static tbm_format pp_formats[] =
+{
+    TBM_FORMAT_ARGB8888,
+    TBM_FORMAT_XRGB8888,
+    TBM_FORMAT_YUYV,
+    TBM_FORMAT_UYVY,
+    TBM_FORMAT_NV12,
+    TBM_FORMAT_NV21,
+    TBM_FORMAT_YUV420,
+    TBM_FORMAT_YVU420
+};
+#else
+static tbm_format *pp_formats = NULL;
+#endif
+
+#if 0
+#define NUM_PP_FORMAT   (sizeof(pp_formats) / sizeof(pp_formats[0]))
+#else
+#define NUM_PP_FORMAT 0
+#endif
+
+static int pp_list_init;
+static struct list_head pp_list;
+
+static int
+_get_index(tdm_sprd_pp_data *pp_data)
+{
+    tdm_sprd_pp_buffer *buffer;
+    int ret = 0;
+
+    while (1)
+    {
+        int found = 0;
+        LIST_FOR_EACH_ENTRY(buffer, &pp_data->pending_buffer_list, link)
+        {
+            if (ret == buffer->index)
+            {
+                found = 1;
+                break;
+            }
+        }
+        if (!found)
+            LIST_FOR_EACH_ENTRY(buffer, &pp_data->buffer_list, link)
+            {
+                if (ret == buffer->index)
+                {
+                    found = 1;
+                    break;
+                }
+            }
+        if (!found)
+            break;
+        ret++;
+    }
+
+    return ret;
+}
+#if 0
+static tdm_error
+_tdm_sprd_pp_set(tdm_sprd_pp_data *pp_data)
+{
+    tdm_sprd_data *sprd_data = pp_data->sprd_data;
+    tdm_info_pp *info = &pp_data->info;
+    struct drm_sprd_ipp_property property;
+    int ret = 0;
+
+    CLEAR(property);
+    property.config[0].ops_id = EXYNOS_DRM_OPS_SRC;
+    property.config[0].fmt = tdm_sprd_format_to_drm_format(info->src_config.format);
+    memcpy(&property.config[0].sz, &info->src_config.size, sizeof(tdm_size));
+    memcpy(&property.config[0].pos, &info->src_config.pos, sizeof(tdm_pos));
+    property.config[1].ops_id = EXYNOS_DRM_OPS_DST;
+    property.config[1].degree = info->transform / 4;
+    property.config[1].flip = (info->transform > 3) ? EXYNOS_DRM_FLIP_HORIZONTAL : 0;
+    property.config[1].fmt = tdm_sprd_format_to_drm_format(info->dst_config.format);
+    memcpy(&property.config[1].sz, &info->dst_config.size, sizeof(tdm_size));
+    memcpy(&property.config[1].pos, &info->dst_config.pos, sizeof(tdm_pos));
+    property.cmd = IPP_CMD_M2M;
+    property.prop_id = pp_data->prop_id;
+
+    TDM_DBG("src : flip(%x) deg(%d) fmt(%c%c%c%c) sz(%dx%d) pos(%d,%d %dx%d)  ",
+            property.config[0].flip, property.config[0].degree, FOURCC_STR(property.config[0].fmt),
+            property.config[0].sz.hsize, property.config[0].sz.vsize,
+            property.config[0].pos.x, property.config[0].pos.y, property.config[0].pos.w, property.config[0].pos.h);
+    TDM_DBG("dst : flip(%x) deg(%d) fmt(%c%c%c%c) sz(%dx%d) pos(%d,%d %dx%d)  ",
+            property.config[1].flip, property.config[1].degree, FOURCC_STR(property.config[1].fmt),
+            property.config[1].sz.hsize, property.config[1].sz.vsize,
+            property.config[1].pos.x, property.config[1].pos.y, property.config[1].pos.w, property.config[1].pos.h);
+
+    ret = ioctl(sprd_data->drm_fd, DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY, &property);
+    if (ret)
+    {
+        TDM_ERR("failed: %m");
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    TDM_DBG("success. prop_id(%d) ", property.prop_id);
+    pp_data->prop_id = property.prop_id;
+    return TDM_ERROR_NONE;
+}
+#endif
+#if 0
+static tdm_error
+_tdm_sprd_pp_queue(tdm_sprd_pp_data *pp_data, tdm_sprd_pp_buffer *buffer, enum drm_sprd_ipp_buf_type type)
+{
+    tdm_sprd_data *sprd_data = pp_data->sprd_data;
+    struct drm_sprd_ipp_queue_buf buf;
+    int i, bo_num, ret = 0;
+
+    CLEAR(buf);
+    buf.prop_id = pp_data->prop_id;
+    buf.ops_id = EXYNOS_DRM_OPS_SRC;
+    buf.buf_type = type;
+    buf.buf_id = buffer->index;
+    buf.user_data = (__u64)(uintptr_t)pp_data;
+    bo_num = tbm_surface_internal_get_num_bos(buffer->src);
+    for (i = 0; i < EXYNOS_DRM_PLANAR_MAX && i < bo_num; i++)
+    {
+        tbm_bo bo = tbm_surface_internal_get_bo(buffer->src, i);
+        buf.handle[i] = (__u32)tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+    }
+
+    TDM_DBG("prop_id(%d) ops_id(%d) ctrl(%d) id(%d) handles(%x %x %x). ",
+            buf.prop_id, buf.ops_id, buf.buf_type, buf.buf_id,
+            buf.handle[0], buf.handle[1], buf.handle[2]);
+
+    ret = ioctl(sprd_data->drm_fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, &buf);
+    if (ret)
+    {
+        TDM_ERR("src failed. prop_id(%d) op(%d) buf(%d) id(%d). %m",
+                buf.prop_id, buf.ops_id, buf.buf_type, buf.buf_id);
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    CLEAR(buf);
+    buf.prop_id = pp_data->prop_id;
+    buf.ops_id = EXYNOS_DRM_OPS_DST;
+    buf.buf_type = type;
+    buf.buf_id = buffer->index;
+    buf.user_data = (__u64)(uintptr_t)pp_data;
+    bo_num = tbm_surface_internal_get_num_bos(buffer->dst);
+    for (i = 0; i < EXYNOS_DRM_PLANAR_MAX && i < bo_num; i++)
+    {
+        tbm_bo bo = tbm_surface_internal_get_bo(buffer->dst, i);
+        buf.handle[i] = (__u32)tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+    }
+
+    TDM_DBG("prop_id(%d) ops_id(%d) ctrl(%d) id(%d) handles(%x %x %x). ",
+            buf.prop_id, buf.ops_id, buf.buf_type, buf.buf_id,
+            buf.handle[0], buf.handle[1], buf.handle[2]);
+
+    ret = ioctl(sprd_data->drm_fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, &buf);
+    if (ret)
+    {
+        TDM_ERR("dst failed. prop_id(%d) op(%d) buf(%d) id(%d). %m",
+                buf.prop_id, buf.ops_id, buf.buf_type, buf.buf_id);
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    TDM_DBG("success. prop_id(%d)", buf.prop_id);
+
+    return TDM_ERROR_NONE;
+}
+#endif
+#if 0
+static tdm_error
+_tdm_sprd_pp_cmd(tdm_sprd_pp_data *pp_data, enum drm_sprd_ipp_ctrl cmd)
+{
+    tdm_sprd_data *sprd_data = pp_data->sprd_data;
+    struct drm_sprd_ipp_cmd_ctrl ctrl;
+    int ret = 0;
+
+    ctrl.prop_id = pp_data->prop_id;
+    ctrl.ctrl = cmd;
+
+    TDM_DBG("prop_id(%d) ctrl(%d). ", ctrl.prop_id, ctrl.ctrl);
+
+    ret = ioctl(sprd_data->drm_fd, DRM_IOCTL_EXYNOS_IPP_CMD_CTRL, &ctrl);
+    if (ret)
+    {
+        TDM_ERR("failed. prop_id(%d) ctrl(%d). %m", ctrl.prop_id, ctrl.ctrl);
+        return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    TDM_DBG("success. prop_id(%d) ", ctrl.prop_id);
+
+    return TDM_ERROR_NONE;
+}
+#endif
+void
+tdm_sprd_pp_handler(unsigned int prop_id, unsigned int *buf_idx,
+                      unsigned int tv_sec, unsigned int tv_usec, void *data)
+{
+    tdm_sprd_pp_data *found = NULL, *pp_data = data;
+    tdm_sprd_pp_buffer *b = NULL, *bb = NULL, *dequeued_buffer = NULL;
+
+    if (!pp_data || !buf_idx)
+    {
+        TDM_ERR("invalid params");
+        return;
+    }
+
+    LIST_FOR_EACH_ENTRY(found, &pp_list, link)
+    {
+        if (found == pp_data)
+            break;
+    }
+    if (!found)
+        return;
+
+    TDM_DBG("pp_data(%p) index(%d, %d)", pp_data, buf_idx[0], buf_idx[1]);
+
+    LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->buffer_list, link)
+    {
+        if (buf_idx[0] == b->index)
+        {
+            dequeued_buffer = b;
+            LIST_DEL(&dequeued_buffer->link);
+            TDM_DBG("dequeued: %d", dequeued_buffer->index);
+            break;
+        }
+    }
+
+    if (!dequeued_buffer)
+    {
+        TDM_ERR("not found buffer index: %d", buf_idx[0]);
+        return;
+    }
+
+    if (!pp_data->first_event)
+    {
+        TDM_DBG("pp(%p) got a first event. ", pp_data);
+        pp_data->first_event = 1;
+    }
+
+    if (pp_data->done_func)
+        pp_data->done_func(pp_data,
+                           dequeued_buffer->src,
+                           dequeued_buffer->dst,
+                           pp_data->done_user_data);
+    free(dequeued_buffer);
+}
+
+tdm_error
+tdm_sprd_pp_get_capability(tdm_sprd_data *sprd_data, tdm_caps_pp *caps)
+{
+    int i;
+
+    if (!caps)
+    {
+        TDM_ERR("invalid params");
+        return TDM_ERROR_INVALID_PARAMETER;
+    }
+
+    caps->capabilities = TDM_PP_CAPABILITY_ASYNC;
+
+    caps->format_count = NUM_PP_FORMAT;
+    caps->formats = NULL;
+    if (NUM_PP_FORMAT)
+    {
+        /* will be freed in frontend */
+        caps->formats = calloc(1, sizeof pp_formats);
+        if (!caps->formats)
+        {
+            TDM_ERR("alloc failed");
+            return TDM_ERROR_OUT_OF_MEMORY;
+        }
+        for (i = 0; i < caps->format_count; i++)
+            caps->formats[i] = pp_formats[i];
+    }
+
+    caps->min_w = 16;
+    caps->min_h = 8;
+    caps->max_w = -1;   /* not defined */
+    caps->max_h = -1;
+    caps->preferred_align = 16;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_pp*
+tdm_sprd_pp_create(tdm_sprd_data *sprd_data, tdm_error *error)
+{
+    tdm_sprd_pp_data *pp_data = calloc(1, sizeof(tdm_sprd_pp_data));
+    if (!pp_data)
+    {
+        TDM_ERR("alloc failed");
+        if (error)
+            *error = TDM_ERROR_OUT_OF_MEMORY;
+        return NULL;
+    }
+
+    pp_data->sprd_data = sprd_data;
+
+    LIST_INITHEAD(&pp_data->pending_buffer_list);
+    LIST_INITHEAD(&pp_data->buffer_list);
+
+    if (!pp_list_init)
+    {
+        pp_list_init = 1;
+        LIST_INITHEAD(&pp_list);
+    }
+    LIST_ADDTAIL(&pp_data->link, &pp_list);
+
+    return pp_data;
+}
+
+void
+sprd_pp_destroy(tdm_pp *pp)
+{
+    tdm_sprd_pp_data *pp_data = pp;
+    tdm_sprd_pp_buffer *b = NULL, *bb = NULL;
+
+    if (!pp_data)
+        return;
+    LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->pending_buffer_list, link)
+    {
+        LIST_DEL(&b->link);
+        free(b);
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->buffer_list, link)
+    {
+        LIST_DEL(&b->link);
+#if 0
+        _tdm_sprd_pp_queue(pp_data, b, IPP_BUF_DEQUEUE);
+#endif
+        free(b);
+    }
+#if 0
+    if (pp_data->prop_id)
+        _tdm_sprd_pp_cmd(pp_data, IPP_CTRL_STOP);
+#endif
+    LIST_DEL(&pp_data->link);
+
+    free(pp_data);
+}
+
+tdm_error
+sprd_pp_set_info(tdm_pp *pp, tdm_info_pp *info)
+{
+    tdm_sprd_pp_data *pp_data = pp;
+
+    RETURN_VAL_IF_FAIL(pp_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER);
+
+    if (info->sync)
+    {
+        TDM_ERR("not support sync mode currently");
+        return TDM_ERROR_INVALID_PARAMETER;
+    }
+
+    pp_data->info = *info;
+    pp_data->info_changed = 1;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst)
+{
+    tdm_sprd_pp_data *pp_data = pp;
+    tdm_sprd_pp_buffer *buffer;
+
+    RETURN_VAL_IF_FAIL(pp_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(src, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(dst, TDM_ERROR_INVALID_PARAMETER);
+
+    buffer = calloc(1, sizeof(tdm_sprd_pp_buffer));
+    if (!buffer)
+    {
+        TDM_ERR("alloc failed");
+        return TDM_ERROR_NONE;
+    }
+
+    LIST_ADDTAIL(&buffer->link, &pp_data->pending_buffer_list);
+    buffer->index = _get_index(pp_data);
+    buffer->src = src;
+    buffer->dst = dst;
+
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_pp_commit(tdm_pp *pp)
+{
+#if 0
+    tdm_sprd_pp_data *pp_data = pp;
+    tdm_sprd_pp_buffer *b = NULL, *bb = NULL;
+    tdm_error ret = TDM_ERROR_NONE;
+    RETURN_VAL_IF_FAIL(pp_data, TDM_ERROR_INVALID_PARAMETER);
+    if (pp_data->info_changed)
+    {
+        if (pp_data->startd)
+            _tdm_sprd_pp_cmd(pp_data, IPP_CTRL_PAUSE);
+
+        ret = _tdm_sprd_pp_set(pp_data);
+        if (ret < 0)
+            return TDM_ERROR_OPERATION_FAILED;
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE(b, bb, &pp_data->pending_buffer_list, link)
+    {
+        LIST_DEL(&b->link);
+        _tdm_sprd_pp_queue(pp_data, b, IPP_BUF_ENQUEUE);
+        TDM_DBG("queued: %d", b->index);
+        LIST_ADDTAIL(&b->link, &pp_data->buffer_list);
+    }
+
+    if (pp_data->info_changed)
+    {
+        pp_data->info_changed = 0;
+
+        if (!pp_data->startd)
+        {
+            pp_data->startd = 1;
+            _tdm_sprd_pp_cmd(pp_data, IPP_CTRL_PLAY);
+        }
+        else
+            _tdm_sprd_pp_cmd(pp_data, IPP_CTRL_RESUME);
+    }
+#endif
+    return TDM_ERROR_NONE;
+}
+
+tdm_error
+sprd_pp_set_done_handler(tdm_pp *pp, tdm_pp_done_handler func, void *user_data)
+{
+    tdm_sprd_pp_data *pp_data = pp;
+
+    RETURN_VAL_IF_FAIL(pp_data, TDM_ERROR_INVALID_PARAMETER);
+    RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER);
+
+    pp_data->done_func = func;
+    pp_data->done_user_data = user_data;
+
+    return TDM_ERROR_NONE;
+}