diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp index 7c696337..c5edcc73 100644 --- a/libhwcomposer/hwc.cpp +++ b/libhwcomposer/hwc.cpp @@ -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); } diff --git a/libhwcomposer/hwc_fbupdate.cpp b/libhwcomposer/hwc_fbupdate.cpp index ef830084..692ce298 100644 --- a/libhwcomposer/hwc_fbupdate.cpp +++ b/libhwcomposer/hwc_fbupdate.cpp @@ -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; } diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp index 487ec776..9b1f81de 100644 --- a/libhwcomposer/hwc_qclient.cpp +++ b/libhwcomposer/hwc_qclient.cpp @@ -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->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; } diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp index a9cd150c..043d7c24 100644 --- a/libhwcomposer/hwc_utils.cpp +++ b/libhwcomposer/hwc_utils.cpp @@ -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, - 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 @@ 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) { diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h index 23157d49..392fd617 100644 --- a/libhwcomposer/hwc_utils.h +++ b/libhwcomposer/hwc_utils.h @@ -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 { diff --git a/libqdutils/display_config.cpp b/libqdutils/display_config.cpp index 03a70463..40b1ef72 100644 --- a/libqdutils/display_config.cpp +++ b/libqdutils/display_config.cpp @@ -27,14 +27,24 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include +#include +#include #include #include +#include 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 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 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 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 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 // ---------------------------------------------------------------------------- diff --git a/libqdutils/display_config.h b/libqdutils/display_config.h index 8bafe910..6a66e9fb 100644 --- a/libqdutils/display_config.h +++ b/libqdutils/display_config.h @@ -31,11 +31,8 @@ #include #include -// 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 diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h index aeb2218d..ad92c830 100644 --- a/libqdutils/mdp_version.h +++ b/libqdutils/mdp_version.h @@ -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; diff --git a/libqservice/IQService.h b/libqservice/IQService.h index 78cbd2ae..8c63f1fd 100644 --- a/libqservice/IQService.h +++ b/libqservice/IQService.h @@ -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, };