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