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 "drmdisplay.h"
30 #include "displayqueue.h"
31 #include "displayplanemanager.h"
33 namespace hwcomposer {
35 static const int32_t kUmPerInch = 25400;
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(¤t_mode_, 0, sizeof(current_mode_));
42 DrmDisplay::~DrmDisplay() {
44 drmModeDestroyPropertyBlob(gpu_fd_, blob_id_);
47 drmModeDestroyPropertyBlob(gpu_fd_, old_blob_id_);
49 display_queue_->SetPowerMode(kOff);
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_);
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("Display is already connected to this connector.");
70 PhysicalDisplay::Connect();
74 IHOTPLUGEVENTTRACE("Display is being connected to a new connector.");
75 connector_ = connector->connector_id;
76 mmWidth_ = connector->mmWidth;
77 mmHeight_ = connector->mmHeight;
78 SetDisplayAttribute(mode_info);
80 ScopedDrmObjectPropertyPtr connector_props(drmModeObjectGetProperties(
81 gpu_fd_, connector_, DRM_MODE_OBJECT_CONNECTOR));
82 if (!connector_props) {
83 ETRACE("Unable to get connector properties.");
87 // GetDrmObjectProperty("DPMS", connector_props, &dpms_prop_);
88 GetDrmObjectProperty("CRTC_ID", connector_props, &crtc_prop_);
89 GetDrmObjectProperty("Broadcast RGB", connector_props, &broadcastrgb_id_);
90 GetDrmObjectProperty("DPMS", connector_props, &dpms_prop_);
92 PhysicalDisplay::Connect();
94 drmModePropertyPtr broadcastrgb_props =
95 drmModeGetProperty(gpu_fd_, broadcastrgb_id_);
97 SetPowerMode(power_mode_);
99 // This is a valid case on DSI panels.
100 if (!broadcastrgb_props)
103 if (!(broadcastrgb_props->flags & DRM_MODE_PROP_ENUM))
106 for (int i = 0; i < broadcastrgb_props->count_enums; i++) {
107 if (!strcmp(broadcastrgb_props->enums[i].name, "Full")) {
108 broadcastrgb_full_ = broadcastrgb_props->enums[i].value;
109 } else if (!strcmp(broadcastrgb_props->enums[i].name, "Automatic")) {
110 broadcastrgb_automatic_ = broadcastrgb_props->enums[i].value;
114 drmModeFreeProperty(broadcastrgb_props);
119 bool DrmDisplay::GetDisplayAttribute(uint32_t config /*config*/,
120 HWCDisplayAttribute attribute,
122 display_lock_.lock();
126 case HWCDisplayAttribute::kWidth:
127 *value = modes_[config].hdisplay;
129 case HWCDisplayAttribute::kHeight:
130 *value = modes_[config].vdisplay;
132 case HWCDisplayAttribute::kRefreshRate:
133 refresh = (modes_[config].clock * 1000.0f) /
134 (modes_[config].htotal * modes_[config].vtotal);
136 if (modes_[config].flags & DRM_MODE_FLAG_INTERLACE)
139 if (modes_[config].flags & DRM_MODE_FLAG_DBLSCAN)
142 if (modes_[config].vscan > 1)
143 refresh /= modes_[config].vscan;
145 *value = 1e9 / refresh;
147 case HWCDisplayAttribute::kDpiX:
148 // Dots per 1000 inches
150 mmWidth_ ? (modes_[config].hdisplay * kUmPerInch) / mmWidth_ : -1;
152 case HWCDisplayAttribute::kDpiY:
153 // Dots per 1000 inches
155 mmHeight_ ? (modes_[config].vdisplay * kUmPerInch) / mmHeight_ : -1;
162 display_lock_.unlock();
166 bool DrmDisplay::GetDisplayConfigs(uint32_t *num_configs, uint32_t *configs) {
168 display_lock_.lock();
169 *num_configs = modes_.size();
170 display_lock_.unlock();
174 uint32_t size = *num_configs;
175 for (uint32_t i = 0; i < size; i++)
181 bool DrmDisplay::GetDisplayName(uint32_t *size, char *name) {
182 std::ostringstream stream;
183 stream << "Display-" << connector_;
184 std::string string = stream.str();
185 size_t length = string.length();
191 *size = std::min<uint32_t>(static_cast<uint32_t>(length - 1), *size);
192 strncpy(name, string.c_str(), *size);
196 void DrmDisplay::UpdateDisplayConfig() {
197 // update the activeConfig
198 display_lock_.lock();
199 flags_ |= DRM_MODE_ATOMIC_ALLOW_MODESET;
200 SetDisplayAttribute(modes_[config_]);
201 display_lock_.unlock();
204 void DrmDisplay::PowerOn() {
206 flags_ |= DRM_MODE_ATOMIC_ALLOW_MODESET;
207 drmModeConnectorSetProperty(gpu_fd_, connector_, dpms_prop_,
211 bool DrmDisplay::SetBroadcastRGB(const char *range_property) {
212 int64_t p_value = -1;
214 if (!strcmp(range_property, "Full")) {
215 p_value = broadcastrgb_full_;
216 } else if (!strcmp(range_property, "Automatic")) {
217 p_value = broadcastrgb_automatic_;
219 ETRACE("Wrong Broadcast RGB value %s", range_property);
226 if (drmModeObjectSetProperty(gpu_fd_, connector_, DRM_MODE_OBJECT_CONNECTOR,
227 broadcastrgb_id_, (uint64_t)p_value) != 0)
233 bool DrmDisplay::Commit(
234 const DisplayPlaneStateList &composition_planes,
235 const DisplayPlaneStateList &previous_composition_planes,
236 bool disable_explicit_fence, int32_t *commit_fence) {
237 // Do the actual commit.
238 ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
241 ETRACE("Failed to allocate property set %d", -ENOMEM);
245 if (display_state_ & kNeedsModeset) {
246 if (!ApplyPendingModeset(pset.get())) {
247 ETRACE("Failed to Modeset.");
250 } else if (!disable_explicit_fence && out_fence_ptr_prop_) {
251 GetFence(pset.get(), commit_fence);
254 if (!CommitFrame(composition_planes, previous_composition_planes, pset.get(),
256 ETRACE("Failed to Commit layers.");
260 if (display_state_ & kNeedsModeset) {
261 display_state_ &= ~kNeedsModeset;
262 if (!disable_explicit_fence) {
264 flags_ |= DRM_MODE_ATOMIC_NONBLOCK;
271 bool DrmDisplay::CommitFrame(
272 const DisplayPlaneStateList &comp_planes,
273 const DisplayPlaneStateList &previous_composition_planes,
274 drmModeAtomicReqPtr pset, uint32_t flags) {
277 ETRACE("Failed to allocate property set %d", -ENOMEM);
281 for (const DisplayPlaneState &comp_plane : previous_composition_planes) {
282 DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
283 plane->SetEnabled(false);
286 for (const DisplayPlaneState &comp_plane : comp_planes) {
287 DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
288 const OverlayLayer *layer = comp_plane.GetOverlayLayer();
289 int32_t fence = layer->GetAcquireFence();
291 plane->SetNativeFence(dup(fence));
293 plane->SetNativeFence(-1);
295 if (!plane->UpdateProperties(pset, crtc_id_, layer))
298 plane->SetEnabled(true);
301 for (const DisplayPlaneState &comp_plane : previous_composition_planes) {
302 DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
303 if (plane->IsEnabled())
306 plane->Disable(pset);
309 int ret = drmModeAtomicCommit(gpu_fd_, pset, flags, NULL);
311 ETRACE("Failed to commit pset ret=%s\n", PRINTERROR());
318 void DrmDisplay::SetDrmModeInfo(const std::vector<drmModeModeInfo> &mode_info) {
319 display_lock_.lock();
320 uint32_t size = mode_info.size();
321 for (uint32_t i = 0; i < size; ++i)
322 modes_.emplace_back(mode_info[i]);
324 display_lock_.unlock();
327 void DrmDisplay::SetDisplayAttribute(const drmModeModeInfo &mode_info) {
328 width_ = mode_info.hdisplay;
329 height_ = mode_info.vdisplay;
330 dpix_ = mmWidth_ ? (width_ * kUmPerInch) / mmWidth_ : -1;
331 dpiy_ = mmHeight_ ? (height_ * kUmPerInch) / mmHeight_ : -1;
332 current_mode_ = mode_info;
335 void DrmDisplay::GetDrmObjectProperty(const char *name,
336 const ScopedDrmObjectPropertyPtr &props,
337 uint32_t *id) const {
338 uint32_t count_props = props->count_props;
339 for (uint32_t i = 0; i < count_props; i++) {
340 ScopedDrmPropertyPtr property(drmModeGetProperty(gpu_fd_, props->props[i]));
341 if (property && !strcmp(property->name, name)) {
342 *id = property->prop_id;
347 ETRACE("Could not find property %s", name);
350 void DrmDisplay::GetDrmObjectPropertyValue(
351 const char *name, const ScopedDrmObjectPropertyPtr &props,
352 uint64_t *value) const {
353 uint32_t count_props = props->count_props;
354 for (uint32_t i = 0; i < count_props; i++) {
355 ScopedDrmPropertyPtr property(drmModeGetProperty(gpu_fd_, props->props[i]));
356 if (property && !strcmp(property->name, name)) {
357 *value = props->prop_values[i];
362 ETRACE("Could not find property value %s", name);
365 void DrmDisplay::ApplyPendingLUT(struct drm_color_lut *lut) const {
366 if (lut_id_prop_ == 0)
369 uint32_t lut_blob_id = 0;
371 drmModeCreatePropertyBlob(
372 gpu_fd_, lut, sizeof(struct drm_color_lut) * lut_size_, &lut_blob_id);
373 if (lut_blob_id == 0) {
377 drmModeObjectSetProperty(gpu_fd_, crtc_id_, DRM_MODE_OBJECT_CRTC,
378 lut_id_prop_, lut_blob_id);
379 drmModeDestroyPropertyBlob(gpu_fd_, lut_blob_id);
382 float DrmDisplay::TransformContrastBrightness(float value, float brightness,
383 float contrast) const {
385 result = (value - 0.5) * contrast + 0.5 + brightness;
394 float DrmDisplay::TransformGamma(float value, float gamma) const {
397 result = pow(value, gamma);
406 void DrmDisplay::SetColorCorrection(struct gamma_colors gamma,
408 uint32_t brightness_c) const {
409 struct drm_color_lut *lut;
414 /* reset lut when contrast and brightness are all 0 */
415 if (contrast_c == 0 && brightness_c == 0) {
417 ApplyPendingLUT(lut);
423 (struct drm_color_lut *)malloc(sizeof(struct drm_color_lut) * lut_size_);
425 ETRACE("Cannot allocate LUT memory");
429 /* Unpack brightness values for each channel */
430 temp[0] = (brightness_c >> 16) & 0xFF;
431 temp[1] = (brightness_c >> 8) & 0xFF;
432 temp[2] = (brightness_c)&0xFF;
434 /* Map brightness from -128 - 127 range into -0.5 - 0.5 range */
435 brightness[0] = (float)(temp[0]) / 255 - 0.5;
436 brightness[1] = (float)(temp[1]) / 255 - 0.5;
437 brightness[2] = (float)(temp[2]) / 255 - 0.5;
439 /* Unpack contrast values for each channel */
440 temp[0] = (contrast_c >> 16) & 0xFF;
441 temp[1] = (contrast_c >> 8) & 0xFF;
442 temp[2] = (contrast_c)&0xFF;
444 /* Map contrast from 0 - 255 range into 0.0 - 2.0 range */
445 contrast[0] = (float)(temp[0]) / 128;
446 contrast[1] = (float)(temp[1]) / 128;
447 contrast[2] = (float)(temp[2]) / 128;
449 for (uint64_t i = 0; i < lut_size_; i++) {
450 /* Set lut[0] as 0 always as the darkest color should has brightness 0 */
458 lut[i].red = 0xFFFF * TransformGamma(TransformContrastBrightness(
459 (float)(i) / lut_size_,
460 brightness[0], contrast[0]),
462 lut[i].green = 0xFFFF * TransformGamma(TransformContrastBrightness(
463 (float)(i) / lut_size_,
464 brightness[1], contrast[1]),
466 lut[i].blue = 0xFFFF * TransformGamma(TransformContrastBrightness(
467 (float)(i) / lut_size_,
468 brightness[2], contrast[2]),
472 ApplyPendingLUT(lut);
476 bool DrmDisplay::ApplyPendingModeset(drmModeAtomicReqPtr property_set) {
478 drmModeDestroyPropertyBlob(gpu_fd_, old_blob_id_);
482 drmModeCreatePropertyBlob(gpu_fd_, ¤t_mode_, sizeof(drmModeModeInfo),
489 int ret = drmModeAtomicAddProperty(property_set, crtc_id_, mode_id_prop_,
491 drmModeAtomicAddProperty(property_set, connector_, crtc_prop_,
493 drmModeAtomicAddProperty(property_set, crtc_id_, active_prop_,
496 ETRACE("Failed to add blob %d to pset", blob_id_);
500 old_blob_id_ = blob_id_;
506 bool DrmDisplay::GetFence(drmModeAtomicReqPtr property_set,
507 int32_t *out_fence) {
508 int ret = drmModeAtomicAddProperty(property_set, crtc_id_,
509 out_fence_ptr_prop_, (uintptr_t)out_fence);
511 ETRACE("Failed to add OUT_FENCE_PTR property to pset: %d", ret);
518 void DrmDisplay::Disable(const DisplayPlaneStateList &composition_planes) {
519 ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
522 int ret = drmModeAtomicAddProperty(pset.get(), crtc_id_, active_prop_,
525 ETRACE("Failed to set display to inactive");
528 for (const DisplayPlaneState &comp_plane : composition_planes) {
529 DrmPlane *plane = static_cast<DrmPlane *>(comp_plane.plane());
530 plane->SetEnabled(false);
531 plane->Disable(pset.get());
534 ret = drmModeAtomicCommit(gpu_fd_, pset.get(),
535 DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
537 ETRACE("Failed to disable display:%s\n", PRINTERROR());
539 ETRACE("Failed to allocate property set %d", -ENOMEM);
542 drmModeConnectorSetProperty(gpu_fd_, connector_, dpms_prop_,
546 bool DrmDisplay::PopulatePlanes(
547 std::unique_ptr<DisplayPlane> &primary_plane,
548 std::unique_ptr<DisplayPlane> &cursor_plane,
549 std::vector<std::unique_ptr<DisplayPlane>> &overlay_planes) {
550 ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(gpu_fd_));
551 if (!plane_resources) {
552 ETRACE("Failed to get plane resources");
556 uint32_t num_planes = plane_resources->count_planes;
557 uint32_t pipe_bit = 1 << pipe_;
558 std::set<uint32_t> plane_ids;
559 for (uint32_t i = 0; i < num_planes; ++i) {
560 ScopedDrmPlanePtr drm_plane(
561 drmModeGetPlane(gpu_fd_, plane_resources->planes[i]));
563 ETRACE("Failed to get plane ");
567 if (!(pipe_bit & drm_plane->possible_crtcs))
570 uint32_t formats_size = drm_plane->count_formats;
571 plane_ids.insert(drm_plane->plane_id);
572 std::unique_ptr<DrmPlane> plane(
573 CreatePlane(drm_plane->plane_id, drm_plane->possible_crtcs));
574 std::vector<uint32_t> supported_formats(formats_size);
575 for (uint32_t j = 0; j < formats_size; j++)
576 supported_formats[j] = drm_plane->formats[j];
578 if (plane->Initialize(gpu_fd_, supported_formats)) {
579 if (plane->type() == DRM_PLANE_TYPE_CURSOR) {
580 cursor_plane.reset(plane.release());
581 } else if (plane->type() == DRM_PLANE_TYPE_PRIMARY) {
582 plane->SetEnabled(true);
583 primary_plane.reset(plane.release());
584 } else if (plane->type() == DRM_PLANE_TYPE_OVERLAY) {
585 overlay_planes.emplace_back(plane.release());
590 if (!primary_plane) {
591 ETRACE("Failed to get primary plane for display %d", crtc_id_);
595 // We expect layers to be in ascending order.
597 overlay_planes.begin(), overlay_planes.end(),
598 [](const std::unique_ptr<DisplayPlane> &l,
599 const std::unique_ptr<DisplayPlane> &r) { return l->id() < r->id(); });
604 bool DrmDisplay::TestCommit(
605 const std::vector<OverlayPlane> &commit_planes) const {
606 ScopedDrmAtomicReqPtr pset(drmModeAtomicAlloc());
607 for (auto i = commit_planes.begin(); i != commit_planes.end(); i++) {
608 DrmPlane *plane = static_cast<DrmPlane *>(i->plane);
609 if (!(plane->UpdateProperties(pset.get(), crtc_id_, i->layer, true))) {
614 if (drmModeAtomicCommit(gpu_fd_, pset.get(), DRM_MODE_ATOMIC_TEST_ONLY,
616 IDISPLAYMANAGERTRACE("Test Commit Failed. %s ", PRINTERROR());
623 std::unique_ptr<DrmPlane> DrmDisplay::CreatePlane(uint32_t plane_id,
624 uint32_t possible_crtcs) {
625 return std::unique_ptr<DrmPlane>(new DrmPlane(plane_id, possible_crtcs));
628 } // namespace hwcomposer