From 62f02ba4f4b7b561aa15408ebd9951600bdd71aa Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sun, 20 May 2012 12:00:36 +0200 Subject: exynos: reorganized and updated from insignal Changes needed on exynos4210 devices: libcsc -> libseccscapi libswconverter -> remove TARGET_HAL_PATH := hardware/samsung/exynos4/hal TARGET_OMX_PATH := hardware/samsung/exynos/multimedia/openmax $(call inherit-product, hardware/samsung/exynos4210.mk) Change-Id: Ic59ef95b85ef37b3f38fb36cf6a364a5414685ee --- exynos5/hal/libhdmi/Android.mk | 17 + exynos5/hal/libhdmi/SecHdmi/Android.mk | 76 + exynos5/hal/libhdmi/SecHdmi/SecGscaler.cpp | 1524 +++++++++++++++ exynos5/hal/libhdmi/SecHdmi/SecGscaler.h | 195 ++ exynos5/hal/libhdmi/SecHdmi/SecHdmi.cpp | 1985 ++++++++++++++++++++ exynos5/hal/libhdmi/SecHdmi/SecHdmiCommon.h | 118 ++ exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.cpp | 1350 +++++++++++++ exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.h | 86 + exynos5/hal/libhdmi/SecHdmi/fimd_api.c | 229 +++ exynos5/hal/libhdmi/SecHdmi/fimd_api.h | 51 + exynos5/hal/libhdmi/libhdmiservice/Android.mk | 95 + exynos5/hal/libhdmi/libhdmiservice/Barrier.h | 55 + exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.cpp | 111 ++ exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.h | 74 + .../hal/libhdmi/libhdmiservice/MessageQueue.cpp | 190 ++ exynos5/hal/libhdmi/libhdmiservice/MessageQueue.h | 118 ++ .../hal/libhdmi/libhdmiservice/SecHdmiClient.cpp | 139 ++ exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.h | 84 + .../hal/libhdmi/libhdmiservice/SecTVOutService.cpp | 377 ++++ .../hal/libhdmi/libhdmiservice/SecTVOutService.h | 175 ++ exynos5/hal/libhdmi/libsForhdmi/Android.mk | 17 + exynos5/hal/libhdmi/libsForhdmi/libcec/Android.mk | 33 + exynos5/hal/libhdmi/libsForhdmi/libcec/cec.h | 11 + exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.c | 386 ++++ exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.h | 209 +++ exynos5/hal/libhdmi/libsForhdmi/libddc/Android.mk | 49 + exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.c | 289 +++ exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.h | 35 + exynos5/hal/libhdmi/libsForhdmi/libedid/Android.mk | 34 + exynos5/hal/libhdmi/libsForhdmi/libedid/edid.h | 181 ++ exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.c | 1265 +++++++++++++ exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.h | 42 + 32 files changed, 9600 insertions(+) create mode 100644 exynos5/hal/libhdmi/Android.mk create mode 100644 exynos5/hal/libhdmi/SecHdmi/Android.mk create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecGscaler.cpp create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecGscaler.h create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecHdmi.cpp create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecHdmiCommon.h create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.cpp create mode 100644 exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.h create mode 100644 exynos5/hal/libhdmi/SecHdmi/fimd_api.c create mode 100644 exynos5/hal/libhdmi/SecHdmi/fimd_api.h create mode 100644 exynos5/hal/libhdmi/libhdmiservice/Android.mk create mode 100644 exynos5/hal/libhdmi/libhdmiservice/Barrier.h create mode 100644 exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.cpp create mode 100644 exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.h create mode 100644 exynos5/hal/libhdmi/libhdmiservice/MessageQueue.cpp create mode 100644 exynos5/hal/libhdmi/libhdmiservice/MessageQueue.h create mode 100644 exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.cpp create mode 100644 exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.h create mode 100644 exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.cpp create mode 100644 exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.h create mode 100644 exynos5/hal/libhdmi/libsForhdmi/Android.mk create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libcec/Android.mk create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libcec/cec.h create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.c create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.h create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libddc/Android.mk create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.c create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.h create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libedid/Android.mk create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libedid/edid.h create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.c create mode 100644 exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.h (limited to 'exynos5/hal/libhdmi') diff --git a/exynos5/hal/libhdmi/Android.mk b/exynos5/hal/libhdmi/Android.mk new file mode 100644 index 0000000..9acbc52 --- /dev/null +++ b/exynos5/hal/libhdmi/Android.mk @@ -0,0 +1,17 @@ +# 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. + +ifeq ($(filter-out exynos5,$(TARGET_BOARD_PLATFORM)),) +include $(all-subdir-makefiles) +endif diff --git a/exynos5/hal/libhdmi/SecHdmi/Android.mk b/exynos5/hal/libhdmi/SecHdmi/Android.mk new file mode 100644 index 0000000..61bd1a9 --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/Android.mk @@ -0,0 +1,76 @@ +# 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. + +ifeq ($(BOARD_USES_HDMI),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := eng + +LOCAL_PRELINK_MODULE := false +#LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := libutils liblog libedid libcec + +LOCAL_SRC_FILES := \ + SecHdmiV4L2Utils.cpp \ + SecHdmi.cpp \ + SecGscaler.cpp \ + fimd_api.c + +LOCAL_C_INCLUDES += $(LOCAL_PATH) +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../include + +ifeq ($(TARGET_SOC),exynos5250) + LOCAL_CFLAGS += -DSAMSUNG_EXYNOS5250 +endif + +LOCAL_CFLAGS += \ + -DSCREEN_WIDTH=$(SCREEN_WIDTH) \ + -DSCREEN_HEIGHT=$(SCREEN_HEIGHT) \ + -DDEFAULT_FB_NUM=$(DEFAULT_FB_NUM) + +LOCAL_SHARED_LIBRARIES += libion + +ifeq ($(BOARD_USES_HDMI_SUBTITLES),true) + LOCAL_CFLAGS += -DBOARD_USES_HDMI_SUBTITLES +endif + +ifeq ($(BOARD_USES_HDMI_FIMGAPI),true) + LOCAL_CFLAGS += -DBOARD_USES_HDMI_FIMGAPI + LOCAL_C_INCLUDES += device/samsung/$(TARGET_BOARD_PLATFORM)/libfimg4x + LOCAL_C_INCLUDES += external/skia/include/core + LOCAL_SHARED_LIBRARIES += libfimg +endif + +ifeq ($(BOARD_HDMI_STD), STD_NTSC_M) + LOCAL_CFLAGS += -DSTD_NTSC_M +endif + +ifeq ($(BOARD_HDMI_STD),STD_480P) + LOCAL_CFLAGS += -DSTD_480P +endif + +ifeq ($(BOARD_HDMI_STD),STD_720P) + LOCAL_CFLAGS += -DSTD_720P +endif + +ifeq ($(BOARD_HDMI_STD),STD_1080P) + LOCAL_CFLAGS += -DSTD_1080P +endif + +LOCAL_MODULE := libhdmi +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/exynos5/hal/libhdmi/SecHdmi/SecGscaler.cpp b/exynos5/hal/libhdmi/SecHdmi/SecGscaler.cpp new file mode 100644 index 0000000..0a84d11 --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecGscaler.cpp @@ -0,0 +1,1524 @@ +/* + * 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. + */ + +//#define DEBUG_LIB_FIMC +#define LOG_TAG "libgscaler" +//#define USE_GSC_USERPTR +#include +#include "../libhdmi/SecHdmi/SecGscaler.h" + +#ifdef USE_GSC_USERPTR +#define V4L2_MEMORY_TYPE V4L2_MEMORY_USERPTR +#else +#define V4L2_MEMORY_TYPE V4L2_MEMORY_MMAP +#endif + +struct yuv_fmt_list yuv_list[] = { + { "V4L2_PIX_FMT_NV12", "YUV420/2P/LSB_CBCR", V4L2_PIX_FMT_NV12, 12, 1 }, + { "V4L2_PIX_FMT_NV12M", "YUV420/2P/LSB_CBCR", V4L2_PIX_FMT_NV12M, 12, 2 }, + { "V4L2_PIX_FMT_NV12MT", "YUV420/2P/LSB_CBCR", V4L2_PIX_FMT_NV12MT, 12, 2 }, + { "V4L2_PIX_FMT_NV21", "YUV420/2P/LSB_CRCB", V4L2_PIX_FMT_NV21, 12, 1 }, + { "V4L2_PIX_FMT_NV21X", "YUV420/2P/MSB_CBCR", V4L2_PIX_FMT_NV21X, 12, 2 }, + { "V4L2_PIX_FMT_NV12X", "YUV420/2P/MSB_CRCB", V4L2_PIX_FMT_NV12X, 12, 2 }, + { "V4L2_PIX_FMT_YUV420", "YUV420/3P", V4L2_PIX_FMT_YUV420, 12, 3 }, + { "V4L2_PIX_FMT_YUV420M", "YUV420/3P", V4L2_PIX_FMT_YUV420M, 12, 3 }, + { "V4L2_PIX_FMT_YUYV", "YUV422/1P/YCBYCR", V4L2_PIX_FMT_YUYV, 16, 1 }, + { "V4L2_PIX_FMT_YVYU", "YUV422/1P/YCRYCB", V4L2_PIX_FMT_YVYU, 16, 1 }, + { "V4L2_PIX_FMT_UYVY", "YUV422/1P/CBYCRY", V4L2_PIX_FMT_UYVY, 16, 1 }, + { "V4L2_PIX_FMT_VYUY", "YUV422/1P/CRYCBY", V4L2_PIX_FMT_VYUY, 16, 1 }, + { "V4L2_PIX_FMT_UV12", "YUV422/2P/LSB_CBCR", V4L2_PIX_FMT_NV16, 16, 2 }, + { "V4L2_PIX_FMT_UV21", "YUV422/2P/LSB_CRCB", V4L2_PIX_FMT_NV61, 16, 2 }, + { "V4L2_PIX_FMT_UV12X", "YUV422/2P/MSB_CBCR", V4L2_PIX_FMT_NV16X, 16, 2 }, + { "V4L2_PIX_FMT_UV21X", "YUV422/2P/MSB_CRCB", V4L2_PIX_FMT_NV61X, 16, 2 }, + { "V4L2_PIX_FMT_YUV422P", "YUV422/3P", V4L2_PIX_FMT_YUV422P, 16, 3 }, +}; + +void dump_pixfmt_mp(struct v4l2_pix_format_mplane *pix_mp) +{ + LOGI("w: %d", pix_mp->width); + LOGI("h: %d", pix_mp->height); + LOGI("color: %x", pix_mp->colorspace); + + switch (pix_mp->pixelformat) { + case V4L2_PIX_FMT_YUYV: + LOGI ("YUYV"); + break; + case V4L2_PIX_FMT_UYVY: + LOGI ("UYVY"); + break; + case V4L2_PIX_FMT_RGB565: + LOGI ("RGB565"); + break; + case V4L2_PIX_FMT_RGB565X: + LOGI ("RGB565X"); + break; + default: + LOGI("not supported"); + } +} + +void dump_crop(struct v4l2_rect *rect) +{ + LOGI("crop l: %d", rect->left); + LOGI("crop t: %d", rect->top); + LOGI("crop w: %d", rect->width); + LOGI("crop h: %d", rect->height); +} + +void gsc_v4l2_dump_state(int fd) +{ + struct v4l2_format format; + struct v4l2_crop crop; + struct v4l2_subdev_crop sCrop; + + format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (ioctl(fd, VIDIOC_G_FMT, &format) < 0) + return; + + LOGI("dumping driver state:"); + dump_pixfmt_mp(&format.fmt.pix_mp); + + crop.type = format.type; + if (ioctl(fd, VIDIOC_G_CROP, &crop) < 0) + return; + + LOGI("input image crop:"); + dump_crop(&(crop.c)); + + sCrop.pad = GSC_SUBDEV_PAD_SOURCE; + sCrop.which = V4L2_SUBDEV_FORMAT_ACTIVE; + if (ioctl(fd, VIDIOC_SUBDEV_G_CROP, &sCrop) < 0) + return; + + LOGI("output image crop:"); + dump_crop(&(sCrop.rect)); + +} + +int gsc_v4l2_querycap(int fd, char *node) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_capability v4l2cap; + + if (ioctl(fd, VIDIOC_QUERYCAP, &v4l2cap) < 0) { + LOGE("%s::VIDIOC_QUERYCAP failed", __func__); + return -1; + } + + if (!(v4l2cap.capabilities & V4L2_CAP_STREAMING)) { + LOGE("%s::%s is not support streaming", __func__, node); + return -1; + } + + if (!(v4l2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) { + LOGE("%s::%s is not support video output mplane", __func__, node); + return -1; + } + + return 0; +} + +int gsc_v4l2_query_buf(int fd, SecBuffer *secBuf, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int buf_index, int num_plane) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_GSCALER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_GSCALER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + if (MAX_BUFFERS_GSCALER <= buf_index || MAX_PLANES_GSCALER < num_plane) { + LOGE("%s::exceed MAX! : buf_index=%d, num_plane=%d", __func__, buf_index, num_plane); + return -1; + } + + buf.type = type; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buf_index; + buf.length = num_plane; + buf.m.planes = planes; + + if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { + LOGE("%s::VIDIOC_QUERYBUF failed, plane_cnt=%d", __func__, buf.length); + return -1; + } + + for (int i = 0; i < num_plane; i++) { + if ((secBuf->virt.extP[i] = (char *)mmap(0, buf.m.planes[i].length, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.planes[i].m.mem_offset)) < 0) { + LOGE("%s::mmap failed", __func__); + LOGE("%s::Offset = 0x%x", __func__, buf.m.planes[i].m.mem_offset); + LOGE("%s::Legnth = %d" , __func__, buf.m.planes[i].length); + LOGE("%s::vaddr[%d][%d] = 0x%x", __func__, buf_index, i, (unsigned int)secBuf->virt.extP[i]); + return -1; + } + secBuf->size.extS[i] = buf.m.planes[i].length; + +#ifdef DEBUG_LIB_FIMC + LOGD("%s::vaddr[bufindex=%d][planeindex=%d] = 0x%x", __func__, buf_index, i, (unsigned int)secBuf->virt.extP[i]); + LOGD("%s::Legnth = %d" , __func__, buf.m.planes[i].length); +#endif + } + + return 0; +} + +int gsc_v4l2_req_buf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int num_bufs) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_requestbuffers reqbuf; + + reqbuf.type = type; + reqbuf.memory = memory; + reqbuf.count = num_bufs; + +#ifdef DEBUG_LIB_FIMC + LOGI("%d buffers needed", reqbuf.count); +#endif + + if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + LOGE("%s::VIDIOC_REQBUFS failed, reqbuf.count=%d", __func__, reqbuf.count); + return -1; + } + +#ifdef DEBUG_LIB_FIMC + LOGI("%d buffers allocated", reqbuf.count); +#endif + + if (reqbuf.count < num_bufs) { + LOGE("%s::VIDIOC_REQBUFS failed ((reqbuf.count(%d) < num_bufs(%d))", + __func__, reqbuf.count, num_bufs); + return -1; + } + + return 0; +} + +int gsc_v4l2_s_ctrl(int fd, int id, int value) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_control vc; + + vc.id = id; + vc.value = value; + + if (ioctl(fd, VIDIOC_S_CTRL, &vc) < 0) { + LOGE("%s::VIDIOC_S_CTRL (id=%d,value=%d) failed", __func__, id, value); + return -1; + } + + return 0; +} + +int gsc_v4l2_set_fmt(int fd, enum v4l2_buf_type type, enum v4l2_field field, s5p_fimc_img_info *img_info) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_format fmt; + struct v4l2_crop crop; + + fmt.type = type; +#ifdef DEBUG_LIB_FIMC + LOGD("%s::fmt.type=%d", __func__, fmt.type); +#endif + if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_G_FMT failed", __func__); + return -1; + } + + switch (fmt.type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt.fmt.pix.width = img_info->full_width; + fmt.fmt.pix.height = img_info->full_height; + fmt.fmt.pix.pixelformat = img_info->color_space; + fmt.fmt.pix.field = field; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt.fmt.pix_mp.width = img_info->full_width; + fmt.fmt.pix_mp.height = img_info->full_height; + fmt.fmt.pix_mp.pixelformat = img_info->color_space; + fmt.fmt.pix_mp.field = field; + fmt.fmt.pix_mp.num_planes = img_info->planes; + break; + default: + LOGE("%s::invalid buffer type", __func__); + return -1; + break; + } + +#ifdef DEBUG_LIB_FIMC +for (int i = 0; i < 3; i++) { + LOGD("%s::fmt.fmt.pix_mp. w=%d, h=%d, pixelformat=0x%08x, filed=%d, num_planes=%d", + __func__, + fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, + fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.field, + fmt.fmt.pix_mp.num_planes); +} +#endif + + if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_S_FMT failed", __func__); + return -1; + } + +#ifdef DEBUG_LIB_FIMC +for (int i = 0; i < 3; i++) { + LOGD("%s::pix_mp.pix_mp.plane_fmt[%d].sizeimage =0x%08x", __func__, i, fmt.fmt.pix_mp.plane_fmt[i].sizeimage); + LOGD("%s::pix_mp.pix_mp.plane_fmt[%d].bytesperline=0x%08x", __func__, i, fmt.fmt.pix_mp.plane_fmt[i].bytesperline); +} +#endif + + crop.type = type; + crop.c.left = img_info->start_x; + crop.c.top = img_info->start_y; + crop.c.width = img_info->width; + crop.c.height = img_info->height; + + if (ioctl(fd, VIDIOC_S_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_S_CROP (x=%d, y=%d, w=%d, h=%d) failed", + __func__, + img_info->start_x, + img_info->start_y, + img_info->width, + img_info->height); + return -1; + } + +#ifdef DEBUG_LIB_FIMC + LOGD("%s::pix_mp pixelformat=0x%08x", __func__, fmt.fmt.pix_mp.pixelformat); + LOGD("%s::pix_mp w=%d, h=%d, planes=%d", __func__, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, fmt.fmt.pix_mp.num_planes); + LOGD("%s::crop x=%d, y=%d, w=%d, h=%d", __func__, crop.c.left, crop.c.top, crop.c.width, crop.c.height); +#endif + + return 0; +} + +int gsc_v4l2_stream_on(int fd, enum v4l2_buf_type type) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { + LOGE("%s::VIDIOC_STREAMON failed", __func__); + return -1; + } + + return 0; +} + +int gsc_v4l2_queue(int fd, SecBuffer *secBuf, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int index, int num_plane) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); + LOGD("%s::num_plane=%d", __func__, num_plane); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_GSCALER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_GSCALER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + buf.type = type; + buf.memory = memory; + buf.length = num_plane; + buf.index = index; + buf.m.planes = planes; + + for (unsigned int i = 0; i < buf.length; i++) { + buf.m.planes[i].m.userptr = (unsigned long)secBuf->virt.extP[i]; + buf.m.planes[i].length = secBuf->size.extS[i]; + buf.m.planes[i].bytesused = buf.m.planes[i].length; +#ifdef DEBUG_LIB_FIMC + LOGD("%s::buf.index=%d", __func__, buf.index); + LOGD("%s::buf.m.planes[%d].m.userptr=0x%08x", __func__, i, (unsigned int)buf.m.planes[i].m.userptr); + LOGD("%s::buf.m.planes[%d].length =0x%08x", __func__, i, buf.m.planes[i].length); + LOGD("%s::buf.m.planes[%d].bytesused=0x%08x", __func__, i, buf.m.planes[i].bytesused); +#endif + } + + if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { + LOGE("%s::VIDIOC_QBUF failed", __func__); + return -1; + } + + return 0; +} + +int gsc_v4l2_dequeue(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int *index, int num_plane) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_GSCALER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_GSCALER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + + buf.type = type; + buf.memory = memory; + buf.length = num_plane; + buf.m.planes = planes; + + if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { + LOGE("%s::VIDIOC_DQBUF failed", __func__); + return -1; + } + + *index = buf.index; + +#ifdef DEBUG_LIB_FIMC + LOGD("%s::buf.index=%d", __func__, buf.index); +#endif + + return 0; +} + +int gsc_v4l2_stream_off(int fd, enum v4l2_buf_type type) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) { + LOGE("%s::VIDIOC_STREAMOFF failed", __func__); + return -1; + } + + return 0; +} + +int gsc_v4l2_clr_buf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_requestbuffers req; + + req.count = 0; + req.type = type; + req.memory = memory; + + if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { + LOGE("%s::VIDIOC_REQBUFS failed", __func__); + return -1; + } + + return 0; +} + +int gsc_subdev_set_fmt(int fd, unsigned int pad, enum v4l2_mbus_pixelcode code, s5p_fimc_img_info *img_info) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + struct v4l2_subdev_format fmt; + struct v4l2_subdev_crop crop; + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = img_info->full_width; + fmt.format.height = img_info->full_height; + fmt.format.code = code; + + if (ioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_FMT failed", __func__); + return -1; + } + + crop.pad = pad; + crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.left = img_info->start_x; + crop.rect.top = img_info->start_y; + crop.rect.width = img_info->width; + crop.rect.height = img_info->height; + +#ifdef DEBUG_LIB_FIMC + LOGD("%s::pix_mp w=%d, h=%d", __func__, fmt.format.width, fmt.format.height); + LOGD("%s::crop x=%d, y=%d, w=%d, h=%d", __func__, crop.rect.left, crop.rect.top, crop.rect.width, crop.rect.height); +#endif + + if (ioctl(fd, VIDIOC_SUBDEV_S_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_CROP failed", __func__); + return -1; + } + + return 0; +} + +static inline int multipleOfN(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; +} + +SecGscaler::SecGscaler() +: mFlagCreate(false) +{ +} + +SecGscaler::~SecGscaler() +{ + if (mFlagCreate == true) { + LOGE("%s::this is not Destroyed fail", __func__); + + if (destroy() == false) + LOGE("%s::destroy failed", __func__); + } +} + +bool SecGscaler::create(enum DEV dev, enum MODE mode, unsigned int numOfBuf) +{ + create(dev, numOfBuf); + return true; +} + +bool SecGscaler::create(enum DEV dev, unsigned int numOfBuf) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + if (mFlagCreate == true) { + LOGE("%s::Already Created", __func__); + return true; + } + + char node[20]; + char subdevname[32]; + char videodevname[32]; + + struct v4l2_capability v4l2cap; + struct media_entity_desc entity_desc; + + mDev = dev; + mVideoNodeNum = dev; + mNumOfBuf = numOfBuf; + + mVideodevFd = 0; + mMediadevFd = 0; + mSubdevFd = 0; + + mSrcIndex = 0; + mRotVal = 0; + mFlagGlobalAlpha = false; + mGlobalAlpha = 0x0; + mFlagLocalAlpha = false; + mFlagColorKey = false; + mColorKey = 0x0; + mFlagSetSrcParam = false; + mFlagSetDstParam = false; + mFlagStreamOn = false; + + memset(&mS5pFimc, 0, sizeof(s5p_fimc_t)); + + switch(mDev) { + case DEV_0: + mVideoNodeNum = 24; + mSubdevNodeNum = 4; + break; + case DEV_1: + mVideoNodeNum = 27; + mSubdevNodeNum = 5; + break; + case DEV_2: + mVideoNodeNum = 30; + mSubdevNodeNum = 6; + break; + case DEV_3: + mVideoNodeNum = 33; + mSubdevNodeNum = 3; // need to modify //carrotsm + break; + default: + LOGE("%s::invalid mDev(%d)", __func__, mDev); + goto err; + break; + } + + sprintf(node, "%s%d", PFX_NODE_MEDIADEV, 0); + mMediadevFd = open(node, O_RDONLY); + + if (mMediadevFd < 0) { + LOGE("%s::open(%s) failed : O_RDONLY", __func__, node); + goto err; + } + + sprintf(subdevname, PFX_ENTITY_SUBDEV_GSC, mDev); + sprintf(videodevname, PFX_ENTITY_OUTPUTDEV_GSC, mDev); + + for (__u32 id = 0; ; id = entity_desc.id) { + entity_desc.id = id | MEDIA_ENT_ID_FLAG_NEXT; + + if (ioctl(mMediadevFd, MEDIA_IOC_ENUM_ENTITIES, &entity_desc) < 0) { + if (errno == EINVAL) { + LOGD("%s::MEDIA_IOC_ENUM_ENTITIES ended", __func__); + break; + } + LOGE("%s::MEDIA_IOC_ENUM_ENTITIES failed", __func__); + goto err; + } + +#ifdef DEBUG_LIB_FIMC + LOGD("%s::entity_desc.id=%d, .minor=%d .name=%s", __func__, entity_desc.id, entity_desc.v4l.minor, entity_desc.name); +#endif + + if (strncmp(entity_desc.name, subdevname, strlen(subdevname)) == 0) + mSubdevEntity = entity_desc.id; + + if (strncmp(entity_desc.name, videodevname, strlen(videodevname)) == 0) + mVideodevEntity = entity_desc.id; + } + + if (0 < mMediadevFd) + close(mMediadevFd); + mMediadevFd = -1; + + sprintf(node, "%s%d", PFX_NODE_SUBDEV, mSubdevNodeNum); + mSubdevFd = open(node, O_RDWR); + if (mSubdevFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto err; + } + + sprintf(node, "%s%d", PFX_NODE_VIDEODEV, mVideoNodeNum); + mVideodevFd = open(node, O_RDWR); + if (mVideodevFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto err; + } + + /* check capability */ + if (gsc_v4l2_querycap(mVideodevFd, node) < 0 ) { + LOGE("%s::tvout_std_v4l2_querycap failed", __func__); + goto err; + } + + mFlagCreate = true; + + return true; + +err : + if (0 < mVideodevFd) + close(mVideodevFd); + + if (0 < mMediadevFd) + close(mMediadevFd); + + if (0 < mSubdevFd) + close(mSubdevFd); + + mVideodevFd = -1; + mMediadevFd = -1; + mSubdevFd = -1; + + return false; +} + +bool SecGscaler::destroy() +{ + s5p_fimc_params_t *params = &(mS5pFimc.params); + + if (mFlagCreate == false) { + LOGE("%s::Already Destroyed", __func__); + return true; + } + + if (mFlagStreamOn == true) { + if (streamOff() == false) { + LOGE("%s::streamOff() failed", __func__); + return false; + } + + if (closeVideodevFd() == false) { + LOGE("%s::closeVideodevFd() failed", __func__); + return false; + } + + mFlagStreamOn = false; + } + + if (0 < mMediadevFd) + close(mMediadevFd); + + if (0 < mSubdevFd) + close(mSubdevFd); + + mVideodevFd = -1; + mMediadevFd = -1; + mSubdevFd = -1; + + mFlagCreate = false; + + return true; +} + +bool SecGscaler::flagCreate(void) +{ + return mFlagCreate; +} + +int SecGscaler::getFd(void) +{ + return getVideodevFd(); +} + +int SecGscaler::getVideodevFd(void) +{ + return mVideodevFd; +} + +bool SecGscaler::openVideodevFd(void) +{ + char node[32]; + + sprintf(node, "%s%d", PFX_NODE_VIDEODEV, mVideoNodeNum); + mVideodevFd = open(node, O_RDWR); + if (mVideodevFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + return false; + } + + return true; +} + +bool SecGscaler::closeVideodevFd(void) +{ + if (mFlagSetSrcParam == true) { + if (gsc_v4l2_clr_buf(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE) < 0) { + LOGE("%s::gsc_v4l2_clr_buf() failed", __func__); + return false; + } + } + + if (0 < mVideodevFd) { + if (close(mVideodevFd) < 0) { + LOGE("%s::close Videodev failed", __func__); + return false; + } + } + mVideodevFd = -1; + + return true; +} + +int SecGscaler::getSubdevFd(void) +{ + return mSubdevFd; +} + +__u32 SecGscaler::getSubdevEntity(void) +{ + return mSubdevEntity; +} + +__u32 SecGscaler::getVideodevEntity(void) +{ + return mVideodevEntity; +} + +bool SecGscaler::getFlagSteamOn(void) +{ + return mFlagStreamOn; +} + +SecBuffer * SecGscaler::getSrcBufferAddr(int index) +{ + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + return &mSrcBuffer[index]; +} + +bool SecGscaler::setSrcParams(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + int v4l2ColorFormat = HAL_PIXEL_FORMAT_2_V4L2_PIX(colorFormat); + if (v4l2ColorFormat < 0) { + LOGE("%s::not supported color format", __func__); + return false; + } + + s5p_fimc_params_t *params = &(mS5pFimc.params); + + unsigned int fimcWidth = *cropWidth; + unsigned int fimcHeight = *cropHeight; + int src_planes = m_getYuvPlanes(v4l2ColorFormat); + int src_bpp = m_getYuvBpp(v4l2ColorFormat); + unsigned int frame_size = width * height; + unsigned int frame_ratio = 8; + + if (m_checkSrcSize(width, height, cropX, cropY, + &fimcWidth, &fimcHeight, v4l2ColorFormat, false) == false) { + LOGE("%s::::size align error!", __func__); + return false; + } + + if (fimcWidth != *cropWidth || fimcHeight != *cropHeight) { + if (forceChange == true) { +#ifdef DEBUG_LIB_FIMC + LOGD("size is changed from [w=%d, h=%d] to [w=%d, h=%d]", + *cropWidth, *cropHeight, fimcWidth, fimcHeight); +#endif + } else { + LOGE("%s::invalid source params", __func__); + return false; + } + } + + if ( (params->src.full_width == width) + && (params->src.full_height == height) + && (params->src.start_x == cropX) + && (params->src.start_y == cropY) + && (params->src.width == fimcWidth) + && (params->src.height == fimcHeight) + && (params->src.color_space == (unsigned int)v4l2ColorFormat)) + return true; + + params->src.full_width = width; + params->src.full_height = height; + params->src.start_x = cropX; + params->src.start_y = cropY; + params->src.width = fimcWidth; + params->src.height = fimcHeight; + params->src.color_space = v4l2ColorFormat; + src_planes = (src_planes == -1) ? 1 : src_planes; + params->src.planes = src_planes; + + mSrcBuffer[mSrcIndex].size.extS[0] = 0; + mSrcBuffer[mSrcIndex].size.extS[1] = 0; + mSrcBuffer[mSrcIndex].size.extS[2] = 0; + + frame_ratio = frame_ratio * (src_planes -1) / (src_bpp - 8); +#ifdef USE_GSC_USERPTR + for (int buf_index = 0; buf_index < MAX_BUFFERS_GSCALER; buf_index++) { + switch (src_planes) { + case 1: + switch (v4l2ColorFormat) { + case V4L2_PIX_FMT_BGR32: + params->src.color_space = V4L2_PIX_FMT_RGB32; + case V4L2_PIX_FMT_RGB32: + mSrcBuffer[buf_index].size.extS[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: + mSrcBuffer[buf_index].size.extS[0] = frame_size << 1; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + mSrcBuffer[buf_index].size.extS[0] = (frame_size * 3) >> 1; + break; + default: + LOGE("%s::invalid color type", __func__); + return false; + break; + } + mSrcBuffer[buf_index].size.extS[1] = 0; + mSrcBuffer[buf_index].size.extS[2] = 0; + break; + case 2: + case 3: + mSrcBuffer[buf_index].size.extS[0] = frame_size; + mSrcBuffer[buf_index].size.extS[1] = frame_size / frame_ratio; + mSrcBuffer[buf_index].size.extS[2] = frame_size / frame_ratio; + break; + default: + LOGE("%s::invalid color foarmt", __func__); + return false; + break; + } + } +#else + if (v4l2ColorFormat == V4L2_PIX_FMT_BGR32) + params->src.color_space = V4L2_PIX_FMT_RGB32; +#endif + + if (mFlagSetSrcParam == true) { + if (gsc_v4l2_clr_buf(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE) < 0) { + LOGE("%s::gsc_v4l2_clr_buf() failed", __func__); + return false; + } + } + + if (gsc_v4l2_set_fmt(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_FIELD_NONE, &(params->src)) < 0) { + LOGE("%s::fimc_v4l2_set_fmt()[src] failed", __func__); + return false; + } + + if (gsc_v4l2_req_buf(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE, mNumOfBuf) < 0) { + LOGE("%s::fimc_v4l2_req_buf()[src] failed", __func__); + return false; + } +#ifdef USE_GSC_USERPTR +#else + for (unsigned int buf_index = 0; buf_index < MAX_BUFFERS_GSCALER; buf_index++) { + if (gsc_v4l2_query_buf(mVideodevFd, &mSrcBuffer[buf_index], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_MMAP, buf_index, params->src.planes) < 0) { + LOGE("%s::gsc_v4l2_query_buf() failed", __func__); + return false; + } + } +#endif + + *cropWidth = fimcWidth; + *cropHeight = fimcHeight; + + mFlagSetSrcParam = true; + + return true; +} + +bool SecGscaler::getSrcParams(unsigned int *width, unsigned int *height, + unsigned int *cropX, unsigned int *cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int *v4l2colorFormat) +{ + struct v4l2_format fmt; + struct v4l2_crop crop; + + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + if (ioctl(mVideodevFd, VIDIOC_G_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_G_FMT(fmt.type : %d) failed", __func__, fmt.type); + return false; + } + + switch (fmt.type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + *width = fmt.fmt.pix.width; + *height = fmt.fmt.pix.height; + *v4l2colorFormat = fmt.fmt.pix.pixelformat; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *width = fmt.fmt.pix_mp.width; + *height = fmt.fmt.pix_mp.height; + *v4l2colorFormat = fmt.fmt.pix_mp.pixelformat; + break; + default: + LOGE("%s::Invalid buffer type", __func__); + return false; + break; + } + + crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (ioctl(mVideodevFd, VIDIOC_G_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_G_CROP failed", __func__); + return false; + } + + *cropX = crop.c.left; + *cropY = crop.c.top; + *cropWidth = crop.c.width; + *cropHeight = crop.c.height; + + return true; +} + +bool SecGscaler::setSrcAddr(unsigned int YAddr, + unsigned int CbAddr, + unsigned int CrAddr, + int colorFormat) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagSetSrcParam == false) { + LOGE("%s::mFlagSetSrcParam == false", __func__); + return false; + } + +#ifdef USE_GSC_USERPTR + mSrcBuffer[mSrcIndex].virt.extP[0] = (char *)YAddr; + mSrcBuffer[mSrcIndex].virt.extP[1] = (char *)CbAddr; + mSrcBuffer[mSrcIndex].virt.extP[2] = (char *)CrAddr; +#else + unsigned int srcAddr[MAX_PLANES_GSCALER]; + + srcAddr[0] = YAddr; + srcAddr[1] = CbAddr; + srcAddr[2] = CrAddr; + + for (int plane_index = 0; plane_index < mS5pFimc.params.src.planes; plane_index++) { + memcpy((void *)mSrcBuffer[mSrcIndex].virt.extP[plane_index], (void *)srcAddr[plane_index], mSrcBuffer[mSrcIndex].size.extS[plane_index]); + } +#endif + + return true; +} + +bool SecGscaler::setDstParams(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + int mbusFormat = V4L2_MBUS_FMT_YUV8_1X24; + + s5p_fimc_params_t *params = &(mS5pFimc.params); + + unsigned int fimcWidth = *cropWidth; + unsigned int fimcHeight = *cropHeight; + + if( m_checkDstSize(width, height, cropX, cropY, + &fimcWidth, &fimcHeight, V4L2_PIX_FMT_YUV444, + mRotVal, true) == false) { + LOGE("%s::size align error!", __func__); + return false; + } + + + if (fimcWidth != *cropWidth || fimcHeight != *cropHeight) { + if (forceChange == true) { +#ifdef DEBUG_LIB_FIMC + LOGD("size is changed from [w = %d, h= %d] to [w = %d, h = %d]", + *cropWidth, *cropHeight, fimcWidth, fimcHeight); +#endif + } else { + LOGE("%s::Invalid destination params", __func__); + return false; + } + } + + if (90 == mRotVal || 270 == mRotVal) { + params->dst.full_width = height; + params->dst.full_height = width; + + if (90 == mRotVal) { + params->dst.start_x = cropY; + params->dst.start_y = width - (cropX + fimcWidth); + } else { + params->dst.start_x = height - (cropY + fimcHeight); + params->dst.start_y = cropX; + } + + params->dst.width = fimcHeight; + params->dst.height = fimcWidth; + + params->dst.start_y += (fimcWidth - params->dst.height); + + } else { + params->dst.full_width = width; + params->dst.full_height = height; + + if (180 == mRotVal) { + params->dst.start_x = width - (cropX + fimcWidth); + params->dst.start_y = height - (cropY + fimcHeight); + } else { + params->dst.start_x = cropX; + params->dst.start_y = cropY; + } + + params->dst.width = fimcWidth; + params->dst.height = fimcHeight; + } + + if (gsc_subdev_set_fmt(mSubdevFd, GSC_SUBDEV_PAD_SOURCE, V4L2_MBUS_FMT_YUV8_1X24, &(params->dst)) < 0) { + LOGE("%s::gsc_subdev_set_fmt()[dst] failed", __func__); + return false; + } + + mFlagSetDstParam = true; + + return true; +} + +bool SecGscaler::getDstParams(unsigned int *width, unsigned int *height, + unsigned int *cropX, unsigned int *cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int *mbusColorFormat) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev_crop crop; + + fmt.pad = GSC_SUBDEV_PAD_SOURCE; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + if (ioctl(mSubdevFd, VIDIOC_SUBDEV_G_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_FMT", __func__); + return -1; + } + + *width = fmt.format.width; + *height = fmt.format.height; + *mbusColorFormat = fmt.format.code; + + crop.pad = GSC_SUBDEV_PAD_SOURCE; + crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + if (ioctl(mSubdevFd, VIDIOC_SUBDEV_G_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_CROP", __func__); + return -1; + } + + *cropX = crop.rect.left; + *cropY = crop.rect.top; + *cropWidth = crop.rect.width; + *cropHeight = crop.rect.height; + + return true; +} + +bool SecGscaler::setDstAddr(unsigned int YAddr, unsigned int CbAddr, unsigned int CrAddr, int buf_index) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + return true; +} + +bool SecGscaler::setRotVal(unsigned int rotVal) +{ + struct v4l2_control vc; + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (gsc_v4l2_s_ctrl(mVideodevFd, V4L2_CID_ROTATE, rotVal) < 0) { + LOGE("%s::fimc_v4l2_s_ctrl(V4L2_CID_ROTATE) failed", __func__); + return false; + } + + mRotVal = rotVal; + return true; +} + +bool SecGscaler::setGlobalAlpha(bool enable, int alpha) +{ + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagStreamOn == true) { + LOGE("%s::mFlagStreamOn == true", __func__); + return false; + } + + mFlagGlobalAlpha = enable; + mGlobalAlpha = alpha; + + return true; + +} + +bool SecGscaler::setLocalAlpha(bool enable) +{ + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagStreamOn == true) { + LOGE("%s::mFlagStreamOn == true", __func__); + return false; + } + + if (mFlagLocalAlpha == enable) + return true; + + return true; +} + +bool SecGscaler::setColorKey(bool enable, int colorKey) +{ + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagStreamOn == true) { + LOGE("%s::mFlagStreamOn == true", __func__); + return false; + } + + mFlagColorKey = enable; + mColorKey = colorKey; + + return true; +} + +bool SecGscaler::run() +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagSetSrcParam == false) { + LOGE("%s::faild : Need to set source parameters of Gscaler", __func__); + return false; + } + + if (mFlagSetDstParam == false) { + LOGE("%s::faild : Need to set destination parameters of Gscaler", __func__); + return false; + } + + s5p_fimc_params_t *params = &(mS5pFimc.params); + int src_planes = m_getYuvPlanes(params->src.color_space); + unsigned int dqIndex = 0; + src_planes = (src_planes == -1) ? 1 : src_planes; + + if (gsc_v4l2_queue(mVideodevFd, &(mSrcBuffer[mSrcIndex]), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE, mSrcIndex, src_planes) < 0) { + LOGE("%s::fimc_v4l2_queue[src](mNumOfBuf : %d,mSrcIndex=%d) failed", __func__, mNumOfBuf, mSrcIndex); + return false; + } + + mSrcIndex++; + if (mSrcIndex >= MAX_BUFFERS_GSCALER) { + mSrcIndex = 0; + } + + if (gsc_v4l2_dequeue(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE, &dqIndex, src_planes) < 0) { + LOGE("%s::fimc_v4l2_dequeue[src](mNumOfBuf : %d, dqIndex=%d) failed", __func__, mNumOfBuf, dqIndex); + return false; + } + + return true; +} + +bool SecGscaler::streamOn(void) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagStreamOn == true) { + LOGE("%s::already streamon", __func__); + return true; + } + + s5p_fimc_params_t *params = &(mS5pFimc.params); + int src_planes = m_getYuvPlanes(params->src.color_space); + src_planes = (src_planes == -1) ? 1 : src_planes; + + if (gsc_v4l2_queue(mVideodevFd, &(mSrcBuffer[mSrcIndex]), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_TYPE, mSrcIndex, src_planes) < 0) { + LOGE("%s::fimc_v4l2_queue[src](mNumOfBuf : %d,mSrcIndex=%d) failed", __func__, mNumOfBuf, mSrcIndex); + return false; + } + + mSrcIndex++; + if (mSrcIndex == MAX_BUFFERS_GSCALER - 1) { + if (gsc_v4l2_stream_on(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) < 0) { + LOGE("%s::fimc_v4l2_stream_on() failed", __func__); + return false; + } + mFlagStreamOn = true; + } + + if (mSrcIndex >= MAX_BUFFERS_GSCALER) { + mSrcIndex = 0; + } + + return true; +} + +bool SecGscaler::streamOff(void) +{ +#ifdef DEBUG_LIB_FIMC + LOGD("%s", __func__); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagStreamOn == false) { + LOGE("%s::already streamoff", __func__); + return true; + } + + if (gsc_v4l2_stream_off(mVideodevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) < 0) { + LOGE("%s::fimc_v4l2_stream_off() failed", __func__); + return false; + } + + mSrcIndex = 0; +#ifdef USE_GSC_USERPTR + SecBuffer zeroBuf; + for (int buf_index = 0; buf_index < MAX_BUFFERS_GSCALER; buf_index++) + mSrcBuffer[buf_index] = zeroBuf; +#else + for (int buf_index = 0; buf_index < MAX_BUFFERS_GSCALER; buf_index++) { + for (int plane_index = 0; plane_index < MAX_PLANES_GSCALER; plane_index++) { + munmap((void *)mSrcBuffer[buf_index].virt.extP[plane_index], mSrcBuffer[buf_index].size.extS[plane_index]); + } + } +#endif + + mFlagStreamOn = false; + + return true; +} + +bool SecGscaler::m_checkSrcSize(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange) +{ + bool ret = true; + + if (8 <= height && *cropHeight < 8) { + if (forceChange) + *cropHeight = 8; + ret = false; + } + + if (16 <= width && *cropWidth < 16) { + if (forceChange) + *cropWidth = 16; + ret = false; + } + + if (height < 8) + return false; + + if (width % 16 != 0) + return false; + + if (*cropWidth % 16 != 0) { + if (forceChange) + *cropWidth = multipleOfN(*cropWidth, 16); + ret = false; + } + + return ret; +} + +bool SecGscaler::m_checkDstSize(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, int rotVal, bool forceChange) +{ + bool ret = true; + unsigned int rotWidth; + unsigned int rotHeight; + unsigned int *rotCropWidth; + unsigned int *rotCropHeight; + + if (rotVal == 90 || rotVal == 270) { + rotWidth = height; + rotHeight = width; + rotCropWidth = cropHeight; + rotCropHeight = cropWidth; + } else { + rotWidth = width; + rotHeight = height; + rotCropWidth = cropWidth; + rotCropHeight = cropHeight; + } + + if (rotHeight < 8) + return false; + + if (rotWidth % 8 != 0) + return false; + + switch (colorFormat) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV12MT: + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YUV420: + if (*rotCropHeight % 2 != 0) { + if (forceChange) + *rotCropHeight = multipleOfN(*rotCropHeight, 2); + ret = false; + } + } + return ret; +} + +int SecGscaler::m_widthOfFimc(int v4l2ColorFormat, int width) +{ + int newWidth = width; + + switch (v4l2ColorFormat) { + case V4L2_PIX_FMT_RGB565: + newWidth = multipleOfN(width, 8); + break; + case V4L2_PIX_FMT_RGB32: + newWidth = multipleOfN(width, 4); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + newWidth = multipleOfN(width, 4); + break; + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV16: + newWidth = multipleOfN(width, 8); + break; + case V4L2_PIX_FMT_YUV422P: + newWidth = multipleOfN(width, 16); + break; + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12MT: + newWidth = multipleOfN(width, 8); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV420M: + newWidth = multipleOfN(width, 16); + break; + default : + break; + } + + return newWidth; +} + +int SecGscaler::m_heightOfFimc(int v4l2ColorFormat, int height) +{ + int newHeight = height; + + switch (v4l2ColorFormat) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_NV12MT: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV420M: + newHeight = multipleOfN(height, 2); + break; + default : + break; + } + return newHeight; +} + +int SecGscaler::m_getYuvBpp(unsigned int fmt) +{ + int i, sel = -1; + + for (i = 0; i < (int)(sizeof(yuv_list) / sizeof(struct yuv_fmt_list)); i++) { + if (yuv_list[i].fmt == fmt) { + sel = i; + break; + } + } + + if (sel == -1) + return sel; + else + return yuv_list[sel].bpp; +} + +int SecGscaler::m_getYuvPlanes(unsigned int fmt) +{ + int i, sel = -1; + + for (i = 0; i < (int)(sizeof(yuv_list) / sizeof(struct yuv_fmt_list)); i++) { + if (yuv_list[i].fmt == fmt) { + sel = i; + break; + } + } + + if (sel == -1) + return sel; + else + return yuv_list[sel].planes; +} diff --git a/exynos5/hal/libhdmi/SecHdmi/SecGscaler.h b/exynos5/hal/libhdmi/SecHdmi/SecGscaler.h new file mode 100644 index 0000000..e9867ed --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecGscaler.h @@ -0,0 +1,195 @@ +/* + * 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. + */ + +#ifndef __SEC_GSC_OUT_H__ +#define __SEC_GSC_OUT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "utils/Timers.h" + +#include "s5p_fimc_v4l2.h" +#include "sec_utils_v4l2.h" +#include "media.h" +#include "v4l2-subdev.h" + +#include "sec_format.h" + +#include "SecBuffer.h" + +#define PFX_NODE_MEDIADEV "/dev/media" +#define PFX_NODE_SUBDEV "/dev/v4l-subdev" +#define PFX_NODE_VIDEODEV "/dev/video" +#define PFX_ENTITY_SUBDEV_GSC "exynos-gsc-sd.%d" +#define PFX_ENTITY_OUTPUTDEV_GSC "exynos-gsc.%d.output" + +#define GSC_SUBDEV_PAD_SINK (0) +#define GSC_SUBDEV_PAD_SOURCE (1) +#define GSC_VIDEODEV_PAD_SOURCE (0) +#define MAX_BUFFERS_GSCALER (2) +#define MAX_PLANES_GSCALER (3) + +#ifdef __cplusplus +} + +class SecGscaler +{ +public: + enum DEV { + DEV_0 = 0, + DEV_1, + DEV_2, + DEV_3, + DEV_MAX, + }; + + enum MODE { + MODE_NONE = 0, + MODE_SINGLE_BUF, + MODE_MULTI_BUF, + MODE_DMA_AUTO, + MODE_MAX, + }; + +private: + bool mFlagCreate; + int mDev; + int mVideoNodeNum; + int mSubdevNodeNum; + int mVideodevFd; + int mMediadevFd; + int mSubdevFd; + unsigned int mNumOfBuf; + unsigned int mSrcIndex; + int mRotVal; + bool mFlagGlobalAlpha; + int mGlobalAlpha; + bool mFlagLocalAlpha; + bool mFlagColorKey; + int mColorKey; + bool mFlagSetSrcParam; + bool mFlagSetDstParam; + bool mFlagStreamOn; + + s5p_fimc_t mS5pFimc; + SecBuffer mSrcBuffer[MAX_BUFFERS_GSCALER]; + + __u32 mSubdevEntity; + __u32 mVideodevEntity; + //struct media_link_desc mlink_desc; + +public: + SecGscaler(); + virtual ~SecGscaler(); + + bool create(enum DEV dev, enum MODE mode, unsigned int numOfBuf); + bool create(enum DEV dev, unsigned int numOfBuf); + + bool destroy(void); + bool flagCreate(void); + + int getFd(void); + int getVideodevFd(void); + bool openVideodevFd(void); + bool closeVideodevFd(void); + + int getSubdevFd(void); + __u32 getSubdevEntity(void); + __u32 getVideodevEntity(void); + bool getFlagSteamOn(void); + + SecBuffer * getSrcBufferAddr(int index); + + bool setSrcParams(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange = true); + + bool getSrcParams(unsigned int *width, unsigned int *height, + unsigned int *cropX, unsigned int *cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int *v4l2colorFormat); + + bool setSrcAddr(unsigned int YAddr, + unsigned int CbAddr = 0, + unsigned int CrAddr = 0, + int colorFormat = 0); + + bool setDstParams(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange = true); + + bool getDstParams(unsigned int *width, unsigned int *height, + unsigned int *cropX, unsigned int *cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int *mbusColorFormat); + + bool setDstAddr(unsigned int YAddr, + unsigned int CbAddr = 0, + unsigned int CrAddr = 0, + int buf_index = 0); + + bool setRotVal(unsigned int rotVal); + bool setGlobalAlpha(bool enable = true, int alpha = 0xff); + bool setLocalAlpha(bool enable); + bool setColorKey(bool enable = true, int colorKey = 0xff); + + bool run(void); + bool streamOn(void); + bool streamOff(void); + +private: + bool m_checkSrcSize(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + bool forceChange = false); + + bool m_checkDstSize(unsigned int width, unsigned int height, + unsigned int cropX, unsigned int cropY, + unsigned int *cropWidth, unsigned int *cropHeight, + int colorFormat, + int rotVal, + bool forceChange = false); + int m_widthOfFimc(int v4l2ColorFormat, int width); + int m_heightOfFimc(int v4l2ColorFormat, int height); + int m_getYuvBpp(unsigned int fmt); + int m_getYuvPlanes(unsigned int fmt); +}; +#endif + +#endif //__SEC_GSC_OUT_H__ diff --git a/exynos5/hal/libhdmi/SecHdmi/SecHdmi.cpp b/exynos5/hal/libhdmi/SecHdmi/SecHdmi.cpp new file mode 100644 index 0000000..8f0f80b --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecHdmi.cpp @@ -0,0 +1,1985 @@ +/* + * 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. + */ + +//#define DEBUG_MSG_ENABLE +//#define LOG_NDEBUG 0 +//#define LOG_TAG "libhdmi" +#include +#include "ion.h" +#include "SecHdmi.h" +#include "SecHdmiV4L2Utils.h" + +#define CHECK_GRAPHIC_LAYER_TIME (0) + +namespace android { + +extern unsigned int output_type; +extern v4l2_std_id t_std_id; +extern int g_hpd_state; +extern unsigned int g_hdcp_en; + +#if defined(BOARD_USES_HDMI_FIMGAPI) +extern unsigned int g2d_reserved_memory0; +extern unsigned int g2d_reserved_memory1; +extern unsigned int g2d_reserved_memory_size; +extern unsigned int cur_g2d_address; +#endif + +#if defined(BOARD_USES_CEC) +SecHdmi::CECThread::~CECThread() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + mFlagRunning = false; +} + +bool SecHdmi::CECThread::threadLoop() +{ + unsigned char buffer[CEC_MAX_FRAME_SIZE]; + int size; + unsigned char lsrc, ldst, opcode; + + { + Mutex::Autolock lock(mThreadLoopLock); + mFlagRunning = true; + + size = CECReceiveMessage(buffer, CEC_MAX_FRAME_SIZE, 100000); + + if (!size) // no data available or ctrl-c + return true; + + if (size == 1) + return true; // "Polling Message" + + lsrc = buffer[0] >> 4; + + /* ignore messages with src address == mLaddr*/ + if (lsrc == mLaddr) + return true; + + opcode = buffer[1]; + + if (CECIgnoreMessage(opcode, lsrc)) { + LOGE("### ignore message coming from address 15 (unregistered)\n"); + return true; + } + + if (!CECCheckMessageSize(opcode, size)) { + LOGE("### invalid message size: %d(opcode: 0x%x) ###\n", size, opcode); + return true; + } + + /* check if message broadcasted/directly addressed */ + if (!CECCheckMessageMode(opcode, (buffer[0] & 0x0F) == CEC_MSG_BROADCAST ? 1 : 0)) { + LOGE("### invalid message mode (directly addressed/broadcast) ###\n"); + return true; + } + + ldst = lsrc; + + //TODO: macroses to extract src and dst logical addresses + //TODO: macros to extract opcode + + switch (opcode) { + case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: + /* responce with "Report Physical Address" */ + buffer[0] = (mLaddr << 4) | CEC_MSG_BROADCAST; + buffer[1] = CEC_OPCODE_REPORT_PHYSICAL_ADDRESS; + buffer[2] = (mPaddr >> 8) & 0xFF; + buffer[3] = mPaddr & 0xFF; + buffer[4] = mDevtype; + size = 5; + break; + + case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: + LOGD("[CEC_OPCODE_REQUEST_ACTIVE_SOURCE]\n"); + /* responce with "Active Source" */ + buffer[0] = (mLaddr << 4) | CEC_MSG_BROADCAST; + buffer[1] = CEC_OPCODE_ACTIVE_SOURCE; + buffer[2] = (mPaddr >> 8) & 0xFF; + buffer[3] = mPaddr & 0xFF; + size = 4; + LOGD("Tx : [CEC_OPCODE_ACTIVE_SOURCE]\n"); + break; + + case CEC_OPCODE_ABORT: + case CEC_OPCODE_FEATURE_ABORT: + default: + /* send "Feature Abort" */ + buffer[0] = (mLaddr << 4) | ldst; + buffer[1] = CEC_OPCODE_FEATURE_ABORT; + buffer[2] = CEC_OPCODE_ABORT; + buffer[3] = 0x04; // "refused" + size = 4; + break; + } + + if (CECSendMessage(buffer, size) != size) + LOGE("CECSendMessage() failed!!!\n"); + + } + return true; +} + +bool SecHdmi::CECThread::start() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mThreadControlLock); + if (exitPending()) { + if (requestExitAndWait() == WOULD_BLOCK) { + LOGE("mCECThread.requestExitAndWait() == WOULD_BLOCK"); + return false; + } + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("EDIDGetCECPhysicalAddress"); +#endif + /* set to not valid physical address */ + mPaddr = CEC_NOT_VALID_PHYSICAL_ADDRESS; + + if (!EDIDGetCECPhysicalAddress(&mPaddr)) { + LOGE("Error: EDIDGetCECPhysicalAddress() failed.\n"); + return false; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("CECOpen"); +#endif + if (!CECOpen()) { + LOGE("CECOpen() failed!!!\n"); + return false; + } + + /* a logical address should only be allocated when a device \ + has a valid physical address, at all other times a device \ + should take the 'Unregistered' logical address (15) + */ + + /* if physical address is not valid device should take \ + the 'Unregistered' logical address (15) + */ + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("CECAllocLogicalAddress"); +#endif + mLaddr = CECAllocLogicalAddress(mPaddr, mDevtype); + + if (!mLaddr) { + LOGE("CECAllocLogicalAddress() failed!!!\n"); + if (!CECClose()) + LOGE("CECClose() failed!\n"); + return false; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("request to run CECThread"); +#endif + + status_t ret = run("SecHdmi::CECThread", PRIORITY_DISPLAY); + if (ret != NO_ERROR) { + LOGE("%s fail to run thread", __func__); + return false; + } + return true; +} + +bool SecHdmi::CECThread::stop() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s request Exit", __func__); +#endif + Mutex::Autolock lock(mThreadControlLock); + if (requestExitAndWait() == WOULD_BLOCK) { + LOGE("mCECThread.requestExitAndWait() == WOULD_BLOCK"); + return false; + } + + if (!CECClose()) + LOGE("CECClose() failed!\n"); + + mFlagRunning = false; + return true; +} +#endif + +SecHdmi::SecHdmi(): +#if defined(BOARD_USES_CEC) + mCECThread(NULL), +#endif + mFlagCreate(false), + mFlagConnected(false), + mPreviousHdmiPresetId(V4L2_DV_1080P60), + mHdmiDstWidth(0), + mHdmiDstHeight(0), + mHdmiSrcYAddr(0), + mHdmiSrcCbCrAddr(0), + mFBaddr(0), + mFBsize(0), + mFBionfd(-1), + mFBoffset(0), + mHdmiOutputMode(DEFAULT_OUPUT_MODE), + mHdmiResolutionValue(DEFAULT_HDMI_RESOLUTION_VALUE), // V4L2_STD_480P_60_4_3 + mHdmiStdId(DEFAULT_HDMI_STD_ID), // V4L2_STD_480P_60_4_3 + mCompositeStd(DEFAULT_COMPOSITE_STD), + mHdcpMode(false), + mAudioMode(2), + mUIRotVal(0), + mG2DUIRotVal(0), + mCurrentHdmiOutputMode(-1), + mCurrentHdmiResolutionValue(0), // 1080960 + mCurrentHdcpMode(false), + mCurrentAudioMode(-1), + mHdmiInfoChange(true), + mFlagGscalerStart(false), + mGscalerDstColorFormat(0), + mDefaultFBFd(-1), + mCurrentsrcW(0), + mCurrentsrcH(0), + mCurrentsrcColorFormat(0), + mCurrentsrcYAddr(0), + mCurrentsrcCbAddr(0), + mCurrentdstX(0), + mCurrentdstY(0), + mCurrenthdmiLayer(0), + mCurrentNumOfHWCLayer(0), + mDisplayWidth(DEFALULT_DISPLAY_WIDTH), + mDisplayHeight(DEFALULT_DISPLAY_HEIGHT) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + for (int i = 0; i < HDMI_LAYER_MAX; i++) { + mFlagLayerEnable[i] = false; + mFlagHdmiStart[i] = false; + + mSrcWidth [i] = 0; + mSrcHeight [i] = 0; + mSrcColorFormat[i] = 0; + mHdmiResolutionWidth [i] = 0; + mHdmiResolutionHeight [i] = 0; + mPreviousNumofHwLayer [i] = 0; + mSrcIndex[i] = 0; + } + + //All layer is on + mFlagLayerEnable[HDMI_LAYER_VIDEO] = true; + mFlagLayerEnable[HDMI_LAYER_GRAPHIC_0] = true; + mFlagLayerEnable[HDMI_LAYER_GRAPHIC_1] = true; + + mHdmiSizeOfResolutionValueList = 14; + + mHdmiResolutionValueList[0] = 1080960; + mHdmiResolutionValueList[1] = 1080950; + mHdmiResolutionValueList[2] = 1080930; + mHdmiResolutionValueList[3] = 1080924; + mHdmiResolutionValueList[4] = 1080160; + mHdmiResolutionValueList[5] = 1080150; + mHdmiResolutionValueList[6] = 720960; + mHdmiResolutionValueList[7] = 7209601; + mHdmiResolutionValueList[8] = 720950; + mHdmiResolutionValueList[9] = 7209501; + mHdmiResolutionValueList[10] = 5769501; + mHdmiResolutionValueList[11] = 5769502; + mHdmiResolutionValueList[12] = 4809601; + mHdmiResolutionValueList[13] = 4809602; + +#if defined(BOARD_USES_CEC) + mCECThread = new CECThread(this); +#endif + + mGscalerForceUpdate = false; +} + +SecHdmi::~SecHdmi() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (mFlagCreate == true) + LOGE("%s::this is not Destroyed fail", __func__); + else + disconnect(); +} + +bool SecHdmi::create(int width, int height) +{ + Mutex::Autolock lock(mLock); + unsigned int fimc_buf_size = 0; + int stride; + int vstride; + + int ionfd_G2D = 0; + void * ion_base_addr = NULL; + + struct s3c_fb_user_ion_client ion_handle; + unsigned int FB_size = ALIGN(width, 16) * ALIGN(height, 16) * HDMI_G2D_BUFFER_BPP_SIZE * 2; + int ionfd_FB = 0; + +/* + * Video plaback (I420): output buffer size of FIMC3 is (1920 x 1088 x 1.5) + * Video plaback (NV12): FIMC3 is not used. + * Camera preview (YV12): output buffer size of FIMC3 is (640 x 480 x 1.5) + * UI mode (ARGB8888) : output buffer size of FIMC3 is (480 x 800 x 1.5) + */ +#ifndef SUPPORT_1080P_FIMC_OUT + setDisplaySize(width, height); +#endif + + stride = ALIGN(HDMI_MAX_WIDTH, 16); + vstride = ALIGN(HDMI_MAX_HEIGHT, 16); + + fimc_buf_size = stride * vstride * HDMI_FIMC_BUFFER_BPP_SIZE; +#if defined(BOARD_USES_HDMI_FIMGAPI) + g2d_reserved_memory_size = stride * vstride * HDMI_G2D_BUFFER_BPP_SIZE; +#endif + +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + if (mFlagCreate == true) { + LOGE("%s::Already Created", __func__); + return true; + } + + if (mDefaultFBFd <= 0) { + if ((mDefaultFBFd = fb_open(DEFAULT_FB)) < 0) { + LOGE("%s:Failed to open default FB", __func__); + return false; + } + } + + if (mSecGscaler.create(SecGscaler::DEV_3, MAX_BUFFERS_GSCALER) == false) { + LOGE("%s::SecGscaler create() failed", __func__); + goto CREATE_FAIL; + } + + mIonClient = ion_client_create(); + if (mIonClient < 0) { + mIonClient = -1; + LOGE("%s::ion_client_create() failed", __func__); + goto CREATE_FAIL; + } + + // get framebuffer virtual address for LCD + if (ioctl(mDefaultFBFd, S3CFB_GET_ION_USER_HANDLE, &ion_handle) == -1) { + LOGE("%s:ioctl(S3CFB_GET_ION_USER_HANDLE) failed", __func__); + return false; + } + + mFBaddr = (unsigned int)ion_map(ion_handle.fd, ALIGN(FB_size, PAGE_SIZE), 0); + mFBsize = FB_size; + mFBionfd = ion_handle.fd; + +#if defined(BOARD_USES_HDMI_FIMGAPI) + ionfd_G2D = ion_alloc(mIonClient, ALIGN(g2d_reserved_memory_size * 2, PAGE_SIZE), 0, ION_HEAP_EXYNOS_MASK); + + if (ionfd_G2D < 0) { + LOGE("%s::ION memory allocation failed", __func__); + } else { + ion_base_addr = ion_map(ionfd_G2D, ALIGN(g2d_reserved_memory_size * 2, PAGE_SIZE), 0); + if (ion_base_addr == MAP_FAILED) + LOGE("%s::ION mmap failed", __func__); + } + + g2d_reserved_memory0 = (unsigned int)ion_base_addr; + g2d_reserved_memory1 = g2d_reserved_memory0 + g2d_reserved_memory_size; +#endif + + v4l2_std_id std_id; + __u32 preset_id; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::mHdmiOutputMode(%d) \n", __func__, mHdmiOutputMode); +#endif + if (mHdmiOutputMode == COMPOSITE_OUTPUT_MODE) { + std_id = composite_std_2_v4l2_std_id(mCompositeStd); + if ((int)std_id < 0) { + LOGE("%s::composite_std_2_v4l2_std_id(%d) fail\n", __func__, mCompositeStd); + goto CREATE_FAIL; + } + if (m_setCompositeResolution(mCompositeStd) == false) { + LOGE("%s::m_setCompositeResolution(%d) fail\n", __func__, mCompositeStd); + goto CREATE_FAIL; + } + } else if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (hdmi_resolution_2_std_id(mHdmiResolutionValue, &mHdmiDstWidth, &mHdmiDstHeight, &std_id, &preset_id) < 0) { + LOGE("%s::hdmi_resolution_2_std_id(%d) fail\n", __func__, mHdmiResolutionValue); + goto CREATE_FAIL; + } + } + + if (m_setupLink() == false) { + LOGE("%s:Enable the link failed", __func__); + return false; + } + + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (m_openLayer(layer) == false) + LOGE("%s::hdmi_init_layer(%d) failed", __func__, layer); + } + + for (int layer = HDMI_LAYER_BASE + 2; layer < HDMI_LAYER_MAX; layer++) { + if (tvout_std_v4l2_s_ctrl(mVideodevFd[layer], V4L2_CID_TV_LAYER_BLEND_ENABLE, 1) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ENABLE) failed", __func__, layer); + return false; + } + + if (tvout_std_v4l2_s_ctrl(mVideodevFd[layer], V4L2_CID_TV_PIXEL_BLEND_ENABLE, 1) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ENABLE) failed", __func__, layer); + return false; + } + + if (tvout_std_v4l2_s_ctrl(mVideodevFd[layer], V4L2_CID_TV_LAYER_BLEND_ALPHA, 255) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ALPHA) failed", __func__, layer); + return false; + } + } + + mFlagCreate = true; + + return true; + +CREATE_FAIL : + + if (mSecGscaler.flagCreate() == true && + mSecGscaler.destroy() == false) + LOGE("%s::Gscaler destory failed", __func__); + + return false; +} + +bool SecHdmi::destroy(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + char node[32]; + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Already Destroyed fail \n", __func__); + goto DESTROY_FAIL; + } + + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (mFlagHdmiStart[layer] == true && m_stopHdmi(layer) == false) { + LOGE("%s::m_stopHdmi: layer[%d] fail \n", __func__, layer); + goto DESTROY_FAIL; + } + } + + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (m_closeLayer(layer) == false) + LOGE("%s::hdmi_deinit_layer(%d) failed", __func__, layer); + } + + if (mSecGscaler.flagCreate() == true && mSecGscaler.destroy() == false) { + LOGE("%s::fimc destory fail \n", __func__); + goto DESTROY_FAIL; + } + +#ifdef USE_LCD_ADDR_IN_HERE + { + if (0 < mDefaultFBFd) { + close(mDefaultFBFd); + mDefaultFBFd = -1; + } + } +#endif //USE_LCD_ADDR_IN_HERE + +#if defined(BOARD_USES_HDMI_FIMGAPI) + ion_unmap((void *)g2d_reserved_memory0, ALIGN(g2d_reserved_memory_size * 2, PAGE_SIZE)); +#endif + + if (0 < mFBaddr) + ion_unmap((void *)mFBaddr, ALIGN(mFBsize, PAGE_SIZE)); + + if (0 < mFBionfd) + ion_free(mFBionfd); + + sprintf(node, "%s%d", PFX_NODE_MEDIADEV, 0); + mMediadevFd = open(node, O_RDONLY); + + if (mMediadevFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto DESTROY_FAIL; + } + + mlink_desc.flags = 0; + if (ioctl(mMediadevFd, MEDIA_IOC_SETUP_LINK, &mlink_desc) < 0) { + LOGE("%s::MEDIA_IOC_SETUP_UNLINK failed", __func__); + goto DESTROY_FAIL; + } + + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (m_closeLayer(layer) == false) + LOGE("%s::hdmi_deinit_layer(%d) failed", __func__, layer); + } + + if (0 < mMediadevFd) + close(mMediadevFd); + + if (0 < mSubdevMixerFd) + close(mSubdevMixerFd); + + mMediadevFd = -1; + mSubdevMixerFd = -1; + + mFlagCreate = false; + + return true; + +DESTROY_FAIL : + + return false; +} + +bool SecHdmi::connect(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + { + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (mFlagConnected == true) { + LOGD("%s::Already Connected.. \n", __func__); + return true; + } + + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (m_flagHWConnected() == false) { + LOGD("%s::m_flagHWConnected() fail \n", __func__); + return false; + } + +#if defined(BOARD_USES_EDID) + if (!EDIDOpen()) + LOGE("EDIDInit() failed!\n"); + + if (!EDIDRead()) { + LOGE("EDIDRead() failed!\n"); + if (!EDIDClose()) + LOGE("EDIDClose() failed!\n"); + } +#endif + +#if defined(BOARD_USES_CEC) + if (!(mCECThread->mFlagRunning)) + mCECThread->start(); +#endif + } + } + + if (this->setHdmiOutputMode(mHdmiOutputMode, true) == false) + LOGE("%s::setHdmiOutputMode(%d) fail \n", __func__, mHdmiOutputMode); + + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (this->setHdmiResolution(mHdmiResolutionValue, true) == false) + LOGE("%s::setHdmiResolution(%d) fail \n", __func__, mHdmiResolutionValue); + + if (this->setHdcpMode(mHdcpMode, false) == false) + LOGE("%s::setHdcpMode(%d) fail \n", __func__, mHdcpMode); + +/* if (this->m_setAudioMode(mAudioMode) == false) + LOGE("%s::m_setAudioMode(%d) fail \n", __func__, mAudioMode); +*/ + mHdmiInfoChange = true; + mFlagConnected = true; + +#if defined(BOARD_USES_EDID) + display_menu(); +#endif + } + + return true; +} + +bool SecHdmi::disconnect(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (mFlagConnected == false) { + LOGE("%s::Already Disconnected.. \n", __func__); + return true; + } + + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { +#if defined(BOARD_USES_CEC) + if (mCECThread->mFlagRunning) + mCECThread->stop(); +#endif + +#if defined(BOARD_USES_EDID) + if (!EDIDClose()) { + LOGE("EDIDClose() failed!\n"); + return false; + } +#endif + } + + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (mFlagHdmiStart[layer] == true && m_stopHdmi(layer) == false) { + LOGE("%s::hdmiLayer(%d) layer fail \n", __func__, layer); + return false; + } + } + + mFlagConnected = false; + mPreviousHdmiPresetId = V4L2_DV_1080P60; + + mHdmiOutputMode = DEFAULT_OUPUT_MODE; + mHdmiResolutionValue = DEFAULT_HDMI_RESOLUTION_VALUE; + mHdmiStdId = DEFAULT_HDMI_STD_ID; + mCompositeStd = DEFAULT_COMPOSITE_STD; + mAudioMode = 2; + mCurrentHdmiOutputMode = -1; + mCurrentHdmiResolutionValue = 0; + mCurrentAudioMode = -1; + + return true; +} + +bool SecHdmi::startHdmi(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + if (mFlagHdmiStart[hdmiLayer] == false && + m_startHdmi(hdmiLayer) == false) { + LOGE("%s::hdmiLayer(%d) fail \n", __func__, hdmiLayer); + return false; + } + return true; +} + +bool SecHdmi::stopHdmi(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + if (mFlagHdmiStart[hdmiLayer] == true && + m_stopHdmi(hdmiLayer) == false) { + LOGE("%s::hdmiLayer(%d) layer fail \n", __func__, hdmiLayer); + return false; + } + tvout_deinit(); + return true; +} + +bool SecHdmi::flagConnected(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + return mFlagConnected; +} + +bool SecHdmi::flush(int srcW, int srcH, int srcColorFormat, + unsigned int srcYAddr, unsigned int srcCbAddr, unsigned int srcCrAddr, + int dstX, int dstY, + int hdmiLayer, + int num_of_hwc_layer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer=%d", __func__, hdmiLayer); +#endif + + Mutex::Autolock lock(mLock); + +#ifdef DEBUG_MSG_ENABLE + LOGD("%s [srcW = %d, srcH = %d, srcColorFormat = 0x%x, srcYAddr= 0x%x, srcCbAddr = 0x%x, srcCrAddr = 0x%x, dstX = %d, dstY = %d, hdmiLayer = %d, num_of_hwc_layer=%d", + __func__, srcW, srcH, srcColorFormat, + srcYAddr, srcCbAddr, srcCrAddr, + dstX, dstY, hdmiLayer, num_of_hwc_layer); + LOGD("saved param(%d, %d, %d)", + mSrcWidth[hdmiLayer], mSrcHeight[hdmiLayer], mSrcColorFormat[hdmiLayer]); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created", __func__); + return false; + } + + if (srcW != mSrcWidth[hdmiLayer] || + srcH != mSrcHeight[hdmiLayer] || + srcColorFormat != mSrcColorFormat[hdmiLayer] || + mHdmiDstWidth != mHdmiResolutionWidth[hdmiLayer] || + mHdmiDstHeight != mHdmiResolutionHeight[hdmiLayer] || + num_of_hwc_layer != mPreviousNumofHwLayer[hdmiLayer] || + mHdmiInfoChange == true) { +#ifdef DEBUG_MSG_ENABLE + LOGD("m_reset param(%d, %d, %d, %d, %d, %d, %d)", + srcW, mSrcWidth[hdmiLayer], + srcH, mSrcHeight[hdmiLayer], + srcColorFormat, mSrcColorFormat[hdmiLayer], + hdmiLayer); +#endif + if (m_reset(srcW, srcH, dstX, dstY, srcColorFormat, hdmiLayer, num_of_hwc_layer) == false) { + LOGE("%s::m_reset(%d, %d, %d, %d) failed", __func__, srcW, srcH, srcColorFormat, hdmiLayer); + return false; + } + } + + if (srcYAddr == 0) { + unsigned int FB_size = ALIGN(srcW, 16) * ALIGN(srcH, 16) * HDMI_G2D_BUFFER_BPP_SIZE; + + srcYAddr = (unsigned int)mFBaddr + mFBoffset; + srcCbAddr = srcYAddr; + + mFBoffset += FB_size; + if (FB_size < mFBoffset) + mFBoffset = 0; + +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::mFBaddr=0x%08x, srcYAddr=0x%08x, mFBoffset=0x%08x", __func__, mFBaddr, srcYAddr, mFBoffset); +#endif + } + + if (hdmiLayer == HDMI_LAYER_VIDEO) { + if (mSecGscaler.setSrcAddr(srcYAddr, srcCbAddr, srcCrAddr, srcColorFormat) == false) { + LOGE("%s::setSrcAddr(0x%08x, 0x%08x, 0x%08x)", __func__, srcYAddr, srcCbAddr, srcCrAddr); + return false; + } + } else { +#if CHECK_GRAPHIC_LAYER_TIME + nsecs_t start, end; + start = systemTime(); +#endif + + if (num_of_hwc_layer == 0) { /* UI only mode */ + struct v4l2_rect rect; + + if (mG2DUIRotVal == 0 || mG2DUIRotVal == 180) { + hdmi_cal_rect(srcW, srcH, mHdmiDstWidth, mHdmiDstHeight, &rect); + } else { + hdmi_cal_rect(srcH, srcW, mHdmiDstWidth, mHdmiDstHeight, &rect); + } + + rect.left = ALIGN(rect.left, 16); + + if (hdmi_set_graphiclayer(mSubdevMixerFd, mVideodevFd[hdmiLayer], hdmiLayer, + srcColorFormat, + srcW, srcH, + srcYAddr, &mSrcBuffer[hdmiLayer][mSrcIndex[hdmiLayer]], + rect.left, rect.top, + rect.width, rect.height, + mG2DUIRotVal) < 0) + return false; + + } else { // Video Playback + UI Mode + if (hdmi_set_graphiclayer(mSubdevMixerFd, mVideodevFd[hdmiLayer], hdmiLayer, + srcColorFormat, + srcW, srcH, + srcYAddr, &mSrcBuffer[hdmiLayer][mSrcIndex[hdmiLayer]], + dstX, dstY, + mHdmiDstWidth, mHdmiDstHeight, + mG2DUIRotVal) < 0) + return false; + } + +#if CHECK_GRAPHIC_LAYER_TIME + end = systemTime(); + LOGD("[UI] hdmi_gl_set_param[end-start] = %ld ms", long(ns2ms(end)) - long(ns2ms(start))); +#endif + } + + if (mFlagConnected) { + if (mFlagHdmiStart[hdmiLayer] == true) { + if (m_run(hdmiLayer) == false) { + LOGE("%s::m_run(%d) failed", __func__, hdmiLayer); + return false; + } + } + + if (mFlagHdmiStart[hdmiLayer] == false && m_startHdmi(hdmiLayer) == false) { + LOGE("%s::start hdmiLayer(%d) failed", __func__, hdmiLayer); + return false; + } + } + return true; +} + +bool SecHdmi::clear(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer = %d", __func__, hdmiLayer); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + if (mFlagHdmiStart[hdmiLayer] == true && m_stopHdmi(hdmiLayer) == false) { + LOGE("%s::m_stopHdmi: layer[%d] fail \n", __func__, hdmiLayer); + return false; + } + return true; +} + +void SecHdmi::clearGraphicLayer(int hdmiLayer) +{ + mSrcWidth[hdmiLayer] = 0; + mSrcHeight[hdmiLayer] = 0; + mSrcColorFormat[hdmiLayer] = 0; +} + +bool SecHdmi::enableGraphicLayer(int hdmiLayer) +{ + Mutex::Autolock lock(mLock); +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::hdmiLayer(%d)",__func__, hdmiLayer); +#endif + switch (hdmiLayer) { + case HDMI_LAYER_VIDEO: + case HDMI_LAYER_GRAPHIC_0: + case HDMI_LAYER_GRAPHIC_1: + mFlagLayerEnable[hdmiLayer] = true; + if (mFlagConnected == true) + m_startHdmi(hdmiLayer); + break; + default: + return false; + } + return true; +} + +bool SecHdmi::disableGraphicLayer(int hdmiLayer) +{ + Mutex::Autolock lock(mLock); +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::hdmiLayer(%d)",__func__, hdmiLayer); +#endif + switch (hdmiLayer) { + case HDMI_LAYER_VIDEO: + case HDMI_LAYER_GRAPHIC_0: + case HDMI_LAYER_GRAPHIC_1: + if (mFlagConnected == true && mFlagLayerEnable[hdmiLayer]) + if (m_stopHdmi(hdmiLayer) == false ) + LOGE("%s::m_stopHdmi: layer[%d] fail \n", __func__, hdmiLayer); + + mFlagLayerEnable[hdmiLayer] = false; + break; + default: + return false; + } + return true; +} + +bool SecHdmi::setHdmiOutputMode(int hdmiOutputMode, bool forceRun) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::hdmiOutputMode = %d, forceRun = %d", __func__, hdmiOutputMode, forceRun); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (forceRun == false && mHdmiOutputMode == hdmiOutputMode) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdmiOutputMode(%d) \n", __func__, hdmiOutputMode); +#endif + return true; + } + + int newHdmiOutputMode = hdmiOutputMode; + + int v4l2OutputType = hdmi_outputmode_2_v4l2_output_type(hdmiOutputMode); + if (v4l2OutputType < 0) { + LOGD("%s::hdmi_outputmode_2_v4l2_output_type(%d) fail\n", __func__, hdmiOutputMode); + return false; + } + +#if defined(BOARD_USES_EDID) + int newV4l2OutputType = hdmi_check_output_mode(v4l2OutputType); + if (newV4l2OutputType != v4l2OutputType) { + newHdmiOutputMode = hdmi_v4l2_output_type_2_outputmode(newV4l2OutputType); + if (newHdmiOutputMode < 0) { + LOGD("%s::hdmi_v4l2_output_type_2_outputmode(%d) fail\n", __func__, newV4l2OutputType); + return false; + } + + LOGD("%s::calibration mode(%d -> %d)... \n", __func__, hdmiOutputMode, newHdmiOutputMode); + mHdmiInfoChange = true; + } +#endif + + if (mHdmiOutputMode != newHdmiOutputMode) { + mHdmiOutputMode = newHdmiOutputMode; + mHdmiInfoChange = true; + } + + return true; +} + +bool SecHdmi::setHdmiResolution(unsigned int hdmiResolutionValue, bool forceRun) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiResolutionValue = %d, forceRun = %d", __func__, hdmiResolutionValue, forceRun); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (forceRun == false && mHdmiResolutionValue == hdmiResolutionValue) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdmiResolutionValue(%d) \n", __func__, hdmiResolutionValue); +#endif + return true; + } + + unsigned int newHdmiResolutionValue = hdmiResolutionValue; + int w = 0; + int h = 0; + v4l2_std_id std_id; + __u32 preset_id; + +#if defined(BOARD_USES_EDID) + // find perfect resolutions.. + if (hdmi_resolution_2_std_id(newHdmiResolutionValue, &w, &h, &std_id, &preset_id) < 0 || + hdmi_check_resolution(std_id) < 0) { + bool flagFoundIndex = false; + int resolutionValueIndex = m_resolutionValueIndex(newHdmiResolutionValue); + + for (int i = resolutionValueIndex + 1; i < mHdmiSizeOfResolutionValueList; i++) { + if (hdmi_resolution_2_std_id(mHdmiResolutionValueList[i], &w, &h, &std_id, &preset_id) == 0 && + hdmi_check_resolution(std_id) == 0) { + newHdmiResolutionValue = mHdmiResolutionValueList[i]; + flagFoundIndex = true; + break; + } + } + + if (flagFoundIndex == false) { + LOGE("%s::hdmi cannot control this resolution(%d) fail \n", __func__, hdmiResolutionValue); + // Set resolution to 480P + newHdmiResolutionValue = mHdmiResolutionValueList[mHdmiSizeOfResolutionValueList-2]; + } else { + LOGD("%s::HDMI resolutions size is calibrated(%d -> %d)..\n", __func__, hdmiResolutionValue, newHdmiResolutionValue); + } + } else { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::find resolutions(%d) at once\n", __func__, hdmiResolutionValue); +#endif + } +#endif + + if (mHdmiResolutionValue != newHdmiResolutionValue) { + mHdmiResolutionValue = newHdmiResolutionValue; + mHdmiInfoChange = true; + } + + return true; +} + +bool SecHdmi::setHdcpMode(bool hdcpMode, bool forceRun) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (forceRun == false && mHdcpMode == hdcpMode) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdcpMode(%d) \n", __func__, hdcpMode); +#endif + return true; + } + + mHdcpMode = hdcpMode; + mHdmiInfoChange = true; + + return true; +} + +bool SecHdmi::setUIRotation(unsigned int rotVal, unsigned int hwcLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + Mutex::Autolock lock(mLock); + + if (mFlagCreate == false) { + LOGE("%s::Not Yet Created \n", __func__); + return false; + } + + if (rotVal % 90 != 0) { + LOGE("%s::Invalid rotation value(%d)", __func__, rotVal); + return false; + } + + /* G2D rotation */ + if (rotVal != mG2DUIRotVal) { + mG2DUIRotVal = rotVal; + mHdmiInfoChange = true; + } + + /* FIMC rotation */ + if (hwcLayer == 0) { /* Rotate in UI only mode */ + if (rotVal != mUIRotVal) { + mSecGscaler.setRotVal(rotVal); + mUIRotVal = rotVal; + mHdmiInfoChange = true; + } + } else { /* Don't rotate video layer when video is played. */ + rotVal = 0; + if (rotVal != mUIRotVal) { + mSecGscaler.setRotVal(rotVal); + mUIRotVal = rotVal; + mHdmiInfoChange = true; + } + } + + return true; +} + +bool SecHdmi::setDisplaySize(int width, int height) +{ + mDisplayWidth = width; + mDisplayHeight = height; + + return true; +} + +void SecHdmi::setDisplayInfo(int srcW, int srcH, int srcColorFormat, + unsigned int srcYAddr, unsigned int srcCbAddr, + int dstX, int dstY, + int hdmiLayer, + int num_of_hwc_layer) +{ +#ifdef DEBUG_MSG_ENABLE +LOGD("%s [srcW = %d, srcH = %d, srcColorFormat = 0x%x, srcYAddr= 0x%x, srcCbAddr = 0x%x, dstX = %d, dstY = %d, hdmiLayer = %d", + __func__, srcW, srcH, srcColorFormat, srcYAddr, srcCbAddr, dstX, dstY, hdmiLayer); +#endif + + mCurrentsrcW = srcW; + mCurrentsrcH = srcH; + mCurrentsrcColorFormat = srcColorFormat; + mCurrentsrcYAddr = srcYAddr; + mCurrentsrcCbAddr = srcCbAddr, + mCurrentdstX = dstX; + mCurrentdstY = dstY; + mCurrenthdmiLayer = hdmiLayer; + mCurrentNumOfHWCLayer = num_of_hwc_layer; + + return; +} + +bool SecHdmi::m_setupLink(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + int ret; + char node[32]; + char subdevname[32]; + char videodevname[32]; + + struct v4l2_capability v4l2cap; + struct media_entity_desc entity_desc; + + sprintf(node, "%s%d", PFX_NODE_MEDIADEV, 0); + mMediadevFd = open(node, O_RDWR); + if (mMediadevFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto err; + } + + /* open subdev fd */ + sprintf(subdevname, PFX_ENTITY_SUBDEV_MIXER, 0); + for (__u32 id = 0; ; id = entity_desc.id) { + entity_desc.id = id | MEDIA_ENT_ID_FLAG_NEXT; + + if (ioctl(mMediadevFd, MEDIA_IOC_ENUM_ENTITIES, &entity_desc) < 0) { + if (errno == EINVAL) { + LOGD("%s::MEDIA_IOC_ENUM_ENTITIES ended", __func__); + break; + } + LOGE("%s::MEDIA_IOC_ENUM_ENTITIES failed", __func__); + goto err; + } + LOGD("%s::entity_desc.id=%d, .minor=%d .name=%s", __func__, entity_desc.id, entity_desc.v4l.minor, entity_desc.name); + + if (strncmp(entity_desc.name, subdevname, strlen(subdevname)) == 0) + mMixerSubdevEntity = entity_desc.id; + } + + mlink_desc.source.entity = mSecGscaler.getSubdevEntity(); + mlink_desc.source.index = GSCALER_SUBDEV_PAD_SOURCE; + mlink_desc.source.flags = MEDIA_PAD_FL_SOURCE; + + mlink_desc.sink.entity = mMixerSubdevEntity; + mlink_desc.sink.index = MIXER_V_SUBDEV_PAD_SINK; + mlink_desc.sink.flags = MEDIA_PAD_FL_SINK; + mlink_desc.flags = MEDIA_LNK_FL_ENABLED; + +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::mlink_desc.source.entity=%02d, .pad=%d", __func__, mlink_desc.source.entity, mlink_desc.source.index); + LOGD("%s::mlink_desc.sink.entity =%02d, .pad=%d", __func__, mlink_desc.sink.entity, mlink_desc.sink.index); +#endif + + if (ioctl(mMediadevFd, MEDIA_IOC_SETUP_LINK, &mlink_desc) < 0) { + LOGE("%s::MEDIA_IOC_SETUP_LINK [src.entity=%d->sink.entity=%d] failed", __func__, mlink_desc.source.entity, mlink_desc.sink.entity); + goto err; + } + + sprintf(node, "%s%d", PFX_NODE_SUBDEV, 4); // Mixer0 minor=132 /dev/v4l-subdev4 // need to modify //carrotsm + mSubdevMixerFd = open(node, O_RDWR, 0); + if (mSubdevMixerFd < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto err; + } + + if (0 < mMediadevFd) + close(mMediadevFd); + mMediadevFd = -1; + + return true; + +err : + + if (0 < mMediadevFd) + close(mMediadevFd); + + if (0 < mSubdevMixerFd) + close(mSubdevMixerFd); + + mMediadevFd = -1; + mSubdevMixerFd = -1; + + return false; +} + +bool SecHdmi::m_openLayer(int layer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::layer=%d", __func__, layer); +#endif + char node[32]; + + switch (layer) { + case HDMI_LAYER_VIDEO: + mVideodevFd[layer] = mSecGscaler.getVideodevFd(); + + if (0 < mVideodevFd[layer]) { + LOGD("%s::Layer[%d] device already opened", __func__, layer); + return true; + } + + if (mSecGscaler.openVideodevFd() == false) + LOGE("%s::open(%s) failed", __func__, node); + else + mVideodevFd[layer] = mSecGscaler.getVideodevFd(); + + goto open_success; + break; + case HDMI_LAYER_GRAPHIC_0: + sprintf(node, "%s", TVOUT0_DEV_G0); + break; + case HDMI_LAYER_GRAPHIC_1: + sprintf(node, "%s", TVOUT0_DEV_G1); + break; + default: + LOGE("%s::unmatched layer[%d]", __func__, layer); + return false; + break; + } + + mVideodevFd[layer] = open(node, O_RDWR); + if (mVideodevFd[layer] < 0) { + LOGE("%s::open(%s) failed", __func__, node); + goto err; + } + +open_success : + +#ifdef DEBUG_MSG_ENABLE + LOGD("layer=%d, mVideodevFd=%d", layer, mVideodevFd[layer]); +#endif + + if (tvout_std_v4l2_querycap(mVideodevFd[layer], node) < 0 ) { + LOGE("%s::tvout_std_v4l2_querycap failed", __func__); + goto err; + } + + return true; + +err : + + if (0 < mVideodevFd[layer]) + close(mVideodevFd[layer]); + + mVideodevFd[layer] = -1; + + return false; + +} + +bool SecHdmi::m_closeLayer(int layer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::layer=%d", __func__, layer); +#endif + switch (layer) { + case HDMI_LAYER_VIDEO: + mVideodevFd[layer] = mSecGscaler.getVideodevFd(); + + if (mVideodevFd[layer] < 0) { + LOGD("%s::Layer[%d] device already closed", __func__, layer); + return true; + } else { + mSecGscaler.closeVideodevFd(); + mVideodevFd[layer] = mSecGscaler.getVideodevFd(); + } + goto close_success; + break; + case HDMI_LAYER_GRAPHIC_0: + case HDMI_LAYER_GRAPHIC_1: + /* clear buffer */ + if (0 < mVideodevFd[layer]) { + if (tvout_std_v4l2_reqbuf(mVideodevFd[layer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_USERPTR, 0) < 0) { + LOGE("%s::tvout_std_v4l2_reqbuf(buf_num=%d)[graphic layer] failed", __func__, 0); + return -1; + } + } + break; + default: + LOGE("%s::unmatched layer[%d]", __func__, layer); + return false; + break; + } + + if (0 < mVideodevFd[layer]) { + if (close(mVideodevFd[layer]) < 0) { + LOGE("%s::close %d layer failed", __func__, layer); + return false; + } + } + + mVideodevFd[layer] = -1; + +close_success : + + return true; +} + +bool SecHdmi::m_reset(int w, int h, int dstX, int dstY, int colorFormat, int hdmiLayer, int num_of_hwc_layer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::w=%d, h=%d, dstX=%d, dstY=%d, colorFormat=%d, hdmiLayer=%d, num_of_hwc_layer=%d", + __func__, w, h, dstX, dstY, colorFormat, hdmiLayer, num_of_hwc_layer); +#endif + + v4l2_std_id std_id = 0; + + int srcW = w; + int srcH = h; + int dstW = 0; + int dstH = 0; + + if (mFlagHdmiStart[hdmiLayer] == true && m_stopHdmi(hdmiLayer) == false) { + LOGE("%s::m_stopHdmi: layer[%d] failed", __func__, hdmiLayer); + return false; + } + + if (m_closeLayer(hdmiLayer) == false) { + LOGE("%s::m_closeLayer: layer[%d] failed", __func__, hdmiLayer); + return false; + } + + if (m_openLayer(hdmiLayer) == false) { + LOGE("%s::m_closeLayer: layer[%d] failed", __func__, hdmiLayer); + return false; + } + + if (w != mSrcWidth[hdmiLayer] || + h != mSrcHeight[hdmiLayer] || + mHdmiDstWidth != mHdmiResolutionWidth[hdmiLayer] || + mHdmiDstHeight != mHdmiResolutionHeight[hdmiLayer] || + num_of_hwc_layer != mPreviousNumofHwLayer[hdmiLayer] || + colorFormat != mSrcColorFormat[hdmiLayer] || + mHdmiInfoChange == true) { + int preVideoSrcColorFormat = mSrcColorFormat[hdmiLayer]; + int videoSrcColorFormat = colorFormat; + + if (preVideoSrcColorFormat != HAL_PIXEL_FORMAT_YCbCr_420_SP && + preVideoSrcColorFormat != HAL_PIXEL_FORMAT_YCrCb_420_SP && + preVideoSrcColorFormat != HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP && + preVideoSrcColorFormat != HAL_PIXEL_FORMAT_CUSTOM_YCrCb_420_SP && + preVideoSrcColorFormat != HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP_TILED) { + LOGI("%s: Unsupported preVideoSrcColorFormat = 0x%x", __func__, preVideoSrcColorFormat); + preVideoSrcColorFormat = HAL_PIXEL_FORMAT_CUSTOM_YCbCr_420_SP_TILED; + } + + if (hdmiLayer == HDMI_LAYER_VIDEO) { + unsigned int full_wdith = ALIGN(w, 16); + unsigned int full_height = ALIGN(h, 16); + + if (mSecGscaler.setSrcParams(full_wdith, full_height, 0, 0, + (unsigned int*)&w, (unsigned int*)&h, colorFormat, true) == false) { + LOGE("%s::mSecGscaler.setSrcParams(w=%d, h=%d, color=%d) failed", + __func__, w, h, colorFormat); + return false; + } + mGscalerDstColorFormat = V4L2_MBUS_FMT_YUV8_1X24; + + /* calculate destination buffer width and height */ + struct v4l2_rect rect; + + if (mUIRotVal == 0 || mUIRotVal == 180) { + hdmi_cal_rect(srcW, srcH, mHdmiDstWidth, mHdmiDstHeight, &rect); + } else { + hdmi_cal_rect(srcH, srcW, mHdmiDstWidth, mHdmiDstHeight, &rect); + } + + rect.width = ALIGN(rect.width, 16); + + if (mSecGscaler.setDstParams((unsigned int)rect.width, (unsigned int)rect.height, 0, 0, + (unsigned int*)&rect.width, (unsigned int*)&rect.height, mGscalerDstColorFormat, true) == false) { + LOGE("%s::mSecGscaler.setDstParams(w=%d, h=%d, V4L2_MBUS_FMT_YUV8_1X24) failed", + __func__, rect.width, rect.height); + return false; + } + + hdmi_set_videolayer(mSubdevMixerFd, mHdmiDstWidth, mHdmiDstHeight, &rect); + } else { + if (tvout_std_v4l2_s_ctrl(mVideodevFd[hdmiLayer], V4L2_CID_TV_LAYER_BLEND_ENABLE, 1) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ENABLE) failed", __func__, hdmiLayer); + return false; + } + + if (tvout_std_v4l2_s_ctrl(mVideodevFd[hdmiLayer], V4L2_CID_TV_PIXEL_BLEND_ENABLE, 1) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ENABLE) failed", __func__, hdmiLayer); + return false; + } + + if (tvout_std_v4l2_s_ctrl(mVideodevFd[hdmiLayer], V4L2_CID_TV_LAYER_BLEND_ALPHA, 255) < 0) { + LOGE("%s::tvout_std_v4l2_s_ctrl [layer=%d] (V4L2_CID_TV_LAYER_BLEND_ALPHA) failed", __func__, hdmiLayer); + return false; + } + + struct v4l2_rect rect; + int tempSrcW, tempSrcH; + int gr_frame_size = 0; + + if (mG2DUIRotVal == 0 || mG2DUIRotVal == 180) { + tempSrcW = srcW; + tempSrcH = srcH; + } else { + tempSrcW = srcH; + tempSrcH = srcW; + } + + hdmi_cal_rect(tempSrcW, tempSrcH, mHdmiDstWidth, mHdmiDstHeight, &rect); + rect.left = ALIGN(rect.left, 16); + + if (num_of_hwc_layer == 0) { /* UI only mode */ + hdmi_set_g_Params(mSubdevMixerFd, mVideodevFd[hdmiLayer], hdmiLayer, + colorFormat, srcW, srcH, + rect.left, rect.top, rect.width, rect.height); + dstW = rect.width; + dstH = rect.height; + } else { /* Video Playback + UI Mode */ + hdmi_set_g_Params(mSubdevMixerFd, mVideodevFd[hdmiLayer], hdmiLayer, + colorFormat, srcW, srcH, + dstX, dstY, mHdmiDstWidth, mHdmiDstHeight); + dstW = mHdmiDstWidth; + dstH = mHdmiDstHeight; + } + +#if defined(BOARD_USES_HDMI_FIMGAPI) + gr_frame_size = dstW * dstH; +#else + gr_frame_size = srcW * srcH; +#endif + for (int buf_index = 0; buf_index < MAX_BUFFERS_MIXER; buf_index++) { + int v4l2ColorFormat = HAL_PIXEL_FORMAT_2_V4L2_PIX(colorFormat); + switch (v4l2ColorFormat) { + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + mSrcBuffer[hdmiLayer][buf_index].size.s = gr_frame_size << 2; + break; + case V4L2_PIX_FMT_RGB565X: + mSrcBuffer[hdmiLayer][buf_index].size.s = gr_frame_size << 1; + break; + default: + LOGE("%s::invalid color type", __func__); + return false; + break; + } + } + + } + + if (preVideoSrcColorFormat != videoSrcColorFormat) + mHdmiInfoChange = true; + + mSrcWidth[hdmiLayer] = srcW; + mSrcHeight[hdmiLayer] = srcH; + mSrcColorFormat[hdmiLayer] = colorFormat; + + mHdmiResolutionWidth[hdmiLayer] = mHdmiDstWidth; + mHdmiResolutionHeight[hdmiLayer] = mHdmiDstHeight; + mPreviousNumofHwLayer[hdmiLayer] = num_of_hwc_layer; + +#ifdef DEBUG_MSG_ENABLE + LOGD("m_reset saved param(%d, %d, %d, %d, %d, %d, %d)", + srcW, mSrcWidth[hdmiLayer], \ + srcH, mSrcHeight[hdmiLayer], \ + colorFormat,mSrcColorFormat[hdmiLayer], \ + hdmiLayer); +#endif + } + + if (mHdmiInfoChange == true) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("mHdmiInfoChange: %d", mHdmiInfoChange); +#endif + +#if defined(BOARD_USES_CEC) + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (mCECThread->mFlagRunning) + mCECThread->stop(); + } +#endif + + if (m_setHdmiOutputMode(mHdmiOutputMode) == false) { + LOGE("%s::m_setHdmiOutputMode() failed", __func__); + return false; + } + if (mHdmiOutputMode == COMPOSITE_OUTPUT_MODE) { + std_id = composite_std_2_v4l2_std_id(mCompositeStd); + if ((int)std_id < 0) { + LOGE("%s::composite_std_2_v4l2_std_id(%d) failed", __func__, mCompositeStd); + return false; + } + if (m_setCompositeResolution(mCompositeStd) == false) { + LOGE("%s::m_setCompositeRsolution() failed", __func__); + return false; + } + } else if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (m_setHdmiResolution(mHdmiResolutionValue) == false) { + LOGE("%s::m_setHdmiResolution() failed", __func__); + return false; + } + + if (m_setHdcpMode(mHdcpMode) == false) { + LOGE("%s::m_setHdcpMode() failed", __func__); + return false; + } + std_id = mHdmiStdId; + } + + if (mPreviousHdmiPresetId != mHdmiPresetId) { + for (int layer = HDMI_LAYER_BASE + 1; layer < HDMI_LAYER_MAX; layer++) { + if (m_stopHdmi(layer) == false) { + LOGE("%s::m_stopHdmi(%d) failed", __func__, layer); + return false; + } + } + + if (tvout_init(mVideodevFd[HDMI_LAYER_GRAPHIC_0], mHdmiPresetId) < 0) { + LOGE("%s::tvout_init(mHdmiPresetId=%d) failed", __func__, mHdmiPresetId); + return false; + } + mPreviousHdmiPresetId = mHdmiPresetId; + } + + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { +#if defined(BOARD_USES_CEC) + if (!(mCECThread->mFlagRunning)) + mCECThread->start(); +#endif + +/* if (m_setAudioMode(mAudioMode) == false) + LOGE("%s::m_setAudioMode() failed", __func__); +*/ + } + + mHdmiInfoChange = false; + + } + + return true; +} + +bool SecHdmi::m_streamOn(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer = %d", __func__, hdmiLayer); +#endif + + if (mFlagCreate == false) { + LOGE("%s::Not yet created", __func__); + return false; + } + + if (mFlagHdmiStart[hdmiLayer] == true) { + LOGE("%s::[layer=%d] already streamon", __func__, hdmiLayer); + return true; + } + + switch(hdmiLayer) { + case HDMI_LAYER_GRAPHIC_0: + break; + case HDMI_LAYER_GRAPHIC_1: + break; + default : + LOGE("%s::unmathced layer(%d) failed", __func__, hdmiLayer); + return false; + break; + } + + if (tvout_std_v4l2_qbuf(mVideodevFd[hdmiLayer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_USERPTR, + mSrcIndex[hdmiLayer], 1, &mSrcBuffer[hdmiLayer][0]) < 0) { + LOGE("%s::gsc_v4l2_queue(index : %d) (mSrcBufNum : %d) failed", __func__, mSrcIndex[hdmiLayer], 1); + return false; + } + + mSrcIndex[hdmiLayer]++; + if (mSrcIndex[hdmiLayer] == MAX_BUFFERS_MIXER) { + if (tvout_std_v4l2_streamon(mVideodevFd[hdmiLayer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) < 0) { + LOGE("%s::gsc_v4l2_stream_on() failed", __func__); + return false; + } + mFlagHdmiStart[hdmiLayer] = true; + } + + if (mSrcIndex[hdmiLayer] >= MAX_BUFFERS_MIXER) { + mSrcIndex[hdmiLayer] = 0; + } + + return true; +} + +bool SecHdmi::m_run(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer = %d", __func__, hdmiLayer); +#endif + + int buf_index = 0; + + if (mFlagHdmiStart[hdmiLayer] == false || mFlagLayerEnable[hdmiLayer] == false) { + LOGD("%s::HDMI(%d layer) started not yet", __func__, hdmiLayer); + return true; + } + + switch (hdmiLayer) { + case HDMI_LAYER_VIDEO: + if (mSecGscaler.run() == false) { + LOGE("%s::mSecGscaler.draw() failed", __func__); + return false; + } + break; + case HDMI_LAYER_GRAPHIC_0 : + case HDMI_LAYER_GRAPHIC_1 : + if (tvout_std_v4l2_dqbuf(mVideodevFd[hdmiLayer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_USERPTR, &buf_index, 1) < 0) { + LOGE("%s::tvout_std_v4l2_dqbuf(mNumOfBuf : %d, dqIndex=%d) failed", __func__, 1, buf_index); + return false; + } + + if (tvout_std_v4l2_qbuf(mVideodevFd[hdmiLayer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_USERPTR, + mSrcIndex[hdmiLayer], 1, &mSrcBuffer[hdmiLayer][mSrcIndex[hdmiLayer]]) < 0) { + LOGE("%s::tvout_std_v4l2_qbuf(mNumOfBuf : %d,mSrcIndex=%d) failed", __func__, 1, mSrcIndex[hdmiLayer]); + return false; + } + + mSrcIndex[hdmiLayer]++; + if (mSrcIndex[hdmiLayer] >= MAX_BUFFERS_MIXER) { + mSrcIndex[hdmiLayer] = 0; + } + + break; + default : + LOGE("%s::unmathced layer(%d) failed", __func__, hdmiLayer); + return false; + break; + } + + return true; +} + +bool SecHdmi::m_startHdmi(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer = %d", __func__, hdmiLayer); +#endif + + int buf_index = 0; + + if (mFlagHdmiStart[hdmiLayer] == true) { + LOGD("%s::already HDMI(%d layer) started..", __func__, hdmiLayer); + return true; + } + +#ifdef DEBUG_MSG_ENABLE + LOGD("### %s: hdmiLayer(%d) called", __func__, hdmiLayer); +#endif + switch (hdmiLayer) { + case HDMI_LAYER_VIDEO: + if (mSecGscaler.streamOn() == false) { + LOGE("%s::mSecGscaler.streamOn() failed", __func__); + return false; + } + if (mSecGscaler.getFlagSteamOn() == true) + mFlagHdmiStart[hdmiLayer] = true; + break; + case HDMI_LAYER_GRAPHIC_0 : + case HDMI_LAYER_GRAPHIC_1 : + if (m_streamOn(hdmiLayer) == false) { + LOGE("%s::m_streamOn layer(%d) failed", __func__, hdmiLayer); + return false; + } + break; + default : + LOGE("%s::unmathced layer(%d) failed", __func__, hdmiLayer); + return false; + break; + } + return true; +} + +bool SecHdmi::m_stopHdmi(int hdmiLayer) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::hdmiLayer = %d", __func__, hdmiLayer); +#endif + + if (mFlagHdmiStart[hdmiLayer] == false) { + LOGD("%s::already HDMI(%d layer) stopped..", __func__, hdmiLayer); + return true; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s : layer[%d] called", __func__, hdmiLayer); +#endif + + switch (hdmiLayer) { + case HDMI_LAYER_VIDEO: + if (mSecGscaler.streamOff() == false) { + LOGE("%s::mSecGscaler.streamOff() failed", __func__); + return false; + } + mFlagHdmiStart[hdmiLayer] = false; + break; + case HDMI_LAYER_GRAPHIC_1 : + case HDMI_LAYER_GRAPHIC_0 : +#if defined(BOARD_USES_HDMI_FIMGAPI) + cur_g2d_address = 0; +#endif + if (tvout_std_v4l2_streamoff(mVideodevFd[hdmiLayer], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) < 0) { + LOGE("%s::tvout_std_v4l2_streamon layer(%d) failed", __func__, hdmiLayer); + return false; + } + + mSrcIndex[hdmiLayer] = 0; + mFlagHdmiStart[hdmiLayer] = false; + break; + default : + LOGE("%s::unmathced layer(%d) failed", __func__, hdmiLayer); + return false; + break; + } + + return true; +} + +bool SecHdmi::m_setHdmiOutputMode(int hdmiOutputMode) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + if (hdmiOutputMode == mCurrentHdmiOutputMode) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdmiOutputMode(%d) \n", __func__, hdmiOutputMode); +#endif + return true; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + int v4l2OutputType = hdmi_outputmode_2_v4l2_output_type(hdmiOutputMode); + if (v4l2OutputType < 0) { + LOGE("%s::hdmi_outputmode_2_v4l2_output_type(%d) fail\n", __func__, hdmiOutputMode); + return false; + } + + output_type = v4l2OutputType; + + mCurrentHdmiOutputMode = hdmiOutputMode; + + return true; +} + +bool SecHdmi::m_setCompositeResolution(unsigned int compositeStdId) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + int w = 0; + int h = 0; + + if (mHdmiOutputMode != COMPOSITE_OUTPUT_MODE) { + LOGE("%s::not supported output type \n", __func__); + return false; + } + + switch (compositeStdId) { + case COMPOSITE_STD_NTSC_M: + case COMPOSITE_STD_NTSC_443: + w = 704; + h = 480; + break; + case COMPOSITE_STD_PAL_BDGHI: + case COMPOSITE_STD_PAL_M: + case COMPOSITE_STD_PAL_N: + case COMPOSITE_STD_PAL_Nc: + case COMPOSITE_STD_PAL_60: + w = 704; + h = 576; + break; + default: + LOGE("%s::unmathced composite_std(%d)", __func__, compositeStdId); + return false; + } + + t_std_id = composite_std_2_v4l2_std_id(mCompositeStd); + + mHdmiDstWidth = w; + mHdmiDstHeight = h; + + mCurrentHdmiResolutionValue = -1; + return true; +} + +bool SecHdmi::m_setHdmiResolution(unsigned int hdmiResolutionValue) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + if (hdmiResolutionValue == mCurrentHdmiResolutionValue) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdmiResolutionValue(%d) \n", __func__, hdmiResolutionValue); +#endif + return true; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + int w = 0; + int h = 0; + + v4l2_std_id std_id; + if (mHdmiOutputMode >= HDMI_OUTPUT_MODE_YCBCR && + mHdmiOutputMode <= HDMI_OUTPUT_MODE_DVI) { + if (hdmi_resolution_2_std_id(hdmiResolutionValue, &w, &h, &std_id, &mHdmiPresetId) < 0) { + LOGE("%s::hdmi_resolution_2_std_id(%d) fail\n", __func__, hdmiResolutionValue); + return false; + } + mHdmiStdId = std_id; + } else { + LOGE("%s::not supported output type \n", __func__); + return false; + } + + t_std_id = std_id; + + mHdmiDstWidth = w; + mHdmiDstHeight = h; + + mCurrentHdmiResolutionValue = hdmiResolutionValue; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::mHdmiDstWidth = %d, mHdmiDstHeight = %d, mHdmiStdId = 0x%x, hdmiResolutionValue = 0x%x\n", + __func__, + mHdmiDstWidth, + mHdmiDstHeight, + mHdmiStdId, + hdmiResolutionValue); +#endif + + return true; +} + +bool SecHdmi::m_setHdcpMode(bool hdcpMode) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + if (hdcpMode == mCurrentHdcpMode) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same hdcpMode(%d) \n", __func__, hdcpMode); +#endif + + return true; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + if (hdcpMode == true) + g_hdcp_en = 1; + else + g_hdcp_en = 0; + + mCurrentHdcpMode = hdcpMode; + + return true; +} + +bool SecHdmi::m_setAudioMode(int audioMode) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + if (audioMode == mCurrentAudioMode) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::same audioMode(%d) \n", __func__, audioMode); +#endif + return true; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + if (hdmi_check_audio(mVideodevFd[HDMI_LAYER_GRAPHIC_0]) < 0) { + LOGE("%s::hdmi_check_audio() fail \n", __func__); + return false; + } + + mCurrentAudioMode = audioMode; + + return true; +} + +int SecHdmi::m_resolutionValueIndex(unsigned int ResolutionValue) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + + int index = -1; + + for (int i = 0; i < mHdmiSizeOfResolutionValueList; i++) { + if (mHdmiResolutionValueList[i] == ResolutionValue) { + index = i; + break; + } + } + return index; +} + +bool SecHdmi::m_flagHWConnected(void) +{ +#ifdef DEBUG_MSG_ENABLE + LOGD("%s", __func__); +#endif + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("### %s called\n", __func__); +#endif + + bool ret = true; + int hdmiStatus = hdmi_cable_status(); + + if (hdmiStatus <= 0) { +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::hdmi_cable_status() fail \n", __func__); +#endif + ret = false; + } else { + ret = true; + } + + return ret; +} + +}; // namespace android diff --git a/exynos5/hal/libhdmi/SecHdmi/SecHdmiCommon.h b/exynos5/hal/libhdmi/SecHdmi/SecHdmiCommon.h new file mode 100644 index 0000000..5ca9e4e --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecHdmiCommon.h @@ -0,0 +1,118 @@ +/* + * 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. +** +** @author Sangwoo, Park(sw5771.park@samsung.com) +** @date 2010-09-10 +** +*/ + +//#define LOG_NDEBUG 0 +//#define LOG_TAG "libhdmi" +#include + +#include "../libhdmi/SecHdmi/fimd_api.h" + +//#define DEBUG_MSG_ENABLE +//#define DEBUG_HDMI_HW_LEVEL +//#define BOARD_USES_EDID +//#define BOARD_USES_CEC +//#define SUPPORT_G2D_UI_MODE + +#define DEFAULT_FB (0) +#define MAX_MIXER_NUM (1) +#define MAX_BUFFERS_MIXER (2) +#define MAX_PLANES_MIXER (3) + +#define HDMI_GSCALER_BUF_NUM (3) +#define HDMI_FIMC_BUFFER_BPP_SIZE (1.5) //NV12 Tiled is 1.5 bytes, RGB565 is 2, RGB888 is 4, Default is NV12 Tiled +#define HDMI_G2D_BUFFER_BPP_SIZE (4) //NV12 Tiled is 1.5 bytes, RGB565 is 2, RGB888 is 4 +#define SUPPORT_1080P_FIMC_OUT +#define HDMI_MAX_WIDTH (1920) +#define HDMI_MAX_HEIGHT (1080) + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +#if defined(STD_NTSC_M) + #define DEFAULT_OUPUT_MODE (COMPOSITE_OUTPUT_MODE) + #define DEFAULT_HDMI_RESOLUTION_VALUE (1080960) // 1080P_60 + #define DEFAULT_HDMI_STD_ID (V4L2_STD_1080P_60) + #define DEFALULT_DISPLAY_WIDTH (720) + #define DEFALULT_DISPLAY_HEIGHT (480) + #define DEFAULT_COMPOSITE_STD (COMPOSITE_STD_NTSC_M) +#elif (STD_1080P) + #define DEFAULT_OUPUT_MODE (HDMI_OUTPUT_MODE_RGB) + #define DEFAULT_HDMI_RESOLUTION_VALUE (1080960) // 1080P_60 + #define DEFAULT_HDMI_STD_ID (V4L2_STD_1080P_60) + #define DEFALULT_DISPLAY_WIDTH (1920) + #define DEFALULT_DISPLAY_HEIGHT (1080) + #define DEFAULT_COMPOSITE_STD (COMPOSITE_STD_NTSC_M) +#elif defined(STD_720P) + #define DEFAULT_OUPUT_MODE (HDMI_OUTPUT_MODE_YCBCR) + #define DEFAULT_HDMI_RESOLUTION_VALUE (720960) // 720P_60 + #define DEFAULT_HDMI_STD_ID (V4L2_STD_720P_60) + #define DEFALULT_DISPLAY_WIDTH (1280) + #define DEFALULT_DISPLAY_HEIGHT (720) + #define DEFAULT_COMPOSITE_STD (COMPOSITE_STD_NTSC_M) +#elif defined(STD_480P) + #define DEFAULT_OUPUT_MODE (HDMI_OUTPUT_MODE_YCBCR) + #define DEFAULT_HDMI_RESOLUTION_VALUE (4809601) // 480P_60_4_3 + #define DEFAULT_HDMI_STD_ID (V4L2_STD_480P_60_16_9) + #define DEFALULT_DISPLAY_WIDTH (720) + #define DEFALULT_DISPLAY_HEIGHT (480) + #define DEFAULT_COMPOSITE_STD (COMPOSITE_STD_NTSC_M) +#else + #define DEFAULT_OUPUT_MODE (HDMI_OUTPUT_MODE_YCBCR) + #define DEFAULT_HDMI_RESOLUTION_VALUE (4809602) // 480P_60_4_3 + #define DEFAULT_HDMI_STD_ID (V4L2_STD_480P_60_4_3) + #define DEFALULT_DISPLAY_WIDTH (720) + #define DEFALULT_DISPLAY_HEIGHT (480) + #define DEFAULT_COMPOSITE_STD (COMPOSITE_STD_NTSC_M) +#endif + +enum hdp_cable_status { + HPD_CABLE_OUT = 0, // HPD_CABLE_OUT indicates HDMI cable out. + HPD_CABLE_IN // HPD_CABLE_IN indicates HDMI cable in. +}; + +enum state { + OFF = 0, + ON = 1, + NOT_SUPPORT = 2, +}; + +enum tv_mode { + HDMI_OUTPUT_MODE_YCBCR = 0, + HDMI_OUTPUT_MODE_RGB = 1, + HDMI_OUTPUT_MODE_DVI = 2, + COMPOSITE_OUTPUT_MODE = 3 +}; + +enum composite_std { + COMPOSITE_STD_NTSC_M = 0, + COMPOSITE_STD_PAL_BDGHI = 1, + COMPOSITE_STD_PAL_M = 2, + COMPOSITE_STD_PAL_N = 3, + COMPOSITE_STD_PAL_Nc = 4, + COMPOSITE_STD_PAL_60 = 5, + COMPOSITE_STD_NTSC_443 = 6 +}; + +enum hdmi_layer { + HDMI_LAYER_BASE = 0, + HDMI_LAYER_VIDEO, + HDMI_LAYER_GRAPHIC_0, + HDMI_LAYER_GRAPHIC_1, + HDMI_LAYER_MAX, +}; diff --git a/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.cpp b/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.cpp new file mode 100644 index 0000000..ac4717b --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.cpp @@ -0,0 +1,1350 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +//#define LOG_TAG "libhdmi" +//#define DEBUG_HDMI_HW_LEVEL +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_utils_v4l2.h" +#include "s5p_tvout_v4l2.h" + +#include "videodev2.h" + +#if defined(BOARD_USES_HDMI_FIMGAPI) +#include "sec_g2d_4x.h" +#include "FimgApi.h" +#endif + +#include "audio.h" +#include "video.h" +#include "../libhdmi/libsForhdmi/libedid/libedid.h" +#include "../libhdmi/libsForhdmi/libcec/libcec.h" + +#include "SecGscaler.h" +#include "SecHdmiCommon.h" +#include "SecHdmiV4L2Utils.h" + +namespace android { + +unsigned int output_type = V4L2_OUTPUT_TYPE_DIGITAL; +v4l2_std_id t_std_id = V4L2_STD_1080P_30; +int g_hpd_state = HPD_CABLE_OUT; +unsigned int g_hdcp_en = 0; + +#if defined(BOARD_USES_HDMI_FIMGAPI) +unsigned int g2d_reserved_memory0 = 0; +unsigned int g2d_reserved_memory1 = 0; +unsigned int g2d_reserved_memory_size = 0; +unsigned int cur_g2d_address = 0; +#endif + +void display_menu(void) +{ + struct HDMIVideoParameter video; + struct HDMIAudioParameter audio; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + + audio.formatCode = LPCM_FORMAT; + audio.outPacket = HDMI_ASP; + audio.channelNum = CH_2; + audio.sampleFreq = SF_44KHZ; + + LOGI("=============== HDMI Audio =============\n"); + + if (EDIDAudioModeSupport(&audio)) + LOGI("= 2CH_PCM 44100Hz audio supported =\n"); + + LOGI("========= HDMI Mode & Color Space =======\n"); + + video.mode = HDMI; + if (EDIDHDMIModeSupport(&video)) { + video.colorSpace = HDMI_CS_YCBCR444; + if (EDIDColorSpaceSupport(&video)) + LOGI("= 1. HDMI(YCbCr) =\n"); + + video.colorSpace = HDMI_CS_RGB; + if (EDIDColorSpaceSupport(&video)) + LOGI("= 2. HDMI(RGB) =\n"); + } else { + video.mode = DVI; + if (EDIDHDMIModeSupport(&video)) + LOGI("= 3. DVI =\n"); + } + + LOGI("=========== HDMI Rseolution ========\n"); + + /* 480P */ + video.resolution = v720x480p_60Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_16_9; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 4. 480P_60_16_9 (0x04000000) =\n"); + + video.resolution = v640x480p_60Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_4_3; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 5. 480P_60_4_3 (0x05000000) =\n"); + + /* 576P */ + video.resolution = v720x576p_50Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_16_9; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 6. 576P_50_16_9 (0x06000000) =\n"); + + video.pixelAspectRatio = HDMI_PIXEL_RATIO_4_3; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 7. 576P_50_4_3 (0x07000000) =\n"); + + /* 720P 60 */ + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 8. 720P_60 (0x08000000) =\n"); + + /* 720P_50 */ + video.resolution = v1280x720p_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 9. 720P_50 (0x09000000) =\n"); + + /* 1080P_60 */ + video.resolution = v1920x1080p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= a. 1080P_60 (0x0a000000) =\n"); + + /* 1080P_50 */ + video.resolution = v1920x1080p_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= b. 1080P_50 (0x0b000000) =\n"); + + /* 1080I_60 */ + video.resolution = v1920x1080i_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= c. 1080I_60 (0x0c000000) =\n"); + + /* 1080I_50 */ + video.resolution = v1920x1080i_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= d. 1080I_50 (0x0d000000) =\n"); + + /* 1080P_30 */ + video.resolution = v1920x1080p_30Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= e. 1080P_30 (0x12000000) =\n"); + + LOGI("=========== HDMI 3D Format ========\n"); + + /* 720P_60_SBS_HALF */ + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_3D_SSH_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= f. 720P_60_SBS_HALF (0x13000000) =\n"); + + /* 720P_59_SBS_HALF */ + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_3D_SSH_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 10. 720P_59_SBS_HALF (0x14000000) =\n"); + + /* 720P_50_TB */ + video.resolution = v1280x720p_50Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 11. 720P_50_TB (0x15000000) =\n"); + + /* 1080P_24_TB */ + video.resolution = v1920x1080p_24Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 12. 1080P_24_TB (0x16000000) =\n"); + + /* 1080P_23_TB */ + video.resolution = v1920x1080p_24Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + if (EDIDVideoResolutionSupport(&video)) + LOGI("= 13. 1080P_24_TB (0x17000000) =\n"); + LOGI("=========================================\n"); +} + +int tvout_init(int fd_tvout, __u32 preset_id) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::preset_id = 0x%x", __func__, preset_id); +#endif + + int ret; + struct v4l2_output output; + struct v4l2_dv_preset preset; + + unsigned int matched = 0, i = 0; + int output_index; + + if (fd_tvout <= 0) { + fd_tvout = open(TVOUT0_DEV_G0, O_RDWR); + if (fd_tvout < 0) { + LOGE("%s::fd_tvout open failed", __func__); + return -1; + } + } +/* + if (output_type >= V4L2_OUTPUT_TYPE_DIGITAL && + output_type <= V4L2_OUTPUT_TYPE_DVI) + if (ioctl(fd_tvout, VIDIOC_HDCP_ENABLE, g_hdcp_en) < 0) + LOGE("%s::VIDIOC_HDCP_ENABLE failed %d", __func__, errno); +*/ + i = 0; + + do { + output.index = i; + ret = tvout_v4l2_enum_output(fd_tvout, &output); + LOGV("%s::output_type=%d output.index=%d .name=%s", __func__, output_type, output.index, output.name); + if (output.type == output_type) { + matched = 1; + break; + } + i++; + } while (ret >=0); +/* + if (!matched) { + LOGE("%s::no matched output type [type=0x%08x]", __func__, output_type); + return -1; + } + + tvout_v4l2_s_output(fd_tvout, output.index); + output_index = 0; + tvout_v4l2_g_output(fd_tvout, &output_index); +*/ + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::input preset_id=0x%08x", __func__, preset_id); +#endif + + if (output.capabilities & V4L2_OUT_CAP_PRESETS) { + tvout_std_v4l2_enum_dv_presets(fd_tvout); + preset.preset = preset_id; + if (tvout_std_v4l2_s_dv_preset(fd_tvout, &preset) < 0 ) { + LOGE("%s::tvout_std_v4l2_s_dv_preset failed", __func__); + return -1; + } + } + + return fd_tvout; +} + +int tvout_deinit() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + + return 0; +} + +int tvout_std_v4l2_querycap(int fd, char *node) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_capability v4l2cap; + + if (ioctl(fd, VIDIOC_QUERYCAP, &v4l2cap) < 0) { + LOGE("%s::VIDIOC_QUERYCAP failed", __func__); + return -1; + } + + if (!(v4l2cap.capabilities & V4L2_CAP_STREAMING)) { + LOGE("%s::%s is not support streaming", __func__, node); + return -1; + } + + if (!(v4l2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) { + LOGE("%s::%s is not support video output mplane", __func__, node); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_s_fmt(int fd, enum v4l2_buf_type type, enum v4l2_field field, int w, int h, int colorformat, int num_planes) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + + struct v4l2_format fmt; + + fmt.type = type; + if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_G_FMT failed", __func__); + return -1; + } + + switch (fmt.type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt.fmt.pix.width = w; + fmt.fmt.pix.height = h; + fmt.fmt.pix.pixelformat = colorformat; + fmt.fmt.pix.field = field; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt.fmt.pix_mp.width = w; + fmt.fmt.pix_mp.height = h; + fmt.fmt.pix_mp.pixelformat = colorformat; + fmt.fmt.pix_mp.field = field; + fmt.fmt.pix_mp.num_planes = num_planes; + break; + default: + LOGE("%s::invalid buffer type", __func__); + return -1; + break; + } + + if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_S_FMT failed", __func__); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_s_crop(int fd, enum v4l2_buf_type type, enum v4l2_field, int x, int y, int w, int h) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_crop crop; + + crop.type = type; + crop.c.left = x; + crop.c.top = y; + crop.c.width = w; + crop.c.height = h; + + if (ioctl(fd, VIDIOC_S_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_S_CROP (x=%d, y=%d, w=%d, h=%d) failed", + __func__, x, y, w, h); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_s_ctrl(int fd, int id, int value) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_control vc; + + vc.id = id; + vc.value = value; + + if (ioctl(fd, VIDIOC_S_CTRL, &vc) < 0) { + LOGE("%s::VIDIOC_S_CTRL (id=%d,value=%d) failed", __func__, id, value); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_reqbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int num_bufs) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_requestbuffers reqbuf; + + reqbuf.type = type; + reqbuf.memory = memory; + reqbuf.count = num_bufs; + + if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) { + LOGE("%s::VIDIOC_REQBUFS failed", __func__); + return -1; + } + + if (reqbuf.count < num_bufs) { + LOGE("%s::VIDIOC_REQBUFS failed ((reqbuf.count(%d) < num_bufs(%d))", + __func__, reqbuf.count, num_bufs); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_querybuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int buf_index, unsigned int num_planes, SecBuffer *secBuf) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_MIXER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_MIXER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + if (MAX_BUFFERS_MIXER <= buf_index || MAX_PLANES_MIXER <= num_planes) { + LOGE("%s::exceed MAX! : buf_index=%d, num_plane=%d", __func__, buf_index, num_planes); + return -1; + } + + buf.type = type; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buf_index; + buf.length = num_planes; + buf.m.planes = planes; + + if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { + LOGE("%s::VIDIOC_QUERYBUF failed, plane_cnt=%d", __func__, buf.length); + return -1; + } + + for (unsigned int i = 0; i < num_planes; i++) { + if ((secBuf->virt.extP[i] = (char *)mmap(0, buf.m.planes[i].length, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.planes[i].m.mem_offset)) < 0) { + LOGE("%s::mmap failed", __func__); + LOGE("%s::Offset = 0x%x", __func__, buf.m.planes[i].m.mem_offset); + LOGE("%s::Legnth = %d" , __func__, buf.m.planes[i].length); + LOGE("%s::vaddr[%d][%d] = 0x%x", __func__, buf_index, i, (unsigned int)secBuf->virt.extP[i]); + return -1; + } + secBuf->size.extS[i] = buf.m.planes[i].length; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::vaddr[bufindex=%d][planeidx=%d] = 0x%x", __func__, buf_index, i, (unsigned int)secBuf->virt.extP[i]); + LOGD("%s::Legnth = %d" , __func__, buf.m.planes[i].length); +#endif + } + + return 0; +} + +int tvout_std_v4l2_qbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, int buf_index, int num_planes, SecBuffer *secBuf) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_MIXER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_MIXER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + buf.type = type; + buf.memory = memory; + buf.length = num_planes; + buf.index = buf_index; + buf.m.planes = planes; + + for (unsigned int i = 0; i < buf.length; i++) { + buf.m.planes[i].m.userptr = (unsigned long)secBuf->virt.extP[i]; + buf.m.planes[i].length = secBuf->size.extS[i]; + buf.m.planes[i].bytesused = buf.m.planes[i].length; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::buf.index=%d", __func__, buf.index); + LOGD("%s::buf.m.planes[%d].m.userptr=0x%08x", __func__, i, (unsigned int)buf.m.planes[i].m.userptr); + LOGD("%s::buf.m.planes[%d].length =0x%08x", __func__, i, buf.m.planes[i].length); + LOGD("%s::buf.m.planes[%d].bytesused=0x%08x", __func__, i, buf.m.planes[i].bytesused); +#endif + } + + if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { + LOGE("%s::VIDIOC_QBUF failed", __func__); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_dqbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, int *buf_index, int num_planes) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_buffer buf; + struct v4l2_plane planes[MAX_PLANES_MIXER]; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + + for (int i = 0; i < MAX_PLANES_GSCALER; i++) + memset(&planes[i], 0, sizeof(struct v4l2_plane)); + + + buf.type = type; + buf.memory = memory; + buf.length = num_planes; + buf.m.planes = planes; + + if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { + LOGE("%s::VIDIOC_DQBUF failed", __func__); + return -1; + } + *buf_index = buf.index; + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::buf.index=%d", __func__, buf.index); +#endif + + return 0; +} + +int tvout_std_v4l2_streamon(int fd, enum v4l2_buf_type type) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { + LOGE("%s::VIDIOC_STREAMON failed", __func__); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_streamoff(int fd, enum v4l2_buf_type type) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) { + LOGE("%s::VIDIOC_STREAMOFF failed", __func__); + return -1; + } + + return 0; +} + +int tvout_v4l2_enum_output(int fd, struct v4l2_output *output) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + int ret = -1 ; + ret = ioctl(fd, VIDIOC_ENUMOUTPUT, output); + + if (ret < 0) { + if (errno == EINVAL) + return -1; + LOGE("%s::VIDIOC_ENUMOUTPUT", __func__); + return -1; + } + LOGD("%s::index=%d, type=0x%08x, name=%s", + __func__, output->index, output->type, output->name); + + return ret; +} + +int tvout_v4l2_s_output(int fd, int index) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_S_OUTPUT, &index) < 0) { + LOGE("%s::VIDIOC_S_OUTPUT failed", __func__); + return -1; + } + + return 0; +} + +int tvout_v4l2_g_output(int fd, int *index) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_G_OUTPUT, index) < 0) { + LOGE("%s::VIDIOC_G_OUTPUT failed", __func__); + return -1; + } + + return 0; +} + +int tvout_std_v4l2_enum_dv_presets(int fd) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_dv_enum_preset enum_preset; + int ret = -1; + + for (int index = 0; ; index++) { + enum_preset.index = index; + ret = ioctl(fd, VIDIOC_ENUM_DV_PRESETS, &enum_preset); + + if (ret < 0) { + if (errno == EINVAL) + break; + LOGE("%s::VIDIOC_ENUM_DV_PRESETS", __func__); + return -1; + } + LOGD("%s::index=%d, preset=0x%08x, name=%s, w=%d, h=%d", + __func__, enum_preset.index, enum_preset.preset, enum_preset.name, enum_preset.width, enum_preset.height); + } + + return 0; +} + +int tvout_std_v4l2_s_dv_preset(int fd, struct v4l2_dv_preset *preset) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (ioctl(fd, VIDIOC_S_DV_PRESET, preset) < 0) { + LOGE("%s::VIDIOC_S_DV_PRESET failed preset_id=%d", __func__, preset->preset); + return -1; + } +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::preset_id=%d", __func__, preset->preset); +#endif + return 0; +} + +int tvout_std_subdev_s_fmt(int fd, unsigned int pad, int w, int h, enum v4l2_mbus_pixelcode code) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = w; + fmt.format.height = h; + fmt.format.code = code; + + if (ioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_FMT", __func__); + return -1; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::format w=%d, h=%d", __func__, fmt.format.width, fmt.format.height); +#endif + return 0; +} +int tvout_std_subdev_s_crop(int fd, unsigned int pad, int x, int y, int w, int h) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::pad=%d, crop x=%d, y=%d, w=%d, h=%d", __func__, pad, x, y, w, h); +#endif + + struct v4l2_subdev_crop crop; + + crop.pad = pad; + crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.left = x; + crop.rect.top = y; + crop.rect.width = w; + crop.rect.height = h; + + if (ioctl(fd, VIDIOC_SUBDEV_S_CROP, &crop) < 0) { + LOGE("%s::VIDIOC_SUBDEV_S_CROP", __func__); + return -1; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s::pad=%d, crop x=%d, y=%d, w=%d, h=%d", __func__, pad, crop.rect.left, crop.rect.top, crop.rect.width, crop.rect.height); +#endif + + return 0; +} + +#define ROUND_UP(value, boundary) ((((uint32_t)(value)) + \ + (((uint32_t) boundary)-1)) & \ + (~(((uint32_t) boundary)-1))) + +void hdmi_cal_rect(int src_w, int src_h, int dst_w, int dst_h, struct v4l2_rect *dst_rect) +{ + if (dst_w * src_h <= dst_h * src_w) { + dst_rect->left = 0; + dst_rect->top = (dst_h - ((dst_w * src_h) / src_w)) >> 1; + dst_rect->width = dst_w; + dst_rect->height = ((dst_w * src_h) / src_w); + } else { + dst_rect->left = (dst_w - ((dst_h * src_w) / src_h)) >> 1; + dst_rect->top = 0; + dst_rect->width = ((dst_h * src_w) / src_h); + dst_rect->height = dst_h; + } +} + +int hdmi_set_videolayer(int fd, int hdmiW, int hdmiH, struct v4l2_rect * rect) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + if (tvout_std_subdev_s_fmt(fd, MIXER_V_SUBDEV_PAD_SINK, hdmiW, hdmiH, V4L2_MBUS_FMT_YUV8_1X24) < 0) { + LOGE("%s::tvout_std_subdev_s_fmt(PAD=%d)[videolayer] failed", __func__, MIXER_V_SUBDEV_PAD_SINK); + return -1; + } + + if (tvout_std_subdev_s_crop(fd, MIXER_V_SUBDEV_PAD_SINK, 0, 0, rect->width, rect->height) < 0) { + LOGE("%s::tvout_std_subdev_s_crop(PAD=%d)[videolayer] failed", __func__, MIXER_V_SUBDEV_PAD_SINK); + return -1; + } + + if (tvout_std_subdev_s_crop(fd, MIXER_V_SUBDEV_PAD_SOURCE, rect->left, rect->top, rect->width, rect->height) < 0) { + LOGE("%s::tvout_std_subdev_s_crop(PAD=%d)[videolayer] failed", __func__, MIXER_V_SUBDEV_PAD_SOURCE); + return -1; + } + return 0; +} + +int hdmi_set_graphiclayer(int fd_subdev, int fd_videodev,int layer, + int srcColorFormat, + int src_w, int src_h, + unsigned int src_address, SecBuffer * dstBuffer, + int dst_x, int dst_y, int dst_w, int dst_h, + int rotVal) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif +#if defined(BOARD_USES_HDMI_FIMGAPI) + int dst_color_format; + int dst_bpp; + unsigned char *dst_addr; + fimg2d_blit BlitParam; + rotation g2d_rotation; + + fimg2d_addr srcAddr; + fimg2d_image srcImage; + fimg2d_rect srcRect; + + fimg2d_addr dstAddr; + fimg2d_image dstImage; + fimg2d_rect dstRect; + + fimg2d_clip dstClip; + fimg2d_scale Scaling; + + switch (t_std_id) { + case V4L2_STD_1080P_60: + case V4L2_STD_1080P_30: + case V4L2_STD_1080I_60: + case V4L2_STD_TVOUT_720P_60_SBS_HALF: + case V4L2_STD_TVOUT_720P_59_SBS_HALF: + case V4L2_STD_TVOUT_1080P_24_TB: + case V4L2_STD_TVOUT_1080P_23_TB: + dst_color_format = CF_ARGB_8888; + dst_bpp = 4; + break; + case V4L2_STD_480P_60_16_9: + case V4L2_STD_576P_50_16_9: + case V4L2_STD_720P_60: + case V4L2_STD_TVOUT_720P_50_TB: + default: + dst_color_format = CF_ARGB_4444; + dst_bpp = 2; + break; + } + + static unsigned int prev_src_addr = 0; + + if ((cur_g2d_address == 0) || (src_address != prev_src_addr)) { + if ((cur_g2d_address == 0) || (cur_g2d_address == g2d_reserved_memory1)) + dst_addr = (unsigned char *)g2d_reserved_memory0; + else + dst_addr = (unsigned char *)g2d_reserved_memory1; + + cur_g2d_address = (unsigned int)dst_addr; + prev_src_addr = src_address; + + srcAddr = {(addr_space)ADDR_USER, (unsigned long)src_address, src_w*src_h*4, 1, 0}; + srcImage = {srcAddr, srcAddr, src_w, src_h, src_w*4, AX_RGB, CF_ARGB_8888}; + srcRect = {0, 0, src_w, src_h}; + + dstAddr = {(addr_space)ADDR_USER, (unsigned long)dst_addr, dst_w*dst_h*dst_bpp, 1, 0}; + dstImage = {dstAddr, dstAddr, dst_w, dst_h, dst_w*dst_bpp, AX_RGB, (color_format)dst_color_format}; + dstRect = {0, 0, dst_w, dst_h}; + dstClip = {0, 0, 0, dst_w, dst_h}; + + if (rotVal == 0 || rotVal == 180) + Scaling = {SCALING_BILINEAR, SCALING_PIXELS, 0, 0, src_w, src_h, dst_w, dst_h}; + else + Scaling = {SCALING_BILINEAR, SCALING_PIXELS, 0, 0, src_w, src_h, dst_h, dst_w}; + + switch (rotVal) { + case 0: + g2d_rotation = ORIGIN; + break; + case 90: + g2d_rotation = ROT_90; + break; + case 180: + g2d_rotation = ROT_180; + break; + case 270: + g2d_rotation = ROT_270; + break; + default: + LOGE("%s::invalid rotVal(%d) : failed", __func__, rotVal); + return -1; + break; + } + + BlitParam = {BLIT_OP_SRC, NON_PREMULTIPLIED, 0xff, 0, g2d_rotation, &Scaling, 0, 0, &dstClip, 0, &srcImage, &dstImage, NULL, &srcRect, &dstRect, NULL, 0}; + + if (stretchFimgApi(&BlitParam) < 0) { + LOGE("%s::stretchFimgApi() failed", __func__); + return -1; + } + + dstBuffer->virt.p = (char *)dst_addr; + } +#else + dstBuffer->virt.p = (char *)src_address; +#endif + + return 0; +} + +int hdmi_set_g_Params(int fd_subdev, int fd_videodev, int layer, + int srcColorFormat, + int src_w, int src_h, + int dst_x, int dst_y, int dst_w, int dst_h) +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + struct v4l2_rect rect; + int src_pad = 0; + int sink_pad = 0; + int v4l2ColorFormat = HAL_PIXEL_FORMAT_2_V4L2_PIX(srcColorFormat); + + switch (layer) { + case HDMI_LAYER_GRAPHIC_0: + sink_pad = MIXER_G0_SUBDEV_PAD_SINK; + src_pad = MIXER_G0_SUBDEV_PAD_SOURCE; + break; + case HDMI_LAYER_GRAPHIC_1: + sink_pad = MIXER_G1_SUBDEV_PAD_SINK; + src_pad = MIXER_G1_SUBDEV_PAD_SOURCE; + break; + default: + LOGE("%s::invalid layer(%d)", __func__, layer); + break; + }; + + rect.left = dst_x; + rect.top = dst_y; + +#if defined(BOARD_USES_HDMI_FIMGAPI) + rect.width = dst_w; + rect.height = dst_h; +#else + rect.width = src_w; + rect.height = src_h; +#endif + + /* set sub device for mixer graphic layer input */ + if (tvout_std_subdev_s_fmt(fd_subdev, sink_pad, rect.width, rect.height, V4L2_MBUS_FMT_XRGB8888_4X8_LE) < 0) { + LOGE("%s::tvout_std_subdev_s_fmt(PAD=%d)[graphic layer] failed", __func__, sink_pad); + return -1; + } + + if (tvout_std_subdev_s_crop(fd_subdev, sink_pad, 0, 0, rect.width, rect.height) < 0) { + LOGE("%s::tvout_std_subdev_s_crop(PAD=%d)[graphic layer] failed", __func__, sink_pad); + return -1; + } + + if (tvout_std_subdev_s_crop(fd_subdev, src_pad, rect.left, rect.top, rect.width, rect.height) < 0) { + LOGE("%s::tvout_std_subdev_s_crop(PAD=%d)[graphic layer] failed", __func__, src_pad); + return -1; + } + + /* set format for mixer graphic layer input device*/ + if (tvout_std_v4l2_s_fmt(fd_videodev, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_FIELD_ANY, rect.width, rect.height, v4l2ColorFormat, 1) < 0) { + LOGE("%s::tvout_std_v4l2_s_fmt()[graphic layer] failed", __func__); + return -1; + } + + if (tvout_std_v4l2_s_crop(fd_videodev, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_FIELD_ANY, rect.left, rect.top, rect.width, rect.height) < 0) { + LOGE("%s::tvout_std_v4l2_s_crop()[graphic layer] failed", __func__); + return -1; + } + + /* request buffer for mixer graphic layer input device */ + if (tvout_std_v4l2_reqbuf(fd_videodev, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_MEMORY_USERPTR, 2) < 0) { + LOGE("%s::tvout_std_v4l2_reqbuf(buf_num=%d)[graphic layer] failed", __func__, 2); + return -1; + } + + return 0; +} + +int hdmi_cable_status() +{ +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("%s", __func__); +#endif + + int cable_status = 0; + int fd = 0; + struct v4l2_control ctrl; + + fd = open(TVOUT0_DEV_G0, O_RDWR); + if (fd <= 0) { + LOGE("%s: graphic layer 0 drv open failed", __func__); + return -1; + } + + ctrl.id = V4L2_CID_TV_HPD_STATUS; + + if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) { + LOGE("Get HPD_STATUS fail"); + cable_status = -1; + } else { + cable_status = ctrl.value; + } + +#ifdef DEBUG_HDMI_HW_LEVEL + LOGD("HPD_STATUS = %d", cable_status); +#endif + + close(fd); + + return cable_status; +} + +int hdmi_outputmode_2_v4l2_output_type(int output_mode) +{ + int v4l2_output_type = -1; + + switch (output_mode) { + case HDMI_OUTPUT_MODE_YCBCR: + v4l2_output_type = V4L2_OUTPUT_TYPE_DIGITAL; + break; + case HDMI_OUTPUT_MODE_RGB: + v4l2_output_type = V4L2_OUTPUT_TYPE_HDMI_RGB; + break; + case HDMI_OUTPUT_MODE_DVI: + v4l2_output_type = V4L2_OUTPUT_TYPE_DVI; + break; + case COMPOSITE_OUTPUT_MODE: + v4l2_output_type = V4L2_OUTPUT_TYPE_COMPOSITE; + break; + default: + LOGE("%s::unmathced HDMI_mode(%d)", __func__, output_mode); + v4l2_output_type = -1; + break; + } + + return v4l2_output_type; +} + +int hdmi_v4l2_output_type_2_outputmode(int v4l2_output_type) +{ + int outputMode = -1; + + switch (v4l2_output_type) { + case V4L2_OUTPUT_TYPE_DIGITAL: + outputMode = HDMI_OUTPUT_MODE_YCBCR; + break; + case V4L2_OUTPUT_TYPE_HDMI_RGB: + outputMode = HDMI_OUTPUT_MODE_RGB; + break; + case V4L2_OUTPUT_TYPE_DVI: + outputMode = HDMI_OUTPUT_MODE_DVI; + break; + case V4L2_OUTPUT_TYPE_COMPOSITE: + outputMode = COMPOSITE_OUTPUT_MODE; + break; + default: + LOGE("%s::unmathced v4l2_output_type(%d)", __func__, v4l2_output_type); + outputMode = -1; + break; + } + + return outputMode; +} + +int composite_std_2_v4l2_std_id(int std) +{ + int std_id = -1; + + switch (std) { + case COMPOSITE_STD_NTSC_M: + std_id = V4L2_STD_NTSC_M; + break; + case COMPOSITE_STD_NTSC_443: + std_id = V4L2_STD_NTSC_443; + break; + case COMPOSITE_STD_PAL_BDGHI: + std_id = V4L2_STD_PAL_BDGHI; + break; + case COMPOSITE_STD_PAL_M: + std_id = V4L2_STD_PAL_M; + break; + case COMPOSITE_STD_PAL_N: + std_id = V4L2_STD_PAL_N; + break; + case COMPOSITE_STD_PAL_Nc: + std_id = V4L2_STD_PAL_Nc; + break; + case COMPOSITE_STD_PAL_60: + std_id = V4L2_STD_PAL_60; + break; + default: + LOGE("%s::unmathced composite_std(%d)", __func__, std); + break; + } + + return std_id; +} + +int hdmi_check_output_mode(int v4l2_output_type) +{ + struct HDMIVideoParameter video; + struct HDMIAudioParameter audio; + int calbirate_v4l2_mode = v4l2_output_type; + + audio.formatCode = LPCM_FORMAT; + audio.outPacket = HDMI_ASP; + audio.channelNum = CH_2; + audio.sampleFreq = SF_44KHZ; + + switch (v4l2_output_type) { + case V4L2_OUTPUT_TYPE_DIGITAL : + video.mode = HDMI; + if (!EDIDHDMIModeSupport(&video)) { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_DVI; + LOGI("Change mode into DVI\n"); + break; + } + + video.colorSpace = HDMI_CS_YCBCR444; + if (!EDIDColorSpaceSupport(&video)) { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_HDMI_RGB; + LOGI("Change mode into HDMI_RGB\n"); + } + break; + + case V4L2_OUTPUT_TYPE_HDMI_RGB: + video.mode = HDMI; + if (!EDIDHDMIModeSupport(&video)) { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_DVI; + LOGI("Change mode into DVI\n"); + break; + } + + video.colorSpace = HDMI_CS_RGB; + if (!EDIDColorSpaceSupport(&video)) { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_DIGITAL; + LOGI("Change mode into HDMI_YCBCR\n"); + } + break; + + case V4L2_OUTPUT_TYPE_DVI: + video.mode = DVI; + if (!EDIDHDMIModeSupport(&video)) { + video.colorSpace = HDMI_CS_YCBCR444; + if (!EDIDColorSpaceSupport(&video)) { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_HDMI_RGB; + LOGI("Change mode into HDMI_RGB\n"); + } else { + calbirate_v4l2_mode = V4L2_OUTPUT_TYPE_DIGITAL; + LOGI("Change mode into HDMI_YCBCR\n"); + } + break; + } + + break; + + default: + break; + } + return calbirate_v4l2_mode; +} + +int hdmi_check_resolution(v4l2_std_id std_id) +{ + struct HDMIVideoParameter video; + struct HDMIAudioParameter audio; + + switch (std_id) { + case V4L2_STD_480P_60_16_9: + video.resolution = v720x480p_60Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_16_9; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_480P_60_4_3: + video.resolution = v640x480p_60Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_4_3; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_576P_50_16_9: + video.resolution = v720x576p_50Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_16_9; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_576P_50_4_3: + video.resolution = v720x576p_50Hz; + video.pixelAspectRatio = HDMI_PIXEL_RATIO_4_3; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_720P_60: + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_720P_50: + video.resolution = v1280x720p_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080P_60: + video.resolution = v1920x1080p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080P_50: + video.resolution = v1920x1080p_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080I_60: + video.resolution = v1920x1080i_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080I_50: + video.resolution = v1920x1080i_50Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_480P_59: + video.resolution = v720x480p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_720P_59: + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080I_59: + video.resolution = v1920x1080i_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080P_59: + video.resolution = v1920x1080p_60Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_1080P_30: + video.resolution = v1920x1080p_30Hz; + video.hdmi_3d_format = HDMI_2D_VIDEO_FORMAT; + break; + case V4L2_STD_TVOUT_720P_60_SBS_HALF: + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_3D_SSH_FORMAT; + break; + case V4L2_STD_TVOUT_720P_59_SBS_HALF: + video.resolution = v1280x720p_60Hz; + video.hdmi_3d_format = HDMI_3D_SSH_FORMAT; + break; + case V4L2_STD_TVOUT_720P_50_TB: + video.resolution = v1280x720p_50Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + break; + case V4L2_STD_TVOUT_1080P_24_TB: + video.resolution = v1920x1080p_24Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + break; + case V4L2_STD_TVOUT_1080P_23_TB: + video.resolution = v1920x1080p_24Hz; + video.hdmi_3d_format = HDMI_3D_TB_FORMAT; + break; + default: + LOGE("%s::unmathced std_id(%lld)", __func__, std_id); + return -1; + break; + } + + if (!EDIDVideoResolutionSupport(&video)) { +#ifdef DEBUG_MSG_ENABLE + LOGD("%s::EDIDVideoResolutionSupport(%llx) fail (not suppoted std_id) \n", __func__, std_id); +#endif + return -1; + } + + return 0; +} + +int hdmi_resolution_2_std_id(unsigned int resolution, int * w, int * h, v4l2_std_id * std_id, __u32 *preset_id) +{ + int ret = 0; + + switch (resolution) { + case 1080960: + *std_id = V4L2_STD_1080P_60; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080P60; + break; + case 1080950: + *std_id = V4L2_STD_1080P_50; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080P50; + break; + case 1080930: + *std_id = V4L2_STD_1080P_30; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080P30; + break; + case 1080924: + *std_id = V4L2_STD_TVOUT_1080P_24_TB; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080P24_TB; + break; + case 1080160: + *std_id = V4L2_STD_1080I_60; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080I60; + break; + case 1080150: + *std_id = V4L2_STD_1080I_50; + *w = 1920; + *h = 1080; + *preset_id = V4L2_DV_1080I50; + break; + case 720960: + *std_id = V4L2_STD_720P_60; + *w = 1280; + *h = 720; + *preset_id = V4L2_DV_720P60; + break; + case 7209601: + *std_id = V4L2_STD_TVOUT_720P_60_SBS_HALF; + *w = 1280; + *h = 720; + *preset_id = V4L2_DV_720P60_SB_HALF; + break; + case 720950: + *std_id = V4L2_STD_720P_50; + *w = 1280; + *h = 720; + *preset_id = V4L2_DV_720P50; + break; + case 7209501: + *std_id = V4L2_STD_TVOUT_720P_50_TB; + *w = 1280; + *h = 720; + *preset_id = V4L2_DV_720P50_TB; + break; + case 5769501: + *std_id = V4L2_STD_576P_50_16_9; + *w = 720; + *h = 576; + *preset_id = V4L2_DV_576P50; + break; + case 5769502: + *std_id = V4L2_STD_576P_50_4_3; + *w = 720; + *h = 576; + *preset_id = V4L2_DV_576P50; + break; + case 4809601: + *std_id = V4L2_STD_480P_60_16_9; + *w = 720; + *h = 480; + *preset_id = V4L2_DV_480P60; + break; + case 4809602: + *std_id = V4L2_STD_480P_60_4_3; + *w = 720; + *h = 480; + *preset_id = V4L2_DV_480P60; + break; + default: + LOGE("%s::unmathced resolution(%d)", __func__, resolution); + ret = -1; + break; + } + + return ret; +} + +int hdmi_enable_hdcp(int fd, unsigned int hdcp_en) +{ + if (ioctl(fd, VIDIOC_HDCP_ENABLE, hdcp_en) < 0) { + LOGD("%s::VIDIOC_HDCP_ENABLE(%d) fail \n", __func__, hdcp_en); + return -1; + } + + return 0; +} + +int hdmi_check_audio(int fd) +{ + struct HDMIAudioParameter audio; + enum state audio_state = ON; + int ret = 0; + + audio.formatCode = LPCM_FORMAT; + audio.outPacket = HDMI_ASP; + audio.channelNum = CH_2; + audio.sampleFreq = SF_44KHZ; + +#if defined(BOARD_USES_EDID) + if (!EDIDAudioModeSupport(&audio)) + audio_state = NOT_SUPPORT; + else + audio_state = ON; +#endif + if (audio_state == ON) { + if (ioctl(fd, VIDIOC_INIT_AUDIO, 1) < 0) { + LOGE("%s::VIDIOC_INIT_AUDIO(1) failed", __func__); + ret = -1; + } + } else { + if (ioctl(fd, VIDIOC_INIT_AUDIO, 0) < 0) { + LOGE("%s::VIDIOC_INIT_AUDIO(0) failed", __func__); + ret = -1; + } + } + + return ret; +} + +} diff --git a/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.h b/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.h new file mode 100644 index 0000000..67c0729 --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/SecHdmiV4L2Utils.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#ifndef __HDMI_HAL_V4L2_UTILS_H__ +#define __HDMI_HAL_V4L2_UTILS_H__ + +//#define LOG_NDEBUG 0 +//#define LOG_TAG "libhdmi" + +#include "fimd_api.h" +#include "SecBuffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +namespace android { + +void display_menu(void); + +int tvout_init(int fd_tvout, __u32 preset_id); +int tvout_deinit(); +int tvout_std_v4l2_querycap(int fd, char *node); +int tvout_std_v4l2_s_fmt(int fd, enum v4l2_buf_type type, enum v4l2_field field, int w, int h, int colorformat, int num_planes); +int tvout_std_v4l2_s_crop(int fd, enum v4l2_buf_type type, enum v4l2_field field, int x, int y, int w, int h); +int tvout_std_v4l2_s_ctrl(int fd, int id, int value); +int tvout_std_v4l2_reqbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int num_bufs); +int tvout_std_v4l2_querybuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, unsigned int buf_index, unsigned int num_planes, SecBuffer *secBuf); +int tvout_std_v4l2_qbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, int buf_index, int num_planes, SecBuffer *secBuf); +int tvout_std_v4l2_dqbuf(int fd, enum v4l2_buf_type type, enum v4l2_memory memory, int *buf_index, int num_planes); +int tvout_std_v4l2_streamon(int fd, enum v4l2_buf_type type); +int tvout_std_v4l2_streamoff(int fd, enum v4l2_buf_type type); + +int tvout_v4l2_enum_output(int fp, struct v4l2_output *output); +int tvout_v4l2_s_output(int fp, int index); +int tvout_v4l2_g_output(int fp, int *index); +int tvout_std_v4l2_enum_dv_presets(int fd); +int tvout_std_v4l2_s_dv_preset(int fd, struct v4l2_dv_preset *preset); +int tvout_std_subdev_s_fmt(int fd, unsigned int pad, int w, int h, enum v4l2_mbus_pixelcode code); +int tvout_std_subdev_s_crop(int fd, unsigned int pad, int w, int h, int x, int y); + +void hdmi_cal_rect(int src_w, int src_h, int dst_w, int dst_h, struct v4l2_rect *dst_rect); +int hdmi_set_videolayer(int fd, int hdmiW, int hdmiH, struct v4l2_rect * rect); +int hdmi_set_graphiclayer(int fd_subdev, int fd_videodev,int layer, + int srcColorFormat, + int src_w, int src_h, + unsigned int src_address, SecBuffer * dstBuffer, + int dst_x, int dst_y, int dst_w, int dst_h, + int rotVal); +int hdmi_set_g_Params(int fd_subdev, int fd_videodev, int layer, + int srcColorFormat, + int src_w, int src_h, + int dst_x, int dst_y, int dst_w, int dst_h); + +int hdmi_cable_status(); +int hdmi_outputmode_2_v4l2_output_type(int output_mode); +int hdmi_v4l2_output_type_2_outputmode(int v4l2_output_type); +int composite_std_2_v4l2_std_id(int std); + +int hdmi_check_output_mode(int v4l2_output_type); +int hdmi_check_resolution(v4l2_std_id std_id); + +int hdmi_resolution_2_std_id(unsigned int resolution, int *w, int *h, v4l2_std_id *std_id, __u32 *preset_id); +int hdmi_enable_hdcp(int fd, unsigned int hdcp_en); +int hdmi_check_audio(int fd); + +#ifdef __cplusplus +} +#endif + +} //namespace android + +#endif //__HDMI_HAL_V4L2_UTILS_H__ diff --git a/exynos5/hal/libhdmi/SecHdmi/fimd_api.c b/exynos5/hal/libhdmi/SecHdmi/fimd_api.c new file mode 100644 index 0000000..0e07ef3 --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/fimd_api.c @@ -0,0 +1,229 @@ +/* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimd_api.h" + +int fb_open(int win) +{ + char node[20]; + int fp = -1; + + sprintf(node, "%s%d", PFX_NODE_FB, win); + + fp = open(node, O_RDWR); + if (fp < 0) + LOGE("%s: fb[%d] open failed", __func__, win); + + return fp; +} + +int fb_close(int fp) +{ + if (fp) + close(fp); + else + LOGE("%s: fb is not allocated %d", __func__, fp); + + return 0; +} + +int get_fscreeninfo(int fp, struct fb_fix_screeninfo *fix) +{ + int ret = -1; + + ret = ioctl(fp, FBIOGET_FSCREENINFO, fix); + if (ret) + LOGE("%s: FBIOGET_FSCREENINFO failed", __func__); + + return ret; +} + +int get_vscreeninfo(int fp, struct fb_var_screeninfo *var) +{ + int ret = -1; + + ret = ioctl(fp, FBIOGET_VSCREENINFO, var); + if (ret) + LOGE("%s:: FBIOGET_VSCREENINFO failed", __func__); + + return ret; +} + +int put_vscreeninfo(int fp, struct fb_var_screeninfo *var) +{ + int ret = -1; + + ret = ioctl(fp, FBIOPUT_VSCREENINFO, var); + if (ret) + LOGE("%s:: FBIOPUT_VSCREENINFO failed", __func__); + + return ret; +} + +int get_bytes_per_pixel(int bits_per_pixel) +{ + return (bits_per_pixel == 24 || bits_per_pixel == 25 || + bits_per_pixel == 28) ? 4 : bits_per_pixel / 8; +} + +char *fb_mmap(__u32 size, int fp) +{ + char *buffer; + + buffer = (char *)mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, fp, 0); + if (!buffer) { + LOGE("%s:: mmap failed", __func__); + return NULL; + } + + return buffer; +} + +int fb_ioctl(int fp, __u32 cmd, void *arg) +{ + int ret = -1; + + ret = ioctl(fp, cmd, arg); + if (ret < 0) + LOGE("%s:: ioctl (%d) failed", __func__, cmd); + + return ret; +} + +int fb_on(int fp) +{ + int ret = -1; + + ret = ioctl(fp, FBIOBLANK, FB_BLANK_UNBLANK); + if (ret) + LOGE("%s:: FBIOBLANK failed", __func__); + + return ret; +} + +int fb_off(int fp) +{ + int ret = -1; + + ret = ioctl(fp, FBIOBLANK, FB_BLANK_POWERDOWN); + if (ret) + LOGE("%s:: FBIOBLANK failed", __func__); + + return ret; +} + +int fb_off_all() +{ + int fp, i; + + for (i = 0; i < TOTAL_FB_NUM; i++) { + fp = fb_open(i); + if (fp < 0) + return -1; + + if (ioctl(fp, FBIOBLANK, FB_BLANK_POWERDOWN) < 0) + LOGE("%s:: FBIOBLANK failed", __func__); + + fb_off(fp); + fb_close(fp); + } + + return 0; +} + +char *fb_init_display(int fp, int width, int height, int left_x, int top_y, + int bpp) +{ + struct fb_var_screeninfo var; + struct s5ptvfb_user_window window; + int fb_size; + char *fb = NULL; + + var.xres = width; + var.yres = height; + var.bits_per_pixel = bpp; + window.x = left_x; + window.y = top_y; + + var.xres_virtual = var.xres; + var.yres_virtual = var.yres; + var.xoffset = 0; + var.yoffset = 0; + var.width = 0; + var.height = 0; + var.transp.length = 0; + var.activate = FB_ACTIVATE_FORCE; + fb_size = var.xres_virtual * var.yres_virtual * bpp / 8; + + /* FBIOPUT_VSCREENINFO should be first */ + put_vscreeninfo(fp, &var); + fb_ioctl(fp, S5PTVFB_WIN_POSITION, &window); + + /* draw image */ + fb = fb_mmap(fb_size, fp); + memset(fb, 0x0, fb_size); + + return fb; +} + +int simple_draw(char *dest, const char *src, int img_width, + struct fb_var_screeninfo *var) +{ + int bytes_per_pixel = get_bytes_per_pixel(var->bits_per_pixel); + unsigned int y; + + for (y = 0; y < var->yres; y++) + memcpy(dest + y * var->xres * bytes_per_pixel, + src + y * img_width * bytes_per_pixel, + var->xres * bytes_per_pixel); + + return 0; +} + +int draw(char *dest, const char *src, int img_width, + struct fb_var_screeninfo *var) +{ + int bytes_per_pixel = get_bytes_per_pixel(var->bits_per_pixel); + unsigned int y; + + if (var->bits_per_pixel == 16) { + memcpy(dest, src, var->xres * var->yres * 2); + } else { + for (y = 0; y < var->yres; y++) + memcpy(dest + y * var->xres * bytes_per_pixel, + src + y * img_width * bytes_per_pixel, + var->xres * bytes_per_pixel); + } + + return 0; +} diff --git a/exynos5/hal/libhdmi/SecHdmi/fimd_api.h b/exynos5/hal/libhdmi/SecHdmi/fimd_api.h new file mode 100644 index 0000000..a8561a4 --- /dev/null +++ b/exynos5/hal/libhdmi/SecHdmi/fimd_api.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef __FIMD_API_H__ +#define __FIMD_API_H__ + +#include +#include "s5p_tvout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TOTAL_FB_NUM 5 + +int fb_open(int win); +int fb_close(int fp); +int fb_on(int fp); +int fb_off(int fp); +int fb_off_all(void); +char *fb_init_display(int fp, int width, int height,\ + int left_x, int top_y, int bpp); +int fb_ioctl(int fp, __u32 cmd, void *arg); +char *fb_mmap(__u32 size, int fp); +int simple_draw(char *dest, const char *src,\ + int img_width, struct fb_var_screeninfo *var); +int draw(char *dest, const char *src,\ + int img_width, struct fb_var_screeninfo *var); +int get_fscreeninfo(int fp, struct fb_fix_screeninfo *fix); +int get_vscreeninfo(int fp, struct fb_var_screeninfo *var); +int put_vscreeninfo(int fp, struct fb_var_screeninfo *var); +int get_bytes_per_pixel(int bits_per_pixel); + +#ifdef __cplusplus +} +#endif + +#endif /* __FIMD_API_H__ */ diff --git a/exynos5/hal/libhdmi/libhdmiservice/Android.mk b/exynos5/hal/libhdmi/libhdmiservice/Android.mk new file mode 100644 index 0000000..bc0aa0d --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/Android.mk @@ -0,0 +1,95 @@ +# 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. + +ifeq ($(filter-out exynos5,$(TARGET_BOARD_PLATFORM)),) +ifeq ($(BOARD_USES_HDMI),true) + +LOCAL_PATH:= $(call my-dir) + +# +# libTVOut +# + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_PRELINK_MODULE := false + +LOCAL_SRC_FILES := \ + SecTVOutService.cpp \ + ISecTVOut.cpp \ + MessageQueue.cpp + +LOCAL_C_INCLUDES := \ + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libutils \ + libcutils + +LOCAL_C_INCLUDES += device/samsung/$(TARGET_BOARD_PLATFORM)/include +LOCAL_C_INCLUDES += device/samsung/$(TARGET_BOARD_PLATFORM)/libhdmi +LOCAL_SHARED_LIBRARIES += libhdmi + +ifeq ($(BOARD_USES_HDMI_SUBTITLES),true) + LOCAL_CFLAGS += -DBOARD_USES_HDMI_SUBTITLES +endif + +LOCAL_MODULE := libTVOut + +include $(BUILD_SHARED_LIBRARY) + +# +# libhdmiclient +# + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional +LOCAL_PRELINK_MODULE := false + +LOCAL_SRC_FILES:= \ + SecHdmiClient.cpp + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libutils \ + libTVOut + +ifeq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS),linux) +ifeq ($(TARGET_ARCH),x86) +LOCAL_LDLIBS += -lpthread -ldl -lrt +endif +endif +endif + +ifeq ($(WITH_MALLOC_LEAK_CHECK),true) + LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK +endif + +ifeq ($(TARGET_SOC),exynos5250) +LOCAL_CFLAGS += -DSAMSUNG_EXYNOS5250 +endif + +LOCAL_CFLAGS += -DBOARD_USES_HDMI + +LOCAL_MODULE:= libhdmiclient + +include $(BUILD_SHARED_LIBRARY) + +endif +endif diff --git a/exynos5/hal/libhdmi/libhdmiservice/Barrier.h b/exynos5/hal/libhdmi/libhdmiservice/Barrier.h new file mode 100644 index 0000000..6f8507e --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/Barrier.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_BARRIER_H +#define ANDROID_BARRIER_H + +#include +#include +#include + +namespace android { + +class Barrier +{ +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + void open() { + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +}; // namespace android + +#endif // ANDROID_BARRIER_H diff --git a/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.cpp b/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.cpp new file mode 100644 index 0000000..a013bf1 --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.cpp @@ -0,0 +1,111 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#include +#include +#include +#include +#include "ISecTVOut.h" + +namespace android { + + enum { + SET_HDMI_STATUS = IBinder::FIRST_CALL_TRANSACTION, + SET_HDMI_MODE, + SET_HDMI_RESOLUTION, + SET_HDMI_HDCP, + SET_HDMI_ROTATE, + SET_HDMI_HWCLAYER, + BLIT_2_HDMI + }; + + void BpSecTVOut::setHdmiCableStatus(uint32_t status) + { + Parcel data, reply; + data.writeInt32(status); + remote()->transact(SET_HDMI_STATUS, data, &reply); + } + + void BpSecTVOut::setHdmiMode(uint32_t mode) + { + Parcel data, reply; + data.writeInt32(mode); + remote()->transact(SET_HDMI_MODE, data, &reply); + } + + void BpSecTVOut::setHdmiResolution(uint32_t resolution) + { + Parcel data, reply; + data.writeInt32(resolution); + remote()->transact(SET_HDMI_RESOLUTION, data, &reply); + } + + void BpSecTVOut::setHdmiHdcp(uint32_t resolution) + { + Parcel data, reply; + data.writeInt32(resolution); + remote()->transact(SET_HDMI_HDCP, data, &reply); + } + + void BpSecTVOut::setHdmiRotate(uint32_t rotVal, uint32_t hwcLayer) + { + Parcel data, reply; + data.writeInt32(rotVal); + data.writeInt32(hwcLayer); + remote()->transact(SET_HDMI_ROTATE, data, &reply); + } + + void BpSecTVOut::setHdmiHwcLayer(uint32_t hwcLayer) + { + Parcel data, reply; + data.writeInt32(hwcLayer); + remote()->transact(SET_HDMI_HWCLAYER, data, &reply); + } + + void BpSecTVOut::blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t physYAddr, + uint32_t physCbAddr, + uint32_t physCrAddr, + uint32_t dstX, + uint32_t dstY, + uint32_t hdmiLayer, + uint32_t num_of_hwc_layer) + { + Parcel data, reply; + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(colorFormat); + data.writeInt32(physYAddr); + data.writeInt32(physCbAddr); + data.writeInt32(physCrAddr); + data.writeInt32(dstX); + data.writeInt32(dstY); + data.writeInt32(hdmiLayer); + data.writeInt32(num_of_hwc_layer); + remote()->transact(BLIT_2_HDMI, data, &reply); + } + + IMPLEMENT_META_INTERFACE(SecTVOut, "android.os.ISecTVOut"); +}; diff --git a/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.h b/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.h new file mode 100644 index 0000000..5506b57 --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/ISecTVOut.h @@ -0,0 +1,74 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#ifndef ISECTVOUT_H +#define ISECTVOUT_H +#include +#include +#include + +namespace android { + class ISecTVOut: public IInterface + { + public: + DECLARE_META_INTERFACE(SecTVOut); + virtual void setHdmiCableStatus(uint32_t status) = 0; + virtual void setHdmiMode(uint32_t mode) = 0; + virtual void setHdmiResolution(uint32_t resolution) = 0; + virtual void setHdmiHdcp(uint32_t enHdcp) = 0; + virtual void setHdmiRotate(uint32_t rotVal, uint32_t hwcLayer) = 0; + virtual void setHdmiHwcLayer(uint32_t hwcLayer) = 0; + virtual void blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t physYAddr, + uint32_t physCbAddr, + uint32_t physCrAddr, + uint32_t dstX, + uint32_t dstY, + uint32_t hdmiLayer, + uint32_t num_of_hwc_layer) = 0; + }; + //-------------------------------------------------------------- + class BpSecTVOut: public BpInterface + { + public: + BpSecTVOut(const sp& impl): BpInterface(impl){} + virtual void setHdmiCableStatus(uint32_t status); + virtual void setHdmiMode(uint32_t mode); + virtual void setHdmiResolution(uint32_t resolution); + virtual void setHdmiHdcp(uint32_t enHdcp); + virtual void setHdmiRotate(uint32_t rotVal, uint32_t hwcLayer); + virtual void setHdmiHwcLayer(uint32_t hwcLayer); + virtual void blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t physYAddr, + uint32_t physCbAddr, + uint32_t physCrAddr, + uint32_t dstX, + uint32_t dstY, + uint32_t hdmiLayer, + uint32_t num_of_hwc_layer); + }; +}; +#endif diff --git a/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.cpp b/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.cpp new file mode 100644 index 0000000..fb58ef9 --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "MessageQueue.h" + +namespace android { + +void MessageList::insert(const sp& node) +{ + LIST::iterator cur(mList.begin()); + LIST::iterator end(mList.end()); + while (cur != end) { + if (*node < **cur) { + mList.insert(cur, node); + return; + } + ++cur; + } + mList.insert(++end, node); +} + +void MessageList::remove(MessageList::LIST::iterator pos) +{ + mList.erase(pos); +} + +MessageQueue::MessageQueue() + : mInvalidate(false) +{ + mInvalidateMessage = new MessageBase(INVALIDATE); +} + +MessageQueue::~MessageQueue() +{ +} + +sp MessageQueue::waitMessage(nsecs_t timeout) +{ + sp result; + + bool again; + do { + const nsecs_t timeoutTime = systemTime() + timeout; + while (true) { + Mutex::Autolock _l(mLock); + nsecs_t now = systemTime(); + nsecs_t nextEventTime = -1; + + LIST::iterator cur(mMessages.begin()); + if (cur != mMessages.end()) { + result = *cur; + } + + if (result != 0) { + if (result->when <= now) { + // there is a message to deliver + mMessages.remove(cur); + break; + } + nextEventTime = result->when; + result = 0; + } + + // see if we have an invalidate message + if (mInvalidate) { + mInvalidate = false; + mInvalidateMessage->when = now; + result = mInvalidateMessage; + break; + } + + if (timeout >= 0) { + if (timeoutTime < now) { + // we timed-out, return a NULL message + result = 0; + break; + } + if (nextEventTime > 0) { + if (nextEventTime > timeoutTime) { + nextEventTime = timeoutTime; + } + } else { + nextEventTime = timeoutTime; + } + } + + if (nextEventTime >= 0) { + //LOGD("nextEventTime = %lld ms", nextEventTime); + if (nextEventTime > 0) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + const nsecs_t reltime = nextEventTime - systemTime(); + if (reltime > 0) { + mCondition.waitRelative(mLock, reltime); + } + } + } else { + //LOGD("going to wait"); + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mCondition.wait(mLock); + } + } + // here we're not holding the lock anymore + + if (result == 0) + break; + + again = result->handler(); + if (again) { + // the message has been processed. release our reference to it + // without holding the lock. + result->notify(); + result = 0; + } + } while (again); + + return result; +} + +status_t MessageQueue::postMessage( + const sp& message, nsecs_t relTime, uint32_t flags) +{ + return queueMessage(message, relTime, flags); +} + +status_t MessageQueue::invalidate() { + Mutex::Autolock _l(mLock); + mInvalidate = true; + mCondition.signal(); + return NO_ERROR; +} + +status_t MessageQueue::queueMessage( + const sp& message, nsecs_t relTime, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + message->when = systemTime() + relTime; + mMessages.insert(message); + + //LOGD("MessageQueue::queueMessage time = %lld ms", message->when); + //dumpLocked(message); + + mCondition.signal(); + return NO_ERROR; +} + +void MessageQueue::dump(const sp& message) +{ + Mutex::Autolock _l(mLock); + dumpLocked(message); +} + +void MessageQueue::dumpLocked(const sp& message) +{ + LIST::const_iterator cur(mMessages.begin()); + LIST::const_iterator end(mMessages.end()); + int c = 0; + while (cur != end) { + const char tick = (*cur == message) ? '>' : ' '; + LOGD("%c %d: msg{.what=%08x, when=%lld}", + tick, c, (*cur)->what, (*cur)->when); + ++cur; + c++; + } +} + +}; // namespace android diff --git a/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.h b/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.h new file mode 100644 index 0000000..4fc752f --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/MessageQueue.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 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. + */ + +#ifndef ANDROID_MESSAGE_QUEUE_H +#define ANDROID_MESSAGE_QUEUE_H + +#include +#include +#include + +#include +#include +#include + +#include "Barrier.h" + +namespace android { + +class MessageBase; + +class MessageList +{ + List< sp > mList; + typedef List< sp > LIST; +public: + inline LIST::iterator begin() { return mList.begin(); } + inline LIST::const_iterator begin() const { return mList.begin(); } + inline LIST::iterator end() { return mList.end(); } + inline LIST::const_iterator end() const { return mList.end(); } + inline bool isEmpty() const { return mList.empty(); } + void insert(const sp& node); + void remove(LIST::iterator pos); +}; + +class MessageBase : + public LightRefBase +{ +public: + nsecs_t when; + uint32_t what; + int32_t arg0; + + MessageBase() : when(0), what(0), arg0(0) { } + MessageBase(uint32_t what, int32_t arg0=0) + : when(0), what(what), arg0(arg0) { } + + // return true if message has a handler + virtual bool handler() { return false; } + + // waits for the handler to be processed + void wait() const { barrier.wait(); } + + // releases all waiters. this is done automatically if + // handler returns true + void notify() const { barrier.open(); } + +protected: + virtual ~MessageBase() { } + +private: + mutable Barrier barrier; + friend class LightRefBase; +}; + +inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) { + return lhs.when < rhs.when; +} + +class MessageQueue +{ + typedef List< sp > LIST; +public: + + MessageQueue(); + ~MessageQueue(); + + // pre-defined messages + enum { + INVALIDATE = '_upd' + }; + + sp waitMessage(nsecs_t timeout = -1); + + status_t postMessage(const sp& message, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t invalidate(); + + void dump(const sp& message); + +private: + status_t queueMessage(const sp& message, + nsecs_t reltime, uint32_t flags); + void dumpLocked(const sp& message); + + Mutex mLock; + Condition mCondition; + MessageList mMessages; + bool mInvalidate; + sp mInvalidateMessage; +}; + +}; // namespace android + +#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.cpp b/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.cpp new file mode 100644 index 0000000..150aadf --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.cpp @@ -0,0 +1,139 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#define LOG_TAG "libhdmiclient" + +#include "SecHdmiClient.h" + +namespace android { + +static sp g_SecTVOutService = 0; + +SecHdmiClient::SecHdmiClient() +{ + g_SecTVOutService = m_getSecTVOutService(); +} + +SecHdmiClient::~SecHdmiClient() +{ +} + +SecHdmiClient * SecHdmiClient::getInstance(void) +{ + static SecHdmiClient singleton; + return &singleton; +} + +void SecHdmiClient::setHdmiCableStatus(int status) +{ + LOGD("%s HDMI status: %d\n", __func__, status); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiCableStatus(status); +} + +void SecHdmiClient::setHdmiMode(int mode) +{ + //LOGD("%s HDMI Mode: %d\n", __func__, mode); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiMode(mode); +} + +void SecHdmiClient::setHdmiResolution(int resolution) +{ + //LOGD("%s HDMI Resolution: %d\n", __func__, resolution); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiResolution(resolution); +} + +void SecHdmiClient::setHdmiHdcp(int enHdcp) +{ + //LOGD("%s HDMI HDCP: %d\n", __func__, enHdcp); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiHdcp(enHdcp); +} + +void SecHdmiClient::setHdmiRotate(int rotVal, uint32_t hwcLayer) +{ + //LOGD("%s HDMI ROTATE: %d\n", __func__, rotVal); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiRotate(rotVal, hwcLayer); +} + +void SecHdmiClient::setHdmiHwcLayer(uint32_t hwcLayer) +{ + //LOGD("%s HDMI HWCLAYER: %d\n", __func__, hwcLayer); + + if (g_SecTVOutService != 0) + g_SecTVOutService->setHdmiHwcLayer(hwcLayer); +} + +void SecHdmiClient::blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t physYAddr, + uint32_t physCbAddr, + uint32_t physCrAddr, + uint32_t dstX, + uint32_t dstY, + uint32_t hdmiLayer, + uint32_t num_of_hwc_layer) +{ + if (g_SecTVOutService != 0 ) + g_SecTVOutService->blit2Hdmi(w, h, colorFormat, physYAddr, physCbAddr, physCrAddr, dstX, dstY, hdmiLayer, num_of_hwc_layer); +} + +sp SecHdmiClient::m_getSecTVOutService(void) +{ + int ret = 0; + + if (g_SecTVOutService == 0) { + sp binder; + sp sc; + sp sm = defaultServiceManager(); + int getSvcTimes = 0; + for(getSvcTimes = 0; getSvcTimes < GETSERVICETIMEOUT; getSvcTimes++) { + binder = sm->getService(String16("SecTVOutService")); + if (binder == 0) { + LOGW("SecTVOutService not published, waiting..."); + usleep(500000); // 0.5 s + } else { + break; + } + } + // grab the lock again for updating g_surfaceFlinger + if (getSvcTimes < GETSERVICETIMEOUT) { + sc = interface_cast(binder); + g_SecTVOutService = sc; + } else { + LOGW("Failed to get SecTVOutService... SecHdmiClient will get it later.."); + } + } + return g_SecTVOutService; +} + +} diff --git a/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.h b/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.h new file mode 100644 index 0000000..e94e19a --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/SecHdmiClient.h @@ -0,0 +1,84 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#ifndef __SEC_HDMI_CLIENT_H__ +#define __SEC_HDMI_CLIENT_H__ + +#include "utils/Log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ISecTVOut.h" + +#define GETSERVICETIMEOUT (5) + +namespace android { + +class SecHdmiClient +{ +public: + enum HDMI_MODE + { + HDMI_MODE_NONE = 0, + HDMI_MODE_UI, + HDMI_MODE_VIDEO, + }; + +private: + SecHdmiClient(); + virtual ~SecHdmiClient(); + +public: + static SecHdmiClient * getInstance(void); + void setHdmiCableStatus(int status); + void setHdmiMode(int mode); + void setHdmiResolution(int resolution); + void setHdmiHdcp(int enHdcp); + void setHdmiRotate(int rotVal, uint32_t hwcLayer); + void setHdmiHwcLayer(uint32_t hwcLayer); + virtual void blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t physYAddr, + uint32_t physCbAddr, + uint32_t physCrAddr, + uint32_t dstX, + uint32_t dstY, + uint32_t hdmiLayer, + uint32_t num_of_hwc_layer); + +private: + sp m_getSecTVOutService(void); + +}; + +}; + +#endif diff --git a/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.cpp b/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.cpp new file mode 100644 index 0000000..39ef742 --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.cpp @@ -0,0 +1,377 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#define LOG_TAG "SecTVOutService" + +#include +#include +#include +#include +#include +#include "SecTVOutService.h" +#include + +namespace android { +#define DEFAULT_LCD_WIDTH 1280 +#define DEFAULT_LCD_HEIGHT 800 + +#define DIRECT_VIDEO_RENDERING (1) +#define DIRECT_UI_RENDERING (0) + + enum { + SET_HDMI_STATUS = IBinder::FIRST_CALL_TRANSACTION, + SET_HDMI_MODE, + SET_HDMI_RESOLUTION, + SET_HDMI_HDCP, + SET_HDMI_ROTATE, + SET_HDMI_HWCLAYER, + BLIT_2_HDMI + }; + + int SecTVOutService::HdmiFlushThread() + { + while (!mExitHdmiFlushThread) { + nsecs_t timeout = -1; + sp msg = mHdmiEventQueue.waitMessage(timeout); + } + + return 0; + } + + int SecTVOutService::instantiate() + { + LOGD("SecTVOutService instantiate"); + int r = defaultServiceManager()->addService(String16( "SecTVOutService"), new SecTVOutService ()); + LOGD("SecTVOutService r=%d", r); + + return r; + } + + SecTVOutService::SecTVOutService () { + LOGV("SecTVOutService created"); + mHdmiCableInserted = false; +#ifdef SUPPORT_G2D_UI_MODE + mUILayerMode = SecHdmi::HDMI_LAYER_GRAPHIC_1; +#else + mUILayerMode = SecHdmi::HDMI_LAYER_VIDEO; +#endif + mHwcLayer = 0; + mExitHdmiFlushThread = false; + + setLCDsize(); + if (mSecHdmi.create(mLCD_width, mLCD_height) == false) + LOGE("%s::mSecHdmi.create() fail", __func__); + else + setHdmiStatus(1); + + mHdmiFlushThread = new HDMIFlushThread(this); + } + + void SecTVOutService::setLCDsize(void) { + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + + int fd = -1; + int i = 0; + char name[64]; + + while ((fd==-1) && device_template[i]) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + i++; + } + if (fd > 0) { + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) != -1) { + mLCD_width = info.xres; + mLCD_height = info.yres; + } else { + mLCD_width = DEFAULT_LCD_WIDTH; + mLCD_height = DEFAULT_LCD_HEIGHT; + } + close(fd); + } + return; + } + + SecTVOutService::~SecTVOutService () { + LOGV ("SecTVOutService destroyed"); + + if (mHdmiFlushThread != NULL) { + mHdmiFlushThread->requestExit(); + mExitHdmiFlushThread = true; + mHdmiFlushThread->requestExitAndWait(); + mHdmiFlushThread.clear(); + } + } + + status_t SecTVOutService::onTransact(uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags) + { + switch (code) { + case SET_HDMI_STATUS: { + int status = data.readInt32(); + setHdmiStatus(status); + } break; + + case SET_HDMI_MODE: { + int mode = data.readInt32(); + setHdmiMode(mode); + } break; + + case SET_HDMI_RESOLUTION: { + int resolution = data.readInt32(); + setHdmiResolution(resolution); + } break; + + case SET_HDMI_HDCP: { + int enHdcp = data.readInt32(); + setHdmiHdcp(enHdcp); + } break; + + case SET_HDMI_ROTATE: { + int rotVal = data.readInt32(); + int hwcLayer = data.readInt32(); + setHdmiRotate(rotVal, hwcLayer); + } break; + + case SET_HDMI_HWCLAYER: { + int hwcLayer = data.readInt32(); + setHdmiHwcLayer((uint32_t)hwcLayer); + } break; + + case BLIT_2_HDMI: { + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t colorFormat = data.readInt32(); + uint32_t physYAddr = data.readInt32(); + uint32_t physCbAddr = data.readInt32(); + uint32_t physCrAddr = data.readInt32(); + uint32_t dstX = data.readInt32(); + uint32_t dstY = data.readInt32(); + uint32_t hdmiLayer = data.readInt32(); + uint32_t num_of_hwc_layer = data.readInt32(); + + blit2Hdmi(w, h, colorFormat, physYAddr, physCbAddr, physCrAddr, dstX, dstY, hdmiLayer, num_of_hwc_layer); + } break; + + default : + LOGE ( "onTransact::default"); + return BBinder::onTransact (code, data, reply, flags); + } + + return NO_ERROR; + } + + void SecTVOutService::setHdmiStatus(uint32_t status) + { + LOGD("%s HDMI cable status = %d", __func__, status); + { + Mutex::Autolock _l(mLock); + + bool hdmiCableInserted = (bool)status; + + if (mHdmiCableInserted == hdmiCableInserted) + return; + + if (hdmiCableInserted == true) { + if (mSecHdmi.connect() == false) { + LOGE("%s::mSecHdmi.connect() fail", __func__); + hdmiCableInserted = false; + } + } else { + if (mSecHdmi.disconnect() == false) + LOGE("%s::mSecHdmi.disconnect() fail", __func__); + } + + mHdmiCableInserted = hdmiCableInserted; + } + + if (hdmiCableInserted() == true) + this->blit2Hdmi(mLCD_width, mLCD_height, HAL_PIXEL_FORMAT_BGRA_8888, 0, 0, 0, 0, 0, HDMI_MODE_UI, 0); + } + + void SecTVOutService::setHdmiMode(uint32_t mode) + { + LOGD("%s TV mode = %d", __func__, mode); + Mutex::Autolock _l(mLock); + + if ((hdmiCableInserted() == true) && (mSecHdmi.setHdmiOutputMode(mode)) == false) { + LOGE("%s::mSecHdmi.setHdmiOutputMode() fail", __func__); + return; + } + } + + void SecTVOutService::setHdmiResolution(uint32_t resolution) + { + LOGD("%s TV resolution = %d", __func__, resolution); + Mutex::Autolock _l(mLock); + + if ((hdmiCableInserted() == true) && (mSecHdmi.setHdmiResolution(resolution)) == false) { + LOGE("%s::mSecHdmi.setHdmiResolution() fail", __func__); + return; + } + } + + void SecTVOutService::setHdmiHdcp(uint32_t hdcp_en) + { + LOGD("%s TV HDCP = %d", __func__, hdcp_en); + Mutex::Autolock _l(mLock); + + if ((hdmiCableInserted() == true) && (mSecHdmi.setHdcpMode(hdcp_en)) == false) { + LOGE("%s::mSecHdmi.setHdcpMode() fail", __func__); + return; + } + } + + void SecTVOutService::setHdmiRotate(uint32_t rotVal, uint32_t hwcLayer) + { + //LOGD("%s TV ROTATE = %d", __func__, rotVal); + Mutex::Autolock _l(mLock); + + if ((hdmiCableInserted() == true) && (mSecHdmi.setUIRotation(rotVal, hwcLayer)) == false) { + LOGE("%s::mSecHdmi.setUIRotation() fail", __func__); + return; + } + } + + void SecTVOutService::setHdmiHwcLayer(uint32_t hwcLayer) + { + //LOGD("%s TV HWCLAYER = %d", __func__, hwcLayer); + Mutex::Autolock _l(mLock); + + mHwcLayer = hwcLayer; + return; + } + + void SecTVOutService::blit2Hdmi(uint32_t w, uint32_t h, uint32_t colorFormat, + uint32_t pPhyYAddr, uint32_t pPhyCbAddr, uint32_t pPhyCrAddr, + uint32_t dstX, uint32_t dstY, + uint32_t hdmiMode, + uint32_t num_of_hwc_layer) + { + Mutex::Autolock _l(mLock); + + if (hdmiCableInserted() == false) + return; + + int hdmiLayer = SecHdmi::HDMI_LAYER_VIDEO; +#if defined(CHECK_UI_TIME) || defined(CHECK_VIDEO_TIME) + nsecs_t start, end; +#endif + + sp msg; + + switch (hdmiMode) { + case HDMI_MODE_UI : + if (mHwcLayer >= 2) + hdmiLayer = SecHdmi::HDMI_LAYER_GRAPHIC_0; + else if (mHwcLayer == 1) + hdmiLayer = SecHdmi::HDMI_LAYER_GRAPHIC_1; + else +#ifdef SUPPORT_G2D_UI_MODE + hdmiLayer = SecHdmi::HDMI_LAYER_GRAPHIC_1; +#else + hdmiLayer = SecHdmi::HDMI_LAYER_VIDEO; +#endif + + if (mHwcLayer == 0) { + for (int layer = SecHdmi::HDMI_LAYER_BASE + 1; layer < SecHdmi::HDMI_LAYER_MAX; layer++) { + if (layer != mUILayerMode) { + if (mSecHdmi.clear(layer) == false) + LOGE("%s::mSecHdmi.clear(%d) fail", __func__, layer); + } + } + } + + if (mUILayerMode != hdmiLayer) { + if (mSecHdmi.clear(mUILayerMode) == false) + LOGE("%s::mSecHdmi.clear(%d) fail", __func__, mUILayerMode); + } + + mUILayerMode = hdmiLayer; + +#if !defined(BOARD_USES_HDMI_SUBTITLES) + if (mHwcLayer == 0) +#endif +#if (DIRECT_UI_RENDERING == 1) + { +#ifdef CHECK_UI_TIME + start = systemTime(); +#endif + if (mSecHdmi.flush(w, h, colorFormat, pPhyYAddr, pPhyCbAddr, pPhyCrAddr, dstX, dstY, + mUILayerMode, mHwcLayer) == false) + LOGE("%s::mSecHdmi.flush() on HDMI_MODE_UI fail", __func__); +#ifdef CHECK_UI_TIME + end = systemTime(); + LOGD("[UI] mSecHdmi.flush[end-start] = %ld ms", long(ns2ms(end)) - long(ns2ms(start))); +#endif + } +#else + { + msg = new SecHdmiEventMsg(&mSecHdmi, w, h, colorFormat, pPhyYAddr, pPhyCbAddr, pPhyCrAddr, + dstX, dstY, mUILayerMode, mHwcLayer, HDMI_MODE_UI); + + /* post to HdmiEventQueue */ + mHdmiEventQueue.postMessage(msg, 0, 0); + } +#endif + break; + + case HDMI_MODE_VIDEO : +#if (DIRECT_VIDEO_RENDERING == 1) +#ifdef CHECK_VIDEO_TIME + start = systemTime(); +#endif + if (mSecHdmi.flush(w, h, colorFormat, pPhyYAddr, pPhyCbAddr, pPhyCrAddr, dstX, dstY, + SecHdmi::HDMI_LAYER_VIDEO, mHwcLayer) == false) + LOGE("%s::mSecHdmi.flush() on HDMI_MODE_VIDEO fail", __func__); +#ifdef CHECK_VIDEO_TIME + end = systemTime(); + LOGD("[Video] mSecHdmi.flush[end-start] = %ld ms", long(ns2ms(end)) - long(ns2ms(start))); +#endif +#else + msg = new SecHdmiEventMsg(&mSecHdmi, w, h, colorFormat, pPhyYAddr, pPhyCbAddr, pPhyCrAddr, + dstX, dstY, SecHdmi::HDMI_LAYER_VIDEO, mHwcLayer, HDMI_MODE_VIDEO); + + /* post to HdmiEventQueue */ + mHdmiEventQueue.postMessage(msg, 0, 0); +#endif + break; + + default: + LOGE("unmatched HDMI_MODE : %d", hdmiMode); + break; + } + + return; + } + + bool SecTVOutService::hdmiCableInserted(void) + { + return mHdmiCableInserted; + } + +} diff --git a/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.h b/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.h new file mode 100644 index 0000000..caece73 --- /dev/null +++ b/exynos5/hal/libhdmi/libhdmiservice/SecTVOutService.h @@ -0,0 +1,175 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** Copyright 2010, 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. +*/ + +/* +** +** @author Taikyung, Yu(taikyung.yu@samsung.com) +** @date 2011-07-06 +*/ + +#ifndef SECTVOUTSERVICE_H +#define SECTVOUTSERVICE_H + +#include +#include +#include +#include + +#include "ISecTVOut.h" +#include "SecHdmi.h" +#include "sec_format.h" +#include "sec_utils_v4l2.h" +#include "MessageQueue.h" + +namespace android { +//#define CHECK_VIDEO_TIME +//#define CHECK_UI_TIME + + class SecTVOutService : public BBinder + { + public : + enum { + HDMI_MODE_NONE = 0, + HDMI_MODE_UI, + HDMI_MODE_VIDEO, + }; + + mutable Mutex mLock; + + class HDMIFlushThread : public Thread { + SecTVOutService *mTVOutService; + public: + HDMIFlushThread(SecTVOutService *service): + Thread(false), + mTVOutService(service) { } + virtual void onFirstRef() { + run("HDMIFlushThread", PRIORITY_URGENT_DISPLAY); + } + virtual bool threadLoop() { + mTVOutService->HdmiFlushThread(); + return false; + } + }; + + sp mHdmiFlushThread; + int HdmiFlushThread(); + + mutable MessageQueue mHdmiEventQueue; + bool mExitHdmiFlushThread; + + SecTVOutService(); + static int instantiate (); + virtual status_t onTransact(uint32_t, const Parcel &, Parcel *, uint32_t); + virtual ~SecTVOutService (); + + virtual void setHdmiStatus(uint32_t status); + virtual void setHdmiMode(uint32_t mode); + virtual void setHdmiResolution(uint32_t resolution); + virtual void setHdmiHdcp(uint32_t enHdcp); + virtual void setHdmiRotate(uint32_t rotVal, uint32_t hwcLayer); + virtual void setHdmiHwcLayer(uint32_t hwcLayer); + virtual void blit2Hdmi(uint32_t w, uint32_t h, + uint32_t colorFormat, + uint32_t pPhyYAddr, uint32_t pPhyCbAddr, uint32_t pPhyCrAddr, + uint32_t dstX, uint32_t dstY, + uint32_t hdmiMode, uint32_t num_of_hwc_layer); + bool hdmiCableInserted(void); + void setLCDsize(void); + + private: + SecHdmi mSecHdmi; + bool mHdmiCableInserted; + int mUILayerMode; + uint32_t mLCD_width, mLCD_height; + uint32_t mHwcLayer; + }; + + class SecHdmiEventMsg : public MessageBase { + public: + enum { + HDMI_MODE_NONE = 0, + HDMI_MODE_UI, + HDMI_MODE_VIDEO, + }; + + mutable Mutex mBlitLock; + + SecHdmi *pSecHdmi; + uint32_t mSrcWidth, mSrcHeight; + uint32_t mSrcColorFormat; + uint32_t mSrcYAddr, mSrcCbAddr, mSrcCrAddr; + uint32_t mDstX, mDstY; + uint32_t mHdmiMode; + uint32_t mHdmiLayer, mHwcLayer; + + SecHdmiEventMsg(SecHdmi *SecHdmi, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcColorFormat, + uint32_t srcYAddr, uint32_t srcCbAddr, uint32_t srcCrAddr, + uint32_t dstX, uint32_t dstY, uint32_t hdmiLayer, uint32_t hwcLayer, uint32_t hdmiMode) + : pSecHdmi(SecHdmi), mSrcWidth(srcWidth), mSrcHeight(srcHeight), mSrcColorFormat(srcColorFormat), + mSrcYAddr(srcYAddr), mSrcCbAddr(srcCbAddr), mSrcCrAddr(srcCrAddr), + mDstX(dstX), mDstY(dstY), mHdmiLayer(hdmiLayer), mHwcLayer(hwcLayer), mHdmiMode(hdmiMode) { + } + + virtual bool handler() { + Mutex::Autolock _l(mBlitLock); + bool ret = true; +#if defined(CHECK_UI_TIME) || defined(CHECK_VIDEO_TIME) + nsecs_t start, end; +#endif + + switch (mHdmiMode) { + case HDMI_MODE_UI: +#ifdef CHECK_UI_TIME + start = systemTime(); +#endif + if (pSecHdmi->flush(mSrcWidth, mSrcHeight, mSrcColorFormat, mSrcYAddr, mSrcCbAddr, mSrcCrAddr, + mDstX, mDstY, mHdmiLayer, mHwcLayer) == false) { + LOGE("%s::pSecHdmi->flush() fail on HDMI_MODE_UI", __func__); + ret = false; + } + +#ifdef CHECK_UI_TIME + end = systemTime(); + LOGD("[UI] pSecHdmi->flush[end-start] = %ld ms", long(ns2ms(end)) - long(ns2ms(start))); +#endif + break; + case HDMI_MODE_VIDEO: +#ifdef CHECK_VIDEO_TIME + start = systemTime(); +#endif + if (pSecHdmi->flush(mSrcWidth, mSrcHeight, mSrcColorFormat, mSrcYAddr, mSrcCbAddr, mSrcCrAddr, + mDstX, mDstY, mHdmiLayer, mHwcLayer) == false) { + LOGE("%s::pSecHdmi->flush() fail on HDMI_MODE_VIDEO", __func__); + ret = false; + } +#ifdef CHECK_VIDEO_TIME + end = systemTime(); + LOGD("[VIDEO] pSecHdmi->flush[end-start] = %ld ms", long(ns2ms(end)) - long(ns2ms(start))); +#endif + break; + default: + LOGE("Undefined HDMI_MODE"); + ret = false; + break; + } + return ret; + } + }; + +}; +#endif diff --git a/exynos5/hal/libhdmi/libsForhdmi/Android.mk b/exynos5/hal/libhdmi/libsForhdmi/Android.mk new file mode 100644 index 0000000..9acbc52 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/Android.mk @@ -0,0 +1,17 @@ +# 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. + +ifeq ($(filter-out exynos5,$(TARGET_BOARD_PLATFORM)),) +include $(all-subdir-makefiles) +endif diff --git a/exynos5/hal/libhdmi/libsForhdmi/libcec/Android.mk b/exynos5/hal/libhdmi/libsForhdmi/libcec/Android.mk new file mode 100644 index 0000000..9a4b721 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libcec/Android.mk @@ -0,0 +1,33 @@ +# 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. + +ifeq ($(BOARD_USES_HDMI),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := eng + +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := libcec.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/../../../include + +LOCAL_MODULE := libcec +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/exynos5/hal/libhdmi/libsForhdmi/libcec/cec.h b/exynos5/hal/libhdmi/libsForhdmi/libcec/cec.h new file mode 100644 index 0000000..4b0d3af --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libcec/cec.h @@ -0,0 +1,11 @@ +#ifndef _LINUX_CEC_H_ +#define _LINUX_CEC_H_ + +#define CEC_IOC_MAGIC 'c' + +/** + * CEC device request code to set logical address. + */ +#define CEC_IOC_SETLADDR _IOW(CEC_IOC_MAGIC, 0, unsigned int) + +#endif /* _LINUX_CEC_H_ */ diff --git a/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.c b/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.c new file mode 100644 index 0000000..e688051 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.c @@ -0,0 +1,386 @@ +/* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* drv. header */ +#include "cec.h" + +#include "libcec.h" + +#define CEC_DEBUG 0 + +/** + * @def CEC_DEVICE_NAME + * Defines simbolic name of the CEC device. + */ +#define CEC_DEVICE_NAME "/dev/CEC" + +static struct { + enum CECDeviceType devtype; + unsigned char laddr; +} laddresses[] = { + { CEC_DEVICE_RECODER, 1 }, + { CEC_DEVICE_RECODER, 2 }, + { CEC_DEVICE_TUNER, 3 }, + { CEC_DEVICE_PLAYER, 4 }, + { CEC_DEVICE_AUDIO, 5 }, + { CEC_DEVICE_TUNER, 6 }, + { CEC_DEVICE_TUNER, 7 }, + { CEC_DEVICE_PLAYER, 8 }, + { CEC_DEVICE_RECODER, 9 }, + { CEC_DEVICE_TUNER, 10 }, + { CEC_DEVICE_PLAYER, 11 }, +}; + +static int CECSetLogicalAddr(unsigned int laddr); + +#ifdef CEC_DEBUG +inline static void CECPrintFrame(unsigned char *buffer, unsigned int size); +#endif + +static int fd = -1; + +/** + * Open device driver and assign CEC file descriptor. + * + * @return If success to assign CEC file descriptor, return 1; otherwise, return 0. + */ +int CECOpen() +{ + int res = 1; + + if (fd != -1) + CECClose(); + + if ((fd = open(CEC_DEVICE_NAME, O_RDWR)) < 0) { + LOGE("Can't open %s!\n", CEC_DEVICE_NAME); + res = 0; + } + + return res; +} + +/** + * Close CEC file descriptor. + * + * @return If success to close CEC file descriptor, return 1; otherwise, return 0. + */ +int CECClose() +{ + int res = 1; + + if (fd != -1) { + if (close(fd) != 0) { + LOGE("close() failed!\n"); + res = 0; + } + fd = -1; + } + + return res; +} + +/** + * Allocate logical address. + * + * @param paddr [in] CEC device physical address. + * @param devtype [in] CEC device type. + * + * @return new logical address, or 0 if an arror occured. + */ +int CECAllocLogicalAddress(int paddr, enum CECDeviceType devtype) +{ + unsigned char laddr = CEC_LADDR_UNREGISTERED; + int i = 0; + + if (fd == -1) { + LOGE("open device first!\n"); + return 0; + } + + if (CECSetLogicalAddr(laddr) < 0) { + LOGE("CECSetLogicalAddr() failed!\n"); + return 0; + } + + if (paddr == CEC_NOT_VALID_PHYSICAL_ADDRESS) + return CEC_LADDR_UNREGISTERED; + + /* send "Polling Message" */ + while (i < sizeof(laddresses)/sizeof(laddresses[0])) { + if (laddresses[i].devtype == devtype) { + unsigned char _laddr = laddresses[i].laddr; + unsigned char message = ((_laddr << 4) | _laddr); + if (CECSendMessage(&message, 1) != 1) { + laddr = _laddr; + break; + } + } + i++; + } + + if (laddr == CEC_LADDR_UNREGISTERED) { + LOGE("All LA addresses in use!!!\n"); + return CEC_LADDR_UNREGISTERED; + } + + if (CECSetLogicalAddr(laddr) < 0) { + LOGE("CECSetLogicalAddr() failed!\n"); + return 0; + } + + /* broadcast "Report Physical Address" */ + unsigned char buffer[5]; + buffer[0] = (laddr << 4) | CEC_MSG_BROADCAST; + buffer[1] = CEC_OPCODE_REPORT_PHYSICAL_ADDRESS; + buffer[2] = (paddr >> 8) & 0xFF; + buffer[3] = paddr & 0xFF; + buffer[4] = devtype; + + if (CECSendMessage(buffer, 5) != 5) { + LOGE("CECSendMessage() failed!\n"); + return 0; + } + + return laddr; +} + +/** + * Send CEC message. + * + * @param *buffer [in] pointer to buffer address where message located. + * @param size [in] message size. + * + * @return number of bytes written, or 0 if an arror occured. + */ +int CECSendMessage(unsigned char *buffer, int size) +{ + if (fd == -1) { + LOGE("open device first!\n"); + return 0; + } + + if (size > CEC_MAX_FRAME_SIZE) { + LOGE("size should not exceed %d\n", CEC_MAX_FRAME_SIZE); + return 0; + } + +#if CEC_DEBUG + LOGI("CECSendMessage() : "); + CECPrintFrame(buffer, size); +#endif + + return write(fd, buffer, size); +} + +/** + * Receive CEC message. + * + * @param *buffer [in] pointer to buffer address where message will be stored. + * @param size [in] buffer size. + * @param timeout [in] timeout in microseconds. + * + * @return number of bytes received, or 0 if an arror occured. + */ +int CECReceiveMessage(unsigned char *buffer, int size, long timeout) +{ + int bytes = 0; + fd_set rfds; + struct timeval tv; + int retval; + + if (fd == -1) { + LOGE("open device first!\n"); + return 0; + } + + tv.tv_sec = 0; + tv.tv_usec = timeout; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + retval = select(fd + 1, &rfds, NULL, NULL, &tv); + + if (retval == -1) { + return 0; + } else if (retval) { + bytes = read(fd, buffer, size); +#if CEC_DEBUG + LOGI("CECReceiveMessage() : size(%d)", bytes); + if(bytes > 0) + CECPrintFrame(buffer, bytes); +#endif + } + + return bytes; +} + +/** + * Set CEC logical address. + * + * @return 1 if success, otherwise, return 0. + */ +int CECSetLogicalAddr(unsigned int laddr) +{ + if (ioctl(fd, CEC_IOC_SETLADDR, &laddr)) { + LOGE("ioctl(CEC_IOC_SETLA) failed!\n"); + return 0; + } + + return 1; +} + +#if CEC_DEBUG +/** + * Print CEC frame. + */ +void CECPrintFrame(unsigned char *buffer, unsigned int size) +{ + if (size > 0) { + int i; + LOGI("fsize: %d ", size); + LOGI("frame: "); + for (i = 0; i < size; i++) + LOGI("0x%02x ", buffer[i]); + + LOGI("\n"); + } +} +#endif + +/** + * Check CEC message. + * + * @param opcode [in] pointer to buffer address where message will be stored. + * @param lsrc [in] buffer size. + * + * @return 1 if message should be ignored, otherwise, return 0. + */ +//TODO: not finished +int CECIgnoreMessage(unsigned char opcode, unsigned char lsrc) +{ + int retval = 0; + + /* if a message coming from address 15 (unregistered) */ + if (lsrc == CEC_LADDR_UNREGISTERED) { + switch (opcode) { + case CEC_OPCODE_DECK_CONTROL: + case CEC_OPCODE_PLAY: + retval = 1; + default: + break; + } + } + + return retval; +} + +/** + * Check CEC message. + * + * @param opcode [in] pointer to buffer address where message will be stored. + * @param size [in] message size. + * + * @return 0 if message should be ignored, otherwise, return 1. + */ +//TODO: not finished +int CECCheckMessageSize(unsigned char opcode, int size) +{ + int retval = 1; + + switch (opcode) { + case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: + if (size != 1) + retval = 0; + break; + case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE: + if (size != 2) + retval = 0; + break; + case CEC_OPCODE_PLAY: + case CEC_OPCODE_DECK_CONTROL: + case CEC_OPCODE_SET_MENU_LANGUAGE: + case CEC_OPCODE_ACTIVE_SOURCE: + case CEC_OPCODE_ROUTING_INFORMATION: + case CEC_OPCODE_SET_STREAM_PATH: + if (size != 3) + retval = 0; + break; + case CEC_OPCODE_FEATURE_ABORT: + case CEC_OPCODE_DEVICE_VENDOR_ID: + case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS: + if (size != 4) + retval = 0; + break; + case CEC_OPCODE_ROUTING_CHANGE: + if (size != 5) + retval = 0; + break; + /* CDC - 1.4 */ + case 0xf8: + if (!(size > 5 && size <= 16)) + retval = 0; + break; + default: + break; + } + + return retval; +} + +/** + * Check CEC message. + * + * @param opcode [in] pointer to buffer address where message will be stored. + * @param broadcast [in] broadcast/direct message. + * + * @return 0 if message should be ignored, otherwise, return 1. + */ +//TODO: not finished +int CECCheckMessageMode(unsigned char opcode, int broadcast) +{ + int retval = 1; + + switch (opcode) { + case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: + case CEC_OPCODE_SET_MENU_LANGUAGE: + case CEC_OPCODE_ACTIVE_SOURCE: + if (!broadcast) + retval = 0; + break; + case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: + case CEC_OPCODE_DECK_CONTROL: + case CEC_OPCODE_PLAY: + case CEC_OPCODE_FEATURE_ABORT: + case CEC_OPCODE_ABORT: + if (broadcast) + retval = 0; + break; + default: + break; + } + + return retval; +} diff --git a/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.h b/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.h new file mode 100644 index 0000000..5bbfc15 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libcec/libcec.h @@ -0,0 +1,209 @@ +/* + * 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. + */ + +#ifndef _LIBCEC_H_ +#define _LIBCEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum CEC frame size */ +#define CEC_MAX_FRAME_SIZE 16 +/** Not valid CEC physical address */ +#define CEC_NOT_VALID_PHYSICAL_ADDRESS 0xFFFF + +/** CEC broadcast address (as destination address) */ +#define CEC_MSG_BROADCAST 0x0F +/** CEC unregistered address (as initiator address) */ +#define CEC_LADDR_UNREGISTERED 0x0F + +/* + * CEC Messages + */ + +//@{ +/** @name Messages for the One Touch Play Feature */ +#define CEC_OPCODE_ACTIVE_SOURCE 0x82 +#define CEC_OPCODE_IMAGE_VIEW_ON 0x04 +#define CEC_OPCODE_TEXT_VIEW_ON 0x0D +//@} + +//@{ +/** @name Messages for the Routing Control Feature */ +#define CEC_OPCODE_INACTIVE_SOURCE 0x9D +#define CEC_OPCODE_REQUEST_ACTIVE_SOURCE 0x85 +#define CEC_OPCODE_ROUTING_CHANGE 0x80 +#define CEC_OPCODE_ROUTING_INFORMATION 0x81 +#define CEC_OPCODE_SET_STREAM_PATH 0x86 +//@} + +//@{ +/** @name Messages for the Standby Feature */ +#define CEC_OPCODE_STANDBY 0x36 +//@} + +//@{ +/** @name Messages for the One Touch Record Feature */ +#define CEC_OPCODE_RECORD_OFF 0x0B +#define CEC_OPCODE_RECORD_ON 0x09 +#define CEC_OPCODE_RECORD_STATUS 0x0A +#define CEC_OPCODE_RECORD_TV_SCREEN 0x0F +//@} + +//@{ +/** @name Messages for the Timer Programming Feature */ +#define CEC_OPCODE_CLEAR_ANALOGUE_TIMER 0x33 +#define CEC_OPCODE_CLEAR_DIGITAL_TIMER 0x99 +#define CEC_OPCODE_CLEAR_EXTERNAL_TIMER 0xA1 +#define CEC_OPCODE_SET_ANALOGUE_TIMER 0x34 +#define CEC_OPCODE_SET_DIGITAL_TIMER 0x97 +#define CEC_OPCODE_SET_EXTERNAL_TIMER 0xA2 +#define CEC_OPCODE_SET_TIMER_PROGRAM_TITLE 0x67 +#define CEC_OPCODE_TIMER_CLEARED_STATUS 0x43 +#define CEC_OPCODE_TIMER_STATUS 0x35 +//@} + +//@{ +/** @name Messages for the System Information Feature */ +#define CEC_OPCODE_CEC_VERSION 0x9E +#define CEC_OPCODE_GET_CEC_VERSION 0x9F +#define CEC_OPCODE_GIVE_PHYSICAL_ADDRESS 0x83 +#define CEC_OPCODE_GET_MENU_LANGUAGE 0x91 +//#define CEC_OPCODE_POLLING_MESSAGE +#define CEC_OPCODE_REPORT_PHYSICAL_ADDRESS 0x84 +#define CEC_OPCODE_SET_MENU_LANGUAGE 0x32 +//@} + +//@{ +/** @name Messages for the Deck Control Feature */ +#define CEC_OPCODE_DECK_CONTROL 0x42 +#define CEC_OPCODE_DECK_STATUS 0x1B +#define CEC_OPCODE_GIVE_DECK_STATUS 0x1A +#define CEC_OPCODE_PLAY 0x41 +//@} + +//@{ +/** @name Messages for the Tuner Control Feature */ +#define CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS 0x08 +#define CEC_OPCODE_SELECT_ANALOGUE_SERVICE 0x92 +#define CEC_OPCODE_SELECT_DIGITAL_SERVICE 0x93 +#define CEC_OPCODE_TUNER_DEVICE_STATUS 0x07 +#define CEC_OPCODE_TUNER_STEP_DECREMENT 0x06 +#define CEC_OPCODE_TUNER_STEP_INCREMENT 0x05 +//@} + +//@{ +/** @name Messages for the Vendor Specific Commands Feature */ +#define CEC_OPCODE_DEVICE_VENDOR_ID 0x87 +#define CEC_OPCODE_GET_DEVICE_VENDOR_ID 0x8C +#define CEC_OPCODE_VENDOR_COMMAND 0x89 +#define CEC_OPCODE_VENDOR_COMMAND_WITH_ID 0xA0 +#define CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN 0x8A +#define CEC_OPCODE_VENDOR_REMOVE_BUTTON_UP 0x8B +//@} + +//@{ +/** @name Messages for the OSD Display Feature */ +#define CEC_OPCODE_SET_OSD_STRING 0x64 +//@} + +//@{ +/** @name Messages for the Device OSD Transfer Feature */ +#define CEC_OPCODE_GIVE_OSD_NAME 0x46 +#define CEC_OPCODE_SET_OSD_NAME 0x47 +//@} + +//@{ +/** @name Messages for the Device Menu Control Feature */ +#define CEC_OPCODE_MENU_REQUEST 0x8D +#define CEC_OPCODE_MENU_STATUS 0x8E +#define CEC_OPCODE_USER_CONTROL_PRESSED 0x44 +#define CEC_OPCODE_USER_CONTROL_RELEASED 0x45 +//@} + +//@{ +/** @name Messages for the Remote Control Passthrough Feature */ +//@} + +//@{ +/** @name Messages for the Power Status Feature */ +#define CEC_OPCODE_GIVE_DEVICE_POWER_STATUS 0x8F +#define CEC_OPCODE_REPORT_POWER_STATUS 0x90 +//@} + +//@{ +/** @name Messages for General Protocol messages */ +#define CEC_OPCODE_FEATURE_ABORT 0x00 +#define CEC_OPCODE_ABORT 0xFF +//@} + +//@{ +/** @name Messages for the System Audio Control Feature */ +#define CEC_OPCODE_GIVE_AUDIO_STATUS 0x71 +#define CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS 0x7D +#define CEC_OPCODE_REPORT_AUDIO_STATUS 0x7A +#define CEC_OPCODE_SET_SYSTEM_AUDIO_MODE 0x72 +#define CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST 0x70 +#define CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS 0x7E +//@} + +//@{ +/** @name Messages for the Audio Rate Control Feature */ +#define CEC_OPCODE_SET_AUDIO_RATE 0x9A +//@} + +//@{ +/** @name CEC Operands */ + +//TODO: not finished + +#define CEC_DECK_CONTROL_MODE_STOP 0x03 +#define CEC_PLAY_MODE_PLAY_FORWARD 0x24 +//@} + +/** + * @enum CECDeviceType + * Type of CEC device + */ +enum CECDeviceType { + /** TV */ + CEC_DEVICE_TV, + /** Recording Device */ + CEC_DEVICE_RECODER, + /** Tuner */ + CEC_DEVICE_TUNER, + /** Playback Device */ + CEC_DEVICE_PLAYER, + /** Audio System */ + CEC_DEVICE_AUDIO, +}; + +int CECOpen(); +int CECClose(); +int CECAllocLogicalAddress(int paddr, enum CECDeviceType devtype); +int CECSendMessage(unsigned char *buffer, int size); +int CECReceiveMessage(unsigned char *buffer, int size, long timeout); + +int CECIgnoreMessage(unsigned char opcode, unsigned char lsrc); +int CECCheckMessageSize(unsigned char opcode, int size); +int CECCheckMessageMode(unsigned char opcode, int broadcast); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBCEC_H_ */ diff --git a/exynos5/hal/libhdmi/libsForhdmi/libddc/Android.mk b/exynos5/hal/libhdmi/libsForhdmi/libddc/Android.mk new file mode 100644 index 0000000..99af09b --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libddc/Android.mk @@ -0,0 +1,49 @@ +# 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. + +ifeq ($(BOARD_USES_HDMI),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := eng + +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := libddc.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/../../../include + +ifeq ($(BOARD_HDMI_DDC_CH), DDC_CH_I2C_7) +LOCAL_CFLAGS += -DDDC_CH_I2C_7 +endif + +ifeq ($(BOARD_HDMI_DDC_CH), DDC_CH_I2C_1) +LOCAL_CFLAGS += -DDDC_CH_I2C_1 +endif + +ifeq ($(BOARD_HDMI_DDC_CH), DDC_CH_I2C_2) +LOCAL_CFLAGS += -DDDC_CH_I2C_2 +endif + +ifeq ($(BOARD_HDMI_DDC_CH), DDC_CH_I2C_8) +LOCAL_CFLAGS += -DDDC_CH_I2C_8 +endif + +LOCAL_MODULE := libddc +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.c b/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.c new file mode 100644 index 0000000..4e6e374 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.c @@ -0,0 +1,289 @@ +/* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "i2c-dev.h" + +#include "libddc.h" + +#define DDC_DEBUG 0 + +/** + * @brief DDC device name. + * User should change this. + */ +#ifdef DDC_CH_I2C_1 +#define DEV_NAME "/dev/i2c-1" +#endif + +#ifdef DDC_CH_I2C_2 +#define DEV_NAME "/dev/i2c-2" +#endif + +#ifdef DDC_CH_I2C_7 +#define DEV_NAME "/dev/i2c-7" +#endif + +#ifdef DDC_CH_I2C_8 +#define DEV_NAME "/dev/i2c-8" +#endif + +/** + * DDC file descriptor + */ +static int ddc_fd = -1; + +/** + * Reference count of DDC file descriptor + */ +static unsigned int ref_cnt = 0; + +/** + * Check if DDC file is already opened or not + * @return If DDC file is already opened, return 1; Otherwise, return 0. + */ +static int DDCFileAvailable() +{ + return (ddc_fd < 0) ? 0 : 1; +} + +/** + * Initialze DDC library. Open DDC device + * @return If succeed in opening DDC device or it is already opened, return 1;@n + * Otherwise, return 0. + */ +int DDCOpen() +{ + int ret = 1; + + // check already open?? + if (ref_cnt > 0) { + ref_cnt++; + return 1; + } + + // open + if ((ddc_fd = open(DEV_NAME,O_RDWR)) < 0) { + LOGE("%s: Cannot open I2C_DDC : %s",__func__, DEV_NAME); + ret = 0; + } + + ref_cnt++; + return ret; +} + +/** + * Finalize DDC library. Close DDC device + * @return If succeed in closing DDC device or it is being used yet, return 1;@n + * Otherwise, return 0. + */ +int DDCClose() +{ + int ret = 1; + // check if fd is available + if (ref_cnt == 0) { +#if DDC_DEBUG + LOGE("%s: I2C_DDC is not available!!!!", __func__); +#endif + return 1; + } + + // close + if (ref_cnt > 1) { + ref_cnt--; + return 1; + } + + if (close(ddc_fd) < 0) { +#if DDC_DEBUG + LOGE("%s: Cannot close I2C_DDC : %s",__func__,DEV_NAME); +#endif + ret = 0; + } + + ref_cnt--; + ddc_fd = -1; + + return ret; +} + +/** + * Read data though DDC. For more information of DDC, refer DDC Spec. + * @param addr [in] Device address + * @param offset [in] Byte offset + * @param size [in] Sizes of data + * @param buffer [out] Pointer to buffer to store data + * @return If succeed in reading, return 1; Otherwise, return 0. + */ +int DDCRead(unsigned char addr, unsigned char offset, + unsigned int size, unsigned char* buffer) +{ + struct i2c_rdwr_ioctl_data msgset; + struct i2c_msg msgs[2]; + int ret = 1; + + if (!DDCFileAvailable()) { +#if DDC_DEBUG + LOGE("%s: I2C_DDC is not available!!!!", __func__); +#endif + return 0; + } + + // set offset + msgs[0].addr = addr>>1; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &offset; + + // read data + msgs[1].addr = addr>>1; + msgs[1].flags = I2C_M_RD; + msgs[1].len = size; + msgs[1].buf = buffer; + + // set rdwr ioctl data + msgset.nmsgs = 2; + msgset.msgs = msgs; + + // i2c fast read + if ((ret = ioctl(ddc_fd, I2C_RDWR, &msgset)) < 0) { + perror("ddc error:"); + ret = 0; + } + + return ret; +} + +/** + * Read data though E-DDC. For more information of E-DDC, refer E-DDC Spec. + * @param segpointer [in] Segment pointer + * @param segment [in] Segment number + * @param addr [in] Device address + * @param offset [in] Byte offset + * @param size [in] Sizes of data + * @param buffer [out] Pointer to buffer to store data + * @return If succeed in reading, return 1; Otherwise, return 0. + */ + +int EDDCRead(unsigned char segpointer, unsigned char segment, unsigned char addr, + unsigned char offset, unsigned int size, unsigned char* buffer) +{ + struct i2c_rdwr_ioctl_data msgset; + struct i2c_msg msgs[3]; + int ret = 1; + + if (!DDCFileAvailable()) { +#if DDC_DEBUG + LOGE("%s: I2C_DDC is not available!!!!", __func__); +#endif + return 0; + } + + // set segment pointer + msgs[0].addr = segpointer>>1; + // ignore ack only if segment is "0" + if (segment == 0) + msgs[0].flags = I2C_M_IGNORE_NAK; + else + msgs[0].flags = 0; + + msgs[0].len = 1; + msgs[0].buf = &segment; + + // set offset + msgs[1].addr = addr>>1; + msgs[1].flags = 0; + msgs[1].len = 1; + msgs[1].buf = &offset; + + // read data + msgs[2].addr = addr>>1; + msgs[2].flags = I2C_M_RD; + msgs[2].len = size; + msgs[2].buf = buffer; + + msgset.nmsgs = 3; + msgset.msgs = msgs; + + // eddc read + if (ioctl(ddc_fd, I2C_RDWR, &msgset) < 0) { +#if DDC_DEBUG + LOGE("%s: ioctl(I2C_RDWR) failed!!!", __func__); +#endif + ret = 0; + } + return ret; +} + +/** + * Write data though DDC. For more information of DDC, refer DDC Spec. + * @param addr [in] Device address + * @param offset [in] Byte offset + * @param size [in] Sizes of data + * @param buffer [out] Pointer to buffer to write + * @return If succeed in writing, return 1; Otherwise, return 0. + */ +int DDCWrite(unsigned char addr, unsigned char offset, unsigned int size, unsigned char* buffer) +{ + unsigned char* temp; + int bytes; + int retval = 0; + + // allocate temporary buffer + temp = (unsigned char*) malloc((size+1)*sizeof(unsigned char)); + if (!temp) { + LOGE("%s: not enough resources at %s", __FUNCTION__); + goto exit; + } + + temp[0] = offset; + memcpy(temp+1,buffer,size); + + if (!DDCFileAvailable()) { + LOGE("%s: I2C_DDC is not available!!!!", __func__); + goto exit; + } + + if (ioctl(ddc_fd, I2C_SLAVE, addr>>1) < 0) { + LOGE("%s: cannot set slave address 0x%02x", __func__,addr); + goto exit; + } + + // write temp buffer + if ((bytes = write(ddc_fd,temp,size+1)) != (size+1)) { + LOGE("%s: fail to write %d bytes, only write %d bytes",__func__, size, bytes); + goto exit; + } + + retval = 1; + +exit: + // free temp buffer + if (temp) + free(temp); + + return retval; +} diff --git a/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.h b/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.h new file mode 100644 index 0000000..368855b --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libddc/libddc.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef _LIBDDC_H_ +#define _LIBDDC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int DDCOpen(); +int DDCRead(unsigned char addr, unsigned char offset, unsigned int size, unsigned char* buffer); +int DDCWrite(unsigned char addr, unsigned char offset, unsigned int size, unsigned char* buffer); +int EDDCRead(unsigned char segpointer, unsigned char segment, unsigned char addr, + unsigned char offset, unsigned int size, unsigned char* buffer); +int DDCClose(); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDDC_H_ */ diff --git a/exynos5/hal/libhdmi/libsForhdmi/libedid/Android.mk b/exynos5/hal/libhdmi/libsForhdmi/libedid/Android.mk new file mode 100644 index 0000000..602ae4d --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libedid/Android.mk @@ -0,0 +1,34 @@ +# 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. + +ifeq ($(BOARD_USES_HDMI),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := eng + +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) +LOCAL_SHARED_LIBRARIES := liblog libddc +LOCAL_SRC_FILES := libedid.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/../../../include + +LOCAL_MODULE := libedid +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/exynos5/hal/libhdmi/libsForhdmi/libedid/edid.h b/exynos5/hal/libhdmi/libsForhdmi/libedid/edid.h new file mode 100644 index 0000000..aea1309 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libedid/edid.h @@ -0,0 +1,181 @@ +/* + * 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. + */ + +#ifndef _EDID_H_ +#define _EDID_H_ + +//@{ +/** + * @name EDID Addresses + */ +#define EDID_ADDR (0xA0) +#define EDID_SEGMENT_POINTER (0x60) +//@} + +//@{ +/** + * @name EDID offset and bit values + */ +#define SIZEOFBYTE (8) +#define SIZEOFEDIDBLOCK (0x80) +#define EDID_EXTENSION_NUMBER_POS (0x7E) + +#define EDID_TIMING_EXT_TAG_ADDR_POS (0) +#define EDID_TIMING_EXT_REV_NUMBER_POS (1) +#define EDID_DETAILED_TIMING_OFFSET_POS (2) +#define EDID_DATA_BLOCK_START_POS (4) + +// for Extension Data Block +#define EDID_TIMING_EXT_TAG_VAL (0x02) +#define EDID_BLOCK_MAP_EXT_TAG_VAL (0xF0) + +#define EDID_SHORT_AUD_DEC_TAG_VAL (1<<5) +#define EDID_SHORT_VID_DEC_TAG_VAL (2<<5) +#define EDID_VSDB_TAG_VAL (3<<5) +#define EDID_SPEAKER_ALLOCATION_TAG_VAL (4<<5) +#define EDID_VESA_DTC_TAG_VAL (5<<5) +#define EDID_RESERVED_TAG_VAL (6<<5) + +#define EDID_EXTENDED_TAG_VAL (7<<5) +#define EDID_EXTENDED_COLORIMETRY_VAL (5) +#define EDID_EXTENDED_COLORIMETRY_BLOCK_LEN (3) + +#define EDID_TAG_CODE_MASK (1<<7 | 1<<6 | 1<<5) +#define EDID_DATA_BLOCK_SIZE_MASK (1<<4 | 1<<3 | 1<<2 | 1<<1 | 1<<0) + +#define EDID_VSDB_MIN_LENGTH_VAL (5) + +// for Established Timings +#define EDID_ET_POS (0x23) +#define EDID_ET_640x480p_VAL (0x20) + +// for DTD +#define EDID_DTD_START_ADDR (0x36) +#define EDID_DTD_BYTE_LENGTH (18) +#define EDID_DTD_TOTAL_LENGTH (EDID_DTD_BYTE_LENGTH*4) + +#define EDID_DTD_PIXELCLOCK_POS1 (0) +#define EDID_DTD_PIXELCLOCK_POS2 (1) + +#define EDID_DTD_HBLANK_POS1 (3) +#define EDID_DTD_HBLANK_POS2 (4) +#define EDID_DTD_HBLANK_POS2_MASK (0xF) + +#define EDID_DTD_HACTIVE_POS1 (2) +#define EDID_DTD_HACTIVE_POS2 (4) +#define EDID_DTD_HACTIVE_POS2_MASK (0xF0) + +#define EDID_DTD_VBLANK_POS1 (6) +#define EDID_DTD_VBLANK_POS2 (7) +#define EDID_DTD_VBLANK_POS2_MASK (0x0F) + +#define EDID_DTD_VACTIVE_POS1 (5) +#define EDID_DTD_VACTIVE_POS2 (7) +#define EDID_DTD_VACTIVE_POS2_MASK (0xF0) + +#define EDID_DTD_INTERLACE_POS (17) +#define EDID_DTD_INTERLACE_MASK (1<<7) + +// for SVD +#define EDID_SVD_VIC_MASK (0x7F) + +// for CS +#define EDID_COLOR_SPACE_POS (3) +#define EDID_YCBCR444_CS_MASK (1<<5) +#define EDID_YCBCR422_CS_MASK (1<<4) + +// for Color Depth +#define EDID_DC_48_VAL (1<<6) +#define EDID_DC_36_VAL (1<<5) +#define EDID_DC_30_VAL (1<<4) +#define EDID_DC_YCBCR_VAL (1<<3) + +#define EDID_DC_POS (6) +#define EDID_DC_MASK (EDID_DC_48_VAL | EDID_DC_36_VAL| EDID_DC_30_VAL | EDID_DC_YCBCR_VAL) + +// for colorimetry +#define EDID_XVYCC601_MASK (1<<0) +#define EDID_XVYCC709_MASK (1<<1) +#define EDID_EXTENDED_MASK (1<<0|1<<1|1<<2) + +// for SAD +#define SHORT_AUD_DESCRIPTOR_LPCM (1<<0) +#define SHORT_AUD_DESCRIPTOR_AC3 (1<<1) +#define SHORT_AUD_DESCRIPTOR_MPEG1 (1<<2) +#define SHORT_AUD_DESCRIPTOR_MP3 (1<<3) +#define SHORT_AUD_DESCRIPTOR_MPEG2 (1<<4) +#define SHORT_AUD_DESCRIPTOR_AAC (1<<5) +#define SHORT_AUD_DESCRIPTOR_DTS (1<<6) +#define SHORT_AUD_DESCRIPTOR_ATRAC (1<<7) + +#define EDID_SAD_CODE_MASK (1<<6 | 1<<5 | 1<<4 | 1<<3) +#define EDID_SAD_CHANNEL_MASK (1<<2 | 1<<1 | 1<<0) +#define EDID_SAD_192KHZ_MASK (1<<6) +#define EDID_SAD_176KHZ_MASK (1<<5) +#define EDID_SAD_96KHZ_MASK (1<<4) +#define EDID_SAD_88KHZ_MASK (1<<3) +#define EDID_SAD_48KHZ_MASK (1<<2) +#define EDID_SAD_44KHZ_MASK (1<<1) +#define EDID_SAD_32KHZ_MASK (1<<0) + +#define EDID_SAD_WORD_24_MASK (1<<2) +#define EDID_SAD_WORD_20_MASK (1<<1) +#define EDID_SAD_WORD_16_MASK (1<<0) + +// for CEC +#define EDID_CEC_PHYICAL_ADDR (4) + +// for 3D +#define EDID_HDMI_EXT_POS (8) +#define EDID_HDMI_VIDEO_PRESENT_MASK (1<<5) + +// latency +#define EDID_HDMI_LATENCY_MASK (1<<7|1<<6) +#define EDID_HDMI_LATENCY_POS (6) + +#define EDID_HDMI_3D_PRESENT_POS (13) +#define EDID_HDMI_3D_PRESENT_MASK (1<<7) +#define EDID_HDMI_3D_MULTI_PRESENT_MASK (1<<6 | 1<<5) +#define EDID_HDMI_3D_MULTI_PRESENT_BIT 5 + +#define EDID_3D_STRUCTURE_ONLY_EXIST (1<<5) +#define EDID_3D_STRUCTURE_MASK_EXIST (1<<6) + +#define EDID_3D_STRUCTURE_FP (0) +#define EDID_3D_STRUCTURE_FA (1) +#define EDID_3D_STRUCTURE_LA (2) +#define EDID_3D_STRUCTURE_SSF (3) +#define EDID_3D_STRUCTURE_LD (4) +#define EDID_3D_STRUCTURE_LDGFX (5) +#define EDID_3D_STRUCTURE_TB (6) +#define EDID_3D_STRUCTURE_SSH (8) + +#define EDID_HDMI_EXT_LENGTH_POS (14) +#define EDID_HDMI_VSDB_VIC_LEN_BIT (5) +#define EDID_HDMI_VSDB_VIC_LEN_MASK (1<<7|1<<6|1<<5) +#define EDID_HDMI_VSDB_3D_LEN_MASK (1<<4|1<<3|1<<2|1<<1|1<<0) + +#define EDID_HDMI_2D_VIC_ORDER_MASK (1<<7|1<<6|1<<5|1<<4) +#define EDID_HDMI_3D_STRUCTURE_MASK (1<<3|1<<2|1<<1|1<<0) + +// for MAX TMDS +#define EDID_MAX_TMDS_POS (7) + +// for 3D Structure +#define NUM_OF_VIC_FOR_3D 16 +//@} + +#endif /* _EDID_H_ */ diff --git a/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.c b/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.c new file mode 100644 index 0000000..c6bed50 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.c @@ -0,0 +1,1265 @@ +/* +* 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. +*/ + +#include +#include +#include + +#include + +#include "edid.h" +#include "libedid.h" +#include "../libddc/libddc.h" + +//#define EDID_DEBUG 1 + +#ifdef EDID_DEBUG +#define DPRINTF(args...) LOGI(args) +#else +#define DPRINTF(args...) +#endif + +#define NUM_OF_VIC_FOR_3D 16 + +/** + * @var gEdidData + * Pointer to EDID data + */ +static unsigned char* gEdidData; + +/** + * @var gExtensions + * Number of EDID extensions + */ +static int gExtensions; + + +/** + * @var aVIC + * This contains first 16 VIC in EDID + */ +static unsigned char aVIC[NUM_OF_VIC_FOR_3D]; + +//! Structure for parsing video timing parameter in EDID +static const struct edid_params { + /** H Total */ + unsigned int HTotal; + + /** H Blank */ + unsigned int HBlank; + + /** V Total */ + unsigned int VTotal; + + /** V Blank */ + unsigned int VBlank; + + /** CEA VIC */ + unsigned char VIC; + + /** CEA VIC for 16:9 aspect ratio */ + unsigned char VIC16_9; + + /** 0 if progressive, 1 if interlaced */ + unsigned char interlaced; + + /** Pixel frequency */ + enum PixelFreq PixelClock; +} aVideoParams[] = +{ + { 800 , 160 , 525 , 45, 1 , 1 , 0, PIXEL_FREQ_25_200 ,}, // v640x480p_60Hz + { 858 , 138 , 525 , 45, 2 , 3 , 0, PIXEL_FREQ_27_027 ,}, // v720x480p_60Hz + { 1650, 370 , 750 , 30, 4 , 4 , 0, PIXEL_FREQ_74_250 ,}, // v1280x720p_60Hz + { 2200, 280 , 1125, 22, 5 , 5 , 1, PIXEL_FREQ_74_250 ,}, // v1920x1080i_60H + { 1716, 276 , 525 , 22, 6 , 7 , 1, PIXEL_FREQ_74_250 ,}, // v720x480i_60Hz + { 1716, 276 , 262 , 22, 8 , 9 , 0, PIXEL_FREQ_27_027 ,}, // v720x240p_60Hz + //{ 1716, 276 , 263 , 23, 8 , 9 , 0, PIXEL_FREQ_27_027 , }, // v720x240p_60Hz(mode 2) + { 3432, 552 , 525 , 22, 10, 11, 1, PIXEL_FREQ_54_054 , }, // v2880x480i_60Hz + { 3432, 552 , 262 , 22, 12, 13, 0, PIXEL_FREQ_54_054 , }, // v2880x240p_60Hz + //{ 3432, 552 , 263 , 23, 12, 13, 0, PIXEL_FREQ_54_054 , }, // v2880x240p_60Hz(mode 2) + { 1716, 276 , 525 , 45, 14, 15, 0, PIXEL_FREQ_54_054 , }, // v1440x480p_60Hz + { 2200, 280 , 1125, 45, 16, 16, 0, PIXEL_FREQ_148_500, }, // v1920x1080p_60H + { 864 , 144 , 625 , 49, 17, 18, 0, PIXEL_FREQ_27 , }, // v720x576p_50Hz + { 1980, 700 , 750 , 30, 19, 19, 0, PIXEL_FREQ_74_250 , }, // v1280x720p_50Hz + { 2640, 720 , 1125, 22, 20, 20, 1, PIXEL_FREQ_74_250 , }, // v1920x1080i_50H + { 1728, 288 , 625 , 24, 21, 22, 1, PIXEL_FREQ_27 , }, // v720x576i_50Hz + { 1728, 288 , 312 , 24, 23, 24, 0, PIXEL_FREQ_27 , }, // v720x288p_50Hz + //{ 1728, 288 , 313 , 25, 23, 24, 0, PIXEL_FREQ_27 , }, // v720x288p_50Hz(mode 2) + //{ 1728, 288 , 314 , 26, 23, 24, 0, PIXEL_FREQ_27 , }, // v720x288p_50Hz(mode 3) + { 3456, 576 , 625 , 24, 25, 26, 1, PIXEL_FREQ_54 , }, // v2880x576i_50Hz + { 3456, 576 , 312 , 24, 27, 28, 0, PIXEL_FREQ_54 , }, // v2880x288p_50Hz + //{ 3456, 576 , 313 , 25, 27, 28, 0, PIXEL_FREQ_54 , }, // v2880x288p_50Hz(mode 2) + //{ 3456, 576 , 314 , 26, 27, 28, 0, PIXEL_FREQ_54 , }, // v2880x288p_50Hz(mode 3) + { 1728, 288 , 625 , 49, 29, 30, 0, PIXEL_FREQ_54 , }, // v1440x576p_50Hz + { 2640, 720 , 1125, 45, 31, 31, 0, PIXEL_FREQ_148_500,}, // v1920x1080p_50Hz + { 2750, 830 , 1125, 45, 32, 32, 0, PIXEL_FREQ_74_250 ,}, // v1920x1080p_24Hz + { 2640, 720 , 1125, 45, 33, 33, 0, PIXEL_FREQ_74_250 ,}, // v1920x1080p_25Hz + { 2200, 280 , 1125, 45, 34, 34, 0, PIXEL_FREQ_74_250 ,}, // v1920x1080p_30Hz + { 3432, 552 , 525 , 45, 35, 36, 0, PIXEL_FREQ_108_108,}, // v2880x480p_60Hz + { 3456, 576 , 625 , 49, 37, 38, 0, PIXEL_FREQ_108 ,}, // v2880x576p_50Hz + { 2304, 384 , 1250, 85, 39, 39, 1, PIXEL_FREQ_72 ,}, // v1920x1080i_50Hz(1250) + { 2640, 720 , 1125, 22, 40, 40, 1, PIXEL_FREQ_148_500, }, // v1920x1080i_100Hz + { 1980, 700 , 750 , 30, 41, 41, 0, PIXEL_FREQ_148_500, }, // v1280x720p_100Hz + { 864 , 144 , 625 , 49, 42, 43, 0, PIXEL_FREQ_54 , }, // v720x576p_100Hz + { 1728, 288 , 625 , 24, 44, 45, 1, PIXEL_FREQ_54 , }, // v720x576i_100Hz + { 2200, 280 , 1125, 22, 46, 46, 1, PIXEL_FREQ_148_500, }, // v1920x1080i_120Hz + { 1650, 370 , 750 , 30, 47, 47, 0, PIXEL_FREQ_148_500, }, // v1280x720p_120Hz + { 858 , 138 , 525 , 54, 48, 49, 0, PIXEL_FREQ_54_054 , }, // v720x480p_120Hz + { 1716, 276 , 525 , 22, 50, 51, 1, PIXEL_FREQ_54_054 , }, // v720x480i_120Hz + { 864 , 144 , 625 , 49, 52, 53, 0, PIXEL_FREQ_108 , }, // v720x576p_200Hz + { 1728, 288 , 625 , 24, 54, 55, 1, PIXEL_FREQ_108 , }, // v720x576i_200Hz + { 858 , 138 , 525 , 45, 56, 57, 0, PIXEL_FREQ_108_108, }, // v720x480p_240Hz + { 1716, 276 , 525 , 22, 58, 59, 1, PIXEL_FREQ_108_108, }, // v720x480i_240Hz + // PHY Freq is not available yet + //{ 3300, 2020, 750 , 30, 60, 60, 0, PIXEL_FREQ_59_400 ,}, // v1280x720p24Hz + { 3960, 2680, 750 , 30, 61, 61, 0, PIXEL_FREQ_74_250 , }, // v1280x720p25Hz + { 3300, 2020, 750 , 30, 62, 62, 0, PIXEL_FREQ_74_250 ,}, // v1280x720p30Hz + // PHY Freq is not available yet + //{ 2200, 280 , 1125, 45, 63, 63, 0, PIXEL_FREQ_297, }, // v1920x1080p120Hz + //{ 2640, 720 , 1125, 45, 64, 64, 0, PIXEL_FREQ_297, }, // v1920x1080p100Hz + //{ 4400, 560 , 2250, 90, 1, 1, 0, 0, PIXEL_FREQ_297, }, // v4Kx2K30Hz +}; + +//! Structure for Checking 3D Mandatory Format in EDID +static const struct edid_3d_mandatory { + /** video Format */ + enum VideoFormat resolution; + + /** 3D Structure */ + enum HDMI3DVideoStructure hdmi_3d_format; +} edid_3d [] = +{ + { v1920x1080p_24Hz, HDMI_3D_FP_FORMAT }, // 1920x1080p @ 23.98/24Hz + { v1280x720p_60Hz, HDMI_3D_FP_FORMAT }, // 1280x720p @ 59.94/60Hz + { v1920x1080i_60Hz, HDMI_3D_SSH_FORMAT }, // 1920x1080i @ 59.94/60Hz + { v1920x1080p_24Hz, HDMI_3D_TB_FORMAT }, // 1920x1080p @ 23.98/24Hz + { v1280x720p_60Hz, HDMI_3D_TB_FORMAT }, // 1280x720p @ 59.94/60Hz + { v1280x720p_50Hz, HDMI_3D_FP_FORMAT }, // 1280x720p @ 50Hz + { v1920x1080i_50Hz, HDMI_3D_SSH_FORMAT }, // 1920x1080i @ 50Hz + { v1280x720p_50Hz, HDMI_3D_TB_FORMAT }, // 1280x720p @ 50Hz +}; + +/** + * Calculate a checksum. + * + * @param buffer [in] Pointer to data to calculate a checksum + * @param size [in] Sizes of data + * + * @return If checksum result is 0, return 1; Otherwise, return 0. + */ +static int CalcChecksum(const unsigned char* const buffer, const int size) +{ + unsigned char i,sum; + int ret = 1; + + // check parameter + if (buffer == NULL ) { + DPRINTF("invalid parameter : buffer\n"); + return 0; + } + for (sum = 0, i = 0 ; i < size; i++) + sum += buffer[i]; + + // check checksum + if (sum != 0) + ret = 0; + + return ret; +} + +/** + * Read EDID Block(128 bytes) + * + * @param blockNum [in] Number of block to read @n + * For example, EDID block = 0, EDID first Extension = 1, and so on. + * @param outBuffer [out] Pointer to buffer to store EDID data + * + * @return If fail to read, return 0; Otherwise, return 1. + */ +static int ReadEDIDBlock(const unsigned int blockNum, unsigned char* const outBuffer) +{ + int segNum, offset, dataPtr; + + // check parameter + if (outBuffer == NULL) { + DPRINTF("invalid parameter : outBuffer\n"); + return 0; + } + + // calculate + segNum = blockNum / 2; + offset = (blockNum % 2) * SIZEOFEDIDBLOCK; + dataPtr = (blockNum) * SIZEOFEDIDBLOCK; + + // read block + if (!EDDCRead(EDID_SEGMENT_POINTER, segNum, EDID_ADDR, offset, SIZEOFEDIDBLOCK, outBuffer)) { + DPRINTF("Fail to Read %dth EDID Block\n", blockNum); + return 0; + } + + if (!CalcChecksum(outBuffer, SIZEOFEDIDBLOCK)) { + DPRINTF("CheckSum fail : %dth EDID Block\n", blockNum); + return 0; + } + + // print data +#ifdef EDID_DEBUG + offset = 0; + do { + LOGI("0x%02X", outBuffer[offset++]); + if (offset % 16) + LOGI(" "); + else + LOGI("\n"); + } while (SIZEOFEDIDBLOCK > offset); +#endif // EDID_DEBUG + return 1; +} + +/** + * Check if EDID data is valid or not. + * + * @return if EDID data is valid, return 1; Otherwise, return 0. + */ +static inline int EDIDValid(void) +{ + return (gEdidData == NULL) ? 0 : 1; +} + +/** + * Search HDMI Vender Specific Data Block(VSDB) in EDID extension block. + * + * @param extension [in] the number of EDID extension block to check + * + * @return if there is a HDMI VSDB, return the offset from start of @n + * EDID extension block. if there is no VSDB, return 0. + */ +static int GetVSDBOffset(const int extension) +{ + unsigned int BlockOffset = extension*SIZEOFEDIDBLOCK; + unsigned int offset = BlockOffset + EDID_DATA_BLOCK_START_POS; + unsigned int tag,blockLen,DTDOffset; + + if (!EDIDValid() || (extension > gExtensions)) { + DPRINTF("EDID Data is not available\n"); + return 0; + } + + DTDOffset = gEdidData[BlockOffset + EDID_DETAILED_TIMING_OFFSET_POS]; + + // check if there is HDMI VSDB + while (offset < BlockOffset + DTDOffset) { + // find the block tag and length + // tag + tag = gEdidData[offset] & EDID_TAG_CODE_MASK; + // block len + blockLen = (gEdidData[offset] & EDID_DATA_BLOCK_SIZE_MASK) + 1; + + // check if it is HDMI VSDB + // if so, check identifier value, if it's hdmi vsbd - return offset + if (tag == EDID_VSDB_TAG_VAL && + gEdidData[offset+1] == 0x03 && + gEdidData[offset+2] == 0x0C && + gEdidData[offset+3] == 0x0 && + blockLen > EDID_VSDB_MIN_LENGTH_VAL ) + return offset; + + // else find next block + offset += blockLen; + } + + // return error + return 0; +} + +/** + * Check if Sink supports the HDMI mode. + * @return If Sink supports HDMI mode, return 1; Otherwise, return 0. + */ +static int CheckHDMIMode(void) +{ + int i; + + // read EDID + if (!EDIDRead()) + return 0; + + // find VSDB + for (i = 1; i <= gExtensions; i++) + if (GetVSDBOffset(i) > 0) // if there is a VSDB, it means RX support HDMI mode + return 1; + + return 0; +} + +/** + * Check if EDID extension block is timing extension block or not. + * @param extension [in] The number of EDID extension block to check + * @return If the block is timing extension, return 1; Otherwise, return 0. + */ +static int IsTimingExtension(const int extension) +{ + int ret = 0; + if (!EDIDValid() || (extension > gExtensions)) { + DPRINTF("EDID Data is not available\n"); + return ret; + } + + if (gEdidData[extension*SIZEOFEDIDBLOCK] == EDID_TIMING_EXT_TAG_VAL) { + // check extension revsion number + // revision num == 3 + if (gEdidData[extension*SIZEOFEDIDBLOCK + EDID_TIMING_EXT_REV_NUMBER_POS] == 3) + ret = 1; + // revison num != 3 && DVI mode + else if (!CheckHDMIMode() && + gEdidData[extension*SIZEOFEDIDBLOCK + EDID_TIMING_EXT_REV_NUMBER_POS] != 2) + ret = 1; + } + return ret; +} + +/** + * Check if the video format is contained in - @n + * Detailed Timing Descriptor(DTD) of EDID extension block. + * @param extension [in] Number of EDID extension block to check + * @param videoFormat [in] Video format to check + * @return If the video format is contained in DTD of EDID extension block, -@n + * return 1; Otherwise, return 0. + */ +static int IsContainVideoDTD(const int extension,const enum VideoFormat videoFormat) +{ + int i, StartOffset, EndOffset; + + if (!EDIDValid() || (extension > gExtensions)) { + DPRINTF("EDID Data is not available\n"); + return 0; + } + + // if edid block( 0th block ) + if (extension == 0) { + StartOffset = EDID_DTD_START_ADDR; + EndOffset = StartOffset + EDID_DTD_TOTAL_LENGTH; + } else { // if edid extension block + StartOffset = extension*SIZEOFEDIDBLOCK + gEdidData[extension*SIZEOFEDIDBLOCK + EDID_DETAILED_TIMING_OFFSET_POS]; + EndOffset = (extension+1)*SIZEOFEDIDBLOCK; + } + + // check DTD(Detailed Timing Description) + for (i = StartOffset; i < EndOffset; i+= EDID_DTD_BYTE_LENGTH) { + unsigned int hblank = 0, hactive = 0, vblank = 0, vactive = 0, interlaced = 0, pixelclock = 0; + unsigned int vHActive = 0, vVActive = 0, vVBlank = 0; + + // get pixel clock + pixelclock = (gEdidData[i+EDID_DTD_PIXELCLOCK_POS2] << SIZEOFBYTE); + pixelclock |= gEdidData[i+EDID_DTD_PIXELCLOCK_POS1]; + + if (!pixelclock) + continue; + + // get HBLANK value in pixels + hblank = gEdidData[i+EDID_DTD_HBLANK_POS2] & EDID_DTD_HBLANK_POS2_MASK; + hblank <<= SIZEOFBYTE; // lower 4 bits + hblank |= gEdidData[i+EDID_DTD_HBLANK_POS1]; + + // get HACTIVE value in pixels + hactive = gEdidData[i+EDID_DTD_HACTIVE_POS2] & EDID_DTD_HACTIVE_POS2_MASK; + hactive <<= (SIZEOFBYTE/2); // upper 4 bits + hactive |= gEdidData[i+EDID_DTD_HACTIVE_POS1]; + + // get VBLANK value in pixels + vblank = gEdidData[i+EDID_DTD_VBLANK_POS2] & EDID_DTD_VBLANK_POS2_MASK; + vblank <<= SIZEOFBYTE; // lower 4 bits + vblank |= gEdidData[i+EDID_DTD_VBLANK_POS1]; + + // get VACTIVE value in pixels + vactive = gEdidData[i+EDID_DTD_VACTIVE_POS2] & EDID_DTD_VACTIVE_POS2_MASK; + vactive <<= (SIZEOFBYTE/2); // upper 4 bits + vactive |= gEdidData[i+EDID_DTD_VACTIVE_POS1]; + + vHActive = aVideoParams[videoFormat].HTotal - aVideoParams[videoFormat].HBlank; + if (aVideoParams[videoFormat].interlaced == 1) { + if (aVideoParams[videoFormat].VIC == v1920x1080i_50Hz_1250) { // VTOP and VBOT are same + vVActive = (aVideoParams[videoFormat].VTotal - aVideoParams[videoFormat].VBlank*2)/2; + vVBlank = aVideoParams[videoFormat].VBlank; + } else { + vVActive = (aVideoParams[videoFormat].VTotal - aVideoParams[videoFormat].VBlank*2 - 1)/2; + vVBlank = aVideoParams[videoFormat].VBlank; + } + } else { + vVActive = aVideoParams[videoFormat].VTotal - aVideoParams[videoFormat].VBlank; + vVBlank = aVideoParams[videoFormat].VBlank; + } + + // get Interlaced Mode Value + interlaced = (int)(gEdidData[i+EDID_DTD_INTERLACE_POS] & EDID_DTD_INTERLACE_MASK); + if (interlaced) + interlaced = 1; + + DPRINTF("EDID: hblank = %d,vblank = %d, hactive = %d, vactive = %d\n" + ,hblank,vblank,hactive,vactive); + DPRINTF("REQ: hblank = %d,vblank = %d, hactive = %d, vactive = %d\n" + ,aVideoParams[videoFormat].HBlank + ,vVBlank,vHActive,vVActive); + + if (hblank == aVideoParams[videoFormat].HBlank && vblank == vVBlank // blank + && hactive == vHActive && vactive == vVActive) { //line + unsigned int EDIDpixelclock = aVideoParams[videoFormat].PixelClock; + EDIDpixelclock /= 100; pixelclock /= 100; + + if (pixelclock == EDIDpixelclock) { + DPRINTF("Sink Support the Video mode\n"); + return 1; + } + } + } + return 0; +} + +/** + * Check if a VIC(Video Identification Code) is contained in -@n + * EDID extension block. + * @param extension [in] Number of EDID extension block to check + * @param VIC [in] VIC to check + * @return If the VIC is contained in contained in EDID extension block, -@n + * return 1; Otherwise, return 0. + */ +static int IsContainVIC(const int extension, const int VIC) +{ + unsigned int StartAddr = extension*SIZEOFEDIDBLOCK; + unsigned int ExtAddr = StartAddr + EDID_DATA_BLOCK_START_POS; + unsigned int tag,blockLen; + unsigned int DTDStartAddr = gEdidData[StartAddr + EDID_DETAILED_TIMING_OFFSET_POS]; + + if (!EDIDValid() || (extension > gExtensions)) { + DPRINTF("EDID Data is not available\n"); + return 0; + } + + // while + while (ExtAddr < StartAddr + DTDStartAddr) { + // find the block tag and length + // tag + tag = gEdidData[ExtAddr] & EDID_TAG_CODE_MASK; + // block len + blockLen = (gEdidData[ExtAddr] & EDID_DATA_BLOCK_SIZE_MASK) + 1; + DPRINTF("tag = %d\n",tag); + DPRINTF("blockLen = %d\n",blockLen-1); + + // check if it is short video description + if (tag == EDID_SHORT_VID_DEC_TAG_VAL) { + // if so, check SVD + unsigned int i; + for (i = 1; i < blockLen; i++) { + DPRINTF("EDIDVIC = %d\n",gEdidData[ExtAddr+i] & EDID_SVD_VIC_MASK); + DPRINTF("VIC = %d\n",VIC); + + // check VIC with SVDB + if (VIC == (gEdidData[ExtAddr+i] & EDID_SVD_VIC_MASK)) { + DPRINTF("Sink Device supports requested video mode\n"); + return 1; + } + } + } + // else find next block + ExtAddr += blockLen; + } + + return 0; +} + +/** + * Check if EDID contains the video format. + * @param videoFormat [in] Video format to check + * @param pixelRatio [in] Pixel aspect ratio of video format to check + * @return if EDID contains the video format, return 1; Otherwise, return 0. + */ +static int CheckResolution(const enum VideoFormat videoFormat, + const enum PixelAspectRatio pixelRatio) +{ + int i, vic; + + // read EDID + if (!EDIDRead()) + return 0; + + // check ET(Established Timings) for 640x480p@60Hz + if (videoFormat == v640x480p_60Hz // if it's 640x480p@60Hz + && (gEdidData[EDID_ET_POS] & EDID_ET_640x480p_VAL)) // it support + return 1; + + // check STI(Standard Timing Identification) + // do not need + + // check DTD(Detailed Timing Description) of EDID block(0th) + if (IsContainVideoDTD(0,videoFormat)) + return 1; + + // check EDID Extension + vic = (pixelRatio == HDMI_PIXEL_RATIO_16_9) ? + aVideoParams[videoFormat].VIC16_9 : aVideoParams[videoFormat].VIC; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i)) // if it's timing block + if (IsContainVIC(i, vic) || IsContainVideoDTD(i, videoFormat)) + return 1; + } + + return 0; +} + +/** + * Check if EDID supports the color depth. + * @param depth [in] Color depth + * @param space [in] Color space + * @return If EDID supports the color depth, return 1; Otherwise, return 0. + */ +static int CheckColorDepth(const enum ColorDepth depth,const enum ColorSpace space) +{ + int i; + unsigned int StartAddr; + + // if color depth == 24 bit, no need to check + if (depth == HDMI_CD_24) + return 1; + + // check EDID data is valid or not + // read EDID + if (!EDIDRead()) + return 0; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i) // if it's timing block + && ((StartAddr = GetVSDBOffset(i)) > 0)) { // check block + int blockLength = gEdidData[StartAddr] & EDID_DATA_BLOCK_SIZE_MASK; + if (blockLength >= EDID_DC_POS) { + // get supported DC value + int deepColor = gEdidData[StartAddr + EDID_DC_POS] & EDID_DC_MASK; + DPRINTF("EDID deepColor = %x\n",deepColor); + // check supported DeepColor + // if YCBCR444 + if (space == HDMI_CS_YCBCR444) { + if ( !(deepColor & EDID_DC_YCBCR_VAL)) + return 0; + } + + // check colorDepth + switch (depth) { + case HDMI_CD_36: + deepColor &= EDID_DC_36_VAL; + break; + case HDMI_CD_30: + deepColor &= EDID_DC_30_VAL; + break; + case HDMI_CD_24: + deepColor = 1; + break; + default : + deepColor = 0; + } + if (deepColor) + return 1; + else + return 0; + } + } + } + + return 0; +} + +/** + * Check if EDID supports the color space. + * @param space [in] Color space + * @return If EDID supports the color space, return 1; Otherwise, return 0. + */ +static int CheckColorSpace(const enum ColorSpace space) +{ + int i; + + // RGB is default + if (space == HDMI_CS_RGB) + return 1; + + // check EDID data is valid or not + // read EDID + if (!EDIDRead()) + return 0; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i)) { // if it's timing block + // read Color Space + int CS = gEdidData[i*SIZEOFEDIDBLOCK + EDID_COLOR_SPACE_POS]; + + if ((space == HDMI_CS_YCBCR444 && (CS & EDID_YCBCR444_CS_MASK)) || // YCBCR444 + (space == HDMI_CS_YCBCR422 && (CS & EDID_YCBCR422_CS_MASK))) // YCBCR422 + return 1; + } + } + return 0; +} + +/** + * Check if EDID supports the colorimetry. + * @param color [in] Colorimetry + * @return If EDID supports the colorimetry, return 1; Otherwise, return 0. + */ +static int CheckColorimetry(const enum HDMIColorimetry color) +{ + int i; + + // do not need to parse if not extended colorimetry + if (color == HDMI_COLORIMETRY_NO_DATA || + color == HDMI_COLORIMETRY_ITU601 || + color == HDMI_COLORIMETRY_ITU709) + return 1; + + // read EDID + if (!EDIDRead()) + return 0; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i)) { // if it's timing block + // check address + unsigned int ExtAddr = i*SIZEOFEDIDBLOCK + EDID_DATA_BLOCK_START_POS; + unsigned int EndAddr = i*SIZEOFEDIDBLOCK + gEdidData[i*SIZEOFEDIDBLOCK + EDID_DETAILED_TIMING_OFFSET_POS]; + unsigned int tag,blockLen; + + while (ExtAddr < EndAddr) { + // find the block tag and length + // tag + tag = gEdidData[ExtAddr] & EDID_TAG_CODE_MASK; + // block len + blockLen = (gEdidData[ExtAddr] & EDID_DATA_BLOCK_SIZE_MASK) + 1; + + // check if it is colorimetry block + if (tag == EDID_EXTENDED_TAG_VAL && // extended tag + gEdidData[ExtAddr+1] == EDID_EXTENDED_COLORIMETRY_VAL && // colorimetry block + (blockLen-1) == EDID_EXTENDED_COLORIMETRY_BLOCK_LEN) { // check length + // get supported DC value + int colorimetry = (gEdidData[ExtAddr + 2]); + int metadata = (gEdidData[ExtAddr + 3]); + + DPRINTF("EDID extened colorimetry = %x\n",colorimetry); + DPRINTF("EDID gamut metadata profile = %x\n",metadata); + + // check colorDepth + switch (color) { + case HDMI_COLORIMETRY_EXTENDED_xvYCC601: + if (colorimetry & EDID_XVYCC601_MASK && metadata) + return 1; + break; + case HDMI_COLORIMETRY_EXTENDED_xvYCC709: + if (colorimetry & EDID_XVYCC709_MASK && metadata) + return 1; + break; + default: + break; + } + return 0; + } + // else find next block + ExtAddr += blockLen; + } + } + } + + return 0; +} + +/** + * Get Max TMDS clock that HDMI Rx can receive. + * @return If available, return MaxTMDS clock; Otherwise, return 0. + */ +static unsigned int GetMaxTMDS(void) +{ + int i; + unsigned int StartAddr; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i) // if it's timing block + && ((StartAddr = GetVSDBOffset(i)) > 0)) { // check block + int blockLength = gEdidData[StartAddr] & EDID_DATA_BLOCK_SIZE_MASK; + if (blockLength >= EDID_MAX_TMDS_POS) { + // get supported DC value + return gEdidData[StartAddr + EDID_MAX_TMDS_POS]; + } + } + } + + return 0; +} + +/** + * Save first 16 VIC of EDID + */ +static void SaveVIC(void) +{ + int extension; + int vic_count = 0; + for (extension = 1; extension <= gExtensions && vic_count < NUM_OF_VIC_FOR_3D; extension++) { + unsigned int StartAddr = extension*SIZEOFEDIDBLOCK; + unsigned int ExtAddr = StartAddr + EDID_DATA_BLOCK_START_POS; + unsigned int tag,blockLen; + unsigned int DTDStartAddr = gEdidData[StartAddr + EDID_DETAILED_TIMING_OFFSET_POS]; + + while (ExtAddr < StartAddr + DTDStartAddr) { + // find the block tag and length + // tag + tag = gEdidData[ExtAddr] & EDID_TAG_CODE_MASK; + // block len + blockLen = (gEdidData[ExtAddr] & EDID_DATA_BLOCK_SIZE_MASK) + 1; + + // check if it is short video description + if (tag == EDID_SHORT_VID_DEC_TAG_VAL) { + // if so, check SVD + unsigned int edid_index; + for (edid_index = 1; edid_index < blockLen && vic_count < NUM_OF_VIC_FOR_3D; edid_index++) { + DPRINTF("EDIDVIC = %d\r\n", gEdidData[ExtAddr+edid_index] & EDID_SVD_VIC_MASK); + + // check VIC with SVDB + aVIC[vic_count++] = (gEdidData[ExtAddr+edid_index] & EDID_SVD_VIC_MASK); + } + } + // else find next block + ExtAddr += blockLen; + } + } +} + +/** + * Check if Rx supports requested 3D format. + * @param pVideo [in] HDMI Video Parameter + * @return If Rx supports requested 3D format, return 1; Otherwise, return 0. + */ +static int EDID3DFormatSupport(const struct HDMIVideoParameter * const pVideo) +{ + int edid_index; + unsigned int StartAddr; + unsigned int vic; + vic = (pVideo->pixelAspectRatio == HDMI_PIXEL_RATIO_16_9) ? + aVideoParams[pVideo->resolution].VIC16_9 : aVideoParams[pVideo->resolution].VIC; + + // if format == 2D, no need to check + if (pVideo->hdmi_3d_format == HDMI_2D_VIDEO_FORMAT) + return 1; + + // check EDID data is valid or not + if (!EDIDRead()) + return 0; + + // save first 16 VIC to check + SaveVIC(); + + // find VSDB + for (edid_index = 1; edid_index <= gExtensions; edid_index++) { + if (IsTimingExtension(edid_index) // if it's timing block + && ((StartAddr = GetVSDBOffset(edid_index)) > 0)) { // check block + unsigned int blockLength = gEdidData[StartAddr] & EDID_DATA_BLOCK_SIZE_MASK; + unsigned int VSDBHdmiVideoPre = 0; + unsigned int VSDB3DPresent = 0; + unsigned int VSDB3DMultiPresent = 0; + unsigned int HDMIVICLen; + unsigned int HDMI3DLen; + int Hdmi3DStructure = 0; + unsigned int Hdmi3DMask = 0xFFFF; + unsigned int latency_offset = 0; + + DPRINTF("VSDB Block length[0x%x] = 0x%x\r\n",StartAddr,blockLength); + + // get HDMI Video Present value + if (blockLength >= EDID_HDMI_EXT_POS) { + VSDBHdmiVideoPre = gEdidData[StartAddr + EDID_HDMI_EXT_POS] + & EDID_HDMI_VIDEO_PRESENT_MASK; + DPRINTF("EDID HDMI Video Present = 0x%x\n",VSDBHdmiVideoPre); + } else { // data related to 3D format is not available + return 0; + } + + // check if latency field is available + latency_offset = (gEdidData[StartAddr + EDID_HDMI_EXT_POS] + & EDID_HDMI_LATENCY_MASK) >> EDID_HDMI_LATENCY_POS; + if (latency_offset == 0) + latency_offset = 4; + else if (latency_offset == 3) + latency_offset = 0; + else + latency_offset = 2; + + StartAddr -= latency_offset; + + // HDMI_VIC_LEN + HDMIVICLen = (gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS] + & EDID_HDMI_VSDB_VIC_LEN_MASK) >> EDID_HDMI_VSDB_VIC_LEN_BIT; + + if (pVideo->hdmi_3d_format == HDMI_VIC_FORMAT) { + if (HDMIVICLen) { + for (edid_index = 0; edid_index < (int)HDMIVICLen; edid_index++) { + if (vic == gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + edid_index]) + return 1; + } + return 0; + } else { + return 0; + } + } + + // HDMI_3D_LEN + HDMI3DLen = gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS] + & EDID_HDMI_VSDB_3D_LEN_MASK; + + DPRINTF("HDMI VIC LENGTH[%x] = %x\r\n", + StartAddr + EDID_HDMI_EXT_LENGTH_POS, HDMIVICLen); + DPRINTF("HDMI 3D LENGTH[%x] = %x\r\n", + StartAddr + EDID_HDMI_EXT_LENGTH_POS, HDMI3DLen); + + // check 3D_Present bit + if (blockLength >= (EDID_HDMI_3D_PRESENT_POS - latency_offset)) { + VSDB3DPresent = gEdidData[StartAddr + EDID_HDMI_3D_PRESENT_POS] + & EDID_HDMI_3D_PRESENT_MASK; + VSDB3DMultiPresent = gEdidData[StartAddr + EDID_HDMI_3D_PRESENT_POS] + & EDID_HDMI_3D_MULTI_PRESENT_MASK; + } + + if (VSDB3DPresent) { + DPRINTF("VSDB 3D Present!!!\r\n"); + // check with 3D madatory format + if (CheckResolution(pVideo->resolution, pVideo->pixelAspectRatio)) { + int size = sizeof(edid_3d)/sizeof(struct edid_3d_mandatory); + for (edid_index = 0; edid_index < size; edid_index++) { + if (edid_3d[edid_index].resolution == pVideo->resolution && + edid_3d[edid_index].hdmi_3d_format == pVideo->hdmi_3d_format ) + return 1; + } + } + } + + // check 3D_Multi_Present bit + if (VSDB3DMultiPresent) { + DPRINTF("VSDB 3D Multi Present!!! = 0x%02x\r\n",VSDB3DMultiPresent); + // 3D Structure only + if (VSDB3DMultiPresent == EDID_3D_STRUCTURE_ONLY_EXIST) { + // 3D Structure All + Hdmi3DStructure = (gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 1] << 8); + Hdmi3DStructure |= gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 2]; + DPRINTF("VSDB 3D Structure!!! = [0x%02x]\r\n",Hdmi3DStructure); + } + + // 3D Structure and Mask + if (VSDB3DMultiPresent == EDID_3D_STRUCTURE_MASK_EXIST) { + // 3D Structure All + Hdmi3DStructure = (gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 1] << 8); + Hdmi3DStructure |= gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 2]; + // 3D Structure Mask + Hdmi3DMask |= (gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 3] << 8); + Hdmi3DMask |= gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + 4]; + DPRINTF("VSDB 3D Structure!!! = [0x%02x]\r\n",Hdmi3DStructure); + DPRINTF("VSDB 3D Mask!!! = [0x%02x]\r\n",Hdmi3DMask); + DPRINTF("Current 3D Video format!!! = [%d]\r\n",pVideo->hdmi_3d_format); + DPRINTF("Current 3D Video format!!! = [0x%02x]\r\n",1<hdmi_3d_format); + } + + // check 3D Structure and Mask + if (Hdmi3DStructure & (1<hdmi_3d_format)) { + DPRINTF("VSDB 3D Structure Contains Current Video Structure!!!\r\n"); + // check first 16 EDID + for (edid_index = 0; edid_index < NUM_OF_VIC_FOR_3D; edid_index++) { + DPRINTF("VIC = %d, EDID Vic = %d!!!\r\n",vic,aVIC[edid_index]); + if (Hdmi3DMask & (1<= (EDID_HDMI_EXT_LENGTH_POS - latency_offset)) { + unsigned int HDMI3DExtLen = HDMI3DLen - (VSDB3DMultiPresent>>EDID_HDMI_3D_MULTI_PRESENT_BIT)*2; + unsigned int VICOrder; + + // check if there is 3D extra data ? + //TODO: check 3D_Detail in case of SSH + if (HDMI3DExtLen) { + // check HDMI 3D Extra Data + for (edid_index = 0; edid_index < (int)(HDMI3DExtLen / 2); edid_index++) { + VICOrder = gEdidData[StartAddr + EDID_HDMI_EXT_LENGTH_POS + HDMIVICLen + + (VSDB3DMultiPresent>>EDID_HDMI_3D_MULTI_PRESENT_BIT) * 2 + edid_index * 2] + & EDID_HDMI_2D_VIC_ORDER_MASK; + VICOrder = (1<>EDID_HDMI_3D_MULTI_PRESENT_BIT) * 2 + edid_index * 2] + & EDID_HDMI_3D_STRUCTURE_MASK; + Hdmi3DStructure = (1<hdmi_3d_format && vic == aVIC[VICOrder]) + return 1; + } + } + } + } + } + + return 0; +} + +/** + * Initialize EDID library. This will intialize DDC library. + * @return If success, return 1; Otherwise, return 0. + */ +int EDIDOpen(void) +{ + // init DDC + return DDCOpen(); +} + +/** + * Finalize EDID library. This will finalize DDC library. + * @return If success, return 1; Otherwise, return 0. + */ +int EDIDClose(void) +{ + // reset EDID + EDIDReset(); + + // close EDDC + return DDCClose(); +} + +/** + * Read EDID data of Rx. + * @return If success, return 1; Otherwise, return 0; + */ +int EDIDRead(void) +{ + int block,dataPtr; + unsigned char temp[SIZEOFEDIDBLOCK]; + + // if already read?? + if (EDIDValid()) + return 1; + + // read EDID Extension Number + // read EDID + if (!ReadEDIDBlock(0,temp)) + return 0; + + // get extension + gExtensions = temp[EDID_EXTENSION_NUMBER_POS]; + + // prepare buffer + gEdidData = (unsigned char*)malloc((gExtensions+1)*SIZEOFEDIDBLOCK); + if (!gEdidData) + return 0; + + // copy EDID Block 0 + memcpy(gEdidData,temp,SIZEOFEDIDBLOCK); + + // read EDID Extension + for (block = 1,dataPtr = SIZEOFEDIDBLOCK; block <= gExtensions; block++,dataPtr+=SIZEOFEDIDBLOCK) { + // read extension 1~gExtensions + if (!ReadEDIDBlock(block, gEdidData+dataPtr)) { + // reset buffer + EDIDReset(); + return 0; + } + } + + // check if extension is more than 1, and first extension block is not block map. + if (gExtensions > 1 && gEdidData[SIZEOFEDIDBLOCK] != EDID_BLOCK_MAP_EXT_TAG_VAL) { + // reset buffer + DPRINTF("EDID has more than 1 extension but, first extension block is not block map\n"); + EDIDReset(); + return 0; + } + + return 1; +} + +/** + * Reset stored EDID data. + */ +void EDIDReset(void) +{ + if (gEdidData) { + free(gEdidData); + gEdidData = NULL; + DPRINTF("\t\t\t\tEDID is reset!!!\n"); + } +} + +/** + * Get CEC physical address. + * @param outAddr [out] CEC physical address. LSB 2 bytes is available. [0:0:AB:CD] + * @return If success, return 1; Otherwise, return 0. + */ +int EDIDGetCECPhysicalAddress(int* const outAddr) +{ + int i; + unsigned int StartAddr; + + // check EDID data is valid or not + // read EDID + if (!EDIDRead()) + return 0; + + // find VSDB + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i) // if it's timing block + && (StartAddr = GetVSDBOffset(i)) > 0) { // check block + // get supported DC value + // int tempDC1 = (int)(gEdidData[tempAddr+EDID_DC_POS]); + int phyAddr = gEdidData[StartAddr + EDID_CEC_PHYICAL_ADDR] << 8; + phyAddr |= gEdidData[StartAddr + EDID_CEC_PHYICAL_ADDR+1]; + + DPRINTF("phyAddr = %x\n",phyAddr); + + *outAddr = phyAddr; + + return 1; + } + } + + return 0; +} + +/** + * Check if Rx supports HDMI/DVI mode or not. + * @param video [in] HDMI or DVI mode to check + * @return If Rx supports requested mode, return 1; Otherwise, return 0. + */ +int EDIDHDMIModeSupport(struct HDMIVideoParameter * const video) +{ + // check if read edid? + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + + // check hdmi mode + if (video->mode == HDMI) { + if (!CheckHDMIMode()) { + DPRINTF("HDMI mode Not Supported\n"); + return 0; + } + } + return 1; +} + +/** + * Check if Rx supports requested video resoultion or not. + * @param video [in] Video parameters to check + * @return If Rx supports video parameters, return 1; Otherwise, return 0. + */ +int EDIDVideoResolutionSupport(struct HDMIVideoParameter * const video) +{ + unsigned int TMDSClock; + unsigned int MaxTMDS = 0; + + // check if read edid? + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + + // get max tmds + MaxTMDS = GetMaxTMDS()*5; + + // Check MAX TMDS + TMDSClock = aVideoParams[video->resolution].PixelClock/100; + if (video->colorDepth == HDMI_CD_36) + TMDSClock *= 1.5; + else if (video->colorDepth == HDMI_CD_30) + TMDSClock *=1.25; + + DPRINTF("MAX TMDS = %d, Current TMDS = %d\n",MaxTMDS, TMDSClock); + if (MaxTMDS != 0 && MaxTMDS < TMDSClock) { + DPRINTF("Pixel clock is beyond Maximun TMDS in EDID\n"); + return 0; + } + + // check resolution + if (!CheckResolution(video->resolution,video->pixelAspectRatio)) { + DPRINTF("Video Resolution Not Supported\n"); + return 0; + } + + // check 3D format + if (!EDID3DFormatSupport(video)) { + DPRINTF("3D Format Not Supported\n"); + return 0; + } + + return 1; +} + +/** + * Check if Rx supports requested color depth or not. + * @param video [in] Video parameters to check + * @return If Rx supports video parameters, return 1; Otherwise, return 0. + */ +int EDIDColorDepthSupport(struct HDMIVideoParameter * const video) +{ + // check if read edid? + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + + // check resolution + if (!CheckColorDepth(video->colorDepth,video->colorSpace)) { + DPRINTF("Color Depth Not Supported\n"); + return 0; + } + + return 1; +} + +/** + * Check if Rx supports requested color space or not. + * @param video [in] Video parameters to check + * @return If Rx supports video parameters, return 1; Otherwise, return 0. + */ +int EDIDColorSpaceSupport(struct HDMIVideoParameter * const video) +{ + // check if read edid? + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + // check color space + if (!CheckColorSpace(video->colorSpace)) { + DPRINTF("Color Space Not Supported\n"); + return 0; + } + + return 1; +} + +/** + * Check if Rx supports requested colorimetry or not. + * @param video [in] Video parameters to check + * @return If Rx supports video parameters, return 1; Otherwise, return 0. + */ +int EDIDColorimetrySupport(struct HDMIVideoParameter * const video) +{ + // check if read edid? + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + + // check colorimetry + if (!CheckColorimetry(video->colorimetry)) { + DPRINTF("Colorimetry Not Supported\n"); + return 0; + } + + return 1; +} + +/** + * Check if Rx supports requested audio parameters or not. + * @param audio [in] Audio parameters to check + * @return If Rx supports audio parameters, return 1; Otherwise, return 0. + */ +int EDIDAudioModeSupport(struct HDMIAudioParameter * const audio) +{ + int i; + + // read EDID + if (!EDIDRead()) { + DPRINTF("EDID Read Fail!!!\n"); + return 0; + } + + // check EDID Extension + // find timing block + for (i = 1; i <= gExtensions; i++) { + if (IsTimingExtension(i)) { // if it's timing block + // find Short Audio Description + unsigned int StartAddr = i*SIZEOFEDIDBLOCK; + unsigned int ExtAddr = StartAddr + EDID_DATA_BLOCK_START_POS; + unsigned int tag,blockLen; + unsigned int DTDStartAddr = gEdidData[StartAddr + EDID_DETAILED_TIMING_OFFSET_POS]; + + while (ExtAddr < StartAddr + DTDStartAddr) { + // find the block tag and length + // tag + tag = gEdidData[ExtAddr] & EDID_TAG_CODE_MASK; + // block len + blockLen = (gEdidData[ExtAddr] & EDID_DATA_BLOCK_SIZE_MASK) + 1; + + DPRINTF("tag = %d\n",tag); + DPRINTF("blockLen = %d\n",blockLen-1); + + // check if it is short video description + if (tag == EDID_SHORT_AUD_DEC_TAG_VAL) { + // if so, check SAD + unsigned int j, channelNum; + int audioFormat,sampleFreq,wordLen; + for (j = 1; j < blockLen; j += 3) { + audioFormat = gEdidData[ExtAddr+j] & EDID_SAD_CODE_MASK; + channelNum = gEdidData[ExtAddr+j] & EDID_SAD_CHANNEL_MASK; + sampleFreq = gEdidData[ExtAddr+j+1]; + wordLen = gEdidData[ExtAddr+j+2]; + + DPRINTF("request = %d, EDIDAudioFormatCode = %d\n",(audio->formatCode)<<3, audioFormat); + DPRINTF("request = %d, EDIDChannelNumber= %d\n",(audio->channelNum)-1, channelNum); + DPRINTF("request = %d, EDIDSampleFreq= %d\n",1<<(audio->sampleFreq), sampleFreq); + DPRINTF("request = %d, EDIDWordLeng= %d\n",1<<(audio->wordLength), wordLen); + + // check parameter + // check audioFormat + if (audioFormat & ( (audio->formatCode) << 3) && // format code + channelNum >= ( (audio->channelNum) -1) && // channel number + (sampleFreq & (1<<(audio->sampleFreq)))) { // sample frequency + if (audioFormat == LPCM_FORMAT) { // check wordLen + int ret = 0; + switch (audio->wordLength) { + case WORD_16: + case WORD_17: + case WORD_18: + case WORD_19: + case WORD_20: + ret = wordLen & (1<<1); + break; + case WORD_21: + case WORD_22: + case WORD_23: + case WORD_24: + ret = wordLen & (1<<2); + break; + } + return ret; + } + return 1; // if not LPCM + } + } + } + // else find next block + ExtAddr += blockLen; + } + } + } + + return 0; +} diff --git a/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.h b/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.h new file mode 100644 index 0000000..dfd3096 --- /dev/null +++ b/exynos5/hal/libhdmi/libsForhdmi/libedid/libedid.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef _LIBEDID_H_ +#define _LIBEDID_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "video.h" +#include "audio.h" + +int EDIDOpen(void); +int EDIDRead(void); +void EDIDReset(void); +int EDIDHDMIModeSupport(struct HDMIVideoParameter *video); +int EDIDVideoResolutionSupport(struct HDMIVideoParameter *video); +int EDIDColorDepthSupport(struct HDMIVideoParameter *video); +int EDIDColorSpaceSupport(struct HDMIVideoParameter *video); +int EDIDColorimetrySupport(struct HDMIVideoParameter *video); +int EDIDAudioModeSupport(struct HDMIAudioParameter *audio); +int EDIDGetCECPhysicalAddress(int* outAddr); +int EDIDClose(void); + +#ifdef __cplusplus +} +#endif +#endif /* _LIBEDID_H_ */ -- cgit v1.2.3