1/* 2// Copyright (c) 2014 Intel Corporation 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#include <common/utils/HwcTrace.h> 18#include <ips/common/RotationBufferProvider.h> 19#include <system/graphics-base.h> 20 21namespace android { 22namespace intel { 23 24#define CHECK_VA_STATUS_RETURN(FUNC) \ 25if (vaStatus != VA_STATUS_SUCCESS) {\ 26 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ 27 return false;\ 28} 29 30#define CHECK_VA_STATUS_BREAK(FUNC) \ 31if (vaStatus != VA_STATUS_SUCCESS) {\ 32 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ 33 break;\ 34} 35 36// With this display value, VA will hook VED driver insead of VSP driver for buffer rotation 37#define DISPLAYVALUE 0x56454450 38 39RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm) 40 : mWsbm(wsbm), 41 mVaInitialized(false), 42 mVaDpy(0), 43 mVaCfg(0), 44 mVaCtx(0), 45 mVaBufFilter(0), 46 mSourceSurface(0), 47 mDisplay(DISPLAYVALUE), 48 mWidth(0), 49 mHeight(0), 50 mTransform(0), 51 mRotatedWidth(0), 52 mRotatedHeight(0), 53 mRotatedStride(0), 54 mTargetIndex(0), 55 mTTMWrappers(), 56 mBobDeinterlace(0) 57{ 58 for (int i = 0; i < MAX_SURFACE_NUM; i++) { 59 mKhandles[i] = 0; 60 mRotatedSurfaces[i] = 0; 61 mDrmBuf[i] = NULL; 62 } 63} 64 65RotationBufferProvider::~RotationBufferProvider() 66{ 67} 68 69uint32_t RotationBufferProvider::getMilliseconds() 70{ 71 struct timeval ptimeval; 72 gettimeofday(&ptimeval, NULL); 73 return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000)); 74} 75 76bool RotationBufferProvider::initialize() 77{ 78 if (NULL == mWsbm) 79 return false; 80 mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT); 81 return true; 82} 83 84void RotationBufferProvider::deinitialize() 85{ 86 stopVA(); 87 reset(); 88} 89 90void RotationBufferProvider::reset() 91{ 92 if (mTTMWrappers.size()) { 93 invalidateCaches(); 94 } 95} 96 97void RotationBufferProvider::invalidateCaches() 98{ 99 void *buf; 100 101 for (size_t i = 0; i < mTTMWrappers.size(); i++) { 102 buf = mTTMWrappers.valueAt(i); 103 if (!mWsbm->destroyTTMBuffer(buf)) 104 WLOGTRACE("failed to free TTMBuffer"); 105 } 106 mTTMWrappers.clear(); 107} 108 109int RotationBufferProvider::transFromHalToVa(int transform) 110{ 111 if (transform == HAL_TRANSFORM_ROT_90) 112 return VA_ROTATION_90; 113 if (transform == HAL_TRANSFORM_ROT_180) 114 return VA_ROTATION_180; 115 if (transform == HAL_TRANSFORM_ROT_270) 116 return VA_ROTATION_270; 117 return 0; 118} 119 120int RotationBufferProvider::getStride(bool isTarget, int width) 121{ 122 int stride = 0; 123 if (width <= 512) 124 stride = 512; 125 else if (width <= 1024) 126 stride = 1024; 127 else if (width <= 1280) { 128 stride = 1280; 129 if (isTarget) 130 stride = 2048; 131 } else if (width <= 2048) 132 stride = 2048; 133 else if (width <= 4096) 134 stride = 4096; 135 else 136 stride = (width + 0x3f) & ~0x3f; 137 return stride; 138} 139 140uint32_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf) 141{ 142 int size = width * height * 3 / 2; // YUV420 NV12 format 143 int allignment = 16 * 2048; // tiling row stride aligned 144 bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf); 145 146 if (ret == false) { 147 ELOGTRACE("failed to allocate TTM buffer"); 148 return 0; 149 } 150 151 return mWsbm->getKBufHandle(*buf); 152} 153 154bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget) 155{ 156 VAStatus vaStatus; 157 VASurfaceAttributeTPI attribTpi; 158 VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi; 159 int stride; 160 unsigned long buffers; 161 VASurfaceID *surface; 162 int width = 0, height = 0, bufferHeight = 0; 163 164 if (isTarget) { 165 if (transFromHalToVa(transform) == VA_ROTATION_180) { 166 width = payload->width; 167 height = payload->height; 168 } else { 169 width = payload->height; 170 height = payload->width; 171 } 172 mRotatedWidth = width; 173 mRotatedHeight = height; 174 bufferHeight = (height + 0x1f) & ~0x1f; 175 stride = getStride(isTarget, width); 176 } else { 177 width = payload->width; 178 height = payload->height; 179 bufferHeight = payload->height; 180 stride = payload->luma_stride; /* NV12 srouce buffer */ 181 } 182 183 if (!stride) { 184 ELOGTRACE("invalid stride value"); 185 return false; 186 } 187 188 // adjust source target for Bob deinterlace 189 if (!isTarget && mBobDeinterlace) { 190 height >>= 1; 191 bufferHeight >>= 1; 192 stride <<= 1; 193 } 194 195 vaSurfaceAttrib->count = 1; 196 vaSurfaceAttrib->width = width; 197 vaSurfaceAttrib->height = height; 198 vaSurfaceAttrib->pixel_format = payload->format; 199 vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer; 200 vaSurfaceAttrib->tiling = payload->tiling; 201 vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2; 202 vaSurfaceAttrib->luma_offset = 0; 203 vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight; 204 vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride 205 = vaSurfaceAttrib->chroma_v_stride 206 = stride; 207 vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset; 208 vaSurfaceAttrib->buffers = &buffers; 209 210 if (isTarget) { 211 int khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]); 212 if (khandle == 0) { 213 ELOGTRACE("failed to create buffer by wsbm"); 214 return false; 215 } 216 217 mKhandles[mTargetIndex] = khandle; 218 vaSurfaceAttrib->buffers[0] = khandle; 219 mRotatedStride = stride; 220 surface = &mRotatedSurfaces[mTargetIndex]; 221 } else { 222 vaSurfaceAttrib->buffers[0] = payload->khandle; 223 surface = &mSourceSurface; 224 /* set src surface width/height to video crop size */ 225 if (payload->crop_width && payload->crop_height) { 226 width = payload->crop_width; 227 height = (payload->crop_height >> mBobDeinterlace); 228 } else { 229 VLOGTRACE("Invalid cropping width or height"); 230 payload->crop_width = width; 231 payload->crop_height = height; 232 } 233 } 234 235 vaStatus = vaCreateSurfacesWithAttribute(mVaDpy, 236 width, 237 height, 238 VA_RT_FORMAT_YUV420, 239 1, 240 surface, 241 vaSurfaceAttrib); 242 if (vaStatus != VA_STATUS_SUCCESS) { 243 ELOGTRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus); 244 ELOGTRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d", 245 isTarget, width, height, bufferHeight, payload->tiling); 246 *surface = 0; 247 return false; 248 } 249 250 return true; 251} 252 253bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform) 254{ 255 bool ret = true; 256 VAStatus vaStatus; 257 VAEntrypoint *entryPoint; 258 VAConfigAttrib attribDummy; 259 int numEntryPoints; 260 bool supportVideoProcessing = false; 261 int majorVer = 0, minorVer = 0; 262 263 // VA will hold a copy of the param pointer, so local varialbe doesn't work 264 mVaDpy = vaGetDisplay(&mDisplay); 265 if (NULL == mVaDpy) { 266 ELOGTRACE("failed to get VADisplay"); 267 return false; 268 } 269 270 vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer); 271 CHECK_VA_STATUS_RETURN("vaInitialize"); 272 273 numEntryPoints = vaMaxNumEntrypoints(mVaDpy); 274 275 if (numEntryPoints <= 0) { 276 ELOGTRACE("numEntryPoints value is invalid"); 277 return false; 278 } 279 280 entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints); 281 if (NULL == entryPoint) { 282 ELOGTRACE("failed to malloc memory for entryPoint"); 283 return false; 284 } 285 286 vaStatus = vaQueryConfigEntrypoints(mVaDpy, 287 VAProfileNone, 288 entryPoint, 289 &numEntryPoints); 290 CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints"); 291 292 for (int i = 0; i < numEntryPoints; i++) 293 if (entryPoint[i] == VAEntrypointVideoProc) 294 supportVideoProcessing = true; 295 296 free(entryPoint); 297 entryPoint = NULL; 298 299 if (!supportVideoProcessing) { 300 ELOGTRACE("VAEntrypointVideoProc is not supported"); 301 return false; 302 } 303 304 vaStatus = vaCreateConfig(mVaDpy, 305 VAProfileNone, 306 VAEntrypointVideoProc, 307 &attribDummy, 308 0, 309 &mVaCfg); 310 CHECK_VA_STATUS_RETURN("vaCreateConfig"); 311 312 // create first target surface 313 ret = createVaSurface(payload, transform, true); 314 if (ret == false) { 315 ELOGTRACE("failed to create target surface with attribute"); 316 return false; 317 } 318 319 vaStatus = vaCreateContext(mVaDpy, 320 mVaCfg, 321 payload->width, 322 payload->height, 323 0, 324 &mRotatedSurfaces[0], 325 1, 326 &mVaCtx); 327 CHECK_VA_STATUS_RETURN("vaCreateContext"); 328 329 VAProcFilterType filters[VAProcFilterCount]; 330 unsigned int numFilters = VAProcFilterCount; 331 vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters); 332 CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters"); 333 334 bool supportVideoProcFilter = false; 335 for (unsigned int j = 0; j < numFilters; j++) 336 if (filters[j] == VAProcFilterNone) 337 supportVideoProcFilter = true; 338 339 if (!supportVideoProcFilter) { 340 ELOGTRACE("VAProcFilterNone is not supported"); 341 return false; 342 } 343 344 VAProcFilterParameterBuffer filter; 345 filter.type = VAProcFilterNone; 346 filter.value = 0; 347 348 vaStatus = vaCreateBuffer(mVaDpy, 349 mVaCtx, 350 VAProcFilterParameterBufferType, 351 sizeof(filter), 352 1, 353 &filter, 354 &mVaBufFilter); 355 CHECK_VA_STATUS_RETURN("vaCreateBuffer"); 356 357 VAProcPipelineCaps pipelineCaps; 358 unsigned int numCaps = 1; 359 vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy, 360 mVaCtx, 361 &mVaBufFilter, 362 numCaps, 363 &pipelineCaps); 364 CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps"); 365 366 if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) { 367 ELOGTRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter", 368 transFromHalToVa(transform)); 369 return false; 370 } 371 372 mBobDeinterlace = payload->bob_deinterlace; 373 mVaInitialized = true; 374 375 return true; 376} 377 378bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform) 379{ 380#ifdef DEBUG_ROTATION_PERFROMANCE 381 uint32_t setup_Begin = getMilliseconds(); 382#endif 383 VAStatus vaStatus; 384 bool ret = false; 385 386 if (payload->format != VA_FOURCC_NV12 || payload->width == 0 || payload->height == 0) { 387 WLOGTRACE("payload data is not correct: format %#x, width %d, height %d", 388 payload->format, payload->width, payload->height); 389 return ret; 390 } 391 392 if (payload->width > 1280) { 393 payload->tiling = 1; 394 } 395 396 do { 397 if (isContextChanged(payload->width, payload->height, transform)) { 398 DLOGTRACE("VA is restarted as rotation context changes"); 399 400 if (mVaInitialized) { 401 stopVA(); // need to re-initialize VA for new rotation config 402 } 403 mTransform = transform; 404 mWidth = payload->width; 405 mHeight = payload->height; 406 } 407 408 if (!mVaInitialized) { 409 ret = startVA(payload, transform); 410 if (ret == false) { 411 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 412 break; 413 } 414 } 415 416 // start to create next target surface 417 if (!mRotatedSurfaces[mTargetIndex]) { 418 ret = createVaSurface(payload, transform, true); 419 if (ret == false) { 420 ELOGTRACE("failed to create target surface with attribute"); 421 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 422 break; 423 } 424 } 425 426 // create source surface 427 ret = createVaSurface(payload, transform, false); 428 if (ret == false) { 429 ELOGTRACE("failed to create source surface with attribute"); 430 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 431 break; 432 } 433 434#ifdef DEBUG_ROTATION_PERFROMANCE 435 uint32_t beginPicture = getMilliseconds(); 436#endif 437 vaStatus = vaBeginPicture(mVaDpy, mVaCtx, mRotatedSurfaces[mTargetIndex]); 438 CHECK_VA_STATUS_BREAK("vaBeginPicture"); 439 440 VABufferID pipelineBuf; 441 void *p; 442 VAProcPipelineParameterBuffer *pipelineParam; 443 vaStatus = vaCreateBuffer(mVaDpy, 444 mVaCtx, 445 VAProcPipelineParameterBufferType, 446 sizeof(*pipelineParam), 447 1, 448 NULL, 449 &pipelineBuf); 450 CHECK_VA_STATUS_BREAK("vaCreateBuffer"); 451 452 vaStatus = vaMapBuffer(mVaDpy, pipelineBuf, &p); 453 CHECK_VA_STATUS_BREAK("vaMapBuffer"); 454 455 pipelineParam = (VAProcPipelineParameterBuffer*)p; 456 pipelineParam->surface = mSourceSurface; 457 pipelineParam->rotation_state = transFromHalToVa(transform); 458 pipelineParam->filters = &mVaBufFilter; 459 pipelineParam->num_filters = 1; 460 vaStatus = vaUnmapBuffer(mVaDpy, pipelineBuf); 461 CHECK_VA_STATUS_BREAK("vaUnmapBuffer"); 462 463 vaStatus = vaRenderPicture(mVaDpy, mVaCtx, &pipelineBuf, 1); 464 CHECK_VA_STATUS_BREAK("vaRenderPicture"); 465 466 vaStatus = vaEndPicture(mVaDpy, mVaCtx); 467 CHECK_VA_STATUS_BREAK("vaEndPicture"); 468 469 vaStatus = vaSyncSurface(mVaDpy, mRotatedSurfaces[mTargetIndex]); 470 CHECK_VA_STATUS_BREAK("vaSyncSurface"); 471 472#ifdef DEBUG_ROTATION_PERFROMANCE 473 ILOGTRACE("time spent %dms from vaBeginPicture to vaSyncSurface", 474 getMilliseconds() - beginPicture); 475#endif 476 477 // Populate payload fields so that overlayPlane can flip the buffer 478 payload->rotated_width = mRotatedStride; 479 payload->rotated_height = mRotatedHeight; 480 payload->rotated_buffer_handle = mKhandles[mTargetIndex]; 481 // setting client transform to 0 to force re-generating rotated buffer whenever needed. 482 payload->client_transform = 0; 483 mTargetIndex++; 484 if (mTargetIndex >= MAX_SURFACE_NUM) 485 mTargetIndex = 0; 486 487 } while (0); 488 489#ifdef DEBUG_ROTATION_PERFROMANCE 490 ILOGTRACE("time spent %dms for setupRotationBuffer", 491 getMilliseconds() - setup_Begin); 492#endif 493 494 if (mSourceSurface > 0) { 495 vaStatus = vaDestroySurfaces(mVaDpy, &mSourceSurface, 1); 496 if (vaStatus != VA_STATUS_SUCCESS) 497 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); 498 mSourceSurface = 0; 499 } 500 501 if (vaStatus != VA_STATUS_SUCCESS) { 502 stopVA(); 503 return false; // To not block HWC, just abort instead of retry 504 } 505 506 if (!payload->khandle) { 507 WLOGTRACE("khandle is reset by decoder, surface is invalid!"); 508 return false; 509 } 510 511 return true; 512} 513 514bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt) 515{ 516 int size; 517 void *buf = NULL; 518 519 payload->width = payload->crop_width = w; 520 payload->height = payload->crop_height = h; 521 payload->format = VA_FOURCC_NV12; 522 payload->tiling = 1; 523 payload->luma_stride = stride; 524 payload->chroma_u_stride = stride; 525 payload->chroma_v_stride = stride; 526 payload->client_transform = 0; 527 528 size = stride * h + stride * h / 2; 529 530 ssize_t index; 531 index = mTTMWrappers.indexOfKey((uint64_t)user_pt); 532 if (index < 0) { 533 VLOGTRACE("wrapped userPt as wsbm buffer"); 534 bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt); 535 if (ret == false) { 536 ELOGTRACE("failed to allocate TTM buffer"); 537 return ret; 538 } 539 540 if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) { 541 WLOGTRACE("mTTMWrappers is unexpectedly full. Invalidate caches"); 542 invalidateCaches(); 543 } 544 545 index = mTTMWrappers.add((uint64_t)user_pt, buf); 546 } else { 547 VLOGTRACE("got wsbmBuffer in saved caches"); 548 buf = mTTMWrappers.valueAt(index); 549 } 550 551 payload->khandle = mWsbm->getKBufHandle(buf); 552 return true; 553} 554 555void RotationBufferProvider::freeVaSurfaces() 556{ 557 bool ret; 558 VAStatus vaStatus; 559 560 for (int i = 0; i < MAX_SURFACE_NUM; i++) { 561 if (NULL != mDrmBuf[i]) { 562 ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]); 563 if (!ret) 564 WLOGTRACE("failed to free TTMBuffer"); 565 mDrmBuf[i] = NULL; 566 } 567 } 568 569 // remove wsbm buffer ref from VA 570 for (int j = 0; j < MAX_SURFACE_NUM; j++) { 571 if (0 != mRotatedSurfaces[j]) { 572 vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1); 573 if (vaStatus != VA_STATUS_SUCCESS) 574 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); 575 } 576 mRotatedSurfaces[j] = 0; 577 } 578} 579 580void RotationBufferProvider::stopVA() 581{ 582 freeVaSurfaces(); 583 584 if (0 != mVaBufFilter) 585 vaDestroyBuffer(mVaDpy, mVaBufFilter); 586 if (0 != mVaCfg) 587 vaDestroyConfig(mVaDpy,mVaCfg); 588 if (0 != mVaCtx) 589 vaDestroyContext(mVaDpy, mVaCtx); 590 if (0 != mVaDpy) 591 vaTerminate(mVaDpy); 592 593 mVaInitialized = false; 594 595 // reset VA variable 596 mVaDpy = 0; 597 mVaCfg = 0; 598 mVaCtx = 0; 599 mVaBufFilter = 0; 600 mSourceSurface = 0; 601 602 mWidth = 0; 603 mHeight = 0; 604 mRotatedWidth = 0; 605 mRotatedHeight = 0; 606 mRotatedStride = 0; 607 mTargetIndex = 0; 608} 609 610bool RotationBufferProvider::isContextChanged(int width, int height, int transform) 611{ 612 // check rotation config 613 if (height == mHeight && 614 width == mWidth && 615 transform == mTransform) { 616 return false; 617 } 618 619 return true; 620} 621 622} // name space intel 623} // name space android 624