diff options
Diffstat (limited to 'liboverlay/overlay.cpp')
-rw-r--r-- | liboverlay/overlay.cpp | 1490 |
1 files changed, 1490 insertions, 0 deletions
diff --git a/liboverlay/overlay.cpp b/liboverlay/overlay.cpp new file mode 100644 index 0000000..d0a4289 --- /dev/null +++ b/liboverlay/overlay.cpp @@ -0,0 +1,1490 @@ +/* + * 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) { + LOGD("format == OVERLAY_FORMAT_DEFAULT\n"); + LOGD("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; + } + + LOGD("Position/X%d/Y%d/W%d/H%d\n", data->posX, data->posY, data->posW, + data->posH); + LOGD("Adjusted Position/X%d/Y%d/W%d/H%d\n", stage->posX, stage->posY, + stage->posW, stage->posH); + LOGD("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; + + LOGD("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; +} |