hdmi.cpp revision abe67dd75cce255d3449ce9af5fa37109c83444e
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
34using namespace android;
35using namespace qdutils;
36
37namespace 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 */
56EDIDData 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
95const int gEDIDCount = (sizeof(gEDIDData)/sizeof(gEDIDData)[0]);
96
97int 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
137void HDMIDisplay::getAttributes(uint32_t& width, uint32_t& height) {
138    uint32_t fps = 0;
139    getAttrForMode(width, height, fps);
140}
141
142int HDMIDisplay::teardown() {
143    closeFrameBuffer();
144    resetInfo();
145    // unset system property
146    property_set("hw.hdmiON", "0");
147    return 0;
148}
149
150HDMIDisplay::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    mFbNum = qdutils::getHDMINode();
156
157    mDisplayId = HWC_DISPLAY_EXTERNAL;
158    // Update the display if HDMI is connected as primary
159    if (isHDMIPrimaryDisplay()) {
160        mDisplayId = HWC_DISPLAY_PRIMARY;
161    }
162
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 */
192void 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
215void HDMIDisplay::setHPD(uint32_t value) {
216    ALOGD_IF(DEBUG,"HPD enabled=%d", value);
217    writeHPDOption(value);
218}
219
220void 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
230int HDMIDisplay::getModeCount() const {
231    ALOGD_IF(DEBUG,"HPD mModeCount=%d", mModeCount);
232    return mModeCount;
233}
234
235void 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
299HDMIDisplay::~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 */
308void 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
335int 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
356bool 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
390bool 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
402bool 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
413void 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
429int 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
441int 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
455int 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
481inline 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
494bool 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
513void 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
563bool 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
587void 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
656void 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*/
670int 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
686bool HDMIDisplay::isHDMIPrimaryDisplay() {
687    return (mFbNum == HWC_DISPLAY_PRIMARY);
688}
689
690int HDMIDisplay::getConnectedState() {
691    int ret = -1;
692    int mFbNum = qdutils::getHDMINode();
693    int connectedNode = openDeviceNode("connected", O_RDONLY);
694    if(connectedNode >= 0) {
695        char opStr[4];
696        ssize_t bytesRead = read(connectedNode, opStr, sizeof(opStr) - 1);
697        if(bytesRead > 0) {
698            opStr[bytesRead] = '\0';
699            ret = atoi(opStr);
700            ALOGD_IF(DEBUG, "%s: Read %d from connected", __FUNCTION__, ret);
701        } else if(bytesRead == 0) {
702            ALOGE("%s: HDMI connected node empty", __FUNCTION__);
703        } else {
704            ALOGE("%s: Read from HDMI connected node failed with error %s",
705                    __FUNCTION__, strerror(errno));
706        }
707        close(connectedNode);
708    } else {
709        ALOGD("%s: /sys/class/graphics/fb%d/connected could not be opened : %s",
710                __FUNCTION__, mFbNum, strerror(errno));
711    }
712    return ret;
713}
714
715void HDMIDisplay::setPrimaryAttributes(uint32_t primaryWidth,
716        uint32_t primaryHeight) {
717    mPrimaryHeight = primaryHeight;
718    mPrimaryWidth = primaryWidth;
719}
720
721int HDMIDisplay::setActiveConfig(int newConfig) {
722    if(newConfig < 0 || newConfig > mModeCount) {
723        ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
724        return -EINVAL;
725    }
726
727    // XXX: Currently, we only support a change in frame rate.
728    // We need to validate the new config before proceeding.
729    if (!isValidConfigChange(newConfig)) {
730        ALOGE("%s Invalid configuration %d", __FUNCTION__, newConfig);
731        return -EINVAL;
732    }
733
734    mCurrentMode =  mEDIDModes[newConfig];
735    mActiveConfig = newConfig;
736    activateDisplay();
737    ALOGD("%s config(%d) mode(%d)", __FUNCTION__, mActiveConfig, mCurrentMode);
738    return 0;
739}
740
741// returns false if the xres or yres of the new config do
742// not match the current config
743bool HDMIDisplay::isValidConfigChange(int newConfig) {
744    int newMode = mEDIDModes[newConfig];
745    uint32_t width = 0, height = 0, refresh = 0;
746    getAttrForConfig(newConfig, width, height, refresh);
747    return ((mXres == width) && (mYres == height)) || mEnableResolutionChange;
748}
749
750int HDMIDisplay::getModeIndex(int mode) {
751    int modeIndex = -1;
752    for(int i = 0; i < mModeCount; i++) {
753        if(mode == mEDIDModes[i]) {
754            modeIndex = i;
755            break;
756        }
757    }
758    return modeIndex;
759}
760
761int HDMIDisplay::getAttrForConfig(int config, uint32_t& xres,
762        uint32_t& yres, uint32_t& refresh) const {
763    if(config < 0 || config > mModeCount) {
764        ALOGE("%s Invalid configuration %d", __FUNCTION__, config);
765        return -EINVAL;
766    }
767    int mode = mEDIDModes[config];
768    uint32_t fps = 0;
769    // Retrieve the mode attributes from gEDIDData
770    for (int dataIndex = 0; dataIndex < gEDIDCount; dataIndex++) {
771        if (gEDIDData[dataIndex].mMode == mode) {
772            xres = gEDIDData[dataIndex].mWidth;
773            yres = gEDIDData[dataIndex].mHeight;
774            fps = gEDIDData[dataIndex].mFps;
775        }
776    }
777    refresh = (uint32_t) 1000000000l / fps;
778    ALOGD_IF(DEBUG, "%s xres(%d) yres(%d) fps(%d) refresh(%d)", __FUNCTION__,
779            xres, yres, fps, refresh);
780    return 0;
781}
782
783int HDMIDisplay::getDisplayConfigs(uint32_t* configs,
784        size_t* numConfigs) const {
785    if (*numConfigs <= 0) {
786        ALOGE("%s Invalid number of configs (%zu)", __FUNCTION__, *numConfigs);
787        return -EINVAL;
788    }
789    *numConfigs = mModeCount;
790    for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
791        configs[configIndex] = (uint32_t)configIndex;
792    }
793    return 0;
794}
795
796};
797