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