Home | History | Annotate | Download | only in fb
      1 /*
      2 * Copyright (c) 2015 - 2016, 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 
     30 #include <stdio.h>
     31 #include <unistd.h>
     32 #include <string.h>
     33 #include <sys/ioctl.h>
     34 #include <ctype.h>
     35 #include <fcntl.h>
     36 #include <linux/videodev2.h>
     37 #include <utils/debug.h>
     38 #include <utils/sys.h>
     39 #include <vector>
     40 #include <map>
     41 #include <utility>
     42 
     43 #include "hw_hdmi.h"
     44 
     45 #define __CLASS__ "HWHDMI"
     46 
     47 namespace sdm {
     48 
     49 static bool MapHDMIDisplayTiming(const msm_hdmi_mode_timing_info *mode,
     50                                  fb_var_screeninfo *info) {
     51   if (!mode || !info) {
     52     return false;
     53   }
     54 
     55   info->reserved[0] = 0;
     56   info->reserved[1] = 0;
     57   info->reserved[2] = 0;
     58   info->reserved[3] = (info->reserved[3] & 0xFFFF) | (mode->video_format << 16);
     59   info->xoffset = 0;
     60   info->yoffset = 0;
     61   info->xres = mode->active_h;
     62   info->yres = mode->active_v;
     63   info->pixclock = (mode->pixel_freq) * 1000;
     64   info->vmode = mode->interlaced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
     65   info->right_margin = mode->front_porch_h;
     66   info->hsync_len = mode->pulse_width_h;
     67   info->left_margin = mode->back_porch_h;
     68   info->lower_margin = mode->front_porch_v;
     69   info->vsync_len = mode->pulse_width_v;
     70   info->upper_margin = mode->back_porch_v;
     71 
     72   info->grayscale = V4L2_PIX_FMT_RGB24;
     73   // If the mode supports YUV420 set grayscale to the FOURCC value for YUV420.
     74   std::bitset<32> pixel_formats = mode->pixel_formats;
     75   if (pixel_formats[1]) {
     76     info->grayscale = V4L2_PIX_FMT_NV12;
     77   }
     78 
     79   return true;
     80 }
     81 
     82 HWHDMI::HWHDMI(BufferSyncHandler *buffer_sync_handler,  HWInfoInterface *hw_info_intf)
     83   : HWDevice(buffer_sync_handler), hw_scan_info_(), active_config_index_(0) {
     84   HWDevice::device_type_ = kDeviceHDMI;
     85   HWDevice::device_name_ = "HDMI Display Device";
     86   HWDevice::hw_info_intf_ = hw_info_intf;
     87 }
     88 
     89 DisplayError HWHDMI::Init() {
     90   DisplayError error = kErrorNone;
     91 
     92   SetSourceProductInformation("vendor_name", "ro.product.manufacturer");
     93   SetSourceProductInformation("product_description", "ro.product.name");
     94 
     95   error = HWDevice::Init();
     96   if (error != kErrorNone) {
     97     return error;
     98   }
     99 
    100   mdp_dest_scalar_data_.resize(hw_resource_.hw_dest_scalar_info.count);
    101 
    102   error = ReadEDIDInfo();
    103   if (error != kErrorNone) {
    104     Deinit();
    105     return error;
    106   }
    107 
    108   if (!IsResolutionFilePresent()) {
    109     Deinit();
    110     return kErrorHardware;
    111   }
    112 
    113   error = ReadTimingInfo();
    114   if (error != kErrorNone) {
    115     Deinit();
    116     return error;
    117   }
    118 
    119   ReadScanInfo();
    120 
    121   GetPanelS3DMode();
    122 
    123   s3d_mode_sdm_to_mdp_.insert(std::pair<HWS3DMode, msm_hdmi_s3d_mode>
    124                              (kS3DModeNone, HDMI_S3D_NONE));
    125   s3d_mode_sdm_to_mdp_.insert(std::pair<HWS3DMode, msm_hdmi_s3d_mode>
    126                              (kS3DModeLR, HDMI_S3D_SIDE_BY_SIDE));
    127   s3d_mode_sdm_to_mdp_.insert(std::pair<HWS3DMode, msm_hdmi_s3d_mode>
    128                              (kS3DModeRL, HDMI_S3D_SIDE_BY_SIDE));
    129   s3d_mode_sdm_to_mdp_.insert(std::pair<HWS3DMode, msm_hdmi_s3d_mode>
    130                              (kS3DModeTB, HDMI_S3D_TOP_AND_BOTTOM));
    131   s3d_mode_sdm_to_mdp_.insert(std::pair<HWS3DMode, msm_hdmi_s3d_mode>
    132                              (kS3DModeFP, HDMI_S3D_FRAME_PACKING));
    133 
    134   return error;
    135 }
    136 
    137 DisplayError HWHDMI::GetNumDisplayAttributes(uint32_t *count) {
    138   *count = UINT32(hdmi_modes_.size());
    139   if (*count <= 0) {
    140     return kErrorHardware;
    141   }
    142 
    143   return kErrorNone;
    144 }
    145 
    146 DisplayError HWHDMI::GetActiveConfig(uint32_t *active_config_index) {
    147   *active_config_index = active_config_index_;
    148   return kErrorNone;
    149 }
    150 
    151 DisplayError HWHDMI::ReadEDIDInfo() {
    152   ssize_t length = -1;
    153   char edid_str[kPageSize] = {'\0'};
    154   char edid_path[kMaxStringLength] = {'\0'};
    155   snprintf(edid_path, sizeof(edid_path), "%s%d/edid_modes", fb_path_, fb_node_index_);
    156   int edid_file = Sys::open_(edid_path, O_RDONLY);
    157   if (edid_file < 0) {
    158     DLOGE("EDID file open failed.");
    159     return kErrorHardware;
    160   }
    161 
    162   length = Sys::pread_(edid_file, edid_str, sizeof(edid_str)-1, 0);
    163   if (length <= 0) {
    164     DLOGE("%s: edid_modes file empty");
    165     return kErrorHardware;
    166   }
    167   Sys::close_(edid_file);
    168 
    169   DLOGI("EDID mode string: %s", edid_str);
    170   while (length > 1 && isspace(edid_str[length-1])) {
    171     --length;
    172   }
    173   edid_str[length] = '\0';
    174 
    175   if (length > 0) {
    176     // Get EDID modes from the EDID string
    177     char *ptr = edid_str;
    178     const uint32_t edid_count_max = 128;
    179     char *tokens[edid_count_max] = { NULL };
    180     uint32_t hdmi_mode_count = 0;
    181 
    182     ParseLine(ptr, tokens, edid_count_max, &hdmi_mode_count);
    183 
    184     supported_video_modes_.resize(hdmi_mode_count);
    185 
    186     hdmi_modes_.resize(hdmi_mode_count);
    187     for (uint32_t i = 0; i < hdmi_mode_count; i++) {
    188       hdmi_modes_[i] = UINT32(atoi(tokens[i]));
    189     }
    190   }
    191 
    192   return kErrorNone;
    193 }
    194 
    195 DisplayError HWHDMI::GetDisplayAttributes(uint32_t index,
    196                                           HWDisplayAttributes *display_attributes) {
    197   DTRACE_SCOPED();
    198 
    199   if (index >= hdmi_modes_.size()) {
    200     return kErrorNotSupported;
    201   }
    202 
    203   // Get the resolution info from the look up table
    204   msm_hdmi_mode_timing_info *timing_mode = &supported_video_modes_[0];
    205   for (uint32_t i = 0; i < hdmi_modes_.size(); i++) {
    206     msm_hdmi_mode_timing_info *cur = &supported_video_modes_[i];
    207     if (cur->video_format == hdmi_modes_[index]) {
    208       timing_mode = cur;
    209       break;
    210     }
    211   }
    212   display_attributes->x_pixels = timing_mode->active_h;
    213   display_attributes->y_pixels = timing_mode->active_v;
    214   display_attributes->v_front_porch = timing_mode->front_porch_v;
    215   display_attributes->v_back_porch = timing_mode->back_porch_v;
    216   display_attributes->v_pulse_width = timing_mode->pulse_width_v;
    217   uint32_t h_blanking = timing_mode->front_porch_h + timing_mode->back_porch_h +
    218       timing_mode->pulse_width_h;
    219   display_attributes->h_total = timing_mode->active_h + h_blanking;
    220   display_attributes->x_dpi = 0;
    221   display_attributes->y_dpi = 0;
    222   display_attributes->fps = timing_mode->refresh_rate / 1000;
    223   display_attributes->vsync_period_ns = UINT32(1000000000L / display_attributes->fps);
    224   display_attributes->is_device_split = false;
    225   if (display_attributes->x_pixels > hw_resource_.max_mixer_width) {
    226     display_attributes->is_device_split = true;
    227     display_attributes->h_total += h_blanking;
    228   }
    229 
    230   GetDisplayS3DSupport(index, display_attributes);
    231   std::bitset<32> pixel_formats = timing_mode->pixel_formats;
    232 
    233   display_attributes->is_yuv = pixel_formats[1];
    234 
    235   return kErrorNone;
    236 }
    237 
    238 DisplayError HWHDMI::SetDisplayAttributes(uint32_t index) {
    239   DTRACE_SCOPED();
    240 
    241   if (index > hdmi_modes_.size()) {
    242     return kErrorNotSupported;
    243   }
    244 
    245   // Variable screen info
    246   fb_var_screeninfo vscreeninfo = {};
    247   if (Sys::ioctl_(device_fd_, FBIOGET_VSCREENINFO, &vscreeninfo) < 0) {
    248     IOCTL_LOGE(FBIOGET_VSCREENINFO, device_type_);
    249     return kErrorHardware;
    250   }
    251 
    252   DLOGI("GetInfo<Mode=%d %dx%d (%d,%d,%d),(%d,%d,%d) %dMHz>", vscreeninfo.reserved[3],
    253         vscreeninfo.xres, vscreeninfo.yres, vscreeninfo.right_margin, vscreeninfo.hsync_len,
    254         vscreeninfo.left_margin, vscreeninfo.lower_margin, vscreeninfo.vsync_len,
    255         vscreeninfo.upper_margin, vscreeninfo.pixclock/1000000);
    256 
    257   msm_hdmi_mode_timing_info *timing_mode = &supported_video_modes_[0];
    258   for (uint32_t i = 0; i < hdmi_modes_.size(); i++) {
    259     msm_hdmi_mode_timing_info *cur = &supported_video_modes_[i];
    260     if (cur->video_format == hdmi_modes_[index]) {
    261       timing_mode = cur;
    262       break;
    263     }
    264   }
    265 
    266   if (MapHDMIDisplayTiming(timing_mode, &vscreeninfo) == false) {
    267     return kErrorParameters;
    268   }
    269 
    270   msmfb_metadata metadata = {};
    271   metadata.op = metadata_op_vic;
    272   metadata.data.video_info_code = timing_mode->video_format;
    273   if (Sys::ioctl_(device_fd_, MSMFB_METADATA_SET, &metadata) < 0) {
    274     IOCTL_LOGE(MSMFB_METADATA_SET, device_type_);
    275     return kErrorHardware;
    276   }
    277 
    278   DLOGI("SetInfo<Mode=%d %dx%d (%d,%d,%d),(%d,%d,%d) %dMHz>", vscreeninfo.reserved[3] & 0xFF00,
    279         vscreeninfo.xres, vscreeninfo.yres, vscreeninfo.right_margin, vscreeninfo.hsync_len,
    280         vscreeninfo.left_margin, vscreeninfo.lower_margin, vscreeninfo.vsync_len,
    281         vscreeninfo.upper_margin, vscreeninfo.pixclock/1000000);
    282 
    283   vscreeninfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
    284   if (Sys::ioctl_(device_fd_, FBIOPUT_VSCREENINFO, &vscreeninfo) < 0) {
    285     IOCTL_LOGE(FBIOPUT_VSCREENINFO, device_type_);
    286     return kErrorHardware;
    287   }
    288 
    289   active_config_index_ = index;
    290 
    291   frame_rate_ = timing_mode->refresh_rate;
    292 
    293   // Get the display attributes for current active config index
    294   GetDisplayAttributes(active_config_index_, &display_attributes_);
    295   UpdateMixerAttributes();
    296 
    297   supported_s3d_modes_.clear();
    298   supported_s3d_modes_.push_back(kS3DModeNone);
    299   for (uint32_t mode = kS3DModeNone + 1; mode < kS3DModeMax; mode ++) {
    300     if (display_attributes_.s3d_config[(HWS3DMode)mode]) {
    301       supported_s3d_modes_.push_back((HWS3DMode)mode);
    302     }
    303   }
    304 
    305   SetS3DMode(kS3DModeNone);
    306 
    307   return kErrorNone;
    308 }
    309 
    310 DisplayError HWHDMI::GetConfigIndex(uint32_t mode, uint32_t *index) {
    311   // Check if the mode is valid and return corresponding index
    312   for (uint32_t i = 0; i < hdmi_modes_.size(); i++) {
    313     if (hdmi_modes_[i] == mode) {
    314       *index = i;
    315       DLOGI("Index = %d for config = %d", *index, mode);
    316       return kErrorNone;
    317     }
    318   }
    319 
    320   DLOGE("Config = %d not supported", mode);
    321   return kErrorNotSupported;
    322 }
    323 
    324 DisplayError HWHDMI::Validate(HWLayers *hw_layers) {
    325   HWDevice::ResetDisplayParams();
    326   return HWDevice::Validate(hw_layers);
    327 }
    328 
    329 DisplayError HWHDMI::GetHWScanInfo(HWScanInfo *scan_info) {
    330   if (!scan_info) {
    331     return kErrorParameters;
    332   }
    333   *scan_info = hw_scan_info_;
    334   return kErrorNone;
    335 }
    336 
    337 DisplayError HWHDMI::GetVideoFormat(uint32_t config_index, uint32_t *video_format) {
    338   if (config_index > hdmi_modes_.size()) {
    339     return kErrorNotSupported;
    340   }
    341 
    342   *video_format = hdmi_modes_[config_index];
    343 
    344   return kErrorNone;
    345 }
    346 
    347 DisplayError HWHDMI::GetMaxCEAFormat(uint32_t *max_cea_format) {
    348   *max_cea_format = HDMI_VFRMT_END;
    349 
    350   return kErrorNone;
    351 }
    352 
    353 DisplayError HWHDMI::OnMinHdcpEncryptionLevelChange(uint32_t min_enc_level) {
    354   DisplayError error = kErrorNone;
    355   int fd = -1;
    356   char data[kMaxStringLength] = {'\0'};
    357 
    358   snprintf(data, sizeof(data), "%s%d/hdcp2p2/min_level_change", fb_path_, fb_node_index_);
    359 
    360   fd = Sys::open_(data, O_WRONLY);
    361   if (fd < 0) {
    362     DLOGW("File '%s' could not be opened.", data);
    363     return kErrorHardware;
    364   }
    365 
    366   snprintf(data, sizeof(data), "%d", min_enc_level);
    367 
    368   ssize_t err = Sys::pwrite_(fd, data, strlen(data), 0);
    369   if (err <= 0) {
    370     DLOGE("Write failed, Error = %s", strerror(errno));
    371     error = kErrorHardware;
    372   }
    373 
    374   Sys::close_(fd);
    375 
    376   return error;
    377 }
    378 
    379 HWScanSupport HWHDMI::MapHWScanSupport(uint32_t value) {
    380   switch (value) {
    381   // TODO(user): Read the scan type from driver defined values instead of hardcoding
    382   case 0:
    383     return kScanNotSupported;
    384   case 1:
    385     return kScanAlwaysOverscanned;
    386   case 2:
    387     return kScanAlwaysUnderscanned;
    388   case 3:
    389     return kScanBoth;
    390   default:
    391     return kScanNotSupported;
    392     break;
    393   }
    394 }
    395 
    396 void HWHDMI::ReadScanInfo() {
    397   int scan_info_file = -1;
    398   ssize_t len = -1;
    399   char data[kPageSize] = {'\0'};
    400 
    401   snprintf(data, sizeof(data), "%s%d/scan_info", fb_path_, fb_node_index_);
    402   scan_info_file = Sys::open_(data, O_RDONLY);
    403   if (scan_info_file < 0) {
    404     DLOGW("File '%s' not found.", data);
    405     return;
    406   }
    407 
    408   memset(&data[0], 0, sizeof(data));
    409   len = Sys::pread_(scan_info_file, data, sizeof(data) - 1, 0);
    410   if (len <= 0) {
    411     Sys::close_(scan_info_file);
    412     DLOGW("File %s%d/scan_info is empty.", fb_path_, fb_node_index_);
    413     return;
    414   }
    415   data[len] = '\0';
    416   Sys::close_(scan_info_file);
    417 
    418   const uint32_t scan_info_max_count = 3;
    419   uint32_t scan_info_count = 0;
    420   char *tokens[scan_info_max_count] = { NULL };
    421   ParseLine(data, tokens, scan_info_max_count, &scan_info_count);
    422   if (scan_info_count != scan_info_max_count) {
    423     DLOGW("Failed to parse scan info string %s", data);
    424     return;
    425   }
    426 
    427   hw_scan_info_.pt_scan_support = MapHWScanSupport(UINT32(atoi(tokens[0])));
    428   hw_scan_info_.it_scan_support = MapHWScanSupport(UINT32(atoi(tokens[1])));
    429   hw_scan_info_.cea_scan_support = MapHWScanSupport(UINT32(atoi(tokens[2])));
    430   DLOGI("PT %d IT %d CEA %d", hw_scan_info_.pt_scan_support, hw_scan_info_.it_scan_support,
    431         hw_scan_info_.cea_scan_support);
    432 }
    433 
    434 int HWHDMI::OpenResolutionFile(int file_mode) {
    435   char file_path[kMaxStringLength];
    436   memset(file_path, 0, sizeof(file_path));
    437   snprintf(file_path , sizeof(file_path), "%s%d/res_info", fb_path_, fb_node_index_);
    438 
    439   int fd = Sys::open_(file_path, file_mode);
    440 
    441   if (fd < 0) {
    442     DLOGE("file '%s' not found : ret = %d err str: %s", file_path, fd, strerror(errno));
    443   }
    444 
    445   return fd;
    446 }
    447 
    448 // Method to request HDMI driver to write a new page of timing info into res_info node
    449 void HWHDMI::RequestNewPage(uint32_t page_number) {
    450   char page_string[kPageSize];
    451   int fd = OpenResolutionFile(O_WRONLY);
    452   if (fd < 0) {
    453     return;
    454   }
    455 
    456   snprintf(page_string, sizeof(page_string), "%d", page_number);
    457 
    458   DLOGI_IF(kTagDriverConfig, "page=%s", page_string);
    459 
    460   ssize_t err = Sys::pwrite_(fd, page_string, sizeof(page_string), 0);
    461   if (err <= 0) {
    462     DLOGE("Write to res_info failed (%s)", strerror(errno));
    463   }
    464 
    465   Sys::close_(fd);
    466 }
    467 
    468 // Reads the contents of res_info node into a buffer if the file is not empty
    469 bool HWHDMI::ReadResolutionFile(char *config_buffer) {
    470   ssize_t bytes_read = 0;
    471   int fd = OpenResolutionFile(O_RDONLY);
    472   if (fd >= 0) {
    473     bytes_read = Sys::pread_(fd, config_buffer, kPageSize, 0);
    474     Sys::close_(fd);
    475   }
    476 
    477   DLOGI_IF(kTagDriverConfig, "bytes_read = %d", bytes_read);
    478 
    479   return (bytes_read > 0);
    480 }
    481 
    482 // Populates the internal timing info structure with the timing info obtained
    483 // from the HDMI driver
    484 DisplayError HWHDMI::ReadTimingInfo() {
    485   uint32_t config_index = 0;
    486   uint32_t page_number = MSM_HDMI_INIT_RES_PAGE;
    487   uint32_t size = sizeof(msm_hdmi_mode_timing_info);
    488 
    489   while (true) {
    490     char config_buffer[kPageSize] = {0};
    491     msm_hdmi_mode_timing_info *info = reinterpret_cast<msm_hdmi_mode_timing_info *>(config_buffer);
    492     RequestNewPage(page_number);
    493 
    494     if (!ReadResolutionFile(config_buffer)) {
    495       break;
    496     }
    497 
    498     while (info->video_format && size < kPageSize && config_index < hdmi_modes_.size()) {
    499       supported_video_modes_[config_index] = *info;
    500       size += sizeof(msm_hdmi_mode_timing_info);
    501 
    502       DLOGI_IF(kTagDriverConfig, "Config=%d Mode %d: (%dx%d) @ %d, pixel formats %d",
    503                config_index,
    504                supported_video_modes_[config_index].video_format,
    505                supported_video_modes_[config_index].active_h,
    506                supported_video_modes_[config_index].active_v,
    507                supported_video_modes_[config_index].refresh_rate,
    508                supported_video_modes_[config_index].pixel_formats);
    509 
    510       info++;
    511       config_index++;
    512     }
    513 
    514     size = sizeof(msm_hdmi_mode_timing_info);
    515     // Request HDMI driver to populate res_info with more
    516     // timing information
    517     page_number++;
    518   }
    519 
    520   if (page_number == MSM_HDMI_INIT_RES_PAGE || config_index == 0) {
    521     DLOGE("No timing information found.");
    522     return kErrorHardware;
    523   }
    524 
    525   return kErrorNone;
    526 }
    527 
    528 bool HWHDMI::IsResolutionFilePresent() {
    529   bool is_file_present = false;
    530   int fd = OpenResolutionFile(O_RDONLY);
    531   if (fd >= 0) {
    532     is_file_present = true;
    533     Sys::close_(fd);
    534   }
    535 
    536   return is_file_present;
    537 }
    538 
    539 void HWHDMI::SetSourceProductInformation(const char *node, const char *name) {
    540   char property_value[kMaxStringLength];
    541   char sys_fs_path[kMaxStringLength];
    542   int hdmi_node_index = GetFBNodeIndex(kDeviceHDMI);
    543   if (hdmi_node_index < 0) {
    544     return;
    545   }
    546 
    547   ssize_t length = 0;
    548   bool prop_read_success = Debug::GetProperty(name, property_value);
    549   if (!prop_read_success) {
    550     return;
    551   }
    552 
    553   snprintf(sys_fs_path , sizeof(sys_fs_path), "%s%d/%s", fb_path_, hdmi_node_index, node);
    554   length = HWDevice::SysFsWrite(sys_fs_path, property_value,
    555                                 static_cast<ssize_t>(strlen(property_value)));
    556   if (length <= 0) {
    557     DLOGW("Failed to write %s = %s", node, property_value);
    558   }
    559 }
    560 
    561 DisplayError HWHDMI::GetDisplayS3DSupport(uint32_t index,
    562                                           HWDisplayAttributes *attrib) {
    563   ssize_t length = -1;
    564   char edid_s3d_str[kPageSize] = {'\0'};
    565   char edid_s3d_path[kMaxStringLength] = {'\0'};
    566   snprintf(edid_s3d_path, sizeof(edid_s3d_path), "%s%d/edid_3d_modes", fb_path_, fb_node_index_);
    567 
    568   if (index > hdmi_modes_.size()) {
    569     return kErrorNotSupported;
    570   }
    571 
    572   attrib->s3d_config[kS3DModeNone] = 1;
    573 
    574   // Three level inception!
    575   // The string looks like 16=SSH,4=FP:TAB:SSH,5=FP:SSH,32=FP:TAB:SSH
    576   // Initialize all the pointers to NULL to avoid crash in function strtok_r()
    577   char *saveptr_l1 = NULL, *saveptr_l2 = NULL, *saveptr_l3 = NULL;
    578   char *l1 = NULL, *l2 = NULL, *l3 = NULL;
    579 
    580   int edid_s3d_node = Sys::open_(edid_s3d_path, O_RDONLY);
    581   if (edid_s3d_node < 0) {
    582     DLOGW("%s could not be opened : %s", edid_s3d_path, strerror(errno));
    583     return kErrorNotSupported;
    584   }
    585 
    586   length = Sys::pread_(edid_s3d_node, edid_s3d_str, sizeof(edid_s3d_str)-1, 0);
    587   if (length <= 0) {
    588     Sys::close_(edid_s3d_node);
    589     return kErrorNotSupported;
    590   }
    591 
    592   l1 = strtok_r(edid_s3d_str, ",", &saveptr_l1);
    593   while (l1 != NULL) {
    594     l2 = strtok_r(l1, "=", &saveptr_l2);
    595     if (l2 != NULL) {
    596       if (hdmi_modes_[index] == (uint32_t)atoi(l2)) {
    597           l3 = strtok_r(saveptr_l2, ":", &saveptr_l3);
    598           while (l3 != NULL) {
    599             if (strncmp("SSH", l3, strlen("SSH")) == 0) {
    600               attrib->s3d_config[kS3DModeLR] = 1;
    601               attrib->s3d_config[kS3DModeRL] = 1;
    602             } else if (strncmp("TAB", l3, strlen("TAB")) == 0) {
    603               attrib->s3d_config[kS3DModeTB] = 1;
    604             } else if (strncmp("FP", l3, strlen("FP")) == 0) {
    605               attrib->s3d_config[kS3DModeFP] = 1;
    606             }
    607             l3 = strtok_r(NULL, ":", &saveptr_l3);
    608           }
    609       }
    610     }
    611     l1 = strtok_r(NULL, ",", &saveptr_l1);
    612   }
    613 
    614   Sys::close_(edid_s3d_node);
    615   return kErrorNone;
    616 }
    617 
    618 bool HWHDMI::IsSupportedS3DMode(HWS3DMode s3d_mode) {
    619   for (uint32_t i = 0; i < supported_s3d_modes_.size(); i++) {
    620     if (supported_s3d_modes_[i] == s3d_mode) {
    621       return true;
    622     }
    623   }
    624   return false;
    625 }
    626 
    627 DisplayError HWHDMI::SetS3DMode(HWS3DMode s3d_mode) {
    628   if (!IsSupportedS3DMode(s3d_mode)) {
    629     DLOGW("S3D mode is not supported s3d_mode = %d", s3d_mode);
    630     return kErrorNotSupported;
    631   }
    632 
    633   std::map<HWS3DMode, msm_hdmi_s3d_mode>::iterator it = s3d_mode_sdm_to_mdp_.find(s3d_mode);
    634   if (it == s3d_mode_sdm_to_mdp_.end()) {
    635     return kErrorNotSupported;
    636   }
    637   msm_hdmi_s3d_mode s3d_mdp_mode = it->second;
    638 
    639   if (active_mdp_s3d_mode_ == s3d_mdp_mode) {
    640     // HDMI_S3D_SIDE_BY_SIDE is an mdp mapping for kS3DModeLR and kS3DModeRL s3d modes. So no need
    641     // to update the s3d_mode node. hw_panel_info needs to be updated to differentiate these two s3d
    642     // modes in strategy
    643     hw_panel_info_.s3d_mode = s3d_mode;
    644     return kErrorNone;
    645   }
    646 
    647   ssize_t length = -1;
    648   char s3d_mode_path[kMaxStringLength] = {'\0'};
    649   char s3d_mode_string[kMaxStringLength] = {'\0'};
    650   snprintf(s3d_mode_path, sizeof(s3d_mode_path), "%s%d/s3d_mode", fb_path_, fb_node_index_);
    651 
    652   int s3d_mode_node = Sys::open_(s3d_mode_path, O_RDWR);
    653   if (s3d_mode_node < 0) {
    654     DLOGW("%s could not be opened : %s", s3d_mode_path, strerror(errno));
    655     return kErrorNotSupported;
    656   }
    657 
    658   snprintf(s3d_mode_string, sizeof(s3d_mode_string), "%d", s3d_mdp_mode);
    659   length = Sys::pwrite_(s3d_mode_node, s3d_mode_string, sizeof(s3d_mode_string), 0);
    660   if (length <= 0) {
    661     DLOGW("Failed to write into s3d node: %s", strerror(errno));
    662     Sys::close_(s3d_mode_node);
    663     return kErrorNotSupported;
    664   }
    665 
    666   active_mdp_s3d_mode_ = s3d_mdp_mode;
    667   hw_panel_info_.s3d_mode = s3d_mode;
    668   Sys::close_(s3d_mode_node);
    669 
    670   DLOGI_IF(kTagDriverConfig, "Set s3d mode %d", hw_panel_info_.s3d_mode);
    671   return kErrorNone;
    672 }
    673 
    674 DisplayError HWHDMI::GetPanelS3DMode() {
    675   ssize_t length = -1;
    676   char s3d_mode_path[kMaxStringLength] = {'\0'};
    677   char s3d_mode_string[kMaxStringLength] = {'\0'};
    678   snprintf(s3d_mode_path, sizeof(s3d_mode_path), "%s%d/s3d_mode", fb_path_, fb_node_index_);
    679   int panel_s3d_mode = 0;
    680 
    681   int s3d_mode_node = Sys::open_(s3d_mode_path, O_RDWR);
    682   if (s3d_mode_node < 0) {
    683     DLOGE("%s could not be opened : %s", s3d_mode_path, strerror(errno));
    684     return kErrorNotSupported;
    685   }
    686 
    687   length = Sys::pread_(s3d_mode_node, s3d_mode_string, sizeof(s3d_mode_string), 0);
    688   if (length <= 0) {
    689     DLOGE("Failed read s3d node: %s", strerror(errno));
    690     Sys::close_(s3d_mode_node);
    691     return kErrorNotSupported;
    692   }
    693 
    694   panel_s3d_mode = atoi(s3d_mode_string);
    695   if (panel_s3d_mode < HDMI_S3D_NONE || panel_s3d_mode >= HDMI_S3D_MAX) {
    696     Sys::close_(s3d_mode_node);
    697     DLOGW("HDMI panel S3D mode is not supported panel_s3d_mode = %d", panel_s3d_mode);
    698     return kErrorUndefined;
    699   }
    700 
    701   active_mdp_s3d_mode_  = static_cast<msm_hdmi_s3d_mode>(panel_s3d_mode);
    702   Sys::close_(s3d_mode_node);
    703 
    704   DLOGI_IF(kTagDriverConfig, "Get HDMI panel s3d mode %d", active_mdp_s3d_mode_);
    705   return kErrorNone;
    706 }
    707 
    708 DisplayError HWHDMI::GetDynamicFrameRateMode(uint32_t refresh_rate, uint32_t *mode,
    709                                              DynamicFPSData *data, uint32_t *config_index) {
    710   msm_hdmi_mode_timing_info *cur = NULL;
    711   msm_hdmi_mode_timing_info *dst = NULL;
    712   uint32_t i = 0;
    713   int pre_refresh_rate_diff = 0;
    714   bool pre_unstd_mode = false;
    715 
    716   for (i = 0; i < hdmi_modes_.size(); i++) {
    717     msm_hdmi_mode_timing_info *timing_mode = &supported_video_modes_[i];
    718     if (timing_mode->video_format == hdmi_modes_[active_config_index_]) {
    719       cur = timing_mode;
    720       break;
    721     }
    722   }
    723 
    724   if (cur == NULL) {
    725     DLOGE("can't find timing info for active config index(%d)", active_config_index_);
    726     return kErrorUndefined;
    727   }
    728 
    729   if (cur->refresh_rate != frame_rate_) {
    730     pre_unstd_mode = true;
    731   }
    732 
    733   if (i >= hdmi_modes_.size()) {
    734     return kErrorNotSupported;
    735   }
    736 
    737   dst = cur;
    738   pre_refresh_rate_diff = static_cast<int>(dst->refresh_rate) - static_cast<int>(refresh_rate);
    739 
    740   for (i = 0; i < hdmi_modes_.size(); i++) {
    741     msm_hdmi_mode_timing_info *timing_mode = &supported_video_modes_[i];
    742     if (cur->active_h == timing_mode->active_h &&
    743        cur->active_v == timing_mode->active_v &&
    744        cur->pixel_formats == timing_mode->pixel_formats ) {
    745       int cur_refresh_rate_diff = static_cast<int>(timing_mode->refresh_rate) -
    746                                   static_cast<int>(refresh_rate);
    747       if (abs(pre_refresh_rate_diff) > abs(cur_refresh_rate_diff)) {
    748         pre_refresh_rate_diff = cur_refresh_rate_diff;
    749         dst = timing_mode;
    750       }
    751     }
    752   }
    753 
    754   if (pre_refresh_rate_diff > kThresholdRefreshRate) {
    755     return kErrorNotSupported;
    756   }
    757 
    758   GetConfigIndex(dst->video_format, config_index);
    759 
    760   data->hor_front_porch = dst->front_porch_h;
    761   data->hor_back_porch = dst->back_porch_h;
    762   data->hor_pulse_width = dst->pulse_width_h;
    763   data->clk_rate_hz = dst->pixel_freq;
    764   data->fps = refresh_rate;
    765 
    766   if (dst->front_porch_h != cur->front_porch_h) {
    767     *mode = kModeHFP;
    768   }
    769 
    770   if (dst->refresh_rate != refresh_rate || dst->pixel_freq != cur->pixel_freq) {
    771     if (*mode == kModeHFP) {
    772       if (dst->refresh_rate != refresh_rate) {
    773         *mode = kModeHFPCalcClock;
    774       } else {
    775         *mode = kModeClockHFP;
    776       }
    777     } else {
    778         *mode = kModeClock;
    779     }
    780   }
    781 
    782   if (pre_unstd_mode && (*mode == kModeHFP)) {
    783     *mode = kModeClockHFP;
    784   }
    785 
    786   return kErrorNone;
    787 }
    788 
    789 DisplayError HWHDMI::SetRefreshRate(uint32_t refresh_rate) {
    790   char mode_path[kMaxStringLength] = {0};
    791   char node_path[kMaxStringLength] = {0};
    792   uint32_t mode = kModeClock;
    793   uint32_t config_index = 0;
    794   DynamicFPSData data;
    795   DisplayError error = kErrorNone;
    796 
    797   if (refresh_rate == frame_rate_) {
    798     return error;
    799   }
    800 
    801   error = GetDynamicFrameRateMode(refresh_rate, &mode, &data, &config_index);
    802   if (error != kErrorNone) {
    803     return error;
    804   }
    805 
    806   snprintf(mode_path, sizeof(mode_path), "%s%d/msm_fb_dfps_mode", fb_path_, fb_node_index_);
    807   snprintf(node_path, sizeof(node_path), "%s%d/dynamic_fps", fb_path_, fb_node_index_);
    808 
    809   int fd_mode = Sys::open_(mode_path, O_WRONLY);
    810   if (fd_mode < 0) {
    811     DLOGE("Failed to open %s with error %s", mode_path, strerror(errno));
    812     return kErrorFileDescriptor;
    813   }
    814 
    815   char dfps_mode[kMaxStringLength];
    816   snprintf(dfps_mode, sizeof(dfps_mode), "%d", mode);
    817   DLOGI_IF(kTagDriverConfig, "Setting dfps_mode  = %d", mode);
    818   ssize_t len = Sys::pwrite_(fd_mode, dfps_mode, strlen(dfps_mode), 0);
    819   if (len < 0) {
    820     DLOGE("Failed to enable dfps mode %d with error %s", mode, strerror(errno));
    821     Sys::close_(fd_mode);
    822     return kErrorUndefined;
    823   }
    824   Sys::close_(fd_mode);
    825 
    826   int fd_node = Sys::open_(node_path, O_WRONLY);
    827   if (fd_node < 0) {
    828     DLOGE("Failed to open %s with error %s", node_path, strerror(errno));
    829     return kErrorFileDescriptor;
    830   }
    831 
    832   char refresh_rate_string[kMaxStringLength];
    833   if (mode == kModeHFP || mode == kModeClock) {
    834     snprintf(refresh_rate_string, sizeof(refresh_rate_string), "%d", data.fps);
    835     DLOGI_IF(kTagDriverConfig, "Setting refresh rate = %d", data.fps);
    836   } else {
    837     snprintf(refresh_rate_string, sizeof(refresh_rate_string), "%d %d %d %d %d",
    838              data.hor_front_porch, data.hor_back_porch, data.hor_pulse_width,
    839              data.clk_rate_hz, data.fps);
    840   }
    841   len = Sys::pwrite_(fd_node, refresh_rate_string, strlen(refresh_rate_string), 0);
    842   if (len < 0) {
    843     DLOGE("Failed to write %d with error %s", refresh_rate, strerror(errno));
    844     Sys::close_(fd_node);
    845     return kErrorUndefined;
    846   }
    847   Sys::close_(fd_node);
    848 
    849   error = ReadTimingInfo();
    850   if (error != kErrorNone) {
    851     return error;
    852   }
    853 
    854   GetDisplayAttributes(config_index, &display_attributes_);
    855   UpdateMixerAttributes();
    856 
    857   frame_rate_ = refresh_rate;
    858   active_config_index_ = config_index;
    859 
    860   DLOGI_IF(kTagDriverConfig, "config_index(%d) Mode(%d) frame_rate(%d)",
    861            config_index,
    862            mode,
    863            frame_rate_);
    864 
    865   return kErrorNone;
    866 }
    867 
    868 void HWHDMI::UpdateMixerAttributes() {
    869   mixer_attributes_.width = display_attributes_.x_pixels;
    870   mixer_attributes_.height = display_attributes_.y_pixels;
    871   mixer_attributes_.split_left = display_attributes_.is_device_split ?
    872       (display_attributes_.x_pixels / 2) : mixer_attributes_.width;
    873 }
    874 
    875 }  // namespace sdm
    876 
    877