Skip to content
Snippets Groups Projects
Commit f0c507f1 authored by Roman Stratiienko's avatar Roman Stratiienko
Browse files

drm_hwcomposer: Add headless mode support

Headless mode required to keep SurfaceFlinger alive when all displays are
disconnected, Without headless mode Android will continuously crash.
Only single internal (primary) display is required to be in HEADLESS mode
to prevent the crash. See [1].

[1]: https://source.android.com/devices/graphics/hotplug#handling-common-scenarios
Closes: https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer/-/issues/57


Signed-off-by: default avatarRoman Stratiienko <roman.o.stratiienko@globallogic.com>
parent aa8ec520
No related branches found
No related tags found
No related merge requests found
......@@ -94,6 +94,9 @@ uint16_t DrmMode::v_scan() const {
}
float DrmMode::v_refresh() const {
if (clock_ == 0) {
return v_refresh_;
}
// Always recalculate refresh to report correct float rate
return static_cast<float>(clock_) / (float)(v_total_ * h_total_) * 1000.0F;
}
......
......@@ -165,9 +165,14 @@ void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) {
void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) {
for (const auto &conn : drmDevice->connectors()) {
if (conn->state() != DRM_MODE_CONNECTED)
int display_id = conn->display();
auto &display = displays_.at(display_id);
if (conn->state() != DRM_MODE_CONNECTED && !display.IsInHeadlessMode())
continue;
HandleDisplayHotplug(conn->display(), conn->state());
HandleDisplayHotplug(conn->display(), display.IsInHeadlessMode()
? DRM_MODE_CONNECTED
: conn->state());
}
}
......@@ -187,15 +192,15 @@ void DrmHwcTwo::HandleHotplugUEvent() {
conn->display());
int display_id = conn->display();
if (cur_state == DRM_MODE_CONNECTED) {
auto &display = displays_.at(display_id);
display.ChosePreferredConfig();
} else {
auto &display = displays_.at(display_id);
auto &display = displays_.at(display_id);
display.ChosePreferredConfig();
if (cur_state != DRM_MODE_CONNECTED) {
display.ClearDisplay();
}
HandleDisplayHotplug(display_id, cur_state);
HandleDisplayHotplug(display_id, display.IsInHeadlessMode()
? DRM_MODE_CONNECTED
: cur_state);
}
}
}
......
......@@ -98,6 +98,11 @@ HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm,
}
void HwcDisplay::ClearDisplay() {
if (IsInHeadlessMode()) {
ALOGE("%s: Headless mode, should never reach here: ", __func__);
return;
}
AtomicCommitArgs a_args = {.clear_active_composition = true};
compositor_.ExecuteAtomicCommit(a_args);
}
......@@ -192,7 +197,7 @@ HWC2::Error HwcDisplay::Init(std::vector<DrmPlane *> *planes) {
HWC2::Error HwcDisplay::ChosePreferredConfig() {
HWC2::Error err = configs_.Update(*connector_);
if (err != HWC2::Error::None)
if (!IsInHeadlessMode() && err != HWC2::Error::None)
return HWC2::Error::BadDisplay;
return SetActiveConfig(configs_.preferred_config_id);
......@@ -230,6 +235,11 @@ HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements,
hwc2_layer_t *layers,
int32_t *types) {
if (IsInHeadlessMode()) {
*num_elements = 0;
return HWC2::Error::None;
}
uint32_t num_changes = 0;
for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
if (l.second.IsTypeChanged()) {
......@@ -250,6 +260,9 @@ HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height,
int32_t dataspace) {
std::pair<uint32_t, uint32_t> min = drm_->min_resolution();
std::pair<uint32_t, uint32_t> max = drm_->max_resolution();
if (IsInHeadlessMode()) {
return HWC2::Error::None;
}
if (width < min.first || height < min.second)
return HWC2::Error::Unsupported;
......@@ -287,8 +300,8 @@ HWC2::Error HwcDisplay::GetDisplayAttribute(hwc2_config_t config,
auto &hwc_config = configs_.hwc_configs[conf];
static const int32_t kUmPerInch = 25400;
uint32_t mm_width = connector_->mm_width();
uint32_t mm_height = connector_->mm_height();
uint32_t mm_width = configs_.mm_width;
uint32_t mm_height = configs_.mm_height;
auto attribute = static_cast<HWC2::Attribute>(attribute_in);
switch (attribute) {
case HWC2::Attribute::Width:
......@@ -398,6 +411,11 @@ HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types,
HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements,
hwc2_layer_t *layers,
int32_t *fences) {
if (IsInHeadlessMode()) {
*num_elements = 0;
return HWC2::Error::None;
}
uint32_t num_layers = 0;
for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
......@@ -418,6 +436,11 @@ HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements,
}
HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
if (IsInHeadlessMode()) {
ALOGE("%s: Display is in headless mode, should never reach here", __func__);
return HWC2::Error::None;
}
// order the layers by z-order
bool use_client_layer = false;
uint32_t client_z_order = UINT32_MAX;
......@@ -497,6 +520,10 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1805
*/
HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) {
if (IsInHeadlessMode()) {
*present_fence = -1;
return HWC2::Error::None;
}
HWC2::Error ret{};
++total_stats_.total_frames_;
......@@ -611,6 +638,10 @@ HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t /*buffer*/,
}
HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
if (IsInHeadlessMode()) {
return HWC2::Error::None;
}
auto mode = static_cast<HWC2::PowerMode>(mode_in);
AtomicCommitArgs a_args{};
......@@ -652,6 +683,10 @@ HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) {
HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types,
uint32_t *num_requests) {
if (IsInHeadlessMode()) {
*num_types = *num_requests = 0;
return HWC2::Error::None;
}
return backend_->ValidateDisplay(this, num_types, num_requests);
}
......
......@@ -200,6 +200,16 @@ class HwcDisplay {
return false;
}
/* Headless mode required to keep SurfaceFlinger alive when all display are
* disconnected, Without headless mode Android will continuously crash.
* Only single internal (primary) display is required to be in HEADLESS mode
* to prevent the crash. See:
* https://source.android.com/devices/graphics/hotplug#handling-common-scenarios
*/
bool IsInHeadlessMode() {
return handle_ == 0 && connector_->state() != DRM_MODE_CONNECTED;
}
private:
enum ClientFlattenningState : int32_t {
Disabled = -3,
......
......@@ -23,10 +23,38 @@
#include "drm/DrmConnector.h"
#include "utils/log.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;
namespace android {
// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
/* In case UpdateModes will fail we will still have one mode for headless
* mode*/
hwc_configs.clear();
last_config_id++;
preferred_config_id = active_config_id = last_config_id;
auto headless_drm_mode_info = (drmModeModeInfo){
.hdisplay = kHeadlessModeDisplayWidthPx,
.vdisplay = kHeadlessModeDisplayHeightPx,
.vrefresh = kHeadlessModeDisplayVRefresh,
.name = "HEADLESS-MODE",
};
hwc_configs[active_config_id] = (HwcDisplayConfig){
.id = active_config_id,
.group_id = 1,
.mode = DrmMode(&headless_drm_mode_info),
};
mm_width = kHeadlessModeDisplayWidthMm;
mm_height = kHeadlessModeDisplayHeightMm;
/* Read real configs */
int ret = connector.UpdateModes();
if (ret != 0) {
ALOGE("Failed to update display modes %d", ret);
......@@ -39,6 +67,9 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
}
hwc_configs.clear();
mm_width = connector.mm_width();
mm_height = connector.mm_height();
preferred_config_id = 0;
int preferred_config_group_id = 0;
......@@ -159,6 +190,8 @@ HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
}
}
/* Set active mode to be valid mode */
active_config_id = preferred_config_id;
return HWC2::Error::None;
}
......
......@@ -47,6 +47,9 @@ struct HwcDisplayConfigs {
int preferred_config_id = 0;
int last_config_id = 1;
uint32_t mm_width = 0;
uint32_t mm_height = 0;
};
} // namespace android
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment