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: Icdd3620bd9d15240a88a26a3f1bf9c07d58d011b
This commit is contained in:
Arun Kumar K.R 2012-07-23 13:44:38 -07:00 committed by Naseer Ahmed
parent 9ccb3975c7
commit a72904b1dc
5 changed files with 166 additions and 66 deletions

View File

@ -29,6 +29,7 @@
#include "hwc_video.h"
#include "hwc_uimirror.h"
#include "hwc_copybit.h"
#include "hwc_external.h"
using namespace qhwc;
@ -107,6 +108,10 @@ static int hwc_eventControl(struct hwc_composer_device* dev,
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;
@ -149,6 +154,8 @@ static int hwc_set(hwc_composer_device_t *dev,
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();

View File

@ -42,22 +42,21 @@ namespace qhwc {
#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 @@ static struct disp_mode_timing_type supported_video_mode_lut[] = {
{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 @@ int ExternalDisplay::parseResolution(char* edidStr, int* edidModes, int len)
// 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 @@ bool ExternalDisplay::readResolution()
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 @@ bool ExternalDisplay::readResolution()
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::readResolution()
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 @@ inline bool ExternalDisplay::isValidMode(int ID)
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 @@ void ExternalDisplay::setResolution(int ID)
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 @@ int ExternalDisplay::getExternalDisplay() const
return mExternalDisplay;
}
void ExternalDisplay::setExternalDisplayStatus(int connected)
void ExternalDisplay::setExternalDisplay(int connected)
{
hwc_context_t* ctx = mHwcContext;
@ -336,15 +372,23 @@ void ExternalDisplay::setExternalDisplayStatus(int connected)
//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 ExternalDisplay::writeHPDOption(int userOption) const
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 @@ bool ExternalDisplay::writeHPDOption(int userOption) const
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 @@ bool ExternalDisplay::writeHPDOption(int userOption) const
}
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;
}
};

View File

@ -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 @@ class ExternalDisplay
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

View File

@ -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 @@ static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
if(str - udata >= len)
break;
}
return;
}
if(hdmi) {
@ -72,7 +82,7 @@ static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
} else if(!(strncmp(str,"offline@",strlen("offline@")))) {
connected = 0;
}
ctx->mExtDisplay->setExternalDisplayStatus(connected);
ctx->mExtDisplay->setExternalDisplay(connected);
}
}

View File

@ -33,7 +33,7 @@ namespace qhwc {
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::sIsUiMirroringOn = false;
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 @@ bool UIMirrorOverlay::configure(hwc_context_t *ctx, hwc_layer_list_t *list)
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 @@ bool UIMirrorOverlay::draw(hwc_context_t *ctx)
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;
}