OSDN Git Service

b1a206cb5df1294bd3b32b8d1ded8246e331853b
[android-x86/external-IA-Hardware-Composer.git] / wsi / drm / drmdisplaymanager.cpp
1 /*
2 // Copyright (c) 2016 Intel Corporation
3 //
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
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
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.
15 */
16
17 #include "drmdisplaymanager.h"
18
19 #include <stdlib.h>
20 #include <time.h>
21 #include <fcntl.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <xf86drm.h>
28 #include <xf86drmMode.h>
29 #include <errno.h>
30
31 #include <linux/types.h>
32 #include <linux/netlink.h>
33
34 #include <hwctrace.h>
35 #include <gpudevice.h>
36
37 #include <nativebufferhandler.h>
38
39 namespace hwcomposer {
40
41 DrmDisplayManager::DrmDisplayManager(GpuDevice *device)
42     : HWCThread(-8, "DisplayManager"), device_(device) {
43   CTRACE();
44 }
45
46 DrmDisplayManager::~DrmDisplayManager() {
47   CTRACE();
48   std::vector<std::unique_ptr<DrmDisplay>>().swap(displays_);
49 #ifndef DISABLE_HOTPLUG_NOTIFICATION
50   close(hotplug_fd_);
51 #endif
52   drmClose(fd_);
53 }
54
55 bool DrmDisplayManager::Initialize() {
56   CTRACE();
57   fd_ = drmOpen("i915", NULL);
58   if (fd_ < 0) {
59     ETRACE("Failed to open dri %s", PRINTERROR());
60     return -ENODEV;
61   }
62
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);
66   if (ret) {
67     ETRACE("Failed to set atomic cap %s", PRINTERROR());
68     return false;
69   }
70
71   ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);
72   if (ret) {
73     ETRACE("Failed to set atomic cap %d", ret);
74     return false;
75   }
76
77   ScopedDrmResourcesPtr res(drmModeGetResources(fd_));
78
79   for (int32_t i = 0; i < res->count_crtcs; ++i) {
80     ScopedDrmCrtcPtr c(drmModeGetCrtc(fd_, res->crtcs[i]));
81     if (!c) {
82       ETRACE("Failed to get crtc %d", res->crtcs[i]);
83       return false;
84     }
85
86     std::unique_ptr<DrmDisplay> display(
87         new DrmDisplay(fd_, i, c->crtc_id, this));
88
89     displays_.emplace_back(std::move(display));
90   }
91
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());
96     return true;
97   }
98
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;
104
105   ret = bind(hotplug_fd_, (struct sockaddr *)&addr, sizeof(addr));
106   if (ret) {
107     ETRACE("Failed to bind sockaddr_nl and hot plug monitor fd. %s",
108            PRINTERROR());
109     return true;
110   }
111
112   fd_handler_.AddFd(hotplug_fd_);
113 #endif
114   IHOTPLUGEVENTTRACE("DisplayManager Initialization succeeded.");
115   return true;
116 }
117
118 void DrmDisplayManager::HotPlugEventHandler() {
119   CTRACE();
120   int fd = hotplug_fd_;
121   char buffer[DRM_HOTPLUG_EVENT_SIZE];
122   int ret;
123
124   memset(&buffer, 0, sizeof(buffer));
125   while (true) {
126     bool drm_event = false, hotplug_event = false;
127     size_t srclen = DRM_HOTPLUG_EVENT_SIZE - 1;
128     ret = read(fd, &buffer, srclen);
129     if (ret <= 0) {
130       if (ret < 0)
131         ETRACE("Failed to read uevent. %s", PRINTERROR());
132
133       return;
134     }
135
136     buffer[ret] = '\0';
137
138     for (int32_t i = 0; i < ret;) {
139       char *event = buffer + i;
140       if (!strcmp(event, "DEVTYPE=drm_minor"))
141         drm_event = true;
142       else if (!strcmp(event, "HOTPLUG=1") ||  // Common hotplug request
143                !strcmp(event,
144                        "HDMI-Change")) {  // Hotplug happened during suspend
145         hotplug_event = true;
146       }
147
148       if (hotplug_event && drm_event)
149         break;
150
151       i += strlen(event) + 1;
152     }
153
154     if (drm_event && hotplug_event) {
155       IHOTPLUGEVENTTRACE(
156           "Recieved Hot Plug event related to display calling "
157           "UpdateDisplayState.");
158       UpdateDisplayState();
159     }
160   }
161 }
162
163 void DrmDisplayManager::HandleWait() {
164   if (fd_handler_.Poll(-1) <= 0) {
165     ETRACE("Poll Failed in DisplayManager %s", PRINTERROR());
166   }
167 }
168
169 void DrmDisplayManager::InitializeDisplayResources() {
170   buffer_handler_.reset(NativeBufferHandler::CreateInstance(fd_));
171   if (!buffer_handler_) {
172     ETRACE("Failed to create native buffer handler instance");
173     return;
174   }
175
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);
180     }
181   }
182
183   virtual_display_.reset(new VirtualDisplay(fd_, buffer_handler_.get(), 0, 0));
184   nested_display_.reset(new NestedDisplay(fd_, buffer_handler_.get()));
185 }
186
187 void DrmDisplayManager::StartHotPlugMonitor() {
188   if (!UpdateDisplayState()) {
189     ETRACE("Failed to connect display.");
190   }
191
192   if (!InitWorker()) {
193     ETRACE("Failed to initalizer thread to monitor Hot Plug events. %s",
194            PRINTERROR());
195   }
196 }
197
198 void DrmDisplayManager::HandleRoutine() {
199   CTRACE();
200   IHOTPLUGEVENTTRACE("DisplayManager::Routine.");
201   if (fd_handler_.IsReady(hotplug_fd_)) {
202     IHOTPLUGEVENTTRACE("Recieved Hot plug notification.");
203     HotPlugEventHandler();
204   }
205 }
206
207 bool DrmDisplayManager::UpdateDisplayState() {
208   CTRACE();
209   ScopedDrmResourcesPtr res(drmModeGetResources(fd_));
210   if (!res) {
211     ETRACE("Failed to get DrmResources resources");
212     return false;
213   }
214
215   spin_lock_.lock();
216   // Start of assuming no displays are connected
217   for (auto &display : displays_) {
218     display->MarkForDisconnect();
219   }
220
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]));
227     if (!connector) {
228       ETRACE("Failed to get connector %d", res->connectors[i]);
229       break;
230     }
231     // check if a monitor is connected.
232     if (connector->connection != DRM_MODE_CONNECTED)
233       continue;
234
235     // Ensure we have atleast one valid mode.
236     if (connector->count_modes == 0)
237       continue;
238
239     if (connector->encoder_id == 0) {
240       no_encoder.emplace_back(i);
241       continue;
242     }
243
244     std::vector<drmModeModeInfo> mode;
245     uint32_t preferred_mode = 0;
246     uint32_t size = connector->count_modes;
247     mode.resize(size);
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) {
252         preferred_mode = i;
253       }
254     }
255
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_) {
260         IHOTPLUGEVENTTRACE(
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(),
266                                     preferred_mode)) {
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);
272           break;
273         }
274       }
275     }
276   }
277
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)]));
283     if (!connector) {
284       ETRACE("Failed to get connector %d", res->connectors[i]);
285       break;
286     }
287
288     std::vector<drmModeModeInfo> mode;
289     uint32_t preferred_mode = 0;
290     uint32_t size = connector->count_modes;
291     mode.resize(size);
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) {
296         preferred_mode = i;
297       }
298     }
299
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]));
305       if (!encoder)
306         continue;
307
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(),
312                                     preferred_mode)) {
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);
317           break;
318         }
319       }
320     }
321   }
322
323   for (auto &display : displays_) {
324     if (!display->IsConnected()) {
325       display->DisConnect();
326     } else if (callback_) {
327       connected_displays.emplace_back(display.get());
328     }
329   }
330
331   if (callback_) {
332     callback_->Callback(connected_displays);
333   }
334
335   spin_lock_.unlock();
336 #ifndef ENABLE_ANDROID_WA
337   notify_client_ = true;
338 #endif
339
340   if (notify_client_ || (!(displays_.at(0)->IsConnected()))) {
341     IHOTPLUGEVENTTRACE("NotifyClientsOfDisplayChangeStatus Called %d %d \n",
342                        notify_client_, displays_.at(0)->IsConnected());
343     NotifyClientsOfDisplayChangeStatus();
344   }
345
346   return true;
347 }
348
349 void DrmDisplayManager::NotifyClientsOfDisplayChangeStatus() {
350   spin_lock_.lock();
351   for (auto &display : displays_) {
352     if (!display->IsConnected()) {
353       display->NotifyClientOfDisConnectedState();
354     } else {
355       display->NotifyClientOfConnectedState();
356     }
357   }
358
359   static bool nested_display_registered = false;
360   if (!nested_display_registered) {
361     nested_display_->HotPlugUpdate(true);
362     nested_display_registered = true;
363   }
364
365 #ifdef ENABLE_ANDROID_WA
366   notify_client_ = true;
367 #endif
368   spin_lock_.unlock();
369 }
370
371 NativeDisplay *DrmDisplayManager::GetVirtualDisplay() {
372   spin_lock_.lock();
373   NativeDisplay *display = virtual_display_.get();
374   spin_lock_.unlock();
375   return display;
376 }
377
378 NativeDisplay *DrmDisplayManager::GetNestedDisplay() {
379   spin_lock_.lock();
380   NativeDisplay *display = nested_display_.get();
381   spin_lock_.unlock();
382   return display;
383 }
384
385 std::vector<NativeDisplay *> DrmDisplayManager::GetAllDisplays() {
386   spin_lock_.lock();
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());
391   }
392   spin_lock_.unlock();
393   return all_displays;
394 }
395
396 void DrmDisplayManager::RegisterHotPlugEventCallback(
397     std::shared_ptr<DisplayHotPlugEventCallback> callback) {
398   spin_lock_.lock();
399   callback_ = callback;
400   spin_lock_.unlock();
401 }
402
403 void DrmDisplayManager::ForceRefresh() {
404   spin_lock_.lock();
405   size_t size = displays_.size();
406   for (size_t i = 0; i < size; ++i) {
407     displays_.at(i)->ForceRefresh();
408   }
409
410   release_lock_ = true;
411   spin_lock_.unlock();
412 }
413
414 void DrmDisplayManager::IgnoreUpdates() {
415   size_t size = displays_.size();
416   for (size_t i = 0; i < size; ++i) {
417     displays_.at(i)->IgnoreUpdates();
418   }
419 }
420
421 void DrmDisplayManager::HandleLazyInitialization() {
422   spin_lock_.lock();
423   if (release_lock_) {
424     device_->DisableWatch();
425     release_lock_ = false;
426   }
427   spin_lock_.unlock();
428 }
429
430 DisplayManager *DisplayManager::CreateDisplayManager(GpuDevice *device) {
431   return new DrmDisplayManager(device);
432 }
433
434 }  // namespace hwcomposer