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