drm_hwcomposer: Added hotplug support of the external display
Unplug of the main display will not work because of
Activity Manager code(ActivityStackSupervisor.java:handleDisplayRemoved).
Only one display can be connected as an external
display (see SurfaceFlinger::determineDisplayType).
Tested-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Andrii Chepurnyi <andrii_chepurnyi@epam.com>
diff --git a/drmdevice.h b/drmdevice.h
index da1b961..91dd38b 100644
--- a/drmdevice.h
+++ b/drmdevice.h
@@ -76,6 +76,9 @@
int CreatePropertyBlob(void *data, size_t length, uint32_t *blob_id);
int DestroyPropertyBlob(uint32_t blob_id);
bool HandlesDisplay(int display) const;
+ void RegisterHotplugHandler(DrmEventHandler *handler) {
+ event_listener_.RegisterHotplugHandler(handler);
+ }
private:
int TryEncoderForDisplay(int display, DrmEncoder *enc);
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index 67f6334..1005598 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -55,6 +55,7 @@
int Composite();
void Dump(std::ostringstream *out) const;
void Vsync(int display, int64_t timestamp);
+ void ClearDisplay();
std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
@@ -82,7 +83,6 @@
int ApplyDpms(DrmDisplayComposition *display_comp);
int DisablePlanes(DrmDisplayComposition *display_comp);
- void ClearDisplay();
void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
int status, bool writeback = false);
int FlattenActiveComposition();
diff --git a/drmeventlistener.cpp b/drmeventlistener.cpp
index 6aab6fb..8f655a7 100644
--- a/drmeventlistener.cpp
+++ b/drmeventlistener.cpp
@@ -64,7 +64,7 @@
void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) {
assert(!hotplug_handler_);
- hotplug_handler_ = handler;
+ hotplug_handler_.reset(handler);
}
void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */,
diff --git a/drmeventlistener.h b/drmeventlistener.h
index d8a61a5..95672ee 100644
--- a/drmeventlistener.h
+++ b/drmeventlistener.h
@@ -58,7 +58,7 @@
int max_fd_ = -1;
DrmDevice *drm_;
- DrmEventHandler *hotplug_handler_ = NULL;
+ std::unique_ptr<DrmEventHandler> hotplug_handler_;
};
} // namespace android
diff --git a/drmhwctwo.cpp b/drmhwctwo.cpp
index 7bd8d64..f506bfc 100644
--- a/drmhwctwo.cpp
+++ b/drmhwctwo.cpp
@@ -57,43 +57,55 @@
getFunction = HookDevGetFunction;
}
-HWC2::Error DrmHwcTwo::Init() {
- int ret = resource_manager_.Init();
- if (ret) {
- ALOGE("Can't initialize the resource manager %d", ret);
- return HWC2::Error::NoResources;
- }
-
- DrmDevice *drm = resource_manager_.GetDrmDevice(HWC_DISPLAY_PRIMARY);
- std::shared_ptr<Importer> importer = resource_manager_.GetImporter(
- HWC_DISPLAY_PRIMARY);
+HWC2::Error DrmHwcTwo::CreateDisplay(hwc2_display_t displ,
+ HWC2::DisplayType type) {
+ DrmDevice *drm = resource_manager_.GetDrmDevice(displ);
+ std::shared_ptr<Importer> importer = resource_manager_.GetImporter(displ);
if (!drm || !importer) {
ALOGE("Failed to get a valid drmresource and importer");
return HWC2::Error::NoResources;
}
-
- displays_.emplace(std::piecewise_construct,
- std::forward_as_tuple(HWC_DISPLAY_PRIMARY),
+ displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ),
std::forward_as_tuple(&resource_manager_, drm, importer,
- HWC_DISPLAY_PRIMARY,
- HWC2::DisplayType::Physical));
+ displ, type));
- DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(HWC_DISPLAY_PRIMARY));
+ DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(displ));
if (!crtc) {
- ALOGE("Failed to get crtc for display %d",
- static_cast<int>(HWC_DISPLAY_PRIMARY));
+ ALOGE("Failed to get crtc for display %d", static_cast<int>(displ));
return HWC2::Error::BadDisplay;
}
-
std::vector<DrmPlane *> display_planes;
for (auto &plane : drm->planes()) {
if (plane->GetCrtcSupported(*crtc))
display_planes.push_back(plane.get());
}
- displays_.at(HWC_DISPLAY_PRIMARY).Init(&display_planes);
+ displays_.at(displ).Init(&display_planes);
return HWC2::Error::None;
}
+HWC2::Error DrmHwcTwo::Init() {
+ int rv = resource_manager_.Init();
+ if (rv) {
+ ALOGE("Can't initialize the resource manager %d", rv);
+ return HWC2::Error::NoResources;
+ }
+
+ HWC2::Error ret = HWC2::Error::None;
+ for (int i = 0; i < resource_manager_.getDisplayCount(); i++) {
+ ret = CreateDisplay(i, HWC2::DisplayType::Physical);
+ if (ret != HWC2::Error::None) {
+ ALOGE("Failed to create display %d with error %d", i, ret);
+ return ret;
+ }
+ }
+
+ auto &drmDevices = resource_manager_.getDrmDevices();
+ for (auto &device : drmDevices) {
+ device->RegisterHotplugHandler(new DrmHotplugHandler(this, device.get()));
+ }
+ return ret;
+}
+
template <typename... Args>
static inline HWC2::Error unsupported(char const *func, Args... /*args*/) {
ALOGV("Unsupported function: %s", func);
@@ -132,6 +144,12 @@
hwc2_function_pointer_t function) {
supported(__func__);
auto callback = static_cast<HWC2::Callback>(descriptor);
+
+ if (!function) {
+ callbacks_.erase(callback);
+ return HWC2::Error::None;
+ }
+
callbacks_.emplace(callback, HwcCallback(data, function));
switch (callback) {
@@ -139,6 +157,9 @@
auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(function);
hotplug(data, HWC_DISPLAY_PRIMARY,
static_cast<int32_t>(HWC2::Connection::Connected));
+ auto &drmDevices = resource_manager_.getDrmDevices();
+ for (auto &device : drmDevices)
+ HandleInitialHotplugState(device.get());
break;
}
case HWC2::Callback::Vsync: {
@@ -165,6 +186,10 @@
supported(__func__);
}
+void DrmHwcTwo::HwcDisplay::ClearDisplay() {
+ compositor_.ClearDisplay();
+}
+
HWC2::Error DrmHwcTwo::HwcDisplay::Init(std::vector<DrmPlane *> *planes) {
supported(__func__);
planner_ = Planner::CreateInstance(drm_);
@@ -204,6 +229,16 @@
return HWC2::Error::BadDisplay;
}
+ ret = vsync_worker_.Init(drm_, display);
+ if (ret) {
+ ALOGE("Failed to create event worker for d=%d %d\n", display, ret);
+ return HWC2::Error::BadDisplay;
+ }
+
+ return ChosePreferredConfig();
+}
+
+HWC2::Error DrmHwcTwo::HwcDisplay::ChosePreferredConfig() {
// Fetch the number of modes from the display
uint32_t num_configs;
HWC2::Error err = GetDisplayConfigs(&num_configs, NULL);
@@ -217,13 +252,6 @@
err = GetDisplayConfigs(&num_configs, &default_config);
if (err != HWC2::Error::None)
return err;
-
- ret = vsync_worker_.Init(drm_, display);
- if (ret) {
- ALOGE("Failed to create event worker for d=%d %d\n", display, ret);
- return HWC2::Error::BadDisplay;
- }
-
return SetActiveConfig(default_config);
}
@@ -617,8 +645,8 @@
ALOGE("Failed to queue dpms composition on %d", ret);
return HWC2::Error::BadConfig;
}
- if (connector_->active_mode().id() == 0)
- connector_->set_active_mode(*mode);
+
+ connector_->set_active_mode(*mode);
// Setup the client layer's dimensions
hwc_rect_t display_frame = {.left = 0,
@@ -880,6 +908,52 @@
layer->SetTransform(static_cast<int32_t>(transform_));
}
+void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) {
+ auto cb = callbacks_.find(HWC2::Callback::Hotplug);
+ if (cb == callbacks_.end())
+ return;
+
+ auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(cb->second.func);
+ hotplug(cb->second.data, displayid,
+ (state == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED
+ : HWC2_CONNECTION_DISCONNECTED));
+}
+
+void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) {
+ for (auto &conn : drmDevice->connectors()) {
+ if (conn->state() != DRM_MODE_CONNECTED)
+ continue;
+ HandleDisplayHotplug(conn->display(), conn->state());
+ }
+}
+
+void DrmHwcTwo::DrmHotplugHandler::HandleEvent(uint64_t timestamp_us) {
+ for (auto &conn : drm_->connectors()) {
+ drmModeConnection old_state = conn->state();
+ drmModeConnection cur_state = conn->UpdateModes()
+ ? DRM_MODE_UNKNOWNCONNECTION
+ : conn->state();
+
+ if (cur_state == old_state)
+ continue;
+
+ ALOGI("%s event @%" PRIu64 " for connector %u on display %d",
+ cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", timestamp_us,
+ conn->id(), conn->display());
+
+ int display_id = conn->display();
+ if (cur_state == DRM_MODE_CONNECTED) {
+ auto &display = hwc2_->displays_.at(display_id);
+ display.ChosePreferredConfig();
+ } else {
+ auto &display = hwc2_->displays_.at(display_id);
+ display.ClearDisplay();
+ }
+
+ hwc2_->HandleDisplayHotplug(display_id, cur_state);
+ }
+}
+
// static
int DrmHwcTwo::HookDevClose(hw_device_t * /*dev*/) {
unsupported(__func__);
diff --git a/drmhwctwo.h b/drmhwctwo.h
index d9ced9b..a71d7cc 100644
--- a/drmhwctwo.h
+++ b/drmhwctwo.h
@@ -143,6 +143,7 @@
HWC2::Error RegisterVsyncCallback(hwc2_callback_data_t data,
hwc2_function_pointer_t func);
+ void ClearDisplay();
// HWC Hooks
HWC2::Error AcceptDisplayChanges();
@@ -173,6 +174,7 @@
int32_t *fences);
HWC2::Error PresentDisplay(int32_t *retire_fence);
HWC2::Error SetActiveConfig(hwc2_config_t config);
+ HWC2::Error ChosePreferredConfig();
HWC2::Error SetClientTarget(buffer_handle_t target, int32_t acquire_fence,
int32_t dataspace, hwc_region_t damage);
HWC2::Error SetColorMode(int32_t mode);
@@ -213,6 +215,18 @@
uint32_t frame_no_ = 0;
};
+ class DrmHotplugHandler : public DrmEventHandler {
+ public:
+ DrmHotplugHandler(DrmHwcTwo *hwc2, DrmDevice *drm)
+ : hwc2_(hwc2), drm_(drm) {
+ }
+ void HandleEvent(uint64_t timestamp_us);
+
+ private:
+ DrmHwcTwo *hwc2_;
+ DrmDevice *drm_;
+ };
+
static DrmHwcTwo *toDrmHwcTwo(hwc2_device_t *dev) {
return static_cast<DrmHwcTwo *>(dev);
}
@@ -261,6 +275,9 @@
uint32_t GetMaxVirtualDisplayCount();
HWC2::Error RegisterCallback(int32_t descriptor, hwc2_callback_data_t data,
hwc2_function_pointer_t function);
+ HWC2::Error CreateDisplay(hwc2_display_t displ, HWC2::DisplayType type);
+ void HandleDisplayHotplug(hwc2_display_t displayid, int state);
+ void HandleInitialHotplugState(DrmDevice *drmDevice);
ResourceManager resource_manager_;
std::map<hwc2_display_t, HwcDisplay> displays_;
diff --git a/resourcemanager.h b/resourcemanager.h
index 463739b..f10af45 100644
--- a/resourcemanager.h
+++ b/resourcemanager.h
@@ -34,6 +34,12 @@
std::shared_ptr<Importer> GetImporter(int display);
const gralloc_module_t *gralloc();
DrmConnector *AvailableWritebackConnector(int display);
+ const std::vector<std::unique_ptr<DrmDevice>> &getDrmDevices() const {
+ return drms_;
+ }
+ int getDisplayCount() const {
+ return num_displays_;
+ }
private:
int AddDrmDevice(std::string path);