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
This commit is contained in:
Saurabh Shah
2015-02-10 15:37:39 -08:00
parent 8557548cfb
commit 90c55cfcf8
9 changed files with 533 additions and 67 deletions

View File

@@ -226,18 +226,11 @@ static void reset(hwc_context_t *ctx, int numDisplays,
}
static void scaleDisplayFrame(hwc_context_t *ctx, int dpy,
hwc_display_contents_1_t *list) {
uint32_t origXres = ctx->dpyAttr[dpy].xres;
uint32_t origYres = ctx->dpyAttr[dpy].yres;
uint32_t newXres = ctx->dpyAttr[dpy].xres_new;
uint32_t newYres = ctx->dpyAttr[dpy].yres_new;
float xresRatio = (float)origXres / (float)newXres;
float yresRatio = (float)origYres / (float)newYres;
static void scaleDisplayFrame(hwc_display_contents_1_t *list, float xresRatio,
float yresRatio) {
for (size_t i = 0; i < list->numHwLayers; i++) {
hwc_layer_1_t *layer = &list->hwLayers[i];
hwc_rect_t& displayFrame = layer->displayFrame;
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
uint32_t layerWidth = displayFrame.right - displayFrame.left;
uint32_t layerHeight = displayFrame.bottom - displayFrame.top;
displayFrame.left = (int)(xresRatio * (float)displayFrame.left);
@@ -249,6 +242,35 @@ static void scaleDisplayFrame(hwc_context_t *ctx, int dpy,
}
}
static void handleFbScaling(hwc_context_t *ctx, int dpy,
hwc_display_contents_1_t *list) {
//We could switch to a config that does not lead to fb scaling, but
//we need to update older display frames and ratios.
if (ctx->dpyAttr[dpy].fbScaling or ctx->dpyAttr[dpy].configSwitched) {
uint32_t xresPanel = ctx->dpyAttr[dpy].xres;
uint32_t yresPanel = ctx->dpyAttr[dpy].yres;
uint32_t xresFB = ctx->dpyAttr[dpy].xresFB;
uint32_t yresFB = ctx->dpyAttr[dpy].yresFB;
float xresRatio = (float)xresPanel / (float)xresFB;
float yresRatio = (float)yresPanel / (float)yresFB;
if(list->flags & HWC_GEOMETRY_CHANGED) {
//In case of geometry changes f/w resets displays frames w.r.t to
//FB's dimensions. So any config switch is automatically absorbed.
scaleDisplayFrame(list, xresRatio, yresRatio);
} else if (ctx->dpyAttr[dpy].configSwitched) {
//If there is a primary panel resolution switch without a geometry
//change we need to scale-back the previous ratio used and then use
//the current ratio. i.e use current ratio / prev ratio
scaleDisplayFrame(list,
xresRatio / ctx->dpyAttr[dpy].fbWidthScaleRatio,
yresRatio / ctx->dpyAttr[dpy].fbHeightScaleRatio);
}
ctx->dpyAttr[dpy].configSwitched = false;
ctx->dpyAttr[dpy].fbWidthScaleRatio = xresRatio;
ctx->dpyAttr[dpy].fbHeightScaleRatio = yresRatio;
}
}
static int hwc_prepare_primary(hwc_composer_device_1 *dev,
hwc_display_contents_1_t *list) {
ATRACE_CALL();
@@ -277,9 +299,7 @@ static int hwc_prepare_primary(hwc_composer_device_1 *dev,
ctx->dpyAttr[dpy].isActive = true;
}
if (ctx->dpyAttr[dpy].customFBSize &&
list->flags & HWC_GEOMETRY_CHANGED)
scaleDisplayFrame(ctx, dpy, list);
handleFbScaling(ctx, dpy, list);
reset_layer_prop(ctx, dpy, (int)list->numHwLayers - 1);
setListStats(ctx, list, dpy);
@@ -834,8 +854,8 @@ int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp,
hotPluggable ? refresh : ctx->dpyAttr[disp].vsync_period;
break;
case HWC_DISPLAY_WIDTH:
if (ctx->dpyAttr[disp].customFBSize)
values[i] = ctx->dpyAttr[disp].xres_new;
if (ctx->dpyAttr[disp].fbScaling)
values[i] = ctx->dpyAttr[disp].xresFB;
else
values[i] = hotPluggable ? xres : ctx->dpyAttr[disp].xres;
@@ -843,8 +863,8 @@ int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp,
values[i]);
break;
case HWC_DISPLAY_HEIGHT:
if (ctx->dpyAttr[disp].customFBSize)
values[i] = ctx->dpyAttr[disp].yres_new;
if (ctx->dpyAttr[disp].fbScaling)
values[i] = ctx->dpyAttr[disp].yresFB;
else
values[i] = hotPluggable ? yres : ctx->dpyAttr[disp].yres;
ALOGD("%s disp = %d, height = %d",__FUNCTION__, disp,
@@ -879,6 +899,10 @@ void hwc_dump(struct hwc_composer_device_1* dev, char *buff, int buff_len)
dumpsys_log(aBuf, " DynRefreshRate=%d\n",
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].dynRefreshRate);
for(int dpy = 0; dpy < HWC_NUM_DISPLAY_TYPES; dpy++) {
if(dpy == HWC_DISPLAY_PRIMARY)
dumpsys_log(aBuf, "Dpy %d: FB Scale Ratio w %.1f, h %.1f\n", dpy,
ctx->dpyAttr[dpy].fbWidthScaleRatio,
ctx->dpyAttr[dpy].fbHeightScaleRatio);
if(ctx->mMDPComp[dpy])
ctx->mMDPComp[dpy]->dump(aBuf, ctx);
}

View File

@@ -49,11 +49,9 @@ IFBUpdate::IFBUpdate(hwc_context_t *ctx, const int& dpy) : mDpy(dpy) {
unsigned int size = 0;
uint32_t xres = ctx->dpyAttr[mDpy].xres;
uint32_t yres = ctx->dpyAttr[mDpy].yres;
if (ctx->dpyAttr[dpy].customFBSize) {
//GPU will render and compose at new resolution
//So need to have FB at new resolution
xres = ctx->dpyAttr[mDpy].xres_new;
yres = ctx->dpyAttr[mDpy].yres_new;
if (ctx->dpyAttr[dpy].fbScaling) {
xres = ctx->dpyAttr[mDpy].xresFB;
yres = ctx->dpyAttr[mDpy].yresFB;
}
getBufferAttributes((int)xres, (int)yres,
HAL_PIXEL_FORMAT_RGBA_8888,
@@ -168,7 +166,7 @@ bool FBUpdateNonSplit::configure(hwc_context_t *ctx, hwc_display_contents_1 *lis
// No FB update optimization on (1) Custom FB resolution,
// (2) External Mirror mode, (3) External orientation
if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
&& !ctx->mExtOrientation) {
sourceCrop = fbUpdatingRect;
displayFrame = fbUpdatingRect;
@@ -191,7 +189,7 @@ bool FBUpdateNonSplit::configure(hwc_context_t *ctx, hwc_display_contents_1 *lis
} else if((mDpy && !extOrient
&& !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
if(ctx->mOverlay->isUIScalingOnExternalSupported() &&
!ctx->dpyAttr[mDpy].customFBSize) {
!ctx->dpyAttr[mDpy].fbScaling) {
getNonWormholeRegion(list, sourceCrop);
displayFrame = sourceCrop;
}
@@ -300,7 +298,7 @@ bool FBUpdateSplit::configure(hwc_context_t *ctx,
// No FB update optimization on (1) Custom FB resolution,
// (2) External Mirror mode, (3) External orientation
if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
&& !ctx->mExtOrientation) {
sourceCrop = fbUpdatingRect;
displayFrame = fbUpdatingRect;
@@ -319,7 +317,7 @@ bool FBUpdateSplit::configure(hwc_context_t *ctx,
} else if((mDpy && !extOrient
&& !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
if(!qdutils::MDPVersion::getInstance().is8x26() &&
!ctx->dpyAttr[mDpy].customFBSize) {
!ctx->dpyAttr[mDpy].fbScaling) {
getNonWormholeRegion(list, sourceCrop);
displayFrame = sourceCrop;
}
@@ -466,7 +464,7 @@ bool FBSrcSplit::configure(hwc_context_t *ctx, hwc_display_contents_1 *list,
// No FB update optimization on (1) Custom FB resolution,
// (2) External Mirror mode, (3) External orientation
if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
&& !ctx->mExtOrientation) {
sourceCrop = fbUpdatingRect;
displayFrame = fbUpdatingRect;
@@ -487,7 +485,7 @@ bool FBSrcSplit::configure(hwc_context_t *ctx, hwc_display_contents_1 *list,
} else if((mDpy && !extOrient
&& !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
if(!qdutils::MDPVersion::getInstance().is8x26() &&
!ctx->dpyAttr[mDpy].customFBSize) {
!ctx->dpyAttr[mDpy].fbScaling) {
getNonWormholeRegion(list, sourceCrop);
displayFrame = sourceCrop;
}

View File

@@ -121,13 +121,8 @@ static void getDisplayAttributes(hwc_context_t* ctx, const Parcel* inParcel,
Parcel* outParcel) {
int dpy = inParcel->readInt32();
outParcel->writeInt32(ctx->dpyAttr[dpy].vsync_period);
if (ctx->dpyAttr[dpy].customFBSize) {
outParcel->writeInt32(ctx->dpyAttr[dpy].xres_new);
outParcel->writeInt32(ctx->dpyAttr[dpy].yres_new);
} else {
outParcel->writeInt32(ctx->dpyAttr[dpy].xres);
outParcel->writeInt32(ctx->dpyAttr[dpy].yres);
}
outParcel->writeFloat(ctx->dpyAttr[dpy].xdpi);
outParcel->writeFloat(ctx->dpyAttr[dpy].ydpi);
//XXX: Need to check what to return for HDMI
@@ -355,6 +350,121 @@ static void setS3DMode(hwc_context_t* ctx, int mode) {
}
}
static status_t setActiveConfig(hwc_context_t* ctx, const Parcel *inParcel,
Parcel *outParcel) {
uint32_t index = inParcel->readInt32();
int dpy = inParcel->readInt32();
//Currently only primary supported
if(dpy > HWC_DISPLAY_PRIMARY) {
return BAD_VALUE;
}
Configs *configs = Configs::getInstance();
if(configs == NULL) {
ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
return INVALID_OPERATION;
}
if(configs->getActiveConfig() == index) {
ALOGI("%s(): Config %u is already set", __FUNCTION__, index);
return ALREADY_EXISTS;
}
ctx->mDrawLock.lock();
//Updates the necessary sysfs nodes and reads split info again which is
//needed to reinitialize composition resources.
if(configs->setActiveConfig(index) == false) {
ALOGE("%s(): Failed to set config %u", __FUNCTION__, index);
ctx->mDrawLock.unlock();
return UNKNOWN_ERROR;
}
qdutils::DisplayAttributes attr = configs->getAttributes(index);
ctx->dpyAttr[dpy].xres = attr.xres;
ctx->dpyAttr[dpy].yres = attr.yres;
ctx->dpyAttr[dpy].fbScaling = ((ctx->dpyAttr[dpy].xres !=
ctx->dpyAttr[dpy].xresFB) || (ctx->dpyAttr[dpy].yres !=
ctx->dpyAttr[dpy].yresFB));
destroyCompositionResources(ctx, dpy);
initCompositionResources(ctx, dpy);
ctx->dpyAttr[dpy].configSwitched = true;
ctx->mDrawLock.unlock();
ctx->proc->invalidate(ctx->proc);
return NO_ERROR;
}
static status_t getActiveConfig(hwc_context_t* ctx, const Parcel *inParcel,
Parcel *outParcel) {
Locker::Autolock _sl(ctx->mDrawLock);
int dpy = inParcel->readInt32();
//Currently only primary supported
if(dpy > HWC_DISPLAY_PRIMARY) {
return BAD_VALUE;
}
Configs *configs = Configs::getInstance();
if(configs == NULL) {
ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
return INVALID_OPERATION;
}
outParcel->writeInt32(configs->getActiveConfig());
return NO_ERROR;
}
static status_t getConfigCount(hwc_context_t* ctx, const Parcel *inParcel,
Parcel *outParcel) {
Locker::Autolock _sl(ctx->mDrawLock);
int dpy = inParcel->readInt32();
//Currently only primary supported
if(dpy > HWC_DISPLAY_PRIMARY) {
return BAD_VALUE;
}
Configs *configs = Configs::getInstance();
if(configs == NULL) {
ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
return INVALID_OPERATION;
}
outParcel->writeInt32(configs->getConfigCount());
return NO_ERROR;
}
static status_t getDisplayAttributesForConfig(hwc_context_t* ctx,
const Parcel *inParcel, Parcel *outParcel) {
Locker::Autolock _sl(ctx->mDrawLock);
uint32_t index = inParcel->readInt32();
int dpy = inParcel->readInt32();
//Currently only primary supported
if(dpy > HWC_DISPLAY_PRIMARY) {
return BAD_VALUE;
}
Configs *configs = Configs::getInstance();
if(configs == NULL) {
ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
return INVALID_OPERATION;
}
//xres, yres are used from the Config class, we assume for now that the
//other params are the same. This might change in the future.
outParcel->writeInt32(ctx->dpyAttr[dpy].vsync_period);
qdutils::DisplayAttributes attr = configs->getAttributes(index);
outParcel->writeInt32(attr.xres);
outParcel->writeInt32(attr.yres);
outParcel->writeFloat(ctx->dpyAttr[dpy].xdpi);
outParcel->writeFloat(ctx->dpyAttr[dpy].ydpi);
outParcel->writeInt32(ctx->mMDP.panel);
return NO_ERROR;
}
status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
Parcel* outParcel) {
status_t ret = NO_ERROR;
@@ -418,6 +528,19 @@ status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
case IQService::SET_S3D_MODE:
setS3DMode(mHwcContext, inParcel->readInt32());
break;
case IQService::SET_ACTIVE_CONFIG:
ret = setActiveConfig(mHwcContext, inParcel, outParcel);
break;
case IQService::GET_ACTIVE_CONFIG:
ret = getActiveConfig(mHwcContext, inParcel, outParcel);
break;
case IQService::GET_CONFIG_COUNT:
ret = getConfigCount(mHwcContext, inParcel, outParcel);
break;
case IQService::GET_DISPLAY_ATTRIBUTES_FOR_CONFIG:
ret = getDisplayAttributesForConfig(mHwcContext, inParcel,
outParcel);
break;
default:
ret = NO_ERROR;
}

View File

@@ -93,33 +93,38 @@ bool isValidResolution(hwc_context_t *ctx, uint32_t xres, uint32_t yres)
(xres < MIN_DISPLAY_XRES || yres < MIN_DISPLAY_YRES));
}
void changeResolution(hwc_context_t *ctx, int xres_orig, int yres_orig,
static void handleFbScaling(hwc_context_t *ctx, int xresPanel, int yresPanel,
int width, int height) {
const int dpy = HWC_DISPLAY_PRIMARY;
//Store original display resolution.
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_orig;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_orig;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = false;
ctx->dpyAttr[dpy].xresFB = xresPanel;
ctx->dpyAttr[dpy].yresFB = yresPanel;
ctx->dpyAttr[dpy].fbScaling = false;
char property[PROPERTY_VALUE_MAX] = {'\0'};
char *yptr = NULL;
if (property_get("debug.hwc.fbsize", property, NULL) > 0) {
yptr = strcasestr(property,"x");
if(yptr) {
int xres_new = atoi(property);
int yres_new = atoi(yptr + 1);
if (isValidResolution(ctx,xres_new,yres_new) &&
xres_new != xres_orig && yres_new != yres_orig) {
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_new;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_new;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = true;
int xresFB = atoi(property);
int yresFB = atoi(yptr + 1);
if (isValidResolution(ctx, xresFB, yresFB) &&
xresFB != xresPanel && yresFB != yresPanel) {
ctx->dpyAttr[dpy].xresFB = xresFB;
ctx->dpyAttr[dpy].yresFB = yresFB;
ctx->dpyAttr[dpy].fbScaling = true;
//Caluculate DPI according to changed resolution.
float xdpi = ((float)xres_new * 25.4f) / (float)width;
float ydpi = ((float)yres_new * 25.4f) / (float)height;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi;
//Calculate DPI according to changed resolution.
float xdpi = ((float)xresFB * 25.4f) / (float)width;
float ydpi = ((float)yresFB * 25.4f) / (float)height;
ctx->dpyAttr[dpy].xdpi = xdpi;
ctx->dpyAttr[dpy].ydpi = ydpi;
}
}
}
ctx->dpyAttr[dpy].fbWidthScaleRatio = (float) ctx->dpyAttr[dpy].xres /
(float) ctx->dpyAttr[dpy].xresFB;
ctx->dpyAttr[dpy].fbHeightScaleRatio = (float) ctx->dpyAttr[dpy].yres /
(float) ctx->dpyAttr[dpy].yresFB;
}
// Initialize hdmi display attributes based on
@@ -234,8 +239,7 @@ static int openFramebufferDevice(hwc_context_t *ctx)
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period =
(uint32_t)(1000000000l / fps);
//To change resolution of primary display
changeResolution(ctx, info.xres, info.yres, info.width, info.height);
handleFbScaling(ctx, info.xres, info.yres, info.width, info.height);
//Unblank primary on first boot
if(ioctl(fb_fd, FBIOBLANK,FB_BLANK_UNBLANK) < 0) {

View File

@@ -112,12 +112,6 @@ struct DisplayAttributes {
int mAsWidthRatio;
int mAsHeightRatio;
//If property fbsize set via adb shell debug.hwc.fbsize = XRESxYRES
//following fields are used.
bool customFBSize;
uint32_t xres_new;
uint32_t yres_new;
// This is the 3D mode to which the TV is set
// The mode may be set via the appearance of a layer with 3D format
// or by forcing the mode via binder.
@@ -130,6 +124,18 @@ struct DisplayAttributes {
// HDMI_S3D_NONE
int s3dMode;
bool s3dModeForced;
//If property fbsize set via adb shell debug.hwc.fbsize = XRESxYRES
//following fields are used.
//Also used when the actual panel's dimensions change and FB remains
//constant
bool fbScaling;
uint32_t xresFB; //FB's width, by default from VSCREEN overridden by prop
uint32_t yresFB; //FB's height, by default from VSCREEN overridden by prop
float fbWidthScaleRatio; // Panel Width / FB Width
float fbHeightScaleRatio; // Panel Height / FB Height
//If configuration changed dynamically without subsequent GEOMETRY changes
//we may still need to adjust destination params
bool configSwitched;
};
struct ListStats {

View File

@@ -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 @@ int getDisplayAttributes(int dpy, DisplayAttributes_t& dpyattr) {
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 @@ int configureDynRefreshRate(uint32_t op, uint32_t refreshRate) {
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
// ----------------------------------------------------------------------------

View File

@@ -31,11 +31,8 @@
#include <mdp_version.h>
#include <hardware/hwcomposer.h>
// This header is for clients to use to set/get global display configuration
// The functions in this header run in the client process and wherever necessary
// do a binder call to HWC to get/set data.
// This header is for clients to use to set/get global display configuration.
// Only primary and external displays are supported here.
// WiFi/virtual displays are not supported.
namespace qdutils {
@@ -69,14 +66,20 @@ enum {
// Display Attributes that are available to clients of this library
// Not to be confused with a similar struct in hwc_utils (in the hwc namespace)
struct DisplayAttributes_t {
typedef struct DisplayAttributes {
uint32_t vsync_period; //nanoseconds
uint32_t xres;
uint32_t yres;
float xdpi;
float ydpi;
char panel_type;
};
DisplayAttributes() : vsync_period(0), xres(0), yres(0), xdpi(0.0f),
ydpi(0.0f), panel_type(0) {}
} DisplayAttributes_t;
//=============================================================================
// The functions below run in the client process and wherever necessary
// do a binder call to HWC to get/set data.
// Check if external display is connected. Useful to check before making
// calls for external displays
@@ -104,4 +107,66 @@ int setSecondaryDisplayStatus(int dpy, uint32_t status);
// Enable/Disable/Set refresh rate dynamically
int configureDynRefreshRate(uint32_t op, uint32_t refreshRate);
// Returns the number of configs supported for the display on success.
// Returns -1 on error.
// Only primary display supported for now, value of dpy ignored.
int getConfigCount(int dpy);
// Returns the index of config that is current set for the display on success.
// Returns -1 on error.
// Only primary display supported for now, value of dpy ignored.
int getActiveConfig(int dpy);
// Sets the config for the display on success and returns 0.
// Returns -1 on error.
// Only primary display supported for now, value of dpy ignored
int setActiveConfig(int configIndex, int dpy);
// Returns the attributes for the specified config for the display on success.
// Returns xres and yres as 0 on error.
// Only primary display supported for now, value of dpy ignored
DisplayAttributes getDisplayAttributes(int configIndex, int dpy);
//=============================================================================
// The functions and methods below run in the context of HWC and
// are called in response to binder calls from clients
class Configs {
public:
DisplayAttributes getAttributes(const uint32_t& index) const;
uint32_t getActiveConfig() const;
bool setActiveConfig(const uint32_t& index);
uint32_t getConfigCount() const;
static Configs *getInstance();
private:
enum { CONFIGS_MAX = 32 };
Configs();
bool init();
bool getCurrentMode(DisplayAttributes& dpyAttr);
DisplayAttributes mConfigs[CONFIGS_MAX];
char *mModeStr[CONFIGS_MAX];
uint32_t mActiveConfig;
uint32_t mConfigsSupported;
static Configs *sConfigs;
};
inline DisplayAttributes Configs::getAttributes(const uint32_t& index) const {
if(index >= mConfigsSupported) {
ALOGE("%s() Invalid index %d, max %d", __FUNCTION__, index,
mConfigsSupported);
return DisplayAttributes(); //All 0s
}
return mConfigs[index];
}
// Retuns the current config index, -1 if called without a setActiveConfig
inline uint32_t Configs::getActiveConfig() const {
return mActiveConfig;
}
inline uint32_t Configs::getConfigCount() const {
return mConfigsSupported;
}
}; //namespace

View File

@@ -152,11 +152,11 @@ public:
bool is8x16();
bool is8x39();
bool is8992();
bool updateSplitInfo();
private:
bool updateSysFsInfo();
void updatePanelInfo();
bool updateSplitInfo();
int tokenizeParams(char *inputParams, const char *delim,
char* tokenStr[], int *idx);
int mFd;

View File

@@ -63,6 +63,10 @@ public:
SET_FRAME_DUMP_CONFIG = 21, // Provides ability to set the frame dump config
SET_S3D_MODE = 22, // Set the 3D mode as specified in msm_hdmi_modes.h
CONNECT_HDMI_CLIENT = 23, // Connect HDMI CEC HAL Client
SET_ACTIVE_CONFIG = 25, //Set a specified display config
GET_ACTIVE_CONFIG = 26, //Get the current config index
GET_CONFIG_COUNT = 27, //Get the number of supported display configs
GET_DISPLAY_ATTRIBUTES_FOR_CONFIG = 28, //Get attr for specified config
COMMAND_LIST_END = 400,
};