Merge "hwc: Add assertive display support."
diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk
index 4b9d04b..87a7815 100644
--- a/libhwcomposer/Android.mk
+++ b/libhwcomposer/Android.mk
@@ -22,6 +22,7 @@
hwc_mdpcomp.cpp \
hwc_copybit.cpp \
hwc_qclient.cpp \
- hwc_dump_layers.cpp
+ hwc_dump_layers.cpp \
+ hwc_ad.cpp
include $(BUILD_SHARED_LIBRARY)
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 6dd77b1..09fef80 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -36,6 +36,7 @@
#include "hwc_dump_layers.h"
#include "external.h"
#include "hwc_copybit.h"
+#include "hwc_ad.h"
#include "profiler.h"
using namespace qhwc;
@@ -107,6 +108,8 @@
if(ctx->mCopyBit[i])
ctx->mCopyBit[i]->reset();
}
+
+ ctx->mAD->reset();
}
//clear prev layer prop flags and realloc for current frame
diff --git a/libhwcomposer/hwc_ad.cpp b/libhwcomposer/hwc_ad.cpp
new file mode 100644
index 0000000..ef5980c
--- /dev/null
+++ b/libhwcomposer/hwc_ad.cpp
@@ -0,0 +1,261 @@
+/*
+* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation. nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <unistd.h>
+#include <overlay.h>
+#include <overlayUtils.h>
+#include <overlayWriteback.h>
+#include <mdp_version.h>
+#include "hwc_ad.h"
+#include "hwc_utils.h"
+#include "external.h"
+
+#define DEBUG 0
+using namespace overlay;
+using namespace overlay::utils;
+namespace qhwc {
+
+//Opens writeback framebuffer and returns fd.
+static int openWbFb() {
+ int wbFd = -1;
+ //Check opening which FB would connect LM to WB
+ const int wbFbNum = Overlay::getFbForDpy(Overlay::DPY_WRITEBACK);
+ if(wbFbNum >= 0) {
+ char wbFbPath[256];
+ snprintf (wbFbPath, sizeof(wbFbPath),
+ "/sys/class/graphics/fb%d", wbFbNum);
+ //Opening writeback fb first time would create ad node if the device
+ //supports adaptive display
+ wbFd = open(wbFbPath, O_RDONLY);
+ if(wbFd < 0) {
+ ALOGE("%s: Failed to open /sys/class/graphics/fb%d with error %s",
+ __func__, wbFbNum, strerror(errno));
+ }
+ } else {
+ ALOGD_IF(DEBUG, "%s: No writeback available", __func__);
+ }
+ return wbFd;
+}
+
+static inline void closeWbFb(int& fd) {
+ if(fd >= 0) {
+ close(fd);
+ fd = -1;
+ } else {
+ ALOGE("%s: Invalid fd %d", __func__, fd);
+ }
+}
+
+//Helper to write data to ad node
+static void adWrite(const int& value) {
+ const int wbFbNum = Overlay::getFbForDpy(Overlay::DPY_WRITEBACK);
+ char wbFbPath[256];
+ snprintf (wbFbPath, sizeof(wbFbPath),
+ "/sys/class/graphics/fb%d/ad", wbFbNum);
+ int adFd = open(wbFbPath, O_WRONLY);
+ if(adFd >= 0) {
+ char opStr[4] = "";
+ snprintf(opStr, sizeof(opStr), "%d", value);
+ int ret = write(adFd, opStr, strlen(opStr));
+ if(ret < 0) {
+ ALOGE("%s: Failed to write %d with error %s",
+ __func__, value, strerror(errno));
+ } else if (ret == 0){
+ ALOGE("%s Nothing written to ad", __func__);
+ } else {
+ ALOGD_IF(DEBUG, "%s: Wrote %d to ad", __func__, value);
+ }
+ close(adFd);
+ } else {
+ ALOGE("%s: Failed to open /sys/class/graphics/fb%d/ad with error %s",
+ __func__, wbFbNum, strerror(errno));
+ }
+}
+
+//Helper to read data from ad node
+static int adRead() {
+ const int wbFbNum = Overlay::getFbForDpy(Overlay::DPY_WRITEBACK);
+ int ret = -1;
+ char wbFbPath[256];
+ snprintf (wbFbPath, sizeof(wbFbPath),
+ "/sys/class/graphics/fb%d/ad", wbFbNum);
+ int adFd = open(wbFbPath, O_RDONLY);
+ if(adFd >= 0) {
+ char opStr[4] = {'\0'};
+ if(read(adFd, opStr, strlen(opStr)) >= 0) {
+ //Should return -1, 0 or 1
+ ret = atoi(opStr);
+ ALOGD_IF(DEBUG, "%s: Read %d from ad", __func__, ret);
+ } else {
+ ALOGE("%s: Read from ad node failed with error %s", __func__,
+ strerror(errno));
+ }
+ close(adFd);
+ } else {
+ ALOGE("%s: Failed to open /sys/class/graphics/fb%d/ad with error %s",
+ __func__, wbFbNum, strerror(errno));
+ }
+ return ret;
+}
+
+AssertiveDisplay::AssertiveDisplay() :mWbFd(-1), mDoable(false),
+ mFeatureEnabled(false), mDest(overlay::utils::OV_INVALID) {
+ int fd = openWbFb();
+ if(fd >= 0) {
+ //-1 means feature is disabled on device
+ // 0 means feature exists but turned off, will be turned on by hwc
+ // 1 means feature is turned on by hwc
+ if(adRead() >= 0) {
+ ALOGD_IF(DEBUG, "Assertive display feature supported");
+ mFeatureEnabled = true;
+ }
+ closeWbFb(fd);
+ }
+}
+
+void AssertiveDisplay::markDoable(hwc_context_t *ctx,
+ const hwc_display_contents_1_t* list) {
+ mDoable = false;
+ if(mFeatureEnabled &&
+ !ctx->mExtDisplay->isExternalConnected() &&
+ ctx->listStats[HWC_DISPLAY_PRIMARY].yuvCount == 1) {
+ int nYuvIndex = ctx->listStats[HWC_DISPLAY_PRIMARY].yuvIndices[0];
+ const hwc_layer_1_t* layer = &list->hwLayers[nYuvIndex];
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ if(hnd && hnd->width <= qdutils::MAX_DISPLAY_DIM) {
+ mDoable = true;
+ }
+ }
+}
+
+bool AssertiveDisplay::prepare(hwc_context_t *ctx,
+ const hwc_rect_t& crop,
+ const Whf& whf,
+ const private_handle_t *hnd) {
+ if(!isDoable()) {
+ if(isModeOn()) {
+ //Cleanup one time during this switch
+ const int off = 0;
+ adWrite(off);
+ closeWbFb(mWbFd);
+ }
+ return false;
+ }
+
+ ovutils::eDest dest = ctx->mOverlay->nextPipe(ovutils::OV_MDP_PIPE_VG,
+ overlay::Overlay::DPY_WRITEBACK, Overlay::MIXER_DEFAULT);
+ if(dest == OV_INVALID) {
+ ALOGE("%s failed: No VG pipe available", __func__);
+ mDoable = false;
+ return false;
+ }
+
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+
+ if(!wb->configureDpyInfo(hnd->width, hnd->height)) {
+ ALOGE("%s: config display failed", __func__);
+ mDoable = false;
+ return false;
+ }
+
+ int tmpW, tmpH, size;
+ int format = ovutils::getHALFormat(wb->getOutputFormat());
+ if(format < 0) {
+ ALOGE("%s invalid format %d", __func__, format);
+ mDoable = false;
+ return false;
+ }
+
+ size = getBufferSizeAndDimensions(hnd->width, hnd->height,
+ format, tmpW, tmpH);
+
+ if(!wb->configureMemory(size, isSecureBuffer(hnd))) {
+ ALOGE("%s: config memory failed", __func__);
+ mDoable = false;
+ return false;
+ }
+
+ eMdpFlags mdpFlags = OV_MDP_FLAGS_NONE;
+ if(isSecureBuffer(hnd)) {
+ ovutils::setMdpFlags(mdpFlags,
+ ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
+ }
+
+ PipeArgs parg(mdpFlags, whf, ZORDER_0, IS_FG_OFF,
+ ROT_FLAGS_NONE);
+ hwc_rect_t dst = crop; //input same as output
+
+ if(configMdp(ctx->mOverlay, parg, OVERLAY_TRANSFORM_0, crop, dst, NULL,
+ dest) < 0) {
+ ALOGE("%s: configMdp failed", __func__);
+ mDoable = false;
+ return false;
+ }
+
+ mDest = dest;
+ if(!isModeOn()) {
+ mWbFd = openWbFb();
+ if(mWbFd >= 0) {
+ //write to sysfs, one time during this switch
+ const int on = 1;
+ adWrite(on);
+ }
+ }
+ return true;
+}
+
+bool AssertiveDisplay::draw(hwc_context_t *ctx, int fd, uint32_t offset) {
+ if(!isDoable() || !isModeOn()) {
+ return false;
+ }
+
+ if (!ctx->mOverlay->queueBuffer(fd, offset, mDest)) {
+ ALOGE("%s: queueBuffer failed", __func__);
+ return false;
+ }
+
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+ if(!wb->writeSync()) {
+ return false;
+ }
+
+ return true;
+}
+
+int AssertiveDisplay::getDstFd(hwc_context_t *ctx) const {
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+ return wb->getDstFd();
+}
+
+uint32_t AssertiveDisplay::getDstOffset(hwc_context_t *ctx) const {
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+ return wb->getOffset();
+}
+
+}
diff --git a/libhwcomposer/hwc_ad.h b/libhwcomposer/hwc_ad.h
new file mode 100644
index 0000000..38b724d
--- /dev/null
+++ b/libhwcomposer/hwc_ad.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation. nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef HWC_AD_H
+#define HWC_AD_H
+
+#include <overlayUtils.h>
+#include <hwc_utils.h>
+
+struct hwc_context_t;
+
+namespace qhwc {
+
+class AssertiveDisplay {
+public:
+ AssertiveDisplay();
+ void markDoable(hwc_context_t *ctx, const hwc_display_contents_1_t* list);
+ bool prepare(hwc_context_t *ctx, const hwc_rect_t& crop,
+ const overlay::utils::Whf& whf,
+ const private_handle_t *hnd);
+ bool draw(hwc_context_t *ctx, int fd, uint32_t offset);
+ //Resets a few members on each draw round
+ void reset() { mDoable = false;
+ mDest = overlay::utils::OV_INVALID;
+ }
+ bool isDoable() const { return mDoable; }
+ bool isModeOn() const { return (mWbFd >= 0); }
+ int getDstFd(hwc_context_t *ctx) const;
+ uint32_t getDstOffset(hwc_context_t *ctx) const;
+
+private:
+ //State of feature turned on and off
+ int mWbFd;
+ bool mDoable;
+ //State of feature existence on certain devices and configs.
+ bool mFeatureEnabled;
+ overlay::utils::eDest mDest;
+};
+
+}
+#endif
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 69e81db..31b9645 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -23,6 +23,7 @@
#include "qdMetaData.h"
#include "mdp_version.h"
#include "hwc_fbupdate.h"
+#include "hwc_ad.h"
#include <overlayRotator.h>
using namespace overlay;
@@ -453,6 +454,10 @@
return false;
}
+ if(ctx->mAD->isDoable()) {
+ return false;
+ }
+
//If all above hard conditions are met we can do full or partial MDP comp.
bool ret = false;
if(fullMDPComp(ctx, list)) {
@@ -955,6 +960,14 @@
int fd = hnd->fd;
uint32_t offset = hnd->offset;
+
+ if(ctx->mAD->isModeOn()) {
+ if(ctx->mAD->draw(ctx, fd, offset)) {
+ fd = ctx->mAD->getDstFd(ctx);
+ offset = ctx->mAD->getDstOffset(ctx);
+ }
+ }
+
Rotator *rot = mCurrentFrame.mdpToLayer[mdpIndex].rot;
if(rot) {
if(!rot->queueBuffer(fd, offset))
@@ -1150,6 +1163,13 @@
int fd = hnd->fd;
int offset = hnd->offset;
+ if(ctx->mAD->isModeOn()) {
+ if(ctx->mAD->draw(ctx, fd, offset)) {
+ fd = ctx->mAD->getDstFd(ctx);
+ offset = ctx->mAD->getDstOffset(ctx);
+ }
+ }
+
if(rot) {
rot->queueBuffer(fd, offset);
fd = rot->getDstMemId();
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 1c881cb..2c02e5b 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -27,9 +27,11 @@
#include <gralloc_priv.h>
#include <overlay.h>
#include <overlayRotator.h>
+#include <overlayWriteback.h>
#include "hwc_utils.h"
#include "hwc_mdpcomp.h"
#include "hwc_fbupdate.h"
+#include "hwc_ad.h"
#include "mdp_version.h"
#include "hwc_copybit.h"
#include "hwc_dump_layers.h"
@@ -161,6 +163,7 @@
}
MDPComp::init(ctx);
+ ctx->mAD = new AssertiveDisplay();
ctx->vstate.enable = false;
ctx->vstate.fakevsync = false;
@@ -234,6 +237,10 @@
ctx->mLayerRotMap[i] = NULL;
}
}
+ if(ctx->mAD) {
+ delete ctx->mAD;
+ ctx->mAD = NULL;
+ }
}
@@ -475,6 +482,9 @@
if(prevYuvCount != ctx->listStats[dpy].yuvCount) {
ctx->mVideoTransFlag = true;
}
+ if(dpy == HWC_DISPLAY_PRIMARY) {
+ ctx->mAD->markDoable(ctx, list);
+ }
}
@@ -991,6 +1001,13 @@
setMdpFlags(layer, mdpFlags, downscale, transform);
trimLayer(ctx, dpy, transform, crop, dst);
+ //Will do something only if feature enabled and conditions suitable
+ //hollow call otherwise
+ if(ctx->mAD->prepare(ctx, crop, whf, hnd)) {
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+ whf.format = wb->getOutputFormat();
+ }
+
if(isYuvBuffer(hnd) && //if 90 component or downscale, use rot
((transform & HWC_TRANSFORM_ROT_90) || downscale)) {
*rot = ctx->mRotMgr->getNext();
@@ -1069,6 +1086,13 @@
setMdpFlags(layer, mdpFlagsL, 0, transform);
trimLayer(ctx, dpy, transform, crop, dst);
+ //Will do something only if feature enabled and conditions suitable
+ //hollow call otherwise
+ if(ctx->mAD->prepare(ctx, crop, whf, hnd)) {
+ overlay::Writeback *wb = overlay::Writeback::getInstance();
+ whf.format = wb->getOutputFormat();
+ }
+
if(isYuvBuffer(hnd) && (transform & HWC_TRANSFORM_ROT_90)) {
(*rot) = ctx->mRotMgr->getNext();
if((*rot) == NULL) return -1;
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index e214aee..c4fd345 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -55,6 +55,7 @@
class MDPComp;
class CopyBit;
class HwcDebug;
+class AssertiveDisplay;
struct MDPInfo {
@@ -321,6 +322,7 @@
qhwc::LayerProp *layerProp[HWC_NUM_DISPLAY_TYPES];
qhwc::MDPComp *mMDPComp[HWC_NUM_DISPLAY_TYPES];
qhwc::HwcDebug *mHwcDebug[HWC_NUM_DISPLAY_TYPES];
+ qhwc::AssertiveDisplay *mAD;
// No animation on External display feature
// Notifies hwcomposer about the device orientation before animation.
diff --git a/liboverlay/overlayWriteback.cpp b/liboverlay/overlayWriteback.cpp
index b6ed53c..f711ca6 100644
--- a/liboverlay/overlayWriteback.cpp
+++ b/liboverlay/overlayWriteback.cpp
@@ -73,12 +73,13 @@
}
//=========== class Writeback =================================================
-Writeback::Writeback() : mXres(0), mYres(0) {
+Writeback::Writeback() : mXres(0), mYres(0), mOpFmt(-1) {
int fbNum = Overlay::getFbForDpy(Overlay::DPY_WRITEBACK);
if(!utils::openDev(mFd, fbNum, Res::fbPath, O_RDWR)) {
ALOGE("%s failed to init %s", __func__, Res::fbPath);
return;
}
+ queryOutputFormat();
startSession();
}
@@ -184,6 +185,17 @@
return writeSync(mWbMem.getDstFd(), mWbMem.getOffset());
}
+void Writeback::queryOutputFormat() {
+ struct msmfb_metadata metadata;
+ memset(&metadata, 0 , sizeof(metadata));
+ metadata.op = metadata_op_wb_format;
+ if (ioctl(mFd.getFD(), MSMFB_METADATA_GET, &metadata) < 0) {
+ ALOGE("Error retrieving MDP Writeback format");
+ return;
+ }
+ mOpFmt = metadata.data.mixer_cfg.writeback_format;
+}
+
//static
Writeback *Writeback::getInstance() {
diff --git a/liboverlay/overlayWriteback.h b/liboverlay/overlayWriteback.h
index fbfd889..6280391 100644
--- a/liboverlay/overlayWriteback.h
+++ b/liboverlay/overlayWriteback.h
@@ -81,6 +81,7 @@
bool queueBuffer(int opFd, uint32_t opOffset);
uint32_t getOffset() const { return mWbMem.getOffset(); }
int getDstFd() const { return mWbMem.getDstFd(); }
+ int getOutputFormat() const { return mOpFmt; }
static Writeback* getInstance();
static void configBegin() { sUsed = false; }
@@ -93,11 +94,13 @@
bool stopSession();
//Actually block_until_write_done for the usage here.
bool dequeueBuffer();
+ void queryOutputFormat();
OvFD mFd;
WritebackMem mWbMem;
struct msmfb_data mFbData;
int mXres;
int mYres;
+ int mOpFmt;
static bool sUsed;
static Writeback *sWb;