hwc.cpp revision 87838778cdb83d1f8d5c708865714268bc8ea167
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * Copyright (C) 2012-2013, The Linux Foundation. All rights reserved.
4 *
5 * Not a Contribution, Apache license notifications and license are retained
6 * for attribution purposes only.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
21#include <fcntl.h>
22#include <errno.h>
23
24#include <cutils/log.h>
25#include <cutils/atomic.h>
26#include <EGL/egl.h>
27#include <utils/Trace.h>
28#include <sys/ioctl.h>
29#include <overlay.h>
30#include <overlayRotator.h>
31#include <overlayWriteback.h>
32#include <mdp_version.h>
33#include "hwc_utils.h"
34#include "hwc_fbupdate.h"
35#include "hwc_mdpcomp.h"
36#include "external.h"
37#include "hwc_copybit.h"
38#include "hwc_ad.h"
39#include "profiler.h"
40
41using namespace qhwc;
42using namespace overlay;
43
44#define VSYNC_DEBUG 0
45#define BLANK_DEBUG 1
46
47static int hwc_device_open(const struct hw_module_t* module,
48                           const char* name,
49                           struct hw_device_t** device);
50
51static struct hw_module_methods_t hwc_module_methods = {
52    open: hwc_device_open
53};
54
55hwc_module_t HAL_MODULE_INFO_SYM = {
56    common: {
57        tag: HARDWARE_MODULE_TAG,
58        version_major: 2,
59        version_minor: 0,
60        id: HWC_HARDWARE_MODULE_ID,
61        name: "Qualcomm Hardware Composer Module",
62        author: "CodeAurora Forum",
63        methods: &hwc_module_methods,
64        dso: 0,
65        reserved: {0},
66    }
67};
68
69/*
70 * Save callback functions registered to HWC
71 */
72static void hwc_registerProcs(struct hwc_composer_device_1* dev,
73                              hwc_procs_t const* procs)
74{
75    ALOGI("%s", __FUNCTION__);
76    hwc_context_t* ctx = (hwc_context_t*)(dev);
77    if(!ctx) {
78        ALOGE("%s: Invalid context", __FUNCTION__);
79        return;
80    }
81    ctx->proc = procs;
82
83    // Now that we have the functions needed, kick off
84    // the uevent & vsync threads
85    init_uevent_thread(ctx);
86    init_vsync_thread(ctx);
87}
88
89//Helper
90static void reset(hwc_context_t *ctx, int numDisplays,
91                  hwc_display_contents_1_t** displays) {
92    for(int i = 0; i < MAX_DISPLAYS; i++) {
93        hwc_display_contents_1_t *list = displays[i];
94        // XXX:SurfaceFlinger no longer guarantees that this
95        // value is reset on every prepare. However, for the layer
96        // cache we need to reset it.
97        // We can probably rethink that later on
98        if (LIKELY(list && list->numHwLayers > 1)) {
99            for(uint32_t j = 0; j < list->numHwLayers; j++) {
100                if(list->hwLayers[j].compositionType != HWC_FRAMEBUFFER_TARGET)
101                    list->hwLayers[j].compositionType = HWC_FRAMEBUFFER;
102            }
103        }
104
105        if(ctx->mFBUpdate[i])
106            ctx->mFBUpdate[i]->reset();
107        if(ctx->mCopyBit[i])
108            ctx->mCopyBit[i]->reset();
109        if(ctx->mLayerRotMap[i])
110            ctx->mLayerRotMap[i]->reset();
111    }
112
113    ctx->mAD->reset();
114}
115
116//clear prev layer prop flags and realloc for current frame
117static void reset_layer_prop(hwc_context_t* ctx, int dpy, int numAppLayers) {
118    if(ctx->layerProp[dpy]) {
119       delete[] ctx->layerProp[dpy];
120       ctx->layerProp[dpy] = NULL;
121    }
122    ctx->layerProp[dpy] = new LayerProp[numAppLayers];
123}
124
125static void handleGeomChange(hwc_context_t *ctx, int dpy,
126        hwc_display_contents_1_t *list) {
127    if(list->flags & HWC_GEOMETRY_CHANGED) {
128        ctx->mOverlay->forceSet(dpy);
129    }
130}
131
132static int display_commit(hwc_context_t *ctx, int dpy) {
133    int fbFd = ctx->dpyAttr[dpy].fd;
134    if(fbFd == -1) {
135        ALOGE("%s: Invalid FB fd for display: %d", __FUNCTION__, dpy);
136        return -1;
137    }
138
139    struct mdp_display_commit commit_info;
140    memset(&commit_info, 0, sizeof(struct mdp_display_commit));
141    commit_info.flags = MDP_DISPLAY_COMMIT_OVERLAY;
142    if(ioctl(fbFd, MSMFB_DISPLAY_COMMIT, &commit_info) == -1) {
143       ALOGE("%s: MSMFB_DISPLAY_COMMIT for primary failed", __FUNCTION__);
144       return -errno;
145    }
146    return 0;
147}
148
149static int hwc_prepare_primary(hwc_composer_device_1 *dev,
150        hwc_display_contents_1_t *list) {
151    hwc_context_t* ctx = (hwc_context_t*)(dev);
152    const int dpy = HWC_DISPLAY_PRIMARY;
153    if (LIKELY(list && list->numHwLayers > 1) &&
154            ctx->dpyAttr[dpy].isActive) {
155        reset_layer_prop(ctx, dpy, list->numHwLayers - 1);
156        handleGeomChange(ctx, dpy, list);
157        uint32_t last = list->numHwLayers - 1;
158        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
159        if(fbLayer->handle) {
160            setListStats(ctx, list, dpy);
161            if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
162                const int fbZ = 0;
163                ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
164            }
165            if (ctx->mMDP.version < qdutils::MDP_V4_0) {
166                if(ctx->mCopyBit[dpy])
167                    ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
168            }
169        }
170    }
171    return 0;
172}
173
174static int hwc_prepare_external(hwc_composer_device_1 *dev,
175        hwc_display_contents_1_t *list, int dpy) {
176
177    hwc_context_t* ctx = (hwc_context_t*)(dev);
178
179    if (LIKELY(list && list->numHwLayers > 1) &&
180            ctx->dpyAttr[dpy].isActive &&
181            ctx->dpyAttr[dpy].connected) {
182        reset_layer_prop(ctx, dpy, list->numHwLayers - 1);
183        handleGeomChange(ctx, dpy, list);
184        uint32_t last = list->numHwLayers - 1;
185        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
186        if(!ctx->dpyAttr[dpy].isPause) {
187            if(fbLayer->handle) {
188                ctx->dpyAttr[dpy].isConfiguring = false;
189                setListStats(ctx, list, dpy);
190                if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
191                    const int fbZ = 0;
192                    ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
193                }
194
195                /* Temporarily commenting out C2D until we support partial
196                   copybit composition for mixed mode MDP
197
198                if((fbZOrder >= 0) && ctx->mCopyBit[dpy])
199                    ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
200                */
201            }
202        } else {
203            // External Display is in Pause state.
204            // ToDo:
205            // Mark all application layers as OVERLAY so that
206            // GPU will not compose. This is done for power
207            // optimization
208        }
209    }
210    return 0;
211}
212
213static int hwc_prepare_virtual(hwc_composer_device_1 *dev,
214                               hwc_display_contents_1_t *list, int dpy) {
215    //XXX: Fix when framework support is added
216    return 0;
217}
218
219static int hwc_prepare(hwc_composer_device_1 *dev, size_t numDisplays,
220                       hwc_display_contents_1_t** displays)
221{
222    int ret = 0;
223    hwc_context_t* ctx = (hwc_context_t*)(dev);
224    //Will be unlocked at the end of set
225    ctx->mBlankLock.lock();
226    ctx->mExtLock.lock();
227    reset(ctx, numDisplays, displays);
228
229    ctx->mOverlay->configBegin();
230    ctx->mRotMgr->configBegin();
231    overlay::Writeback::configBegin();
232
233    Overlay::setDMAMode(Overlay::DMA_LINE_MODE);
234
235    for (int32_t i = numDisplays - 1; i >= 0; i--) {
236        hwc_display_contents_1_t *list = displays[i];
237        switch(i) {
238            case HWC_DISPLAY_PRIMARY:
239                ret = hwc_prepare_primary(dev, list);
240                break;
241            case HWC_DISPLAY_EXTERNAL:
242                ret = hwc_prepare_external(dev, list, i);
243                break;
244            case HWC_DISPLAY_VIRTUAL:
245                ret = hwc_prepare_virtual(dev, list, i);
246                break;
247            default:
248                ret = -EINVAL;
249        }
250    }
251
252    ctx->mOverlay->configDone();
253    ctx->mRotMgr->configDone();
254    overlay::Writeback::configDone();
255
256    return ret;
257}
258
259static int hwc_eventControl(struct hwc_composer_device_1* dev, int dpy,
260                             int event, int enable)
261{
262    int ret = 0;
263    hwc_context_t* ctx = (hwc_context_t*)(dev);
264    switch(event) {
265        case HWC_EVENT_VSYNC:
266            if(!ctx->dpyAttr[dpy].isActive) {
267                ALOGE("Display is blanked - Cannot %s vsync",
268                        enable ? "enable" : "disable");
269                return -EINVAL;
270            }
271
272            if (ctx->vstate.enable == enable)
273                break;
274            ret = hwc_vsync_control(ctx, dpy, enable);
275            if(ret == 0)
276                ctx->vstate.enable = !!enable;
277            ALOGD_IF (VSYNC_DEBUG, "VSYNC state changed to %s",
278                      (enable)?"ENABLED":"DISABLED");
279            break;
280        default:
281            ret = -EINVAL;
282    }
283    return ret;
284}
285
286static int hwc_blank(struct hwc_composer_device_1* dev, int dpy, int blank)
287{
288    ATRACE_CALL();
289    hwc_context_t* ctx = (hwc_context_t*)(dev);
290
291    Locker::Autolock _l(ctx->mBlankLock);
292    int ret = 0;
293    ALOGD_IF(BLANK_DEBUG, "%s: %s display: %d", __FUNCTION__,
294          blank==1 ? "Blanking":"Unblanking", dpy);
295    if(blank) {
296        // free up all the overlay pipes in use
297        // when we get a blank for either display
298        // makes sure that all pipes are freed
299        ctx->mOverlay->configBegin();
300        ctx->mOverlay->configDone();
301        ctx->mRotMgr->clear();
302        overlay::Writeback::clear();
303    }
304    switch(dpy) {
305        case HWC_DISPLAY_PRIMARY:
306            if(blank) {
307                ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,
308                            FB_BLANK_POWERDOWN);
309            } else {
310                ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,FB_BLANK_UNBLANK);
311            }
312            break;
313        case HWC_DISPLAY_EXTERNAL:
314        case HWC_DISPLAY_VIRTUAL:
315            if(blank) {
316                // call external framebuffer commit on blank,
317                // so that any pipe unsets gets committed
318                if (display_commit(ctx, dpy) < 0) {
319                    ret = -1;
320                    ALOGE("%s:post failed for external display !! ",
321                          __FUNCTION__);
322                }
323            } else {
324            }
325            break;
326        default:
327            return -EINVAL;
328    }
329    // Enable HPD here, as during bootup unblank is called
330    // when SF is completely initialized
331    ctx->mExtDisplay->setHPD(1);
332    if(ret == 0){
333        ctx->dpyAttr[dpy].isActive = !blank;
334    } else {
335        ALOGE("%s: Failed in %s display: %d error:%s", __FUNCTION__,
336              blank==1 ? "blanking":"unblanking", dpy, strerror(errno));
337        return ret;
338    }
339
340    ALOGD_IF(BLANK_DEBUG, "%s: Done %s display: %d", __FUNCTION__,
341          blank==1 ? "blanking":"unblanking", dpy);
342    return 0;
343}
344
345static int hwc_query(struct hwc_composer_device_1* dev,
346                     int param, int* value)
347{
348    hwc_context_t* ctx = (hwc_context_t*)(dev);
349    int supported = HWC_DISPLAY_PRIMARY_BIT;
350
351    switch (param) {
352    case HWC_BACKGROUND_LAYER_SUPPORTED:
353        // Not supported for now
354        value[0] = 0;
355        break;
356    case HWC_DISPLAY_TYPES_SUPPORTED:
357        if(ctx->mMDP.hasOverlay)
358            supported |= HWC_DISPLAY_EXTERNAL_BIT;
359        value[0] = supported;
360        break;
361    default:
362        return -EINVAL;
363    }
364    return 0;
365
366}
367
368
369static int hwc_set_primary(hwc_context_t *ctx, hwc_display_contents_1_t* list) {
370    ATRACE_CALL();
371    int ret = 0;
372    const int dpy = HWC_DISPLAY_PRIMARY;
373
374    if (LIKELY(list) && ctx->dpyAttr[dpy].isActive) {
375        uint32_t last = list->numHwLayers - 1;
376        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
377        int fd = -1; //FenceFD from the Copybit(valid in async mode)
378        bool copybitDone = false;
379        if(ctx->mCopyBit[dpy])
380            copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
381        if(list->numHwLayers > 1)
382            hwc_sync(ctx, list, dpy, fd);
383
384        if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
385            ALOGE("%s: MDPComp draw failed", __FUNCTION__);
386            ret = -1;
387        }
388
389        //TODO We dont check for SKIP flag on this layer because we need PAN
390        //always. Last layer is always FB
391        private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
392        if(copybitDone) {
393            hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
394        }
395
396        if(hnd) {
397            if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
398                ALOGE("%s: FBUpdate draw failed", __FUNCTION__);
399                ret = -1;
400            }
401        }
402
403        if (display_commit(ctx, dpy) < 0) {
404            ALOGE("%s: display commit fail!", __FUNCTION__);
405            ret = -1;
406        }
407    }
408
409    closeAcquireFds(list);
410    return ret;
411}
412
413static int hwc_set_external(hwc_context_t *ctx,
414                            hwc_display_contents_1_t* list, int dpy)
415{
416    ATRACE_CALL();
417    int ret = 0;
418
419    if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
420        !ctx->dpyAttr[dpy].isPause &&
421        ctx->dpyAttr[dpy].connected) {
422        uint32_t last = list->numHwLayers - 1;
423        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
424        int fd = -1; //FenceFD from the Copybit(valid in async mode)
425        bool copybitDone = false;
426        if(ctx->mCopyBit[dpy])
427            copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
428
429        if(list->numHwLayers > 1)
430            hwc_sync(ctx, list, dpy, fd);
431
432        if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
433            ALOGE("%s: MDPComp draw failed", __FUNCTION__);
434            ret = -1;
435        }
436
437        private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
438        if(copybitDone) {
439            hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
440        }
441
442        if(hnd) {
443            if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
444                ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
445                ret = -1;
446            }
447        }
448
449        if (display_commit(ctx, dpy) < 0) {
450            ALOGE("%s: display commit fail!", __FUNCTION__);
451            ret = -1;
452        }
453    }
454
455    closeAcquireFds(list);
456    return ret;
457}
458
459static int hwc_set_virtual(hwc_context_t *ctx,
460                           hwc_display_contents_1_t* list, int dpy)
461{
462    //XXX: Implement set.
463    closeAcquireFds(list);
464    if (list) {
465        // SF assumes HWC waits for the acquire fence and returns a new fence
466        // that signals when we're done. Since we don't wait, and also don't
467        // touch the buffer, we can just handle the acquire fence back to SF
468        // as the retire fence.
469        list->retireFenceFd = list->outbufAcquireFenceFd;
470    }
471    return 0;
472}
473
474
475static int hwc_set(hwc_composer_device_1 *dev,
476                   size_t numDisplays,
477                   hwc_display_contents_1_t** displays)
478{
479    int ret = 0;
480    hwc_context_t* ctx = (hwc_context_t*)(dev);
481    for (uint32_t i = 0; i < numDisplays; i++) {
482        hwc_display_contents_1_t* list = displays[i];
483        switch(i) {
484            case HWC_DISPLAY_PRIMARY:
485                ret = hwc_set_primary(ctx, list);
486                break;
487            case HWC_DISPLAY_EXTERNAL:
488                ret = hwc_set_external(ctx, list, i);
489                break;
490            case HWC_DISPLAY_VIRTUAL:
491                ret = hwc_set_virtual(ctx, list, i);
492                break;
493            default:
494                ret = -EINVAL;
495        }
496    }
497    // This is only indicative of how many times SurfaceFlinger posts
498    // frames to the display.
499    CALC_FPS();
500    MDPComp::resetIdleFallBack();
501    ctx->mVideoTransFlag = false;
502    //Was locked at the beginning of prepare
503    ctx->mExtLock.unlock();
504    ctx->mBlankLock.unlock();
505    return ret;
506}
507
508int hwc_getDisplayConfigs(struct hwc_composer_device_1* dev, int disp,
509        uint32_t* configs, size_t* numConfigs) {
510    int ret = 0;
511    hwc_context_t* ctx = (hwc_context_t*)(dev);
512    //in 1.1 there is no way to choose a config, report as config id # 0
513    //This config is passed to getDisplayAttributes. Ignore for now.
514    switch(disp) {
515        case HWC_DISPLAY_PRIMARY:
516            if(*numConfigs > 0) {
517                configs[0] = 0;
518                *numConfigs = 1;
519            }
520            ret = 0; //NO_ERROR
521            break;
522        case HWC_DISPLAY_EXTERNAL:
523            ret = -1; //Not connected
524            if(ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected) {
525                ret = 0; //NO_ERROR
526                if(*numConfigs > 0) {
527                    configs[0] = 0;
528                    *numConfigs = 1;
529                }
530            }
531            break;
532    }
533    return ret;
534}
535
536int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp,
537        uint32_t config, const uint32_t* attributes, int32_t* values) {
538
539    hwc_context_t* ctx = (hwc_context_t*)(dev);
540    //If hotpluggable displays are inactive return error
541    if(disp == HWC_DISPLAY_EXTERNAL && !ctx->dpyAttr[disp].connected) {
542        return -1;
543    }
544
545    //From HWComposer
546    static const uint32_t DISPLAY_ATTRIBUTES[] = {
547        HWC_DISPLAY_VSYNC_PERIOD,
548        HWC_DISPLAY_WIDTH,
549        HWC_DISPLAY_HEIGHT,
550        HWC_DISPLAY_DPI_X,
551        HWC_DISPLAY_DPI_Y,
552        HWC_DISPLAY_NO_ATTRIBUTE,
553    };
554
555    const int NUM_DISPLAY_ATTRIBUTES = (sizeof(DISPLAY_ATTRIBUTES) /
556            sizeof(DISPLAY_ATTRIBUTES)[0]);
557
558    for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
559        switch (attributes[i]) {
560        case HWC_DISPLAY_VSYNC_PERIOD:
561            values[i] = ctx->dpyAttr[disp].vsync_period;
562            break;
563        case HWC_DISPLAY_WIDTH:
564            values[i] = ctx->dpyAttr[disp].xres;
565            ALOGD("%s disp = %d, width = %d",__FUNCTION__, disp,
566                    ctx->dpyAttr[disp].xres);
567            break;
568        case HWC_DISPLAY_HEIGHT:
569            values[i] = ctx->dpyAttr[disp].yres;
570            ALOGD("%s disp = %d, height = %d",__FUNCTION__, disp,
571                    ctx->dpyAttr[disp].yres);
572            break;
573        case HWC_DISPLAY_DPI_X:
574            values[i] = (int32_t) (ctx->dpyAttr[disp].xdpi*1000.0);
575            break;
576        case HWC_DISPLAY_DPI_Y:
577            values[i] = (int32_t) (ctx->dpyAttr[disp].ydpi*1000.0);
578            break;
579        default:
580            ALOGE("Unknown display attribute %d",
581                    attributes[i]);
582            return -EINVAL;
583        }
584    }
585    return 0;
586}
587
588void hwc_dump(struct hwc_composer_device_1* dev, char *buff, int buff_len)
589{
590    hwc_context_t* ctx = (hwc_context_t*)(dev);
591    android::String8 aBuf("");
592    dumpsys_log(aBuf, "Qualcomm HWC state:\n");
593    dumpsys_log(aBuf, "  MDPVersion=%d\n", ctx->mMDP.version);
594    dumpsys_log(aBuf, "  DisplayPanel=%c\n", ctx->mMDP.panel);
595    for(int dpy = 0; dpy < MAX_DISPLAYS; dpy++) {
596        if(ctx->mMDPComp[dpy])
597            ctx->mMDPComp[dpy]->dump(aBuf);
598    }
599    char ovDump[2048] = {'\0'};
600    ctx->mOverlay->getDump(ovDump, 2048);
601    dumpsys_log(aBuf, ovDump);
602    ovDump[0] = '\0';
603    ctx->mRotMgr->getDump(ovDump, 1024);
604    dumpsys_log(aBuf, ovDump);
605    strlcpy(buff, aBuf.string(), buff_len);
606}
607
608static int hwc_device_close(struct hw_device_t *dev)
609{
610    if(!dev) {
611        ALOGE("%s: NULL device pointer", __FUNCTION__);
612        return -1;
613    }
614    closeContext((hwc_context_t*)dev);
615    free(dev);
616
617    return 0;
618}
619
620static int hwc_device_open(const struct hw_module_t* module, const char* name,
621                           struct hw_device_t** device)
622{
623    int status = -EINVAL;
624
625    if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
626        struct hwc_context_t *dev;
627        dev = (hwc_context_t*)malloc(sizeof(*dev));
628        memset(dev, 0, sizeof(*dev));
629
630        //Initialize hwc context
631        initContext(dev);
632
633        //Setup HWC methods
634        dev->device.common.tag          = HARDWARE_DEVICE_TAG;
635        dev->device.common.version      = HWC_DEVICE_API_VERSION_1_3;
636        dev->device.common.module       = const_cast<hw_module_t*>(module);
637        dev->device.common.close        = hwc_device_close;
638        dev->device.prepare             = hwc_prepare;
639        dev->device.set                 = hwc_set;
640        dev->device.eventControl        = hwc_eventControl;
641        dev->device.blank               = hwc_blank;
642        dev->device.query               = hwc_query;
643        dev->device.registerProcs       = hwc_registerProcs;
644        dev->device.dump                = hwc_dump;
645        dev->device.getDisplayConfigs   = hwc_getDisplayConfigs;
646        dev->device.getDisplayAttributes = hwc_getDisplayAttributes;
647        *device = &dev->device.common;
648        status = 0;
649    }
650    return status;
651}
652