diff options
Diffstat (limited to 'libscaler/libscaler.cpp')
-rw-r--r-- | libscaler/libscaler.cpp | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/libscaler/libscaler.cpp b/libscaler/libscaler.cpp new file mode 100644 index 0000000..28cceb3 --- /dev/null +++ b/libscaler/libscaler.cpp @@ -0,0 +1,765 @@ + /* + * Copyright (C) 2013 The Android Open Source Project + * Copyright@ Samsung Electronics Co. LTD + * + * 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. + */ + +/*! + * \file libscaler.cpp + * \brief source file for Scaler HAL + * \author Sunyoung Kang (sy0816.kang@samsung.com) + * \date 2013/02/01 + * + * <b>Revision History: </b> + * - 2013.02.01 : Sunyoung Kang (sy0816.kang@samsung.com) \n + * Create + * - 2013.04.10 : Cho KyongHo (pullip.cho@samsung.com) \n + * Refactoring + * + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <system/graphics.h> + +#include "exynos_scaler.h" + +#include "libscaler-common.h" +#include "libscalerblend-v4l2.h" +#include "libscaler-v4l2.h" +#include "libscaler-m2m1shot.h" + +int hal_pixfmt_to_v4l2(int hal_pixel_format) +{ + int v4l2_pixel_format = -1; + + switch (hal_pixel_format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + v4l2_pixel_format = V4L2_PIX_FMT_RGB32; + break; + + case HAL_PIXEL_FORMAT_RGB_888: + v4l2_pixel_format = V4L2_PIX_FMT_RGB24; + break; + + case HAL_PIXEL_FORMAT_RGB_565: + v4l2_pixel_format = V4L2_PIX_FMT_RGB565; + break; + + case HAL_PIXEL_FORMAT_BGRA_8888: + v4l2_pixel_format = V4L2_PIX_FMT_BGR32; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YV12_M: + v4l2_pixel_format = V4L2_PIX_FMT_YVU420M; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P_M: + v4l2_pixel_format = V4L2_PIX_FMT_YUV420M; + break; + + case HAL_PIXEL_FORMAT_YV12: + v4l2_pixel_format = V4L2_PIX_FMT_YVU420; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_P: + v4l2_pixel_format = V4L2_PIX_FMT_YUV420; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_PN: + v4l2_pixel_format = V4L2_PIX_FMT_YUV420N; + break; + + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + v4l2_pixel_format = V4L2_PIX_FMT_NV16; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP: + v4l2_pixel_format = V4L2_PIX_FMT_NV12; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN: + v4l2_pixel_format = V4L2_PIX_FMT_NV12N; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M: + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_PRIV: + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_S10B: + v4l2_pixel_format = V4L2_PIX_FMT_NV12M; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_S10B: + v4l2_pixel_format = V4L2_PIX_FMT_NV12N_10B; + break; + + case HAL_PIXEL_FORMAT_YCbCr_422_I: + v4l2_pixel_format = V4L2_PIX_FMT_YUYV; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_CbYCrY_422_I: + v4l2_pixel_format = V4L2_PIX_FMT_UYVY; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_422_SP: + v4l2_pixel_format = V4L2_PIX_FMT_NV61; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M: + case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_420_SP_M_FULL: + v4l2_pixel_format = V4L2_PIX_FMT_NV21M; + break; + + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + v4l2_pixel_format = V4L2_PIX_FMT_NV21; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SP_M_TILED: +#ifdef USES_FIMC + v4l2_pixel_format = V4L2_PIX_FMT_NV12MT; +#else + v4l2_pixel_format = V4L2_PIX_FMT_NV12MT_16X16; +#endif + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_420_SPN_TILED: + v4l2_pixel_format = V4L2_PIX_FMT_NV12NT; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCrCb_422_I: + v4l2_pixel_format = V4L2_PIX_FMT_YVYU; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_CrYCbY_422_I: + v4l2_pixel_format = V4L2_PIX_FMT_VYUY; + break; + + case HAL_PIXEL_FORMAT_EXYNOS_YCbCr_P010_M: + v4l2_pixel_format = V4L2_PIX_FMT_NV12M_P010; + break; + + default: + ALOGE("%s::unmatched HAL_PIXEL_FORMAT color_space(0x%x)\n", + __func__, hal_pixel_format); + break; + } + + return v4l2_pixel_format; +} + +static bool find_pixel(unsigned int sc_pxfmt, unsigned int __out *v4l2_pxfmt) +{ + const static unsigned int sc_fmt_tbl[][2] = { + {EXYNOS_SC_FMT_RGB32, V4L2_PIX_FMT_RGB32}, + {EXYNOS_SC_FMT_BGR32, V4L2_PIX_FMT_BGR32}, + {EXYNOS_SC_FMT_RGB565, V4L2_PIX_FMT_RGB565}, + {EXYNOS_SC_FMT_RGB555X, V4L2_PIX_FMT_RGB555X}, + {EXYNOS_SC_FMT_RGB444, V4L2_PIX_FMT_RGB444}, + }; + + for (size_t i = 0; i < ARRSIZE(sc_fmt_tbl); i++) { + if (sc_pxfmt == sc_fmt_tbl[i][0]) { + *v4l2_pxfmt = sc_fmt_tbl[i][1]; + return true; + } + } + + SC_LOGE("Unknown format value %d", sc_pxfmt); + + return false; +} + +bool exynos_sc_copy_pixels(exynos_sc_pxinfo *pxinfo, int dev_num) +{ + unsigned int srcfmt; + unsigned int dstfmt; + + CScalerM2M1SHOT sc(dev_num); + + if (!sc.Valid()) + return false; + + if (!find_pixel(pxinfo->src.pxfmt, &srcfmt)) + return false; + + if (!find_pixel(pxinfo->dst.pxfmt, &dstfmt)) + return false; + + if (!sc.SetSrcFormat(pxinfo->src.width, pxinfo->src.height, srcfmt)) + return false; + + if (!sc.SetDstFormat(pxinfo->dst.width, pxinfo->dst.height, dstfmt)) + return false; + + if (!sc.SetSrcCrop(pxinfo->src.crop_left, pxinfo->src.crop_top, + pxinfo->src.crop_width, pxinfo->src.crop_height)) + return false; + + if (!sc.SetDstCrop(pxinfo->dst.crop_left, pxinfo->dst.crop_top, + pxinfo->dst.crop_width, pxinfo->dst.crop_height)) + return false; + + if (!sc.SetRotate(pxinfo->rotate, pxinfo->hflip, pxinfo->vflip)) + return false; + + // the first argument ot CScalerM2M1SHOT.SetXXXAddr() must be void *[3] + // it is safe to pass void *[1] which is not an array actually + // because CScalerM2M1SHOT.SetAddr() just accesses the array elements + // that are used for the specified format and this function just specifies + // RGB formats with one planar. + void *addr[SC_NUM_OF_PLANES]; + for (size_t i = 1; i < SC_NUM_OF_PLANES; i++) + addr[i] = NULL; + + addr[0] = pxinfo->src.addr; + if (!sc.SetSrcAddr(addr, V4L2_MEMORY_USERPTR)) + return false; + + addr[0] = pxinfo->dst.addr; + if (!sc.SetDstAddr(addr, V4L2_MEMORY_USERPTR)) + return false; + + return sc.Run(); +} + +#ifdef SCALER_USE_M2M1SHOT +typedef CScalerM2M1SHOT CScalerNonStream; +#else +typedef CScalerV4L2 CScalerNonStream; +#endif + +static CScalerNonStream *GetNonStreamScaler(void *handle) +{ + if (handle == NULL) { + SC_LOGE("NULL Scaler handle"); + return NULL; + } + + CScalerNonStream *sc = reinterpret_cast<CScalerNonStream *>(handle); + if (!sc->Valid()) { + SC_LOGE("Invalid Scaler handle %p", handle); + return NULL; + } + + return sc; +} + +void *exynos_sc_create(int dev_num) +{ + CScalerNonStream *sc = new CScalerNonStream(dev_num); + + if (!sc) { + SC_LOGE("Failed to allocate a Scaler handle for instance %d", dev_num); + return NULL; + } + + if (!sc->Valid()) { + SC_LOGE("Failed to create a Scaler handle for instance %d", dev_num); + delete sc; + return NULL; + } + + return reinterpret_cast<void *>(sc); +} + +int exynos_sc_destroy(void *handle) +{ + int ret = 0; + + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + if (!sc->Stop()) { + SC_LOGE("Failed to stop Scaler (handle %p)", handle); + ret = -1; + } + + delete sc; + + return ret; +} + +int exynos_sc_set_csc_property( + void *handle, + unsigned int csc_range, + unsigned int v4l2_colorspace, + unsigned int filter) + +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + sc->SetCSCWide(csc_range); + sc->SetCSCEq(v4l2_colorspace); + sc->SetFilter(filter); + + return 0; +} + +int exynos_sc_set_src_format( + void *handle, + unsigned int width, + unsigned int height, + unsigned int crop_left, + unsigned int crop_top, + unsigned int crop_width, + unsigned int crop_height, + unsigned int v4l2_colorformat, + unsigned int cacheable, + unsigned int mode_drm, + unsigned int premultiplied) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + sc->SetSrcPremultiplied(premultiplied != 0); + sc->SetSrcCacheable(cacheable != 0); + sc->SetDRM(mode_drm != 0); + + if (!sc->SetSrcFormat(width, height, v4l2_colorformat)) + return -1; + + return sc->SetSrcCrop(crop_left, crop_top, crop_width, crop_height) ? 0 : -1; +} + +int exynos_sc_set_dst_format( + void *handle, + unsigned int width, + unsigned int height, + unsigned int crop_left, + unsigned int crop_top, + unsigned int crop_width, + unsigned int crop_height, + unsigned int v4l2_colorformat, + unsigned int cacheable, + unsigned int mode_drm, + unsigned int premultiplied) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + sc->SetDstPremultiplied(premultiplied != 0); + sc->SetDstCacheable(cacheable != 0); + sc->SetDRM(mode_drm != 0); + + if (!sc->SetDstFormat(width, height, v4l2_colorformat)) + return -1; + + if (!sc->SetDstCrop(crop_left, crop_top, crop_width, crop_height)) + return -1; + + return 0; +} + +int exynos_sc_set_rotation( + void *handle, + int rot, + int flip_h, + int flip_v) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + return sc->SetRotate(rot, flip_h, flip_v) ? 0 : -1; +} + +void exynos_sc_set_framerate( + void *handle, + int framerate) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return; + + sc->SetFrameRate(framerate); +} + +int exynos_sc_set_src_addr( + void *handle, + void *addr[SC_NUM_OF_PLANES], + int mem_type, + int __UNUSED__ acquireFenceFd) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + // acquireFenceFd is ignored by blocking mode + return sc->SetSrcAddr(addr, mem_type) ? 0 : -1; +} + +int exynos_sc_set_dst_addr( + void *handle, + void *addr[SC_NUM_OF_PLANES], + int mem_type, + int __UNUSED__ acquireFenceFd) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + // acquireFenceFd is ignored by blocking mode + return sc->SetDstAddr(addr, mem_type) ? 0 : -1; +} + +int exynos_sc_convert(void *handle) +{ + CScalerNonStream *sc = GetNonStreamScaler(handle); + if (!sc) + return -1; + + return sc->Run() ? 0 : -1; +} + +static CScalerBlendV4L2 *GetScalerBlend(void *handle) +{ + if (handle == NULL) { + SC_LOGE("NULL Scaler handle"); + return NULL; + } + + CScalerBlendV4L2 *sc = reinterpret_cast<CScalerBlendV4L2 *>(handle); + if (!sc->Valid()) { + SC_LOGE("Invalid Scaler handle %p", handle); + return NULL; + } + + return sc; +} + +static CScalerV4L2 *GetScaler(void *handle) +{ + if (handle == NULL) { + SC_LOGE("NULL Scaler handle"); + return NULL; + } + + CScalerV4L2 *sc = reinterpret_cast<CScalerV4L2 *>(handle); + if (!sc->Valid()) { + SC_LOGE("Invalid Scaler handle %p", handle); + return NULL; + } + + return sc; +} + +void *exynos_sc_create_exclusive( + int dev_num, + int allow_drm + ) +{ + CScalerV4L2 *sc; + + sc = new CScalerV4L2(dev_num, allow_drm); + if (!sc) { + SC_LOGE("Failed to allocate a Scaler handle for instance %d", dev_num); + return NULL; + } + + if (!sc->Valid()) { + SC_LOGE("Failed to create a Scaler handle for instance %d", dev_num); + delete sc; + return NULL; + } + + SC_LOGD("Scaler %d is successfully created", dev_num); + return reinterpret_cast<void *>(sc); +} + +int exynos_sc_free_and_close(void *handle) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + int ret = 0; + if (!sc->Stop()) { + SC_LOGE("Failed to stop Scaler (handle %p)", handle); + ret = -1; + } + + delete sc; + + return ret; +} + +int exynos_sc_stop_exclusive(void *handle) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + if (!sc->Stop()) { + SC_LOGE("Failed to stop Scaler (handle %p)", handle); + return -1; + } + + return 0; +} + +int exynos_sc_csc_exclusive(void *handle, + unsigned int range_full, + unsigned int v4l2_colorspace) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + sc->SetCSCWide(range_full); + sc->SetCSCEq(v4l2_colorspace); + + return 0; +} + +int exynos_sc_config_exclusive( + void *handle, + exynos_sc_img *src_img, + exynos_sc_img *dst_img) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + if (src_img->drmMode && !sc->IsDRMAllowed()) { + SC_LOGE("Invalid DRM state request for Scaler%d (s=%d d=%d)", + sc->GetScalerID(), src_img->drmMode, dst_img->drmMode); + return -1; + } + + unsigned int rot = 0; + unsigned int flip_h = 0; + unsigned int flip_v = 0; + + if (dst_img->rot == HAL_TRANSFORM_ROT_270) { + rot = 270; + } else { + if (dst_img->rot & HAL_TRANSFORM_FLIP_H) + flip_h = 1; + if (dst_img->rot & HAL_TRANSFORM_FLIP_V) + flip_v = 1; + if (dst_img->rot & HAL_TRANSFORM_ROT_90) + rot = 90; + } + + if (!sc->SetRotate(rot, flip_h, flip_v)) { + SC_LOGE("Failed to set rotation degree %d, hflip %d, vflip %d", + rot, flip_h, flip_v); + return -1; + } + + int32_t src_color_space = hal_pixfmt_to_v4l2(src_img->format); + int32_t dst_color_space = hal_pixfmt_to_v4l2(dst_img->format); + + if (!sc->SetSrcFormat(src_img->fw, src_img->fh, src_color_space)) + return -1; + + if (!sc->SetSrcCrop(src_img->x, src_img->y, src_img->w, src_img->h)) + return -1; + + if (!sc->SetDstFormat(dst_img->fw, dst_img->fh, dst_color_space)) + return -1; + + if (!sc->SetDstCrop(dst_img->x, dst_img->y, dst_img->w, dst_img->h)) + return -1; + + sc->SetCSCWide(!dst_img->narrowRgb); + + sc->SetSrcCacheable(src_img->cacheable != 0); + sc->SetDstCacheable(dst_img->cacheable != 0); + + sc->SetSrcPremultiplied(src_img->pre_multi); + sc->SetDstPremultiplied(dst_img->pre_multi); + + sc->SetDRM(src_img->drmMode != 0 || dst_img->drmMode != 0); + + return 0; +} + +int exynos_sc_run_exclusive( + void *handle, + exynos_sc_img *src_img, + exynos_sc_img *dst_img) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + void *addr[SC_NUM_OF_PLANES]; + + addr[0] = (void *)src_img->yaddr; + addr[1] = (void *)src_img->uaddr; + addr[2] = (void *)src_img->vaddr; + sc->SetSrcAddr(addr, src_img->mem_type, src_img->acquireFenceFd); + + addr[0] = (void *)dst_img->yaddr; + addr[1] = (void *)dst_img->uaddr; + addr[2] = (void *)dst_img->vaddr; + sc->SetDstAddr(addr, dst_img->mem_type, dst_img->acquireFenceFd); + + if (!sc->DevSetCtrl()) + return -1; + + if (!sc->DevSetFormat()) + return -1; + + if (!sc->ReqBufs()) + return -1; + + int fdSrcReleaseFence, fdDstReleaseFence; + + if (!sc->QBuf(&fdSrcReleaseFence, &fdDstReleaseFence)) + return -1; + + if (!sc->StreamOn()) { + close(fdSrcReleaseFence); + close(fdDstReleaseFence); + return -1; + } + + src_img->releaseFenceFd = fdSrcReleaseFence; + dst_img->releaseFenceFd = fdDstReleaseFence; + + return 0; +} + +void *exynos_sc_create_blend_exclusive( + int dev_num, + int allow_drm + ) +{ + CScalerBlendV4L2 *sc; + + sc = new CScalerBlendV4L2(dev_num, allow_drm); + if (!sc) { + SC_LOGE("Failed to allocate a Scaler blend handle for instance %d", dev_num); + return NULL; + } + + if (!sc->Valid()) { + SC_LOGE("Failed to create a Scaler blend handle for instance %d", dev_num); + delete sc; + return NULL; + } + + SC_LOGD("Scaler blend %d is successfully created", dev_num); + return reinterpret_cast<void *>(sc); +} + +int exynos_sc_config_blend_exclusive( + void *handle, + exynos_sc_img *src_img, + exynos_sc_img *dst_img, + struct SrcBlendInfo *srcblendinfo) +{ + + CScalerBlendV4L2 *sc = GetScalerBlend(handle); + if (!sc) + return -1; + + if (src_img->drmMode && !sc->IsDRMAllowed()) { + SC_LOGE("Invalid DRM state request for Scaler%d (s=%d d=%d)", + sc->GetScalerID(), src_img->drmMode, dst_img->drmMode); + return -1; + } + unsigned int rot = 0; + unsigned int flip_h = 0; + unsigned int flip_v = 0; + + if (dst_img->rot == HAL_TRANSFORM_ROT_270) { + rot = 270; + } else { + if (dst_img->rot & HAL_TRANSFORM_FLIP_H) + flip_h = 1; + if (dst_img->rot & HAL_TRANSFORM_FLIP_V) + flip_v = 1; + if (dst_img->rot & HAL_TRANSFORM_ROT_90) + rot = 90; + } + + if (!sc->SetRotate(rot, flip_h, flip_v)) { + SC_LOGE("Failed to set rotation degree %d, hflip %d, vflip %d", + rot, flip_h, flip_v); + return -1; + } + + int32_t src_color_space = hal_pixfmt_to_v4l2(src_img->format); + int32_t dst_color_space = hal_pixfmt_to_v4l2(dst_img->format); + int32_t src_blend_color_space = hal_pixfmt_to_v4l2(srcblendinfo->srcblendfmt); + + sc->GetCustomAlphaBlendFmt(src_color_space, src_blend_color_space); + if (src_color_space < 0) { + SC_LOGE("src blending format not supported"); + return -1; + } + + sc->SetSrcBlendOp(srcblendinfo->blop); + + sc->SetSrcGlobalAlpha(srcblendinfo->globalalpha.val, + srcblendinfo->globalalpha.enable); + + sc->SetSrcBlendVPos(srcblendinfo->srcblendvpos); + + sc->SetSrcBlendHPos(srcblendinfo->srcblendhpos); + + sc->SetSrcBlendPremulti(srcblendinfo->srcblendpremulti); + + sc->SetSrcBlendFmt(src_blend_color_space); + + sc->SetSrcBlendStride(srcblendinfo->srcblendstride); + + sc->SetSrcBlendWidth(srcblendinfo->srcblendwidth); + + sc->SetSrcBlendHeight(srcblendinfo->srcblendheight); + + sc->SetSrcBlendCSCSpace(srcblendinfo->cscspec.enable, + srcblendinfo->cscspec.space, + srcblendinfo->cscspec.wide); + + if (!sc->SetSrcFormat(src_img->fw, src_img->fh, src_color_space)) + return -1; + + if (!sc->SetSrcCrop(src_img->x, src_img->y, src_img->w, src_img->h)) + return -1; + + if (!sc->SetDstFormat(dst_img->fw, dst_img->fh, dst_color_space)) + return -1; + + if (!sc->SetDstCrop(dst_img->x, dst_img->y, dst_img->w, dst_img->h)) + return -1; + + sc->SetSrcCacheable(src_img->cacheable != 0); + sc->SetDstCacheable(dst_img->cacheable != 0); + + sc->SetDRM(src_img->drmMode != 0 || dst_img->drmMode != 0); + + return 0; +} + +int exynos_sc_wait_frame_done_exclusive( + void *handle) +{ + CScalerV4L2 *sc = GetScaler(handle); + if (!sc) + return -1; + + return sc->DQBuf() ? 0 : -1; +} |