OSDN Git Service

Re-work hot plug event handling.
[android-x86/external-IA-Hardware-Composer.git] / wsi / drm / drmdisplay.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 "drmdisplay.h"
18
19 #include <cmath>
20 #include <set>
21
22 #include <hwcdefs.h>
23 #include <hwclayer.h>
24 #include <hwctrace.h>
25
26 #include <algorithm>
27 #include <string>
28 #include <sstream>
29
30 #include "displayqueue.h"
31 #include "displayplanemanager.h"
32
33 namespace hwcomposer {
34
35 static const int32_t kUmPerInch = 25400;
36
37 DrmDisplay::DrmDisplay(uint32_t gpu_fd, uint32_t pipe_id, uint32_t crtc_id)
38     : PhysicalDisplay(gpu_fd, pipe_id), crtc_id_(crtc_id), connector_(0) {
39   memset(&current_mode_, 0, sizeof(current_mode_));
40 }
41
42 DrmDisplay::~DrmDisplay() {
43   if (blob_id_)
44     drmModeDestroyPropertyBlob(gpu_fd_, blob_id_);
45
46   if (old_blob_id_)
47     drmModeDestroyPropertyBlob(gpu_fd_, old_blob_id_);
48
49   display_queue_->SetPowerMode(kOff);
50 }
51
52 bool DrmDisplay::InitializeDisplay() {
53   ScopedDrmObjectPropertyPtr crtc_props(
54       drmModeObjectGetProperties(gpu_fd_, crtc_id_, DRM_MODE_OBJECT_CRTC));
55   GetDrmObjectProperty("ACTIVE", crtc_props, &active_prop_);
56   GetDrmObjectProperty("MODE_ID", crtc_props, &mode_id_prop_);
57   GetDrmObjectProperty("GAMMA_LUT", crtc_props, &lut_id_prop_);
58   GetDrmObjectPropertyValue("GAMMA_LUT_SIZE", crtc_props, &lut_size_);
59   GetDrmObjectProperty("OUT_FENCE_PTR", crtc_props, &out_fence_ptr_prop_);
60
61   return true;
62 }
63
64 bool DrmDisplay::ConnectDisplay(const drmModeModeInfo &mode_info,
65                                 const drmModeConnector *connector) {
66   IHOTPLUGEVENTTRACE("DrmDisplay::Connect recieved.");
67   // TODO(kalyan): Add support for multi monitor case.
68   if (connector_ && connector->connector_id == connector_) {
69     IHOTPLUGEVENTTRACE(
70         "Display is already connected to this connector. %d %d %p \n",
71         connector->connector_id, connector_, this);
72     PhysicalDisplay::Connect();
73     return true;
74   }
75
76   IHOTPLUGEVENTTRACE(
77       "Display is being connected to a new connector.%d %d %p \n",
78       connector->connector_id, connector_, this);
79   connector_ = connector->connector_id;
80   mmWidth_ = connector->mmWidth;
81   mmHeight_ = connector->mmHeight;
82   SetDisplayAttribute(mode_info);
83
84   ScopedDrmObjectPropertyPtr connector_props(drmModeObjectGetProperties(
85       gpu_fd_, connector_, DRM_MODE_OBJECT_CONNECTOR));
86   if (!connector_props) {
87     ETRACE("Unable to get connector properties.");
88     return false;
89   }
90
91   // GetDrmObjectProperty("DPMS", connector_props, &dpms_prop_);
92   GetDrmObjectProperty("CRTC_ID", connector_props, &crtc_prop_);
93   GetDrmObjectProperty("Broadcast RGB", connector_props, &broadcastrgb_id_);
94   GetDrmObjectProperty("DPMS", connector_props, &dpms_prop_);
95
96   PhysicalDisplay::Connect();
97
98   drmModePropertyPtr broadcastrgb_props =
99       drmModeGetProperty(gpu_fd_, broadcastrgb_id_);
100
101   SetPowerMode(power_mode_);
102
103   // This is a valid case on DSI panels.
104   if (!broadcastrgb_props)
105     return true;
106
107   if (!(broadcastrgb_props->flags & DRM_MODE_PROP_ENUM))
108     return false;
109
110   for (int i = 0; i < broadcastrgb_props->count_enums; i++) {
111     if (!strcmp(broadcastrgb_props->enums[i].name, "Full")) {
112       broadcastrgb_full_ = broadcastrgb_props->enums[i].value;
113     } else if (!strcmp(broadcastrgb_props->enums[i].name, "Automatic")) {
114       broadcastrgb_automatic_ = broadcastrgb_props->enums[i].value;
115     }
116   }
117
118   drmModeFreeProperty(broadcastrgb_props);
119
120   return true;
121 }
122
123 bool DrmDisplay::GetDisplayAttribute(uint32_t config /*config*/,
124                                      HWCDisplayAttribute attribute,
125                                      int32_t *value) {
126   display_lock_.lock();
127   float refresh;
128   bool status = true;
129   switch (attribute) {
130     case HWCDisplayAttribute::kWidth:
131       *value = modes_[config].hdisplay;
132       break;
133     case HWCDisplayAttribute::kHeight:
134       *value = modes_[config].vdisplay;
135       break;
136     case HWCDisplayAttribute::kRefreshRate:
137       refresh = (modes_[config].clock * 1000.0f) /
138                 (modes_[config].htotal * modes_[config].vtotal);
139
140       if (modes_[config].flags & DRM_MODE_FLAG_INTERLACE)
141         refresh *= 2;
142
143       if (modes_[config].flags & DRM_MODE_FLAG_DBLSCAN)
144         refresh /= 2;
145
146       if (modes_[config].vscan > 1)
147         refresh /= modes_[config].vscan;
148       // in nanoseconds
149       *value = 1e9 / refresh;
150       break;
151     case HWCDisplayAttribute::kDpiX:
152       // Dots per 1000 inches
153       *value =
154           mmWidth_ ? (modes_[config].hdisplay * kUmPerInch) / mmWidth_ : -1;
155       break;
156     case HWCDisplayAttribute::kDpiY:
157       // Dots per 1000 inches
158       *value =
159           mmHeight_ ? (modes_[config].vdisplay * kUmPerInch) / mmHeight_ : -1;
160       break;
161     default:
162       *value = -1;
163       status = false;
164   }
165
166   display_lock_.unlock();
167   return status;
168 }
169
170 bool DrmDisplay::GetDisplayConfigs(uint32_t *num_configs, uint32_t *configs) {
171   if (!configs) {
172     display_lock_.lock();
173     *num_configs = modes_.size();
174     display_lock_.unlock();
175     return true;
176   }
177
178   uint32_t size = *num_configs;
179   for (uint32_t i = 0; i < size; i++)
180     configs[i] = i;
181
182   return true;
183 }
184
185 bool DrmDisplay::GetDisplayName(uint32_t *size, char *name) {
186   std::ostringstream stream;
187   stream << "Display-" << connector_;
188   std::string string = stream.str();
189   size_t length = string.length();
190   if (!name) {
191     *size = length;
192     return true;
193   }
194
195   *size = std::min<uint32_t>(static_cast<uint32_t>(length - 1), *size);
196   strncpy(name, string.c_str(), *size);
197   return true;
198 }
199
200 void DrmDisplay::UpdateDisplayConfig() {
201   // update the activeConfig
202   display_lock_.lock();
203   flags_ |= DRM_MODE_ATOMIC_ALLOW_MODESET;
204   SetDisplayAttribute(modes_[config_]);
205   display_lock_.unlock();
206 }
207
208 void DrmDisplay::PowerOn() {
209   flags_ = 0;
210   flags_ |= DRM_MODE_ATOMIC_ALLOW_MODESET;
211   drmModeConnectorSetProperty(gpu_fd_, connector_, dpms_prop_,
212                               DRM_MODE_DPMS_ON);
213 }
214
215 bool DrmDisplay::SetBroadcastRGB(const char *range_property) {
216   int64_t p_value = -1;
217
218   if (!strcmp(range_property, "Full")) {
219     p_value = broadcastrgb_full_;
220   } else if (!strcmp(range_property, "Automatic")) {
221     p_value = broadcastrgb_automatic_;
222   } else {
223     ETRACE("Wrong Broadcast RGB value %s", range_property);
224     return false;
225   }
226
227   if (p_value < 0)
228     return false;
229
230   if (drmModeObjectSetProperty(gpu_fd_, connector_, DRM_MODE_OBJECT_CONNECTOR,
231                                broadcastrgb_id_, (uint64_t)p_value) != 0)
232     return false;
233
234   return true;
235 }
236
237 bool DrmDisplay::Commit(
238     const DisplayPlaneStateList &composition_planes,
239     const DisplayPlaneStateList &previous_composition_planes,
240     bool disable_explicit_fence, int32_t *commit_fence) {
241   // Do the actual commit.
242   ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
243
244   if (!pset) {
245     ETRACE("Failed to allocate property set %d", -ENOMEM);
246     return false;
247   }
248
249   if (display_state_ & kNeedsModeset) {
250     if (!ApplyPendingModeset(pset.get())) {
251       ETRACE("Failed to Modeset.");
252       return false;
253     }
254   } else if (!disable_explicit_fence && out_fence_ptr_prop_) {
255     GetFence(pset.get(), commit_fence);
256   }
257
258   if (!CommitFrame(composition_planes, previous_composition_planes, pset.get(),
259                    flags_)) {
260     ETRACE("Failed to Commit layers.");
261     return false;
262   }
263
264   if (display_state_ & kNeedsModeset) {
265     display_state_ &= ~kNeedsModeset;
266     if (!disable_explicit_fence) {
267       flags_ = 0;
268       flags_ |= DRM_MODE_ATOMIC_NONBLOCK;
269     }
270   }
271
272   return true;
273 }
274
275 bool DrmDisplay::CommitFrame(
276     const DisplayPlaneStateList &comp_planes,
277     const DisplayPlaneStateList &previous_composition_planes,
278     drmModeAtomicReqPtr pset, uint32_t flags) {
279   CTRACE();
280   if (!pset) {
281     ETRACE("Failed to allocate property set %d", -ENOMEM);
282     return false;
283   }
284
285   for (const DisplayPlaneState &comp_plane : previous_composition_planes) {
286     DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
287     plane->SetEnabled(false);
288   }
289
290   for (const DisplayPlaneState &comp_plane : comp_planes) {
291     DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
292     const OverlayLayer *layer = comp_plane.GetOverlayLayer();
293     int32_t fence = layer->GetAcquireFence();
294     if (fence > 0) {
295       plane->SetNativeFence(dup(fence));
296     } else {
297       plane->SetNativeFence(-1);
298     }
299     if (!plane->UpdateProperties(pset, crtc_id_, layer))
300       return false;
301
302     plane->SetEnabled(true);
303   }
304
305   for (const DisplayPlaneState &comp_plane : previous_composition_planes) {
306     DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
307     if (plane->IsEnabled())
308       continue;
309
310     plane->Disable(pset);
311   }
312
313   int ret = drmModeAtomicCommit(gpu_fd_, pset, flags, NULL);
314   if (ret) {
315     ETRACE("Failed to commit pset ret=%s\n", PRINTERROR());
316     return false;
317   }
318
319   return true;
320 }
321
322 void DrmDisplay::SetDrmModeInfo(const std::vector<drmModeModeInfo> &mode_info) {
323   display_lock_.lock();
324   uint32_t size = mode_info.size();
325   std::vector<drmModeModeInfo>().swap(modes_);
326   for (uint32_t i = 0; i < size; ++i) {
327     modes_.emplace_back(mode_info[i]);
328   }
329
330   display_lock_.unlock();
331 }
332
333 void DrmDisplay::SetDisplayAttribute(const drmModeModeInfo &mode_info) {
334   width_ = mode_info.hdisplay;
335   height_ = mode_info.vdisplay;
336   dpix_ = mmWidth_ ? (width_ * kUmPerInch) / mmWidth_ : -1;
337   dpiy_ = mmHeight_ ? (height_ * kUmPerInch) / mmHeight_ : -1;
338   current_mode_ = mode_info;
339 }
340
341 void DrmDisplay::GetDrmObjectProperty(const char *name,
342                                       const ScopedDrmObjectPropertyPtr &props,
343                                       uint32_t *id) const {
344   uint32_t count_props = props->count_props;
345   for (uint32_t i = 0; i < count_props; i++) {
346     ScopedDrmPropertyPtr property(drmModeGetProperty(gpu_fd_, props->props[i]));
347     if (property && !strcmp(property->name, name)) {
348       *id = property->prop_id;
349       break;
350     }
351   }
352   if (!(*id))
353     ETRACE("Could not find property %s", name);
354 }
355
356 void DrmDisplay::GetDrmObjectPropertyValue(
357     const char *name, const ScopedDrmObjectPropertyPtr &props,
358     uint64_t *value) const {
359   uint32_t count_props = props->count_props;
360   for (uint32_t i = 0; i < count_props; i++) {
361     ScopedDrmPropertyPtr property(drmModeGetProperty(gpu_fd_, props->props[i]));
362     if (property && !strcmp(property->name, name)) {
363       *value = props->prop_values[i];
364       break;
365     }
366   }
367   if (!(*value))
368     ETRACE("Could not find property value %s", name);
369 }
370
371 void DrmDisplay::ApplyPendingLUT(struct drm_color_lut *lut) const {
372   if (lut_id_prop_ == 0)
373     return;
374
375   uint32_t lut_blob_id = 0;
376
377   drmModeCreatePropertyBlob(
378       gpu_fd_, lut, sizeof(struct drm_color_lut) * lut_size_, &lut_blob_id);
379   if (lut_blob_id == 0) {
380     return;
381   }
382
383   drmModeObjectSetProperty(gpu_fd_, crtc_id_, DRM_MODE_OBJECT_CRTC,
384                            lut_id_prop_, lut_blob_id);
385   drmModeDestroyPropertyBlob(gpu_fd_, lut_blob_id);
386 }
387
388 float DrmDisplay::TransformContrastBrightness(float value, float brightness,
389                                               float contrast) const {
390   float result;
391   result = (value - 0.5) * contrast + 0.5 + brightness;
392
393   if (result < 0.0)
394     result = 0.0;
395   if (result > 1.0)
396     result = 1.0;
397   return result;
398 }
399
400 float DrmDisplay::TransformGamma(float value, float gamma) const {
401   float result;
402
403   result = pow(value, gamma);
404   if (result < 0.0)
405     result = 0.0;
406   if (result > 1.0)
407     result = 1.0;
408
409   return result;
410 }
411
412 void DrmDisplay::SetColorCorrection(struct gamma_colors gamma,
413                                     uint32_t contrast_c,
414                                     uint32_t brightness_c) const {
415   struct drm_color_lut *lut;
416   float brightness[3];
417   float contrast[3];
418   uint8_t temp[3];
419
420   /* reset lut when contrast and brightness are all 0 */
421   if (contrast_c == 0 && brightness_c == 0) {
422     lut = NULL;
423     ApplyPendingLUT(lut);
424     free(lut);
425     return;
426   }
427
428   lut =
429       (struct drm_color_lut *)malloc(sizeof(struct drm_color_lut) * lut_size_);
430   if (!lut) {
431     ETRACE("Cannot allocate LUT memory");
432     return;
433   }
434
435   /* Unpack brightness values for each channel */
436   temp[0] = (brightness_c >> 16) & 0xFF;
437   temp[1] = (brightness_c >> 8) & 0xFF;
438   temp[2] = (brightness_c)&0xFF;
439
440   /* Map brightness from -128 - 127 range into -0.5 - 0.5 range */
441   brightness[0] = (float)(temp[0]) / 255 - 0.5;
442   brightness[1] = (float)(temp[1]) / 255 - 0.5;
443   brightness[2] = (float)(temp[2]) / 255 - 0.5;
444
445   /* Unpack contrast values for each channel */
446   temp[0] = (contrast_c >> 16) & 0xFF;
447   temp[1] = (contrast_c >> 8) & 0xFF;
448   temp[2] = (contrast_c)&0xFF;
449
450   /* Map contrast from 0 - 255 range into 0.0 - 2.0 range */
451   contrast[0] = (float)(temp[0]) / 128;
452   contrast[1] = (float)(temp[1]) / 128;
453   contrast[2] = (float)(temp[2]) / 128;
454
455   for (uint64_t i = 0; i < lut_size_; i++) {
456     /* Set lut[0] as 0 always as the darkest color should has brightness 0 */
457     if (i == 0) {
458       lut[i].red = 0;
459       lut[i].green = 0;
460       lut[i].blue = 0;
461       continue;
462     }
463
464     lut[i].red = 0xFFFF * TransformGamma(TransformContrastBrightness(
465                                              (float)(i) / lut_size_,
466                                              brightness[0], contrast[0]),
467                                          gamma.red);
468     lut[i].green = 0xFFFF * TransformGamma(TransformContrastBrightness(
469                                                (float)(i) / lut_size_,
470                                                brightness[1], contrast[1]),
471                                            gamma.green);
472     lut[i].blue = 0xFFFF * TransformGamma(TransformContrastBrightness(
473                                               (float)(i) / lut_size_,
474                                               brightness[2], contrast[2]),
475                                           gamma.blue);
476   }
477
478   ApplyPendingLUT(lut);
479   free(lut);
480 }
481
482 bool DrmDisplay::ApplyPendingModeset(drmModeAtomicReqPtr property_set) {
483   if (old_blob_id_) {
484     drmModeDestroyPropertyBlob(gpu_fd_, old_blob_id_);
485     old_blob_id_ = 0;
486   }
487
488   drmModeCreatePropertyBlob(gpu_fd_, &current_mode_, sizeof(drmModeModeInfo),
489                             &blob_id_);
490   if (blob_id_ == 0)
491     return false;
492
493   bool active = true;
494
495   int ret = drmModeAtomicAddProperty(property_set, crtc_id_, mode_id_prop_,
496                                      blob_id_) < 0 ||
497             drmModeAtomicAddProperty(property_set, connector_, crtc_prop_,
498                                      crtc_id_) < 0 ||
499             drmModeAtomicAddProperty(property_set, crtc_id_, active_prop_,
500                                      active) < 0;
501   if (ret) {
502     ETRACE("Failed to add blob %d to pset", blob_id_);
503     return false;
504   }
505
506   old_blob_id_ = blob_id_;
507   blob_id_ = 0;
508
509   return true;
510 }
511
512 bool DrmDisplay::GetFence(drmModeAtomicReqPtr property_set,
513                           int32_t *out_fence) {
514   int ret = drmModeAtomicAddProperty(property_set, crtc_id_,
515                                      out_fence_ptr_prop_, (uintptr_t)out_fence);
516   if (ret < 0) {
517     ETRACE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
518     return false;
519   }
520
521   return true;
522 }
523
524 void DrmDisplay::Disable(const DisplayPlaneStateList &composition_planes) {
525   ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
526   if (pset) {
527     bool active = false;
528     int ret = drmModeAtomicAddProperty(pset.get(), crtc_id_, active_prop_,
529                                        active) < 0;
530     if (ret) {
531       ETRACE("Failed to set display to inactive");
532     }
533
534     for (const DisplayPlaneState &comp_plane : composition_planes) {
535       DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
536       plane->SetEnabled(false);
537       plane->Disable(pset.get());
538     }
539
540     ret = drmModeAtomicCommit(gpu_fd_, pset.get(),
541                               DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
542     if (ret)
543       ETRACE("Failed to disable display:%s\n", PRINTERROR());
544   } else {
545     ETRACE("Failed to allocate property set %d", -ENOMEM);
546   }
547
548   drmModeConnectorSetProperty(gpu_fd_, connector_, dpms_prop_,
549                               DRM_MODE_DPMS_OFF);
550 }
551
552 bool DrmDisplay::PopulatePlanes(
553     std::unique_ptr<DisplayPlane> &primary_plane,
554     std::unique_ptr<DisplayPlane> &cursor_plane,
555     std::vector<std::unique_ptr<DisplayPlane>> &overlay_planes) {
556   ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(gpu_fd_));
557   if (!plane_resources) {
558     ETRACE("Failed to get plane resources");
559     return false;
560   }
561
562   uint32_t num_planes = plane_resources->count_planes;
563   uint32_t pipe_bit = 1 << pipe_;
564   std::set<uint32_t> plane_ids;
565   for (uint32_t i = 0; i < num_planes; ++i) {
566     ScopedDrmPlanePtr drm_plane(
567         drmModeGetPlane(gpu_fd_, plane_resources->planes[i]));
568     if (!drm_plane) {
569       ETRACE("Failed to get plane ");
570       return false;
571     }
572
573     if (!(pipe_bit & drm_plane->possible_crtcs))
574       continue;
575
576     uint32_t formats_size = drm_plane->count_formats;
577     plane_ids.insert(drm_plane->plane_id);
578     std::unique_ptr<DrmPlane> plane(
579         CreatePlane(drm_plane->plane_id, drm_plane->possible_crtcs));
580     std::vector<uint32_t> supported_formats(formats_size);
581     for (uint32_t j = 0; j < formats_size; j++)
582       supported_formats[j] = drm_plane->formats[j];
583
584     if (plane->Initialize(gpu_fd_, supported_formats)) {
585       if (plane->type() == DRM_PLANE_TYPE_CURSOR) {
586         cursor_plane.reset(plane.release());
587       } else if (plane->type() == DRM_PLANE_TYPE_PRIMARY) {
588         plane->SetEnabled(true);
589         primary_plane.reset(plane.release());
590       } else if (plane->type() == DRM_PLANE_TYPE_OVERLAY) {
591         overlay_planes.emplace_back(plane.release());
592       }
593     }
594   }
595
596   if (!primary_plane) {
597     ETRACE("Failed to get primary plane for display %d", crtc_id_);
598     return false;
599   }
600
601   // We expect layers to be in ascending order.
602   std::sort(
603       overlay_planes.begin(), overlay_planes.end(),
604       [](const std::unique_ptr<DisplayPlane> &l,
605          const std::unique_ptr<DisplayPlane> &r) { return l->id() < r->id(); });
606
607   return true;
608 }
609
610 bool DrmDisplay::TestCommit(
611     const std::vector<OverlayPlane> &commit_planes) const {
612   ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
613   for (auto i = commit_planes.begin(); i != commit_planes.end(); i++) {
614     DrmPlane *plane = static_cast<DrmPlane *>(i->plane);
615     if (!(plane->UpdateProperties(pset.get(), crtc_id_, i->layer, true))) {
616       return false;
617     }
618   }
619
620   if (drmModeAtomicCommit(gpu_fd_, pset.get(), DRM_MODE_ATOMIC_TEST_ONLY,
621                           NULL)) {
622     IDISPLAYMANAGERTRACE("Test Commit Failed. %s ", PRINTERROR());
623     return false;
624   }
625
626   return true;
627 }
628
629 std::unique_ptr<DrmPlane> DrmDisplay::CreatePlane(uint32_t plane_id,
630                                                   uint32_t possible_crtcs) {
631   return std::unique_ptr<DrmPlane>(new DrmPlane(plane_id, possible_crtcs));
632 }
633
634 }  // namespace hwcomposer