Home | History | Annotate | Download | only in libhdmi
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  * Copyright (C) 2012-2015, The Linux Foundation. All rights reserved.
      4  *
      5  * Not a Contribution, Apache license notifications and license are
      6  * retained for attribution purposes only.
      7  *
      8  * Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *      http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 
     21 #define DEBUG 0
     22 #include <fcntl.h>
     23 #include <linux/msm_mdp.h>
     24 #include <video/msm_hdmi_modes.h>
     25 #include <linux/fb.h>
     26 #include <sys/ioctl.h>
     27 #include <cutils/properties.h>
     28 #include "hwc_utils.h"
     29 #include "hdmi.h"
     30 #include "overlayUtils.h"
     31 #include "overlay.h"
     32 #include "qd_utils.h"
     33 
     34 using namespace android;
     35 using namespace qdutils;
     36 
     37 namespace qhwc {
     38 #define UNKNOWN_STRING                  "unknown"
     39 #define SPD_NAME_LENGTH                 16
     40 
     41 /* The array gEDIDData contains a list of modes currently
     42  * supported by HDMI and display, and modes that are not
     43  * supported i.e. interlaced modes.
     44 
     45  * In order to add support for a new mode, the mode must be
     46  * appended to the end of the array.
     47  *
     48  * Each new entry must contain the following:
     49  * -Mode: a video format defined in msm_hdmi_modes.h
     50  * -Width: x resolution for the mode
     51  * -Height: y resolution for the mode
     52  * -FPS: the frame rate for the mode
     53  * -Mode Order: the priority for the new mode that is used when determining
     54  *  the best mode when the HDMI display is connected.
     55  */
     56 EDIDData gEDIDData [] = {
     57     EDIDData(HDMI_VFRMT_1440x480i60_4_3, 1440, 480, 60, 1),
     58     EDIDData(HDMI_VFRMT_1440x480i60_16_9, 1440, 480, 60, 2),
     59     EDIDData(HDMI_VFRMT_1440x576i50_4_3, 1440, 576, 50, 3),
     60     EDIDData(HDMI_VFRMT_1440x576i50_16_9, 1440, 576, 50, 4),
     61     EDIDData(HDMI_VFRMT_1920x1080i60_16_9, 1920, 1080, 60, 5),
     62     EDIDData(HDMI_VFRMT_640x480p60_4_3, 640, 480, 60, 6),
     63     EDIDData(HDMI_VFRMT_720x480p60_4_3, 720, 480, 60, 7),
     64     EDIDData(HDMI_VFRMT_720x480p60_16_9, 720, 480, 60, 8),
     65     EDIDData(HDMI_VFRMT_720x576p50_4_3, 720, 576, 50, 9),
     66     EDIDData(HDMI_VFRMT_720x576p50_16_9, 720, 576, 50, 10),
     67     EDIDData(HDMI_VFRMT_800x600p60_4_3, 800, 600, 60, 11),
     68     EDIDData(HDMI_VFRMT_848x480p60_16_9, 848, 480, 60, 12),
     69     EDIDData(HDMI_VFRMT_1024x768p60_4_3, 1024, 768, 60, 13),
     70     EDIDData(HDMI_VFRMT_1280x1024p60_5_4, 1280, 1024, 60, 14),
     71     EDIDData(HDMI_VFRMT_1280x720p50_16_9, 1280, 720, 50, 15),
     72     EDIDData(HDMI_VFRMT_1280x720p60_16_9, 1280, 720, 60, 16),
     73     EDIDData(HDMI_VFRMT_1280x800p60_16_10, 1280, 800, 60, 17),
     74     EDIDData(HDMI_VFRMT_1280x960p60_4_3, 1280, 960, 60, 18),
     75     EDIDData(HDMI_VFRMT_1360x768p60_16_9, 1360, 768, 60, 19),
     76     EDIDData(HDMI_VFRMT_1366x768p60_16_10, 1366, 768, 60, 20),
     77     EDIDData(HDMI_VFRMT_1440x900p60_16_10, 1440, 900, 60, 21),
     78     EDIDData(HDMI_VFRMT_1400x1050p60_4_3, 1400, 1050, 60, 22),
     79     EDIDData(HDMI_VFRMT_1680x1050p60_16_10, 1680, 1050, 60, 23),
     80     EDIDData(HDMI_VFRMT_1600x1200p60_4_3, 1600, 1200, 60, 24),
     81     EDIDData(HDMI_VFRMT_1920x1080p24_16_9, 1920, 1080, 24, 25),
     82     EDIDData(HDMI_VFRMT_1920x1080p25_16_9, 1920, 1080, 25, 26),
     83     EDIDData(HDMI_VFRMT_1920x1080p30_16_9, 1920, 1080, 30, 27),
     84     EDIDData(HDMI_VFRMT_1920x1080p50_16_9, 1920, 1080, 50, 28),
     85     EDIDData(HDMI_VFRMT_1920x1080p60_16_9, 1920, 1080, 60, 29),
     86     EDIDData(HDMI_VFRMT_1920x1200p60_16_10, 1920, 1200, 60, 30),
     87     EDIDData(HDMI_VFRMT_2560x1600p60_16_9, 2560, 1600, 60, 31),
     88     EDIDData(HDMI_VFRMT_3840x2160p24_16_9, 3840, 2160, 24, 32),
     89     EDIDData(HDMI_VFRMT_3840x2160p25_16_9, 3840, 2160, 25, 33),
     90     EDIDData(HDMI_VFRMT_3840x2160p30_16_9, 3840, 2160, 30, 34),
     91     EDIDData(HDMI_VFRMT_4096x2160p24_16_9, 4096, 2160, 24, 35),
     92 };
     93 
     94 // Number of modes in gEDIDData
     95 const int gEDIDCount = (sizeof(gEDIDData)/sizeof(gEDIDData)[0]);
     96 
     97 int HDMIDisplay::configure() {
     98     if(!openFrameBuffer()) {
     99         ALOGE("%s: Failed to open FB: %d", __FUNCTION__, mFbNum);
    100         return -1;
    101     }
    102     readCEUnderscanInfo();
    103     readResolution();
    104     /* Used for changing the resolution
    105      * getUserConfig will get the preferred
    106      * config index set thru adb shell */
    107     mActiveConfig = getUserConfig();
    108     if (mActiveConfig == -1) {
    109         //Get the best mode and set
    110         mActiveConfig = getBestConfig();
    111     }
    112 
    113     // Read the system property to determine if downscale feature is enabled.
    114     char value[PROPERTY_VALUE_MAX];
    115     mMDPDownscaleEnabled = false;
    116     if(property_get("sys.hwc.mdp_downscale_enabled", value, "false")
    117             && !strcmp(value, "true")) {
    118         mMDPDownscaleEnabled = true;
    119     }
    120 
    121     // Set the mode corresponding to the active index
    122     mCurrentMode = mEDIDModes[mActiveConfig];
    123     setAttributes();
    124     // set system property
    125     property_set("hw.hdmiON", "1");
    126 
    127     // XXX: A debug property can be used to enable resolution change for
    128     // testing purposes: debug.hwc.enable_resolution_change
    129     mEnableResolutionChange = false;
    130     if(property_get("debug.hwc.enable_resolution_change", value, "false")
    131             && !strcmp(value, "true")) {
    132         mEnableResolutionChange = true;
    133     }
    134     return 0;
    135 }
    136 
    137 void HDMIDisplay::getAttributes(uint32_t& width, uint32_t& height) {
    138     uint32_t fps = 0;
    139     getAttrForMode(width, height, fps);
    140 }
    141 
    142 int HDMIDisplay::teardown() {
    143     closeFrameBuffer();
    144     resetInfo();
    145     // unset system property
    146     property_set("hw.hdmiON", "0");
    147     return 0;
    148 }
    149 
    150 HDMIDisplay::HDMIDisplay():mFd(-1),
    151     mCurrentMode(-1), mModeCount(0), mPrimaryWidth(0), mPrimaryHeight(0),
    152     mUnderscanSupported(false), mMDPDownscaleEnabled(false)
    153 {
    154     memset(&mVInfo, 0, sizeof(mVInfo));
    155 
    156     mDisplayId = HWC_DISPLAY_EXTERNAL;
    157     // Update the display if HDMI is connected as primary
    158     if (isHDMIPrimaryDisplay()) {
    159         mDisplayId = HWC_DISPLAY_PRIMARY;
    160     }
    161 
    162     mFbNum = overlay::Overlay::getInstance()->getFbForDpy(mDisplayId);
    163     // Disable HPD at start if HDMI is external, it will be enabled later
    164     // when the display powers on
    165     // This helps for framework reboot or adb shell stop/start
    166     if (mDisplayId) {
    167         writeHPDOption(0);
    168     }
    169 
    170     // for HDMI - retreive all the modes supported by the driver
    171     if(mFbNum != -1) {
    172         supported_video_mode_lut =
    173                         new msm_hdmi_mode_timing_info[HDMI_VFRMT_MAX];
    174         // Populate the mode table for supported modes
    175         MSM_HDMI_MODES_INIT_TIMINGS(supported_video_mode_lut);
    176         MSM_HDMI_MODES_SET_SUPP_TIMINGS(supported_video_mode_lut,
    177                                         MSM_HDMI_MODES_ALL);
    178         // Update the Source Product Information
    179         // Vendor Name
    180         setSPDInfo("vendor_name", "ro.product.manufacturer");
    181         // Product Description
    182         setSPDInfo("product_description", "ro.product.name");
    183     }
    184 
    185     ALOGD_IF(DEBUG, "%s mDisplayId(%d) mFbNum(%d)",
    186             __FUNCTION__, mDisplayId, mFbNum);
    187 }
    188 /* gets the product manufacturer and product name and writes it
    189  * to the sysfs node, so that the driver can get that information
    190  * Used to show QCOM 8974 instead of Input 1 for example
    191  */
    192 void HDMIDisplay::setSPDInfo(const char* node, const char* property) {
    193     char info[PROPERTY_VALUE_MAX];
    194     ssize_t err = -1;
    195     int spdFile = openDeviceNode(node, O_RDWR);
    196     if (spdFile >= 0) {
    197         memset(info, 0, sizeof(info));
    198         property_get(property, info, UNKNOWN_STRING);
    199         ALOGD_IF(DEBUG, "In %s: %s = %s",
    200                 __FUNCTION__, property, info);
    201         if (strncmp(info, UNKNOWN_STRING, SPD_NAME_LENGTH)) {
    202             err = write(spdFile, info, strlen(info));
    203             if (err <= 0) {
    204                 ALOGE("%s: file write failed for '%s'"
    205                       "err no = %d", __FUNCTION__, node, errno);
    206             }
    207         } else {
    208             ALOGD_IF(DEBUG, "%s: property_get failed for SPD %s",
    209                          __FUNCTION__, node);
    210         }
    211         close(spdFile);
    212     }
    213 }
    214 
    215 void HDMIDisplay::setHPD(uint32_t value) {
    216     ALOGD_IF(DEBUG,"HPD enabled=%d", value);
    217     writeHPDOption(value);
    218 }
    219 
    220 void HDMIDisplay::setActionSafeDimension(int w, int h) {
    221     ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
    222     char actionsafeWidth[PROPERTY_VALUE_MAX];
    223     char actionsafeHeight[PROPERTY_VALUE_MAX];
    224     snprintf(actionsafeWidth, sizeof(actionsafeWidth), "%d", w);
    225     property_set("persist.sys.actionsafe.width", actionsafeWidth);
    226     snprintf(actionsafeHeight, sizeof(actionsafeHeight), "%d", h);
    227     property_set("persist.sys.actionsafe.height", actionsafeHeight);
    228 }
    229 
    230 int HDMIDisplay::getModeCount() const {
    231     ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
    232     return mModeCount;
    233 }
    234 
    235 void HDMIDisplay::readCEUnderscanInfo()
    236 {
    237     int hdmiScanInfoFile = -1;
    238     ssize_t len = -1;
    239     char scanInfo[17];
    240     char *ce_info_str = NULL;
    241     char *save_ptr;
    242     const char token[] = ", \n";
    243     int ce_info = -1;
    244 
    245     memset(scanInfo, 0, sizeof(scanInfo));
    246     hdmiScanInfoFile = openDeviceNode("scan_info", O_RDONLY);
    247     if (hdmiScanInfoFile < 0) {
    248         return;
    249     } else {
    250         len = read(hdmiScanInfoFile, scanInfo, sizeof(scanInfo)-1);
    251         ALOGD("%s: Scan Info string: %s length = %zu",
    252                  __FUNCTION__, scanInfo, len);
    253         if (len <= 0) {
    254             close(hdmiScanInfoFile);
    255             ALOGE("%s: Scan Info file empty", __FUNCTION__);
    256             return;
    257         }
    258         scanInfo[len] = '\0';  /* null terminate the string */
    259         close(hdmiScanInfoFile);
    260     }
    261 
    262     /*
    263      * The scan_info contains the three fields
    264      * PT - preferred video format
    265      * IT - video format
    266      * CE video format - containing the underscan support information
    267      */
    268 
    269     /* PT */
    270     ce_info_str = strtok_r(scanInfo, token, &save_ptr);
    271     if (ce_info_str) {
    272         /* IT */
    273         ce_info_str = strtok_r(NULL, token, &save_ptr);
    274         if (ce_info_str) {
    275             /* CE */
    276             ce_info_str = strtok_r(NULL, token, &save_ptr);
    277             if (ce_info_str)
    278                 ce_info = atoi(ce_info_str);
    279         }
    280     }
    281 
    282     if (ce_info_str) {
    283         // ce_info contains the underscan information
    284         if (ce_info == HDMI_SCAN_ALWAYS_UNDERSCANED ||
    285             ce_info == HDMI_SCAN_BOTH_SUPPORTED)
    286             // if TV supported underscan, then driver will always underscan
    287             // hence no need to apply action safe rectangle
    288             mUnderscanSupported = true;
    289     } else {
    290         ALOGE("%s: scan_info string error", __FUNCTION__);
    291     }
    292 
    293     // Store underscan support info in a system property
    294     const char* prop = (mUnderscanSupported) ? "1" : "0";
    295     property_set("hw.underscan_supported", prop);
    296     return;
    297 }
    298 
    299 HDMIDisplay::~HDMIDisplay()
    300 {
    301     delete [] supported_video_mode_lut;
    302     closeFrameBuffer();
    303 }
    304 
    305 /*
    306  * sets the fb_var_screeninfo from the hdmi_mode_timing_info
    307  */
    308 void setDisplayTiming(struct fb_var_screeninfo &info,
    309                                 const msm_hdmi_mode_timing_info* mode)
    310 {
    311     info.reserved[0] = 0;
    312     info.reserved[1] = 0;
    313     info.reserved[2] = 0;
    314 #ifndef FB_METADATA_VIDEO_INFO_CODE_SUPPORT
    315     info.reserved[3] = (info.reserved[3] & 0xFFFF) |
    316               (mode->video_format << 16);
    317 #endif
    318     info.xoffset = 0;
    319     info.yoffset = 0;
    320     info.xres = mode->active_h;
    321     info.yres = mode->active_v;
    322 
    323     info.pixclock = (mode->pixel_freq)*1000;
    324     info.vmode = mode->interlaced ?
    325                     FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
    326 
    327     info.right_margin = mode->front_porch_h;
    328     info.hsync_len = mode->pulse_width_h;
    329     info.left_margin = mode->back_porch_h;
    330     info.lower_margin = mode->front_porch_v;
    331     info.vsync_len = mode->pulse_width_v;
    332     info.upper_margin = mode->back_porch_v;
    333 }
    334 
    335 int HDMIDisplay::parseResolution(char* edidStr)
    336 {
    337     char delim = ',';
    338     int count = 0;
    339     char *start, *end;
    340     // EDIDs are string delimited by ','
    341     // Ex: 16,4,5,3,32,34,1
    342     // Parse this string to get mode(int)
    343     start = (char*) edidStr;
    344     end = &delim;
    345     while(*end == delim) {
    346         mEDIDModes[count] = (int) strtol(start, &end, 10);
    347         start = end+1;
    348         count++;
    349     }
    350     ALOGD_IF(DEBUG, "In %s: count = %d", __FUNCTION__, count);
    351     for (int i = 0; i < count; i++)
    352         ALOGD_IF(DEBUG, "Mode[%d] = %d", i, mEDIDModes[i]);
    353     return count;
    354 }
    355 
    356 bool HDMIDisplay::readResolution()
    357 {
    358     ssize_t len = -1;
    359     char edidStr[128] = {'\0'};
    360 
    361     int hdmiEDIDFile = openDeviceNode("edid_modes", O_RDONLY);
    362     if (hdmiEDIDFile < 0) {
    363         return false;
    364     } else {
    365         len = read(hdmiEDIDFile, edidStr, sizeof(edidStr)-1);
    366         ALOGD_IF(DEBUG, "%s: EDID string: %s length = %zu",
    367                  __FUNCTION__, edidStr, len);
    368         if (len <= 0) {
    369             ALOGE("%s: edid_modes file empty", __FUNCTION__);
    370             edidStr[0] = '\0';
    371         }
    372         else {
    373             while (len > 1 && isspace(edidStr[len-1])) {
    374                 --len;
    375             }
    376             edidStr[len] = '\0';
    377         }
    378         close(hdmiEDIDFile);
    379     }
    380     if(len > 0) {
    381         // Get EDID modes from the EDID strings
    382         mModeCount = parseResolution(edidStr);
    383         ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
    384                  mModeCount);
    385     }
    386 
    387     return (len > 0);
    388 }
    389 
    390 bool HDMIDisplay::openFrameBuffer()
    391 {
    392     if (mFd == -1) {
    393         char strDevPath[MAX_SYSFS_FILE_PATH];
    394         snprintf(strDevPath, MAX_SYSFS_FILE_PATH, "/dev/graphics/fb%d", mFbNum);
    395         mFd = open(strDevPath, O_RDWR);
    396         if (mFd < 0)
    397             ALOGE("%s: %s is not available", __FUNCTION__, strDevPath);
    398     }
    399     return (mFd > 0);
    400 }
    401 
    402 bool HDMIDisplay::closeFrameBuffer()
    403 {
    404     int ret = 0;
    405     if(mFd >= 0) {
    406         ret = close(mFd);
    407         mFd = -1;
    408     }
    409     return (ret == 0);
    410 }
    411 
    412 // clears the vinfo, edid, best modes
    413 void HDMIDisplay::resetInfo()
    414 {
    415     memset(&mVInfo, 0, sizeof(mVInfo));
    416     memset(mEDIDModes, 0, sizeof(mEDIDModes));
    417     mModeCount = 0;
    418     mCurrentMode = -1;
    419     mUnderscanSupported = false;
    420     mXres = 0;
    421     mYres = 0;
    422     mVsyncPeriod = 0;
    423     mMDPScalingMode = false;
    424     // Reset the underscan supported system property
    425     const char* prop = "0";
    426     property_set("hw.underscan_supported", prop);
    427 }
    428 
    429 int HDMIDisplay::getModeOrder(int mode)
    430 {
    431     for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
    432         if (gEDIDData[dataIndex].mMode == mode) {
    433             return gEDIDData[dataIndex].mModeOrder;
    434         }
    435     }
    436     ALOGE("%s Mode not found: %d", __FUNCTION__, mode);
    437     return -1;
    438 }
    439 
    440 /// Returns the index of the user mode set(if any) using adb shell
    441 int HDMIDisplay::getUserConfig() {
    442     /* Based on the property set the resolution */
    443     char property_value[PROPERTY_VALUE_MAX];
    444     property_get("hw.hdmi.resolution", property_value, "-1");
    445     int mode = atoi(property_value);
    446     // We dont support interlaced modes
    447     if(isValidMode(mode) && !isInterlacedMode(mode)) {
    448         ALOGD_IF(DEBUG, "%s: setting the HDMI mode = %d", __FUNCTION__, mode);
    449         return getModeIndex(mode);
    450     }
    451     return -1;
    452 }
    453 
    454 // Get the index of the best mode for the current HD TV
    455 int HDMIDisplay::getBestConfig() {
    456     int bestOrder = 0;
    457     int bestMode = HDMI_VFRMT_640x480p60_4_3;
    458     int bestModeIndex = -1;
    459     // for all the edid read, get the best mode
    460     for(int i = 0; i < mModeCount; i++) {
    461         int mode = mEDIDModes[i];
    462         int order = getModeOrder(mode);
    463         if (order > bestOrder) {
    464             bestOrder = order;
    465             bestMode = mode;
    466             bestModeIndex = i;
    467         }
    468     }
    469     // If we fail to read from EDID when HDMI is connected, then
    470     // mModeCount will be 0 and bestModeIndex will be invalid.
    471     // In this case, we populate the mEDIDModes structure with
    472     // a default mode at index 0.
    473     if (bestModeIndex == -1) {
    474         bestModeIndex = 0;
    475         mModeCount = 1;
    476         mEDIDModes[bestModeIndex] = bestMode;
    477     }
    478     return bestModeIndex;
    479 }
    480 
    481 inline bool HDMIDisplay::isValidMode(int ID)
    482 {
    483     bool valid = false;
    484     for (int i = 0; i < mModeCount; i++) {
    485         if(ID == mEDIDModes[i]) {
    486             valid = true;
    487             break;
    488         }
    489     }
    490     return valid;
    491 }
    492 
    493 // returns true if the mode(ID) is interlaced mode format
    494 bool HDMIDisplay::isInterlacedMode(int ID) {
    495     bool interlaced = false;
    496     switch(ID) {
    497         case HDMI_VFRMT_1440x480i60_4_3:
    498         case HDMI_VFRMT_1440x480i60_16_9:
    499         case HDMI_VFRMT_1440x576i50_4_3:
    500         case HDMI_VFRMT_1440x576i50_16_9:
    501         case HDMI_VFRMT_1920x1080i60_16_9:
    502             interlaced = true;
    503             break;
    504         default:
    505             interlaced = false;
    506             break;
    507     }
    508     return interlaced;
    509 }
    510 
    511 // Does a put_vscreen info on the HDMI interface which will update
    512 // the configuration (resolution, timing info) to match mCurrentMode
    513 void HDMIDisplay::activateDisplay()
    514 {
    515     int ret = 0;
    516     ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
    517     if(ret < 0) {
    518         ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
    519                                                             strerror(errno));
    520     }
    521     ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
    522             "(%d,%d,%d) %dMHz>", __FUNCTION__,
    523             mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
    524             mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
    525             mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
    526             mVInfo.pixclock/1000/1000);
    527 
    528     const struct msm_hdmi_mode_timing_info *mode =
    529             &supported_video_mode_lut[0];
    530     for (unsigned int i = 0; i < HDMI_VFRMT_MAX; ++i) {
    531         const struct msm_hdmi_mode_timing_info *cur =
    532                 &supported_video_mode_lut[i];
    533         if (cur->video_format == (uint32_t)mCurrentMode) {
    534             mode = cur;
    535             break;
    536         }
    537     }
    538     setDisplayTiming(mVInfo, mode);
    539     ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
    540             "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, mCurrentMode,
    541             mode->video_format, mVInfo.xres, mVInfo.yres,
    542             mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
    543             mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
    544             mVInfo.pixclock/1000/1000);
    545 #ifdef FB_METADATA_VIDEO_INFO_CODE_SUPPORT
    546     struct msmfb_metadata metadata;
    547     memset(&metadata, 0 , sizeof(metadata));
    548     metadata.op = metadata_op_vic;
    549     metadata.data.video_info_code = mode->video_format;
    550     if (ioctl(mFd, MSMFB_METADATA_SET, &metadata) == -1) {
    551         ALOGD("In %s: MSMFB_METADATA_SET failed Err Str = %s",
    552                 __FUNCTION__, strerror(errno));
    553     }
    554 #endif
    555     mVInfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
    556     ret = ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo);
    557     if(ret < 0) {
    558         ALOGD("In %s: FBIOPUT_VSCREENINFO failed Err Str = %s",
    559                 __FUNCTION__, strerror(errno));
    560     }
    561 }
    562 
    563 bool HDMIDisplay::writeHPDOption(int userOption) const
    564 {
    565     bool ret = true;
    566     if(mFbNum != -1) {
    567         int hdmiHPDFile = openDeviceNode("hpd", O_RDWR);
    568         if (hdmiHPDFile >= 0) {
    569             ssize_t err = -1;
    570             ALOGD_IF(DEBUG, "%s: option = %d",
    571                     __FUNCTION__, userOption);
    572             if(userOption)
    573                 err = write(hdmiHPDFile, "1", 2);
    574             else
    575                 err = write(hdmiHPDFile, "0" , 2);
    576             if (err <= 0) {
    577                 ALOGE("%s: file write failed 'hpd'", __FUNCTION__);
    578                 ret = false;
    579             }
    580             close(hdmiHPDFile);
    581         }
    582     }
    583     return ret;
    584 }
    585 
    586 
    587 void HDMIDisplay::setAttributes() {
    588     uint32_t fps = 0;
    589     // Always set dpyAttr res to mVInfo res
    590     getAttrForMode(mXres, mYres, fps);
    591     mMDPScalingMode = false;
    592 
    593     if(overlay::Overlay::getInstance()->isUIScalingOnExternalSupported()
    594         && mMDPDownscaleEnabled) {
    595         // if primary resolution is more than the hdmi resolution
    596         // configure dpy attr to primary resolution and set MDP
    597         // scaling mode
    598         // Restrict this upto 1080p resolution max, if target does not
    599         // support source split feature.
    600         uint32_t primaryArea = mPrimaryWidth * mPrimaryHeight;
    601         if(((primaryArea) > (mXres * mYres)) &&
    602             (((primaryArea) <= SUPPORTED_DOWNSCALE_AREA) ||
    603                 qdutils::MDPVersion::getInstance().isSrcSplit())) {
    604             // tmpW and tmpH will hold the primary dimensions before we
    605             // update the aspect ratio if necessary.
    606             int tmpW = mPrimaryWidth;
    607             int tmpH = mPrimaryHeight;
    608             // HDMI is always in landscape, so always assign the higher
    609             // dimension to hdmi's xres
    610             if(mPrimaryHeight > mPrimaryWidth) {
    611                 tmpW = mPrimaryHeight;
    612                 tmpH = mPrimaryWidth;
    613             }
    614             // The aspect ratios of the external and primary displays
    615             // can be different. As a result, directly assigning primary
    616             // resolution could lead to an incorrect final image.
    617             // We get around this by calculating a new resolution by
    618             // keeping aspect ratio intact.
    619             hwc_rect r = {0, 0, 0, 0};
    620             qdutils::getAspectRatioPosition(tmpW, tmpH, mXres, mYres, r);
    621             uint32_t newExtW = r.right - r.left;
    622             uint32_t newExtH = r.bottom - r.top;
    623             uint32_t alignedExtW;
    624             uint32_t alignedExtH;
    625             // On 8994 and below targets MDP supports only 4X downscaling,
    626             // Restricting selected external resolution to be exactly 4X
    627             // greater resolution than actual external resolution
    628             uint32_t maxMDPDownScale =
    629                     qdutils::MDPVersion::getInstance().getMaxMDPDownscale();
    630             if((mXres * mYres * maxMDPDownScale) < (newExtW * newExtH)) {
    631                 float upScaleFactor = (float)maxMDPDownScale / 2.0f;
    632                 newExtW = (int)((float)mXres * upScaleFactor);
    633                 newExtH = (int)((float)mYres * upScaleFactor);
    634             }
    635             // Align it down so that the new aligned resolution does not
    636             // exceed the maxMDPDownscale factor
    637             alignedExtW = overlay::utils::aligndown(newExtW, 4);
    638             alignedExtH = overlay::utils::aligndown(newExtH, 4);
    639             mXres = alignedExtW;
    640             mYres = alignedExtH;
    641             // Set External Display MDP Downscale mode indicator
    642             mMDPScalingMode = true;
    643         }
    644     }
    645     ALOGD_IF(DEBUG_MDPDOWNSCALE, "Selected external resolution [%d X %d] "
    646             "maxMDPDownScale %d mMDPScalingMode %d srcSplitEnabled %d "
    647             "MDPDownscale feature %d",
    648             mXres, mYres,
    649             qdutils::MDPVersion::getInstance().getMaxMDPDownscale(),
    650             mMDPScalingMode, qdutils::MDPVersion::getInstance().isSrcSplit(),
    651             mMDPDownscaleEnabled);
    652     mVsyncPeriod = (int) 1000000000l / fps;
    653     ALOGD_IF(DEBUG, "%s xres=%d, yres=%d", __FUNCTION__, mXres, mYres);
    654 }
    655 
    656 void HDMIDisplay::getAttrForMode(uint32_t& width, uint32_t& height,
    657         uint32_t& fps) {
    658     for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
    659         if (gEDIDData[dataIndex].mMode == mCurrentMode) {
    660             width = gEDIDData[dataIndex].mWidth;
    661             height = gEDIDData[dataIndex].mHeight;
    662             fps = gEDIDData[dataIndex].mFps;
    663             return;
    664         }
    665     }
    666     ALOGE("%s Unable to get attributes for %d", __FUNCTION__, mCurrentMode);
    667 }
    668 
    669 /* returns the fd related to the node specified*/
    670 int HDMIDisplay::openDeviceNode(const char* node, int fileMode) const {
    671     char sysFsFilePath[MAX_SYSFS_FILE_PATH];
    672     memset(sysFsFilePath, 0, sizeof(sysFsFilePath));
    673     snprintf(sysFsFilePath , sizeof(sysFsFilePath),
    674             "/sys/devices/virtual/graphics/fb%d/%s",
    675             mFbNum, node);
    676 
    677     int fd = open(sysFsFilePath, fileMode, 0);
    678 
    679     if (fd < 0) {
    680         ALOGE("%s: file '%s' not found : ret = %d err str: %s",
    681                 __FUNCTION__, sysFsFilePath, fd, strerror(errno));
    682     }
    683     return fd;
    684 }
    685 
    686 bool HDMIDisplay::isHDMIPrimaryDisplay() {
    687     int hdmiNode = qdutils::getHDMINode();
    688     return (hdmiNode == HWC_DISPLAY_PRIMARY);
    689 }
    690 
    691 int HDMIDisplay::getConnectedState() {
    692     int ret = -1;
    693     int mFbNum = qdutils::getHDMINode();
    694     int connectedNode = openDeviceNode("connected", O_RDONLY);
    695     if(connectedNode >= 0) {
    696         char opStr[4];
    697         ssize_t bytesRead = read(connectedNode, opStr, sizeof(opStr) - 1);
    698         if(bytesRead > 0) {
    699             opStr[bytesRead] = '\0';
    700             ret = atoi(opStr);
    701             ALOGD_IF(DEBUG, "%s: Read %d from connected", __FUNCTION__, ret);
    702         } else if(bytesRead == 0) {
    703             ALOGE("%s: HDMI connected node empty", __FUNCTION__);
    704         } else {
    705             ALOGE("%s: Read from HDMI connected node failed with error %s",
    706                     __FUNCTION__, strerror(errno));
    707         }
    708         close(connectedNode);
    709     } else {
    710         ALOGD("%s: /sys/class/graphics/fb%d/connected could not be opened : %s",
    711                 __FUNCTION__, mFbNum, strerror(errno));
    712     }
    713     return ret;
    714 }
    715 
    716 void HDMIDisplay::setPrimaryAttributes(uint32_t primaryWidth,
    717         uint32_t primaryHeight) {
    718     mPrimaryHeight = primaryHeight;
    719     mPrimaryWidth = primaryWidth;
    720 }
    721 
    722 int HDMIDisplay::setActiveConfig(int newConfig) {
    723     if(newConfig < 0 || newConfig > mModeCount) {
    724         ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
    725         return -EINVAL;
    726     }
    727 
    728     // XXX: Currently, we only support a change in frame rate.
    729     // We need to validate the new config before proceeding.
    730     if (!isValidConfigChange(newConfig)) {
    731         ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
    732         return -EINVAL;
    733     }
    734 
    735     mCurrentMode =  mEDIDModes[newConfig];
    736     mActiveConfig = newConfig;
    737     activateDisplay();
    738     ALOGD("%s config(%d) mode(%d)", __FUNCTION__, mActiveConfig, mCurrentMode);
    739     return 0;
    740 }
    741 
    742 // returns false if the xres or yres of the new config do
    743 // not match the current config
    744 bool HDMIDisplay::isValidConfigChange(int newConfig) {
    745     int newMode = mEDIDModes[newConfig];
    746     uint32_t width = 0, height = 0, refresh = 0;
    747     getAttrForConfig(newConfig, width, height, refresh);
    748     return ((mXres == width) && (mYres == height)) || mEnableResolutionChange;
    749 }
    750 
    751 int HDMIDisplay::getModeIndex(int mode) {
    752     int modeIndex = -1;
    753     for(int i = 0; i < mModeCount; i++) {
    754         if(mode == mEDIDModes[i]) {
    755             modeIndex = i;
    756             break;
    757         }
    758     }
    759     return modeIndex;
    760 }
    761 
    762 int HDMIDisplay::getAttrForConfig(int config, uint32_t& xres,
    763         uint32_t& yres, uint32_t& refresh) const {
    764     if(config < 0 || config > mModeCount) {
    765         ALOGE("%s Invalid configuration %d", __FUNCTION__, config);
    766         return -EINVAL;
    767     }
    768     int mode = mEDIDModes[config];
    769     uint32_t fps = 0;
    770     // Retrieve the mode attributes from gEDIDData
    771     for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
    772         if (gEDIDData[dataIndex].mMode == mode) {
    773             xres = gEDIDData[dataIndex].mWidth;
    774             yres = gEDIDData[dataIndex].mHeight;
    775             fps = gEDIDData[dataIndex].mFps;
    776         }
    777     }
    778     refresh = (uint32_t) 1000000000l / fps;
    779     ALOGD_IF(DEBUG, "%s xres(%d) yres(%d) fps(%d) refresh(%d)", __FUNCTION__,
    780             xres, yres, fps, refresh);
    781     return 0;
    782 }
    783 
    784 int HDMIDisplay::getDisplayConfigs(uint32_t* configs,
    785         size_t* numConfigs) const {
    786     if (*numConfigs <= 0) {
    787         ALOGE("%s Invalid number of configs (%d)", __FUNCTION__, *numConfigs);
    788         return -EINVAL;
    789     }
    790     *numConfigs = mModeCount;
    791     for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
    792         configs[configIndex] = (uint32_t)configIndex;
    793     }
    794     return 0;
    795 }
    796 
    797 };
    798