hwc: enable vsync for external display
- wait for hdmi vsync when connected to hdmi
- add commit to call PANDISPLAY for updating ext display
- add functions to close fb and reset info
Change-Id: I49afbeb85935b111055a872b29a1f65d87ab72c9
Acked-by: Arun Kumar K.R <akumarkr@codeaurora.org>
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index c0bca9d..ba87f90 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -29,6 +29,7 @@
#include "hwc_video.h"
#include "hwc_uimirror.h"
#include "hwc_copybit.h"
+#include "hwc_external.h"
using namespace qhwc;
@@ -108,6 +109,10 @@
case HWC_EVENT_VSYNC:
if(ioctl(m->framebuffer->fd, MSMFB_OVERLAY_VSYNC_CTRL, &enabled) < 0)
ret = -errno;
+
+ if(ctx->mExtDisplay->getExternalDisplay()) {
+ ret = ctx->mExtDisplay->enableHDMIVsync(enabled);
+ }
break;
default:
ret = -EINVAL;
@@ -150,6 +155,8 @@
CopyBit::draw(ctx, list, (EGLDisplay)dpy, (EGLSurface)sur);
EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur);
UIMirrorOverlay::draw(ctx);
+ if(ctx->mExtDisplay->getExternalDisplay())
+ ctx->mExtDisplay->commit();
} else {
ctx->mOverlay->setState(ovutils::OV_CLOSED);
ctx->qbuf->unlockAllPrevious();
diff --git a/libhwcomposer/hwc_external.cpp b/libhwcomposer/hwc_external.cpp
index 3507134..b5e1e89 100644
--- a/libhwcomposer/hwc_external.cpp
+++ b/libhwcomposer/hwc_external.cpp
@@ -42,22 +42,21 @@
#define DEVICE_ROOT "/sys/devices/virtual/graphics"
#define DEVICE_NODE "fb1"
-#define SYSFS_CONNECTED DEVICE_ROOT "/" DEVICE_NODE "/connected"
#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd"
-ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):fd(-1),
- mCurrentID(-1), mHwcContext(ctx)
+ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
+ mCurrentMode(-1), mHwcContext(ctx)
{
+ memset(&mVInfo, 0, sizeof(mVInfo));
//Enable HPD for HDMI
writeHPDOption(1);
}
ExternalDisplay::~ExternalDisplay()
{
- if (fd > 0)
- close(fd);
+ closeFrameBuffer();
}
struct disp_mode_timing_type {
@@ -143,7 +142,7 @@
{m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false},
};
-int ExternalDisplay::parseResolution(char* edidStr, int* edidModes, int len)
+int ExternalDisplay::parseResolution(char* edidStr, int* edidModes)
{
char delim = ',';
int count = 0;
@@ -152,27 +151,25 @@
// Ex: 16,4,5,3,32,34,1
// Parse this string to get mode(int)
start = (char*) edidStr;
- for(int i=0; i<len; i++) {
- edidModes[i] = (int) strtol(start, &end, 10);
- if(*end != delim) {
- // return as we reached end of string
- return count;
- }
+ end = &delim;
+ while(*end == delim) {
+ edidModes[count] = (int) strtol(start, &end, 10);
start = end+1;
count++;
}
+ ALOGD_IF(DEBUG, "In %s: count = %d", __FUNCTION__, count);
+ for (int i = 0; i < count; i++)
+ ALOGD_IF(DEBUG, "Mode[%d] = %d", i, edidModes[i]);
return count;
}
+
bool ExternalDisplay::readResolution()
{
int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
int len = -1;
- memset(mEDIDs, 0, sizeof(mEDIDs));
- memset(mEDIDModes, 0, sizeof(mEDIDModes));
- mModeCount = 0;
if (hdmiEDIDFile < 0) {
- ALOGD_IF(DEBUG, "%s: edid_modes file '%s' not found",
+ ALOGE("%s: edid_modes file '%s' not found",
__FUNCTION__, SYSFS_EDID_MODES);
return false;
} else {
@@ -180,7 +177,7 @@
ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
__FUNCTION__, mEDIDs, len);
if ( len <= 0) {
- ALOGD_IF(DEBUG, "%s: edid_modes file empty '%s'",
+ ALOGE("%s: edid_modes file empty '%s'",
__FUNCTION__, SYSFS_EDID_MODES);
}
else {
@@ -192,7 +189,7 @@
close(hdmiEDIDFile);
if(len > 0) {
// GEt EDID modes from the EDID strings
- mModeCount = parseResolution(mEDIDs, mEDIDModes, len);
+ mModeCount = parseResolution(mEDIDs, mEDIDModes);
ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
mModeCount);
}
@@ -202,15 +199,33 @@
bool ExternalDisplay::openFramebuffer()
{
- if (fd == -1) {
- fd = open("/dev/graphics/fb1", O_RDWR);
- if (fd < 0)
- ALOGD_IF(DEBUG, "%s: /dev/graphics/fb1 not available"
- "\n", __FUNCTION__);
+ if (mFd == -1) {
+ mFd = open("/dev/graphics/fb1", O_RDWR);
+ if (mFd < 0)
+ ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
}
- return (fd > 0);
+ return (mFd > 0);
}
+bool ExternalDisplay::closeFrameBuffer()
+{
+ int ret = 0;
+ if(mFd > 0) {
+ ret = close(mFd);
+ mFd = -1;
+ }
+ return (ret == 0);
+}
+
+// clears the vinfo, edid, best modes
+void ExternalDisplay::resetInfo()
+{
+ memset(&mVInfo, 0, sizeof(mVInfo));
+ memset(mEDIDs, 0, sizeof(mEDIDs));
+ memset(mEDIDModes, 0, sizeof(mEDIDModes));
+ mModeCount = 0;
+ mCurrentMode = -1;
+}
int ExternalDisplay::getModeOrder(int mode)
{
@@ -278,10 +293,23 @@
void ExternalDisplay::setResolution(int ID)
{
struct fb_var_screeninfo info;
+ int ret = 0;
if (!openFramebuffer())
return;
+ ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+ strerror(errno));
+ }
+
+ ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
+ "(%d,%d,%d) %dMHz>", __FUNCTION__,
+ mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
+ mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
+ mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
+ mVInfo.pixclock/1000/1000);
//If its a valid mode and its a new ID - update var_screeninfo
- if ((isValidMode(ID)) && mCurrentID != ID) {
+ if ((isValidMode(ID)) && mCurrentMode != ID) {
const struct disp_mode_timing_type *mode =
&supported_video_mode_lut[0];
unsigned count = sizeof(supported_video_mode_lut)/sizeof
@@ -292,30 +320,38 @@
if (cur->video_format == ID)
mode = cur;
}
- ioctl(fd, FBIOGET_VSCREENINFO, &info);
- ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
- "(%d,%d,%d) %dMHz>", __FUNCTION__,
- info.reserved[3], info.xres, info.yres,
- info.right_margin, info.hsync_len, info.left_margin,
- info.lower_margin, info.vsync_len, info.upper_margin,
- info.pixclock/1000/1000);
- mode->set_info(info);
- ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx%d"
+ mode->set_info(mVInfo);
+ ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
"(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
- info.reserved[3], info.xres, info.yres,
- info.right_margin, info.hsync_len, info.left_margin,
- info.lower_margin, info.vsync_len, info.upper_margin,
- info.pixclock/1000/1000);
- info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
- ioctl(fd, FBIOPUT_VSCREENINFO, &info);
- mCurrentID = ID;
+ mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
+ mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
+ mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
+ mVInfo.pixclock/1000/1000);
+ mVInfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
+ ret = ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOPUT_VSCREENINFO failed Err Str = %s",
+ __FUNCTION__, strerror(errno));
+ }
+ mCurrentMode = ID;
}
//Powerup
- ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK);
- ioctl(fd, FBIOGET_VSCREENINFO, &info);
+ ret = ioctl(mFd, FBIOBLANK, FB_BLANK_UNBLANK);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOBLANK failed Err Str = %s", __FUNCTION__,
+ strerror(errno));
+ }
+ ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+ strerror(errno));
+ }
//Pan_Display
- ioctl(fd, FBIOPAN_DISPLAY, &info);
- property_set("hw.hdmiON", "1");
+ ret = ioctl(mFd, FBIOPAN_DISPLAY, &mVInfo);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOPAN_DISPLAY failed Err Str = %s", __FUNCTION__,
+ strerror(errno));
+ }
}
@@ -324,7 +360,7 @@
return mExternalDisplay;
}
-void ExternalDisplay::setExternalDisplayStatus(int connected)
+void ExternalDisplay::setExternalDisplay(int connected)
{
hwc_context_t* ctx = mHwcContext;
@@ -336,15 +372,23 @@
//Get the best mode and set
// TODO: DO NOT call this for WFD
setResolution(getBestMode());
+ //enable hdmi vsync
+ enableHDMIVsync(connected);
} else {
- close(fd);
+ // Disable the hdmi vsync
+ enableHDMIVsync(connected);
+ closeFrameBuffer();
+ resetInfo();
}
// Store the external display
mExternalDisplay = connected;
+ const char* prop = (connected) ? "1" : "0";
+ // set system property
+ property_set("hw.hdmiON", prop);
//Invalidate
hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
if(!proc) {
- ALOGD_IF(DEBUG, "%s: HWC proc not registered",
+ ALOGE("%s: HWC proc not registered",
__FUNCTION__);
} else {
/* Trigger redraw */
@@ -360,8 +404,9 @@
bool ret = true;
int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
if (hdmiHPDFile < 0) {
- ALOGD_IF(DEBUG, "%s: state file '%s' not found : ret%d"
- "err str: %s", __FUNCTION__, SYSFS_HPD, hdmiHPDFile, strerror(errno));
+ ALOGE("%s: state file '%s' not found : ret%d"
+ "err str: %s", __FUNCTION__, SYSFS_HPD, hdmiHPDFile,
+ strerror(errno));
ret = false;
} else {
int err = -1;
@@ -372,7 +417,7 @@
else
err = write(hdmiHPDFile, "0" , 2);
if (err <= 0) {
- ALOGD_IF(DEBUG, "%s: file write failed '%s'",
+ ALOGE("%s: file write failed '%s'",
__FUNCTION__, SYSFS_HPD);
ret = false;
}
@@ -380,5 +425,30 @@
}
return ret;
}
+
+bool ExternalDisplay::commit()
+{
+ if(mFd == -1) {
+ return false;
+ } else if(ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo) == -1) {
+ ALOGE("%s: FBIOPUT_VSCREENINFO failed, str: %s", __FUNCTION__,
+ strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+int ExternalDisplay::enableHDMIVsync(int enable)
+{
+ if(mFd > 0) {
+ int ret = ioctl(mFd, MSMFB_OVERLAY_VSYNC_CTRL, &enable);
+ if (ret<0) {
+ ALOGE("%s: enabling HDMI vsync failed, str: %s", __FUNCTION__,
+ strerror(errno));
+ }
+ }
+ return -errno;
+}
+
};
diff --git a/libhwcomposer/hwc_external.h b/libhwcomposer/hwc_external.h
index 9e5f455..55773c0 100644
--- a/libhwcomposer/hwc_external.h
+++ b/libhwcomposer/hwc_external.h
@@ -21,6 +21,8 @@
#ifndef HWC_EXTERNAL_DISPLAY_H
#define HWC_EXTERNAL_DISPLAY_H
+#include <fb_priv.h>
+
struct hwc_context_t;
namespace qhwc {
@@ -43,26 +45,31 @@
ExternalDisplay(hwc_context_t* ctx);
~ExternalDisplay();
int getExternalDisplay() const;
- void setExternalDisplayStatus(int connected);
+ void setExternalDisplay(int connected);
+ bool commit();
+ int enableHDMIVsync(int enable);
private:
bool readResolution();
- int parseResolution(char* edidStr, int* edidModes, int len);
+ int parseResolution(char* edidStr, int* edidModes);
void setResolution(int ID);
bool openFramebuffer();
+ bool closeFrameBuffer();
bool writeHPDOption(int userOption) const;
bool isValidMode(int ID);
void handleUEvent(char* str, int len);
int getModeOrder(int mode);
int getBestMode();
+ void resetInfo();
- int fd;
+ int mFd;
int mExternalDisplay;
- int mCurrentID;
+ int mCurrentMode;
char mEDIDs[128];
int mEDIDModes[64];
int mModeCount;
hwc_context_t *mHwcContext;
+ fb_var_screeninfo mVInfo;
};
}; //qhwc
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index a1df74b..f58fbd3 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -30,21 +30,30 @@
namespace qhwc {
-const char* MSMFB_DEVICE_CHANGE = "change@/devices/virtual/graphics/fb0";
+const char* MSMFB_DEVICE_FB0 = "change@/devices/virtual/graphics/fb0";
+const char* MSMFB_DEVICE_FB1 = "change@/devices/virtual/graphics/fb1";
const char* MSMFB_HDMI_NODE = "fb1";
static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
{
- int vsync;
+ int vsync = 0;
char* hdmi;
int64_t timestamp = 0;
const char *str = udata;
- hwc_procs* proc = reinterpret_cast<hwc_procs*>(ctx->device.reserved_proc[0]);
+ hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
+ int hdmiconnected = ctx->mExtDisplay->getExternalDisplay();
- vsync = !strncmp(str, MSMFB_DEVICE_CHANGE, strlen(MSMFB_DEVICE_CHANGE));
- hdmi = strcasestr(str, MSMFB_HDMI_NODE);
- if(!vsync && !hdmi)
+ if(!strcasestr(str, "@/devices/virtual/graphics/fb")) {
+ ALOGD_IF(DEBUG, "%s: Not Ext Disp Event ", __FUNCTION__);
return;
+ }
+
+ if(hdmiconnected)
+ vsync = !strncmp(str, MSMFB_DEVICE_FB1, strlen(MSMFB_DEVICE_FB1));
+ else
+ vsync = !strncmp(str, MSMFB_DEVICE_FB0, strlen(MSMFB_DEVICE_FB0));
+
+ hdmi = strcasestr(str, MSMFB_HDMI_NODE);
if(vsync) {
str += strlen(str) + 1;
while(*str) {
@@ -56,6 +65,7 @@
if(str - udata >= len)
break;
}
+ return;
}
if(hdmi) {
@@ -72,7 +82,7 @@
} else if(!(strncmp(str,"offline@",strlen("offline@")))) {
connected = 0;
}
- ctx->mExtDisplay->setExternalDisplayStatus(connected);
+ ctx->mExtDisplay->setExternalDisplay(connected);
}
}
diff --git a/libhwcomposer/hwc_uimirror.cpp b/libhwcomposer/hwc_uimirror.cpp
index 9cf91af..3bbe30e 100644
--- a/libhwcomposer/hwc_uimirror.cpp
+++ b/libhwcomposer/hwc_uimirror.cpp
@@ -33,7 +33,7 @@
int getDeviceOrientation(hwc_context_t* ctx, hwc_layer_list_t *list) {
int orientation = list->hwLayers[0].transform;
if(!ctx) {
- ALOGD_IF(HWC_UI_MIRROR, "In %s: ctx is NULL!!", __FUNCTION__);
+ ALOGE("In %s: ctx is NULL!!", __FUNCTION__);
return -1;
}
for(size_t i=0; i <= list->numHwLayers;i++ )
@@ -60,6 +60,12 @@
bool UIMirrorOverlay::prepare(hwc_context_t *ctx, hwc_layer_list_t *list) {
sState = ovutils::OV_CLOSED;
sIsUiMirroringOn = false;
+
+ if(!ctx->hasOverlay) {
+ ALOGD_IF(HWC_UI_MIRROR, "%s, this hw doesnt support mirroring",
+ __FUNCTION__);
+ return false;
+ }
// If external display is connected
if(ctx->mExtDisplay->getExternalDisplay()) {
sState = ovutils::OV_UI_MIRROR;
@@ -95,7 +101,7 @@
dest = ovutils::OV_PIPE0;
}
- ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_MEMORY_ID_TYPE_FB;
+ ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_FLAGS_NONE;
/* - TODO: Secure content
if (hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
ovutils::setMdpFlags(mdpFlags,
@@ -160,20 +166,20 @@
if (!ov.queueBuffer(m->framebuffer->fd, m->currentOffset,
ovutils::OV_PIPE0)) {
ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
+ ret = false;
}
break;
case ovutils::OV_2D_TRUE_UI_MIRROR:
if (!ov.queueBuffer(m->framebuffer->fd, m->currentOffset,
ovutils::OV_PIPE2)) {
ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
+ ret = false;
}
break;
default:
break;
}
- // TODO:
- // Call PANDISPLAY ioctl here to kickoff
}
return ret;
}