1 /*
2 // Copyright (c) 2014 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 #include <fcntl.h>
17 #include <errno.h>
18 #include <common/utils/HwcTrace.h>
19 #include <IDisplayDevice.h>
20 #include <DrmConfig.h>
21 #include <common/base/Drm.h>
22 #include <Hwcomposer.h>
23
24 namespace android {
25 namespace intel {
26
Drm()27 Drm::Drm()
28 : mDrmFd(0),
29 mLock(),
30 mInitialized(false)
31 {
32 memset(&mOutputs, 0, sizeof(mOutputs));
33 }
34
~Drm()35 Drm::~Drm()
36 {
37 WARN_IF_NOT_DEINIT();
38 }
39
initialize()40 bool Drm::initialize()
41 {
42 if (mInitialized) {
43 WLOGTRACE("Drm object has been initialized");
44 return true;
45 }
46
47 const char *path = DrmConfig::getDrmPath();
48 mDrmFd = open(path, O_RDWR, 0);
49 if (mDrmFd < 0) {
50 ELOGTRACE("failed to open Drm, error: %s", strerror(errno));
51 return false;
52 }
53 DLOGTRACE("mDrmFd = %d", mDrmFd);
54
55 memset(&mOutputs, 0, sizeof(mOutputs));
56 mInitialized = true;
57 return true;
58 }
59
deinitialize()60 void Drm::deinitialize()
61 {
62 for (int i = 0; i < OUTPUT_MAX; i++) {
63 resetOutput(i);
64 }
65
66 if (mDrmFd) {
67 close(mDrmFd);
68 mDrmFd = 0;
69 }
70 mInitialized = false;
71 }
72
detect(int device)73 bool Drm::detect(int device)
74 {
75 RETURN_FALSE_IF_NOT_INIT();
76
77 Mutex::Autolock _l(mLock);
78 int outputIndex = getOutputIndex(device);
79 if (outputIndex < 0 ) {
80 return false;
81 }
82
83 resetOutput(outputIndex);
84
85 // get drm resources
86 drmModeResPtr resources = drmModeGetResources(mDrmFd);
87 if (!resources) {
88 ELOGTRACE("fail to get drm resources, error: %s", strerror(errno));
89 return false;
90 }
91
92 drmModeConnectorPtr connector = NULL;
93 DrmOutput *output = &mOutputs[outputIndex];
94 bool ret = false;
95
96 // find connector for the given device
97 for (int i = 0; i < resources->count_connectors; i++) {
98 if (!resources->connectors || !resources->connectors[i]) {
99 ELOGTRACE("fail to get drm resources connectors, error: %s", strerror(errno));
100 continue;
101 }
102
103 connector = drmModeGetConnector(mDrmFd, resources->connectors[i]);
104 if (!connector) {
105 ELOGTRACE("drmModeGetConnector failed");
106 continue;
107 }
108
109 if (connector->connector_type != DrmConfig::getDrmConnector(device)) {
110 drmModeFreeConnector(connector);
111 continue;
112 }
113
114 if (connector->connection != DRM_MODE_CONNECTED) {
115 ILOGTRACE("device %d is not connected", device);
116 drmModeFreeConnector(connector);
117 ret = true;
118 break;
119 }
120
121 output->connector = connector;
122 output->connected = true;
123
124 // get proper encoder for the given connector
125 if (connector->encoder_id) {
126 ILOGTRACE("Drm connector has encoder attached on device %d", device);
127 output->encoder = drmModeGetEncoder(mDrmFd, connector->encoder_id);
128 if (!output->encoder) {
129 ELOGTRACE("failed to get encoder from a known encoder id");
130 // fall through to get an encoder
131 }
132 }
133 if (!output->encoder) {
134 ILOGTRACE("getting encoder for device %d", device);
135 drmModeEncoderPtr encoder;
136 for (int j = 0; j < resources->count_encoders; j++) {
137 if (!resources->encoders || !resources->encoders[j]) {
138 ELOGTRACE("fail to get drm resources encoders, error: %s", strerror(errno));
139 continue;
140 }
141
142 encoder = drmModeGetEncoder(mDrmFd, resources->encoders[i]);
143 if (!encoder) {
144 ELOGTRACE("drmModeGetEncoder failed");
145 continue;
146 }
147 if (encoder->encoder_type == DrmConfig::getDrmEncoder(device)) {
148 output->encoder = encoder;
149 break;
150 }
151 drmModeFreeEncoder(encoder);
152 encoder = NULL;
153 }
154 }
155 if (!output->encoder) {
156 ELOGTRACE("failed to get drm encoder");
157 break;
158 }
159
160 // get an attached crtc or spare crtc
161 if (output->encoder->crtc_id) {
162 ILOGTRACE("Drm encoder has crtc attached on device %d", device);
163 output->crtc = drmModeGetCrtc(mDrmFd, output->encoder->crtc_id);
164 if (!output->crtc) {
165 ELOGTRACE("failed to get crtc from a known crtc id");
166 // fall through to get a spare crtc
167 }
168 }
169 if (!output->crtc) {
170 ILOGTRACE("getting crtc for device %d", device);
171 drmModeCrtcPtr crtc;
172 for (int j = 0; j < resources->count_crtcs; j++) {
173 if (!resources->crtcs || !resources->crtcs[j]) {
174 ELOGTRACE("fail to get drm resources crtcs, error: %s", strerror(errno));
175 continue;
176 }
177
178 crtc = drmModeGetCrtc(mDrmFd, resources->crtcs[j]);
179 if (!crtc) {
180 ELOGTRACE("drmModeGetCrtc failed");
181 continue;
182 }
183 // check if legal crtc to the encoder
184 if (output->encoder->possible_crtcs & (1<<j)) {
185 if (crtc->buffer_id == 0) {
186 output->crtc = crtc;
187 break;
188 }
189 }
190 drmModeFreeCrtc(crtc);
191 }
192 }
193 if (!output->crtc) {
194 ELOGTRACE("failed to get drm crtc");
195 break;
196 }
197
198 // current mode
199 if (output->crtc->mode_valid) {
200 ILOGTRACE("mode is valid, kernel mode settings");
201 memcpy(&output->mode, &output->crtc->mode, sizeof(drmModeModeInfo));
202 //output->fbId = output->crtc->buffer_id;
203 ret = true;
204 } else {
205 ELOGTRACE("mode is invalid. Kernel mode setting is not completed");
206 ret = false;
207 }
208
209 if (outputIndex == OUTPUT_PRIMARY) {
210 if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) {
211 ELOGTRACE("failed to get device %d orientation", device);
212 output->panelOrientation = PANEL_ORIENTATION_0;
213 }
214 } else {
215 output->panelOrientation = PANEL_ORIENTATION_0;
216 }
217 break;
218 }
219
220 if (!ret) {
221 if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) {
222 // a fatal failure on primary device
223 // non fatal on secondary device
224 WLOGTRACE("device %d is disabled?", device);
225 ret = true;
226 }
227 resetOutput(outputIndex);
228 } else if (output->connected) {
229 ILOGTRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh);
230 }
231
232 drmModeFreeResources(resources);
233 return ret;
234 }
235
isSameDrmMode(drmModeModeInfoPtr value,drmModeModeInfoPtr base) const236 bool Drm::isSameDrmMode(drmModeModeInfoPtr value,
237 drmModeModeInfoPtr base) const
238 {
239 if (base->hdisplay == value->hdisplay &&
240 base->vdisplay == value->vdisplay &&
241 base->vrefresh == value->vrefresh &&
242 (base->flags & value->flags) == value->flags) {
243 VLOGTRACE("Drm mode is not changed");
244 return true;
245 }
246
247 return false;
248 }
249
setDrmMode(int device,drmModeModeInfo & value)250 bool Drm::setDrmMode(int device, drmModeModeInfo& value)
251 {
252 RETURN_FALSE_IF_NOT_INIT();
253 Mutex::Autolock _l(mLock);
254
255 if (device != IDisplayDevice::DEVICE_EXTERNAL) {
256 WLOGTRACE("Setting mode on invalid device %d", device);
257 return false;
258 }
259
260 int outputIndex = getOutputIndex(device);
261 if (outputIndex < 0 ) {
262 ELOGTRACE("invalid device");
263 return false;
264 }
265
266 DrmOutput *output= &mOutputs[outputIndex];
267 if (!output->connected) {
268 ELOGTRACE("device is not connected");
269 return false;
270 }
271
272 if (output->connector->count_modes <= 0) {
273 ELOGTRACE("invalid count of modes");
274 return false;
275 }
276
277 drmModeModeInfoPtr mode;
278 int index = 0;
279 for (int i = 0; i < output->connector->count_modes; i++) {
280 mode = &output->connector->modes[i];
281 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
282 index = i;
283 }
284 if (isSameDrmMode(&value, mode)) {
285 index = i;
286 break;
287 }
288 }
289
290 mode = &output->connector->modes[index];
291 return setDrmMode(outputIndex, mode);
292 }
293
setRefreshRate(int device,int hz)294 bool Drm::setRefreshRate(int device, int hz)
295 {
296 RETURN_FALSE_IF_NOT_INIT();
297 Mutex::Autolock _l(mLock);
298
299 if (device != IDisplayDevice::DEVICE_EXTERNAL) {
300 WLOGTRACE("Setting mode on invalid device %d", device);
301 return false;
302 }
303
304 int outputIndex = getOutputIndex(device);
305 if (outputIndex < 0 ) {
306 ELOGTRACE("invalid device");
307 return false;
308 }
309
310 DrmOutput *output= &mOutputs[outputIndex];
311 if (!output->connected) {
312 ELOGTRACE("device is not connected");
313 return false;
314 }
315
316 if (output->connector->count_modes <= 0) {
317 ELOGTRACE("invalid count of modes");
318 return false;
319 }
320
321 drmModeModeInfoPtr mode;
322 int index = 0;
323 for (int i = 0; i < output->connector->count_modes; i++) {
324 mode = &output->connector->modes[i];
325 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
326 index = i;
327 }
328 if (mode->hdisplay == output->mode.hdisplay &&
329 mode->vdisplay == output->mode.vdisplay &&
330 mode->vrefresh == (uint32_t)hz) {
331 index = i;
332 break;
333 }
334 }
335
336 mode = &output->connector->modes[index];
337 return setDrmMode(outputIndex, mode);
338 }
339
writeReadIoctl(unsigned long cmd,void * data,unsigned long size)340 bool Drm::writeReadIoctl(unsigned long cmd, void *data,
341 unsigned long size)
342 {
343 int err;
344
345 if (mDrmFd <= 0) {
346 ELOGTRACE("drm is not initialized");
347 return false;
348 }
349
350 if (!data || !size) {
351 ELOGTRACE("invalid parameters");
352 return false;
353 }
354
355 err = drmCommandWriteRead(mDrmFd, cmd, data, size);
356 if (err) {
357 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
358 return false;
359 }
360
361 return true;
362 }
363
writeIoctl(unsigned long cmd,void * data,unsigned long size)364 bool Drm::writeIoctl(unsigned long cmd, void *data,
365 unsigned long size)
366 {
367 int err;
368
369 if (mDrmFd <= 0) {
370 ELOGTRACE("drm is not initialized");
371 return false;
372 }
373
374 if (!data || !size) {
375 ELOGTRACE("invalid parameters");
376 return false;
377 }
378
379 err = drmCommandWrite(mDrmFd, cmd, data, size);
380 if (err) {
381 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
382 return false;
383 }
384
385 return true;
386 }
387
388
readIoctl(unsigned long cmd,void * data,unsigned long size)389 bool Drm::readIoctl(unsigned long cmd, void *data,
390 unsigned long size)
391 {
392 int err;
393
394 if (mDrmFd <= 0) {
395 ELOGTRACE("drm is not initialized");
396 return false;
397 }
398
399 if (!data || !size) {
400 ELOGTRACE("invalid parameters");
401 return false;
402 }
403
404 err = drmCommandRead(mDrmFd, cmd, data, size);
405 if (err) {
406 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
407 return false;
408 }
409
410 return true;
411 }
412
413
getDrmFd() const414 int Drm::getDrmFd() const
415 {
416 return mDrmFd;
417 }
418
getModeInfo(int device,drmModeModeInfo & mode)419 bool Drm::getModeInfo(int device, drmModeModeInfo& mode)
420 {
421 Mutex::Autolock _l(mLock);
422
423 int outputIndex = getOutputIndex(device);
424 if (outputIndex < 0 ) {
425 return false;
426 }
427
428 DrmOutput *output= &mOutputs[outputIndex];
429 if (output->connected == false) {
430 ELOGTRACE("device is not connected");
431 return false;
432 }
433
434 if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) {
435 ELOGTRACE("invalid width or height");
436 return false;
437 }
438
439 memcpy(&mode, &output->mode, sizeof(drmModeModeInfo));
440
441 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
442 // FIXME: use default fb size instead of hdmi mode, because to
443 // support hdmi primary, we cannot report dynamic mode to SF.
444 mode.hdisplay = DEFAULT_DRM_FB_WIDTH;
445 mode.vdisplay = DEFAULT_DRM_FB_HEIGHT;
446 #endif
447
448 return true;
449 }
450
getPhysicalSize(int device,uint32_t & width,uint32_t & height)451 bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height)
452 {
453 Mutex::Autolock _l(mLock);
454
455 int outputIndex = getOutputIndex(device);
456 if (outputIndex < 0 ) {
457 return false;
458 }
459
460 DrmOutput *output= &mOutputs[outputIndex];
461 if (output->connected == false) {
462 ELOGTRACE("device is not connected");
463 return false;
464 }
465
466 width = output->connector->mmWidth;
467 height = output->connector->mmHeight;
468 return true;
469 }
470
getDisplayResolution(int device,uint32_t & width,uint32_t & height)471 bool Drm::getDisplayResolution(int device, uint32_t& width, uint32_t& height)
472 {
473 Mutex::Autolock _l(mLock);
474
475 int outputIndex = getOutputIndex(device);
476 if (outputIndex < 0) {
477 return false;
478 }
479
480 DrmOutput *output= &mOutputs[outputIndex];
481 if (output->connected == false) {
482 ELOGTRACE("device is not connected");
483 return false;
484 }
485
486 width = output->mode.hdisplay;
487 height = output->mode.vdisplay;
488
489 if (!width || !height) {
490 ELOGTRACE("invalid width or height");
491 return false;
492 }
493 return true;
494 }
495
isConnected(int device)496 bool Drm::isConnected(int device)
497 {
498 Mutex::Autolock _l(mLock);
499
500 int output = getOutputIndex(device);
501 if (output < 0 ) {
502 return false;
503 }
504
505 return mOutputs[output].connected;
506 }
507
setDpmsMode(int device,int mode)508 bool Drm::setDpmsMode(int device, int mode)
509 {
510 Mutex::Autolock _l(mLock);
511
512 int output = getOutputIndex(device);
513 if (output < 0 ) {
514 return false;
515 }
516
517 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF &&
518 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY &&
519 mode != IDisplayDevice::DEVICE_DISPLAY_ON) {
520 ELOGTRACE("invalid mode %d", mode);
521 return false;
522 }
523
524 DrmOutput *out = &mOutputs[output];
525 if (!out->connected) {
526 ELOGTRACE("device is not connected");
527 return false;
528 }
529
530 drmModePropertyPtr props;
531 for (int i = 0; i < out->connector->count_props; i++) {
532 props = drmModeGetProperty(mDrmFd, out->connector->props[i]);
533 if (!props) {
534 continue;
535 }
536
537 if (strcmp(props->name, "DPMS") == 0) {
538 int ret = drmModeConnectorSetProperty(
539 mDrmFd,
540 out->connector->connector_id,
541 props->prop_id,
542 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON :
543 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ?
544 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF);
545 drmModeFreeProperty(props);
546 if (ret != 0) {
547 ELOGTRACE("unable to set DPMS %d", mode);
548 return false;
549 } else {
550 return true;
551 }
552 }
553 drmModeFreeProperty(props);
554 }
555 return false;
556 }
557
resetOutput(int index)558 void Drm::resetOutput(int index)
559 {
560 DrmOutput *output = &mOutputs[index];
561
562 output->connected = false;
563 memset(&output->mode, 0, sizeof(drmModeModeInfo));
564
565 if (output->connector) {
566 drmModeFreeConnector(output->connector);
567 output->connector = 0;
568 }
569 if (output->encoder) {
570 drmModeFreeEncoder(output->encoder);
571 output->encoder = 0;
572 }
573 if (output->crtc) {
574 drmModeFreeCrtc(output->crtc);
575 output->crtc = 0;
576 }
577 if (output->fbId) {
578 drmModeRmFB(mDrmFd, output->fbId);
579 output->fbId = 0;
580 }
581 if (output->fbHandle) {
582 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle);
583 output->fbHandle = 0;
584 }
585 }
586
initDrmMode(int outputIndex)587 bool Drm::initDrmMode(int outputIndex)
588 {
589 DrmOutput *output= &mOutputs[outputIndex];
590 if (output->connector->count_modes <= 0) {
591 ELOGTRACE("invalid count of modes");
592 return false;
593 }
594
595 drmModeModeInfoPtr mode;
596 int index = 0;
597 for (int i = 0; i < output->connector->count_modes; i++) {
598 mode = &output->connector->modes[i];
599 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
600 index = i;
601 break;
602 }
603 }
604
605 return setDrmMode(outputIndex, &output->connector->modes[index]);
606 }
607
setDrmMode(int index,drmModeModeInfoPtr mode)608 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode)
609 {
610 DrmOutput *output = &mOutputs[index];
611
612 int oldFbId = 0;
613 int oldFbHandle = 0;
614 // reuse current frame buffer if there is no resolution change
615 int fbId = -1;
616
617 drmModeModeInfo currentMode;
618 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo));
619
620 if (isSameDrmMode(mode, ¤tMode))
621 return true;
622
623 if (currentMode.hdisplay != mode->hdisplay ||
624 currentMode.vdisplay != mode->vdisplay) {
625
626 oldFbId = output->fbId;
627 oldFbHandle = output->fbHandle;
628
629 // allocate frame buffer
630 int stride = 0;
631 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
632 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
633 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride);
634 #else
635 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
636 mode->hdisplay, mode->vdisplay, &stride);
637 #endif
638 if (output->fbHandle == 0) {
639 ELOGTRACE("failed to allocate frame buffer");
640 return false;
641 }
642
643 int ret = 0;
644 ret = drmModeAddFB(
645 mDrmFd,
646 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
647 DEFAULT_DRM_FB_WIDTH,
648 DEFAULT_DRM_FB_HEIGHT,
649 #else
650 mode->hdisplay,
651 mode->vdisplay,
652 #endif
653 DrmConfig::getFrameBufferDepth(),
654 DrmConfig::getFrameBufferBpp(),
655 stride,
656 output->fbHandle,
657 &output->fbId);
658 if (ret != 0) {
659 ELOGTRACE("drmModeAddFB failed, error: %d", ret);
660 return false;
661 }
662 fbId = output->fbId;
663 }
664
665 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh);
666
667 int ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, fbId, 0, 0,
668 &output->connector->connector_id, 1, mode);
669
670 if (ret == 0) {
671 //save mode
672 memcpy(&output->mode, mode, sizeof(drmModeModeInfo));
673 } else {
674 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret);
675 }
676
677 if (oldFbId) {
678 drmModeRmFB(mDrmFd, oldFbId);
679 }
680
681 if (oldFbHandle) {
682 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle);
683 }
684
685 return ret == 0;
686 }
687
getOutputIndex(int device)688 int Drm::getOutputIndex(int device)
689 {
690 switch (device) {
691 case IDisplayDevice::DEVICE_PRIMARY:
692 return OUTPUT_PRIMARY;
693 case IDisplayDevice::DEVICE_EXTERNAL:
694 return OUTPUT_EXTERNAL;
695 default:
696 ELOGTRACE("invalid display device");
697 break;
698 }
699
700 return -1;
701 }
702
getPanelOrientation(int device)703 int Drm::getPanelOrientation(int device)
704 {
705 int outputIndex = getOutputIndex(device);
706 if (outputIndex < 0) {
707 ELOGTRACE("invalid device");
708 return PANEL_ORIENTATION_0;
709 }
710
711 DrmOutput *output= &mOutputs[outputIndex];
712 if (output->connected == false) {
713 ELOGTRACE("device is not connected");
714 return PANEL_ORIENTATION_0;
715 }
716
717 return output->panelOrientation;
718 }
719
720 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs
721 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the
722 // user space to decide what speed to send.
detectAllConfigs(int device,int * modeCount)723 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount)
724 {
725 RETURN_NULL_IF_NOT_INIT();
726 Mutex::Autolock _l(mLock);
727
728 if (modeCount != NULL)
729 *modeCount = 0;
730 else
731 return NULL;
732
733 int outputIndex = getOutputIndex(device);
734 if (outputIndex < 0) {
735 ELOGTRACE("invalid device");
736 return NULL;
737 }
738
739 DrmOutput *output= &mOutputs[outputIndex];
740 if (!output->connected) {
741 ELOGTRACE("device is not connected");
742 return NULL;
743 }
744
745 if (output->connector->count_modes <= 0) {
746 ELOGTRACE("invalid count of modes");
747 return NULL;
748 }
749
750 *modeCount = output->connector->count_modes;
751 return output->connector->modes;
752 }
753
754 } // namespace intel
755 } // namespace android
756
757