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 <HwcTrace.h>
19#include <IDisplayDevice.h>
20#include <DrmConfig.h>
21#include <Drm.h>
22#include <Hwcomposer.h>
23
24namespace android {
25namespace intel {
26
27Drm::Drm()
28    : mDrmFd(0),
29      mLock(),
30      mInitialized(false)
31{
32    memset(&mOutputs, 0, sizeof(mOutputs));
33}
34
35Drm::~Drm()
36{
37    WARN_IF_NOT_DEINIT();
38}
39
40bool Drm::initialize()
41{
42    if (mInitialized) {
43        WTRACE("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        ETRACE("failed to open Drm, error: %s", strerror(errno));
51        return false;
52    }
53    DTRACE("mDrmFd = %d", mDrmFd);
54
55    memset(&mOutputs, 0, sizeof(mOutputs));
56    mInitialized = true;
57    return true;
58}
59
60void 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
73bool 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        ETRACE("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            ETRACE("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            ETRACE("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            ITRACE("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            ITRACE("Drm connector has encoder attached on device %d", device);
127            output->encoder = drmModeGetEncoder(mDrmFd, connector->encoder_id);
128            if (!output->encoder) {
129                ETRACE("failed to get encoder from a known encoder id");
130                // fall through to get an encoder
131            }
132        }
133        if (!output->encoder) {
134            ITRACE("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                    ETRACE("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                    ETRACE("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            ETRACE("failed to get drm encoder");
157            break;
158        }
159
160        // get an attached crtc or spare crtc
161        if (output->encoder->crtc_id) {
162            ITRACE("Drm encoder has crtc attached on device %d", device);
163            output->crtc = drmModeGetCrtc(mDrmFd, output->encoder->crtc_id);
164            if (!output->crtc) {
165                ETRACE("failed to get crtc from a known crtc id");
166                // fall through to get a spare crtc
167            }
168        }
169        if (!output->crtc) {
170            ITRACE("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                    ETRACE("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                    ETRACE("drmModeGetCrtc failed");
181                    continue;
182                }
183                if (crtc->buffer_id == 0) {
184                    output->crtc = crtc;
185                    break;
186                }
187                drmModeFreeCrtc(crtc);
188            }
189        }
190        if (!output->crtc) {
191            ETRACE("failed to get drm crtc");
192            break;
193        }
194
195        // current mode
196        if (output->crtc->mode_valid) {
197            ITRACE("mode is valid, kernel mode settings");
198            memcpy(&output->mode, &output->crtc->mode, sizeof(drmModeModeInfo));
199            ret = true;
200        } else {
201            ITRACE("mode is invalid, setting preferred mode");
202            ret = initDrmMode(outputIndex);
203        }
204
205        if (outputIndex == OUTPUT_PRIMARY) {
206            if (!readIoctl(DRM_PSB_PANEL_ORIENTATION, &output->panelOrientation, sizeof(int))) {
207                ETRACE("failed to get device %d orientation", device);
208                output->panelOrientation = PANEL_ORIENTATION_0;
209            }
210        } else {
211            output->panelOrientation = PANEL_ORIENTATION_0;
212        }
213        break;
214    }
215
216    if (!ret) {
217        if (output->connector == NULL && outputIndex != OUTPUT_PRIMARY) {
218            // a fatal failure on primary device
219            // non fatal on secondary device
220            WTRACE("device %d is disabled?", device);
221            ret = true;
222        }
223         resetOutput(outputIndex);
224    } else if (output->connected) {
225        ITRACE("mode is: %dx%d@%dHz", output->mode.hdisplay, output->mode.vdisplay, output->mode.vrefresh);
226    }
227
228    drmModeFreeResources(resources);
229    return ret;
230}
231
232bool Drm::isSameDrmMode(drmModeModeInfoPtr value,
233        drmModeModeInfoPtr base) const
234{
235    if (base->hdisplay == value->hdisplay &&
236        base->vdisplay == value->vdisplay &&
237        base->vrefresh == value->vrefresh &&
238        (base->flags & value->flags) == value->flags) {
239        VTRACE("Drm mode is not changed");
240        return true;
241    }
242
243    return false;
244}
245
246bool Drm::setDrmMode(int device, drmModeModeInfo& value)
247{
248    RETURN_FALSE_IF_NOT_INIT();
249    Mutex::Autolock _l(mLock);
250
251    if (device != IDisplayDevice::DEVICE_EXTERNAL) {
252        WTRACE("Setting mode on invalid device %d", device);
253        return false;
254    }
255
256    int outputIndex = getOutputIndex(device);
257    if (outputIndex < 0 ) {
258        ETRACE("invalid device");
259        return false;
260    }
261
262    DrmOutput *output= &mOutputs[outputIndex];
263    if (!output->connected) {
264        ETRACE("device is not connected");
265        return false;
266    }
267
268    if (output->connector->count_modes <= 0) {
269        ETRACE("invalid count of modes");
270        return false;
271    }
272
273    drmModeModeInfoPtr mode;
274    int index = 0;
275    for (int i = 0; i < output->connector->count_modes; i++) {
276        mode = &output->connector->modes[i];
277        if (mode->type & DRM_MODE_TYPE_PREFERRED) {
278            index = i;
279        }
280        if (isSameDrmMode(&value, mode)) {
281            index = i;
282            break;
283        }
284    }
285
286    mode = &output->connector->modes[index];
287    return setDrmMode(outputIndex, mode);
288}
289
290bool Drm::setRefreshRate(int device, int hz)
291{
292    RETURN_FALSE_IF_NOT_INIT();
293    Mutex::Autolock _l(mLock);
294
295    if (device != IDisplayDevice::DEVICE_EXTERNAL) {
296        WTRACE("Setting mode on invalid device %d", device);
297        return false;
298    }
299
300    int outputIndex = getOutputIndex(device);
301    if (outputIndex < 0 ) {
302        ETRACE("invalid device");
303        return false;
304    }
305
306    DrmOutput *output= &mOutputs[outputIndex];
307    if (!output->connected) {
308        ETRACE("device is not connected");
309        return false;
310    }
311
312    if (output->connector->count_modes <= 0) {
313        ETRACE("invalid count of modes");
314        return false;
315    }
316
317    drmModeModeInfoPtr mode;
318    int index = 0;
319    for (int i = 0; i < output->connector->count_modes; i++) {
320        mode = &output->connector->modes[i];
321        if (mode->type & DRM_MODE_TYPE_PREFERRED) {
322            index = i;
323        }
324        if (mode->hdisplay == output->mode.hdisplay &&
325            mode->vdisplay == output->mode.vdisplay &&
326            mode->vrefresh == (uint32_t)hz) {
327            index = i;
328            break;
329        }
330    }
331
332    mode = &output->connector->modes[index];
333    return setDrmMode(outputIndex, mode);
334}
335
336bool Drm::writeReadIoctl(unsigned long cmd, void *data,
337                           unsigned long size)
338{
339    int err;
340
341    if (mDrmFd <= 0) {
342        ETRACE("drm is not initialized");
343        return false;
344    }
345
346    if (!data || !size) {
347        ETRACE("invalid parameters");
348        return false;
349    }
350
351    err = drmCommandWriteRead(mDrmFd, cmd, data, size);
352    if (err) {
353        WTRACE("failed to call %ld ioctl with failure %d", cmd, err);
354        return false;
355    }
356
357    return true;
358}
359
360bool Drm::writeIoctl(unsigned long cmd, void *data,
361                       unsigned long size)
362{
363    int err;
364
365    if (mDrmFd <= 0) {
366        ETRACE("drm is not initialized");
367        return false;
368    }
369
370    if (!data || !size) {
371        ETRACE("invalid parameters");
372        return false;
373    }
374
375    err = drmCommandWrite(mDrmFd, cmd, data, size);
376    if (err) {
377        WTRACE("failed to call %ld ioctl with failure %d", cmd, err);
378        return false;
379    }
380
381    return true;
382}
383
384
385bool Drm::readIoctl(unsigned long cmd, void *data,
386                       unsigned long size)
387{
388    int err;
389
390    if (mDrmFd <= 0) {
391        ETRACE("drm is not initialized");
392        return false;
393    }
394
395    if (!data || !size) {
396        ETRACE("invalid parameters");
397        return false;
398    }
399
400    err = drmCommandRead(mDrmFd, cmd, data, size);
401    if (err) {
402        WTRACE("failed to call %ld ioctl with failure %d", cmd, err);
403        return false;
404    }
405
406    return true;
407}
408
409
410int Drm::getDrmFd() const
411{
412    return mDrmFd;
413}
414
415bool Drm::getModeInfo(int device, drmModeModeInfo& mode)
416{
417    Mutex::Autolock _l(mLock);
418
419    int outputIndex = getOutputIndex(device);
420    if (outputIndex < 0 ) {
421        return false;
422    }
423
424    DrmOutput *output= &mOutputs[outputIndex];
425    if (output->connected == false) {
426        ETRACE("device is not connected");
427        return false;
428    }
429
430    if (output->mode.hdisplay == 0 || output->mode.vdisplay == 0) {
431        ETRACE("invalid width or height");
432        return false;
433    }
434
435    memcpy(&mode, &output->mode, sizeof(drmModeModeInfo));
436    return true;
437}
438
439bool Drm::getPhysicalSize(int device, uint32_t& width, uint32_t& height)
440{
441    Mutex::Autolock _l(mLock);
442
443    int outputIndex = getOutputIndex(device);
444    if (outputIndex < 0 ) {
445        return false;
446    }
447
448    DrmOutput *output= &mOutputs[outputIndex];
449    if (output->connected == false) {
450        ETRACE("device is not connected");
451        return false;
452    }
453
454    width = output->connector->mmWidth;
455    height = output->connector->mmHeight;
456    return true;
457}
458
459bool Drm::isConnected(int device)
460{
461    Mutex::Autolock _l(mLock);
462
463    int output = getOutputIndex(device);
464    if (output < 0 ) {
465        return false;
466    }
467
468    return mOutputs[output].connected;
469}
470
471bool Drm::setDpmsMode(int device, int mode)
472{
473    Mutex::Autolock _l(mLock);
474
475    int output = getOutputIndex(device);
476    if (output < 0 ) {
477        return false;
478    }
479
480    if (mode != IDisplayDevice::DEVICE_DISPLAY_OFF &&
481            mode != IDisplayDevice::DEVICE_DISPLAY_STANDBY &&
482            mode != IDisplayDevice::DEVICE_DISPLAY_ON) {
483        ETRACE("invalid mode %d", mode);
484        return false;
485    }
486
487    DrmOutput *out = &mOutputs[output];
488    if (!out->connected) {
489        ETRACE("device is not connected");
490        return false;
491    }
492
493    drmModePropertyPtr props;
494    for (int i = 0; i < out->connector->count_props; i++) {
495        props = drmModeGetProperty(mDrmFd, out->connector->props[i]);
496        if (!props) {
497            continue;
498        }
499
500        if (strcmp(props->name, "DPMS") == 0) {
501            int ret = drmModeConnectorSetProperty(
502                mDrmFd,
503                out->connector->connector_id,
504                props->prop_id,
505                (mode == IDisplayDevice::DEVICE_DISPLAY_ON) ? DRM_MODE_DPMS_ON :
506                        IDisplayDevice::DEVICE_DISPLAY_STANDBY == mode ?
507                        DRM_MODE_DPMS_STANDBY : DRM_MODE_DPMS_OFF);
508            drmModeFreeProperty(props);
509            if (ret != 0) {
510                ETRACE("unable to set DPMS %d", mode);
511                return false;
512            } else {
513                return true;
514            }
515        }
516        drmModeFreeProperty(props);
517    }
518    return false;
519}
520
521void Drm::resetOutput(int index)
522{
523    DrmOutput *output = &mOutputs[index];
524
525    output->connected = false;
526    memset(&output->mode, 0, sizeof(drmModeModeInfo));
527
528    if (output->connector) {
529        drmModeFreeConnector(output->connector);
530        output->connector = 0;
531    }
532    if (output->encoder) {
533        drmModeFreeEncoder(output->encoder);
534        output->encoder = 0;
535    }
536    if (output->crtc) {
537        drmModeFreeCrtc(output->crtc);
538        output->crtc = 0;
539    }
540    if (output->fbId) {
541        drmModeRmFB(mDrmFd, output->fbId);
542        output->fbId = 0;
543    }
544    if (output->fbHandle) {
545        Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer(
546            (buffer_handle_t)output->fbHandle);
547        output->fbHandle = 0;
548    }
549}
550
551bool Drm::initDrmMode(int outputIndex)
552{
553    DrmOutput *output= &mOutputs[outputIndex];
554    if (output->connector->count_modes <= 0) {
555        ETRACE("invalid count of modes");
556        return false;
557    }
558
559    drmModeModeInfoPtr mode;
560    int index = 0;
561    for (int i = 0; i < output->connector->count_modes; i++) {
562        mode = &output->connector->modes[i];
563        if (mode->type & DRM_MODE_TYPE_PREFERRED) {
564            index = i;
565            break;
566        }
567    }
568
569    return setDrmMode(outputIndex, &output->connector->modes[index]);
570}
571
572bool Drm::setDrmMode(int index, drmModeModeInfoPtr mode)
573{
574    DrmOutput *output = &mOutputs[index];
575
576    int oldFbId =0;
577    buffer_handle_t oldFbHandle = 0;
578
579    drmModeModeInfo currentMode;
580    memcpy(&currentMode, &output->mode, sizeof(drmModeModeInfo));
581
582    if (isSameDrmMode(mode, &currentMode))
583        return true;
584
585
586    if (output->fbId) {
587        oldFbId = output->fbId;
588        output->fbId = 0;
589    }
590
591    if (output->fbHandle) {
592        oldFbHandle = output->fbHandle;
593        output->fbHandle = 0;
594    }
595
596    // allocate frame buffer
597    int stride = 0;
598    output->fbHandle = Hwcomposer::getInstance().getBufferManager()->allocFrameBuffer(
599        mode->hdisplay, mode->vdisplay, &stride);
600    if (output->fbHandle == 0) {
601        ETRACE("failed to allocate frame buffer");
602        return false;
603    }
604
605    uint32_t bo_handles[4] = {0};
606    uint32_t pitches[4] = {0};
607    uint32_t offsets[4] = {0};
608    int ret = 0;
609
610    // We use bo_handles[0] and bo_handles[1] to store buffer_handle_t
611    // to support 32 and 64 platforms.
612    bo_handles[0] = ((unsigned long)(output->fbHandle)) & 0xffffffff;
613    bo_handles[1] = ((unsigned long)(output->fbHandle) >> 32) & 0xffffffff;
614    pitches[0] = stride * DrmConfig::getFrameBufferBpp() / 8;
615
616    ret = drmModeAddFB2(
617        mDrmFd,
618        mode->hdisplay,
619        mode->vdisplay,
620        DrmConfig::convertHalFormatToDrmFormat(DrmConfig::getFrameBufferFormat()),
621        bo_handles,
622        pitches,
623        offsets,
624        &output->fbId,
625        0);
626    if (ret != 0) {
627        ETRACE("drmModeAddFB2 failed, error: %d", ret);
628        return false;
629    }
630
631    ITRACE("mode set: %dx%d@%dHz", mode->hdisplay, mode->vdisplay, mode->vrefresh);
632
633    ret = drmModeSetCrtc(mDrmFd, output->crtc->crtc_id, output->fbId, 0, 0,
634                   &output->connector->connector_id, 1, mode);
635    if (ret == 0) {
636        //save mode
637        memcpy(&output->mode, mode, sizeof(drmModeModeInfo));
638    } else {
639        ETRACE("drmModeSetCrtc failed. error: %d", ret);
640    }
641
642    if (oldFbId) {
643        drmModeRmFB(mDrmFd, oldFbId);
644    }
645
646    if (oldFbHandle) {
647        Hwcomposer::getInstance().getBufferManager()->freeFrameBuffer((buffer_handle_t)oldFbHandle);
648    }
649
650    return ret == 0;
651}
652
653int Drm::getOutputIndex(int device)
654{
655    switch (device) {
656    case IDisplayDevice::DEVICE_PRIMARY:
657        return OUTPUT_PRIMARY;
658    case IDisplayDevice::DEVICE_EXTERNAL:
659        return OUTPUT_EXTERNAL;
660    default:
661        ETRACE("invalid display device");
662        break;
663    }
664
665    return -1;
666}
667
668int Drm::getPanelOrientation(int device)
669{
670    int outputIndex = getOutputIndex(device);
671    if (outputIndex < 0) {
672        ETRACE("invalid device");
673        return PANEL_ORIENTATION_0;
674    }
675
676    DrmOutput *output= &mOutputs[outputIndex];
677    if (output->connected == false) {
678        ETRACE("device is not connected");
679        return PANEL_ORIENTATION_0;
680    }
681
682    return output->panelOrientation;
683}
684
685// HWC 1.4 requires that we return all of the compatible configs in getDisplayConfigs
686// this is needed so getActiveConfig/setActiveConfig work correctly.  It is up to the
687// user space to decide what speed to send.
688drmModeModeInfoPtr Drm::detectAllConfigs(int device, int *modeCount)
689{
690    RETURN_NULL_IF_NOT_INIT();
691    Mutex::Autolock _l(mLock);
692
693    if (modeCount != NULL)
694        *modeCount = 0;
695    else
696        return NULL;
697
698    int outputIndex = getOutputIndex(device);
699    if (outputIndex < 0) {
700        ETRACE("invalid device");
701        return NULL;
702    }
703
704    DrmOutput *output= &mOutputs[outputIndex];
705    if (!output->connected) {
706        ETRACE("device is not connected");
707        return NULL;
708    }
709
710    if (output->connector->count_modes <= 0) {
711        ETRACE("invalid count of modes");
712        return NULL;
713    }
714
715    *modeCount = output->connector->count_modes;
716    return output->connector->modes;
717}
718
719} // namespace intel
720} // namespace android
721
722