summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2017-08-23 19:54:39 +0200
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2017-08-23 19:54:39 +0200
commit4b01867f69b18bfddfea33a131ac1219eabc88a5 (patch)
treecf2cba025744b8fda58a95edc0f38a8b1c24f07c
Import of Rockchip Android 7.1.1 SDKHEADmaster
-rw-r--r--.clang-format4
-rw-r--r--Android.mk168
-rw-r--r--autofd.h106
-rw-r--r--autogl.h110
-rw-r--r--autolock.cpp56
-rw-r--r--autolock.h42
-rwxr-xr-xdrmcomposition.cpp174
-rw-r--r--drmcomposition.h91
-rw-r--r--drmcompositor.cpp111
-rw-r--r--drmcompositor.h57
-rwxr-xr-xdrmcompositorworker.cpp94
-rw-r--r--drmcompositorworker.h41
-rwxr-xr-xdrmconnector.cpp218
-rwxr-xr-xdrmconnector.h112
-rwxr-xr-xdrmcrtc.cpp145
-rwxr-xr-xdrmcrtc.h87
-rwxr-xr-xdrmdisplaycomposition.cpp864
-rwxr-xr-xdrmdisplaycomposition.h240
-rwxr-xr-xdrmdisplaycompositor.cpp1816
-rwxr-xr-xdrmdisplaycompositor.h218
-rwxr-xr-xdrmencoder.cpp55
-rwxr-xr-xdrmencoder.h58
-rw-r--r--drmeventlistener.cpp135
-rw-r--r--drmeventlistener.h65
-rwxr-xr-xdrmframebuffer.h194
-rw-r--r--drmhwcgralloc.h35
-rwxr-xr-xdrmhwcomposer.h247
-rw-r--r--drmmode.cpp186
-rw-r--r--drmmode.h89
-rwxr-xr-xdrmplane.cpp291
-rwxr-xr-xdrmplane.h131
-rwxr-xr-xdrmproperty.cpp136
-rwxr-xr-xdrmproperty.h76
-rwxr-xr-xdrmresources.cpp1059
-rwxr-xr-xdrmresources.h145
-rwxr-xr-xglworker.cpp823
-rw-r--r--glworker.h83
-rwxr-xr-xhwc_debug.cpp234
-rwxr-xr-xhwc_debug.h63
-rwxr-xr-xhwc_rockchip.cpp1722
-rwxr-xr-xhwc_rockchip.h125
-rw-r--r--hwc_util.cpp89
-rw-r--r--hwc_util.h14
-rwxr-xr-xhwcomposer.cpp2081
-rwxr-xr-xplatform.cpp179
-rwxr-xr-xplatform.h181
-rwxr-xr-xplatformdrmgeneric.cpp239
-rw-r--r--platformdrmgeneric.h50
-rw-r--r--platformnv.cpp281
-rw-r--r--platformnv.h80
-rw-r--r--separate_rects.cpp416
-rw-r--r--separate_rects.h169
-rwxr-xr-xvirtualcompositorworker.cpp181
-rw-r--r--virtualcompositorworker.h56
-rwxr-xr-xvsyncworker.cpp225
-rw-r--r--vsyncworker.h57
-rw-r--r--worker.cpp208
-rw-r--r--worker.h76
58 files changed, 15288 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..5382f9b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,4 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..6b5a3a9
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,168 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+BOARD_USES_DRM_HWCOMPOSER=true
+ifeq ($(strip $(BOARD_USES_DRM_HWCOMPOSER)),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libdrm \
+ libEGL \
+ libGLESv2 \
+ libhardware \
+ liblog \
+ libsync \
+ libui \
+ libutils \
+ librga
+
+LOCAL_C_INCLUDES := \
+ hardware/rockchip/libgralloc \
+ external/libdrm \
+ external/libdrm/include/drm \
+ system/core/include/utils \
+ system/core/libsync \
+ system/core/libsync/include \
+ hardware/rockchip/librga
+
+LOCAL_SRC_FILES := \
+ autolock.cpp \
+ drmresources.cpp \
+ drmcomposition.cpp \
+ drmcompositor.cpp \
+ drmcompositorworker.cpp \
+ drmconnector.cpp \
+ drmcrtc.cpp \
+ drmdisplaycomposition.cpp \
+ drmdisplaycompositor.cpp \
+ drmencoder.cpp \
+ drmeventlistener.cpp \
+ drmmode.cpp \
+ drmplane.cpp \
+ drmproperty.cpp \
+ glworker.cpp \
+ hwcomposer.cpp \
+ platform.cpp \
+ platformdrmgeneric.cpp \
+ platformnv.cpp \
+ separate_rects.cpp \
+ virtualcompositorworker.cpp \
+ vsyncworker.cpp \
+ worker.cpp \
+ hwc_util.cpp \
+ hwc_rockchip.cpp \
+ hwc_debug.cpp
+
+ifeq ($(strip $(BOARD_DRM_HWCOMPOSER_BUFFER_IMPORTER)),nvidia-gralloc)
+LOCAL_CPPFLAGS += -DUSE_NVIDIA_IMPORTER
+else
+
+ifeq ($(strip $(TARGET_BOARD_PLATFORM)),rk3368)
+LOCAL_CPPFLAGS += -DTARGET_BOARD_PLATFORM_RK3368
+USE_AFBC_LAYER = 0
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),tablet)
+LOCAL_CPPFLAGS += -DRK3368_MID
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),box)
+LOCAL_CPPFLAGS += -DRK3368_BOX
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),phone)
+LOCAL_CPPFLAGS += -DRK3368_PHONE
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),vr)
+LOCAL_CPPFLAGS += -DRK3368_VR
+endif
+endif
+
+ifeq ($(strip $(TARGET_BOARD_PLATFORM)),rk3399)
+LOCAL_CPPFLAGS += -DTARGET_BOARD_PLATFORM_RK3399 -DRK_DRM_GRALLOC=1 \
+ -DMALI_AFBC_GRALLOC=1
+USE_AFBC_LAYER = 1
+
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),tablet)
+LOCAL_CPPFLAGS += -DRK3399_MID
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),box)
+LOCAL_CPPFLAGS += -DRK3399_BOX
+USE_AFBC_LAYER = 0
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),phone)
+LOCAL_CPPFLAGS += -DRK3399_PHONE
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),vr)
+LOCAL_CPPFLAGS += -DRK3399_VR
+endif
+endif
+
+ifeq ($(strip $(TARGET_BOARD_PLATFORM)),rk3288)
+LOCAL_CPPFLAGS += -DTARGET_BOARD_PLATFORM_RK3288 -DRK_DRM_GRALLOC=1 \
+ -DMALI_AFBC_GRALLOC=1
+USE_AFBC_LAYER = 0
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),tablet)
+LOCAL_CPPFLAGS += -DRK3288_MID
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),box)
+LOCAL_CPPFLAGS += -DRK3288_BOX
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),phone)
+LOCAL_CPPFLAGS += -DRK3288_PHONE
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),vr)
+LOCAL_CPPFLAGS += -DRK3288_VR
+endif
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),stbvr)
+LOCAL_CPPFLAGS += -DRK3288_VR
+endif
+endif
+
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),tablet)
+LOCAL_CFLAGS += -DRK_MID
+else
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),box)
+LOCAL_CFLAGS += -DRK_BOX
+else
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),phone)
+LOCAL_CFLAGS += -DRK_PHONE
+else
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),vr)
+LOCAL_CFLAGS += -DRK_VIR
+else
+ifeq ($(strip $(TARGET_BOARD_PLATFORM_PRODUCT)),stbvr)
+LOCAL_CFLAGS += -DRK_VIR
+endif #stbvr
+endif #vr
+endif #phone
+endif #box
+endif #tablet
+
+LOCAL_CPPFLAGS += -DUSE_SQUASH=0 -DRK_RGA_TEST=0 -DRK_VR=0 -DRK_STEREO=0
+LOCAL_CPPFLAGS += -DUSE_DRM_GENERIC_IMPORTER \
+ -DUSE_MULTI_AREAS=1 -DRK_RGA=1 \
+ -DUSE_AFBC_LAYER=$(USE_AFBC_LAYER) -DRK_SKIP_SUB=1 \
+ -DRK_VIDEO_UI_OPT=1 -DRK_VIDEO_SKIP_LINE=1 \
+ -DRK_INVALID_REFRESH=1
+MAJOR_VERSION := "RK_GRAPHICS_VER=commit-id:$(shell cd $(LOCAL_PATH) && git log -1 --oneline | awk '{print $$1}')"
+LOCAL_CPPFLAGS += -DRK_GRAPHICS_VER=\"$(MAJOR_VERSION)\"
+endif
+
+LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_HARDWARE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX)
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/autofd.h b/autofd.h
new file mode 100644
index 0000000..1edc9c5
--- /dev/null
+++ b/autofd.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUTO_FD_H_
+#define ANDROID_AUTO_FD_H_
+
+#include <unistd.h>
+
+namespace android {
+
+class UniqueFd {
+ public:
+ UniqueFd() = default;
+ UniqueFd(int fd) : fd_(fd) {
+ }
+ UniqueFd(UniqueFd &&rhs) {
+ fd_ = rhs.fd_;
+ rhs.fd_ = -1;
+ }
+
+ UniqueFd &operator=(UniqueFd &&rhs) {
+ Set(rhs.Release());
+ return *this;
+ }
+
+ ~UniqueFd() {
+ if (fd_ >= 0)
+ close(fd_);
+ }
+
+ int Release() {
+ int old_fd = fd_;
+ fd_ = -1;
+ return old_fd;
+ }
+
+ int Set(int fd) {
+ if (fd_ >= 0)
+ close(fd_);
+ fd_ = fd;
+ return fd_;
+ }
+
+ void Close() {
+ if (fd_ >= 0)
+ close(fd_);
+ fd_ = -1;
+ }
+
+ int get() const {
+ return fd_;
+ }
+
+ private:
+ int fd_ = -1;
+};
+
+struct OutputFd {
+ OutputFd() = default;
+ OutputFd(int *fd) : fd_(fd) {
+ }
+ OutputFd(OutputFd &&rhs) {
+ fd_ = rhs.fd_;
+ rhs.fd_ = NULL;
+ }
+
+ OutputFd &operator=(OutputFd &&rhs) {
+ fd_ = rhs.fd_;
+ rhs.fd_ = NULL;
+ return *this;
+ }
+
+ int Set(int fd) {
+ if (*fd_ >= 0)
+ close(*fd_);
+ *fd_ = fd;
+ return fd;
+ }
+
+ int get() {
+ return *fd_;
+ }
+
+ operator bool() const {
+ return fd_ != NULL;
+ }
+
+ private:
+ int *fd_ = NULL;
+};
+}
+
+#endif
diff --git a/autogl.h b/autogl.h
new file mode 100644
index 0000000..fc77fb0
--- /dev/null
+++ b/autogl.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUTO_GL_H_
+#define ANDROID_AUTO_GL_H_
+
+#include <memory>
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+// TODO(zachr): use hwc_drm_bo to turn buffer handles into textures
+#ifndef EGL_NATIVE_HANDLE_ANDROID_NVX
+#define EGL_NATIVE_HANDLE_ANDROID_NVX 0x322A
+#endif
+
+namespace android {
+
+#define AUTO_GL_TYPE(name, type, zero, deleter) \
+ struct name##Deleter { \
+ typedef type pointer; \
+ \
+ void operator()(pointer p) const { \
+ if (p != zero) { \
+ deleter; \
+ } \
+ } \
+ }; \
+ typedef std::unique_ptr<type, name##Deleter> name;
+
+AUTO_GL_TYPE(AutoGLFramebuffer, GLuint, 0, glDeleteFramebuffers(1, &p))
+AUTO_GL_TYPE(AutoGLBuffer, GLuint, 0, glDeleteBuffers(1, &p))
+AUTO_GL_TYPE(AutoGLTexture, GLuint, 0, glDeleteTextures(1, &p))
+AUTO_GL_TYPE(AutoGLShader, GLint, 0, glDeleteShader(p))
+AUTO_GL_TYPE(AutoGLProgram, GLint, 0, glDeleteProgram(p))
+
+struct AutoEGLDisplayImage {
+ AutoEGLDisplayImage() = default;
+
+ AutoEGLDisplayImage(EGLDisplay display, EGLImageKHR image)
+ : display_(display), image_(image) {
+ }
+
+ AutoEGLDisplayImage(const AutoEGLDisplayImage& rhs) = delete;
+ AutoEGLDisplayImage(AutoEGLDisplayImage&& rhs) {
+ display_ = rhs.display_;
+ image_ = rhs.image_;
+ rhs.display_ = EGL_NO_DISPLAY;
+ rhs.image_ = EGL_NO_IMAGE_KHR;
+ }
+
+ ~AutoEGLDisplayImage() {
+ clear();
+ }
+
+ AutoEGLDisplayImage& operator=(const AutoEGLDisplayImage& rhs) = delete;
+ AutoEGLDisplayImage& operator=(AutoEGLDisplayImage&& rhs) {
+ clear();
+ std::swap(display_, rhs.display_);
+ std::swap(image_, rhs.image_);
+ return *this;
+ }
+
+ void reset(EGLDisplay display, EGLImageKHR image) {
+ clear();
+ display_ = display;
+ image_ = image;
+ }
+
+ void clear() {
+ if (image_ != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(display_, image_);
+ display_ = EGL_NO_DISPLAY;
+ image_ = EGL_NO_IMAGE_KHR;
+ }
+ }
+
+ EGLImageKHR image() const {
+ return image_;
+ }
+
+ private:
+ EGLDisplay display_ = EGL_NO_DISPLAY;
+ EGLImageKHR image_ = EGL_NO_IMAGE_KHR;
+};
+
+struct AutoEGLImageAndGLTexture {
+ AutoEGLDisplayImage image;
+ AutoGLTexture texture;
+};
+}
+
+#endif
diff --git a/autolock.cpp b/autolock.cpp
new file mode 100644
index 0000000..1a2ded7
--- /dev/null
+++ b/autolock.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwc-drm-auto-lock"
+
+#include "autolock.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+int AutoLock::Lock() {
+ if (locked_) {
+ ALOGE("Invalid attempt to double lock AutoLock %s", name_);
+ return -EINVAL;
+ }
+ int ret = pthread_mutex_lock(mutex_);
+ if (ret) {
+ ALOGE("Failed to acquire %s lock %d", name_, ret);
+ return ret;
+ }
+ locked_ = true;
+ return 0;
+}
+
+int AutoLock::Unlock() {
+ if (!locked_) {
+ ALOGE("Invalid attempt to unlock unlocked AutoLock %s", name_);
+ return -EINVAL;
+ }
+ int ret = pthread_mutex_unlock(mutex_);
+ if (ret) {
+ ALOGE("Failed to release %s lock %d", name_, ret);
+ return ret;
+ }
+ locked_ = false;
+ return 0;
+}
+}
diff --git a/autolock.h b/autolock.h
new file mode 100644
index 0000000..3b824e2
--- /dev/null
+++ b/autolock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+namespace android {
+
+class AutoLock {
+ public:
+ AutoLock(pthread_mutex_t *mutex, const char *const name)
+ : mutex_(mutex), name_(name) {
+ }
+ ~AutoLock() {
+ if (locked_)
+ Unlock();
+ }
+
+ AutoLock(const AutoLock &rhs) = delete;
+ AutoLock &operator=(const AutoLock &rhs) = delete;
+
+ int Lock();
+ int Unlock();
+
+ private:
+ pthread_mutex_t *const mutex_;
+ bool locked_ = false;
+ const char *const name_;
+};
+}
diff --git a/drmcomposition.cpp b/drmcomposition.cpp
new file mode 100755
index 0000000..8161e33
--- /dev/null
+++ b/drmcomposition.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-composition"
+
+#include "drmcomposition.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+#include "platform.h"
+
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <sw_sync.h>
+#include <sync/sync.h>
+
+namespace android {
+
+DrmComposition::DrmComposition(DrmResources *drm, Importer *importer,
+ Planner *planner)
+ : drm_(drm), importer_(importer), planner_(planner) {
+ char use_overlay_planes_prop[PROPERTY_VALUE_MAX];
+ property_get("hwc.drm.use_overlay_planes", use_overlay_planes_prop, "1");
+ bool use_overlay_planes = atoi(use_overlay_planes_prop);
+ for (auto &plane : drm->sort_planes()) {
+ if (plane->type() == DRM_PLANE_TYPE_PRIMARY)
+ primary_planes_.push_back(plane);
+ else if (use_overlay_planes && (plane->type() == DRM_PLANE_TYPE_OVERLAY ||
+ plane->type() == DRM_PLANE_TYPE_CURSOR))
+ overlay_planes_.push_back(plane);
+ }
+}
+
+int DrmComposition::Init(uint64_t frame_no) {
+ int i;
+
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ composition_map_[i].reset(new DrmDisplayComposition());
+ if (!composition_map_[i]) {
+ ALOGE("Failed to allocate new display composition\n");
+ return -ENOMEM;
+ }
+
+ DrmConnector *c = drm_->GetConnectorFromType(i);
+ if (!c || c->state() != DRM_MODE_CONNECTED)
+ continue;
+ DrmCrtc *crtc = drm_->GetCrtcFromConnector(c);
+ if (!crtc)
+ continue;
+
+ int ret = composition_map_[i]->Init(drm_, crtc, importer_, planner_, frame_no);
+ if (ret) {
+ ALOGE("Failed to init display composition for %d", c->display());
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int DrmComposition::SetLayers(size_t num_displays,
+ DrmCompositionDisplayLayersMap *maps) {
+ int ret = 0;
+ for (size_t display_index = 0; display_index < num_displays;
+ display_index++) {
+ DrmCompositionDisplayLayersMap &map = maps[display_index];
+ int display = map.display;
+
+ if (!composition_map_[display]->crtc())
+ continue;
+
+ ret = composition_map_[display]->SetLayers(
+ map.layers.data(), map.layers.size(), map.geometry_changed);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int DrmComposition::SetMode3D(int display, Mode3D mode) {
+ return composition_map_[display]->SetMode3D(mode);
+}
+
+int DrmComposition::SetDpmsMode(int display, uint32_t dpms_mode) {
+ return composition_map_[display]->SetDpmsMode(dpms_mode);
+}
+
+int DrmComposition::SetDisplayMode(int display, const DrmMode &display_mode) {
+ return composition_map_[display]->SetDisplayMode(display_mode);
+}
+
+int DrmComposition::SetCompPlanes(int display, std::vector<DrmCompositionPlane>& composition_planes) {
+ return composition_map_[display]->SetCompPlanes(composition_planes);
+}
+
+std::unique_ptr<DrmDisplayComposition> DrmComposition::TakeDisplayComposition(
+ int display) {
+ return std::move(composition_map_[display]);
+}
+
+int DrmComposition::Plan(std::map<int, DrmDisplayCompositor> &compositor_map) {
+ int i, ret = 0;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ if (!composition_map_[i]->crtc())
+ continue;
+ DrmDisplayComposition *comp = GetDisplayComposition(i);
+ ret = comp->Plan(compositor_map[i].squash_state(), &primary_planes_,
+ &overlay_planes_);
+ if (ret) {
+ ALOGE("Failed to plan composition for dislay %d", i);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int DrmComposition::DisableUnusedPlanes() {
+std::vector<PlaneGroup *>& plane_groups = drm_->GetPlaneGroups();
+
+ int i;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmCrtc *crtc = composition_map_[i]->crtc();
+ if (!crtc)
+
+ continue;
+
+ DrmDisplayComposition *comp = GetDisplayComposition(i);
+
+ /*
+ * Leave empty compositions alone
+ * TODO: re-visit this and potentially disable leftover planes after the
+ * active compositions have gobbled up all they can
+ */
+ if (comp->type() == DRM_COMPOSITION_TYPE_EMPTY ||
+ comp->type() == DRM_COMPOSITION_TYPE_MODESET)
+ continue;
+
+ //loop plane groups.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ //loop plane
+ for(std::vector<DrmPlane*> ::const_iterator iter_plane=(*iter)->planes.begin();
+ !(*iter)->planes.empty() && iter_plane != (*iter)->planes.end(); ++iter_plane) {
+ if ((*iter_plane)->GetCrtcSupported(*crtc) && !(*iter_plane)->is_use()) {
+ ALOGD_IF(log_level(DBG_DEBUG),"DisableUnusedPlanes plane_groups plane id=%d",(*iter_plane)->id());
+ comp->AddPlaneDisable(*iter_plane);
+ // break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+DrmDisplayComposition *DrmComposition::GetDisplayComposition(int display) {
+ return composition_map_[display].get();
+}
+}
diff --git a/drmcomposition.h b/drmcomposition.h
new file mode 100644
index 0000000..108d271
--- /dev/null
+++ b/drmcomposition.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITION_H_
+#define ANDROID_DRM_COMPOSITION_H_
+
+#include "drmhwcomposer.h"
+#include "drmdisplaycomposition.h"
+#include "drmplane.h"
+#include "platform.h"
+
+#include <map>
+#include <vector>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+class DrmDisplayCompositor;
+
+struct DrmCompositionDisplayLayersMap {
+ int display;
+ bool geometry_changed = true;
+ std::vector<DrmHwcLayer> layers;
+
+ DrmCompositionDisplayLayersMap() = default;
+ DrmCompositionDisplayLayersMap(DrmCompositionDisplayLayersMap &&rhs) =
+ default;
+};
+
+struct DrmCompositionDisplayPlane {
+ int display;
+ std::vector<DrmCompositionPlane> composition_planes;
+
+ DrmCompositionDisplayPlane() = default;
+ DrmCompositionDisplayPlane(DrmCompositionDisplayPlane &&rhs) =
+ default;
+};
+
+class DrmComposition {
+ public:
+ DrmComposition(DrmResources *drm, Importer *importer, Planner *planner);
+
+ int Init(uint64_t frame_no);
+
+ int SetLayers(size_t num_displays, DrmCompositionDisplayLayersMap *maps);
+ int SetMode3D(int display, Mode3D mode);
+ int SetDpmsMode(int display, uint32_t dpms_mode);
+ int SetDisplayMode(int display, const DrmMode &display_mode);
+ int SetCompPlanes(int display, std::vector<DrmCompositionPlane>& composition_planes);
+
+ std::unique_ptr<DrmDisplayComposition> TakeDisplayComposition(int display);
+ DrmDisplayComposition *GetDisplayComposition(int display);
+
+ int Plan(std::map<int, DrmDisplayCompositor> &compositor_map);
+ int DisableUnusedPlanes();
+
+ private:
+ DrmComposition(const DrmComposition &) = delete;
+
+ DrmResources *drm_;
+ Importer *importer_;
+ Planner *planner_;
+
+ std::vector<DrmPlane *> primary_planes_;
+ std::vector<DrmPlane *> overlay_planes_;
+ std::vector<DrmCompositionDisplayPlane> comp_plane_group;
+
+ /*
+ * This _must_ be read-only after it's passed to QueueComposition. Otherwise
+ * locking is required to maintain consistency across the compositor threads.
+ */
+ std::map<int, std::unique_ptr<DrmDisplayComposition>> composition_map_;
+};
+}
+
+#endif // ANDROID_DRM_COMPOSITION_H_
diff --git a/drmcompositor.cpp b/drmcompositor.cpp
new file mode 100644
index 0000000..148b166
--- /dev/null
+++ b/drmcompositor.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-compositor"
+
+#include "drmcompositor.h"
+#include "drmdisplaycompositor.h"
+#include "drmresources.h"
+#include "platform.h"
+
+#include <sstream>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+DrmCompositor::DrmCompositor(DrmResources *drm) : drm_(drm), frame_no_(0) {
+}
+
+DrmCompositor::~DrmCompositor() {
+}
+
+int DrmCompositor::Init() {
+ int i;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ int ret = compositor_map_[i].Init(drm_, i);
+ if (ret) {
+ ALOGE("Failed to initialize display compositor for %d", i);
+ return ret;
+ }
+ }
+ planner_ = Planner::CreateInstance(drm_);
+ if (!planner_) {
+ ALOGE("Failed to create planner instance for composition");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+std::unique_ptr<DrmComposition> DrmCompositor::CreateComposition(
+ Importer *importer) {
+ std::unique_ptr<DrmComposition> composition(
+ new DrmComposition(drm_, importer, planner_.get()));
+ int ret = composition->Init(++frame_no_);
+ if (ret) {
+ ALOGE("Failed to initialize drm composition %d", ret);
+ return nullptr;
+ }
+ return composition;
+}
+
+int DrmCompositor::QueueComposition(
+ std::unique_ptr<DrmComposition> composition) {
+ int i, ret;
+
+ ret = composition->Plan(compositor_map_);
+ if (ret)
+ return ret;
+
+ ret = composition->DisableUnusedPlanes();
+ if (ret)
+ return ret;
+
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ int ret = compositor_map_[i].QueueComposition(
+ composition->TakeDisplayComposition(i));
+ if (ret) {
+ ALOGV("Failed to queue composition for display %d (%d)", i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int DrmCompositor::Composite() {
+ /*
+ * This shouldn't be called, we should be calling Composite() on the display
+ * compositors directly.
+ */
+ ALOGE("Calling base drm compositor Composite() function");
+ return -EINVAL;
+}
+
+void DrmCompositor::ClearDisplay(int display) {
+ compositor_map_[display].ClearDisplay();
+}
+
+void DrmCompositor::Dump(std::ostringstream *out) const {
+ *out << "DrmCompositor stats:\n";
+ int i;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ compositor_map_[i].Dump(out);
+ }
+}
+}
diff --git a/drmcompositor.h b/drmcompositor.h
new file mode 100644
index 0000000..fffff75
--- /dev/null
+++ b/drmcompositor.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITOR_H_
+#define ANDROID_DRM_COMPOSITOR_H_
+
+#include "drmcomposition.h"
+#include "drmdisplaycompositor.h"
+#include "platform.h"
+
+#include <map>
+#include <memory>
+#include <sstream>
+
+namespace android {
+
+class DrmCompositor {
+ public:
+ DrmCompositor(DrmResources *drm);
+ ~DrmCompositor();
+
+ int Init();
+
+ std::unique_ptr<DrmComposition> CreateComposition(Importer *importer);
+
+ int QueueComposition(std::unique_ptr<DrmComposition> composition);
+ int Composite();
+ void ClearDisplay(int display);
+ void Dump(std::ostringstream *out) const;
+
+ private:
+ DrmCompositor(const DrmCompositor &) = delete;
+
+ DrmResources *drm_;
+ std::unique_ptr<Planner> planner_;
+
+ uint64_t frame_no_;
+
+ // mutable for Dump() propagation
+ mutable std::map<int, DrmDisplayCompositor> compositor_map_;
+};
+}
+
+#endif // ANDROID_DRM_COMPOSITOR_H_
diff --git a/drmcompositorworker.cpp b/drmcompositorworker.cpp
new file mode 100755
index 0000000..d5c572a
--- /dev/null
+++ b/drmcompositorworker.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-compositor-worker"
+
+#include "drmdisplaycompositor.h"
+#include "drmcompositorworker.h"
+#include "worker.h"
+
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+
+namespace android {
+
+static const int64_t kSquashWait = 500000000LL;
+
+DrmCompositorWorker::DrmCompositorWorker(DrmDisplayCompositor *compositor)
+ : Worker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+ compositor_(compositor) {
+}
+
+DrmCompositorWorker::~DrmCompositorWorker() {
+}
+
+int DrmCompositorWorker::Init() {
+ return InitWorker();
+}
+
+void DrmCompositorWorker::Routine() {
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------DrmCompositorWorker Routine start----------------------------");
+
+ int ret;
+ if (!compositor_->HaveQueuedComposites()) {
+ ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock worker, %d", ret);
+ return;
+ }
+
+ // Only use a timeout if we didn't do a SquashAll last time. This will
+ // prevent wait_ret == -ETIMEDOUT which would trigger a SquashAll and be a
+ // pointless drain on resources.
+ int wait_ret = did_squash_all_ ? WaitForSignalOrExitLocked()
+ : WaitForSignalOrExitLocked(kSquashWait);
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock worker, %d", ret);
+ return;
+ }
+
+ switch (wait_ret) {
+ case 0:
+ break;
+ case -EINTR:
+ return;
+//close pre-comp for static screen.
+ case -ETIMEDOUT:
+#if 0
+ ret = compositor_->SquashAll();
+ if (ret)
+ ALOGD_IF(log_level(DBG_DEBUG),"Failed to squash all %d", ret);
+ did_squash_all_ = true;
+#endif
+ return;
+ default:
+ ALOGE("Failed to wait for signal, %d", wait_ret);
+ return;
+ }
+ }
+
+ ret = compositor_->Composite();
+ if (ret)
+ ALOGE("Failed to composite! %d", ret);
+ did_squash_all_ = false;
+
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------DrmCompositorWorker Routine end----------------------------");
+}
+}
diff --git a/drmcompositorworker.h b/drmcompositorworker.h
new file mode 100644
index 0000000..731bc65
--- /dev/null
+++ b/drmcompositorworker.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITOR_WORKER_H_
+#define ANDROID_DRM_COMPOSITOR_WORKER_H_
+
+#include "worker.h"
+
+namespace android {
+
+class DrmDisplayCompositor;
+
+class DrmCompositorWorker : public Worker {
+ public:
+ DrmCompositorWorker(DrmDisplayCompositor *compositor);
+ ~DrmCompositorWorker() override;
+
+ int Init();
+
+ protected:
+ void Routine() override;
+
+ DrmDisplayCompositor *compositor_;
+ bool did_squash_all_ = false;
+};
+}
+
+#endif
diff --git a/drmconnector.cpp b/drmconnector.cpp
new file mode 100755
index 0000000..76901f7
--- /dev/null
+++ b/drmconnector.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-connector"
+
+#include "drmconnector.h"
+#include "drmresources.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <cutils/log.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmConnector::DrmConnector(DrmResources *drm, drmModeConnectorPtr c,
+ DrmEncoder *current_encoder,
+ std::vector<DrmEncoder *> &possible_encoders)
+ : drm_(drm),
+ id_(c->connector_id),
+ encoder_(current_encoder),
+ display_(-1),
+ type_(c->connector_type),
+ state_(c->connection),
+ force_disconnect_(false),
+ mm_width_(c->mmWidth),
+ mm_height_(c->mmHeight),
+ possible_encoders_(possible_encoders),
+ connector_(c) {
+}
+
+int DrmConnector::Init() {
+ int ret = drm_->GetConnectorProperty(*this, "DPMS", &dpms_property_);
+ if (ret) {
+ ALOGE("Could not get DPMS property\n");
+ return ret;
+ }
+ ret = drm_->GetConnectorProperty(*this, "CRTC_ID", &crtc_id_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_ID property\n");
+ return ret;
+ }
+ return 0;
+}
+
+uint32_t DrmConnector::id() const {
+ return id_;
+}
+
+int DrmConnector::display() const {
+ return display_;
+}
+
+void DrmConnector::set_display(int display) {
+ display_ = display;
+}
+
+bool DrmConnector::built_in() const {
+ return type_ == DRM_MODE_CONNECTOR_LVDS || type_ == DRM_MODE_CONNECTOR_eDP ||
+ type_ == DRM_MODE_CONNECTOR_DSI || type_ == DRM_MODE_CONNECTOR_VIRTUAL;
+}
+
+const DrmMode &DrmConnector::best_mode() const {
+ return best_mode_;
+}
+
+int DrmConnector::UpdateModes() {
+ int fd = drm_->fd();
+
+ drmModeConnectorPtr c = drmModeGetConnector(fd, id_);
+ if (!c) {
+ ALOGE("Failed to get connector %d", id_);
+ return -ENODEV;
+ }
+
+ state_ = c->connection;
+ if (!c->count_modes)
+ state_ = DRM_MODE_DISCONNECTED;
+
+ std::vector<DrmMode> new_modes;
+ for (int i = 0; i < c->count_modes; ++i) {
+ bool exists = false;
+ for (const DrmMode &mode : modes_) {
+ if (mode == c->modes[i]) {
+ new_modes.push_back(mode);
+ exists = true;
+ break;
+ }
+ }
+ if (exists)
+ continue;
+
+ DrmMode m(&c->modes[i]);
+ m.set_id(drm_->next_mode_id());
+ new_modes.push_back(m);
+ }
+ modes_.swap(new_modes);
+ return 0;
+}
+
+void DrmConnector::update_size(int w, int h) {
+ mm_width_ = w;
+ mm_height_ = h;
+}
+
+void DrmConnector::update_state(drmModeConnection state) {
+ state_ = state;
+}
+
+const DrmMode &DrmConnector::active_mode() const {
+ return active_mode_;
+}
+
+const DrmMode &DrmConnector::current_mode() const {
+ return current_mode_;
+}
+
+void DrmConnector::SetDpmsMode(uint32_t dpms_mode) {
+ int ret = drmModeConnectorSetProperty(drm_->fd(), id_, dpms_property_.id(), dpms_mode);
+ if (ret) {
+ ALOGE("Failed to set dpms mode %d %d", ret, dpms_mode);
+ return;
+ }
+}
+
+void DrmConnector::set_best_mode(const DrmMode &mode) {
+ best_mode_ = mode;
+}
+
+void DrmConnector::set_active_mode(const DrmMode &mode) {
+ active_mode_ = mode;
+}
+
+void DrmConnector::set_current_mode(const DrmMode &mode) {
+ current_mode_ = mode;
+}
+
+const DrmProperty &DrmConnector::dpms_property() const {
+ return dpms_property_;
+}
+
+const DrmProperty &DrmConnector::crtc_id_property() const {
+ return crtc_id_property_;
+}
+
+DrmEncoder *DrmConnector::encoder() const {
+ return encoder_;
+}
+
+void DrmConnector::set_encoder(DrmEncoder *encoder) {
+ encoder_ = encoder;
+}
+
+void DrmConnector::force_disconnect(bool force) {
+ force_disconnect_ = force;
+}
+
+drmModeConnection DrmConnector::state() const {
+ if (force_disconnect_)
+ return DRM_MODE_DISCONNECTED;
+ return state_;
+}
+
+uint32_t DrmConnector::mm_width() const {
+ return mm_width_;
+}
+
+uint32_t DrmConnector::mm_height() const {
+ return mm_height_;
+}
+
+void DrmConnector::dump_connector(std::ostringstream *out) const {
+ int j;
+
+ *out << connector_->connector_id << "\t"
+ << connector_->encoder_id << "\t"
+ << drm_->connector_status_str(connector_->connection) << "\t"
+ << drm_->connector_type_str(connector_->connector_type) << "\t"
+ << connector_->mmWidth << "\t"
+ << connector_->mmHeight << "\t"
+ << connector_->count_modes << "\t";
+
+ for (j = 0; j < connector_->count_encoders; j++) {
+ if(j > 0) {
+ *out << ", ";
+ } else {
+ *out << "";
+ }
+ *out << connector_->encoders[j];
+ }
+ *out << "\n";
+
+ if (connector_->count_modes) {
+ *out << " modes:\n";
+ *out << "\tname refresh (Hz) hdisp hss hse htot vdisp "
+ "vss vse vtot)\n";
+ for (j = 0; j < connector_->count_modes; j++)
+ drm_->dump_mode(&connector_->modes[j],out);
+ }
+
+ drm_->DumpConnectorProperty(*this,out);
+}
+
+}
diff --git a/drmconnector.h b/drmconnector.h
new file mode 100755
index 0000000..4a5e3f7
--- /dev/null
+++ b/drmconnector.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_CONNECTOR_H_
+#define ANDROID_DRM_CONNECTOR_H_
+
+#include "drmencoder.h"
+#include "drmmode.h"
+#include "drmproperty.h"
+
+#include <stdint.h>
+#include <vector>
+#include <xf86drmMode.h>
+
+namespace android {
+
+class DrmResources;
+
+class DrmConnector {
+ public:
+ DrmConnector(DrmResources *drm, drmModeConnectorPtr c,
+ DrmEncoder *current_encoder,
+ std::vector<DrmEncoder *> &possible_encoders);
+ DrmConnector(const DrmProperty &) = delete;
+ DrmConnector &operator=(const DrmProperty &) = delete;
+
+ int Init();
+
+ uint32_t id() const;
+
+ int display() const;
+ void set_display(int display);
+
+ bool built_in() const;
+
+ int UpdateModes();
+
+ const std::vector<DrmMode> &modes() const {
+ return modes_;
+ }
+ const DrmMode &best_mode() const;
+ const DrmMode &active_mode() const;
+ const DrmMode &current_mode() const;
+ void set_best_mode(const DrmMode &mode);
+ void set_active_mode(const DrmMode &mode);
+ void set_current_mode(const DrmMode &mode);
+ void SetDpmsMode(uint32_t dpms_mode);
+
+ const DrmProperty &dpms_property() const;
+ const DrmProperty &crtc_id_property() const;
+
+ const std::vector<DrmEncoder *> &possible_encoders() const {
+ return possible_encoders_;
+ }
+ DrmEncoder *encoder() const;
+ void set_encoder(DrmEncoder *encoder);
+
+ drmModeConnection state() const;
+ void force_disconnect(bool force);
+
+ uint32_t get_type() { return type_; }
+
+ uint32_t mm_width() const;
+ uint32_t mm_height() const;
+ drmModeConnectorPtr get_connector() { return connector_; }
+ void update_state(drmModeConnection state);
+ void update_size(int w, int h);
+ void dump_connector(std::ostringstream *out) const;
+
+
+ private:
+ DrmResources *drm_;
+
+ uint32_t id_;
+ DrmEncoder *encoder_;
+ int display_;
+
+ uint32_t type_;
+ drmModeConnection state_;
+ bool force_disconnect_;
+
+ uint32_t mm_width_;
+ uint32_t mm_height_;
+
+ DrmMode active_mode_;
+ DrmMode current_mode_;
+ DrmMode best_mode_;
+ std::vector<DrmMode> modes_;
+
+ DrmProperty dpms_property_;
+ DrmProperty crtc_id_property_;
+
+ std::vector<DrmEncoder *> possible_encoders_;
+
+ drmModeConnectorPtr connector_;
+};
+}
+
+#endif // ANDROID_DRM_PLANE_H_
diff --git a/drmcrtc.cpp b/drmcrtc.cpp
new file mode 100755
index 0000000..215e328
--- /dev/null
+++ b/drmcrtc.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-crtc"
+
+#include "drmcrtc.h"
+#include "drmresources.h"
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+DrmCrtc::DrmCrtc(DrmResources *drm, drmModeCrtcPtr c, unsigned pipe)
+ : drm_(drm),
+ id_(c->crtc_id),
+ pipe_(pipe),
+ display_(-1),
+ x_(c->x),
+ y_(c->y),
+ width_(c->width),
+ height_(c->height),
+ b_afbc_(false),
+ mode_(&c->mode),
+ mode_valid_(c->mode_valid),
+ crtc_(c) {
+}
+
+int DrmCrtc::Init() {
+ int ret = drm_->GetCrtcProperty(*this, "ACTIVE", &active_property_);
+ if (ret) {
+ ALOGE("Failed to get ACTIVE property");
+ return ret;
+ }
+
+ ret = drm_->GetCrtcProperty(*this, "MODE_ID", &mode_property_);
+ if (ret) {
+ ALOGE("Failed to get MODE_ID property");
+ return ret;
+ }
+
+ ret = drm_->GetCrtcProperty(*this, "FEATURE", &feature_property_);
+ if (ret)
+ ALOGE("Could not get FEATURE property");
+
+ uint64_t feature=0;
+ feature_property_.set_feature("afbdc");
+ feature_property_.value(&feature);
+ b_afbc_ = (feature ==1)?true:false;
+
+ can_overscan_ = true;
+ ret = drm_->GetCrtcProperty(*this, "left margin", &left_margin_property_);
+ if (ret) {
+ ALOGE("Failed to get left margin property");
+ can_overscan_ = false;
+ }
+ ret = drm_->GetCrtcProperty(*this, "right margin", &right_margin_property_);
+ if (ret) {
+ ALOGE("Failed to get right margin property");
+ can_overscan_ = false;
+ }
+ ret = drm_->GetCrtcProperty(*this, "top margin", &top_margin_property_);
+ if (ret) {
+ ALOGE("Failed to get top margin property");
+ can_overscan_ = false;
+ }
+ ret = drm_->GetCrtcProperty(*this, "bottom margin", &bottom_margin_property_);
+ if (ret) {
+ ALOGE("Failed to get bottom margin property");
+ can_overscan_ = false;
+ }
+
+ return 0;
+}
+
+bool DrmCrtc::get_afbc() const {
+ return b_afbc_;
+}
+
+uint32_t DrmCrtc::id() const {
+ return id_;
+}
+
+unsigned DrmCrtc::pipe() const {
+ return pipe_;
+}
+
+bool DrmCrtc::can_overscan() const {
+ return can_overscan_;
+}
+
+const DrmProperty &DrmCrtc::active_property() const {
+ return active_property_;
+}
+
+const DrmProperty &DrmCrtc::mode_property() const {
+ return mode_property_;
+}
+
+const DrmProperty &DrmCrtc::left_margin_property() const {
+ return left_margin_property_;
+}
+
+const DrmProperty &DrmCrtc::right_margin_property() const {
+ return right_margin_property_;
+}
+
+const DrmProperty &DrmCrtc::top_margin_property() const {
+ return top_margin_property_;
+}
+
+const DrmProperty &DrmCrtc::bottom_margin_property() const {
+ return bottom_margin_property_;
+}
+
+void DrmCrtc::dump_crtc(std::ostringstream *out) const
+{
+ uint32_t j;
+
+ *out << crtc_->crtc_id << "\t"
+ << crtc_->buffer_id << "\t"
+ << "(" << crtc_->x << "," << crtc_->y << ")\t("
+ << crtc_->width << "x" << crtc_->height << ")\n";
+
+ drm_->dump_mode(&crtc_->mode, out);
+
+ drm_->DumpCrtcProperty(*this,out);
+}
+
+}
diff --git a/drmcrtc.h b/drmcrtc.h
new file mode 100755
index 0000000..fab60d7
--- /dev/null
+++ b/drmcrtc.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_CRTC_H_
+#define ANDROID_DRM_CRTC_H_
+
+#include "drmmode.h"
+#include "drmproperty.h"
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+class DrmResources;
+
+class DrmCrtc {
+ public:
+ DrmCrtc(DrmResources *drm, drmModeCrtcPtr c, unsigned pipe);
+ DrmCrtc(const DrmCrtc &) = delete;
+ DrmCrtc &operator=(const DrmCrtc &) = delete;
+
+ int Init();
+
+ uint32_t id() const;
+ unsigned pipe() const;
+
+ bool can_bind(int display) const;
+ bool can_overscan() const;
+
+ const DrmProperty &active_property() const;
+ const DrmProperty &mode_property() const;
+ bool get_afbc() const;
+ const DrmProperty &left_margin_property() const;
+ const DrmProperty &right_margin_property() const;
+ const DrmProperty &top_margin_property() const;
+ const DrmProperty &bottom_margin_property() const;
+ void dump_crtc(std::ostringstream *out) const;
+
+
+ DrmResources *getDrmReoources()
+ {
+ return drm_;
+ }
+
+ private:
+ DrmResources *drm_;
+
+ uint32_t id_;
+ unsigned pipe_;
+ int display_;
+
+ uint32_t x_;
+ uint32_t y_;
+ uint32_t width_;
+ uint32_t height_;
+ bool b_afbc_;
+
+ DrmMode mode_;
+ bool mode_valid_;
+ bool can_overscan_;
+
+ DrmProperty active_property_;
+ DrmProperty mode_property_;
+ DrmProperty feature_property_;
+ DrmProperty left_margin_property_;
+ DrmProperty top_margin_property_;
+ DrmProperty right_margin_property_;
+ DrmProperty bottom_margin_property_;
+ drmModeCrtcPtr crtc_;
+};
+}
+
+#endif // ANDROID_DRM_CRTC_H_
diff --git a/drmdisplaycomposition.cpp b/drmdisplaycomposition.cpp
new file mode 100755
index 0000000..3e5df5d
--- /dev/null
+++ b/drmdisplaycomposition.cpp
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-display-composition"
+
+#include "drmdisplaycomposition.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+#include "platform.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <unordered_set>
+
+#include <cutils/log.h>
+#include <sw_sync.h>
+#include <sync/sync.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmDisplayComposition::~DrmDisplayComposition() {
+ if (timeline_fd_ >= 0) {
+ SignalCompositionDone();
+ close(timeline_fd_);
+ }
+}
+
+int DrmDisplayComposition::Init(DrmResources *drm, DrmCrtc *crtc,
+ Importer *importer, Planner *planner,
+ uint64_t frame_no) {
+ drm_ = drm;
+ crtc_ = crtc; // Can be NULL if we haven't modeset yet
+ importer_ = importer;
+ planner_ = planner;
+ frame_no_ = frame_no;
+
+ int ret = sw_sync_timeline_create();
+ if (ret < 0) {
+ ALOGE("Failed to create sw sync timeline %d", ret);
+ return ret;
+ }
+ timeline_fd_ = ret;
+ mode_3d_ = NON_3D;
+ return 0;
+}
+
+bool DrmDisplayComposition::validate_composition_type(DrmCompositionType des) {
+ return type_ == DRM_COMPOSITION_TYPE_EMPTY || type_ == des;
+}
+
+int DrmDisplayComposition::CreateNextTimelineFence(const char* fence_name) {
+ ++timeline_;
+ return sw_sync_fence_create(timeline_fd_, fence_name,
+ timeline_);
+}
+
+int DrmDisplayComposition::IncreaseTimelineToPoint(int point) {
+ int timeline_increase = point - timeline_current_;
+ if (timeline_increase <= 0)
+ return 0;
+
+ int ret = sw_sync_timeline_inc(timeline_fd_, timeline_increase);
+ if (ret)
+ ALOGE("Failed to increment sync timeline %d", ret);
+ else
+ timeline_current_ = point;
+
+ return ret;
+}
+
+int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers,
+ bool geometry_changed) {
+ if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
+ return -EINVAL;
+
+ geometry_changed_ = geometry_changed;
+
+ for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
+ layers_.emplace_back(std::move(layers[layer_index]));
+ }
+
+ type_ = DRM_COMPOSITION_TYPE_FRAME;
+ return 0;
+}
+
+int DrmDisplayComposition::SetMode3D(Mode3D mode) {
+ mode_3d_ = mode;
+ return 0;
+}
+
+int DrmDisplayComposition::SetDpmsMode(uint32_t dpms_mode) {
+ if (!validate_composition_type(DRM_COMPOSITION_TYPE_DPMS))
+ return -EINVAL;
+ dpms_mode_ = dpms_mode;
+ type_ = DRM_COMPOSITION_TYPE_DPMS;
+ return 0;
+}
+
+int DrmDisplayComposition::SetDisplayMode(const DrmMode &display_mode) {
+ if (!validate_composition_type(DRM_COMPOSITION_TYPE_MODESET))
+ return -EINVAL;
+ display_mode_ = display_mode;
+ dpms_mode_ = DRM_MODE_DPMS_ON;
+ type_ = DRM_COMPOSITION_TYPE_MODESET;
+ return 0;
+}
+
+int DrmDisplayComposition::SetCompPlanes(std::vector<DrmCompositionPlane>& composition_planes) {
+ for(size_t i = 0; i < composition_planes.size(); i++)
+ {
+ composition_planes_.emplace_back(std::move(composition_planes[i]));
+ }
+ return 0;
+}
+
+int DrmDisplayComposition::AddPlaneDisable(DrmPlane *plane) {
+ composition_planes_.emplace_back(DrmCompositionPlane::Type::kDisable, plane,
+ crtc_);
+ return 0;
+}
+
+static std::vector<size_t> SetBitsToVector(
+ uint64_t in, const std::vector<size_t> &index_map) {
+ std::vector<size_t> out;
+ size_t msb = sizeof(in) * 8 - 1;
+ uint64_t mask = (uint64_t)1 << msb;
+ for (size_t i = msb; mask != (uint64_t)0; i--, mask >>= 1)
+ if (in & mask)
+ out.push_back(index_map[i]);
+ return out;
+}
+
+int DrmDisplayComposition::AddPlaneComposition(DrmCompositionPlane plane) {
+ composition_planes_.emplace_back(std::move(plane));
+ return 0;
+}
+
+void DrmDisplayComposition::SeparateLayers(DrmHwcRect<int> *exclude_rects,
+ size_t num_exclude_rects) {
+ DrmCompositionPlane *comp = NULL;
+ std::vector<size_t> dedicated_layers;
+#if RK_SKIP_SUB
+ bool bSkipSub = false;
+#endif
+ // Go through the composition and find the precomp layer as well as any
+ // layers that have a dedicated plane located below the precomp layer.
+ for (auto &i : composition_planes_) {
+ if (i.type() == DrmCompositionPlane::Type::kLayer) {
+ dedicated_layers.insert(dedicated_layers.end(), i.source_layers().begin(),
+ i.source_layers().end());
+ } else if (i.type() == DrmCompositionPlane::Type::kPrecomp) {
+ comp = &i;
+ break;
+ }
+ }
+ if (!comp)
+ return;
+
+ const std::vector<size_t> &comp_layers = comp->source_layers();
+ if (comp_layers.size() > 64) {
+ ALOGE("Failed to separate layers because there are more than 64");
+ return;
+ }
+
+#if RK_SKIP_SUB
+ if(comp_layers.size() > 0) {
+ DrmHwcLayer &comp_layer = layers_[comp_layers.front()];
+ if(!strcmp(comp_layer.name.c_str(),"SurfaceView")) {
+ if(dedicated_layers.size() > 0) {
+ DrmHwcLayer &dedicated_layer = layers_[dedicated_layers.front()];
+ if(!strcmp(dedicated_layer.name.c_str(),
+ "android.rk.RockVideoPlayer/android.rk.RockVideoPlayer.VideoP")) {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s skip subtract for video case", __FUNCTION__);
+ bSkipSub = true;
+ }
+ }
+ }
+ }
+#endif
+
+ // Index at which the actual layers begin
+ size_t layer_offset = num_exclude_rects + dedicated_layers.size();
+ if (comp_layers.size() + layer_offset > 64) {
+ ALOGW(
+ "Exclusion rectangles are being truncated to make the rectangle count "
+ "fit into 64");
+ num_exclude_rects = 64 - comp_layers.size() - dedicated_layers.size();
+ }
+
+ // We inject all the exclude rects into the rects list. Any resulting rect
+ // that includes ANY of the first num_exclude_rects is rejected. After the
+ // exclude rects, we add the lower layers. The rects that intersect with
+ // these layers will be inspected and only those which are to be composited
+ // above the layer will be included in the composition regions.
+ std::vector<DrmHwcRect<int>> layer_rects(comp_layers.size() + layer_offset);
+ std::copy(exclude_rects, exclude_rects + num_exclude_rects,
+ layer_rects.begin());
+ std::transform(
+ dedicated_layers.begin(), dedicated_layers.end(),
+ layer_rects.begin() + num_exclude_rects,
+ [=](size_t layer_index) { return layers_[layer_index].display_frame; });
+ std::transform(comp_layers.begin(), comp_layers.end(),
+ layer_rects.begin() + layer_offset, [=](size_t layer_index) {
+ return layers_[layer_index].display_frame;
+ });
+
+ std::vector<separate_rects::RectSet<uint64_t, int>> separate_regions;
+ separate_rects::separate_rects_64(layer_rects, &separate_regions);
+ uint64_t exclude_mask = ((uint64_t)1 << num_exclude_rects) - 1;
+ uint64_t dedicated_mask = (((uint64_t)1 << dedicated_layers.size()) - 1)
+ << num_exclude_rects;
+ for (separate_rects::RectSet<uint64_t, int> &region : separate_regions) {
+ if (region.id_set.getBits() & exclude_mask)
+ {
+ continue;
+ }
+
+ // If a rect intersects one of the dedicated layers, we need to remove the
+ // layers from the composition region which appear *below* the dedicated
+ // layer. This effectively punches a hole through the composition layer such
+ // that the dedicated layer can be placed below the composition and not
+ // be occluded.
+ uint64_t dedicated_intersect = region.id_set.getBits() & dedicated_mask;
+ for (size_t i = 0; dedicated_intersect && i < dedicated_layers.size();
+ ++i) {
+ // Only exclude layers if they intersect this particular dedicated layer
+ if (!(dedicated_intersect & (1 << (i + num_exclude_rects))))
+ continue;
+#if RK_SKIP_SUB
+ if(!bSkipSub)
+#endif
+ {
+ for (size_t j = 0; j < comp_layers.size(); ++j) {
+ if (comp_layers[j] < dedicated_layers[i])
+ region.id_set.subtract(j + layer_offset);
+ }
+ }
+ }
+ if (!(region.id_set.getBits() >> layer_offset))
+ continue;
+
+ pre_comp_regions_.emplace_back(DrmCompositionRegion{
+ region.rect,
+ SetBitsToVector(region.id_set.getBits() >> layer_offset, comp_layers)});
+ }
+}
+
+void DrmCompositionPlane::dump_drm_com_plane(int index, std::ostringstream *out) const {
+ *out << "DrmCompositionPlane[" << index << "]"
+ << " plane=" << (plane_ ? plane_->id() : -1)
+ << " type=";
+ switch (type_) {
+ case DrmCompositionPlane::Type::kDisable:
+ *out << "DISABLE";
+ break;
+ case DrmCompositionPlane::Type::kLayer:
+ *out << "LAYER";
+ break;
+ case DrmCompositionPlane::Type::kPrecomp:
+ *out << "PRECOMP";
+ break;
+ case DrmCompositionPlane::Type::kSquash:
+ *out << "SQUASH";
+ break;
+ default:
+ *out << "<invalid>";
+ break;
+ }
+
+ *out << "\n";
+}
+
+int DrmDisplayComposition::CreateAndAssignReleaseFences() {
+ std::unordered_set<DrmHwcLayer *> squash_layers;
+ std::unordered_set<DrmHwcLayer *> pre_comp_layers;
+ std::unordered_set<DrmHwcLayer *> comp_layers;
+
+
+ for (const DrmCompositionRegion &region : squash_regions_) {
+ for (size_t source_layer_index : region.source_layers) {
+ DrmHwcLayer *source_layer = &layers_[source_layer_index];
+ squash_layers.emplace(source_layer);
+ }
+ }
+
+ for (const DrmCompositionRegion &region : pre_comp_regions_) {
+ for (size_t source_layer_index : region.source_layers) {
+ DrmHwcLayer *source_layer = &layers_[source_layer_index];
+ pre_comp_layers.emplace(source_layer);
+ squash_layers.erase(source_layer);
+ }
+ }
+
+ for (const DrmCompositionPlane &plane : composition_planes_) {
+ if (plane.type() == DrmCompositionPlane::Type::kLayer) {
+ for (auto i : plane.source_layers()) {
+ DrmHwcLayer *source_layer = &layers_[i];
+ comp_layers.emplace(source_layer);
+ pre_comp_layers.erase(source_layer);
+ }
+ }
+ }
+
+ for (DrmHwcLayer *layer : squash_layers) {
+ if (!layer->release_fence)
+ continue;
+ int ret = layer->release_fence.Set(CreateNextTimelineFence("squash_layers"));
+ if (ret < 0)
+ return ret;
+ }
+ timeline_squash_done_ = timeline_;
+
+ for (DrmHwcLayer *layer : pre_comp_layers) {
+ if (!layer->release_fence)
+ continue;
+ int ret = layer->release_fence.Set(CreateNextTimelineFence("pre_comp_layers"));
+ if (ret < 0)
+ return ret;
+ }
+ timeline_pre_comp_done_ = timeline_;
+
+ char acBuf[50];
+ for (DrmHwcLayer *layer : comp_layers) {
+ if (!layer->release_fence)
+ {
+ continue;
+ }
+#if RK_VR
+ if(layer->release_fence.get() > -1 && (layer->gralloc_buffer_usage & 0x08000000))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),">>>close releaseFenceFd:%d,layername=%s",
+ layer->release_fence.get(),layer->name.c_str());
+ close(layer->release_fence.get());
+ layer->release_fence.Set(-1);
+ }
+ else
+#endif
+ {
+ sprintf(acBuf,"frame-%d",layer->frame_no);
+ int ret = layer->release_fence.Set(CreateNextTimelineFence(acBuf));
+ if (ret < 0)
+ {
+ ALOGE("creat release fence failed ret=%d,%s",ret,strerror(errno));
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static bool is_rec1_intersect_rec2(DrmHwcRect<int>* rec1,DrmHwcRect<int>* rec2)
+{
+ int iMaxLeft,iMaxTop,iMinRight,iMinBottom;
+ ALOGD_IF(log_level(DBG_DEBUG),"is_not_intersect: rec1[%d,%d,%d,%d],rec2[%d,%d,%d,%d]",rec1->left,rec1->top,
+ rec1->right,rec1->bottom,rec2->left,rec2->top,rec2->right,rec2->bottom);
+
+ iMaxLeft = rec1->left > rec2->left ? rec1->left: rec2->left;
+ iMaxTop = rec1->top > rec2->top ? rec1->top: rec2->top;
+ iMinRight = rec1->right <= rec2->right ? rec1->right: rec2->right;
+ iMinBottom = rec1->bottom <= rec2->bottom ? rec1->bottom: rec2->bottom;
+
+ if(iMaxLeft > iMinRight || iMaxTop > iMinBottom)
+ return false;
+ else
+ return true;
+
+ return false;
+}
+
+static bool is_layer_combine(DrmHwcLayer * layer_one,DrmHwcLayer * layer_two)
+{
+ //Don't care format.
+ if(/*layer_one->format != layer_two->format
+ ||*/ layer_one->alpha != layer_two->alpha
+ || layer_one->is_scale || layer_two->is_scale
+ || is_rec1_intersect_rec2(&layer_one->display_frame,&layer_two->display_frame))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"is_layer_combine layer one alpha=%d,is_scale=%d",layer_one->alpha,layer_one->is_scale);
+ ALOGD_IF(log_level(DBG_DEBUG),"is_layer_combine layer two alpha=%d,is_scale=%d",layer_two->alpha,layer_two->is_scale);
+ return false;
+ }
+
+ return true;
+}
+
+static bool has_layer(std::vector<DrmHwcLayer*>& layer_vector,DrmHwcLayer &layer)
+{
+ for (std::vector<DrmHwcLayer*>::const_iterator iter = layer_vector.begin();
+ iter != layer_vector.end(); ++iter) {
+ if((*iter)->sf_handle==layer.sf_handle)
+ return true;
+ }
+
+ return false;
+}
+
+#define MOST_WIN_ZONES 4
+int DrmDisplayComposition::combine_layer()
+{
+ /*Group layer*/
+ int zpos = 0;
+ size_t i,j;
+ uint32_t sort_cnt=0;
+ bool is_combine = false;
+ size_t min_size = (MOST_WIN_ZONES<layers_.size())?MOST_WIN_ZONES:layers_.size();
+
+ layer_map_.clear();
+
+ for (i = 0; i < layers_.size(); ) {
+ sort_cnt=0;
+ if(i == 0)
+ {
+ layer_map_[zpos].push_back(&layers_[0]);
+ }
+
+ if(i == min_size)
+ {
+ //We can use pre-comp to optimise.
+ ALOGD_IF(log_level(DBG_DEBUG),"combine_layer fail: it remain layer i=%zu, min_size=%zu",i,min_size);
+ return -1;
+ }
+
+ for(j = i+1; j < min_size; j++) {
+ DrmHwcLayer &layer_one = layers_[j];
+ layer_one.index = j;
+ is_combine = false;
+ for(size_t k = 0; k <= sort_cnt; k++ ) {
+ DrmHwcLayer &layer_two = layers_[j-1-k];
+ layer_two.index = j-1-k;
+ //juage the layer is contained in layer_vector
+ bool bHasLayerOne = has_layer(layer_map_[zpos],layer_one);
+ bool bHasLayerTwo = has_layer(layer_map_[zpos],layer_two);
+
+ //If it contain both of layers,then don't need to go down.
+ if(bHasLayerOne && bHasLayerTwo)
+ continue;
+
+ if(is_layer_combine(&layer_one,&layer_two)) {
+ //append layer into layer_vector of layer_map_.
+ if(!bHasLayerOne && !bHasLayerTwo)
+ {
+ layer_map_[zpos].emplace_back(&layer_one);
+ layer_map_[zpos].emplace_back(&layer_two);
+ is_combine = true;
+ }
+ else if(!bHasLayerTwo)
+ {
+ is_combine = true;
+ for(std::vector<DrmHwcLayer*>::const_iterator iter= layer_map_[zpos].begin();
+ iter != layer_map_[zpos].end();++iter)
+ {
+ if((*iter)->sf_handle==layer_one.sf_handle)
+ continue;
+
+ if(!is_layer_combine(*iter,&layer_two))
+ {
+ is_combine = false;
+ break;
+ }
+ }
+
+ if(is_combine)
+ layer_map_[zpos].emplace_back(&layer_two);
+ }
+ else if(!bHasLayerOne)
+ {
+ is_combine = true;
+ for(std::vector<DrmHwcLayer*>::const_iterator iter= layer_map_[zpos].begin();
+ iter != layer_map_[zpos].end();++iter)
+ {
+ if((*iter)->sf_handle==layer_two.sf_handle)
+ continue;
+
+ if(!is_layer_combine(*iter,&layer_one))
+ {
+ is_combine = false;
+ break;
+ }
+ }
+
+ if(is_combine)
+ layer_map_[zpos].emplace_back(&layer_one);
+ }
+ }
+
+ if(!is_combine)
+ {
+ //if it cann't combine two layer,it need start a new group.
+ if(!bHasLayerOne)
+ {
+ zpos++;
+ layer_map_[zpos].emplace_back(&layer_one);
+ }
+ break;
+ }
+ }
+ sort_cnt++; //update sort layer count
+ if(!is_combine)
+ {
+ break;
+ }
+ }
+
+ if(is_combine) //all remain layer or limit MOST_WIN_ZONES layer is combine well,it need start a new group.
+ zpos++;
+ if(sort_cnt)
+ i+=sort_cnt; //jump the sort compare layers.
+ else
+ i++;
+ }
+
+ //sort layer by xpos
+ for (LayerMap::iterator iter = layer_map_.begin();
+ iter != layer_map_.end(); ++iter) {
+ if(iter->second.size() > 1) {
+ for(uint32_t i=0;i < iter->second.size()-1;i++) {
+ for(uint32_t j=i+1;j < iter->second.size();j++) {
+ if(iter->second[i]->display_frame.left > iter->second[j]->display_frame.left) {
+ ALOGD_IF(log_level(DBG_DEBUG),"swap %s and %s",iter->second[i]->name.c_str(),iter->second[j]->name.c_str());
+ std::swap(iter->second[i],iter->second[j]);
+ }
+ }
+ }
+ }
+ }
+
+ for (LayerMap::iterator iter = layer_map_.begin();
+ iter != layer_map_.end(); ++iter) {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer map id=%d,size=%zu",iter->first,iter->second.size());
+ for(std::vector<DrmHwcLayer*>::const_iterator iter_layer = iter->second.begin();
+ iter_layer != iter->second.end();++iter_layer)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"\tlayer name=%s",(*iter_layer)->name.c_str());
+ }
+ }
+
+ return 0;
+}
+
+int DrmDisplayComposition::Plan(SquashState *squash,
+ std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes) {
+ if (type_ != DRM_COMPOSITION_TYPE_FRAME)
+ return 0;
+
+ // Used to track which layers should be sent to the planner. We exclude layers
+ // that are entirely squashed so the planner can provision a precomposition
+ // layer as appropriate (ex: if 5 layers are squashed and 1 is not, we don't
+ // want to plan a precomposition layer that will be comprised of the already
+ // squashed layers).
+ std::map<size_t, DrmHwcLayer *> to_composite;
+ bool use_squash_framebuffer = false;
+
+ if (!crtc_) {
+ ALOGE("can't not plan when crtc is NULL\n");
+ return -EINVAL;
+ }
+
+#if USE_SQUASH
+ // Used to determine which layers were entirely squashed
+ std::vector<int> layer_squash_area(layers_.size(), 0);
+ // Used to avoid rerendering regions that were squashed
+ std::vector<DrmHwcRect<int>> exclude_rects;
+ if (squash != NULL) {
+ if (geometry_changed_) {
+ squash->Init(layers_.data(), layers_.size());
+ } else {
+ std::vector<bool> changed_regions;
+ squash->GenerateHistory(layers_.data(), layers_.size(), changed_regions);
+
+ std::vector<bool> stable_regions;
+ squash->StableRegionsWithMarginalHistory(changed_regions, stable_regions);
+
+ // Only if SOME region is stable
+ use_squash_framebuffer =
+ std::find(stable_regions.begin(), stable_regions.end(), true) !=
+ stable_regions.end();
+
+ squash->RecordHistory(layers_.data(), layers_.size(), changed_regions);
+
+ // Changes in which regions are squashed triggers a rerender via
+ // squash_regions.
+ bool render_squash = squash->RecordAndCompareSquashed(stable_regions);
+
+ for (size_t region_index = 0; region_index < stable_regions.size();
+ region_index++) {
+ const SquashState::Region &region = squash->regions()[region_index];
+ if (!stable_regions[region_index])
+ continue;
+
+ exclude_rects.emplace_back(region.rect);
+
+ if (render_squash) {
+ squash_regions_.emplace_back();
+ squash_regions_.back().frame = region.rect;
+ }
+
+ int frame_area = region.rect.area();
+ // Source layers are sorted front to back i.e. top layer has lowest
+ // index.
+ for (size_t layer_index = layers_.size();
+ layer_index-- > 0; // Yes, I double checked this
+ /* See condition */) {
+ if (!region.layer_refs[layer_index])
+ continue;
+ layer_squash_area[layer_index] += frame_area;
+ if (render_squash)
+ squash_regions_.back().source_layers.push_back(layer_index);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < layers_.size(); ++i) {
+ if (layer_squash_area[i] < layers_[i].display_frame.area())
+ {
+ to_composite.emplace(std::make_pair(i, &layers_[i]));
+ }
+ else
+ {
+ layers_[i].is_match = true;
+ ALOGD_IF(log_level(DBG_DEBUG),"add Squash layer[%zu] %s",i,layers_[i].name.c_str());
+ }
+ }
+ } else {
+ for (size_t i = 0; i < layers_.size(); ++i)
+ to_composite.emplace(std::make_pair(i, &layers_[i]));
+ }
+#else
+ UN_USED(squash);
+ for (size_t i = 0; i < layers_.size(); ++i)
+ to_composite.emplace(std::make_pair(i, &layers_[i]));
+#endif
+
+ //must set composition_planes_ before.
+ if(composition_planes_.size() <= 0)
+ {
+ ALOGE("composition_planes_ is null");
+ return -1;
+ }
+
+ // Remove the planes we used from the pool before returning. This ensures they
+ // won't be reused by another display in the composition.
+ for (auto &i : composition_planes_) {
+ if (!i.plane())
+ continue;
+
+ std::vector<DrmPlane *> *container;
+ if (i.plane()->type() == DRM_PLANE_TYPE_PRIMARY)
+ container = primary_planes;
+ else
+ container = overlay_planes;
+ for (auto j = container->begin(); j != container->end(); ++j) {
+ if (*j == i.plane()) {
+ container->erase(j);
+ break;
+ }
+ }
+ }
+
+ size_t j=0;
+ for (const DrmCompositionPlane &plane : composition_planes_) {
+ std::ostringstream out;
+ plane.dump_drm_com_plane(j,&out);
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ j++;
+ }
+
+#if USE_SQUASH
+ return FinalizeComposition(exclude_rects.data(), exclude_rects.size());
+#else
+ return FinalizeComposition();
+#endif
+}
+
+int DrmDisplayComposition::FinalizeComposition() {
+ return FinalizeComposition(NULL, 0);
+}
+
+int DrmDisplayComposition::FinalizeComposition(DrmHwcRect<int> *exclude_rects,
+ size_t num_exclude_rects) {
+ SeparateLayers(exclude_rects, num_exclude_rects);
+ return CreateAndAssignReleaseFences();
+}
+
+static const char *DrmCompositionTypeToString(DrmCompositionType type) {
+ switch (type) {
+ case DRM_COMPOSITION_TYPE_EMPTY:
+ return "EMPTY";
+ case DRM_COMPOSITION_TYPE_FRAME:
+ return "FRAME";
+ case DRM_COMPOSITION_TYPE_DPMS:
+ return "DPMS";
+ case DRM_COMPOSITION_TYPE_MODESET:
+ return "MODESET";
+ default:
+ return "<invalid>";
+ }
+}
+
+static const char *DPMSModeToString(int dpms_mode) {
+ switch (dpms_mode) {
+ case DRM_MODE_DPMS_ON:
+ return "ON";
+ case DRM_MODE_DPMS_OFF:
+ return "OFF";
+ default:
+ return "<invalid>";
+ }
+}
+
+static void DumpBuffer(const DrmHwcBuffer &buffer, std::ostringstream *out) {
+ if (!buffer) {
+ *out << "buffer=<invalid>";
+ return;
+ }
+
+ *out << "buffer[w/h/format]=";
+ *out << buffer->width << "/" << buffer->height << "/" << buffer->format;
+}
+
+static void DumpTransform(uint32_t transform, std::ostringstream *out) {
+ *out << "[";
+
+ if (transform == 0)
+ *out << "IDENTITY";
+
+ bool separator = false;
+ if (transform & DrmHwcTransform::kFlipH) {
+ *out << "FLIPH";
+ separator = true;
+ }
+ if (transform & DrmHwcTransform::kFlipV) {
+ if (separator)
+ *out << "|";
+ *out << "FLIPV";
+ separator = true;
+ }
+ if (transform & DrmHwcTransform::kRotate90) {
+ if (separator)
+ *out << "|";
+ *out << "ROTATE90";
+ separator = true;
+ }
+ if (transform & DrmHwcTransform::kRotate180) {
+ if (separator)
+ *out << "|";
+ *out << "ROTATE180";
+ separator = true;
+ }
+ if (transform & DrmHwcTransform::kRotate270) {
+ if (separator)
+ *out << "|";
+ *out << "ROTATE270";
+ separator = true;
+ }
+
+ uint32_t valid_bits = DrmHwcTransform::kFlipH | DrmHwcTransform::kFlipH |
+ DrmHwcTransform::kRotate90 |
+ DrmHwcTransform::kRotate180 |
+ DrmHwcTransform::kRotate270;
+ if (transform & ~valid_bits) {
+ if (separator)
+ *out << "|";
+ *out << "INVALID";
+ }
+ *out << "]";
+}
+
+static const char *BlendingToString(DrmHwcBlending blending) {
+ switch (blending) {
+ case DrmHwcBlending::kNone:
+ return "NONE";
+ case DrmHwcBlending::kPreMult:
+ return "PREMULT";
+ case DrmHwcBlending::kCoverage:
+ return "COVERAGE";
+ default:
+ return "<invalid>";
+ }
+}
+
+static void DumpRegion(const DrmCompositionRegion &region,
+ std::ostringstream *out) {
+ *out << "frame";
+ region.frame.Dump(out);
+ *out << " source_layers=(";
+
+ const std::vector<size_t> &source_layers = region.source_layers;
+ for (size_t i = 0; i < source_layers.size(); i++) {
+ *out << source_layers[i];
+ if (i < source_layers.size() - 1) {
+ *out << " ";
+ }
+ }
+
+ *out << ")";
+}
+
+void DrmDisplayComposition::Dump(std::ostringstream *out) const {
+ *out << "----DrmDisplayComposition"
+ << " crtc=" << (crtc_ ? crtc_->id() : -1)
+ << " type=" << DrmCompositionTypeToString(type_);
+
+ switch (type_) {
+ case DRM_COMPOSITION_TYPE_DPMS:
+ *out << " dpms_mode=" << DPMSModeToString(dpms_mode_);
+ break;
+ case DRM_COMPOSITION_TYPE_MODESET:
+ *out << " display_mode=" << display_mode_.h_display() << "x"
+ << display_mode_.v_display();
+ break;
+ default:
+ break;
+ }
+
+ *out << " timeline[current/squash/pre-comp/done]=" << timeline_current_ << "/"
+ << timeline_squash_done_ << "/" << timeline_pre_comp_done_ << "/"
+ << timeline_ << "\n";
+
+ *out << " Layers: count=" << layers_.size() << "\n";
+ for (size_t i = 0; i < layers_.size(); i++) {
+ const DrmHwcLayer &layer = layers_[i];
+ layer.dump_drm_layer(i,out);
+ }
+
+ *out << " Planes: count=" << composition_planes_.size() << "\n";
+ for (size_t i = 0; i < composition_planes_.size(); i++) {
+ const DrmCompositionPlane &comp_plane = composition_planes_[i];
+ comp_plane.dump_drm_com_plane(i,out);
+ }
+
+ *out << " Squash Regions: count=" << squash_regions_.size() << "\n";
+ for (size_t i = 0; i < squash_regions_.size(); i++) {
+ *out << " [" << i << "] ";
+ DumpRegion(squash_regions_[i], out);
+ *out << "\n";
+ }
+
+ *out << " Pre-Comp Regions: count=" << pre_comp_regions_.size() << "\n";
+ for (size_t i = 0; i < pre_comp_regions_.size(); i++) {
+ *out << " [" << i << "] ";
+ DumpRegion(pre_comp_regions_[i], out);
+ *out << "\n";
+ }
+}
+
+}
diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h
new file mode 100755
index 0000000..e6639d1
--- /dev/null
+++ b/drmdisplaycomposition.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_DISPLAY_COMPOSITION_H_
+#define ANDROID_DRM_DISPLAY_COMPOSITION_H_
+
+#include "drmcrtc.h"
+#include "drmhwcomposer.h"
+#include "drmplane.h"
+#include "glworker.h"
+
+#include <sstream>
+#include <vector>
+#include <map>
+
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+class Importer;
+class Planner;
+class SquashState;
+
+ typedef std::map<int, std::vector<DrmHwcLayer*>> LayerMap;
+ typedef LayerMap::iterator LayerMapIter;
+
+
+enum DrmCompositionType {
+ DRM_COMPOSITION_TYPE_EMPTY,
+ DRM_COMPOSITION_TYPE_FRAME,
+ DRM_COMPOSITION_TYPE_DPMS,
+ DRM_COMPOSITION_TYPE_MODESET,
+};
+
+struct DrmCompositionRegion {
+ DrmHwcRect<int> frame;
+ std::vector<size_t> source_layers;
+};
+
+class DrmCompositionPlane {
+ public:
+ enum class Type : int32_t {
+ kDisable,
+ kLayer,
+ kPrecomp,
+ kSquash,
+ };
+
+ DrmCompositionPlane() = default;
+ DrmCompositionPlane(DrmCompositionPlane &&rhs) = default;
+ DrmCompositionPlane &operator=(DrmCompositionPlane &&other) = default;
+ DrmCompositionPlane(Type type, DrmPlane *plane, DrmCrtc *crtc)
+ : type_(type), plane_(plane), crtc_(crtc) {
+ }
+ DrmCompositionPlane(Type type, DrmPlane *plane, DrmCrtc *crtc,
+ size_t source_layer)
+ : type_(type),
+ plane_(plane),
+ crtc_(crtc),
+ source_layers_(1, source_layer) {
+ }
+
+ Type type() const {
+ return type_;
+ }
+
+ DrmPlane *plane() const {
+ return plane_;
+ }
+ void set_plane(DrmPlane *plane) {
+ plane_ = plane;
+ }
+
+ DrmCrtc *crtc() const {
+ return crtc_;
+ }
+
+ std::vector<size_t> &source_layers() {
+ return source_layers_;
+ }
+
+ const std::vector<size_t> &source_layers() const {
+ return source_layers_;
+ }
+
+ int get_zpos() { return zpos_; }
+ void set_zpos( int zpos) { zpos_ = zpos; }
+
+ void dump_drm_com_plane(int index, std::ostringstream *out) const;
+
+ private:
+ int zpos_;
+ Type type_ = Type::kDisable;
+ DrmPlane *plane_ = NULL;
+ DrmCrtc *crtc_ = NULL;
+ std::vector<size_t> source_layers_;
+};
+
+class DrmDisplayComposition {
+ public:
+ DrmDisplayComposition() = default;
+ DrmDisplayComposition(const DrmDisplayComposition &) = delete;
+ ~DrmDisplayComposition();
+
+ int Init(DrmResources *drm, DrmCrtc *crtc, Importer *importer,
+ Planner *planner, uint64_t frame_no);
+
+ int SetLayers(DrmHwcLayer *layers, size_t num_layers, bool geometry_changed);
+ int AddPlaneComposition(DrmCompositionPlane plane);
+ int AddPlaneDisable(DrmPlane *plane);
+ int SetMode3D(Mode3D mode);
+ int SetDpmsMode(uint32_t dpms_mode);
+ int SetDisplayMode(const DrmMode &display_mode);
+ int SetCompPlanes(std::vector<DrmCompositionPlane>& composition_planes);
+
+ int Plan(SquashState *squash, std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes);
+
+ int FinalizeComposition();
+
+ int CreateNextTimelineFence(const char* fence_name);
+ int SignalSquashDone() {
+ return IncreaseTimelineToPoint(timeline_squash_done_);
+ }
+ int SignalPreCompDone() {
+ return IncreaseTimelineToPoint(timeline_pre_comp_done_);
+ }
+ int SignalCompositionDone() {
+ return IncreaseTimelineToPoint(timeline_);
+ }
+
+ std::vector<DrmHwcLayer> &layers() {
+ return layers_;
+ }
+
+ std::vector<DrmCompositionRegion> &squash_regions() {
+ return squash_regions_;
+ }
+
+ std::vector<DrmCompositionRegion> &pre_comp_regions() {
+ return pre_comp_regions_;
+ }
+
+ std::vector<DrmCompositionPlane> &composition_planes() {
+ return composition_planes_;
+ }
+
+ bool geometry_changed() const {
+ return geometry_changed_;
+ }
+
+ uint64_t frame_no() const {
+ return frame_no_;
+ }
+
+ DrmCompositionType type() const {
+ return type_;
+ }
+
+ Mode3D mode_3d() const {
+ return mode_3d_;
+ }
+
+ uint32_t dpms_mode() const {
+ return dpms_mode_;
+ }
+
+ const DrmMode &display_mode() const {
+ return display_mode_;
+ }
+
+ DrmCrtc *crtc() const {
+ return crtc_;
+ }
+
+ Importer *importer() const {
+ return importer_;
+ }
+
+ Planner *planner() const {
+ return planner_;
+ }
+
+ void Dump(std::ostringstream *out) const;
+
+ private:
+ bool validate_composition_type(DrmCompositionType desired);
+
+ int IncreaseTimelineToPoint(int point);
+
+ int FinalizeComposition(DrmHwcRect<int> *exclude_rects,
+ size_t num_exclude_rects);
+ void SeparateLayers(DrmHwcRect<int> *exclude_rects, size_t num_exclude_rects);
+ int CreateAndAssignReleaseFences();
+ int combine_layer();
+
+ DrmResources *drm_ = NULL;
+ DrmCrtc *crtc_ = NULL;
+ Importer *importer_ = NULL;
+ Planner *planner_ = NULL;
+
+ DrmCompositionType type_ = DRM_COMPOSITION_TYPE_EMPTY;
+ uint32_t dpms_mode_ = DRM_MODE_DPMS_ON;
+ DrmMode display_mode_;
+ Mode3D mode_3d_;
+
+ int timeline_fd_ = -1;
+ int timeline_ = 0;
+ int timeline_current_ = 0;
+ int timeline_squash_done_ = 0;
+ int timeline_pre_comp_done_ = 0;
+
+ bool geometry_changed_;
+ std::vector<DrmHwcLayer> layers_;
+ std::vector<DrmCompositionRegion> squash_regions_;
+ std::vector<DrmCompositionRegion> pre_comp_regions_;
+ std::vector<DrmCompositionPlane> composition_planes_;
+
+ LayerMap layer_map_;
+
+ uint64_t frame_no_ = 0;
+};
+}
+
+#endif // ANDROID_DRM_DISPLAY_COMPOSITION_H_
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
new file mode 100755
index 0000000..b613dcd
--- /dev/null
+++ b/drmdisplaycompositor.cpp
@@ -0,0 +1,1816 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwc-drm-display-compositor"
+
+#include "drmdisplaycompositor.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sstream>
+#include <vector>
+
+#include <cutils/log.h>
+#include <drm/drm_mode.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+#include "autolock.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+#include "glworker.h"
+#include "hwc_util.h"
+#include "hwc_debug.h"
+
+
+#define DRM_QUEUE_USLEEP 10
+#define DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH 1
+
+namespace android {
+
+void SquashState::Init(DrmHwcLayer *layers, size_t num_layers) {
+ generation_number_++;
+ valid_history_ = 0;
+ regions_.clear();
+ last_handles_.clear();
+
+ std::vector<DrmHwcRect<int>> in_rects;
+ for (size_t i = 0; i < num_layers; i++) {
+ DrmHwcLayer *layer = &layers[i];
+ in_rects.emplace_back(layer->display_frame);
+ last_handles_.push_back(layer->sf_handle);
+ }
+
+ std::vector<separate_rects::RectSet<uint64_t, int>> out_regions;
+ separate_rects::separate_rects_64(in_rects, &out_regions);
+
+ for (const separate_rects::RectSet<uint64_t, int> &out_region : out_regions) {
+ regions_.emplace_back();
+ Region &region = regions_.back();
+ region.rect = out_region.rect;
+ region.layer_refs = out_region.id_set.getBits();
+ }
+}
+
+void SquashState::GenerateHistory(DrmHwcLayer *layers, size_t num_layers,
+ std::vector<bool> &changed_regions) const {
+ changed_regions.resize(regions_.size());
+ if (num_layers != last_handles_.size()) {
+ ALOGE("SquashState::GenerateHistory expected %zu layers but got %zu layers",
+ last_handles_.size(), num_layers);
+ return;
+ }
+ std::bitset<kMaxLayers> changed_layers;
+ for (size_t i = 0; i < last_handles_.size(); i++) {
+ DrmHwcLayer *layer = &layers[i];
+ // Protected layers can't be squashed so we treat them as constantly
+ // changing.
+ if (layer->protected_usage() || last_handles_[i] != layer->sf_handle)
+ changed_layers.set(i);
+ }
+
+ for (size_t i = 0; i < regions_.size(); i++) {
+ changed_regions[i] = (regions_[i].layer_refs & changed_layers).any();
+ }
+}
+
+void SquashState::StableRegionsWithMarginalHistory(
+ const std::vector<bool> &changed_regions,
+ std::vector<bool> &stable_regions) const {
+ stable_regions.resize(regions_.size());
+ for (size_t i = 0; i < regions_.size(); i++) {
+ stable_regions[i] = !changed_regions[i] && is_stable(i);
+ }
+}
+
+void SquashState::RecordHistory(DrmHwcLayer *layers, size_t num_layers,
+ const std::vector<bool> &changed_regions) {
+ if (num_layers != last_handles_.size()) {
+ ALOGE("SquashState::RecordHistory expected %zu layers but got %zu layers",
+ last_handles_.size(), num_layers);
+ return;
+ }
+ if (changed_regions.size() != regions_.size()) {
+ ALOGE("SquashState::RecordHistory expected %zu regions but got %zu regions",
+ regions_.size(), changed_regions.size());
+ return;
+ }
+
+ for (size_t i = 0; i < last_handles_.size(); i++) {
+ DrmHwcLayer *layer = &layers[i];
+ last_handles_[i] = layer->sf_handle;
+ }
+
+ for (size_t i = 0; i < regions_.size(); i++) {
+ regions_[i].change_history <<= 1;
+ regions_[i].change_history.set(/* LSB */ 0, changed_regions[i]);
+ }
+
+ valid_history_++;
+}
+
+bool SquashState::RecordAndCompareSquashed(
+ const std::vector<bool> &squashed_regions) {
+ if (squashed_regions.size() != regions_.size()) {
+ ALOGE(
+ "SquashState::RecordAndCompareSquashed expected %zu regions but got "
+ "%zu regions",
+ regions_.size(), squashed_regions.size());
+ return false;
+ }
+ bool changed = false;
+ for (size_t i = 0; i < regions_.size(); i++) {
+ if (regions_[i].squashed != squashed_regions[i]) {
+ regions_[i].squashed = squashed_regions[i];
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+void SquashState::Dump(std::ostringstream *out) const {
+ *out << "----SquashState generation=" << generation_number_
+ << " history=" << valid_history_ << "\n"
+ << " Regions: count=" << regions_.size() << "\n";
+ for (size_t i = 0; i < regions_.size(); i++) {
+ const Region &region = regions_[i];
+ *out << " [" << i << "]"
+ << " history=" << region.change_history << " rect";
+ region.rect.Dump(out);
+ *out << " layers=(";
+ bool first = true;
+ for (size_t layer_index = 0; layer_index < kMaxLayers; layer_index++) {
+ if ((region.layer_refs &
+ std::bitset<kMaxLayers>((size_t)1 << layer_index))
+ .any()) {
+ if (!first)
+ *out << " ";
+ first = false;
+ *out << layer_index;
+ }
+ }
+ *out << ")";
+ if (region.squashed)
+ *out << " squashed";
+ *out << "\n";
+ }
+}
+
+static bool UsesSquash(const std::vector<DrmCompositionPlane> &comp_planes) {
+ return std::any_of(comp_planes.begin(), comp_planes.end(),
+ [](const DrmCompositionPlane &plane) {
+ return plane.type() == DrmCompositionPlane::Type::kSquash;
+ });
+}
+
+DrmDisplayCompositor::FrameWorker::FrameWorker(DrmDisplayCompositor *compositor)
+ : Worker("frame-worker", HAL_PRIORITY_URGENT_DISPLAY),
+ compositor_(compositor) {
+}
+
+DrmDisplayCompositor::FrameWorker::~FrameWorker() {
+ pthread_cond_destroy(&frame_queue_cond_);
+}
+
+int DrmDisplayCompositor::FrameWorker::Init() {
+ pthread_cond_init(&frame_queue_cond_, NULL);
+ return InitWorker();
+}
+
+void DrmDisplayCompositor::FrameWorker::QueueFrame(
+ std::unique_ptr<DrmDisplayComposition> composition, int status) {
+
+ /* ----------rk modified----------
+ * Block the queue if it gets too large.
+ * If we don't set this limitation,sometimes it will lead many frames' acquirefence don't signal,
+ * finanlly,lead fd leak out.
+ */
+ //while (frame_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH) {
+ // usleep(DRM_QUEUE_USLEEP);
+ //}
+
+ Lock();
+ while(frame_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH)
+ {
+ pthread_cond_wait(&frame_queue_cond_,getLock());
+ }
+
+ FrameState frame;
+ frame.composition = std::move(composition);
+ frame.status = status;
+ frame_queue_.push(std::move(frame));
+ SignalLocked();
+ Unlock();
+}
+
+void DrmDisplayCompositor::FrameWorker::Routine() {
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------FrameWorker Routine start----------------------------");
+
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock worker, %d", ret);
+ return;
+ }
+
+ int wait_ret = 0;
+ if (frame_queue_.empty()) {
+ wait_ret = WaitForSignalOrExitLocked();
+ }
+
+ FrameState frame;
+ if (!frame_queue_.empty()) {
+ frame = std::move(frame_queue_.front());
+ frame_queue_.pop();
+ pthread_cond_signal(&frame_queue_cond_);
+ }
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock worker, %d", ret);
+ return;
+ }
+
+ if (wait_ret == -EINTR) {
+ return;
+ } else if (wait_ret) {
+ ALOGE("Failed to wait for signal, %d", wait_ret);
+ return;
+ }
+
+ compositor_->ApplyFrame(std::move(frame.composition), frame.status);
+
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------FrameWorker Routine end----------------------------");
+}
+
+#if RK_DEBUG_CHECK_CRC
+static int crcTable[256];
+static void initCrcTable(void)
+{
+ unsigned int c;
+ unsigned int i, j;
+
+ for (i = 0; i < 256; i++) {
+ c = (unsigned int)i;
+ for (j = 0; j < 8; j++) {
+ if (c & 1) {
+ c = 0xedb88320L ^ (c >> 1);
+ } else {
+ c = c >> 1;
+ }
+ }
+ crcTable[i] = c;
+ }
+}
+
+static unsigned int createCrc32(unsigned int crc,unsigned const char *buffer, unsigned int size)
+{
+ unsigned int i;
+ for (i = 0; i < size; i++) {
+ crc = crcTable[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8);
+ }
+ return crc ;
+}
+#endif
+
+DrmDisplayCompositor::DrmDisplayCompositor()
+ : drm_(NULL),
+ display_(-1),
+ worker_(this),
+ frame_worker_(this),
+ initialized_(false),
+ active_(false),
+ use_hw_overlays_(true),
+ framebuffer_index_(0),
+#if RK_RGA
+ rgaBuffer_index_(0),
+ mRga_(RockchipRga::get()),
+ mUseRga_(false),
+#endif
+ squash_framebuffer_index_(0),
+ dump_frames_composited_(0),
+ dump_last_timestamp_ns_(0) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ return;
+ dump_last_timestamp_ns_ = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+
+ int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&gralloc_);
+ if (ret) {
+ ALOGE("Failed to open gralloc module %d", ret);
+ }
+
+#if RK_DEBUG_CHECK_CRC
+ initCrcTable();
+#endif
+
+}
+
+DrmDisplayCompositor::~DrmDisplayCompositor() {
+ if (!initialized_)
+ return;
+
+ worker_.Exit();
+ frame_worker_.Exit();
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret)
+ ALOGE("Failed to acquire compositor lock %d", ret);
+
+ while (!composite_queue_.empty()) {
+ composite_queue_.front().reset();
+ composite_queue_.pop();
+ }
+ active_composition_.reset();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret)
+ ALOGE("Failed to acquire compositor lock %d", ret);
+
+ pthread_mutex_destroy(&lock_);
+ pthread_cond_destroy(&composite_queue_cond_);
+}
+
+int DrmDisplayCompositor::Init(DrmResources *drm, int display) {
+ drm_ = drm;
+ display_ = display;
+
+ int ret = pthread_mutex_init(&lock_, NULL);
+ if (ret) {
+ ALOGE("Failed to initialize drm compositor lock %d\n", ret);
+ return ret;
+ }
+
+ ret = worker_.Init();
+ if (ret) {
+ pthread_mutex_destroy(&lock_);
+ ALOGE("Failed to initialize compositor worker %d\n", ret);
+ return ret;
+ }
+ ret = frame_worker_.Init();
+ if (ret) {
+ pthread_mutex_destroy(&lock_);
+ ALOGE("Failed to initialize frame worker %d\n", ret);
+ return ret;
+ }
+
+ pthread_cond_init(&composite_queue_cond_, NULL);
+
+ initialized_ = true;
+ return 0;
+}
+
+std::unique_ptr<DrmDisplayComposition> DrmDisplayCompositor::CreateComposition()
+ const {
+ return std::unique_ptr<DrmDisplayComposition>(new DrmDisplayComposition());
+}
+
+int DrmDisplayCompositor::QueueComposition(
+ std::unique_ptr<DrmDisplayComposition> composition) {
+ switch (composition->type()) {
+ case DRM_COMPOSITION_TYPE_FRAME:
+ break;
+ case DRM_COMPOSITION_TYPE_DPMS:
+ break;
+ case DRM_COMPOSITION_TYPE_MODESET:
+ break;
+ case DRM_COMPOSITION_TYPE_EMPTY:
+ return 0;
+ default:
+ ALOGE("Unknown composition type %d/%d", composition->type(), display_);
+ return -ENOENT;
+ }
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return ret;
+ }
+
+ while(composite_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH)
+ {
+ pthread_cond_wait(&composite_queue_cond_,&lock_);
+ }
+
+#if 0
+ // Block the queue if it gets too large. Otherwise, SurfaceFlinger will start
+ // to eat our buffer handles when we get about 1 second behind.
+ while (composite_queue_.size() >= DRM_DISPLAY_COMPOSITOR_MAX_QUEUE_DEPTH) {
+
+#if 0
+ pthread_mutex_unlock(&lock_);
+#if RK_DRM_HWC
+ //sched_yield will lead cpu schedule abnormal.
+ usleep(DRM_QUEUE_USLEEP);
+#else
+ sched_yield();
+#endif
+ pthread_mutex_lock(&lock_);
+#endif
+ }
+#endif
+
+ composite_queue_.push(std::move(composition));
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ worker_.Signal();
+ return 0;
+}
+
+std::tuple<uint32_t, uint32_t, int>
+DrmDisplayCompositor::GetActiveModeResolution() {
+ DrmConnector *connector = drm_->GetConnectorFromType(display_);
+ if (connector == NULL) {
+ ALOGE("Failed to determine display mode: no connector for display %d",
+ display_);
+ return std::make_tuple(0, 0, -ENODEV);
+ }
+
+ const DrmMode &mode = connector->active_mode();
+ return std::make_tuple(mode.h_display(), mode.v_display(), 0);
+}
+
+int DrmDisplayCompositor::PrepareFramebuffer(
+ DrmFramebuffer &fb, DrmDisplayComposition *display_comp) {
+ int ret = fb.WaitReleased(-1);
+ if (ret) {
+ ALOGE("Failed to wait for framebuffer release %d", ret);
+ return ret;
+ }
+ uint32_t width, height;
+ std::tie(width, height, ret) = GetActiveModeResolution();
+ if (ret) {
+ ALOGE(
+ "Failed to allocate framebuffer because the display resolution could "
+ "not be determined %d",
+ ret);
+ return ret;
+ }
+
+ fb.set_release_fence_fd(-1);
+ if (!fb.Allocate(width, height)) {
+ ALOGE("Failed to allocate framebuffer with size %dx%d", width, height);
+ return -ENOMEM;
+ }
+
+ display_comp->layers().emplace_back();
+ DrmHwcLayer &pre_comp_layer = display_comp->layers().back();
+ pre_comp_layer.sf_handle = fb.buffer()->handle;
+ pre_comp_layer.blending = DrmHwcBlending::kPreMult;
+ pre_comp_layer.source_crop = DrmHwcRect<float>(0, 0, width, height);
+ pre_comp_layer.display_frame = DrmHwcRect<int>(0, 0, width, height);
+ ret = pre_comp_layer.buffer.ImportBuffer(fb.buffer()->handle,
+ display_comp->importer()
+#if RK_VIDEO_SKIP_LINE
+ , false
+#endif
+ );
+ if (ret) {
+ ALOGE("Failed to import framebuffer for display %d", ret);
+ return ret;
+ }
+
+#if USE_AFBC_LAYER
+ ret = gralloc_->perform(gralloc_, GRALLOC_MODULE_PERFORM_GET_INTERNAL_FORMAT,
+ fb.buffer()->handle, &pre_comp_layer.internal_format);
+ if (ret) {
+ ALOGE("Failed to get internal_format for buffer %p (%d)", fb.buffer()->handle, ret);
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+#if RK_RGA
+int DrmDisplayCompositor::PrepareRgaBuffer(
+DrmRgaBuffer &rgaBuffer, DrmDisplayComposition *display_comp, DrmHwcLayer &layer) {
+ int rga_transform = 0;
+ int src_l,src_t,src_w,src_h;
+ int dst_l,dst_t,dst_r,dst_b;
+ int ret;
+ int dst_w,dst_h,dst_stride;
+ hwc_region_t * visible_region = &layer.mlayer->visibleRegionScreen;
+ hwc_rect_t const * visible_rects = visible_region->rects;
+ hwc_rect_t rect_merge;
+ int left_min = 0, top_min = 0, right_max = 0, bottom_max=0;
+ rga_info_t src, dst;
+ int alloc_format = 0;
+
+ memset(&src, 0, sizeof(rga_info_t));
+ memset(&dst, 0, sizeof(rga_info_t));
+ src.fd = -1;
+ dst.fd = -1;
+
+#ifndef TARGET_BOARD_PLATFORM_RK3368
+ rect_merge.left = layer.display_frame.left;
+ rect_merge.top = layer.display_frame.top;
+ rect_merge.right = layer.display_frame.right;
+ rect_merge.bottom = layer.display_frame.bottom;
+
+ if(visible_rects){
+ left_min = visible_rects[0].left;
+ top_min = visible_rects[0].top;
+ right_max = visible_rects[0].right;
+ bottom_max = visible_rects[0].bottom;
+
+ for (int r = 0; r < (int) visible_region->numRects; r++) {
+ int r_left;
+ int r_top;
+ int r_right;
+ int r_bottom;
+
+ r_left = hwcMAX(layer.display_frame.left, visible_rects[r].left);
+ left_min = hwcMIN(r_left, left_min);
+ r_top = hwcMAX(layer.display_frame.top, visible_rects[r].top);
+ top_min = hwcMIN(r_top, top_min);
+ r_right = hwcMIN(layer.display_frame.right, visible_rects[r].right);
+ right_max = hwcMAX(r_right, right_max);
+ r_bottom = hwcMIN(layer.display_frame.bottom, visible_rects[r].bottom);
+ bottom_max = hwcMAX(r_bottom, bottom_max);
+ }
+
+ if(layer.format == HAL_PIXEL_FORMAT_YCrCb_NV12_VIDEO
+ || layer.format == HAL_PIXEL_FORMAT_YCrCb_NV12){
+ rect_merge.left = layer.display_frame.left;
+ rect_merge.top = layer.display_frame.top;
+ rect_merge.right = layer.display_frame.right;
+ rect_merge.bottom = layer.display_frame.bottom;
+ }
+ else
+ {
+ rect_merge.left = hwcMAX(layer.display_frame.left, left_min);
+ rect_merge.top = hwcMAX(layer.display_frame.top, top_min);
+ rect_merge.right = hwcMIN(layer.display_frame.right, right_max);
+ rect_merge.bottom = hwcMIN(layer.display_frame.bottom, bottom_max);
+ }
+ }
+#endif
+ ret = rgaBuffer.WaitReleased(-1);
+ if (ret) {
+ ALOGE("Failed to wait for rga buffer release %d", ret);
+ return ret;
+ }
+ rgaBuffer.set_release_fence_fd(-1);
+
+ src_l = (int)layer.source_crop.left;
+ src_t = (int)layer.source_crop.top;
+ src_w = (int)(layer.source_crop.right - layer.source_crop.left);
+ src_h = (int)(layer.source_crop.bottom - layer.source_crop.top);
+ src_l = ALIGN_DOWN(src_l, 2);
+ src_w = ALIGN_DOWN(src_w, 2);
+ dst_l = 0;
+ dst_t = 0;
+
+#ifdef TARGET_BOARD_PLATFORM_RK3368
+ if(layer.transform & DrmHwcTransform::kRotate90 || layer.transform & DrmHwcTransform::kRotate270)
+ {
+ dst_r = (int)(layer.source_crop.bottom - layer.source_crop.top);
+ dst_b = (int)(layer.source_crop.right - layer.source_crop.left);
+ src_h = ALIGN_DOWN(src_h, 8);
+ src_w = ALIGN_DOWN(src_w, 2);
+ }
+ else
+ {
+ dst_r = (int)(layer.source_crop.right - layer.source_crop.left);
+ dst_b = (int)(layer.source_crop.bottom - layer.source_crop.top);
+ src_w = ALIGN_DOWN(src_w, 8);
+ src_h = ALIGN_DOWN(src_h, 2);
+ }
+ dst_w = dst_r - dst_l;
+ dst_h = dst_b - dst_t;
+
+ dst_w = ALIGN_DOWN(dst_w, 8);
+ dst_h = ALIGN_DOWN(dst_h, 2);
+#else
+ src_l = ALIGN_DOWN(src_l, 2);
+ src_w = ALIGN_DOWN(src_w, 2);
+
+ dst_w = rect_merge.right - rect_merge.left;
+ dst_h = rect_merge.bottom - rect_merge.top;
+
+ dst_w = ALIGN(dst_w, 8);
+ dst_h = ALIGN(dst_h, 2);
+#endif
+
+ if(dst_w < 0 || dst_h <0 )
+ ALOGE("RGA invalid dst_w=%d,dst_h=%d",dst_w,dst_h);
+
+ //If the layer's format is NV12_10,then use RGA to switch it to NV12.
+ if(layer.format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ alloc_format = HAL_PIXEL_FORMAT_YCrCb_NV12;
+ else
+ alloc_format = layer.format;
+
+ if (!rgaBuffer.Allocate(dst_w, dst_h, alloc_format)) {
+ ALOGE("Failed to allocate rga buffer with size %dx%d", dst_w, dst_h);
+ return -ENOMEM;
+ }
+
+ dst_stride = rgaBuffer.buffer()->getStride();
+
+ //DumpLayer("rga", layer.sf_handle);
+
+ if(layer.transform & DrmHwcTransform::kRotate90) {
+ rga_transform = DRM_RGA_TRANSFORM_ROT_90;
+ }
+ else if(layer.transform & DrmHwcTransform::kRotate270) {
+ rga_transform = DRM_RGA_TRANSFORM_ROT_270;
+ }
+ else if(layer.transform & DrmHwcTransform::kRotate180) {
+ rga_transform = DRM_RGA_TRANSFORM_ROT_180;
+ }
+ else if(layer.transform & DrmHwcTransform::kRotate0) {
+ rga_transform = DRM_RGA_TRANSFORM_ROT_0;
+ }
+ else if(layer.transform & DrmHwcTransform::kFlipH) {
+ rga_transform = DRM_RGA_TRANSFORM_FLIP_H;
+ }
+ else if(layer.transform & DrmHwcTransform::kFlipV) {
+ rga_transform = DRM_RGA_TRANSFORM_FLIP_V;
+ }
+ else {
+ ALOGE("%s: line=%d, wrong transform=0x%x", __FUNCTION__, __LINE__, layer.transform);
+ ret = -1;
+ return ret;
+ }
+
+ if(rga_transform != DRM_RGA_TRANSFORM_FLIP_H && layer.transform & DrmHwcTransform::kFlipH)
+ rga_transform |= DRM_RGA_TRANSFORM_FLIP_H;
+
+ if (rga_transform != DRM_RGA_TRANSFORM_FLIP_V && layer.transform & DrmHwcTransform::kFlipV)
+ rga_transform |= DRM_RGA_TRANSFORM_FLIP_V;
+
+ rga_set_rect(&src.rect,
+ src_l, src_t, src_w, src_h,
+ layer.stride, layer.height, layer.format);
+ rga_set_rect(&dst.rect, dst_l, dst_t, dst_w, dst_h, dst_stride, dst_h, alloc_format);
+ ALOGD_IF(log_level(DBG_DEBUG),"rgaRotateScale : src[x=%d,y=%d,w=%d,h=%d,ws=%d,hs=%d,format=0x%x],dst[x=%d,y=%d,w=%d,h=%d,ws=%d,hs=%d,format=0x%x]",
+ src.rect.xoffset, src.rect.yoffset, src.rect.width, src.rect.height, src.rect.wstride, src.rect.hstride, src.rect.format,
+ dst.rect.xoffset, dst.rect.yoffset, dst.rect.width, dst.rect.height, dst.rect.wstride, dst.rect.hstride, dst.rect.format);
+ ALOGD_IF(log_level(DBG_DEBUG),"rgaRotateScale : src hnd=%p,dst hnd=%p, format=0x%x, transform=0x%x\n",
+ (void*)layer.sf_handle, (void*)(rgaBuffer.buffer()->handle), layer.format, rga_transform);
+
+ src.hnd = layer.sf_handle;
+ dst.hnd = rgaBuffer.buffer()->handle;
+ src.rotation = rga_transform;
+ ret = mRga_.RkRgaBlit(&src, &dst, NULL);
+ if(ret) {
+ ALOGE("rgaRotateScale error : src[x=%d,y=%d,w=%d,h=%d,ws=%d,hs=%d,format=0x%x],dst[x=%d,y=%d,w=%d,h=%d,ws=%d,hs=%d,format=0x%x]",
+ src.rect.xoffset, src.rect.yoffset, src.rect.width, src.rect.height, src.rect.wstride, src.rect.hstride, src.rect.format,
+ dst.rect.xoffset, dst.rect.yoffset, dst.rect.width, dst.rect.height, dst.rect.wstride, dst.rect.hstride, dst.rect.format);
+ ALOGE("rgaRotateScale error : %s,src hnd=%p,dst hnd=%p",
+ strerror(errno), (void*)layer.sf_handle, (void*)(rgaBuffer.buffer()->handle));
+ }
+
+ //DumpLayer("rga", dst.hnd);
+
+ //instead of the original DrmHwcLayer
+ layer.is_rotate_by_rga = true;
+ layer.buffer.Clear();
+ layer.source_crop = DrmHwcRect<float>(dst_l,dst_t,dst_w,dst_h);
+ //The dst layer's format is NV12.
+ if(layer.format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ layer.format = HAL_PIXEL_FORMAT_YCrCb_NV12;
+ layer.sf_handle = rgaBuffer.buffer()->handle;
+
+#if RK_VIDEO_SKIP_LINE
+ layer.bSkipLine = false;
+#endif
+
+ ret = layer.buffer.ImportBuffer(rgaBuffer.buffer()->handle,
+ display_comp->importer()
+#if RK_VIDEO_SKIP_LINE
+ , layer.bSkipLine
+#endif
+ );
+ if (ret) {
+ ALOGE("Failed to import rga buffer ret=%d", ret);
+ return ret;
+ }
+
+ ret = layer.handle.CopyBufferHandle(rgaBuffer.buffer()->handle, gralloc_);
+ if (ret) {
+ ALOGE("Failed to copy rga handle ret=%d", ret);
+ return ret;
+ }
+
+ return ret;
+}
+#endif
+
+int DrmDisplayCompositor::ApplySquash(DrmDisplayComposition *display_comp) {
+ int ret = 0;
+
+ DrmFramebuffer &fb = squash_framebuffers_[squash_framebuffer_index_];
+ ret = PrepareFramebuffer(fb, display_comp);
+ if (ret) {
+ ALOGE("Failed to prepare framebuffer for squash %d", ret);
+ return ret;
+ }
+
+ std::vector<DrmCompositionRegion> &regions = display_comp->squash_regions();
+ ret = pre_compositor_->Composite(display_comp->layers().data(),
+ regions.data(), regions.size(), fb.buffer());
+ pre_compositor_->Finish();
+
+ if (ret) {
+ ALOGE("Failed to squash layers");
+ return ret;
+ }
+
+ ret = display_comp->CreateNextTimelineFence("PreLayer");
+ if (ret <= 0) {
+ ALOGE("Failed to create PreLayer framebuffer release fence %d", ret);
+ return ret;
+ }
+
+ fb.set_release_fence_fd(ret);
+ display_comp->SignalSquashDone();
+
+ return 0;
+}
+
+int DrmDisplayCompositor::ApplyPreComposite(
+ DrmDisplayComposition *display_comp) {
+ int ret = 0;
+
+ DrmFramebuffer &fb = framebuffers_[framebuffer_index_];
+ ret = PrepareFramebuffer(fb, display_comp);
+ if (ret) {
+ ALOGE("Failed to prepare framebuffer for pre-composite %d", ret);
+ return ret;
+ }
+
+ std::vector<DrmCompositionRegion> &regions = display_comp->pre_comp_regions();
+ ret = pre_compositor_->Composite(display_comp->layers().data(),
+ regions.data(), regions.size(), fb.buffer());
+ pre_compositor_->Finish();
+
+ if (ret) {
+ ALOGE("Failed to pre-composite layers");
+ return ret;
+ }
+
+ ret = display_comp->CreateNextTimelineFence("ApplyPreComposite");
+ if (ret <= 0) {
+ ALOGE("Failed to create pre-composite framebuffer release fence %d", ret);
+ return ret;
+ }
+
+ fb.set_release_fence_fd(ret);
+ display_comp->SignalPreCompDone();
+
+ return 0;
+}
+
+#if RK_RGA
+static int fence_merge(char* value,int fd1,int fd2)
+{
+ int ret = -1;
+ if(fd1 >= 0 && fd2 >= 0) {
+ ret = sync_merge(value, fd1, fd2);
+ close(fd1);close(fd2);
+ } else if (fd1 >= 0) {
+ ret = sync_merge(value, fd1, fd1);
+ close(fd1);
+ } else if (fd2 >= 0) {
+ ret = sync_merge(value, fd2, fd2);
+ close(fd2);
+ }
+ if(ret < 0) {
+ ALOGD("%s:merge[%d,%d]:%s",value,fd1,fd2,strerror(errno));
+ }
+ ALOGD_IF(log_level(DBG_DEBUG),"merge fd[%d,%d] to fd=%d",fd1,fd2,ret);
+ return ret;
+}
+
+int DrmDisplayCompositor::ApplyPreRotate(
+ DrmDisplayComposition *display_comp, DrmHwcLayer &layer) {
+ int ret = 0;
+
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:rgaBuffer_index_=%d", __FUNCTION__, rgaBuffer_index_);
+
+ DrmRgaBuffer &rga_buffer = rgaBuffers_[rgaBuffer_index_];
+ ret = PrepareRgaBuffer(rga_buffer, display_comp, layer);
+ if (ret) {
+ ALOGE("Failed to prepare rga buffer for RGA rotate %d", ret);
+ return ret;
+ }
+
+ ret = display_comp->CreateNextTimelineFence("ApplyPreRotate");
+ if (ret <= 0) {
+ ALOGE("Failed to create RGA rotate release fence %d", ret);
+ return ret;
+ }
+
+ rga_buffer.set_release_fence_fd(ret);
+
+ return 0;
+}
+
+void DrmDisplayCompositor::freeRgaBuffers() {
+ for(int i = 0; i < MaxRgaBuffers; i++) {
+ rgaBuffers_[i].Clear();
+ }
+}
+#endif
+
+int DrmDisplayCompositor::DisablePlanes(DrmDisplayComposition *display_comp) {
+ drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
+ if (!pset) {
+ ALOGE("Failed to allocate property set");
+ return -ENOMEM;
+ }
+
+ int ret;
+ std::vector<DrmCompositionPlane> &comp_planes =
+ display_comp->composition_planes();
+ for (DrmCompositionPlane &comp_plane : comp_planes) {
+ DrmPlane *plane = comp_plane.plane();
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->crtc_property().id(), 0) < 0 ||
+ drmModeAtomicAddProperty(pset, plane->id(), plane->fb_property().id(),
+ 0) < 0;
+ if (ret) {
+ ALOGE("Failed to add plane %d disable to pset", plane->id());
+ drmModeAtomicFree(pset);
+ return ret;
+ }
+ }
+
+ ret = drmModeAtomicCommit(drm_->fd(), pset, 0, drm_);
+ if (ret) {
+ ALOGE("Failed to commit pset ret=%d\n", ret);
+ drmModeAtomicFree(pset);
+ return ret;
+ }
+
+ drmModeAtomicFree(pset);
+ return 0;
+}
+
+int DrmDisplayCompositor::PrepareFrame(DrmDisplayComposition *display_comp) {
+ int ret = 0;
+
+ std::vector<DrmHwcLayer> &layers = display_comp->layers();
+ std::vector<DrmCompositionPlane> &comp_planes =
+ display_comp->composition_planes();
+ std::vector<DrmCompositionRegion> &squash_regions =
+ display_comp->squash_regions();
+ std::vector<DrmCompositionRegion> &pre_comp_regions =
+ display_comp->pre_comp_regions();
+
+ int squash_layer_index = -1;
+ if (squash_regions.size() > 0) {
+ squash_framebuffer_index_ = (squash_framebuffer_index_ + 1) % 2;
+ ret = ApplySquash(display_comp);
+ if (ret)
+ return ret;
+
+ squash_layer_index = layers.size() - 1;
+ } else {
+ if (UsesSquash(comp_planes)) {
+ DrmFramebuffer &fb = squash_framebuffers_[squash_framebuffer_index_];
+ layers.emplace_back();
+ squash_layer_index = layers.size() - 1;
+ DrmHwcLayer &squash_layer = layers.back();
+ ret = squash_layer.buffer.ImportBuffer(fb.buffer()->handle,
+ display_comp->importer()
+#if RK_VIDEO_SKIP_LINE
+ , false
+#endif
+ );
+ if (ret) {
+ ALOGE("Failed to import old squashed framebuffer %d", ret);
+ return ret;
+ }
+ squash_layer.sf_handle = fb.buffer()->handle;
+ squash_layer.blending = DrmHwcBlending::kPreMult;
+ squash_layer.source_crop = DrmHwcRect<float>(
+ 0, 0, squash_layer.buffer->width, squash_layer.buffer->height);
+ squash_layer.display_frame = DrmHwcRect<int>(
+ 0, 0, squash_layer.buffer->width, squash_layer.buffer->height);
+#if USE_AFBC_LAYER
+ ret = gralloc_->perform(gralloc_, GRALLOC_MODULE_PERFORM_GET_INTERNAL_FORMAT,
+ fb.buffer()->handle, &squash_layer.internal_format);
+ if (ret) {
+ ALOGE("Failed to get internal_format for buffer %p (%d)", fb.buffer()->handle, ret);
+ return ret;
+ }
+#endif
+ ret = display_comp->CreateNextTimelineFence("SquashLayer");
+ if (ret <= 0) {
+ ALOGE("Failed to create squash framebuffer release fence %d", ret);
+ return ret;
+ }
+
+ fb.set_release_fence_fd(ret);
+ ret = 0;
+ }
+ }
+
+ bool do_pre_comp = pre_comp_regions.size() > 0;
+ int pre_comp_layer_index = -1;
+ if (do_pre_comp) {
+ ret = ApplyPreComposite(display_comp);
+ if (ret)
+ return ret;
+
+ pre_comp_layer_index = layers.size() - 1;
+ framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
+ }
+
+#if RK_RGA
+ bool bUseRga = false;
+#endif
+
+ for (DrmCompositionPlane &comp_plane : comp_planes) {
+ std::vector<size_t> &source_layers = comp_plane.source_layers();
+
+ switch (comp_plane.type()) {
+ case DrmCompositionPlane::Type::kSquash:
+ if (source_layers.size())
+ ALOGE("Squash source_layers is expected to be empty (%zu/%d)",
+ source_layers[0], squash_layer_index);
+ source_layers.push_back(squash_layer_index);
+ break;
+ case DrmCompositionPlane::Type::kPrecomp:
+ if (!do_pre_comp) {
+ ALOGE(
+ "Can not use pre composite framebuffer with no pre composite "
+ "regions");
+ return -EINVAL;
+ }
+ // Replace source_layers with the output of the precomposite
+ source_layers.clear();
+ source_layers.push_back(pre_comp_layer_index);
+ break;
+#if RK_RGA
+ case DrmCompositionPlane::Type::kLayer:
+ if(drm_->isSupportRkRga() && !source_layers.empty())
+ {
+ DrmHwcLayer &layer = layers[source_layers.front()];
+ if(/*layer.is_yuv &&*/ layer.transform!=0)
+ {
+ ret = ApplyPreRotate(display_comp,layer);
+ if (ret)
+ {
+ freeRgaBuffers();
+ mUseRga_ = mUseRga_ ? false : mUseRga_;
+ return ret;
+ }
+
+ rgaBuffer_index_ = (rgaBuffer_index_ + 1) % MaxRgaBuffers;
+ bUseRga = true;
+ mUseRga_ = mUseRga_ ? mUseRga_ : true;
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+#if RK_RGA
+ if(mUseRga_ && !bUseRga)
+ {
+ freeRgaBuffers();
+ mUseRga_ = false;
+ }
+#endif
+
+ return ret;
+}
+
+static const char *RotatingToString(uint64_t rotating) {
+ switch (rotating) {
+ case (1 << DRM_REFLECT_X):
+ return "DRM_REFLECT_X";
+ case (1 << DRM_REFLECT_Y):
+ return "DRM_REFLECT_Y";
+ case (1 << DRM_ROTATE_90):
+ return "DRM_ROTATE_90";
+ case (1 << DRM_ROTATE_180):
+ return "DRM_ROTATE_180";
+ case (1 << DRM_ROTATE_270):
+ return "DRM_ROTATE_270";
+ case (0):
+ return "DRM_ROTATE_0";
+ default:
+ return "<invalid>";
+ }
+}
+
+int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
+ bool test_only) {
+ ATRACE_CALL();
+
+ int ret = 0;
+ uint32_t afbc_plane_id = 0;
+
+ std::vector<DrmHwcLayer> &layers = display_comp->layers();
+ std::vector<DrmCompositionPlane> &comp_planes =
+ display_comp->composition_planes();
+ std::vector<DrmCompositionRegion> &pre_comp_regions =
+ display_comp->pre_comp_regions();
+
+ DrmCrtc *crtc = display_comp->crtc();
+ if (!crtc) {
+ ALOGE("Could not locate crtc for display %d", display_);
+ return -ENODEV;
+ }
+
+ drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
+ if (!pset) {
+ ALOGE("Failed to allocate property set");
+ return -ENOMEM;
+ }
+
+ if (crtc->can_overscan()) {
+ char overscan[PROPERTY_VALUE_MAX];
+ int left_margin, right_margin, top_margin, bottom_margin;
+
+ if(display_comp->mode_3d() != NON_3D)
+ {
+ left_margin = 100;
+ top_margin = 100;
+ right_margin = 100;
+ bottom_margin = 100;
+ }
+ else
+ {
+ if (display_ == 0)
+ property_get("persist.sys.overscan.main", overscan, "overscan 100,100,100,100");
+ else
+ property_get("persist.sys.overscan.aux", overscan, "overscan 100,100,100,100");
+
+ sscanf(overscan, "overscan %d,%d,%d,%d", &left_margin, &top_margin,
+ &right_margin, &bottom_margin);
+ }
+ ret = drmModeAtomicAddProperty(pset, crtc->id(), crtc->left_margin_property().id(), left_margin) < 0 ||
+ drmModeAtomicAddProperty(pset, crtc->id(), crtc->right_margin_property().id(), right_margin) < 0 ||
+ drmModeAtomicAddProperty(pset, crtc->id(), crtc->top_margin_property().id(), top_margin) < 0 ||
+ drmModeAtomicAddProperty(pset, crtc->id(), crtc->bottom_margin_property().id(), bottom_margin) < 0;
+ if (ret) {
+ ALOGE("Failed to add overscan to pset");
+ drmModeAtomicFree(pset);
+ return ret;
+ }
+ }
+
+#if RK_VR
+ float w_scale=1.0,h_scale=1.0;
+ int xxx_w = hwc_get_int_property("sys.xxx.x_w","720");
+ int xxx_h = hwc_get_int_property("sys.xxx.x_h","1280");
+ uint32_t act_w, act_h;
+ std::tie(act_w, act_h, ret) = GetActiveModeResolution();
+ if (ret) {
+ ALOGE(
+ "Failed to allocate framebuffer because the display resolution could "
+ "not be determined %d",
+ ret);
+ return ret;
+ }
+ if(act_w && xxx_w)
+ {
+ w_scale = (float)act_w / xxx_w;
+ ALOGD("zxl xxx_w=%d,act_w=%d,w_scale=%f,w_scale=%d",xxx_w,act_w,w_scale,(int)w_scale);
+ }
+
+ if(act_h && xxx_h)
+ {
+ h_scale = (float)act_h / xxx_h;
+ }
+#endif
+
+ //Find out the fb target for clone layer.
+ int fb_target_fb_id = -1;
+ if(display_comp->mode_3d() == FPS_3D)
+ {
+ for (DrmCompositionPlane &comp_plane : comp_planes) {
+ if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
+ std::vector<size_t> &source_layers = comp_plane.source_layers();
+
+ if (source_layers.size() > 1) {
+ ALOGE("Can't handle more than one source layer sz=%zu type=%d",
+ source_layers.size(), comp_plane.type());
+ continue;
+ }
+
+ if (source_layers.empty() || source_layers.front() >= layers.size()) {
+ ALOGE("Source layer index %zu out of bounds %zu type=%d",
+ source_layers.front(), layers.size(), comp_plane.type());
+ break;
+ }
+ DrmHwcLayer &layer = layers[source_layers.front()];
+ if(layer.bFbTarget_ && !layer.bClone_ && layer.buffer)
+ {
+ fb_target_fb_id = layer.buffer->fb_id;
+ break;
+ }
+ }
+ }
+ }
+
+ for (DrmCompositionPlane &comp_plane : comp_planes) {
+ DrmPlane *plane = comp_plane.plane();
+ DrmCrtc *crtc = comp_plane.crtc();
+ std::vector<size_t> &source_layers = comp_plane.source_layers();
+
+ int fb_id = -1;
+ bool is_yuv = false;
+ DrmHwcRect<int> display_frame = DrmHwcRect<int>(0, 0, 0, 0);
+ DrmHwcRect<float> source_crop = DrmHwcRect<float>(0.0, 0.0, 0.0, 0.0);
+#if RK_VIDEO_SKIP_LINE
+ bool bSkipLine = false;
+#endif
+ uint64_t rotation = 0;
+ uint64_t alpha = 0xFF;
+#if RK_RGA
+ bool is_rotate_by_rga = false;
+#endif
+ int zpos = 0;
+#if RK_DEBUG_CHECK_CRC
+ unsigned int crc32 = 0;
+#endif
+#if USE_AFBC_LAYER
+ bool is_afbc = false;
+#endif
+ if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
+ if (source_layers.size() > 1) {
+ ALOGE("Can't handle more than one source layer sz=%zu type=%d",
+ source_layers.size(), comp_plane.type());
+ continue;
+ }
+
+ if (source_layers.empty() || source_layers.front() >= layers.size()) {
+ ALOGE("Source layer index %zu out of bounds %zu type=%d",
+ source_layers.front(), layers.size(), comp_plane.type());
+ break;
+ }
+
+ zpos = comp_plane.get_zpos();
+ if(zpos < 0)
+ {
+ ALOGE("The zpos(%d) is invalid", zpos);
+ }
+
+ DrmHwcLayer &layer = layers[source_layers.front()];
+ if (!test_only && layer.acquire_fence.get() >= 0) {
+ int acquire_fence = layer.acquire_fence.get();
+ int total_fence_timeout = 0;
+#if RK_VR
+ if(!(layer.gralloc_buffer_usage & 0x08000000))
+#endif
+ {
+ for (int i = 0; i < kAcquireWaitTries; ++i) {
+ int fence_timeout = kAcquireWaitTimeoutMs * (1 << i);
+ total_fence_timeout += fence_timeout;
+ ret = sync_wait(acquire_fence, -1);
+ if (ret)
+ ALOGW("Acquire fence %d wait %d failed (%d). Total time %d",
+ acquire_fence, i, ret, total_fence_timeout);
+ else
+ break; //rk: wait successfully
+ }
+ if (ret) {
+ ALOGE("Failed to wait for acquire %d/%d", acquire_fence, ret);
+ break;
+ }
+ }
+ layer.acquire_fence.Close();
+ }
+ if (!layer.bClone_ && !layer.buffer) {
+ ALOGE("Expected a valid framebuffer for pset");
+ break;
+ }
+
+ DumpLayer(layer.name.c_str(),layer.get_usable_handle());
+
+#if RK_VIDEO_SKIP_LINE
+ bSkipLine = layer.bSkipLine;
+#endif
+ if(layer.bClone_)
+ {
+ if(fb_target_fb_id > 0)
+ fb_id = fb_target_fb_id;
+ else
+ ALOGE("Invalid fb_target_fb_id=%d in 3D FPS mode", fb_target_fb_id);
+ }
+ else
+ fb_id = layer.buffer->fb_id;
+ display_frame = layer.display_frame;
+ source_crop = layer.source_crop;
+ is_yuv = layer.is_yuv;
+ if (layer.blending == DrmHwcBlending::kPreMult)
+ alpha = layer.alpha;
+
+
+#if RK_DEBUG_CHECK_CRC
+ void* cpu_addr;
+ gralloc_->lock(gralloc_, layer.sf_handle, GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK, //gr_handle->usage,
+ 0, 0, layer.width, layer.height, (void **)&cpu_addr);
+ crc32 = createCrc32(0xFFFFFFFF,(unsigned const char *)cpu_addr,sizeof(layer.width*layer.height));
+ ALOGD("layer=%s, w=%d, h=%d, crc32=0x%x",layer.name.c_str(),layer.width,layer.height,crc32);
+ gralloc_->unlock(gralloc_, layer.sf_handle);
+#endif
+
+#if USE_AFBC_LAYER
+ is_afbc = layer.is_afbc;
+ if((afbc_plane_id== 0) && is_afbc)
+ {
+ afbc_plane_id = plane->id();
+ ALOGD_IF(log_level(DBG_VERBOSE),"fbdc layer %s,plane id=%d",layer.name.c_str(),afbc_plane_id);
+ }
+#endif
+
+#if RK_RGA
+ is_rotate_by_rga = layer.is_rotate_by_rga;
+#endif
+ rotation = 0;
+ if (layer.transform & DrmHwcTransform::kFlipH)
+ rotation |= 1 << DRM_REFLECT_X;
+ if (layer.transform & DrmHwcTransform::kFlipV)
+ rotation |= 1 << DRM_REFLECT_Y;
+ if (layer.transform & DrmHwcTransform::kRotate90)
+ rotation |= 1 << DRM_ROTATE_90;
+ else if (layer.transform & DrmHwcTransform::kRotate180)
+ rotation |= 1 << DRM_ROTATE_180;
+ else if (layer.transform & DrmHwcTransform::kRotate270)
+ rotation |= 1 << DRM_ROTATE_270;
+ }
+
+ // Disable the plane if there's no framebuffer
+ if (fb_id < 0) {
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->crtc_property().id(), 0) < 0 ||
+ drmModeAtomicAddProperty(pset, plane->id(),
+ plane->fb_property().id(), 0) < 0;
+ if (ret) {
+ ALOGE("Failed to add plane %d disable to pset", plane->id());
+ break;
+ }
+ continue;
+ }
+
+ // TODO: Once we have atomic test, this should fall back to GL
+ if (
+#if RK_RGA
+ !is_rotate_by_rga &&
+#endif
+ rotation && !(rotation & plane->get_rotate())) {
+ ALOGE("Rotation is not supported on plane %d", plane->id());
+ ret = -EINVAL;
+ break;
+ }
+
+ // TODO: Once we have atomic test, this should fall back to GL
+ if (alpha != 0xFF && plane->alpha_property().id() == 0) {
+ ALOGE("Alpha is not supported on plane %d", plane->id());
+ ret = -EINVAL;
+ break;
+ }
+
+ int dst_l,dst_t,dst_w,dst_h;
+ int src_l,src_t,src_w,src_h;
+
+ src_l = (int)source_crop.left;
+ src_t = (int)source_crop.top;
+ src_w = (int)(source_crop.right - source_crop.left);
+#if RK_VIDEO_SKIP_LINE
+ if(bSkipLine)
+ src_h = (int)(source_crop.bottom - source_crop.top)/2;
+ else
+#endif
+ src_h = (int)(source_crop.bottom - source_crop.top);
+
+ dst_l = display_frame.left;
+ dst_t = display_frame.top;
+ dst_w = display_frame.right - display_frame.left;
+ dst_h = display_frame.bottom - display_frame.top;
+
+#if RK_VR
+ dst_l = dst_l * w_scale;
+ dst_t = dst_t * h_scale;
+ dst_w = dst_w * w_scale;
+ dst_h = dst_h * h_scale;
+ ALOGD_IF(log_level(DBG_VERBOSE),"scale dst: w_scale=%f,h_scale=%f",w_scale,h_scale);
+#endif
+
+//zxl: src_l/src_w need 16bytes aligned and src_t/src_h need 4bytes aligned in FBDC area.
+#if USE_AFBC_LAYER
+ if(afbc_plane_id == plane->id())
+ {
+ src_l = IS_ALIGN(src_l, 16)?src_l:ALIGN(src_l, 16);
+ src_t = IS_ALIGN(src_t, 4)?src_t:ALIGN(src_t, 4);
+ src_w = IS_ALIGN(src_w, 16)?src_w:(ALIGN(src_w - src_l, 16)-16);
+ src_h = IS_ALIGN(src_h, 4)?src_h:(ALIGN(src_h - src_t, 4)-4);
+ }
+#endif
+ if(is_yuv)
+ src_l = ALIGN_DOWN(src_l, 2);
+
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->crtc_property().id(), crtc->id()) < 0;
+ ret |= drmModeAtomicAddProperty(pset, plane->id(),
+ plane->fb_property().id(), fb_id) < 0;
+ ret |= drmModeAtomicAddProperty(pset, plane->id(),
+ plane->crtc_x_property().id(),
+ dst_l) < 0;
+ ret |= drmModeAtomicAddProperty(pset, plane->id(),
+ plane->crtc_y_property().id(),
+ dst_t) < 0;
+ ret |= drmModeAtomicAddProperty(
+ pset, plane->id(), plane->crtc_w_property().id(),
+ dst_w) < 0;
+ ret |= drmModeAtomicAddProperty(
+ pset, plane->id(), plane->crtc_h_property().id(),
+ dst_h) < 0;
+ ret |= drmModeAtomicAddProperty(pset, plane->id(),
+ plane->src_x_property().id(),
+ src_l << 16) < 0;
+ ret |= drmModeAtomicAddProperty(pset, plane->id(),
+ plane->src_y_property().id(),
+ src_t << 16) < 0;
+ ret |= drmModeAtomicAddProperty(
+ pset, plane->id(), plane->src_w_property().id(),
+ src_w << 16) < 0;
+ ret |= drmModeAtomicAddProperty(
+ pset, plane->id(), plane->src_h_property().id(),
+ src_h << 16) < 0;
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->zpos_property().id(), zpos) < 0;
+ if (ret) {
+ ALOGE("Failed to add plane %d to set", plane->id());
+ break;
+ }
+
+ size_t index=0;
+ std::ostringstream out_log;
+
+ out_log << "DrmDisplayCompositor[" << index << "]"
+ << " plane=" << (plane ? plane->id() : -1)
+ << " crct id=" << crtc->id()
+ << " fb id=" << fb_id
+ << " display_frame[" << dst_l << ","
+ << dst_t << "," << dst_w
+ << "," << dst_h << "]"
+ << " source_crop[" << src_l << ","
+ << src_t << "," << src_w
+ << "," << src_h << "]"
+ << ", zpos=" << zpos
+#if USE_AFBC_LAYER
+ << ", is_afbc=" << is_afbc
+#endif
+ ;
+ index++;
+
+ if (
+#if RK_RGA
+ !is_rotate_by_rga &&
+#endif
+ plane->rotation_property().id()) {
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->rotation_property().id(),
+ rotation) < 0;
+ if (ret) {
+ ALOGE("Failed to add rotation property %d to plane %d",
+ plane->rotation_property().id(), plane->id());
+ break;
+ }
+ out_log << " rotation=" << RotatingToString(rotation);
+ }
+ if (plane->alpha_property().id()) {
+ ret = drmModeAtomicAddProperty(pset, plane->id(),
+ plane->alpha_property().id(),
+ alpha) < 0;
+ if (ret) {
+ ALOGE("Failed to add alpha property %d to plane %d",
+ plane->alpha_property().id(), plane->id());
+ break;
+ }
+ out_log << " alpha=" << std::hex << alpha;
+ }
+ out_log << "\n";
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out_log.str().c_str());
+ out_log.clear();
+ }
+
+out:
+
+ if (!ret) {
+ uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+ if (test_only)
+ flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+
+PRINT_TIME_START;
+ char value[PROPERTY_VALUE_MAX];
+ int new_value;
+ property_get("sys.hwc.msleep", value, "0");
+ new_value = atoi(value);
+ usleep(new_value*1000);
+
+ ret = drmModeAtomicCommit(drm_->fd(), pset, flags, drm_);
+ if (ret) {
+ if (test_only)
+ ALOGI("Commit test pset failed ret=%d\n", ret);
+ else
+ ALOGE("Failed to commit pset ret=%d\n", ret);
+ drmModeAtomicFree(pset);
+ return ret;
+ }
+PRINT_TIME_END("commit");
+ }
+
+ if (pset)
+ drmModeAtomicFree(pset);
+
+ return ret;
+}
+
+int DrmDisplayCompositor::ApplyDpms(DrmDisplayComposition *display_comp) {
+ DrmConnector *conn = drm_->GetConnectorFromType(display_);
+ if (!conn) {
+ ALOGE("Failed to get DrmConnector for display %d", display_);
+ return -ENODEV;
+ }
+
+ const DrmProperty &prop = conn->dpms_property();
+ int ret = drmModeConnectorSetProperty(drm_->fd(), conn->id(), prop.id(),
+ display_comp->dpms_mode());
+ if (ret) {
+ ALOGE("Failed to set DPMS property for connector %d", conn->id());
+ return ret;
+ }
+ return 0;
+}
+
+void DrmDisplayCompositor::ClearDisplay() {
+ AutoLock lock(&lock_, "compositor");
+ int ret = lock.Lock();
+ if (ret)
+ return;
+
+ if (!active_composition_)
+ return;
+
+ if (DisablePlanes(active_composition_.get()))
+ return;
+
+ active_composition_->SignalCompositionDone();
+
+ active_composition_.reset(NULL);
+}
+
+void DrmDisplayCompositor::ApplyFrame(
+ std::unique_ptr<DrmDisplayComposition> composition, int status) {
+ int ret = status;
+ if (!ret)
+ ret = CommitFrame(composition.get(), false);
+
+ if (ret) {
+ ALOGE("Composite failed for display %d", display_);
+ // Disable the hw used by the last active composition. This allows us to
+ // signal the release fences from that composition to avoid hanging.
+ ClearDisplay();
+ return;
+ }
+ ++dump_frames_composited_;
+
+ if (active_composition_)
+ active_composition_->SignalCompositionDone();
+
+
+ ret = pthread_mutex_lock(&lock_);
+ if (ret)
+ ALOGE("Failed to acquire lock for active_composition swap");
+
+ active_composition_.swap(composition);
+
+ if (!ret)
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret)
+ ALOGE("Failed to release lock for active_composition swap");
+}
+
+int DrmDisplayCompositor::Composite() {
+ ATRACE_CALL();
+
+ if (!pre_compositor_) {
+ pre_compositor_.reset(new GLWorkerCompositor());
+ int ret = pre_compositor_->Init();
+ if (ret) {
+ ALOGE("Failed to initialize OpenGL compositor %d", ret);
+ return ret;
+ }
+ }
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return ret;
+ }
+ if (composite_queue_.empty()) {
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret)
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ std::unique_ptr<DrmDisplayComposition> composition(
+ std::move(composite_queue_.front()));
+
+ composite_queue_.pop();
+ pthread_cond_signal(&composite_queue_cond_);
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ switch (composition->type()) {
+ case DRM_COMPOSITION_TYPE_FRAME:
+ ret = PrepareFrame(composition.get());
+ if (ret) {
+ ALOGE("Failed to prepare frame for display %d", display_);
+ return ret;
+ }
+
+ if (composition->geometry_changed()) {
+ // Send the composition to the kernel to ensure we can commit it. This
+ // is just a test, it won't actually commit the frame. If rejected,
+ // squash the frame into one layer and use the squashed composition
+ ret = CommitFrame(composition.get(), true);
+ if (ret)
+ ALOGI("Commit test failed, squashing frame for display %d", display_);
+ use_hw_overlays_ = !ret;
+ }
+
+ // If use_hw_overlays_ is false, we can't use hardware to composite the
+ // frame. So squash all layers into a single composition and queue that
+ // instead.
+ if (!use_hw_overlays_) {
+ std::unique_ptr<DrmDisplayComposition> squashed = CreateComposition();
+ ret = SquashFrame(composition.get(), squashed.get());
+ if (!ret) {
+ composition = std::move(squashed);
+ } else {
+ ALOGE("Failed to squash frame for display %d", display_);
+ // Disable the hw used by the last active composition. This allows us
+ // to signal the release fences from that composition to avoid
+ // hanging.
+ ClearDisplay();
+ return ret;
+ }
+ }
+ frame_worker_.QueueFrame(std::move(composition), ret);
+ break;
+ case DRM_COMPOSITION_TYPE_DPMS:
+ ret = ApplyDpms(composition.get());
+ if (ret)
+ ALOGE("Failed to apply dpms for display %d", display_);
+
+#if 0
+ //zxl:Fix fence timeout bug when plug out HDMI.
+ if(composition.get()->dpms_mode() == DRM_MODE_DPMS_OFF && active_composition_)
+ {
+ active_composition_->SignalCompositionDone();
+ }
+#else
+ if(composition.get()->dpms_mode() == DRM_MODE_DPMS_OFF)
+ ClearDisplay();
+#endif
+ return ret;
+ case DRM_COMPOSITION_TYPE_MODESET:
+ return 0;
+ default:
+ ALOGE("Unknown composition type %d", composition->type());
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+bool DrmDisplayCompositor::HaveQueuedComposites() const {
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return false;
+ }
+
+ bool empty_ret = !composite_queue_.empty();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return false;
+ }
+
+ return empty_ret;
+}
+
+int DrmDisplayCompositor::SquashAll() {
+ AutoLock lock(&lock_, "compositor");
+ int ret = lock.Lock();
+ if (ret)
+ return ret;
+
+ if (!active_composition_)
+ return 0;
+
+ std::unique_ptr<DrmDisplayComposition> comp = CreateComposition();
+ ret = SquashFrame(active_composition_.get(), comp.get());
+
+ // ApplyFrame needs the lock
+ lock.Unlock();
+
+ if (!ret)
+ ApplyFrame(std::move(comp), 0);
+
+ return ret;
+}
+
+// Returns:
+// - 0 if src is successfully squashed into dst
+// - -EALREADY if the src is already squashed
+// - Appropriate error if the squash fails
+int DrmDisplayCompositor::SquashFrame(DrmDisplayComposition *src,
+ DrmDisplayComposition *dst) {
+ if (src->type() != DRM_COMPOSITION_TYPE_FRAME)
+ return -ENOTSUP;
+
+ std::vector<DrmCompositionPlane> &src_planes = src->composition_planes();
+ std::vector<DrmHwcLayer> &src_layers = src->layers();
+
+ // Make sure there is more than one layer to squash.
+ size_t src_planes_with_layer = std::count_if(
+ src_planes.begin(), src_planes.end(), [](DrmCompositionPlane &p) {
+ return p.type() != DrmCompositionPlane::Type::kDisable;
+ });
+ if (src_planes_with_layer <= 1)
+ return -EALREADY;
+
+ int pre_comp_layer_index;
+
+ int ret = dst->Init(drm_, src->crtc(), src->importer(), src->planner(),
+ src->frame_no());
+ if (ret) {
+ ALOGE("Failed to init squash all composition %d", ret);
+ return ret;
+ }
+
+ DrmCompositionPlane squashed_comp(DrmCompositionPlane::Type::kPrecomp, NULL,
+ src->crtc());
+ std::vector<DrmHwcLayer> dst_layers;
+ for (DrmCompositionPlane &comp_plane : src_planes) {
+ // Composition planes without DRM planes should never happen
+ if (comp_plane.plane() == NULL) {
+ ALOGE("Skipping squash all because of NULL plane");
+ ret = -EINVAL;
+ goto move_layers_back;
+ }
+
+ if (comp_plane.type() == DrmCompositionPlane::Type::kDisable) {
+ dst->AddPlaneDisable(comp_plane.plane());
+ continue;
+ }
+
+ for (auto i : comp_plane.source_layers()) {
+ DrmHwcLayer &layer = src_layers[i];
+
+ // Squashing protected layers is impossible.
+ if (layer.protected_usage()) {
+ ret = -ENOTSUP;
+ goto move_layers_back;
+ }
+
+ // The OutputFds point to freed memory after hwc_set returns. They are
+ // returned to the default to prevent DrmDisplayComposition::Plan from
+ // filling the OutputFds.
+ layer.release_fence = OutputFd();
+ dst_layers.emplace_back(std::move(layer));
+ squashed_comp.source_layers().push_back(
+ squashed_comp.source_layers().size());
+ }
+
+ if (!squashed_comp.plane())
+ {
+ squashed_comp.set_plane(comp_plane.plane());
+ }
+ else
+ dst->AddPlaneDisable(comp_plane.plane());
+ }
+
+ ret = dst->SetLayers(dst_layers.data(), dst_layers.size(), false);
+ if (ret) {
+ ALOGE("Failed to set layers for squash all composition %d", ret);
+ goto move_layers_back;
+ }
+
+ ret = dst->AddPlaneComposition(std::move(squashed_comp));
+ if (ret) {
+ ALOGE("Failed to add squashed plane composition %d", ret);
+ goto move_layers_back;
+ }
+
+ ret = dst->FinalizeComposition();
+ if (ret) {
+ ALOGE("Failed to plan for squash all composition %d", ret);
+ goto move_layers_back;
+ }
+
+ ret = ApplyPreComposite(dst);
+ if (ret) {
+ ALOGE("Failed to pre-composite for squash all composition %d", ret);
+ goto move_layers_back;
+ }
+
+ pre_comp_layer_index = dst->layers().size() - 1;
+ framebuffer_index_ = (framebuffer_index_ + 1) % DRM_DISPLAY_BUFFERS;
+
+ for (DrmCompositionPlane &plane : dst->composition_planes()) {
+ if (plane.type() == DrmCompositionPlane::Type::kPrecomp) {
+ // Replace source_layers with the output of the precomposite
+ plane.source_layers().clear();
+ plane.source_layers().push_back(pre_comp_layer_index);
+ break;
+ }
+ }
+
+ return 0;
+
+// TODO(zachr): think of a better way to transfer ownership back to the active
+// composition.
+move_layers_back:
+ for (size_t plane_index = 0;
+ plane_index < src_planes.size() && plane_index < dst_layers.size();) {
+ if (src_planes[plane_index].source_layers().empty()) {
+ plane_index++;
+ continue;
+ }
+ for (auto i : src_planes[plane_index].source_layers())
+ src_layers[i] = std::move(dst_layers[plane_index++]);
+ }
+
+ return ret;
+}
+
+void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret)
+ return;
+
+ uint64_t num_frames = dump_frames_composited_;
+ dump_frames_composited_ = 0;
+
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret) {
+ pthread_mutex_unlock(&lock_);
+ return;
+ }
+
+ uint64_t cur_ts = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ uint64_t num_ms = (cur_ts - dump_last_timestamp_ns_) / (1000 * 1000);
+ float fps = num_ms ? (num_frames * 1000.0f) / (num_ms) : 0.0f;
+
+ *out << "--DrmDisplayCompositor[" << display_
+ << "]: num_frames=" << num_frames << " num_ms=" << num_ms
+ << " fps=" << fps << "\n";
+
+ dump_last_timestamp_ns_ = cur_ts;
+
+ if (active_composition_)
+ active_composition_->Dump(out);
+
+ squash_state_.Dump(out);
+
+ pthread_mutex_unlock(&lock_);
+}
+}
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
new file mode 100755
index 0000000..998a7bd
--- /dev/null
+++ b/drmdisplaycompositor.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_DISPLAY_COMPOSITOR_H_
+#define ANDROID_DRM_DISPLAY_COMPOSITOR_H_
+
+#include "drmhwcomposer.h"
+#include "drmcomposition.h"
+#include "drmcompositorworker.h"
+#include "drmframebuffer.h"
+#include "separate_rects.h"
+
+#include <pthread.h>
+#include <memory>
+#include <queue>
+#include <sstream>
+#include <tuple>
+
+#if RK_RGA
+#include <RockchipRga.h>
+#endif
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+// One for the front, one for the back, and one for cases where we need to
+// squash a frame that the hw can't display with hw overlays.
+#define DRM_DISPLAY_BUFFERS (3)
+#define MaxRgaBuffers (3)
+#define RGA_MAX_WIDTH (4096)
+#define RGA_MAX_HEIGHT (2304)
+
+namespace android {
+
+class GLWorkerCompositor;
+
+class SquashState {
+ public:
+ static const unsigned kHistoryLength = 6; // TODO: make this number not magic
+ static const unsigned kMaxLayers = 64;
+
+ struct Region {
+ DrmHwcRect<int> rect;
+ std::bitset<kMaxLayers> layer_refs;
+ std::bitset<kHistoryLength> change_history;
+ bool squashed = false;
+ };
+
+ bool is_stable(int region_index) const {
+ return valid_history_ >= kHistoryLength &&
+ regions_[region_index].change_history.none();
+ }
+
+ const std::vector<Region> &regions() const {
+ return regions_;
+ }
+
+ void Init(DrmHwcLayer *layers, size_t num_layers);
+ void GenerateHistory(DrmHwcLayer *layers, size_t num_layers,
+ std::vector<bool> &changed_regions) const;
+ void StableRegionsWithMarginalHistory(
+ const std::vector<bool> &changed_regions,
+ std::vector<bool> &stable_regions) const;
+ void RecordHistory(DrmHwcLayer *layers, size_t num_layers,
+ const std::vector<bool> &changed_regions);
+ bool RecordAndCompareSquashed(const std::vector<bool> &squashed_regions);
+
+ void Dump(std::ostringstream *out) const;
+
+ private:
+ size_t generation_number_ = 0;
+ unsigned valid_history_ = 0;
+ std::vector<buffer_handle_t> last_handles_;
+
+ std::vector<Region> regions_;
+};
+
+class DrmDisplayCompositor {
+ public:
+ DrmDisplayCompositor();
+ ~DrmDisplayCompositor();
+
+ int Init(DrmResources *drm, int display);
+
+ std::unique_ptr<DrmDisplayComposition> CreateComposition() const;
+ int QueueComposition(std::unique_ptr<DrmDisplayComposition> composition);
+ int Composite();
+ void ClearDisplay();
+ int SquashAll();
+ void Dump(std::ostringstream *out) const;
+
+ std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
+
+ bool HaveQueuedComposites() const;
+
+ SquashState *squash_state() {
+ return &squash_state_;
+ }
+
+ private:
+ struct FrameState {
+ std::unique_ptr<DrmDisplayComposition> composition;
+ int status = 0;
+ };
+
+ class FrameWorker : public Worker {
+ public:
+ FrameWorker(DrmDisplayCompositor *compositor);
+ ~FrameWorker() override;
+
+ int Init();
+ void QueueFrame(std::unique_ptr<DrmDisplayComposition> composition,
+ int status);
+
+ protected:
+ void Routine() override;
+
+ private:
+ pthread_cond_t frame_queue_cond_; //rk: limit frame queue size;
+ DrmDisplayCompositor *compositor_;
+ std::queue<FrameState> frame_queue_;
+ };
+
+ struct ModeState {
+ bool needs_modeset = false;
+ DrmMode mode;
+ uint32_t blob_id = 0;
+ };
+
+ DrmDisplayCompositor(const DrmDisplayCompositor &) = delete;
+
+ // We'll wait for acquire fences to fire for kAcquireWaitTimeoutMs,
+ // kAcquireWaitTries times, logging a warning in between.
+ static const int kAcquireWaitTries = 5;
+ static const int kAcquireWaitTimeoutMs = 100;
+
+ int PrepareFramebuffer(DrmFramebuffer &fb,
+ DrmDisplayComposition *display_comp);
+#if RK_RGA
+ int PrepareRgaBuffer(DrmRgaBuffer &rgaBuffer,
+ DrmDisplayComposition *display_comp, DrmHwcLayer &layer);
+#endif
+ int ApplySquash(DrmDisplayComposition *display_comp);
+ int ApplyPreComposite(DrmDisplayComposition *display_comp);
+#if RK_RGA
+ int ApplyPreRotate(DrmDisplayComposition *display_comp,
+ DrmHwcLayer &layer);
+ void freeRgaBuffers();
+#endif
+ int PrepareFrame(DrmDisplayComposition *display_comp);
+ int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
+ int SquashFrame(DrmDisplayComposition *src, DrmDisplayComposition *dst);
+ int ApplyDpms(DrmDisplayComposition *display_comp);
+ int DisablePlanes(DrmDisplayComposition *display_comp);
+
+ void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
+ int status);
+
+ std::tuple<int, uint32_t> CreateModeBlob(const DrmMode &mode);
+
+ DrmResources *drm_;
+ int display_;
+
+ DrmCompositorWorker worker_;
+ FrameWorker frame_worker_;
+
+ std::queue<std::unique_ptr<DrmDisplayComposition>> composite_queue_;
+ std::unique_ptr<DrmDisplayComposition> active_composition_;
+
+ pthread_cond_t composite_queue_cond_; //rk: limit composite queue size;
+
+ bool initialized_;
+ bool active_;
+ bool use_hw_overlays_;
+
+ mutable pthread_mutex_t mode_lock_;
+ ModeState mode_;
+
+ int framebuffer_index_;
+ DrmFramebuffer framebuffers_[DRM_DISPLAY_BUFFERS];
+#if RK_RGA
+ int rgaBuffer_index_;
+ DrmRgaBuffer rgaBuffers_[MaxRgaBuffers];
+ RockchipRga& mRga_;
+ bool mUseRga_;
+#endif
+ std::unique_ptr<GLWorkerCompositor> pre_compositor_;
+
+ SquashState squash_state_;
+ int squash_framebuffer_index_;
+ DrmFramebuffer squash_framebuffers_[2];
+
+ // mutable since we need to acquire in HaveQueuedComposites
+ mutable pthread_mutex_t lock_;
+
+ // State tracking progress since our last Dump(). These are mutable since
+ // we need to reset them on every Dump() call.
+ mutable uint64_t dump_frames_composited_;
+ mutable uint64_t dump_last_timestamp_ns_;
+
+ const gralloc_module_t *gralloc_;
+};
+}
+
+#endif // ANDROID_DRM_DISPLAY_COMPOSITOR_H_
diff --git a/drmencoder.cpp b/drmencoder.cpp
new file mode 100755
index 0000000..1b3a02c
--- /dev/null
+++ b/drmencoder.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "drmcrtc.h"
+#include "drmencoder.h"
+#include "drmresources.h"
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmEncoder::DrmEncoder(DrmResources *drm, drmModeEncoderPtr e, DrmCrtc *current_crtc,
+ const std::vector<DrmCrtc *> &possible_crtcs)
+ : id_(e->encoder_id),
+ crtc_(current_crtc),
+ drm_(drm),
+ type_(e->encoder_type),
+ possible_crtcs_(possible_crtcs),
+ encoder_(e) {
+}
+
+uint32_t DrmEncoder::id() const {
+ return id_;
+}
+
+DrmCrtc *DrmEncoder::crtc() const {
+ return crtc_;
+}
+
+void DrmEncoder::set_crtc(DrmCrtc *crtc) {
+ crtc_ = crtc;
+}
+
+void DrmEncoder::dump_encoder(std::ostringstream *out) const {
+ *out << encoder_->encoder_id << "\t"
+ << encoder_->crtc_id << "\t"
+ << drm_->encoder_type_str(encoder_->encoder_type) << "\t"
+ << std::hex << encoder_->possible_crtcs << "\t"
+ << std::hex << encoder_->possible_clones << "\n";
+}
+}
diff --git a/drmencoder.h b/drmencoder.h
new file mode 100755
index 0000000..a9a1cf6
--- /dev/null
+++ b/drmencoder.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_ENCODER_H_
+#define ANDROID_DRM_ENCODER_H_
+
+#include "drmcrtc.h"
+
+#include <stdint.h>
+#include <vector>
+#include <xf86drmMode.h>
+
+namespace android {
+
+class DrmEncoder {
+ public:
+ DrmEncoder(DrmResources *drm, drmModeEncoderPtr e, DrmCrtc *current_crtc,
+ const std::vector<DrmCrtc *> &possible_crtcs);
+ DrmEncoder(const DrmEncoder &) = delete;
+ DrmEncoder &operator=(const DrmEncoder &) = delete;
+
+ uint32_t id() const;
+
+ DrmCrtc *crtc() const;
+ void set_crtc(DrmCrtc *crtc);
+
+ const std::vector<DrmCrtc *> &possible_crtcs() const {
+ return possible_crtcs_;
+ }
+ void dump_encoder(std::ostringstream *out) const;
+
+ private:
+ uint32_t id_;
+ DrmCrtc *crtc_;
+ DrmResources *drm_;
+
+ uint32_t type_;
+
+ std::vector<DrmCrtc *> possible_crtcs_;
+
+ drmModeEncoderPtr encoder_;
+};
+}
+
+#endif // ANDROID_DRM_ENCODER_H_
diff --git a/drmeventlistener.cpp b/drmeventlistener.cpp
new file mode 100644
index 0000000..b3e2328
--- /dev/null
+++ b/drmeventlistener.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-event-listener"
+
+#include "drmeventlistener.h"
+#include "drmresources.h"
+
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include <cutils/log.h>
+#include <xf86drm.h>
+
+namespace android {
+
+DrmEventListener::DrmEventListener(DrmResources *drm)
+ : Worker("drm-event-listener", HAL_PRIORITY_URGENT_DISPLAY),
+ drm_(drm) {
+}
+
+int DrmEventListener::Init() {
+ uevent_fd_.Set(socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT));
+ if (uevent_fd_.get() < 0) {
+ ALOGE("Failed to open uevent socket %d", uevent_fd_.get());
+ return uevent_fd_.get();
+ }
+
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = 0xFFFFFFFF;
+
+ int ret = bind(uevent_fd_.get(), (struct sockaddr *)&addr, sizeof(addr));
+ if (ret) {
+ ALOGE("Failed to bind uevent socket %d", -errno);
+ return -errno;
+ }
+
+ FD_ZERO(&fds_);
+ FD_SET(drm_->fd(), &fds_);
+ FD_SET(uevent_fd_.get(), &fds_);
+ max_fd_ = std::max(drm_->fd(), uevent_fd_.get());
+
+ return InitWorker();
+}
+
+void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) {
+ assert(!hotplug_handler_);
+ hotplug_handler_ = handler;
+}
+
+void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *user_data) {
+ DrmEventHandler *handler = (DrmEventHandler *)user_data;
+ if (!handler)
+ return;
+
+ handler->HandleEvent((uint64_t)tv_sec * 1000 * 1000 + tv_usec);
+ delete handler;
+}
+
+void DrmEventListener::UEventHandler() {
+ char buffer[1024];
+ int ret;
+
+ struct timespec ts;
+ uint64_t timestamp = 0;
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (!ret)
+ timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ else
+ ALOGE("Failed to get monotonic clock on hotplug %d", ret);
+
+ while (true) {
+ ret = read(uevent_fd_.get(), &buffer, sizeof(buffer));
+ if (ret == 0) {
+ return;
+ } else if (ret < 0) {
+ ALOGE("Got error reading uevent %d", ret);
+ return;
+ }
+
+ if (!hotplug_handler_)
+ continue;
+
+ bool drm_event = false, hotplug_event = false;
+ for (int i = 0; i < ret;) {
+ char *event = buffer + i;
+ if (strcmp(event, "DEVTYPE=drm_minor"))
+ drm_event = true;
+ else if (strcmp(event, "HOTPLUG=1"))
+ hotplug_event = true;
+
+ i += strlen(event) + 1;
+ }
+
+ if (drm_event && hotplug_event)
+ hotplug_handler_->HandleEvent(timestamp);
+ }
+}
+
+void DrmEventListener::Routine() {
+ int ret;
+ do {
+ ret = select(max_fd_ + 1, &fds_, NULL, NULL, NULL);
+ } while (ret == -1 && errno == EINTR);
+
+ if (FD_ISSET(drm_->fd(), &fds_)) {
+ drmEventContext event_context = {
+ .version = DRM_EVENT_CONTEXT_VERSION,
+ .vblank_handler = NULL,
+ .page_flip_handler = DrmEventListener::FlipHandler};
+ drmHandleEvent(drm_->fd(), &event_context);
+ }
+
+ if (FD_ISSET(uevent_fd_.get(), &fds_))
+ UEventHandler();
+}
+}
diff --git a/drmeventlistener.h b/drmeventlistener.h
new file mode 100644
index 0000000..61eefe8
--- /dev/null
+++ b/drmeventlistener.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_EVENT_LISTENER_H_
+#define ANDROID_DRM_EVENT_LISTENER_H_
+
+#include "autofd.h"
+#include "worker.h"
+
+namespace android {
+
+class DrmResources;
+
+class DrmEventHandler {
+ public:
+ DrmEventHandler() {
+ }
+ virtual ~DrmEventHandler() {
+ }
+
+ virtual void HandleEvent(uint64_t timestamp_us) = 0;
+};
+
+class DrmEventListener : public Worker {
+ public:
+ DrmEventListener(DrmResources *drm);
+ virtual ~DrmEventListener() {
+ }
+
+ int Init();
+
+ void RegisterHotplugHandler(DrmEventHandler *handler);
+
+ static void FlipHandler(int fd, unsigned int sequence, unsigned int tv_sec,
+ unsigned int tv_usec, void *user_data);
+
+ protected:
+ virtual void Routine();
+
+ private:
+ void UEventHandler();
+
+ fd_set fds_;
+ UniqueFd uevent_fd_;
+ int max_fd_ = -1;
+
+ DrmResources *drm_;
+ DrmEventHandler *hotplug_handler_ = NULL;
+};
+}
+
+#endif
diff --git a/drmframebuffer.h b/drmframebuffer.h
new file mode 100755
index 0000000..9826e43
--- /dev/null
+++ b/drmframebuffer.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_FRAMEBUFFER_
+#define ANDROID_DRM_FRAMEBUFFER_
+
+#include <stdint.h>
+
+#include <sync/sync.h>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+#if RK_RGA
+struct DrmRgaBuffer {
+ DrmRgaBuffer() : release_fence_fd_(-1) {
+ }
+
+ ~DrmRgaBuffer() {
+ if (release_fence_fd() >= 0)
+ close(release_fence_fd());
+ }
+
+ bool is_valid() {
+ return buffer_ != NULL;
+ }
+
+ sp<GraphicBuffer> buffer() {
+ return buffer_;
+ }
+
+ int release_fence_fd() {
+ return release_fence_fd_;
+ }
+
+ void set_release_fence_fd(int fd) {
+ if (release_fence_fd_ >= 0)
+ close(release_fence_fd_);
+ release_fence_fd_ = fd;
+ }
+
+ bool Allocate(uint32_t w, uint32_t h, int32_t format) {
+ if (is_valid()) {
+ if (buffer_->getWidth() == w && buffer_->getHeight() == h && buffer_->getPixelFormat() == format)
+ return true;
+
+ if (release_fence_fd_ >= 0) {
+ if (sync_wait(release_fence_fd_, kReleaseWaitTimeoutMs) != 0) {
+ ALOGE("Wait for release fence failed\n");
+ return false;
+ }
+ }
+ Clear();
+ }
+ ALOGD_IF(log_level(DBG_DEBUG), "RGA Allocate buffer %d x %d", w, h);
+ buffer_ = new GraphicBuffer(w, h, format,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ "DRM_HWC_RgaBuffer");
+ release_fence_fd_ = -1;
+ return is_valid();
+ }
+
+ void Clear() {
+ if (!is_valid())
+ return;
+
+ if (release_fence_fd_ >= 0) {
+ close(release_fence_fd_);
+ release_fence_fd_ = -1;
+ }
+ ALOGD_IF(log_level(DBG_DEBUG), "RGA free buffer %d x %d", buffer_->getWidth(), buffer_->getHeight());
+ buffer_.clear();
+ }
+
+ int WaitReleased(int timeout_milliseconds) {
+ if (!is_valid())
+ return 0;
+ if (release_fence_fd_ < 0)
+ return 0;
+
+ int ret = sync_wait(release_fence_fd_, timeout_milliseconds);
+ return ret;
+ }
+
+ // Somewhat arbitrarily chosen, but wanted to stay below 3000ms, which is the
+ // system timeout
+ static const int kReleaseWaitTimeoutMs = 1500;
+
+ private:
+ sp<GraphicBuffer> buffer_;
+ int release_fence_fd_;
+};
+#endif
+
+struct DrmFramebuffer {
+ DrmFramebuffer() : release_fence_fd_(-1) {
+ }
+
+ ~DrmFramebuffer() {
+ if (release_fence_fd() >= 0)
+ close(release_fence_fd());
+ }
+
+ bool is_valid() {
+ return buffer_ != NULL;
+ }
+
+ sp<GraphicBuffer> buffer() {
+ return buffer_;
+ }
+
+ int release_fence_fd() {
+ return release_fence_fd_;
+ }
+
+ void set_release_fence_fd(int fd) {
+ if (release_fence_fd_ >= 0)
+ close(release_fence_fd_);
+ release_fence_fd_ = fd;
+ }
+
+ bool Allocate(uint32_t w, uint32_t h) {
+ if (is_valid()) {
+ if (buffer_->getWidth() == w && buffer_->getHeight() == h)
+ return true;
+
+ if (release_fence_fd_ >= 0) {
+ if (sync_wait(release_fence_fd_, kReleaseWaitTimeoutMs) != 0) {
+ ALOGE("Wait for release fence failed\n");
+ return false;
+ }
+ }
+ Clear();
+ }
+
+ buffer_ = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_COMPOSER
+//close fbdc for pre-comp and squash layer.
+#if USE_AFBC_LAYER
+ | MAGIC_USAGE_FOR_AFBC_LAYER
+#endif
+ , "DRM_HWC_Framebuffer");
+ release_fence_fd_ = -1;
+ return is_valid();
+ }
+
+ void Clear() {
+ if (!is_valid())
+ return;
+
+ if (release_fence_fd_ >= 0) {
+ close(release_fence_fd_);
+ release_fence_fd_ = -1;
+ }
+
+ buffer_.clear();
+ }
+
+ int WaitReleased(int timeout_milliseconds) {
+ if (!is_valid())
+ return 0;
+ if (release_fence_fd_ < 0)
+ return 0;
+
+ int ret = sync_wait(release_fence_fd_, timeout_milliseconds);
+ return ret;
+ }
+
+ // Somewhat arbitrarily chosen, but wanted to stay below 3000ms, which is the
+ // system timeout
+ static const int kReleaseWaitTimeoutMs = 1500;
+
+ private:
+ sp<GraphicBuffer> buffer_;
+ int release_fence_fd_;
+};
+}
+
+#endif // ANDROID_DRM_FRAMEBUFFER_
diff --git a/drmhwcgralloc.h b/drmhwcgralloc.h
new file mode 100644
index 0000000..1255d38
--- /dev/null
+++ b/drmhwcgralloc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRMHWCGRALLOC_H_
+#define ANDROID_DRMHWCGRALLOC_H_
+
+#include <stdint.h>
+#include <hardware/gralloc.h>
+
+typedef struct hwc_drm_bo {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format; /* DRM_FORMAT_* from drm_fourcc.h */
+ uint32_t pitches[4];
+ uint32_t offsets[4];
+ uint32_t gem_handles[4];
+ uint32_t fb_id;
+ int acquire_fence_fd;
+ void *priv;
+} hwc_drm_bo_t;
+
+#endif // ANDROID_DRMHWCGRALLOC_H_
diff --git a/drmhwcomposer.h b/drmhwcomposer.h
new file mode 100755
index 0000000..f2e2355
--- /dev/null
+++ b/drmhwcomposer.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_HWCOMPOSER_H_
+#define ANDROID_DRM_HWCOMPOSER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include "autofd.h"
+#include "separate_rects.h"
+#include "drmhwcgralloc.h"
+#include "hwc_debug.h"
+
+/*hwc version*/
+#define GHWC_VERSION "0.26"
+
+/* hdr usage */
+#define HDRUSAGE 0x3000000
+
+struct hwc_import_context;
+
+int hwc_import_init(struct hwc_import_context **ctx);
+int hwc_import_destroy(struct hwc_import_context *ctx);
+
+int hwc_import_bo_create(int fd, struct hwc_import_context *ctx,
+ buffer_handle_t buf, struct hwc_drm_bo *bo);
+bool hwc_import_bo_release(int fd, struct hwc_import_context *ctx,
+ struct hwc_drm_bo *bo);
+
+namespace android {
+
+#define UN_USED(arg) (arg=arg)
+
+#if USE_AFBC_LAYER
+#define GRALLOC_ARM_INTFMT_EXTENSION_BIT_START 32
+/* This format will use AFBC */
+#define GRALLOC_ARM_INTFMT_AFBC (1ULL << (GRALLOC_ARM_INTFMT_EXTENSION_BIT_START+0))
+#define SKIP_BOOT (1)
+#define MAGIC_USAGE_FOR_AFBC_LAYER (0x88)
+#endif
+
+#if SKIP_BOOT
+#define BOOT_COUNT (2)
+#endif
+
+typedef enum tagMode3D
+{
+ NON_3D = 0,
+ H_3D=1,
+ V_3D=2,
+ FPS_3D=8,
+}Mode3D;
+
+class Importer;
+
+class DrmHwcBuffer {
+ public:
+ DrmHwcBuffer() = default;
+ DrmHwcBuffer(const hwc_drm_bo &bo, Importer *importer)
+ : bo_(bo), importer_(importer) {
+ }
+ DrmHwcBuffer(DrmHwcBuffer &&rhs) : bo_(rhs.bo_), importer_(rhs.importer_) {
+ rhs.importer_ = NULL;
+ }
+
+ ~DrmHwcBuffer() {
+ Clear();
+ }
+
+ DrmHwcBuffer &operator=(DrmHwcBuffer &&rhs) {
+ Clear();
+ importer_ = rhs.importer_;
+ rhs.importer_ = NULL;
+ bo_ = rhs.bo_;
+ return *this;
+ }
+
+ operator bool() const {
+ return importer_ != NULL;
+ }
+
+ const hwc_drm_bo *operator->() const;
+
+ void Clear();
+
+#if RK_VIDEO_SKIP_LINE
+ int ImportBuffer(buffer_handle_t handle, Importer *importer, bool bSkipLine);
+#else
+ int ImportBuffer(buffer_handle_t handle, Importer *importer);
+#endif
+
+ private:
+ hwc_drm_bo bo_;
+ Importer *importer_ = NULL;
+};
+
+class DrmHwcNativeHandle {
+ public:
+ DrmHwcNativeHandle() = default;
+
+ DrmHwcNativeHandle(const gralloc_module_t *gralloc, native_handle_t *handle)
+ : gralloc_(gralloc), handle_(handle) {
+ }
+
+ DrmHwcNativeHandle(DrmHwcNativeHandle &&rhs) {
+ gralloc_ = rhs.gralloc_;
+ rhs.gralloc_ = NULL;
+ handle_ = rhs.handle_;
+ rhs.handle_ = NULL;
+ }
+
+ ~DrmHwcNativeHandle();
+
+ DrmHwcNativeHandle &operator=(DrmHwcNativeHandle &&rhs) {
+ Clear();
+ gralloc_ = rhs.gralloc_;
+ rhs.gralloc_ = NULL;
+ handle_ = rhs.handle_;
+ rhs.handle_ = NULL;
+ return *this;
+ }
+
+ int CopyBufferHandle(buffer_handle_t handle, const gralloc_module_t *gralloc);
+
+ void Clear();
+
+ buffer_handle_t get() const {
+ return handle_;
+ }
+
+ private:
+ const gralloc_module_t *gralloc_ = NULL;
+ native_handle_t *handle_ = NULL;
+};
+
+template <typename T>
+using DrmHwcRect = separate_rects::Rect<T>;
+
+enum DrmHwcTransform {
+ kIdentity = 0,
+ kFlipH = 1 << 0,
+ kFlipV = 1 << 1,
+ kRotate90 = 1 << 2,
+ kRotate180 = 1 << 3,
+ kRotate270 = 1 << 4,
+ kRotate0 = 1 << 5
+};
+
+enum class DrmHwcBlending : int32_t {
+ kNone = HWC_BLENDING_NONE,
+ kPreMult = HWC_BLENDING_PREMULT,
+ kCoverage = HWC_BLENDING_COVERAGE,
+};
+
+struct DrmHwcLayer {
+ buffer_handle_t sf_handle = NULL;
+ int gralloc_buffer_usage = 0;
+ DrmHwcBuffer buffer;
+ DrmHwcNativeHandle handle;
+ uint32_t transform;
+ DrmHwcBlending blending = DrmHwcBlending::kNone;
+ uint8_t alpha = 0xff;
+ uint32_t frame_no = 0;
+ DrmHwcRect<float> source_crop;
+ DrmHwcRect<int> display_frame;
+ std::vector<DrmHwcRect<int>> source_damage;
+
+ UniqueFd acquire_fence;
+ OutputFd release_fence;
+
+ bool bSkipLayer;
+ bool is_match;
+ bool is_take;
+ bool is_yuv;
+ bool is_scale;
+ bool is_large;
+ int zpos;
+
+#if USE_AFBC_LAYER
+ uint64_t internal_format;
+ bool is_afbc;
+#endif
+
+#if RK_RGA
+ bool is_rotate_by_rga;
+#endif
+ float h_scale_mul;
+ float v_scale_mul;
+#if RK_VIDEO_SKIP_LINE
+ bool bSkipLine;
+#endif
+ bool bClone_;
+ bool bFbTarget_;
+ bool bUse;
+ bool bMix;
+ int stereo;
+ hwc_layer_1_t *raw_sf_layer;
+ int format;
+ int width;
+ int height;
+ int stride;
+ int bpp;
+ int group_id;
+ int share_id;
+ std::string name;
+ size_t index;
+ hwc_layer_1_t *mlayer;
+
+ int ImportBuffer(hwc_layer_1_t *sf_layer, Importer *importer);
+ int InitFromHwcLayer(struct hwc_context_t *ctx, int display, hwc_layer_1_t *sf_layer, Importer *importer,
+ const gralloc_module_t *gralloc, bool bClone);
+
+void dump_drm_layer(int index, std::ostringstream *out) const;
+
+ buffer_handle_t get_usable_handle() const {
+ return handle.get() != NULL ? handle.get() : sf_handle;
+ }
+
+ bool protected_usage() const {
+ return (gralloc_buffer_usage & GRALLOC_USAGE_PROTECTED) ==
+ GRALLOC_USAGE_PROTECTED;
+ }
+};
+
+struct DrmHwcDisplayContents {
+ OutputFd retire_fence;
+ std::vector<DrmHwcLayer> layers;
+};
+}
+
+#endif
diff --git a/drmmode.cpp b/drmmode.cpp
new file mode 100644
index 0000000..af077e5
--- /dev/null
+++ b/drmmode.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "drmmode.h"
+#include "drmresources.h"
+
+#include <stdint.h>
+#include <string>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmMode::DrmMode(drmModeModeInfoPtr m)
+ : id_(0),
+ clock_(m->clock),
+ h_display_(m->hdisplay),
+ h_sync_start_(m->hsync_start),
+ h_sync_end_(m->hsync_end),
+ h_total_(m->htotal),
+ h_skew_(m->hskew),
+ v_display_(m->vdisplay),
+ v_sync_start_(m->vsync_start),
+ v_sync_end_(m->vsync_end),
+ v_total_(m->vtotal),
+ v_scan_(m->vscan),
+ v_refresh_(m->vrefresh),
+ flags_(m->flags),
+ type_(m->type),
+ name_(m->name) {
+}
+
+DrmMode::~DrmMode()
+{
+ if (blob_id_ && fd_) {
+ struct drm_mode_destroy_blob destroy_blob;
+ memset(&destroy_blob, 0, sizeof(destroy_blob));
+ destroy_blob.blob_id = (__u32)blob_id_;
+ int ret = drmIoctl(fd_, DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
+ if (ret)
+ ALOGE("Failed to destroy mode property blob %d, ret=%d", blob_id_, ret);
+ }
+}
+
+bool DrmMode::operator==(const drmModeModeInfo &m) const {
+ return clock_ == m.clock && h_display_ == m.hdisplay &&
+ h_sync_start_ == m.hsync_start && h_sync_end_ == m.hsync_end &&
+ h_total_ == m.htotal && h_skew_ == m.hskew &&
+ v_display_ == m.vdisplay && v_sync_start_ == m.vsync_start &&
+ v_sync_end_ == m.vsync_end && v_total_ == m.vtotal &&
+ v_scan_ == m.vscan && flags_ == m.flags && type_ == m.type;
+}
+
+bool DrmMode::operator==(const DrmMode &m) const {
+ return clock_ == m.clock() && h_display_ == m.h_display()&&
+ h_sync_start_ == m.h_sync_start() && h_sync_end_ == m.h_sync_end() &&
+ h_total_ == m.h_total() && h_skew_ == m.h_skew() &&
+ v_display_ == m.v_display() && v_sync_start_ == m.v_sync_start() &&
+ v_sync_end_ == m.v_sync_end() && v_total_ == m.v_total() &&
+ v_scan_ == m.v_scan() && flags_ == m.flags() && type_ == m.type();
+}
+
+bool DrmMode::equal(uint32_t width, uint32_t height, uint32_t vrefresh,
+ bool interlaced) const
+{
+ int interlaced_ = !!(flags_ & DRM_MODE_FLAG_INTERLACE);
+
+ if (h_display_ == width && v_display_ == height &&
+ interlaced_ == interlaced && v_refresh_ == vrefresh)
+ return true;
+ return false;
+}
+
+bool DrmMode::equal(uint32_t width, uint32_t height, uint32_t vrefresh,
+ uint32_t flag, uint32_t clk, bool interlaced) const
+{
+ int interlaced_ = !!(flags_ & DRM_MODE_FLAG_INTERLACE);
+ ALOGV("DrmMode h=%d,v=%d,interlaced=%d,v_refresh_=%d,flags=%d,clk=%d",
+ h_display_, v_display_, interlaced_, v_refresh_, flags_,clock_);
+ if (h_display_ == width && v_display_ == height &&
+ interlaced_ == interlaced && v_refresh_ == vrefresh &&
+ flags_ == flag && clock_ == clk)
+ return true;
+ return false;
+}
+
+void DrmMode::ToDrmModeModeInfo(drm_mode_modeinfo *m) const {
+ m->clock = clock_;
+ m->hdisplay = h_display_;
+ m->hsync_start = h_sync_start_;
+ m->hsync_end = h_sync_end_;
+ m->htotal = h_total_;
+ m->hskew = h_skew_;
+ m->vdisplay = v_display_;
+ m->vsync_start = v_sync_start_;
+ m->vsync_end = v_sync_end_;
+ m->vtotal = v_total_;
+ m->vscan = v_scan_;
+ m->vrefresh = v_refresh_;
+ m->flags = flags_;
+ m->type = type_;
+ strncpy(m->name, name_.c_str(), DRM_DISPLAY_MODE_LEN);
+}
+
+uint32_t DrmMode::id() const {
+ return id_;
+}
+
+void DrmMode::set_id(uint32_t id) {
+ id_ = id;
+}
+
+uint32_t DrmMode::clock() const {
+ return clock_;
+}
+
+uint32_t DrmMode::h_display() const {
+ return h_display_;
+}
+
+uint32_t DrmMode::h_sync_start() const {
+ return h_sync_start_;
+}
+
+uint32_t DrmMode::h_sync_end() const {
+ return h_sync_end_;
+}
+
+uint32_t DrmMode::h_total() const {
+ return h_total_;
+}
+
+uint32_t DrmMode::h_skew() const {
+ return h_skew_;
+}
+
+uint32_t DrmMode::v_display() const {
+ return v_display_;
+}
+
+uint32_t DrmMode::v_sync_start() const {
+ return v_sync_start_;
+}
+
+uint32_t DrmMode::v_sync_end() const {
+ return v_sync_end_;
+}
+
+uint32_t DrmMode::v_total() const {
+ return v_total_;
+}
+
+uint32_t DrmMode::v_scan() const {
+ return v_scan_;
+}
+
+float DrmMode::v_refresh() const {
+ return v_refresh_ ? v_refresh_ * 1.0f :
+ clock_ / (float)(v_total_ * h_total_) * 1000.0f;
+}
+
+uint32_t DrmMode::flags() const {
+ return flags_;
+}
+
+uint32_t DrmMode::type() const {
+ return type_;
+}
+
+std::string DrmMode::name() const {
+ return name_;
+}
+}
diff --git a/drmmode.h b/drmmode.h
new file mode 100644
index 0000000..0cb8796
--- /dev/null
+++ b/drmmode.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_MODE_H_
+#define ANDROID_DRM_MODE_H_
+
+#include <stdint.h>
+#include <string>
+#include <xf86drmMode.h>
+
+namespace android {
+
+class DrmMode {
+ public:
+ DrmMode() = default;
+ DrmMode(drmModeModeInfoPtr m);
+ ~DrmMode();
+
+ bool operator==(const drmModeModeInfo &m) const;
+ bool operator==(const DrmMode &m) const;
+ bool equal(uint32_t width, uint32_t height, uint32_t vrefresh, bool interlaced) const;
+ bool equal(uint32_t width, uint32_t height, uint32_t vrefresh,
+ uint32_t flag, uint32_t clk, bool interlaced) const;
+ void ToDrmModeModeInfo(drm_mode_modeinfo *m) const;
+
+ uint32_t id() const;
+ void set_id(uint32_t id);
+
+ uint32_t clock() const;
+
+ uint32_t h_display() const;
+ uint32_t h_sync_start() const;
+ uint32_t h_sync_end() const;
+ uint32_t h_total() const;
+ uint32_t h_skew() const;
+
+ uint32_t v_display() const;
+ uint32_t v_sync_start() const;
+ uint32_t v_sync_end() const;
+ uint32_t v_total() const;
+ uint32_t v_scan() const;
+ float v_refresh() const;
+
+ uint32_t flags() const;
+ uint32_t type() const;
+
+ std::string name() const;
+
+ private:
+ int fd_ = 0;
+ uint32_t id_ = 0;
+ uint32_t blob_id_ = 0;
+
+ uint32_t clock_ = 0;
+
+ uint32_t h_display_ = 0;
+ uint32_t h_sync_start_ = 0;
+ uint32_t h_sync_end_ = 0;
+ uint32_t h_total_ = 0;
+ uint32_t h_skew_ = 0;
+
+ uint32_t v_display_ = 0;
+ uint32_t v_sync_start_ = 0;
+ uint32_t v_sync_end_ = 0;
+ uint32_t v_total_ = 0;
+ uint32_t v_scan_ = 0;
+ uint32_t v_refresh_ = 0;
+
+ uint32_t flags_ = 0;
+ uint32_t type_ = 0;
+
+ std::string name_;
+};
+}
+
+#endif // ANDROID_DRM_MODE_H_
diff --git a/drmplane.cpp b/drmplane.cpp
new file mode 100755
index 0000000..cf5e06b
--- /dev/null
+++ b/drmplane.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-plane"
+
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <cinttypes>
+#include <errno.h>
+#include <stdint.h>
+
+#include <cutils/log.h>
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmPlane::DrmPlane(DrmResources *drm, drmModePlanePtr p)
+ : drm_(drm), id_(p->plane_id), possible_crtc_mask_(p->possible_crtcs), plane_(p) {
+}
+
+int DrmPlane::Init() {
+ DrmProperty p;
+
+ int ret = drm_->GetPlaneProperty(*this, "type", &p);
+ if (ret) {
+ ALOGE("Could not get plane type property");
+ return ret;
+ }
+
+ uint64_t type;
+ ret = p.value(&type);
+ if (ret) {
+ ALOGE("Failed to get plane type property value");
+ return ret;
+ }
+ switch (type) {
+ case DRM_PLANE_TYPE_OVERLAY:
+ case DRM_PLANE_TYPE_PRIMARY:
+ case DRM_PLANE_TYPE_CURSOR:
+ type_ = (uint32_t)type;
+ break;
+ default:
+ ALOGE("Invalid plane type %" PRIu64, type);
+ return -EINVAL;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "CRTC_ID", &crtc_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_ID property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "FB_ID", &fb_property_);
+ if (ret) {
+ ALOGE("Could not get FB_ID property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "CRTC_X", &crtc_x_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_X property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "CRTC_Y", &crtc_y_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_Y property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "CRTC_W", &crtc_w_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_W property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "CRTC_H", &crtc_h_property_);
+ if (ret) {
+ ALOGE("Could not get CRTC_H property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "SRC_X", &src_x_property_);
+ if (ret) {
+ ALOGE("Could not get SRC_X property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "SRC_Y", &src_y_property_);
+ if (ret) {
+ ALOGE("Could not get SRC_Y property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "SRC_W", &src_w_property_);
+ if (ret) {
+ ALOGE("Could not get SRC_W property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "SRC_H", &src_h_property_);
+ if (ret) {
+ ALOGE("Could not get SRC_H property");
+ return ret;
+ }
+
+ ret = drm_->GetPlaneProperty(*this, "rotation", &rotation_property_);
+ if (ret)
+ ALOGE("Could not get rotation property");
+
+ ret = drm_->GetPlaneProperty(*this, "alpha", &alpha_property_);
+ if (ret)
+ ALOGI("Could not get alpha property");
+
+ ret = drm_->GetPlaneProperty(*this, "ZPOS", &zpos_property_);
+ if (ret)
+ ALOGE("Could not get ZPOS property");
+
+ ret = drm_->GetPlaneProperty(*this, "SHARE_ID", &share_id_property_);
+ if (ret)
+ ALOGE("Could not get SHARE_ID property");
+
+ ret = drm_->GetPlaneProperty(*this, "FEATURE", &feature_property_);
+ if (ret)
+ ALOGE("Could not get FEATURE property");
+
+ b_reserved_= false;
+ b_use_ = false;
+ b_yuv_ = false;
+
+
+ uint64_t feature=0, rotate=0;
+
+ feature_property_.set_feature("scale");
+ feature_property_.value(&feature);
+ b_scale_ = (feature ==1)?true:false;
+
+
+ rotation_property().set_feature("rotate");
+ rotation_property().value(&rotate);
+ b_rotate_ = rotate;
+
+ return 0;
+}
+
+uint32_t DrmPlane::id() const {
+ return id_;
+}
+
+bool DrmPlane::GetCrtcSupported(const DrmCrtc &crtc) const {
+ return !!((1 << crtc.pipe()) & possible_crtc_mask_);
+}
+
+uint32_t DrmPlane::type() const {
+ return type_;
+}
+
+const DrmProperty &DrmPlane::crtc_property() const {
+ return crtc_property_;
+}
+
+const DrmProperty &DrmPlane::fb_property() const {
+ return fb_property_;
+}
+
+const DrmProperty &DrmPlane::crtc_x_property() const {
+ return crtc_x_property_;
+}
+
+const DrmProperty &DrmPlane::crtc_y_property() const {
+ return crtc_y_property_;
+}
+
+const DrmProperty &DrmPlane::crtc_w_property() const {
+ return crtc_w_property_;
+}
+
+const DrmProperty &DrmPlane::crtc_h_property() const {
+ return crtc_h_property_;
+}
+
+const DrmProperty &DrmPlane::src_x_property() const {
+ return src_x_property_;
+}
+
+const DrmProperty &DrmPlane::src_y_property() const {
+ return src_y_property_;
+}
+
+const DrmProperty &DrmPlane::src_w_property() const {
+ return src_w_property_;
+}
+
+const DrmProperty &DrmPlane::src_h_property() const {
+ return src_h_property_;
+}
+
+const DrmProperty &DrmPlane::rotation_property() const {
+ return rotation_property_;
+}
+
+const DrmProperty &DrmPlane::alpha_property() const {
+ return alpha_property_;
+}
+
+bool DrmPlane::get_scale(){
+ return b_scale_;
+}
+
+uint64_t DrmPlane::get_rotate(){
+ return b_rotate_;
+}
+
+bool DrmPlane::get_yuv(){
+ return b_yuv_;
+}
+
+void DrmPlane::set_yuv(bool b_yuv)
+{
+ b_yuv_ = b_yuv;
+}
+
+bool DrmPlane::is_use(){
+ return b_use_;
+}
+
+void DrmPlane::set_use(bool b_use)
+{
+ b_use_ = b_use;
+}
+
+const DrmProperty &DrmPlane::zpos_property() const {
+ return zpos_property_;
+}
+
+const DrmProperty &DrmPlane::share_id_property() const {
+ return share_id_property_;
+}
+
+const DrmProperty &DrmPlane::feature_property() const {
+ return feature_property_;
+}
+
+bool DrmPlane::is_reserved(){
+ return b_reserved_;
+}
+
+void DrmPlane::set_reserved(bool b_reserved) {
+ b_reserved_ = b_reserved;
+}
+
+void DrmPlane::dump_plane(std::ostringstream *out) const {
+ unsigned int j;
+
+ *out << plane_->plane_id << "\t"
+ << plane_->crtc_id << "\t"
+ << plane_->fb_id << "\t"
+ << plane_->crtc_x << "\t"
+ << plane_->crtc_y << "\t"
+ << plane_->x << "\t"
+ << plane_->y << "\t"
+ << plane_->gamma_size << "\t"
+ << std::hex << plane_->possible_crtcs << "\n";
+
+ if (!plane_->count_formats)
+ return;
+
+ *out << " formats:";
+ for (j = 0; j < plane_->count_formats; j++)
+ *out << &plane_->formats[j] << " "; //printf(" %4.4s", (char *)&ovr->formats[j]);
+
+ *out << ("\n");
+
+ drm_->DumpPlaneProperty(*this,out);
+}
+
+}
diff --git a/drmplane.h b/drmplane.h
new file mode 100755
index 0000000..400fa97
--- /dev/null
+++ b/drmplane.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_PLANE_H_
+#define ANDROID_DRM_PLANE_H_
+
+#include "drmcrtc.h"
+#include "drmproperty.h"
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+#include <vector>
+
+namespace android {
+
+enum DrmWinType {
+ DRM_WIN0,
+ DRM_WIN1,
+ DRM_WIN2,
+ DRM_WIN3,
+ DRM_CURSOR,
+};
+
+enum DrmAreaType {
+ DRM_AREA0,
+ DRM_AREA1,
+ DRM_AREA2_0,
+ DRM_AREA2_1,
+ DRM_AREA2_2,
+ DRM_AREA2_3,
+ DRM_AREA3_0,
+ DRM_AREA3_1,
+ DRM_AREA3_2,
+ DRM_AREA3_3,
+};
+
+class DrmResources;
+
+class DrmPlane {
+ public:
+ DrmPlane(DrmResources *drm, drmModePlanePtr p);
+ DrmPlane(const DrmPlane &) = delete;
+ DrmPlane &operator=(const DrmPlane &) = delete;
+
+ int Init();
+
+ uint32_t id() const;
+
+ bool GetCrtcSupported(const DrmCrtc &crtc) const;
+
+ uint32_t type() const;
+
+ const DrmProperty &crtc_property() const;
+ const DrmProperty &fb_property() const;
+ const DrmProperty &crtc_x_property() const;
+ const DrmProperty &crtc_y_property() const;
+ const DrmProperty &crtc_w_property() const;
+ const DrmProperty &crtc_h_property() const;
+ const DrmProperty &src_x_property() const;
+ const DrmProperty &src_y_property() const;
+ const DrmProperty &src_w_property() const;
+ const DrmProperty &src_h_property() const;
+ const DrmProperty &rotation_property() const;
+ const DrmProperty &alpha_property() const;
+ bool is_use();
+ void set_use(bool b_use);
+ bool get_scale();
+ uint64_t get_rotate();
+ bool get_yuv();
+ void set_yuv(bool b_yuv);
+ const DrmProperty &zpos_property() const;
+ const DrmProperty &share_id_property() const;
+ const DrmProperty &feature_property() const;
+ bool is_reserved();
+ void set_reserved(bool b_reserved);
+ inline uint32_t get_possible_crtc_mask() const
+ {
+ return possible_crtc_mask_;
+ }
+
+ void dump_plane(std::ostringstream *out) const;
+
+
+ private:
+ DrmResources *drm_;
+ uint32_t id_;
+
+ uint32_t possible_crtc_mask_;
+
+ uint32_t type_;
+
+ DrmProperty crtc_property_;
+ DrmProperty fb_property_;
+ DrmProperty crtc_x_property_;
+ DrmProperty crtc_y_property_;
+ DrmProperty crtc_w_property_;
+ DrmProperty crtc_h_property_;
+ DrmProperty src_x_property_;
+ DrmProperty src_y_property_;
+ DrmProperty src_w_property_;
+ DrmProperty src_h_property_;
+ DrmProperty rotation_property_;
+ DrmProperty alpha_property_;
+
+ DrmProperty zpos_property_;
+ DrmProperty share_id_property_;
+ DrmProperty feature_property_;
+ bool b_reserved_;
+ bool b_use_;
+ bool b_yuv_;
+ bool b_scale_;
+ bool b_rotate_;
+
+ drmModePlanePtr plane_;
+};
+}
+
+#endif // ANDROID_DRM_PLANE_H_
diff --git a/drmproperty.cpp b/drmproperty.cpp
new file mode 100755
index 0000000..ba7a589
--- /dev/null
+++ b/drmproperty.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "drmproperty.h"
+#include "drmresources.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <string>
+
+#include <xf86drmMode.h>
+
+namespace android {
+
+DrmProperty::DrmPropertyEnum::DrmPropertyEnum(drm_mode_property_enum *e)
+ : value_(e->value), name_(e->name) {
+}
+
+DrmProperty::DrmPropertyEnum::~DrmPropertyEnum() {
+}
+
+DrmProperty::DrmProperty(drmModePropertyPtr p, uint64_t value)
+ : id_(0), type_(DRM_PROPERTY_TYPE_INVALID), flags_(0), name_("") {
+ Init(p, value);
+}
+
+void DrmProperty::Init(drmModePropertyPtr p, uint64_t value) {
+ id_ = p->prop_id;
+ flags_ = p->flags;
+ name_ = p->name;
+ value_ = value;
+
+ for (int i = 0; i < p->count_values; ++i)
+ values_.push_back(p->values[i]);
+
+ for (int i = 0; i < p->count_enums; ++i)
+ enums_.push_back(DrmPropertyEnum(&p->enums[i]));
+
+ for (int i = 0; i < p->count_blobs; ++i)
+ blob_ids_.push_back(p->blob_ids[i]);
+
+
+ if (flags_ & DRM_MODE_PROP_RANGE)
+ type_ = DRM_PROPERTY_TYPE_INT;
+ else if (flags_ & DRM_MODE_PROP_ENUM)
+ type_ = DRM_PROPERTY_TYPE_ENUM;
+ else if (flags_ & DRM_MODE_PROP_OBJECT)
+ type_ = DRM_PROPERTY_TYPE_OBJECT;
+ else if (flags_ & DRM_MODE_PROP_BLOB)
+ type_ = DRM_PROPERTY_TYPE_BLOB;
+ else if (flags_ & DRM_MODE_PROP_BITMASK)
+ type_ = DRM_PROPERTY_TYPE_BITMASK;
+
+ feature_name_ = NULL;
+}
+
+uint32_t DrmProperty::id() const {
+ return id_;
+}
+
+std::string DrmProperty::name() const {
+ return name_;
+}
+
+void DrmProperty::set_feature(const char* pcFeature) const{
+ feature_name_ = pcFeature;
+}
+
+int DrmProperty::value(uint64_t *value) const {
+ if (type_ == DRM_PROPERTY_TYPE_BLOB) {
+ *value = value_;
+ return 0;
+ }
+
+ if (values_.size() == 0)
+ return -ENOENT;
+
+ switch (type_) {
+ case DRM_PROPERTY_TYPE_INT:
+ *value = value_;
+ return 0;
+
+ case DRM_PROPERTY_TYPE_ENUM:
+ if (value_ >= enums_.size())
+ return -ENOENT;
+
+ *value = enums_[value_].value_;
+ return 0;
+
+ case DRM_PROPERTY_TYPE_OBJECT:
+ *value = value_;
+ return 0;
+
+ case DRM_PROPERTY_TYPE_BITMASK:
+ if(feature_name_ == NULL)
+ {
+ ALOGE("You don't set feature name for %s",name_.c_str());
+ return -EINVAL;
+ }
+ //if(!strcmp(feature_name_,"scale"))
+ if(strlen(feature_name_) > 0)
+ {
+ for (auto &drm_enum : enums_)
+ {
+ if(!strncmp(drm_enum.name_.c_str(),(const char*)feature_name_,strlen(feature_name_)))
+ {
+ *value = (value_ & (1LL << drm_enum.value_));
+ break;
+ }
+ }
+ }
+ else
+ {
+ *value = value_;
+ }
+
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+}
diff --git a/drmproperty.h b/drmproperty.h
new file mode 100755
index 0000000..b0ddd5f
--- /dev/null
+++ b/drmproperty.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_PROPERTY_H_
+#define ANDROID_DRM_PROPERTY_H_
+
+#include <stdint.h>
+#include <string>
+#include <xf86drmMode.h>
+#include <vector>
+
+namespace android {
+
+enum DrmPropertyType {
+ DRM_PROPERTY_TYPE_INT,
+ DRM_PROPERTY_TYPE_ENUM,
+ DRM_PROPERTY_TYPE_OBJECT,
+ DRM_PROPERTY_TYPE_BLOB,
+ DRM_PROPERTY_TYPE_BITMASK,
+ DRM_PROPERTY_TYPE_INVALID,
+};
+
+class DrmProperty {
+ public:
+ DrmProperty() = default;
+ DrmProperty(drmModePropertyPtr p, uint64_t value);
+ DrmProperty(const DrmProperty &) = delete;
+ DrmProperty &operator=(const DrmProperty &) = delete;
+
+ void Init(drmModePropertyPtr p, uint64_t value);
+
+ uint32_t id() const;
+ std::string name() const;
+
+ int value(uint64_t *value) const;
+
+ void set_feature(const char* pcFeature)const;
+
+ private:
+ class DrmPropertyEnum {
+ public:
+ DrmPropertyEnum(drm_mode_property_enum *e);
+ ~DrmPropertyEnum();
+
+ uint64_t value_;
+ std::string name_;
+ };
+
+ uint32_t id_ = 0;
+
+ DrmPropertyType type_ = DRM_PROPERTY_TYPE_INVALID;
+ uint32_t flags_ = 0;
+ std::string name_;
+ mutable const char* feature_name_;
+ uint64_t value_ = 0;
+
+ std::vector<uint64_t> values_;
+ std::vector<DrmPropertyEnum> enums_;
+ std::vector<uint32_t> blob_ids_;
+};
+}
+
+#endif // ANDROID_DRM_PROPERTY_H_
diff --git a/drmresources.cpp b/drmresources.cpp
new file mode 100755
index 0000000..90685c4
--- /dev/null
+++ b/drmresources.cpp
@@ -0,0 +1,1059 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-resources"
+
+#include "drmconnector.h"
+#include "drmcrtc.h"
+#include "drmencoder.h"
+#include "drmeventlistener.h"
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <cinttypes>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <drm_fourcc.h>
+
+#include <inttypes.h>
+
+//you can define it in external/libdrm/include/drm/drm.h
+#define DRM_CLIENT_CAP_SHARE_PLANES 4
+
+namespace android {
+
+DrmResources::DrmResources() : compositor_(this), event_listener_(this) {
+}
+
+DrmResources::~DrmResources() {
+ event_listener_.Exit();
+}
+
+bool PlaneSortByZpos(const DrmPlane* plane1,const DrmPlane* plane2)
+{
+ uint64_t zpos1,zpos2;
+ plane1->zpos_property().value(&zpos1);
+ plane2->zpos_property().value(&zpos2);
+ return zpos1 < zpos2;
+}
+
+bool SortByZpos(const PlaneGroup* planeGroup1,const PlaneGroup* planeGroup2)
+{
+ return planeGroup1->zpos < planeGroup2->zpos;
+}
+
+int DrmResources::Init() {
+ char path[PROPERTY_VALUE_MAX];
+ property_get("hwc.drm.device", path, "/dev/dri/card0");
+
+ /* TODO: Use drmOpenControl here instead */
+ fd_.Set(open(path, O_RDWR));
+ if (fd() < 0) {
+ ALOGE("Failed to open dri- %s", strerror(-errno));
+ return -ENODEV;
+ }
+
+ int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ if (ret) {
+ ALOGE("Failed to set universal plane cap %d", ret);
+ return ret;
+ }
+
+ ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
+ if (ret) {
+ ALOGE("Failed to set atomic cap %d", ret);
+ return ret;
+ }
+
+ //Enable 3d function.
+ ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_STEREO_3D, 1);
+ if (ret) {
+ ALOGE("Failed to set stereo 3d cap %d", ret);
+ return ret;
+ }
+
+#if USE_MULTI_AREAS
+ //Open Multi-area support.
+ ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_SHARE_PLANES, 1);
+ if (ret) {
+ ALOGE("Failed to set share planes %d", ret);
+ return ret;
+ }
+#endif
+
+ drmModeResPtr res = drmModeGetResources(fd());
+ if (!res) {
+ ALOGE("Failed to get DrmResources resources");
+ return -ENODEV;
+ }
+
+ bool found_primary = false;
+ int display_num = 1;
+
+ std::ostringstream out;
+ out << "Frame buffers:\n";
+ out << "id\tsize\tpitch\n";
+ for (int i = 0; !ret && i < res->count_fbs; ++i) {
+ drmModeFBPtr fb = drmModeGetFB(fd(), res->fbs[i]);
+ if (!fb) {
+ ALOGE("Failed to get FB %d", res->fbs[i]);
+ ret = -ENODEV;
+ break;
+ }
+
+ out << fb->fb_id << "\t("
+ << fb->width << "x"
+ << fb->height << ")\t"
+ << fb->pitch << "\n";
+
+ drmModeFreeFB(fb);
+ }
+
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ out.str("");
+
+ out << "CRTCs:\n";
+ out << "id\tfb\tpos\tsize\n";
+
+ for (int i = 0; !ret && i < res->count_crtcs; ++i) {
+ drmModeCrtcPtr c = drmModeGetCrtc(fd(), res->crtcs[i]);
+ if (!c) {
+ ALOGE("Failed to get crtc %d", res->crtcs[i]);
+ ret = -ENODEV;
+ break;
+ }
+
+ std::unique_ptr<DrmCrtc> crtc(new DrmCrtc(this, c, i));
+
+ crtc->dump_crtc(&out);
+ out << "\n";
+
+ drmModeFreeCrtc(c);
+
+ ret = crtc->Init();
+ if (ret) {
+ ALOGE("Failed to initialize crtc %d", res->crtcs[i]);
+ break;
+ }
+ crtcs_.emplace_back(std::move(crtc));
+ }
+
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ out.str("");
+
+ out << "Encoders:\n";
+ out << "id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n";
+
+ for (int i = 0; !ret && i < res->count_encoders; ++i) {
+ drmModeEncoderPtr e = drmModeGetEncoder(fd(), res->encoders[i]);
+ if (!e) {
+ ALOGE("Failed to get encoder %d", res->encoders[i]);
+ ret = -ENODEV;
+ break;
+ }
+
+ std::vector<DrmCrtc *> possible_crtcs;
+ DrmCrtc *current_crtc = NULL;
+ for (auto &crtc : crtcs_) {
+ if ((1 << crtc->pipe()) & e->possible_crtcs)
+ possible_crtcs.push_back(crtc.get());
+
+ if (crtc->id() == e->crtc_id)
+ current_crtc = crtc.get();
+ }
+
+ std::unique_ptr<DrmEncoder> enc(
+ new DrmEncoder(this, e, current_crtc, possible_crtcs));
+
+ enc->dump_encoder(&out);
+ out << "\n";
+
+ drmModeFreeEncoder(e);
+
+ encoders_.emplace_back(std::move(enc));
+ }
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ out.str("");
+
+
+ out << "Connectors:\n";
+ out << "id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n";
+ for (int i = 0; !ret && i < res->count_connectors; ++i) {
+ drmModeConnectorPtr c = drmModeGetConnector(fd(), res->connectors[i]);
+ if (!c) {
+ ALOGE("Failed to get connector %d", res->connectors[i]);
+ ret = -ENODEV;
+ break;
+ }
+
+ std::vector<DrmEncoder *> possible_encoders;
+ DrmEncoder *current_encoder = NULL;
+ for (int j = 0; j < c->count_encoders; ++j) {
+ for (auto &encoder : encoders_) {
+ if (encoder->id() == c->encoders[j])
+ possible_encoders.push_back(encoder.get());
+ if (encoder->id() == c->encoder_id)
+ current_encoder = encoder.get();
+ }
+ }
+
+ std::unique_ptr<DrmConnector> conn(
+ new DrmConnector(this, c, current_encoder, possible_encoders));
+
+ conn->dump_connector(&out);
+ out << "\n";
+
+ drmModeFreeConnector(c);
+
+ ret = conn->Init();
+ if (ret) {
+ ALOGE("Init connector %d failed", res->connectors[i]);
+ break;
+ }
+ conn->UpdateModes();
+
+ if (conn->built_in() && !found_primary) {
+ conn->set_display(0);
+ found_primary = true;
+ SetPrimaryDisplay(conn.get());
+ } else {
+ conn->set_display(display_num);
+ ++display_num;
+ }
+
+ connectors_.emplace_back(std::move(conn));
+ }
+
+ if (!found_primary) {
+ display_num = 0;
+ for (auto &conn : connectors_) {
+ conn->set_display(display_num);
+ display_num++;
+ }
+ }
+ for (auto &conn : connectors_) {
+ if (conn->display() == 0)
+ SetPrimaryDisplay(conn.get());
+ else if (conn->state() == DRM_MODE_CONNECTED)
+ SetExtendDisplay(conn.get());
+ }
+
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ out.str("");
+
+ if (res)
+ drmModeFreeResources(res);
+
+ // Catch-all for the above loops
+ if (ret)
+ return ret;
+
+ drmModePlaneResPtr plane_res = drmModeGetPlaneResources(fd());
+ if (!plane_res) {
+ ALOGE("Failed to get plane resources");
+ return -ENOENT;
+ }
+
+ out << "Planes:\n";
+ out << "id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n";
+
+ for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
+ drmModePlanePtr p = drmModeGetPlane(fd(), plane_res->planes[i]);
+ if (!p) {
+ ALOGE("Failed to get plane %d", plane_res->planes[i]);
+ ret = -ENODEV;
+ break;
+ }
+
+ std::unique_ptr<DrmPlane> plane(new DrmPlane(this, p));
+
+ plane->dump_plane(&out);
+ out << "\n";
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s",out.str().c_str());
+ out.str("");
+
+ ret = plane->Init();
+ if (ret) {
+ ALOGE("Init plane %d failed", plane_res->planes[i]);
+ break;
+ }
+ uint64_t share_id,zpos,crtc_id;
+ plane->share_id_property().value(&share_id);
+ plane->zpos_property().value(&zpos);
+ plane->crtc_property().value(&crtc_id);
+
+ std::vector<PlaneGroup *> ::const_iterator iter;
+ for (iter = plane_groups_.begin();
+ iter != plane_groups_.end(); ++iter)
+ {
+ if((*iter)->share_id == share_id /*&& (*iter)->zpos == zpos*/)
+ {
+ (*iter)->planes.push_back(plane.get());
+ break;
+ }
+ }
+ if(iter == plane_groups_.end())
+ {
+ PlaneGroup* plane_group = new PlaneGroup();
+ plane_group->bUse= false;
+ plane_group->zpos = zpos;
+ plane_group->possible_crtcs = p->possible_crtcs;
+ plane_group->share_id = share_id;
+ plane_group->planes.push_back(plane.get());
+ plane_groups_.push_back(plane_group);
+ }
+
+ for (uint32_t j = 0; j < p->count_formats; j++) {
+ if (p->formats[j] == DRM_FORMAT_NV12 ||
+ p->formats[j] == DRM_FORMAT_NV21) {
+ plane->set_yuv(true);
+ }
+ }
+ sort_planes_.emplace_back(plane.get());
+
+ drmModeFreePlane(p);
+
+ planes_.emplace_back(std::move(plane));
+ }
+
+ std::sort(sort_planes_.begin(),sort_planes_.end(),PlaneSortByZpos);
+
+ for (std::vector<DrmPlane*>::const_iterator iter= sort_planes_.begin();
+ iter != sort_planes_.end(); ++iter) {
+ uint64_t share_id,zpos;
+ (*iter)->share_id_property().value(&share_id);
+ (*iter)->zpos_property().value(&zpos);
+ ALOGD_IF(log_level(DBG_VERBOSE),"sort_planes_ share_id=%" PRIu64 ",zpos=%" PRIu64 "",share_id,zpos);
+ }
+
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups_.begin();
+ iter != plane_groups_.end(); ++iter)
+ {
+ ALOGD_IF(log_level(DBG_VERBOSE),"Plane groups: zpos=%d,share_id=%" PRIu64 ",plane size=%zu",
+ (*iter)->zpos,(*iter)->share_id,(*iter)->planes.size());
+ for(std::vector<DrmPlane*> ::const_iterator iter_plane = (*iter)->planes.begin();
+ iter_plane != (*iter)->planes.end(); ++iter_plane)
+ {
+ ALOGD_IF(log_level(DBG_VERBOSE),"\tPlane id=%d",(*iter_plane)->id());
+ }
+ }
+ ALOGD_IF(log_level(DBG_VERBOSE),"--------------------sort plane--------------------");
+ std::sort(plane_groups_.begin(),plane_groups_.end(),SortByZpos);
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups_.begin();
+ iter != plane_groups_.end(); ++iter)
+ {
+ ALOGD_IF(log_level(DBG_VERBOSE),"Plane groups: zpos=%d,share_id=%" PRIu64 ",plane size=%zu,possible_crtcs=0x%x",
+ (*iter)->zpos,(*iter)->share_id,(*iter)->planes.size(),(*iter)->possible_crtcs);
+ for(std::vector<DrmPlane*> ::const_iterator iter_plane = (*iter)->planes.begin();
+ iter_plane != (*iter)->planes.end(); ++iter_plane)
+ {
+ ALOGD_IF(log_level(DBG_VERBOSE),"\tPlane id=%d",(*iter_plane)->id());
+ }
+ }
+
+ drmModeFreePlaneResources(plane_res);
+ if (ret)
+ return ret;
+
+ ret = compositor_.Init();
+ if (ret)
+ return ret;
+
+ ret = event_listener_.Init();
+ if (ret) {
+ ALOGE("Can't initialize event listener %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void DrmResources::DisplayChanged(void) {
+ enable_changed_ = true;
+};
+
+void DrmResources::SetPrimaryDisplay(DrmConnector *c) {
+ if (primary_ != c) {
+ primary_ = c;
+ }
+ enable_changed_ = true;
+};
+
+void DrmResources::SetExtendDisplay(DrmConnector *c) {
+ if (extend_ != c) {
+ extend_ = c;
+ enable_changed_ = true;
+ }
+};
+
+DrmConnector *DrmResources::GetConnectorFromType(int display_type) const {
+ if (display_type == HWC_DISPLAY_PRIMARY)
+ return primary_;
+ else if (display_type == HWC_DISPLAY_EXTERNAL)
+ return extend_;
+ return NULL;
+}
+
+DrmCrtc *DrmResources::GetCrtcFromConnector(DrmConnector *conn) const {
+ if (conn->encoder())
+ return conn->encoder()->crtc();
+ return NULL;
+}
+
+DrmPlane *DrmResources::GetPlane(uint32_t id) const {
+ for (auto &plane : planes_) {
+ if (plane->id() == id)
+ return plane.get();
+ }
+ return NULL;
+}
+
+uint32_t DrmResources::next_mode_id() {
+ return ++mode_id_;
+}
+
+void DrmResources::ClearDisplay(void)
+{
+ for (int i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+ if (conn && conn->state() == DRM_MODE_CONNECTED &&
+ conn->current_mode().id() && conn->encoder() &&
+ conn->encoder()->crtc())
+ continue;
+ compositor_.ClearDisplay(i);
+ }
+}
+
+int DrmResources::UpdateDisplayRoute(void)
+{
+ bool mode_changed = false;
+ int i;
+
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+
+ if (!conn || conn->state() != DRM_MODE_CONNECTED || !conn->current_mode().id())
+ continue;
+ if (conn->current_mode() == conn->active_mode())
+ continue;
+ mode_changed = true;
+ }
+
+ if (!enable_changed_ && !mode_changed)
+ return 0;
+
+ DrmConnector *primary = GetConnectorFromType(HWC_DISPLAY_PRIMARY);
+ if (!primary) {
+ ALOGE("Failed to find primary display\n");
+ return -EINVAL;
+ }
+ DrmConnector *extend = GetConnectorFromType(HWC_DISPLAY_EXTERNAL);
+ if (enable_changed_) {
+ primary->set_encoder(NULL);
+ if (extend)
+ extend->set_encoder(NULL);
+ if (primary->state() == DRM_MODE_CONNECTED) {
+ for (DrmEncoder *enc : primary->possible_encoders()) {
+ for (DrmCrtc *crtc : enc->possible_crtcs()) {
+ if (crtc->get_afbc()) {
+ enc->set_crtc(crtc);
+ primary->set_encoder(enc);
+ ALOGE("set primary with conn[%d] crtc=%d\n",primary->id(), crtc->id());
+ }
+ }
+ }
+ /*
+ * not limit
+ */
+ if (!primary->encoder() || !primary->encoder()->crtc()) {
+ for (DrmEncoder *enc : primary->possible_encoders()) {
+ for (DrmCrtc *crtc : enc->possible_crtcs()) {
+ enc->set_crtc(crtc);
+ primary->set_encoder(enc);
+ ALOGE("set primary with conn[%d] crtc=%d\n",primary->id(), crtc->id());
+ }
+ }
+ }
+ }
+ if (extend && extend->state() == DRM_MODE_CONNECTED) {
+ for (DrmEncoder *enc : extend->possible_encoders()) {
+ for (DrmCrtc *crtc : enc->possible_crtcs()) {
+ if (primary && primary->encoder() && primary->encoder()->crtc()) {
+ if (crtc == primary->encoder()->crtc())
+ continue;
+ }
+ ALOGE("set extend[%d] with crtc=%d\n", extend->id(), crtc->id());
+ enc->set_crtc(crtc);
+ extend->set_encoder(enc);
+ }
+ }
+ if (!extend->encoder() || !extend->encoder()->crtc()) {
+ for (DrmEncoder *enc : extend->possible_encoders()) {
+ for (DrmCrtc *crtc : enc->possible_crtcs()) {
+ enc->set_crtc(crtc);
+ extend->set_encoder(enc);
+ ALOGE("set extend[%d] with crtc=%d\n", extend->id(), crtc->id());
+ if (primary && primary->encoder() && primary->encoder()->crtc()) {
+ if (crtc == primary->encoder()->crtc()) {
+ for (DrmEncoder *primary_enc : primary->possible_encoders()) {
+ for (DrmCrtc *primary_crtc : primary_enc->possible_crtcs()) {
+ if (extend && extend->encoder() && extend->encoder()->crtc()) {
+ if (primary_crtc == extend->encoder()->crtc())
+ continue;
+ }
+
+ primary_enc->set_crtc(primary_crtc);
+ primary->set_encoder(primary_enc);
+ ALOGE("set primary with conn[%d] crtc=%d\n",primary->id(), primary_crtc->id());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
+ if (!pset) {
+ ALOGE("Failed to allocate property set");
+ return -ENOMEM;
+ }
+
+#define DRM_ATOMIC_ADD_PROP(object_id, prop_id, value) \
+ ret = drmModeAtomicAddProperty(pset, object_id, prop_id, value); \
+ if (ret < 0) { \
+ ALOGE("Failed to add prop[%d] to [%d]", prop_id, object_id); \
+ }
+
+ int ret;
+ uint32_t blob_id[HWC_NUM_PHYSICAL_DISPLAY_TYPES] = {0};
+
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+
+ if (!conn)
+ continue;
+ if (conn->state() != DRM_MODE_CONNECTED ||
+ !conn->current_mode().id() || !conn->encoder() ||
+ !conn->encoder()->crtc()) {
+ DRM_ATOMIC_ADD_PROP(conn->id(), conn->crtc_id_property().id(), 0);
+ continue;
+ }
+
+ struct drm_mode_modeinfo drm_mode;
+ memset(&drm_mode, 0, sizeof(drm_mode));
+ conn->current_mode().ToDrmModeModeInfo(&drm_mode);
+
+ ret = CreatePropertyBlob(&drm_mode, sizeof(drm_mode), &blob_id[i]);
+ if (ret)
+ continue;
+
+ DrmCrtc *crtc = conn->encoder()->crtc();
+
+// connector->SetDpmsMode(DRM_MODE_DPMS_ON);
+// DRM_ATOMIC_ADD_PROP(conn->id(), conn->dpms_property().id(), DRM_MODE_DPMS_ON);
+ DRM_ATOMIC_ADD_PROP(conn->id(), conn->crtc_id_property().id(), crtc->id());
+ DRM_ATOMIC_ADD_PROP(crtc->id(), crtc->mode_property().id(), blob_id[i]);
+ DRM_ATOMIC_ADD_PROP(crtc->id(), crtc->active_property().id(), 1);
+ }
+ /*
+ * Disable unused connector
+ */
+ for (auto &connector : connectors_) {
+ bool in_use = false;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+ if (!conn || conn->state() != DRM_MODE_CONNECTED ||
+ !conn->current_mode().id() || !conn->encoder() ||
+ !conn->encoder()->crtc())
+ continue;
+ if (conn->id() == connector->id()) {
+ in_use = true;
+ break;
+ }
+ }
+ if (!in_use) {
+ DrmCrtc *mirror = NULL;
+ if (connector->state() == DRM_MODE_CONNECTED) {
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+ if (!conn || conn->state() != DRM_MODE_CONNECTED ||
+ !conn->current_mode().id() || !conn->encoder() ||
+ !conn->encoder()->crtc())
+ continue;
+ for (const DrmMode &conn_mode : connector->modes()) {
+ if (conn_mode == conn->current_mode()) {
+ mirror = conn->encoder()->crtc();
+ break;
+ }
+ }
+ if (mirror)
+ break;
+ }
+ }
+ if (mirror) {
+ connector->SetDpmsMode(DRM_MODE_DPMS_ON);
+ DRM_ATOMIC_ADD_PROP(connector->id(), connector->crtc_id_property().id(),
+ mirror->id());
+ } else {
+ connector->SetDpmsMode(DRM_MODE_DPMS_OFF);
+ DRM_ATOMIC_ADD_PROP(connector->id(), connector->crtc_id_property().id(), 0);
+ }
+ }
+ }
+ for (auto &crtc : crtcs_) {
+ bool in_use = false;
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+ if (!conn || conn->state() != DRM_MODE_CONNECTED ||
+ !conn->current_mode().id() || !conn->encoder() ||
+ !conn->encoder()->crtc())
+ continue;
+ if (crtc->id() == conn->encoder()->crtc()->id()) {
+ in_use = true;
+ break;
+ }
+ }
+ if (!in_use) {
+ DRM_ATOMIC_ADD_PROP(crtc->id(), crtc->mode_property().id(), 0);
+ DRM_ATOMIC_ADD_PROP(crtc->id(), crtc->active_property().id(), 0);
+ }
+ }
+
+
+ uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+ ret = drmModeAtomicCommit(fd_.get(), pset, flags, this);
+ if (ret < 0) {
+ ALOGE("Failed to commit pset ret=%d\n", ret);
+ drmModeAtomicFree(pset);
+ return ret;
+ }
+
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ if (blob_id[i])
+ DestroyPropertyBlob(blob_id[i]);
+ }
+ for (i = 0; i < HWC_NUM_PHYSICAL_DISPLAY_TYPES; i++) {
+ DrmConnector *conn = GetConnectorFromType(i);
+
+ if (!conn || conn->state() != DRM_MODE_CONNECTED || !conn->current_mode().id())
+ continue;
+
+ if (!conn->encoder() || !conn->encoder()->crtc())
+ continue;
+ conn->set_active_mode(conn->current_mode());
+ }
+ enable_changed_ = false;
+
+ drmModeAtomicFree(pset);
+
+ return 0;
+}
+
+int DrmResources::CreatePropertyBlob(void *data, size_t length,
+ uint32_t *blob_id) {
+ struct drm_mode_create_blob create_blob;
+ memset(&create_blob, 0, sizeof(create_blob));
+ create_blob.length = length;
+ create_blob.data = (__u64)data;
+
+ int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
+ if (ret) {
+ ALOGE("Failed to create mode property blob %d", ret);
+ return ret;
+ }
+ *blob_id = create_blob.blob_id;
+ return 0;
+}
+
+int DrmResources::DestroyPropertyBlob(uint32_t blob_id) {
+ if (!blob_id)
+ return 0;
+
+ struct drm_mode_destroy_blob destroy_blob;
+ memset(&destroy_blob, 0, sizeof(destroy_blob));
+ destroy_blob.blob_id = (__u32)blob_id;
+ int ret = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
+ if (ret) {
+ ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", blob_id, ret);
+ return ret;
+ }
+ return 0;
+}
+
+int DrmResources::SetDisplayActiveMode(int display, const DrmMode &mode) {
+ std::unique_ptr<DrmComposition> comp(compositor_.CreateComposition(NULL));
+ if (!comp) {
+ ALOGE("Failed to create composition for dpms on %d", display);
+ return -ENOMEM;
+ }
+ int ret = comp->SetDisplayMode(display, mode);
+ if (ret) {
+ ALOGE("Failed to add mode to composition on %d %d", display, ret);
+ return ret;
+ }
+ ret = compositor_.QueueComposition(std::move(comp));
+ if (ret) {
+ ALOGE("Failed to queue dpms composition on %d %d", display, ret);
+ return ret;
+ }
+ return 0;
+}
+
+int DrmResources::SetDpmsMode(int display, uint64_t mode) {
+ if (mode != DRM_MODE_DPMS_ON && mode != DRM_MODE_DPMS_OFF) {
+ ALOGE("Invalid dpms mode %" PRIu64, mode);
+ return -EINVAL;
+ }
+
+ std::unique_ptr<DrmComposition> comp(compositor_.CreateComposition(NULL));
+ if (!comp) {
+ ALOGE("Failed to create composition for dpms on %d", display);
+ return -ENOMEM;
+ }
+ int ret = comp->SetDpmsMode(display, mode);
+ if (ret) {
+ ALOGE("Failed to add dpms %" PRIu64 " to composition on %d %d", mode,
+ display, ret);
+ return ret;
+ }
+ ret = compositor_.QueueComposition(std::move(comp));
+ if (ret) {
+ ALOGE("Failed to queue dpms composition on %d %d", display, ret);
+ return ret;
+ }
+ return 0;
+}
+
+DrmCompositor *DrmResources::compositor() {
+ return &compositor_;
+}
+
+DrmEventListener *DrmResources::event_listener() {
+ return &event_listener_;
+}
+
+int DrmResources::GetProperty(uint32_t obj_id, uint32_t obj_type,
+ const char *prop_name, DrmProperty *property) {
+ drmModeObjectPropertiesPtr props;
+
+ props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
+ if (!props) {
+ ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
+ return -ENODEV;
+ }
+
+ bool found = false;
+ for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
+ drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
+ if (!strcmp(p->name, prop_name)) {
+ property->Init(p, props->prop_values[i]);
+ found = true;
+ }
+ drmModeFreeProperty(p);
+ }
+
+ drmModeFreeObjectProperties(props);
+ return found ? 0 : -ENOENT;
+}
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+static inline int64_t U642I64(uint64_t val)
+{
+ return (int64_t)*((int64_t *)&val);
+}
+
+struct type_name {
+ int type;
+ const char *name;
+};
+
+#define type_name_fn(res) \
+const char * DrmResources::res##_str(int type) { \
+ unsigned int i; \
+ for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
+ if (res##_names[i].type == type) \
+ return res##_names[i].name; \
+ } \
+ return "(invalid)"; \
+}
+
+struct type_name encoder_type_names[] = {
+ { DRM_MODE_ENCODER_NONE, "none" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
+};
+
+type_name_fn(encoder_type)
+
+struct type_name connector_status_names[] = {
+ { DRM_MODE_CONNECTED, "connected" },
+ { DRM_MODE_DISCONNECTED, "disconnected" },
+ { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
+};
+
+type_name_fn(connector_status)
+
+struct type_name connector_type_names[] = {
+ { DRM_MODE_CONNECTOR_Unknown, "unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+ { DRM_MODE_CONNECTOR_TV, "TV" },
+ { DRM_MODE_CONNECTOR_eDP, "eDP" },
+};
+
+type_name_fn(connector_type)
+
+#define bit_name_fn(res) \
+const char * res##_str(int type, std::ostringstream *out) { \
+ unsigned int i; \
+ const char *sep = ""; \
+ for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
+ if (type & (1 << i)) { \
+ *out << sep << res##_names[i]; \
+ sep = ", "; \
+ } \
+ } \
+ return NULL; \
+}
+
+static const char *mode_type_names[] = {
+ "builtin",
+ "clock_c",
+ "crtc_c",
+ "preferred",
+ "default",
+ "userdef",
+ "driver",
+};
+
+static bit_name_fn(mode_type)
+
+static const char *mode_flag_names[] = {
+ "phsync",
+ "nhsync",
+ "pvsync",
+ "nvsync",
+ "interlace",
+ "dblscan",
+ "csync",
+ "pcsync",
+ "ncsync",
+ "hskew",
+ "bcast",
+ "pixmux",
+ "dblclk",
+ "clkdiv2"
+};
+static bit_name_fn(mode_flag)
+
+void DrmResources::dump_mode(drmModeModeInfo *mode, std::ostringstream *out) {
+ *out << mode->name << " " << mode->vrefresh << " "
+ << mode->hdisplay << " " << mode->hsync_start << " "
+ << mode->hsync_end << " " << mode->htotal << " "
+ << mode->vdisplay << " " << mode->vsync_start << " "
+ << mode->vsync_end << " " << mode->vtotal;
+
+ *out << " flags: ";
+ mode_flag_str(mode->flags, out);
+ *out << " types: " << mode->type << "\n";
+ mode_type_str(mode->type, out);
+}
+
+void DrmResources::dump_blob(uint32_t blob_id, std::ostringstream *out) {
+ uint32_t i;
+ unsigned char *blob_data;
+ drmModePropertyBlobPtr blob;
+
+ blob = drmModeGetPropertyBlob(fd(), blob_id);
+ if (!blob) {
+ *out << "\n";
+ return;
+ }
+
+ blob_data = (unsigned char*)blob->data;
+
+ for (i = 0; i < blob->length; i++) {
+ if (i % 16 == 0)
+ *out << "\n\t\t\t";
+ *out << std::hex << blob_data[i];
+ }
+ *out << "\n";
+
+ drmModeFreePropertyBlob(blob);
+}
+
+void DrmResources::dump_prop(drmModePropertyPtr prop,
+ uint32_t prop_id, uint64_t value, std::ostringstream *out) {
+ int i;
+
+ *out << "\t" << prop_id;
+ if (!prop) {
+ *out << "\n";
+ return;
+ }
+ALOGD_IF(log_level(DBG_VERBOSE),"%s",out->str().c_str());
+out->str("");
+ *out << " " << prop->name << ":\n";
+
+ *out << "\t\tflags:";
+ if (prop->flags & DRM_MODE_PROP_PENDING)
+ *out << " pending";
+ if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
+ *out << " immutable";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
+ *out << " signed range";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
+ *out << " range";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
+ *out << " enum";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
+ *out << " bitmask";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
+ *out << " blob";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
+ *out << " object";
+ *out << "\n";
+
+ if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
+ *out << "\t\tvalues:";
+ for (i = 0; i < prop->count_values; i++)
+ *out << U642I64(prop->values[i]);
+ *out << "\n";
+ }
+
+ if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
+ *out << "\t\tvalues:";
+ for (i = 0; i < prop->count_values; i++)
+ *out << prop->values[i];
+ *out << "\n";
+ }
+
+ if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
+ *out << "\t\tenums:";
+ for (i = 0; i < prop->count_enums; i++)
+ *out << prop->enums[i].name << "=" << prop->enums[i].value;
+ *out << "\n";
+ } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
+ *out << "\t\tvalues:";
+ for (i = 0; i < prop->count_enums; i++)
+ *out << prop->enums[i].name << "=" << std::hex << (1LL << prop->enums[i].value);
+ *out << "\n";
+ } else {
+ assert(prop->count_enums == 0);
+ }
+
+ if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
+ *out << "\t\tblobs:\n";
+ for (i = 0; i < prop->count_blobs; i++)
+ dump_blob(prop->blob_ids[i], out);
+ *out << "\n";
+ } else {
+ assert(prop->count_blobs == 0);
+ }
+
+ *out << "\t\tvalue:";
+ if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
+ dump_blob(value, out);
+ else
+ *out << value;
+
+ *out << "\n";
+}
+
+int DrmResources::DumpProperty(uint32_t obj_id, uint32_t obj_type, std::ostringstream *out) {
+ drmModePropertyPtr* prop_info;
+ drmModeObjectPropertiesPtr props;
+
+ props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
+ if (!props) {
+ ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
+ return -ENODEV;
+ }
+ prop_info = (drmModePropertyPtr*)malloc(props->count_props * sizeof *prop_info);
+ if (!prop_info) {
+ ALOGE("Malloc drmModePropertyPtr array failed");
+ return -ENOMEM;
+ }
+
+ *out << " props:\n";
+ for (int i = 0;(size_t)i < props->count_props; ++i) {
+ prop_info[i] = drmModeGetProperty(fd(), props->props[i]);
+
+ dump_prop(prop_info[i],props->props[i],props->prop_values[i],out);
+
+ drmModeFreeProperty(prop_info[i]);
+ }
+
+ drmModeFreeObjectProperties(props);
+ free(prop_info);
+ return 0;
+}
+
+int DrmResources::DumpPlaneProperty(const DrmPlane &plane, std::ostringstream *out) {
+ return DumpProperty(plane.id(), DRM_MODE_OBJECT_PLANE, out);
+}
+
+int DrmResources::DumpCrtcProperty(const DrmCrtc &crtc, std::ostringstream *out) {
+ return DumpProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, out);
+}
+
+int DrmResources::DumpConnectorProperty(const DrmConnector &connector, std::ostringstream *out) {
+ return DumpProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, out);
+}
+
+int DrmResources::GetPlaneProperty(const DrmPlane &plane, const char *prop_name,
+ DrmProperty *property) {
+ return GetProperty(plane.id(), DRM_MODE_OBJECT_PLANE, prop_name, property);
+}
+
+int DrmResources::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
+ DrmProperty *property) {
+ return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property);
+}
+
+int DrmResources::GetConnectorProperty(const DrmConnector &connector,
+ const char *prop_name,
+ DrmProperty *property) {
+ return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
+ property);
+}
+
+std::vector<PlaneGroup *>& DrmResources::GetPlaneGroups() {
+ return plane_groups_;
+}
+
+}
diff --git a/drmresources.h b/drmresources.h
new file mode 100755
index 0000000..0fa048c
--- /dev/null
+++ b/drmresources.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_H_
+#define ANDROID_DRM_H_
+
+#include "drmcompositor.h"
+#include "drmconnector.h"
+#include "drmcrtc.h"
+#include "drmencoder.h"
+#include "drmeventlistener.h"
+#include "drmplane.h"
+
+#include <stdint.h>
+#if RK_RGA
+#include <RockchipRga.h>
+#endif
+
+namespace android {
+#define type_name_define(res) \
+const char * res##_str(int type);
+
+typedef struct tagPlaneGroup{
+ bool bUse;
+ uint32_t zpos;
+ uint32_t possible_crtcs;
+ uint64_t share_id;
+ std::vector<DrmPlane*> planes;
+}PlaneGroup;
+
+class DrmResources {
+ public:
+ DrmResources();
+ ~DrmResources();
+
+ int Init();
+
+ int fd() const {
+ return fd_.get();
+ }
+
+ const gralloc_module_t * getGralloc() {
+ return gralloc_;
+ }
+
+ void setGralloc(const gralloc_module_t *gralloc) {
+ gralloc_ = gralloc;
+ }
+
+ const std::vector<std::unique_ptr<DrmConnector>> &connectors() const {
+ return connectors_;
+ }
+
+ const std::vector<std::unique_ptr<DrmPlane>> &planes() const {
+ return planes_;
+ }
+
+ const std::vector<DrmPlane*> &sort_planes() const {
+ return sort_planes_;
+ }
+
+ void DisplayChanged(void);
+ void SetPrimaryDisplay(DrmConnector *c);
+ void SetExtendDisplay(DrmConnector *c);
+
+ DrmCrtc *GetCrtcFromConnector(DrmConnector *conn) const;
+ DrmConnector *GetConnectorFromType(int display_type) const;
+ DrmPlane *GetPlane(uint32_t id) const;
+ DrmCompositor *compositor();
+ DrmEventListener *event_listener();
+
+ int GetPlaneProperty(const DrmPlane &plane, const char *prop_name,
+ DrmProperty *property);
+ int GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
+ DrmProperty *property);
+ int GetConnectorProperty(const DrmConnector &connector, const char *prop_name,
+ DrmProperty *property);
+
+ uint32_t next_mode_id();
+ int SetDisplayActiveMode(int display, const DrmMode &mode);
+ int SetDpmsMode(int display, uint64_t mode);
+ int UpdateDisplayRoute(void);
+ void ClearDisplay(void);
+
+ int CreatePropertyBlob(void *data, size_t length, uint32_t *blob_id);
+ int DestroyPropertyBlob(uint32_t blob_id);
+ type_name_define(encoder_type);
+ type_name_define(connector_status);
+ type_name_define(connector_type);
+ int DumpPlaneProperty(const DrmPlane &plane, std::ostringstream *out);
+ int DumpCrtcProperty(const DrmCrtc &crtc, std::ostringstream *out);
+ int DumpConnectorProperty(const DrmConnector &connector, std::ostringstream *out);
+ void dump_mode(drmModeModeInfo *mode,std::ostringstream *out);
+
+ std::vector<PlaneGroup *>& GetPlaneGroups();
+
+#if RK_RGA
+ bool isSupportRkRga() {
+ RockchipRga& rkRga(RockchipRga::get());
+ return rkRga.RkRgaIsReady();
+ }
+#endif
+
+ private:
+ int TryEncoderForDisplay(int display, DrmEncoder *enc);
+ int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name,
+ DrmProperty *property);
+
+ void dump_blob(uint32_t blob_id, std::ostringstream *out);
+ void dump_prop(drmModePropertyPtr prop,
+ uint32_t prop_id, uint64_t value, std::ostringstream *out);
+ int DumpProperty(uint32_t obj_id, uint32_t obj_type, std::ostringstream *out);
+
+ UniqueFd fd_;
+ uint32_t mode_id_ = 0;
+ bool enable_changed_;
+ DrmConnector *primary_;
+ DrmConnector *extend_;
+
+ std::vector<std::unique_ptr<DrmConnector>> connectors_;
+ std::vector<std::unique_ptr<DrmEncoder>> encoders_;
+ std::vector<std::unique_ptr<DrmCrtc>> crtcs_;
+ std::vector<std::unique_ptr<DrmPlane>> planes_;
+ std::vector<DrmPlane*> sort_planes_;
+ std::vector<PlaneGroup *> plane_groups_;
+ DrmCompositor compositor_;
+ DrmEventListener event_listener_;
+ const gralloc_module_t *gralloc_;
+};
+}
+
+#endif // ANDROID_DRM_H_
diff --git a/glworker.cpp b/glworker.cpp
new file mode 100755
index 0000000..f48bc54
--- /dev/null
+++ b/glworker.cpp
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwc-gl-worker"
+
+#include <algorithm>
+#include <string>
+#include <sstream>
+#include <unordered_set>
+
+#include <sys/resource.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+
+#include <utils/Trace.h>
+
+#include "drmdisplaycomposition.h"
+
+#include "glworker.h"
+
+// TODO(zachr): use hwc_drm_bo to turn buffer handles into textures
+#ifndef EGL_NATIVE_HANDLE_ANDROID_NVX
+#define EGL_NATIVE_HANDLE_ANDROID_NVX 0x322A
+#endif
+
+#define MAX_OVERLAPPING_LAYERS 64
+
+namespace android {
+
+// clang-format off
+// Column-major order:
+// float mat[4] = { 1, 2, 3, 4 } ===
+// [ 1 3 ]
+// [ 2 4 ]
+float kTextureTransformMatrices[] = {
+ 1.0f, 0.0f, 0.0f, 1.0f, // identity matrix
+ 0.0f, 1.0f, 1.0f, 0.0f, // swap x and y
+};
+// clang-format on
+
+static const char *GetGLError(void) {
+ switch (glGetError()) {
+ case GL_NO_ERROR:
+ return "GL_NO_ERROR";
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ return "GL_INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY";
+ default:
+ return "Unknown error";
+ }
+}
+
+static const char *GetGLFramebufferError(void) {
+ switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return "GL_FRAMEBUFFER_COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ default:
+ return "Unknown error";
+ }
+}
+
+static const char *GetEGLError(void) {
+ switch (eglGetError()) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "Unknown error";
+ }
+}
+
+static bool HasExtension(const char *extension, const char *extensions) {
+ const char *start, *where, *terminator;
+ start = extensions;
+ for (;;) {
+ where = (char *)strstr((const char *)start, extension);
+ if (!where)
+ break;
+ terminator = where + strlen(extension);
+ if (where == start || *(where - 1) == ' ')
+ if (*terminator == ' ' || *terminator == '\0')
+ return true;
+ start = terminator;
+ }
+ return false;
+}
+
+static AutoGLShader CompileAndCheckShader(GLenum type, unsigned source_count,
+ const GLchar **sources,
+ std::ostringstream *shader_log) {
+ GLint status;
+ AutoGLShader shader(glCreateShader(type));
+ if (shader.get() == 0) {
+ if (shader_log)
+ *shader_log << "Failed glCreateShader call";
+ return 0;
+ }
+
+ glShaderSource(shader.get(), source_count, sources, NULL);
+ glCompileShader(shader.get());
+ glGetShaderiv(shader.get(), GL_COMPILE_STATUS, &status);
+ if (!status) {
+ if (shader_log) {
+ GLint log_length;
+ glGetShaderiv(shader.get(), GL_INFO_LOG_LENGTH, &log_length);
+ std::string info_log(log_length, ' ');
+ glGetShaderInfoLog(shader.get(), log_length, NULL, &info_log.front());
+ *shader_log << "Failed to compile shader:\n" << info_log.c_str()
+ << "\nShader Source:\n";
+ for (unsigned i = 0; i < source_count; i++) {
+ *shader_log << sources[i];
+ }
+ *shader_log << "\n";
+ }
+ return 0;
+ }
+
+ return shader;
+}
+
+static std::string GenerateVertexShader(int layer_count) {
+ std::ostringstream vertex_shader_stream;
+ vertex_shader_stream
+ << "#version 300 es\n"
+ << "#define LAYER_COUNT " << layer_count << "\n"
+ << "precision mediump int;\n"
+ << "uniform vec4 uViewport;\n"
+ << "uniform vec4 uLayerCrop[LAYER_COUNT];\n"
+ << "uniform mat2 uTexMatrix[LAYER_COUNT];\n"
+ << "in vec2 vPosition;\n"
+ << "in vec2 vTexCoords;\n"
+ << "out vec2 fTexCoords[LAYER_COUNT];\n"
+ << "void main() {\n"
+ << " for (int i = 0; i < LAYER_COUNT; i++) {\n"
+ << " vec2 tempCoords = vTexCoords * uTexMatrix[i];\n"
+ << " fTexCoords[i] =\n"
+ << " uLayerCrop[i].xy + tempCoords * uLayerCrop[i].zw;\n"
+ << " }\n"
+ << " vec2 scaledPosition = uViewport.xy + vPosition * uViewport.zw;\n"
+ << " gl_Position =\n"
+ << " vec4(scaledPosition * vec2(2.0) - vec2(1.0), 0.0, 1.0);\n"
+ << "}\n";
+ return vertex_shader_stream.str();
+}
+
+static std::string GenerateFragmentShader(int layer_count) {
+ std::ostringstream fragment_shader_stream;
+ fragment_shader_stream << "#version 300 es\n"
+ << "#define LAYER_COUNT " << layer_count << "\n"
+ << "#extension GL_OES_EGL_image_external : require\n"
+ << "precision mediump float;\n";
+ for (int i = 0; i < layer_count; ++i) {
+ fragment_shader_stream << "uniform samplerExternalOES uLayerTexture" << i
+ << ";\n";
+ }
+ fragment_shader_stream << "uniform float uLayerAlpha[LAYER_COUNT];\n"
+ << "uniform float uLayerPremult[LAYER_COUNT];\n"
+ << "in vec2 fTexCoords[LAYER_COUNT];\n"
+ << "out vec4 oFragColor;\n"
+ << "void main() {\n"
+ << " vec3 color = vec3(0.0, 0.0, 0.0);\n"
+ << " float alphaCover = 1.0;\n"
+ << " vec4 texSample;\n"
+ << " vec3 multRgb;\n";
+ for (int i = 0; i < layer_count; ++i) {
+ if (i > 0)
+ fragment_shader_stream << " if (alphaCover > 0.5/255.0) {\n";
+ // clang-format off
+ fragment_shader_stream
+ << " texSample = texture(uLayerTexture" << i << ",\n"
+ << " fTexCoords[" << i << "]);\n"
+ << " multRgb = texSample.rgb *\n"
+ << " max(texSample.a, uLayerPremult[" << i << "]);\n"
+ << " color += multRgb * uLayerAlpha[" << i << "] * alphaCover;\n"
+ << " alphaCover *= 1.0 - texSample.a * uLayerAlpha[" << i << "];\n";
+ // clang-format on
+ }
+ for (int i = 0; i < layer_count - 1; ++i)
+ fragment_shader_stream << " }\n";
+ fragment_shader_stream << " oFragColor = vec4(color, 1.0 - alphaCover);\n"
+ << "}\n";
+ return fragment_shader_stream.str();
+}
+
+static AutoGLProgram GenerateProgram(unsigned num_textures,
+ std::ostringstream *shader_log) {
+ std::string vertex_shader_string = GenerateVertexShader(num_textures);
+ const GLchar *vertex_shader_source = vertex_shader_string.c_str();
+ AutoGLShader vertex_shader = CompileAndCheckShader(
+ GL_VERTEX_SHADER, 1, &vertex_shader_source, shader_log);
+ if (!vertex_shader.get())
+ return 0;
+
+ std::string fragment_shader_string = GenerateFragmentShader(num_textures);
+ const GLchar *fragment_shader_source = fragment_shader_string.c_str();
+ AutoGLShader fragment_shader = CompileAndCheckShader(
+ GL_FRAGMENT_SHADER, 1, &fragment_shader_source, shader_log);
+ if (!fragment_shader.get())
+ return 0;
+
+ AutoGLProgram program(glCreateProgram());
+ if (!program.get()) {
+ if (shader_log)
+ *shader_log << "Failed to create program: " << GetGLError() << "\n";
+ return 0;
+ }
+
+ glAttachShader(program.get(), vertex_shader.get());
+ glAttachShader(program.get(), fragment_shader.get());
+ glBindAttribLocation(program.get(), 0, "vPosition");
+ glBindAttribLocation(program.get(), 1, "vTexCoords");
+ glLinkProgram(program.get());
+ glDetachShader(program.get(), vertex_shader.get());
+ glDetachShader(program.get(), fragment_shader.get());
+
+ GLint status;
+ glGetProgramiv(program.get(), GL_LINK_STATUS, &status);
+ if (!status) {
+ if (shader_log) {
+ GLint log_length;
+ glGetProgramiv(program.get(), GL_INFO_LOG_LENGTH, &log_length);
+ std::string program_log(log_length, ' ');
+ glGetProgramInfoLog(program.get(), log_length, NULL,
+ &program_log.front());
+ *shader_log << "Failed to link program:\n" << program_log.c_str() << "\n";
+ }
+ return 0;
+ }
+
+ return program;
+}
+
+struct RenderingCommand {
+ struct TextureSource {
+ unsigned texture_index;
+ float crop_bounds[4];
+ float alpha;
+ float premult;
+ float texture_matrix[4];
+ };
+
+ float bounds[4];
+ unsigned texture_count = 0;
+ TextureSource textures[MAX_OVERLAPPING_LAYERS];
+};
+
+static void ConstructCommand(const DrmHwcLayer *layers,
+ const DrmCompositionRegion &region,
+ RenderingCommand &cmd) {
+ std::copy_n(region.frame.bounds, 4, cmd.bounds);
+
+ for (size_t texture_index : region.source_layers) {
+ const DrmHwcLayer &layer = layers[texture_index];
+
+ DrmHwcRect<float> display_rect(layer.display_frame);
+ float display_size[2] = {display_rect.bounds[2] - display_rect.bounds[0],
+ display_rect.bounds[3] - display_rect.bounds[1]};
+
+ float tex_width = layer.buffer->width;
+ float tex_height = layer.buffer->height;
+
+#if RK_VIDEO_SKIP_LINE
+ if(layer.bSkipLine)
+ {
+ tex_height*=2;
+ }
+#endif
+
+ DrmHwcRect<float> crop_rect(layer.source_crop.left / tex_width,
+ layer.source_crop.top / tex_height,
+ layer.source_crop.right / tex_width,
+ layer.source_crop.bottom / tex_height);
+ float crop_size[2] = {crop_rect.bounds[2] - crop_rect.bounds[0],
+ crop_rect.bounds[3] - crop_rect.bounds[1]};
+
+ ALOGD_IF(log_level(DBG_SILENT),"ConstructCommand name[%zu]=%s,source_crop(%f,%f,%f,%f),display_rect(%d,%d,%d,%d)",
+ texture_index,layer.name.c_str(),layer.source_crop.left,layer.source_crop.top,
+ layer.source_crop.right,layer.source_crop.bottom,layer.display_frame.left,
+ layer.display_frame.top,layer.display_frame.right,layer.display_frame.bottom);
+ ALOGD_IF(log_level(DBG_SILENT),"ConstructCommand crop_rect(%f,%f,%f,%f),tex_width=%f,tex_height=%f",crop_rect.left,crop_rect.top,
+ crop_rect.right,crop_rect.bottom,tex_width,tex_height);
+ ALOGD_IF(log_level(DBG_SILENT),"ConstructCommand cmd.bounds[%f,%f,%f,%f]",cmd.bounds[0],cmd.bounds[1],cmd.bounds[2],cmd.bounds[3]);
+
+ RenderingCommand::TextureSource &src = cmd.textures[cmd.texture_count];
+ cmd.texture_count++;
+ src.texture_index = texture_index;
+
+ bool swap_xy = false;
+ bool flip_xy[2] = { false, false };
+
+#if RK_RGA
+ if(!layer.is_rotate_by_rga)
+#endif
+ {
+ if (layer.transform == DrmHwcTransform::kRotate180) {
+ swap_xy = false;
+ flip_xy[0] = true;
+ flip_xy[1] = true;
+ } else if (layer.transform == DrmHwcTransform::kRotate270) {
+ swap_xy = true;
+ flip_xy[0] = true;
+ flip_xy[1] = false;
+ } else if (layer.transform & DrmHwcTransform::kRotate90) {
+ swap_xy = true;
+ if (layer.transform & DrmHwcTransform::kFlipH) {
+ flip_xy[0] = true;
+ flip_xy[1] = true;
+ } else if (layer.transform & DrmHwcTransform::kFlipV) {
+ flip_xy[0] = false;
+ flip_xy[1] = false;
+ } else {
+ flip_xy[0] = false;
+ flip_xy[1] = true;
+ }
+ } else {
+ if (layer.transform & DrmHwcTransform::kFlipH)
+ flip_xy[0] = true;
+ if (layer.transform & DrmHwcTransform::kFlipV)
+ flip_xy[1] = true;
+ }
+ }
+ if (swap_xy)
+ std::copy_n(&kTextureTransformMatrices[4], 4, src.texture_matrix);
+ else
+ std::copy_n(&kTextureTransformMatrices[0], 4, src.texture_matrix);
+
+ for (int j = 0; j < 4; j++) {
+ int b = j ^ (swap_xy ? 1 : 0);
+ float bound_percent =
+ (cmd.bounds[b] - display_rect.bounds[b % 2]) / display_size[b % 2];
+ if (flip_xy[j % 2]) {
+ src.crop_bounds[j] =
+ crop_rect.bounds[j % 2 + 2] - bound_percent * crop_size[j % 2];
+ } else {
+ src.crop_bounds[j] =
+ crop_rect.bounds[j % 2] + bound_percent * crop_size[j % 2];
+ }
+ }
+
+ if (layer.blending == DrmHwcBlending::kNone) {
+ src.alpha = src.premult = 1.0f;
+ // This layer is opaque. There is no point in using layers below this one.
+ break;
+ }
+
+ src.alpha = layer.alpha / 255.0f;
+ src.premult = (layer.blending == DrmHwcBlending::kPreMult) ? 1.0f : 0.0f;
+ }
+}
+
+static int EGLFenceWait(EGLDisplay egl_display, int acquireFenceFd) {
+ int ret = 0;
+
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, acquireFenceFd,
+ EGL_NONE};
+ EGLSyncKHR egl_sync =
+ eglCreateSyncKHR(egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (egl_sync == EGL_NO_SYNC_KHR) {
+ ALOGE("Failed to make EGLSyncKHR from acquireFenceFd: %s", GetEGLError());
+ close(acquireFenceFd);
+ return 1;
+ }
+
+ EGLint success = eglWaitSyncKHR(egl_display, egl_sync, 0);
+ if (success == EGL_FALSE) {
+ ALOGE("Failed to wait for acquire: %s", GetEGLError());
+ ret = 1;
+ }
+ eglDestroySyncKHR(egl_display, egl_sync);
+
+ return ret;
+}
+
+static int CreateTextureFromHandle(EGLDisplay egl_display,
+ buffer_handle_t handle,
+ AutoEGLImageAndGLTexture *out) {
+ EGLImageKHR image = eglCreateImageKHR(
+ egl_display, EGL_NO_CONTEXT, EGL_NATIVE_HANDLE_ANDROID_NVX,
+ (EGLClientBuffer)handle, NULL /* no attribs */);
+
+ if (image == EGL_NO_IMAGE_KHR) {
+ ALOGE("Failed to make image %s %p", GetEGLError(), handle);
+ return -EINVAL;
+ }
+
+ GLuint texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
+
+ out->image.reset(egl_display, image);
+ out->texture.reset(texture);
+
+ return 0;
+}
+
+GLWorkerCompositor::GLWorkerCompositor()
+ : egl_display_(EGL_NO_DISPLAY), egl_ctx_(EGL_NO_CONTEXT) {
+}
+
+int GLWorkerCompositor::Init() {
+ int ret = 0;
+ const char *egl_extensions;
+ const char *gl_extensions;
+ EGLint num_configs;
+ EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
+ EGLConfig egl_config;
+
+ // clang-format off
+ const GLfloat verts[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 2.0f, 0.0f, 2.0f,
+ 2.0f, 0.0f, 2.0f, 0.0f
+ };
+ // clang-format on
+
+ const EGLint config_attribs[] = {EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_NONE};
+
+ const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+
+ egl_display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (egl_display_ == EGL_NO_DISPLAY) {
+ ALOGE("Failed to get egl display");
+ return 1;
+ }
+
+ if (!eglInitialize(egl_display_, NULL, NULL)) {
+ ALOGE("Failed to initialize egl: %s", GetEGLError());
+ return 1;
+ }
+
+ egl_extensions = eglQueryString(egl_display_, EGL_EXTENSIONS);
+
+ // These extensions are all technically required but not always reported due
+ // to meta EGL filtering them out.
+ if (!HasExtension("EGL_KHR_image_base", egl_extensions))
+ ALOGW("EGL_KHR_image_base extension not supported");
+
+ if (!HasExtension("EGL_ANDROID_image_native_buffer", egl_extensions))
+ ALOGW("EGL_ANDROID_image_native_buffer extension not supported");
+
+ if (!HasExtension("EGL_ANDROID_native_fence_sync", egl_extensions))
+ ALOGW("EGL_ANDROID_native_fence_sync extension not supported");
+
+ if (!eglChooseConfig(egl_display_, config_attribs, &egl_config, 1,
+ &num_configs)) {
+ ALOGE("eglChooseConfig() failed with error: %s", GetEGLError());
+ return 1;
+ }
+
+ egl_ctx_ =
+ eglCreateContext(egl_display_, egl_config,
+ EGL_NO_CONTEXT /* No shared context */, context_attribs);
+
+ if (egl_ctx_ == EGL_NO_CONTEXT) {
+ ALOGE("Failed to create OpenGL ES Context: %s", GetEGLError());
+ return 1;
+ }
+
+ if (!eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_ctx_)) {
+ ALOGE("Failed to make the OpenGL ES Context current: %s", GetEGLError());
+ return 1;
+ }
+
+ gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
+
+ if (!HasExtension("GL_OES_EGL_image", gl_extensions))
+ ALOGW("GL_OES_EGL_image extension not supported");
+
+ if (!HasExtension("GL_OES_EGL_image_external", gl_extensions))
+ ALOGW("GL_OES_EGL_image_external extension not supported");
+
+ GLuint vertex_buffer;
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ vertex_buffer_.reset(vertex_buffer);
+
+ std::ostringstream shader_log;
+ blend_programs_.emplace_back(GenerateProgram(1, &shader_log));
+ if (blend_programs_.back().get() == 0) {
+ ALOGE("%s", shader_log.str().c_str());
+ return 1;
+ }
+
+ return 0;
+}
+
+GLWorkerCompositor::~GLWorkerCompositor() {
+ if (egl_display_ != EGL_NO_DISPLAY && egl_ctx_ != EGL_NO_CONTEXT)
+ if (eglDestroyContext(egl_display_, egl_ctx_) == EGL_FALSE)
+ ALOGE("Failed to destroy OpenGL ES Context: %s", GetEGLError());
+}
+
+int GLWorkerCompositor::Composite(DrmHwcLayer *layers,
+ DrmCompositionRegion *regions,
+ size_t num_regions,
+ const sp<GraphicBuffer> &framebuffer) {
+ ATRACE_CALL();
+ int ret = 0;
+ std::vector<AutoEGLImageAndGLTexture> layer_textures;
+ std::vector<RenderingCommand> commands;
+
+ if (num_regions == 0) {
+ return -EALREADY;
+ }
+
+ GLint frame_width = framebuffer->getWidth();
+ GLint frame_height = framebuffer->getHeight();
+ CachedFramebuffer *cached_framebuffer =
+ PrepareAndCacheFramebuffer(framebuffer);
+ if (cached_framebuffer == NULL) {
+ ALOGE("Composite failed because of failed framebuffer");
+ return -EINVAL;
+ }
+
+ std::unordered_set<size_t> layers_used_indices;
+ for (size_t region_index = 0; region_index < num_regions; region_index++) {
+ DrmCompositionRegion &region = regions[region_index];
+ layers_used_indices.insert(region.source_layers.begin(),
+ region.source_layers.end());
+ commands.emplace_back();
+ ConstructCommand(layers, region, commands.back());
+ }
+
+ for (size_t layer_index = 0; layer_index < MAX_OVERLAPPING_LAYERS;
+ layer_index++) {
+ DrmHwcLayer *layer = &layers[layer_index];
+
+ layer_textures.emplace_back();
+
+ if (layers_used_indices.count(layer_index) == 0)
+ continue;
+ ALOGD_IF(log_level(DBG_DEBUG),"Squash layer name=%s",layer->name.c_str());
+ ret = CreateTextureFromHandle(egl_display_, layer->get_usable_handle(),
+ &layer_textures.back());
+
+ if (!ret) {
+ ret = EGLFenceWait(egl_display_, layer->acquire_fence.Release());
+ }
+ if (ret) {
+ layer_textures.pop_back();
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret)
+ return ret;
+
+ glViewport(0, 0, frame_width, frame_height);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_.get());
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, NULL);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4,
+ (void *)(sizeof(float) * 2));
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnable(GL_SCISSOR_TEST);
+
+ for (const RenderingCommand &cmd : commands) {
+ if (cmd.texture_count == 0)
+ continue;
+
+ // TODO(zachr): handle the case of too many overlapping textures for one
+ // area by falling back to rendering as many layers as possible using
+ // multiple blending passes.
+ GLint program = PrepareAndCacheProgram(cmd.texture_count);
+ if (program == 0) {
+ ALOGE("Too many layers to render in one area");
+ continue;
+ }
+
+ glUseProgram(program);
+ GLint gl_viewport_loc = glGetUniformLocation(program, "uViewport");
+ GLint gl_crop_loc = glGetUniformLocation(program, "uLayerCrop");
+ GLint gl_alpha_loc = glGetUniformLocation(program, "uLayerAlpha");
+ GLint gl_premult_loc = glGetUniformLocation(program, "uLayerPremult");
+ GLint gl_tex_matrix_loc = glGetUniformLocation(program, "uTexMatrix");
+ glUniform4f(gl_viewport_loc, cmd.bounds[0] / (float)frame_width,
+ cmd.bounds[1] / (float)frame_height,
+ (cmd.bounds[2] - cmd.bounds[0]) / (float)frame_width,
+ (cmd.bounds[3] - cmd.bounds[1]) / (float)frame_height);
+
+ for (unsigned src_index = 0; src_index < cmd.texture_count; src_index++) {
+ std::ostringstream texture_name_formatter;
+ texture_name_formatter << "uLayerTexture" << src_index;
+ GLint gl_tex_loc =
+ glGetUniformLocation(program, texture_name_formatter.str().c_str());
+
+ const RenderingCommand::TextureSource &src = cmd.textures[src_index];
+ glUniform1f(gl_alpha_loc + src_index, src.alpha);
+ glUniform1f(gl_premult_loc + src_index, src.premult);
+ glUniform4f(gl_crop_loc + src_index, src.crop_bounds[0],
+ src.crop_bounds[1], src.crop_bounds[2] - src.crop_bounds[0],
+ src.crop_bounds[3] - src.crop_bounds[1]);
+ glUniform1i(gl_tex_loc, src_index);
+ glUniformMatrix2fv(gl_tex_matrix_loc + src_index, 1, GL_FALSE,
+ src.texture_matrix);
+ glActiveTexture(GL_TEXTURE0 + src_index);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES,
+ layer_textures[src.texture_index].texture.get());
+ }
+
+ glScissor(cmd.bounds[0], cmd.bounds[1], cmd.bounds[2] - cmd.bounds[0],
+ cmd.bounds[3] - cmd.bounds[1]);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ for (unsigned src_index = 0; src_index < cmd.texture_count; src_index++) {
+ glActiveTexture(GL_TEXTURE0 + src_index);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
+ }
+ }
+
+ glDisable(GL_SCISSOR_TEST);
+ glActiveTexture(GL_TEXTURE0);
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glUseProgram(0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return ret;
+}
+
+void GLWorkerCompositor::Finish() {
+ ATRACE_CALL();
+ glFinish();
+
+ char use_framebuffer_cache_opt[PROPERTY_VALUE_MAX];
+ //disable use_framebuffer_cache,otherwise it will lead mem leak when re-allocate DrmFramebuffer
+ property_get("hwc.drm.use_framebuffer_cache", use_framebuffer_cache_opt, "0");
+ bool use_framebuffer_cache = atoi(use_framebuffer_cache_opt);
+
+ if (use_framebuffer_cache) {
+ for (auto &fb : cached_framebuffers_)
+ fb.strong_framebuffer.clear();
+ } else {
+ cached_framebuffers_.clear();
+ }
+}
+
+GLWorkerCompositor::CachedFramebuffer::CachedFramebuffer(
+ const sp<GraphicBuffer> &gb, AutoEGLDisplayImage &&image,
+ AutoGLTexture &&tex, AutoGLFramebuffer &&fb)
+ : strong_framebuffer(gb),
+ weak_framebuffer(gb),
+ egl_fb_image(std::move(image)),
+ gl_fb_tex(std::move(tex)),
+ gl_fb(std::move(fb)) {
+}
+
+bool GLWorkerCompositor::CachedFramebuffer::Promote() {
+ if (strong_framebuffer.get() != NULL)
+ return true;
+ strong_framebuffer = weak_framebuffer.promote();
+ return strong_framebuffer.get() != NULL;
+}
+
+GLWorkerCompositor::CachedFramebuffer *
+GLWorkerCompositor::FindCachedFramebuffer(
+ const sp<GraphicBuffer> &framebuffer) {
+ for (auto &fb : cached_framebuffers_)
+ if (fb.weak_framebuffer == framebuffer)
+ return &fb;
+ return NULL;
+}
+
+GLWorkerCompositor::CachedFramebuffer *
+GLWorkerCompositor::PrepareAndCacheFramebuffer(
+ const sp<GraphicBuffer> &framebuffer) {
+ CachedFramebuffer *cached_framebuffer = FindCachedFramebuffer(framebuffer);
+ if (cached_framebuffer != NULL) {
+ if (cached_framebuffer->Promote()) {
+ glBindFramebuffer(GL_FRAMEBUFFER, cached_framebuffer->gl_fb.get());
+ return cached_framebuffer;
+ }
+
+ for (auto it = cached_framebuffers_.begin();
+ it != cached_framebuffers_.end(); ++it) {
+ if (it->weak_framebuffer == framebuffer) {
+ cached_framebuffers_.erase(it);
+ break;
+ }
+ }
+ }
+
+ AutoEGLDisplayImage egl_fb_image(
+ egl_display_,
+ eglCreateImageKHR(egl_display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)framebuffer->getNativeBuffer(),
+ NULL /* no attribs */));
+
+ if (egl_fb_image.image() == EGL_NO_IMAGE_KHR) {
+ ALOGE("Failed to make image from target buffer: %s", GetEGLError());
+ return NULL;
+ }
+
+ GLuint gl_fb_tex;
+ glGenTextures(1, &gl_fb_tex);
+ AutoGLTexture gl_fb_tex_auto(gl_fb_tex);
+ glBindTexture(GL_TEXTURE_2D, gl_fb_tex);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
+ (GLeglImageOES)egl_fb_image.image());
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ GLuint gl_fb;
+ glGenFramebuffers(1, &gl_fb);
+ AutoGLFramebuffer gl_fb_auto(gl_fb);
+ glBindFramebuffer(GL_FRAMEBUFFER, gl_fb);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ gl_fb_tex, 0);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Failed framebuffer check for created target buffer: %s",
+ GetGLFramebufferError());
+ return NULL;
+ }
+
+ cached_framebuffers_.emplace_back(framebuffer, std::move(egl_fb_image),
+ std::move(gl_fb_tex_auto),
+ std::move(gl_fb_auto));
+ return &cached_framebuffers_.back();
+}
+
+GLint GLWorkerCompositor::PrepareAndCacheProgram(unsigned texture_count) {
+ if (blend_programs_.size() >= texture_count) {
+ GLint program = blend_programs_[texture_count - 1].get();
+ if (program != 0)
+ return program;
+ }
+
+ AutoGLProgram program = GenerateProgram(texture_count, NULL);
+ if (program.get() != 0) {
+ if (blend_programs_.size() < texture_count)
+ blend_programs_.resize(texture_count);
+ blend_programs_[texture_count - 1] = std::move(program);
+ return blend_programs_[texture_count - 1].get();
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/glworker.h b/glworker.h
new file mode 100644
index 0000000..222cf6f
--- /dev/null
+++ b/glworker.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GL_WORKER_H_
+#define ANDROID_GL_WORKER_H_
+
+#include <vector>
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "autogl.h"
+
+namespace android {
+
+struct DrmHwcLayer;
+struct DrmCompositionRegion;
+
+class GLWorkerCompositor {
+ public:
+ GLWorkerCompositor();
+ ~GLWorkerCompositor();
+
+ int Init();
+ int Composite(DrmHwcLayer *layers, DrmCompositionRegion *regions,
+ size_t num_regions, const sp<GraphicBuffer> &framebuffer);
+ void Finish();
+
+ private:
+ struct CachedFramebuffer {
+ // If the strong_framebuffer is non-NULL, we are holding a strong reference
+ // until we are sure rendering is done. The weak reference will be equal in
+ // that case.
+ sp<GraphicBuffer> strong_framebuffer;
+ wp<GraphicBuffer> weak_framebuffer;
+ AutoEGLDisplayImage egl_fb_image;
+ AutoGLTexture gl_fb_tex;
+ AutoGLFramebuffer gl_fb;
+
+ CachedFramebuffer(const sp<GraphicBuffer> &gb, AutoEGLDisplayImage &&image,
+ AutoGLTexture &&tex, AutoGLFramebuffer &&fb);
+
+ bool Promote();
+ };
+
+ CachedFramebuffer *FindCachedFramebuffer(
+ const sp<GraphicBuffer> &framebuffer);
+ CachedFramebuffer *PrepareAndCacheFramebuffer(
+ const sp<GraphicBuffer> &framebuffer);
+
+ GLint PrepareAndCacheProgram(unsigned texture_count);
+
+ EGLDisplay egl_display_;
+ EGLContext egl_ctx_;
+
+ std::vector<AutoGLProgram> blend_programs_;
+ AutoGLBuffer vertex_buffer_;
+
+ std::vector<CachedFramebuffer> cached_framebuffers_;
+};
+}
+
+#endif
diff --git a/hwc_debug.cpp b/hwc_debug.cpp
new file mode 100755
index 0000000..4916f4f
--- /dev/null
+++ b/hwc_debug.cpp
@@ -0,0 +1,234 @@
+#define LOG_TAG "hwc_debug"
+
+#include "hwc_debug.h"
+#include "hwc_rockchip.h"
+#include <sstream>
+
+namespace android {
+
+unsigned int g_log_level;
+unsigned int g_frame;
+
+void inc_frame()
+{
+ g_frame++;
+}
+
+void dec_frame()
+{
+ g_frame--;
+}
+
+int get_frame()
+{
+ return g_frame;
+}
+
+int init_log_level()
+{
+ char value[PROPERTY_VALUE_MAX];
+ int iValue;
+ property_get("sys.hwc.log", value, "0");
+ g_log_level = atoi(value);
+ return 0;
+}
+
+bool log_level(LOG_LEVEL log_level)
+{
+ return g_log_level & log_level;
+}
+
+void init_rk_debug()
+{
+ g_log_level = 0;
+ g_frame = 0;
+ init_log_level();
+}
+
+
+/**
+ * @brief Dump Layer data.
+ *
+ * @param layer_index layer index
+ * @param layer layer data
+ * @return Errno no
+ */
+#define DUMP_LAYER_CNT (10)
+int DumpLayer(const char* layer_name,buffer_handle_t handle)
+{
+ char pro_value[PROPERTY_VALUE_MAX];
+
+ property_get("sys.dump",pro_value,0);
+
+ if(handle && !strcmp(pro_value,"true"))
+ {
+ static int DumpSurfaceCount = 0;
+ FILE * pfile = NULL;
+ char data_name[100] ;
+ const gralloc_module_t *gralloc;
+ void* cpu_addr;
+ int i;
+ int width,height,stride,byte_stride,format,size;
+
+ int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&gralloc);
+ if (ret) {
+ ALOGE("Failed to open gralloc module");
+ return ret;
+ }
+#if RK_DRM_GRALLOC
+ width = hwc_get_handle_attibute(gralloc,handle,ATT_WIDTH);
+ height = hwc_get_handle_attibute(gralloc,handle,ATT_HEIGHT);
+ stride = hwc_get_handle_attibute(gralloc,handle,ATT_STRIDE);
+ byte_stride = hwc_get_handle_attibute(gralloc,handle,ATT_BYTE_STRIDE);
+ format = hwc_get_handle_attibute(gralloc,handle,ATT_FORMAT);
+ size = hwc_get_handle_attibute(gralloc,handle,ATT_SIZE);
+#else
+ width = hwc_get_handle_width(gralloc,handle);
+ height = hwc_get_handle_height(gralloc,handle);
+ stride = hwc_get_handle_stride(gralloc,handle);
+ byte_stride = hwc_get_handle_byte_stride(gralloc,handle);
+ format = hwc_get_handle_format(gralloc,handle);
+ size = hwc_get_handle_size(gralloc,handle);
+#endif
+ system("mkdir /data/dump/ && chmod /data/dump/ 777 ");
+ DumpSurfaceCount++;
+ sprintf(data_name,"/data/dump/dmlayer%d_%d_%d.bin", DumpSurfaceCount,
+ stride,height);
+ gralloc->lock(gralloc, handle, GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK, //gr_handle->usage,
+ 0, 0, width, height, (void **)&cpu_addr);
+ pfile = fopen(data_name,"wb");
+ if(pfile)
+ {
+ fwrite((const void *)cpu_addr,(size_t)(size),1,pfile);
+ fflush(pfile);
+ fclose(pfile);
+ ALOGD(" dump surface layer_name: %s,data_name %s,w:%d,h:%d,stride :%d,size=%d,cpu_addr=%p",
+ layer_name,data_name,width,height,byte_stride,size,cpu_addr);
+ }
+ else
+ {
+ ALOGE("Open %s fail", data_name);
+ ALOGD(" dump surface layer_name: %s,data_name %s,w:%d,h:%d,stride :%d,size=%d,cpu_addr=%p",
+ layer_name,data_name,width,height,byte_stride,size,cpu_addr);
+ }
+ gralloc->unlock(gralloc, handle);
+ //only dump once time.
+ if(DumpSurfaceCount == DUMP_LAYER_CNT)
+ property_set("sys.dump","0");
+ }
+ return 0;
+}
+
+static unsigned int HWC_Clockms(void)
+{
+ struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (unsigned int)(t.tv_sec * 1000 + t.tv_nsec / 1000000);
+}
+
+void hwc_dump_fps(void)
+{
+ static unsigned int n_frames = 0;
+ static unsigned int lastTime = 0;
+
+ ++n_frames;
+
+ if (property_get_bool("sys.hwc.fps", 0))
+ {
+ unsigned int time = HWC_Clockms();
+ unsigned int intv = time - lastTime;
+ if (intv >= HWC_DEBUG_FPS_INTERVAL_MS)
+ {
+ unsigned int fps = n_frames * 1000 / intv;
+ ALOGD_IF(log_level(DBG_DEBUG),"fps %u", fps);
+
+ n_frames = 0;
+ lastTime = time;
+ }
+ }
+}
+
+void dump_layer(const gralloc_module_t *gralloc, bool bDump, hwc_layer_1_t *layer, int index) {
+ size_t i;
+ std::ostringstream out;
+ int format;
+
+ if (!log_level(DBG_VERBOSE))
+ return;
+
+ if(layer->flags & HWC_SKIP_LAYER)
+ {
+ ALOGD_IF(log_level(DBG_VERBOSE),"layer %p skipped", layer);
+ }
+ else
+ {
+ if(layer->handle)
+ {
+#if RK_DRM_GRALLOC
+ format = hwc_get_handle_attibute(gralloc, layer->handle, ATT_FORMAT);
+#else
+ format = hwc_get_handle_format(gralloc, layer->handle);
+#endif
+ out << "layer[" << index << "]=" << layer->LayerName
+ << "\n\tlayer=" << layer
+ << ",type=" << layer->compositionType
+ << ",hints=" << layer->compositionType
+ << ",flags=" << layer->flags
+ << ",handle=" << layer->handle
+ << ",format=0x" << std::hex << format
+ << ",fd =" << std::dec << hwc_get_handle_primefd(gralloc, layer->handle)
+ << ",transform=0x" << std::hex << layer->transform
+ << ",blend=0x" << layer->blending
+ << ",sourceCropf{" << std::dec
+ << layer->sourceCropf.left << "," << layer->sourceCropf.top << ","
+ << layer->sourceCropf.right << "," << layer->sourceCropf.bottom
+ << "},sourceCrop{"
+ << layer->sourceCrop.left << ","
+ << layer->sourceCrop.top << ","
+ << layer->sourceCrop.right << ","
+ << layer->sourceCrop.bottom
+ << "},displayFrame{"
+ << layer->displayFrame.left << ","
+ << layer->displayFrame.top << ","
+ << layer->displayFrame.right << ","
+ << layer->displayFrame.bottom << "},";
+ }
+ else
+ {
+ out << "layer[" << index << "]=" << layer->LayerName
+ << "\n\tlayer=" << layer
+ << ",type=" << layer->compositionType
+ << ",hints=" << layer->compositionType
+ << ",flags=" << layer->flags
+ << ",handle=" << layer->handle
+ << ",transform=0x" << std::hex << layer->transform
+ << ",blend=0x" << layer->blending
+ << ",sourceCropf{" << std::dec
+ << layer->sourceCropf.left << "," << layer->sourceCropf.top << ","
+ << layer->sourceCropf.right << "," << layer->sourceCropf.bottom
+ << "},sourceCrop{"
+ << layer->sourceCrop.left << ","
+ << layer->sourceCrop.top << ","
+ << layer->sourceCrop.right << ","
+ << layer->sourceCrop.bottom
+ << "},displayFrame{"
+ << layer->displayFrame.left << ","
+ << layer->displayFrame.top << ","
+ << layer->displayFrame.right << ","
+ << layer->displayFrame.bottom << "},";
+ }
+ for (i = 0; i < layer->visibleRegionScreen.numRects; i++)
+ {
+ out << "rect[" << i << "]={"
+ << layer->visibleRegionScreen.rects[i].left << ","
+ << layer->visibleRegionScreen.rects[i].top << ","
+ << layer->visibleRegionScreen.rects[i].right << ","
+ << layer->visibleRegionScreen.rects[i].bottom << "},";
+ }
+ out << "\n";
+ ALOGD_IF(log_level(DBG_VERBOSE) || bDump,"%s",out.str().c_str());
+ }
+}
+
+}
diff --git a/hwc_debug.h b/hwc_debug.h
new file mode 100755
index 0000000..ed59c75
--- /dev/null
+++ b/hwc_debug.h
@@ -0,0 +1,63 @@
+#ifndef _HWC_DEBUG_H_
+#define _HWC_DEBUG_H_
+
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/hwcomposer.h>
+#if RK_DRM_GRALLOC
+#include "gralloc_drm_handle.h"
+#endif
+
+namespace android {
+
+enum LOG_LEVEL
+{
+ //Log level flag
+ /*1*/
+ DBG_VERBOSE = 1 << 0,
+ /*2*/
+ DBG_DEBUG = 1 << 1,
+ /*4*/
+ DBG_INFO = 1 << 2,
+ /*8*/
+ DBG_WARN = 1 << 3,
+ /*16*/
+ DBG_ERROR = 1 << 4,
+ /*32*/
+ DBG_FETAL = 1 << 5,
+ /*64*/
+ DBG_SILENT = 1 << 6,
+};
+
+bool log_level(LOG_LEVEL log_level);
+
+/* interval ms of print fps.*/
+#define HWC_DEBUG_FPS_INTERVAL_MS 1
+
+/* print time macros. */
+#define PRINT_TIME_START \
+ struct timeval tpend1, tpend2;\
+ long usec1 = 0;\
+ gettimeofday(&tpend1,NULL);\
+
+#define PRINT_TIME_END(tag) \
+ gettimeofday(&tpend2,NULL);\
+ usec1 = 1000*(tpend2.tv_sec - tpend1.tv_sec) + (tpend2.tv_usec- tpend1.tv_usec)/1000;\
+ if (property_get_bool("sys.hwc.time", 0)) \
+ ALOGD_IF(1,"%s use time=%ld ms",tag,usec1);
+
+
+void inc_frame();
+void dec_frame();
+int get_frame();
+int init_log_level();
+bool log_level(LOG_LEVEL log_level);
+void init_rk_debug();
+int DumpLayer(const char* layer_name,buffer_handle_t handle);
+void hwc_dump_fps(void);
+void dump_layer(const gralloc_module_t *gralloc, bool bDump, hwc_layer_1_t *layer, int index);
+}
+
+#endif
+
diff --git a/hwc_rockchip.cpp b/hwc_rockchip.cpp
new file mode 100755
index 0000000..e7f4219
--- /dev/null
+++ b/hwc_rockchip.cpp
@@ -0,0 +1,1722 @@
+#define LOG_TAG "hwc_rk"
+
+#include <inttypes.h>
+#include "hwc_rockchip.h"
+#include "hwc_util.h"
+
+namespace android {
+
+int hwc_init_version()
+{
+ char acVersion[50];
+ char acCommit[50];
+ memset(acVersion,0,sizeof(acVersion));
+
+ strcpy(acVersion,GHWC_VERSION);
+
+#ifdef TARGET_BOARD_PLATFORM_RK3288
+ strcat(acVersion,"-rk3288");
+#endif
+#ifdef TARGET_BOARD_PLATFORM_RK3368
+ strcat(acVersion,"-rk3368");
+#endif
+#ifdef TARGET_BOARD_PLATFORM_RK3366
+ strcat(acVersion,"-rk3366");
+#endif
+#ifdef TARGET_BOARD_PLATFORM_RK3399
+ strcat(acVersion,"-rk3399");
+#endif
+
+#ifdef RK_MID
+ strcat(acVersion,"-MID");
+#endif
+#ifdef RK_BOX
+ strcat(acVersion,"-BOX");
+#endif
+#ifdef RK_PHONE
+ strcat(acVersion,"-PHONE");
+#endif
+#ifdef RK_VIR
+ strcat(acVersion,"-VR");
+#endif
+
+ /* RK_GRAPHICS_VER=commit-id:067e5d0: only keep string after '=' */
+ sscanf(RK_GRAPHICS_VER, "%*[^=]=%s", acCommit);
+
+ property_set("sys.ghwc.version", acVersion);
+ property_set("sys.ghwc.commit", acCommit);
+ ALOGD(RK_GRAPHICS_VER);
+ return 0;
+}
+
+#if USE_AFBC_LAYER
+bool isAfbcInternalFormat(uint64_t internal_format)
+{
+ return (internal_format & GRALLOC_ARM_INTFMT_AFBC);
+}
+#endif
+
+int init_thread_pamaters(threadPamaters* mThreadPamaters)
+{
+ if(mThreadPamaters) {
+ mThreadPamaters->count = 0;
+ pthread_mutex_init(&mThreadPamaters->mtx, NULL);
+ pthread_mutex_init(&mThreadPamaters->mlk, NULL);
+ pthread_cond_init(&mThreadPamaters->cond, NULL);
+ } else {
+ ALOGE("{%s}%d,mThreadPamaters is NULL",__FUNCTION__,__LINE__);
+ }
+ return 0;
+}
+
+int free_thread_pamaters(threadPamaters* mThreadPamaters)
+{
+ if(mThreadPamaters) {
+ pthread_mutex_destroy(&mThreadPamaters->mtx);
+ pthread_mutex_destroy(&mThreadPamaters->mlk);
+ pthread_cond_destroy(&mThreadPamaters->cond);
+ } else {
+ ALOGE("{%s}%d,mThreadPamaters is NULL",__FUNCTION__,__LINE__);
+ }
+ return 0;
+}
+
+#if RK_INVALID_REFRESH
+void TimeInt2Obj(int imSecond, timeval *ptVal)
+{
+ ptVal->tv_sec=imSecond/1000;
+ ptVal->tv_usec=(imSecond%1000)*1000;
+}
+
+int hwc_static_screen_opt_set(bool isGLESComp)
+{
+ struct itimerval tv = {{0,0},{0,0}};
+ if (!isGLESComp) {
+ int interval_value = hwc_get_int_property("sys.vwb.time", "2500");
+ interval_value = interval_value > 5000? 5000:interval_value;
+ interval_value = interval_value < 250? 250:interval_value;
+ TimeInt2Obj(interval_value,&tv.it_value);
+ ALOGD_IF(log_level(DBG_VERBOSE),"reset timer!");
+ } else {
+ tv.it_value.tv_usec = 0;
+ ALOGD_IF(log_level(DBG_VERBOSE),"close timer!");
+ }
+ setitimer(ITIMER_REAL, &tv, NULL);
+ return 0;
+}
+#endif
+
+#if 1
+int detect_3d_mode(hwc_drm_display_t *hd, hwc_display_contents_1_t *display_content, int display)
+{
+ bool is_3d = false;
+ int force3d = 0;
+ unsigned int numlayer = display_content->numHwLayers;
+ int needStereo = 0;
+
+ for (unsigned int j = 0; j <(numlayer - 1); j++) {
+ if(display_content->hwLayers[j].alreadyStereo) {
+ needStereo = display_content->hwLayers[j].alreadyStereo;
+ break;
+ }
+ }
+
+ if(!needStereo)
+ {
+ force3d = hwc_get_int_property("sys.hwc.force3d.primary","0");
+
+ if(1==force3d || 2==force3d){
+ if(display == 0 || display == 1)
+ needStereo = force3d;
+ }
+ }
+
+ if(needStereo)
+ {
+ is_3d = true;
+ if(needStereo == 1)
+ hd->stereo_mode = H_3D;
+ else if (needStereo == 2)
+ hd->stereo_mode = V_3D;
+ else if (needStereo == 8)
+ hd->stereo_mode = FPS_3D;
+ else
+ ALOGD_IF(log_level(DBG_VERBOSE),"It is unknow 3d mode needStereo=%d",needStereo);
+ }
+
+ for (unsigned int j = 0; j <(numlayer - 1); j++) {
+ display_content->hwLayers[j].displayStereo = needStereo;
+ }
+
+ if (needStereo & 0x8000) {
+ for (unsigned int j = 0; j <(numlayer - 1); j++) {
+ display_content->hwLayers[j].alreadyStereo = 0;
+ display_content->hwLayers[j].displayStereo = (needStereo & (~0x8000));
+ }
+ }
+#if 0
+ if(1==display && numlayer > 1) {
+ ALOGD_IF(log_level(DBG_VERBOSE),"Wake up hwc control stereo");
+ pthread_mutex_lock(&mControlStereo->mlk);
+ ctx->mControlStereo.count = needStereo;
+ pthread_mutex_unlock(&mControlStereo->mlk);
+ pthread_cond_signal(&mControlStereo->cond);
+ }
+#endif
+ return is_3d;
+}
+#endif
+
+#if 0
+int map_3d_mode(int value, int flag)
+{
+ if(flag == READ_3D_MODE)
+ {
+ switch (value)
+ {
+ case 0:
+ return 8;
+ case 6:
+ return 2;
+ case 8:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ else if(flag == WRITE_3D_MODE)
+ {
+ switch (value)
+ {
+ case 1:
+ return 8;
+ case 2:
+ return 6;
+ case 8:
+ return 0;
+ default:
+ return -1;
+ }
+ }
+ else
+ {
+ ALOGE("%s:line=%d invalid flag =%d", __FUNCTION__, __LINE__, flag);
+ return -1;
+ }
+
+}
+
+/*
+ * get or set 3d mode.
+ * flag : 0--read 1--write
+ */
+int hwc_control_3dmode(int fd_3d, int value, int flag)
+{
+ int ret = 0;
+ int iMode;
+ ssize_t err;
+ char buf[200];
+
+ if(fd_3d < 0)
+ return -1;
+
+ switch(flag){
+ case READ_3D_MODE: //read
+ memset(buf,0,sizeof(buf));
+ lseek(fd,0,SEEK_SET);
+ err = read(fd, buf, sizeof(buf));
+ if(err <= 0)
+ ALOGW("read hdmi 3dmode err=%zd",err);
+
+ int mode,hdmi3dmode;
+ sscanf(buf,"3dmodes=%d cur3dmode=%d",&mode,&hdmi3dmode);
+ ALOGI_IF(log_level(DBG_VERBOSE),"hdmi3dmode=%d,mode=%d",hdmi3dmode,mode);
+
+ ret = map_3d_mode(value, READ_3D_MODE);
+ break;
+
+ case WRITE_3D_MODE: //write
+ lseek(fd,0,SEEK_SET);
+ iMode = map_3d_mode(value, WRITE_3D_MODE);
+ char acMode[25];
+ if(iMode != -1)
+ {
+ sprintf(acMode,"%d",iMode);
+ ret = write(fd, acMode, 2);
+ if(ret < 0)
+ {
+ ALOGE("change 3dmode to %d err is %s",value,strerror(errno));
+ }
+ }
+ else
+ {
+ ALOGE("%s:line=%d invalid write mode", __FUNCTION__, __LINE__);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+#endif
+
+int hwc_get_handle_width(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_WIDTH;
+ int width = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &width);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return width;
+}
+
+int hwc_get_handle_height(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_HEIGHT;
+ int height = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &height);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return height;
+}
+
+int hwc_get_handle_stride(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_STRIDE;
+ int stride = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &stride);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return stride;
+}
+
+int hwc_get_handle_byte_stride(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_BYTE_STRIDE;
+ int byte_stride = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &byte_stride);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return byte_stride;
+}
+
+int hwc_get_handle_format(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_FORMAT;
+ int format = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &format);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return format;
+}
+
+int hwc_get_handle_usage(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_USAGE;
+ int usage = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &usage);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return usage;
+}
+
+int hwc_get_handle_size(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_SIZE;
+ int size = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &size);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return size;
+}
+
+/*
+@func hwc_get_handle_attributes:get attributes from handle.Before call this api,As far as now,
+ we need register the buffer first.May be the register is good for processer I think
+
+@param hnd:
+@param attrs: if size of attrs is small than 5,it will return EINVAL else
+ width = attrs[0]
+ height = attrs[1]
+ stride = attrs[2]
+ format = attrs[3]
+ size = attrs[4]
+*/
+int hwc_get_handle_attributes(const gralloc_module_t *gralloc, buffer_handle_t hnd, std::vector<int> *attrs)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_ATTRIBUTES;
+
+ if (!hnd)
+ return -EINVAL;
+
+ if(gralloc && gralloc->perform)
+ {
+ ret = gralloc->perform(gralloc, op, hnd, attrs);
+ }
+ else
+ {
+ ret = -EINVAL;
+ }
+
+
+ if(ret) {
+ ALOGE("hwc_get_handle_attributes fail %d for:%s hnd=%p",ret,strerror(ret),hnd);
+ }
+
+ return ret;
+}
+
+int hwc_get_handle_attibute(const gralloc_module_t *gralloc, buffer_handle_t hnd, attribute_flag_t flag)
+{
+ std::vector<int> attrs;
+ int ret=0;
+
+ if(!hnd)
+ {
+ ALOGE("%s handle is null",__FUNCTION__);
+ return -1;
+ }
+
+ ret = hwc_get_handle_attributes(gralloc, hnd, &attrs);
+ if(ret < 0)
+ {
+ ALOGE("getHandleAttributes fail %d for:%s",ret,strerror(ret));
+ return ret;
+ }
+ else
+ {
+ return attrs.at(flag);
+ }
+}
+
+/*
+@func getHandlePrimeFd:get prime_fd from handle.Before call this api,As far as now, we
+ need register the buffer first.May be the register is good for processer I think
+
+@param hnd:
+@return fd: prime_fd. and driver can call the dma_buf_get to get the buffer
+
+*/
+int hwc_get_handle_primefd(const gralloc_module_t *gralloc, buffer_handle_t hnd)
+{
+ int ret = 0;
+ int op = GRALLOC_MODULE_PERFORM_GET_HADNLE_PRIME_FD;
+ int fd = -1;
+
+ if(gralloc && gralloc->perform)
+ ret = gralloc->perform(gralloc, op, hnd, &fd);
+ else
+ ret = -EINVAL;
+
+ if(ret != 0)
+ {
+ ALOGE("%s:cann't get value from gralloc", __FUNCTION__);
+ }
+
+ return fd;
+}
+
+bool vop_support_format(uint32_t hal_format) {
+ switch (hal_format) {
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_YCrCb_NV12:
+ case HAL_PIXEL_FORMAT_YCrCb_NV12_10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool vop_support_scale(hwc_layer_1_t *layer) {
+ float hfactor;
+ float vfactor;
+ DrmHwcRect<float> source_crop;
+ DrmHwcRect<int> display_frame;
+
+ source_crop = DrmHwcRect<float>(
+ layer->sourceCropf.left, layer->sourceCropf.top,
+ layer->sourceCropf.right, layer->sourceCropf.bottom);
+ display_frame = DrmHwcRect<int>(
+ layer->displayFrame.left, layer->displayFrame.top,
+ layer->displayFrame.right, layer->displayFrame.bottom);
+
+ if((layer->transform == HWC_TRANSFORM_ROT_90)
+ ||(layer->transform == HWC_TRANSFORM_ROT_270)){
+ hfactor = (float) (source_crop.bottom - source_crop.top)
+ / (display_frame.right - display_frame.left);
+ vfactor = (float) (source_crop.right - source_crop.left)
+ / (display_frame.bottom - display_frame.top);
+ } else {
+ hfactor = (float) (source_crop.right - source_crop.left)
+ / (display_frame.right - display_frame.left);
+ vfactor = (float) (source_crop.bottom - source_crop.top)
+ / (display_frame.bottom - display_frame.top);
+ }
+ if(hfactor >= 8.0 || vfactor >= 8.0 || hfactor <= 0.125 || vfactor <= 0.125 ){
+ ALOGD_IF(log_level(DBG_DEBUG), "scale [%f,%f]not support! at line=%d", hfactor, vfactor, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_rec1_intersect_rec2(DrmHwcRect<int>* rec1,DrmHwcRect<int>* rec2)
+{
+ int iMaxLeft,iMaxTop,iMinRight,iMinBottom;
+ ALOGD_IF(log_level(DBG_DEBUG),"is_not_intersect: rec1[%d,%d,%d,%d],rec2[%d,%d,%d,%d]",rec1->left,rec1->top,
+ rec1->right,rec1->bottom,rec2->left,rec2->top,rec2->right,rec2->bottom);
+
+ iMaxLeft = rec1->left > rec2->left ? rec1->left: rec2->left;
+ iMaxTop = rec1->top > rec2->top ? rec1->top: rec2->top;
+ iMinRight = rec1->right <= rec2->right ? rec1->right: rec2->right;
+ iMinBottom = rec1->bottom <= rec2->bottom ? rec1->bottom: rec2->bottom;
+
+ if(iMaxLeft > iMinRight || iMaxTop > iMinBottom)
+ return false;
+ else
+ return true;
+
+ return false;
+}
+
+int is_x_intersect(DrmHwcRect<int>* rec,DrmHwcRect<int>* rec2)
+{
+ if(rec2->top == rec->top)
+ return 1;
+ else if(rec2->top < rec->top)
+ {
+ if(rec2->bottom > rec->top)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ {
+ if(rec->bottom > rec2->top )
+ return 1;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+
+static bool is_layer_combine(DrmHwcLayer * layer_one,DrmHwcLayer * layer_two)
+{
+ //Don't care format.
+ if(/*layer_one->format != layer_two->format
+ ||*/ layer_one->alpha!= layer_two->alpha
+ || layer_one->is_scale || layer_two->is_scale
+ || is_rec1_intersect_rec2(&layer_one->display_frame,&layer_two->display_frame)
+ #ifdef TARGET_BOARD_PLATFORM_RK3288
+ || is_x_intersect(&layer_one->display_frame,&layer_two->display_frame)
+ #endif
+ )
+ {
+ ALOGD_IF(log_level(DBG_SILENT),"is_layer_combine layer one alpha=%d,is_scale=%d",layer_one->alpha,layer_one->is_scale);
+ ALOGD_IF(log_level(DBG_SILENT),"is_layer_combine layer two alpha=%d,is_scale=%d",layer_two->alpha,layer_two->is_scale);
+ return false;
+ }
+
+ return true;
+}
+
+static bool has_layer(std::vector<DrmHwcLayer*>& layer_vector,DrmHwcLayer &layer)
+{
+ for (std::vector<DrmHwcLayer*>::const_iterator iter = layer_vector.begin();
+ iter != layer_vector.end(); ++iter) {
+ if((*iter)->sf_handle==layer.sf_handle)
+ if((*iter)->bClone_ == layer.bClone_)
+ return true;
+ }
+
+ return false;
+}
+
+static int combine_layer(LayerMap& layer_map,std::vector<DrmHwcLayer>& layers,
+ int iPlaneSize)
+{
+ /*Group layer*/
+ int zpos = 0;
+ size_t i,j;
+ uint32_t sort_cnt=0;
+ bool is_combine = false;
+ int layers_size = (int)layers.size();
+ size_t min_size = (iPlaneSize < layers_size) ? iPlaneSize:layers_size;
+
+ layer_map.clear();
+
+ for (i = 0; i < layers.size(); ) {
+ if(!layers[i].bUse)
+ continue;
+
+ sort_cnt=0;
+ if(i == 0)
+ {
+ layer_map[zpos].push_back(&layers[0]);
+ }
+
+ if(i == min_size)
+ {
+ //We can use pre-comp to optimise.
+ ALOGD_IF(log_level(DBG_DEBUG),"combine_layer fail: it remain layer i=%zu, min_size=%zu",i,min_size);
+ return -1;
+ }
+
+ for(j = i+1; j < min_size; j++) {
+ DrmHwcLayer &layer_one = layers[j];
+ //layer_one.index = j;
+ is_combine = false;
+
+ for(size_t k = 0; k <= sort_cnt; k++ ) {
+ DrmHwcLayer &layer_two = layers[j-1-k];
+ //layer_two.index = j-1-k;
+ //juage the layer is contained in layer_vector
+ bool bHasLayerOne = has_layer(layer_map[zpos],layer_one);
+ bool bHasLayerTwo = has_layer(layer_map[zpos],layer_two);
+
+ //If it contain both of layers,then don't need to go down.
+ if(bHasLayerOne && bHasLayerTwo)
+ continue;
+
+ if(is_layer_combine(&layer_one,&layer_two)) {
+ //append layer into layer_vector of layer_map_.
+ if(!bHasLayerOne && !bHasLayerTwo)
+ {
+ layer_map[zpos].emplace_back(&layer_one);
+ layer_map[zpos].emplace_back(&layer_two);
+ is_combine = true;
+ }
+ else if(!bHasLayerTwo)
+ {
+ is_combine = true;
+ for(std::vector<DrmHwcLayer*>::const_iterator iter= layer_map[zpos].begin();
+ iter != layer_map[zpos].end();++iter)
+ {
+ if((*iter)->sf_handle==layer_one.sf_handle)
+ if((*iter)->bClone_==layer_one.bClone_)
+ continue;
+
+ if(!is_layer_combine(*iter,&layer_two))
+ {
+ is_combine = false;
+ break;
+ }
+ }
+
+ if(is_combine)
+ layer_map[zpos].emplace_back(&layer_two);
+ }
+ else if(!bHasLayerOne)
+ {
+ is_combine = true;
+ for(std::vector<DrmHwcLayer*>::const_iterator iter= layer_map[zpos].begin();
+ iter != layer_map[zpos].end();++iter)
+ {
+ if((*iter)->sf_handle==layer_two.sf_handle)
+ if((*iter)->bClone_==layer_two.bClone_)
+ continue;
+
+ if(!is_layer_combine(*iter,&layer_one))
+ {
+ is_combine = false;
+ break;
+ }
+ }
+
+ if(is_combine)
+ {
+ layer_map[zpos].emplace_back(&layer_one);
+ }
+ }
+ }
+
+ if(!is_combine)
+ {
+ //if it cann't combine two layer,it need start a new group.
+ if(!bHasLayerOne)
+ {
+ zpos++;
+ layer_map[zpos].emplace_back(&layer_one);
+ }
+ is_combine = false;
+ break;
+ }
+ }
+ sort_cnt++; //update sort layer count
+ if(!is_combine)
+ {
+ break;
+ }
+ }
+
+ if(is_combine) //all remain layer or limit MOST_WIN_ZONES layer is combine well,it need start a new group.
+ zpos++;
+ if(sort_cnt)
+ i+=sort_cnt; //jump the sort compare layers.
+ else
+ i++;
+ }
+
+#ifndef TARGET_BOARD_PLATFORM_RK3288
+ //sort layer by xpos
+ for (LayerMap::iterator iter = layer_map.begin();
+ iter != layer_map.end(); ++iter) {
+ if(iter->second.size() > 1) {
+ for(uint32_t i=0;i < iter->second.size()-1;i++) {
+ for(uint32_t j=i+1;j < iter->second.size();j++) {
+ if(iter->second[i]->display_frame.left > iter->second[j]->display_frame.left) {
+ ALOGD_IF(log_level(DBG_DEBUG),"swap %s and %s",iter->second[i]->name.c_str(),iter->second[j]->name.c_str());
+ std::swap(iter->second[i],iter->second[j]);
+ }
+ }
+ }
+ }
+ }
+#else
+ //sort layer by ypos
+ for (LayerMap::iterator iter = layer_map.begin();
+ iter != layer_map.end(); ++iter) {
+ if(iter->second.size() > 1) {
+ for(uint32_t i=0;i < iter->second.size()-1;i++) {
+ for(uint32_t j=i+1;j < iter->second.size();j++) {
+ if(iter->second[i]->display_frame.top > iter->second[j]->display_frame.top) {
+ ALOGD_IF(log_level(DBG_DEBUG),"swap %s and %s",iter->second[i]->name.c_str(),iter->second[j]->name.c_str());
+ std::swap(iter->second[i],iter->second[j]);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ for (LayerMap::iterator iter = layer_map.begin();
+ iter != layer_map.end(); ++iter) {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer map id=%d,size=%zu",iter->first,iter->second.size());
+ for(std::vector<DrmHwcLayer*>::const_iterator iter_layer = iter->second.begin();
+ iter_layer != iter->second.end();++iter_layer)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"\tlayer name=%s",(*iter_layer)->name.c_str());
+ }
+ }
+
+ return 0;
+}
+
+static bool rkHasPlanesWithSize(DrmCrtc *crtc, int layer_size) {
+ DrmResources* drm = crtc->getDrmReoources();
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+
+ //loop plane groups.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ if(!(*iter)->bUse && (*iter)->planes.size() == (size_t)layer_size)
+ return true;
+ }
+ return false;
+}
+
+static std::vector<DrmPlane *> rkGetNoYuvUsablePlanes(DrmCrtc *crtc) {
+ DrmResources* drm = crtc->getDrmReoources();
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ std::vector<DrmPlane *> usable_planes;
+ //loop plane groups.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ if(!(*iter)->bUse)
+ //only count the first plane in plane group.
+ std::copy_if((*iter)->planes.begin(), (*iter)->planes.begin()+1,
+ std::back_inserter(usable_planes),
+ [=](DrmPlane *plane) {
+ return !plane->is_use() && plane->GetCrtcSupported(*crtc) && !plane->get_yuv(); }
+ );
+ }
+ return usable_planes;
+}
+
+static std::vector<DrmPlane *> rkGetNoScaleUsablePlanes(DrmCrtc *crtc) {
+ DrmResources* drm = crtc->getDrmReoources();
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ std::vector<DrmPlane *> usable_planes;
+ //loop plane groups.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ if(!(*iter)->bUse)
+ //only count the first plane in plane group.
+ std::copy_if((*iter)->planes.begin(), (*iter)->planes.begin()+1,
+ std::back_inserter(usable_planes),
+ [=](DrmPlane *plane) {
+ return !plane->is_use() && plane->GetCrtcSupported(*crtc) && !plane->get_scale(); }
+ );
+ }
+ return usable_planes;
+}
+
+static std::vector<DrmPlane *> rkGetNoAlphaUsablePlanes(DrmCrtc *crtc) {
+ DrmResources* drm = crtc->getDrmReoources();
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ std::vector<DrmPlane *> usable_planes;
+ //loop plane groups.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ if(!(*iter)->bUse)
+ //only count the first plane in plane group.
+ std::copy_if((*iter)->planes.begin(), (*iter)->planes.begin()+1,
+ std::back_inserter(usable_planes),
+ [=](DrmPlane *plane) {
+ return !plane->is_use() && plane->GetCrtcSupported(*crtc) && !plane->alpha_property().id(); }
+ );
+ }
+ return usable_planes;
+}
+
+//According to zpos and combine layer count,find the suitable plane.
+static bool MatchPlane(std::vector<DrmHwcLayer*>& layer_vector,
+ uint64_t* zpos,
+ DrmCrtc *crtc,
+ DrmResources *drm,
+ std::vector<DrmCompositionPlane>& composition_planes,
+ bool bMulArea)
+{
+ uint32_t combine_layer_count = 0;
+ uint32_t layer_size = layer_vector.size();
+ bool b_yuv=false,b_scale=false,b_alpha=false;
+ std::vector<PlaneGroup *> ::const_iterator iter;
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ uint64_t rotation = 0;
+ uint64_t alpha = 0xFF;
+
+ //loop plane groups.
+ for (iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ ALOGD_IF(log_level(DBG_DEBUG),"line=%d,last zpos=%" PRIu64 ",group(%" PRIu64 ") zpos=%d,group bUse=%d,crtc=0x%x,possible_crtcs=0x%x",
+ __LINE__, *zpos, (*iter)->share_id, (*iter)->zpos, (*iter)->bUse, (1<<crtc->pipe()), (*iter)->possible_crtcs);
+ //find the match zpos plane group
+ if(!(*iter)->bUse)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"line=%d,layer_size=%d,planes size=%zu",__LINE__,layer_size,(*iter)->planes.size());
+
+ //find the match combine layer count with plane size.
+ if(layer_size <= (*iter)->planes.size())
+ {
+ //loop layer
+ for(std::vector<DrmHwcLayer*>::const_iterator iter_layer= layer_vector.begin();
+ iter_layer != layer_vector.end();++iter_layer)
+ {
+ //reset is_match to false
+ (*iter_layer)->is_match = false;
+
+ if(bMulArea
+ && !(*iter_layer)->is_yuv
+ && !(*iter_layer)->is_scale
+ && !((*iter_layer)->blending == DrmHwcBlending::kPreMult && (*iter_layer)->alpha != 0xFF)
+ && layer_size == 1
+ && layer_size < (*iter)->planes.size())
+ {
+ if(rkHasPlanesWithSize(crtc, layer_size))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Planes(%" PRIu64 ") don't need use multi area feature",(*iter)->share_id);
+ continue;
+ }
+ }
+
+ //loop plane
+ for(std::vector<DrmPlane*> ::const_iterator iter_plane=(*iter)->planes.begin();
+ !(*iter)->planes.empty() && iter_plane != (*iter)->planes.end(); ++iter_plane)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"line=%d,crtc=0x%x,plane(%d) is_use=%d,possible_crtc_mask=0x%x",__LINE__,(1<<crtc->pipe()),
+ (*iter_plane)->id(),(*iter_plane)->is_use(),(*iter_plane)->get_possible_crtc_mask());
+ if(!(*iter_plane)->is_use() && (*iter_plane)->GetCrtcSupported(*crtc))
+ {
+
+ bool bNeed = false;
+ b_yuv = (*iter_plane)->get_yuv();
+ if((*iter_layer)->is_yuv)
+ {
+ if(!b_yuv)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) cann't support yuv",(*iter_plane)->id());
+ continue;
+ }
+ else
+ bNeed = true;
+ }
+
+ b_scale = (*iter_plane)->get_scale();
+ if((*iter_layer)->is_scale)
+ {
+ if(!b_scale)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) cann't support scale",(*iter_plane)->id());
+ continue;
+ }
+ else
+ {
+ if((*iter_layer)->h_scale_mul >= 8.0 || (*iter_layer)->v_scale_mul >= 8.0 ||
+ (*iter_layer)->h_scale_mul <= 0.125 || (*iter_layer)->v_scale_mul <= 0.125)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) cann't support scale factor(%f,%f)",
+ (*iter_plane)->id(), (*iter_layer)->h_scale_mul, (*iter_layer)->v_scale_mul);
+ continue;
+ }
+ else
+ bNeed = true;
+ }
+ }
+
+ if ((*iter_layer)->blending == DrmHwcBlending::kPreMult)
+ alpha = (*iter_layer)->alpha;
+
+ b_alpha = (*iter_plane)->alpha_property().id()?true:false;
+ if(alpha != 0xFF)
+ {
+ if(!b_alpha)
+ {
+ ALOGV("layer name=%s,plane id=%d",(*iter_layer)->name.c_str(),(*iter_plane)->id());
+ ALOGV("layer alpha=0x%x,alpha id=%d",(*iter_layer)->alpha,(*iter_plane)->alpha_property().id());
+ continue;
+ }
+ else
+ bNeed = true;
+ }
+
+ if(!bNeed && !bMulArea)
+ {
+ if(!(*iter_layer)->is_yuv && b_yuv)
+ {
+ std::vector<DrmPlane *> no_yuv_planes = rkGetNoYuvUsablePlanes(crtc);
+ if(no_yuv_planes.size() > 0)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) don't need use yuv feature",(*iter_plane)->id());
+ continue;
+ }
+ }
+
+ if(!(*iter_layer)->is_scale && b_scale)
+ {
+ std::vector<DrmPlane *> no_scale_planes = rkGetNoScaleUsablePlanes(crtc);
+ if(no_scale_planes.size() > 0)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) don't need use scale feature",(*iter_plane)->id());
+ continue;
+ }
+ }
+
+ if(alpha == 0xFF && b_alpha)
+ {
+ std::vector<DrmPlane *> no_alpha_planes = rkGetNoAlphaUsablePlanes(crtc);
+ if(no_alpha_planes.size() > 0)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Plane(%d) don't need use alpha feature",(*iter_plane)->id());
+ continue;
+ }
+ }
+ }
+#if RK_RGA
+ if(!drm->isSupportRkRga()
+#if USE_AFBC_LAYER
+ || isAfbcInternalFormat((*iter_layer)->internal_format)
+#endif
+ )
+#endif
+ {
+ rotation = 0;
+ if ((*iter_layer)->transform & DrmHwcTransform::kFlipH)
+ rotation |= 1 << DRM_REFLECT_X;
+ if ((*iter_layer)->transform & DrmHwcTransform::kFlipV)
+ rotation |= 1 << DRM_REFLECT_Y;
+ if ((*iter_layer)->transform & DrmHwcTransform::kRotate90)
+ rotation |= 1 << DRM_ROTATE_90;
+ else if ((*iter_layer)->transform & DrmHwcTransform::kRotate180)
+ rotation |= 1 << DRM_ROTATE_180;
+ else if ((*iter_layer)->transform & DrmHwcTransform::kRotate270)
+ rotation |= 1 << DRM_ROTATE_270;
+ if(rotation && !(rotation & (*iter_plane)->get_rotate()))
+ continue;
+ }
+
+ ALOGD_IF(log_level(DBG_DEBUG),"MatchPlane: match layer=%s,plane=%d,(*iter_layer)->index=%zu",(*iter_layer)->name.c_str(),
+ (*iter_plane)->id(),(*iter_layer)->index);
+ //Find the match plane for layer,it will be commit.
+ composition_planes.emplace_back(DrmCompositionPlane::Type::kLayer, (*iter_plane), crtc, (*iter_layer)->zpos);
+ (*iter_layer)->is_match = true;
+ (*iter_plane)->set_use(true);
+ composition_planes.back().set_zpos((*iter_layer)->zpos);
+ combine_layer_count++;
+ break;
+
+ }
+ }
+ }
+ if(combine_layer_count == layer_size)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"line=%d all match",__LINE__);
+ //update zpos for the next time.
+ *zpos += 1;
+ (*iter)->bUse = true;
+ return true;
+ }
+ }
+ /*else
+ {
+ //1. cut out combine_layer_count to (*iter)->planes.size().
+ //2. combine_layer_count layer assign planes.
+ //3. extern layers assign planes.
+ return false;
+ }*/
+ }
+
+ }
+
+ return false;
+}
+
+bool MatchPlanes(
+ std::map<int, std::vector<DrmHwcLayer*>> &layer_map,
+ DrmCrtc *crtc,
+ DrmResources *drm,
+ std::vector<DrmCompositionPlane>& composition_planes,
+ bool bMulArea)
+{
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ uint64_t last_zpos=0;
+ bool bMatch = false;
+ uint32_t planes_can_use=0;
+
+ //set use flag to false.
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ (*iter)->bUse=false;
+ for(std::vector<DrmPlane *> ::const_iterator iter_plane=(*iter)->planes.begin();
+ iter_plane != (*iter)->planes.end(); ++iter_plane) {
+ if((*iter_plane)->GetCrtcSupported(*crtc)) //only init the special crtc's plane
+ (*iter_plane)->set_use(false);
+ }
+ }
+
+ //clear composition_plane
+ composition_planes.clear();
+
+ for (LayerMap::iterator iter = layer_map.begin();
+ iter != layer_map.end(); ++iter) {
+ bMatch = MatchPlane(iter->second, &last_zpos, crtc, drm, composition_planes, bMulArea);
+ if(!bMatch)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"hwc_prepare: Cann't find the match plane for layer group %d",iter->first);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static float getPixelWidthByAndroidFormat(int format)
+{
+ float pixelWidth = 0.0;
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ pixelWidth = 4.0;
+ break;
+
+ case HAL_PIXEL_FORMAT_RGB_888:
+ pixelWidth = 3.0;
+ break;
+
+ case HAL_PIXEL_FORMAT_RGB_565:
+ pixelWidth = 2.0;
+ break;
+
+ case HAL_PIXEL_FORMAT_sRGB_A_8888:
+ case HAL_PIXEL_FORMAT_sRGB_X_8888:
+ ALOGE("format 0x%x not support",format);
+ break;
+
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case HAL_PIXEL_FORMAT_YCrCb_NV12:
+ case HAL_PIXEL_FORMAT_YCrCb_NV12_VIDEO:
+ pixelWidth = 1.0;
+ break;
+
+ case HAL_PIXEL_FORMAT_YCrCb_NV12_10:
+ pixelWidth = 2;
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP_10:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP_10:
+ pixelWidth = 1.0;
+ break;
+
+ default:
+ pixelWidth = 0.0;
+ ALOGE("format 0x%x not support",format);
+ break;
+ }
+ return pixelWidth;
+}
+
+static float vop_band_width(hwc_drm_display_t *hd, std::vector<DrmHwcLayer>& layers)
+{
+ float scale_factor = 0;
+
+ if(hd->mixMode == HWC_MIX_DOWN || hd->mixMode == HWC_MIX_UP ||
+ hd->mixMode == HWC_MIX_CROSS)
+ {
+ scale_factor += 1.0;
+ }
+
+ for(size_t i = 0; i < layers.size(); ++i)
+ {
+ scale_factor += layers[i].h_scale_mul * layers[i].v_scale_mul;
+ }
+
+ return scale_factor;
+}
+
+bool GetCrtcSupported(const DrmCrtc &crtc, uint32_t possible_crtc_mask) {
+ return !!((1 << crtc.pipe()) & possible_crtc_mask);
+}
+
+bool match_process(DrmResources* drm, DrmCrtc *crtc,
+ std::vector<DrmHwcLayer>& layers, int iPlaneSize,
+ std::vector<DrmCompositionPlane>& composition_planes)
+{
+ int zpos = 0;
+ LayerMap layer_map;
+ int iMatchCnt = 0;
+ bool bMatch = false;
+
+ if(!crtc)
+ {
+ ALOGE("%s:line=%d crtc is null",__FUNCTION__,__LINE__);
+ return false;
+ }
+
+ //update zpos of layer
+ for (size_t i = 0; i < layers.size(); ++i)
+ {
+ layers[i].zpos = zpos;
+ zpos++;
+ }
+
+ int ret = combine_layer(layer_map, layers, iPlaneSize);
+ if(ret == 0)
+ {
+ bool bMulArea = layers.size() > layer_map.size();
+ bMatch = MatchPlanes(layer_map,crtc,drm,composition_planes,bMulArea);
+ }
+
+ if(bMatch)
+ {
+ for(std::vector<DrmHwcLayer>::const_iterator iter_layer= layers.begin();
+ iter_layer != layers.end();++iter_layer)
+ {
+ if((*iter_layer).is_match)
+ {
+ iMatchCnt++;
+ }
+ }
+
+ if(iMatchCnt == (int)layers.size())
+ return true;
+ }
+
+ return false;
+}
+
+static bool try_mix_policy(DrmResources* drm, DrmCrtc *crtc,
+ std::vector<DrmHwcLayer>& layers, std::vector<DrmHwcLayer>& tmp_layers,
+ int iPlaneSize, std::vector<DrmCompositionPlane>& composition_planes,
+ int iFirst, int iLast)
+{
+ bool bAllMatch = false;
+
+ if(iFirst < 0 || iLast < 0 || iFirst > iLast)
+ {
+ ALOGE("invalid value iFirst=%d, iLast=%d", iFirst, iLast);
+ return false;
+ }
+
+ /*************************mix down*************************
+ many layers
+ -----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------
+ GLES | 711aa61e80 | 0000 | 0000 | 00 | 0100 | RGBx_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.systemui.ImageWallpaper
+ GLES | 711ab1ef00 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.launcher3/com.android.launcher3.Launcher
+ HWC | 711aa61100 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 2.0 | 0, 0, 2400, 2 | StatusBar
+ HWC | 711ec5ad80 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 84.0 | 0, 1516, 2400, 1600 | taskbar
+ HWC | 711ec5a900 | 0000 | 0002 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 39.0, 49.0 | 941, 810, 980, 859 | Sprite
+ ************************************************************/
+ ALOGD_IF(log_level(DBG_DEBUG), "Go into Mix policy");
+ int interval = layers.size()-1-iLast;
+ ALOGD_IF(log_level(DBG_DEBUG), "try_mix_policy iFirst=%d,interval=%d",iFirst,interval);
+ for (auto i = layers.begin() + iFirst; i != layers.end() - interval;)
+ {
+ if((*i).bClone_)
+ continue;
+
+ (*i).bMix = true;
+ (*i).raw_sf_layer->compositionType = HWC_MIX;
+
+ //move gles layers
+ tmp_layers.emplace_back(std::move(*i));
+ i = layers.erase(i);
+ }
+
+ //add fb layer.
+ int pos = iFirst;
+ for (auto i = tmp_layers.begin(); i != tmp_layers.end();)
+ {
+ if((*i).raw_sf_layer->compositionType == HWC_FRAMEBUFFER_TARGET)
+ {
+ layers.insert(layers.begin() + pos, std::move(*i));
+ pos++;
+ i = tmp_layers.erase(i);
+ continue;
+ }
+ i++;
+ }
+
+ bAllMatch = match_process(drm, crtc, layers, iPlaneSize, composition_planes);
+ if(bAllMatch)
+ return true;
+
+ return false;
+}
+
+void move_fb_layer_to_tmp(std::vector<DrmHwcLayer>& layers, std::vector<DrmHwcLayer>& tmp_layers)
+{
+ for (auto i = layers.begin(); i != layers.end();)
+ {
+ if((*i).raw_sf_layer->compositionType == HWC_FRAMEBUFFER_TARGET)
+ {
+ tmp_layers.emplace_back(std::move(*i));
+ i = layers.erase(i);
+ continue;
+ }
+ i++;
+ }
+}
+
+void resore_all_tmp_layers(std::vector<DrmHwcLayer>& layers, std::vector<DrmHwcLayer>& tmp_layers)
+{
+ for (auto i = tmp_layers.begin(); i != tmp_layers.end();)
+ {
+ layers.emplace_back(std::move(*i));
+ i = tmp_layers.erase(i);
+ }
+
+ //sort
+ for (auto i = layers.begin(); i != layers.end()-1; i++)
+ {
+ for (auto j = i+1; j != layers.end(); j++)
+ {
+ if((*i).index > (*j).index)
+ {
+ std::swap(*i, *j);
+ }
+ }
+ }
+}
+
+void resore_tmp_layers_except_fb(std::vector<DrmHwcLayer>& layers, std::vector<DrmHwcLayer>& tmp_layers)
+{
+ for (auto i = tmp_layers.begin(); i != tmp_layers.end();)
+ {
+ layers.emplace_back(std::move(*i));
+ i = tmp_layers.erase(i);
+ }
+
+ //sort by layer index
+ for (auto i = layers.begin(); i != layers.end()-1; i++)
+ {
+ for (auto j = i+1; j != layers.end(); j++)
+ {
+ if((*i).index > (*j).index)
+ {
+ std::swap(*i, *j);
+ }
+ }
+ }
+
+ move_fb_layer_to_tmp(layers, tmp_layers);
+}
+
+bool mix_policy(DrmResources* drm, DrmCrtc *crtc, hwc_drm_display_t *hd,
+ std::vector<DrmHwcLayer>& layers, int iPlaneSize,
+ std::vector<DrmCompositionPlane>& composition_planes)
+{
+ bool bAllMatch = false, bHasSkipLayer = false;
+ std::vector<DrmHwcLayer> tmp_layers;
+ int skipCnt = 0;
+ int iUsePlane = 0;
+ std::vector<PlaneGroup *>& plane_groups = drm->GetPlaneGroups();
+ // Since we can't composite HWC_SKIP_LAYERs by ourselves, we'll let SF
+ // handle all layers in between the first and last skip layers. So find the
+ // outer indices and mark everything in between as HWC_FRAMEBUFFER
+ std::pair<int, int> skip_layer_indices(-1, -1);
+ std::pair<int, int> layer_indices(-1, -1);
+
+
+ if(!crtc)
+ {
+ ALOGE("%s:line=%d crtc is null",__FUNCTION__,__LINE__);
+ return false;
+ }
+
+ //save fb into tmp_layers
+ move_fb_layer_to_tmp(layers, tmp_layers);
+
+ //caculate the first and last skip layer
+ for (int i = 0; i < (int)layers.size(); ++i) {
+ DrmHwcLayer& layer = layers[i];
+
+ if (!layer.bSkipLayer)
+ continue;
+
+ if (skip_layer_indices.first == -1)
+ skip_layer_indices.first = i;
+ skip_layer_indices.second = i;
+ }
+
+ if(skip_layer_indices.first != -1)
+ {
+ bHasSkipLayer = true;
+ skipCnt = skip_layer_indices.second - skip_layer_indices.first + 1;
+ }
+
+ //OPT: Adjust skip_layer_indices.first and skip_layer_indices.second to limit in iPlaneSize.
+ if(bHasSkipLayer && ((int)layers.size() - skipCnt + 1) > iPlaneSize)
+ {
+ int tmp_index = -1;
+ if(skip_layer_indices.first != 0)
+ {
+ tmp_index = skip_layer_indices.first;
+ skip_layer_indices.first = 0;
+ skipCnt = skip_layer_indices.second - skip_layer_indices.first + 1;
+ if(((int)layers.size() - skipCnt + 1) > iPlaneSize && skip_layer_indices.second != (int)layers.size()-1)
+ {
+ skip_layer_indices.first = tmp_index;
+ tmp_index = skip_layer_indices.second;
+ skip_layer_indices.second = layers.size()-1;
+ skipCnt = skip_layer_indices.second - skip_layer_indices.first + 1;
+ if(((int)layers.size() - skipCnt + 1) > iPlaneSize)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d fail match (%d,%d)",__FUNCTION__,__LINE__,skip_layer_indices.first, tmp_index);
+ goto FailMatch;
+ }
+ }
+ }
+ }
+
+ /*************************mix skip layer*************************/
+ if(bHasSkipLayer && ((int)layers.size() - skipCnt + 1) <= iPlaneSize)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:has skip layer (%d,%d)",__FUNCTION__,skip_layer_indices.first, skip_layer_indices.second);
+ if(hd->mixMode != HWC_MIX_CROSS)
+ hd->mixMode = HWC_MIX_CROSS;
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ skip_layer_indices.first, skip_layer_indices.second);
+ if(bAllMatch)
+ goto AllMatch;
+ else
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d fail match (%d,%d)",__FUNCTION__,__LINE__,skip_layer_indices.first, skip_layer_indices.second);
+ goto FailMatch;
+ }
+ }
+
+ /*************************mix 3d layer(mix up)*************************/
+ if(hd->is_3d)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix 3d (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ if(hd->mixMode != HWC_MIX_3D)
+ hd->mixMode = HWC_MIX_3D;
+
+ if(hd->stereo_mode == H_3D || hd->stereo_mode == V_3D || hd->stereo_mode == FPS_3D)
+ {
+ if(layers[0].stereo)
+ {
+ layer_indices.first = 1;
+ layer_indices.second = layers.size() - 1;
+
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ if(bAllMatch)
+ goto AllMatch;
+ else
+ {
+ //ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d fail match (%d,%d)",__FUNCTION__,__LINE__,skip_layer_indices.first, skip_layer_indices.second);
+ resore_tmp_layers_except_fb(layers, tmp_layers);
+ }
+ }
+ else
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d fail match (%d,%d)",__FUNCTION__,__LINE__,skip_layer_indices.first, skip_layer_indices.second);
+ goto FailMatch;
+ }
+ }
+ }
+
+ /*************************common match*************************/
+ bAllMatch = match_process(drm, crtc, layers, iPlaneSize, composition_planes);
+
+ if(bAllMatch)
+ goto AllMatch;
+
+ if((int)layers.size() < iPlaneSize || iPlaneSize < 4)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d fail match iPlaneSize=%d, layer size=%d",__FUNCTION__,__LINE__,iPlaneSize,(int)layers.size());
+ goto FailMatch;
+ }
+
+ /*************************mix up*************************
+ Video ovelay
+ -----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------
+ HWC | 711aa61e80 | 0000 | 0000 | 00 | 0100 | RGBx_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.systemui.ImageWallpaper
+ HWC | 711ab1ef00 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.launcher3/com.android.launcher3.Launcher
+ HWC | 711aa61700 | 0000 | 0000 | 00 | 0100 | ? 00000017 | 0.0, 0.0, 3840.0, 2160.0 | 600, 562, 1160, 982 | SurfaceView - MediaView
+ GLES | 711ab1e580 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 560.0, 420.0 | 600, 562, 1160, 982 | MediaView
+ GLES | 70b34c9c80 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 2.0 | 0, 0, 2400, 2 | StatusBar
+ GLES | 70b34c9080 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 84.0 | 0, 1516, 2400, 1600 | taskbar
+ GLES | 711ec5a900 | 0000 | 0002 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 39.0, 49.0 | 1136, 1194, 1175, 1243 | Sprite
+ ************************************************************/
+ if(hd->isVideo)
+ {
+ if(hd->mixMode != HWC_MIX_UP)
+ hd->mixMode = HWC_MIX_UP;
+ layer_indices.first = 3;
+ layer_indices.second = layers.size() - 1;
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix up for video (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ if(bAllMatch)
+ goto AllMatch;
+ else
+ resore_tmp_layers_except_fb(layers, tmp_layers);
+ }
+
+ /*************************mix down*************************
+ Sprite layer
+ -----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------
+ GLES | 711aa61e80 | 0000 | 0000 | 00 | 0100 | RGBx_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.systemui.ImageWallpaper
+ GLES | 711ab1ef00 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 1600.0 | 0, 0, 2400, 1600 | com.android.launcher3/com.android.launcher3.Launcher
+ HWC | 711aa61100 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 2.0 | 0, 0, 2400, 2 | StatusBar
+ HWC | 711ec5ad80 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 2400.0, 84.0 | 0, 1516, 2400, 1600 | taskbar
+ HWC | 711ec5a900 | 0000 | 0002 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 39.0, 49.0 | 941, 810, 980, 859 | Sprite
+ ************************************************************/
+ if(layers.size() >= 4 && layers.size() <= 6 )
+ {
+ if(hd->mixMode != HWC_MIX_DOWN)
+ hd->mixMode = HWC_MIX_DOWN;
+ layer_indices.first = 0;
+ layer_indices.second = 2;
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix down (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ if(bAllMatch)
+ goto AllMatch;
+ else
+ resore_tmp_layers_except_fb(layers, tmp_layers);
+ }
+
+ /*************************mix up*************************
+ Many layers
+ ************************************************************/
+ if(!hd->isVideo)
+ {
+ if(hd->mixMode != HWC_MIX_UP)
+ hd->mixMode = HWC_MIX_UP;
+ layer_indices.first = 3;
+ layer_indices.second = layers.size() - 1;
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix up (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ if(bAllMatch)
+ goto AllMatch;
+ else
+ goto FailMatch;
+ }
+ else
+ {
+ goto FailMatch;
+ }
+
+AllMatch:
+#if 1
+ /*************************vop band width limit*************************/
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter) {
+ if(GetCrtcSupported(*crtc, (*iter)->possible_crtcs) && (*iter)->bUse)
+ iUsePlane++;
+ }
+
+ if(iUsePlane >= hd->iPlaneSize)
+ {
+ float scale_factor = vop_band_width(hd, layers);
+ float head_factor = 0.0, tail_factor = 0.0;
+ if(scale_factor > 3.3)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "scale_factor=%f is so big",scale_factor);
+ if(layers.size() >= 4)
+ {
+ resore_tmp_layers_except_fb(layers, tmp_layers);
+
+ for(int k = 0; k < 2; k++)
+ {
+ head_factor += layers[k].h_scale_mul * layers[k].v_scale_mul;
+ }
+
+ for(size_t k = layers.size()-2; k < layers.size(); k++)
+ {
+ tail_factor += layers[k].h_scale_mul * layers[k].v_scale_mul;
+ }
+
+ if(head_factor > tail_factor)
+ {
+ //mix down
+ if(hd->mixMode != HWC_MIX_DOWN)
+ hd->mixMode = HWC_MIX_DOWN;
+ layer_indices.first = 0;
+ layer_indices.second = 1;
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix down (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ scale_factor = vop_band_width(hd, layers);
+ if(bAllMatch && scale_factor <= 3.3)
+ {
+ return true;
+ }
+ else
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d vop band with is too big,fail match (%d,%d),scale_factor=%f",
+ __FUNCTION__, __LINE__, layer_indices.first, layer_indices.second, scale_factor);
+ goto FailMatch;
+ }
+ }
+ else
+ {
+ //mix up
+ if(hd->mixMode != HWC_MIX_UP)
+ hd->mixMode = HWC_MIX_UP;
+ layer_indices.first = layers.size() - 2;
+ layer_indices.second = layers.size() - 1;
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:mix up (%d,%d)",__FUNCTION__,layer_indices.first, layer_indices.second);
+ bAllMatch = try_mix_policy(drm, crtc, layers, tmp_layers, iPlaneSize, composition_planes,
+ layer_indices.first, layer_indices.second);
+ scale_factor = vop_band_width(hd, layers);
+ if(bAllMatch && scale_factor <= 3.3)
+ return true;
+ else
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d vop band with is too big,fail match (%d,%d),scale_factor=%f",
+ __FUNCTION__, __LINE__, layer_indices.first, layer_indices.second, scale_factor);
+ goto FailMatch;
+ }
+ }
+ }
+ else
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d vop band with is too big,fail match layers.size=%zu",__FUNCTION__,__LINE__,layers.size());
+ goto FailMatch;
+ }
+ }
+ }
+#endif
+
+ return true;
+FailMatch:
+ ALOGD_IF(log_level(DBG_DEBUG), "%s:line=%d Fail match",__FUNCTION__,__LINE__);
+ //restore tmp layers to layers.
+ resore_all_tmp_layers(layers, tmp_layers);
+ //reset mix mode.
+ hd->mixMode = HWC_DEFAULT;
+
+ return false;
+}
+
+#if RK_VIDEO_UI_OPT
+void video_ui_optimize(const gralloc_module_t *gralloc, hwc_display_contents_1_t *display_content, hwc_drm_display_t *hd)
+{
+ int ret = 0;
+ int format = 0;
+ int num_layers = display_content->numHwLayers;
+ if(num_layers == 3)
+ {
+ hwc_layer_1_t *first_layer = &display_content->hwLayers[0];
+ if(first_layer->handle)
+ {
+#if RK_DRM_GRALLOC
+ format = hwc_get_handle_attibute(gralloc, first_layer->handle, ATT_FORMAT);
+#else
+ format = hwc_get_handle_format(gralloc, first_layer->handle);
+#endif
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12 || format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ {
+ bool bDiff = true;
+ int iUiFd = 0;
+ hwc_layer_1_t * second_layer = &display_content->hwLayers[1];
+#if RK_DRM_GRALLOC
+ format = hwc_get_handle_attibute(gralloc, second_layer->handle, ATT_FORMAT);
+#else
+ format = hwc_get_handle_format(gralloc, second_layer->handle);
+#endif
+ if(second_layer->handle &&
+ (format == HAL_PIXEL_FORMAT_RGBA_8888 ||
+ format == HAL_PIXEL_FORMAT_RGBX_8888 ||
+ format == HAL_PIXEL_FORMAT_BGRA_8888)
+ )
+ {
+ iUiFd = hwc_get_handle_primefd(gralloc, second_layer->handle);
+ bDiff = (iUiFd != hd->iUiFd);
+
+ if(bDiff)
+ {
+ hd->bHideUi = false;
+ /* Update the backup ui fd */
+ hd->iUiFd = iUiFd;
+ }
+ else if(!hd->bHideUi)
+ {
+#if RK_DRM_GRALLOC
+ int iWidth = hwc_get_handle_attibute(gralloc,second_layer->handle,ATT_WIDTH);
+ int iHeight = hwc_get_handle_attibute(gralloc,second_layer->handle,ATT_HEIGHT);
+#else
+ int iWidth = hwc_get_handle_width(gralloc,second_layer->handle);
+ int iHeight = hwc_get_handle_height(gralloc,second_layer->handle);
+#endif
+ unsigned int *cpu_addr;
+ gralloc->lock(gralloc, second_layer->handle, GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK,
+ 0, 0, iWidth, iHeight, (void **)&cpu_addr);
+ ret = DetectValidData((int *)(cpu_addr),iWidth,iHeight);
+ if(!ret){
+ hd->bHideUi = true;
+ ALOGD_IF(log_level(DBG_VERBOSE), "@video UI close,iWidth=%d,iHeight=%d",iWidth,iHeight);
+ }
+ gralloc->unlock(gralloc, second_layer->handle);
+ }
+
+ if(hd->bHideUi)
+ {
+ second_layer->compositionType = HWC_NODRAW;
+ }
+ else
+ {
+ second_layer->compositionType = HWC_FRAMEBUFFER;
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+void hwc_list_nodraw(hwc_display_contents_1_t *list)
+{
+ if (list == NULL)
+ {
+ return;
+ }
+ for (unsigned int i = 0; i < list->numHwLayers - 1; i++)
+ {
+ list->hwLayers[i].compositionType = HWC_NODRAW;
+ }
+ return;
+}
+
+void hwc_sync_release(hwc_display_contents_1_t *list)
+{
+ for (int i=0; i< (int)list->numHwLayers; i++){
+ hwc_layer_1_t* layer = &list->hwLayers[i];
+ if (layer == NULL){
+ return ;
+ }
+ if (layer->acquireFenceFd>0){
+ ALOGV(">>>close acquireFenceFd:%d,layername=%s",layer->acquireFenceFd,layer->LayerName);
+ close(layer->acquireFenceFd);
+ list->hwLayers[i].acquireFenceFd = -1;
+ }
+ }
+
+ if (list->outbufAcquireFenceFd>0){
+ ALOGV(">>>close outbufAcquireFenceFd:%d",list->outbufAcquireFenceFd);
+ close(list->outbufAcquireFenceFd);
+ list->outbufAcquireFenceFd = -1;
+ }
+}
+
+
+
+}
+
diff --git a/hwc_rockchip.h b/hwc_rockchip.h
new file mode 100755
index 0000000..5622b30
--- /dev/null
+++ b/hwc_rockchip.h
@@ -0,0 +1,125 @@
+#ifndef _HWC_ROCKCHIP_H_
+#define _HWC_ROCKCHIP_H_
+
+#include <map>
+#include <vector>
+#include "drmhwcomposer.h"
+#include "drmresources.h"
+#include "vsyncworker.h"
+
+
+namespace android {
+
+
+#define MOST_WIN_ZONES 4
+#if RK_STEREO
+#define READ_3D_MODE (0)
+#define WRITE_3D_MODE (1)
+#endif
+
+typedef std::map<int, std::vector<DrmHwcLayer*>> LayerMap;
+typedef LayerMap::iterator LayerMapIter;
+struct hwc_context_t;
+class VSyncWorker;
+
+typedef enum attribute_flag {
+ ATT_WIDTH = 0,
+ ATT_HEIGHT,
+ ATT_STRIDE,
+ ATT_FORMAT,
+ ATT_SIZE,
+ ATT_BYTE_STRIDE
+}attribute_flag_t;
+
+typedef enum tagMixMode
+{
+ HWC_DEFAULT,
+ HWC_MIX_DOWN,
+ HWC_MIX_UP,
+ HWC_MIX_CROSS,
+ HWC_MIX_3D,
+ HWC_POLICY_NUM
+}MixMode;
+
+#if RK_INVALID_REFRESH
+typedef struct _threadPamaters
+{
+ int count;
+ pthread_mutex_t mlk;
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+}threadPamaters;
+#endif
+
+typedef struct hwc_drm_display {
+ struct hwc_context_t *ctx;
+ int display;
+#if RK_VIDEO_UI_OPT
+ int iUiFd;
+ bool bHideUi;
+#endif
+ bool is10bitVideo;
+ MixMode mixMode;
+ bool isVideo;
+ int framebuffer_width;
+ int framebuffer_height;
+ int rel_xres;
+ int rel_yres;
+ int v_total;
+ int vrefresh;
+ int iPlaneSize;
+ float w_scale;
+ float h_scale;
+ bool active;
+ bool is_3d;
+ Mode3D stereo_mode;
+} hwc_drm_display_t;
+
+int hwc_init_version();
+
+#if USE_AFBC_LAYER
+bool isAfbcInternalFormat(uint64_t internal_format);
+#endif
+
+int init_thread_pamaters(threadPamaters* mThreadPamaters);
+int free_thread_pamaters(threadPamaters* mThreadPamaters);
+
+#if RK_INVALID_REFRESH
+int hwc_static_screen_opt_set(bool isGLESComp);
+#endif
+
+#if 1
+int detect_3d_mode(hwc_drm_display_t *hd, hwc_display_contents_1_t *display_content, int display);
+#endif
+#if 0
+int hwc_control_3dmode(int fd_3d, int value, int flag);
+#endif
+int hwc_get_handle_width(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_height(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_format(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_stride(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_byte_stride(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_usage(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_size(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+int hwc_get_handle_attributes(const gralloc_module_t *gralloc, buffer_handle_t hnd, std::vector<int> *attrs);
+int hwc_get_handle_attibute(const gralloc_module_t *gralloc, buffer_handle_t hnd, attribute_flag_t flag);
+int hwc_get_handle_primefd(const gralloc_module_t *gralloc, buffer_handle_t hnd);
+bool vop_support_format(uint32_t hal_format);
+bool vop_support_scale(hwc_layer_1_t *layer);
+bool GetCrtcSupported(const DrmCrtc &crtc, uint32_t possible_crtc_mask);
+bool match_process(DrmResources* drm, DrmCrtc *crtc,
+ std::vector<DrmHwcLayer>& layers, int iPlaneSize,
+ std::vector<DrmCompositionPlane>& composition_planes);
+bool mix_policy(DrmResources* drm, DrmCrtc *crtc, hwc_drm_display_t *hd,
+ std::vector<DrmHwcLayer>& layers, int iPlaneSize,
+ std::vector<DrmCompositionPlane>& composition_planes);
+#if RK_VIDEO_UI_OPT
+void video_ui_optimize(const gralloc_module_t *gralloc, hwc_display_contents_1_t *display_content, hwc_drm_display_t *hd);
+#endif
+void hwc_list_nodraw(hwc_display_contents_1_t *list);
+void hwc_sync_release(hwc_display_contents_1_t *list);
+
+
+}
+
+#endif
diff --git a/hwc_util.cpp b/hwc_util.cpp
new file mode 100644
index 0000000..c2e5f6b
--- /dev/null
+++ b/hwc_util.cpp
@@ -0,0 +1,89 @@
+#include "hwc_util.h"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+
+int hwc_get_int_property(const char* pcProperty,const char* default_value)
+{
+ char value[PROPERTY_VALUE_MAX];
+ int new_value = 0;
+
+ if(pcProperty == NULL || default_value == NULL)
+ {
+ ALOGE("hwc_get_int_property: invalid param");
+ return -1;
+ }
+
+ property_get(pcProperty, value, default_value);
+ new_value = atoi(value);
+
+ return new_value;
+}
+
+int hwc_get_string_property(const char* pcProperty,const char* default_value,char* retult)
+{
+ if(pcProperty == NULL || default_value == NULL || retult == NULL)
+ {
+ ALOGE("hwc_get_string_property: invalid param");
+ return -1;
+ }
+
+ property_get(pcProperty, retult, default_value);
+
+ return 0;
+}
+
+static int CompareLines(int *da,int w)
+{
+ int i,j;
+ for(i = 0;i<1;i++) // compare 4 lins
+ {
+ for(j= 0;j<w;j+=8)
+ {
+ if((unsigned int)*da != 0xff000000 && (unsigned int)*da != 0x0)
+ {
+ return 1;
+ }
+ da +=8;
+
+ }
+ }
+ return 0;
+}
+
+int DetectValidData(int *data,int w,int h)
+{
+ int i,j;
+ int *da;
+ int ret;
+ /* detect model
+ -------------------------
+ | | | | | |
+ | | | | | |
+ |------------------------|
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ |------------------------|
+ | | | | | |
+ | | | | | |
+ |------------------------|
+ | | | | | |
+ | | | | | |
+ |------------------------|
+ | | | | | |
+ --------------------------
+ */
+ if(data == NULL)
+ return 1;
+ for(i = 2; i<h; i+= 8)
+ {
+ da = data + i *w;
+ if(CompareLines(da,w))
+ return 1;
+ }
+
+ return 0;
+}
+
+
diff --git a/hwc_util.h b/hwc_util.h
new file mode 100644
index 0000000..d3f48df
--- /dev/null
+++ b/hwc_util.h
@@ -0,0 +1,14 @@
+#ifndef _HWC_UTIL_
+#define _HWC_UTIL_
+
+#define hwcMIN(x, y) (((x) <= (y)) ? (x) : (y))
+#define hwcMAX(x, y) (((x) >= (y)) ? (x) : (y))
+#define IS_ALIGN(val,align) (((val)&(align-1))==0)
+#define ALIGN( value, base ) (((value) + ((base) - 1)) & ~((base) - 1))
+#define ALIGN_DOWN( value, base) (value & (~(base-1)) )
+
+int hwc_get_int_property(const char* pcProperty,const char* default_value);
+int hwc_get_string_property(const char* pcProperty,const char* default_value,char* retult);
+int DetectValidData(int *data,int w,int h);
+
+#endif // _HWC_UTIL_
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
new file mode 100755
index 0000000..173271f
--- /dev/null
+++ b/hwcomposer.cpp
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwcomposer-drm"
+
+#include "drmhwcomposer.h"
+#include "drmeventlistener.h"
+#include "drmresources.h"
+#include "platform.h"
+#include "virtualcompositorworker.h"
+#include "vsyncworker.h"
+
+#include <stdlib.h>
+
+#include <cinttypes>
+#include <map>
+#include <vector>
+#include <sstream>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <sw_sync.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+#include <drm_fourcc.h>
+#if RK_DRM_GRALLOC
+#include "gralloc_drm_handle.h"
+#endif
+#include <linux/fb.h>
+
+#include "hwc_util.h"
+#include "hwc_rockchip.h"
+
+#define UM_PER_INCH 25400
+
+namespace android {
+
+static int hwc_set_active_config(struct hwc_composer_device_1 *dev, int display,
+ int index);
+
+
+#if SKIP_BOOT
+static unsigned int g_boot_cnt = 0;
+#endif
+#if RK_INVALID_REFRESH
+hwc_context_t* g_ctx = NULL;
+#endif
+
+class DummySwSyncTimeline {
+ public:
+ int Init() {
+ int ret = timeline_fd_.Set(sw_sync_timeline_create());
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+
+ UniqueFd CreateDummyFence() {
+ int ret = sw_sync_fence_create(timeline_fd_.get(), "dummy fence",
+ timeline_pt_ + 1);
+ if (ret < 0) {
+ ALOGE("Failed to create dummy fence %d", ret);
+ return ret;
+ }
+
+ UniqueFd ret_fd(ret);
+
+ ret = sw_sync_timeline_inc(timeline_fd_.get(), 1);
+ if (ret) {
+ ALOGE("Failed to increment dummy sync timeline %d", ret);
+ return ret;
+ }
+
+ ++timeline_pt_;
+ return ret_fd;
+ }
+
+ private:
+ UniqueFd timeline_fd_;
+ int timeline_pt_ = 0;
+};
+
+struct CheckedOutputFd {
+ CheckedOutputFd(int *fd, const char *description,
+ DummySwSyncTimeline &timeline)
+ : fd_(fd), description_(description), timeline_(timeline) {
+ }
+ CheckedOutputFd(CheckedOutputFd &&rhs)
+ : description_(rhs.description_), timeline_(rhs.timeline_) {
+ std::swap(fd_, rhs.fd_);
+ }
+
+ CheckedOutputFd &operator=(const CheckedOutputFd &rhs) = delete;
+
+ ~CheckedOutputFd() {
+ if (fd_ == NULL)
+ return;
+
+ if (*fd_ >= 0)
+ return;
+
+ *fd_ = timeline_.CreateDummyFence().Release();
+
+ if (*fd_ < 0)
+ ALOGE("Failed to fill %s (%p == %d) before destruction",
+ description_.c_str(), fd_, *fd_);
+ }
+
+ private:
+ int *fd_ = NULL;
+ std::string description_;
+ DummySwSyncTimeline &timeline_;
+};
+
+/**
+ * sys.3d_resolution.main 1920x1080p60-114693:148500
+ * width x height p|i refresh-flag:clock
+ */
+static int update_display_bestmode(hwc_drm_display_t *hd, int display, DrmConnector *c)
+{
+ char resolution[PROPERTY_VALUE_MAX];
+ char resolution_3d[PROPERTY_VALUE_MAX];
+ uint32_t width, height, vrefresh;
+ uint32_t width_3d, height_3d, vrefresh_3d, flag_3d, clk_3d;
+ bool interlaced, interlaced_3d;
+ char val,val_3d;
+
+ if (display == HWC_DISPLAY_PRIMARY)
+ {
+ property_get("persist.sys.resolution.main", resolution, "0x0p0");
+ property_get("sys.3d_resolution.main", resolution_3d, "0x0p0-0:0");
+ }
+ else
+ {
+ property_get("persist.sys.resolution.aux", resolution, "0x0p0");
+ property_get("sys.3d_resolution.aux", resolution_3d, "0x0p0-0:0");
+ }
+
+ if(hd->is_3d && strcmp(resolution_3d,"0x0p0-0:0"))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "Enter 3d resolution=%s",resolution_3d);
+ sscanf(resolution_3d, "%dx%d%c%d-%d:%d", &width_3d, &height_3d, &val_3d,
+ &vrefresh_3d, &flag_3d, &clk_3d);
+
+ if (val_3d == 'i')
+ interlaced_3d = true;
+ else
+ interlaced_3d = false;
+
+ if (width_3d != 0 && height_3d != 0) {
+ for (const DrmMode &conn_mode : c->modes()) {
+ if (conn_mode.equal(width_3d, height_3d, vrefresh_3d, flag_3d, clk_3d, interlaced_3d)) {
+ ALOGD_IF(log_level(DBG_DEBUG), "Match 3D parameters: w=%d,h=%d,val=%c,vrefresh_3d=%d,flag=%d,clk=%d",
+ width_3d,height_3d,val_3d,vrefresh_3d,flag_3d,clk_3d);
+ c->set_best_mode(conn_mode);
+ return 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ sscanf(resolution, "%dx%d%c%d", &width, &height, &val, &vrefresh);
+ if (val == 'i')
+ interlaced = true;
+ else
+ interlaced = false;
+
+ if (width != 0 && height != 0) {
+ for (const DrmMode &conn_mode : c->modes()) {
+ if (conn_mode.equal(width, height, vrefresh, interlaced)) {
+ c->set_best_mode(conn_mode);
+ return 0;
+ }
+ }
+ }
+ }
+
+ for (const DrmMode &conn_mode : c->modes()) {
+ if (conn_mode.type() & DRM_MODE_TYPE_PREFERRED) {
+ c->set_best_mode(conn_mode);
+ return 0;
+ }
+ }
+
+ for (const DrmMode &conn_mode : c->modes()) {
+ c->set_best_mode(conn_mode);
+ return 0;
+ }
+
+ ALOGE("Error: Should not get here display=%d %s %d\n", display, __FUNCTION__, __LINE__);
+ DrmMode mode;
+ c->set_best_mode(mode);
+
+ return -ENOENT;
+}
+
+
+// map of display:hwc_drm_display_t
+typedef std::map<int, hwc_drm_display_t> DisplayMap;
+class DrmHotplugHandler : public DrmEventHandler {
+ public:
+ void Init(DisplayMap* displays, DrmResources *drm, const struct hwc_procs *procs) {
+ displays_ = displays;
+ drm_ = drm;
+ procs_ = procs;
+ }
+
+ void HandleEvent(uint64_t timestamp_us) {
+ int ret;
+ DrmConnector *extend = NULL;
+
+ for (auto &conn : drm_->connectors()) {
+ drmModeConnection old_state = conn->state();
+
+ conn->UpdateModes();
+
+ drmModeConnection cur_state = conn->state();
+
+ if (cur_state == old_state)
+ continue;
+ ALOGI("%s event @%" PRIu64 " for connector %u\n",
+ cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", timestamp_us,
+ conn->id());
+
+ if (cur_state == DRM_MODE_CONNECTED) {
+ if (conn->display())
+ extend = conn.get();
+ }
+ }
+
+ /*
+ * status changed?
+ */
+ drm_->DisplayChanged();
+
+ DrmConnector *primary = drm_->GetConnectorFromType(HWC_DISPLAY_PRIMARY);
+ if (!primary) {
+ ALOGE("%s %d Failed to find primary display\n", __FUNCTION__, __LINE__);
+ return;
+ }
+ DrmConnector *old_extend = drm_->GetConnectorFromType(HWC_DISPLAY_EXTERNAL);
+ extend = extend ? extend : old_extend;
+ if (!extend || extend->state() != DRM_MODE_CONNECTED) {
+ extend = NULL;
+ for (auto &conn : drm_->connectors()) {
+ if (conn->id() == primary->id())
+ continue;
+ if (conn->state() == DRM_MODE_CONNECTED) {
+ extend = conn.get();
+ break;
+ }
+ }
+ }
+ drm_->SetExtendDisplay(extend);
+ if (!extend) {
+ procs_->hotplug(procs_, HWC_DISPLAY_EXTERNAL, 0);
+ procs_->invalidate(procs_);
+ return;
+ }
+
+ hwc_drm_display_t *hd = &(*displays_)[extend->display()];
+ update_display_bestmode(hd, HWC_DISPLAY_EXTERNAL, extend);
+ DrmMode mode = extend->best_mode();
+
+ if (mode.h_display() > mode.v_display() && mode.v_display() >= 2160) {
+ hd->framebuffer_width = mode.h_display() * (1080.0 / mode.v_display());
+ hd->framebuffer_height = 1080;
+ } else {
+ hd->framebuffer_width = mode.h_display();
+ hd->framebuffer_height = mode.v_display();
+ }
+ hd->rel_xres = mode.h_display();
+ hd->rel_yres = mode.v_display();
+ hd->v_total = mode.v_total();
+ hd->active = false;
+ procs_->hotplug(procs_, HWC_DISPLAY_EXTERNAL, 0);
+ hd->active = true;
+ procs_->hotplug(procs_, HWC_DISPLAY_EXTERNAL, 1);
+ procs_->invalidate(procs_);
+ }
+
+ private:
+ DrmResources *drm_ = NULL;
+ const struct hwc_procs *procs_ = NULL;
+ DisplayMap* displays_ = NULL;
+};
+
+struct hwc_context_t {
+ // map of display:hwc_drm_display_t
+ typedef std::map<int, hwc_drm_display_t> DisplayMap;
+
+ ~hwc_context_t() {
+ virtual_compositor_worker.Exit();
+ }
+
+ hwc_composer_device_1_t device;
+ hwc_procs_t const *procs = NULL;
+
+ DisplayMap displays;
+ DrmResources drm;
+ std::unique_ptr<Importer> importer;
+ const gralloc_module_t *gralloc;
+ DummySwSyncTimeline dummy_timeline;
+ VirtualCompositorWorker virtual_compositor_worker;
+ DrmHotplugHandler hotplug_handler;
+ VSyncWorker primary_vsync_worker;
+ VSyncWorker extend_vsync_worker;
+
+ int fb_fd;
+ int fb_blanked;
+#if RK_INVALID_REFRESH
+ bool isGLESComp;
+ bool mOneWinOpt;
+ threadPamaters mRefresh;
+#endif
+
+#if RK_STEREO
+ bool is_3d;
+ //int fd_3d;
+ //threadPamaters mControlStereo;
+#endif
+
+ std::vector<DrmCompositionDisplayPlane> comp_plane_group;
+ std::vector<DrmHwcDisplayContents> layer_contents;
+};
+
+static native_handle_t *dup_buffer_handle(buffer_handle_t handle) {
+ native_handle_t *new_handle =
+ native_handle_create(handle->numFds, handle->numInts);
+ if (new_handle == NULL)
+ return NULL;
+
+ const int *old_data = handle->data;
+ int *new_data = new_handle->data;
+ for (int i = 0; i < handle->numFds; i++) {
+ *new_data = dup(*old_data);
+ old_data++;
+ new_data++;
+ }
+ memcpy(new_data, old_data, sizeof(int) * handle->numInts);
+
+ return new_handle;
+}
+
+static void free_buffer_handle(native_handle_t *handle) {
+ int ret = native_handle_close(handle);
+ if (ret)
+ ALOGE("Failed to close native handle %d", ret);
+ ret = native_handle_delete(handle);
+ if (ret)
+ ALOGE("Failed to delete native handle %d", ret);
+}
+
+const hwc_drm_bo *DrmHwcBuffer::operator->() const {
+ if (importer_ == NULL) {
+ ALOGE("Access of non-existent BO");
+ exit(1);
+ return NULL;
+ }
+ return &bo_;
+}
+
+void DrmHwcBuffer::Clear() {
+ if (importer_ != NULL) {
+ importer_->ReleaseBuffer(&bo_);
+ importer_ = NULL;
+ }
+}
+
+int DrmHwcBuffer::ImportBuffer(buffer_handle_t handle, Importer *importer
+#if RK_VIDEO_SKIP_LINE
+, bool bSkipLine
+#endif
+) {
+ hwc_drm_bo tmp_bo;
+
+ int ret = importer->ImportBuffer(handle, &tmp_bo
+#if RK_VIDEO_SKIP_LINE
+ , bSkipLine
+#endif
+ );
+ if (ret)
+ return ret;
+
+ if (importer_ != NULL) {
+ importer_->ReleaseBuffer(&bo_);
+ }
+
+ importer_ = importer;
+
+ bo_ = tmp_bo;
+
+ return 0;
+}
+
+int DrmHwcNativeHandle::CopyBufferHandle(buffer_handle_t handle,
+ const gralloc_module_t *gralloc) {
+ native_handle_t *handle_copy = dup_buffer_handle(handle);
+ if (handle_copy == NULL) {
+ ALOGE("Failed to duplicate handle");
+ return -ENOMEM;
+ }
+
+ int ret = gralloc->registerBuffer(gralloc, handle_copy);
+ if (ret) {
+ ALOGE("Failed to register buffer handle %d", ret);
+ free_buffer_handle(handle_copy);
+ return ret;
+ }
+
+ Clear();
+
+ gralloc_ = gralloc;
+ handle_ = handle_copy;
+
+ return 0;
+}
+
+DrmHwcNativeHandle::~DrmHwcNativeHandle() {
+ Clear();
+}
+
+void DrmHwcNativeHandle::Clear() {
+ if (gralloc_ != NULL && handle_ != NULL) {
+ gralloc_->unregisterBuffer(gralloc_, handle_);
+ free_buffer_handle(handle_);
+ gralloc_ = NULL;
+ handle_ = NULL;
+ }
+}
+
+static const char *DrmFormatToString(uint32_t drm_format) {
+ switch (drm_format) {
+ case DRM_FORMAT_BGR888:
+ return "DRM_FORMAT_BGR888";
+ case DRM_FORMAT_ARGB8888:
+ return "DRM_FORMAT_ARGB8888";
+ case DRM_FORMAT_XBGR8888:
+ return "DRM_FORMAT_XBGR8888";
+ case DRM_FORMAT_ABGR8888:
+ return "DRM_FORMAT_ABGR8888";
+ case DRM_FORMAT_BGR565:
+ return "DRM_FORMAT_BGR565";
+ case DRM_FORMAT_YVU420:
+ return "DRM_FORMAT_YVU420";
+ case DRM_FORMAT_NV12:
+ return "DRM_FORMAT_NV12";
+ default:
+ return "<invalid>";
+ }
+}
+
+static void DumpBuffer(const DrmHwcBuffer &buffer, std::ostringstream *out) {
+ if (!buffer) {
+ *out << "buffer=<invalid>";
+ return;
+ }
+
+ *out << "buffer[w/h/format]=";
+ *out << buffer->width << "/" << buffer->height << "/" << DrmFormatToString(buffer->format);
+}
+
+static const char *TransformToString(uint32_t transform) {
+ switch (transform) {
+ case DrmHwcTransform::kIdentity:
+ return "IDENTITY";
+ case DrmHwcTransform::kFlipH:
+ return "FLIPH";
+ case DrmHwcTransform::kFlipV:
+ return "FLIPV";
+ case DrmHwcTransform::kRotate90:
+ return "ROTATE90";
+ case DrmHwcTransform::kRotate180:
+ return "ROTATE180";
+ case DrmHwcTransform::kRotate270:
+ return "ROTATE270";
+ default:
+ return "<invalid>";
+ }
+}
+
+static const char *BlendingToString(DrmHwcBlending blending) {
+ switch (blending) {
+ case DrmHwcBlending::kNone:
+ return "NONE";
+ case DrmHwcBlending::kPreMult:
+ return "PREMULT";
+ case DrmHwcBlending::kCoverage:
+ return "COVERAGE";
+ default:
+ return "<invalid>";
+ }
+}
+
+void DrmHwcLayer::dump_drm_layer(int index, std::ostringstream *out) const {
+ *out << "DrmHwcLayer[" << index << "] ";
+ DumpBuffer(buffer,out);
+
+ *out << " transform=" << TransformToString(transform)
+ << " blending[a=" << (int)alpha
+ << "]=" << BlendingToString(blending) << " source_crop";
+ source_crop.Dump(out);
+ *out << " display_frame";
+ display_frame.Dump(out);
+
+ *out << "\n";
+}
+
+int DrmHwcLayer::InitFromHwcLayer(struct hwc_context_t *ctx, int display, hwc_layer_1_t *sf_layer, Importer *importer,
+ const gralloc_module_t *gralloc, bool bClone) {
+ DrmConnector *c;
+ DrmMode mode;
+ unsigned int size;
+
+ int ret = 0;
+
+ UN_USED(importer);
+
+ bClone_ = bClone;
+ stereo = sf_layer->alreadyStereo;
+ if(sf_layer->compositionType == HWC_FRAMEBUFFER_TARGET)
+ bFbTarget_ = true;
+ else
+ bFbTarget_ = false;
+
+ if(sf_layer->flags & HWC_SKIP_LAYER)
+ bSkipLayer = true;
+ else
+ bSkipLayer = false;
+#if RK_VIDEO_SKIP_LINE
+ bSkipLine = false;
+#endif
+ bUse = true;
+ sf_handle = sf_layer->handle;
+ alpha = sf_layer->planeAlpha;
+ frame_no = get_frame();
+ source_crop = DrmHwcRect<float>(
+ sf_layer->sourceCropf.left, sf_layer->sourceCropf.top,
+ sf_layer->sourceCropf.right, sf_layer->sourceCropf.bottom);
+
+ DrmConnector *conn = ctx->drm.GetConnectorFromType(display);
+ if (!conn) {
+ ALOGE("Failed to get connector for display %d line=%d", display,__LINE__);
+ return -ENODEV;
+ }
+
+ hwc_drm_display_t *hd = &ctx->displays[conn->display()];
+
+ if(bClone)
+ {
+ //int panle_height = hd->rel_yres + hd->v_total;
+ //int y_offset = (panle_height - panle_height * 3 / 147) / 2 + panle_height * 3 / 147;
+ int y_offset = hd->v_total;
+ display_frame = DrmHwcRect<int>(
+ hd->w_scale * sf_layer->displayFrame.left, hd->h_scale * sf_layer->displayFrame.top + y_offset,
+ hd->w_scale * sf_layer->displayFrame.right, hd->h_scale * sf_layer->displayFrame.bottom + y_offset);
+ }
+ else
+ {
+ if(stereo == FPS_3D)
+ {
+ int y_offset = hd->v_total;
+ display_frame = DrmHwcRect<int>(
+ hd->w_scale * sf_layer->displayFrame.left, hd->h_scale * sf_layer->displayFrame.top,
+ hd->w_scale * sf_layer->displayFrame.right, hd->h_scale * sf_layer->displayFrame.bottom + y_offset);
+ }
+ else
+ {
+ display_frame = DrmHwcRect<int>(
+ hd->w_scale * sf_layer->displayFrame.left, hd->h_scale * sf_layer->displayFrame.top,
+ hd->w_scale * sf_layer->displayFrame.right, hd->h_scale * sf_layer->displayFrame.bottom);
+ }
+ }
+
+ c = ctx->drm.GetConnectorFromType(HWC_DISPLAY_PRIMARY);
+ if (!c) {
+ ALOGE("Failed to get DrmConnector for display %d", 0);
+ return -ENODEV;
+ }
+ mode = c->active_mode();
+
+
+ if((sf_layer->transform == HWC_TRANSFORM_ROT_90)
+ ||(sf_layer->transform == HWC_TRANSFORM_ROT_270)){
+ h_scale_mul = (float) (source_crop.bottom - source_crop.top)
+ / (display_frame.right - display_frame.left);
+ v_scale_mul = (float) (source_crop.right - source_crop.left)
+ / (display_frame.bottom - display_frame.top);
+ } else {
+ h_scale_mul = (float) (source_crop.right - source_crop.left)
+ / (display_frame.right - display_frame.left);
+ v_scale_mul = (float) (source_crop.bottom - source_crop.top)
+ / (display_frame.bottom - display_frame.top);
+ }
+#if RK_DRM_GRALLOC
+ width = hwc_get_handle_attibute(ctx->gralloc,sf_layer->handle,ATT_WIDTH);
+ height = hwc_get_handle_attibute(ctx->gralloc,sf_layer->handle,ATT_HEIGHT);
+ stride = hwc_get_handle_attibute(ctx->gralloc,sf_layer->handle,ATT_STRIDE);
+ format = hwc_get_handle_attibute(ctx->gralloc,sf_layer->handle,ATT_FORMAT);
+#else
+ width = hwc_get_handle_width(ctx->gralloc,sf_layer->handle);
+ height = hwc_get_handle_height(ctx->gralloc,sf_layer->handle);
+ stride = hwc_get_handle_stride(ctx->gralloc,sf_layer->handle);
+ format = hwc_get_handle_format(ctx->gralloc,sf_layer->handle);
+#endif
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12 || format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ is_yuv = true;
+ else
+ is_yuv = false;
+
+#if RK_VIDEO_SKIP_LINE
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12 || format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ {
+ if(width >= 3840)
+ {
+ if(h_scale_mul > 1.0 || v_scale_mul > 1.0)
+ {
+ bSkipLine = true;
+ }
+ }
+ }
+#endif
+
+ is_scale = (h_scale_mul != 1.0) || (v_scale_mul != 1.0);
+ is_match = false;
+ is_take = false;
+#if USE_AFBC_LAYER
+ is_afbc = false;
+#endif
+#if RK_RGA
+ is_rotate_by_rga = false;
+#endif
+ bMix = false;
+ raw_sf_layer = sf_layer;
+ bpp = android::bytesPerPixel(format);
+ size = (source_crop.right - source_crop.left) * (source_crop.bottom - source_crop.top) * bpp;
+ is_large = (mode.h_display()*mode.v_display()*4*3/4 > size)? true:false;
+ name = sf_layer->LayerName;
+ mlayer = sf_layer;
+
+ ALOGV("\t layerName=%s,sourceCropf(%f,%f,%f,%f)",sf_layer->LayerName,
+ source_crop.left,source_crop.top,source_crop.right,source_crop.bottom);
+ ALOGV("h_scale_mul=%f,v_scale_mul=%f,is_scale=%d,is_large=%d",h_scale_mul,v_scale_mul,is_scale,is_large);
+
+ transform = 0;
+
+ // 270* and 180* cannot be combined with flips. More specifically, they
+ // already contain both horizontal and vertical flips, so those fields are
+ // redundant in this case. 90* rotation can be combined with either horizontal
+ // flip or vertical flip, so treat it differently
+ if (sf_layer->transform == HWC_TRANSFORM_ROT_270) {
+ transform = DrmHwcTransform::kRotate270;
+ } else if (sf_layer->transform == HWC_TRANSFORM_ROT_180) {
+ transform = DrmHwcTransform::kRotate180;
+ } else {
+ if (sf_layer->transform & HWC_TRANSFORM_FLIP_H)
+ transform |= DrmHwcTransform::kFlipH;
+ if (sf_layer->transform & HWC_TRANSFORM_FLIP_V)
+ transform |= DrmHwcTransform::kFlipV;
+ if (sf_layer->transform & HWC_TRANSFORM_ROT_90)
+ transform |= DrmHwcTransform::kRotate90;
+ }
+
+#if RK_RGA_TEST
+ if((format==HAL_PIXEL_FORMAT_RGB_565) && strstr(sf_layer->LayerName,"SurfaceView"))
+ transform |= DrmHwcTransform::kRotate90;
+
+#endif
+
+ switch (sf_layer->blending) {
+ case HWC_BLENDING_NONE:
+ blending = DrmHwcBlending::kNone;
+ break;
+ case HWC_BLENDING_PREMULT:
+ blending = DrmHwcBlending::kPreMult;
+ break;
+ case HWC_BLENDING_COVERAGE:
+ blending = DrmHwcBlending::kCoverage;
+ break;
+ default:
+ ALOGE("Invalid blending in hwc_layer_1_t %d", sf_layer->blending);
+ return -EINVAL;
+ }
+
+#if 0
+ ret = buffer.ImportBuffer(sf_layer->handle, importer
+#if RK_VIDEO_SKIP_LINE
+ , bSkipLine
+#endif
+ );
+ if (ret)
+ return ret;
+#endif
+
+ ret = handle.CopyBufferHandle(sf_layer->handle, gralloc);
+ if (ret)
+ return ret;
+
+#if 0
+ gralloc_buffer_usage= drm_handle->usage;
+#else
+ ret = gralloc->perform(gralloc, GRALLOC_MODULE_PERFORM_GET_USAGE,
+ handle.get(), &gralloc_buffer_usage);
+ if (ret) {
+ ALOGE("Failed to get usage for buffer %p (%d)", handle.get(), ret);
+ return ret;
+ }
+#endif
+
+#if USE_AFBC_LAYER
+ ret = gralloc->perform(gralloc, GRALLOC_MODULE_PERFORM_GET_INTERNAL_FORMAT,
+ handle.get(), &internal_format);
+ if (ret) {
+ ALOGE("Failed to get internal_format for buffer %p (%d)", handle.get(), ret);
+ return ret;
+ }
+
+ if(isAfbcInternalFormat(internal_format))
+ is_afbc = true;
+#endif
+
+ return 0;
+}
+
+int DrmHwcLayer::ImportBuffer(hwc_layer_1_t *sf_layer, Importer *importer)
+{
+ int ret = buffer.ImportBuffer(sf_layer->handle, importer
+#if RK_VIDEO_SKIP_LINE
+ , bSkipLine
+#endif
+ );
+
+ return ret;
+}
+
+static void hwc_dump(struct hwc_composer_device_1 *dev, char *buff,
+ int buff_len) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ std::ostringstream out;
+
+ ctx->drm.compositor()->Dump(&out);
+ std::string out_str = out.str();
+ strncpy(buff, out_str.c_str(),
+ std::min((size_t)buff_len, out_str.length() + 1));
+ buff[buff_len - 1] = '\0';
+}
+
+static bool hwc_skip_layer(const std::pair<int, int> &indices, int i) {
+ return indices.first >= 0 && i >= indices.first && i <= indices.second;
+}
+
+static bool is_use_gles_comp(struct hwc_context_t *ctx, hwc_display_contents_1_t *display_content, int display_id)
+{
+ int num_layers = display_content->numHwLayers;
+
+ //force go into GPU
+ /*
+ <=0: DISPLAY_PRIMARY & DISPLAY_EXTERNAL both go into GPU.
+ =1: DISPLAY_PRIMARY go into overlay,DISPLAY_EXTERNAL go into GPU.
+ =2: DISPLAY_EXTERNAL go into overlay,DISPLAY_PRIMARY go into GPU.
+ others: DISPLAY_PRIMARY & DISPLAY_EXTERNAL both go into overlay.
+ */
+ int iMode = hwc_get_int_property("sys.hwc.compose_policy","0");
+ if( iMode <= 0 || (iMode == 1 && display_id == 1) || (iMode == 2 && display_id == 0) )
+ return true;
+
+ iMode = hwc_get_int_property("sys.hwc","1");
+ if( iMode <= 0 )
+ return true;
+
+ if(num_layers == 1)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"No layer,go to GPU GLES at line=%d", __LINE__);
+ return true;
+ }
+
+#if RK_INVALID_REFRESH
+ if(ctx->mOneWinOpt)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Enter static screen opt,go to GPU GLES at line=%d", __LINE__);
+ return true;
+ }
+#endif
+
+#if RK_STEREO
+ if(ctx->is_3d)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Is 3d mode,go to GPU GLES at line=%d", __LINE__);
+ return true;
+ }
+#endif
+
+ //If the transform nv12 layers is bigger than one,then go into GPU GLES.
+ //If the transform normal layers is bigger than zero,then go into GPU GLES.
+ int transform_nv12 = 0;
+ int transform_normal = 0;
+ int ret = 0;
+ int format = 0;
+ int usage = 0;
+#if USE_AFBC_LAYER
+ uint64_t internal_format = 0;
+ int iFbdcCnt = 0;
+#endif
+
+ for (int j = 0; j < num_layers-1; j++) {
+ hwc_layer_1_t *layer = &display_content->hwLayers[j];
+
+ if (layer->flags & HWC_SKIP_LAYER)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer is skipped,go to GPU GLES at line=%d", __LINE__);
+ return true;
+ }
+
+ if(
+#if RK_RGA
+ !ctx->drm.isSupportRkRga() && layer->transform
+#else
+ layer->transform
+#endif
+ )
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer's transform=0x%x,go to GPU GLES at line=%d", layer->transform, __LINE__);
+ return true;
+ }
+
+ if( (layer->blending == HWC_BLENDING_PREMULT)&& layer->planeAlpha!=0xFF )
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer's blending planeAlpha=0x%x,go to GPU GLES at line=%d", layer->planeAlpha, __LINE__);
+ return true;
+ }
+
+ if(layer->handle)
+ {
+ //DumpLayer(layer->LayerName,layer->handle);
+#if RK_DRM_GRALLOC
+ format = hwc_get_handle_attibute(ctx->gralloc,layer->handle,ATT_FORMAT);
+#else
+ format = hwc_get_handle_format(ctx->gralloc,layer->handle);
+#endif
+ if(!vop_support_format(format))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer's format=0x%x is not support,go to GPU GLES at line=%d", format, __LINE__);
+ return true;
+ }
+
+ ret = ctx->gralloc->perform(ctx->gralloc, GRALLOC_MODULE_PERFORM_GET_USAGE,
+ layer->handle, &usage);
+ if (ret) {
+ ALOGE("Failed to get usage for buffer %p (%d)", layer->handle, ret);
+ return ret;
+ }
+
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12_10 && (usage & HDRUSAGE))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG), "layer is hdr video usage=0x%x,go to GPU GLES at line=%d", usage, __LINE__);
+ return true;
+ }
+
+#if 1
+ if(!vop_support_scale(layer))
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"layer's scale is not support,go to GPU GLES at line=%d", __LINE__);
+ return true;
+ }
+#endif
+ if(layer->transform)
+ {
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12 || format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ transform_nv12++;
+ else
+ transform_normal++;
+ }
+
+#if USE_AFBC_LAYER
+ ret = ctx->gralloc->perform(ctx->gralloc, GRALLOC_MODULE_PERFORM_GET_INTERNAL_FORMAT,
+ layer->handle, &internal_format);
+ if (ret) {
+ ALOGE("Failed to get internal_format for buffer %p (%d)", layer->handle, ret);
+ return false;
+ }
+
+ if(internal_format & GRALLOC_ARM_INTFMT_AFBC)
+ iFbdcCnt++;
+#endif
+ }
+ }
+ if(transform_nv12 > 1 || transform_normal > 0)
+ {
+ return true;
+ }
+
+#if USE_AFBC_LAYER
+ if(iFbdcCnt > 1)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"iFbdcCnt=%d,go to GPU GLES",iFbdcCnt);
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+static int hwc_prepare(hwc_composer_device_1_t *dev, size_t num_displays,
+ hwc_display_contents_1_t **display_contents) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+
+ init_log_level();
+ hwc_dump_fps();
+ ALOGD_IF(log_level(DBG_VERBOSE),"----------------------------frame=%d start ----------------------------",get_frame());
+ ctx->layer_contents.clear();
+ ctx->layer_contents.reserve(num_displays);
+ ctx->comp_plane_group.clear();
+ for (int i = 0; i < (int)num_displays; ++i) {
+ bool use_framebuffer_target = false;
+ drmModeConnection state;
+
+ if (!display_contents[i])
+ continue;
+
+ int num_layers = display_contents[i]->numHwLayers;
+ for (int j = 0; j < num_layers; j++) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+ dump_layer(ctx->gralloc, false, layer, j);
+ }
+
+ ctx->layer_contents.emplace_back();
+ DrmHwcDisplayContents &layer_content = ctx->layer_contents.back();
+ ctx->comp_plane_group.emplace_back();
+ DrmCompositionDisplayPlane &comp_plane = ctx->comp_plane_group.back();
+ comp_plane.display = i;
+
+ DrmConnector *connector = ctx->drm.GetConnectorFromType(i);
+ if (!connector) {
+ ALOGE("Failed to get connector for display %d line=%d", i,__LINE__);
+ continue;
+ }
+
+ DrmCrtc *crtc = ctx->drm.GetCrtcFromConnector(connector);
+ if (connector->state() != DRM_MODE_CONNECTED || !crtc) {
+ hwc_list_nodraw(display_contents[i]);
+ continue;
+ }
+
+ hwc_drm_display_t *hd = &ctx->displays[connector->display()];
+ update_display_bestmode(hd, i, connector);
+ DrmMode mode = connector->best_mode();
+ connector->set_current_mode(mode);
+ hd->rel_xres = mode.h_display();
+ hd->rel_yres = mode.v_display();
+ hd->v_total = mode.v_total();
+ hd->w_scale = (float)mode.h_display() / hd->framebuffer_width;
+ hd->h_scale = (float)mode.v_display() / hd->framebuffer_height;
+ //get plane size for display
+ std::vector<PlaneGroup *>& plane_groups = ctx->drm.GetPlaneGroups();
+ hd->iPlaneSize = 0;
+ for (std::vector<PlaneGroup *> ::const_iterator iter = plane_groups.begin();
+ iter != plane_groups.end(); ++iter)
+ {
+ if(GetCrtcSupported(*crtc, (*iter)->possible_crtcs))
+ hd->iPlaneSize++;
+ }
+
+#if SKIP_BOOT
+ if(g_boot_cnt < BOOT_COUNT)
+ {
+ hwc_list_nodraw(display_contents[i]);
+ ALOGD_IF(log_level(DBG_DEBUG),"prepare skip %d",g_boot_cnt);
+ return 0;
+ }
+#endif
+
+ for (int j = 0; j < num_layers-1; j++) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+
+ if(layer->handle)
+ {
+ if(layer->compositionType == HWC_NODRAW)
+ layer->compositionType = HWC_FRAMEBUFFER;
+ }
+ }
+
+ // Since we can't composite HWC_SKIP_LAYERs by ourselves, we'll let SF
+ // handle all layers in between the first and last skip layers. So find the
+ // outer indices and mark everything in between as HWC_FRAMEBUFFER
+ std::pair<int, int> skip_layer_indices(-1, -1);
+
+ int format = 0;
+ hd->is10bitVideo = false;
+ hd->isVideo = false;
+ for (int j = 0; j < num_layers-1; j++) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+
+ if(layer->handle)
+ {
+#if RK_DRM_GRALLOC
+ format = hwc_get_handle_attibute(ctx->gralloc,layer->handle, ATT_FORMAT);
+#else
+ format = hwc_get_handle_format(ctx->gralloc,layer->handle);
+#endif
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12)
+ {
+ hd->isVideo = true;
+ }
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ {
+ hd->is10bitVideo = true;
+ hd->isVideo = true;
+ break;
+ }
+ }
+ }
+
+#if 1
+ hd->stereo_mode = NON_3D;
+ hd->is_3d = detect_3d_mode(hd, display_contents[i], i);
+#endif
+
+ int iLastFps = num_layers-1;
+ if(hd->stereo_mode == FPS_3D)
+ {
+ for(int j=num_layers-1; j>=0; j--) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+ if(layer->alreadyStereo == FPS_3D) {
+ iLastFps = j;
+ break;
+ }
+ }
+
+ for (int j = 0; j < iLastFps; j++)
+ {
+ display_contents[i]->hwLayers[j].compositionType = HWC_NODRAW;
+ }
+ }
+
+ if(!use_framebuffer_target)
+ use_framebuffer_target = is_use_gles_comp(ctx, display_contents[i], i);
+
+#if RK_VIDEO_UI_OPT
+ video_ui_optimize(ctx->gralloc, display_contents[i], &ctx->displays[i]);
+#endif
+
+ int ret = -1;
+ bool bHasFPS_3D_UI = false;
+ int index = 0;
+ for (int j = 0; j < num_layers; j++) {
+ hwc_layer_1_t *sf_layer = &display_contents[i]->hwLayers[j];
+ if(sf_layer->handle == NULL)
+ continue;
+ if(sf_layer->compositionType == HWC_NODRAW)
+ continue;
+
+ if(hd->stereo_mode == FPS_3D && iLastFps < num_layers-1)
+ {
+ if(j>iLastFps && sf_layer->alreadyStereo != FPS_3D && sf_layer->displayStereo)
+ {
+ bHasFPS_3D_UI = true;
+ }
+ }
+
+ layer_content.layers.emplace_back();
+ DrmHwcLayer &layer = layer_content.layers.back();
+ ret = layer.InitFromHwcLayer(ctx, i, sf_layer, ctx->importer.get(), ctx->gralloc, false);
+ if (ret) {
+ ALOGE("Failed to init composition from layer %d", ret);
+ return ret;
+ }
+ layer.index = j;
+ index = j;
+
+ std::ostringstream out;
+ layer.dump_drm_layer(j,&out);
+ ALOGD_IF(log_level(DBG_DEBUG),"%s",out.str().c_str());
+ }
+
+ if(bHasFPS_3D_UI)
+ {
+ hwc_layer_1_t *sf_layer = &display_contents[i]->hwLayers[num_layers-1];
+ if(sf_layer->handle == NULL)
+ continue;
+
+ layer_content.layers.emplace_back();
+ DrmHwcLayer &layer = layer_content.layers.back();
+ ret = layer.InitFromHwcLayer(ctx, i, sf_layer, ctx->importer.get(), ctx->gralloc, true);
+ if (ret) {
+ ALOGE("Failed to init composition from layer %d", ret);
+ return ret;
+ }
+ index++;
+ layer.index = index;
+
+ std::ostringstream out;
+ layer.dump_drm_layer(index,&out);
+ ALOGD_IF(log_level(DBG_DEBUG),"clone layer: %s",out.str().c_str());
+ }
+
+ if(!use_framebuffer_target)
+ {
+ bool bAllMatch = false;
+ int iUsePlane = 0;
+
+ hd->mixMode = HWC_DEFAULT;
+ if(crtc && layer_content.layers.size()>0)
+ {
+ bAllMatch = mix_policy(&ctx->drm, crtc, &ctx->displays[i],layer_content.layers,
+ hd->iPlaneSize, comp_plane.composition_planes);
+ }
+ if(!bAllMatch)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"mix_policy failed,go to GPU GLES at line=%d", __LINE__);
+ use_framebuffer_target = true;
+ }
+ }
+
+ for (int j = 0; j < num_layers; ++j) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+
+
+ if (!use_framebuffer_target && layer->compositionType != HWC_MIX) {
+ // If the layer is off the screen, don't earmark it for an overlay.
+ // We'll leave it as-is, which effectively just drops it from the frame
+ const hwc_rect_t *frame = &layer->displayFrame;
+ if ((frame->right - frame->left) <= 0 ||
+ (frame->bottom - frame->top) <= 0 ||
+ frame->right <= 0 || frame->bottom <= 0 ||
+ frame->left >= (int)hd->framebuffer_width ||
+ frame->top >= (int)hd->framebuffer_height)
+ {
+ continue;
+ }
+
+ if (layer->compositionType == HWC_FRAMEBUFFER)
+ layer->compositionType = HWC_OVERLAY;
+ } else {
+ switch (layer->compositionType) {
+ case HWC_MIX:
+ case HWC_OVERLAY:
+ case HWC_BACKGROUND:
+ case HWC_SIDEBAND:
+ case HWC_CURSOR_OVERLAY:
+ layer->compositionType = HWC_FRAMEBUFFER;
+ break;
+ }
+ }
+ }
+
+ if(use_framebuffer_target)
+ ctx->isGLESComp = true;
+ else
+ ctx->isGLESComp = false;
+
+ if(ctx->isGLESComp)
+ {
+ //remove all layers except fb layer
+ for (auto k = layer_content.layers.begin(); k != layer_content.layers.end();)
+ {
+ //remove gles layers
+ if((*k).mlayer->compositionType != HWC_FRAMEBUFFER_TARGET)
+ k = layer_content.layers.erase(k);
+ else
+ k++;
+ }
+
+ //match plane for gles composer.
+ bool bAllMatch = match_process(&ctx->drm, crtc, layer_content.layers,
+ hd->iPlaneSize, comp_plane.composition_planes);
+ if(!bAllMatch)
+ ALOGE("Fetal error when match plane for fb layer");
+ }
+
+ for (int j = 0; j < num_layers; ++j) {
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+
+ if(layer->compositionType==HWC_FRAMEBUFFER)
+ ALOGD_IF(log_level(DBG_DEBUG),"%s: HWC_FRAMEBUFFER",layer->LayerName);
+ else if(layer->compositionType==HWC_OVERLAY)
+ ALOGD_IF(log_level(DBG_DEBUG),"%s: HWC_OVERLAY",layer->LayerName);
+ else
+ ALOGD_IF(log_level(DBG_DEBUG),"%s: HWC_OTHER",layer->LayerName);
+ }
+ }
+
+#if RK_INVALID_REFRESH
+ if(ctx->mOneWinOpt)
+ ctx->mOneWinOpt = false;
+#endif
+
+ return 0;
+}
+
+static void hwc_add_layer_to_retire_fence(
+ hwc_layer_1_t *layer, hwc_display_contents_1_t *display_contents) {
+ if (layer->releaseFenceFd < 0)
+ return;
+
+ if (display_contents->retireFenceFd >= 0) {
+ int old_retire_fence = display_contents->retireFenceFd;
+ display_contents->retireFenceFd =
+ sync_merge("dc_retire", old_retire_fence, layer->releaseFenceFd);
+ close(old_retire_fence);
+ } else {
+ display_contents->retireFenceFd = dup(layer->releaseFenceFd);
+ }
+}
+
+static int hwc_set(hwc_composer_device_1_t *dev, size_t num_displays,
+ hwc_display_contents_1_t **sf_display_contents) {
+ ATRACE_CALL();
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ int ret = 0;
+
+ ALOGD_IF(log_level(DBG_VERBOSE),"----------------------------frame=%d end----------------------------",get_frame());
+ inc_frame();
+
+ std::vector<CheckedOutputFd> checked_output_fences;
+ std::vector<DrmHwcDisplayContents> displays_contents;
+ std::vector<DrmCompositionDisplayLayersMap> layers_map;
+ std::vector<std::vector<size_t>> layers_indices;
+ std::vector<uint32_t> fail_displays;
+
+ // layers_map.reserve(num_displays);
+ layers_indices.reserve(num_displays);
+
+ // Phase one does nothing that would cause errors. Only take ownership of FDs.
+ for (size_t i = 0; i < num_displays; ++i) {
+ hwc_display_contents_1_t *dc = sf_display_contents[i];
+ DrmHwcDisplayContents &display_contents = ctx->layer_contents[i];
+ displays_contents.emplace_back();
+ DrmHwcDisplayContents &display_contents_tmp = displays_contents.back();
+ layers_indices.emplace_back();
+
+ if (!sf_display_contents[i])
+ continue;
+#if SKIP_BOOT
+ if(g_boot_cnt < BOOT_COUNT) {
+ hwc_sync_release(sf_display_contents[i]);
+ if(0 == i)
+ g_boot_cnt++;
+ ALOGD_IF(log_level(DBG_DEBUG),"set skip %d",g_boot_cnt);
+ return 0;
+ }
+#endif
+
+ if (i == HWC_DISPLAY_VIRTUAL) {
+ ctx->virtual_compositor_worker.QueueComposite(dc);
+ continue;
+ }
+
+ DrmConnector *c = ctx->drm.GetConnectorFromType(i);
+ if (!c || c->state() != DRM_MODE_CONNECTED) {
+ hwc_sync_release(sf_display_contents[i]);
+ continue;
+ }
+ hwc_drm_display_t *hd = &ctx->displays[c->display()];
+
+ std::ostringstream display_index_formatter;
+ display_index_formatter << "retire fence for display " << i;
+ std::string display_fence_description(display_index_formatter.str());
+ checked_output_fences.emplace_back(&dc->retireFenceFd,
+ display_fence_description.c_str(),
+ ctx->dummy_timeline);
+ display_contents.retire_fence = OutputFd(&dc->retireFenceFd);
+
+ size_t num_dc_layers = dc->numHwLayers;
+ int framebuffer_target_index = -1;
+ for (size_t j = 0; j < num_dc_layers; ++j) {
+ hwc_layer_1_t *sf_layer = &dc->hwLayers[j];
+ if (sf_layer->compositionType == HWC_FRAMEBUFFER_TARGET) {
+ framebuffer_target_index = j;
+ break;
+ }
+ }
+
+ for (size_t j = 0; j < num_dc_layers; ++j) {
+ size_t k = 0;
+ hwc_layer_1_t *sf_layer = &dc->hwLayers[j];
+
+ // In prepare() we marked all layers FRAMEBUFFER between SKIP_LAYER's.
+ // This means we should insert the FB_TARGET layer in the composition
+ // stack at the location of the first skip layer, and ignore the rest.
+
+ if (sf_layer->flags & HWC_SKIP_LAYER) {
+ //rk: SurfaceFlinger will create acquireFenceFd for nodraw skip layer.
+ // Close it here to avoid anon_inode:sync_fence fd leak.
+ if(sf_layer->compositionType == HWC_NODRAW)
+ {
+ if(sf_layer->acquireFenceFd >= 0)
+ {
+ close(sf_layer->acquireFenceFd);
+ sf_layer->acquireFenceFd = -1;
+ }
+ }
+
+ if (framebuffer_target_index < 0)
+ continue;
+ int idx = framebuffer_target_index;
+ framebuffer_target_index = -1;
+ hwc_layer_1_t *fbt_layer = &dc->hwLayers[idx];
+ if (!fbt_layer->handle || (fbt_layer->flags & HWC_SKIP_LAYER)) {
+ ALOGE("Invalid HWC_FRAMEBUFFER_TARGET with HWC_SKIP_LAYER present");
+ continue;
+ }
+ continue;
+ }
+
+#if 0
+ // rk: wait acquireFenceFd at hwc_set.
+ if(sf_layer->acquireFenceFd > 0)
+ {
+ sync_wait(sf_layer->acquireFenceFd, -1);
+ close(sf_layer->acquireFenceFd);
+ sf_layer->acquireFenceFd = -1;
+ }
+#endif
+
+ for (k = 0; k < display_contents.layers.size(); ++k)
+ {
+ DrmHwcLayer &layer = display_contents.layers[k];
+ if(j == layer.index)
+ {
+ // sf_layer = layer.raw_sf_layer;
+ layer.acquire_fence.Set(sf_layer->acquireFenceFd);
+ sf_layer->acquireFenceFd = -1;
+
+ std::ostringstream layer_fence_formatter;
+ layer_fence_formatter << "release fence for layer " << j << " of display "
+ << i;
+ std::string layer_fence_description(layer_fence_formatter.str());
+ checked_output_fences.emplace_back(&sf_layer->releaseFenceFd,
+ layer_fence_description.c_str(),
+ ctx->dummy_timeline);
+ layer.release_fence = OutputFd(&sf_layer->releaseFenceFd);
+ break;
+ }
+ }
+
+ if(k == display_contents.layers.size())
+ {
+ display_contents_tmp.layers.emplace_back();
+ DrmHwcLayer &layer = display_contents_tmp.layers.back();
+
+ layer.acquire_fence.Set(sf_layer->acquireFenceFd);
+ sf_layer->acquireFenceFd = -1;
+
+ std::ostringstream layer_fence_formatter;
+ layer_fence_formatter << "release fence for layer " << j << " of display "
+ << i;
+ std::string layer_fence_description(layer_fence_formatter.str());
+ checked_output_fences.emplace_back(&sf_layer->releaseFenceFd,
+ layer_fence_description.c_str(),
+ ctx->dummy_timeline);
+ layer.release_fence = OutputFd(&sf_layer->releaseFenceFd);
+ }
+ }
+
+
+ if(display_contents.layers.size() == 0 && framebuffer_target_index >= 0)
+ {
+ hwc_layer_1_t *sf_layer = &dc->hwLayers[framebuffer_target_index];
+ if (!sf_layer->handle || (sf_layer->flags & HWC_SKIP_LAYER)) {
+ ALOGE(
+ "Expected valid layer with HWC_FRAMEBUFFER_TARGET when all "
+ "HWC_OVERLAY layers are skipped.");
+ fail_displays.emplace_back(i);
+ ret = -EINVAL;
+ }
+ }
+ }
+
+#if 0
+ if (ret)
+ return ret;
+#endif
+ for (size_t i = 0; i < num_displays; ++i) {
+ hwc_display_contents_1_t *dc = sf_display_contents[i];
+ DrmHwcDisplayContents &display_contents = ctx->layer_contents[i];
+ bool bFindDisplay = false;
+ if (!sf_display_contents[i] || i == HWC_DISPLAY_VIRTUAL)
+ continue;
+
+ for (auto &fail_display : fail_displays) {
+ if( i == fail_display )
+ {
+ bFindDisplay = true;
+ ALOGD_IF(log_level(DBG_VERBOSE),"%s:line=%d,Find fail display %zu",__FUNCTION__,__LINE__,i);
+ break;
+ }
+ }
+ if(bFindDisplay)
+ continue;
+
+ layers_map.emplace_back();
+ DrmCompositionDisplayLayersMap &map = layers_map.back();
+ map.display = i;
+ map.geometry_changed =
+ (dc->flags & HWC_GEOMETRY_CHANGED) == HWC_GEOMETRY_CHANGED;
+ for (size_t j=0; j< display_contents.layers.size(); j++) {
+ DrmHwcLayer &layer = display_contents.layers[j];
+ if(!layer.bClone_)
+ layer.ImportBuffer(layer.raw_sf_layer, ctx->importer.get());
+ map.layers.emplace_back(std::move(layer));
+ }
+ }
+
+ ctx->drm.UpdateDisplayRoute();
+ ctx->drm.ClearDisplay();
+ std::unique_ptr<DrmComposition> composition(
+ ctx->drm.compositor()->CreateComposition(ctx->importer.get()));
+ if (!composition) {
+ ALOGE("Drm composition init failed");
+ return -EINVAL;
+ }
+
+ ret = composition->SetLayers(layers_map.size(), layers_map.data());
+ if (ret) {
+ return -EINVAL;
+ }
+
+ for (size_t i = 0; i < num_displays; ++i) {
+ if (!sf_display_contents[i])
+ continue;
+
+ DrmConnector *c = ctx->drm.GetConnectorFromType(i);
+ if (!c || c->state() != DRM_MODE_CONNECTED) {
+ continue;
+ }
+ hwc_drm_display_t *hd = &ctx->displays[c->display()];
+ composition->SetMode3D(i, hd->stereo_mode);
+ }
+
+ for (size_t i = 0; i < ctx->comp_plane_group.size(); ++i) {
+ if(ctx->comp_plane_group[i].composition_planes.size() > 0)
+ {
+ ret = composition->SetCompPlanes(ctx->comp_plane_group[i].display, ctx->comp_plane_group[i].composition_planes);
+ if (ret) {
+ return -EINVAL;
+ }
+ }
+ else
+ {
+ if (sf_display_contents[i])
+ hwc_sync_release(sf_display_contents[i]);
+ }
+ }
+
+ ret = ctx->drm.compositor()->QueueComposition(std::move(composition));
+ if (ret) {
+ return -EINVAL;
+ }
+
+ for (size_t i = 0; i < num_displays; ++i) {
+ hwc_display_contents_1_t *dc = sf_display_contents[i];
+ bool bFindDisplay = false;
+ if (!dc)
+ continue;
+
+ DrmConnector *c = ctx->drm.GetConnectorFromType(i);
+ if (!c || c->state() != DRM_MODE_CONNECTED) {
+ hwc_sync_release(sf_display_contents[i]);
+ continue;
+ }
+ hwc_drm_display_t *hd = &ctx->displays[c->display()];
+
+ for (auto &fail_display : fail_displays) {
+ if( i == fail_display )
+ {
+ bFindDisplay = true;
+ ALOGD_IF(log_level(DBG_DEBUG),"%s:line=%d,Find fail display %zu",__FUNCTION__,__LINE__,i);
+ break;
+ }
+ }
+ if(bFindDisplay)
+ continue;
+
+ size_t num_dc_layers = dc->numHwLayers;
+ for (size_t j = 0; j < num_dc_layers; ++j) {
+ hwc_layer_1_t *layer = &dc->hwLayers[j];
+ if (layer->flags & HWC_SKIP_LAYER)
+ continue;
+ hwc_add_layer_to_retire_fence(layer, dc);
+ }
+ }
+
+ composition.reset(NULL);
+
+#if RK_INVALID_REFRESH
+ hwc_static_screen_opt_set(ctx->isGLESComp);
+#endif
+
+ return ret;
+}
+
+static int hwc_event_control(struct hwc_composer_device_1 *dev, int display,
+ int event, int enabled) {
+ if (event != HWC_EVENT_VSYNC || (enabled != 0 && enabled != 1))
+ return -EINVAL;
+
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ if (display == HWC_DISPLAY_PRIMARY)
+ return ctx->primary_vsync_worker.VSyncControl(enabled);
+ else if (display == HWC_DISPLAY_EXTERNAL)
+ return ctx->extend_vsync_worker.VSyncControl(enabled);
+
+ ALOGE("Can't support vsync control for display %d\n", display);
+ return -EINVAL;
+}
+
+static int hwc_set_power_mode(struct hwc_composer_device_1 *dev, int display,
+ int mode) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+
+ uint64_t dpmsValue = 0;
+ switch (mode) {
+ case HWC_POWER_MODE_OFF:
+ dpmsValue = DRM_MODE_DPMS_OFF;
+ break;
+
+ /* We can't support dozing right now, so go full on */
+ case HWC_POWER_MODE_DOZE:
+ case HWC_POWER_MODE_DOZE_SUSPEND:
+ case HWC_POWER_MODE_NORMAL:
+ dpmsValue = DRM_MODE_DPMS_ON;
+ break;
+ };
+
+ int fb_blank = 0;
+ if(dpmsValue == DRM_MODE_DPMS_OFF)
+ fb_blank = FB_BLANK_POWERDOWN;
+ else if(dpmsValue == DRM_MODE_DPMS_ON)
+ fb_blank = FB_BLANK_UNBLANK;
+ else
+ ALOGE("dpmsValue is invalid value= %" PRIu64 "",dpmsValue);
+ int err = ioctl(ctx->fb_fd, FBIOBLANK, fb_blank);
+ ALOGD_IF(log_level(DBG_DEBUG),"%s Notice fb_blank to fb=%d", __FUNCTION__, fb_blank);
+ if (err < 0) {
+ if (errno == EBUSY)
+ ALOGD("fb_blank ioctl failed display=%d,fb_blank=%d,dpmsValue=%" PRIu64 "",
+ display,fb_blank,dpmsValue);
+ else
+ ALOGE("fb_blank ioctl failed(%s) display=%d,fb_blank=%d,dpmsValue=%" PRIu64 "",
+ strerror(errno),display,fb_blank,dpmsValue);
+ return -errno;
+ }
+ else
+ {
+ ctx->fb_blanked = fb_blank;
+ }
+
+ DrmConnector *connector = ctx->drm.GetConnectorFromType(display);
+ if (!connector) {
+ ALOGE("Failed to get connector for display %d line=%d", display,__LINE__);
+ return -ENODEV;
+ }
+
+ connector->force_disconnect(dpmsValue == DRM_MODE_DPMS_OFF);
+ ctx->drm.DisplayChanged();
+ ctx->drm.UpdateDisplayRoute();
+ ctx->drm.ClearDisplay();
+
+ return 0;
+}
+
+static int hwc_query(struct hwc_composer_device_1 * /* dev */, int what,
+ int *value) {
+ switch (what) {
+ case HWC_BACKGROUND_LAYER_SUPPORTED:
+ *value = 0; /* TODO: We should do this */
+ break;
+ case HWC_VSYNC_PERIOD:
+ ALOGW("Query for deprecated vsync value, returning 60Hz");
+ *value = 1000 * 1000 * 1000 / 60;
+ break;
+ case HWC_DISPLAY_TYPES_SUPPORTED:
+ *value = HWC_DISPLAY_PRIMARY_BIT | HWC_DISPLAY_EXTERNAL_BIT |
+ HWC_DISPLAY_VIRTUAL_BIT;
+ break;
+ }
+ return 0;
+}
+
+static void hwc_register_procs(struct hwc_composer_device_1 *dev,
+ hwc_procs_t const *procs) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+
+ ctx->procs = procs;
+
+ ctx->primary_vsync_worker.SetProcs(procs);
+ ctx->extend_vsync_worker.SetProcs(procs);
+ ctx->hotplug_handler.Init(&ctx->displays, &ctx->drm, procs);
+ ctx->drm.event_listener()->RegisterHotplugHandler(&ctx->hotplug_handler);
+}
+
+static int hwc_get_display_configs(struct hwc_composer_device_1 *dev,
+ int display, uint32_t *configs,
+ size_t *num_configs) {
+ if (!num_configs)
+ return 0;
+
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ DrmConnector *connector = ctx->drm.GetConnectorFromType(display);
+ if (!connector) {
+ ALOGE("Failed to get connector for display %d line=%d", display,__LINE__);
+ return -ENODEV;
+ }
+
+ hwc_drm_display_t *hd = &ctx->displays[connector->display()];
+ if (!hd->active)
+ return -ENODEV;
+
+ int ret = connector->UpdateModes();
+ if (ret) {
+ ALOGE("Failed to update display modes %d", ret);
+ return ret;
+ }
+
+ if (connector->state() != DRM_MODE_CONNECTED && display == HWC_DISPLAY_EXTERNAL) {
+ ALOGE("connector is not connected with display %d", display);
+ return -ENODEV;
+ }
+
+ update_display_bestmode(hd, display, connector);
+ DrmMode mode = connector->best_mode();
+ connector->set_current_mode(mode);
+
+ char framebuffer_size[PROPERTY_VALUE_MAX];
+ uint32_t width, height, vrefresh;
+ property_get("persist.sys.framebuffer.main", framebuffer_size, "0x0@60");
+ sscanf(framebuffer_size, "%dx%d@%d", &width, &height, &vrefresh);
+ if (width && height) {
+ hd->framebuffer_width = width;
+ hd->framebuffer_height = height;
+ hd->vrefresh = vrefresh ? vrefresh : 60;
+ } else if (mode.h_display() && mode.v_display() && mode.v_refresh()) {
+ hd->framebuffer_width = mode.h_display();
+ hd->framebuffer_height = mode.v_display();
+ hd->vrefresh = mode.v_refresh();
+ /*
+ * Limit to 1080p if large than 2160p
+ */
+ if (hd->framebuffer_height >= 2160 && hd->framebuffer_width >= hd->framebuffer_height) {
+ hd->framebuffer_width = hd->framebuffer_width * (1080.0 / hd->framebuffer_height);
+ hd->framebuffer_height = 1080;
+ }
+ } else {
+ hd->framebuffer_width = 1920;
+ hd->framebuffer_height = 1080;
+ hd->vrefresh = 60;
+ ALOGE("Failed to find available display mode for display %d\n", display);
+ }
+
+ hd->rel_xres = mode.h_display();
+ hd->rel_yres = mode.v_display();
+ hd->v_total = mode.v_total();
+
+ *num_configs = 1;
+ configs[0] = connector->display();
+
+ return 0;
+}
+
+static int hwc_get_display_attributes(struct hwc_composer_device_1 *dev,
+ int display, uint32_t config,
+ const uint32_t *attributes,
+ int32_t *values) {
+ UN_USED(config);
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ DrmConnector *c = ctx->drm.GetConnectorFromType(display);
+ if (!c) {
+ ALOGE("Failed to get DrmConnector for display %d", display);
+ return -ENODEV;
+ }
+ hwc_drm_display_t *hd = &ctx->displays[c->display()];
+ if (!hd->active)
+ return -ENODEV;
+ uint32_t mm_width = c->mm_width();
+ uint32_t mm_height = c->mm_height();
+ int w = hd->framebuffer_width;
+ int h = hd->framebuffer_height;
+ int vrefresh = hd->vrefresh;
+
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; ++i) {
+ switch (attributes[i]) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ values[i] = 1000 * 1000 * 1000 / vrefresh;
+ break;
+ case HWC_DISPLAY_WIDTH:
+ values[i] = w;
+ break;
+ case HWC_DISPLAY_HEIGHT:
+ values[i] = h;
+ break;
+ case HWC_DISPLAY_DPI_X:
+ /* Dots per 1000 inches */
+ values[i] = mm_width ? (w * UM_PER_INCH) / mm_width : 0;
+ break;
+ case HWC_DISPLAY_DPI_Y:
+ /* Dots per 1000 inches */
+ values[i] =
+ mm_height ? (h * UM_PER_INCH) / mm_height : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int hwc_get_active_config(struct hwc_composer_device_1 *dev,
+ int display) {
+ UN_USED(dev);
+ UN_USED(display);
+ return 0;
+}
+
+static int hwc_set_active_config(struct hwc_composer_device_1 *dev, int display,
+ int index) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+
+ UN_USED(index);
+ DrmConnector *c = ctx->drm.GetConnectorFromType(display);
+ if (!c) {
+ ALOGE("Failed to get connector for display %d line=%d", display,__LINE__);
+ return -ENODEV;
+ }
+
+ if (c->state() != DRM_MODE_CONNECTED) {
+ /*
+ * fake primary display if primary is not connected.
+ */
+ if (display == HWC_DISPLAY_PRIMARY)
+ return 0;
+
+ return -ENODEV;
+ }
+
+ hwc_drm_display_t *hd = &ctx->displays[c->display()];
+
+
+ DrmMode mode = c->best_mode();
+ if (!mode.id()) {
+ ALOGE("Could not find active mode for display=%d", display);
+ return -ENOENT;
+ }
+ hd->w_scale = (float)mode.h_display() / hd->framebuffer_width;
+ hd->h_scale = (float)mode.v_display() / hd->framebuffer_height;
+
+ c->set_current_mode(mode);
+ ctx->drm.UpdateDisplayRoute();
+
+ return 0;
+}
+
+static int hwc_device_close(struct hw_device_t *dev) {
+ struct hwc_context_t *ctx = (struct hwc_context_t *)dev;
+
+#if RK_INVALID_REFRESH
+ free_thread_pamaters(&ctx->mRefresh);
+#endif
+#if 0
+ if(ctx->fd_3d >= 0)
+ {
+ close(ctx->fd_3d);
+ ctx->fd_3d = -1;
+ }
+ free_thread_pamaters(&ctx->mControlStereo);
+#endif
+ delete ctx;
+ return 0;
+}
+
+/*
+ * TODO: This function sets the active config to the first one in the list. This
+ * should be fixed such that it selects the preferred mode for the display, or
+ * some other, saner, method of choosing the config.
+ */
+static int hwc_set_initial_config(struct hwc_context_t *ctx, int display) {
+ uint32_t config;
+ size_t num_configs = 1;
+ int ret = hwc_get_display_configs(&ctx->device, display, &config,
+ &num_configs);
+ if (ret || !num_configs)
+ return 0;
+
+ ret = hwc_set_active_config(&ctx->device, display, 0);
+ if (ret) {
+ ALOGE("Failed to set active config d=%d ret=%d", display, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int hwc_initialize_display(struct hwc_context_t *ctx, int display) {
+ hwc_drm_display_t *hd = &ctx->displays[display];
+ hd->ctx = ctx;
+#if RK_VIDEO_UI_OPT
+ hd->iUiFd = -1;
+ hd->bHideUi = false;
+#endif
+ hd->framebuffer_width = 0;
+ hd->framebuffer_height = 0;
+ hd->rel_xres = 0;
+ hd->rel_yres = 0;
+ hd->v_total = 0;
+ hd->w_scale = 1.0;
+ hd->h_scale = 1.0;
+ hd->active = true;
+
+ return 0;
+}
+
+static int hwc_enumerate_displays(struct hwc_context_t *ctx) {
+ int ret, num_connectors = 0;
+
+ for (auto &conn : ctx->drm.connectors()) {
+ ret = hwc_initialize_display(ctx, conn->display());
+ if (ret) {
+ ALOGE("Failed to initialize display %d", conn->display());
+ return ret;
+ }
+ num_connectors++;
+ }
+#if 0
+ ret = hwc_set_initial_config(ctx, HWC_DISPLAY_PRIMARY);
+ if (ret) {
+ ALOGE("Failed to set initial config for primary display ret=%d", ret);
+ return ret;
+ }
+
+ ret = hwc_set_initial_config(ctx, HWC_DISPLAY_EXTERNAL);
+ if (ret) {
+ ALOGE("Failed to set initial config for extend display ret=%d", ret);
+// return ret;
+ }
+#endif
+
+ ret = ctx->primary_vsync_worker.Init(&ctx->drm, HWC_DISPLAY_PRIMARY);
+ if (ret) {
+ ALOGE("Failed to create event worker for primary display %d\n", ret);
+ return ret;
+ }
+
+ if (num_connectors > 1) {
+ ret = ctx->extend_vsync_worker.Init(&ctx->drm, HWC_DISPLAY_EXTERNAL);
+ if (ret) {
+ ALOGE("Failed to create event worker for extend display %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = ctx->virtual_compositor_worker.Init();
+ if (ret) {
+ ALOGE("Failed to initialize virtual compositor worker");
+ return ret;
+ }
+ return 0;
+}
+
+#if RK_INVALID_REFRESH
+static void hwc_static_screen_opt_handler(int sig)
+{
+ hwc_context_t* ctx = g_ctx;
+ if (sig == SIGALRM) {
+ ctx->mOneWinOpt = true;
+ pthread_mutex_lock(&ctx->mRefresh.mlk);
+ ctx->mRefresh.count = 100;
+ ALOGD_IF(log_level(DBG_VERBOSE),"hwc_static_screen_opt_handler:mRefresh.count=%d",ctx->mRefresh.count);
+ pthread_mutex_unlock(&ctx->mRefresh.mlk);
+ pthread_cond_signal(&ctx->mRefresh.cond);
+ }
+
+ return;
+}
+
+void *invalidate_refresh(void *arg)
+{
+ hwc_context_t* ctx = (hwc_context_t*)arg;
+ int count = 0;
+ int nMaxCnt = 25;
+ unsigned int nSleepTime = 200;
+
+ pthread_cond_wait(&ctx->mRefresh.cond,&ctx->mRefresh.mtx);
+ while(true) {
+ for(count = 0; count < nMaxCnt; count++) {
+ usleep(nSleepTime*1000);
+ pthread_mutex_lock(&ctx->mRefresh.mlk);
+ count = ctx->mRefresh.count;
+ ctx->mRefresh.count ++;
+ ALOGD_IF(log_level(DBG_VERBOSE),"invalidate_refresh mRefresh.count=%d",ctx->mRefresh.count);
+ pthread_mutex_unlock(&ctx->mRefresh.mlk);
+ ctx->procs->invalidate(ctx->procs);
+ }
+ pthread_cond_wait(&ctx->mRefresh.cond,&ctx->mRefresh.mtx);
+ count = 0;
+ }
+
+ pthread_exit(NULL);
+ return NULL;
+}
+#endif
+
+#if 0
+void* hwc_control_3dmode_thread(void *arg)
+{
+ hwc_context_t* ctx = (hwc_context_t*)arg;
+ int ret = -1;
+ int needStereo = 0;
+
+ ALOGD("hwc_control_3dmode_thread creat");
+ pthread_cond_wait(&ctx->mControlStereo.cond,&ctx->mControlStereo.mtx);
+ while(true) {
+ pthread_mutex_lock(&ctx->mControlStereo.mlk);
+ needStereo = ctx->mControlStereo.count;
+ pthread_mutex_unlock(&ctx->mControlStereo.mlk);
+ ret = hwc_control_3dmode(ctx->fb_3d, 2, READ_3D_MODE);
+ if(needStereo != ret) {
+ hwc_control_3dmode(ctx, needStereo,WRITE_3D_MODE);
+ ALOGI_IF(log_level(DBG_VERBOSE),"change stereo mode %d to %d",ret,needStereo);
+ }
+ ALOGD_IF(log_level(DBG_VERBOSE),"mControlStereo.count=%d",needStereo);
+ pthread_cond_wait(&ctx->mControlStereo.cond,&ctx->mControlStereo.mtx);
+ }
+ ALOGD("hwc_control_3dmode_thread exit");
+ pthread_exit(NULL);
+ return NULL;
+}
+#endif
+
+static int hwc_device_open(const struct hw_module_t *module, const char *name,
+ struct hw_device_t **dev) {
+ if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+ ALOGE("Invalid module name- %s", name);
+ return -EINVAL;
+ }
+
+ init_rk_debug();
+
+ std::unique_ptr<hwc_context_t> ctx(new hwc_context_t());
+ if (!ctx) {
+ ALOGE("Failed to allocate hwc context");
+ return -ENOMEM;
+ }
+
+ int ret = ctx->drm.Init();
+ if (ret) {
+ ALOGE("Can't initialize Drm object %d", ret);
+ return ret;
+ }
+
+ ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&ctx->gralloc);
+ if (ret) {
+ ALOGE("Failed to open gralloc module %d", ret);
+ return ret;
+ }
+
+ ctx->drm.setGralloc(ctx->gralloc);
+
+ ret = ctx->dummy_timeline.Init();
+ if (ret) {
+ ALOGE("Failed to create dummy sw sync timeline %d", ret);
+ return ret;
+ }
+
+ ctx->importer.reset(Importer::CreateInstance(&ctx->drm));
+ if (!ctx->importer) {
+ ALOGE("Failed to create importer instance");
+ return ret;
+ }
+
+ ret = hwc_enumerate_displays(ctx.get());
+ if (ret) {
+ ALOGE("Failed to enumerate displays: %s", strerror(ret));
+ return ret;
+ }
+
+ ctx->device.common.tag = HARDWARE_DEVICE_TAG;
+ ctx->device.common.version = HWC_DEVICE_API_VERSION_1_4;
+ ctx->device.common.module = const_cast<hw_module_t *>(module);
+ ctx->device.common.close = hwc_device_close;
+
+ ctx->device.dump = hwc_dump;
+ ctx->device.prepare = hwc_prepare;
+ ctx->device.set = hwc_set;
+ ctx->device.eventControl = hwc_event_control;
+ ctx->device.setPowerMode = hwc_set_power_mode;
+ ctx->device.query = hwc_query;
+ ctx->device.registerProcs = hwc_register_procs;
+ ctx->device.getDisplayConfigs = hwc_get_display_configs;
+ ctx->device.getDisplayAttributes = hwc_get_display_attributes;
+ ctx->device.getActiveConfig = hwc_get_active_config;
+ ctx->device.setActiveConfig = hwc_set_active_config;
+ ctx->device.setCursorPositionAsync = NULL; /* TODO: Add cursor */
+
+
+ g_ctx = ctx.get();
+
+ ctx->fb_fd = open("/dev/graphics/fb0", O_RDWR, 0);
+ if(ctx->fb_fd < 0)
+ {
+ ALOGE("Open fb0 fail in %s",__FUNCTION__);
+ return -1;
+ }
+
+ hwc_init_version();
+
+
+#if RK_INVALID_REFRESH
+ ctx->mOneWinOpt = false;
+ ctx->isGLESComp = false;
+ init_thread_pamaters(&ctx->mRefresh);
+ pthread_t invalidate_refresh_th;
+ if (pthread_create(&invalidate_refresh_th, NULL, invalidate_refresh, ctx.get()))
+ {
+ ALOGE("Create invalidate_refresh_th thread error .");
+ }
+
+ signal(SIGALRM, hwc_static_screen_opt_handler);
+#endif
+
+#if 0
+ init_thread_pamaters(&ctx->mControlStereo);
+ ctx->fd_3d = open("/sys/class/display/HDMI/3dmode", O_RDWR, 0);
+ if(ctx->fd_3d < 0){
+ ALOGE("open /sys/class/display/HDMI/3dmode fail");
+ }
+
+ pthread_t thread_3d;
+ if (pthread_create(&thread_3d, NULL, hwc_control_3dmode_thread, ctx.get()))
+ {
+ ALOGE("Create hwc_control_3dmode_thread thread error .");
+ }
+
+#endif
+
+ *dev = &ctx->device.common;
+ ctx.release();
+
+ return 0;
+}
+}
+
+static struct hw_module_methods_t hwc_module_methods = {
+ .open = android::hwc_device_open
+};
+
+hwc_module_t HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = HWC_HARDWARE_MODULE_ID,
+ .name = "DRM hwcomposer module",
+ .author = "The Android Open Source Project",
+ .methods = &hwc_module_methods,
+ .dso = NULL,
+ .reserved = {0},
+ }
+};
diff --git a/platform.cpp b/platform.cpp
new file mode 100755
index 0000000..0ed677c
--- /dev/null
+++ b/platform.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-platform"
+
+#include "drmresources.h"
+#include "platform.h"
+
+#include <cutils/log.h>
+#include <inttypes.h>
+
+namespace android {
+
+std::vector<DrmPlane *> Planner::GetUsablePlanes(
+ DrmCrtc *crtc, std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes) {
+ std::vector<DrmPlane *> usable_planes;
+ std::copy_if(primary_planes->begin(), primary_planes->end(),
+ std::back_inserter(usable_planes),
+ [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); });
+ std::copy_if(overlay_planes->begin(), overlay_planes->end(),
+ std::back_inserter(usable_planes),
+ [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); });
+ return usable_planes;
+}
+
+std::tuple<int, std::vector<DrmCompositionPlane>> Planner::ProvisionPlanes(
+ std::map<size_t, DrmHwcLayer *> &layers, bool use_squash_fb, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes) {
+ std::vector<DrmCompositionPlane> composition;
+
+ std::vector<DrmPlane *> planes =
+ GetUsablePlanes(crtc, primary_planes, overlay_planes);
+
+ if (planes.empty()) {
+ ALOGE("has no usable planes");
+ return std::make_tuple(-ENODEV, std::vector<DrmCompositionPlane>());
+ }
+
+ // If needed, reserve the squash plane at the highest z-order
+ DrmPlane *squash_plane = NULL;
+ if (use_squash_fb) {
+ if (!planes.empty()) {
+ squash_plane = planes.back();
+ planes.pop_back();
+ } else {
+ ALOGI("Not enough planes to reserve for squash fb");
+ }
+ }
+
+ // If needed, reserve the precomp plane at the next highest z-order
+ DrmPlane *precomp_plane = NULL;
+ if (layers.size() > planes.size()) {
+ if (!planes.empty()) {
+ precomp_plane = planes.back();
+ planes.pop_back();
+ composition.emplace_back(DrmCompositionPlane::Type::kPrecomp,
+ precomp_plane, crtc);
+ } else {
+ ALOGE("Not enough planes to reserve for precomp fb");
+ }
+ }
+
+ // Go through the provisioning stages and provision planes
+ for (auto &i : stages_) {
+ int ret = i->ProvisionPlanes(&composition, layers, crtc, &planes);
+ if (ret) {
+ ALOGE("Failed provision stage with ret %d", ret);
+ return std::make_tuple(ret, std::vector<DrmCompositionPlane>());
+ }
+ }
+
+#if USE_SQUASH
+ if (squash_plane)
+ {
+ composition.emplace_back(DrmCompositionPlane::Type::kSquash, squash_plane,
+ crtc);
+ }
+#endif
+
+ return std::make_tuple(0, std::move(composition));
+}
+
+int PlanStageProtected::ProvisionPlanes(
+ std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes) {
+ int ret;
+ int protected_zorder = -1;
+ for (auto i = layers.begin(); i != layers.end();) {
+ if (!i->second->protected_usage()) {
+ ++i;
+ continue;
+ }
+
+ ret = Emplace(composition, planes, DrmCompositionPlane::Type::kLayer, crtc,
+ i->first);
+ if (ret)
+ ALOGE("Failed to dedicate protected layer! Dropping it.");
+
+ protected_zorder = i->first;
+ i = layers.erase(i);
+ }
+
+ if (protected_zorder == -1)
+ return 0;
+
+ // Add any layers below the protected content to the precomposition since we
+ // need to punch a hole through them.
+ for (auto i = layers.begin(); i != layers.end();) {
+ // Skip layers above the z-order of the protected content
+ if (i->first > static_cast<size_t>(protected_zorder)) {
+ ++i;
+ continue;
+ }
+
+ // If there's no precomp layer already queued, queue one now.
+ DrmCompositionPlane *precomp = GetPrecomp(composition);
+ if (precomp) {
+ precomp->source_layers().emplace_back(i->first);
+ } else {
+ if (!planes->empty()) {
+ DrmPlane *precomp_plane = planes->back();
+ planes->pop_back();
+ composition->emplace_back(DrmCompositionPlane::Type::kPrecomp,
+ precomp_plane, crtc, i->first);
+ } else {
+ ALOGE("Not enough planes to reserve for precomp fb");
+ }
+ }
+ i = layers.erase(i);
+ }
+ return 0;
+}
+
+int PlanStageGreedy::ProvisionPlanes(
+ std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes) {
+ // Fill up the remaining planes
+ for (auto i = layers.begin(); i != layers.end(); i = layers.erase(i)) {
+ int ret = Emplace(composition, planes, DrmCompositionPlane::Type::kLayer,
+ crtc, i->first);
+ // We don't have any planes left
+ if (ret == -ENOENT)
+ break;
+ else if (ret)
+ {
+ ALOGD_IF(log_level(DBG_DEBUG),"Failed to emplace layer %zu, dropping it", i->first);
+ break;
+ }
+ }
+
+ // Put the rest of the layers in the precomp plane
+ DrmCompositionPlane *precomp = GetPrecomp(composition);
+ if (precomp) {
+ for (auto i = layers.begin(); i != layers.end(); i = layers.erase(i)) {
+
+ precomp->source_layers().emplace_back(i->first);
+ }
+ }
+
+ return 0;
+}
+}
diff --git a/platform.h b/platform.h
new file mode 100755
index 0000000..3f51eb3
--- /dev/null
+++ b/platform.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DRM_PLATFORM_H_
+#define ANDROID_DRM_PLATFORM_H_
+
+#include "drmdisplaycomposition.h"
+#include "drmhwcomposer.h"
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+#include <map>
+#include <vector>
+
+namespace android {
+
+class DrmResources;
+
+class Importer {
+ public:
+ virtual ~Importer() {
+ }
+
+ // Creates a platform-specific importer instance
+ static Importer *CreateInstance(DrmResources *drm);
+
+ // Imports the buffer referred to by handle into bo.
+ //
+ // Note: This can be called from a different thread than ReleaseBuffer. The
+ // implementation is responsible for ensuring thread safety.
+#if RK_VIDEO_SKIP_LINE
+ virtual int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo, bool bSkipLine) = 0;
+#else
+ virtual int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo) = 0;
+#endif
+
+ // Releases the buffer object (ie: does the inverse of ImportBuffer)
+ //
+ // Note: This can be called from a different thread than ImportBuffer. The
+ // implementation is responsible for ensuring thread safety.
+ virtual int ReleaseBuffer(hwc_drm_bo_t *bo) = 0;
+};
+
+class Planner {
+ public:
+ class PlanStage {
+ public:
+ virtual ~PlanStage() {
+ }
+
+ virtual int ProvisionPlanes(std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers,
+ DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes) = 0;
+
+ // Finds and returns the squash layer from the composition
+ static DrmCompositionPlane *GetPrecomp(
+ std::vector<DrmCompositionPlane> *composition) {
+ auto l = GetPrecompIter(composition);
+ if (l == composition->end())
+ return NULL;
+ return &(*l);
+ }
+
+ protected:
+ // Removes and returns the next available plane from planes
+ static DrmPlane *PopPlane(std::vector<DrmPlane *> *planes) {
+ if (planes->empty())
+ return NULL;
+ DrmPlane *plane = planes->front();
+ planes->erase(planes->begin());
+ return plane;
+ }
+
+ // Inserts the given layer:plane in the composition right before the precomp
+ // layer
+ static int Emplace(std::vector<DrmCompositionPlane> *composition,
+ std::vector<DrmPlane *> *planes,
+ DrmCompositionPlane::Type type, DrmCrtc *crtc,
+ size_t source_layer) {
+ DrmPlane *plane = PopPlane(planes);
+ if (!plane)
+ return -ENOENT;
+
+ auto precomp = GetPrecompIter(composition);
+ composition->emplace(precomp, type, plane, crtc, source_layer);
+
+ return 0;
+ }
+
+ private:
+ static std::vector<DrmCompositionPlane>::iterator GetPrecompIter(
+ std::vector<DrmCompositionPlane> *composition) {
+ return std::find_if(composition->begin(), composition->end(),
+ [](const DrmCompositionPlane &p) {
+ return p.type() == DrmCompositionPlane::Type::kPrecomp;
+ });
+ }
+ };
+
+ // Creates a planner instance with platform-specific planning stages
+ static std::unique_ptr<Planner> CreateInstance(DrmResources *drm);
+
+ // Takes a stack of layers and provisions hardware planes for them. If the
+ // entire stack can't fit in hardware, the Planner may place the remaining
+ // layers in a PRECOMP plane. Layers in the PRECOMP plane will be composited
+ // using GL. PRECOMP planes should be placed above any 1:1 layer:plane
+ // compositions. If use_squash_fb is true, the Planner should try to reserve a
+ // plane at the highest z-order with type SQUASH.
+ //
+ // @layers: a map of index:layer of layers to composite
+ // @use_squash_fb: reserve a squash framebuffer
+ // @primary_planes: a vector of primary planes available for this frame
+ // @overlay_planes: a vector of overlay planes available for this frame
+ //
+ // Returns: A tuple with the status of the operation (0 for success) and
+ // a vector of the resulting plan (ie: layer->plane mapping).
+ std::tuple<int, std::vector<DrmCompositionPlane>> ProvisionPlanes(
+ std::map<size_t, DrmHwcLayer *> &layers, bool use_squash_fb,
+ DrmCrtc *crtc, std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes);
+
+ bool MatchPlane(std::vector<DrmHwcLayer*>& layer_vector,
+ uint64_t* zpos,
+ DrmCrtc *crtc,
+ DrmResources *drm,
+ std::vector<DrmCompositionPlane>* composition_plane);
+
+ std::tuple<int, std::vector<DrmCompositionPlane>> MatchPlanes(
+ std::map<int, std::vector<DrmHwcLayer*>> &layer_map,
+ DrmCrtc *crtc,
+ DrmResources *drm);
+
+ template <typename T, typename... A>
+ void AddStage(A &&... args) {
+ stages_.emplace_back(
+ std::unique_ptr<PlanStage>(new T(std::forward(args)...)));
+ }
+
+ private:
+ std::vector<DrmPlane *> GetUsablePlanes(
+ DrmCrtc *crtc, std::vector<DrmPlane *> *primary_planes,
+ std::vector<DrmPlane *> *overlay_planes);
+
+ std::vector<std::unique_ptr<PlanStage>> stages_;
+};
+
+// This plan stage extracts all protected layers and places them on dedicated
+// planes.
+class PlanStageProtected : public Planner::PlanStage {
+ public:
+ int ProvisionPlanes(std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes);
+};
+
+// This plan stage places as many layers on dedicated planes as possible (first
+// come first serve), and then sticks the rest in a precomposition plane (if
+// needed).
+class PlanStageGreedy : public Planner::PlanStage {
+ public:
+ int ProvisionPlanes(std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes);
+};
+}
+#endif
diff --git a/platformdrmgeneric.cpp b/platformdrmgeneric.cpp
new file mode 100755
index 0000000..eae5519
--- /dev/null
+++ b/platformdrmgeneric.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-platform-drm-generic"
+
+#include "drmresources.h"
+#include "platform.h"
+#include "platformdrmgeneric.h"
+
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+#if RK_DRM_GRALLOC
+#include <gralloc_drm_handle.h>
+#endif
+#include <hardware/gralloc.h>
+#include "hwc_util.h"
+#include "hwc_rockchip.h"
+
+namespace android {
+
+#ifdef USE_DRM_GENERIC_IMPORTER
+// static
+Importer *Importer::CreateInstance(DrmResources *drm) {
+ DrmGenericImporter *importer = new DrmGenericImporter(drm);
+ if (!importer)
+ return NULL;
+
+ int ret = importer->Init();
+ if (ret) {
+ ALOGE("Failed to initialize the nv importer %d", ret);
+ delete importer;
+ return NULL;
+ }
+ return importer;
+}
+#endif
+
+DrmGenericImporter::DrmGenericImporter(DrmResources *drm) : drm_(drm) {
+}
+
+DrmGenericImporter::~DrmGenericImporter() {
+}
+
+int DrmGenericImporter::Init() {
+ int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&gralloc_);
+ if (ret) {
+ ALOGE("Failed to open gralloc module");
+ return ret;
+ }
+ return 0;
+}
+
+uint32_t DrmGenericImporter::ConvertHalFormatToDrm(uint32_t hal_format) {
+ switch (hal_format) {
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return DRM_FORMAT_BGR888;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return DRM_FORMAT_ARGB8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_XBGR8888;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return DRM_FORMAT_ABGR8888;
+ //Fix color error in NenaMark2.
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return DRM_FORMAT_RGB565;
+ case HAL_PIXEL_FORMAT_YV12:
+ return DRM_FORMAT_YVU420;
+ case HAL_PIXEL_FORMAT_YCrCb_NV12:
+ return DRM_FORMAT_NV12;
+ case HAL_PIXEL_FORMAT_YCrCb_NV12_10:
+ return DRM_FORMAT_NV12_10;
+ default:
+ ALOGE("Cannot convert hal format to drm format %u", hal_format);
+ return -EINVAL;
+ }
+}
+
+#ifndef u64
+#define u64 uint64_t
+#endif
+int DrmGenericImporter::ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo
+#if RK_VIDEO_SKIP_LINE
+, bool bSkipLine
+#endif
+) {
+ int fd,width,height,byte_stride,format;
+
+ fd = hwc_get_handle_primefd(gralloc_, handle);
+#if RK_DRM_GRALLOC
+ width = hwc_get_handle_attibute(gralloc_,handle,ATT_WIDTH);
+ height = hwc_get_handle_attibute(gralloc_,handle,ATT_HEIGHT);
+ byte_stride = hwc_get_handle_attibute(gralloc_,handle,ATT_BYTE_STRIDE);
+ format = hwc_get_handle_attibute(gralloc_,handle,ATT_FORMAT);
+#else
+ width = hwc_get_handle_width(gralloc_,handle);
+ height = hwc_get_handle_height(gralloc_,handle);
+ byte_stride = hwc_get_handle_byte_stride(gralloc_,handle);
+ format = hwc_get_handle_format(gralloc_,handle);
+#endif
+ uint32_t gem_handle;
+ int ret = drmPrimeFDToHandle(drm_->fd(), fd, &gem_handle);
+ if (ret) {
+ ALOGE("failed to import prime fd %d ret=%d", fd, ret);
+ return ret;
+ }
+
+ memset(bo, 0, sizeof(hwc_drm_bo_t));
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ {
+ bo->width = width/1.25;
+ bo->width = ALIGN_DOWN(bo->width,2);
+ }
+ else
+ {
+ bo->width = width;
+ }
+
+#if RK_VIDEO_SKIP_LINE
+ if(bSkipLine)
+ {
+ bo->pitches[0] = byte_stride*2;
+ bo->height = height/2;
+ }
+ else
+#endif
+ {
+ bo->pitches[0] = byte_stride;
+ bo->height = height;
+ }
+
+ bo->format = ConvertHalFormatToDrm(format);
+ bo->gem_handles[0] = gem_handle;
+ bo->offsets[0] = 0;
+
+ if(format == HAL_PIXEL_FORMAT_YCrCb_NV12 || format == HAL_PIXEL_FORMAT_YCrCb_NV12_10)
+ {
+ bo->pitches[1] = bo->pitches[0];
+ bo->gem_handles[1] = gem_handle;
+ bo->offsets[1] = bo->pitches[1] * bo->height;
+ }
+#if USE_AFBC_LAYER
+ __u64 modifier[4];
+ uint64_t internal_format;
+ memset(modifier, 0, sizeof(modifier));
+ gralloc_->perform(gralloc_, GRALLOC_MODULE_PERFORM_GET_INTERNAL_FORMAT,
+ handle, &internal_format);
+ if (isAfbcInternalFormat(internal_format))
+ modifier[0] = DRM_FORMAT_MOD_ARM_AFBC;
+
+ ret = drmModeAddFB2_ext(drm_->fd(), bo->width, bo->height, bo->format,
+ bo->gem_handles, bo->pitches, bo->offsets, modifier,
+ &bo->fb_id, DRM_MODE_FB_MODIFIERS);
+#else
+ ret = drmModeAddFB2(drm_->fd(), bo->width, bo->height, bo->format,
+ bo->gem_handles, bo->pitches, bo->offsets, &bo->fb_id, 0);
+#endif
+ if (ret) {
+ ALOGE("could not create drm fb %d", ret);
+ ALOGE("ImportBuffer fd=%d,w=%d,h=%d,format=0x%x,bo->format=0x%x,gem_handle=%d,bo->pitches[0]=%d,fb_id=%d",
+ drm_->fd(), bo->width, bo->height, format,bo->format,
+ gem_handle, bo->pitches[0], bo->fb_id);
+#if RK_VIDEO_SKIP_LINE
+ ALOGE("bSkipLine=%d",bSkipLine);
+#endif
+ return ret;
+ }
+
+ //Fix "Failed to close gem handle" bug which lead by no reference counting.
+#if 1
+ struct drm_gem_close gem_close;
+ int num_gem_handles;
+ memset(&gem_close, 0, sizeof(gem_close));
+ num_gem_handles = 1;
+
+ for (int i = 0; i < num_gem_handles; i++) {
+ if (!bo->gem_handles[i])
+ continue;
+
+ gem_close.handle = bo->gem_handles[i];
+ int ret = drmIoctl(drm_->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close);
+ if (ret)
+ ALOGE("Failed to close gem handle %d %d", i, ret);
+ else
+ bo->gem_handles[i] = 0;
+ }
+#endif
+
+ return ret;
+}
+
+int DrmGenericImporter::ReleaseBuffer(hwc_drm_bo_t *bo) {
+ if (bo->fb_id)
+ if (drmModeRmFB(drm_->fd(), bo->fb_id))
+ ALOGE("Failed to rm fb");
+
+#if 0
+ struct drm_gem_close gem_close;
+ memset(&gem_close, 0, sizeof(gem_close));
+ int num_gem_handles = sizeof(bo->gem_handles) / sizeof(bo->gem_handles[0]);
+ for (int i = 0; i < num_gem_handles; i++) {
+ if (!bo->gem_handles[i])
+ continue;
+
+ gem_close.handle = bo->gem_handles[i];
+ int ret = drmIoctl(drm_->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close);
+ if (ret)
+ ALOGE("Failed to close gem handle %d %d", i, ret);
+ else
+ bo->gem_handles[i] = 0;
+ }
+#endif
+ return 0;
+}
+
+#ifdef USE_DRM_GENERIC_IMPORTER
+std::unique_ptr<Planner> Planner::CreateInstance(DrmResources *) {
+ std::unique_ptr<Planner> planner(new Planner);
+ planner->AddStage<PlanStageGreedy>();
+ return planner;
+}
+#endif
+}
diff --git a/platformdrmgeneric.h b/platformdrmgeneric.h
new file mode 100644
index 0000000..62aa4e2
--- /dev/null
+++ b/platformdrmgeneric.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PLATFORM_DRM_GENERIC_H_
+#define ANDROID_PLATFORM_DRM_GENERIC_H_
+
+#include "drmresources.h"
+#include "platform.h"
+
+#include <hardware/gralloc.h>
+
+namespace android {
+
+class DrmGenericImporter : public Importer {
+ public:
+ DrmGenericImporter(DrmResources *drm);
+ ~DrmGenericImporter() override;
+
+ int Init();
+
+#if RK_VIDEO_SKIP_LINE
+ int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo, bool bSkipLine) override;
+#else
+ int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+#endif
+ int ReleaseBuffer(hwc_drm_bo_t *bo) override;
+
+ private:
+ uint32_t ConvertHalFormatToDrm(uint32_t hal_format);
+
+ DrmResources *drm_;
+
+ const gralloc_module_t *gralloc_;
+};
+}
+
+#endif
diff --git a/platformnv.cpp b/platformnv.cpp
new file mode 100644
index 0000000..5fc167c
--- /dev/null
+++ b/platformnv.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-platform-nv"
+
+#include "drmresources.h"
+#include "platform.h"
+#include "platformnv.h"
+
+#include <cinttypes>
+#include <stdatomic.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+#include <hardware/gralloc.h>
+
+namespace android {
+
+#ifdef USE_NVIDIA_IMPORTER
+// static
+Importer *Importer::CreateInstance(DrmResources *drm) {
+ NvImporter *importer = new NvImporter(drm);
+ if (!importer)
+ return NULL;
+
+ int ret = importer->Init();
+ if (ret) {
+ ALOGE("Failed to initialize the nv importer %d", ret);
+ delete importer;
+ return NULL;
+ }
+ return importer;
+}
+#endif
+
+NvImporter::NvImporter(DrmResources *drm) : drm_(drm) {
+}
+
+NvImporter::~NvImporter() {
+}
+
+int NvImporter::Init() {
+ int ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&gralloc_);
+ if (ret) {
+ ALOGE("Failed to open gralloc module %d", ret);
+ return ret;
+ }
+
+ if (strcasecmp(gralloc_->common.author, "NVIDIA"))
+ ALOGW("Using non-NVIDIA gralloc module: %s/%s\n", gralloc_->common.name,
+ gralloc_->common.author);
+
+ return 0;
+}
+
+int NvImporter::ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo
+#if RK_VIDEO_SKIP_LINE
+, bool bSkipLine
+#endif
+) {
+#if RK_VIDEO_SKIP_LINE
+ UN_USED(bSkipLine);
+#endif
+ memset(bo, 0, sizeof(hwc_drm_bo_t));
+ NvBuffer_t *buf = GrallocGetNvBuffer(handle);
+ if (buf) {
+ atomic_fetch_add(&buf->ref, 1);
+ *bo = buf->bo;
+ return 0;
+ }
+
+ buf = new NvBuffer_t();
+ if (!buf) {
+ ALOGE("Failed to allocate new NvBuffer_t");
+ return -ENOMEM;
+ }
+ buf->bo.priv = buf;
+ buf->importer = this;
+
+ // We initialize the reference count to 2 since NvGralloc is still using this
+ // buffer (will be cleared in the NvGrallocRelease), and the other
+ // reference is for HWC (this ImportBuffer call).
+ atomic_init(&buf->ref, 2);
+
+ int ret = gralloc_->perform(gralloc_, GRALLOC_MODULE_PERFORM_DRM_IMPORT,
+ drm_->fd(), handle, &buf->bo);
+ if (ret) {
+ ALOGE("GRALLOC_MODULE_PERFORM_DRM_IMPORT failed %d", ret);
+ delete buf;
+ return ret;
+ }
+
+ ret = drmModeAddFB2(drm_->fd(), buf->bo.width, buf->bo.height, buf->bo.format,
+ buf->bo.gem_handles, buf->bo.pitches, buf->bo.offsets,
+ &buf->bo.fb_id, 0);
+ if (ret) {
+ ALOGE("Failed to add fb %d", ret);
+ ReleaseBufferImpl(&buf->bo);
+ delete buf;
+ return ret;
+ }
+
+ ret = GrallocSetNvBuffer(handle, buf);
+ if (ret) {
+ /* This will happen is persist.tegra.gpu_mapping_cache is 0/off,
+ * or if NV gralloc runs out of "priv slots" (currently 3 per buffer,
+ * only one of which should be used by drm_hwcomposer). */
+ ALOGE("Failed to register free callback for imported buffer %d", ret);
+ ReleaseBufferImpl(&buf->bo);
+ delete buf;
+ return ret;
+ }
+ *bo = buf->bo;
+ return 0;
+}
+
+int NvImporter::ReleaseBuffer(hwc_drm_bo_t *bo) {
+ NvBuffer_t *buf = (NvBuffer_t *)bo->priv;
+ if (!buf) {
+ ALOGE("Freeing bo %" PRIu32 ", buf is NULL!", bo->fb_id);
+ return 0;
+ }
+ if (atomic_fetch_sub(&buf->ref, 1) > 1)
+ return 0;
+
+ ReleaseBufferImpl(bo);
+ delete buf;
+ return 0;
+}
+
+// static
+void NvImporter::NvGrallocRelease(void *nv_buffer) {
+ NvBuffer_t *buf = (NvBuffer *)nv_buffer;
+ buf->importer->ReleaseBuffer(&buf->bo);
+}
+
+void NvImporter::ReleaseBufferImpl(hwc_drm_bo_t *bo) {
+ if (bo->fb_id) {
+ int ret = drmModeRmFB(drm_->fd(), bo->fb_id);
+ if (ret)
+ ALOGE("Failed to rm fb %d", ret);
+ }
+
+ struct drm_gem_close gem_close;
+ memset(&gem_close, 0, sizeof(gem_close));
+ int num_gem_handles = sizeof(bo->gem_handles) / sizeof(bo->gem_handles[0]);
+ for (int i = 0; i < num_gem_handles; i++) {
+ if (!bo->gem_handles[i])
+ continue;
+
+ gem_close.handle = bo->gem_handles[i];
+ int ret = drmIoctl(drm_->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close);
+ if (ret) {
+ ALOGE("Failed to close gem handle %d %d", i, ret);
+ } else {
+ /* Clear any duplicate gem handle as well but don't close again */
+ for (int j = i + 1; j < num_gem_handles; j++)
+ if (bo->gem_handles[j] == bo->gem_handles[i])
+ bo->gem_handles[j] = 0;
+ bo->gem_handles[i] = 0;
+ }
+ }
+}
+
+NvImporter::NvBuffer_t *NvImporter::GrallocGetNvBuffer(buffer_handle_t handle) {
+ void *priv = NULL;
+ int ret =
+ gralloc_->perform(gralloc_, GRALLOC_MODULE_PERFORM_GET_IMPORTER_PRIVATE,
+ handle, NvGrallocRelease, &priv);
+ return ret ? NULL : (NvBuffer_t *)priv;
+}
+
+int NvImporter::GrallocSetNvBuffer(buffer_handle_t handle, NvBuffer_t *buf) {
+ return gralloc_->perform(gralloc_,
+ GRALLOC_MODULE_PERFORM_SET_IMPORTER_PRIVATE, handle,
+ NvGrallocRelease, buf);
+}
+
+#ifdef USE_NVIDIA_IMPORTER
+// static
+std::unique_ptr<Planner> Planner::CreateInstance(DrmResources *) {
+ std::unique_ptr<Planner> planner(new Planner);
+ planner->AddStage<PlanStageProtectedRotated>();
+ planner->AddStage<PlanStageProtected>();
+ planner->AddStage<PlanStageGreedy>();
+ return planner;
+}
+#endif
+
+static DrmPlane *GetCrtcPrimaryPlane(DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes) {
+ for (auto i = planes->begin(); i != planes->end(); ++i) {
+ if ((*i)->GetCrtcSupported(*crtc)) {
+ DrmPlane *plane = *i;
+ planes->erase(i);
+ return plane;
+ }
+ }
+ return NULL;
+}
+
+int PlanStageProtectedRotated::ProvisionPlanes(
+ std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes) {
+ int ret;
+ int protected_zorder = -1;
+ for (auto i = layers.begin(); i != layers.end();) {
+ if (!i->second->protected_usage() || !i->second->transform) {
+ ++i;
+ continue;
+ }
+
+ auto primary_iter = planes->begin();
+ for (; primary_iter != planes->end(); ++primary_iter) {
+ if ((*primary_iter)->type() == DRM_PLANE_TYPE_PRIMARY)
+ break;
+ }
+
+ // We cheat a little here. Since there can only be one primary plane per
+ // crtc, we know we'll only hit this case once. So we blindly insert the
+ // protected content at the beginning of the composition, knowing this path
+ // won't be taken a second time during the loop.
+ if (primary_iter != planes->end()) {
+ composition->emplace(composition->begin(),
+ DrmCompositionPlane::Type::kLayer, *primary_iter,
+ crtc, i->first);
+ planes->erase(primary_iter);
+ protected_zorder = i->first;
+ } else {
+ ALOGE("Could not provision primary plane for protected/rotated layer");
+ }
+ i = layers.erase(i);
+ }
+
+ if (protected_zorder == -1)
+ return 0;
+
+ // Add any layers below the protected content to the precomposition since we
+ // need to punch a hole through them.
+ for (auto i = layers.begin(); i != layers.end();) {
+ // Skip layers above the z-order of the protected content
+ if (i->first > static_cast<size_t>(protected_zorder)) {
+ ++i;
+ continue;
+ }
+
+ // If there's no precomp layer already queued, queue one now.
+ DrmCompositionPlane *precomp = GetPrecomp(composition);
+ if (precomp) {
+ precomp->source_layers().emplace_back(i->first);
+ } else {
+ if (planes->size()) {
+ DrmPlane *precomp_plane = planes->back();
+ planes->pop_back();
+ composition->emplace_back(DrmCompositionPlane::Type::kPrecomp,
+ precomp_plane, crtc, i->first);
+ } else {
+ ALOGE("Not enough planes to reserve for precomp fb");
+ }
+ }
+ i = layers.erase(i);
+ }
+ return 0;
+}
+}
diff --git a/platformnv.h b/platformnv.h
new file mode 100644
index 0000000..c22cdad
--- /dev/null
+++ b/platformnv.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PLATFORM_NV_H_
+#define ANDROID_PLATFORM_NV_H_
+
+#include "drmresources.h"
+#include "platform.h"
+#include "platformdrmgeneric.h"
+
+#include <stdatomic.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+
+class NvImporter : public Importer {
+ public:
+ NvImporter(DrmResources *drm);
+ ~NvImporter() override;
+
+ int Init();
+
+#if RK_VIDEO_SKIP_LINE
+ int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo, bool bSkipLine) override;
+#else
+ int ImportBuffer(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+#endif
+ int ReleaseBuffer(hwc_drm_bo_t *bo) override;
+
+ private:
+ typedef struct NvBuffer {
+ NvImporter *importer;
+ hwc_drm_bo_t bo;
+ atomic_int ref;
+ } NvBuffer_t;
+
+ static void NvGrallocRelease(void *nv_buffer);
+ void ReleaseBufferImpl(hwc_drm_bo_t *bo);
+
+ NvBuffer_t *GrallocGetNvBuffer(buffer_handle_t handle);
+ int GrallocSetNvBuffer(buffer_handle_t handle, NvBuffer_t *buf);
+
+ DrmResources *drm_;
+
+ const gralloc_module_t *gralloc_;
+};
+
+// This stage looks for any layers that contain transformed protected content
+// and puts it in the primary plane since Tegra doesn't support planar rotation
+// on the overlay planes.
+//
+// There are two caveats to this approach: 1- Protected content isn't
+// necessarily planar, but it's usually a safe bet, and 2- This doesn't catch
+// non-protected planar content. If we wanted to fix this, we'd need to import
+// the buffer in this stage and peek at it's format. The overhead of doing this
+// doesn't seem worth it since we'll end up displaying the right thing in both
+// cases anyways.
+class PlanStageProtectedRotated : public Planner::PlanStage {
+ public:
+ int ProvisionPlanes(std::vector<DrmCompositionPlane> *composition,
+ std::map<size_t, DrmHwcLayer *> &layers, DrmCrtc *crtc,
+ std::vector<DrmPlane *> *planes);
+};
+}
+
+#endif
diff --git a/separate_rects.cpp b/separate_rects.cpp
new file mode 100644
index 0000000..9fd1ae4
--- /dev/null
+++ b/separate_rects.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "separate_rects.h"
+#include <algorithm>
+#include <assert.h>
+#include <iostream>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+namespace separate_rects {
+
+enum EventType { START, END };
+
+template <typename TId, typename TNum>
+struct StartedRect {
+ IdSet<TId> id_set;
+ TNum left, top, bottom;
+
+ // Note that this->left is not part of the key. That field is only to mark the
+ // left edge of the rectangle.
+ bool operator<(const StartedRect<TId, TNum> &rhs) const {
+ return (top < rhs.top || (top == rhs.top && bottom < rhs.bottom)) ||
+ (top == rhs.top && bottom == rhs.bottom && id_set < rhs.id_set);
+ }
+};
+
+template <typename TId, typename TNum>
+struct SweepEvent {
+ EventType type;
+ union {
+ TNum x;
+ TNum y;
+ };
+
+ TId rect_id;
+
+ bool operator<(const SweepEvent<TId, TNum> &rhs) const {
+ return (y < rhs.y || (y == rhs.y && rect_id < rhs.rect_id));
+ }
+};
+
+template <typename TNum>
+std::ostream &operator<<(std::ostream &os, const Rect<TNum> &rect) {
+ return os << rect.bounds[0] << ", " << rect.bounds[1] << ", "
+ << rect.bounds[2] << ", " << rect.bounds[3];
+}
+
+template <typename TUInt>
+std::ostream &operator<<(std::ostream &os, const IdSet<TUInt> &obj) {
+ int bits = IdSet<TUInt>::max_elements;
+ TUInt mask = ((TUInt)0x1) << (bits - 1);
+ for (int i = 0; i < bits; i++)
+ os << ((obj.getBits() & (mask >> i)) ? "1" : "0");
+ return os;
+}
+
+template <typename TNum, typename TId>
+void separate_rects(const std::vector<Rect<TNum>> &in,
+ std::vector<RectSet<TId, TNum>> *out) {
+ // Overview:
+ // This algorithm is a line sweep algorithm that travels from left to right.
+ // The sweep stops at each vertical edge of each input rectangle in sorted
+ // order of x-coordinate. At each stop, the sweep line is examined in order of
+ // y-coordinate from top to bottom. Along the way, a running set of rectangle
+ // IDs is either added to or subtracted from as the top and bottom edges are
+ // encountered, respectively. At each change of that running set, a copy of
+ // that set is recorded in along with the the y-coordinate it happened at in a
+ // list. This list is then interpreted as a sort of vertical cross section of
+ // our output set of non-overlapping rectangles. Based of the algorithm found
+ // at: http://stackoverflow.com/a/2755498
+
+ if (in.size() > IdSet<TId>::max_elements) {
+ return;
+ }
+
+ // Events are when the sweep line encounters the starting or ending edge of
+ // any input rectangle.
+ std::set<SweepEvent<TId, TNum>> sweep_h_events; // Left or right bounds
+ std::set<SweepEvent<TId, TNum>> sweep_v_events; // Top or bottom bounds
+
+ // A started rect is a rectangle whose left, top, bottom edge, and set of
+ // rectangle IDs is known. The key of this map includes all that information
+ // (except the left edge is never used to determine key equivalence or
+ // ordering),
+ std::map<StartedRect<TId, TNum>, bool> started_rects;
+
+ // This is cleared after every event. Its declaration is here to avoid
+ // reallocating a vector and its buffers every event.
+ std::vector<std::pair<TNum, IdSet<TId>>> active_regions;
+
+ // This pass will add rectangle start and end events to be triggered as the
+ // algorithm sweeps from left to right.
+ for (TId i = 0; i < in.size(); i++) {
+ const Rect<TNum> &rect = in[i];
+
+ // Filter out empty or invalid rects.
+ if (rect.left >= rect.right || rect.top >= rect.bottom)
+ continue;
+
+ SweepEvent<TId, TNum> evt;
+ evt.rect_id = i;
+
+ evt.type = START;
+ evt.x = rect.left;
+ sweep_h_events.insert(evt);
+
+ evt.type = END;
+ evt.x = rect.right;
+ sweep_h_events.insert(evt);
+ }
+
+ for (typename std::set<SweepEvent<TId, TNum>>::iterator it =
+ sweep_h_events.begin();
+ it != sweep_h_events.end(); ++it) {
+ const SweepEvent<TId, TNum> &h_evt = *it;
+ const Rect<TNum> &rect = in[h_evt.rect_id];
+
+ // During this event, we have encountered a vertical starting or ending edge
+ // of a rectangle so want to append or remove (respectively) that rectangles
+ // top and bottom from the vertical sweep line.
+ SweepEvent<TId, TNum> v_evt;
+ v_evt.rect_id = h_evt.rect_id;
+ if (h_evt.type == START) {
+ v_evt.type = START;
+ v_evt.y = rect.top;
+ sweep_v_events.insert(v_evt);
+
+ v_evt.type = END;
+ v_evt.y = rect.bottom;
+ sweep_v_events.insert(v_evt);
+ } else {
+ v_evt.type = START;
+ v_evt.y = rect.top;
+ typename std::set<SweepEvent<TId, TNum>>::iterator start_it =
+ sweep_v_events.find(v_evt);
+ assert(start_it != sweep_v_events.end());
+ sweep_v_events.erase(start_it);
+
+ v_evt.type = END;
+ v_evt.y = rect.bottom;
+ typename std::set<SweepEvent<TId, TNum>>::iterator end_it =
+ sweep_v_events.find(v_evt);
+ assert(end_it != sweep_v_events.end());
+ sweep_v_events.erase(end_it);
+ }
+
+ // Peeks ahead to see if there are other rectangles sharing a vertical edge
+ // with the current sweep line. If so, we want to continue marking up the
+ // sweep line before actually processing the rectangles the sweep line is
+ // intersecting.
+ typename std::set<SweepEvent<TId, TNum>>::iterator next_it = it;
+ ++next_it;
+ if (next_it != sweep_h_events.end()) {
+ if (next_it->x == h_evt.x) {
+ continue;
+ }
+ }
+
+#ifdef RECTS_DEBUG
+ std::cout << h_evt.x << std::endl;
+#endif
+
+ // After the following for loop, active_regions will be a list of
+ // y-coordinates paired with the set of rectangle IDs that are intersect at
+ // that y-coordinate (and the current sweep line's x-coordinate). For
+ // example if the current sweep line were the left edge of a scene with only
+ // one rectangle of ID 0 and bounds (left, top, right, bottom) == (2, 3, 4,
+ // 5), active_regions will be [({ 0 }, 3), {}, 5].
+ active_regions.clear();
+ IdSet<TId> active_set;
+ for (typename std::set<SweepEvent<TId, TNum>>::iterator it =
+ sweep_v_events.begin();
+ it != sweep_v_events.end(); ++it) {
+ const SweepEvent<TId, TNum> &v_evt = *it;
+
+ if (v_evt.type == START) {
+ active_set.add(v_evt.rect_id);
+ } else {
+ active_set.subtract(v_evt.rect_id);
+ }
+
+ if (active_regions.size() > 0 && active_regions.back().first == v_evt.y) {
+ active_regions.back().second = active_set;
+ } else {
+ active_regions.push_back(std::make_pair(v_evt.y, active_set));
+ }
+ }
+
+#ifdef RECTS_DEBUG
+ std::cout << "x:" << h_evt.x;
+ for (std::vector<std::pair<TNum, IdSet>>::iterator it =
+ active_regions.begin();
+ it != active_regions.end(); ++it) {
+ std::cout << " " << it->first << "(" << it->second << ")"
+ << ",";
+ }
+ std::cout << std::endl;
+#endif
+
+ // To determine which started rectangles are ending this event, we make them
+ // all as false, or unseen during this sweep line.
+ for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it =
+ started_rects.begin();
+ it != started_rects.end(); ++it) {
+ it->second = false;
+ }
+
+ // This for loop will iterate all potential new rectangles and either
+ // discover it was already started (and then mark it true), or that it is a
+ // new rectangle and add it to the started rectangles. A started rectangle
+ // is unique if it has a distinct top, bottom, and set of rectangle IDs.
+ // This is tricky because a potential rectangle could be encountered here
+ // that has a non-unique top and bottom, so it shares geometry with an
+ // already started rectangle, but the set of rectangle IDs differs. In that
+ // case, we have a new rectangle, and the already existing started rectangle
+ // will not be marked as seen ("true" in the std::pair) and will get ended
+ // by the for loop after this one. This is as intended.
+ for (typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator it =
+ active_regions.begin();
+ it != active_regions.end(); ++it) {
+ IdSet<TId> region_set = it->second;
+
+ if (region_set.isEmpty())
+ continue;
+
+ // An important property of active_regions is that each region where a set
+ // of rectangles applies is bounded at the bottom by the next (in the
+ // vector) region's starting y-coordinate.
+ typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator next_it = it;
+ ++next_it;
+ assert(next_it != active_regions.end());
+
+ TNum region_top = it->first;
+ TNum region_bottom = next_it->first;
+
+ StartedRect<TId, TNum> rect_key;
+ rect_key.id_set = region_set;
+ rect_key.left = h_evt.x;
+ rect_key.top = region_top;
+ rect_key.bottom = region_bottom;
+
+ // Remember that rect_key.left is ignored for the purposes of searching
+ // the started rects. This follows from the fact that a previously started
+ // rectangle would by definition have a left bound less than the current
+ // event's x-coordinate. We are interested in continuing the started
+ // rectangles by marking them seen (true) but we don't know, care, or wish
+ // to change the left bound at this point. If there are no matching
+ // rectangles for this region, start a new one and mark it as seen (true).
+ typename std::map<StartedRect<TId, TNum>, bool>::iterator
+ started_rect_it = started_rects.find(rect_key);
+ if (started_rect_it == started_rects.end()) {
+ started_rects[rect_key] = true;
+ } else {
+ started_rect_it->second = true;
+ }
+ }
+
+ // This for loop ends all rectangles that were unseen during this event.
+ // Because this is the first event where we didn't see this rectangle, it's
+ // right edge is exactly the current event's x-coordinate. With this, we
+ // have the final piece of information to output this rectangle's geometry
+ // and set of input rectangle IDs. To end a started rectangle, we erase it
+ // from the started_rects map and append the completed rectangle to the
+ // output vector.
+ for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it =
+ started_rects.begin();
+ it != started_rects.end();
+ /* inc in body */) {
+ if (!it->second) {
+ const StartedRect<TId, TNum> &proto_rect = it->first;
+ Rect<TNum> out_rect;
+ out_rect.left = proto_rect.left;
+ out_rect.top = proto_rect.top;
+ out_rect.right = h_evt.x;
+ out_rect.bottom = proto_rect.bottom;
+ out->push_back(RectSet<TId, TNum>(proto_rect.id_set, out_rect));
+ started_rects.erase(it++); // Also increments out iterator.
+
+#ifdef RECTS_DEBUG
+ std::cout << " <" << proto_rect.id_set << "(" << rect << ")"
+ << std::endl;
+#endif
+ } else {
+ // Remember this for loop has no built in increment step. We do it here.
+ ++it;
+ }
+ }
+ }
+}
+
+void separate_frects_64(const std::vector<Rect<float>> &in,
+ std::vector<RectSet<uint64_t, float>> *out) {
+ separate_rects(in, out);
+}
+
+void separate_rects_64(const std::vector<Rect<int>> &in,
+ std::vector<RectSet<uint64_t, int>> *out) {
+ separate_rects(in, out);
+}
+
+} // namespace separate_rects
+
+#ifdef RECTS_TEST
+
+using namespace separate_rects;
+
+int main(int argc, char **argv) {
+#define RectSet RectSet<TId, TNum>
+#define Rect Rect<TNum>
+#define IdSet IdSet<TId>
+ typedef uint64_t TId;
+ typedef float TNum;
+
+ std::vector<Rect> in;
+ std::vector<RectSet> out;
+ std::vector<RectSet> expected_out;
+
+ in.push_back({0, 0, 4, 5});
+ in.push_back({2, 0, 6, 6});
+ in.push_back({4, 0, 8, 5});
+ in.push_back({0, 7, 8, 9});
+
+ in.push_back({10, 0, 18, 5});
+ in.push_back({12, 0, 16, 5});
+
+ in.push_back({20, 11, 24, 17});
+ in.push_back({22, 13, 26, 21});
+ in.push_back({32, 33, 36, 37});
+ in.push_back({30, 31, 38, 39});
+
+ in.push_back({40, 43, 48, 45});
+ in.push_back({44, 41, 46, 47});
+
+ in.push_back({50, 51, 52, 53});
+ in.push_back({50, 51, 52, 53});
+ in.push_back({50, 51, 52, 53});
+
+ in.push_back({0, 0, 0, 10});
+ in.push_back({0, 0, 10, 0});
+ in.push_back({10, 0, 0, 10});
+ in.push_back({0, 10, 10, 0});
+
+ for (int i = 0; i < 100000; i++) {
+ out.clear();
+ separate_rects(in, &out);
+ }
+
+ for (int i = 0; i < out.size(); i++) {
+ std::cout << out[i].id_set << "(" << out[i].rect << ")" << std::endl;
+ }
+
+ std::cout << "# of rects: " << out.size() << std::endl;
+
+ expected_out.push_back(RectSet(IdSet(0), Rect(0, 0, 2, 5)));
+ expected_out.push_back(RectSet(IdSet(1), Rect(2, 5, 6, 6)));
+ expected_out.push_back(RectSet(IdSet(1) | 0, Rect(2, 0, 4, 5)));
+ expected_out.push_back(RectSet(IdSet(1) | 2, Rect(4, 0, 6, 5)));
+ expected_out.push_back(RectSet(IdSet(2), Rect(6, 0, 8, 5)));
+ expected_out.push_back(RectSet(IdSet(3), Rect(0, 7, 8, 9)));
+ expected_out.push_back(RectSet(IdSet(4), Rect(10, 0, 12, 5)));
+ expected_out.push_back(RectSet(IdSet(5) | 4, Rect(12, 0, 16, 5)));
+ expected_out.push_back(RectSet(IdSet(4), Rect(16, 0, 18, 5)));
+ expected_out.push_back(RectSet(IdSet(6), Rect(20, 11, 22, 17)));
+ expected_out.push_back(RectSet(IdSet(6) | 7, Rect(22, 13, 24, 17)));
+ expected_out.push_back(RectSet(IdSet(6), Rect(22, 11, 24, 13)));
+ expected_out.push_back(RectSet(IdSet(7), Rect(22, 17, 24, 21)));
+ expected_out.push_back(RectSet(IdSet(7), Rect(24, 13, 26, 21)));
+ expected_out.push_back(RectSet(IdSet(9), Rect(30, 31, 32, 39)));
+ expected_out.push_back(RectSet(IdSet(8) | 9, Rect(32, 33, 36, 37)));
+ expected_out.push_back(RectSet(IdSet(9), Rect(32, 37, 36, 39)));
+ expected_out.push_back(RectSet(IdSet(9), Rect(32, 31, 36, 33)));
+ expected_out.push_back(RectSet(IdSet(9), Rect(36, 31, 38, 39)));
+ expected_out.push_back(RectSet(IdSet(10), Rect(40, 43, 44, 45)));
+ expected_out.push_back(RectSet(IdSet(10) | 11, Rect(44, 43, 46, 45)));
+ expected_out.push_back(RectSet(IdSet(11), Rect(44, 41, 46, 43)));
+ expected_out.push_back(RectSet(IdSet(11), Rect(44, 45, 46, 47)));
+ expected_out.push_back(RectSet(IdSet(10), Rect(46, 43, 48, 45)));
+ expected_out.push_back(RectSet(IdSet(12) | 13 | 14, Rect(50, 51, 52, 53)));
+
+ for (int i = 0; i < expected_out.size(); i++) {
+ RectSet &ex_out = expected_out[i];
+ if (std::find(out.begin(), out.end(), ex_out) == out.end()) {
+ std::cout << "Missing Rect: " << ex_out.id_set << "(" << ex_out.rect
+ << ")" << std::endl;
+ }
+ }
+
+ for (int i = 0; i < out.size(); i++) {
+ RectSet &actual_out = out[i];
+ if (std::find(expected_out.begin(), expected_out.end(), actual_out) ==
+ expected_out.end()) {
+ std::cout << "Extra Rect: " << actual_out.id_set << "(" << actual_out.rect
+ << ")" << std::endl;
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/separate_rects.h b/separate_rects.h
new file mode 100644
index 0000000..de8b660
--- /dev/null
+++ b/separate_rects.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DRM_HWCOMPOSER_SEPARATE_RECTS_H_
+#define DRM_HWCOMPOSER_SEPARATE_RECTS_H_
+
+#include <stdint.h>
+
+#include <sstream>
+#include <vector>
+
+namespace separate_rects {
+
+template <typename TFloat>
+struct Rect {
+ union {
+ struct {
+ TFloat left, top, right, bottom;
+ };
+ struct {
+ TFloat x1, y1, x2, y2;
+ };
+ TFloat bounds[4];
+ };
+
+ typedef TFloat TNum;
+
+ Rect() {
+ }
+
+ Rect(TFloat xx1, TFloat yy1, TFloat xx2, TFloat yy2)
+ : x1(xx1), y1(yy1), x2(xx2), y2(yy2) {
+ }
+
+ template <typename T>
+ Rect(const Rect<T> &rhs) {
+ for (int i = 0; i < 4; i++)
+ bounds[i] = rhs.bounds[i];
+ }
+
+ template <typename T>
+ Rect<TFloat> &operator=(const Rect<T> &rhs) {
+ for (int i = 0; i < 4; i++)
+ bounds[i] = rhs.bounds[i];
+ return *this;
+ }
+
+ bool operator==(const Rect &rhs) const {
+ for (int i = 0; i < 4; i++) {
+ if (bounds[i] != rhs.bounds[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ TFloat width() const {
+ return bounds[2] - bounds[0];
+ }
+
+ TFloat height() const {
+ return bounds[3] - bounds[1];
+ }
+
+ TFloat area() const {
+ return width() * height();
+ }
+
+ void Dump(std::ostringstream *out) const {
+ *out << "[x/y/w/h]=" << left << "/" << top << "/" << width() << "/"
+ << height();
+ }
+};
+
+template <typename TUInt>
+struct IdSet {
+ public:
+ typedef TUInt TId;
+
+ IdSet() : bitset(0) {
+ }
+
+ IdSet(TId id) : bitset(0) {
+ add(id);
+ }
+
+ void add(TId id) {
+ bitset |= ((TUInt)1) << id;
+ }
+
+ void subtract(TId id) {
+ bitset &= ~(((TUInt)1) << id);
+ }
+
+ bool isEmpty() const {
+ return bitset == 0;
+ }
+
+ TUInt getBits() const {
+ return bitset;
+ }
+
+ bool operator==(const IdSet<TId> &rhs) const {
+ return bitset == rhs.bitset;
+ }
+
+ bool operator<(const IdSet<TId> &rhs) const {
+ return bitset < rhs.bitset;
+ }
+
+ IdSet<TId> operator|(const IdSet<TId> &rhs) const {
+ IdSet ret;
+ ret.bitset = bitset | rhs.bitset;
+ return ret;
+ }
+
+ IdSet<TId> operator|(TId id) const {
+ IdSet<TId> ret;
+ ret.bitset = bitset;
+ ret.add(id);
+ return ret;
+ }
+
+ static const int max_elements = sizeof(TId) * 8;
+
+ private:
+ TUInt bitset;
+};
+
+template <typename TId, typename TNum>
+struct RectSet {
+ IdSet<TId> id_set;
+ Rect<TNum> rect;
+
+ RectSet(const IdSet<TId> &i, const Rect<TNum> &r) : id_set(i), rect(r) {
+ }
+
+ bool operator==(const RectSet<TId, TNum> &rhs) const {
+ return id_set == rhs.id_set && rect == rhs.rect;
+ }
+};
+
+// Separates up to a maximum of 64 input rectangles into mutually non-
+// overlapping rectangles that cover the exact same area and outputs those non-
+// overlapping rectangles. Each output rectangle also includes the set of input
+// rectangle indices that overlap the output rectangle encoded in a bitset. For
+// example, an output rectangle that overlaps input rectangles in[0], in[1], and
+// in[4], the bitset would be (ommitting leading zeroes) 10011.
+void separate_frects_64(const std::vector<Rect<float>> &in,
+ std::vector<RectSet<uint64_t, float>> *out);
+void separate_rects_64(const std::vector<Rect<int>> &in,
+ std::vector<RectSet<uint64_t, int>> *out);
+
+} // namespace separate_rects
+
+#endif
diff --git a/virtualcompositorworker.cpp b/virtualcompositorworker.cpp
new file mode 100755
index 0000000..d6413f2
--- /dev/null
+++ b/virtualcompositorworker.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-virtual-compositor-worker"
+
+#include "virtualcompositorworker.h"
+#include "worker.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <sched.h>
+#include <sw_sync.h>
+#include <sync/sync.h>
+
+namespace android {
+
+static const int kMaxQueueDepth = 3;
+static const int kAcquireWaitTimeoutMs = 3000;
+
+VirtualCompositorWorker::VirtualCompositorWorker()
+ : Worker("virtual-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+ timeline_fd_(-1),
+ timeline_(0),
+ timeline_current_(0) {
+}
+
+VirtualCompositorWorker::~VirtualCompositorWorker() {
+ if (timeline_fd_ >= 0) {
+ FinishComposition(timeline_);
+ close(timeline_fd_);
+ timeline_fd_ = -1;
+ }
+}
+
+int VirtualCompositorWorker::Init() {
+ int ret = sw_sync_timeline_create();
+ if (ret < 0) {
+ ALOGE("Failed to create sw sync timeline %d", ret);
+ return ret;
+ }
+ timeline_fd_ = ret;
+ return InitWorker();
+}
+
+void VirtualCompositorWorker::QueueComposite(hwc_display_contents_1_t *dc) {
+ std::unique_ptr<VirtualComposition> composition(new VirtualComposition);
+
+ composition->outbuf_acquire_fence.Set(dc->outbufAcquireFenceFd);
+ dc->outbufAcquireFenceFd = -1;
+ if (dc->retireFenceFd >= 0)
+ close(dc->retireFenceFd);
+ dc->retireFenceFd = CreateNextTimelineFence();
+
+ for (size_t i = 0; i < dc->numHwLayers; ++i) {
+ hwc_layer_1_t *layer = &dc->hwLayers[i];
+ if (layer->flags & HWC_SKIP_LAYER)
+ continue;
+ composition->layer_acquire_fences.emplace_back(layer->acquireFenceFd);
+ layer->acquireFenceFd = -1;
+ if (layer->releaseFenceFd >= 0)
+ close(layer->releaseFenceFd);
+ layer->releaseFenceFd = CreateNextTimelineFence();
+ }
+
+ composition->release_timeline = timeline_;
+
+ Lock();
+ while (composite_queue_.size() >= kMaxQueueDepth) {
+ Unlock();
+ sched_yield();
+ Lock();
+ }
+
+ composite_queue_.push(std::move(composition));
+ SignalLocked();
+ Unlock();
+}
+
+void VirtualCompositorWorker::Routine() {
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------VirtualCompositorWorker Routine start----------------------------");
+
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock worker, %d", ret);
+ return;
+ }
+
+ int wait_ret = 0;
+ if (composite_queue_.empty()) {
+ wait_ret = WaitForSignalOrExitLocked();
+ }
+
+ std::unique_ptr<VirtualComposition> composition;
+ if (!composite_queue_.empty()) {
+ composition = std::move(composite_queue_.front());
+ composite_queue_.pop();
+ }
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock worker, %d", ret);
+ return;
+ }
+
+ if (wait_ret == -EINTR) {
+ return;
+ } else if (wait_ret) {
+ ALOGE("Failed to wait for signal, %d", wait_ret);
+ return;
+ }
+
+ Compose(std::move(composition));
+
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------VirtualCompositorWorker Routine end----------------------------");
+}
+
+int VirtualCompositorWorker::CreateNextTimelineFence() {
+ ++timeline_;
+ return sw_sync_fence_create(timeline_fd_, "drm_fence", timeline_);
+}
+
+int VirtualCompositorWorker::FinishComposition(int point) {
+ int timeline_increase = point - timeline_current_;
+ if (timeline_increase <= 0)
+ return 0;
+ int ret = sw_sync_timeline_inc(timeline_fd_, timeline_increase);
+ if (ret)
+ ALOGE("Failed to increment sync timeline %d", ret);
+ else
+ timeline_current_ = point;
+ return ret;
+}
+
+void VirtualCompositorWorker::Compose(
+ std::unique_ptr<VirtualComposition> composition) {
+ if (!composition.get())
+ return;
+
+ int ret;
+ int outbuf_acquire_fence = composition->outbuf_acquire_fence.get();
+ if (outbuf_acquire_fence >= 0) {
+ ret = sync_wait(outbuf_acquire_fence, kAcquireWaitTimeoutMs);
+ if (ret) {
+ ALOGE("Failed to wait for outbuf acquire %d/%d", outbuf_acquire_fence,
+ ret);
+ return;
+ }
+ composition->outbuf_acquire_fence.Close();
+ }
+ for (size_t i = 0; i < composition->layer_acquire_fences.size(); ++i) {
+ int layer_acquire_fence = composition->layer_acquire_fences[i].get();
+ if (layer_acquire_fence >= 0) {
+ ret = sync_wait(layer_acquire_fence, kAcquireWaitTimeoutMs);
+ if (ret) {
+ ALOGE("Failed to wait for layer acquire %d/%d", layer_acquire_fence,
+ ret);
+ return;
+ }
+ composition->layer_acquire_fences[i].Close();
+ }
+ }
+ FinishComposition(composition->release_timeline);
+}
+}
diff --git a/virtualcompositorworker.h b/virtualcompositorworker.h
new file mode 100644
index 0000000..1fc5e43
--- /dev/null
+++ b/virtualcompositorworker.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VIRTUAL_COMPOSITOR_WORKER_H_
+#define ANDROID_VIRTUAL_COMPOSITOR_WORKER_H_
+
+#include "drmhwcomposer.h"
+#include "worker.h"
+
+#include <queue>
+
+namespace android {
+
+class VirtualCompositorWorker : public Worker {
+ public:
+ VirtualCompositorWorker();
+ ~VirtualCompositorWorker() override;
+
+ int Init();
+ void QueueComposite(hwc_display_contents_1_t *dc);
+
+ protected:
+ void Routine() override;
+
+ private:
+ struct VirtualComposition {
+ UniqueFd outbuf_acquire_fence;
+ std::vector<UniqueFd> layer_acquire_fences;
+ int release_timeline;
+ };
+
+ int CreateNextTimelineFence();
+ int FinishComposition(int timeline);
+ void Compose(std::unique_ptr<VirtualComposition> composition);
+
+ std::queue<std::unique_ptr<VirtualComposition>> composite_queue_;
+ int timeline_fd_;
+ int timeline_;
+ int timeline_current_;
+};
+}
+
+#endif
diff --git a/vsyncworker.cpp b/vsyncworker.cpp
new file mode 100755
index 0000000..a76f605
--- /dev/null
+++ b/vsyncworker.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-vsync-worker"
+
+#include "drmresources.h"
+#include "vsyncworker.h"
+#include "worker.h"
+
+#include <map>
+#include <stdlib.h>
+#include <time.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+
+namespace android {
+
+VSyncWorker::VSyncWorker()
+ : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY),
+ drm_(NULL),
+ procs_(NULL),
+ display_(-1),
+ last_timestamp_(-1) {
+}
+
+VSyncWorker::~VSyncWorker() {
+}
+
+int VSyncWorker::Init(DrmResources *drm, int display) {
+ drm_ = drm;
+ display_ = display;
+
+ return InitWorker();
+}
+
+int VSyncWorker::SetProcs(hwc_procs_t const *procs) {
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock vsync worker lock %d\n", ret);
+ return ret;
+ }
+
+ procs_ = procs;
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock vsync worker lock %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+int VSyncWorker::VSyncControl(bool enabled) {
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock vsync worker lock %d\n", ret);
+ return ret;
+ }
+
+ enabled_ = enabled;
+ last_timestamp_ = -1;
+ int signal_ret = SignalLocked();
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock vsync worker lock %d\n", ret);
+ return ret;
+ }
+
+ return signal_ret;
+}
+
+/*
+ * Returns the timestamp of the next vsync in phase with last_timestamp_.
+ * For example:
+ * last_timestamp_ = 137
+ * frame_ns = 50
+ * current = 683
+ *
+ * ret = (50 * ((683 - 137)/50 + 1)) + 137
+ * ret = 687
+ *
+ * Thus, we must sleep until timestamp 687 to maintain phase with the last
+ * timestamp.
+ */
+int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) {
+ if (last_timestamp_ < 0)
+ return current + frame_ns;
+
+ return frame_ns * ((current - last_timestamp_) / frame_ns + 1) +
+ last_timestamp_;
+}
+
+static const int64_t kOneSecondNs = 1 * 1000 * 1000 * 1000;
+
+int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) {
+ struct timespec vsync;
+ int ret = clock_gettime(CLOCK_MONOTONIC, &vsync);
+
+ float refresh = 60.0f; // Default to 60Hz refresh rate
+ DrmConnector *conn = drm_->GetConnectorFromType(display_);
+ if (conn && conn->state() == DRM_MODE_CONNECTED) {
+ if (conn->active_mode().v_refresh() > 0.0f)
+ refresh = conn->active_mode().v_refresh();
+ else
+ ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn,
+ conn->active_mode().v_refresh());
+ }
+
+ int64_t phased_timestamp = GetPhasedVSync(
+ kOneSecondNs / refresh, vsync.tv_sec * kOneSecondNs + vsync.tv_nsec);
+ vsync.tv_sec = phased_timestamp / kOneSecondNs;
+ vsync.tv_nsec = phased_timestamp - (vsync.tv_sec * kOneSecondNs);
+ do {
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, NULL);
+ } while (ret == -1 && errno == EINTR);
+ if (ret)
+ return ret;
+
+ *timestamp = (int64_t)vsync.tv_sec * kOneSecondNs + (int64_t)vsync.tv_nsec;
+ return 0;
+}
+
+void VSyncWorker::Routine() {
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------VSyncWorker Routine start----------------------------");
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock worker %d", ret);
+ return;
+ }
+
+ if (!enabled_) {
+ ret = WaitForSignalOrExitLocked();
+ if (ret == -EINTR) {
+ return;
+ }
+ }
+
+ bool enabled = enabled_;
+ int display = display_;
+ hwc_procs_t const *procs = procs_;
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock worker %d", ret);
+ }
+
+ if (!enabled)
+ return;
+
+ PRINT_TIME_START;
+ int64_t timestamp;
+ DrmConnector *conn = drm_->GetConnectorFromType(display);
+ if (!conn) {
+ ALOGE("Failed to get connector for display");
+ return;
+ }
+ DrmCrtc *crtc = NULL;
+ if (conn && conn->state() == DRM_MODE_CONNECTED) {
+ crtc = drm_->GetCrtcFromConnector(conn);
+ if (!crtc) {
+ ALOGE("Failed to get crtc for display");
+ return;
+ }
+ }
+
+ if (!crtc) {
+ ret = SyntheticWaitVBlank(&timestamp);
+ if (ret)
+ return;
+ } else {
+ uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT);
+
+ drmVBlank vblank;
+ memset(&vblank, 0, sizeof(vblank));
+ vblank.request.type = (drmVBlankSeqType)(
+ DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK));
+ vblank.request.sequence = 1;
+
+ ret = drmWaitVBlank(drm_->fd(), &vblank);
+ if (ret == -EINTR) {
+ return;
+ } else if (ret) {
+ ret = SyntheticWaitVBlank(&timestamp);
+ if (ret)
+ return;
+ } else {
+ timestamp = (int64_t)vblank.reply.tval_sec * kOneSecondNs +
+ (int64_t)vblank.reply.tval_usec * 1000;
+ }
+ }
+ /*
+ * There's a race here where a change in procs_ will not take effect until
+ * the next subsequent requested vsync. This is unavoidable since we can't
+ * call the vsync hook while holding the thread lock.
+ *
+ * We could shorten the race window by caching procs_ right before calling
+ * the hook. However, in practice, procs_ is only updated once, so it's not
+ * worth the overhead.
+ */
+ if (procs && procs->vsync)
+ procs->vsync(procs, display, timestamp);
+ last_timestamp_ = timestamp;
+ PRINT_TIME_END("vsync");
+
+ ALOGD_IF(log_level(DBG_INFO),"----------------------------VSyncWorker Routine end----------------------------");
+}
+}
diff --git a/vsyncworker.h b/vsyncworker.h
new file mode 100644
index 0000000..98ac546
--- /dev/null
+++ b/vsyncworker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EVENT_WORKER_H_
+#define ANDROID_EVENT_WORKER_H_
+
+#include "drmresources.h"
+#include "worker.h"
+
+#include <map>
+#include <stdint.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+class VSyncWorker : public Worker {
+ public:
+ VSyncWorker();
+ ~VSyncWorker() override;
+
+ int Init(DrmResources *drm, int display);
+ int SetProcs(hwc_procs_t const *procs);
+
+ int VSyncControl(bool enabled);
+
+ protected:
+ void Routine() override;
+
+ private:
+ int64_t GetPhasedVSync(int64_t frame_ns, int64_t current);
+ int SyntheticWaitVBlank(int64_t *timestamp);
+
+ DrmResources *drm_;
+ hwc_procs_t const *procs_;
+
+ int display_;
+ bool enabled_;
+ int64_t last_timestamp_;
+};
+}
+
+#endif
diff --git a/worker.cpp b/worker.cpp
new file mode 100644
index 0000000..6549eb8
--- /dev/null
+++ b/worker.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-drm-worker"
+
+#include "worker.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+#include <time.h>
+
+#include <cutils/log.h>
+
+namespace android {
+
+static const int64_t kBillion = 1000000000LL;
+
+Worker::Worker(const char *name, int priority)
+ : name_(name), priority_(priority), exit_(false), initialized_(false) {
+}
+
+Worker::~Worker() {
+ if (!initialized_)
+ return;
+
+ pthread_kill(thread_, SIGTERM);
+ pthread_cond_destroy(&cond_);
+ pthread_mutex_destroy(&lock_);
+}
+
+int Worker::InitWorker() {
+ pthread_condattr_t cond_attr;
+ pthread_condattr_init(&cond_attr);
+ pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
+ int ret = pthread_cond_init(&cond_, &cond_attr);
+ if (ret) {
+ ALOGE("Failed to int thread %s condition %d", name_.c_str(), ret);
+ return ret;
+ }
+
+ ret = pthread_mutex_init(&lock_, NULL);
+ if (ret) {
+ ALOGE("Failed to init thread %s lock %d", name_.c_str(), ret);
+ pthread_cond_destroy(&cond_);
+ return ret;
+ }
+
+ ret = pthread_create(&thread_, NULL, InternalRoutine, this);
+ if (ret) {
+ ALOGE("Could not create thread %s %d", name_.c_str(), ret);
+ pthread_mutex_destroy(&lock_);
+ pthread_cond_destroy(&cond_);
+ return ret;
+ }
+ initialized_ = true;
+ return 0;
+}
+
+bool Worker::initialized() const {
+ return initialized_;
+}
+
+
+pthread_mutex_t* Worker::getLock() {
+ return &lock_;
+}
+
+int Worker::Lock() {
+ return pthread_mutex_lock(&lock_);
+}
+
+int Worker::Unlock() {
+ return pthread_mutex_unlock(&lock_);
+}
+
+int Worker::SignalLocked() {
+ return SignalThreadLocked(false);
+}
+
+int Worker::ExitLocked() {
+ int signal_ret = SignalThreadLocked(true);
+ if (signal_ret)
+ ALOGE("Failed to signal thread %s with exit %d", name_.c_str(), signal_ret);
+
+ int join_ret = pthread_join(thread_, NULL);
+ if (join_ret && join_ret != ESRCH)
+ ALOGE("Failed to join thread %s in exit %d", name_.c_str(), join_ret);
+
+ return signal_ret | join_ret;
+}
+
+int Worker::Signal() {
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to acquire lock in Signal() %d\n", ret);
+ return ret;
+ }
+
+ int signal_ret = SignalLocked();
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to release lock in Signal() %d\n", ret);
+ return ret;
+ }
+ return signal_ret;
+}
+
+int Worker::Exit() {
+ int ret = Lock();
+ if (ret) {
+ ALOGE("Failed to acquire lock in Exit() %d\n", ret);
+ return ret;
+ }
+
+ int exit_ret = ExitLocked();
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to release lock in Exit() %d\n", ret);
+ return ret;
+ }
+ return exit_ret;
+}
+
+int Worker::WaitForSignalOrExitLocked(int64_t max_nanoseconds) {
+ if (exit_)
+ return -EINTR;
+
+ int ret = 0;
+ if (max_nanoseconds < 0) {
+ ret = pthread_cond_wait(&cond_, &lock_);
+ } else {
+ struct timespec abs_deadline;
+ ret = clock_gettime(CLOCK_MONOTONIC, &abs_deadline);
+ if (ret)
+ return ret;
+ int64_t nanos = (int64_t)abs_deadline.tv_nsec + max_nanoseconds;
+ abs_deadline.tv_sec += nanos / kBillion;
+ abs_deadline.tv_nsec = nanos % kBillion;
+ ret = pthread_cond_timedwait(&cond_, &lock_, &abs_deadline);
+ if (ret == ETIMEDOUT)
+ ret = -ETIMEDOUT;
+ }
+
+ if (exit_)
+ return -EINTR;
+
+ return ret;
+}
+
+// static
+void *Worker::InternalRoutine(void *arg) {
+ Worker *worker = (Worker *)arg;
+
+ setpriority(PRIO_PROCESS, 0, worker->priority_);
+
+ while (true) {
+ int ret = worker->Lock();
+ if (ret) {
+ ALOGE("Failed to lock %s thread %d", worker->name_.c_str(), ret);
+ continue;
+ }
+
+ bool exit = worker->exit_;
+
+ ret = worker->Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock %s thread %d", worker->name_.c_str(), ret);
+ break;
+ }
+ if (exit)
+ break;
+
+ worker->Routine();
+ }
+ return NULL;
+}
+
+int Worker::SignalThreadLocked(bool exit) {
+ if (exit)
+ exit_ = exit;
+
+ int ret = pthread_cond_signal(&cond_);
+ if (ret) {
+ ALOGE("Failed to signal condition on %s thread %d", name_.c_str(), ret);
+ return ret;
+ }
+
+ return 0;
+}
+}
diff --git a/worker.h b/worker.h
new file mode 100644
index 0000000..38502fa
--- /dev/null
+++ b/worker.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WORKER_H_
+#define ANDROID_WORKER_H_
+
+#include <pthread.h>
+#include <stdint.h>
+#include <string>
+
+namespace android {
+
+class Worker {
+ public:
+ pthread_mutex_t* getLock();
+ int Lock();
+ int Unlock();
+
+ // Must be called with the lock acquired
+ int SignalLocked();
+ int ExitLocked();
+
+ // Convenience versions of above, acquires the lock
+ int Signal();
+ int Exit();
+
+ protected:
+ Worker(const char *name, int priority);
+ virtual ~Worker();
+
+ int InitWorker();
+
+ bool initialized() const;
+
+ virtual void Routine() = 0;
+
+ /*
+ * Must be called with the lock acquired. max_nanoseconds may be negative to
+ * indicate infinite timeout, otherwise it indicates the maximum time span to
+ * wait for a signal before returning.
+ * Returns -EINTR if interrupted by exit request, or -ETIMEDOUT if timed out
+ */
+ int WaitForSignalOrExitLocked(int64_t max_nanoseconds = -1);
+
+ private:
+ static void *InternalRoutine(void *worker);
+
+ // Must be called with the lock acquired
+ int SignalThreadLocked(bool exit);
+
+ std::string name_;
+ int priority_;
+
+ pthread_t thread_;
+ pthread_mutex_t lock_;
+ pthread_cond_t cond_;
+
+ bool exit_;
+ bool initialized_;
+};
+}
+
+#endif