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/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 7c69633..c5edcc7 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -226,18 +226,11 @@
}
-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 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 @@
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 @@
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 @@
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 @@
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);
}
diff --git a/libhwcomposer/hwc_fbupdate.cpp b/libhwcomposer/hwc_fbupdate.cpp
index ef83008..692ce29 100644
--- a/libhwcomposer/hwc_fbupdate.cpp
+++ b/libhwcomposer/hwc_fbupdate.cpp
@@ -49,11 +49,9 @@
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 @@
// 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 @@
} 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 @@
// 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 @@
} 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 @@
// 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 @@
} 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;
}
diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp
index 487ec77..9b1f81d 100644
--- a/libhwcomposer/hwc_qclient.cpp
+++ b/libhwcomposer/hwc_qclient.cpp
@@ -121,13 +121,8 @@
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->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 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 @@
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;
}
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index a9cd150..043d7c2 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -93,33 +93,38 @@
(xres < MIN_DISPLAY_XRES || yres < MIN_DISPLAY_YRES));
}
-void changeResolution(hwc_context_t *ctx, int xres_orig, int yres_orig,
- int width, int height) {
+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 @@
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) {
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 23157d4..392fd61 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -112,12 +112,6 @@
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 @@
// 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 {