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
20 namespace android {
21 namespace intel {
22
23 #define CHECK_VA_STATUS_RETURN(FUNC) \
24 if (vaStatus != VA_STATUS_SUCCESS) {\
25 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\
26 return false;\
27 }
28
29 #define CHECK_VA_STATUS_BREAK(FUNC) \
30 if (vaStatus != VA_STATUS_SUCCESS) {\
31 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\
32 break;\
33 }
34
35 // With this display value, VA will hook VED driver insead of VSP driver for buffer rotation
36 #define DISPLAYVALUE 0x56454450
37
RotationBufferProvider(Wsbm * wsbm)38 RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm)
39 : mWsbm(wsbm),
40 mVaInitialized(false),
41 mVaDpy(0),
42 mVaCfg(0),
43 mVaCtx(0),
44 mVaBufFilter(0),
45 mSourceSurface(0),
46 mDisplay(DISPLAYVALUE),
47 mWidth(0),
48 mHeight(0),
49 mTransform(0),
50 mRotatedWidth(0),
51 mRotatedHeight(0),
52 mRotatedStride(0),
53 mTargetIndex(0),
54 mTTMWrappers(),
55 mBobDeinterlace(0)
56 {
57 for (int i = 0; i < MAX_SURFACE_NUM; i++) {
58 mKhandles[i] = 0;
59 mRotatedSurfaces[i] = 0;
60 mDrmBuf[i] = NULL;
61 }
62 }
63
~RotationBufferProvider()64 RotationBufferProvider::~RotationBufferProvider()
65 {
66 }
67
getMilliseconds()68 uint32_t RotationBufferProvider::getMilliseconds()
69 {
70 struct timeval ptimeval;
71 gettimeofday(&ptimeval, NULL);
72 return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000));
73 }
74
initialize()75 bool RotationBufferProvider::initialize()
76 {
77 if (NULL == mWsbm)
78 return false;
79 mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT);
80 return true;
81 }
82
deinitialize()83 void RotationBufferProvider::deinitialize()
84 {
85 stopVA();
86 reset();
87 }
88
reset()89 void RotationBufferProvider::reset()
90 {
91 if (mTTMWrappers.size()) {
92 invalidateCaches();
93 }
94 }
95
invalidateCaches()96 void RotationBufferProvider::invalidateCaches()
97 {
98 void *buf;
99
100 for (size_t i = 0; i < mTTMWrappers.size(); i++) {
101 buf = mTTMWrappers.valueAt(i);
102 if (!mWsbm->destroyTTMBuffer(buf))
103 WLOGTRACE("failed to free TTMBuffer");
104 }
105 mTTMWrappers.clear();
106 }
107
transFromHalToVa(int transform)108 int RotationBufferProvider::transFromHalToVa(int transform)
109 {
110 if (transform == HAL_TRANSFORM_ROT_90)
111 return VA_ROTATION_90;
112 if (transform == HAL_TRANSFORM_ROT_180)
113 return VA_ROTATION_180;
114 if (transform == HAL_TRANSFORM_ROT_270)
115 return VA_ROTATION_270;
116 return 0;
117 }
118
getStride(bool isTarget,int width)119 int RotationBufferProvider::getStride(bool isTarget, int width)
120 {
121 int stride = 0;
122 if (width <= 512)
123 stride = 512;
124 else if (width <= 1024)
125 stride = 1024;
126 else if (width <= 1280) {
127 stride = 1280;
128 if (isTarget)
129 stride = 2048;
130 } else if (width <= 2048)
131 stride = 2048;
132 else if (width <= 4096)
133 stride = 4096;
134 else
135 stride = (width + 0x3f) & ~0x3f;
136 return stride;
137 }
138
createWsbmBuffer(int width,int height,void ** buf)139 uint32_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf)
140 {
141 int size = width * height * 3 / 2; // YUV420 NV12 format
142 int allignment = 16 * 2048; // tiling row stride aligned
143 bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf);
144
145 if (ret == false) {
146 ELOGTRACE("failed to allocate TTM buffer");
147 return 0;
148 }
149
150 return mWsbm->getKBufHandle(*buf);
151 }
152
createVaSurface(VideoPayloadBuffer * payload,int transform,bool isTarget)153 bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget)
154 {
155 VAStatus vaStatus;
156 VASurfaceAttributeTPI attribTpi;
157 VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi;
158 int stride;
159 unsigned long buffers;
160 VASurfaceID *surface;
161 int width = 0, height = 0, bufferHeight = 0;
162
163 if (isTarget) {
164 if (transFromHalToVa(transform) == VA_ROTATION_180) {
165 width = payload->width;
166 height = payload->height;
167 } else {
168 width = payload->height;
169 height = payload->width;
170 }
171 mRotatedWidth = width;
172 mRotatedHeight = height;
173 bufferHeight = (height + 0x1f) & ~0x1f;
174 stride = getStride(isTarget, width);
175 } else {
176 width = payload->width;
177 height = payload->height;
178 bufferHeight = payload->height;
179 stride = payload->luma_stride; /* NV12 srouce buffer */
180 }
181
182 if (!stride) {
183 ELOGTRACE("invalid stride value");
184 return false;
185 }
186
187 // adjust source target for Bob deinterlace
188 if (!isTarget && mBobDeinterlace) {
189 height >>= 1;
190 bufferHeight >>= 1;
191 stride <<= 1;
192 }
193
194 vaSurfaceAttrib->count = 1;
195 vaSurfaceAttrib->width = width;
196 vaSurfaceAttrib->height = height;
197 vaSurfaceAttrib->pixel_format = payload->format;
198 vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer;
199 vaSurfaceAttrib->tiling = payload->tiling;
200 vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2;
201 vaSurfaceAttrib->luma_offset = 0;
202 vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight;
203 vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride
204 = vaSurfaceAttrib->chroma_v_stride
205 = stride;
206 vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset;
207 vaSurfaceAttrib->buffers = &buffers;
208
209 if (isTarget) {
210 int khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]);
211 if (khandle == 0) {
212 ELOGTRACE("failed to create buffer by wsbm");
213 return false;
214 }
215
216 mKhandles[mTargetIndex] = khandle;
217 vaSurfaceAttrib->buffers[0] = khandle;
218 mRotatedStride = stride;
219 surface = &mRotatedSurfaces[mTargetIndex];
220 } else {
221 vaSurfaceAttrib->buffers[0] = payload->khandle;
222 surface = &mSourceSurface;
223 /* set src surface width/height to video crop size */
224 if (payload->crop_width && payload->crop_height) {
225 width = payload->crop_width;
226 height = (payload->crop_height >> mBobDeinterlace);
227 } else {
228 VLOGTRACE("Invalid cropping width or height");
229 payload->crop_width = width;
230 payload->crop_height = height;
231 }
232 }
233
234 vaStatus = vaCreateSurfacesWithAttribute(mVaDpy,
235 width,
236 height,
237 VA_RT_FORMAT_YUV420,
238 1,
239 surface,
240 vaSurfaceAttrib);
241 if (vaStatus != VA_STATUS_SUCCESS) {
242 ELOGTRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus);
243 ELOGTRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d",
244 isTarget, width, height, bufferHeight, payload->tiling);
245 *surface = 0;
246 return false;
247 }
248
249 return true;
250 }
251
startVA(VideoPayloadBuffer * payload,int transform)252 bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform)
253 {
254 bool ret = true;
255 VAStatus vaStatus;
256 VAEntrypoint *entryPoint;
257 VAConfigAttrib attribDummy;
258 int numEntryPoints;
259 bool supportVideoProcessing = false;
260 int majorVer = 0, minorVer = 0;
261
262 // VA will hold a copy of the param pointer, so local varialbe doesn't work
263 mVaDpy = vaGetDisplay(&mDisplay);
264 if (NULL == mVaDpy) {
265 ELOGTRACE("failed to get VADisplay");
266 return false;
267 }
268
269 vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer);
270 CHECK_VA_STATUS_RETURN("vaInitialize");
271
272 numEntryPoints = vaMaxNumEntrypoints(mVaDpy);
273
274 if (numEntryPoints <= 0) {
275 ELOGTRACE("numEntryPoints value is invalid");
276 return false;
277 }
278
279 entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints);
280 if (NULL == entryPoint) {
281 ELOGTRACE("failed to malloc memory for entryPoint");
282 return false;
283 }
284
285 vaStatus = vaQueryConfigEntrypoints(mVaDpy,
286 VAProfileNone,
287 entryPoint,
288 &numEntryPoints);
289 CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints");
290
291 for (int i = 0; i < numEntryPoints; i++)
292 if (entryPoint[i] == VAEntrypointVideoProc)
293 supportVideoProcessing = true;
294
295 free(entryPoint);
296 entryPoint = NULL;
297
298 if (!supportVideoProcessing) {
299 ELOGTRACE("VAEntrypointVideoProc is not supported");
300 return false;
301 }
302
303 vaStatus = vaCreateConfig(mVaDpy,
304 VAProfileNone,
305 VAEntrypointVideoProc,
306 &attribDummy,
307 0,
308 &mVaCfg);
309 CHECK_VA_STATUS_RETURN("vaCreateConfig");
310
311 // create first target surface
312 ret = createVaSurface(payload, transform, true);
313 if (ret == false) {
314 ELOGTRACE("failed to create target surface with attribute");
315 return false;
316 }
317
318 vaStatus = vaCreateContext(mVaDpy,
319 mVaCfg,
320 payload->width,
321 payload->height,
322 0,
323 &mRotatedSurfaces[0],
324 1,
325 &mVaCtx);
326 CHECK_VA_STATUS_RETURN("vaCreateContext");
327
328 VAProcFilterType filters[VAProcFilterCount];
329 unsigned int numFilters = VAProcFilterCount;
330 vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters);
331 CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters");
332
333 bool supportVideoProcFilter = false;
334 for (unsigned int j = 0; j < numFilters; j++)
335 if (filters[j] == VAProcFilterNone)
336 supportVideoProcFilter = true;
337
338 if (!supportVideoProcFilter) {
339 ELOGTRACE("VAProcFilterNone is not supported");
340 return false;
341 }
342
343 VAProcFilterParameterBuffer filter;
344 filter.type = VAProcFilterNone;
345 filter.value = 0;
346
347 vaStatus = vaCreateBuffer(mVaDpy,
348 mVaCtx,
349 VAProcFilterParameterBufferType,
350 sizeof(filter),
351 1,
352 &filter,
353 &mVaBufFilter);
354 CHECK_VA_STATUS_RETURN("vaCreateBuffer");
355
356 VAProcPipelineCaps pipelineCaps;
357 unsigned int numCaps = 1;
358 vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy,
359 mVaCtx,
360 &mVaBufFilter,
361 numCaps,
362 &pipelineCaps);
363 CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps");
364
365 if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) {
366 ELOGTRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter",
367 transFromHalToVa(transform));
368 return false;
369 }
370
371 mBobDeinterlace = payload->bob_deinterlace;
372 mVaInitialized = true;
373
374 return true;
375 }
376
setupRotationBuffer(VideoPayloadBuffer * payload,int transform)377 bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform)
378 {
379 #ifdef DEBUG_ROTATION_PERFROMANCE
380 uint32_t setup_Begin = getMilliseconds();
381 #endif
382 VAStatus vaStatus;
383 int stride;
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
prepareBufferInfo(int w,int h,int stride,VideoPayloadBuffer * payload,void * user_pt)514 bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt)
515 {
516 int chroma_offset, 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 chroma_offset = stride * h;
529 size = stride * h + stride * h / 2;
530
531 ssize_t index;
532 index = mTTMWrappers.indexOfKey((uint64_t)user_pt);
533 if (index < 0) {
534 VLOGTRACE("wrapped userPt as wsbm buffer");
535 bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt);
536 if (ret == false) {
537 ELOGTRACE("failed to allocate TTM buffer");
538 return ret;
539 }
540
541 if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) {
542 WLOGTRACE("mTTMWrappers is unexpectedly full. Invalidate caches");
543 invalidateCaches();
544 }
545
546 index = mTTMWrappers.add((uint64_t)user_pt, buf);
547 } else {
548 VLOGTRACE("got wsbmBuffer in saved caches");
549 buf = mTTMWrappers.valueAt(index);
550 }
551
552 payload->khandle = mWsbm->getKBufHandle(buf);
553 return true;
554 }
555
freeVaSurfaces()556 void RotationBufferProvider::freeVaSurfaces()
557 {
558 bool ret;
559 VAStatus vaStatus;
560
561 for (int i = 0; i < MAX_SURFACE_NUM; i++) {
562 if (NULL != mDrmBuf[i]) {
563 ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]);
564 if (!ret)
565 WLOGTRACE("failed to free TTMBuffer");
566 mDrmBuf[i] = NULL;
567 }
568 }
569
570 // remove wsbm buffer ref from VA
571 for (int j = 0; j < MAX_SURFACE_NUM; j++) {
572 if (0 != mRotatedSurfaces[j]) {
573 vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1);
574 if (vaStatus != VA_STATUS_SUCCESS)
575 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus);
576 }
577 mRotatedSurfaces[j] = 0;
578 }
579 }
580
stopVA()581 void RotationBufferProvider::stopVA()
582 {
583 freeVaSurfaces();
584
585 if (0 != mVaBufFilter)
586 vaDestroyBuffer(mVaDpy, mVaBufFilter);
587 if (0 != mVaCfg)
588 vaDestroyConfig(mVaDpy,mVaCfg);
589 if (0 != mVaCtx)
590 vaDestroyContext(mVaDpy, mVaCtx);
591 if (0 != mVaDpy)
592 vaTerminate(mVaDpy);
593
594 mVaInitialized = false;
595
596 // reset VA variable
597 mVaDpy = 0;
598 mVaCfg = 0;
599 mVaCtx = 0;
600 mVaBufFilter = 0;
601 mSourceSurface = 0;
602
603 mWidth = 0;
604 mHeight = 0;
605 mRotatedWidth = 0;
606 mRotatedHeight = 0;
607 mRotatedStride = 0;
608 mTargetIndex = 0;
609 }
610
isContextChanged(int width,int height,int transform)611 bool RotationBufferProvider::isContextChanged(int width, int height, int transform)
612 {
613 // check rotation config
614 if (height == mHeight &&
615 width == mWidth &&
616 transform == mTransform) {
617 return false;
618 }
619
620 return true;
621 }
622
623 } // name space intel
624 } // name space android
625