Files
android_vendor_qcom_opensou…/libexternal/external.cpp
Saurabh Shah 1a8cda0b2c hwc: clean up overlay for external from the draw thread only
Cleanup overlay for external from the draw thread. If done from the uevent
thread, its possible that the object being used by draw thread is deleted by
uevent thread.

This also removes unnecessary side-effects where libexternal sets states in
hwc, whereas, it could be set from hwc itself. There should be no need for
libexternal to modify states in hwc.

Bug: 7335863 (partial fix)
Change-Id: If07483e640abae2ced2418e0d5c8f278f8c6ec33
Signed-off-by: Iliyan Malchev <malchev@google.com>
2012-10-15 12:02:33 -07:00

530 lines
16 KiB
C++

/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012, The Linux Foundation. All rights reserved.
*
* Not a Contribution, Apache license notifications and license are
* retained for attribution purposes only.
*
* 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 DEBUG 0
#include <ctype.h>
#include <fcntl.h>
#include <media/IAudioPolicyService.h>
#include <media/AudioSystem.h>
#include <utils/threads.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <linux/msm_mdp.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/resource.h>
#include <cutils/properties.h>
#include "hwc_utils.h"
#include "external.h"
#include "overlayUtils.h"
using namespace android;
namespace qhwc {
#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"
ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
mCurrentMode(-1), mExternalDisplay(0), mModeCount(0), mHwcContext(ctx)
{
memset(&mVInfo, 0, sizeof(mVInfo));
//Enable HPD for HDMI
writeHPDOption(1);
}
void ExternalDisplay::setEDIDMode(int resMode) {
ALOGD_IF(DEBUG,"resMode=%d ", resMode);
int extDispType;
{
Mutex::Autolock lock(mExtDispLock);
extDispType = mExternalDisplay;
setExternalDisplay(0);
setResolution(resMode);
}
setExternalDisplay(extDispType);
}
void ExternalDisplay::setHPD(uint32_t startEnd) {
ALOGD_IF(DEBUG,"HPD enabled=%d", startEnd);
writeHPDOption(startEnd);
}
void ExternalDisplay::setActionSafeDimension(int w, int h) {
ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
Mutex::Autolock lock(mExtDispLock);
overlay::utils::ActionSafe::getInstance()->setDimension(w, h);
setExternalDisplay(mExternalDisplay);
}
int ExternalDisplay::getModeCount() const {
ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
Mutex::Autolock lock(mExtDispLock);
return mModeCount;
}
void ExternalDisplay::getEDIDModes(int *out) const {
Mutex::Autolock lock(mExtDispLock);
for(int i = 0;i < mModeCount;i++) {
out[i] = mEDIDModes[i];
}
}
ExternalDisplay::~ExternalDisplay()
{
closeFrameBuffer();
}
struct disp_mode_timing_type {
int video_format;
int active_h;
int active_v;
int front_porch_h;
int pulse_width_h;
int back_porch_h;
int front_porch_v;
int pulse_width_v;
int back_porch_v;
int pixel_freq;
bool interlaced;
void set_info(struct fb_var_screeninfo &info) const;
};
void disp_mode_timing_type::set_info(struct fb_var_screeninfo &info) const
{
info.reserved[0] = 0;
info.reserved[1] = 0;
info.reserved[2] = 0;
info.reserved[3] = (info.reserved[3] & 0xFFFF) | (video_format << 16);
info.xoffset = 0;
info.yoffset = 0;
info.xres = active_h;
info.yres = active_v;
info.pixclock = pixel_freq*1000;
info.vmode = interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
info.right_margin = front_porch_h;
info.hsync_len = pulse_width_h;
info.left_margin = back_porch_h;
info.lower_margin = front_porch_v;
info.vsync_len = pulse_width_v;
info.upper_margin = back_porch_v;
}
/* Video formates supported by the HDMI Standard */
/* Indicates the resolution, pix clock and the aspect ratio */
#define m640x480p60_4_3 1
#define m720x480p60_4_3 2
#define m720x480p60_16_9 3
#define m1280x720p60_16_9 4
#define m1920x1080i60_16_9 5
#define m1440x480i60_4_3 6
#define m1440x480i60_16_9 7
#define m1920x1080p60_16_9 16
#define m720x576p50_4_3 17
#define m720x576p50_16_9 18
#define m1280x720p50_16_9 19
#define m1440x576i50_4_3 21
#define m1440x576i50_16_9 22
#define m1920x1080p50_16_9 31
#define m1920x1080p24_16_9 32
#define m1920x1080p25_16_9 33
#define m1920x1080p30_16_9 34
static struct disp_mode_timing_type supported_video_mode_lut[] = {
{m640x480p60_4_3, 640, 480, 16, 96, 48, 10, 2, 33, 25200, false},
{m720x480p60_4_3, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
{m720x480p60_16_9, 720, 480, 16, 62, 60, 9, 6, 30, 27030, false},
{m1280x720p60_16_9, 1280, 720, 110, 40, 220, 5, 5, 20, 74250, false},
{m1920x1080i60_16_9, 1920, 540, 88, 44, 148, 2, 5, 5, 74250, false},
{m1440x480i60_4_3, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
{m1440x480i60_16_9, 1440, 240, 38, 124, 114, 4, 3, 15, 27000, true},
{m1920x1080p60_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 148500, false},
{m720x576p50_4_3, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
{m720x576p50_16_9, 720, 576, 12, 64, 68, 5, 5, 39, 27000, false},
{m1280x720p50_16_9, 1280, 720, 440, 40, 220, 5, 5, 20, 74250, false},
{m1440x576i50_4_3, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
{m1440x576i50_16_9, 1440, 288, 24, 126, 138, 2, 3, 19, 27000, true},
{m1920x1080p50_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 148500, false},
{m1920x1080p24_16_9, 1920, 1080, 638, 44, 148, 4, 5, 36, 74250, false},
{m1920x1080p25_16_9, 1920, 1080, 528, 44, 148, 4, 5, 36, 74250, false},
{m1920x1080p30_16_9, 1920, 1080, 88, 44, 148, 4, 5, 36, 74250, false},
};
int ExternalDisplay::parseResolution(char* edidStr, int* edidModes)
{
char delim = ',';
int count = 0;
char *start, *end;
// EDIDs are string delimited by ','
// Ex: 16,4,5,3,32,34,1
// Parse this string to get mode(int)
start = (char*) edidStr;
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;
if (hdmiEDIDFile < 0) {
ALOGE("%s: edid_modes file '%s' not found",
__FUNCTION__, SYSFS_EDID_MODES);
return false;
} else {
len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
__FUNCTION__, mEDIDs, len);
if ( len <= 0) {
ALOGE("%s: edid_modes file empty '%s'",
__FUNCTION__, SYSFS_EDID_MODES);
}
else {
while (len > 1 && isspace(mEDIDs[len-1]))
--len;
mEDIDs[len] = 0;
}
}
close(hdmiEDIDFile);
if(len > 0) {
// GEt EDID modes from the EDID strings
mModeCount = parseResolution(mEDIDs, mEDIDModes);
ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
mModeCount);
}
return (strlen(mEDIDs) > 0);
}
bool ExternalDisplay::openFramebuffer()
{
if (mFd == -1) {
mFd = open("/dev/graphics/fb1", O_RDWR);
if (mFd < 0)
ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
}
if(mHwcContext) {
mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
}
return (mFd > 0);
}
bool ExternalDisplay::closeFrameBuffer()
{
int ret = 0;
if(mFd > 0) {
ret = close(mFd);
mFd = -1;
}
if(mHwcContext) {
mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
}
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)
{
switch (mode) {
default:
case m1440x480i60_4_3:
return 1; // 480i 4:3
case m1440x480i60_16_9:
return 2; // 480i 16:9
case m1440x576i50_4_3:
return 3; // i576i 4:3
case m1440x576i50_16_9:
return 4; // 576i 16:9
case m640x480p60_4_3:
return 5; // 640x480 4:3
case m720x480p60_4_3:
return 6; // 480p 4:3
case m720x480p60_16_9:
return 7; // 480p 16:9
case m720x576p50_4_3:
return 8; // 576p 4:3
case m720x576p50_16_9:
return 9; // 576p 16:9
case m1920x1080i60_16_9:
return 10; // 1080i 16:9
case m1280x720p50_16_9:
return 11; // 720p@50Hz
case m1280x720p60_16_9:
return 12; // 720p@60Hz
case m1920x1080p24_16_9:
return 13; //1080p@24Hz
case m1920x1080p25_16_9:
return 14; //108-p@25Hz
case m1920x1080p30_16_9:
return 15; //1080p@30Hz
case m1920x1080p50_16_9:
return 16; //1080p@50Hz
case m1920x1080p60_16_9:
return 17; //1080p@60Hz
}
}
// Get the best mode for the current HD TV
int ExternalDisplay::getBestMode() {
int bestOrder = 0;
int bestMode = m640x480p60_4_3;
Mutex::Autolock lock(mExtDispLock);
// for all the edid read, get the best mode
for(int i = 0; i < mModeCount; i++) {
int mode = mEDIDModes[i];
int order = getModeOrder(mode);
if (order > bestOrder) {
bestOrder = order;
bestMode = mode;
}
}
return bestMode;
}
inline bool ExternalDisplay::isValidMode(int ID)
{
return ((ID >= m640x480p60_4_3) && (ID <= m1920x1080p30_16_9));
}
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)) && mCurrentMode != ID) {
const struct disp_mode_timing_type *mode =
&supported_video_mode_lut[0];
unsigned count = sizeof(supported_video_mode_lut)/sizeof
(*supported_video_mode_lut);
for (unsigned int i = 0; i < count; ++i) {
const struct disp_mode_timing_type *cur =
&supported_video_mode_lut[i];
if (cur->video_format == ID)
mode = cur;
}
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,
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;
}
}
void ExternalDisplay::setExternalDisplay(int connected)
{
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();
}
// Store the external display
mExternalDisplay = connected;
const char* prop = (connected) ? "1" : "0";
// set system property
property_set("hw.hdmiON", prop);
}
return;
}
bool ExternalDisplay::writeHPDOption(int userOption) const
{
bool ret = true;
int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
if (hdmiHPDFile < 0) {
ALOGE("%s: state file '%s' not found : ret%d"
"err str: %s", __FUNCTION__, SYSFS_HPD, hdmiHPDFile,
strerror(errno));
ret = false;
} else {
int err = -1;
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);
ret = false;
}
close(hdmiHPDFile);
}
return ret;
}
/*
* commits the changes to the external display
* mExternalDisplay has the mixer number(1-> HDMI 2-> WFD)
*/
bool ExternalDisplay::post()
{
if(mFd == -1) {
return false;
} else if(ioctl(mFd, MSMFB_OVERLAY_COMMIT, &mExternalDisplay) == -1) {
ALOGE("%s: MSMFB_OVERLAY_COMMIT failed, str: %s", __FUNCTION__,
strerror(errno));
return false;
}
return true;
}
void ExternalDisplay::setDpyAttr() {
int width = 0, height = 0, fps = 0;
getAttrForMode(width, height, fps);
if(mHwcContext) {
ALOGD("ExtDisplay setting xres = %d, yres = %d", width, height);
mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = width;
mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = height;
mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
1000000000l / fps;
}
}
void ExternalDisplay::getAttrForMode(int& width, int& height,
int& fps) {
switch (mCurrentMode) {
case m640x480p60_4_3:
width = 640;
height = 480;
fps = 60;
break;
case m720x480p60_4_3:
case m720x480p60_16_9:
width = 720;
height = 480;
fps = 60;
break;
case m720x576p50_4_3:
case m720x576p50_16_9:
width = 720;
height = 576;
fps = 50;
break;
case m1280x720p50_16_9:
width = 1280;
height = 720;
fps = 50;
break;
case m1280x720p60_16_9:
width = 1280;
height = 720;
fps = 60;
break;
case m1920x1080p24_16_9:
width = 1920;
height = 1080;
fps = 24;
break;
case m1920x1080p25_16_9:
width = 1920;
height = 1080;
fps = 25;
break;
case m1920x1080p30_16_9:
width = 1920;
height = 1080;
fps = 30;
break;
case m1920x1080p50_16_9:
width = 1920;
height = 1080;
fps = 50;
break;
case m1920x1080p60_16_9:
width = 1920;
height = 1080;
fps = 60;
break;
}
}
};