1 /* 2 * Copyright (c) 2017, 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 <dlfcn.h> 31 #include <drm/drm_fourcc.h> 32 #include <drm_lib_loader.h> 33 #include <drm_master.h> 34 #include <drm_res_mgr.h> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sys/stat.h> 40 #include <sys/types.h> 41 #include <utils/constants.h> 42 #include <utils/debug.h> 43 #include <utils/sys.h> 44 45 #include <algorithm> 46 #include <fstream> 47 #include <iostream> 48 #include <map> 49 #include <memory> 50 #include <string> 51 #include <utility> 52 #include <vector> 53 54 #include "hw_info_drm.h" 55 56 #define __CLASS__ "HWInfoDRM" 57 58 using drm_utils::DRMMaster; 59 using drm_utils::DRMResMgr; 60 using drm_utils::DRMLogger; 61 using drm_utils::DRMLibLoader; 62 using sde_drm::GetDRMManager; 63 using sde_drm::DRMPlanesInfo; 64 using sde_drm::DRMCrtcInfo; 65 using sde_drm::DRMPlaneType; 66 67 using std::vector; 68 using std::map; 69 using std::string; 70 using std::fstream; 71 using std::to_string; 72 73 namespace sdm { 74 75 class DRMLoggerImpl : public DRMLogger { 76 public: 77 #define PRINTLOG(method, format, buf) \ 78 va_list list; \ 79 va_start(list, format); \ 80 vsnprintf(buf, sizeof(buf), format, list); \ 81 va_end(list); \ 82 Debug::Get()->method(kTagNone, "%s", buf); 83 84 void Error(const char *format, ...) { PRINTLOG(Error, format, buf_); } 85 void Warning(const char *format, ...) { PRINTLOG(Warning, format, buf_); } 86 void Info(const char *format, ...) { PRINTLOG(Info, format, buf_); } 87 void Debug(const char *format, ...) { PRINTLOG(Debug, format, buf_); } 88 89 private: 90 char buf_[1024] = {}; 91 }; 92 93 HWResourceInfo *HWInfoDRM::hw_resource_ = nullptr; 94 95 HWInfoDRM::HWInfoDRM() { 96 DRMLogger::Set(new DRMLoggerImpl()); 97 default_mode_ = (DRMLibLoader::GetInstance()->IsLoaded() == false); 98 if (!default_mode_) { 99 DRMMaster *drm_master = {}; 100 int dev_fd = -1; 101 DRMMaster::GetInstance(&drm_master); 102 if (!drm_master) { 103 DLOGE("Failed to acquire DRMMaster instance"); 104 return; 105 } 106 drm_master->GetHandle(&dev_fd); 107 DRMLibLoader::GetInstance()->FuncGetDRMManager()(dev_fd, &drm_mgr_intf_); 108 } 109 } 110 111 HWInfoDRM::~HWInfoDRM() { 112 delete hw_resource_; 113 hw_resource_ = nullptr; 114 115 if (drm_mgr_intf_) { 116 DRMLibLoader::GetInstance()->FuncDestroyDRMManager()(); 117 drm_mgr_intf_ = nullptr; 118 } 119 } 120 121 DisplayError HWInfoDRM::GetDynamicBWLimits(HWResourceInfo *hw_resource) { 122 HWDynBwLimitInfo* bw_info = &hw_resource->dyn_bw_info; 123 for (int index = 0; index < kBwModeMax; index++) { 124 bw_info->total_bw_limit[index] = UINT32(hw_resource->max_bandwidth_low); 125 bw_info->pipe_bw_limit[index] = hw_resource->max_pipe_bw; 126 } 127 128 return kErrorNone; 129 } 130 131 DisplayError HWInfoDRM::GetHWResourceInfo(HWResourceInfo *hw_resource) { 132 if (hw_resource_) { 133 *hw_resource = *hw_resource_; 134 return kErrorNone; 135 } 136 137 hw_resource->num_blending_stages = 1; 138 hw_resource->max_pipe_width = 2560; 139 hw_resource->max_cursor_size = 128; 140 hw_resource->max_scale_down = 1; 141 hw_resource->max_scale_up = 1; 142 hw_resource->has_decimation = false; 143 hw_resource->max_bandwidth_low = 9600000; 144 hw_resource->max_bandwidth_high = 9600000; 145 hw_resource->max_pipe_bw = 4500000; 146 hw_resource->max_sde_clk = 412500000; 147 hw_resource->clk_fudge_factor = FLOAT(105) / FLOAT(100); 148 hw_resource->macrotile_nv12_factor = 8; 149 hw_resource->macrotile_factor = 4; 150 hw_resource->linear_factor = 1; 151 hw_resource->scale_factor = 1; 152 hw_resource->extra_fudge_factor = 2; 153 hw_resource->amortizable_threshold = 0; 154 hw_resource->system_overhead_lines = 0; 155 hw_resource->hw_dest_scalar_info.count = 0; 156 hw_resource->hw_dest_scalar_info.max_scale_up = 0; 157 hw_resource->hw_dest_scalar_info.max_input_width = 0; 158 hw_resource->hw_dest_scalar_info.max_output_width = 0; 159 hw_resource->is_src_split = true; 160 hw_resource->perf_calc = false; 161 hw_resource->has_dyn_bw_support = false; 162 hw_resource->has_qseed3 = false; 163 hw_resource->has_concurrent_writeback = false; 164 165 // TODO(user): Deprecate 166 hw_resource->hw_version = kHWMdssVersion5; 167 hw_resource->hw_revision = 0; 168 hw_resource->max_mixer_width = 0; 169 hw_resource->writeback_index = 0; 170 hw_resource->has_bwc = false; 171 hw_resource->has_ubwc = true; 172 hw_resource->has_macrotile = true; 173 hw_resource->separate_rotator = true; 174 hw_resource->has_non_scalar_rgb = false; 175 176 GetSystemInfo(hw_resource); 177 GetHWPlanesInfo(hw_resource); 178 GetWBInfo(hw_resource); 179 180 // Disable destination scalar count to 0 if extension library is not present 181 DynLib extension_lib; 182 if (!extension_lib.Open("libsdmextension.so")) { 183 hw_resource->hw_dest_scalar_info.count = 0; 184 } 185 186 DLOGI("Max plane width = %d", hw_resource->max_pipe_width); 187 DLOGI("Max cursor width = %d", hw_resource->max_cursor_size); 188 DLOGI("Max plane upscale = %d", hw_resource->max_scale_up); 189 DLOGI("Max plane downscale = %d", hw_resource->max_scale_down); 190 DLOGI("Has Decimation = %d", hw_resource->has_decimation); 191 DLOGI("Max Blending Stages = %d", hw_resource->num_blending_stages); 192 DLOGI("Has Source Split = %d", hw_resource->is_src_split); 193 DLOGI("Has QSEED3 = %d", hw_resource->has_qseed3); 194 DLOGI("Has UBWC = %d", hw_resource->has_ubwc); 195 DLOGI("Has Concurrent Writeback = %d", hw_resource->has_concurrent_writeback); 196 DLOGI("Max Low Bw = %" PRIu64 "", hw_resource->max_bandwidth_low); 197 DLOGI("Max High Bw = % " PRIu64 "", hw_resource->max_bandwidth_high); 198 DLOGI("Max Pipe Bw = %" PRIu64 " KBps", hw_resource->max_pipe_bw); 199 DLOGI("MaxSDEClock = % " PRIu64 " Hz", hw_resource->max_sde_clk); 200 DLOGI("Clock Fudge Factor = %f", hw_resource->clk_fudge_factor); 201 DLOGI("Prefill factors:"); 202 DLOGI("\tTiled_NV12 = %d", hw_resource->macrotile_nv12_factor); 203 DLOGI("\tTiled = %d", hw_resource->macrotile_factor); 204 DLOGI("\tLinear = %d", hw_resource->linear_factor); 205 DLOGI("\tScale = %d", hw_resource->scale_factor); 206 DLOGI("\tFudge_factor = %d", hw_resource->extra_fudge_factor); 207 208 if (hw_resource->separate_rotator || hw_resource->num_dma_pipe) { 209 GetHWRotatorInfo(hw_resource); 210 } 211 212 if (hw_resource->has_dyn_bw_support) { 213 DisplayError ret = GetDynamicBWLimits(hw_resource); 214 if (ret != kErrorNone) { 215 DLOGE("Failed to read dynamic band width info"); 216 return ret; 217 } 218 219 DLOGI("Has Support for multiple bw limits shown below"); 220 for (int index = 0; index < kBwModeMax; index++) { 221 DLOGI("Mode-index=%d total_bw_limit=%d and pipe_bw_limit=%d", index, 222 hw_resource->dyn_bw_info.total_bw_limit[index], 223 hw_resource->dyn_bw_info.pipe_bw_limit[index]); 224 } 225 } 226 227 if (!hw_resource_) { 228 hw_resource_ = new HWResourceInfo(); 229 *hw_resource_ = *hw_resource; 230 } 231 232 return kErrorNone; 233 } 234 235 void HWInfoDRM::GetSystemInfo(HWResourceInfo *hw_resource) { 236 DRMCrtcInfo info; 237 drm_mgr_intf_->GetCrtcInfo(0 /* system_info */, &info); 238 hw_resource->is_src_split = info.has_src_split; 239 hw_resource->has_qseed3 = (info.qseed_version == sde_drm::QSEEDVersion::V3); 240 hw_resource->num_blending_stages = info.max_blend_stages; 241 } 242 243 void HWInfoDRM::GetHWPlanesInfo(HWResourceInfo *hw_resource) { 244 DRMPlanesInfo info; 245 drm_mgr_intf_->GetPlanesInfo(&info); 246 for (auto &pipe_obj : info.planes) { 247 HWPipeCaps pipe_caps; 248 string name = {}; 249 switch (pipe_obj.second) { 250 case DRMPlaneType::RGB: 251 pipe_caps.type = kPipeTypeRGB; 252 hw_resource->num_rgb_pipe++; 253 name = "RGB"; 254 break; 255 case DRMPlaneType::VIG: 256 pipe_caps.type = kPipeTypeVIG; 257 hw_resource->num_vig_pipe++; 258 name = "VIG"; 259 break; 260 case DRMPlaneType::DMA: 261 pipe_caps.type = kPipeTypeDMA; 262 hw_resource->num_dma_pipe++; 263 name = "DMA"; 264 break; 265 case DRMPlaneType::CURSOR: 266 pipe_caps.type = kPipeTypeCursor; 267 hw_resource->num_cursor_pipe++; 268 name = "CURSOR"; 269 break; 270 default: 271 break; 272 } 273 pipe_caps.id = pipe_obj.first; 274 pipe_caps.max_rects = 1; 275 DLOGI("%s Pipe : Id %d", name.c_str(), pipe_obj.first); 276 hw_resource->hw_pipes.push_back(std::move(pipe_caps)); 277 } 278 279 for (auto &pipe_type : info.types) { 280 vector<LayerBufferFormat> supported_sdm_formats = {}; 281 for (auto &fmts : pipe_type.second.formats_supported) { 282 GetSDMFormat(fmts.first, fmts.second, &supported_sdm_formats); 283 } 284 285 HWSubBlockType sub_blk_type = kHWSubBlockMax; 286 switch (pipe_type.first) { 287 case DRMPlaneType::RGB: 288 sub_blk_type = kHWRGBPipe; 289 // These properties are per plane but modeled in SDM as system-wide. 290 hw_resource->max_pipe_width = pipe_type.second.max_linewidth; 291 hw_resource->max_scale_down = pipe_type.second.max_downscale; 292 hw_resource->max_scale_up = pipe_type.second.max_upscale; 293 hw_resource->has_decimation = 294 pipe_type.second.max_horizontal_deci > 1 && pipe_type.second.max_vertical_deci > 1; 295 break; 296 case DRMPlaneType::VIG: 297 sub_blk_type = kHWVIGPipe; 298 // These properties are per plane but modeled in SDM as system-wide. 299 hw_resource->max_pipe_width = pipe_type.second.max_linewidth; 300 hw_resource->max_scale_down = pipe_type.second.max_downscale; 301 hw_resource->max_scale_up = pipe_type.second.max_upscale; 302 hw_resource->has_decimation = 303 pipe_type.second.max_horizontal_deci > 1 && pipe_type.second.max_vertical_deci > 1; 304 break; 305 case DRMPlaneType::DMA: 306 sub_blk_type = kHWDMAPipe; 307 break; 308 case DRMPlaneType::CURSOR: 309 sub_blk_type = kHWCursorPipe; 310 hw_resource->max_cursor_size = pipe_type.second.max_linewidth; 311 break; 312 default: 313 break; 314 } 315 316 if (sub_blk_type != kHWSubBlockMax) { 317 hw_resource->supported_formats_map.erase(sub_blk_type); 318 hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats)); 319 } 320 } 321 } 322 323 void HWInfoDRM::GetWBInfo(HWResourceInfo *hw_resource) { 324 HWSubBlockType sub_blk_type = kHWWBIntfOutput; 325 vector<LayerBufferFormat> supported_sdm_formats = {}; 326 sde_drm::DRMDisplayToken token; 327 328 // Fake register 329 if (drm_mgr_intf_->RegisterDisplay(sde_drm::DRMDisplayType::VIRTUAL, &token)) { 330 return; 331 } 332 333 sde_drm::DRMConnectorInfo connector_info; 334 drm_mgr_intf_->GetConnectorInfo(token.conn_id, &connector_info); 335 for (auto &fmts : connector_info.formats_supported) { 336 GetSDMFormat(fmts.first, fmts.second, &supported_sdm_formats); 337 } 338 339 hw_resource->supported_formats_map.erase(sub_blk_type); 340 hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats)); 341 342 drm_mgr_intf_->UnregisterDisplay(token); 343 } 344 345 DisplayError HWInfoDRM::GetHWRotatorInfo(HWResourceInfo *hw_resource) { 346 const uint32_t kMaxV4L2Nodes = 64; 347 bool found = false; 348 349 for (uint32_t i = 0; (i < kMaxV4L2Nodes) && (false == found); i++) { 350 string path = "/sys/class/video4linux/video" + to_string(i) + "/name"; 351 Sys::fstream fs(path, fstream::in); 352 if (!fs.is_open()) { 353 continue; 354 } 355 356 string line; 357 if (Sys::getline_(fs, line) && (!strncmp(line.c_str(), "sde_rotator", strlen("sde_rotator")))) { 358 hw_resource->hw_rot_info.device_path = string("/dev/video" + to_string(i)); 359 hw_resource->hw_rot_info.num_rotator++; 360 hw_resource->hw_rot_info.type = HWRotatorInfo::ROT_TYPE_V4L2; 361 hw_resource->hw_rot_info.has_downscale = true; 362 // We support only 1 rotator 363 found = true; 364 } 365 } 366 367 DLOGI("V4L2 Rotator: Count = %d, Downscale = %d", hw_resource->hw_rot_info.num_rotator, 368 hw_resource->hw_rot_info.has_downscale); 369 370 return kErrorNone; 371 } 372 373 void HWInfoDRM::GetSDMFormat(uint32_t drm_format, uint64_t drm_format_modifier, 374 vector<LayerBufferFormat> *sdm_formats) { 375 vector<LayerBufferFormat> &fmts(*sdm_formats); 376 switch (drm_format) { 377 case DRM_FORMAT_ARGB8888: 378 fmts.push_back(kFormatARGB8888); 379 break; 380 case DRM_FORMAT_RGBA8888: 381 fmts.push_back(drm_format_modifier ? kFormatRGBA8888Ubwc : kFormatRGBA8888); 382 break; 383 case DRM_FORMAT_BGRA8888: 384 fmts.push_back(kFormatBGRA8888); 385 break; 386 case DRM_FORMAT_XRGB8888: 387 fmts.push_back(kFormatXRGB8888); 388 break; 389 case DRM_FORMAT_RGBX8888: 390 fmts.push_back(drm_format_modifier ? kFormatRGBX8888Ubwc : kFormatRGBX8888); 391 break; 392 case DRM_FORMAT_BGRX8888: 393 fmts.push_back(kFormatBGRX8888); 394 break; 395 case DRM_FORMAT_RGBA5551: 396 fmts.push_back(kFormatRGBA5551); 397 break; 398 case DRM_FORMAT_RGBA4444: 399 fmts.push_back(kFormatRGBA4444); 400 break; 401 case DRM_FORMAT_RGB888: 402 fmts.push_back(kFormatRGB888); 403 break; 404 case DRM_FORMAT_BGR888: 405 fmts.push_back(kFormatBGR888); 406 break; 407 case DRM_FORMAT_RGB565: 408 fmts.push_back(drm_format_modifier ? kFormatBGR565Ubwc : kFormatBGR565); 409 break; 410 case DRM_FORMAT_BGR565: 411 fmts.push_back(kFormatBGR565); 412 break; 413 case DRM_FORMAT_RGBA1010102: 414 fmts.push_back(drm_format_modifier ? kFormatRGBA1010102Ubwc : kFormatRGBA1010102); 415 break; 416 case DRM_FORMAT_ARGB2101010: 417 fmts.push_back(kFormatARGB2101010); 418 break; 419 case DRM_FORMAT_RGBX1010102: 420 fmts.push_back(drm_format_modifier ? kFormatRGBX1010102Ubwc : kFormatRGBX1010102); 421 break; 422 case DRM_FORMAT_XRGB2101010: 423 fmts.push_back(kFormatXRGB2101010); 424 break; 425 case DRM_FORMAT_BGRA1010102: 426 fmts.push_back(kFormatBGRA1010102); 427 break; 428 case DRM_FORMAT_ABGR2101010: 429 fmts.push_back(kFormatABGR2101010); 430 break; 431 case DRM_FORMAT_BGRX1010102: 432 fmts.push_back(kFormatBGRX1010102); 433 break; 434 case DRM_FORMAT_XBGR2101010: 435 fmts.push_back(kFormatXBGR2101010); 436 break; 437 /* case DRM_FORMAT_P010: 438 fmts.push_back(drm_format_modifier == (DRM_FORMAT_MOD_QCOM_COMPRESSED | 439 DRM_FORMAT_MOD_QCOM_TIGHT) ? 440 kFormatYCbCr420TP10Ubwc : kFormatYCbCr420P010; */ 441 case DRM_FORMAT_YVU420: 442 fmts.push_back(kFormatYCrCb420PlanarStride16); 443 break; 444 case DRM_FORMAT_NV12: 445 if (drm_format_modifier) { 446 fmts.push_back(kFormatYCbCr420SPVenusUbwc); 447 } else { 448 fmts.push_back(kFormatYCbCr420SemiPlanarVenus); 449 fmts.push_back(kFormatYCbCr420SemiPlanar); 450 } 451 break; 452 case DRM_FORMAT_NV21: 453 fmts.push_back(kFormatYCrCb420SemiPlanarVenus); 454 fmts.push_back(kFormatYCrCb420SemiPlanar); 455 break; 456 case DRM_FORMAT_NV16: 457 fmts.push_back(kFormatYCbCr422H2V1SemiPlanar); 458 break; 459 default: 460 break; 461 } 462 } 463 464 DisplayError HWInfoDRM::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) { 465 hw_disp_info->type = kPrimary; 466 hw_disp_info->is_connected = true; 467 468 return kErrorNone; 469 } 470 471 } // namespace sdm 472