chromium/ui/ozone/platform/drm/common/drm_util.h

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#ifndef UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
#define UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_

#include <stddef.h>
#include <stdint.h>
#include <xf86drmMode.h>

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/logging.h"
#include "base/notreached.h"
#include "ui/display/display_features.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/util/edid_parser.h"
#include "ui/ozone/platform/drm/common/drm_wrapper.h"
#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
#include "ui/ozone/platform/drm/common/tile_property.h"

typedef struct _drmModeModeInfo drmModeModeInfo;

namespace display {
class DisplayMode;
}  // namespace display

namespace ui {
class HardwareDisplayControllerInfo;

// TODO(b/193019614): clean |kMaxDrmCount|'s and |kMaxDrmConnectors|'s
// assignment up once EDID-based ID migration is complete and the flag is
// removed.
// It is safe to assume there will be no more than 256 connected DRM devices.
const size_t kMaxDrmCount =
    display::features::IsEdidBasedDisplayIdsEnabled() ? 256u : 16u;

// It is safe to assume there will be no more than 256 connectors per DRM.
const size_t kMaxDrmConnectors =
    display::features::IsEdidBasedDisplayIdsEnabled() ? 256u : 16u;

// Using a moderate size e.g. 256 for the cursor is enough in most cases.
const int kMaxCursorBufferSize = 256;

// DRM property names.
const char kContentProtectionKey[] = "Content Protection Key";
const char kContentProtection[] = "Content Protection";
const char kHdcpContentType[] = "HDCP Content Type";

const char kColorSpace[] = "Colorspace";
const char kColorSpaceBT2020RGBEnumName[] = "BT2020_RGB";
const char kColorSpaceDefaultEnumName[] = "Default";

const char kHdrOutputMetadata[] = "HDR_OUTPUT_METADATA";

constexpr char kPrivacyScreenPropertyNameLegacy[] = "privacy-screen";
constexpr char kPrivacyScreenHwStatePropertyName[] = "privacy-screen hw-state";
constexpr char kPrivacyScreenSwStatePropertyName[] = "privacy-screen sw-state";

constexpr char kVrrCapablePropertyName[] = "vrr_capable";
constexpr char kVrrEnabledPropertyName[] = "VRR_ENABLED";

// DRM property enum to internal type mappings.
template <typename InternalType>
struct DrmPropertyEnumToInternalTypeMapping {
  const char* drm_enum;
  const InternalType internal_state;
};

constexpr std::array<
    DrmPropertyEnumToInternalTypeMapping<display::ContentProtectionMethod>,
    2>
    kHdcpContentTypeStates{
        {{"HDCP Type0", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0},
         {"HDCP Type1", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1}}};

constexpr std::array<DrmPropertyEnumToInternalTypeMapping<display::HDCPState>,
                     3>
    kContentProtectionStates{{{"Undesired", display::HDCP_STATE_UNDESIRED},
                              {"Desired", display::HDCP_STATE_DESIRED},
                              {"Enabled", display::HDCP_STATE_ENABLED}}};

constexpr std::
    array<DrmPropertyEnumToInternalTypeMapping<display::PrivacyScreenState>, 4>
        kPrivacyScreenStates{{{"Disabled", display::kDisabled},
                              {"Enabled", display::kEnabled},
                              {"Disabled-locked", display::kDisabledLocked},
                              {"Enabled-locked", display::kEnabledLocked}}};

// Looks-up and parses the native display configurations returning all available
// displays and CRTCs that weren't picked as best CRTC for each connector.
// TODO(markyacoub): Create unit tests that tests the different bits and pieces
// that this function goes through.
std::pair<std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>,
          std::vector<uint32_t>>
GetDisplayInfosAndInvalidCrtcs(const DrmWrapper& drm);

// Returns the display infos parsed in |GetDisplayInfosAndInvalidCrtcs|
std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>
GetAvailableDisplayControllerInfos(const DrmWrapper& drm);

// Returns a bitmask of possible CRTCs for at least one encoder in
// |encoder_ids|. The index in the bitmask corresponds to drm_crtc_index().
uint32_t GetPossibleCrtcsBitmaskFromEncoders(
    const DrmWrapper& drm,
    const std::vector<uint32_t>& encoder_ids);

// Returns a list of all possible CRTCs for encoders with IDs in |encoder_ids|.
std::vector<uint32_t> GetPossibleCrtcIdsFromBitmask(
    const DrmWrapper& drm,
    const uint32_t possible_crtcs_bitmask);

bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);

std::unique_ptr<display::DisplayMode> CreateDisplayMode(
    const drmModeModeInfo& mode,
    const std::optional<uint16_t>& vsync_rate_min_from_edid);

// Returns a virtual mode based on |base_mode| with its vtotal altered to
// achieve the specified |virtual_refresh_rate|, or nullptr if it could not be
// created.
std::unique_ptr<drmModeModeInfo> CreateVirtualMode(
    const drmModeModeInfo& base_mode,
    float virtual_refresh_rate);

// Extracts the display modes list from |info| as well as the current and native
// display modes given the |active_pixel_size| which is retrieved from the first
// detailed timing descriptor in the EDID.
display::DisplaySnapshot::DisplayModeList ExtractDisplayModes(
    HardwareDisplayControllerInfo* info,
    const gfx::Size& active_pixel_size,
    const display::DisplayMode** out_current_mode,
    const display::DisplayMode** out_native_mode);

// |info| provides the DRM information related to the display, |drm| is the
// access point to the DRM device to which |info| is related to.
std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot(
    const DrmWrapper& drm,
    HardwareDisplayControllerInfo* info,
    uint8_t device_index);

int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format);

gfx::Size GetMaximumCursorSize(const DrmWrapper& drm);

ScopedDrmPropertyPtr FindDrmProperty(const DrmWrapper& drm,
                                     drmModeObjectProperties* properties,
                                     const char* name);

bool HasColorCorrectionMatrix(const DrmWrapper& drm, drmModeCrtc* crtc);

bool MatchMode(const display::DisplayMode& display_mode,
               const drmModeModeInfo& m);

const gfx::Size ModeSize(const drmModeModeInfo& mode);

float ModeRefreshRate(const drmModeModeInfo& mode);

bool ModeIsInterlaced(const drmModeModeInfo& mode);

// Computes the precise minimum vsync rate using the mode's timing details.
// The value obtained from the EDID has a loss of precision due to being an
// integer. The precise rate must correspond to an integer valued vtotal.
const std::optional<float> ModeVSyncRateMin(
    const drmModeModeInfo& mode,
    const std::optional<uint16_t>& vsync_rate_min_from_edid);

bool IsVrrCapable(const DrmWrapper& drm, drmModeConnector* connector);

bool IsVrrEnabled(const DrmWrapper& drm, drmModeCrtc* crtc);

display::VariableRefreshRateState GetVariableRefreshRateState(
    const DrmWrapper& drm,
    HardwareDisplayControllerInfo* info);

const char* GetNameForColorspace(const gfx::ColorSpace color_space);
uint64_t GetEnumValueForName(const DrmWrapper& drm,
                             int property_id,
                             const char* str);

std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob);

std::optional<TileProperty> ParseTileBlob(
    const drmModePropertyBlobRes& tile_blob);

// Whether or not |drm| supports supplying modifiers for AddFramebuffer2.
bool IsAddfb2ModifierCapable(const DrmWrapper& drm);

// Extracts the DRM |property| current value's enum. Returns an empty string
// upon failure.
std::string GetEnumNameForProperty(
    const drmModePropertyRes& property,
    const drmModeObjectProperties& property_values);

// Extracts the DRM property's numeric value that maps to |internal_state|.
// Returns the maximal numeric value for uint64_t upon failure.
template <typename InternalType, typename DrmPropertyToInternalTypeMap>
uint64_t GetDrmValueForInternalType(const InternalType& internal_state,
                                    const drmModePropertyRes& property,
                                    const DrmPropertyToInternalTypeMap& map) {
  std::string drm_enum;
  for (const auto& pair : map) {
    if (pair.internal_state == internal_state) {
      drm_enum = pair.drm_enum;
      break;
    }
  }
  DCHECK(!drm_enum.empty())
      << "Property " << property.name
      << " has no enum value for the requested internal state (value <"
      << internal_state << ">).";

  for (int i = 0; i < property.count_enums; ++i) {
    if (drm_enum == property.enums[i].name)
      return property.enums[i].value;
  }

  NOTREACHED_IN_MIGRATION()
      << "Failed to extract DRM value for property '" << property.name
      << "' and enum '" << drm_enum << "'";
  return std::numeric_limits<uint64_t>::max();
}

// Returns the internal type value that maps to the DRM property's current
// value. Returns nullptr upon failure.
template <typename InternalType, size_t size>
const InternalType* GetDrmPropertyCurrentValueAsInternalType(
    const std::array<DrmPropertyEnumToInternalTypeMapping<InternalType>, size>&
        array,
    const drmModePropertyRes& property,
    const drmModeObjectProperties& property_values) {
  const std::string drm_enum =
      GetEnumNameForProperty(property, property_values);
  if (drm_enum.empty()) {
    LOG(ERROR) << "Failed to fetch DRM enum for property '" << property.name
               << "'";
    return nullptr;
  }

  for (const auto& pair : array) {
    if (drm_enum == pair.drm_enum) {
      VLOG(3) << "Internal state value: " << pair.internal_state << " ("
              << drm_enum << ")";
      return &pair.internal_state;
    }
  }

  NOTREACHED_IN_MIGRATION()
      << "Failed to extract internal value for DRM property '" << property.name
      << "'";
  return nullptr;
}

// Returns the internal type value that maps to |drm_enum| within |array|.
// Returns nullptr upon failure.
template <typename InternalType, size_t size>
const InternalType* GetInternalTypeValueFromDrmEnum(
    const std::string& drm_enum,
    const std::array<DrmPropertyEnumToInternalTypeMapping<InternalType>, size>&
        array) {
  if (drm_enum.empty()) {
    LOG(ERROR) << "DRM property value enum is empty.";
    return nullptr;
  }

  for (const auto& pair : array) {
    if (drm_enum == pair.drm_enum) {
      VLOG(3) << "Internal state value: " << pair.internal_state << " ("
              << drm_enum << ")";
      return &pair.internal_state;
    }
  }

  LOG(ERROR) << "Failed to extract internal value for DRM property enum '"
             << drm_enum << "'";
  return nullptr;
}

// Get the DRM driver name.
std::optional<std::string> GetDrmDriverNameFromFd(int fd);
std::optional<std::string> GetDrmDriverNameFromPath(
    const char* device_file_name);

// Get an ordered list of preferred DRM driver names for the
// system. Uses DMI information to determine what the system is.
std::vector<const char*> GetPreferredDrmDrivers();

// Given |display_infos|, where each HardwareDisplayControllerInfo can represent
// a regular display or a tile, consolidate all tiles belonging to the same
// display into one HardwareDisplayControllerInfo. All non-tile
// HardwareDisplayControllerInfo will not be altered.
void ConsolidateTiledDisplayInfo(
    std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>& display_infos);

// Get the total tile-composited size of a tiled display.
gfx::Size GetTotalTileDisplaySize(const TileProperty& tile_property);

// A custom comparator of gfx::Size used to sort cursor sizes.
struct CursorSizeComparator {
  bool operator()(const gfx::Size& a, const gfx::Size& b) const {
    if (a.GetArea() == b.GetArea()) {
      if (a.width() == b.width()) {
        return a.height() < b.height();
      } else {
        return a.width() < b.width();
      }
    } else {
      return a.GetArea() < b.GetArea();
    }
  }
};
}  // namespace ui

#endif  // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_