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 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
513 device = IDisplayDevice::DEVICE_EXTERNAL;
514 #endif
515
516 int output = getOutputIndex(device);
517 if (output < 0 ) {
518 return false;
519 }
520
521 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF &&
522 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY &&
523 mode != IDisplayDevice::DEVICE_DISPLAY_ON) {
524 ELOGTRACE("invalid mode %d", mode);
525 return false;
526 }
527
528 DrmOutput *out = &mOutputs[output];
529 if (!out->connected) {
530 ELOGTRACE("device is not connected");
531 return false;
532 }
533
534 drmModePropertyPtr props;
535 for (int i = 0; i < out->connector->count_props; i++) {
536 props = drmModeGetProperty(mDrmFd, out->connector->props[i]);
537 if (!props) {
538 continue;
539 }
540
541 if (strcmp(props->name, "DPMS") == 0) {
542 int ret = drmModeConnectorSetProperty(
543 mDrmFd,
544 out->connector->connector_id,
545 props->prop_id,
546 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON :
547 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ?
548 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF);
549 drmModeFreeProperty(props);
550 if (ret != 0) {
551 ELOGTRACE("unable to set DPMS %d", mode);
552 return false;
553 } else {
554 return true;
555 }
556 }
557 drmModeFreeProperty(props);
558 }
559 return false;
560 }
561
resetOutput(int index)562 void Drm::resetOutput(int index)
563 {
564 DrmOutput *output = &mOutputs[index];
565
566 output->connected = false;
567 memset(&output->mode, 0, sizeof(drmModeModeInfo));
568
569 if (output->connector) {
570 drmModeFreeConnector(output->connector);
571 output->connector = 0;
572 }
573 if (output->encoder) {
574 drmModeFreeEncoder(output->encoder);
575 output->encoder = 0;
576 }
577 if (output->crtc) {
578 drmModeFreeCrtc(output->crtc);
579 output->crtc = 0;
580 }
581 if (output->fbId) {
582 drmModeRmFB(mDrmFd, output->fbId);
583 output->fbId = 0;
584 }
585 if (output->fbHandle) {
586 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle);
587 output->fbHandle = 0;
588 }
589 }
590
initDrmMode(int outputIndex)591 bool Drm::initDrmMode(int outputIndex)
592 {
593 DrmOutput *output= &mOutputs[outputIndex];
594 if (output->connector->count_modes <= 0) {
595 ELOGTRACE("invalid count of modes");
596 return false;
597 }
598
599 drmModeModeInfoPtr mode;
600 int index = 0;
601 for (int i = 0; i < output->connector->count_modes; i++) {
602 mode = &output->connector->modes[i];
603 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
604 index = i;
605 break;
606 }
607 }
608
609 return setDrmMode(outputIndex, &output->connector->modes[index]);
610 }
611
setDrmMode(int index,drmModeModeInfoPtr mode)612 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode)
613 {
614 DrmOutput *output = &mOutputs[index];
615
616 int oldFbId = 0;
617 int oldFbHandle = 0;
618 // reuse current frame buffer if there is no resolution change
619 int fbId = -1;
620
621 drmModeModeInfo currentMode;
622 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo));
623
624 if (isSameDrmMode(mode, ¤tMode))
625 return true;
626
627 if (currentMode.hdisplay != mode->hdisplay ||
628 currentMode.vdisplay != mode->vdisplay) {
629
630 oldFbId = output->fbId;
631 oldFbHandle = output->fbHandle;
632
633 // allocate frame buffer
634 int stride = 0;
635 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
636 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
637 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride);
638 #else
639 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
640 mode->hdisplay, mode->vdisplay, &stride);
641 #endif
642 if (output->fbHandle == 0) {
643 ELOGTRACE("failed to allocate frame buffer");
644 return false;
645 }
646
647 int ret = 0;
648 ret = drmModeAddFB(
649 mDrmFd,
650 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
651 DEFAULT_DRM_FB_WIDTH,
652 DEFAULT_DRM_FB_HEIGHT,
653 #else
654 mode->hdisplay,
655 mode->vdisplay,
656 #endif
657 DrmConfig::getFrameBufferDepth(),
658 DrmConfig::getFrameBufferBpp(),
659 stride,
660 output->fbHandle,
661 &output->fbId);
662 if (ret != 0) {
663 ELOGTRACE("drmModeAddFB failed, error: %d", ret);
664 return false;
665 }
666 fbId = output->fbId;
667 }
668
669 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh);
670
671 int ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, fbId, 0, 0,
672 &output->connector->connector_id, 1, mode);
673
674 if (ret == 0) {
675 //save mode
676 memcpy(&output->mode, mode, sizeof(drmModeModeInfo));
677 } else {
678 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret);
679 }
680
681 if (oldFbId) {
682 drmModeRmFB(mDrmFd, oldFbId);
683 }
684
685 if (oldFbHandle) {
686 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle);
687 }
688
689 return ret == 0;
690 }
691
getOutputIndex(int device)692 int Drm::getOutputIndex(int device)
693 {
694 switch (device) {
695 case IDisplayDevice::DEVICE_PRIMARY:
696 return OUTPUT_PRIMARY;
697 case IDisplayDevice::DEVICE_EXTERNAL:
698 return OUTPUT_EXTERNAL;
699 default:
700 ELOGTRACE("invalid display device");
701 break;
702 }
703
704 return -1;
705 }
706
getPanelOrientation(int device)707 int Drm::getPanelOrientation(int device)
708 {
709 int outputIndex = getOutputIndex(device);
710 if (outputIndex < 0) {
711 ELOGTRACE("invalid device");
712 return PANEL_ORIENTATION_0;
713 }
714
715 DrmOutput *output= &mOutputs[outputIndex];
716 if (output->connected == false) {
717 ELOGTRACE("device is not connected");
718 return PANEL_ORIENTATION_0;
719 }
720
721 return output->panelOrientation;
722 }
723
724 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs
725 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the
726 // user space to decide what speed to send.
detectAllConfigs(int device,int * modeCount)727 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount)
728 {
729 RETURN_NULL_IF_NOT_INIT();
730 Mutex::Autolock _l(mLock);
731
732 if (modeCount != NULL)
733 *modeCount = 0;
734 else
735 return NULL;
736
737 int outputIndex = getOutputIndex(device);
738 if (outputIndex < 0) {
739 ELOGTRACE("invalid device");
740 return NULL;
741 }
742
743 DrmOutput *output= &mOutputs[outputIndex];
744 if (!output->connected) {
745 ELOGTRACE("device is not connected");
746 return NULL;
747 }
748
749 if (output->connector->count_modes <= 0) {
750 ELOGTRACE("invalid count of modes");
751 return NULL;
752 }
753
754 *modeCount = output->connector->count_modes;
755 return output->connector->modes;
756 }
757
758 } // namespace intel
759 } // namespace android
760
761