1
0
Fork 0

drm_hwcomposer: Add property to force display mode

* Display resolution and refresh rate can be forced using a property.
  Other modes reported as supported by the display are ignored when forcing
  a specific mode.
* Force resolution and refresh rate for headless mode as well.

This is based on following 4 commits with further updates.

Author: Chih-Wei Huang <cwhuang@linux.org.tw>
Date:   Wed Sep 19 22:57:32 2018 +0800

    drm_hwcomposer: allow to force mode by a property

    The desired resolution could be set by property debug.drm.mode.force.
    The other modes are ignored.

Author: Konsta <konsta09@gmail.com>
Date:   Sun Dec 5 15:40:40 2021 +0200

    drm_hwcomposer: use first refresh rate reported by the display

    * Android can switch between different refresh rates which
      can lead to display changing modes in some cases.
    * Use the first refresh rate reported by the display when
      forcing the resolution unless specified otherwise.

Author: Konsta <konsta09@gmail.com>
Date:   Tue Jan 25 17:46:33 2022 +0200

    drm_hwcomposer: Default to 1920x1080 resolution for headless mode

Author: Konsta <konsta09@gmail.com>
Date:   Tue Feb 1 17:40:43 2022 +0200

    drm_hwcomposer: Allow setting resolution for headless mode
This commit is contained in:
Konsta 2025-11-10 15:40:00 +02:00
parent 5ee173b752
commit e2e9c8cff7
4 changed files with 62 additions and 6 deletions

View file

@ -29,6 +29,7 @@
#include "DrmDevice.h"
#include "compositor/DisplayInfo.h"
#include "utils/properties.h"
#ifndef DRM_MODE_CONNECTOR_SPI
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
@ -242,6 +243,18 @@ std::string DrmConnector::GetName() const {
}
int DrmConnector::UpdateModes() {
std::string force_mode = Properties::GetForceMode();
uint32_t xres = 0, yres = 0, rate = 0;
// Parse <xres>x<yres>[@<refreshrate>]
if (sscanf(force_mode.c_str(), "%dx%d@%d", &xres, &yres, &rate) != 3) {
rate = 0;
if (sscanf(force_mode.c_str(), "%dx%d", &xres, &yres) != 2) {
xres = yres = 0;
}
}
ALOGI_IF(xres && yres, "Force mode %dx%d@%dHz for display in connector %s",
xres, yres, rate, GetName().c_str());
auto conn = MakeDrmModeConnectorUnique(*drm_->GetFd(), GetId());
if (!conn) {
ALOGE("Failed to get connector %d", GetId());
@ -260,7 +273,25 @@ int DrmConnector::UpdateModes() {
}
if (!exists) {
modes_.emplace_back(&connector_->modes[i]);
DrmMode m(&connector_->modes[i]);
ALOGV("Supported mode %dx%d@%fHz for display in connector %s",
m.GetRawMode().hdisplay, m.GetRawMode().vdisplay,
m.GetVRefresh(), GetName().c_str());
if (xres && yres) {
if (!rate && m.GetRawMode().hdisplay == xres
&& m.GetRawMode().vdisplay == yres) {
rate = m.GetVRefresh();
}
if (m.GetRawMode().hdisplay != xres
|| m.GetRawMode().vdisplay != yres
|| m.GetVRefresh() != rate) {
continue;
}
}
ALOGD("Add mode %dx%d@%fHz for display in connector %s",
m.GetRawMode().hdisplay, m.GetRawMode().vdisplay,
m.GetVRefresh(), GetName().c_str());
modes_.emplace_back(m);
}
}

View file

@ -23,12 +23,8 @@
#include "compositor/DisplayInfo.h"
#include "drm/DrmConnector.h"
#include "utils/properties.h"
constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
constexpr uint32_t kSyncLen = 10;
constexpr uint32_t kBackPorch = 10;
constexpr uint32_t kFrontPorch = 10;
@ -37,6 +33,24 @@ constexpr uint32_t kHzInKHz = 1000;
namespace android::drm_hwcomposer {
void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
std::string force_mode = Properties::GetForceMode();
uint32_t xres = 0, yres = 0, rate = 0;
// Parse <xres>x<yres>[@<refreshrate>]
if (sscanf(force_mode.c_str(), "%dx%d@%d", &xres, &yres, &rate) != 3) {
rate = 0;
if (sscanf(force_mode.c_str(), "%dx%d", &xres, &yres) != 2) {
xres = yres = 0;
}
}
ALOGI_IF(xres && yres, "Force mode %dx%d@%dHz for HEADLESS-MODE",
xres, yres, rate);
const uint32_t kHeadlessModeDisplayWidthMm = xres ? xres : 160;
const uint32_t kHeadlessModeDisplayHeightMm = yres ? yres : 90;
const uint32_t kHeadlessModeDisplayWidthPx = xres ? xres : 1920;
const uint32_t kHeadlessModeDisplayHeightPx = yres ? yres : 1080;
const uint32_t kHeadlessModeDisplayVRefresh = rate ? rate : 60;
hwc_configs.clear();
preferred_config_id = active_config_id = next_config_id++;
@ -81,6 +95,10 @@ void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
mm_width = kHeadlessModeDisplayWidthMm;
mm_height = kHeadlessModeDisplayHeightMm;
ALOGI("Add mode %dx%d@%dHz for %s",
headless_drm_mode_info.hdisplay, headless_drm_mode_info.vdisplay,
headless_drm_mode_info.vrefresh, headless_drm_mode_info.name);
}
bool HwcDisplayConfigs::Init(DrmConnector &connector) {

View file

@ -144,4 +144,10 @@ auto Properties::GetDevicePath() -> std::string {
return {path_pattern};
}
auto Properties::GetForceMode() -> std::string {
char force_mode[PROPERTY_VALUE_MAX];
property_get("vendor.hwc.drm.force_mode", force_mode, "");
return {force_mode};
}
} // namespace android::drm_hwcomposer

View file

@ -36,6 +36,7 @@ class Properties {
static auto GetCtmHandling() -> CtmHandling;
static auto GetBackendOverride() -> std::string;
static auto GetDevicePath() -> std::string;
static auto GetForceMode() -> std::string;
};
} // namespace android::drm_hwcomposer