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(&currentMode, &output->mode, sizeof(drmModeModeInfo));
616 
617     if (isSameDrmMode(mode, &currentMode))
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