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 ret = true;
203 } else {
204 ELOGTRACE("mode is invalid. Kernel mode setting is not completed");
205 ret = false;
206 }
207
208 if (outputIndex == OUTPUT_PRIMARY) {
209 if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) {
210 ELOGTRACE("failed to get device %d orientation", device);
211 output->panelOrientation = PANEL_ORIENTATION_0;
212 }
213 } else {
214 output->panelOrientation = PANEL_ORIENTATION_0;
215 }
216 break;
217 }
218
219 if (!ret) {
220 if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) {
221 // a fatal failure on primary device
222 // non fatal on secondary device
223 WLOGTRACE("device %d is disabled?", device);
224 ret = true;
225 }
226 resetOutput(outputIndex);
227 } else if (output->connected) {
228 ILOGTRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh);
229 }
230
231 drmModeFreeResources(resources);
232 return ret;
233 }
234
isSameDrmMode(drmModeModeInfoPtr value,drmModeModeInfoPtr base) const235 bool Drm::isSameDrmMode(drmModeModeInfoPtr value,
236 drmModeModeInfoPtr base) const
237 {
238 if (base->hdisplay == value->hdisplay &&
239 base->vdisplay == value->vdisplay &&
240 base->vrefresh == value->vrefresh &&
241 (base->flags & value->flags) == value->flags) {
242 VLOGTRACE("Drm mode is not changed");
243 return true;
244 }
245
246 return false;
247 }
248
setDrmMode(int device,drmModeModeInfo & value)249 bool Drm::setDrmMode(int device, drmModeModeInfo& value)
250 {
251 RETURN_FALSE_IF_NOT_INIT();
252 Mutex::Autolock _l(mLock);
253
254 if (device != IDisplayDevice::DEVICE_EXTERNAL) {
255 WLOGTRACE("Setting mode on invalid device %d", device);
256 return false;
257 }
258
259 int outputIndex = getOutputIndex(device);
260 if (outputIndex < 0 ) {
261 ELOGTRACE("invalid device");
262 return false;
263 }
264
265 DrmOutput *output= &mOutputs[outputIndex];
266 if (!output->connected) {
267 ELOGTRACE("device is not connected");
268 return false;
269 }
270
271 if (output->connector->count_modes <= 0) {
272 ELOGTRACE("invalid count of modes");
273 return false;
274 }
275
276 drmModeModeInfoPtr mode;
277 int index = 0;
278 for (int i = 0; i < output->connector->count_modes; i++) {
279 mode = &output->connector->modes[i];
280 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
281 index = i;
282 }
283 if (isSameDrmMode(&value, mode)) {
284 index = i;
285 break;
286 }
287 }
288
289 mode = &output->connector->modes[index];
290 return setDrmMode(outputIndex, mode);
291 }
292
setRefreshRate(int device,int hz)293 bool Drm::setRefreshRate(int device, int hz)
294 {
295 RETURN_FALSE_IF_NOT_INIT();
296 Mutex::Autolock _l(mLock);
297
298 if (device != IDisplayDevice::DEVICE_EXTERNAL) {
299 WLOGTRACE("Setting mode on invalid device %d", device);
300 return false;
301 }
302
303 int outputIndex = getOutputIndex(device);
304 if (outputIndex < 0 ) {
305 ELOGTRACE("invalid device");
306 return false;
307 }
308
309 DrmOutput *output= &mOutputs[outputIndex];
310 if (!output->connected) {
311 ELOGTRACE("device is not connected");
312 return false;
313 }
314
315 if (output->connector->count_modes <= 0) {
316 ELOGTRACE("invalid count of modes");
317 return false;
318 }
319
320 drmModeModeInfoPtr mode;
321 int index = 0;
322 for (int i = 0; i < output->connector->count_modes; i++) {
323 mode = &output->connector->modes[i];
324 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
325 index = i;
326 }
327 if (mode->hdisplay == output->mode.hdisplay &&
328 mode->vdisplay == output->mode.vdisplay &&
329 mode->vrefresh == (uint32_t)hz) {
330 index = i;
331 break;
332 }
333 }
334
335 mode = &output->connector->modes[index];
336 return setDrmMode(outputIndex, mode);
337 }
338
writeReadIoctl(unsigned long cmd,void * data,unsigned long size)339 bool Drm::writeReadIoctl(unsigned long cmd, void *data,
340 unsigned long size)
341 {
342 int err;
343
344 if (mDrmFd <= 0) {
345 ELOGTRACE("drm is not initialized");
346 return false;
347 }
348
349 if (!data || !size) {
350 ELOGTRACE("invalid parameters");
351 return false;
352 }
353
354 err = drmCommandWriteRead(mDrmFd, cmd, data, size);
355 if (err) {
356 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
357 return false;
358 }
359
360 return true;
361 }
362
writeIoctl(unsigned long cmd,void * data,unsigned long size)363 bool Drm::writeIoctl(unsigned long cmd, void *data,
364 unsigned long size)
365 {
366 int err;
367
368 if (mDrmFd <= 0) {
369 ELOGTRACE("drm is not initialized");
370 return false;
371 }
372
373 if (!data || !size) {
374 ELOGTRACE("invalid parameters");
375 return false;
376 }
377
378 err = drmCommandWrite(mDrmFd, cmd, data, size);
379 if (err) {
380 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
381 return false;
382 }
383
384 return true;
385 }
386
387
readIoctl(unsigned long cmd,void * data,unsigned long size)388 bool Drm::readIoctl(unsigned long cmd, void *data,
389 unsigned long size)
390 {
391 int err;
392
393 if (mDrmFd <= 0) {
394 ELOGTRACE("drm is not initialized");
395 return false;
396 }
397
398 if (!data || !size) {
399 ELOGTRACE("invalid parameters");
400 return false;
401 }
402
403 err = drmCommandRead(mDrmFd, cmd, data, size);
404 if (err) {
405 WLOGTRACE("failed to call %ld ioctl with failure %d", cmd, err);
406 return false;
407 }
408
409 return true;
410 }
411
412
getDrmFd() const413 int Drm::getDrmFd() const
414 {
415 return mDrmFd;
416 }
417
getModeInfo(int device,drmModeModeInfo & mode)418 bool Drm::getModeInfo(int device, drmModeModeInfo& mode)
419 {
420 Mutex::Autolock _l(mLock);
421
422 int outputIndex = getOutputIndex(device);
423 if (outputIndex < 0 ) {
424 return false;
425 }
426
427 DrmOutput *output= &mOutputs[outputIndex];
428 if (output->connected == false) {
429 ELOGTRACE("device is not connected");
430 return false;
431 }
432
433 if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) {
434 ELOGTRACE("invalid width or height");
435 return false;
436 }
437
438 memcpy(&mode, &output->mode, sizeof(drmModeModeInfo));
439
440 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
441 // FIXME: use default fb size instead of hdmi mode, because to
442 // support hdmi primary, we cannot report dynamic mode to SF.
443 mode.hdisplay = DEFAULT_DRM_FB_WIDTH;
444 mode.vdisplay = DEFAULT_DRM_FB_HEIGHT;
445 #endif
446
447 return true;
448 }
449
getPhysicalSize(int device,uint32_t & width,uint32_t & height)450 bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height)
451 {
452 Mutex::Autolock _l(mLock);
453
454 int outputIndex = getOutputIndex(device);
455 if (outputIndex < 0 ) {
456 return false;
457 }
458
459 DrmOutput *output= &mOutputs[outputIndex];
460 if (output->connected == false) {
461 ELOGTRACE("device is not connected");
462 return false;
463 }
464
465 width = output->connector->mmWidth;
466 height = output->connector->mmHeight;
467 return true;
468 }
469
getDisplayResolution(int device,uint32_t & width,uint32_t & height)470 bool Drm::getDisplayResolution(int device, uint32_t& width, uint32_t& height)
471 {
472 Mutex::Autolock _l(mLock);
473
474 int outputIndex = getOutputIndex(device);
475 if (outputIndex < 0) {
476 return false;
477 }
478
479 DrmOutput *output= &mOutputs[outputIndex];
480 if (output->connected == false) {
481 ELOGTRACE("device is not connected");
482 return false;
483 }
484
485 width = output->mode.hdisplay;
486 height = output->mode.vdisplay;
487
488 if (!width || !height) {
489 ELOGTRACE("invalid width or height");
490 return false;
491 }
492 return true;
493 }
494
isConnected(int device)495 bool Drm::isConnected(int device)
496 {
497 Mutex::Autolock _l(mLock);
498
499 int output = getOutputIndex(device);
500 if (output < 0 ) {
501 return false;
502 }
503
504 return mOutputs[output].connected;
505 }
506
setDpmsMode(int device,int mode)507 bool Drm::setDpmsMode(int device, int mode)
508 {
509 Mutex::Autolock _l(mLock);
510
511 int output = getOutputIndex(device);
512 if (output < 0 ) {
513 return false;
514 }
515
516 if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF &&
517 mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY &&
518 mode != IDisplayDevice::DEVICE_DISPLAY_ON) {
519 ELOGTRACE("invalid mode %d", mode);
520 return false;
521 }
522
523 DrmOutput *out = &mOutputs[output];
524 if (!out->connected) {
525 ELOGTRACE("device is not connected");
526 return false;
527 }
528
529 drmModePropertyPtr props;
530 for (int i = 0; i < out->connector->count_props; i++) {
531 props = drmModeGetProperty(mDrmFd, out->connector->props[i]);
532 if (!props) {
533 continue;
534 }
535
536 if (strcmp(props->name, "DPMS") == 0) {
537 int ret = drmModeConnectorSetProperty(
538 mDrmFd,
539 out->connector->connector_id,
540 props->prop_id,
541 (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON :
542 IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ?
543 DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF);
544 drmModeFreeProperty(props);
545 if (ret != 0) {
546 ELOGTRACE("unable to set DPMS %d", mode);
547 return false;
548 } else {
549 return true;
550 }
551 }
552 drmModeFreeProperty(props);
553 }
554 return false;
555 }
556
resetOutput(int index)557 void Drm::resetOutput(int index)
558 {
559 DrmOutput *output = &mOutputs[index];
560
561 output->connected = false;
562 memset(&output->mode, 0, sizeof(drmModeModeInfo));
563
564 if (output->connector) {
565 drmModeFreeConnector(output->connector);
566 output->connector = 0;
567 }
568 if (output->encoder) {
569 drmModeFreeEncoder(output->encoder);
570 output->encoder = 0;
571 }
572 if (output->crtc) {
573 drmModeFreeCrtc(output->crtc);
574 output->crtc = 0;
575 }
576 if (output->fbId) {
577 drmModeRmFB(mDrmFd, output->fbId);
578 output->fbId = 0;
579 }
580 if (output->fbHandle) {
581 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(output->fbHandle);
582 output->fbHandle = 0;
583 }
584 }
585
initDrmMode(int outputIndex)586 bool Drm::initDrmMode(int outputIndex)
587 {
588 DrmOutput *output= &mOutputs[outputIndex];
589 if (output->connector->count_modes <= 0) {
590 ELOGTRACE("invalid count of modes");
591 return false;
592 }
593
594 drmModeModeInfoPtr mode;
595 int index = 0;
596 for (int i = 0; i < output->connector->count_modes; i++) {
597 mode = &output->connector->modes[i];
598 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
599 index = i;
600 break;
601 }
602 }
603
604 return setDrmMode(outputIndex, &output->connector->modes[index]);
605 }
606
setDrmMode(int index,drmModeModeInfoPtr mode)607 bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode)
608 {
609 DrmOutput *output = &mOutputs[index];
610
611 int oldFbId =0;
612 int oldFbHandle = 0;
613
614 drmModeModeInfo currentMode;
615 memcpy(¤tMode, &output->mode, sizeof(drmModeModeInfo));
616
617 if (isSameDrmMode(mode, ¤tMode))
618 return true;
619
620
621 if (output->fbId) {
622 oldFbId = output->fbId ;
623 output->fbId = 0;
624 }
625
626 if (output->fbHandle) {
627 oldFbHandle = output->fbHandle;
628 output->fbHandle = 0;
629 }
630
631 // allocate frame buffer
632 int stride = 0;
633 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
634 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
635 DEFAULT_DRM_FB_WIDTH, DEFAULT_DRM_FB_HEIGHT, &stride);
636 #else
637 output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
638 mode->hdisplay, mode->vdisplay, &stride);
639 #endif
640 if (output->fbHandle == 0) {
641 ELOGTRACE("failed to allocate frame buffer");
642 return false;
643 }
644
645 int ret = 0;
646 ret = drmModeAddFB(
647 mDrmFd,
648 #ifdef INTEL_SUPPORT_HDMI_PRIMARY
649 DEFAULT_DRM_FB_WIDTH,
650 DEFAULT_DRM_FB_HEIGHT,
651 #else
652 mode->hdisplay,
653 mode->vdisplay,
654 #endif
655 DrmConfig::getFrameBufferDepth(),
656 DrmConfig::getFrameBufferBpp(),
657 stride,
658 output->fbHandle,
659 &output->fbId);
660 if (ret != 0) {
661 ELOGTRACE("drmModeAddFB failed, error: %d", ret);
662 return false;
663 }
664
665 ILOGTRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh);
666
667 ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, output->fbId, 0, 0,
668 &output->connector->connector_id, 1, mode);
669 if (ret == 0) {
670 //save mode
671 memcpy(&output->mode, mode, sizeof(drmModeModeInfo));
672 } else {
673 ELOGTRACE("drmModeSetCrtc failed. error: %d", ret);
674 }
675
676 if (oldFbId) {
677 drmModeRmFB(mDrmFd, oldFbId);
678 }
679
680 if (oldFbHandle) {
681 Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(oldFbHandle);
682 }
683
684 return ret == 0;
685 }
686
getOutputIndex(int device)687 int Drm::getOutputIndex(int device)
688 {
689 switch (device) {
690 case IDisplayDevice::DEVICE_PRIMARY:
691 return OUTPUT_PRIMARY;
692 case IDisplayDevice::DEVICE_EXTERNAL:
693 return OUTPUT_EXTERNAL;
694 default:
695 ELOGTRACE("invalid display device");
696 break;
697 }
698
699 return -1;
700 }
701
getPanelOrientation(int device)702 int Drm::getPanelOrientation(int device)
703 {
704 int outputIndex = getOutputIndex(device);
705 if (outputIndex < 0) {
706 ELOGTRACE("invalid device");
707 return PANEL_ORIENTATION_0;
708 }
709
710 DrmOutput *output= &mOutputs[outputIndex];
711 if (output->connected == false) {
712 ELOGTRACE("device is not connected");
713 return PANEL_ORIENTATION_0;
714 }
715
716 return output->panelOrientation;
717 }
718
719 // HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs
720 // this is needed so getActiveConfig/setActiveConfig work correctly. It is up to the
721 // user space to decide what speed to send.
detectAllConfigs(int device,int * modeCount)722 drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount)
723 {
724 RETURN_NULL_IF_NOT_INIT();
725 Mutex::Autolock _l(mLock);
726
727 if (modeCount != NULL)
728 *modeCount = 0;
729 else
730 return NULL;
731
732 int outputIndex = getOutputIndex(device);
733 if (outputIndex < 0) {
734 ELOGTRACE("invalid device");
735 return NULL;
736 }
737
738 DrmOutput *output= &mOutputs[outputIndex];
739 if (!output->connected) {
740 ELOGTRACE("device is not connected");
741 return NULL;
742 }
743
744 if (output->connector->count_modes <= 0) {
745 ELOGTRACE("invalid count of modes");
746 return NULL;
747 }
748
749 *modeCount = output->connector->count_modes;
750 return output->connector->modes;
751 }
752
753 } // namespace intel
754 } // namespace android
755
756