1 /* 2 * Copyright (C) 2010 The Android Open Source Project 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 17 /* 18 * 19 * @author Rama, Meka(v.meka (at) samsung.com) 20 Sangwoo, Park(sw5771.park (at) samsung.com) 21 Jamie Oh (jung-min.oh (at) samsung.com) 22 * @date 2011-07-28 23 * 24 */ 25 26 #include <sys/resource.h> 27 #include <cutils/log.h> 28 #include <cutils/atomic.h> 29 #include <EGL/egl.h> 30 #include <GLES/gl.h> 31 #include <hardware_legacy/uevent.h> 32 #include "SecHWCUtils.h" 33 34 static IMG_gralloc_module_public_t *gpsGrallocModule; 35 36 static int hwc_device_open(const struct hw_module_t* module, const char* name, 37 struct hw_device_t** device); 38 39 static struct hw_module_methods_t hwc_module_methods = { 40 open: hwc_device_open 41 }; 42 43 hwc_module_t HAL_MODULE_INFO_SYM = { 44 common: { 45 tag: HARDWARE_MODULE_TAG, 46 module_api_version: HWC_MODULE_API_VERSION_0_1, 47 hal_api_version: HARDWARE_HAL_API_VERSION, 48 id: HWC_HARDWARE_MODULE_ID, 49 name: "Samsung S5PC11X hwcomposer module", 50 author: "SAMSUNG", 51 methods: &hwc_module_methods, 52 } 53 }; 54 55 static void dump_layer(hwc_layer_t const* l) { 56 ALOGD("\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, {%d,%d,%d,%d}, {%d,%d,%d,%d}", 57 l->compositionType, l->flags, l->handle, l->transform, l->blending, 58 l->sourceCrop.left, 59 l->sourceCrop.top, 60 l->sourceCrop.right, 61 l->sourceCrop.bottom, 62 l->displayFrame.left, 63 l->displayFrame.top, 64 l->displayFrame.right, 65 l->displayFrame.bottom); 66 } 67 68 static int set_src_dst_info(hwc_layer_t *cur, 69 struct hwc_win_info_t *win, 70 struct sec_img *src_img, 71 struct sec_img *dst_img, 72 struct sec_rect *src_rect, 73 struct sec_rect *dst_rect, 74 int win_idx) 75 { 76 IMG_native_handle_t *prev_handle = (IMG_native_handle_t *)(cur->handle); 77 78 // set src image 79 src_img->w = prev_handle->iWidth; 80 src_img->h = prev_handle->iHeight; 81 src_img->format = prev_handle->iFormat; 82 src_img->base = NULL; 83 src_img->offset = 0; 84 src_img->mem_id =0; 85 86 src_img->mem_type = HWC_PHYS_MEM_TYPE; 87 src_img->w = (src_img->w + 15) & (~15); 88 src_img->h = (src_img->h + 1) & (~1) ; 89 90 //set src rect 91 src_rect->x = SEC_MAX(cur->sourceCrop.left, 0); 92 src_rect->y = SEC_MAX(cur->sourceCrop.top, 0); 93 src_rect->w = SEC_MAX(cur->sourceCrop.right - cur->sourceCrop.left, 0); 94 src_rect->w = SEC_MIN(src_rect->w, src_img->w - src_rect->x); 95 src_rect->h = SEC_MAX(cur->sourceCrop.bottom - cur->sourceCrop.top, 0); 96 src_rect->h = SEC_MIN(src_rect->h, src_img->h - src_rect->y); 97 98 //set dst image 99 dst_img->w = win->lcd_info.xres; 100 dst_img->h = win->lcd_info.yres; 101 102 switch (win->lcd_info.bits_per_pixel) { 103 case 32: 104 dst_img->format = HAL_PIXEL_FORMAT_RGBX_8888; 105 break; 106 default: 107 dst_img->format = HAL_PIXEL_FORMAT_RGB_565; 108 break; 109 } 110 111 dst_img->base = win->addr[win->buf_index]; 112 dst_img->offset = 0; 113 dst_img->mem_id = 0; 114 dst_img->mem_type = HWC_PHYS_MEM_TYPE; 115 116 //set dst rect 117 //fimc dst image will be stored from left top corner 118 dst_rect->x = 0; 119 dst_rect->y = 0; 120 dst_rect->w = win->rect_info.w; 121 dst_rect->h = win->rect_info.h; 122 123 ALOGV("%s::sr_x %d sr_y %d sr_w %d sr_h %d dr_x %d dr_y %d dr_w %d dr_h %d ", 124 __func__, src_rect->x, src_rect->y, src_rect->w, src_rect->h, 125 dst_rect->x, dst_rect->y, dst_rect->w, dst_rect->h); 126 127 return 0; 128 } 129 130 static int get_hwc_compos_decision(hwc_layer_t* cur) 131 { 132 if(cur->flags & HWC_SKIP_LAYER || !cur->handle) { 133 ALOGV("%s::is_skip_layer %d cur->handle %x", 134 __func__, cur->flags & HWC_SKIP_LAYER, (uint32_t)cur->handle); 135 return HWC_FRAMEBUFFER; 136 } 137 138 IMG_native_handle_t *prev_handle = (IMG_native_handle_t *)(cur->handle); 139 int compositionType = HWC_FRAMEBUFFER; 140 141 /* check here....if we have any resolution constraints */ 142 if (((cur->sourceCrop.right - cur->sourceCrop.left) < 16) || 143 ((cur->sourceCrop.bottom - cur->sourceCrop.top) < 8)) 144 return compositionType; 145 146 if ((cur->transform == HAL_TRANSFORM_ROT_90) || 147 (cur->transform == HAL_TRANSFORM_ROT_270)) { 148 if(((cur->displayFrame.right - cur->displayFrame.left) < 4)|| 149 ((cur->displayFrame.bottom - cur->displayFrame.top) < 8)) 150 return compositionType; 151 } else if (((cur->displayFrame.right - cur->displayFrame.left) < 8) || 152 ((cur->displayFrame.bottom - cur->displayFrame.top) < 4)) 153 return compositionType; 154 155 if((prev_handle->usage & GRALLOC_USAGE_PHYS_CONTIG) && 156 (cur->blending == HWC_BLENDING_NONE)) 157 compositionType = HWC_OVERLAY; 158 else 159 compositionType = HWC_FRAMEBUFFER; 160 161 ALOGV("%s::compositionType %d bpp %d format %x usage %x", 162 __func__,compositionType, prev_handle->uiBpp, prev_handle->iFormat, 163 prev_handle->usage & GRALLOC_USAGE_PHYS_CONTIG); 164 165 return compositionType; 166 } 167 168 static int assign_overlay_window(struct hwc_context_t *ctx, 169 hwc_layer_t *cur, 170 int win_idx, 171 int layer_idx) 172 { 173 struct hwc_win_info_t *win; 174 sec_rect rect; 175 int ret = 0; 176 177 if(NUM_OF_WIN <= win_idx) 178 return -1; 179 180 win = &ctx->win[win_idx]; 181 182 rect.x = SEC_MAX(cur->displayFrame.left, 0); 183 rect.y = SEC_MAX(cur->displayFrame.top, 0); 184 rect.w = SEC_MIN(cur->displayFrame.right - rect.x, win->lcd_info.xres - rect.x); 185 rect.h = SEC_MIN(cur->displayFrame.bottom - rect.y, win->lcd_info.yres - rect.y); 186 win->set_win_flag = 0; 187 188 if((rect.x != win->rect_info.x) || (rect.y != win->rect_info.y) || 189 (rect.w != win->rect_info.w) || (rect.h != win->rect_info.h)){ 190 win->rect_info.x = rect.x; 191 win->rect_info.y = rect.y; 192 win->rect_info.w = rect.w; 193 win->rect_info.h = rect.h; 194 win->set_win_flag = 1; 195 win->layer_prev_buf = 0; 196 } 197 198 win->layer_index = layer_idx; 199 win->status = HWC_WIN_RESERVED; 200 201 ALOGV("%s:: win_x %d win_y %d win_w %d win_h %d lay_idx %d win_idx %d", 202 __func__, win->rect_info.x, win->rect_info.y, win->rect_info.w, 203 win->rect_info.h, win->layer_index, win_idx ); 204 205 return 0; 206 } 207 208 static void reset_win_rect_info(hwc_win_info_t *win) 209 { 210 win->rect_info.x = 0; 211 win->rect_info.y = 0; 212 win->rect_info.w = 0; 213 win->rect_info.h = 0; 214 return; 215 } 216 217 static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) 218 { 219 220 struct hwc_context_t* ctx = (struct hwc_context_t*)dev; 221 int overlay_win_cnt = 0; 222 int compositionType = 0; 223 int ret; 224 225 //if geometry is not changed, there is no need to do any work here 226 if( !list || (!(list->flags & HWC_GEOMETRY_CHANGED))) 227 return 0; 228 229 //all the windows are free here.... 230 for (int i = 0; i < NUM_OF_WIN; i++) { 231 ctx->win[i].status = HWC_WIN_FREE; 232 ctx->win[i].buf_index = 0; 233 } 234 ctx->num_of_hwc_layer = 0; 235 ctx->num_of_fb_layer = 0; 236 ALOGV("%s:: hwc_prepare list->numHwLayers %d", __func__, list->numHwLayers); 237 238 for (int i = 0; i < list->numHwLayers ; i++) { 239 hwc_layer_t* cur = &list->hwLayers[i]; 240 241 if (overlay_win_cnt < NUM_OF_WIN) { 242 compositionType = get_hwc_compos_decision(cur); 243 244 if (compositionType == HWC_FRAMEBUFFER) { 245 cur->compositionType = HWC_FRAMEBUFFER; 246 ctx->num_of_fb_layer++; 247 } else { 248 ret = assign_overlay_window(ctx, cur, overlay_win_cnt, i); 249 if (ret != 0) { 250 cur->compositionType = HWC_FRAMEBUFFER; 251 ctx->num_of_fb_layer++; 252 continue; 253 } 254 255 cur->compositionType = HWC_OVERLAY; 256 cur->hints = HWC_HINT_CLEAR_FB; 257 overlay_win_cnt++; 258 ctx->num_of_hwc_layer++; 259 } 260 } else { 261 cur->compositionType = HWC_FRAMEBUFFER; 262 ctx->num_of_fb_layer++; 263 } 264 } 265 266 if(list->numHwLayers != (ctx->num_of_fb_layer + ctx->num_of_hwc_layer)) 267 ALOGV("%s:: numHwLayers %d num_of_fb_layer %d num_of_hwc_layer %d ", 268 __func__, list->numHwLayers, ctx->num_of_fb_layer, 269 ctx->num_of_hwc_layer); 270 271 if (overlay_win_cnt < NUM_OF_WIN) { 272 //turn off the free windows 273 for (int i = overlay_win_cnt; i < NUM_OF_WIN; i++) { 274 window_hide(&ctx->win[i]); 275 reset_win_rect_info(&ctx->win[i]); 276 } 277 } 278 return 0; 279 } 280 281 static int hwc_set(hwc_composer_device_t *dev, 282 hwc_display_t dpy, 283 hwc_surface_t sur, 284 hwc_layer_list_t* list) 285 { 286 struct hwc_context_t *ctx = (struct hwc_context_t *)dev; 287 unsigned int phyAddr[MAX_NUM_PLANES]; 288 int skipped_window_mask = 0; 289 hwc_layer_t* cur; 290 struct hwc_win_info_t *win; 291 int ret; 292 struct sec_img src_img; 293 struct sec_img dst_img; 294 struct sec_rect src_rect; 295 struct sec_rect dst_rect; 296 297 if (dpy == NULL && sur == NULL && list == NULL) { 298 // release our resources, the screen is turning off 299 // in our case, there is nothing to do. 300 ctx->num_of_fb_layer_prev = 0; 301 return 0; 302 } 303 304 bool need_swap_buffers = ctx->num_of_fb_layer > 0; 305 306 /* 307 * H/W composer documentation states: 308 * There is an implicit layer containing opaque black 309 * pixels behind all the layers in the list. 310 * It is the responsibility of the hwcomposer module to make 311 * sure black pixels are output (or blended from). 312 * 313 * Since we're using a blitter, we need to erase the frame-buffer when 314 * switching to all-overlay mode. 315 * 316 */ 317 if (ctx->num_of_hwc_layer && 318 ctx->num_of_fb_layer==0 && ctx->num_of_fb_layer_prev) { 319 /* we're clearing the screen using GLES here, this is very 320 * hack-ish, ideal we would use the fimc (if it can do it) */ 321 glDisable(GL_SCISSOR_TEST); 322 glClearColor(0, 0, 0, 0); 323 glClear(GL_COLOR_BUFFER_BIT); 324 glEnable(GL_SCISSOR_TEST); 325 need_swap_buffers = true; 326 } 327 328 ctx->num_of_fb_layer_prev = ctx->num_of_fb_layer; 329 330 if (need_swap_buffers || !list) { 331 EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur); 332 if (!sucess) { 333 return HWC_EGL_ERROR; 334 } 335 } 336 337 if (!list) { 338 /* turn off the all windows */ 339 for (int i = 0; i < NUM_OF_WIN; i++) { 340 window_hide(&ctx->win[i]); 341 reset_win_rect_info(&ctx->win[i]); 342 ctx->win[i].status = HWC_WIN_FREE; 343 } 344 ctx->num_of_hwc_layer = 0; 345 return 0; 346 } 347 348 if(ctx->num_of_hwc_layer > NUM_OF_WIN) 349 ctx->num_of_hwc_layer = NUM_OF_WIN; 350 351 /* compose hardware layers here */ 352 for (uint32_t i = 0; i < ctx->num_of_hwc_layer; i++) { 353 win = &ctx->win[i]; 354 if (win->status == HWC_WIN_RESERVED) { 355 cur = &list->hwLayers[win->layer_index]; 356 357 if (cur->compositionType == HWC_OVERLAY) { 358 359 ret = gpsGrallocModule->GetPhyAddrs(gpsGrallocModule, 360 cur->handle, phyAddr); 361 if (ret) { 362 ALOGE("%s::GetPhyAddrs fail : ret=%d\n", __func__, ret); 363 skipped_window_mask |= (1 << i); 364 continue; 365 } 366 367 /* initialize the src & dist context for fimc */ 368 set_src_dst_info (cur, win, &src_img, &dst_img, &src_rect, 369 &dst_rect, i); 370 371 ret = runFimc(ctx, &src_img, &src_rect, &dst_img, &dst_rect, 372 phyAddr, cur->transform); 373 if (ret < 0){ 374 ALOGE("%s::runFimc fail : ret=%d\n", __func__, ret); 375 skipped_window_mask |= (1 << i); 376 continue; 377 } 378 379 if (win->set_win_flag == 1) { 380 /* turnoff the window and set the window position with new conf... */ 381 if (window_set_pos(win) < 0) { 382 ALOGE("%s::window_set_pos is failed : %s", __func__, 383 strerror(errno)); 384 skipped_window_mask |= (1 << i); 385 continue; 386 } 387 win->set_win_flag = 0; 388 } 389 390 /* is the frame didn't change, it needs to be composited 391 * because something else below it could have changed, however 392 * it doesn't need to be swapped. 393 */ 394 if (win->layer_prev_buf != (uint32_t)cur->handle) { 395 win->layer_prev_buf = (uint32_t)cur->handle; 396 window_pan_display(win); 397 win->buf_index = (win->buf_index + 1) % NUM_OF_WIN_BUF; 398 } 399 400 if(win->power_state == 0) 401 window_show(win); 402 403 } else { 404 ALOGE("%s:: error : layer %d compositionType should have been \ 405 HWC_OVERLAY", __func__, win->layer_index); 406 skipped_window_mask |= (1 << i); 407 continue; 408 } 409 } else { 410 ALOGE("%s:: error : window status should have been HWC_WIN_RESERVED \ 411 by now... ", __func__); 412 skipped_window_mask |= (1 << i); 413 continue; 414 } 415 } 416 417 if (skipped_window_mask) { 418 //turn off the free windows 419 for (int i = 0; i < NUM_OF_WIN; i++) { 420 if (skipped_window_mask & (1 << i)) 421 window_hide(&ctx->win[i]); 422 } 423 } 424 425 return 0; 426 } 427 428 static void hwc_registerProcs(struct hwc_composer_device* dev, 429 hwc_procs_t const* procs) 430 { 431 struct hwc_context_t* ctx = (struct hwc_context_t*)dev; 432 ctx->procs = const_cast<hwc_procs_t *>(procs); 433 } 434 435 static int hwc_query(struct hwc_composer_device* dev, 436 int what, int* value) 437 { 438 struct hwc_context_t* ctx = (struct hwc_context_t*)dev; 439 440 switch (what) { 441 case HWC_BACKGROUND_LAYER_SUPPORTED: 442 // we don't support the background layer yet 443 value[0] = 0; 444 break; 445 case HWC_VSYNC_PERIOD: 446 // vsync period in nanosecond 447 value[0] = 1000000000.0 / gpsGrallocModule->psFrameBufferDevice->base.fps; 448 break; 449 default: 450 // unsupported query 451 return -EINVAL; 452 } 453 return 0; 454 } 455 456 static int hwc_eventControl(struct hwc_composer_device* dev, 457 int event, int enabled) 458 { 459 struct hwc_context_t* ctx = (struct hwc_context_t*)dev; 460 461 switch (event) { 462 case HWC_EVENT_VSYNC: 463 int val = !!enabled; 464 int err = ioctl(ctx->global_lcd_win.fd, S3CFB_SET_VSYNC_INT, &val); 465 if (err < 0) 466 return -errno; 467 468 return 0; 469 } 470 471 return -EINVAL; 472 } 473 474 void handle_vsync_uevent(hwc_context_t *ctx, const char *buff, int len) 475 { 476 uint64_t timestamp = 0; 477 const char *s = buff; 478 479 if(!ctx->procs || !ctx->procs->vsync) 480 return; 481 482 s += strlen(s) + 1; 483 484 while(*s) { 485 if (!strncmp(s, "VSYNC=", strlen("VSYNC="))) 486 timestamp = strtoull(s + strlen("VSYNC="), NULL, 0); 487 488 s += strlen(s) + 1; 489 if (s - buff >= len) 490 break; 491 } 492 493 ctx->procs->vsync(ctx->procs, 0, timestamp); 494 } 495 496 static void *hwc_vsync_thread(void *data) 497 { 498 hwc_context_t *ctx = (hwc_context_t *)(data); 499 char uevent_desc[4096]; 500 memset(uevent_desc, 0, sizeof(uevent_desc)); 501 502 setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); 503 504 uevent_init(); 505 while(true) { 506 int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2); 507 508 bool vsync = !strcmp(uevent_desc, "change@/devices/platform/s3cfb"); 509 if(vsync) 510 handle_vsync_uevent(ctx, uevent_desc, len); 511 } 512 513 return NULL; 514 } 515 516 static int hwc_device_close(struct hw_device_t *dev) 517 { 518 struct hwc_context_t* ctx = (struct hwc_context_t*)dev; 519 int ret = 0; 520 int i; 521 522 if (ctx) { 523 if (destroyFimc(&ctx->fimc) < 0) { 524 ALOGE("%s::destroyFimc fail", __func__); 525 ret = -1; 526 } 527 528 if (window_close(&ctx->global_lcd_win) < 0) { 529 ALOGE("%s::window_close() fail", __func__); 530 ret = -1; 531 } 532 533 for (i = 0; i < NUM_OF_WIN; i++) { 534 if (window_close(&ctx->win[i]) < 0) { 535 ALOGE("%s::window_close() fail", __func__); 536 ret = -1; 537 } 538 } 539 540 // TODO: stop vsync_thread 541 542 free(ctx); 543 } 544 return ret; 545 } 546 547 static const struct hwc_methods hwc_methods = { 548 eventControl: hwc_eventControl 549 }; 550 551 static int hwc_device_open(const struct hw_module_t* module, const char* name, 552 struct hw_device_t** device) 553 { 554 int status = 0; 555 int err; 556 struct hwc_win_info_t *win; 557 558 if(hw_get_module(GRALLOC_HARDWARE_MODULE_ID, 559 (const hw_module_t**)&gpsGrallocModule)) 560 return -EINVAL; 561 562 if(strcmp(gpsGrallocModule->base.common.author, "Imagination Technologies")) 563 return -EINVAL; 564 565 if (strcmp(name, HWC_HARDWARE_COMPOSER)) 566 return -EINVAL; 567 568 struct hwc_context_t *dev; 569 dev = (hwc_context_t*)malloc(sizeof(*dev)); 570 571 /* initialize our state here */ 572 memset(dev, 0, sizeof(*dev)); 573 574 /* initialize the procs */ 575 dev->device.common.tag = HARDWARE_DEVICE_TAG; 576 dev->device.common.version = HWC_DEVICE_API_VERSION_0_3; 577 dev->device.common.module = const_cast<hw_module_t*>(module); 578 dev->device.common.close = hwc_device_close; 579 580 dev->device.prepare = hwc_prepare; 581 dev->device.set = hwc_set; 582 dev->device.registerProcs = hwc_registerProcs; 583 dev->device.query = hwc_query; 584 dev->device.methods = &hwc_methods; 585 586 *device = &dev->device.common; 587 588 /* initializing */ 589 memset(&(dev->fimc), 0, sizeof(s5p_fimc_t)); 590 dev->fimc.dev_fd = -1; 591 592 /* open WIN0 & WIN1 here */ 593 for (int i = 0; i < NUM_OF_WIN; i++) { 594 if (window_open(&(dev->win[i]), i) < 0) { 595 ALOGE("%s:: Failed to open window %d device ", __func__, i); 596 status = -EINVAL; 597 goto err; 598 } 599 } 600 601 /* open window 2, used to query global LCD info */ 602 if (window_open(&dev->global_lcd_win, 2) < 0) { 603 ALOGE("%s:: Failed to open window 2 device ", __func__); 604 status = -EINVAL; 605 goto err; 606 } 607 608 /* get default window config */ 609 if (window_get_global_lcd_info(dev) < 0) { 610 ALOGE("%s::window_get_global_lcd_info is failed : %s", 611 __func__, strerror(errno)); 612 status = -EINVAL; 613 goto err; 614 } 615 616 dev->lcd_info.yres_virtual = dev->lcd_info.yres * NUM_OF_WIN_BUF; 617 618 /* initialize the window context */ 619 for (int i = 0; i < NUM_OF_WIN; i++) { 620 win = &dev->win[i]; 621 memcpy(&win->lcd_info, &dev->lcd_info, sizeof(struct fb_var_screeninfo)); 622 memcpy(&win->var_info, &dev->lcd_info, sizeof(struct fb_var_screeninfo)); 623 624 win->rect_info.x = 0; 625 win->rect_info.y = 0; 626 win->rect_info.w = win->var_info.xres; 627 win->rect_info.h = win->var_info.yres; 628 629 if (window_set_pos(win) < 0) { 630 ALOGE("%s::window_set_pos is failed : %s", 631 __func__, strerror(errno)); 632 status = -EINVAL; 633 goto err; 634 } 635 636 if (window_get_info(win) < 0) { 637 ALOGE("%s::window_get_info is failed : %s", 638 __func__, strerror(errno)); 639 status = -EINVAL; 640 goto err; 641 } 642 643 win->size = win->fix_info.line_length * win->var_info.yres; 644 645 if (!win->fix_info.smem_start){ 646 ALOGE("%s:: win-%d failed to get the reserved memory", __func__, i); 647 status = -EINVAL; 648 goto err; 649 } 650 651 for (int j = 0; j < NUM_OF_WIN_BUF; j++) { 652 win->addr[j] = win->fix_info.smem_start + (win->size * j); 653 ALOGI("%s::win-%d add[%d] %x ", __func__, i, j, win->addr[j]); 654 } 655 } 656 657 /* open pp */ 658 if (createFimc(&dev->fimc) < 0) { 659 ALOGE("%s::creatFimc() fail", __func__); 660 status = -EINVAL; 661 goto err; 662 } 663 664 err = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev); 665 if (err) { 666 ALOGE("%s::pthread_create() failed : %s", __func__, strerror(err)); 667 status = -err; 668 goto err; 669 } 670 671 ALOGD("%s:: success\n", __func__); 672 673 return 0; 674 675 err: 676 if (destroyFimc(&dev->fimc) < 0) 677 ALOGE("%s::destroyFimc() fail", __func__); 678 679 if (window_close(&dev->global_lcd_win) < 0) 680 ALOGE("%s::window_close() fail", __func__); 681 682 for (int i = 0; i < NUM_OF_WIN; i++) { 683 if (window_close(&dev->win[i]) < 0) 684 ALOGE("%s::window_close() fail", __func__); 685 } 686 687 return status; 688 } 689