hwc: support WFD external display
Change-Id: I3d24ff1757e79c4b41ff6c6db17d0274cd665671
(cherry picked from commit f8942672226a7af26218aa8d325c1677ae2b83dd)
diff --git a/libexternal/external.cpp b/libexternal/external.cpp
index 28fbd58..f9ee94c 100644
--- a/libexternal/external.cpp
+++ b/libexternal/external.cpp
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012, The Linux Foundation. All rights reserved.
@@ -37,35 +36,142 @@
#include "hwc_utils.h"
#include "external.h"
#include "overlayUtils.h"
+#include "overlay.h"
using namespace android;
namespace qhwc {
+#define MAX_FRAME_BUFFER_NAME_SIZE (80)
+#define MAX_DISPLAY_DEVICES (3)
-#define DEVICE_ROOT "/sys/devices/virtual/graphics"
-#define DEVICE_NODE "fb1"
-#define SYSFS_EDID_MODES DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
-#define SYSFS_HPD DEVICE_ROOT "/" DEVICE_NODE "/hpd"
+const char* msmFbDevicePath[] = { "/dev/graphics/fb1",
+ "/dev/graphics/fb2"};
+/*
+ * Updates extDeviceFbIndex Array with the correct frame buffer indices
+ * of avaiable external devices
+ *
+ */
+void ExternalDisplay::updateExtDispDevFbIndex()
+{
+ FILE *displayDeviceFP = NULL;
+ char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
+ char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
+
+ for(int j = 1; j < MAX_DISPLAY_DEVICES; j++) {
+ sprintf (msmFbTypePath,"/sys/class/graphics/fb%d/msm_fb_type", j);
+ displayDeviceFP = fopen(msmFbTypePath, "r");
+ if(displayDeviceFP){
+ fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
+ displayDeviceFP);
+ if(strncmp(fbType, "dtv panel", strlen("dtv panel")) == 0){
+ ALOGD_IF(DEBUG,"hdmi framebuffer index is %d",j);
+ mHdmiFbNum = j;
+ } else if(strncmp(fbType, "writeback panel",
+ strlen("writeback panel")) == 0){
+ ALOGD_IF(DEBUG,"wfd framebuffer index is %d",j);
+ mWfdFbNum = j;
+ }
+ fclose(displayDeviceFP);
+ }
+ }
+ ALOGD_IF(DEBUG,"%s: mHdmiFbNum: %d mWfdFbNum: %d ",__FUNCTION__,
+ mHdmiFbNum, mWfdFbNum);
+}
+
+int ExternalDisplay::configureHDMIDisplay() {
+ openFrameBuffer(mHdmiFbNum);
+ if(mFd == -1)
+ return -1;
+ readResolution();
+ //Get the best mode and set
+ // TODO: Move this to activate
+ setResolution(getBestMode());
+ setDpyHdmiAttr();
+ setExternalDisplay(true, mHdmiFbNum);
+ return 0;
+}
+
+int ExternalDisplay::configureWFDDisplay() {
+ int ret = 0;
+ if(mConnectedFbNum == mHdmiFbNum) {
+ ALOGE("%s: Cannot process WFD connection while HDMI is active",
+ __FUNCTION__);
+ return -1;
+ }
+ openFrameBuffer(mWfdFbNum);
+ if(mFd == -1)
+ return -1;
+ ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+ if(ret < 0) {
+ ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+ strerror(errno));
+ }
+ setDpyWfdAttr();
+ setExternalDisplay(true, mWfdFbNum);
+ return 0;
+}
+
+int ExternalDisplay::teardownHDMIDisplay() {
+ if(mConnectedFbNum == mHdmiFbNum) {
+ // hdmi offline event..!
+ closeFrameBuffer();
+ resetInfo();
+ setExternalDisplay(false);
+ }
+ return 0;
+}
+
+int ExternalDisplay::teardownWFDDisplay() {
+ if(mConnectedFbNum == mWfdFbNum) {
+ // wfd offline event..!
+ closeFrameBuffer();
+ memset(&mVInfo, 0, sizeof(mVInfo));
+ setExternalDisplay(false);
+ }
+ return 0;
+}
+
+void ExternalDisplay::processUEventOnline(const char *str) {
+ const char *s1 = str + strlen("change@/devices/virtual/switch/");
+ if(!strncmp(s1,"hdmi",strlen(s1))) {
+ // hdmi online event..!
+ configureHDMIDisplay();
+ }else if(!strncmp(s1,"wfd",strlen(s1))) {
+ // wfd online event..!
+ configureWFDDisplay();
+ }
+}
+
+void ExternalDisplay::processUEventOffline(const char *str) {
+ const char *s1 = str + strlen("change@/devices/virtual/switch/");
+ if(!strncmp(s1,"hdmi",strlen(s1))) {
+ teardownHDMIDisplay();
+ }else if(!strncmp(s1,"wfd",strlen(s1))) {
+ teardownWFDDisplay();
+ }
+}
ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
- mCurrentMode(-1), mExternalDisplay(0), mModeCount(0), mHwcContext(ctx)
+ mCurrentMode(-1), mConnected(0), mConnectedFbNum(0), mModeCount(0),
+ mHwcContext(ctx), mHdmiFbNum(-1), mWfdFbNum(-1)
{
memset(&mVInfo, 0, sizeof(mVInfo));
+ //Determine the fb index for external display devices.
+ updateExtDispDevFbIndex();
}
void ExternalDisplay::setEDIDMode(int resMode) {
ALOGD_IF(DEBUG,"resMode=%d ", resMode);
- int extDispType;
{
Mutex::Autolock lock(mExtDispLock);
- extDispType = mExternalDisplay;
- setExternalDisplay(0);
+ setExternalDisplay(false);
+ openFrameBuffer(mHdmiFbNum);
setResolution(resMode);
}
- setExternalDisplay(extDispType);
+ setExternalDisplay(true, mHdmiFbNum);
}
void ExternalDisplay::setHPD(uint32_t startEnd) {
@@ -77,7 +183,7 @@
ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
Mutex::Autolock lock(mExtDispLock);
overlay::utils::ActionSafe::getInstance()->setDimension(w, h);
- setExternalDisplay(mExternalDisplay);
+ setExternalDisplay(true, mHdmiFbNum);
}
int ExternalDisplay::getModeCount() const {
@@ -204,12 +310,16 @@
bool ExternalDisplay::readResolution()
{
- int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
+ char sysFsEDIDFilePath[255];
+ sprintf(sysFsEDIDFilePath , "/sys/devices/virtual/graphics/fb%d/edid_modes",
+ mHdmiFbNum);
+
+ int hdmiEDIDFile = open(sysFsEDIDFilePath, O_RDONLY, 0);
int len = -1;
if (hdmiEDIDFile < 0) {
ALOGE("%s: edid_modes file '%s' not found",
- __FUNCTION__, SYSFS_EDID_MODES);
+ __FUNCTION__, sysFsEDIDFilePath);
return false;
} else {
len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
@@ -217,7 +327,7 @@
__FUNCTION__, mEDIDs, len);
if ( len <= 0) {
ALOGE("%s: edid_modes file empty '%s'",
- __FUNCTION__, SYSFS_EDID_MODES);
+ __FUNCTION__, sysFsEDIDFilePath);
}
else {
while (len > 1 && isspace(mEDIDs[len-1]))
@@ -227,7 +337,7 @@
}
close(hdmiEDIDFile);
if(len > 0) {
- // GEt EDID modes from the EDID strings
+ // Get EDID modes from the EDID strings
mModeCount = parseResolution(mEDIDs, mEDIDModes);
ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
mModeCount);
@@ -236,15 +346,16 @@
return (strlen(mEDIDs) > 0);
}
-bool ExternalDisplay::openFramebuffer()
+bool ExternalDisplay::openFrameBuffer(int fbNum)
{
if (mFd == -1) {
- mFd = open("/dev/graphics/fb1", O_RDWR);
+ mFd = open(msmFbDevicePath[fbNum-1], O_RDWR);
if (mFd < 0)
- ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
- }
- if(mHwcContext) {
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+ ALOGE("%s: %s is not available", __FUNCTION__,
+ msmFbDevicePath[fbNum-1]);
+ if(mHwcContext) {
+ mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+ }
}
return (mFd > 0);
}
@@ -339,8 +450,6 @@
{
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__,
@@ -382,54 +491,49 @@
}
}
-void ExternalDisplay::setExternalDisplay(int connected)
+void ExternalDisplay::setExternalDisplay(bool connected, int extFbNum)
{
-
hwc_context_t* ctx = mHwcContext;
if(ctx) {
- ALOGD_IF(DEBUG, "%s: status = %d", __FUNCTION__,
- connected);
- if(connected) {
- readResolution();
- //Get the best mode and set
- // TODO: Move this to activate
- setResolution(getBestMode());
- setDpyAttr();
- //enable hdmi vsync
- } else {
- // Disable the hdmi vsync
- closeFrameBuffer();
- resetInfo();
- }
+ ALOGD_IF(DEBUG, "%s: connected = %d", __FUNCTION__, connected);
// Store the external display
- mExternalDisplay = connected;
- const char* prop = (connected) ? "1" : "0";
- // set system property
- property_set("hw.hdmiON", prop);
+ mConnected = connected;
+ mConnectedFbNum = extFbNum;
+ mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = connected;
+ // Update external fb number in Overlay context
+ overlay::Overlay::getInstance()->setExtFbNum(extFbNum);
}
- return;
+}
+
+int ExternalDisplay::getExtFbNum(int &fbNum) {
+ int ret = -1;
+ if(mConnected) {
+ fbNum = mConnectedFbNum;
+ ret = 0;
+ }
+ return ret;
}
bool ExternalDisplay::writeHPDOption(int userOption) const
{
bool ret = true;
- int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
+ char sysFsHPDFilePath[255];
+ sprintf(sysFsHPDFilePath ,"/sys/devices/virtual/graphics/fb%d/hpd",
+ mHdmiFbNum);
+ int hdmiHPDFile = open(sysFsHPDFilePath,O_RDWR, 0);
if (hdmiHPDFile < 0) {
- ALOGE("%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__,
+ sysFsHPDFilePath, hdmiHPDFile, strerror(errno));
ret = false;
} else {
int err = -1;
- ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__,
- userOption);
+ ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__, userOption);
if(userOption)
err = write(hdmiHPDFile, "1", 2);
else
err = write(hdmiHPDFile, "0" , 2);
if (err <= 0) {
- ALOGE("%s: file write failed '%s'",
- __FUNCTION__, SYSFS_HPD);
+ ALOGE("%s: file write failed '%s'", __FUNCTION__, sysFsHPDFilePath);
ret = false;
}
close(hdmiHPDFile);
@@ -439,25 +543,32 @@
/*
* commits the changes to the external display
- * mExternalDisplay has the mixer number(1-> HDMI 2-> WFD)
*/
bool ExternalDisplay::post()
{
if(mFd == -1)
return false;
-
struct mdp_display_commit ext_commit;
ext_commit.flags |= MDP_DISPLAY_COMMIT_OVERLAY;
if (ioctl(mFd, MSMFB_DISPLAY_COMMIT, &ext_commit) == -1) {
- ALOGE("%s: MSMFB_DISPLAY_COMMIT for external failed, str: %s",
- __FUNCTION__, strerror(errno));
- return false;
+ ALOGE("%s: MSMFB_DISPLAY_COMMIT for external failed, str: %s",
+ __FUNCTION__, strerror(errno));
+ return false;
}
-
return true;
}
-void ExternalDisplay::setDpyAttr() {
+void ExternalDisplay::setDpyWfdAttr() {
+ if(mHwcContext) {
+ mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = mVInfo.xres;
+ mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = mVInfo.yres;
+ mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
+ 1000000000l /60;
+ ALOGD_IF(DEBUG,"%s: wfd...connected..!",__FUNCTION__);
+ }
+}
+
+void ExternalDisplay::setDpyHdmiAttr() {
int width = 0, height = 0, fps = 0;
getAttrForMode(width, height, fps);
if(mHwcContext) {
@@ -469,8 +580,7 @@
}
}
-void ExternalDisplay::getAttrForMode(int& width, int& height,
-int& fps) {
+void ExternalDisplay::getAttrForMode(int& width, int& height, int& fps) {
switch (mCurrentMode) {
case m640x480p60_4_3:
width = 640;
@@ -528,4 +638,3 @@
}
};
-