drm_hwcomposer: Add property hwc.drm.primary_display_order
There are cases when primary display lookup order should be
overriden. This could be achieved with adding system property
hwc.drm.primary_display_order.
Example of primary_display_order property usage:
hwc.drm.primary_display_order=HDMI-A-2,HDMI-A-1,...
This means that first priority goes to HDMI-A-2 connector, then
HDMI-A-1 connector, then everything else. Internal connectors have
higher priority than any other connectors not mentioned in property.
Connected connector with highest priority would be selected as a
primary display.
Signed-off-by: Roman Kovalivskyi <roman.kovalivskyi@globallogic.com>
diff --git a/drm/drmdevice.cpp b/drm/drmdevice.cpp
index 2007fdd..bcb9ddd 100644
--- a/drm/drmdevice.cpp
+++ b/drm/drmdevice.cpp
@@ -30,11 +30,91 @@
#include <xf86drmMode.h>
#include <cinttypes>
+#include <algorithm>
+#include <array>
+#include <string>
+
#include <cutils/properties.h>
#include <log/log.h>
+static void trim_left(std::string &str) {
+ str.erase(std::begin(str),
+ std::find_if(std::begin(str), std::end(str),
+ [](int ch) { return !std::isspace(ch); }));
+}
+
+static void trim_right(std::string &str) {
+ str.erase(std::find_if(std::rbegin(str), std::rend(str),
+ [](int ch) { return !std::isspace(ch); })
+ .base(),
+ std::end(str));
+}
+
+static void trim(std::string &str) {
+ trim_left(str);
+ trim_right(str);
+}
+
namespace android {
+static std::vector<std::string> read_primary_display_order_prop() {
+ std::array<char, PROPERTY_VALUE_MAX> display_order_buf;
+ property_get("hwc.drm.primary_display_order", display_order_buf.data(),
+ "...");
+
+ std::vector<std::string> display_order;
+ std::istringstream str(display_order_buf.data());
+ for (std::string conn_name = ""; std::getline(str, conn_name, ',');) {
+ trim(conn_name);
+ display_order.push_back(std::move(conn_name));
+ }
+ return display_order;
+}
+
+static std::vector<DrmConnector *> make_primary_display_candidates(
+ std::vector<std::unique_ptr<DrmConnector>> &connectors) {
+ std::vector<DrmConnector *> primary_candidates;
+ std::transform(std::begin(connectors), std::end(connectors),
+ std::back_inserter(primary_candidates),
+ [](std::unique_ptr<DrmConnector> &conn) {
+ return conn.get();
+ });
+ primary_candidates.erase(std::remove_if(std::begin(primary_candidates),
+ std::end(primary_candidates),
+ [](const DrmConnector *conn) {
+ return conn->state() !=
+ DRM_MODE_CONNECTED;
+ }),
+ std::end(primary_candidates));
+
+ std::vector<std::string> display_order = read_primary_display_order_prop();
+ bool use_other = display_order.back() == "...";
+
+ // putting connectors from primary_display_order first
+ auto curr_connector = std::begin(primary_candidates);
+ for (const std::string &display_name : display_order) {
+ auto it = std::find_if(std::begin(primary_candidates),
+ std::end(primary_candidates),
+ [&display_name](const DrmConnector *conn) {
+ return conn->name() == display_name;
+ });
+ if (it != std::end(primary_candidates)) {
+ std::iter_swap(it, curr_connector);
+ ++curr_connector;
+ }
+ }
+
+ if (use_other) {
+ // then putting internal connectors second, everything else afterwards
+ std::partition(curr_connector, std::end(primary_candidates),
+ [](const DrmConnector *conn) { return conn->internal(); });
+ } else {
+ primary_candidates.erase(curr_connector, std::end(primary_candidates));
+ }
+
+ return primary_candidates;
+}
+
DrmDevice::DrmDevice() : event_listener_(this) {
}
@@ -173,19 +253,26 @@
connectors_.emplace_back(std::move(conn));
}
- // First look for primary amongst internal connectors
- for (auto &conn : connectors_) {
- if (conn->internal() && !found_primary) {
- conn->set_display(num_displays);
- displays_[num_displays] = num_displays;
- ++num_displays;
- found_primary = true;
- break;
- }
+ // Primary display priority:
+ // 1) hwc.drm.primary_display_order property
+ // 2) internal connectors
+ // 3) anything else
+ std::vector<DrmConnector *>
+ primary_candidates = make_primary_display_candidates(connectors_);
+ if (!primary_candidates.empty() && !found_primary) {
+ DrmConnector &conn = **std::begin(primary_candidates);
+ conn.set_display(num_displays);
+ displays_[num_displays] = num_displays;
+ ++num_displays;
+ found_primary = true;
+ } else {
+ ALOGE(
+ "Failed to find primary display from \"hwc.drm.primary_display_order\" "
+ "property");
}
- // Then pick first available as primary and for the others assign
- // consecutive display_numbers.
+ // If no priority display were found then pick first available as primary and
+ // for the others assign consecutive display_numbers.
for (auto &conn : connectors_) {
if (conn->external() || conn->internal()) {
if (!found_primary) {