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 <HwcTrace.h>
17#include <Hwcomposer.h>
18#include <Drm.h>
19#include <PhysicalDevice.h>
20#include <cutils/properties.h>
21
22namespace android {
23namespace intel {
24
25PhysicalDevice::PhysicalDevice(uint32_t type, Hwcomposer& hwc, DeviceControlFactory* controlFactory)
26    : mType(type),
27      mHwc(hwc),
28      mActiveDisplayConfig(-1),
29      mBlankControl(NULL),
30      mVsyncObserver(NULL),
31      mControlFactory(controlFactory),
32      mLayerList(NULL),
33      mConnected(false),
34      mBlank(false),
35      mDisplayState(DEVICE_DISPLAY_ON),
36      mInitialized(false),
37      mFpsDivider(1)
38{
39    CTRACE();
40
41    switch (type) {
42    case DEVICE_PRIMARY:
43        mName = "Primary";
44        break;
45    case DEVICE_EXTERNAL:
46        mName = "External";
47        break;
48    default:
49        mName = "Unknown";
50    }
51
52    mDisplayConfigs.setCapacity(DEVICE_COUNT);
53}
54
55PhysicalDevice::~PhysicalDevice()
56{
57    WARN_IF_NOT_DEINIT();
58}
59
60void PhysicalDevice::onGeometryChanged(hwc_display_contents_1_t *list)
61{
62    if (!list) {
63        ETRACE("list is NULL");
64        return;
65    }
66
67    ATRACE("disp = %d, layer number = %d", mType, list->numHwLayers);
68
69    // NOTE: should NOT be here
70    if (mLayerList) {
71        WTRACE("mLayerList exists");
72        DEINIT_AND_DELETE_OBJ(mLayerList);
73    }
74
75    // create a new layer list
76    mLayerList = new HwcLayerList(list, mType);
77    if (!mLayerList) {
78        WTRACE("failed to create layer list");
79    }
80}
81
82bool PhysicalDevice::prePrepare(hwc_display_contents_1_t *display)
83{
84    RETURN_FALSE_IF_NOT_INIT();
85    Mutex::Autolock _l(mLock);
86
87    // for a null list, delete hwc list
88    if (!mConnected || !display || mBlank) {
89        if (mLayerList) {
90            DEINIT_AND_DELETE_OBJ(mLayerList);
91        }
92        return true;
93    }
94
95    // check if geometry is changed, if changed delete list
96    if ((display->flags & HWC_GEOMETRY_CHANGED) && mLayerList) {
97        DEINIT_AND_DELETE_OBJ(mLayerList);
98    }
99    return true;
100}
101
102bool PhysicalDevice::prepare(hwc_display_contents_1_t *display)
103{
104    RETURN_FALSE_IF_NOT_INIT();
105    Mutex::Autolock _l(mLock);
106
107    if (!mConnected || !display || mBlank)
108        return true;
109
110    // check if geometry is changed
111    if (display->flags & HWC_GEOMETRY_CHANGED) {
112        onGeometryChanged(display);
113    }
114    if (!mLayerList) {
115        WTRACE("null HWC layer list");
116        return true;
117    }
118
119    // update list with new list
120    return mLayerList->update(display);
121}
122
123
124bool PhysicalDevice::commit(hwc_display_contents_1_t *display, IDisplayContext *context)
125{
126    RETURN_FALSE_IF_NOT_INIT();
127
128    if (!display || !context || !mLayerList || mBlank) {
129        return true;
130    }
131    return context->commitContents(display, mLayerList);
132}
133
134bool PhysicalDevice::vsyncControl(bool enabled)
135{
136    RETURN_FALSE_IF_NOT_INIT();
137
138    ATRACE("disp = %d, enabled = %d", mType, enabled);
139    return mVsyncObserver->control(enabled);
140}
141
142bool PhysicalDevice::blank(bool blank)
143{
144    RETURN_FALSE_IF_NOT_INIT();
145
146    if (!mConnected)
147        return false;
148
149    mBlank = blank;
150    bool ret = mBlankControl->blank(mType, blank);
151    if (ret == false) {
152        ETRACE("failed to blank device");
153        return false;
154    }
155
156    return true;
157}
158
159bool PhysicalDevice::getDisplaySize(int *width, int *height)
160{
161    RETURN_FALSE_IF_NOT_INIT();
162    Mutex::Autolock _l(mLock);
163    if (!width || !height) {
164        ETRACE("invalid parameters");
165        return false;
166    }
167
168    *width = 0;
169    *height = 0;
170    drmModeModeInfo mode;
171    Drm *drm = Hwcomposer::getInstance().getDrm();
172    bool ret = drm->getModeInfo(mType, mode);
173    if (!ret) {
174        return false;
175    }
176
177    *width = mode.hdisplay;
178    *height = mode.vdisplay;
179    return true;
180}
181
182template <typename T>
183static inline T min(T a, T b) {
184    return a<b ? a : b;
185}
186
187bool PhysicalDevice::getDisplayConfigs(uint32_t *configs,
188                                         size_t *numConfigs)
189{
190    RETURN_FALSE_IF_NOT_INIT();
191
192    Mutex::Autolock _l(mLock);
193
194    if (!mConnected) {
195        ITRACE("device is not connected");
196        return false;
197    }
198
199    if (!configs || !numConfigs || *numConfigs < 1) {
200        ETRACE("invalid parameters");
201        return false;
202    }
203
204    // fill in all config handles
205    *numConfigs = min(*numConfigs, mDisplayConfigs.size());
206    for (int i = 0; i < static_cast<int>(*numConfigs); i++) {
207        configs[i] = i;
208    }
209
210    return true;
211}
212bool PhysicalDevice::getDisplayAttributes(uint32_t config,
213                                            const uint32_t *attributes,
214                                            int32_t *values)
215{
216    RETURN_FALSE_IF_NOT_INIT();
217
218    Mutex::Autolock _l(mLock);
219
220    if (!mConnected) {
221        ITRACE("device is not connected");
222        return false;
223    }
224
225    if (!attributes || !values) {
226        ETRACE("invalid parameters");
227        return false;
228    }
229
230    DisplayConfig *configChosen = mDisplayConfigs.itemAt(config);
231    if  (!configChosen) {
232        WTRACE("failed to get display config");
233        return false;
234    }
235
236    int i = 0;
237    while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) {
238        switch (attributes[i]) {
239        case HWC_DISPLAY_VSYNC_PERIOD:
240            if (configChosen->getRefreshRate()) {
241                values[i] = 1e9 / configChosen->getRefreshRate();
242            } else {
243                ETRACE("refresh rate is 0!!!");
244                values[i] = 0;
245            }
246            break;
247        case HWC_DISPLAY_WIDTH:
248            values[i] = configChosen->getWidth();
249            break;
250        case HWC_DISPLAY_HEIGHT:
251            values[i] = configChosen->getHeight();
252            break;
253        case HWC_DISPLAY_DPI_X:
254            values[i] = configChosen->getDpiX() * 1000.0f;
255            break;
256        case HWC_DISPLAY_DPI_Y:
257            values[i] = configChosen->getDpiY() * 1000.0f;
258            break;
259        default:
260            ETRACE("unknown attribute %d", attributes[i]);
261            break;
262        }
263        i++;
264    }
265
266    return true;
267}
268
269bool PhysicalDevice::compositionComplete()
270{
271    CTRACE();
272    // do nothing by default
273    return true;
274}
275
276void PhysicalDevice::removeDisplayConfigs()
277{
278    for (size_t i = 0; i < mDisplayConfigs.size(); i++) {
279        DisplayConfig *config = mDisplayConfigs.itemAt(i);
280        delete config;
281    }
282
283    mDisplayConfigs.clear();
284    mActiveDisplayConfig = -1;
285}
286
287bool PhysicalDevice::detectDisplayConfigs()
288{
289    Mutex::Autolock _l(mLock);
290
291    Drm *drm = Hwcomposer::getInstance().getDrm();
292    if (!drm->detect(mType)) {
293        ETRACE("drm detection on device %d failed ", mType);
294        return false;
295    }
296    return updateDisplayConfigs();
297}
298
299bool PhysicalDevice::updateDisplayConfigs()
300{
301    bool ret;
302    Drm *drm = Hwcomposer::getInstance().getDrm();
303
304    // reset display configs
305    removeDisplayConfigs();
306
307    // update device connection status
308    mConnected = drm->isConnected(mType);
309    if (!mConnected) {
310        return true;
311    }
312
313    // reset the number of display configs
314    mDisplayConfigs.setCapacity(1);
315
316    drmModeModeInfo mode;
317    ret = drm->getModeInfo(mType, mode);
318    if (!ret) {
319        ETRACE("failed to get mode info");
320        mConnected = false;
321        return false;
322    }
323
324    uint32_t mmWidth, mmHeight;
325    ret = drm->getPhysicalSize(mType, mmWidth, mmHeight);
326    if (!ret) {
327        ETRACE("failed to get physical size");
328        mConnected = false;
329        return false;
330    }
331
332    float physWidthInch = (float)mmWidth * 0.039370f;
333    float physHeightInch = (float)mmHeight * 0.039370f;
334
335    // use current drm mode, likely it's preferred mode
336    int dpiX = 0;
337    int dpiY = 0;
338    if (physWidthInch && physHeightInch) {
339        dpiX = mode.hdisplay / physWidthInch;
340        dpiY = mode.vdisplay / physHeightInch;
341    } else {
342        ETRACE("invalid physical size, EDID read error?");
343        // don't bail out as it is not a fatal error
344    }
345    // use active fb dimension as config width/height
346    // add display config vfresh/mFpsDivider to lower FPS
347    DisplayConfig *config = new DisplayConfig(mode.vrefresh/mFpsDivider,
348                                              mode.hdisplay,
349                                              mode.vdisplay,
350                                              dpiX, dpiY);
351    // add it to the front of other configs
352    mDisplayConfigs.push_front(config);
353
354    // init the active display config
355    mActiveDisplayConfig = 0;
356
357    drmModeModeInfoPtr modes;
358    drmModeModeInfoPtr compatMode;
359    int modeCount = 0;
360
361    modes = drm->detectAllConfigs(mType, &modeCount);
362
363    for (int i = 0; i < modeCount; i++) {
364        if (modes) {
365            compatMode = &modes[i];
366            if (!compatMode)
367                continue;
368            if (compatMode->hdisplay == mode.hdisplay &&
369                compatMode->vdisplay == mode.vdisplay &&
370                compatMode->vrefresh != mode.vrefresh) {
371
372                bool found = false;
373                for (size_t j = 0; j < mDisplayConfigs.size(); j++) {
374                     DisplayConfig *config = mDisplayConfigs.itemAt(j);
375                     if (config->getRefreshRate() == (int)compatMode->vrefresh) {
376                         found = true;
377                         break;
378                     }
379                }
380
381                if (found) {
382                    continue;
383                }
384
385                DisplayConfig *config = new DisplayConfig(compatMode->vrefresh,
386                                              compatMode->hdisplay,
387                                              compatMode->vdisplay,
388                                              dpiX, dpiY);
389                // add it to the end of configs
390                mDisplayConfigs.push_back(config);
391            }
392        }
393    }
394
395    return true;
396}
397
398bool PhysicalDevice::initialize()
399{
400    CTRACE();
401    char prop[PROPERTY_VALUE_MAX];
402    char *retptr;
403
404    if (property_get("hwc.fps_divider", prop, "1") > 0) {
405        uint32_t divider = strtoul(prop, &retptr, 10);
406        if (*retptr == '\0' && divider > 1 && divider < 60) {
407            mFpsDivider = divider;
408            ALOGI("%s display, setting HWC FPS divider to %d", mName, mFpsDivider);
409        }
410    }
411
412    if (mType != DEVICE_PRIMARY && mType != DEVICE_EXTERNAL) {
413        ETRACE("invalid device type");
414        return false;
415    }
416
417    // detect display configs
418    bool ret = detectDisplayConfigs();
419    if (ret == false) {
420        DEINIT_AND_RETURN_FALSE("failed to detect display config");
421    }
422
423    if (!mControlFactory) {
424        DEINIT_AND_RETURN_FALSE("failed to provide a controlFactory ");
425    }
426
427    // create blank control
428    mBlankControl = mControlFactory->createBlankControl();
429    if (!mBlankControl) {
430        DEINIT_AND_RETURN_FALSE("failed to create blank control");
431    }
432
433    // create vsync event observer
434    mVsyncObserver = new VsyncEventObserver(*this);
435    if (!mVsyncObserver || !mVsyncObserver->initialize()) {
436        DEINIT_AND_RETURN_FALSE("failed to create vsync observer");
437    }
438
439    mInitialized = true;
440    return true;
441}
442
443void PhysicalDevice::deinitialize()
444{
445    Mutex::Autolock _l(mLock);
446    if (mLayerList) {
447        DEINIT_AND_DELETE_OBJ(mLayerList);
448    }
449
450    DEINIT_AND_DELETE_OBJ(mVsyncObserver);
451
452    // destroy blank control
453    if (mBlankControl) {
454        delete mBlankControl;
455        mBlankControl = 0;
456    }
457
458    if (mControlFactory){
459        delete mControlFactory;
460        mControlFactory = 0;
461    }
462
463    // remove configs
464    removeDisplayConfigs();
465
466    mInitialized = false;
467}
468
469bool PhysicalDevice::isConnected() const
470{
471    RETURN_FALSE_IF_NOT_INIT();
472
473    return mConnected;
474}
475
476const char* PhysicalDevice::getName() const
477{
478    return mName;
479}
480
481int PhysicalDevice::getType() const
482{
483    return mType;
484}
485
486void PhysicalDevice::onVsync(int64_t timestamp)
487{
488    RETURN_VOID_IF_NOT_INIT();
489    ATRACE("timestamp = %lld", timestamp);
490
491    if (!mConnected)
492        return;
493
494    // notify hwc
495    mHwc.vsync(mType, timestamp);
496}
497
498void PhysicalDevice::dump(Dump& d)
499{
500    Mutex::Autolock _l(mLock);
501    d.append("-------------------------------------------------------------\n");
502    d.append("Device Name: %s (%s)\n", mName,
503            mConnected ? "connected" : "disconnected");
504    d.append("Display configs (count = %d):\n", mDisplayConfigs.size());
505    d.append(" CONFIG | VSYNC_PERIOD | WIDTH | HEIGHT | DPI_X | DPI_Y \n");
506    d.append("--------+--------------+-------+--------+-------+-------\n");
507    for (size_t i = 0; i < mDisplayConfigs.size(); i++) {
508        DisplayConfig *config = mDisplayConfigs.itemAt(i);
509        if (config) {
510            d.append("%s %2d   |     %4d     | %5d |  %4d  |  %3d  |  %3d  \n",
511                     (i == (size_t)mActiveDisplayConfig) ? "* " : "  ",
512                     i,
513                     config->getRefreshRate(),
514                     config->getWidth(),
515                     config->getHeight(),
516                     config->getDpiX(),
517                     config->getDpiY());
518        }
519    }
520    // dump layer list
521    if (mLayerList)
522        mLayerList->dump(d);
523}
524
525uint32_t PhysicalDevice::getFpsDivider()
526{
527    return mFpsDivider;
528}
529
530bool PhysicalDevice::setPowerMode(int mode)
531{
532    // TODO: set proper power modes for HWC 1.4
533    ATRACE("mode = %d", mode);
534
535    bool ret;
536    int arg = mode;
537
538    Drm *drm = Hwcomposer::getInstance().getDrm();
539    ret = drm->writeIoctl(DRM_PSB_PM_SET, &arg, sizeof(arg));
540    if (ret == false) {
541          ETRACE("psb power mode set fail");
542          return false;
543    }
544
545    return true;
546}
547
548int PhysicalDevice::getActiveConfig()
549{
550    return mActiveDisplayConfig;
551}
552
553bool PhysicalDevice::setActiveConfig(int index)
554{
555    // TODO: for now only implement in external
556    if (index == 0)
557        return true;
558    return false;
559}
560
561} // namespace intel
562} // namespace android
563