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     bool ret = false;
384 
385     if (payload->format != VA_FOURCC_NV12 || payload->width == 0 || payload->height == 0) {
386         WLOGTRACE("payload data is not correct: format %#x, width %d, height %d",
387             payload->format, payload->width, payload->height);
388         return ret;
389     }
390 
391     if (payload->width > 1280) {
392         payload->tiling = 1;
393     }
394 
395     do {
396         if (isContextChanged(payload->width, payload->height, transform)) {
397             DLOGTRACE("VA is restarted as rotation context changes");
398 
399             if (mVaInitialized) {
400                 stopVA(); // need to re-initialize VA for new rotation config
401             }
402             mTransform = transform;
403             mWidth = payload->width;
404             mHeight = payload->height;
405         }
406 
407         if (!mVaInitialized) {
408             ret = startVA(payload, transform);
409             if (ret == false) {
410                 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
411                 break;
412             }
413         }
414 
415         // start to create next target surface
416         if (!mRotatedSurfaces[mTargetIndex]) {
417             ret = createVaSurface(payload, transform, true);
418             if (ret == false) {
419                 ELOGTRACE("failed to create target surface with attribute");
420                 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
421                 break;
422             }
423         }
424 
425         // create source surface
426         ret = createVaSurface(payload, transform, false);
427         if (ret == false) {
428             ELOGTRACE("failed to create source surface with attribute");
429             vaStatus = VA_STATUS_ERROR_OPERATION_FAILED;
430             break;
431         }
432 
433 #ifdef DEBUG_ROTATION_PERFROMANCE
434         uint32_t beginPicture = getMilliseconds();
435 #endif
436         vaStatus = vaBeginPicture(mVaDpy, mVaCtx, mRotatedSurfaces[mTargetIndex]);
437         CHECK_VA_STATUS_BREAK("vaBeginPicture");
438 
439         VABufferID pipelineBuf;
440         void *p;
441         VAProcPipelineParameterBuffer *pipelineParam;
442         vaStatus = vaCreateBuffer(mVaDpy,
443                                   mVaCtx,
444                                   VAProcPipelineParameterBufferType,
445                                   sizeof(*pipelineParam),
446                                   1,
447                                   NULL,
448                                   &pipelineBuf);
449         CHECK_VA_STATUS_BREAK("vaCreateBuffer");
450 
451         vaStatus = vaMapBuffer(mVaDpy, pipelineBuf, &p);
452         CHECK_VA_STATUS_BREAK("vaMapBuffer");
453 
454         pipelineParam = (VAProcPipelineParameterBuffer*)p;
455         pipelineParam->surface = mSourceSurface;
456         pipelineParam->rotation_state = transFromHalToVa(transform);
457         pipelineParam->filters = &mVaBufFilter;
458         pipelineParam->num_filters = 1;
459         vaStatus = vaUnmapBuffer(mVaDpy, pipelineBuf);
460         CHECK_VA_STATUS_BREAK("vaUnmapBuffer");
461 
462         vaStatus = vaRenderPicture(mVaDpy, mVaCtx, &pipelineBuf, 1);
463         CHECK_VA_STATUS_BREAK("vaRenderPicture");
464 
465         vaStatus = vaEndPicture(mVaDpy, mVaCtx);
466         CHECK_VA_STATUS_BREAK("vaEndPicture");
467 
468         vaStatus = vaSyncSurface(mVaDpy, mRotatedSurfaces[mTargetIndex]);
469         CHECK_VA_STATUS_BREAK("vaSyncSurface");
470 
471 #ifdef DEBUG_ROTATION_PERFROMANCE
472         ILOGTRACE("time spent %dms from vaBeginPicture to vaSyncSurface",
473              getMilliseconds() - beginPicture);
474 #endif
475 
476         // Populate payload fields so that overlayPlane can flip the buffer
477         payload->rotated_width = mRotatedStride;
478         payload->rotated_height = mRotatedHeight;
479         payload->rotated_buffer_handle = mKhandles[mTargetIndex];
480         // setting client transform to 0 to force re-generating rotated buffer whenever needed.
481         payload->client_transform = 0;
482         mTargetIndex++;
483         if (mTargetIndex >= MAX_SURFACE_NUM)
484             mTargetIndex = 0;
485 
486     } while (0);
487 
488 #ifdef DEBUG_ROTATION_PERFROMANCE
489     ILOGTRACE("time spent %dms for setupRotationBuffer",
490          getMilliseconds() - setup_Begin);
491 #endif
492 
493     if (mSourceSurface > 0) {
494         vaStatus = vaDestroySurfaces(mVaDpy, &mSourceSurface, 1);
495         if (vaStatus != VA_STATUS_SUCCESS)
496             WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus);
497         mSourceSurface = 0;
498     }
499 
500     if (vaStatus != VA_STATUS_SUCCESS) {
501         stopVA();
502         return false; // To not block HWC, just abort instead of retry
503     }
504 
505     if (!payload->khandle) {
506         WLOGTRACE("khandle is reset by decoder, surface is invalid!");
507         return false;
508     }
509 
510     return true;
511 }
512 
prepareBufferInfo(int w,int h,int stride,VideoPayloadBuffer * payload,void * user_pt)513 bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt)
514 {
515     int size;
516     void *buf = NULL;
517 
518     payload->width = payload->crop_width = w;
519     payload->height = payload->crop_height = h;
520     payload->format = VA_FOURCC_NV12;
521     payload->tiling = 1;
522     payload->luma_stride = stride;
523     payload->chroma_u_stride = stride;
524     payload->chroma_v_stride = stride;
525     payload->client_transform = 0;
526 
527     size = stride * h + stride * h / 2;
528 
529     ssize_t index;
530     index = mTTMWrappers.indexOfKey((uint64_t)user_pt);
531     if (index < 0) {
532         VLOGTRACE("wrapped userPt as wsbm buffer");
533         bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt);
534         if (ret == false) {
535             ELOGTRACE("failed to allocate TTM buffer");
536             return ret;
537         }
538 
539         if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) {
540             WLOGTRACE("mTTMWrappers is unexpectedly full. Invalidate caches");
541             invalidateCaches();
542         }
543 
544         index = mTTMWrappers.add((uint64_t)user_pt, buf);
545     } else {
546         VLOGTRACE("got wsbmBuffer in saved caches");
547         buf = mTTMWrappers.valueAt(index);
548     }
549 
550     payload->khandle = mWsbm->getKBufHandle(buf);
551     return true;
552 }
553 
freeVaSurfaces()554 void RotationBufferProvider::freeVaSurfaces()
555 {
556     bool ret;
557     VAStatus vaStatus;
558 
559     for (int i = 0; i < MAX_SURFACE_NUM; i++) {
560         if (NULL != mDrmBuf[i]) {
561             ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]);
562             if (!ret)
563                 WLOGTRACE("failed to free TTMBuffer");
564             mDrmBuf[i] = NULL;
565         }
566     }
567 
568     // remove wsbm buffer ref from VA
569     for (int j = 0; j < MAX_SURFACE_NUM; j++) {
570         if (0 != mRotatedSurfaces[j]) {
571             vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1);
572             if (vaStatus != VA_STATUS_SUCCESS)
573                 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus);
574         }
575         mRotatedSurfaces[j] = 0;
576     }
577 }
578 
stopVA()579 void RotationBufferProvider::stopVA()
580 {
581     freeVaSurfaces();
582 
583     if (0 != mVaBufFilter)
584         vaDestroyBuffer(mVaDpy, mVaBufFilter);
585     if (0 != mVaCfg)
586         vaDestroyConfig(mVaDpy,mVaCfg);
587     if (0 != mVaCtx)
588         vaDestroyContext(mVaDpy, mVaCtx);
589     if (0 != mVaDpy)
590         vaTerminate(mVaDpy);
591 
592     mVaInitialized = false;
593 
594     // reset VA variable
595     mVaDpy = 0;
596     mVaCfg = 0;
597     mVaCtx = 0;
598     mVaBufFilter = 0;
599     mSourceSurface = 0;
600 
601     mWidth = 0;
602     mHeight = 0;
603     mRotatedWidth = 0;
604     mRotatedHeight = 0;
605     mRotatedStride = 0;
606     mTargetIndex = 0;
607 }
608 
isContextChanged(int width,int height,int transform)609 bool RotationBufferProvider::isContextChanged(int width, int height, int transform)
610 {
611     // check rotation config
612     if (height == mHeight &&
613         width == mWidth &&
614         transform == mTransform) {
615         return false;
616     }
617 
618     return true;
619 }
620 
621 } // name space intel
622 } // name space android
623