Merge "hwc: hdmi: Use res_info sysfs node to get HDMI timing info"
diff --git a/libhdmi/hdmi.cpp b/libhdmi/hdmi.cpp
index 7d709cb..bca7a0b 100644
--- a/libhdmi/hdmi.cpp
+++ b/libhdmi/hdmi.cpp
@@ -21,7 +21,6 @@
#define DEBUG 0
#include <fcntl.h>
#include <linux/msm_mdp.h>
-#include <video/msm_hdmi_modes.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <cutils/properties.h>
@@ -38,62 +37,6 @@
#define UNKNOWN_STRING "unknown"
#define SPD_NAME_LENGTH 16
-/* The array gEDIDData contains a list of modes currently
- * supported by HDMI and display, and modes that are not
- * supported i.e. interlaced modes.
-
- * In order to add support for a new mode, the mode must be
- * appended to the end of the array.
- *
- * Each new entry must contain the following:
- * -Mode: a video format defined in msm_hdmi_modes.h
- * -Width: x resolution for the mode
- * -Height: y resolution for the mode
- * -FPS: the frame rate for the mode
- * -Mode Order: the priority for the new mode that is used when determining
- * the best mode when the HDMI display is connected.
- */
-EDIDData gEDIDData [] = {
- EDIDData(HDMI_VFRMT_1440x480i60_4_3, 1440, 480, 60, 1),
- EDIDData(HDMI_VFRMT_1440x480i60_16_9, 1440, 480, 60, 2),
- EDIDData(HDMI_VFRMT_1440x576i50_4_3, 1440, 576, 50, 3),
- EDIDData(HDMI_VFRMT_1440x576i50_16_9, 1440, 576, 50, 4),
- EDIDData(HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, 60, 5),
- EDIDData(HDMI_VFRMT_640x480p60_4_3, 640, 480, 60, 6),
- EDIDData(HDMI_VFRMT_720x480p60_4_3, 720, 480, 60, 7),
- EDIDData(HDMI_VFRMT_720x480p60_16_9, 720, 480, 60, 8),
- EDIDData(HDMI_VFRMT_720x576p50_4_3, 720, 576, 50, 9),
- EDIDData(HDMI_VFRMT_720x576p50_16_9, 720, 576, 50, 10),
- EDIDData(HDMI_VFRMT_800x600p60_4_3, 800, 600, 60, 11),
- EDIDData(HDMI_VFRMT_848x480p60_16_9, 848, 480, 60, 12),
- EDIDData(HDMI_VFRMT_1024x768p60_4_3, 1024, 768, 60, 13),
- EDIDData(HDMI_VFRMT_1280x1024p60_5_4, 1280, 1024, 60, 14),
- EDIDData(HDMI_VFRMT_1280x720p50_16_9, 1280, 720, 50, 15),
- EDIDData(HDMI_VFRMT_1280x720p60_16_9, 1280, 720, 60, 16),
- EDIDData(HDMI_VFRMT_1280x800p60_16_10, 1280, 800, 60, 17),
- EDIDData(HDMI_VFRMT_1280x960p60_4_3, 1280, 960, 60, 18),
- EDIDData(HDMI_VFRMT_1360x768p60_16_9, 1360, 768, 60, 19),
- EDIDData(HDMI_VFRMT_1366x768p60_16_10, 1366, 768, 60, 20),
- EDIDData(HDMI_VFRMT_1440x900p60_16_10, 1440, 900, 60, 21),
- EDIDData(HDMI_VFRMT_1400x1050p60_4_3, 1400, 1050, 60, 22),
- EDIDData(HDMI_VFRMT_1680x1050p60_16_10, 1680, 1050, 60, 23),
- EDIDData(HDMI_VFRMT_1600x1200p60_4_3, 1600, 1200, 60, 24),
- EDIDData(HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, 24, 25),
- EDIDData(HDMI_VFRMT_1920x1080p25_16_9, 1920, 1080, 25, 26),
- EDIDData(HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, 30, 27),
- EDIDData(HDMI_VFRMT_1920x1080p50_16_9, 1920, 1080, 50, 28),
- EDIDData(HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, 60, 29),
- EDIDData(HDMI_VFRMT_1920x1200p60_16_10, 1920, 1200, 60, 30),
- EDIDData(HDMI_VFRMT_2560x1600p60_16_9, 2560, 1600, 60, 31),
- EDIDData(HDMI_VFRMT_3840x2160p24_16_9, 3840, 2160, 24, 32),
- EDIDData(HDMI_VFRMT_3840x2160p25_16_9, 3840, 2160, 25, 33),
- EDIDData(HDMI_VFRMT_3840x2160p30_16_9, 3840, 2160, 30, 34),
- EDIDData(HDMI_VFRMT_4096x2160p24_16_9, 4096, 2160, 24, 35),
-};
-
-// Number of modes in gEDIDData
-const int gEDIDCount = (sizeof(gEDIDData)/sizeof(gEDIDData)[0]);
-
int HDMIDisplay::configure() {
if(!openFrameBuffer()) {
ALOGE("%s: Failed to open FB: %d", __FUNCTION__, mFbNum);
@@ -135,8 +78,8 @@
}
void HDMIDisplay::getAttributes(uint32_t& width, uint32_t& height) {
- uint32_t fps = 0;
- getAttrForMode(width, height, fps);
+ uint32_t refresh = 0, fps = 0;
+ getAttrForConfig(mActiveConfig, width, height, refresh, fps);
}
int HDMIDisplay::teardown() {
@@ -167,14 +110,7 @@
writeHPDOption(0);
}
- // for HDMI - retreive all the modes supported by the driver
if(mFbNum != -1) {
- supported_video_mode_lut =
- new msm_hdmi_mode_timing_info[HDMI_VFRMT_MAX];
- // Populate the mode table for supported modes
- MSM_HDMI_MODES_INIT_TIMINGS(supported_video_mode_lut);
- MSM_HDMI_MODES_SET_SUPP_TIMINGS(supported_video_mode_lut,
- MSM_HDMI_MODES_ALL);
// Update the Source Product Information
// Vendor Name
setSPDInfo("vendor_name", "ro.product.manufacturer");
@@ -298,7 +234,6 @@
HDMIDisplay::~HDMIDisplay()
{
- delete [] supported_video_mode_lut;
closeFrameBuffer();
}
@@ -356,7 +291,7 @@
bool HDMIDisplay::readResolution()
{
ssize_t len = -1;
- char edidStr[128] = {'\0'};
+ char edidStr[PAGE_SIZE] = {'\0'};
int hdmiEDIDFile = openDeviceNode("edid_modes", O_RDONLY);
if (hdmiEDIDFile < 0) {
@@ -383,6 +318,25 @@
ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
mModeCount);
}
+ // Populate the internal data structure with the timing information
+ // for each edid mode read from the driver
+ if (mModeCount > 0) {
+ mDisplayConfigs = new msm_hdmi_mode_timing_info[mModeCount];
+ readConfigs();
+ } else {
+ // If we fail to read from EDID when HDMI is connected, then
+ // mModeCount will be 0 and bestConfigIndex will be invalid.
+ // In this case, we populate the mEDIDModes structure with
+ // a default mode at config index 0.
+ uint32_t defaultConfigIndex = 0;
+ mModeCount = 1;
+ mEDIDModes[defaultConfigIndex] = HDMI_VFRMT_640x480p60_4_3;
+ struct msm_hdmi_mode_timing_info defaultMode =
+ HDMI_VFRMT_640x480p60_4_3_TIMING;
+ mDisplayConfigs = new msm_hdmi_mode_timing_info[mModeCount];
+ mDisplayConfigs[defaultConfigIndex] = defaultMode;
+ ALOGD("%s Defaulting to HDMI_VFRMT_640x480p60_4_3", __FUNCTION__);
+ }
return (len > 0);
}
@@ -421,30 +375,22 @@
mYres = 0;
mVsyncPeriod = 0;
mMDPScalingMode = false;
+ if (mDisplayConfigs) {
+ delete [] mDisplayConfigs;
+ mDisplayConfigs = 0;
+ }
// Reset the underscan supported system property
const char* prop = "0";
property_set("hw.underscan_supported", prop);
}
-int HDMIDisplay::getModeOrder(int mode)
-{
- for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
- if (gEDIDData[dataIndex].mMode == mode) {
- return gEDIDData[dataIndex].mModeOrder;
- }
- }
- ALOGE("%s Mode not found: %d", __FUNCTION__, mode);
- return -1;
-}
-
/// Returns the index of the user mode set(if any) using adb shell
int HDMIDisplay::getUserConfig() {
/* Based on the property set the resolution */
char property_value[PROPERTY_VALUE_MAX];
property_get("hw.hdmi.resolution", property_value, "-1");
int mode = atoi(property_value);
- // We dont support interlaced modes
- if(isValidMode(mode) && !isInterlacedMode(mode)) {
+ if(isValidMode(mode)) {
ALOGD_IF(DEBUG, "%s: setting the HDMI mode = %d", __FUNCTION__, mode);
return getModeIndex(mode);
}
@@ -453,61 +399,137 @@
// Get the index of the best mode for the current HD TV
int HDMIDisplay::getBestConfig() {
- int bestOrder = 0;
- int bestMode = HDMI_VFRMT_640x480p60_4_3;
- int bestModeIndex = -1;
- // 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;
- bestModeIndex = i;
+ int bestConfigIndex = 0;
+ int edidMode = -1;
+ struct msm_hdmi_mode_timing_info currentModeInfo = {0};
+ struct msm_hdmi_mode_timing_info bestModeInfo = {0};
+ bestModeInfo.video_format = 0;
+ bestModeInfo.active_v = 0;
+ bestModeInfo.active_h = 0;
+ bestModeInfo.refresh_rate = 0;
+ bestModeInfo.ar = HDMI_RES_AR_INVALID;
+
+ // for all the timing info read, get the best config
+ for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
+ currentModeInfo = mDisplayConfigs[configIndex];
+
+ if (!currentModeInfo.supported) {
+ ALOGD("%s EDID Mode %d is not supported", __FUNCTION__, edidMode);
+ continue;
+ }
+
+ ALOGD_IF(DEBUG, "%s Best (%d) : (%dx%d) @ %d;"
+ " Current (%d) (%dx%d) @ %d",
+ __FUNCTION__, bestConfigIndex, bestModeInfo.active_h,
+ bestModeInfo.active_v, bestModeInfo.refresh_rate, configIndex,
+ currentModeInfo.active_h, currentModeInfo.active_v,
+ currentModeInfo.refresh_rate);
+
+ // Compare two HDMI modes in order of height, width, refresh rate and
+ // aspect ratio.
+ if (currentModeInfo.active_v > bestModeInfo.active_v) {
+ bestConfigIndex = configIndex;
+ } else if (currentModeInfo.active_v == bestModeInfo.active_v) {
+ if (currentModeInfo.active_h > bestModeInfo.active_h) {
+ bestConfigIndex = configIndex;
+ } else if (currentModeInfo.active_h == bestModeInfo.active_h) {
+ if (currentModeInfo.refresh_rate > bestModeInfo.refresh_rate) {
+ bestConfigIndex = configIndex;
+ } else if (currentModeInfo.refresh_rate ==
+ bestModeInfo.refresh_rate) {
+ if (currentModeInfo.ar > bestModeInfo.ar) {
+ bestConfigIndex = configIndex;
+ }
+ }
+ }
+ }
+ if (bestConfigIndex == configIndex) {
+ bestModeInfo = mDisplayConfigs[bestConfigIndex];
}
}
- // If we fail to read from EDID when HDMI is connected, then
- // mModeCount will be 0 and bestModeIndex will be invalid.
- // In this case, we populate the mEDIDModes structure with
- // a default mode at index 0.
- if (bestModeIndex == -1) {
- bestModeIndex = 0;
- mModeCount = 1;
- mEDIDModes[bestModeIndex] = bestMode;
+ return bestConfigIndex;
+}
+
+// Utility function used to request HDMI driver to write a new page of timing
+// info into res_info node
+void HDMIDisplay::requestNewPage(int pageNumber) {
+ char pageString[PAGE_SIZE];
+ int fd = openDeviceNode("res_info", O_WRONLY);
+ if (fd >= 0) {
+ snprintf(pageString, sizeof(pageString), "%d", pageNumber);
+ ALOGD_IF(DEBUG, "%s: page=%s", __FUNCTION__, pageString);
+ ssize_t err = write(fd, pageString, sizeof(pageString));
+ if (err <= 0) {
+ ALOGE("%s: Write to res_info failed (%d)", __FUNCTION__, errno);
+ }
+ close(fd);
}
- return bestModeIndex;
+}
+
+// Reads the contents of res_info node into a buffer if the file is not empty
+bool HDMIDisplay::readResFile(char * configBuffer) {
+ bool fileRead = false;
+ size_t bytesRead = 0;
+ int fd = openDeviceNode("res_info", O_RDONLY);
+ if (fd >= 0 && (bytesRead = read(fd, configBuffer, PAGE_SIZE)) != 0) {
+ fileRead = true;
+ }
+ close(fd);
+ ALOGD_IF(DEBUG, "%s: bytesRead=%d fileRead=%d",
+ __FUNCTION__, bytesRead, fileRead);
+ return fileRead;
+}
+
+// Populates the internal timing info structure with the timing info obtained
+// from the HDMI driver
+void HDMIDisplay::readConfigs() {
+ int configIndex = 0;
+ int pageNumber = MSM_HDMI_INIT_RES_PAGE;
+ long unsigned int size = sizeof(msm_hdmi_mode_timing_info);
+
+ while (true) {
+ char configBuffer[PAGE_SIZE] = {0};
+ msm_hdmi_mode_timing_info *info =
+ (msm_hdmi_mode_timing_info*) configBuffer;
+
+ if (!readResFile(configBuffer))
+ break;
+
+ while (info->video_format && size < PAGE_SIZE) {
+ mDisplayConfigs[configIndex] = *info;
+ size += sizeof(msm_hdmi_mode_timing_info);
+ info++;
+ ALOGD_IF(DEBUG, "%s: Config=%d Mode %d: (%dx%d) @ %d",
+ __FUNCTION__, configIndex,
+ mDisplayConfigs[configIndex].video_format,
+ mDisplayConfigs[configIndex].active_h,
+ mDisplayConfigs[configIndex].active_v,
+ mDisplayConfigs[configIndex].refresh_rate);
+ configIndex++;
+ }
+ size = sizeof(msm_hdmi_mode_timing_info);
+ // Request HDMI driver to populate res_info with more
+ // timing information
+ pageNumber++;
+ requestNewPage(pageNumber);
+ }
}
inline bool HDMIDisplay::isValidMode(int ID)
{
bool valid = false;
- for (int i = 0; i < mModeCount; i++) {
- if(ID == mEDIDModes[i]) {
- valid = true;
- break;
- }
+ int modeIndex = getModeIndex(ID);
+ if (ID <= 0 || modeIndex < 0 || modeIndex > mModeCount) {
+ return false;
+ }
+ struct msm_hdmi_mode_timing_info* mode = &mDisplayConfigs[modeIndex];
+ // We dont support interlaced modes
+ if (mode->supported && !mode->interlaced) {
+ valid = true;
}
return valid;
}
-// returns true if the mode(ID) is interlaced mode format
-bool HDMIDisplay::isInterlacedMode(int ID) {
- bool interlaced = false;
- switch(ID) {
- case HDMI_VFRMT_1440x480i60_4_3:
- case HDMI_VFRMT_1440x480i60_16_9:
- case HDMI_VFRMT_1440x576i50_4_3:
- case HDMI_VFRMT_1440x576i50_16_9:
- case HDMI_VFRMT_1920x1080i60_16_9:
- interlaced = true;
- break;
- default:
- interlaced = false;
- break;
- }
- return interlaced;
-}
-
// Does a put_vscreen info on the HDMI interface which will update
// the configuration (resolution, timing info) to match mCurrentMode
void HDMIDisplay::activateDisplay()
@@ -525,16 +547,7 @@
mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
mVInfo.pixclock/1000/1000);
- const struct msm_hdmi_mode_timing_info *mode =
- &supported_video_mode_lut[0];
- for (unsigned int i = 0; i < HDMI_VFRMT_MAX; ++i) {
- const struct msm_hdmi_mode_timing_info *cur =
- &supported_video_mode_lut[i];
- if (cur->video_format == (uint32_t)mCurrentMode) {
- mode = cur;
- break;
- }
- }
+ struct msm_hdmi_mode_timing_info *mode = &mDisplayConfigs[mActiveConfig];
setDisplayTiming(mVInfo, mode);
ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
"(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, mCurrentMode,
@@ -585,9 +598,9 @@
void HDMIDisplay::setAttributes() {
- uint32_t fps = 0;
+ uint32_t refresh = 0, fps = 0;
// Always set dpyAttr res to mVInfo res
- getAttrForMode(mXres, mYres, fps);
+ getAttrForConfig(mActiveConfig, mXres, mYres, refresh, fps);
mMDPScalingMode = false;
if(overlay::Overlay::getInstance()->isUIScalingOnExternalSupported()
@@ -653,19 +666,6 @@
ALOGD_IF(DEBUG, "%s xres=%d, yres=%d", __FUNCTION__, mXres, mYres);
}
-void HDMIDisplay::getAttrForMode(uint32_t& width, uint32_t& height,
- uint32_t& fps) {
- for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
- if (gEDIDData[dataIndex].mMode == mCurrentMode) {
- width = gEDIDData[dataIndex].mWidth;
- height = gEDIDData[dataIndex].mHeight;
- fps = gEDIDData[dataIndex].mFps;
- return;
- }
- }
- ALOGE("%s Unable to get attributes for %d", __FUNCTION__, mCurrentMode);
-}
-
/* returns the fd related to the node specified*/
int HDMIDisplay::openDeviceNode(const char* node, int fileMode) const {
char sysFsFilePath[MAX_SYSFS_FILE_PATH];
@@ -743,8 +743,8 @@
// not match the current config
bool HDMIDisplay::isValidConfigChange(int newConfig) {
int newMode = mEDIDModes[newConfig];
- uint32_t width = 0, height = 0, refresh = 0;
- getAttrForConfig(newConfig, width, height, refresh);
+ uint32_t width = 0, height = 0, refresh = 0, fps = 0;
+ getAttrForConfig(newConfig, width, height, refresh, fps);
return ((mXres == width) && (mYres == height)) || mEnableResolutionChange;
}
@@ -760,21 +760,16 @@
}
int HDMIDisplay::getAttrForConfig(int config, uint32_t& xres,
- uint32_t& yres, uint32_t& refresh) const {
+ uint32_t& yres, uint32_t& refresh, uint32_t& fps) const {
if(config < 0 || config > mModeCount) {
ALOGE("%s Invalid configuration %d", __FUNCTION__, config);
return -EINVAL;
}
- int mode = mEDIDModes[config];
- uint32_t fps = 0;
- // Retrieve the mode attributes from gEDIDData
- for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
- if (gEDIDData[dataIndex].mMode == mode) {
- xres = gEDIDData[dataIndex].mWidth;
- yres = gEDIDData[dataIndex].mHeight;
- fps = gEDIDData[dataIndex].mFps;
- }
- }
+
+ xres = mDisplayConfigs[config].active_h;
+ yres = mDisplayConfigs[config].active_v;
+ fps = (mDisplayConfigs[config].refresh_rate / 1000);
+
refresh = (uint32_t) 1000000000l / fps;
ALOGD_IF(DEBUG, "%s xres(%d) yres(%d) fps(%d) refresh(%d)", __FUNCTION__,
xres, yres, fps, refresh);
diff --git a/libhdmi/hdmi.h b/libhdmi/hdmi.h
index d262a63..32c48ff 100644
--- a/libhdmi/hdmi.h
+++ b/libhdmi/hdmi.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 The Android Open Source Project
- * Copyright (C) 2012-2014, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2012-2015, The Linux Foundation. All rights reserved.
*
* Not a Contribution, Apache license notifications and license are
* retained for attribution purposes only.
@@ -22,6 +22,7 @@
#define HWC_HDMI_DISPLAY_H
#include <linux/fb.h>
+#include <video/msm_hdmi_modes.h>
struct msm_hdmi_mode_timing_info;
@@ -35,16 +36,6 @@
HDMI_SCAN_BOTH_SUPPORTED = 3
};
-// Structure to store EDID related data
-struct EDIDData {
- int mMode, mWidth, mHeight, mFps;
- // Predetermined ordering for each mode
- int mModeOrder;
- EDIDData(int mode, int width, int height, int fps, int order)
- : mMode(mode), mWidth(width), mHeight(height), mFps(fps), mModeOrder(order)
- { }
-};
-
class HDMIDisplay
{
public:
@@ -71,7 +62,7 @@
int getActiveConfig() const { return mActiveConfig; };
int setActiveConfig(int newConfig);
int getAttrForConfig(int config, uint32_t& xres,
- uint32_t& yres, uint32_t& refresh) const;
+ uint32_t& yres, uint32_t& refresh, uint32_t& fps) const;
int getDisplayConfigs(uint32_t* configs, size_t* numConfigs) const;
private:
@@ -90,10 +81,12 @@
bool isInterlacedMode(int mode);
void resetInfo();
void setAttributes();
- void getAttrForMode(uint32_t& width, uint32_t& height, uint32_t& fps);
int openDeviceNode(const char* node, int fileMode) const;
int getModeIndex(int mode);
bool isValidConfigChange(int newConfig);
+ void requestNewPage(int pageNumber);
+ void readConfigs();
+ bool readResFile(char* configBuffer);
int mFd;
int mFbNum;
@@ -105,11 +98,11 @@
int mActiveConfig;
// mEDIDModes contains a list of HDMI video formats (modes) supported by the
// HDMI display
- int mEDIDModes[64];
+ int mEDIDModes[HDMI_VFRMT_MAX];
int mModeCount;
fb_var_screeninfo mVInfo;
// Holds all the HDMI modes and timing info supported by driver
- msm_hdmi_mode_timing_info* supported_video_mode_lut;
+ msm_hdmi_mode_timing_info *mDisplayConfigs;
uint32_t mXres, mYres, mVsyncPeriod, mPrimaryWidth, mPrimaryHeight;
bool mMDPScalingMode;
bool mUnderscanSupported;
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index fd3bb69..32e8474 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -809,10 +809,11 @@
const size_t NUM_DISPLAY_ATTRIBUTES = (sizeof(DISPLAY_ATTRIBUTES) /
sizeof(DISPLAY_ATTRIBUTES)[0]);
- uint32_t xres = 0, yres = 0, refresh = 0;
+ uint32_t xres = 0, yres = 0, refresh = 0, fps = 0;
int ret = 0;
if (hotPluggable) {
- ret = ctx->mHDMIDisplay->getAttrForConfig(config, xres, yres, refresh);
+ ret = ctx->mHDMIDisplay->getAttrForConfig(config, xres,
+ yres, refresh, fps);
if(ret < 0) {
ALOGE("%s Error getting attributes for config %d",
__FUNCTION__, config);