2 // Copyright (c) 2016 Intel Corporation
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
17 #include "drmdisplaymanager.h"
24 #include <sys/socket.h>
25 #include <sys/types.h>
28 #include <xf86drmMode.h>
31 #include <linux/types.h>
32 #include <linux/netlink.h>
35 #include <gpudevice.h>
37 #include <nativebufferhandler.h>
39 namespace hwcomposer {
41 DrmDisplayManager::DrmDisplayManager(GpuDevice *device)
42 : HWCThread(-8, "DisplayManager"), device_(device) {
46 DrmDisplayManager::~DrmDisplayManager() {
48 std::vector<std::unique_ptr<DrmDisplay>>().swap(displays_);
49 #ifndef DISABLE_HOTPLUG_NOTIFICATION
55 bool DrmDisplayManager::Initialize() {
57 fd_ = drmOpen("i915", NULL);
59 ETRACE("Failed to open dri %s", PRINTERROR());
63 struct drm_set_client_cap cap = {DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1};
64 drmIoctl(fd_, DRM_IOCTL_SET_CLIENT_CAP, &cap);
65 int ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);
67 ETRACE("Failed to set atomic cap %s", PRINTERROR());
71 ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);
73 ETRACE("Failed to set atomic cap %d", ret);
77 ScopedDrmResourcesPtr res(drmModeGetResources(fd_));
79 for (int32_t i = 0; i < res->count_crtcs; ++i) {
80 ScopedDrmCrtcPtr c(drmModeGetCrtc(fd_, res->crtcs[i]));
82 ETRACE("Failed to get crtc %d", res->crtcs[i]);
86 std::unique_ptr<DrmDisplay> display(
87 new DrmDisplay(fd_, i, c->crtc_id, this));
89 displays_.emplace_back(std::move(display));
92 #ifndef DISABLE_HOTPLUG_NOTIFICATION
93 hotplug_fd_ = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
94 if (hotplug_fd_ < 0) {
95 ETRACE("Failed to create socket for hot plug monitor. %s", PRINTERROR());
99 struct sockaddr_nl addr;
100 memset(&addr, 0, sizeof(addr));
101 addr.nl_family = AF_NETLINK;
102 addr.nl_pid = getpid();
103 addr.nl_groups = 0xffffffff;
105 ret = bind(hotplug_fd_, (struct sockaddr *)&addr, sizeof(addr));
107 ETRACE("Failed to bind sockaddr_nl and hot plug monitor fd. %s",
112 fd_handler_.AddFd(hotplug_fd_);
114 IHOTPLUGEVENTTRACE("DisplayManager Initialization succeeded.");
118 void DrmDisplayManager::HotPlugEventHandler() {
120 int fd = hotplug_fd_;
121 char buffer[DRM_HOTPLUG_EVENT_SIZE];
124 memset(&buffer, 0, sizeof(buffer));
126 bool drm_event = false, hotplug_event = false;
127 size_t srclen = DRM_HOTPLUG_EVENT_SIZE - 1;
128 ret = read(fd, &buffer, srclen);
131 ETRACE("Failed to read uevent. %s", PRINTERROR());
138 for (int32_t i = 0; i < ret;) {
139 char *event = buffer + i;
140 if (!strcmp(event, "DEVTYPE=drm_minor"))
142 else if (!strcmp(event, "HOTPLUG=1") || // Common hotplug request
144 "HDMI-Change")) { // Hotplug happened during suspend
145 hotplug_event = true;
148 if (hotplug_event && drm_event)
151 i += strlen(event) + 1;
154 if (drm_event && hotplug_event) {
156 "Recieved Hot Plug event related to display calling "
157 "UpdateDisplayState.");
158 UpdateDisplayState();
163 void DrmDisplayManager::HandleWait() {
164 if (fd_handler_.Poll(-1) <= 0) {
165 ETRACE("Poll Failed in DisplayManager %s", PRINTERROR());
169 void DrmDisplayManager::InitializeDisplayResources() {
170 buffer_handler_.reset(NativeBufferHandler::CreateInstance(fd_));
171 if (!buffer_handler_) {
172 ETRACE("Failed to create native buffer handler instance");
176 int size = displays_.size();
177 for (int i = 0; i < size; ++i) {
178 if (!displays_.at(i)->Initialize(buffer_handler_.get())) {
179 ETRACE("Failed to Initialize Display %d", i);
183 virtual_display_.reset(new VirtualDisplay(fd_, buffer_handler_.get(), 0, 0));
184 nested_display_.reset(new NestedDisplay(fd_, buffer_handler_.get()));
187 void DrmDisplayManager::StartHotPlugMonitor() {
188 if (!UpdateDisplayState()) {
189 ETRACE("Failed to connect display.");
193 ETRACE("Failed to initalizer thread to monitor Hot Plug events. %s",
198 void DrmDisplayManager::HandleRoutine() {
200 IHOTPLUGEVENTTRACE("DisplayManager::Routine.");
201 if (fd_handler_.IsReady(hotplug_fd_)) {
202 IHOTPLUGEVENTTRACE("Recieved Hot plug notification.");
203 HotPlugEventHandler();
207 bool DrmDisplayManager::UpdateDisplayState() {
209 ScopedDrmResourcesPtr res(drmModeGetResources(fd_));
211 ETRACE("Failed to get DrmResources resources");
216 // Start of assuming no displays are connected
217 for (auto &display : displays_) {
218 display->MarkForDisconnect();
221 std::vector<NativeDisplay *> connected_displays;
222 std::vector<uint32_t> no_encoder;
223 uint32_t total_connectors = res->count_connectors;
224 for (uint32_t i = 0; i < total_connectors; ++i) {
225 ScopedDrmConnectorPtr connector(
226 drmModeGetConnector(fd_, res->connectors[i]));
228 ETRACE("Failed to get connector %d", res->connectors[i]);
231 // check if a monitor is connected.
232 if (connector->connection != DRM_MODE_CONNECTED)
235 // Ensure we have atleast one valid mode.
236 if (connector->count_modes == 0)
239 if (connector->encoder_id == 0) {
240 no_encoder.emplace_back(i);
244 std::vector<drmModeModeInfo> mode;
245 uint32_t preferred_mode = 0;
246 uint32_t size = connector->count_modes;
248 for (uint32_t i = 0; i < size; ++i) {
249 mode[i] = connector->modes[i];
250 // There is only one preferred mode per connector.
251 if (mode[i].type & DRM_MODE_TYPE_PREFERRED) {
256 // Lets try to find crts for any connected encoder.
257 ScopedDrmEncoderPtr encoder(drmModeGetEncoder(fd_, connector->encoder_id));
258 if (encoder && encoder->crtc_id) {
259 for (auto &display : displays_) {
261 "Trying to connect %d with crtc: %d is display connected: %d \n",
262 encoder->crtc_id, display->CrtcId(), display->IsConnected());
263 // At initilaization preferred mode is set!
264 if (!display->IsConnected() && encoder->crtc_id == display->CrtcId() &&
265 display->ConnectDisplay(mode.at(preferred_mode), connector.get(),
267 IHOTPLUGEVENTTRACE("Connected %d with crtc: %d pipe:%d \n",
268 encoder->crtc_id, display->CrtcId(),
269 display->GetDisplayPipe());
270 // Set the modes supported for each display
271 display->SetDrmModeInfo(mode);
278 // Deal with connectors with encoder_id == 0.
279 uint32_t size = no_encoder.size();
280 for (uint32_t i = 0; i < size; ++i) {
281 ScopedDrmConnectorPtr connector(
282 drmModeGetConnector(fd_, res->connectors[no_encoder.at(i)]));
284 ETRACE("Failed to get connector %d", res->connectors[i]);
288 std::vector<drmModeModeInfo> mode;
289 uint32_t preferred_mode = 0;
290 uint32_t size = connector->count_modes;
292 for (uint32_t i = 0; i < size; ++i) {
293 mode[i] = connector->modes[i];
294 // There is only one preferred mode per connector.
295 if (mode[i].type & DRM_MODE_TYPE_PREFERRED) {
300 // Try to find an encoder for the connector.
301 size = connector->count_encoders;
302 for (uint32_t j = 0; j < size; ++j) {
303 ScopedDrmEncoderPtr encoder(
304 drmModeGetEncoder(fd_, connector->encoders[j]));
308 for (auto &display : displays_) {
309 if (!display->IsConnected() &&
310 (encoder->possible_crtcs & (1 << display->GetDisplayPipe())) &&
311 display->ConnectDisplay(mode.at(preferred_mode), connector.get(),
313 IHOTPLUGEVENTTRACE("Connected with crtc: %d pipe:%d \n",
314 display->CrtcId(), display->GetDisplayPipe());
315 // Set the modes supported for each display
316 display->SetDrmModeInfo(mode);
323 for (auto &display : displays_) {
324 if (!display->IsConnected()) {
325 display->DisConnect();
326 } else if (callback_) {
327 connected_displays.emplace_back(display.get());
332 callback_->Callback(connected_displays);
336 #ifndef ENABLE_ANDROID_WA
337 notify_client_ = true;
340 if (notify_client_ || (!(displays_.at(0)->IsConnected()))) {
341 IHOTPLUGEVENTTRACE("NotifyClientsOfDisplayChangeStatus Called %d %d \n",
342 notify_client_, displays_.at(0)->IsConnected());
343 NotifyClientsOfDisplayChangeStatus();
349 void DrmDisplayManager::NotifyClientsOfDisplayChangeStatus() {
351 for (auto &display : displays_) {
352 if (!display->IsConnected()) {
353 display->NotifyClientOfDisConnectedState();
355 display->NotifyClientOfConnectedState();
359 static bool nested_display_registered = false;
360 if (!nested_display_registered) {
361 nested_display_->HotPlugUpdate(true);
362 nested_display_registered = true;
365 #ifdef ENABLE_ANDROID_WA
366 notify_client_ = true;
371 NativeDisplay *DrmDisplayManager::GetVirtualDisplay() {
373 NativeDisplay *display = virtual_display_.get();
378 NativeDisplay *DrmDisplayManager::GetNestedDisplay() {
380 NativeDisplay *display = nested_display_.get();
385 std::vector<NativeDisplay *> DrmDisplayManager::GetAllDisplays() {
387 std::vector<NativeDisplay *> all_displays;
388 size_t size = displays_.size();
389 for (size_t i = 0; i < size; ++i) {
390 all_displays.emplace_back(displays_.at(i).get());
396 void DrmDisplayManager::RegisterHotPlugEventCallback(
397 std::shared_ptr<DisplayHotPlugEventCallback> callback) {
399 callback_ = callback;
403 void DrmDisplayManager::ForceRefresh() {
405 size_t size = displays_.size();
406 for (size_t i = 0; i < size; ++i) {
407 displays_.at(i)->ForceRefresh();
410 release_lock_ = true;
414 void DrmDisplayManager::IgnoreUpdates() {
415 size_t size = displays_.size();
416 for (size_t i = 0; i < size; ++i) {
417 displays_.at(i)->IgnoreUpdates();
421 void DrmDisplayManager::HandleLazyInitialization() {
424 device_->DisableWatch();
425 release_lock_ = false;
430 DisplayManager *DisplayManager::CreateDisplayManager(GpuDevice *device) {
431 return new DrmDisplayManager(device);
434 } // namespace hwcomposer