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