hwc: Add support for frame rate change on HDMI devices
When an HDMI display is connected, we report all the available
configurations to the framework by translating the EDID data.
This information can be queried, and subsequently used to change
the frame rate of the HDMI display.
Note: this change does not provide support for changing the resolution
of the HDMI display.
Change-Id: I4b8b07e2886efe2a01480aed9d82d36075bb342c
diff --git a/libhdmi/hdmi.cpp b/libhdmi/hdmi.cpp
index 4fb7cfa..1aee664 100644
--- a/libhdmi/hdmi.cpp
+++ b/libhdmi/hdmi.cpp
@@ -101,15 +101,16 @@
}
readCEUnderscanInfo();
readResolution();
- // TODO: Move this to activate
/* Used for changing the resolution
- * getUserMode will get the preferred
- * mode set thru adb shell */
- mCurrentMode = getUserMode();
- if (mCurrentMode == -1) {
+ * getUserConfig will get the preferred
+ * config index set thru adb shell */
+ mActiveConfig = getUserConfig();
+ if (mActiveConfig == -1) {
//Get the best mode and set
- mCurrentMode = getBestMode();
+ mActiveConfig = getBestConfig();
}
+ // Set the mode corresponding to the active index
+ mCurrentMode = mEDIDModes[mActiveConfig];
setAttributes();
// set system property
property_set("hw.hdmiON", "1");
@@ -121,6 +122,14 @@
&& !strcmp(value, "true")) {
mMDPDownscaleEnabled = true;
}
+
+ // XXX: A debug property can be used to enable resolution change for
+ // testing purposes: debug.hwc.enable_resolution_change
+ mEnableResolutionChange = false;
+ if(property_get("debug.hwc.enable_resolution_change", value, "false")
+ && !strcmp(value, "true")) {
+ mEnableResolutionChange = true;
+ }
return 0;
}
@@ -425,8 +434,8 @@
return -1;
}
-/// Returns the user mode set(if any) using adb shell
-int HDMIDisplay::getUserMode() {
+/// 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");
@@ -434,15 +443,16 @@
// We dont support interlaced modes
if(isValidMode(mode) && !isInterlacedMode(mode)) {
ALOGD_IF(DEBUG, "%s: setting the HDMI mode = %d", __FUNCTION__, mode);
- return mode;
+ return getModeIndex(mode);
}
return -1;
}
-// Get the best mode for the current HD TV
-int HDMIDisplay::getBestMode() {
+// 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];
@@ -450,9 +460,19 @@
if (order > bestOrder) {
bestOrder = order;
bestMode = mode;
+ bestModeIndex = i;
}
}
- return bestMode;
+ // 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 bestModeIndex;
}
inline bool HDMIDisplay::isValidMode(int ID)
@@ -696,4 +716,79 @@
mPrimaryWidth = primaryWidth;
}
+int HDMIDisplay::setActiveConfig(int newConfig) {
+ if(newConfig < 0 || newConfig > mModeCount) {
+ ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
+ return -EINVAL;
+ }
+
+ // XXX: Currently, we only support a change in frame rate.
+ // We need to validate the new config before proceeding.
+ if (!isValidConfigChange(newConfig)) {
+ ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
+ return -EINVAL;
+ }
+
+ mCurrentMode = mEDIDModes[newConfig];
+ mActiveConfig = newConfig;
+ activateDisplay();
+ ALOGD("%s config(%d) mode(%d)", __FUNCTION__, mActiveConfig, mCurrentMode);
+ return 0;
+}
+
+// returns false if the xres or yres of the new config do
+// 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);
+ return ((mXres == width) && (mYres == height)) || mEnableResolutionChange;
+}
+
+int HDMIDisplay::getModeIndex(int mode) {
+ int modeIndex = -1;
+ for(int i = 0; i < mModeCount; i++) {
+ if(mode == mEDIDModes[i]) {
+ modeIndex = i;
+ break;
+ }
+ }
+ return modeIndex;
+}
+
+int HDMIDisplay::getAttrForConfig(int config, uint32_t& xres,
+ uint32_t& yres, uint32_t& refresh) 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;
+ }
+ }
+ refresh = (uint32_t) 1000000000l / fps;
+ ALOGD_IF(DEBUG, "%s xres(%d) yres(%d) fps(%d) refresh(%d)", __FUNCTION__,
+ xres, yres, fps, refresh);
+ return 0;
+}
+
+int HDMIDisplay::getDisplayConfigs(uint32_t* configs,
+ size_t* numConfigs) const {
+ if (*numConfigs <= 0) {
+ ALOGE("%s Invalid number of configs (%d)", __FUNCTION__, *numConfigs);
+ return -EINVAL;
+ }
+ *numConfigs = mModeCount;
+ for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
+ configs[configIndex] = (uint32_t)configIndex;
+ }
+ return 0;
+}
+
};
diff --git a/libhdmi/hdmi.h b/libhdmi/hdmi.h
index 605d9be..d262a63 100644
--- a/libhdmi/hdmi.h
+++ b/libhdmi/hdmi.h
@@ -68,6 +68,11 @@
/* when HDMI is an EXTERNAL display, PRIMARY display attributes are needed
for scaling mode */
void setPrimaryAttributes(uint32_t primaryWidth, uint32_t primaryHeight);
+ int getActiveConfig() const { return mActiveConfig; };
+ int setActiveConfig(int newConfig);
+ int getAttrForConfig(int config, uint32_t& xres,
+ uint32_t& yres, uint32_t& refresh) const;
+ int getDisplayConfigs(uint32_t* configs, size_t* numConfigs) const;
private:
int getModeCount() const;
@@ -80,17 +85,26 @@
bool writeHPDOption(int userOption) const;
bool isValidMode(int mode);
int getModeOrder(int mode);
- int getUserMode();
- int getBestMode();
+ int getUserConfig();
+ int getBestConfig();
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);
int mFd;
int mFbNum;
+ // mCurrentMode is the HDMI video format that corresponds to the mEDIDMode
+ // entry referenced by mActiveConfig
int mCurrentMode;
+ // mActiveConfig is the index correponding to the currently active mode for
+ // the HDMI display. It basically indexes the mEDIDMode array
+ int mActiveConfig;
+ // mEDIDModes contains a list of HDMI video formats (modes) supported by the
+ // HDMI display
int mEDIDModes[64];
int mModeCount;
fb_var_screeninfo mVInfo;
@@ -102,6 +116,7 @@
// Downscale feature switch, set via system property
// sys.hwc.mdp_downscale_enabled
bool mMDPDownscaleEnabled;
+ bool mEnableResolutionChange;
int mDisplayId;
};