hwc.cpp revision 01f9a13e06f5b25788dfb042d20583ce2f974579
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->mDrawLock.lock();
226    reset(ctx, numDisplays, displays);
227
228    ctx->mOverlay->configBegin();
229    ctx->mRotMgr->configBegin();
230    overlay::Writeback::configBegin();
231
232    Overlay::setDMAMode(Overlay::DMA_LINE_MODE);
233
234    for (int32_t i = numDisplays - 1; i >= 0; i--) {
235        hwc_display_contents_1_t *list = displays[i];
236        switch(i) {
237            case HWC_DISPLAY_PRIMARY:
238                ret = hwc_prepare_primary(dev, list);
239                break;
240            case HWC_DISPLAY_EXTERNAL:
241                ret = hwc_prepare_external(dev, list, i);
242                break;
243            case HWC_DISPLAY_VIRTUAL:
244                ret = hwc_prepare_virtual(dev, list, i);
245                break;
246            default:
247                ret = -EINVAL;
248        }
249    }
250
251    ctx->mOverlay->configDone();
252    ctx->mRotMgr->configDone();
253    overlay::Writeback::configDone();
254
255    return ret;
256}
257
258static int hwc_eventControl(struct hwc_composer_device_1* dev, int dpy,
259                             int event, int enable)
260{
261    int ret = 0;
262    hwc_context_t* ctx = (hwc_context_t*)(dev);
263    switch(event) {
264        case HWC_EVENT_VSYNC:
265            if(!ctx->dpyAttr[dpy].isActive) {
266                ALOGE("Display is blanked - Cannot %s vsync",
267                        enable ? "enable" : "disable");
268                return -EINVAL;
269            }
270
271            if (ctx->vstate.enable == enable)
272                break;
273            ret = hwc_vsync_control(ctx, dpy, enable);
274            if(ret == 0)
275                ctx->vstate.enable = !!enable;
276            ALOGD_IF (VSYNC_DEBUG, "VSYNC state changed to %s",
277                      (enable)?"ENABLED":"DISABLED");
278            break;
279        default:
280            ret = -EINVAL;
281    }
282    return ret;
283}
284
285static int hwc_blank(struct hwc_composer_device_1* dev, int dpy, int blank)
286{
287    ATRACE_CALL();
288    hwc_context_t* ctx = (hwc_context_t*)(dev);
289
290    Locker::Autolock _l(ctx->mDrawLock);
291    int ret = 0;
292    ALOGD_IF(BLANK_DEBUG, "%s: %s display: %d", __FUNCTION__,
293          blank==1 ? "Blanking":"Unblanking", dpy);
294    if(blank) {
295        // free up all the overlay pipes in use
296        // when we get a blank for either display
297        // makes sure that all pipes are freed
298        ctx->mOverlay->configBegin();
299        ctx->mOverlay->configDone();
300        ctx->mRotMgr->clear();
301        overlay::Writeback::clear();
302    }
303    switch(dpy) {
304        case HWC_DISPLAY_PRIMARY:
305            if(blank) {
306                ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,
307                            FB_BLANK_POWERDOWN);
308            } else {
309                ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,FB_BLANK_UNBLANK);
310            }
311            break;
312        case HWC_DISPLAY_EXTERNAL:
313        case HWC_DISPLAY_VIRTUAL:
314            if(blank) {
315                // call external framebuffer commit on blank,
316                // so that any pipe unsets gets committed
317                if (display_commit(ctx, dpy) < 0) {
318                    ret = -1;
319                    ALOGE("%s:post failed for external display !! ",
320                          __FUNCTION__);
321                }
322            } else {
323            }
324            break;
325        default:
326            return -EINVAL;
327    }
328    // Enable HPD here, as during bootup unblank is called
329    // when SF is completely initialized
330    ctx->mExtDisplay->setHPD(1);
331    if(ret == 0){
332        ctx->dpyAttr[dpy].isActive = !blank;
333    } else {
334        ALOGE("%s: Failed in %s display: %d error:%s", __FUNCTION__,
335              blank==1 ? "blanking":"unblanking", dpy, strerror(errno));
336        return ret;
337    }
338
339    ALOGD_IF(BLANK_DEBUG, "%s: Done %s display: %d", __FUNCTION__,
340          blank==1 ? "blanking":"unblanking", dpy);
341    return 0;
342}
343
344static int hwc_query(struct hwc_composer_device_1* dev,
345                     int param, int* value)
346{
347    hwc_context_t* ctx = (hwc_context_t*)(dev);
348    int supported = HWC_DISPLAY_PRIMARY_BIT;
349
350    switch (param) {
351    case HWC_BACKGROUND_LAYER_SUPPORTED:
352        // Not supported for now
353        value[0] = 0;
354        break;
355    case HWC_DISPLAY_TYPES_SUPPORTED:
356        if(ctx->mMDP.hasOverlay)
357            supported |= HWC_DISPLAY_EXTERNAL_BIT;
358        value[0] = supported;
359        break;
360    default:
361        return -EINVAL;
362    }
363    return 0;
364
365}
366
367
368static int hwc_set_primary(hwc_context_t *ctx, hwc_display_contents_1_t* list) {
369    ATRACE_CALL();
370    int ret = 0;
371    const int dpy = HWC_DISPLAY_PRIMARY;
372
373    if (LIKELY(list) && ctx->dpyAttr[dpy].isActive) {
374        uint32_t last = list->numHwLayers - 1;
375        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
376        int fd = -1; //FenceFD from the Copybit(valid in async mode)
377        bool copybitDone = false;
378        if(ctx->mCopyBit[dpy])
379            copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
380        if(list->numHwLayers > 1)
381            hwc_sync(ctx, list, dpy, fd);
382
383        if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
384            ALOGE("%s: MDPComp draw failed", __FUNCTION__);
385            ret = -1;
386        }
387
388        //TODO We dont check for SKIP flag on this layer because we need PAN
389        //always. Last layer is always FB
390        private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
391        if(copybitDone) {
392            hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
393        }
394
395        if(hnd) {
396            if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
397                ALOGE("%s: FBUpdate draw failed", __FUNCTION__);
398                ret = -1;
399            }
400        }
401
402        if (display_commit(ctx, dpy) < 0) {
403            ALOGE("%s: display commit fail!", __FUNCTION__);
404            ret = -1;
405        }
406    }
407
408    closeAcquireFds(list);
409    return ret;
410}
411
412static int hwc_set_external(hwc_context_t *ctx,
413                            hwc_display_contents_1_t* list, int dpy)
414{
415    ATRACE_CALL();
416    int ret = 0;
417
418    if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
419        !ctx->dpyAttr[dpy].isPause &&
420        ctx->dpyAttr[dpy].connected) {
421        uint32_t last = list->numHwLayers - 1;
422        hwc_layer_1_t *fbLayer = &list->hwLayers[last];
423        int fd = -1; //FenceFD from the Copybit(valid in async mode)
424        bool copybitDone = false;
425        if(ctx->mCopyBit[dpy])
426            copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
427
428        if(list->numHwLayers > 1)
429            hwc_sync(ctx, list, dpy, fd);
430
431        if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
432            ALOGE("%s: MDPComp draw failed", __FUNCTION__);
433            ret = -1;
434        }
435
436        private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
437        if(copybitDone) {
438            hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
439        }
440
441        if(hnd) {
442            if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
443                ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
444                ret = -1;
445            }
446        }
447
448        if (display_commit(ctx, dpy) < 0) {
449            ALOGE("%s: display commit fail!", __FUNCTION__);
450            ret = -1;
451        }
452    }
453
454    closeAcquireFds(list);
455    return ret;
456}
457
458static int hwc_set_virtual(hwc_context_t *ctx,
459                           hwc_display_contents_1_t* list, int dpy)
460{
461    //XXX: Implement set.
462    closeAcquireFds(list);
463    if (list) {
464        // SF assumes HWC waits for the acquire fence and returns a new fence
465        // that signals when we're done. Since we don't wait, and also don't
466        // touch the buffer, we can just handle the acquire fence back to SF
467        // as the retire fence.
468        list->retireFenceFd = list->outbufAcquireFenceFd;
469    }
470    return 0;
471}
472
473
474static int hwc_set(hwc_composer_device_1 *dev,
475                   size_t numDisplays,
476                   hwc_display_contents_1_t** displays)
477{
478    int ret = 0;
479    hwc_context_t* ctx = (hwc_context_t*)(dev);
480    for (uint32_t i = 0; i < numDisplays; i++) {
481        hwc_display_contents_1_t* list = displays[i];
482        switch(i) {
483            case HWC_DISPLAY_PRIMARY:
484                ret = hwc_set_primary(ctx, list);
485                break;
486            case HWC_DISPLAY_EXTERNAL:
487                ret = hwc_set_external(ctx, list, i);
488                break;
489            case HWC_DISPLAY_VIRTUAL:
490                ret = hwc_set_virtual(ctx, list, i);
491                break;
492            default:
493                ret = -EINVAL;
494        }
495    }
496    // This is only indicative of how many times SurfaceFlinger posts
497    // frames to the display.
498    CALC_FPS();
499    MDPComp::resetIdleFallBack();
500    ctx->mVideoTransFlag = false;
501    //Was locked at the beginning of prepare
502    ctx->mDrawLock.unlock();
503    return ret;
504}
505
506int hwc_getDisplayConfigs(struct hwc_composer_device_1* dev, int disp,
507        uint32_t* configs, size_t* numConfigs) {
508    int ret = 0;
509    hwc_context_t* ctx = (hwc_context_t*)(dev);
510    //in 1.1 there is no way to choose a config, report as config id # 0
511    //This config is passed to getDisplayAttributes. Ignore for now.
512    switch(disp) {
513        case HWC_DISPLAY_PRIMARY:
514            if(*numConfigs > 0) {
515                configs[0] = 0;
516                *numConfigs = 1;
517            }
518            ret = 0; //NO_ERROR
519            break;
520        case HWC_DISPLAY_EXTERNAL:
521            ret = -1; //Not connected
522            if(ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected) {
523                ret = 0; //NO_ERROR
524                if(*numConfigs > 0) {
525                    configs[0] = 0;
526                    *numConfigs = 1;
527                }
528            }
529            break;
530    }
531    return ret;
532}
533
534int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp,
535        uint32_t config, const uint32_t* attributes, int32_t* values) {
536
537    hwc_context_t* ctx = (hwc_context_t*)(dev);
538    //If hotpluggable displays are inactive return error
539    if(disp == HWC_DISPLAY_EXTERNAL && !ctx->dpyAttr[disp].connected) {
540        return -1;
541    }
542
543    //From HWComposer
544    static const uint32_t DISPLAY_ATTRIBUTES[] = {
545        HWC_DISPLAY_VSYNC_PERIOD,
546        HWC_DISPLAY_WIDTH,
547        HWC_DISPLAY_HEIGHT,
548        HWC_DISPLAY_DPI_X,
549        HWC_DISPLAY_DPI_Y,
550        HWC_DISPLAY_NO_ATTRIBUTE,
551    };
552
553    const int NUM_DISPLAY_ATTRIBUTES = (sizeof(DISPLAY_ATTRIBUTES) /
554            sizeof(DISPLAY_ATTRIBUTES)[0]);
555
556    for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
557        switch (attributes[i]) {
558        case HWC_DISPLAY_VSYNC_PERIOD:
559            values[i] = ctx->dpyAttr[disp].vsync_period;
560            break;
561        case HWC_DISPLAY_WIDTH:
562            values[i] = ctx->dpyAttr[disp].xres;
563            ALOGD("%s disp = %d, width = %d",__FUNCTION__, disp,
564                    ctx->dpyAttr[disp].xres);
565            break;
566        case HWC_DISPLAY_HEIGHT:
567            values[i] = ctx->dpyAttr[disp].yres;
568            ALOGD("%s disp = %d, height = %d",__FUNCTION__, disp,
569                    ctx->dpyAttr[disp].yres);
570            break;
571        case HWC_DISPLAY_DPI_X:
572            values[i] = (int32_t) (ctx->dpyAttr[disp].xdpi*1000.0);
573            break;
574        case HWC_DISPLAY_DPI_Y:
575            values[i] = (int32_t) (ctx->dpyAttr[disp].ydpi*1000.0);
576            break;
577        default:
578            ALOGE("Unknown display attribute %d",
579                    attributes[i]);
580            return -EINVAL;
581        }
582    }
583    return 0;
584}
585
586void hwc_dump(struct hwc_composer_device_1* dev, char *buff, int buff_len)
587{
588    hwc_context_t* ctx = (hwc_context_t*)(dev);
589    Locker::Autolock _l(ctx->mDrawLock);
590    android::String8 aBuf("");
591    dumpsys_log(aBuf, "Qualcomm HWC state:\n");
592    dumpsys_log(aBuf, "  MDPVersion=%d\n", ctx->mMDP.version);
593    dumpsys_log(aBuf, "  DisplayPanel=%c\n", ctx->mMDP.panel);
594    for(int dpy = 0; dpy < MAX_DISPLAYS; dpy++) {
595        if(ctx->mMDPComp[dpy])
596            ctx->mMDPComp[dpy]->dump(aBuf);
597    }
598    char ovDump[2048] = {'\0'};
599    ctx->mOverlay->getDump(ovDump, 2048);
600    dumpsys_log(aBuf, ovDump);
601    ovDump[0] = '\0';
602    ctx->mRotMgr->getDump(ovDump, 1024);
603    dumpsys_log(aBuf, ovDump);
604    strlcpy(buff, aBuf.string(), buff_len);
605}
606
607static int hwc_device_close(struct hw_device_t *dev)
608{
609    if(!dev) {
610        ALOGE("%s: NULL device pointer", __FUNCTION__);
611        return -1;
612    }
613    closeContext((hwc_context_t*)dev);
614    free(dev);
615
616    return 0;
617}
618
619static int hwc_device_open(const struct hw_module_t* module, const char* name,
620                           struct hw_device_t** device)
621{
622    int status = -EINVAL;
623
624    if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
625        struct hwc_context_t *dev;
626        dev = (hwc_context_t*)malloc(sizeof(*dev));
627        memset(dev, 0, sizeof(*dev));
628
629        //Initialize hwc context
630        initContext(dev);
631
632        //Setup HWC methods
633        dev->device.common.tag          = HARDWARE_DEVICE_TAG;
634        dev->device.common.version      = HWC_DEVICE_API_VERSION_1_3;
635        dev->device.common.module       = const_cast<hw_module_t*>(module);
636        dev->device.common.close        = hwc_device_close;
637        dev->device.prepare             = hwc_prepare;
638        dev->device.set                 = hwc_set;
639        dev->device.eventControl        = hwc_eventControl;
640        dev->device.blank               = hwc_blank;
641        dev->device.query               = hwc_query;
642        dev->device.registerProcs       = hwc_registerProcs;
643        dev->device.dump                = hwc_dump;
644        dev->device.getDisplayConfigs   = hwc_getDisplayConfigs;
645        dev->device.getDisplayAttributes = hwc_getDisplayAttributes;
646        *device = &dev->device.common;
647        status = 0;
648    }
649    return status;
650}
651