/* * Copyright (C) 2008 The Android Open Source Project * * 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. */ /*#define LOG_NDEBUG 0*/ #define LOG_TAG "SEC_Overlay" #include <hardware/hardware.h> #include <hardware/overlay.h> extern "C" { #include "v4l2_utils.h" } #include <pthread.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #include <linux/videodev.h> #include <cutils/log.h> #include <cutils/ashmem.h> #include <cutils/atomic.h> #include "linux/fb.h" /*****************************************************************************/ #define LOG_FUNCTION_NAME LOGV(" %s %s", __FILE__, __func__) #define NUM_OVERLAY_BUFFERS_REQUESTED (3) /* OVRLYSHM on phone keypad*/ #define SHARED_DATA_MARKER (0x68759746) /* These values should come from Surface Flinger */ unsigned int g_lcd_width = 480; unsigned int g_lcd_height = 800; unsigned int g_lcd_bpp = 32; #define CACHEABLE_BUFFERS 0x1 /* shared with Camera/Video Playback HAL */ #define ALL_BUFFERS_FLUSHED -66 uint32_t phyAddr; s5p_fimc_t g_s5p_fimc; typedef struct { uint32_t posX; uint32_t posY; uint32_t posW; uint32_t posH; uint32_t rotation; uint32_t flip; uint32_t posX_org; uint32_t posY_org; uint32_t posW_org; uint32_t posH_org; } overlay_ctrl_t; typedef struct { uint32_t cropX; uint32_t cropY; uint32_t cropW; uint32_t cropH; } overlay_data_t; typedef struct { uint32_t marker; uint32_t size; volatile int32_t refCnt; uint32_t controlReady; /* Only updated by the control side */ uint32_t dataReady; /* Only updated by the data side */ pthread_mutex_t lock; pthread_mutexattr_t attr; uint32_t streamEn; uint32_t streamingReset; uint32_t dispW; uint32_t dispH; } overlay_shared_t; /* Only one instance is created per platform */ struct overlay_control_context_t { struct overlay_control_device_t device; /* our private state goes below here */ struct overlay_t* overlay_video1; struct overlay_t* overlay_video2; }; /* A separate instance is created per overlay data side user*/ struct overlay_data_context_t { struct overlay_data_device_t device; /* our private state goes below here */ int ctl_fd; int shared_fd; int shared_size; int width; int height; int format; int num_buffers; size_t *buffers_len; void **buffers; overlay_data_t data; overlay_shared_t *shared; struct mapping_data *mapping_data; /* Need to count Qd buffers to be sure we don't block DQ'ing when exiting */ int qd_buf_count; int cacheable_buffers; bool zerocopy; }; static int create_shared_data(overlay_shared_t **shared); static void destroy_shared_data(int shared_fd, overlay_shared_t *shared, bool closefd); static int open_shared_data(overlay_data_context_t *ctx); static void close_shared_data(overlay_data_context_t *ctx); enum { LOCK_REQUIRED = 1, NO_LOCK_NEEDED = 0 }; static int enable_streaming(overlay_shared_t *shared, int ovly_fd, int lock_required ); static int overlay_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device); static int check_fimc_dst_constraints(s5p_fimc_t *s5p_fimc, unsigned int rotation); static int check_fimc_src_constraints(s5p_fimc_t *s5p_fimc); static struct hw_module_methods_t overlay_module_methods = { open: overlay_device_open }; struct overlay_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: OVERLAY_HARDWARE_MODULE_ID, name: "SEC Overlay module", author: "The Android Open Source Project", methods: &overlay_module_methods, } }; /*****************************************************************************/ /* * This is the overlay_t object, it is returned to the user and represents * an overlay. here we use a subclass, where we can store our own state. * This handles will be passed across processes and possibly given to other * HAL modules (for instance video decode modules). */ struct handle_t : public native_handle { /* add the data fields we need here, for instance: */ int ctl_fd; int shared_fd; int width; int height; int format; int num_buffers; int shared_size; }; static int handle_format(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->format; } static int handle_ctl_fd(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->ctl_fd; } static int handle_shared_fd(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->shared_fd; } static int handle_num_buffers(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->num_buffers; } static int handle_width(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->width; } static int handle_height(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->height; } static int handle_shared_size(const overlay_handle_t overlay) { return static_cast<const struct handle_t *>(overlay)->shared_size; } /* A separate instance of this class is created per overlay */ class overlay_object : public overlay_t { handle_t mHandle; overlay_ctrl_t mCtl; overlay_ctrl_t mCtlStage; overlay_shared_t *mShared; static overlay_handle_t getHandleRef(struct overlay_t* overlay) { /* returns a reference to the handle, caller doesn't take ownership */ return &(static_cast<overlay_object *>(overlay)->mHandle); } public: overlay_object(int ctl_fd, int shared_fd, int shared_size, int w, int h, int format, int num_buffers) { this->overlay_t::getHandleRef = getHandleRef; mHandle.version = sizeof(native_handle); mHandle.numFds = 2; mHandle.numInts = 5; /* extra ints we have in our handle */ mHandle.ctl_fd = ctl_fd; mHandle.shared_fd = shared_fd; mHandle.width = w; mHandle.height = h; mHandle.format = format; mHandle.num_buffers = num_buffers; mHandle.shared_size = shared_size; this->w = w; this->h = h; this->format = format; memset( &mCtl, 0, sizeof( mCtl ) ); memset( &mCtlStage, 0, sizeof( mCtlStage ) ); } int ctl_fd() { return mHandle.ctl_fd; } int shared_fd() { return mHandle.shared_fd; } overlay_ctrl_t* data() { return &mCtl; } overlay_ctrl_t* staging() { return &mCtlStage; } overlay_shared_t* getShared() { return mShared; } void setShared( overlay_shared_t *p ) { mShared = p; } }; /***************************************************************************** * Local Functions *****************************************************************************/ static int create_shared_data(overlay_shared_t **shared) { int fd; /* assuming sizeof(overlay_shared_t) < a single page */ int size = getpagesize(); overlay_shared_t *p; if ((fd = ashmem_create_region("overlay_data", size)) < 0) { LOGE("Failed to Create Overlay Shared Data!\n"); return fd; } p = (overlay_shared_t*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { LOGE("Failed to Map Overlay Shared Data!\n"); close(fd); return -1; } memset(p, 0, size); p->marker = SHARED_DATA_MARKER; p->size = size; p->refCnt = 1; if (pthread_mutexattr_init(&p->attr) != 0) { LOGE("Failed to initialize overlay mutex attr"); goto MutexAttrErr; } if (pthread_mutexattr_setpshared(&p->attr, PTHREAD_PROCESS_SHARED) != 0) { LOGE("Failed to set the overlay mutex attr to be shared across-processes"); goto MutexAttrSetErr; } if (pthread_mutex_init(&p->lock, &p->attr) != 0) { LOGE("Failed to initialize overlay mutex\n"); goto MutexErr; } *shared = p; return fd; MutexErr: MutexAttrSetErr: pthread_mutexattr_destroy(&p->attr); MutexAttrErr: munmap(p, size); close(fd); return -1; } static void destroy_shared_data(int shared_fd, overlay_shared_t *shared, bool closefd ) { if (shared == NULL) return; /* Last side deallocated releases the mutex, otherwise the remaining */ /* side will deadlock trying to use an already released mutex */ if (android_atomic_dec(&shared->refCnt) == 1) { if (pthread_mutex_destroy(&shared->lock)) { LOGE("Failed to uninitialize overlay mutex!\n"); } if (pthread_mutexattr_destroy(&shared->attr)) { LOGE("Failed to uninitialize the overlay mutex attr!\n"); } shared->marker = 0; } if (munmap(shared, shared->size)) { LOGE("Failed to Unmap Overlay Shared Data!\n"); } if (closefd && close(shared_fd)) { LOGE("Failed to Close Overlay Shared Data!\n"); } } static int open_shared_data( overlay_data_context_t *ctx ) { int rc = -1; int mode = PROT_READ | PROT_WRITE; int fd = ctx->shared_fd; int size = ctx->shared_size; if (ctx->shared != NULL) { /* Already open, return success */ LOGI("Overlay Shared Data Already Open\n"); return 0; } ctx->shared = (overlay_shared_t*)mmap(0, size, mode, MAP_SHARED, fd, 0); if (ctx->shared == MAP_FAILED) { LOGE("Failed to Map Overlay Shared Data!\n"); } else if ( ctx->shared->marker != SHARED_DATA_MARKER ) { LOGE("Invalid Overlay Shared Marker!\n"); munmap( ctx->shared, size); } else if ( (int)ctx->shared->size != size ) { LOGE("Invalid Overlay Shared Size!\n"); munmap(ctx->shared, size); } else { android_atomic_inc(&ctx->shared->refCnt); rc = 0; } return rc; } static void close_shared_data(overlay_data_context_t *ctx) { destroy_shared_data(ctx->shared_fd, ctx->shared, false); ctx->shared = NULL; } static int enable_streaming_locked(overlay_shared_t *shared, int ovly_fd) { int rc = 0; if (!shared->controlReady || !shared->dataReady) { LOGI("Postponing Stream Enable/%d/%d\n", shared->controlReady, shared->dataReady); } else { shared->streamEn = 1; rc = v4l2_overlay_stream_on(ovly_fd); if (rc) { LOGE("Stream Enable Failed!/%d\n", rc); shared->streamEn = 0; } } return rc; } static int enable_streaming(overlay_shared_t *shared, int ovly_fd) { int ret; pthread_mutex_lock(&shared->lock); ret = enable_streaming_locked(shared, ovly_fd); pthread_mutex_unlock(&shared->lock); return ret; } static int disable_streaming_locked(overlay_shared_t *shared, int ovly_fd) { int ret = 0; if (shared->streamEn) { ret = v4l2_overlay_stream_off( ovly_fd ); if (ret) { LOGE("Stream Off Failed!/%d\n", ret); } else { shared->streamingReset = 1; shared->streamEn = 0; } } return ret; } static void set_color_space(unsigned int overlay_color_format, unsigned int *v4l2_color_format) { switch (overlay_color_format) { case OVERLAY_FORMAT_RGB_565: *v4l2_color_format = V4L2_PIX_FMT_RGB565; break; case OVERLAY_FORMAT_YCbYCr_422_I: case HAL_PIXEL_FORMAT_CUSTOM_YCbCr_422_I: *v4l2_color_format = V4L2_PIX_FMT_YUYV; break; case OVERLAY_FORMAT_CbYCrY_422_I: case HAL_PIXEL_FORMAT_CUSTOM_CbYCrY_422_I: *v4l2_color_format = V4L2_PIX_FMT_UYVY; break; case HAL_PIXEL_FORMAT_YCbCr_420_P: *v4l2_color_format = V4L2_PIX_FMT_YUV420; break; case HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP: *v4l2_color_format = V4L2_PIX_FMT_NV12T; break; case HAL_PIXEL_FORMAT_CUSTOM_YCrCb_420_SP: *v4l2_color_format = V4L2_PIX_FMT_NV21; break; default : LOGE("unsupported pixel format (0x%x)", overlay_color_format); *v4l2_color_format = -1; } } /**************************************************************************** * Control module *****************************************************************************/ static int overlay_get(struct overlay_control_device_t *dev, int name) { int result = -1; switch (name) { /* 0 = no limit */ case OVERLAY_MINIFICATION_LIMIT: result = 0; break; /* 0 = no limit */ case OVERLAY_MAGNIFICATION_LIMIT: result = 0; break; /* 0 = infinite */ case OVERLAY_SCALING_FRAC_BITS: result = 0; break; /* 90 rotation steps (for instance) */ case OVERLAY_ROTATION_STEP_DEG: result = 90; break; /* 1-pixel alignment */ case OVERLAY_HORIZONTAL_ALIGNMENT: result = 1; break; /* 1-pixel alignment */ case OVERLAY_VERTICAL_ALIGNMENT: result = 1; break; /* 1-pixel alignment */ case OVERLAY_WIDTH_ALIGNMENT: result = 1; break; case OVERLAY_HEIGHT_ALIGNMENT: break; } return result; } static int get_fb_var_screeninfo( struct fb_var_screeninfo *info ) { int fd = -1; int i=0; char name[64]; int ret = 0; char const * const device_template[] = { "/dev/graphics/fb%u", "/dev/fb%u", 0 }; while ((fd==-1) && device_template[i]) { snprintf(name, 64, device_template[i], 0); fd = open(name, O_RDWR, 0); i++; } if (fd < 0) ret = -EINVAL; if (ioctl(fd, FBIOGET_VSCREENINFO, info) == -1) ret = -EINVAL; if (fd > 0) close(fd); return 0; } static overlay_t* overlay_createOverlay(struct overlay_control_device_t *dev, uint32_t w, uint32_t h, int32_t format) { LOGD("overlay_createOverlay:IN w=%d h=%d format=%d\n", w, h, format); LOG_FUNCTION_NAME; overlay_object *overlay; overlay_control_context_t *ctx = (overlay_control_context_t *)dev; overlay_shared_t *shared; int ret; uint32_t num = NUM_OVERLAY_BUFFERS_REQUESTED; int fd; int shared_fd; struct fb_var_screeninfo info; bool zerocopy = false; phyAddr = 0; if (format == OVERLAY_FORMAT_DEFAULT) { LOGV("format == OVERLAY_FORMAT_DEFAULT\n"); LOGV("set to HAL_PIXEL_FORMAT_CUSTOM_YCrCb_420_SP\n"); format = HAL_PIXEL_FORMAT_CUSTOM_YCrCb_420_SP; } if (ctx->overlay_video1) { LOGE("Error - overlays already in use\n"); return NULL; } shared_fd = create_shared_data(&shared); if (shared_fd < 0) { LOGE("Failed to create shared data"); return NULL; } fd = v4l2_overlay_open(V4L2_OVERLAY_PLANE_VIDEO1); if (fd < 0) { LOGE("Failed to open overlay device : %s\n", strerror(errno)); goto error; } g_s5p_fimc.params.src.full_width = w; g_s5p_fimc.params.src.full_height = h; g_s5p_fimc.params.src.width = w; g_s5p_fimc.params.src.height = h; set_color_space(format, &g_s5p_fimc.params.src.color_space); ret = check_fimc_src_constraints(&g_s5p_fimc); if(ret != 0) { if(ret < 0) { LOGE("Not supported source image size"); goto error1; } else { LOGD("src width, height are changed [w= %d, h= %d]->[w=%d, h= %d]" , w, h, g_s5p_fimc.params.src.width , g_s5p_fimc.params.src.height); w = g_s5p_fimc.params.src.width; h = g_s5p_fimc.params.src.height; } } if (v4l2_overlay_init(fd, w, h, format, phyAddr)) { LOGE("Failed initializing overlays\n"); goto error1; } if (v4l2_overlay_set_crop(fd, 0, 0, w, h)) { LOGE("Failed defaulting crop window\n"); goto error1; } if (v4l2_overlay_set_flip(fd, 0)) { LOGE("Failed defaulting flip\n"); goto error1; } if (v4l2_overlay_set_rotation(fd, 0, 0)) { LOGE("Failed defaulting rotation\n"); goto error1; } if (format >= HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP && format < HAL_PIXEL_FORMAT_CUSTOM_MAX) zerocopy = true; if (v4l2_overlay_req_buf(fd, &num, 0, (int)zerocopy)) { LOGE("Failed requesting buffers\n"); goto error1; } v4l2_overlay_init_fimc(fd, &g_s5p_fimc); overlay = new overlay_object(fd, shared_fd, shared->size, w, h, format, num); if (overlay == NULL) { LOGE("Failed to create overlay object\n"); goto error1; } ctx->overlay_video1 = overlay; overlay->setShared(shared); shared->controlReady = 0; shared->streamEn = 0; shared->streamingReset = 0; /* get lcd size from kernel framebuffer */ if(get_fb_var_screeninfo(&info) == 0) { shared->dispW = info.xres; shared->dispH = info.yres; g_lcd_width = info.xres; g_lcd_height = info.yres; g_lcd_bpp = info.bits_per_pixel; } else { shared->dispW = g_lcd_width; /* Need to determine this properly */ shared->dispH = g_lcd_height; /* Need to determine this properly */ } LOGI("Opened video1/fd=%d/obj=%08lx/shm=%d/size=%d", fd, (unsigned long)overlay, shared_fd, shared->size); return overlay; error1: close(fd); error: destroy_shared_data(shared_fd, shared, true); return NULL; } static void overlay_destroyOverlay(struct overlay_control_device_t *dev, overlay_t* overlay) { LOGD("overlay_destroyOverlay:IN dev (%p) and overlay (%p)", dev, overlay); LOG_FUNCTION_NAME; overlay_control_context_t *ctx = (overlay_control_context_t *)dev; overlay_object *obj = static_cast<overlay_object *>(overlay); int rc; int fd = obj->ctl_fd(); uint32_t num = 0; overlay_shared_t *shared = obj->getShared(); if (shared == NULL) { LOGE("Overlay was already destroyed - nothing needs to be done\n"); return; } pthread_mutex_lock(&shared->lock); disable_streaming_locked(shared, fd); pthread_mutex_unlock(&shared->lock); destroy_shared_data(obj->shared_fd(), shared, true); obj->setShared(NULL); if (v4l2_overlay_req_buf(fd, &num, 0, 0)) { LOGE("Failed requesting buffers\n"); } LOGI("Destroying overlay/fd=%d/obj=%08lx", fd, (unsigned long)overlay); if (close(fd)) { LOGE( "Error closing overly fd/%d\n", errno); } if (overlay) { if (ctx->overlay_video1 == overlay) ctx->overlay_video1 = NULL; delete overlay; overlay = NULL; } LOGD("overlay_destroyOverlay:OUT"); } static int overlay_setPosition(struct overlay_control_device_t *dev, overlay_t* overlay, int x, int y, uint32_t w, uint32_t h) { LOG_FUNCTION_NAME; overlay_object *obj = static_cast<overlay_object *>(overlay); overlay_ctrl_t *stage = obj->staging(); overlay_shared_t *shared = obj->getShared(); int rc = 0; int temp_x = x, temp_y = y, temp_w = w, temp_h = h; /* * This logic here is to return an error if the rectangle is not fully * within the display, unless we have not received a valid position yet, * in which case we will do our best to adjust the rectangle to be within * the display. */ /* Require a minimum size */ if (temp_w < 16) temp_w = 16; if (temp_h < 8) temp_h = 8; if (!shared->controlReady) { if ( temp_x < 0 ) temp_x = 0; if ( temp_y < 0 ) temp_y = 0; if ( temp_w > shared->dispW ) temp_w = shared->dispW; if ( temp_h > shared->dispH ) temp_h = shared->dispH; if ( (temp_x + temp_w) > shared->dispW ) temp_w = shared->dispW - temp_x; if ( (temp_y + temp_h) > shared->dispH ) temp_h = shared->dispH - temp_y; } else if (temp_x < 0 || temp_y < 0 || (temp_x + temp_w) > shared->dispW || (temp_y + temp_h) > shared->dispH) { /* Return an error */ rc = -1; } if (rc == 0) { stage->posX = temp_x; stage->posY = temp_y; stage->posW = temp_w; stage->posH = temp_h; stage->posX_org = x; stage->posY_org = y; stage->posW_org = w; stage->posH_org = h; } return rc; } static int overlay_getPosition(struct overlay_control_device_t *dev, overlay_t* overlay, int* x, int* y, uint32_t* w, uint32_t* h) { LOG_FUNCTION_NAME; overlay_object *obj = static_cast<overlay_object *>(overlay); overlay_ctrl_t *stage = obj->staging(); *x = stage->posX_org; *y = stage->posY_org; *w = stage->posW_org; *h = stage->posH_org; return 0; } static int overlay_setParameter(struct overlay_control_device_t *dev, overlay_t* overlay, int param, int value) { LOG_FUNCTION_NAME; overlay_ctrl_t *stage = static_cast<overlay_object *>(overlay)->staging(); int rc = 0; switch (param) { case OVERLAY_DITHER: break; case OVERLAY_TRANSFORM: switch ( value ) { case 0: stage->rotation = 0; stage->flip = 0; break; case OVERLAY_TRANSFORM_ROT_90: stage->rotation = 90; stage->flip = 0; break; case OVERLAY_TRANSFORM_ROT_180: stage->rotation = 180; stage->flip = 0; break; case OVERLAY_TRANSFORM_ROT_270: stage->rotation = 270; stage->flip = 0; break; // FIMC VFLIP = android overlay FLIP_H. case OVERLAY_TRANSFORM_FLIP_H: stage->rotation = 0; stage->flip = V4L2_CID_VFLIP; break; case OVERLAY_TRANSFORM_FLIP_V: stage->rotation = 0; stage->flip = V4L2_CID_HFLIP; break; // FIMC rotates first but android flips first. case OVERLAY_TRANSFORM_ROT_90+OVERLAY_TRANSFORM_FLIP_H: stage->rotation = 90; stage->flip = V4L2_CID_HFLIP; break; case OVERLAY_TRANSFORM_ROT_90+OVERLAY_TRANSFORM_FLIP_V: stage->rotation = 90; stage->flip = V4L2_CID_VFLIP; break; default: rc = -EINVAL; break; } break; } return rc; } static int overlay_stage(struct overlay_control_device_t *dev, overlay_t* overlay) { return 0; } static int overlay_commit(struct overlay_control_device_t *dev, overlay_t* overlay) { LOG_FUNCTION_NAME; overlay_object *obj = static_cast<overlay_object *>(overlay); overlay_ctrl_t *data = obj->data(); overlay_ctrl_t *stage = obj->staging(); overlay_shared_t *shared = obj->getShared(); int ret = 0; int fd = obj->ctl_fd(); if (shared == NULL) { LOGI("Shared Data Not Init'd!\n"); return -1; } pthread_mutex_lock(&shared->lock); if (!shared->controlReady) { shared->controlReady = 1; } g_s5p_fimc.params.dst.full_width = g_lcd_width; g_s5p_fimc.params.dst.full_height = g_lcd_height; g_s5p_fimc.params.dst.width = stage->posW; g_s5p_fimc.params.dst.height = stage->posH; if (g_lcd_bpp == 32) g_s5p_fimc.params.dst.color_space = V4L2_PIX_FMT_RGB32; else g_s5p_fimc.params.dst.color_space = V4L2_PIX_FMT_RGB565; ret = check_fimc_dst_constraints(&g_s5p_fimc, stage->rotation); if (ret != 0) { if (ret < 0) { LOGE("Unsupported destination image size"); goto end; } else { LOGD("dst width, height have changed [w= %d, h= %d] -> [w=%d, h= %d]", stage->posW, stage->posH, g_s5p_fimc.params.dst.width, g_s5p_fimc.params.dst.height); stage->posW = g_s5p_fimc.params.dst.width; stage->posH = g_s5p_fimc.params.dst.height; } } if (data->posX == stage->posX && data->posY == stage->posY && data->posW == stage->posW && data->posH == stage->posH && data->rotation == stage->rotation && data->flip == stage->flip) { LOGI("Nothing to do!\n"); goto end; } LOGV("Position/X%d/Y%d/W%d/H%d\n", data->posX, data->posY, data->posW, data->posH); LOGV("Adjusted Position/X%d/Y%d/W%d/H%d\n", stage->posX, stage->posY, stage->posW, stage->posH); LOGV("Rotation/%d\n", stage->rotation ); if ((ret = disable_streaming_locked(shared, fd))) goto end; if (stage->flip != data->flip) { ret = v4l2_overlay_set_flip(fd, stage->flip); if (ret) { LOGE("Set Flip Failed!/%d\n", ret); goto end; } } if (stage->rotation != data->rotation) { ret = v4l2_overlay_set_rotation(fd, stage->rotation, 0); if (ret) { LOGE("Set Rotation Failed!/%d\n", ret); goto end; } v4l2_overlay_s_fbuf(fd, stage->rotation); } ret = v4l2_overlay_set_position(fd, stage->posX, stage->posY, stage->posW, stage->posH, stage->rotation); if (ret) { LOGE("Set Position Failed!/%d\n", ret); goto end; } data->posX = stage->posX; data->posY = stage->posY; data->posW = stage->posW; data->posH = stage->posH; data->rotation = stage->rotation; data->flip = stage->flip; ret = enable_streaming_locked(shared, fd); end: pthread_mutex_unlock(&shared->lock); return ret; } static int overlay_control_close(struct hw_device_t *dev) { LOG_FUNCTION_NAME; struct overlay_control_context_t* ctx = (struct overlay_control_context_t*)dev; overlay_object *overlay_v1; if (ctx) { overlay_v1 = static_cast<overlay_object *>(ctx->overlay_video1); overlay_destroyOverlay((struct overlay_control_device_t *)ctx, overlay_v1); free(ctx); } return 0; } static int get_pixel_format_type(unsigned int pixelformat) { switch(pixelformat) { case V4L2_PIX_FMT_RGB32: case V4L2_PIX_FMT_RGB565: return PFT_RGB; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12T: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_YUV420: return PFT_YUV420; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_YUV422P: return PFT_YUV422; default: return PFT_YUV444; } } /* check the constraints of destination image size */ static int check_fimc_dst_constraints(s5p_fimc_t *s5p_fimc, unsigned int rotation) { int tmp = 0; if((s5p_fimc->params.dst.height > 0) && (s5p_fimc->params.dst.height < 16)) s5p_fimc->params.dst.height = 16; if(s5p_fimc->params.dst.width%8 != 0) { tmp = s5p_fimc->params.dst.width - (s5p_fimc->params.dst.width%8); if(tmp <= 0) return -1; else s5p_fimc->params.dst.width = tmp; } return 1; } /* check the constraints of source image size */ static int check_fimc_src_constraints(s5p_fimc_t *s5p_fimc) { int format_type = 0; if(s5p_fimc->params.src.full_width < 16 || s5p_fimc->params.src.full_height < 8 ) return -1; if(s5p_fimc->hw_ver == 0x50) { format_type = get_pixel_format_type(s5p_fimc->params.src.color_space); switch (format_type) { case PFT_YUV420: if (s5p_fimc->params.src.height%2 != 0) s5p_fimc->params.src.height = s5p_fimc->params.src.height - (s5p_fimc->params.src.height)%2; if (s5p_fimc->params.src.width%2 != 0) s5p_fimc->params.src.width = s5p_fimc->params.src.width - (s5p_fimc->params.src.width)%2; break; case PFT_YUV422: if (s5p_fimc->params.src.width%2 != 0) s5p_fimc->params.src.width = s5p_fimc->params.src.width - (s5p_fimc->params.src.width)%2; } } else { if (s5p_fimc->params.src.height < 8) { s5p_fimc->params.src.height = 8; } if (s5p_fimc->params.src.width%16 != 0) { s5p_fimc->params.src.width = s5p_fimc->params.src.width - (s5p_fimc->params.src.width)%16; } } return 1; } /**************************************************************************** * Data module *****************************************************************************/ int overlay_initialize(struct overlay_data_device_t *dev, overlay_handle_t handle) { LOG_FUNCTION_NAME; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; struct stat stat; int i; int rc = -1; ctx->num_buffers = handle_num_buffers(handle); ctx->width = handle_width(handle); ctx->height = handle_height(handle); ctx->format = handle_format(handle); ctx->ctl_fd = handle_ctl_fd(handle); ctx->shared_fd = handle_shared_fd(handle); ctx->shared_size = handle_shared_size(handle); ctx->shared = NULL; ctx->qd_buf_count = 0; ctx->cacheable_buffers = 0; if (ctx->format >= HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP && ctx->format < HAL_PIXEL_FORMAT_CUSTOM_MAX) ctx->zerocopy = true; else ctx->zerocopy = false; if (fstat(ctx->ctl_fd, &stat)) { LOGE("Error = %s from %s\n", strerror(errno), "overlay initialize"); return -1; } if (open_shared_data(ctx)) { return -1; } ctx->shared->dataReady = 0; ctx->mapping_data = new struct mapping_data; ctx->buffers = new void* [ctx->num_buffers]; ctx->buffers_len = new size_t[ctx->num_buffers]; if (!ctx->buffers || !ctx->buffers_len || !ctx->mapping_data) { LOGE("Failed alloc'ing buffer arrays\n"); goto error; } else { /* * in the zero copy case, * don't need to mmap buffer for source */ if (ctx->zerocopy) rc = 0; else { for (i = 0; i < ctx->num_buffers; i++) { rc = v4l2_overlay_map_buf(ctx->ctl_fd, i, &ctx->buffers[i], &ctx->buffers_len[i]); if (rc) { LOGE("Failed mapping buffers\n"); goto error; } } } } v4l2_overlay_init_fimc(ctx->ctl_fd, &g_s5p_fimc); return ( rc ); error: if(ctx->mapping_data) delete (ctx->mapping_data); if(ctx->buffers) delete [] ctx->buffers; if(ctx->buffers_len) delete [] ctx->buffers_len; close_shared_data( ctx ); return -1; } static int overlay_resizeInput(struct overlay_data_device_t *dev, uint32_t w, uint32_t h) { int rc = -1; int ret = 0; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; if ((ctx->width == (int)w) && (ctx->width == (int)h)) { LOGV("same as current width and height. so do nothing"); return 0; } if (!ctx->shared) { LOGI("Shared Data Not Init'd!\n"); return -1; } if (ctx->shared->dataReady) { LOGV("Either setCrop() or queueBuffer() was called prior to this!" "Therefore failing this call.\n"); return -1; } pthread_mutex_lock(&ctx->shared->lock); if ((rc = disable_streaming_locked(ctx->shared, ctx->ctl_fd))) goto end; if (!ctx->zerocopy) { for (int i = 0; i < ctx->num_buffers; i++) { v4l2_overlay_unmap_buf(ctx->buffers[i], ctx->buffers_len[i]); } } g_s5p_fimc.params.src.full_width = w; g_s5p_fimc.params.src.full_height = h; g_s5p_fimc.params.src.width = w; g_s5p_fimc.params.src.height = h; set_color_space(ctx->format, &g_s5p_fimc.params.src.color_space); ret = check_fimc_src_constraints(&g_s5p_fimc); if(ret != 0) { if(ret < 0) { LOGE("Not supported source image size"); goto end; } else { LOGD("src width, height are changed [w= %d, h= %d] -> [w=%d, h= %d]" , w, h, g_s5p_fimc.params.src.width , g_s5p_fimc.params.src.height); w = g_s5p_fimc.params.src.width; h = g_s5p_fimc.params.src.height; } } rc = v4l2_overlay_init(ctx->ctl_fd, w, h, ctx->format, phyAddr); if (rc) { LOGE("Error initializing overlay"); goto end; } rc = v4l2_overlay_set_crop(ctx->ctl_fd, 0, 0, w, h); if (rc) { LOGE("Error setting crop window\n"); goto end; } rc = v4l2_overlay_req_buf(ctx->ctl_fd, (uint32_t *)(&ctx->num_buffers), ctx->cacheable_buffers, (int)ctx->zerocopy); if (rc) { LOGE("Error creating buffers"); goto end; } if (!ctx->zerocopy) { for (int i = 0; i < ctx->num_buffers; i++) v4l2_overlay_map_buf(ctx->ctl_fd, i, &ctx->buffers[i], &ctx->buffers_len[i]); } rc = enable_streaming_locked(ctx->shared, ctx->ctl_fd); end: pthread_mutex_unlock(&ctx->shared->lock); return rc; } static int overlay_data_setParameter(struct overlay_data_device_t *dev, int param, int value) { int ret = 0; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; if (ctx->shared == NULL) { LOGI("Shared Data Not Init'd!\n"); return -1; } if (ctx->shared->dataReady) { LOGI("Too late. Cant set it now!\n"); return -1; } if (param == CACHEABLE_BUFFERS) ctx->cacheable_buffers = value; return ( ret ); } static int overlay_setCrop(struct overlay_data_device_t *dev, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { LOG_FUNCTION_NAME; int rc = 0; int cnt = 0; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; if (ctx->shared == NULL) { LOGI("Shared Data Not Init'd!\n"); return -1; } pthread_mutex_lock(&ctx->shared->lock); ctx->shared->dataReady = 1; if (ctx->data.cropX == x && ctx->data.cropY == y && ctx->data.cropW == w && ctx->data.cropH == h) { goto end; } ctx->data.cropX = x; ctx->data.cropY = y; ctx->data.cropW = w; ctx->data.cropH = h; LOGV("Crop Win/X%d/Y%d/W%d/H%d\n", x, y, w, h ); if ((rc = disable_streaming_locked(ctx->shared, ctx->ctl_fd))) goto end; rc = v4l2_overlay_set_crop(ctx->ctl_fd, x, y, w, h); if (rc) { LOGE("Set Crop Window Failed!/%d\n", rc); } rc = enable_streaming_locked(ctx->shared, ctx->ctl_fd); end: pthread_mutex_unlock(&ctx->shared->lock); return rc; } static int overlay_getCrop(struct overlay_data_device_t *dev , uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) { struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; return v4l2_overlay_get_crop(ctx->ctl_fd, x, y, w, h); } int overlay_dequeueBuffer(struct overlay_data_device_t *dev, overlay_buffer_t *buffer) { /* blocks until a buffer is available and return an opaque structure * representing this buffer. */ struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; int rc=0; int i = -1; uint32_t num = 0; int cnt = 0; pthread_mutex_lock(&ctx->shared->lock); if ( ctx->shared->streamingReset ) { ctx->shared->streamingReset = 0; pthread_mutex_unlock(&ctx->shared->lock); return ALL_BUFFERS_FLUSHED; } pthread_mutex_unlock(&ctx->shared->lock); /* If we are not streaming dequeue will fail, skip to prevent error printouts */ if (ctx->shared->streamEn && ctx->qd_buf_count) { if ((rc = v4l2_overlay_dq_buf( ctx->ctl_fd, &i ,ctx->zerocopy)) != 0) { LOGE("Failed to DQ/%d\n", rc); } else if (i < 0 || i > ctx->num_buffers) { rc = -EINVAL; } else { *((int *)buffer) = i; ctx->qd_buf_count --; } } else { rc = -1; } return rc; } int overlay_queueBuffer(struct overlay_data_device_t *dev, overlay_buffer_t buffer) { struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; int cnt = 0; pthread_mutex_lock(&ctx->shared->lock); if ( ctx->shared->streamingReset ) { ctx->shared->streamingReset = 0; pthread_mutex_unlock(&ctx->shared->lock); return ALL_BUFFERS_FLUSHED; } pthread_mutex_unlock(&ctx->shared->lock); /* Catch the case where the data side had no need to set the crop window */ if (!ctx->shared->dataReady) { ctx->shared->dataReady = 1; enable_streaming(ctx->shared, ctx->ctl_fd); } if (!ctx->shared->controlReady) return -1; int rc = v4l2_overlay_q_buf( ctx->ctl_fd, (int)buffer, (int) ctx->zerocopy ); if (rc == 0 && ctx->qd_buf_count < ctx->num_buffers) { ctx->qd_buf_count ++; } return rc; } void *overlay_getBufferAddress(struct overlay_data_device_t *dev, overlay_buffer_t buffer) { LOG_FUNCTION_NAME; /* this may fail (NULL) if this feature is not supported. In that case, * presumably, there is some other HAL module that can fill the buffer, * using a DSP for instance */ int ret; struct v4l2_buffer buf; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; if (ctx->zerocopy) return NULL; if ((int)buffer >= 0 && (int)buffer < ctx->num_buffers) return (void*) ctx->buffers[(int)buffer]; else return NULL; } int overlay_getBufferCount(struct overlay_data_device_t *dev) { LOG_FUNCTION_NAME; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; return (ctx->num_buffers); } static int overlay_data_close(struct hw_device_t *dev) { LOG_FUNCTION_NAME; struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; int rc; if (ctx) { overlay_data_device_t *overlay_dev = &ctx->device; int buf; int i; pthread_mutex_lock(&ctx->shared->lock); if (!ctx->zerocopy) for (i = 0; i < ctx->num_buffers; i++) { LOGV("Unmap Buffer/%d/%08lx/%d", i, (unsigned long)ctx->buffers[i], ctx->buffers_len[i] ); rc = v4l2_overlay_unmap_buf(ctx->buffers[i], ctx->buffers_len[i]); if (rc != 0) { LOGE("Error unmapping the buffer/%d/%d", i, rc); } } delete (ctx->mapping_data); delete [] ctx->buffers; delete [] ctx->buffers_len; pthread_mutex_unlock(&ctx->shared->lock); ctx->shared->dataReady = 0; close_shared_data( ctx ); free(ctx); } return 0; } /*****************************************************************************/ static int overlay_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { LOG_FUNCTION_NAME; int status = -EINVAL; if (!strcmp(name, OVERLAY_HARDWARE_CONTROL)) { struct overlay_control_context_t *dev; dev = (overlay_control_context_t*)malloc(sizeof(*dev)); /* initialize our state here */ memset(dev, 0, sizeof(*dev)); /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast<hw_module_t*>(module); dev->device.common.close = overlay_control_close; dev->device.get = overlay_get; dev->device.createOverlay = overlay_createOverlay; dev->device.destroyOverlay = overlay_destroyOverlay; dev->device.setPosition = overlay_setPosition; dev->device.getPosition = overlay_getPosition; dev->device.setParameter = overlay_setParameter; dev->device.stage = overlay_stage; dev->device.commit = overlay_commit; *device = &dev->device.common; status = 0; } else if (!strcmp(name, OVERLAY_HARDWARE_DATA)) { struct overlay_data_context_t *dev; dev = (overlay_data_context_t*)malloc(sizeof(*dev)); /* initialize our state here */ memset(dev, 0, sizeof(*dev)); /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast<hw_module_t*>(module); dev->device.common.close = overlay_data_close; dev->device.initialize = overlay_initialize; dev->device.resizeInput = overlay_resizeInput; dev->device.setCrop = overlay_setCrop; dev->device.getCrop = overlay_getCrop; dev->device.setParameter = overlay_data_setParameter; dev->device.dequeueBuffer = overlay_dequeueBuffer; dev->device.queueBuffer = overlay_queueBuffer; dev->device.getBufferAddress = overlay_getBufferAddress; dev->device.getBufferCount = overlay_getBufferCount; *device = &dev->device.common; status = 0; } return status; }