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