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