| /* |
| * Copyright (C) 2008 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 exynos_gscaler.c |
| * \brief header file for Gscaler HAL |
| * \author ShinWon Lee (shinwon.lee@samsung.com) |
| * \date 2012/01/09 |
| * |
| * <b>Revision History: </b> |
| * - 2012.01.09 : ShinWon Lee(shinwon.lee@samsung.com) \n |
| * Create |
| * |
| * - 2012.02.07 : ShinWon Lee(shinwon.lee@samsung.com) \n |
| * Change file name to exynos_gscaler.h |
| * |
| * - 2012.02.09 : Sangwoo, Parkk(sw5771.park@samsung.com) \n |
| * Use Multiple Gscaler by Multiple Process |
| * |
| * - 2012.02.20 : Sangwoo, Park(sw5771.park@samsung.com) \n |
| * Add exynos_gsc_set_rotation() API |
| * |
| * - 2012.02.20 : ShinWon Lee(shinwon.lee@samsung.com) \n |
| * Add size constrain |
| * |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #include "exynos_gsc_utils.h" |
| #include "content_protect.h" |
| |
| static int exynos_gsc_m2m_wait_frame_done(void *handle); |
| static int exynos_gsc_m2m_stop(void *handle); |
| |
| static unsigned int m_gsc_get_plane_count( |
| int v4l_pixel_format) |
| { |
| int plane_count = 0; |
| |
| switch (v4l_pixel_format) { |
| case V4L2_PIX_FMT_RGB32: |
| case V4L2_PIX_FMT_BGR32: |
| case V4L2_PIX_FMT_RGB24: |
| case V4L2_PIX_FMT_RGB565: |
| case V4L2_PIX_FMT_RGB555X: |
| case V4L2_PIX_FMT_RGB444: |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_NV61: |
| plane_count = 1; |
| break; |
| case V4L2_PIX_FMT_NV12M: |
| case V4L2_PIX_FMT_NV12MT_16X16: |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV21M: |
| plane_count = 2; |
| break; |
| case V4L2_PIX_FMT_YVU420M: |
| case V4L2_PIX_FMT_YUV422P: |
| case V4L2_PIX_FMT_YUV420M: |
| plane_count = 3; |
| break; |
| default: |
| ALOGE("%s::unmatched v4l_pixel_format color_space(0x%x)\n", |
| __func__, v4l_pixel_format); |
| plane_count = -1; |
| break; |
| } |
| |
| return plane_count; |
| } |
| |
| static unsigned int m_gsc_get_plane_size( |
| unsigned int *plane_size, |
| unsigned int width, |
| unsigned int height, |
| int v4l_pixel_format) |
| { |
| switch (v4l_pixel_format) { |
| /* 1 plane */ |
| case V4L2_PIX_FMT_RGB32: |
| case V4L2_PIX_FMT_BGR32: |
| plane_size[0] = width * height * 4; |
| plane_size[1] = 0; |
| plane_size[2] = 0; |
| break; |
| case V4L2_PIX_FMT_RGB24: |
| plane_size[0] = width * height * 3; |
| plane_size[1] = 0; |
| plane_size[2] = 0; |
| break; |
| case V4L2_PIX_FMT_RGB565: |
| case V4L2_PIX_FMT_RGB555X: |
| case V4L2_PIX_FMT_RGB444: |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_UYVY: |
| plane_size[0] = width * height * 2; |
| plane_size[1] = 0; |
| plane_size[2] = 0; |
| break; |
| /* 2 planes */ |
| case V4L2_PIX_FMT_NV12M: |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV21M: |
| plane_size[0] = width * height; |
| plane_size[1] = width * (height / 2); |
| plane_size[2] = 0; |
| break; |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_NV61: |
| plane_size[0] = width * height * 2; |
| plane_size[1] = 0; |
| plane_size[2] = 0; |
| break; |
| case V4L2_PIX_FMT_NV12MT_16X16: |
| plane_size[0] = ALIGN(width, 16) * ALIGN(height, 16); |
| plane_size[1] = ALIGN(width, 16) * ALIGN(height / 2, 8); |
| plane_size[2] = 0; |
| break; |
| /* 3 planes */ |
| case V4L2_PIX_FMT_YVU420M: |
| case V4L2_PIX_FMT_YUV422P: |
| plane_size[0] = width * height; |
| plane_size[1] = (width / 2) * (height / 2); |
| plane_size[2] = (width / 2) * (height / 2); |
| break; |
| default: |
| ALOGE("%s::unmatched v4l_pixel_format color_space(0x%x)\n", |
| __func__, v4l_pixel_format); |
| return -1; |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int m_exynos_gsc_multiple_of_n( |
| int number, int N) |
| { |
| int result = number; |
| switch (N) { |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| case 16: |
| case 32: |
| case 64: |
| case 128: |
| case 256: |
| result = (number - (number & (N-1))); |
| break; |
| default: |
| result = number - (number % N); |
| break; |
| } |
| return result; |
| } |
| |
| static bool m_exynos_gsc_check_src_size( |
| unsigned int *w, unsigned int *h, |
| unsigned int *crop_x, unsigned int *crop_y, |
| unsigned int *crop_w, unsigned int *crop_h, |
| int v4l2_colorformat) |
| { |
| if (*w < GSC_MIN_W_SIZE || *h < GSC_MIN_H_SIZE) { |
| ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)", |
| __func__, GSC_MIN_W_SIZE, *w, GSC_MIN_H_SIZE, *h); |
| return false; |
| } |
| |
| if (*crop_w < GSC_MIN_W_SIZE || *crop_h < GSC_MIN_H_SIZE) { |
| ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)", |
| __func__, GSC_MIN_W_SIZE,* crop_w, GSC_MIN_H_SIZE, *crop_h); |
| return false; |
| } |
| |
| switch (v4l2_colorformat) { |
| // YUV420 |
| case V4L2_PIX_FMT_YUV420M: |
| case V4L2_PIX_FMT_YVU420M: |
| case V4L2_PIX_FMT_NV12M: |
| case V4L2_PIX_FMT_NV12MT: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV21M: |
| *w = (*w + 15) & ~15; |
| *h = (*h + 15) & ~15; |
| //*w = m_exynos_gsc_multiple_of_n(*w, 16); |
| //*h = m_exynos_gsc_multiple_of_n(*h, 16); |
| *crop_w = m_exynos_gsc_multiple_of_n(*crop_w, 4); |
| *crop_h = m_exynos_gsc_multiple_of_n(*crop_h, 4); |
| break; |
| // YUV422 |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_YUV422P: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_YVYU: |
| case V4L2_PIX_FMT_VYUY: |
| *h = (*h + 7) & ~7; |
| //*h = m_exynos_gsc_multiple_of_n(*h, 8); |
| *crop_w = m_exynos_gsc_multiple_of_n(*crop_w, 4); |
| *crop_h = m_exynos_gsc_multiple_of_n(*crop_h, 2); |
| break; |
| // RGB |
| case V4L2_PIX_FMT_RGB32: |
| case V4L2_PIX_FMT_RGB24: |
| case V4L2_PIX_FMT_RGB565: |
| case V4L2_PIX_FMT_BGR32: |
| case V4L2_PIX_FMT_RGB555X: |
| case V4L2_PIX_FMT_RGB444: |
| default: |
| *h = (*h + 7) & ~7; |
| //*h = m_exynos_gsc_multiple_of_n(*h, 8); |
| *crop_w = m_exynos_gsc_multiple_of_n(*crop_w, 2); |
| *crop_h = m_exynos_gsc_multiple_of_n(*crop_h, 2); |
| break; |
| } |
| |
| return true; |
| } |
| |
| static bool m_exynos_gsc_check_dst_size( |
| unsigned int *w, unsigned int *h, |
| unsigned int *crop_x, unsigned int *crop_y, |
| unsigned int *crop_w, unsigned int *crop_h, |
| int v4l2_colorformat, |
| int rotation) |
| { |
| unsigned int *new_w; |
| unsigned int *new_h; |
| unsigned int *new_crop_w; |
| unsigned int *new_crop_h; |
| |
| new_w = w; |
| new_h = h; |
| new_crop_w = crop_w; |
| new_crop_h = crop_h; |
| |
| if (*w < GSC_MIN_W_SIZE || *h < GSC_MIN_H_SIZE) { |
| ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)", |
| __func__, GSC_MIN_W_SIZE, *w, GSC_MIN_H_SIZE, *h); |
| return false; |
| } |
| |
| if (*crop_w < GSC_MIN_W_SIZE || *crop_h < GSC_MIN_H_SIZE) { |
| ALOGE("%s::too small size (w : %d < %d) (h : %d < %d)", |
| __func__, GSC_MIN_W_SIZE,* crop_w, GSC_MIN_H_SIZE, *crop_h); |
| return false; |
| } |
| |
| switch (v4l2_colorformat) { |
| // YUV420 |
| case V4L2_PIX_FMT_NV12M: |
| case V4L2_PIX_FMT_NV12MT: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV21M: |
| case V4L2_PIX_FMT_YUV420M: |
| case V4L2_PIX_FMT_YVU420M: |
| *new_w = m_exynos_gsc_multiple_of_n(*new_w, 2); |
| *new_h = m_exynos_gsc_multiple_of_n(*new_h, 2); |
| break; |
| // YUV422 |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_YUV422P: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_YVYU: |
| case V4L2_PIX_FMT_VYUY: |
| *new_w = m_exynos_gsc_multiple_of_n(*new_w, 2); |
| break; |
| // RGB |
| case V4L2_PIX_FMT_RGB32: |
| case V4L2_PIX_FMT_RGB24: |
| case V4L2_PIX_FMT_RGB565: |
| case V4L2_PIX_FMT_BGR32: |
| case V4L2_PIX_FMT_RGB555X: |
| case V4L2_PIX_FMT_RGB444: |
| default: |
| break; |
| } |
| |
| return true; |
| } |
| |
| static int m_exynos_gsc_output_create( |
| struct GSC_HANDLE *gsc_handle, |
| int dev_num, |
| int out_mode) |
| { |
| struct media_device *media0; |
| struct media_entity *gsc_sd_entity; |
| struct media_entity *gsc_vd_entity; |
| struct media_entity *sink_sd_entity; |
| struct media_link *links; |
| char node[32]; |
| char devname[32]; |
| unsigned int cap; |
| int i; |
| int fd = 0; |
| |
| Exynos_gsc_In(); |
| |
| if ((out_mode != GSC_OUT_FIMD) && |
| (out_mode != GSC_OUT_TV)) |
| return -1; |
| |
| gsc_handle->out_mode = out_mode; |
| /* GSCX => FIMD_WINX : arbitrary linking is not allowed */ |
| if ((out_mode == GSC_OUT_FIMD) && |
| (dev_num > 2)) |
| return -1; |
| |
| /* media0 */ |
| sprintf(node, "%s%d", PFX_NODE_MEDIADEV, 0); |
| media0 = exynos_media_open(node); |
| if (media0 == NULL) { |
| ALOGE("%s::exynos_media_open failed (node=%s)", __func__, node); |
| return false; |
| } |
| |
| /* Get the sink subdev entity by name and make the node of sink subdev*/ |
| if (out_mode == GSC_OUT_FIMD) |
| sprintf(devname, PFX_FIMD_ENTITY, dev_num); |
| else |
| sprintf(devname, PFX_MXR_ENTITY, 0); |
| |
| sink_sd_entity = exynos_media_get_entity_by_name(media0, devname, strlen(devname)); |
| sink_sd_entity->fd = exynos_subdev_open_devname(devname, O_RDWR); |
| if ( sink_sd_entity->fd < 0) { |
| ALOGE("%s:: failed to open sink subdev node", __func__); |
| goto gsc_output_err; |
| } |
| |
| /* get GSC video dev & sub dev entity by name*/ |
| sprintf(devname, PFX_GSC_VIDEODEV_ENTITY, dev_num); |
| gsc_vd_entity= exynos_media_get_entity_by_name(media0, devname, strlen(devname)); |
| |
| sprintf(devname, PFX_GSC_SUBDEV_ENTITY, dev_num); |
| gsc_sd_entity= exynos_media_get_entity_by_name(media0, devname, strlen(devname)); |
| |
| /* gsc sub-dev open */ |
| sprintf(devname, PFX_GSC_SUBDEV_ENTITY, dev_num); |
| gsc_sd_entity->fd = exynos_subdev_open_devname(devname, O_RDWR); |
| |
| /* setup link : GSC : video device --> sub device */ |
| for (i = 0; i < (int) gsc_vd_entity->num_links; i++) { |
| links = &gsc_vd_entity->links[i]; |
| |
| if (links == NULL || |
| links->source->entity != gsc_vd_entity || |
| links->sink->entity != gsc_sd_entity) { |
| continue; |
| } else if (exynos_media_setup_link(media0, links->source, links->sink, MEDIA_LNK_FL_ENABLED) < 0) { |
| ALOGE("%s::exynos_media_setup_link [src.entity=%d->sink.entity=%d] failed", |
| __func__, links->source->entity->info.id, links->sink->entity->info.id); |
| return -1; |
| } |
| } |
| |
| /* setup link : GSC: sub device --> sink device */ |
| for (i = 0; i < (int) gsc_sd_entity->num_links; i++) { |
| links = &gsc_sd_entity->links[i]; |
| |
| if (links == NULL || links->source->entity != gsc_sd_entity || |
| links->sink->entity != sink_sd_entity) { |
| continue; |
| } else if (exynos_media_setup_link(media0, links->source, links->sink, MEDIA_LNK_FL_ENABLED) < 0) { |
| ALOGE("%s::exynos_media_setup_link [src.entity=%d->sink.entity=%d] failed", |
| __func__, links->source->entity->info.id, links->sink->entity->info.id); |
| return -1; |
| } |
| } |
| |
| /* gsc video-dev open */ |
| sprintf(devname, PFX_GSC_VIDEODEV_ENTITY, dev_num); |
| gsc_vd_entity->fd = exynos_v4l2_open_devname(devname, O_RDWR); |
| cap = V4L2_CAP_STREAMING | |
| V4L2_CAP_VIDEO_OUTPUT_MPLANE; |
| |
| if (exynos_v4l2_querycap(gsc_vd_entity->fd, cap) == false) { |
| ALOGE("%s::exynos_v4l2_querycap() fail", __func__); |
| goto gsc_output_err; |
| } |
| gsc_handle->gsc_sd_entity = gsc_sd_entity; |
| gsc_handle->gsc_vd_entity = gsc_vd_entity; |
| gsc_handle->sink_sd_entity = sink_sd_entity; |
| gsc_handle->media0 = media0; |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| |
| gsc_output_err: |
| /* to do */ |
| return -1; |
| |
| } |
| |
| static int m_exynos_gsc_m2m_create( |
| int dev) |
| { |
| int fd = 0; |
| int video_node_num; |
| unsigned int cap; |
| char node[32]; |
| |
| Exynos_gsc_In(); |
| |
| switch(dev) { |
| case 0: |
| video_node_num = NODE_NUM_GSC_0; |
| break; |
| case 1: |
| video_node_num = NODE_NUM_GSC_1; |
| break; |
| case 2: |
| video_node_num = NODE_NUM_GSC_2; |
| break; |
| case 3: |
| video_node_num = NODE_NUM_GSC_3; |
| break; |
| default: |
| ALOGE("%s::unexpected dev(%d) fail", __func__, dev); |
| return -1; |
| break; |
| } |
| |
| sprintf(node, "%s%d", PFX_NODE_GSC, video_node_num); |
| fd = exynos_v4l2_open(node, O_RDWR); |
| if (fd < 0) { |
| ALOGE("%s::exynos_v4l2_open(%s) fail", __func__, node); |
| return -1; |
| } |
| |
| cap = V4L2_CAP_STREAMING | |
| V4L2_CAP_VIDEO_OUTPUT_MPLANE | |
| V4L2_CAP_VIDEO_CAPTURE_MPLANE; |
| |
| if (exynos_v4l2_querycap(fd, cap) == false) { |
| ALOGE("%s::exynos_v4l2_querycap() fail", __func__); |
| if (0 < fd) |
| close(fd); |
| fd = 0; |
| return -1; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return fd; |
| } |
| |
| |
| static bool m_exynos_gsc_out_destroy(struct GSC_HANDLE *gsc_handle) |
| { |
| struct media_link * links; |
| int i; |
| |
| Exynos_gsc_In(); |
| |
| if (gsc_handle == NULL) { |
| ALOGE("%s::gsc_handle is NULL", __func__); |
| return false; |
| } |
| |
| if (gsc_handle->src.stream_on == true) { |
| if (exynos_gsc_out_stop((void *)gsc_handle) < 0) |
| ALOGE("%s::exynos_gsc_out_stop() fail", __func__); |
| |
| gsc_handle->src.stream_on = false; |
| } |
| |
| /* unlink : gscaler-out --> fimd */ |
| for (i = 0; i < (int) gsc_handle->gsc_sd_entity->num_links; i++) { |
| links = &gsc_handle->gsc_sd_entity->links[i]; |
| |
| if (links == NULL || links->source->entity != gsc_handle->gsc_sd_entity || |
| links->sink->entity != gsc_handle->sink_sd_entity) { |
| continue; |
| } else if (exynos_media_setup_link(gsc_handle->media0, links->source, |
| links->sink, 0) < 0) { |
| ALOGE("%s::exynos_media_setup_unlink [src.entity=%d->sink.entity=%d] failed", |
| __func__, links->source->entity->info.id, links->sink->entity->info.id); |
| } |
| } |
| |
| close(gsc_handle->gsc_vd_entity->fd); |
| close(gsc_handle->gsc_sd_entity->fd); |
| gsc_handle->gsc_vd_entity->fd = -1; |
| gsc_handle->gsc_vd_entity->fd = -1; |
| |
| Exynos_gsc_Out(); |
| |
| return true; |
| |
| } |
| |
| static bool m_exynos_gsc_destroy( |
| struct GSC_HANDLE *gsc_handle) |
| { |
| Exynos_gsc_In(); |
| |
| /* just in case, we call stop here because we cannot afford to leave |
| * secure side protection on if things failed. |
| */ |
| exynos_gsc_m2m_stop(gsc_handle); |
| |
| if (0 < gsc_handle->gsc_fd) |
| close(gsc_handle->gsc_fd); |
| gsc_handle->gsc_fd = 0; |
| |
| Exynos_gsc_Out(); |
| |
| return true; |
| } |
| |
| bool m_exynos_gsc_find_and_trylock_and_create( |
| struct GSC_HANDLE *gsc_handle) |
| { |
| int i = 0; |
| bool flag_find_new_gsc = false; |
| unsigned int total_sleep_time = 0; |
| |
| Exynos_gsc_In(); |
| |
| do { |
| for (i = 0; i < NUM_OF_GSC_HW; i++) { |
| // HACK : HWComposer, HDMI uses gscaler with their own code. |
| // So, This obj_mutex cannot defense their open() |
| if (i == 0 || i == 3) |
| continue; |
| |
| if (exynos_mutex_trylock(gsc_handle->obj_mutex[i]) == true) { |
| |
| // destroy old one. |
| m_exynos_gsc_destroy(gsc_handle); |
| |
| // create new one. |
| gsc_handle->gsc_id = i; |
| gsc_handle->gsc_fd = m_exynos_gsc_m2m_create(i); |
| if (gsc_handle->gsc_fd < 0) { |
| gsc_handle->gsc_fd = 0; |
| exynos_mutex_unlock(gsc_handle->obj_mutex[i]); |
| continue; |
| } |
| |
| if (gsc_handle->cur_obj_mutex) |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| |
| gsc_handle->cur_obj_mutex = gsc_handle->obj_mutex[i]; |
| |
| flag_find_new_gsc = true; |
| break; |
| } |
| } |
| |
| // waiting for another process doesn't use gscaler. |
| // we need to make decision how to do. |
| if (flag_find_new_gsc == false) { |
| usleep(GSC_WAITING_TIME_FOR_TRYLOCK); |
| total_sleep_time += GSC_WAITING_TIME_FOR_TRYLOCK; |
| ALOGV("%s::waiting for anthere process doens't use gscaler", __func__); |
| } |
| |
| } while( flag_find_new_gsc == false |
| && total_sleep_time < MAX_GSC_WAITING_TIME_FOR_TRYLOCK); |
| |
| if (flag_find_new_gsc == false) |
| ALOGE("%s::we don't have no available gsc.. fail", __func__); |
| |
| Exynos_gsc_Out(); |
| |
| return flag_find_new_gsc; |
| } |
| |
| static bool m_exynos_gsc_set_format( |
| int fd, |
| struct gsc_info *info) |
| { |
| Exynos_gsc_In(); |
| |
| struct v4l2_requestbuffers req_buf; |
| int plane_count; |
| |
| plane_count = m_gsc_get_plane_count(info->v4l2_colorformat); |
| if (plane_count < 0) { |
| ALOGE("%s::not supported v4l2_colorformat", __func__); |
| return false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(fd, V4L2_CID_ROTATE, info->rotation) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_ROTATE) fail", __func__); |
| return false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(fd, V4L2_CID_VFLIP, info->flip_horizontal) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_VFLIP) fail", __func__); |
| return false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(fd, V4L2_CID_HFLIP, info->flip_vertical) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_HFLIP) fail", __func__); |
| return false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(fd, V4L2_CID_CSC_RANGE, info->csc_range) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_CSC_RANGE) fail", __func__); |
| return false; |
| } |
| info->format.type = info->buf_type; |
| info->format.fmt.pix_mp.width = info->width; |
| info->format.fmt.pix_mp.height = info->height; |
| info->format.fmt.pix_mp.pixelformat = info->v4l2_colorformat; |
| info->format.fmt.pix_mp.field = V4L2_FIELD_ANY; |
| info->format.fmt.pix_mp.num_planes = plane_count; |
| |
| if (exynos_v4l2_s_fmt(fd, &info->format) < 0) { |
| ALOGE("%s::exynos_v4l2_s_fmt() fail", __func__); |
| return false; |
| } |
| |
| info->crop.type = info->buf_type; |
| info->crop.c.left = info->crop_left; |
| info->crop.c.top = info->crop_top; |
| info->crop.c.width = info->crop_width; |
| info->crop.c.height = info->crop_height; |
| |
| if (exynos_v4l2_s_crop(fd, &info->crop) < 0) { |
| ALOGE("%s::exynos_v4l2_s_crop() fail", __func__); |
| return false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(fd, V4L2_CID_CACHEABLE, info->cacheable) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl() fail", __func__); |
| return false; |
| } |
| |
| req_buf.count = 1; |
| req_buf.type = info->buf_type; |
| req_buf.memory = V4L2_MEMORY_DMABUF; |
| if (exynos_v4l2_reqbufs(fd, &req_buf) < 0) { |
| ALOGE("%s::exynos_v4l2_reqbufs() fail", __func__); |
| return false; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return true; |
| } |
| |
| static bool m_exynos_gsc_set_addr( |
| int fd, |
| struct gsc_info *info) |
| { |
| unsigned int i; |
| unsigned int plane_size[NUM_OF_GSC_PLANES]; |
| |
| m_gsc_get_plane_size(plane_size, |
| info->width, |
| info->height, |
| info->v4l2_colorformat); |
| |
| info->buffer.index = 0; |
| info->buffer.flags = V4L2_BUF_FLAG_USE_SYNC; |
| info->buffer.type = info->buf_type; |
| info->buffer.memory = V4L2_MEMORY_DMABUF; |
| info->buffer.m.planes = info->planes; |
| info->buffer.length = info->format.fmt.pix_mp.num_planes; |
| info->buffer.reserved = info->acquireFenceFd; |
| |
| for (i = 0; i < info->format.fmt.pix_mp.num_planes; i++) { |
| info->buffer.m.planes[i].m.fd = (int)info->addr[i]; |
| info->buffer.m.planes[i].length = plane_size[i]; |
| info->buffer.m.planes[i].bytesused = 0; |
| } |
| |
| if (exynos_v4l2_qbuf(fd, &info->buffer) < 0) { |
| ALOGE("%s::exynos_v4l2_qbuf() fail", __func__); |
| return false; |
| } |
| info->buffer_queued = true; |
| |
| info->releaseFenceFd = info->buffer.reserved; |
| |
| return true; |
| } |
| |
| void *exynos_gsc_create( |
| void) |
| { |
| int i = 0; |
| int op_id = 0; |
| char mutex_name[32]; |
| |
| Exynos_gsc_In(); |
| |
| struct GSC_HANDLE *gsc_handle = (struct GSC_HANDLE *)malloc(sizeof(struct GSC_HANDLE)); |
| if (gsc_handle == NULL) { |
| ALOGE("%s::malloc(struct GSC_HANDLE) fail", __func__); |
| goto err; |
| } |
| |
| gsc_handle->gsc_fd = 0; |
| memset(&gsc_handle->src, 0, sizeof(struct gsc_info)); |
| memset(&gsc_handle->dst, 0, sizeof(struct gsc_info)); |
| |
| gsc_handle->src.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| gsc_handle->dst.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| |
| gsc_handle->op_mutex = NULL; |
| for (i = 0; i < NUM_OF_GSC_HW; i++) |
| gsc_handle->obj_mutex[i] = NULL; |
| |
| gsc_handle->cur_obj_mutex = NULL; |
| gsc_handle->flag_local_path = false; |
| gsc_handle->flag_exclusive_open = false; |
| |
| srand(time(NULL)); |
| op_id = rand() % 1000000; // just make random id |
| sprintf(mutex_name, "%sOp%d", LOG_TAG, op_id); |
| gsc_handle->op_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_PRIVATE, mutex_name); |
| if (gsc_handle->op_mutex == NULL) { |
| ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name); |
| goto err; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| // check if it is available |
| for (i = 0; i < NUM_OF_GSC_HW; i++) { |
| sprintf(mutex_name, "%sObject%d", LOG_TAG, i); |
| |
| gsc_handle->obj_mutex[i] = exynos_mutex_create(EXYNOS_MUTEX_TYPE_SHARED, mutex_name); |
| if (gsc_handle->obj_mutex[i] == NULL) { |
| ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name); |
| goto err; |
| } |
| } |
| |
| if (m_exynos_gsc_find_and_trylock_and_create(gsc_handle) == false) { |
| ALOGE("%s::m_exynos_gsc_find_and_trylock_and_create() fail", __func__); |
| goto err; |
| } |
| |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| return (void *)gsc_handle; |
| |
| err: |
| if (gsc_handle) { |
| m_exynos_gsc_destroy(gsc_handle); |
| |
| if (gsc_handle->cur_obj_mutex) |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| |
| for (i = 0; i < NUM_OF_GSC_HW; i++) { |
| if ((gsc_handle->obj_mutex[i] != NULL) && |
| (exynos_mutex_get_created_status(gsc_handle->obj_mutex[i]) == true)) { |
| if (exynos_mutex_destroy(gsc_handle->obj_mutex[i]) == false) |
| ALOGE("%s::exynos_mutex_destroy() fail", __func__); |
| } |
| } |
| |
| if (gsc_handle->op_mutex) |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| if (exynos_mutex_destroy(gsc_handle->op_mutex) == false) |
| ALOGE("%s::exynos_mutex_destroy(op_mutex) fail", __func__); |
| |
| free(gsc_handle); |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return NULL; |
| } |
| |
| void *exynos_gsc_reserve(int dev_num) |
| { |
| char mutex_name[32]; |
| unsigned int total_sleep_time = 0; |
| bool gsc_flag = false; |
| |
| if ((dev_num < 0) || (dev_num >= NUM_OF_GSC_HW)) { |
| ALOGE("%s::fail:: dev_num is not valid(%d) ", __func__, dev_num); |
| return NULL; |
| } |
| |
| struct GSC_HANDLE *gsc_handle = (struct GSC_HANDLE *)malloc(sizeof(struct GSC_HANDLE)); |
| if (gsc_handle == NULL) { |
| ALOGE("%s::malloc(struct GSC_HANDLE) fail", __func__); |
| goto err; |
| } |
| |
| gsc_handle->gsc_fd = -1; |
| gsc_handle->op_mutex = NULL; |
| gsc_handle->cur_obj_mutex = NULL; |
| |
| sprintf(mutex_name, "%sObject%d", LOG_TAG, dev_num); |
| gsc_handle->cur_obj_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_SHARED, mutex_name); |
| if (gsc_handle->cur_obj_mutex == NULL) { |
| ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name); |
| goto err; |
| } |
| |
| do { |
| if (exynos_mutex_trylock(gsc_handle->cur_obj_mutex) == true) { |
| gsc_flag = true; |
| break; |
| } |
| usleep(GSC_WAITING_TIME_FOR_TRYLOCK); |
| total_sleep_time += GSC_WAITING_TIME_FOR_TRYLOCK; |
| ALOGV("%s::waiting for another process to release the requested gscaler", __func__); |
| } while(total_sleep_time < MAX_GSC_WAITING_TIME_FOR_TRYLOCK); |
| |
| if (gsc_flag == true) |
| return (void *)gsc_handle; |
| |
| err: |
| if (gsc_handle) { |
| free(gsc_handle); |
| } |
| |
| return NULL; |
| } |
| |
| void exynos_gsc_release(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return; |
| } |
| |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| exynos_mutex_destroy(gsc_handle->cur_obj_mutex); |
| free(gsc_handle); |
| return; |
| } |
| |
| void *exynos_gsc_create_exclusive( |
| int dev_num, |
| int mode, |
| int out_mode, |
| int allow_drm) |
| { |
| int i = 0; |
| int op_id = 0; |
| char mutex_name[32]; |
| unsigned int total_sleep_time = 0; |
| bool gsc_flag = false; |
| int ret = 0; |
| |
| Exynos_gsc_In(); |
| |
| if ((dev_num < 0) || (dev_num >= NUM_OF_GSC_HW)) { |
| ALOGE("%s::fail:: dev_num is not valid(%d) ", __func__, dev_num); |
| return NULL; |
| } |
| |
| if ((mode < 0) || (mode >= NUM_OF_GSC_HW)) { |
| ALOGE("%s::fail:: mode is not valid(%d) ", __func__, mode); |
| return NULL; |
| } |
| |
| /* currently only gscalers 0 and 3 are DRM capable */ |
| if (allow_drm && (dev_num != 0 && dev_num != 3)) { |
| ALOGE("%s::fail:: gscaler %d does not support drm\n", __func__, |
| dev_num); |
| return NULL; |
| } |
| |
| struct GSC_HANDLE *gsc_handle = (struct GSC_HANDLE *)malloc(sizeof(struct GSC_HANDLE)); |
| if (gsc_handle == NULL) { |
| ALOGE("%s::malloc(struct GSC_HANDLE) fail", __func__); |
| goto err; |
| } |
| memset(gsc_handle, 0, sizeof(struct GSC_HANDLE)); |
| gsc_handle->gsc_fd = -1; |
| gsc_handle->gsc_mode = mode; |
| gsc_handle->gsc_id = dev_num; |
| gsc_handle->allow_drm = allow_drm; |
| |
| gsc_handle->src.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| gsc_handle->dst.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| |
| gsc_handle->op_mutex = NULL; |
| for (i = 0; i < NUM_OF_GSC_HW; i++) |
| gsc_handle->obj_mutex[i] = NULL; |
| |
| gsc_handle->cur_obj_mutex = NULL; |
| gsc_handle->flag_local_path = false; |
| gsc_handle->flag_exclusive_open = true; |
| |
| srand(time(NULL)); |
| op_id = rand() % 1000000; // just make random id |
| sprintf(mutex_name, "%sOp%d", LOG_TAG, op_id); |
| gsc_handle->op_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_PRIVATE, mutex_name); |
| if (gsc_handle->op_mutex == NULL) { |
| ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name); |
| goto err; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| sprintf(mutex_name, "%sObject%d", LOG_TAG, dev_num); |
| gsc_handle->cur_obj_mutex = exynos_mutex_create(EXYNOS_MUTEX_TYPE_SHARED, mutex_name); |
| if (gsc_handle->cur_obj_mutex == NULL) { |
| ALOGE("%s::exynos_mutex_create(%s) fail", __func__, mutex_name); |
| goto err; |
| } |
| |
| do { |
| if (exynos_mutex_trylock(gsc_handle->cur_obj_mutex) == true) { |
| if (mode == GSC_M2M_MODE) { |
| gsc_handle->gsc_fd = m_exynos_gsc_m2m_create(dev_num); |
| if (gsc_handle->gsc_fd < 0) { |
| ALOGE("%s::m_exynos_gsc_m2m_create(%i) fail", __func__, dev_num); |
| goto err; |
| } |
| } else if (mode == GSC_OUTPUT_MODE) { |
| ret = m_exynos_gsc_output_create(gsc_handle, dev_num, out_mode); |
| if (ret < 0) { |
| ALOGE("%s::m_exynos_gsc_output_create(%i) fail", __func__, dev_num); |
| goto err; |
| } |
| } |
| /*else |
| gsc_handle->gsc_fd = m_exynos_gsc_capture_create(dev_num);*/ |
| |
| gsc_flag = true; |
| break; |
| } |
| usleep(GSC_WAITING_TIME_FOR_TRYLOCK); |
| total_sleep_time += GSC_WAITING_TIME_FOR_TRYLOCK; |
| ALOGV("%s::waiting for anthere process doens't use gscaler", __func__); |
| } while(total_sleep_time < MAX_GSC_WAITING_TIME_FOR_TRYLOCK); |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| if (gsc_flag == true) { |
| Exynos_gsc_Out(); |
| return (void *)gsc_handle; |
| } |
| |
| err: |
| if (gsc_handle) { |
| m_exynos_gsc_destroy(gsc_handle); |
| |
| if (gsc_handle->cur_obj_mutex) |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| |
| for (i = 0; i < NUM_OF_GSC_HW; i++) { |
| if ((gsc_handle->obj_mutex[i] != NULL) && |
| (exynos_mutex_get_created_status(gsc_handle->obj_mutex[i]) == true)) { |
| if (exynos_mutex_destroy(gsc_handle->obj_mutex[i]) == false) |
| ALOGE("%s::exynos_mutex_destroy() fail", __func__); |
| } |
| } |
| |
| if (gsc_handle->op_mutex) |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| if (exynos_mutex_destroy(gsc_handle->op_mutex) == false) |
| ALOGE("%s::exynos_mutex_destroy(op_mutex) fail", __func__); |
| |
| free(gsc_handle); |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return NULL; |
| } |
| |
| void exynos_gsc_destroy( |
| void *handle) |
| { |
| int i = 0; |
| struct GSC_HANDLE *gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| if (gsc_handle->flag_exclusive_open == false) |
| exynos_mutex_lock(gsc_handle->cur_obj_mutex); |
| |
| if (gsc_handle->gsc_mode == GSC_OUTPUT_MODE) |
| m_exynos_gsc_out_destroy(gsc_handle); |
| else |
| m_exynos_gsc_destroy(gsc_handle); |
| |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| |
| for (i = 0; i < NUM_OF_GSC_HW; i++) { |
| if ((gsc_handle->obj_mutex[i] != NULL) && |
| (exynos_mutex_get_created_status(gsc_handle->obj_mutex[i]) == true)) { |
| if (exynos_mutex_destroy(gsc_handle->obj_mutex[i]) == false) |
| ALOGE("%s::exynos_mutex_destroy(obj_mutex) fail", __func__); |
| } |
| } |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| if (exynos_mutex_destroy(gsc_handle->op_mutex) == false) |
| ALOGE("%s::exynos_mutex_destroy(op_mutex) fail", __func__); |
| |
| if (gsc_handle) |
| free(gsc_handle); |
| |
| Exynos_gsc_Out(); |
| |
| } |
| |
| int exynos_gsc_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) |
| { |
| Exynos_gsc_In(); |
| |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->src.width = width; |
| gsc_handle->src.height = height; |
| gsc_handle->src.crop_left = crop_left; |
| gsc_handle->src.crop_top = crop_top; |
| gsc_handle->src.crop_width = crop_width; |
| gsc_handle->src.crop_height = crop_height; |
| gsc_handle->src.v4l2_colorformat = v4l2_colorformat; |
| gsc_handle->src.cacheable = cacheable; |
| gsc_handle->src.mode_drm = mode_drm; |
| gsc_handle->src.dirty = true; |
| |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| int exynos_gsc_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 narrowRgb) |
| { |
| Exynos_gsc_In(); |
| |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->dst.width = width; |
| gsc_handle->dst.height = height; |
| gsc_handle->dst.crop_left = crop_left; |
| gsc_handle->dst.crop_top = crop_top; |
| gsc_handle->dst.crop_width = crop_width; |
| gsc_handle->dst.crop_height = crop_height; |
| gsc_handle->dst.v4l2_colorformat = v4l2_colorformat; |
| gsc_handle->dst.cacheable = cacheable; |
| gsc_handle->dst.mode_drm = mode_drm; |
| gsc_handle->dst.dirty = true; |
| gsc_handle->dst.csc_range = !narrowRgb; |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| return 0; |
| } |
| |
| int exynos_gsc_set_rotation( |
| void *handle, |
| int rotation, |
| int flip_horizontal, |
| int flip_vertical) |
| { |
| int ret = -1; |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return ret; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| int new_rotation = rotation % 360; |
| |
| if (new_rotation % 90 != 0) { |
| ALOGE("%s::rotation(%d) cannot be acceptable fail", __func__, rotation); |
| goto done; |
| } |
| |
| if(new_rotation < 0) |
| new_rotation = -new_rotation; |
| |
| gsc_handle->dst.rotation = new_rotation; |
| gsc_handle->dst.flip_horizontal = flip_horizontal; |
| gsc_handle->dst.flip_vertical = flip_vertical; |
| |
| ret = 0; |
| done: |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_set_src_addr( |
| void *handle, |
| void *addr[3], |
| int acquireFenceFd) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->src.addr[0] = addr[0]; |
| gsc_handle->src.addr[1] = addr[1]; |
| gsc_handle->src.addr[2] = addr[2]; |
| gsc_handle->src.acquireFenceFd = acquireFenceFd; |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| int exynos_gsc_set_dst_addr( |
| void *handle, |
| void *addr[3], |
| int acquireFenceFd) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| int ret = 0; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->dst.addr[0] = addr[0]; |
| gsc_handle->dst.addr[1] = addr[1]; |
| gsc_handle->dst.addr[2] = addr[2]; |
| gsc_handle->dst.acquireFenceFd = acquireFenceFd; |
| |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| static void rotateValueHAL2GSC(unsigned int transform, |
| unsigned int *rotate, |
| unsigned int *hflip, |
| unsigned int *vflip) |
| { |
| int rotate_flag = transform & 0x7; |
| *rotate = 0; |
| *hflip = 0; |
| *vflip = 0; |
| |
| switch (rotate_flag) { |
| case HAL_TRANSFORM_ROT_90: |
| *rotate = 90; |
| break; |
| case HAL_TRANSFORM_ROT_180: |
| *rotate = 180; |
| break; |
| case HAL_TRANSFORM_ROT_270: |
| *rotate = 270; |
| break; |
| case HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90: |
| *rotate = 90; |
| *vflip = 1; /* set vflip to compensate the rot & flip order. */ |
| break; |
| case HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90: |
| *rotate = 90; |
| *hflip = 1; /* set hflip to compensate the rot & flip order. */ |
| break; |
| case HAL_TRANSFORM_FLIP_H: |
| *hflip = 1; |
| break; |
| case HAL_TRANSFORM_FLIP_V: |
| *vflip = 1; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static bool get_plane_size(int V4L2_PIX, |
| unsigned int * size, |
| unsigned int frame_size, |
| int src_planes) |
| { |
| unsigned int frame_ratio = 1; |
| int src_bpp = get_yuv_bpp(V4L2_PIX); |
| |
| src_planes = (src_planes == -1) ? 1 : src_planes; |
| frame_ratio = 8 * (src_planes -1) / (src_bpp - 8); |
| |
| switch (src_planes) { |
| case 1: |
| switch (V4L2_PIX) { |
| case V4L2_PIX_FMT_BGR32: |
| case V4L2_PIX_FMT_RGB32: |
| size[0] = frame_size << 2; |
| break; |
| case V4L2_PIX_FMT_RGB565X: |
| case V4L2_PIX_FMT_NV16: |
| case V4L2_PIX_FMT_NV61: |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_UYVY: |
| case V4L2_PIX_FMT_VYUY: |
| case V4L2_PIX_FMT_YVYU: |
| size[0] = frame_size << 1; |
| break; |
| case V4L2_PIX_FMT_YUV420: |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV21: |
| case V4L2_PIX_FMT_NV21M: |
| size[0] = (frame_size * 3) >> 1; |
| break; |
| default: |
| ALOGE("%s::invalid color type", __func__); |
| return false; |
| break; |
| } |
| size[1] = 0; |
| size[2] = 0; |
| break; |
| case 2: |
| size[0] = frame_size; |
| size[1] = frame_size / frame_ratio; |
| size[2] = 0; |
| break; |
| case 3: |
| size[0] = frame_size; |
| size[1] = frame_size / frame_ratio; |
| size[2] = frame_size / frame_ratio; |
| break; |
| default: |
| ALOGE("%s::invalid color foarmt", __func__); |
| return false; |
| break; |
| } |
| |
| return true; |
| } |
| |
| int exynos_gsc_m2m_config(void *handle, |
| exynos_gsc_img *src_img, |
| exynos_gsc_img *dst_img) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int32_t src_color_space; |
| int32_t dst_color_space; |
| int ret; |
| unsigned int rotate; |
| unsigned int hflip; |
| unsigned int vflip; |
| |
| Exynos_gsc_In(); |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (gsc_handle == NULL) { |
| ALOGE("%s::gsc_handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| if ((src_img->drmMode && !gsc_handle->allow_drm) || |
| (src_img->drmMode != dst_img->drmMode)) { |
| ALOGE("%s::invalid drm state request for gsc%d (s=%d d=%d)", |
| __func__, gsc_handle->gsc_id, |
| src_img->drmMode, dst_img->drmMode); |
| return -1; |
| } |
| |
| src_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(src_img->format); |
| dst_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(dst_img->format); |
| rotateValueHAL2GSC(dst_img->rot, &rotate, &hflip, &vflip); |
| exynos_gsc_set_rotation(gsc_handle, rotate, hflip, vflip); |
| |
| ret = exynos_gsc_set_src_format(gsc_handle, src_img->fw, src_img->fh, |
| src_img->x, src_img->y, src_img->w, src_img->h, |
| src_color_space, src_img->cacheable, src_img->drmMode); |
| if (ret < 0) { |
| ALOGE("%s: fail: exynos_gsc_set_src_format [fw %d fh %d x %d y %d w %d h %d f %x rot %d]", |
| __func__, src_img->fw, src_img->fh, src_img->x, src_img->y, src_img->w, src_img->h, |
| src_color_space, src_img->rot); |
| return -1; |
| } |
| |
| ret = exynos_gsc_set_dst_format(gsc_handle, dst_img->fw, dst_img->fh, |
| dst_img->x, dst_img->y, dst_img->w, dst_img->h, |
| dst_color_space, dst_img->cacheable, dst_img->drmMode, |
| dst_img->narrowRgb); |
| if (ret < 0) { |
| ALOGE("%s: fail: exynos_gsc_set_dst_format [fw %d fh %d x %d y %d w %d h %d f %x rot %d]", |
| __func__, dst_img->fw, dst_img->fh, dst_img->x, dst_img->y, dst_img->w, dst_img->h, |
| src_color_space, dst_img->rot); |
| return -1; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| int exynos_gsc_out_config(void *handle, |
| exynos_gsc_img *src_img, |
| exynos_gsc_img *dst_img) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| struct v4l2_format fmt; |
| struct v4l2_crop crop; |
| struct v4l2_requestbuffers reqbuf; |
| struct v4l2_subdev_format sd_fmt; |
| struct v4l2_subdev_crop sd_crop; |
| int i; |
| unsigned int rotate; |
| unsigned int hflip; |
| unsigned int vflip; |
| unsigned int plane_size[NUM_OF_GSC_PLANES]; |
| bool rgb; |
| int csc_range = !dst_img->narrowRgb; |
| |
| struct v4l2_rect dst_rect; |
| int32_t src_color_space; |
| int32_t dst_color_space; |
| int32_t src_planes; |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (gsc_handle == NULL) { |
| ALOGE("%s::gsc_handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| Exynos_gsc_In(); |
| |
| if (gsc_handle->src.stream_on != false) { |
| ALOGE("Error: Src is already streamed on !!!!"); |
| return -1; |
| } |
| |
| memcpy(&gsc_handle->src_img, src_img, sizeof(exynos_gsc_img)); |
| memcpy(&gsc_handle->dst_img, dst_img, sizeof(exynos_gsc_img)); |
| src_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(src_img->format); |
| dst_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(dst_img->format); |
| src_planes = get_yuv_planes(src_color_space); |
| src_planes = (src_planes == -1) ? 1 : src_planes; |
| rgb = get_yuv_planes(dst_color_space) == -1; |
| rotateValueHAL2GSC(dst_img->rot, &rotate, &hflip, &vflip); |
| |
| if (m_exynos_gsc_check_src_size(&gsc_handle->src_img.fw, &gsc_handle->src_img.fh, |
| &gsc_handle->src_img.x, &gsc_handle->src_img.y, |
| &gsc_handle->src_img.w, &gsc_handle->src_img.h, |
| src_color_space) == false) { |
| ALOGE("%s::m_exynos_gsc_check_src_size() fail", __func__); |
| return -1; |
| } |
| |
| if (m_exynos_gsc_check_dst_size(&gsc_handle->dst_img.fw, &gsc_handle->dst_img.fh, |
| &gsc_handle->dst_img.x, &gsc_handle->dst_img.y, |
| &gsc_handle->dst_img.w, &gsc_handle->dst_img.h, |
| dst_color_space, |
| rotate) == false) { |
| ALOGE("%s::m_exynos_gsc_check_dst_size() fail", __func__); |
| return -1; |
| } |
| |
| /*set: src v4l2_buffer*/ |
| gsc_handle->src.src_buf_idx = 0; |
| gsc_handle->src.qbuf_cnt = 0; |
| /* set format: src pad of GSC sub-dev*/ |
| sd_fmt.pad = GSCALER_SUBDEV_PAD_SOURCE; |
| sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| if (gsc_handle->out_mode == GSC_OUT_FIMD) { |
| sd_fmt.format.width = gsc_handle->dst_img.fw; |
| sd_fmt.format.height = gsc_handle->dst_img.fh; |
| } else { |
| sd_fmt.format.width = gsc_handle->dst_img.w; |
| sd_fmt.format.height = gsc_handle->dst_img.h; |
| } |
| sd_fmt.format.code = rgb ? V4L2_MBUS_FMT_XRGB8888_4X8_LE : |
| V4L2_MBUS_FMT_YUV8_1X24; |
| if (exynos_subdev_s_fmt(gsc_handle->gsc_sd_entity->fd, &sd_fmt) < 0) { |
| ALOGE("%s::GSC subdev set format failed", __func__); |
| return -1; |
| } |
| |
| /* set crop: src crop of GSC sub-dev*/ |
| sd_crop.pad = GSCALER_SUBDEV_PAD_SOURCE; |
| sd_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| if (gsc_handle->out_mode == GSC_OUT_FIMD) { |
| sd_crop.rect.left = gsc_handle->dst_img.x; |
| sd_crop.rect.top = gsc_handle->dst_img.y; |
| sd_crop.rect.width = gsc_handle->dst_img.w; |
| sd_crop.rect.height = gsc_handle->dst_img.h; |
| } else { |
| sd_crop.rect.left = 0; |
| sd_crop.rect.top = 0; |
| sd_crop.rect.width = gsc_handle->dst_img.w; |
| sd_crop.rect.height = gsc_handle->dst_img.h; |
| } |
| if (exynos_subdev_s_crop(gsc_handle->gsc_sd_entity->fd, &sd_crop) < 0) { |
| ALOGE("%s::GSC subdev set crop failed", __func__); |
| return -1; |
| } |
| |
| /* sink pad is connected to GSC out */ |
| /* set format: sink sub-dev */ |
| if (gsc_handle->out_mode == GSC_OUT_FIMD) { |
| sd_fmt.pad = FIMD_SUBDEV_PAD_SINK; |
| sd_fmt.format.width = gsc_handle->dst_img.w; |
| sd_fmt.format.height = gsc_handle->dst_img.h; |
| } else { |
| sd_fmt.pad = MIXER_V_SUBDEV_PAD_SINK; |
| sd_fmt.format.width = gsc_handle->dst_img.w + gsc_handle->dst_img.x*2; |
| sd_fmt.format.height = gsc_handle->dst_img.h + gsc_handle->dst_img.y*2; |
| } |
| |
| sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| sd_fmt.format.code = rgb ? V4L2_MBUS_FMT_XRGB8888_4X8_LE : |
| V4L2_MBUS_FMT_YUV8_1X24; |
| if (exynos_subdev_s_fmt(gsc_handle->sink_sd_entity->fd, &sd_fmt) < 0) { |
| ALOGE("%s::sink:set format failed (PAD=%d)", __func__, sd_fmt.pad); |
| return -1; |
| } |
| |
| /* set crop: sink sub-dev */ |
| if (gsc_handle->out_mode == GSC_OUT_FIMD) |
| sd_crop.pad = FIMD_SUBDEV_PAD_SINK; |
| else |
| sd_crop.pad = MIXER_V_SUBDEV_PAD_SINK; |
| |
| sd_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| if (gsc_handle->out_mode == GSC_OUT_FIMD) { |
| sd_crop.rect.left = gsc_handle->dst_img.x; |
| sd_crop.rect.top = gsc_handle->dst_img.y; |
| sd_crop.rect.width = gsc_handle->dst_img.w; |
| sd_crop.rect.height = gsc_handle->dst_img.h; |
| } else { |
| sd_crop.rect.left = 0; |
| sd_crop.rect.top = 0; |
| sd_crop.rect.width = gsc_handle->dst_img.w; |
| sd_crop.rect.height = gsc_handle->dst_img.h; |
| } |
| if (exynos_subdev_s_crop(gsc_handle->sink_sd_entity->fd, &sd_crop) < 0) { |
| ALOGE("%s::sink: subdev set crop failed(PAD=%d)", __func__, sd_crop.pad); |
| return -1; |
| } |
| |
| if (gsc_handle->out_mode != GSC_OUT_FIMD) { |
| sd_fmt.pad = MIXER_V_SUBDEV_PAD_SOURCE; |
| sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| sd_fmt.format.width = gsc_handle->dst_img.w + gsc_handle->dst_img.x*2; |
| sd_fmt.format.height = gsc_handle->dst_img.h + gsc_handle->dst_img.y*2; |
| sd_fmt.format.code = V4L2_MBUS_FMT_XRGB8888_4X8_LE; |
| if (exynos_subdev_s_fmt(gsc_handle->sink_sd_entity->fd, &sd_fmt) < 0) { |
| ALOGE("%s::sink:set format failed (PAD=%d)", __func__, sd_fmt.pad); |
| return -1; |
| } |
| |
| sd_fmt.pad = MIXER_V_SUBDEV_PAD_SOURCE; |
| sd_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| sd_crop.rect.left = gsc_handle->dst_img.x; |
| sd_crop.rect.top = gsc_handle->dst_img.y; |
| sd_crop.rect.width = gsc_handle->dst_img.w; |
| sd_crop.rect.height = gsc_handle->dst_img.h; |
| if (exynos_subdev_s_crop(gsc_handle->sink_sd_entity->fd, &sd_crop) < 0) { |
| ALOGE("%s::sink: subdev set crop failed(PAD=%d)", __func__, sd_crop.pad); |
| return -1; |
| } |
| } |
| |
| /*set GSC ctrls */ |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, V4L2_CID_ROTATE, rotate) < 0) { |
| ALOGE("%s:: exynos_v4l2_s_ctrl (V4L2_CID_ROTATE: %d) failed", __func__, rotate); |
| return -1; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, V4L2_CID_HFLIP, hflip) < 0) { |
| ALOGE("%s:: exynos_v4l2_s_ctrl (V4L2_CID_HFLIP: %d) failed", __func__, hflip); |
| return -1; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, V4L2_CID_VFLIP, vflip) < 0) { |
| ALOGE("%s:: exynos_v4l2_s_ctrl (V4L2_CID_VFLIP: %d) failed", __func__, vflip); |
| return -1; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, V4L2_CID_CACHEABLE, 1) < 0) { |
| ALOGE("%s:: exynos_v4l2_s_ctrl (V4L2_CID_CACHEABLE: 1) failed", __func__); |
| return -1; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, |
| V4L2_CID_CONTENT_PROTECTION, gsc_handle->src_img.drmMode) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_CONTENT_PROTECTION) fail", __func__); |
| return -1; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_vd_entity->fd, V4L2_CID_CSC_RANGE, |
| csc_range)) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_CSC_RANGE: %d) fail", __func__, |
| csc_range); |
| return -1; |
| } |
| |
| /* set src format :GSC video dev*/ |
| fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| fmt.fmt.pix_mp.width = gsc_handle->src_img.fw; |
| fmt.fmt.pix_mp.height = gsc_handle->src_img.fh; |
| fmt.fmt.pix_mp.pixelformat = src_color_space; |
| fmt.fmt.pix_mp.field = V4L2_FIELD_NONE; |
| fmt.fmt.pix_mp.num_planes = src_planes; |
| |
| if (exynos_v4l2_s_fmt(gsc_handle->gsc_vd_entity->fd, &fmt) < 0) { |
| ALOGE("%s::videodev set format failed", __func__); |
| return -1; |
| } |
| |
| /* set src crop info :GSC video dev*/ |
| crop.type = fmt.type; |
| crop.c.left = gsc_handle->src_img.x; |
| crop.c.top = gsc_handle->src_img.y; |
| crop.c.width = gsc_handle->src_img.w; |
| crop.c.height = gsc_handle->src_img.h; |
| |
| if (exynos_v4l2_s_crop(gsc_handle->gsc_vd_entity->fd, &crop) < 0) { |
| ALOGE("%s::videodev set crop failed", __func__); |
| return -1; |
| } |
| |
| reqbuf.type = fmt.type; |
| reqbuf.memory = V4L2_MEMORY_DMABUF; |
| reqbuf.count = MAX_BUFFERS_GSCALER_OUT; |
| |
| if (exynos_v4l2_reqbufs(gsc_handle->gsc_vd_entity->fd, &reqbuf) < 0) { |
| ALOGE("%s::request buffers failed", __func__); |
| return -1; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| static int exynos_gsc_out_run(void *handle, |
| exynos_gsc_img *src_img) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| struct v4l2_plane planes[NUM_OF_GSC_PLANES]; |
| struct v4l2_buffer buf; |
| int32_t src_color_space; |
| int32_t src_planes; |
| int i; |
| unsigned int plane_size[NUM_OF_GSC_PLANES]; |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| /* All buffers have been queued, dequeue one */ |
| if (gsc_handle->src.qbuf_cnt == MAX_BUFFERS_GSCALER_OUT) { |
| memset(&buf, 0, sizeof(struct v4l2_buffer)); |
| for (i = 0; i < MAX_BUFFERS_GSCALER_OUT; i++) |
| memset(&planes[i], 0, sizeof(struct v4l2_plane)); |
| |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| buf.memory = V4L2_MEMORY_DMABUF; |
| buf.length = src_planes; |
| buf.m.planes = planes; |
| |
| if (exynos_v4l2_dqbuf(gsc_handle->gsc_vd_entity->fd, &buf) < 0) { |
| ALOGE("%s::dequeue buffer failed (index=%d)(mSrcBufNum=%d)", __func__, |
| gsc_handle->src.src_buf_idx, MAX_BUFFERS_GSCALER_OUT); |
| return -1; |
| } |
| gsc_handle->src.qbuf_cnt--; |
| } |
| |
| memset(&buf, 0, sizeof(struct v4l2_buffer)); |
| for (i = 0; i < NUM_OF_GSC_PLANES; i++) |
| memset(&planes[i], 0, sizeof(struct v4l2_plane)); |
| |
| src_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(gsc_handle->src_img.format); |
| src_planes = get_yuv_planes(src_color_space); |
| src_planes = (src_planes == -1) ? 1 : src_planes; |
| |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| buf.memory = V4L2_MEMORY_DMABUF; |
| buf.flags = V4L2_BUF_FLAG_USE_SYNC; |
| buf.length = src_planes; |
| buf.index = gsc_handle->src.src_buf_idx; |
| buf.m.planes = planes; |
| buf.reserved = src_img->acquireFenceFd; |
| |
| gsc_handle->src.addr[0] = src_img->yaddr; |
| gsc_handle->src.addr[1] = src_img->uaddr; |
| gsc_handle->src.addr[2] = src_img->vaddr; |
| |
| if (get_plane_size(src_color_space, plane_size, |
| gsc_handle->src_img.fw * gsc_handle->src_img.fh, src_planes) != true) { |
| ALOGE("%s:get_plane_size:fail", __func__); |
| return -1; |
| } |
| |
| for (i = 0; i < buf.length; i++) { |
| buf.m.planes[i].m.fd = (int)gsc_handle->src.addr[i]; |
| buf.m.planes[i].length = plane_size[i]; |
| buf.m.planes[i].bytesused = plane_size[i]; |
| } |
| |
| /* Queue the buf */ |
| if (exynos_v4l2_qbuf(gsc_handle->gsc_vd_entity->fd, &buf) < 0) { |
| ALOGE("%s::queue buffer failed (index=%d)(mSrcBufNum=%d)", __func__, |
| gsc_handle->src.src_buf_idx, MAX_BUFFERS_GSCALER_OUT); |
| return -1; |
| } |
| gsc_handle->src.src_buf_idx++; |
| gsc_handle->src.src_buf_idx = gsc_handle->src.src_buf_idx % MAX_BUFFERS_GSCALER_OUT; |
| gsc_handle->src.qbuf_cnt++; |
| |
| if (gsc_handle->src.stream_on == false) { |
| if (exynos_v4l2_streamon(gsc_handle->gsc_vd_entity->fd, buf.type) < 0) { |
| ALOGE("%s::stream on failed", __func__); |
| return -1; |
| } |
| gsc_handle->src.stream_on = true; |
| } |
| |
| src_img->releaseFenceFd = buf.reserved; |
| return 0; |
| } |
| |
| int exynos_gsc_out_stop(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| struct v4l2_requestbuffers reqbuf; |
| struct v4l2_buffer buf; |
| struct v4l2_plane planes[NUM_OF_GSC_PLANES]; |
| int i; |
| |
| Exynos_gsc_In(); |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| if (gsc_handle->src.stream_on == true) { |
| if (exynos_v4l2_streamoff(gsc_handle->gsc_vd_entity->fd, |
| V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) < 0) { |
| ALOGE("%s::stream off failed", __func__); |
| return -1; |
| } |
| gsc_handle->src.stream_on = false; |
| } |
| |
| gsc_handle->src.src_buf_idx = 0; |
| gsc_handle->src.qbuf_cnt = 0; |
| |
| reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| reqbuf.memory = V4L2_MEMORY_DMABUF; |
| reqbuf.count = 0; |
| |
| if (exynos_v4l2_reqbufs(gsc_handle->gsc_vd_entity->fd, &reqbuf) < 0) { |
| ALOGE("%s::request buffers failed", __func__); |
| return -1; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| static int exynos_gsc_m2m_run_core(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| bool is_dirty; |
| bool is_drm; |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| is_dirty = gsc_handle->src.dirty || gsc_handle->dst.dirty; |
| is_drm = gsc_handle->src.mode_drm; |
| |
| if (is_dirty && (gsc_handle->src.mode_drm != gsc_handle->dst.mode_drm)) { |
| ALOGE("%s: drm mode mismatch between src and dst, gsc%d (s=%d d=%d)", |
| __func__, gsc_handle->gsc_id, gsc_handle->src.mode_drm, |
| gsc_handle->dst.mode_drm); |
| return -1; |
| } else if (is_drm && !gsc_handle->allow_drm) { |
| ALOGE("%s: drm mode is not supported on gsc%d", __func__, |
| gsc_handle->gsc_id); |
| return -1; |
| } |
| |
| if (m_exynos_gsc_check_src_size(&gsc_handle->src.width, &gsc_handle->src.height, |
| &gsc_handle->src.crop_left, &gsc_handle->src.crop_top, |
| &gsc_handle->src.crop_width, &gsc_handle->src.crop_height, |
| gsc_handle->src.v4l2_colorformat) == false) { |
| ALOGE("%s::m_exynos_gsc_check_src_size() fail", __func__); |
| return -1; |
| } |
| |
| if (m_exynos_gsc_check_dst_size(&gsc_handle->dst.width, &gsc_handle->dst.height, |
| &gsc_handle->dst.crop_left, &gsc_handle->dst.crop_top, |
| &gsc_handle->dst.crop_width, &gsc_handle->dst.crop_height, |
| gsc_handle->dst.v4l2_colorformat, |
| gsc_handle->dst.rotation) == false) { |
| ALOGE("%s::m_exynos_gsc_check_dst_size() fail", __func__); |
| return -1; |
| } |
| |
| /* dequeue buffers from previous work if necessary */ |
| if (gsc_handle->src.stream_on == true) { |
| if (exynos_gsc_m2m_wait_frame_done(handle) < 0) { |
| ALOGE("%s::exynos_gsc_m2m_wait_frame_done fail", __func__); |
| return -1; |
| } |
| } |
| |
| /* |
| * need to set the content protection flag before doing reqbufs |
| * in set_format |
| */ |
| if (is_dirty && gsc_handle->allow_drm && is_drm) { |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_fd, |
| V4L2_CID_CONTENT_PROTECTION, is_drm) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl() fail", __func__); |
| return -1; |
| } |
| } |
| |
| /* |
| * from this point on, we have to ensure to call stop to clean up whatever |
| * state we have set. |
| */ |
| |
| if (gsc_handle->src.dirty) { |
| if (m_exynos_gsc_set_format(gsc_handle->gsc_fd, &gsc_handle->src) == false) { |
| ALOGE("%s::m_exynos_gsc_set_format(src) fail", __func__); |
| goto done; |
| } |
| gsc_handle->src.dirty = false; |
| } |
| |
| if (gsc_handle->dst.dirty) { |
| if (m_exynos_gsc_set_format(gsc_handle->gsc_fd, &gsc_handle->dst) == false) { |
| ALOGE("%s::m_exynos_gsc_set_format(dst) fail", __func__); |
| goto done; |
| } |
| gsc_handle->dst.dirty = false; |
| } |
| |
| /* if we are enabling drm, make sure to enable hw protection. |
| * Need to do this before queuing buffers so that the mmu is reserved |
| * and power domain is kept on. |
| */ |
| if (is_dirty && gsc_handle->allow_drm && is_drm) { |
| unsigned int protect_id = 0; |
| |
| if (gsc_handle->gsc_id == 0) { |
| protect_id = CP_PROTECT_GSC0; |
| } else if (gsc_handle->gsc_id == 3) { |
| protect_id = CP_PROTECT_GSC3; |
| } else { |
| ALOGE("%s::invalid gscaler id %d for content protection", __func__, |
| gsc_handle->gsc_id); |
| goto done; |
| } |
| |
| if (CP_Enable_Path_Protection(protect_id) != 0) { |
| ALOGE("%s::CP_Enable_Path_Protection failed", __func__); |
| goto done; |
| } |
| gsc_handle->protection_enabled = true; |
| } |
| |
| if (m_exynos_gsc_set_addr(gsc_handle->gsc_fd, &gsc_handle->src) == false) { |
| ALOGE("%s::m_exynos_gsc_set_addr(src) fail", __func__); |
| goto done; |
| } |
| |
| if (m_exynos_gsc_set_addr(gsc_handle->gsc_fd, &gsc_handle->dst) == false) { |
| ALOGE("%s::m_exynos_gsc_set_addr(dst) fail", __func__); |
| goto done; |
| } |
| |
| if (gsc_handle->src.stream_on == false) { |
| if (exynos_v4l2_streamon(gsc_handle->gsc_fd, gsc_handle->src.buf_type) < 0) { |
| ALOGE("%s::exynos_v4l2_streamon(src) fail", __func__); |
| goto done; |
| } |
| gsc_handle->src.stream_on = true; |
| } |
| |
| if (gsc_handle->dst.stream_on == false) { |
| if (exynos_v4l2_streamon(gsc_handle->gsc_fd, gsc_handle->dst.buf_type) < 0) { |
| ALOGE("%s::exynos_v4l2_streamon(dst) fail", __func__); |
| goto done; |
| } |
| gsc_handle->dst.stream_on = true; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| |
| done: |
| exynos_gsc_m2m_stop(handle); |
| return -1; |
| } |
| |
| static int exynos_gsc_m2m_wait_frame_done(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| if ((gsc_handle->src.stream_on == false) || (gsc_handle->dst.stream_on == false)) { |
| ALOGE("%s:: src_strean_on or dst_stream_on are false", __func__); |
| return -1; |
| } |
| |
| if (gsc_handle->src.buffer_queued) { |
| if (exynos_v4l2_dqbuf(gsc_handle->gsc_fd, &gsc_handle->src.buffer) < 0) { |
| ALOGE("%s::exynos_v4l2_dqbuf(src) fail", __func__); |
| return -1; |
| } |
| gsc_handle->src.buffer_queued = false; |
| } |
| |
| if (gsc_handle->dst.buffer_queued) { |
| if (exynos_v4l2_dqbuf(gsc_handle->gsc_fd, &gsc_handle->dst.buffer) < 0) { |
| ALOGE("%s::exynos_v4l2_dqbuf(dst) fail", __func__); |
| return -1; |
| } |
| gsc_handle->dst.buffer_queued = false; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| static int exynos_gsc_m2m_stop(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| struct v4l2_requestbuffers req_buf; |
| int ret = 0; |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (!gsc_handle->src.stream_on && !gsc_handle->dst.stream_on) { |
| /* wasn't streaming, return success */ |
| return 0; |
| } else if (gsc_handle->src.stream_on != gsc_handle->dst.stream_on) { |
| ALOGE("%s: invalid state, queue stream state doesn't match (%d != %d)", |
| __func__, gsc_handle->src.stream_on, gsc_handle->dst.stream_on); |
| ret = -1; |
| } |
| |
| /* |
| * we need to plow forward on errors below to make sure that if we had |
| * turned on content protection on secure side, we turn it off. |
| * |
| * also, if we only failed to turn on one of the streams, we'll turn |
| * the other one off correctly. |
| */ |
| if (gsc_handle->src.stream_on == true) { |
| if (exynos_v4l2_streamoff(gsc_handle->gsc_fd, gsc_handle->src.buf_type) < 0) { |
| ALOGE("%s::exynos_v4l2_streamoff(src) fail", __func__); |
| ret = -1; |
| } |
| gsc_handle->src.stream_on = false; |
| } |
| |
| |
| if (gsc_handle->dst.stream_on == true) { |
| if (exynos_v4l2_streamoff(gsc_handle->gsc_fd, gsc_handle->dst.buf_type) < 0) { |
| ALOGE("%s::exynos_v4l2_streamoff(dst) fail", __func__); |
| ret = -1; |
| } |
| gsc_handle->dst.stream_on = false; |
| } |
| |
| /* if drm is enabled */ |
| if (gsc_handle->allow_drm && gsc_handle->protection_enabled) { |
| unsigned int protect_id = 0; |
| |
| if (gsc_handle->gsc_id == 0) |
| protect_id = CP_PROTECT_GSC0; |
| else if (gsc_handle->gsc_id == 3) |
| protect_id = CP_PROTECT_GSC3; |
| |
| CP_Disable_Path_Protection(protect_id); |
| gsc_handle->protection_enabled = false; |
| } |
| |
| if (exynos_v4l2_s_ctrl(gsc_handle->gsc_fd, |
| V4L2_CID_CONTENT_PROTECTION, 0) < 0) { |
| ALOGE("%s::exynos_v4l2_s_ctrl(V4L2_CID_CONTENT_PROTECTION) fail", |
| __func__); |
| ret = -1; |
| } |
| |
| /* src: clear_buf */ |
| req_buf.count = 0; |
| req_buf.type = gsc_handle->src.buf_type; |
| req_buf.memory = V4L2_MEMORY_DMABUF; |
| if (exynos_v4l2_reqbufs(gsc_handle->gsc_fd, &req_buf) < 0) { |
| ALOGE("%s::exynos_v4l2_reqbufs():src: fail", __func__); |
| ret = -1; |
| } |
| |
| /* dst: clear_buf */ |
| req_buf.count = 0; |
| req_buf.type = gsc_handle->dst.buf_type; |
| req_buf.memory = V4L2_MEMORY_DMABUF; |
| if (exynos_v4l2_reqbufs(gsc_handle->gsc_fd, &req_buf) < 0) { |
| ALOGE("%s::exynos_v4l2_reqbufs():dst: fail", __func__); |
| ret = -1; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_convert( |
| void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int ret = -1; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| if (gsc_handle->flag_local_path == true) { |
| ALOGE("%s::this exynos_gsc is connected by another hw internaly. So, don't call exynos_gsc_convert()", __func__); |
| goto done; |
| } |
| |
| if (exynos_gsc_m2m_run_core(handle) < 0) { |
| ALOGE("%s::exynos_gsc_run_core fail", __func__); |
| goto done; |
| } |
| |
| if (exynos_gsc_m2m_wait_frame_done(handle) < 0) { |
| ALOGE("%s::exynos_gsc_m2m_wait_frame_done", __func__); |
| goto done; |
| } |
| |
| if (gsc_handle->src.releaseFenceFd >= 0) { |
| close(gsc_handle->src.releaseFenceFd); |
| gsc_handle->src.releaseFenceFd = -1; |
| } |
| |
| if (gsc_handle->dst.releaseFenceFd >= 0) { |
| close(gsc_handle->dst.releaseFenceFd); |
| gsc_handle->dst.releaseFenceFd = -1; |
| } |
| |
| if (exynos_gsc_m2m_stop(handle) < 0) { |
| ALOGE("%s::exynos_gsc_m2m_stop", __func__); |
| goto done; |
| } |
| |
| ret = 0; |
| |
| done: |
| if (gsc_handle->flag_exclusive_open == false) { |
| if (gsc_handle->flag_local_path == false) |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| } |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_m2m_run(void *handle, |
| exynos_gsc_img *src_img, |
| exynos_gsc_img *dst_img) |
| { |
| struct GSC_HANDLE *gsc_handle = handle; |
| void *addr[3] = {NULL, NULL, NULL}; |
| int ret = 0; |
| |
| Exynos_gsc_In(); |
| |
| addr[0] = (void *)src_img->yaddr; |
| addr[1] = (void *)src_img->uaddr; |
| addr[2] = (void *)src_img->vaddr; |
| ret = exynos_gsc_set_src_addr(handle, addr, src_img->acquireFenceFd); |
| if (ret < 0) { |
| ALOGE("%s::fail: exynos_gsc_set_src_addr[%x %x %x]", __func__, |
| (unsigned int)addr[0], (unsigned int)addr[1], (unsigned int)addr[2]); |
| return -1; |
| } |
| |
| addr[0] = (void *)dst_img->yaddr; |
| addr[1] = (void *)dst_img->uaddr; |
| addr[2] = (void *)dst_img->vaddr; |
| ret = exynos_gsc_set_dst_addr(handle, addr, dst_img->acquireFenceFd); |
| if (ret < 0) { |
| ALOGE("%s::fail: exynos_gsc_set_dst_addr[%x %x %x]", __func__, |
| (unsigned int)addr[0], (unsigned int)addr[1], (unsigned int)addr[2]); |
| return -1; |
| } |
| |
| ret = exynos_gsc_m2m_run_core(handle); |
| if (ret < 0) { |
| ALOGE("%s::fail: exynos_gsc_m2m_run_core", __func__); |
| return -1; |
| } |
| |
| if (src_img->acquireFenceFd >= 0) { |
| close(src_img->acquireFenceFd); |
| src_img->acquireFenceFd = -1; |
| } |
| |
| if (dst_img->acquireFenceFd >= 0) { |
| close(dst_img->acquireFenceFd); |
| dst_img->acquireFenceFd = -1; |
| } |
| |
| src_img->releaseFenceFd = gsc_handle->src.releaseFenceFd; |
| dst_img->releaseFenceFd = gsc_handle->dst.releaseFenceFd; |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |
| |
| int exynos_gsc_config_exclusive(void *handle, |
| exynos_gsc_img *src_img, |
| exynos_gsc_img *dst_img) |
| { |
| |
| Exynos_gsc_In(); |
| |
| struct GSC_HANDLE *gsc_handle; |
| int ret = 0; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| switch (gsc_handle->gsc_mode) { |
| case GSC_M2M_MODE: |
| ret = exynos_gsc_m2m_config(handle, src_img, dst_img); |
| break; |
| case GSC_OUTPUT_MODE: |
| ret = exynos_gsc_out_config(handle, src_img, dst_img); |
| break; |
| case GSC_CAPTURE_MODE: |
| //to do |
| break; |
| default: |
| break; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| |
| } |
| |
| int exynos_gsc_run_exclusive(void *handle, |
| exynos_gsc_img *src_img, |
| exynos_gsc_img *dst_img) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int ret = 0; |
| |
| Exynos_gsc_In(); |
| |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| switch (gsc_handle->gsc_mode) { |
| case GSC_M2M_MODE: |
| ret = exynos_gsc_m2m_run(handle, src_img, dst_img); |
| break; |
| case GSC_OUTPUT_MODE: |
| ret = exynos_gsc_out_run(handle, src_img); |
| break; |
| case GSC_CAPTURE_MODE: |
| //to do |
| break; |
| default: |
| break; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_wait_frame_done_exclusive(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int ret = 0; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| if (gsc_handle->gsc_mode == GSC_M2M_MODE) |
| ret = exynos_gsc_m2m_wait_frame_done(handle); |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_stop_exclusive(void *handle) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int ret = 0; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| switch (gsc_handle->gsc_mode) { |
| case GSC_M2M_MODE: |
| ret = exynos_gsc_m2m_stop(handle); |
| break; |
| case GSC_OUTPUT_MODE: |
| ret = exynos_gsc_out_stop(handle); |
| break; |
| case GSC_CAPTURE_MODE: |
| //to do |
| break; |
| default: |
| break; |
| } |
| |
| Exynos_gsc_Out(); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_connect( |
| void *handle, |
| void *hw) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| int ret = -1; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->flag_local_path = true; |
| |
| if (exynos_mutex_trylock(gsc_handle->cur_obj_mutex) == false) { |
| if (m_exynos_gsc_find_and_trylock_and_create(gsc_handle) == false) { |
| ALOGE("%s::m_exynos_gsc_find_and_trylock_and_create() fail", __func__); |
| goto done; |
| } |
| } |
| |
| ret = 0; |
| |
| Exynos_gsc_Out(); |
| |
| done: |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| return ret; |
| } |
| |
| int exynos_gsc_disconnect( |
| void *handle, |
| void *hw) |
| { |
| struct GSC_HANDLE *gsc_handle; |
| gsc_handle = (struct GSC_HANDLE *)handle; |
| |
| Exynos_gsc_In(); |
| |
| if (handle == NULL) { |
| ALOGE("%s::handle == NULL() fail", __func__); |
| return -1; |
| } |
| |
| exynos_mutex_lock(gsc_handle->op_mutex); |
| |
| gsc_handle->flag_local_path = false; |
| |
| exynos_mutex_unlock(gsc_handle->cur_obj_mutex); |
| |
| exynos_mutex_unlock(gsc_handle->op_mutex); |
| |
| Exynos_gsc_Out(); |
| |
| return 0; |
| } |