hwc: Add support for panel resolution switch
Add support for panel resolution switch. This feature makes use of
the Linux modedb feature that lists available modes in
/sys/class/graphics/fb0/modes and accepts a mode change when written
to /sys/class/graphics/fb0/mode
Clients can link to APIs exposed in display_config.h, these
internally resolve to binder calls into HWC. For debugging, mode
changes can be made over binder using shell commands.
adb shell service call display.qservice CODE ARGS
ARGS can include config index and display (only primary supported)
setActiveConfig():
adb shell service call display.qservice 25 i32 INDEX i32 0
getActiveConfig():
adb shell service call display.qservice 26 i32 0
getConfigCount():
adb shell service call display.qservice 27 i32 0
getDisplayAttributes():
adb shell service call display.qservice 28 i32 INDEX i32 0
Change-Id: I97d34cc9c0e521a3bd5c948eeea6d1e07db7b7ff
diff --git a/libqdutils/display_config.cpp b/libqdutils/display_config.cpp
index 03a7046..40b1ef7 100644
--- a/libqdutils/display_config.cpp
+++ b/libqdutils/display_config.cpp
@@ -27,14 +27,24 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <display_config.h>
#include <QServiceUtils.h>
+#include <qd_utils.h>
using namespace android;
using namespace qService;
namespace qdutils {
+//=============================================================================
+// The functions below run in the client process and wherever necessary
+// do a binder call to HWC to get/set data.
+
int isExternalConnected(void) {
int ret;
status_t err = (status_t) FAILED_TRANSACTION;
@@ -70,7 +80,7 @@
dpyattr.ydpi = outParcel.readFloat();
dpyattr.panel_type = (char) outParcel.readInt32();
} else {
- ALOGE("%s: Failed to get display attributes err=%d", __FUNCTION__, err);
+ ALOGE("%s() failed with err %d", __FUNCTION__, err);
}
return err;
}
@@ -170,6 +180,238 @@
return err;
}
+int getConfigCount(int /*dpy*/) {
+ int numConfigs = -1;
+ sp<IQService> binder = getBinder();
+ if(binder != NULL) {
+ Parcel inParcel, outParcel;
+ inParcel.writeInt32(DISPLAY_PRIMARY);
+ status_t err = binder->dispatch(IQService::GET_CONFIG_COUNT,
+ &inParcel, &outParcel);
+ if(!err) {
+ numConfigs = outParcel.readInt32();
+ ALOGI("%s() Received num configs %d", __FUNCTION__, numConfigs);
+ } else {
+ ALOGE("%s() failed with err %d", __FUNCTION__, err);
+ }
+ }
+ return numConfigs;
+}
+
+int getActiveConfig(int /*dpy*/) {
+ int configIndex = -1;
+ sp<IQService> binder = getBinder();
+ if(binder != NULL) {
+ Parcel inParcel, outParcel;
+ inParcel.writeInt32(DISPLAY_PRIMARY);
+ status_t err = binder->dispatch(IQService::GET_ACTIVE_CONFIG,
+ &inParcel, &outParcel);
+ if(!err) {
+ configIndex = outParcel.readInt32();
+ ALOGI("%s() Received active config index %d", __FUNCTION__,
+ configIndex);
+ } else {
+ ALOGE("%s() failed with err %d", __FUNCTION__, err);
+ }
+ }
+ return configIndex;
+}
+
+int setActiveConfig(int configIndex, int /*dpy*/) {
+ status_t err = (status_t) FAILED_TRANSACTION;
+ sp<IQService> binder = getBinder();
+ if(binder != NULL) {
+ Parcel inParcel, outParcel;
+ inParcel.writeInt32(configIndex);
+ inParcel.writeInt32(DISPLAY_PRIMARY);
+ err = binder->dispatch(IQService::SET_ACTIVE_CONFIG,
+ &inParcel, &outParcel);
+ if(!err) {
+ ALOGI("%s() Successfully set active config index %d", __FUNCTION__,
+ configIndex);
+ } else {
+ ALOGE("%s() failed with err %d", __FUNCTION__, err);
+ }
+ }
+ return err;
+}
+
+DisplayAttributes getDisplayAttributes(int configIndex, int /*dpy*/) {
+ DisplayAttributes dpyattr;
+ sp<IQService> binder = getBinder();
+ if(binder != NULL) {
+ Parcel inParcel, outParcel;
+ inParcel.writeInt32(configIndex);
+ inParcel.writeInt32(DISPLAY_PRIMARY);
+ status_t err = binder->dispatch(
+ IQService::GET_DISPLAY_ATTRIBUTES_FOR_CONFIG, &inParcel,
+ &outParcel);
+ if(!err) {
+ dpyattr.vsync_period = outParcel.readInt32();
+ dpyattr.xres = outParcel.readInt32();
+ dpyattr.yres = outParcel.readInt32();
+ dpyattr.xdpi = outParcel.readFloat();
+ dpyattr.ydpi = outParcel.readFloat();
+ dpyattr.panel_type = (char) outParcel.readInt32();
+ ALOGI("%s() Received attrs for index %d: xres %d, yres %d",
+ __FUNCTION__, configIndex, dpyattr.xres, dpyattr.yres);
+ } else {
+ ALOGE("%s() failed with err %d", __FUNCTION__, err);
+ }
+ }
+ return dpyattr;
+}
+
+//=============================================================================
+// The functions/methods below run in the context of HWC and
+// are called in response to binder calls from clients
+
+Configs* Configs::getInstance() {
+ if(sConfigs == NULL) {
+ sConfigs = new Configs();
+ if(sConfigs->init() == false) {
+ ALOGE("%s(): Configs initialization failed", __FUNCTION__);
+ delete sConfigs;
+ sConfigs = NULL;
+ }
+ }
+ return sConfigs;
+}
+
+Configs::Configs() : mActiveConfig(0), mConfigsSupported(0) {}
+
+bool Configs::init() {
+ DisplayAttributes dpyAttr;
+ if(not getCurrentMode(dpyAttr)) {
+ ALOGE("%s(): Mode switch is disabled", __FUNCTION__);
+ return false;
+ }
+
+ FILE *fHnd;
+ size_t len = PAGE_SIZE;
+ ssize_t read = 0;
+ uint32_t configCount = 0;
+ char sysfsPath[MAX_SYSFS_FILE_PATH];
+
+ memset(sysfsPath, '\0', sizeof(sysfsPath));
+ snprintf(sysfsPath , sizeof(sysfsPath),
+ "/sys/class/graphics/fb0/modes");
+
+ fHnd = fopen(sysfsPath, "r");
+ if (fHnd == NULL) {
+ ALOGE("%s(): Opening file %s failed with error %s", __FUNCTION__,
+ sysfsPath, strerror(errno));
+ return false;
+ }
+
+ memset(mModeStr, 0, sizeof(mModeStr));
+ while((configCount < CONFIGS_MAX) and
+ ((read = getline(&mModeStr[configCount], &len, fHnd)) > 0)) {
+ //String is of form "U:1600x2560p-0". Documentation/fb/modedb.txt in the
+ //kernel has more info on the format.
+ char *xptr = strcasestr(mModeStr[configCount], ":");
+ char *yptr = strcasestr(mModeStr[configCount], "x");
+ if(xptr && yptr) {
+ mConfigs[configCount].xres = atoi(xptr + 1);
+ mConfigs[configCount].yres = atoi(yptr + 1);
+ ALOGI("%s(): Parsed Config %s", __FUNCTION__,
+ mModeStr[configCount]);
+ ALOGI("%s(): Config %u: %u x %u", __FUNCTION__, configCount,
+ mConfigs[configCount].xres, mConfigs[configCount].yres);
+ if(mConfigs[configCount].xres == dpyAttr.xres and
+ mConfigs[configCount].yres == dpyAttr.yres) {
+ mActiveConfig = configCount;
+ }
+ } else {
+ ALOGE("%s(): Tokenizing str %s failed", __FUNCTION__,
+ mModeStr[configCount]);
+ //Free memory allocated internally by getline()
+ for(uint32_t i = 0; i <= configCount; i++) {
+ free(mModeStr[i]);
+ }
+ fclose(fHnd);
+ return false;
+ }
+ configCount++;
+ }
+
+ fclose(fHnd);
+
+ if(configCount == 0) {
+ ALOGE("%s No configs found", __FUNCTION__);
+ return false;
+ }
+ mConfigsSupported = configCount;
+ return true;
+}
+
+bool Configs::getCurrentMode(DisplayAttributes& dpyAttr) {
+ bool ret = false;
+ FILE *fHnd = fopen("/sys/class/graphics/fb0/mode", "r");
+ if(fHnd) {
+ char *buffer = NULL; //getline will allocate
+ size_t len = PAGE_SIZE;
+ if(getline(&buffer, &len, fHnd) > 0) {
+ //String is of form "U:1600x2560p-0". Documentation/fb/modedb.txt in
+ //kernel has more info on the format.
+ char *xptr = strcasestr(buffer, ":");
+ char *yptr = strcasestr(buffer, "x");
+ if(xptr && yptr) {
+ dpyAttr.xres = atoi(xptr + 1);
+ dpyAttr.yres = atoi(yptr + 1);
+ ALOGI("%s(): Parsed Current Config Str %s", __FUNCTION__,
+ buffer);
+ ALOGI("%s(): Current Config: %u x %u", __FUNCTION__,
+ dpyAttr.xres, dpyAttr.yres);
+ ret = true;
+ }
+ }
+
+ if(buffer)
+ free(buffer);
+
+ fclose(fHnd);
+ }
+ return ret;
+}
+
+bool Configs::setActiveConfig(const uint32_t& index) {
+ if(index >= mConfigsSupported) {
+ ALOGE("%s(): Invalid Index %u", __FUNCTION__, index);
+ return false;
+ }
+
+ bool ret = true;
+ int fd = -1;
+ size_t len = PAGE_SIZE;
+ char sysfsPath[MAX_SYSFS_FILE_PATH];
+ memset(sysfsPath, '\0', sizeof(sysfsPath));
+ snprintf(sysfsPath , sizeof(sysfsPath),
+ "/sys/class/graphics/fb0/mode");
+
+ fd = open(sysfsPath, O_WRONLY);
+ if (fd < 0) {
+ ALOGE("%s(): Opening file %s failed", __FUNCTION__, sysfsPath);
+ return false;
+ }
+
+ ssize_t written = pwrite(fd, mModeStr[index], strlen(mModeStr[index]), 0);
+ if(written <= 0) {
+ ALOGE("%s(): Writing config %s to %s failed with error: %s",
+ __FUNCTION__, mModeStr[index], sysfsPath, strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ ALOGI("%s(): Successfully set config %u", __FUNCTION__, index);
+ mActiveConfig = index;
+ MDPVersion::getInstance().updateSplitInfo();
+ close(fd);
+ return true;
+}
+
+Configs* Configs::sConfigs = NULL;
+
}; //namespace
// ----------------------------------------------------------------------------