1 /* 2 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. 3 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above 10 * copyright notice, this list of conditions and the following 11 * disclaimer in the documentation and/or other materials provided 12 * with the distribution. 13 * * Neither the name of The Linux Foundation nor the names of its 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 #include <cutils/log.h> 30 #include <linux/msm_mdp.h> 31 #include "mdp_version.h" 32 #include "qd_utils.h" 33 34 #define DEBUG 0 35 36 ANDROID_SINGLETON_STATIC_INSTANCE(qdutils::MDPVersion); 37 namespace qdutils { 38 39 #define TOKEN_PARAMS_DELIM "=" 40 41 // chip variants have same major number and minor numbers usually vary 42 // for e.g., MDSS_MDP_HW_REV_101 is 0x10010000 43 // 1001 - major number 44 // 0000 - minor number 45 // 8x26 v1 minor number is 0000 46 // v2 minor number is 0001 etc.. 47 #ifndef MDSS_MDP_HW_REV_100 48 #define MDSS_MDP_HW_REV_100 0x10000000 //8974 v1 49 #endif 50 #ifndef MDSS_MDP_HW_REV_101 51 #define MDSS_MDP_HW_REV_101 0x10010000 //8x26 52 #endif 53 #ifndef MDSS_MDP_HW_REV_102 54 #define MDSS_MDP_HW_REV_102 0x10020000 //8974 v2 55 #endif 56 #ifndef MDSS_MDP_HW_REV_103 57 #define MDSS_MDP_HW_REV_103 0x10030000 //8084 58 #endif 59 #ifndef MDSS_MDP_HW_REV_104 60 #define MDSS_MDP_HW_REV_104 0x10040000 //Unused 61 #endif 62 #ifndef MDSS_MDP_HW_REV_105 63 #define MDSS_MDP_HW_REV_105 0x10050000 //8994 64 #endif 65 #ifndef MDSS_MDP_HW_REV_106 66 #define MDSS_MDP_HW_REV_106 0x10060000 //8x16 67 #endif 68 #ifndef MDSS_MDP_HW_REV_107 69 #define MDSS_MDP_HW_REV_107 0x10070000 //Unused 70 #endif 71 #ifndef MDSS_MDP_HW_REV_108 72 #define MDSS_MDP_HW_REV_108 0x10080000 //8x39 & 8x36 73 #endif 74 #ifndef MDSS_MDP_HW_REV_109 75 #define MDSS_MDP_HW_REV_109 0x10090000 //8994 v2 76 #endif 77 #ifndef MDSS_MDP_HW_REV_110 78 #define MDSS_MDP_HW_REV_110 0x100a0000 //8992 79 #endif 80 #ifndef MDSS_MDP_HW_REV_200 81 #define MDSS_MDP_HW_REV_200 0x20000000 //8092 82 #endif 83 #ifndef MDSS_MDP_HW_REV_206 84 #define MDSS_MDP_HW_REV_206 0x20060000 //Future 85 #endif 86 87 MDPVersion::MDPVersion() 88 { 89 mMDPVersion = MDSS_V5; 90 mMdpRev = 0; 91 mRGBPipes = 0; 92 mVGPipes = 0; 93 mDMAPipes = 0; 94 mFeatures = 0; 95 mMDPUpscale = 1; 96 mMDPDownscale = 1; 97 mMacroTileEnabled = false; 98 mLowBw = 0; 99 mHighBw = 0; 100 mSourceSplit = false; 101 mSourceSplitAlways = false; 102 mRGBHasNoScalar = false; 103 mRotDownscale = false; 104 mBlendStages = 4; //min no. of stages supported by MDP. 105 106 // this is the default limit of mixer unless driver reports it. 107 // For resolutions beyond this, we use dual mixer/ping pong split. 108 mMaxMixerWidth = 2048; 109 110 // Default width of MDSS SSPP. For layer resolutions beyond this, we drive 111 // using two SSPP's. 112 mMaxPipeWidth = 2048; 113 114 updatePanelInfo(); 115 116 if(!updateSysFsInfo()) { 117 ALOGE("Unable to read display sysfs node"); 118 } 119 if (mMdpRev == MDP_V3_0_4){ 120 mMDPVersion = MDP_V3_0_4; 121 } 122 else if (mMdpRev == MDP_V3_0_5){ 123 mMDPVersion = MDP_V3_0_5; 124 } 125 126 mHasOverlay = false; 127 if((mMDPVersion >= MDP_V4_0) || 128 (mMDPVersion == MDP_V_UNKNOWN) || 129 (mMDPVersion == MDP_V3_0_4) || 130 (mMDPVersion == MDP_V3_0_5)) 131 mHasOverlay = true; 132 if(!updateSplitInfo()) { 133 ALOGE("Unable to read display split node"); 134 } 135 } 136 137 MDPVersion::~MDPVersion() { 138 close(mFd); 139 } 140 141 int MDPVersion::tokenizeParams(char *inputParams, const char *delim, 142 char* tokenStr[], int *idx) { 143 char *tmp_token = NULL; 144 char *temp_ptr; 145 int index = 0; 146 if (!inputParams) { 147 return -1; 148 } 149 tmp_token = strtok_r(inputParams, delim, &temp_ptr); 150 while (tmp_token != NULL) { 151 tokenStr[index++] = tmp_token; 152 tmp_token = strtok_r(NULL, " ", &temp_ptr); 153 } 154 *idx = index; 155 return 0; 156 } 157 // This function reads the sysfs node to read the primary panel type 158 // and updates information accordingly 159 void MDPVersion::updatePanelInfo() { 160 FILE *displayDeviceFP = NULL; 161 FILE *panelInfoNodeFP = NULL; 162 char fbType[MAX_FRAME_BUFFER_NAME_SIZE]; 163 const char *strCmdPanel = "mipi dsi cmd panel"; 164 const char *strVideoPanel = "mipi dsi video panel"; 165 const char *strLVDSPanel = "lvds panel"; 166 const char *strEDPPanel = "edp panel"; 167 168 displayDeviceFP = fopen("/sys/class/graphics/fb0/msm_fb_type", "r"); 169 if(displayDeviceFP){ 170 fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE, 171 displayDeviceFP); 172 if(strncmp(fbType, strCmdPanel, strlen(strCmdPanel)) == 0) { 173 mPanelInfo.mType = MIPI_CMD_PANEL; 174 } 175 else if(strncmp(fbType, strVideoPanel, strlen(strVideoPanel)) == 0) { 176 mPanelInfo.mType = MIPI_VIDEO_PANEL; 177 } 178 else if(strncmp(fbType, strLVDSPanel, strlen(strLVDSPanel)) == 0) { 179 mPanelInfo.mType = LVDS_PANEL; 180 } 181 else if(strncmp(fbType, strEDPPanel, strlen(strEDPPanel)) == 0) { 182 mPanelInfo.mType = EDP_PANEL; 183 } 184 fclose(displayDeviceFP); 185 } else { 186 ALOGE("Unable to read Primary Panel Information"); 187 } 188 189 panelInfoNodeFP = fopen("/sys/class/graphics/fb0/msm_fb_panel_info", "r"); 190 if(panelInfoNodeFP){ 191 size_t len = PAGE_SIZE; 192 ssize_t read; 193 char *readLine = (char *) malloc (len); 194 char property[PROPERTY_VALUE_MAX]; 195 while((read = getline((char **)&readLine, &len, 196 panelInfoNodeFP)) != -1) { 197 int token_ct=0; 198 char *tokens[10]; 199 memset(tokens, 0, sizeof(tokens)); 200 201 if(!tokenizeParams(readLine, TOKEN_PARAMS_DELIM, tokens, 202 &token_ct)) { 203 if(!strncmp(tokens[0], "pu_en", strlen("pu_en"))) { 204 mPanelInfo.mPartialUpdateEnable = atoi(tokens[1]); 205 ALOGI("PartialUpdate status: %s", 206 mPanelInfo.mPartialUpdateEnable? "Enabled" : 207 "Disabled"); 208 } 209 if(!strncmp(tokens[0], "xstart", strlen("xstart"))) { 210 mPanelInfo.mLeftAlign = atoi(tokens[1]); 211 ALOGI("Left Align: %d", mPanelInfo.mLeftAlign); 212 } 213 if(!strncmp(tokens[0], "walign", strlen("walign"))) { 214 mPanelInfo.mWidthAlign = atoi(tokens[1]); 215 ALOGI("Width Align: %d", mPanelInfo.mWidthAlign); 216 } 217 if(!strncmp(tokens[0], "ystart", strlen("ystart"))) { 218 mPanelInfo.mTopAlign = atoi(tokens[1]); 219 ALOGI("Top Align: %d", mPanelInfo.mTopAlign); 220 } 221 if(!strncmp(tokens[0], "halign", strlen("halign"))) { 222 mPanelInfo.mHeightAlign = atoi(tokens[1]); 223 ALOGI("Height Align: %d", mPanelInfo.mHeightAlign); 224 } 225 if(!strncmp(tokens[0], "min_w", strlen("min_w"))) { 226 mPanelInfo.mMinROIWidth = atoi(tokens[1]); 227 ALOGI("Min ROI Width: %d", mPanelInfo.mMinROIWidth); 228 } 229 if(!strncmp(tokens[0], "min_h", strlen("min_h"))) { 230 mPanelInfo.mMinROIHeight = atoi(tokens[1]); 231 ALOGI("Min ROI Height: %d", mPanelInfo.mMinROIHeight); 232 } 233 if(!strncmp(tokens[0], "roi_merge", strlen("roi_merge"))) { 234 mPanelInfo.mNeedsROIMerge = atoi(tokens[1]); 235 ALOGI("Needs ROI Merge: %d", mPanelInfo.mNeedsROIMerge); 236 } 237 if(!strncmp(tokens[0], "dyn_fps_en", strlen("dyn_fps_en"))) { 238 mPanelInfo.mDynFpsSupported = atoi(tokens[1]); 239 ALOGI("Dynamic Fps: %s", mPanelInfo.mDynFpsSupported ? 240 "Enabled" : "Disabled"); 241 } 242 if(!strncmp(tokens[0], "min_fps", strlen("min_fps"))) { 243 mPanelInfo.mMinFps = atoi(tokens[1]); 244 ALOGI("Min Panel fps: %d", mPanelInfo.mMinFps); 245 } 246 if(!strncmp(tokens[0], "max_fps", strlen("max_fps"))) { 247 mPanelInfo.mMaxFps = atoi(tokens[1]); 248 ALOGI("Max Panel fps: %d", mPanelInfo.mMaxFps); 249 } 250 } 251 } 252 if((property_get("persist.hwc.pubypass", property, 0) > 0) && 253 (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || 254 (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { 255 mPanelInfo.mPartialUpdateEnable = 0; 256 ALOGI("PartialUpdate disabled by property"); 257 } 258 fclose(panelInfoNodeFP); 259 free(readLine); 260 } else { 261 ALOGE("Failed to open msm_fb_panel_info node"); 262 } 263 } 264 265 // This function reads the sysfs node to read MDP capabilities 266 // and parses and updates information accordingly. 267 bool MDPVersion::updateSysFsInfo() { 268 FILE *sysfsFd; 269 size_t len = PAGE_SIZE; 270 ssize_t read; 271 char *line = NULL; 272 char sysfsPath[255]; 273 memset(sysfsPath, 0, sizeof(sysfsPath)); 274 snprintf(sysfsPath , sizeof(sysfsPath), 275 "/sys/class/graphics/fb0/mdp/caps"); 276 char property[PROPERTY_VALUE_MAX]; 277 bool enableMacroTile = false; 278 279 if((property_get("persist.hwc.macro_tile_enable", property, NULL) > 0) && 280 (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || 281 (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { 282 enableMacroTile = true; 283 } 284 285 sysfsFd = fopen(sysfsPath, "rb"); 286 287 if (sysfsFd == NULL) { 288 ALOGE("%s: sysFsFile file '%s' not found", 289 __FUNCTION__, sysfsPath); 290 return false; 291 } else { 292 line = (char *) malloc(len); 293 while((read = getline(&line, &len, sysfsFd)) != -1) { 294 int index=0; 295 char *tokens[10]; 296 memset(tokens, 0, sizeof(tokens)); 297 298 // parse the line and update information accordingly 299 if(!tokenizeParams(line, TOKEN_PARAMS_DELIM, tokens, &index)) { 300 if(!strncmp(tokens[0], "hw_rev", strlen("hw_rev"))) { 301 mMdpRev = atoi(tokens[1]); 302 } 303 else if(!strncmp(tokens[0], "rgb_pipes", strlen("rgb_pipes"))) { 304 mRGBPipes = (uint8_t)atoi(tokens[1]); 305 } 306 else if(!strncmp(tokens[0], "vig_pipes", strlen("vig_pipes"))) { 307 mVGPipes = (uint8_t)atoi(tokens[1]); 308 } 309 else if(!strncmp(tokens[0], "dma_pipes", strlen("dma_pipes"))) { 310 mDMAPipes = (uint8_t)atoi(tokens[1]); 311 } 312 else if(!strncmp(tokens[0], "blending_stages", 313 strlen("blending_stages"))) { 314 mBlendStages = (uint8_t)atoi(tokens[1]); 315 } 316 else if(!strncmp(tokens[0], "max_downscale_ratio", 317 strlen("max_downscale_ratio"))) { 318 mMDPDownscale = atoi(tokens[1]); 319 } 320 else if(!strncmp(tokens[0], "max_upscale_ratio", 321 strlen("max_upscale_ratio"))) { 322 mMDPUpscale = atoi(tokens[1]); 323 } else if(!strncmp(tokens[0], "max_bandwidth_low", 324 strlen("max_bandwidth_low"))) { 325 mLowBw = atol(tokens[1]); 326 } else if(!strncmp(tokens[0], "max_bandwidth_high", 327 strlen("max_bandwidth_high"))) { 328 mHighBw = atol(tokens[1]); 329 } else if(!strncmp(tokens[0], "max_mixer_width", 330 strlen("max_mixer_width"))) { 331 mMaxMixerWidth = atoi(tokens[1]); 332 } else if(!strncmp(tokens[0], "max_pipe_width", 333 strlen("max_pipe_width"))) { 334 mMaxPipeWidth = atoi(tokens[1]); 335 } else if(!strncmp(tokens[0], "features", strlen("features"))) { 336 for(int i=1; i<index;i++) { 337 if(!strncmp(tokens[i], "bwc", strlen("bwc"))) { 338 mFeatures |= MDP_BWC_EN; 339 } else if(!strncmp(tokens[i], "decimation", 340 strlen("decimation"))) { 341 mFeatures |= MDP_DECIMATION_EN; 342 } else if(!strncmp(tokens[i], "tile_format", 343 strlen("tile_format"))) { 344 if(enableMacroTile) 345 mMacroTileEnabled = true; 346 } else if(!strncmp(tokens[i], "src_split", 347 strlen("src_split"))) { 348 mSourceSplit = true; 349 } else if(!strncmp(tokens[i], "non_scalar_rgb", 350 strlen("non_scalar_rgb"))) { 351 mRGBHasNoScalar = true; 352 } else if(!strncmp(tokens[i], "rotator_downscale", 353 strlen("rotator_downscale"))) { 354 mRotDownscale = true; 355 } 356 } 357 } 358 } 359 } 360 free(line); 361 fclose(sysfsFd); 362 } 363 364 if(mMDPVersion >= qdutils::MDP_V4_2 and mMDPVersion < qdutils::MDSS_V5) { 365 mRotDownscale = true; 366 } 367 368 if(mSourceSplit) { 369 memset(sysfsPath, 0, sizeof(sysfsPath)); 370 snprintf(sysfsPath , sizeof(sysfsPath), 371 "/sys/class/graphics/fb0/msm_fb_src_split_info"); 372 373 sysfsFd = fopen(sysfsPath, "rb"); 374 if (sysfsFd == NULL) { 375 ALOGE("%s: Opening file %s failed with error %s", __FUNCTION__, 376 sysfsPath, strerror(errno)); 377 return false; 378 } else { 379 line = (char *) malloc(len); 380 if((read = getline(&line, &len, sysfsFd)) != -1) { 381 if(!strncmp(line, "src_split_always", 382 strlen("src_split_always"))) { 383 mSourceSplitAlways = true; 384 } 385 } 386 free(line); 387 fclose(sysfsFd); 388 } 389 } 390 391 ALOGD_IF(DEBUG, "%s: mMDPVersion: %d mMdpRev: %x mRGBPipes:%d," 392 "mVGPipes:%d", __FUNCTION__, mMDPVersion, mMdpRev, 393 mRGBPipes, mVGPipes); 394 ALOGD_IF(DEBUG, "%s:mDMAPipes:%d \t mMDPDownscale:%d, mFeatures:%d", 395 __FUNCTION__, mDMAPipes, mMDPDownscale, mFeatures); 396 ALOGD_IF(DEBUG, "%s:mLowBw: %lu mHighBw: %lu", __FUNCTION__, mLowBw, 397 mHighBw); 398 399 return true; 400 } 401 402 // This function reads the sysfs node to read MDP capabilities 403 // and parses and updates information accordingly. 404 bool MDPVersion::updateSplitInfo() { 405 if(mMDPVersion >= MDSS_V5) { 406 char split[64] = {0}; 407 FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r"); 408 if(fp){ 409 //Format "left right" space as delimiter 410 if(fread(split, sizeof(char), 64, fp)) { 411 split[sizeof(split) - 1] = '\0'; 412 mSplit.mLeft = atoi(split); 413 ALOGI_IF(mSplit.mLeft, "Left Split=%d", mSplit.mLeft); 414 char *rght = strpbrk(split, " "); 415 if(rght) 416 mSplit.mRight = atoi(rght + 1); 417 ALOGI_IF(mSplit.mRight, "Right Split=%d", mSplit.mRight); 418 } 419 } else { 420 ALOGE("Failed to open mdss_fb_split node"); 421 return false; 422 } 423 if(fp) 424 fclose(fp); 425 } 426 return true; 427 } 428 429 430 bool MDPVersion::hasMinCropWidthLimitation() const { 431 return mMdpRev <= MDSS_MDP_HW_REV_102; 432 } 433 434 bool MDPVersion::supportsDecimation() { 435 return mFeatures & MDP_DECIMATION_EN; 436 } 437 438 uint32_t MDPVersion::getMaxMDPDownscale() { 439 return mMDPDownscale; 440 } 441 442 uint32_t MDPVersion::getMaxMDPUpscale() { 443 return mMDPUpscale; 444 } 445 446 bool MDPVersion::supportsBWC() { 447 // BWC - Bandwidth Compression 448 return (mFeatures & MDP_BWC_EN); 449 } 450 451 bool MDPVersion::supportsMacroTile() { 452 // MACRO TILE support 453 return mMacroTileEnabled; 454 } 455 456 bool MDPVersion::isSrcSplit() const { 457 return mSourceSplit; 458 } 459 460 bool MDPVersion::isSrcSplitAlways() const { 461 return mSourceSplitAlways; 462 } 463 464 bool MDPVersion::isRGBScalarSupported() const { 465 return (!mRGBHasNoScalar); 466 } 467 468 bool MDPVersion::is8x26() { 469 return (mMdpRev >= MDSS_MDP_HW_REV_101 and 470 mMdpRev < MDSS_MDP_HW_REV_102); 471 } 472 473 bool MDPVersion::is8x74v2() { 474 return (mMdpRev >= MDSS_MDP_HW_REV_102 and 475 mMdpRev < MDSS_MDP_HW_REV_103); 476 } 477 478 bool MDPVersion::is8084() { 479 return (mMdpRev >= MDSS_MDP_HW_REV_103 and 480 mMdpRev < MDSS_MDP_HW_REV_104); 481 } 482 483 bool MDPVersion::is8092() { 484 return (mMdpRev >= MDSS_MDP_HW_REV_200 and 485 mMdpRev < MDSS_MDP_HW_REV_206); 486 } 487 488 bool MDPVersion::is8994() { 489 return ((mMdpRev >= MDSS_MDP_HW_REV_105 and 490 mMdpRev < MDSS_MDP_HW_REV_106) or 491 (mMdpRev >= MDSS_MDP_HW_REV_109 and 492 mMdpRev < MDSS_MDP_HW_REV_110)); 493 } 494 495 bool MDPVersion::is8x16() { 496 return (mMdpRev >= MDSS_MDP_HW_REV_106 and 497 mMdpRev < MDSS_MDP_HW_REV_107); 498 } 499 500 bool MDPVersion::is8x39() { 501 return (mMdpRev >= MDSS_MDP_HW_REV_108 and 502 mMdpRev < MDSS_MDP_HW_REV_109); 503 } 504 505 506 }; //namespace qdutils 507 508