Home | History | Annotate | Download | only in fb
      1 /*
      2 * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
      3 *
      4 * Redistribution and use in source and binary forms, with or without modification, are permitted
      5 * provided that the following conditions are met:
      6 *    * Redistributions of source code must retain the above copyright notice, this list of
      7 *      conditions and the following disclaimer.
      8 *    * Redistributions in binary form must reproduce the above copyright notice, this list of
      9 *      conditions and the following disclaimer in the documentation and/or other materials provided
     10 *      with the distribution.
     11 *    * Neither the name of The Linux Foundation nor the names of its contributors may be used to
     12 *      endorse or promote products derived from this software without specific prior written
     13 *      permission.
     14 *
     15 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     17 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
     18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     21 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23 */
     24 
     25 #include <string.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <fcntl.h>
     29 #include <sys/stat.h>
     30 #include <sys/types.h>
     31 #include <utils/constants.h>
     32 #include <utils/debug.h>
     33 #include <utils/sys.h>
     34 #include <dlfcn.h>
     35 
     36 #include <algorithm>
     37 #include <iostream>
     38 #include <fstream>
     39 #include <map>
     40 #include <memory>
     41 #include <string>
     42 #include <utility>
     43 #include <vector>
     44 
     45 #include "hw_info.h"
     46 
     47 #define __CLASS__ "HWInfo"
     48 
     49 using std::vector;
     50 using std::map;
     51 using std::string;
     52 using std::fstream;
     53 using std::to_string;
     54 
     55 namespace sdm {
     56 
     57 // kDefaultFormatSupport contains the bit map of supported formats for each hw blocks.
     58 // For eg: if Cursor supports MDP_RGBA_8888[bit-13] and MDP_RGB_565[bit-0], then cursor pipe array
     59 // contains { 0x01[0-3], 0x00[4-7], 0x00[8-12], 0x01[13-16], 0x00[17-20], 0x00[21-24], 0x00[24-28] }
     60 const std::bitset<8> HWInfo::kDefaultFormatSupport[kHWSubBlockMax][
     61                                                       BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)] = {
     62   { 0xFF, 0xF5, 0x1C, 0x1E, 0x20, 0xFF, 0x01, 0x00, 0xFE, 0x1F },  // kHWVIGPipe
     63   { 0x33, 0xE0, 0x00, 0x16, 0x00, 0xBF, 0x00, 0x00, 0xFE, 0x07 },  // kHWRGBPipe
     64   { 0x33, 0xE0, 0x00, 0x16, 0x00, 0xBF, 0x00, 0x00, 0xFE, 0x07 },  // kHWDMAPipe
     65   { 0x12, 0x60, 0x0C, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00 },  // kHWCursorPipe
     66   { 0xFF, 0xF5, 0x1C, 0x1E, 0x20, 0xFF, 0x01, 0x00, 0xFE, 0x1F },  // kHWRotatorInput
     67   { 0xFF, 0xF5, 0x1C, 0x1E, 0x20, 0xFF, 0x01, 0x00, 0xFE, 0x1F },  // kHWRotatorOutput
     68   { 0x3F, 0xF4, 0x10, 0x1E, 0x20, 0xFF, 0x01, 0x00, 0xAA, 0x16 },  // kHWWBIntfOutput
     69 };
     70 
     71 int HWInfo::ParseString(const char *input, char *tokens[], const uint32_t max_token,
     72                         const char *delim, uint32_t *count) {
     73   char *tmp_token = NULL;
     74   char *temp_ptr;
     75   uint32_t index = 0;
     76   if (!input) {
     77     return -1;
     78   }
     79   tmp_token = strtok_r(const_cast<char *>(input), delim, &temp_ptr);
     80   while (tmp_token && index < max_token) {
     81     tokens[index++] = tmp_token;
     82     tmp_token = strtok_r(NULL, delim, &temp_ptr);
     83   }
     84   *count = index;
     85 
     86   return 0;
     87 }
     88 
     89 DisplayError HWInfo::GetDynamicBWLimits(HWResourceInfo *hw_resource) {
     90   Sys::fstream fs(kBWModeBitmap, fstream::in);
     91   if (!fs.is_open()) {
     92     DLOGE("File '%s' not found", kBWModeBitmap);
     93     return kErrorHardware;
     94   }
     95 
     96   HWDynBwLimitInfo* bw_info = &hw_resource->dyn_bw_info;
     97   for (int index = 0; index < kBwModeMax; index++) {
     98     bw_info->total_bw_limit[index] = UINT32(hw_resource->max_bandwidth_low);
     99     bw_info->pipe_bw_limit[index] = hw_resource->max_pipe_bw;
    100   }
    101 
    102   uint32_t token_count = 0;
    103   const uint32_t max_count = kBwModeMax;
    104   char *tokens[max_count] = { NULL };
    105   string line;
    106   while (Sys::getline_(fs, line)) {
    107     if (!ParseString(line.c_str(), tokens, max_count, ":, =\n", &token_count)) {
    108       if (!strncmp(tokens[0], "default_pipe", strlen("default_pipe"))) {
    109         bw_info->pipe_bw_limit[kBwDefault] = UINT32(atoi(tokens[1]));
    110       } else if (!strncmp(tokens[0], "camera_pipe", strlen("camera_pipe"))) {
    111         bw_info->pipe_bw_limit[kBwCamera] = UINT32(atoi(tokens[1]));
    112       } else if (!strncmp(tokens[0], "vflip_pipe", strlen("vflip_pipe"))) {
    113         bw_info->pipe_bw_limit[kBwVFlip] = UINT32(atoi(tokens[1]));
    114       } else if (!strncmp(tokens[0], "hflip_pipe", strlen("hflip_pipe"))) {
    115         bw_info->pipe_bw_limit[kBwHFlip] = UINT32(atoi(tokens[1]));
    116       } else if (!strncmp(tokens[0], "default", strlen("default"))) {
    117         bw_info->total_bw_limit[kBwDefault] = UINT32(atoi(tokens[1]));
    118       } else if (!strncmp(tokens[0], "camera", strlen("camera"))) {
    119         bw_info->total_bw_limit[kBwCamera] = UINT32(atoi(tokens[1]));
    120       } else if (!strncmp(tokens[0], "vflip", strlen("vflip"))) {
    121         bw_info->total_bw_limit[kBwVFlip] = UINT32(atoi(tokens[1]));
    122       } else if (!strncmp(tokens[0], "hflip", strlen("hflip"))) {
    123         bw_info->total_bw_limit[kBwHFlip] = UINT32(atoi(tokens[1]));
    124       }
    125     }
    126   }
    127 
    128   return kErrorNone;
    129 }
    130 
    131 DisplayError HWInfo::GetHWResourceInfo(HWResourceInfo *hw_resource) {
    132   if (hw_resource_) {
    133     *hw_resource = *hw_resource_;
    134     return kErrorNone;
    135   }
    136   string fb_path = "/sys/devices/virtual/graphics/fb"
    137                       + to_string(kHWCapabilitiesNode) + "/mdp/caps";
    138 
    139   Sys::fstream fs(fb_path, fstream::in);
    140   if (!fs.is_open()) {
    141     DLOGE("File '%s' not found", fb_path.c_str());
    142     return kErrorHardware;
    143   }
    144 
    145   hw_resource_ = new HWResourceInfo;
    146 
    147   InitSupportedFormatMap(hw_resource_);
    148   hw_resource_->hw_version = kHWMdssVersion5;
    149 
    150   uint32_t token_count = 0;
    151   const uint32_t max_count = 256;
    152   char *tokens[max_count] = { NULL };
    153   string line;
    154   while (Sys::getline_(fs, line)) {
    155     // parse the line and update information accordingly
    156     if (!ParseString(line.c_str(), tokens, max_count, ":, =\n", &token_count)) {
    157       if (!strncmp(tokens[0], "hw_rev", strlen("hw_rev"))) {
    158         hw_resource_->hw_revision = UINT32(atoi(tokens[1]));  // HW Rev, v1/v2
    159       } else if (!strncmp(tokens[0], "rot_input_fmts", strlen("rot_input_fmts"))) {
    160         ParseFormats(&tokens[1], (token_count - 1), kHWRotatorInput, hw_resource_);
    161       } else if (!strncmp(tokens[0], "rot_output_fmts", strlen("rot_output_fmts"))) {
    162         ParseFormats(&tokens[1], (token_count - 1), kHWRotatorOutput, hw_resource_);
    163       } else if (!strncmp(tokens[0], "wb_output_fmts", strlen("wb_output_fmts"))) {
    164         ParseFormats(&tokens[1], (token_count - 1), kHWWBIntfOutput, hw_resource_);
    165       } else if (!strncmp(tokens[0], "blending_stages", strlen("blending_stages"))) {
    166         hw_resource_->num_blending_stages = UINT8(atoi(tokens[1]));
    167       } else if (!strncmp(tokens[0], "max_downscale_ratio", strlen("max_downscale_ratio"))) {
    168         hw_resource_->max_scale_down = UINT32(atoi(tokens[1]));
    169       } else if (!strncmp(tokens[0], "max_upscale_ratio", strlen("max_upscale_ratio"))) {
    170         hw_resource_->max_scale_up = UINT32(atoi(tokens[1]));
    171       } else if (!strncmp(tokens[0], "max_bandwidth_low", strlen("max_bandwidth_low"))) {
    172         hw_resource_->max_bandwidth_low = UINT64(atol(tokens[1]));
    173       } else if (!strncmp(tokens[0], "max_bandwidth_high", strlen("max_bandwidth_high"))) {
    174         hw_resource_->max_bandwidth_high = UINT64(atol(tokens[1]));
    175       } else if (!strncmp(tokens[0], "max_mixer_width", strlen("max_mixer_width"))) {
    176         hw_resource_->max_mixer_width = UINT32(atoi(tokens[1]));
    177       } else if (!strncmp(tokens[0], "max_pipe_width", strlen("max_pipe_width"))) {
    178         hw_resource_->max_pipe_width = UINT32(atoi(tokens[1]));
    179       } else if (!strncmp(tokens[0], "max_cursor_size", strlen("max_cursor_size"))) {
    180         hw_resource_->max_cursor_size = UINT32(atoi(tokens[1]));
    181       } else if (!strncmp(tokens[0], "max_pipe_bw", strlen("max_pipe_bw"))) {
    182         hw_resource_->max_pipe_bw = UINT32(atoi(tokens[1]));
    183       } else if (!strncmp(tokens[0], "max_mdp_clk", strlen("max_mdp_clk"))) {
    184         hw_resource_->max_sde_clk = UINT32(atoi(tokens[1]));
    185       } else if (!strncmp(tokens[0], "clk_fudge_factor", strlen("clk_fudge_factor"))) {
    186         hw_resource_->clk_fudge_factor = FLOAT(atoi(tokens[1])) / FLOAT(atoi(tokens[2]));
    187       } else if (!strncmp(tokens[0], "fmt_mt_nv12_factor", strlen("fmt_mt_nv12_factor"))) {
    188         hw_resource_->macrotile_nv12_factor = UINT32(atoi(tokens[1]));
    189       } else if (!strncmp(tokens[0], "fmt_mt_factor", strlen("fmt_mt_factor"))) {
    190         hw_resource_->macrotile_factor = UINT32(atoi(tokens[1]));
    191       } else if (!strncmp(tokens[0], "fmt_linear_factor", strlen("fmt_linear_factor"))) {
    192         hw_resource_->linear_factor = UINT32(atoi(tokens[1]));
    193       } else if (!strncmp(tokens[0], "scale_factor", strlen("scale_factor"))) {
    194         hw_resource_->scale_factor = UINT32(atoi(tokens[1]));
    195       } else if (!strncmp(tokens[0], "xtra_ff_factor", strlen("xtra_ff_factor"))) {
    196         hw_resource_->extra_fudge_factor = UINT32(atoi(tokens[1]));
    197       } else if (!strncmp(tokens[0], "amortizable_threshold", strlen("amortizable_threshold"))) {
    198         hw_resource_->amortizable_threshold = UINT32(atoi(tokens[1]));
    199       } else if (!strncmp(tokens[0], "system_overhead_lines", strlen("system_overhead_lines"))) {
    200         hw_resource_->system_overhead_lines = UINT32(atoi(tokens[1]));
    201       } else if (!strncmp(tokens[0], "wb_intf_index", strlen("wb_intf_index"))) {
    202         hw_resource_->writeback_index = UINT32(atoi(tokens[1]));
    203       } else if (!strncmp(tokens[0], "dest_scaler_count", strlen("dest_scaler_count"))) {
    204         hw_resource_->hw_dest_scalar_info.count = UINT32(atoi(tokens[1]));
    205       } else if (!strncmp(tokens[0], "max_dest_scale_up", strlen("max_dest_scale_up"))) {
    206         hw_resource_->hw_dest_scalar_info.max_scale_up = UINT32(atoi(tokens[1]));
    207       } else if (!strncmp(tokens[0], "max_dest_scaler_input_width",
    208                  strlen("max_dest_scaler_input_width"))) {
    209         hw_resource_->hw_dest_scalar_info.max_input_width = UINT32(atoi(tokens[1]));
    210       } else if (!strncmp(tokens[0], "max_dest_scaler_output_width",
    211                  strlen("max_dest_scaler_output_width"))) {
    212         hw_resource_->hw_dest_scalar_info.max_output_width = UINT32(atoi(tokens[1]));
    213       } else if (!strncmp(tokens[0], "features", strlen("features"))) {
    214         for (uint32_t i = 0; i < token_count; i++) {
    215           if (!strncmp(tokens[i], "bwc", strlen("bwc"))) {
    216             hw_resource_->has_bwc = true;
    217           } else if (!strncmp(tokens[i], "ubwc", strlen("ubwc"))) {
    218             hw_resource_->has_ubwc = true;
    219           } else if (!strncmp(tokens[i], "decimation", strlen("decimation"))) {
    220             hw_resource_->has_decimation = true;
    221           } else if (!strncmp(tokens[i], "tile_format", strlen("tile_format"))) {
    222             hw_resource_->has_macrotile = true;
    223           } else if (!strncmp(tokens[i], "src_split", strlen("src_split"))) {
    224             hw_resource_->is_src_split = true;
    225           } else if (!strncmp(tokens[i], "non_scalar_rgb", strlen("non_scalar_rgb"))) {
    226             hw_resource_->has_non_scalar_rgb = true;
    227           } else if (!strncmp(tokens[i], "perf_calc", strlen("perf_calc"))) {
    228             hw_resource_->perf_calc = true;
    229           } else if (!strncmp(tokens[i], "dynamic_bw_limit", strlen("dynamic_bw_limit"))) {
    230             hw_resource_->has_dyn_bw_support = true;
    231           } else if (!strncmp(tokens[i], "separate_rotator", strlen("separate_rotator"))) {
    232             hw_resource_->separate_rotator = true;
    233           } else if (!strncmp(tokens[i], "qseed3", strlen("qseed3"))) {
    234             hw_resource_->has_qseed3 = true;
    235           } else if (!strncmp(tokens[i], "has_ppp", strlen("has_ppp"))) {
    236             hw_resource_->has_ppp = true;
    237           } else if (!strncmp(tokens[i], "concurrent_writeback", strlen("concurrent_writeback"))) {
    238             hw_resource_->has_concurrent_writeback = true;
    239           } else if (!strncmp(tokens[i], "avr", strlen("avr"))) {
    240             hw_resource_->has_avr = true;
    241           } else if (!strncmp(tokens[i], "hdr", strlen("hdr"))) {
    242             hw_resource_->has_hdr = true;
    243           }
    244         }
    245       } else if (!strncmp(tokens[0], "pipe_count", strlen("pipe_count"))) {
    246         uint32_t pipe_count = UINT8(atoi(tokens[1]));
    247         for (uint32_t i = 0; i < pipe_count; i++) {
    248           Sys::getline_(fs, line);
    249           if (!ParseString(line.c_str(), tokens, max_count, ": =\n", &token_count)) {
    250             HWPipeCaps pipe_caps;
    251             pipe_caps.type = kPipeTypeUnused;
    252             for (uint32_t j = 0; j < token_count; j += 2) {
    253               if (!strncmp(tokens[j], "pipe_type", strlen("pipe_type"))) {
    254                 if (!strncmp(tokens[j+1], "vig", strlen("vig"))) {
    255                   pipe_caps.type = kPipeTypeVIG;
    256                   hw_resource_->num_vig_pipe++;
    257                 } else if (!strncmp(tokens[j+1], "rgb", strlen("rgb"))) {
    258                   pipe_caps.type = kPipeTypeRGB;
    259                   hw_resource_->num_rgb_pipe++;
    260                 } else if (!strncmp(tokens[j+1], "dma", strlen("dma"))) {
    261                   pipe_caps.type = kPipeTypeDMA;
    262                   hw_resource_->num_dma_pipe++;
    263                 } else if (!strncmp(tokens[j+1], "cursor", strlen("cursor"))) {
    264                   pipe_caps.type = kPipeTypeCursor;
    265                   hw_resource_->num_cursor_pipe++;
    266                 }
    267               } else if (!strncmp(tokens[j], "pipe_ndx", strlen("pipe_ndx"))) {
    268                 pipe_caps.id = UINT32(atoi(tokens[j+1]));
    269               } else if (!strncmp(tokens[j], "rects", strlen("rects"))) {
    270                 pipe_caps.max_rects = UINT32(atoi(tokens[j+1]));
    271               } else if (!strncmp(tokens[j], "fmts_supported", strlen("fmts_supported"))) {
    272                 char *tokens_fmt[max_count] = { NULL };
    273                 uint32_t token_fmt_count = 0;
    274                 if (!ParseString(tokens[j+1], tokens_fmt, max_count, ",\n", &token_fmt_count)) {
    275                   if (pipe_caps.type == kPipeTypeVIG) {
    276                     ParseFormats(tokens_fmt, token_fmt_count, kHWVIGPipe, hw_resource_);
    277                   } else if (pipe_caps.type == kPipeTypeRGB) {
    278                     ParseFormats(tokens_fmt, token_fmt_count, kHWRGBPipe, hw_resource_);
    279                   } else if (pipe_caps.type == kPipeTypeDMA) {
    280                     ParseFormats(tokens_fmt, token_fmt_count, kHWDMAPipe, hw_resource_);
    281                   } else if (pipe_caps.type == kPipeTypeCursor) {
    282                     ParseFormats(tokens_fmt, token_fmt_count, kHWCursorPipe, hw_resource_);
    283                   }
    284                 }
    285               }
    286             }
    287             hw_resource_->hw_pipes.push_back(pipe_caps);
    288           }
    289         }
    290       }
    291     }
    292   }
    293 
    294   // Disable destination scalar count to 0 if extension library is not present
    295   DynLib extension_lib;
    296   if (!extension_lib.Open("libsdmextension.so")) {
    297     hw_resource_->hw_dest_scalar_info.count = 0;
    298   }
    299 
    300   DLOGI("SDE Version = %d, SDE Revision = %x, RGB = %d, VIG = %d, DMA = %d, Cursor = %d",
    301         hw_resource_->hw_version, hw_resource_->hw_revision, hw_resource_->num_rgb_pipe,
    302         hw_resource_->num_vig_pipe, hw_resource_->num_dma_pipe, hw_resource_->num_cursor_pipe);
    303   DLOGI("Upscale Ratio = %d, Downscale Ratio = %d, Blending Stages = %d",
    304         hw_resource_->max_scale_up, hw_resource_->max_scale_down,
    305         hw_resource_->num_blending_stages);
    306   DLOGI("SourceSplit = %d QSEED3 = %d", hw_resource_->is_src_split, hw_resource_->has_qseed3);
    307   DLOGI("BWC = %d, UBWC = %d, Decimation = %d, Tile Format = %d Concurrent Writeback = %d",
    308         hw_resource_->has_bwc, hw_resource_->has_ubwc, hw_resource_->has_decimation,
    309         hw_resource_->has_macrotile, hw_resource_->has_concurrent_writeback);
    310   DLOGI("MaxLowBw = %" PRIu64 " , MaxHighBw = % " PRIu64 "", hw_resource_->max_bandwidth_low,
    311         hw_resource_->max_bandwidth_high);
    312   DLOGI("MaxPipeBw = %" PRIu64 " KBps, MaxSDEClock = % " PRIu64 " Hz, ClockFudgeFactor = %f",
    313         hw_resource_->max_pipe_bw, hw_resource_->max_sde_clk, hw_resource_->clk_fudge_factor);
    314   DLOGI("Prefill factors: Tiled_NV12 = %d, Tiled = %d, Linear = %d, Scale = %d, Fudge_factor = %d",
    315         hw_resource_->macrotile_nv12_factor, hw_resource_->macrotile_factor,
    316         hw_resource_->linear_factor, hw_resource_->scale_factor, hw_resource_->extra_fudge_factor);
    317 
    318   // Avoid rotator for MDP3 harware.
    319   if ((hw_resource_->separate_rotator || hw_resource_->num_dma_pipe) && !hw_resource_->has_ppp) {
    320     GetHWRotatorInfo(hw_resource_);
    321   }
    322 
    323   // If the driver doesn't spell out the wb index, assume it to be the number of rotators,
    324   // based on legacy implementation.
    325   if (hw_resource_->writeback_index == kHWBlockMax) {
    326     hw_resource_->writeback_index = hw_resource_->hw_rot_info.num_rotator;
    327   }
    328 
    329   if (hw_resource_->has_dyn_bw_support) {
    330     DisplayError ret = GetDynamicBWLimits(hw_resource_);
    331     if (ret != kErrorNone) {
    332       DLOGE("Failed to read dynamic band width info");
    333       return ret;
    334     }
    335 
    336     DLOGI("Has Support for multiple bw limits shown below");
    337     for (int index = 0; index < kBwModeMax; index++) {
    338       DLOGI("Mode-index=%d  total_bw_limit=%d and pipe_bw_limit=%d",
    339             index, hw_resource_->dyn_bw_info.total_bw_limit[index],
    340             hw_resource_->dyn_bw_info.pipe_bw_limit[index]);
    341     }
    342   }
    343 
    344   *hw_resource = *hw_resource_;
    345 
    346   return kErrorNone;
    347 }
    348 
    349 DisplayError HWInfo::GetHWRotatorInfo(HWResourceInfo *hw_resource) {
    350   if (GetMDSSRotatorInfo(hw_resource) != kErrorNone)
    351     return GetV4L2RotatorInfo(hw_resource);
    352 
    353   return kErrorNone;
    354 }
    355 
    356 DisplayError HWInfo::GetMDSSRotatorInfo(HWResourceInfo *hw_resource) {
    357   Sys::fstream fs(kRotatorCapsPath, fstream::in);
    358   if (!fs.is_open()) {
    359     DLOGW("File '%s' not found", kRotatorCapsPath);
    360     return kErrorNotSupported;
    361   }
    362 
    363   uint32_t token_count = 0;
    364   const uint32_t max_count = 10;
    365   char *tokens[max_count] = { NULL };
    366   string line;
    367 
    368   hw_resource->hw_rot_info.type = HWRotatorInfo::ROT_TYPE_MDSS;
    369   while (Sys::getline_(fs, line)) {
    370     if (!ParseString(line.c_str(), tokens, max_count, ":, =\n", &token_count)) {
    371       if (!strncmp(tokens[0], "wb_count", strlen("wb_count"))) {
    372         hw_resource->hw_rot_info.num_rotator = UINT8(atoi(tokens[1]));
    373         hw_resource->hw_rot_info.device_path = "/dev/mdss_rotator";
    374       } else if (!strncmp(tokens[0], "downscale", strlen("downscale"))) {
    375         hw_resource->hw_rot_info.has_downscale = UINT8(atoi(tokens[1]));
    376       }
    377     }
    378   }
    379 
    380   DLOGI("MDSS Rotator: Count = %d, Downscale = %d, Min_downscale = %f",
    381         hw_resource->hw_rot_info.num_rotator, hw_resource->hw_rot_info.has_downscale,
    382         hw_resource->hw_rot_info.min_downscale);
    383 
    384   return kErrorNone;
    385 }
    386 
    387 DisplayError HWInfo::GetV4L2RotatorInfo(HWResourceInfo *hw_resource) {
    388   string v4l2_path = "/sys/class/video4linux/video";
    389   const uint32_t kMaxV4L2Nodes = 64;
    390   bool found = false;
    391 
    392   for (uint32_t i = 0; (i < kMaxV4L2Nodes) && (false == found); i++) {
    393     string path = v4l2_path + to_string(i) + "/name";
    394     Sys::fstream fs(path, fstream::in);
    395     if (!fs.is_open()) {
    396       continue;
    397     }
    398 
    399     string line;
    400     if (Sys::getline_(fs, line) &&
    401         (!strncmp(line.c_str(), "sde_rotator", strlen("sde_rotator")))) {
    402        hw_resource->hw_rot_info.device_path = string("/dev/video" + to_string(i));
    403        hw_resource->hw_rot_info.num_rotator++;
    404        hw_resource->hw_rot_info.type = HWRotatorInfo::ROT_TYPE_V4L2;
    405        hw_resource->hw_rot_info.has_downscale = true;
    406 
    407        string caps_path = v4l2_path + to_string(i) + "/device/caps";
    408        Sys::fstream caps_fs(caps_path, fstream::in);
    409 
    410        if (caps_fs.is_open()) {
    411          uint32_t token_count = 0;
    412          const uint32_t max_count = 10;
    413          char *tokens[max_count] = { NULL };
    414          string caps;
    415          while (Sys::getline_(caps_fs, caps)) {
    416            if (!ParseString(caps.c_str(), tokens, max_count, ":, =\n", &token_count)) {
    417              if (!strncmp(tokens[0], "downscale_compression", strlen("downscale_compression"))) {
    418                hw_resource->hw_rot_info.downscale_compression = UINT8(atoi(tokens[1]));
    419              } else if (!strncmp(tokens[0], "min_downscale", strlen("min_downscale"))) {
    420                hw_resource->hw_rot_info.min_downscale = FLOAT(atof(tokens[1]));
    421              }
    422            }
    423          }
    424        }
    425 
    426        // We support only 1 rotator
    427        found = true;
    428     }
    429   }
    430 
    431   DLOGI("V4L2 Rotator: Count = %d, Downscale = %d, Min_downscale = %f, Downscale_compression = %d",
    432         hw_resource->hw_rot_info.num_rotator, hw_resource->hw_rot_info.has_downscale,
    433         hw_resource->hw_rot_info.min_downscale, hw_resource->hw_rot_info.downscale_compression);
    434 
    435   return kErrorNone;
    436 }
    437 
    438 LayerBufferFormat HWInfo::GetSDMFormat(int mdp_format) {
    439   switch (mdp_format) {
    440   case MDP_ARGB_8888:              return kFormatARGB8888;
    441   case MDP_RGBA_8888:              return kFormatRGBA8888;
    442   case MDP_BGRA_8888:              return kFormatBGRA8888;
    443   case MDP_XRGB_8888:              return kFormatXRGB8888;
    444   case MDP_RGBX_8888:              return kFormatRGBX8888;
    445   case MDP_BGRX_8888:              return kFormatBGRX8888;
    446   case MDP_RGBA_5551:              return kFormatRGBA5551;
    447   case MDP_RGBA_4444:              return kFormatRGBA4444;
    448   case MDP_RGB_888:                return kFormatRGB888;
    449   case MDP_BGR_888:                return kFormatBGR888;
    450   case MDP_RGB_565:                return kFormatRGB565;
    451   case MDP_BGR_565:                return kFormatBGR565;
    452   case MDP_RGBA_8888_UBWC:         return kFormatRGBA8888Ubwc;
    453   case MDP_RGBX_8888_UBWC:         return kFormatRGBX8888Ubwc;
    454   case MDP_RGB_565_UBWC:           return kFormatBGR565Ubwc;
    455   case MDP_Y_CB_CR_H2V2:           return kFormatYCbCr420Planar;
    456   case MDP_Y_CR_CB_H2V2:           return kFormatYCrCb420Planar;
    457   case MDP_Y_CR_CB_GH2V2:          return kFormatYCrCb420PlanarStride16;
    458   case MDP_Y_CBCR_H2V2:            return kFormatYCbCr420SemiPlanar;
    459   case MDP_Y_CRCB_H2V2:            return kFormatYCrCb420SemiPlanar;
    460   case MDP_Y_CBCR_H2V2_VENUS:      return kFormatYCbCr420SemiPlanarVenus;
    461   case MDP_Y_CBCR_H1V2:            return kFormatYCbCr422H1V2SemiPlanar;
    462   case MDP_Y_CRCB_H1V2:            return kFormatYCrCb422H1V2SemiPlanar;
    463   case MDP_Y_CBCR_H2V1:            return kFormatYCbCr422H2V1SemiPlanar;
    464   case MDP_Y_CRCB_H2V1:            return kFormatYCrCb422H2V1SemiPlanar;
    465   case MDP_Y_CBCR_H2V2_UBWC:       return kFormatYCbCr420SPVenusUbwc;
    466   case MDP_Y_CRCB_H2V2_VENUS:      return kFormatYCrCb420SemiPlanarVenus;
    467   case MDP_YCBYCR_H2V1:            return kFormatYCbCr422H2V1Packed;
    468   case MDP_RGBA_1010102:           return kFormatRGBA1010102;
    469   case MDP_ARGB_2101010:           return kFormatARGB2101010;
    470   case MDP_RGBX_1010102:           return kFormatRGBX1010102;
    471   case MDP_XRGB_2101010:           return kFormatXRGB2101010;
    472   case MDP_BGRA_1010102:           return kFormatBGRA1010102;
    473   case MDP_ABGR_2101010:           return kFormatABGR2101010;
    474   case MDP_BGRX_1010102:           return kFormatBGRX1010102;
    475   case MDP_XBGR_2101010:           return kFormatXBGR2101010;
    476   case MDP_RGBA_1010102_UBWC:      return kFormatRGBA1010102Ubwc;
    477   case MDP_RGBX_1010102_UBWC:      return kFormatRGBX1010102Ubwc;
    478   case MDP_Y_CBCR_H2V2_P010:       return kFormatYCbCr420P010;
    479   case MDP_Y_CBCR_H2V2_TP10_UBWC:  return kFormatYCbCr420TP10Ubwc;
    480   default:                         return kFormatInvalid;
    481   }
    482 }
    483 
    484 void HWInfo::InitSupportedFormatMap(HWResourceInfo *hw_resource) {
    485   hw_resource->supported_formats_map.clear();
    486 
    487   for (int sub_blk_type = INT(kHWVIGPipe); sub_blk_type < INT(kHWSubBlockMax); sub_blk_type++) {
    488     PopulateSupportedFormatMap(kDefaultFormatSupport[sub_blk_type], MDP_IMGTYPE_LIMIT1,
    489                                (HWSubBlockType)sub_blk_type, hw_resource);
    490   }
    491 }
    492 
    493 void HWInfo::ParseFormats(char *tokens[], uint32_t token_count, HWSubBlockType sub_blk_type,
    494                           HWResourceInfo *hw_resource) {
    495   if (token_count > BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)) {
    496     return;
    497   }
    498 
    499   std::unique_ptr<std::bitset<8>[]> format_supported(new std::bitset<8>[token_count]);
    500   for (uint32_t i = 0; i < token_count; i++) {
    501     format_supported[i] = UINT8(atoi(tokens[i]));
    502   }
    503 
    504   PopulateSupportedFormatMap(format_supported.get(), (token_count << 3), sub_blk_type, hw_resource);
    505 }
    506 
    507 void HWInfo::PopulateSupportedFormatMap(const std::bitset<8> *format_supported,
    508                                         uint32_t format_count, HWSubBlockType sub_blk_type,
    509                                         HWResourceInfo *hw_resource) {
    510   vector <LayerBufferFormat> supported_sdm_formats;
    511   for (uint32_t mdp_format = 0; mdp_format < format_count; mdp_format++) {
    512     if (format_supported[mdp_format >> 3][mdp_format & 7]) {
    513       LayerBufferFormat sdm_format = GetSDMFormat(INT(mdp_format));
    514       if (sdm_format != kFormatInvalid) {
    515         supported_sdm_formats.push_back(sdm_format);
    516       }
    517     }
    518   }
    519 
    520   hw_resource->supported_formats_map.erase(sub_blk_type);
    521   hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats));
    522 }
    523 
    524 DisplayError HWInfo::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) {
    525   Sys::fstream fs("/sys/devices/virtual/graphics/fb0/msm_fb_type", fstream::in);
    526   if (!fs.is_open()) {
    527     return kErrorHardware;
    528   }
    529 
    530   string line;
    531   if (!Sys::getline_(fs, line)) {
    532     return kErrorHardware;
    533   }
    534 
    535   if (!strncmp(line.c_str(), "dtv panel", strlen("dtv panel")) ||
    536       !strncmp(line.c_str(), "dp panel", strlen("dp panel"))) {
    537     hw_disp_info->type = kHDMI;
    538     DLOGI("First display is HDMI");
    539   } else {
    540     hw_disp_info->type = kPrimary;
    541     DLOGI("First display is internal display");
    542   }
    543 
    544   fs.close();
    545   fs.open("/sys/devices/virtual/graphics/fb0/connected", fstream::in);
    546   if (!fs.is_open()) {
    547     // If fb0 is for a DSI/connected panel, then connected node will not exist.
    548     hw_disp_info->is_connected = true;
    549   } else {
    550     if (!Sys::getline_(fs, line)) {
    551       return kErrorHardware;
    552     }
    553 
    554     hw_disp_info->is_connected =  (!strncmp(line.c_str(), "1", strlen("1")));
    555   }
    556 
    557   return kErrorNone;
    558 }
    559 
    560 }  // namespace sdm
    561 
    562