diff options
author | Junkyeong Kim <jk0430.kim@samsung.com> | 2015-12-17 11:40:07 +0900 |
---|---|---|
committer | Boram Park <boram1288.park@samsung.com> | 2015-12-21 21:34:38 +0900 |
commit | fcc98bd4aedf49f5eecca577bdd57f6336af9673 (patch) | |
tree | b74f2a0ec7b00f33985f89f0d34972dee31b333c | |
parent | 18d08e277a25cbeb5c2a49bb74ab0324261fd1cf (diff) | |
download | libtdm-drm-fcc98bd4aedf49f5eecca577bdd57f6336af9673.tar.gz libtdm-drm-fcc98bd4aedf49f5eecca577bdd57f6336af9673.tar.bz2 libtdm-drm-fcc98bd4aedf49f5eecca577bdd57f6336af9673.zip |
init libtdm-drm backendsubmit/tizen/20151221.124351accepted/tizen/wearable/20151221.224526accepted/tizen/tv/20151221.224513accepted/tizen/mobile/20151221.224452
Change-Id: I461763439b1cb1ac397d98755f029d71d92365a5
-rw-r--r-- | COPYING | 21 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rwxr-xr-x | autogen.sh | 6 | ||||
-rw-r--r-- | configure.ac | 55 | ||||
-rw-r--r-- | packaging/libtdm-drm.manifest | 5 | ||||
-rw-r--r-- | packaging/libtdm-drm.spec | 41 | ||||
-rw-r--r-- | src/Makefile.am | 13 | ||||
-rw-r--r-- | src/tdm_drm.c | 270 | ||||
-rw-r--r-- | src/tdm_drm.h | 101 | ||||
-rw-r--r-- | src/tdm_drm_display.c | 1496 | ||||
-rw-r--r-- | src/tdm_drm_format.c | 106 |
11 files changed, 2115 insertions, 0 deletions
@@ -0,0 +1,21 @@ +Copyright 2013 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 index 0000000..af437a6 --- /dev/null +++ b/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..30d679f --- /dev/null +++ b/autogen.sh @@ -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 index 0000000..642db2e --- /dev/null +++ b/configure.ac @@ -0,0 +1,55 @@ +AC_PREREQ([2.60]) +AC_INIT([libtdm-drm], + [1.0.0], + [https://www.tizen.org], + [libtdm-drm]) + +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_DRM, libtdm libtbm libdrm) +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_DRM_CFLAGS="$TDM_DRM_CFLAGS $UDEV_CFLAGS" + TDM_DRM_LIBS="$TDM_DRM_LIBS $UDEV_LIBS" +fi + +AC_SUBST(TDM_DRM_CFLAGS) +AC_SUBST(TDM_DRM_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_DRM_CFLAGS : $TDM_DRM_CFLAGS" +echo "TDM_DRM_LIBS : $TDM_DRM_LIBS" +echo "TDM_MODULE_DIR : $TDM_MODULE_PATH" +echo "" diff --git a/packaging/libtdm-drm.manifest b/packaging/libtdm-drm.manifest new file mode 100644 index 0000000..75b0fa5 --- /dev/null +++ b/packaging/libtdm-drm.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_"/> + </request> +</manifest> diff --git a/packaging/libtdm-drm.spec b/packaging/libtdm-drm.spec new file mode 100644 index 0000000..52dc4a7 --- /dev/null +++ b/packaging/libtdm-drm.spec @@ -0,0 +1,41 @@ +Name: libtdm-drm +Version: 1.0.0 +Release: 0 +Summary: Tizen Display Manager DRM Back-End Library +Group: Development/Libraries +License: MIT +Source0: %{name}-%{version}.tar.gz +Source1001: %{name}.manifest +BuildRequires: pkgconfig(libdrm) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(libtdm) + +%description +Back-End library of Tizen Display Manager DRM : libtdm-mgr DRM 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-drm.so %{_libdir}/tdm/libtdm-default.so + +%postun -p /sbin/ldconfig + +%files +%manifest %{name}.manifest +%license COPYING +%defattr(-,root,root,-) +%{_libdir}/tdm/libtdm-drm.so diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6e6d8c3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,13 @@ +AM_CFLAGS = \ + $(TDM_DRM_CFLAGS) \ + -I$(top_srcdir)/src + +libtdm_drm_la_LTLIBRARIES = libtdm-drm.la +libtdm_drm_ladir = $(TDM_MODULE_PATH) +libtdm_drm_la_LDFLAGS = -module -avoid-version +libtdm_drm_la_LIBADD = $(TDM_DRM_LIBS) -ldl + +libtdm_drm_la_SOURCES = \ + tdm_drm_format.c \ + tdm_drm_display.c \ + tdm_drm.c diff --git a/src/tdm_drm.c b/src/tdm_drm.c new file mode 100644 index 0000000..3200569 --- /dev/null +++ b/src/tdm_drm.c @@ -0,0 +1,270 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_UDEV +#include <libudev.h> +#endif + +#include "tdm_drm.h" +#include <tdm_helper.h> + +#define TDM_DRM_NAME "vigs" + +static tdm_func_display drm_func_display = +{ + drm_display_get_capabilitiy, + NULL, //display_get_pp_capability, + NULL, //display_get_capture_capability + drm_display_get_outputs, + drm_display_get_fd, + drm_display_handle_events, + NULL, //display_create_pp, + drm_output_get_capability, + drm_output_get_layers, + drm_output_set_property, + drm_output_get_property, + drm_output_wait_vblank, + drm_output_set_vblank_handler, + drm_output_commit, + drm_output_set_commit_handler, + drm_output_set_dpms, + drm_output_get_dpms, + drm_output_set_mode, + drm_output_get_mode, + NULL, //output_create_capture + drm_layer_get_capability, + drm_layer_set_property, + drm_layer_get_property, + drm_layer_set_info, + drm_layer_get_info, + drm_layer_set_buffer, + drm_layer_unset_buffer, + NULL, //layer_create_capture +}; + +static tdm_drm_data *drm_data; + +#ifdef HAVE_UDEV +static struct udev_device* +_tdm_find_primary_gpu(void) +{ + struct udev *udev; + struct udev_enumerate *e; + struct udev_list_entry *entry; + const char *path, *id; + struct udev_device *device, *drm_device, *pci; + + udev = udev_new(); + if (!udev) + { + TDM_ERR("fail to initialize udev context\n"); + return NULL; + } + + 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)) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + if (!device) + continue; + + pci = udev_device_get_parent_with_subsystem_devtype(device, + "pci", NULL); + if (pci) { + id = udev_device_get_sysattr_value(pci, "boot_vga"); + if (id && !strcmp(id, "1")) { + if (drm_device) + udev_device_unref(drm_device); + drm_device = device; + break; + } + } + + if (!drm_device) + drm_device = device; + else + udev_device_unref(device); + } + + udev_enumerate_unref(e); + return drm_device; +} +#endif + +static int +_tdm_drm_open_drm(void) +{ + int fd = -1; + + fd = drmOpen(TDM_DRM_NAME, NULL); + if (fd < 0) + { + TDM_ERR("Cannot open '%s' drm", TDM_DRM_NAME); + } + +#ifdef HAVE_UDEV + if (fd < 0) + { + struct udev_device *drm_device = NULL; + const char *filename; + TDM_WRN("Cannot open drm device.. search by udev"); + + drm_device = _tdm_find_primary_gpu(); + if (drm_device == NULL) + { + TDM_ERR("fail to find drm device\n"); + 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); + + TDM_DBG("open drm device (name:%s, fd:%d)", filename, fd); + + udev_device_unref(drm_device); + } +close_l: +#endif + return fd; +} + +void +tdm_drm_deinit(tdm_backend_data *bdata) +{ + if (drm_data != bdata) + return; + + TDM_INFO("deinit"); + + tdm_drm_display_destroy_output_list(drm_data); + + if (drm_data->plane_res) + drmModeFreePlaneResources(drm_data->plane_res); + if (drm_data->mode_res) + drmModeFreeResources(drm_data->mode_res); + if (drm_data->drm_fd >= 0) + close(drm_data->drm_fd); + + free(drm_data); + drm_data = NULL; +} + +tdm_backend_data* +tdm_drm_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 (drm_data) + { + TDM_ERR("failed: init twice"); + if (error) + *error = TDM_ERROR_BAD_REQUEST; + return NULL; + } + + drm_data = calloc(1, sizeof(tdm_drm_data)); + if (!drm_data) + { + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_INITHEAD(&drm_data->output_list); + LIST_INITHEAD(&drm_data->buffer_list); + + ret = tdm_backend_register_func_display(dpy, &drm_func_display); + if (ret != TDM_ERROR_NONE) + goto failed; + + drm_data->dpy = dpy; + + drm_data->drm_fd = -1; + if (tdm_helper_drm_fd >= 0) + drm_data->drm_fd = tdm_helper_drm_fd; + + if (drm_data->drm_fd < 0) + drm_data->drm_fd = _tdm_drm_open_drm(); + + if (drm_data->drm_fd < 0) + { + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + if (drmSetClientCap(drm_data->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) + TDM_WRN("Set DRM_CLIENT_CAP_UNIVERSAL_PLANES failed"); + + drm_data->mode_res = drmModeGetResources(drm_data->drm_fd); + if (!drm_data->mode_res) + { + TDM_ERR("no drm resource: %m"); + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + drm_data->plane_res = drmModeGetPlaneResources(drm_data->drm_fd); + if (!drm_data->plane_res) + { + TDM_ERR("no drm plane resource: %m"); + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + if (drm_data->plane_res->count_planes <= 0) + { + TDM_ERR("no drm plane resource"); + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + ret = tdm_drm_display_create_output_list(drm_data); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_drm_display_create_layer_list(drm_data); + if (ret != TDM_ERROR_NONE) + goto failed; + + if (error) + *error = TDM_ERROR_NONE; + + TDM_INFO("init success!"); + + return (tdm_backend_data*)drm_data; +failed: + if (error) + *error = ret; + + tdm_drm_deinit(drm_data); + + TDM_ERR("init failed!"); + return NULL; +} + +tdm_backend_module tdm_backend_module_data = +{ + "vigs", + "Samsung", + TDM_BACKEND_ABI_VERSION, + tdm_drm_init, + tdm_drm_deinit +}; diff --git a/src/tdm_drm.h b/src/tdm_drm.h new file mode 100644 index 0000000..5fc9153 --- /dev/null +++ b/src/tdm_drm.h @@ -0,0 +1,101 @@ +#ifndef _TDM_DRM_H_ +#define _TDM_DRM_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> + +/* drm backend functions (display) */ +tdm_error drm_display_get_capabilitiy(tdm_backend_data *bdata, tdm_caps_display *caps); +tdm_output** drm_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error); +tdm_error drm_display_get_fd(tdm_backend_data *bdata, int *fd); +tdm_error drm_display_handle_events(tdm_backend_data *bdata); +tdm_pp* drm_display_create_pp(tdm_backend_data *bdata, tdm_error *error); +tdm_error drm_output_get_capability(tdm_output *output, tdm_caps_output *caps); +tdm_layer** drm_output_get_layers(tdm_output *output, int *count, tdm_error *error); +tdm_error drm_output_set_property(tdm_output *output, unsigned int id, tdm_value value); +tdm_error drm_output_get_property(tdm_output *output, unsigned int id, tdm_value *value); +tdm_error drm_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data); +tdm_error drm_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func); +tdm_error drm_output_commit(tdm_output *output, int sync, void *user_data); +tdm_error drm_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func); +tdm_error drm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value); +tdm_error drm_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value); +tdm_error drm_output_set_mode(tdm_output *output, tdm_output_mode *mode); +tdm_error drm_output_get_mode(tdm_output *output, const tdm_output_mode **mode); +tdm_error drm_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps); +tdm_error drm_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value); +tdm_error drm_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value); +tdm_error drm_layer_set_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error drm_layer_get_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error drm_layer_set_buffer(tdm_layer *layer, tdm_buffer *buffer); +tdm_error drm_layer_unset_buffer(tdm_layer *layer); + +/* drm 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_drm_data +{ + tdm_display *dpy; + + int drm_fd; + + drmModeResPtr mode_res; + drmModePlaneResPtr plane_res; + + struct list_head output_list; + struct list_head buffer_list; +} tdm_drm_data; + +uint32_t tdm_drm_format_to_drm_format(tbm_format format); +tbm_format tdm_drm_format_to_tbm_format(uint32_t format); + +tdm_error tdm_drm_display_create_output_list(tdm_drm_data *drm_data); +void tdm_drm_display_destroy_output_list(tdm_drm_data *drm_data); +tdm_error tdm_drm_display_create_layer_list(tdm_drm_data *drm_data); + +#endif /* _TDM_DRM_H_ */ diff --git a/src/tdm_drm_display.c b/src/tdm_drm_display.c new file mode 100644 index 0000000..645a086 --- /dev/null +++ b/src/tdm_drm_display.c @@ -0,0 +1,1496 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <drm_fourcc.h> +#include <tdm_helper.h> +#include "tdm_drm.h" + +#define MIN_WIDTH 32 + +typedef struct _tdm_drm_output_data tdm_drm_output_data; +typedef struct _tdm_drm_layer_data tdm_drm_layer_data; +typedef struct _tdm_drm_vblank_data tdm_drm_vblank_data; + +typedef enum +{ + VBLANK_TYPE_WAIT, + VBLANK_TYPE_COMMIT, +} vblank_type; + +typedef struct _tdm_drm_display_buffer +{ + struct list_head link; + + unsigned int fb_id; + tdm_buffer *buffer; + int width; +} tdm_drm_display_buffer; + +struct _tdm_drm_vblank_data +{ + vblank_type type; + tdm_drm_output_data *output_data; + void *user_data; +}; + +struct _tdm_drm_output_data +{ + struct list_head link; + + /* data which are fixed at initializing */ + tdm_drm_data *drm_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_drm_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_drm_layer_data +{ + struct list_head link; + + /* data which are fixed at initializing */ + tdm_drm_data *drm_data; + tdm_drm_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_drm_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); +} Drm_Event_Context; + +static drmModeModeInfoPtr +_tdm_drm_display_get_mode(tdm_drm_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_drm_display_buffer* +_tdm_drm_display_find_buffer(tdm_drm_data *drm_data, tdm_buffer *buffer) +{ + tdm_drm_display_buffer *display_buffer; + + LIST_FOR_EACH_ENTRY(display_buffer, &drm_data->buffer_list, link) + { + if (display_buffer->buffer == buffer) + return display_buffer; + } + + return NULL; +} + +static void +_tdm_drm_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_drm_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_drm_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_drm_display_commit_primary_layer(tdm_drm_layer_data *layer_data) +{ + tdm_drm_data *drm_data = layer_data->drm_data; + tdm_drm_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_drm_display_get_mode(output_data); + if (!mode) + { + TDM_ERR("couldn't find proper mode"); + return TDM_ERROR_BAD_REQUEST; + } + + if (drmModeSetCrtc(drm_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(drm_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(drm_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_drm_display_commit_layer(tdm_drm_layer_data *layer_data) +{ + tdm_drm_data *drm_data = layer_data->drm_data; + tdm_drm_output_data *output_data = layer_data->output_data; + 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(drm_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(drm_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; + } + + /* Source values are 16.16 fixed point */ + fx = ((unsigned int)layer_data->info.src_config.pos.x) << 16; + fy = ((unsigned int)layer_data->info.src_config.pos.y) << 16; + fw = ((unsigned int)layer_data->info.src_config.pos.w) << 16; + fh = ((unsigned int)layer_data->info.src_config.pos.h) << 16; + + if (drmModeSetPlane(drm_data->drm_fd, layer_data->plane_id, + output_data->crtc_id, layer_data->display_buffer->fb_id, 0, + layer_data->info.dst_pos.x, layer_data->info.dst_pos.y, + layer_data->info.dst_pos.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_drm_display_cb_vblank(int fd, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_drm_vblank_data *vblank_data = user_data; + tdm_drm_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 int +_tdm_drm_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 (len > MAX_BUF_SIZE) + { + TDM_WRN("warning: the size of the drm_event can be over the maximum size."); + return -1; + } + + 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; + case DRM_EVENT_FLIP_COMPLETE: + /* do nothing for flip complete */ + break; + default: + break; + } + i += e->length; + } + + return 0; +} + +static tdm_error +_tdm_drm_display_create_layer_list(tdm_drm_data *drm_data) +{ + int i; + + if (drm_data->plane_res->count_planes == 0) + { + TDM_ERR("no layer error"); + return TDM_ERROR_OPERATION_FAILED; + } + + for (i = 0; i < drm_data->plane_res->count_planes; i++) + { + tdm_drm_output_data *output_data; + tdm_drm_layer_data *layer_data; + drmModePlanePtr plane; + + if (i == 3) + { + TDM_DBG("doesn't need more layer set"); + break; + } + + LIST_FOR_EACH_ENTRY(output_data, &drm_data->output_list, link) + { + if (output_data->pipe == 0) + break; + } + + plane = drmModeGetPlane(drm_data->drm_fd, drm_data->plane_res->planes[i]); + if (!plane) + { + TDM_ERR("no plane"); + continue; + } + + layer_data = calloc(1, sizeof(tdm_drm_layer_data)); + if (!layer_data) + { + TDM_ERR("alloc failed"); + drmModeFreePlane(plane); + continue; + } + + layer_data->drm_data = drm_data; + layer_data->output_data = output_data; + layer_data->plane_id = drm_data->plane_res->planes[i]; + + if (drm_data->plane_res->count_planes == 1) + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + output_data->primary_layer = layer_data; + } + else if (drm_data->plane_res->count_planes == 2) + { + if (i == 0) + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + output_data->primary_layer = layer_data; + } + else + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC; + } + } + else + { + if (i == 0) + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_CURSOR | TDM_LAYER_CAPABILITY_GRAPHIC; + } + else if (i == 1) + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + output_data->primary_layer = layer_data; + } + else + { + layer_data->capabilities = TDM_LAYER_CAPABILITY_OVERLAY | TDM_LAYER_CAPABILITY_GRAPHIC; + } + } + + TDM_DBG("layer_data(%p) plane_id(%d) crtc_id(%d) capabilities(%x)", + layer_data, layer_data->plane_id, layer_data->output_data->crtc_id, + layer_data->capabilities); + + LIST_ADDTAIL(&layer_data->link, &output_data->layer_list); + + drmModeFreePlane(plane); + } + + return TDM_ERROR_NONE; +} + +static void +_tdm_drm_display_cb_destroy_buffer(tdm_buffer *buffer, void *user_data) +{ + tdm_drm_data *drm_data; + tdm_drm_display_buffer *display_buffer; + int ret; + + if (!user_data) + { + TDM_ERR("no user_data"); + return; + } + if (!buffer) + { + TDM_ERR("no buffer"); + return; + } + + drm_data = (tdm_drm_data *)user_data; + + display_buffer = _tdm_drm_display_find_buffer(drm_data, buffer); + if (!display_buffer) + { + TDM_ERR("no display_buffer"); + return; + } + LIST_DEL(&display_buffer->link); + + if (display_buffer->fb_id > 0) + { + ret = drmModeRmFB(drm_data->drm_fd, display_buffer->fb_id); + if (ret < 0) + { + TDM_ERR("rm fb failed"); + return; + } + TDM_DBG("drmModeRmFB success!!! fb_id:%d", display_buffer->fb_id); + } + else + TDM_DBG("drmModeRmFB not called fb_id:%d", display_buffer->fb_id); + + free(display_buffer); +} + +tdm_error +tdm_drm_display_create_layer_list(tdm_drm_data *drm_data) +{ + tdm_drm_output_data *output_data; + tdm_error ret; + + ret = _tdm_drm_display_create_layer_list(drm_data); + if (ret != TDM_ERROR_NONE) + return ret; + + LIST_FOR_EACH_ENTRY(output_data, &drm_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_drm_display_destroy_output_list(tdm_drm_data *drm_data) +{ + tdm_drm_output_data *o = NULL, *oo = NULL; + + if (LIST_IS_EMPTY(&drm_data->output_list)) + return; + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &drm_data->output_list, link) + { + LIST_DEL(&o->link); + if (!LIST_IS_EMPTY(&o->layer_list)) + { + tdm_drm_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_drm_display_create_output_list(tdm_drm_data *drm_data) +{ + tdm_drm_output_data *output_data; + int i; + tdm_error ret; + int allocated = 0; + + RETURN_VAL_IF_FAIL(LIST_IS_EMPTY(&drm_data->output_list), TDM_ERROR_OPERATION_FAILED); + + for (i = 0; i < drm_data->mode_res->count_connectors; i++) + { + drmModeConnectorPtr connector; + drmModeEncoderPtr encoder; + int crtc_id = 0, c, j; + + connector = drmModeGetConnector(drm_data->drm_fd, drm_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(drm_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 < drm_data->mode_res->count_crtcs; c++) + { + if (allocated & (1 << c)) + continue; + + if ((encoder->possible_crtcs & (1 << c)) == 0) + continue; + + crtc_id = drm_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_drm_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->drm_data = drm_data; + output_data->connector_id = drm_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(drm_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_drm_display_to_tdm_mode(&output_data->drm_modes[j], &output_data->output_modes[j]); + } + + LIST_ADDTAIL(&output_data->link, &drm_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", drm_data->mode_res->count_connectors); + + return TDM_ERROR_NONE; +failed_create: + tdm_drm_display_destroy_output_list(drm_data); + return ret; +} + +tdm_error +drm_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_output** +drm_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error) +{ + tdm_drm_data *drm_data = bdata; + tdm_drm_output_data *output_data; + tdm_output **outputs; + tdm_error ret; + int i; + + RETURN_VAL_IF_FAIL(drm_data, NULL); + RETURN_VAL_IF_FAIL(count, NULL); + + *count = 0; + LIST_FOR_EACH_ENTRY(output_data, &drm_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_drm_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, &drm_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 +drm_display_get_fd(tdm_backend_data *bdata, int *fd) +{ + tdm_drm_data *drm_data = bdata; + + RETURN_VAL_IF_FAIL(drm_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(fd, TDM_ERROR_INVALID_PARAMETER); + + *fd = drm_data->drm_fd; + + return TDM_ERROR_NONE; +} + +tdm_error +drm_display_handle_events(tdm_backend_data *bdata) +{ + tdm_drm_data *drm_data = bdata; + Drm_Event_Context ctx; + + RETURN_VAL_IF_FAIL(drm_data, TDM_ERROR_INVALID_PARAMETER); + + memset(&ctx, 0, sizeof(Drm_Event_Context)); + + ctx.vblank_handler = _tdm_drm_display_cb_vblank; + + _tdm_drm_display_events_handle(drm_data->drm_fd, &ctx); + + return TDM_ERROR_NONE; +} + +tdm_error +drm_output_get_capability(tdm_output *output, tdm_caps_output *caps) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_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)); + + drm_data = output_data->drm_data; + + caps->status = output_data->status; + caps->type = output_data->connector_type; + caps->type_id = output_data->connector_type_id; + + connector = drmModeGetConnector(drm_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 = drm_data->mode_res->min_width; + caps->min_h = drm_data->mode_res->min_height; + caps->max_w = drm_data->mode_res->max_width; + caps->max_h = drm_data->mode_res->max_height; + caps->preferred_align = -1; + + crtc = drmModeGetCrtc(drm_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(drm_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(drm_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** +drm_output_get_layers(tdm_output *output, int *count, tdm_error *error) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_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_drm_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 +drm_output_set_property(tdm_output *output, unsigned int id, tdm_value value) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_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); + + drm_data = output_data->drm_data; + ret = drmModeObjectSetProperty(drm_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 +drm_output_get_property(tdm_output *output, unsigned int id, tdm_value *value) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_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); + + drm_data = output_data->drm_data; + props = drmModeObjectGetProperties(drm_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 +drm_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_data; + tdm_drm_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_drm_vblank_data)); + if (!vblank_data) + { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + drm_data = output_data->drm_data; + + ret = _tdm_drm_display_get_cur_msc(drm_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_drm_display_wait_vblank(drm_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 +drm_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func) +{ + tdm_drm_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 +drm_output_commit(tdm_output *output, int sync, void *user_data) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_data; + tdm_drm_layer_data *layer_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + drm_data = output_data->drm_data; + + LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) + { + if (layer_data == output_data->primary_layer) + { + ret = _tdm_drm_display_commit_primary_layer(layer_data); + if (ret != TDM_ERROR_NONE) + return ret; + } + else + { + ret = _tdm_drm_display_commit_layer(layer_data); + if (ret != TDM_ERROR_NONE) + return ret; + } + } + + if (tdm_helper_drm_fd == -1) + { + tdm_drm_vblank_data *vblank_data = calloc(1, sizeof(tdm_drm_vblank_data)); + uint target_msc; + + if (!vblank_data) + { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + ret = _tdm_drm_display_get_cur_msc(drm_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_drm_display_wait_vblank(drm_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 +drm_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func) +{ + tdm_drm_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 +drm_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_data; + int ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + drm_data = output_data->drm_data; + ret = drmModeObjectSetProperty(drm_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 +drm_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value) +{ + tdm_drm_output_data *output_data = output; + tdm_drm_data *drm_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); + + drm_data = output_data->drm_data; + props = drmModeObjectGetProperties(drm_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 +drm_output_set_mode(tdm_output *output, tdm_output_mode *mode) +{ + tdm_drm_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 +drm_output_get_mode(tdm_output *output, const tdm_output_mode **mode) +{ + tdm_drm_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 +drm_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps) +{ + tdm_drm_layer_data *layer_data = layer; + tdm_drm_data *drm_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)); + + drm_data = layer_data->drm_data; + plane = drmModeGetPlane(drm_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_drm_format_to_tbm_format(plane->formats[i]); + } + + props = drmModeObjectGetProperties(drm_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(drm_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 +drm_layer_set_property(tdm_layer *layer, unsigned int id, tdm_value value) +{ + tdm_drm_layer_data *layer_data = layer; + tdm_drm_data *drm_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); + + drm_data = layer_data->drm_data; + ret = drmModeObjectSetProperty(drm_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 +drm_layer_get_property(tdm_layer *layer, unsigned int id, tdm_value *value) +{ + tdm_drm_layer_data *layer_data = layer; + tdm_drm_data *drm_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); + + drm_data = layer_data->drm_data; + props = drmModeObjectGetProperties(drm_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 +drm_layer_set_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_drm_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 +drm_layer_get_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_drm_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 +drm_layer_set_buffer(tdm_layer *layer, tdm_buffer *buffer) +{ + tdm_drm_layer_data *layer_data = layer; + tdm_drm_data *drm_data; + tdm_drm_display_buffer *display_buffer; + tdm_error err = TDM_ERROR_NONE; + tbm_surface_h surface; + int ret, i, count; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(buffer, TDM_ERROR_INVALID_PARAMETER); + + drm_data = layer_data->drm_data; + surface = tdm_buffer_get_surface(buffer); + + display_buffer = _tdm_drm_display_find_buffer(drm_data, buffer); + if (!display_buffer) + { + display_buffer = calloc(1, sizeof(tdm_drm_display_buffer)); + if (!display_buffer) + { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + display_buffer->buffer = buffer; + + err = tdm_buffer_add_destroy_handler(buffer, _tdm_drm_display_cb_destroy_buffer, drm_data); + if (err != TDM_ERROR_NONE) + { + TDM_ERR("add destroy handler fail"); + return TDM_ERROR_OPERATION_FAILED; + } + + LIST_ADDTAIL(&display_buffer->link, &drm_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(surface); + height = tbm_surface_get_height(surface); + format = tbm_surface_get_format(surface); + count = tbm_surface_internal_get_num_bos(surface); + for (i = 0; i < count; i++) + { + tbm_bo bo = tbm_surface_internal_get_bo(surface, 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(surface, i, &size, &offsets[i], &pitches[i]); + + ret = drmModeAddFB2(drm_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; + } + TDM_DBG("drm_data->drm_fd : %d, display_buffer->fb_id:%u", drm_data->drm_fd, display_buffer->fb_id); + + 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 +drm_layer_unset_buffer(tdm_layer *layer) +{ + tdm_drm_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_drm_format.c b/src/tdm_drm_format.c new file mode 100644 index 0000000..c85b1de --- /dev/null +++ b/src/tdm_drm_format.c @@ -0,0 +1,106 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <drm_fourcc.h> +#include <tbm_surface.h> + +#include "tdm_drm.h" + +typedef struct +{ + tbm_format tbm_format; + uint32_t drm_format; +} tbm_drm_format_data; + +static const tbm_drm_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}, +}; + +#define NUM_FORMATS (sizeof(formats) / sizeof(formats[0])) + +uint32_t +tdm_drm_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_drm_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; +} |