1 /*
2 * Copyright (C) 2015 The Android Open Source Project
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 #define LOG_TAG "hwc-drm-connector"
18
19 #include "drmconnector.h"
20 #include "drmdevice.h"
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdint.h>
25
26 #include <array>
27 #include <sstream>
28
29 #include <log/log.h>
30 #include <xf86drmMode.h>
31
32 #ifndef DRM_MODE_CONNECTOR_WRITEBACK
33 #define DRM_MODE_CONNECTOR_WRITEBACK 18
34 #endif
35
36 namespace android {
37
38 constexpr size_t TYPES_COUNT = 18;
39
DrmConnector(DrmDevice * drm,drmModeConnectorPtr c,DrmEncoder * current_encoder,std::vector<DrmEncoder * > & possible_encoders)40 DrmConnector::DrmConnector(DrmDevice *drm, drmModeConnectorPtr c,
41 DrmEncoder *current_encoder,
42 std::vector<DrmEncoder *> &possible_encoders)
43 : drm_(drm),
44 id_(c->connector_id),
45 encoder_(current_encoder),
46 display_(-1),
47 type_(c->connector_type),
48 type_id_(c->connector_type_id),
49 state_(c->connection),
50 mm_width_(c->mmWidth),
51 mm_height_(c->mmHeight),
52 possible_encoders_(possible_encoders) {
53 }
54
Init()55 int DrmConnector::Init() {
56 int ret = drm_->GetConnectorProperty(*this, "DPMS", &dpms_property_);
57 if (ret) {
58 ALOGE("Could not get DPMS property\n");
59 return ret;
60 }
61 ret = drm_->GetConnectorProperty(*this, "CRTC_ID", &crtc_id_property_);
62 if (ret) {
63 ALOGE("Could not get CRTC_ID property\n");
64 return ret;
65 }
66 ret = drm_->GetConnectorProperty(*this, "EDID", &edid_property_);
67 if (ret) {
68 ALOGW("Could not get EDID property\n");
69 }
70 if (writeback()) {
71 ret = drm_->GetConnectorProperty(*this, "WRITEBACK_PIXEL_FORMATS",
72 &writeback_pixel_formats_);
73 if (ret) {
74 ALOGE("Could not get WRITEBACK_PIXEL_FORMATS connector_id = %d\n", id_);
75 return ret;
76 }
77 ret = drm_->GetConnectorProperty(*this, "WRITEBACK_FB_ID",
78 &writeback_fb_id_);
79 if (ret) {
80 ALOGE("Could not get WRITEBACK_FB_ID connector_id = %d\n", id_);
81 return ret;
82 }
83 ret = drm_->GetConnectorProperty(*this, "WRITEBACK_OUT_FENCE_PTR",
84 &writeback_out_fence_);
85 if (ret) {
86 ALOGE("Could not get WRITEBACK_OUT_FENCE_PTR connector_id = %d\n", id_);
87 return ret;
88 }
89 }
90
91 ret = drm_->GetConnectorProperty(*this, "max_luminance", &max_luminance_);
92 if (ret) {
93 ALOGE("Could not get max_luminance property\n");
94 }
95
96 ret = drm_->GetConnectorProperty(*this, "max_avg_luminance", &max_avg_luminance_);
97 if (ret) {
98 ALOGE("Could not get max_avg_luminance property\n");
99 }
100
101 ret = drm_->GetConnectorProperty(*this, "min_luminance", &min_luminance_);
102 if (ret) {
103 ALOGE("Could not get min_luminance property\n");
104 }
105
106 ret = drm_->GetConnectorProperty(*this, "hdr_formats", &hdr_formats_);
107 if (ret) {
108 ALOGE("Could not get hdr_formats property\n");
109 }
110
111 ret = drm_->GetConnectorProperty(*this, "panel orientation", &orientation_);
112 if (ret) {
113 ALOGE("Could not get orientation property\n");
114 }
115
116 ret = drm_->GetConnectorProperty(*this, "lp_mode", &lp_mode_property_);
117 if (!ret) {
118 UpdateLpMode();
119 } else {
120 ALOGE("Could not get lp_mode property\n");
121 }
122
123 ret = drm_->GetConnectorProperty(*this, "brightness_capability", &brightness_cap_);
124 if (ret) {
125 ALOGE("Could not get brightness_capability property\n");
126 }
127
128 ret = drm_->GetConnectorProperty(*this, "brightness_level", &brightness_level_);
129 if (ret) {
130 ALOGE("Could not get brightness_level property\n");
131 }
132
133 ret = drm_->GetConnectorProperty(*this, "hbm_mode", &hbm_mode_);
134 if (ret) {
135 ALOGE("Could not get hbm_mode property\n");
136 }
137
138 ret = drm_->GetConnectorProperty(*this, "dimming_on", &dimming_on_);
139 if (ret) {
140 ALOGE("Could not get dimming_on property\n");
141 }
142
143 ret = drm_->GetConnectorProperty(*this, "local_hbm_mode", &lhbm_on_);
144 if (ret) {
145 ALOGE("Could not get local_hbm_mode property\n");
146 }
147
148 ret = drm_->GetConnectorProperty(*this, "mipi_sync", &mipi_sync_);
149 if (ret) {
150 ALOGE("Could not get mipi_sync property\n");
151 }
152
153 ret = drm_->GetConnectorProperty(*this, "panel_idle_support", &panel_idle_support_);
154 if (ret) {
155 ALOGE("Could not get panel_idle_support property\n");
156 }
157
158 ret = drm_->GetConnectorProperty(*this, "vrr_switch_duration", &vrr_switch_duration_);
159 if (ret) {
160 ALOGE("Could not get vrr_switch_duration property\n");
161 }
162
163 ret = drm_->GetConnectorProperty(*this, "operation_rate", &operation_rate_);
164 if (ret) {
165 ALOGE("Could not get operation_rate property\n");
166 }
167
168 ret = drm_->GetConnectorProperty(*this, "refresh_on_lp", &refresh_on_lp_);
169 if (ret) {
170 ALOGE("Could not get refresh_on_lp property\n");
171 }
172
173 properties_.push_back(&dpms_property_);
174 properties_.push_back(&crtc_id_property_);
175 properties_.push_back(&edid_property_);
176 if (writeback()) {
177 properties_.push_back(&writeback_pixel_formats_);
178 properties_.push_back(&writeback_fb_id_);
179 properties_.push_back(&writeback_out_fence_);
180 }
181 properties_.push_back(&max_luminance_);
182 properties_.push_back(&max_avg_luminance_);
183 properties_.push_back(&min_luminance_);
184 properties_.push_back(&hdr_formats_);
185 properties_.push_back(&orientation_);
186 properties_.push_back(&lp_mode_property_);
187 properties_.push_back(&brightness_cap_);
188 properties_.push_back(&brightness_level_);
189 properties_.push_back(&hbm_mode_);
190 properties_.push_back(&dimming_on_);
191 properties_.push_back(&lhbm_on_);
192 properties_.push_back(&mipi_sync_);
193 properties_.push_back(&panel_idle_support_);
194 properties_.push_back(&vrr_switch_duration_);
195 properties_.push_back(&operation_rate_);
196 properties_.push_back(&refresh_on_lp_);
197
198 return 0;
199 }
200
id() const201 uint32_t DrmConnector::id() const {
202 return id_;
203 }
204
display() const205 int DrmConnector::display() const {
206 return display_;
207 }
208
set_display(int display)209 void DrmConnector::set_display(int display) {
210 display_ = display;
211 }
212
internal() const213 bool DrmConnector::internal() const {
214 return type_ == DRM_MODE_CONNECTOR_LVDS || type_ == DRM_MODE_CONNECTOR_eDP ||
215 type_ == DRM_MODE_CONNECTOR_DSI ||
216 type_ == DRM_MODE_CONNECTOR_VIRTUAL || type_ == DRM_MODE_CONNECTOR_DPI;
217 }
218
external() const219 bool DrmConnector::external() const {
220 return type_ == DRM_MODE_CONNECTOR_HDMIA ||
221 type_ == DRM_MODE_CONNECTOR_DisplayPort ||
222 type_ == DRM_MODE_CONNECTOR_DVID || type_ == DRM_MODE_CONNECTOR_DVII ||
223 type_ == DRM_MODE_CONNECTOR_VGA;
224 }
225
writeback() const226 bool DrmConnector::writeback() const {
227 #ifdef DRM_MODE_CONNECTOR_WRITEBACK
228 return type_ == DRM_MODE_CONNECTOR_WRITEBACK;
229 #else
230 return false;
231 #endif
232 }
233
valid_type() const234 bool DrmConnector::valid_type() const {
235 return internal() || external() || writeback();
236 }
237
name() const238 std::string DrmConnector::name() const {
239 constexpr std::array<const char *, TYPES_COUNT> names =
240 {"None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite",
241 "SVIDEO", "LVDS", "Component", "DIN", "DP", "HDMI-A",
242 "HDMI-B", "TV", "eDP", "Virtual", "DSI", "DPI"};
243
244 if (type_ < TYPES_COUNT) {
245 std::ostringstream name_buf;
246 name_buf << names[type_] << "-" << type_id_;
247 return name_buf.str();
248 } else {
249 ALOGE("Unknown type in connector %d, could not make his name", id_);
250 return "None";
251 }
252 }
253
UpdateModes()254 int DrmConnector::UpdateModes() {
255 std::lock_guard<std::recursive_mutex> lock(modes_lock_);
256
257 int fd = drm_->fd();
258
259 drmModeConnectorPtr c = drmModeGetConnector(fd, id_);
260 if (!c) {
261 ALOGE("Failed to get connector %d", id_);
262 return -ENODEV;
263 }
264
265 if (state_ == DRM_MODE_CONNECTED &&
266 c->connection == DRM_MODE_CONNECTED && modes_.size() > 0) {
267 // no need to update modes
268 return 0;
269 }
270
271 if (state_ == DRM_MODE_DISCONNECTED &&
272 c->connection == DRM_MODE_DISCONNECTED && modes_.size() == 0) {
273 // no need to update modes
274 return 0;
275 }
276
277 state_ = c->connection;
278
279 bool preferred_mode_found = false;
280 std::vector<DrmMode> new_modes;
281 for (int i = 0; i < c->count_modes; ++i) {
282 bool exists = false;
283 for (const DrmMode &mode : modes_) {
284 if (mode == c->modes[i]) {
285 new_modes.push_back(mode);
286 exists = true;
287 break;
288 }
289 }
290 if (!exists) {
291 DrmMode m(&c->modes[i]);
292 m.set_id(drm_->next_mode_id());
293 new_modes.push_back(m);
294 }
295 // Use only the first DRM_MODE_TYPE_PREFERRED mode found
296 if (!preferred_mode_found &&
297 (new_modes.back().type() & DRM_MODE_TYPE_PREFERRED)) {
298 preferred_mode_id_ = new_modes.back().id();
299 preferred_mode_found = true;
300 }
301 }
302 modes_.swap(new_modes);
303 if (!preferred_mode_found && modes_.size() != 0) {
304 preferred_mode_id_ = modes_[0].id();
305 }
306 return 1;
307 }
308
UpdateEdidProperty()309 int DrmConnector::UpdateEdidProperty() {
310 return drm_->UpdateConnectorProperty(*this, &edid_property_);
311 }
312
active_mode() const313 const DrmMode &DrmConnector::active_mode() const {
314 return active_mode_;
315 }
316
set_active_mode(const DrmMode & mode)317 void DrmConnector::set_active_mode(const DrmMode &mode) {
318 active_mode_ = mode;
319 }
320
dpms_property() const321 const DrmProperty &DrmConnector::dpms_property() const {
322 return dpms_property_;
323 }
324
crtc_id_property() const325 const DrmProperty &DrmConnector::crtc_id_property() const {
326 return crtc_id_property_;
327 }
328
edid_property() const329 const DrmProperty &DrmConnector::edid_property() const {
330 return edid_property_;
331 }
332
writeback_pixel_formats() const333 const DrmProperty &DrmConnector::writeback_pixel_formats() const {
334 return writeback_pixel_formats_;
335 }
336
writeback_fb_id() const337 const DrmProperty &DrmConnector::writeback_fb_id() const {
338 return writeback_fb_id_;
339 }
340
writeback_out_fence() const341 const DrmProperty &DrmConnector::writeback_out_fence() const {
342 return writeback_out_fence_;
343 }
344
max_luminance() const345 const DrmProperty &DrmConnector::max_luminance() const {
346 return max_luminance_;
347 }
348
max_avg_luminance() const349 const DrmProperty &DrmConnector::max_avg_luminance() const {
350 return max_avg_luminance_;
351 }
352
min_luminance() const353 const DrmProperty &DrmConnector::min_luminance() const {
354 return min_luminance_;
355 }
356
brightness_cap() const357 const DrmProperty &DrmConnector::brightness_cap() const {
358 return brightness_cap_;
359 }
360
brightness_level() const361 const DrmProperty &DrmConnector::brightness_level() const {
362 return brightness_level_;
363 }
364
hbm_mode() const365 const DrmProperty &DrmConnector::hbm_mode() const {
366 return hbm_mode_;
367 }
368
dimming_on() const369 const DrmProperty &DrmConnector::dimming_on() const {
370 return dimming_on_;
371 }
372
lhbm_on() const373 const DrmProperty &DrmConnector::lhbm_on() const {
374 return lhbm_on_;
375 }
376
mipi_sync() const377 const DrmProperty &DrmConnector::mipi_sync() const {
378 return mipi_sync_;
379 }
380
hdr_formats() const381 const DrmProperty &DrmConnector::hdr_formats() const {
382 return hdr_formats_;
383 }
384
orientation() const385 const DrmProperty &DrmConnector::orientation() const {
386 return orientation_;
387 }
388
lp_mode() const389 const DrmMode &DrmConnector::lp_mode() const {
390 return lp_mode_;
391 }
392
operation_rate() const393 const DrmProperty &DrmConnector::operation_rate() const {
394 return operation_rate_;
395 }
396
refresh_on_lp() const397 const DrmProperty &DrmConnector::refresh_on_lp() const {
398 return refresh_on_lp_;
399 }
400
UpdateLpMode()401 int DrmConnector::UpdateLpMode() {
402 auto [ret, blobId] = lp_mode_property_.value();
403 if (ret) {
404 ALOGE("Fail to get blob id for lp mode");
405 return ret;
406 }
407 drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(drm_->fd(), blobId);
408 if (!blob) {
409 ALOGE("Fail to get blob for lp mode(%" PRId64 ")", blobId);
410 return -ENOENT;
411 }
412
413 auto modeInfoPtr = static_cast<drmModeModeInfoPtr>(blob->data);
414 lp_mode_ = DrmMode(modeInfoPtr);
415 drmModeFreePropertyBlob(blob);
416
417 ALOGD("Updating LP mode to: %s", lp_mode_.name().c_str());
418
419 return 0;
420 }
421
ResetLpMode()422 int DrmConnector::ResetLpMode() {
423 int ret = drm_->UpdateConnectorProperty(*this, &lp_mode_property_);
424
425 if (ret) {
426 return ret;
427 }
428
429 UpdateLpMode();
430
431 return 0;
432 }
433
panel_idle_support() const434 const DrmProperty &DrmConnector::panel_idle_support() const {
435 return panel_idle_support_;
436 }
437
vrr_switch_duration() const438 const DrmProperty &DrmConnector::vrr_switch_duration() const {
439 return vrr_switch_duration_;
440 }
441
encoder() const442 DrmEncoder *DrmConnector::encoder() const {
443 return encoder_;
444 }
445
set_encoder(DrmEncoder * encoder)446 void DrmConnector::set_encoder(DrmEncoder *encoder) {
447 encoder_ = encoder;
448 }
449
state() const450 drmModeConnection DrmConnector::state() const {
451 return state_;
452 }
453
mm_width() const454 uint32_t DrmConnector::mm_width() const {
455 return mm_width_;
456 }
457
mm_height() const458 uint32_t DrmConnector::mm_height() const {
459 return mm_height_;
460 }
461 } // namespace android
462