/* // Copyright (c) 2014 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include <math.h> #include <HwcTrace.h> #include <Drm.h> #include <Hwcomposer.h> #include <PhysicalDevice.h> #include <common/OverlayPlaneBase.h> #include <common/TTMBufferMapper.h> #include <common/GrallocSubBuffer.h> #include <DisplayQuery.h> // FIXME: remove it #include <OMX_IVCommon.h> #include <OMX_IntelVideoExt.h> namespace android { namespace intel { OverlayPlaneBase::OverlayPlaneBase(int index, int disp) : DisplayPlane(index, PLANE_OVERLAY, disp), mTTMBuffers(), mActiveTTMBuffers(), mCurrent(0), mWsbm(0), mPipeConfig(0), mBobDeinterlace(0), mUseScaledBuffer(0) { CTRACE(); for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { mBackBuffer[i] = 0; } } OverlayPlaneBase::~OverlayPlaneBase() { CTRACE(); } bool OverlayPlaneBase::initialize(uint32_t bufferCount) { Drm *drm = Hwcomposer::getInstance().getDrm(); CTRACE(); // NOTE: use overlay's data buffer count for the overlay plane if (bufferCount < OVERLAY_DATA_BUFFER_COUNT) { ITRACE("override overlay buffer count from %d to %d", bufferCount, OVERLAY_DATA_BUFFER_COUNT); bufferCount = OVERLAY_DATA_BUFFER_COUNT; } if (!DisplayPlane::initialize(bufferCount)) { DEINIT_AND_RETURN_FALSE("failed to initialize display plane"); } mTTMBuffers.setCapacity(bufferCount); mActiveTTMBuffers.setCapacity(MIN_DATA_BUFFER_COUNT); // init wsbm mWsbm = new Wsbm(drm->getDrmFd()); if (!mWsbm || !mWsbm->initialize()) { DEINIT_AND_RETURN_FALSE("failed to create wsbm"); } // create overlay back buffer for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { mBackBuffer[i] = createBackBuffer(); if (!mBackBuffer[i]) { DEINIT_AND_RETURN_FALSE("failed to create overlay back buffer"); } // reset back buffer resetBackBuffer(i); } // disable overlay when created flush(PLANE_DISABLE); return true; } bool OverlayPlaneBase::isDisabled() { RETURN_FALSE_IF_NOT_INIT(); struct drm_psb_register_rw_arg arg; memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg)); arg.get_plane_state_mask = 1; arg.plane.type = DC_OVERLAY_PLANE; arg.plane.index = mIndex; // pass the pipe index to check its enabled status // now we can pass the device id directly since // their values are just equal arg.plane.ctx = mDevice; // not used in kernel Drm *drm = Hwcomposer::getInstance().getDrm(); bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg)); if (ret == false) { WTRACE("overlay plane query failed with error code %d", ret); return false; } DTRACE("overlay %d status %s on device %d, current device %d", mIndex, arg.plane.ctx ? "DISABLED" : "ENABLED", mDevice, mDevice); return arg.plane.ctx == PSB_DC_PLANE_DISABLED; } void OverlayPlaneBase::deinitialize() { if (mTTMBuffers.size()) { invalidateBufferCache(); } if (mActiveTTMBuffers.size() > 0) { invalidateActiveTTMBuffers(); } // delete back buffer for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { if (mBackBuffer[i]) { deleteBackBuffer(i); mBackBuffer[i] = NULL; } } DEINIT_AND_DELETE_OBJ(mWsbm); DisplayPlane::deinitialize(); } void OverlayPlaneBase::invalidateBufferCache() { // clear plane buffer cache DisplayPlane::invalidateBufferCache(); invalidateTTMBuffers(); } bool OverlayPlaneBase::assignToDevice(int disp) { uint32_t pipeConfig = 0; RETURN_FALSE_IF_NOT_INIT(); VTRACE("overlay %d assigned to disp %d", mIndex, disp); switch (disp) { case IDisplayDevice::DEVICE_EXTERNAL: pipeConfig = (0x2 << 6); break; case IDisplayDevice::DEVICE_PRIMARY: default: pipeConfig = 0; break; } // if pipe switching happened, then disable overlay first if (mPipeConfig != pipeConfig) { DTRACE("overlay %d switched from %d to %d", mIndex, mDevice, disp); disable(); } mPipeConfig = pipeConfig; DisplayPlane::assignToDevice(disp); enable(); return true; } void OverlayPlaneBase::setZOrderConfig(ZOrderConfig& zorderConfig, void *nativeConfig) { CTRACE(); // setup overlay z order int ovaZOrder = -1; int ovcZOrder = -1; for (size_t i = 0; i < zorderConfig.size(); i++) { DisplayPlane *plane = zorderConfig[i]->plane; if (plane->getType() == DisplayPlane::PLANE_OVERLAY) { if (plane->getIndex() == 0) { ovaZOrder = i; } else if (plane->getIndex() == 1) { ovcZOrder = i; } } } for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; if (!backBuffer) return; // force overlay c above overlay a if ((ovaZOrder >= 0) && (ovaZOrder < ovcZOrder)) { backBuffer->OCONFIG |= (1 << 15); } else { backBuffer->OCONFIG &= ~(1 << 15); } } } bool OverlayPlaneBase::reset() { RETURN_FALSE_IF_NOT_INIT(); DisplayPlane::reset(); // invalidate active TTM buffers if (mActiveTTMBuffers.size() > 0) { invalidateActiveTTMBuffers(); } // reset back buffers for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { resetBackBuffer(i); } return true; } bool OverlayPlaneBase::enable() { RETURN_FALSE_IF_NOT_INIT(); for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; if (!backBuffer) return false; if (backBuffer->OCMD & 0x1) return true; backBuffer->OCMD |= 0x1; } // flush flush(PLANE_ENABLE); return true; } bool OverlayPlaneBase::disable() { RETURN_FALSE_IF_NOT_INIT(); for (int i = 0; i < OVERLAY_BACK_BUFFER_COUNT; i++) { OverlayBackBufferBlk *backBuffer = mBackBuffer[i]->buf; if (!backBuffer) return false; if (!(backBuffer->OCMD & 0x1)) return true; backBuffer->OCMD &= ~0x1; } // flush flush(PLANE_DISABLE); return true; } OverlayBackBuffer* OverlayPlaneBase::createBackBuffer() { CTRACE(); // create back buffer OverlayBackBuffer *backBuffer = (OverlayBackBuffer *)malloc(sizeof(OverlayBackBuffer)); if (!backBuffer) { ETRACE("failed to allocate back buffer"); return 0; } int size = sizeof(OverlayBackBufferBlk); int alignment = 64 * 1024; void *wsbmBufferObject = 0; bool ret = mWsbm->allocateTTMBuffer(size, alignment, &wsbmBufferObject); if (ret == false) { ETRACE("failed to allocate TTM buffer"); return 0; } void *virtAddr = mWsbm->getCPUAddress(wsbmBufferObject); uint32_t gttOffsetInPage = mWsbm->getGttOffset(wsbmBufferObject); backBuffer->buf = (OverlayBackBufferBlk *)virtAddr; backBuffer->gttOffsetInPage = gttOffsetInPage; backBuffer->bufObject = wsbmBufferObject; VTRACE("cpu %p, gtt %d", virtAddr, gttOffsetInPage); return backBuffer; } void OverlayPlaneBase::deleteBackBuffer(int buf) { if (!mBackBuffer[buf]) return; void *wsbmBufferObject = mBackBuffer[buf]->bufObject; bool ret = mWsbm->destroyTTMBuffer(wsbmBufferObject); if (ret == false) { WTRACE("failed to destroy TTM buffer"); } // free back buffer free(mBackBuffer[buf]); mBackBuffer[buf] = 0; } void OverlayPlaneBase::resetBackBuffer(int buf) { CTRACE(); if (!mBackBuffer[buf] || !mBackBuffer[buf]->buf) return; OverlayBackBufferBlk *backBuffer = mBackBuffer[buf]->buf; memset(backBuffer, 0, sizeof(OverlayBackBufferBlk)); // reset overlay backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | (OVERLAY_INIT_BRIGHTNESS & 0xff); backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; backBuffer->DCLRKV = OVERLAY_INIT_COLORKEY; backBuffer->DCLRKM = OVERLAY_INIT_COLORKEYMASK; backBuffer->OCONFIG = 0; backBuffer->OCONFIG |= (0x1 << 3); backBuffer->OCONFIG |= (0x1 << 27); backBuffer->SCHRKEN &= ~(0x7 << 24); backBuffer->SCHRKEN |= 0xff; } BufferMapper* OverlayPlaneBase::getTTMMapper(BufferMapper& grallocMapper, struct VideoPayloadBuffer *payload) { buffer_handle_t khandle; uint32_t w, h; uint32_t yStride, uvStride; stride_t stride; int srcX, srcY, srcW, srcH; int tmp; DataBuffer *buf; ssize_t index; TTMBufferMapper *mapper; bool ret; if (!payload) { ETRACE("invalid payload buffer"); return 0; } srcX = grallocMapper.getCrop().x; srcY = grallocMapper.getCrop().y; srcW = grallocMapper.getCrop().w; srcH = grallocMapper.getCrop().h; // init ttm buffer if (mUseScaledBuffer) { khandle = payload->scaling_khandle; } else { khandle = payload->rotated_buffer_handle; } index = mTTMBuffers.indexOfKey(khandle); if (index < 0) { VTRACE("unmapped TTM buffer, will map it"); if (mUseScaledBuffer) { w = payload->scaling_width; h = payload->scaling_height; } else { w = payload->rotated_width; h = payload->rotated_height; checkCrop(srcX, srcY, srcW, srcH, payload->coded_width, payload->coded_height); } uint32_t format = grallocMapper.getFormat(); // this is for sw decode with tiled buffer in landscape mode if (payload->tiling) format = OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled; // calculate stride switch (format) { case HAL_PIXEL_FORMAT_YV12: case HAL_PIXEL_FORMAT_I420: uint32_t yStride_align; yStride_align = DisplayQuery::getOverlayLumaStrideAlignment(grallocMapper.getFormat()); if (yStride_align > 0) { yStride = align_to(align_to(w, 32), yStride_align); } else { yStride = align_to(align_to(w, 32), 64); } uvStride = align_to(yStride >> 1, 64); stride.yuv.yStride = yStride; stride.yuv.uvStride = uvStride; break; case HAL_PIXEL_FORMAT_NV12: yStride = align_to(align_to(w, 32), 64); uvStride = yStride; stride.yuv.yStride = yStride; stride.yuv.uvStride = uvStride; break; case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: if (mUseScaledBuffer) { stride.yuv.yStride = payload->scaling_luma_stride; stride.yuv.uvStride = payload->scaling_chroma_u_stride; } else { yStride = align_to(align_to(w, 32), 64); uvStride = yStride; stride.yuv.yStride = yStride; stride.yuv.uvStride = uvStride; } break; case HAL_PIXEL_FORMAT_YUY2: case HAL_PIXEL_FORMAT_UYVY: yStride = align_to((align_to(w, 32) << 1), 64); uvStride = 0; stride.yuv.yStride = yStride; stride.yuv.uvStride = uvStride; break; } DataBuffer buf(khandle); // update buffer buf.setStride(stride); buf.setWidth(w); buf.setHeight(h); buf.setCrop(srcX, srcY, srcW, srcH); buf.setFormat(format); // create buffer mapper bool res = false; do { mapper = new TTMBufferMapper(*mWsbm, buf); if (!mapper) { ETRACE("failed to allocate mapper"); break; } // map ttm buffer ret = mapper->map(); if (!ret) { ETRACE("failed to map"); invalidateTTMBuffers(); ret = mapper->map(); if (!ret) { ETRACE("failed to remap"); break; } } if (mTTMBuffers.size() >= OVERLAY_DATA_BUFFER_COUNT) { invalidateTTMBuffers(); } // add mapper index = mTTMBuffers.add(khandle, mapper); if (index < 0) { ETRACE("failed to add TTMMapper"); break; } // increase mapper refCount since it is added to mTTMBuffers mapper->incRef(); res = true; } while (0); if (!res) { // error handling if (mapper) { mapper->unmap(); delete mapper; mapper = NULL; } return 0; } } else { VTRACE("got mapper in saved ttm buffers"); mapper = reinterpret_cast<TTMBufferMapper *>(mTTMBuffers.valueAt(index)); if (mapper->getCrop().x != srcX || mapper->getCrop().y != srcY || mapper->getCrop().w != srcW || mapper->getCrop().h != srcH) { if(!mUseScaledBuffer) checkCrop(srcX, srcY, srcW, srcH, payload->coded_width, payload->coded_height); mapper->setCrop(srcX, srcY, srcW, srcH); } } XTRACE(); return mapper; } void OverlayPlaneBase::putTTMMapper(BufferMapper* mapper) { if (!mapper) return; if (!mapper->decRef()) { // unmap it mapper->unmap(); // destroy this mapper delete mapper; } } bool OverlayPlaneBase::isActiveTTMBuffer(BufferMapper *mapper) { for (size_t i = 0; i < mActiveTTMBuffers.size(); i++) { BufferMapper *activeMapper = mActiveTTMBuffers.itemAt(i); if (!activeMapper) continue; if (activeMapper->getKey() == mapper->getKey()) return true; } return false; } void OverlayPlaneBase::updateActiveTTMBuffers(BufferMapper *mapper) { // unmap the first entry (oldest buffer) if (mActiveTTMBuffers.size() >= MAX_ACTIVE_TTM_BUFFERS) { BufferMapper *oldest = mActiveTTMBuffers.itemAt(0); putTTMMapper(oldest); mActiveTTMBuffers.removeAt(0); } // queue it to cached buffers if (!isActiveTTMBuffer(mapper)) { mapper->incRef(); mActiveTTMBuffers.push_back(mapper); } } void OverlayPlaneBase::invalidateActiveTTMBuffers() { BufferMapper* mapper; RETURN_VOID_IF_NOT_INIT(); for (size_t i = 0; i < mActiveTTMBuffers.size(); i++) { mapper = mActiveTTMBuffers.itemAt(i); // unmap it putTTMMapper(mapper); } // clear recorded data buffers mActiveTTMBuffers.clear(); } void OverlayPlaneBase::invalidateTTMBuffers() { BufferMapper* mapper; for (size_t i = 0; i < mTTMBuffers.size(); i++) { mapper = mTTMBuffers.valueAt(i); // putTTMMapper removes mapper from cache putTTMMapper(mapper); } mTTMBuffers.clear(); } bool OverlayPlaneBase::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper) { struct VideoPayloadBuffer *payload; uint32_t format; // only NV12_VED has rotated buffer format = mapper.getFormat(); if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) return false; payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); // check payload if (!payload) { ETRACE("no payload found"); return false; } if (payload->force_output_method == FORCE_OUTPUT_GPU) return false; if (payload->client_transform != mTransform) { if (payload->surface_protected) { payload->hwc_timestamp = systemTime(); payload->layer_transform = mTransform; } WTRACE("client is not ready"); return false; } rotatedMapper = getTTMMapper(mapper, payload); return true; } bool OverlayPlaneBase::useOverlayRotation(BufferMapper& mapper) { // by default overlay plane does not support rotation. return false; } bool OverlayPlaneBase::scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload) { return false; } void OverlayPlaneBase::checkPosition(int& x, int& y, int& w, int& h) { drmModeModeInfoPtr mode = &mModeInfo; if (mode->hdisplay == 0 || mode->vdisplay == 0) return; if (x < 0) x = 0; if (y < 0) y = 0; if ((x + w) > mode->hdisplay) w = mode->hdisplay - x; if ((y + h) > mode->vdisplay) h = mode->vdisplay - y; } void OverlayPlaneBase::checkCrop(int& srcX, int& srcY, int& srcW, int& srcH, int coded_width, int coded_height) { int tmp; if (mTransform) srcH >>= mBobDeinterlace; if (mTransform == HWC_TRANSFORM_ROT_90 || mTransform == HWC_TRANSFORM_ROT_270) { tmp = srcH; srcH = srcW; srcW = tmp; tmp = srcX; srcX = srcY; srcY = tmp; tmp = coded_width; coded_width = coded_height; coded_height = tmp; } // skip pading bytes in rotate buffer switch(mTransform) { case HWC_TRANSFORM_ROT_90: srcX = (coded_width >> mBobDeinterlace) - srcW - srcX; break; case HWC_TRANSFORM_ROT_180: srcX = coded_width - srcW - srcX; srcY = (coded_height >> mBobDeinterlace) - srcH - srcY; break; case HWC_TRANSFORM_ROT_270: srcY = coded_height - srcH - srcY; break; default: break; } } bool OverlayPlaneBase::bufferOffsetSetup(BufferMapper& mapper) { CTRACE(); OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; if (!backBuffer) { ETRACE("invalid back buffer"); return false; } uint32_t format = mapper.getFormat(); uint32_t gttOffsetInBytes = (mapper.getGttOffsetInPage(0) << 12); uint32_t yStride = mapper.getStride().yuv.yStride; uint32_t uvStride = mapper.getStride().yuv.uvStride; uint32_t w = mapper.getWidth(); uint32_t h = mapper.getHeight(); uint32_t srcX= mapper.getCrop().x; uint32_t srcY= mapper.getCrop().y; // clear original format setting backBuffer->OCMD &= ~(0xf << 10); backBuffer->OCMD &= ~OVERLAY_MEMORY_LAYOUT_TILED; // Y/U/V plane must be 4k bytes aligned. backBuffer->OSTART_0Y = gttOffsetInBytes; if (mIsProtectedBuffer) { // temporary workaround until vsync event logic is corrected. // it seems that overlay buffer update and renderring can be overlapped, // as such encryption bit may be cleared during HW rendering backBuffer->OSTART_0Y |= 0x01; } backBuffer->OSTART_0U = gttOffsetInBytes; backBuffer->OSTART_0V = gttOffsetInBytes; backBuffer->OSTART_1Y = backBuffer->OSTART_0Y; backBuffer->OSTART_1U = backBuffer->OSTART_0U; backBuffer->OSTART_1V = backBuffer->OSTART_0V; switch(format) { case HAL_PIXEL_FORMAT_YV12: // YV12 backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0V = yStride * h; backBuffer->OBUF_0U = backBuffer->OBUF_0V + (uvStride * (h / 2)); backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; break; case HAL_PIXEL_FORMAT_I420: // I420 backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = yStride * h; backBuffer->OBUF_0V = backBuffer->OBUF_0U + (uvStride * (h / 2)); backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420; break; case HAL_PIXEL_FORMAT_NV12: // NV12 backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = yStride * h; backBuffer->OBUF_0V = 0; backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; break; // NOTE: this is the decoded video format, align the height to 32B //as it's defined by video driver case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: // Intel codec NV12 backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = yStride * align_to(h, 32); backBuffer->OBUF_0V = 0; backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; break; case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: //NV12_tiled backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = yStride * align_to(h, 32); backBuffer->OBUF_0V = 0; backBuffer->OSTART_0U += yStride * align_to(h, 32); backBuffer->OSTART_0V += yStride * align_to(h, 32); backBuffer->OSTART_1U = backBuffer->OSTART_0U; backBuffer->OSTART_1V = backBuffer->OSTART_0V; backBuffer->OTILEOFF_0Y = srcX + (srcY << 16); backBuffer->OTILEOFF_1Y = backBuffer->OTILEOFF_0Y; backBuffer->OTILEOFF_0U = srcX + ((srcY / 2) << 16); backBuffer->OTILEOFF_1U = backBuffer->OTILEOFF_0U; backBuffer->OTILEOFF_0V = backBuffer->OTILEOFF_0U; backBuffer->OTILEOFF_1V = backBuffer->OTILEOFF_0U; backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2; backBuffer->OCMD |= OVERLAY_MEMORY_LAYOUT_TILED; break; case HAL_PIXEL_FORMAT_YUY2: // YUY2 backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = 0; backBuffer->OBUF_0V = 0; backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; backBuffer->OCMD |= OVERLAY_PACKED_ORDER_YUY2; break; case HAL_PIXEL_FORMAT_UYVY: // UYVY backBuffer->OBUF_0Y = 0; backBuffer->OBUF_0U = 0; backBuffer->OBUF_0V = 0; backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422; backBuffer->OCMD |= OVERLAY_PACKED_ORDER_UYVY; break; default: ETRACE("unsupported format %d", format); return false; } backBuffer->OBUF_0Y += srcY * yStride + srcX; backBuffer->OBUF_0V += (srcY / 2) * uvStride + srcX; backBuffer->OBUF_0U += (srcY / 2) * uvStride + srcX; backBuffer->OBUF_1Y = backBuffer->OBUF_0Y; backBuffer->OBUF_1U = backBuffer->OBUF_0U; backBuffer->OBUF_1V = backBuffer->OBUF_0V; VTRACE("done. offset (%d, %d, %d)", backBuffer->OBUF_0Y, backBuffer->OBUF_0U, backBuffer->OBUF_0V); return true; } uint32_t OverlayPlaneBase::calculateSWidthSW(uint32_t offset, uint32_t width) { ATRACE("offset = %d, width = %d", offset, width); uint32_t swidth = ((offset + width + 0x3F) >> 6) - (offset >> 6); swidth <<= 1; swidth -= 1; return swidth; } bool OverlayPlaneBase::coordinateSetup(BufferMapper& mapper) { CTRACE(); OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; if (!backBuffer) { ETRACE("invalid back buffer"); return false; } uint32_t swidthy = 0; uint32_t swidthuv = 0; uint32_t format = mapper.getFormat(); uint32_t width = mapper.getCrop().w; uint32_t height = mapper.getCrop().h; uint32_t yStride = mapper.getStride().yuv.yStride; uint32_t uvStride = mapper.getStride().yuv.uvStride; uint32_t offsety = backBuffer->OBUF_0Y; uint32_t offsetu = backBuffer->OBUF_0U; switch (format) { case HAL_PIXEL_FORMAT_YV12: // YV12 case HAL_PIXEL_FORMAT_I420: // I420 case HAL_PIXEL_FORMAT_NV12: // NV12 case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar: // NV12 case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled: // NV12_tiled break; case HAL_PIXEL_FORMAT_YUY2: // YUY2 case HAL_PIXEL_FORMAT_UYVY: // UYVY width <<= 1; break; default: ETRACE("unsupported format %d", format); return false; } if (width <= 0 || height <= 0) { ETRACE("invalid src dim"); return false; } if (yStride <=0 && uvStride <= 0) { ETRACE("invalid source stride"); return false; } backBuffer->SWIDTH = width | ((width / 2) << 16); swidthy = calculateSWidthSW(offsety, width); swidthuv = calculateSWidthSW(offsetu, width / 2); backBuffer->SWIDTHSW = (swidthy << 2) | (swidthuv << 18); backBuffer->SHEIGHT = height | ((height / 2) << 16); backBuffer->OSTRIDE = (yStride & (~0x3f)) | ((uvStride & (~0x3f)) << 16); XTRACE(); return true; } bool OverlayPlaneBase::setCoeffRegs(double *coeff, int mantSize, coeffPtr pCoeff, int pos) { int maxVal, icoeff, res; int sign; double c; sign = 0; maxVal = 1 << mantSize; c = *coeff; if (c < 0.0) { sign = 1; c = -c; } res = 12 - mantSize; if ((icoeff = (int)(c * 4 * maxVal + 0.5)) < maxVal) { pCoeff[pos].exponent = 3; pCoeff[pos].mantissa = icoeff << res; *coeff = (double)icoeff / (double)(4 * maxVal); } else if ((icoeff = (int)(c * 2 * maxVal + 0.5)) < maxVal) { pCoeff[pos].exponent = 2; pCoeff[pos].mantissa = icoeff << res; *coeff = (double)icoeff / (double)(2 * maxVal); } else if ((icoeff = (int)(c * maxVal + 0.5)) < maxVal) { pCoeff[pos].exponent = 1; pCoeff[pos].mantissa = icoeff << res; *coeff = (double)icoeff / (double)(maxVal); } else if ((icoeff = (int)(c * maxVal * 0.5 + 0.5)) < maxVal) { pCoeff[pos].exponent = 0; pCoeff[pos].mantissa = icoeff << res; *coeff = (double)icoeff / (double)(maxVal / 2); } else { // Coeff out of range return false; } pCoeff[pos].sign = sign; if (sign) *coeff = -(*coeff); return true; } void OverlayPlaneBase::updateCoeff(int taps, double fCutoff, bool isHoriz, bool isY, coeffPtr pCoeff) { int i, j, j1, num, pos, mantSize; double pi = 3.1415926535, val, sinc, window, sum; double rawCoeff[MAX_TAPS * 32], coeffs[N_PHASES][MAX_TAPS]; double diff; int tapAdjust[MAX_TAPS], tap2Fix; bool isVertAndUV; if (isHoriz) mantSize = 7; else mantSize = 6; isVertAndUV = !isHoriz && !isY; num = taps * 16; for (i = 0; i < num * 2; i++) { val = (1.0 / fCutoff) * taps * pi * (i - num) / (2 * num); if (val == 0.0) sinc = 1.0; else sinc = sin(val) / val; // Hamming window window = (0.54 - 0.46 * cos(2 * i * pi / (2 * num - 1))); rawCoeff[i] = sinc * window; } for (i = 0; i < N_PHASES; i++) { // Normalise the coefficients sum = 0.0; for (j = 0; j < taps; j++) { pos = i + j * 32; sum += rawCoeff[pos]; } for (j = 0; j < taps; j++) { pos = i + j * 32; coeffs[i][j] = rawCoeff[pos] / sum; } // Set the register values for (j = 0; j < taps; j++) { pos = j + i * taps; if ((j == (taps - 1) / 2) && !isVertAndUV) setCoeffRegs(&coeffs[i][j], mantSize + 2, pCoeff, pos); else setCoeffRegs(&coeffs[i][j], mantSize, pCoeff, pos); } tapAdjust[0] = (taps - 1) / 2; for (j = 1, j1 = 1; j <= tapAdjust[0]; j++, j1++) { tapAdjust[j1] = tapAdjust[0] - j; tapAdjust[++j1] = tapAdjust[0] + j; } // Adjust the coefficients sum = 0.0; for (j = 0; j < taps; j++) sum += coeffs[i][j]; if (sum != 1.0) { for (j1 = 0; j1 < taps; j1++) { tap2Fix = tapAdjust[j1]; diff = 1.0 - sum; coeffs[i][tap2Fix] += diff; pos = tap2Fix + i * taps; if ((tap2Fix == (taps - 1) / 2) && !isVertAndUV) setCoeffRegs(&coeffs[i][tap2Fix], mantSize + 2, pCoeff, pos); else setCoeffRegs(&coeffs[i][tap2Fix], mantSize, pCoeff, pos); sum = 0.0; for (j = 0; j < taps; j++) sum += coeffs[i][j]; if (sum == 1.0) break; } } } } bool OverlayPlaneBase::scalingSetup(BufferMapper& mapper) { int xscaleInt, xscaleFract, yscaleInt, yscaleFract; int xscaleIntUV, xscaleFractUV; int yscaleIntUV, yscaleFractUV; int deinterlace_factor = 1; // UV is half the size of Y -- YUV420 int uvratio = 2; uint32_t newval; coeffRec xcoeffY[N_HORIZ_Y_TAPS * N_PHASES]; coeffRec xcoeffUV[N_HORIZ_UV_TAPS * N_PHASES]; int i, j, pos; bool scaleChanged = false; int x, y, w, h; OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; if (!backBuffer) { ETRACE("invalid back buffer"); return false; } x = mPosition.x; y = mPosition.y; w = mPosition.w; h = mPosition.h; // check position checkPosition(x, y, w, h); VTRACE("final position (%d, %d, %d, %d)", x, y, w, h); if ((w <= 0) || (h <= 0)) { ETRACE("invalid dst width/height"); return false; } // setup dst position backBuffer->DWINPOS = (y << 16) | x; backBuffer->DWINSZ = (h << 16) | w; uint32_t srcWidth = mapper.getCrop().w; uint32_t srcHeight = mapper.getCrop().h; uint32_t dstWidth = w; uint32_t dstHeight = h; if (mBobDeinterlace && !mTransform) deinterlace_factor = 2; VTRACE("src (%dx%d), dst (%dx%d)", srcWidth, srcHeight, dstWidth, dstHeight); // Y down-scale factor as a multiple of 4096 if (srcWidth == dstWidth && srcHeight == dstHeight) { xscaleFract = (1 << 12); yscaleFract = (1 << 12)/deinterlace_factor; } else { xscaleFract = ((srcWidth - 1) << 12) / dstWidth; yscaleFract = ((srcHeight - 1) << 12) / (dstHeight * deinterlace_factor); } // Calculate the UV scaling factor xscaleFractUV = xscaleFract / uvratio; yscaleFractUV = yscaleFract / uvratio; // To keep the relative Y and UV ratios exact, round the Y scales // to a multiple of the Y/UV ratio. xscaleFract = xscaleFractUV * uvratio; yscaleFract = yscaleFractUV * uvratio; // Integer (un-multiplied) values xscaleInt = xscaleFract >> 12; yscaleInt = yscaleFract >> 12; xscaleIntUV = xscaleFractUV >> 12; yscaleIntUV = yscaleFractUV >> 12; // Check scaling ratio if (xscaleInt > INTEL_OVERLAY_MAX_SCALING_RATIO) { ETRACE("xscaleInt > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); return false; } // shouldn't get here if (xscaleIntUV > INTEL_OVERLAY_MAX_SCALING_RATIO) { ETRACE("xscaleIntUV > %d", INTEL_OVERLAY_MAX_SCALING_RATIO); return false; } newval = (xscaleInt << 15) | ((xscaleFract & 0xFFF) << 3) | ((yscaleFract & 0xFFF) << 20); if (newval != backBuffer->YRGBSCALE) { scaleChanged = true; backBuffer->YRGBSCALE = newval; } newval = (xscaleIntUV << 15) | ((xscaleFractUV & 0xFFF) << 3) | ((yscaleFractUV & 0xFFF) << 20); if (newval != backBuffer->UVSCALE) { scaleChanged = true; backBuffer->UVSCALE = newval; } newval = yscaleInt << 16 | yscaleIntUV; if (newval != backBuffer->UVSCALEV) { scaleChanged = true; backBuffer->UVSCALEV = newval; } // Recalculate coefficients if the scaling changed // Only Horizontal coefficients so far. if (scaleChanged) { double fCutoffY; double fCutoffUV; fCutoffY = xscaleFract / 4096.0; fCutoffUV = xscaleFractUV / 4096.0; // Limit to between 1.0 and 3.0 if (fCutoffY < MIN_CUTOFF_FREQ) fCutoffY = MIN_CUTOFF_FREQ; if (fCutoffY > MAX_CUTOFF_FREQ) fCutoffY = MAX_CUTOFF_FREQ; if (fCutoffUV < MIN_CUTOFF_FREQ) fCutoffUV = MIN_CUTOFF_FREQ; if (fCutoffUV > MAX_CUTOFF_FREQ) fCutoffUV = MAX_CUTOFF_FREQ; updateCoeff(N_HORIZ_Y_TAPS, fCutoffY, true, true, xcoeffY); updateCoeff(N_HORIZ_UV_TAPS, fCutoffUV, true, false, xcoeffUV); for (i = 0; i < N_PHASES; i++) { for (j = 0; j < N_HORIZ_Y_TAPS; j++) { pos = i * N_HORIZ_Y_TAPS + j; backBuffer->Y_HCOEFS[pos] = (xcoeffY[pos].sign << 15 | xcoeffY[pos].exponent << 12 | xcoeffY[pos].mantissa); } } for (i = 0; i < N_PHASES; i++) { for (j = 0; j < N_HORIZ_UV_TAPS; j++) { pos = i * N_HORIZ_UV_TAPS + j; backBuffer->UV_HCOEFS[pos] = (xcoeffUV[pos].sign << 15 | xcoeffUV[pos].exponent << 12 | xcoeffUV[pos].mantissa); } } } XTRACE(); return true; } bool OverlayPlaneBase::colorSetup(BufferMapper& mapper) { CTRACE(); OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; if (!backBuffer) { ETRACE("invalid back buffer"); return false; } uint32_t format = mapper.getFormat(); if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar && format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { VTRACE("Not video layer, use default color setting"); backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | (OVERLAY_INIT_BRIGHTNESS & 0xff); backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; backBuffer->OCONFIG &= ~(1 << 5); return true; } struct VideoPayloadBuffer *payload; payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1); // check payload if (!payload) { ETRACE("no payload found"); return false; } // BT.601 or BT.709 backBuffer->OCONFIG &= ~(1 << 5); backBuffer->OCONFIG |= ((payload->csc_mode & 1) << 5); // no level expansion for video on HDMI if (payload->video_range || mPipeConfig == (0x2 << 6)) { // full range, no need to do level expansion backBuffer->OCLRC0 = 0x1000000; backBuffer->OCLRC1 = 0x80; } else { // level expansion for limited range backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) | (OVERLAY_INIT_BRIGHTNESS & 0xff); backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION; } return true; } bool OverlayPlaneBase::setDataBuffer(BufferMapper& grallocMapper) { BufferMapper *mapper; BufferMapper *videoBufferMapper = 0; bool ret; uint32_t format; RETURN_FALSE_IF_NOT_INIT(); // get gralloc mapper mapper = &grallocMapper; format = grallocMapper.getFormat(); if (format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar || format == OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) { struct VideoPayloadBuffer *payload; payload = (struct VideoPayloadBuffer *)grallocMapper.getCpuAddress(SUB_BUFFER1); if (!payload) { ETRACE("invalid payload buffer"); return 0; } mBobDeinterlace = payload->bob_deinterlace; int srcW, srcH; srcW = grallocMapper.getCrop().w - grallocMapper.getCrop().x; srcH = grallocMapper.getCrop().h - grallocMapper.getCrop().y; if ((srcW > INTEL_OVERLAY_MAX_WIDTH - 1) || (srcH > INTEL_OVERLAY_MAX_HEIGHT - 1)) { if (mTransform) { int x, y, w, h; x = mSrcCrop.x; y = mSrcCrop.y; w = mSrcCrop.w; h = mSrcCrop.h; setSourceCrop(0, 0, payload->scaling_width, payload->scaling_height); if (!useOverlayRotation(grallocMapper)) { DTRACE("The scaled buffer will hit overlay rotation limitation, fall back to GLES"); setSourceCrop(x, y, w, h); return false; } } if (!scaledBufferReady(grallocMapper, videoBufferMapper, payload)) { DTRACE("scaled buffer is not ready, fall back to GLES"); return false; } else { videoBufferMapper->setFormat(OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar); mapper = videoBufferMapper; } } } if (!mUseScaledBuffer && mTransform && !useOverlayRotation(grallocMapper)) { if (!rotatedBufferReady(grallocMapper, videoBufferMapper)) { DTRACE("rotated buffer is not ready"); return false; } if (!videoBufferMapper) { ETRACE("failed to get rotated buffer"); return false; } mapper = videoBufferMapper; } OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf; if (!backBuffer) { ETRACE("invalid back buffer"); return false; } ret = bufferOffsetSetup(*mapper); if (ret == false) { ETRACE("failed to set up buffer offsets"); return false; } ret = coordinateSetup(*mapper); if (ret == false) { ETRACE("failed to set up overlay coordinates"); return false; } ret = scalingSetup(*mapper); if (ret == false) { ETRACE("failed to set up scaling parameters"); return false; } backBuffer->OCMD |= 0x1; ret = colorSetup(grallocMapper); if (ret == false) { ETRACE("failed to set up color parameters"); return false; } if (mBobDeinterlace && !mTransform) { backBuffer->OCMD |= BUF_TYPE_FIELD; backBuffer->OCMD &= ~FIELD_SELECT; backBuffer->OCMD |= FIELD0; backBuffer->OCMD &= ~(BUFFER_SELECT); backBuffer->OCMD |= BUFFER0; } // add to active ttm buffers if it's a rotated buffer if (videoBufferMapper) { updateActiveTTMBuffers(mapper); } mUseScaledBuffer = 0; return true; } } // namespace intel } // namespace android