diff --git a/exynos3/s5pc110/Android.mk b/exynos3/s5pc110/Android.mk index 56cb24c7..46596e93 100644 --- a/exynos3/s5pc110/Android.mk +++ b/exynos3/s5pc110/Android.mk @@ -17,7 +17,7 @@ ifeq ($(TARGET_BOARD_PLATFORM),s5pc110) # audio, camera, sensor and light HALs are device specifc -s5pc110_dirs := libhwcomposer libs3cjpeg libstagefrighthw sec_mm power +s5pc110_dirs := libhdmi libhwcomposer libs3cjpeg libstagefrighthw sec_mm power include $(call all-named-subdir-makefiles,$(s5pc110_dirs)) diff --git a/exynos3/s5pc110/include/s5p_tvout.h b/exynos3/s5pc110/include/s5p_tvout.h new file mode 100644 index 00000000..85b8dad5 --- /dev/null +++ b/exynos3/s5pc110/include/s5p_tvout.h @@ -0,0 +1,189 @@ +/* + * 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 __S5P_TVOUT_H__ +#define __S5P_TVOUT_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************* + * Define + *******************************************/ +/* TVOUT control */ + +#define TVOUT_DEV "/dev/video14" +#define TVOUT_DEV_V "/dev/video21" +#define TVOUT_DEV_V2 "/dev/video22" + +/* ------------- Output -----------------*/ +/* type */ +#define V4L2_OUTPUT_TYPE_MSDMA 4 +#define V4L2_OUTPUT_TYPE_COMPOSITE 5 +#define V4L2_OUTPUT_TYPE_SVIDEO 6 +#define V4L2_OUTPUT_TYPE_YPBPR_INERLACED 7 +#define V4L2_OUTPUT_TYPE_YPBPR_PROGRESSIVE 8 +#define V4L2_OUTPUT_TYPE_RGB_PROGRESSIVE 9 +#define V4L2_OUTPUT_TYPE_DIGITAL 10 +#define V4L2_OUTPUT_TYPE_HDMI V4L2_OUTPUT_TYPE_DIGITAL +#define V4L2_OUTPUT_TYPE_HDMI_RGB 11 +#define V4L2_OUTPUT_TYPE_DVI 12 + +/* ------------- STD -------------------*/ +#define V4L2_STD_PAL_BDGHI\ + (V4L2_STD_PAL_B|V4L2_STD_PAL_D|V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_PAL_I) + +#define V4L2_STD_480P_60_16_9 ((v4l2_std_id)0x04000000) +#define V4L2_STD_480P_60_4_3 ((v4l2_std_id)0x05000000) +#define V4L2_STD_576P_50_16_9 ((v4l2_std_id)0x06000000) +#define V4L2_STD_576P_50_4_3 ((v4l2_std_id)0x07000000) +#define V4L2_STD_720P_60 ((v4l2_std_id)0x08000000) +#define V4L2_STD_720P_50 ((v4l2_std_id)0x09000000) +#define V4L2_STD_1080P_60 ((v4l2_std_id)0x0a000000) +#define V4L2_STD_1080P_50 ((v4l2_std_id)0x0b000000) +#define V4L2_STD_1080I_60 ((v4l2_std_id)0x0c000000) +#define V4L2_STD_1080I_50 ((v4l2_std_id)0x0d000000) +#define V4L2_STD_480P_59 ((v4l2_std_id)0x0e000000) +#define V4L2_STD_720P_59 ((v4l2_std_id)0x0f000000) +#define V4L2_STD_1080I_59 ((v4l2_std_id)0x10000000) +#define V4L2_STD_1080P_59 ((v4l2_std_id)0x11000000) +#define V4L2_STD_1080P_30 ((v4l2_std_id)0x12000000) +#define V4L2_STD_TVOUT_720P_60_SBS_HALF ((v4l2_std_id)0x13000000) +#define V4L2_STD_TVOUT_720P_59_SBS_HALF ((v4l2_std_id)0x14000000) +#define V4L2_STD_TVOUT_720P_50_TB ((v4l2_std_id)0x15000000) +#define V4L2_STD_TVOUT_1080P_24_TB ((v4l2_std_id)0x16000000) +#define V4L2_STD_TVOUT_1080P_23_TB ((v4l2_std_id)0x17000000) + +/* ------------- Input ------------------*/ +/* type */ +#define V4L2_INPUT_TYPE_MSDMA 3 +#define V4L2_INPUT_TYPE_FIFO 4 + +/* TVOUT video */ +#define PFX_NODE_FB "/dev/graphics/fb" + +/******************************************* + * structures + *******************************************/ + +/* TVOUT */ +struct v4l2_vid_overlay_src { + void *base_y; + void *base_c; + struct v4l2_pix_format pix_fmt; +}; + +struct v4l2_window_s5p_tvout { + __u32 capability; + __u32 flags; + __u32 priority; + struct v4l2_window win; +}; + +struct v4l2_pix_format_s5p_tvout { + void *base_y; + void *base_c; + __u32 src_img_endian; + struct v4l2_pix_format pix_fmt; +}; + +struct vid_overlay_param { + struct v4l2_vid_overlay_src src; + struct v4l2_rect src_crop; + struct v4l2_framebuffer dst; + struct v4l2_window dst_win; +}; + +struct tvout_param { + struct v4l2_pix_format_s5p_tvout tvout_src; + struct v4l2_window_s5p_tvout tvout_rect; + struct v4l2_rect tvout_dst; +}; + +struct overlay_param { + struct v4l2_framebuffer overlay_frame; + struct v4l2_window_s5p_tvout overlay_rect; + struct v4l2_rect overlay_dst; +}; + +/* FB */ +struct s5ptvfb_user_window { + int x; + int y; +}; + +struct s5ptvfb_user_plane_alpha { + int channel; + unsigned char alpha; +}; + +struct s5ptvfb_user_chroma { + int enabled; + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +enum s5ptvfb_ver_scaling_t { + VERTICAL_X1, + VERTICAL_X2, +}; + +enum s5ptvfb_hor_scaling_t { + HORIZONTAL_X1, + HORIZONTAL_X2, +}; + +struct s5ptvfb_user_scaling { + enum s5ptvfb_ver_scaling_t ver; + enum s5ptvfb_hor_scaling_t hor; +}; + +/******************************************* + * custom ioctls + *******************************************/ + +#define VIDIOC_S_BASEADDR _IOR('V', 83, int) + +#define VIDIOC_HDCP_ENABLE _IOWR('V', 100, unsigned int) +#define VIDIOC_HDCP_STATUS _IOR('V', 101, unsigned int) +#define VIDIOC_HDCP_PROT_STATUS _IOR('V', 102, unsigned int) + +#define VIDIOC_INIT_AUDIO _IOR('V', 103, unsigned int) +#define VIDIOC_AV_MUTE _IOR('V', 104, unsigned int) +#define VIDIOC_G_AVMUTE _IOR('V', 105, unsigned int) +#define HPD_GET_STATE _IOR('H', 100, unsigned int) + +#define S5PTVFB_WIN_POSITION _IOW('F', 213, struct s5ptvfb_user_window) +#define S5PTVFB_WIN_SET_PLANE_ALPHA _IOW('F', 214, struct s5ptvfb_user_plane_alpha) +#define S5PTVFB_WIN_SET_CHROMA _IOW('F', 215, struct s5ptvfb_user_chroma) + +#define S5PTVFB_SET_VSYNC_INT _IOW('F', 216, unsigned int) +#define S5PTVFB_WAITFORVSYNC _IO('F', 32) +#define S5PTVFB_WIN_SET_ADDR _IOW('F', 219, unsigned int) +#define S5PTVFB_SET_WIN_ON _IOW('F', 220, unsigned int) +#define S5PTVFB_SET_WIN_OFF _IOW('F', 221, unsigned int) +#define S5PTVFB_SCALING _IOW('F', 222, struct s5ptvfb_user_scaling) + +#ifdef __cplusplus +} +#endif + +#endif /* __S5P_TVOUT_H__ */ diff --git a/exynos3/s5pc110/include/sec_lcd.h b/exynos3/s5pc110/include/sec_lcd.h index 82ef42f1..f104d2d7 100755 --- a/exynos3/s5pc110/include/sec_lcd.h +++ b/exynos3/s5pc110/include/sec_lcd.h @@ -26,6 +26,18 @@ struct secfb_user_window { int y; }; +struct s3cfb_next_info { + unsigned int phy_start_addr; + unsigned int xres; /* visible resolution*/ + unsigned int yres; + unsigned int xres_virtual; /* virtual resolution*/ + unsigned int yres_virtual; + unsigned int xoffset; /* offset from virtual to visible */ + unsigned int yoffset; /* resolution */ + unsigned int lcd_offset_x; + unsigned int lcd_offset_y; +}; + /* * C U S T O M I O C T L S * @@ -34,13 +46,15 @@ struct secfb_user_window { #define FBIO_WAITFORVSYNC _IO ('F', 32) #define SECFB_WIN_POSITION _IOW ('F', 203, struct secfb_user_window) #define S3CFB_SET_VSYNC_INT _IOW ('F', 206, uint32_t) +#define S3CFB_GET_CURR_FB_INFO _IOR ('F', 305, struct s3cfb_next_info) #define S3CFB_WAIT_FOR_VSYNC _IOR ('F', 311, uint64_t) -#define DEFAULT_LCD_WIDTH (480) -#define DEFAULT_LCD_HEIGHT (800) +#define DEFAULT_LCD_WIDTH (600) +#define DEFAULT_LCD_HEIGHT (1024) #define DEFAULT_LCD_BPP (32) /***************** LCD frame buffer *****************/ +#define S3CFB_SIZE 5 #define FB0_NAME "/dev/fb0" #define FB1_NAME "/dev/fb1" #define FB2_NAME "/dev/fb2" diff --git a/exynos3/s5pc110/include/sec_utils.h b/exynos3/s5pc110/include/sec_utils.h index 3e41afe2..be8eaba2 100644 --- a/exynos3/s5pc110/include/sec_utils.h +++ b/exynos3/s5pc110/include/sec_utils.h @@ -22,6 +22,8 @@ //---------------------------------------------------------// #include +#include + #include "sec_format.h" #ifdef __cplusplus diff --git a/exynos3/s5pc110/libhdmi/Android.mk b/exynos3/s5pc110/libhdmi/Android.mk new file mode 100644 index 00000000..9bfa1800 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/Android.mk @@ -0,0 +1,38 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -fno-short-enums +LOCAL_CFLAGS += -DLOG_TAG=\"hdmi.$(TARGET_BOARD_PLATFORM)\" + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include + +LOCAL_SRC_FILES := \ + fimc.c \ + fimd.c \ + SecHDMI.cpp \ + hal_module.cpp + +LOCAL_MODULE := hdmi.$(TARGET_BOARD_PLATFORM) +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := liblog libutils + +include $(BUILD_SHARED_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/exynos3/s5pc110/libhdmi/SecHDMI.cpp b/exynos3/s5pc110/libhdmi/SecHDMI.cpp new file mode 100644 index 00000000..eeacb4d5 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/SecHDMI.cpp @@ -0,0 +1,846 @@ +/* + * Copyright 2011, Havlena Petr + * + * 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 +#include + +#include +#include +#include + +#include + +#include "SecHDMI.h" +#include "fimd.h" + +using namespace android; + +#define RETURN_IF(return_value) \ + if (return_value < 0) { \ + ALOGE("%s::%d fail. errno: %s", \ + __func__, __LINE__, strerror(errno)); \ + return -1; \ + } + +#define ALOG_IF(return_value) \ + if (return_value < 0) { \ + ALOGE("%s::%d fail. errno: %s", \ + __func__, __LINE__, strerror(errno)); \ + } + +#define ALIGN_TO_32B(x) ((((x) + (1 << 5) - 1) >> 5) << 5) +#define ALIGN_TO_128B(x) ((((x) + (1 << 7) - 1) >> 7) << 7) +#define ALIGN_TO_8KB(x) ((((x) + (1 << 13) - 1) >> 13) << 13) + +struct s5p_tv_standart_internal { + int index; + unsigned long value; +} s5p_tv_standards[] = { + { + S5P_TV_STD_NTSC_M, + V4L2_STD_NTSC_M, + }, { + S5P_TV_STD_PAL_BDGHI, + V4L2_STD_PAL_BDGHI, + }, { + S5P_TV_STD_PAL_M, + V4L2_STD_PAL_M, + }, { + S5P_TV_STD_PAL_N, + V4L2_STD_PAL_N, + }, { + S5P_TV_STD_PAL_Nc, + V4L2_STD_PAL_Nc, + }, { + S5P_TV_STD_PAL_60, + V4L2_STD_PAL_60, + }, { + S5P_TV_STD_NTSC_443, + V4L2_STD_NTSC_443, + }, { + S5P_TV_STD_480P_60_16_9, + V4L2_STD_480P_60_16_9, + }, { + S5P_TV_STD_480P_60_4_3, + V4L2_STD_480P_60_4_3, + }, { + S5P_TV_STD_576P_50_16_9, + V4L2_STD_576P_50_16_9, + }, { + S5P_TV_STD_576P_50_4_3, + V4L2_STD_576P_50_4_3, + }, { + S5P_TV_STD_720P_60, + V4L2_STD_720P_60, + }, { + S5P_TV_STD_720P_50, + V4L2_STD_720P_50, + }, +}; + +static inline int calcFrameSize(int format, int width, int height) +{ + int size = 0; + + switch (format) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + size = (width * height * 3 / 2); + break; + + case V4L2_PIX_FMT_NV12T: + size = ALIGN_TO_8KB(ALIGN_TO_128B(width) * ALIGN_TO_32B(height)) + + ALIGN_TO_8KB(ALIGN_TO_128B(width) * ALIGN_TO_32B(height / 2)); + break; + + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + size = (width * height * 2); + break; + + default : + ALOGE("ERR(%s):Invalid V4L2 pixel format(%d)\n", __func__, format); + case V4L2_PIX_FMT_RGB565: + size = (width * height * 2); + break; + } + return size; +} + +static int get_pixel_depth(unsigned int fmt) +{ + int depth = 0; + + switch (fmt) { + case V4L2_PIX_FMT_NV12: + depth = 12; + break; + case V4L2_PIX_FMT_NV12T: + depth = 12; + break; + case V4L2_PIX_FMT_NV21: + depth = 12; + break; + case V4L2_PIX_FMT_YUV420: + depth = 12; + break; + + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YUV422P: + depth = 16; + break; + + case V4L2_PIX_FMT_RGB32: + depth = 32; + break; + } + + return depth; +} + +// ====================================================================== +// Video ioctls + +static int tv20_v4l2_querycap(int fp) +{ + struct v4l2_capability cap; + + int ret = ioctl(fp, VIDIOC_QUERYCAP, &cap); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_QUERYCAP failed", __func__); + return -1; + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { + ALOGE("ERR(%s):no output devices\n", __func__); + return -1; + } + ALOGV("Name of cap driver is %s", cap.driver); + + return ret; +} + +static const __u8* tv20_v4l2_enum_output(int fp, int index) +{ + static struct v4l2_output output; + + output.index = index; + if (ioctl(fp, VIDIOC_ENUMOUTPUT, &output) != 0) { + ALOGE("ERR(%s):No matching index found", __func__); + return NULL; + } + ALOGV("Name of output channel[%d] is %s", output.index, output.name); + + return output.name; +} + +static const __u8* tv20_v4l2_enum_standarts(int fp, int index) +{ + static struct v4l2_standard standart; + + standart.index = index; + if (ioctl(fp, VIDIOC_ENUMSTD, &standart) != 0) { + ALOGE("ERR(%s):No matching index found\n", __func__); + return NULL; + } + ALOGV("Name of output standart[%d] is %s\n", standart.index, standart.name); + + return standart.name; +} + +static int tv20_v4l2_s_output(int fp, int index) +{ + struct v4l2_output output; + int ret; + + output.index = index; + + ret = ioctl(fp, VIDIOC_S_OUTPUT, &output); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_S_OUPUT failed\n", __func__); + return ret; + } + return ret; +} + +static int tv20_v4l2_s_std(int fp, unsigned long id) +{ + v4l2_std_id std; + int ret; + + std = id; + + ret = ioctl(fp, VIDIOC_S_STD, &std); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_S_OUPUT failed\n", __func__); + return ret; + } + return ret; +} + +static int tv20_v4l2_enum_fmt(int fp, unsigned int fmt) +{ + struct v4l2_fmtdesc fmtdesc; + int found = 0; + + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmtdesc.index = 0; + + while (ioctl(fp, VIDIOC_ENUM_FMT, &fmtdesc) == 0) { + if (fmtdesc.pixelformat == fmt) { + ALOGV("passed fmt = %#x found pixel format[%d]: %s\n", fmt, fmtdesc.index, fmtdesc.description); + found = 1; + break; + } + + fmtdesc.index++; + } + + if (!found) { + ALOGE("unsupported pixel format\n"); + return -1; + } + + return 0; +} + +static int tv20_v4l2_s_fmt(int fp, int width, int height, + unsigned int fmt, unsigned int yAddr, unsigned int cAddr) +{ + struct v4l2_format v4l2_fmt; + struct v4l2_pix_format_s5p_tvout pixfmt; + int ret; + + v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +#if 0 + ret = ioctl(fp, VIDIOC_G_FMT, &v4l2_fmt); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_G_FMT failed", __func__); + return -1; + } +#endif + + memset(&pixfmt, 0, sizeof(pixfmt)); + pixfmt.pix_fmt.width = width; + pixfmt.pix_fmt.height = height; + pixfmt.pix_fmt.pixelformat = fmt; + pixfmt.pix_fmt.sizeimage = (width * height * get_pixel_depth(fmt)) / 8; + pixfmt.pix_fmt.field = V4L2_FIELD_NONE; + + // here we must set addresses of our memory for video out + pixfmt.base_y = (void *)yAddr; + pixfmt.base_c = (void* )cAddr; + + v4l2_fmt.fmt.pix = pixfmt.pix_fmt; + memcpy(v4l2_fmt.fmt.raw_data, &pixfmt, + sizeof(struct v4l2_pix_format_s5p_tvout)); + + /* Set up for capture */ + ret = ioctl(fp, VIDIOC_S_FMT, &v4l2_fmt); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_S_FMT failed\n", __func__); + return -1; + } + return 0; +} + +static int tv20_v4l2_streamon(int fp) +{ + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + int ret; + + ret = ioctl(fp, VIDIOC_STREAMON, &type); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_STREAMON failed\n", __func__); + return ret; + } + + return ret; +} + +static int tv20_v4l2_streamoff(int fp) +{ + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + int ret; + + ALOGV("%s :", __func__); + ret = ioctl(fp, VIDIOC_STREAMOFF, &type); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_STREAMOFF failed\n", __func__); + return ret; + } + + return ret; +} + +static int tv20_v4l2_g_parm(int fp, struct v4l2_streamparm *streamparm) +{ + int ret; + + streamparm->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + ret = ioctl(fp, VIDIOC_G_PARM, streamparm); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_G_PARM failed\n", __func__); + return -1; + } + + ALOGV("%s : timeperframe: numerator %d, denominator %d\n", __func__, + streamparm->parm.capture.timeperframe.numerator, + streamparm->parm.capture.timeperframe.denominator); + + return 0; +} + +static int tv20_v4l2_s_parm(int fp, struct v4l2_streamparm *streamparm) +{ + int ret; + + streamparm->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + ret = ioctl(fp, VIDIOC_S_PARM, streamparm); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_S_PARM failed\n", __func__); + return ret; + } + return 0; +} + +static int tv20_v4l2_s_crop(int fp, int offset_x, int offset_y, int width, int height) +{ + struct v4l2_crop crop; + + crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + crop.c.left = offset_x; + crop.c.top = offset_y; + crop.c.width = width; + crop.c.height = height; + + int ret = ioctl(fp, VIDIOC_S_CROP, &crop); + if (ret < 0) { + ALOGE("ERR(%s):VIDIOC_S_PARM failed\n", __func__); + return ret; + } + return 0; +} + +static int tv20_v4l2_start_overlay(int fp) +{ + int ret, start = 1; + + ret = ioctl(fp, VIDIOC_OVERLAY, &start); + if (ret < 0) { + ALOGE("ERR(%s): VIDIOC_OVERLAY start failed\n", __func__); + return ret; + } + + return ret; +} + +static int tv20_v4l2_stop_overlay(int fp) +{ + int ret, stop = 0; + + ret = ioctl(fp, VIDIOC_OVERLAY, &stop); + if (ret < 0) { + ALOGE("ERR(%s): VIDIOC_OVERLAY stop failed\n", __func__); + return ret; + } + + return ret; +} + +static int tv20_v4l2_s_baseaddr(int fp, void *base_addr) +{ + int ret; + + ret = ioctl(fp, S5PTVFB_WIN_SET_ADDR, base_addr); + if (ret < 0) { + ALOGE("ERR(%s): VIDIOC_S_BASEADDR failed %d", __func__, ret); + return ret; + } + + return 0; +} + +static int tv20_v4l2_s_position(int fp, int x, int y) +{ + int ret; + struct s5ptvfb_user_window window; + + memset(&window, 0, sizeof(struct s5ptvfb_user_window)); + window.x = x; + window.y = y; + + ret = ioctl(fp, S5PTVFB_WIN_POSITION, &window); + if (ret < 0) { + ALOGE("ERR(%s): VIDIOC_S_WIN_POSITION failed %d", __func__, ret); + return ret; + } + + return 0; +} + +// ====================================================================== +// Audio ioctls + +static int tv20_v4l2_audio_enable(int fp) +{ + return ioctl(fp, VIDIOC_INIT_AUDIO, 1); +} + +static int tv20_v4l2_audio_disable(int fp) +{ + return ioctl(fp, VIDIOC_INIT_AUDIO, 0); +} + +static int tv20_v4l2_audio_mute(int fp) +{ + return ioctl(fp, VIDIOC_AV_MUTE, 1); +} + +static int tv20_v4l2_audio_unmute(int fp) +{ + return ioctl(fp, VIDIOC_AV_MUTE, 0); +} + +static int tv20_v4l2_audio_get_mute_state(int fp) +{ + return ioctl(fp, VIDIOC_G_AVMUTE, 0); +} + +// ====================================================================== +// Class which comunicate with kernel driver + +SecHDMI::SecHDMI() + : mTvOutFd(-1), + mTvOutVFd(-1), + mLcdFd(-1), + mHdcpEnabled(0), + mFlagConnected(false) +{ + ALOGV("%s", __func__); + + memset(&mParams, 0, sizeof(struct v4l2_streamparm)); + memset(&mFlagLayerEnable, 0, sizeof(bool) * S5P_TV_LAYER_MAX); + + int ret = ioctl(mTvOutFd, VIDIOC_HDCP_ENABLE, &mHdcpEnabled); + ALOG_IF(ret); +} + +SecHDMI::~SecHDMI() +{ + destroy(); +} + +/* static */ +int SecHDMI::getCableStatus() +{ + int fd = 0; + char value[8] = {0}; + + ALOGV("%s", __func__); + + fd = open("/sys/class/switch/h2w/state", O_RDWR); + if(fd < 0) { + goto close; + } + + if(read(fd, &value, 8) <= 0) { + goto close; + } + +close: + close(fd); + return strtol(value, NULL, 10); +} + +const __u8* SecHDMI::getName(int index) +{ + ALOGV("%s", __func__); + return tv20_v4l2_enum_output(mTvOutFd, index); +} + +int SecHDMI::destroy() +{ + ALOGV("%s", __func__); + + if(mFlagConnected) { + disconnect(); + } + if(mTvOutFd > 0) { + close(mTvOutFd); + mTvOutFd = -1; + } + if(mFimc.dev_fd > 0) { + fimc_close(&mFimc); + mFimc.dev_fd = -1; + } + if (mLcdFd > 0) { + fb_close(mLcdFd); + mLcdFd = -1; + } + + return 0; +} + +int SecHDMI::startLayer(s5p_tv_layer layer) +{ + int ret; + + if (mFlagLayerEnable[layer]) { + return 0; + } + + switch (layer) { + case S5P_TV_LAYER_VIDEO: + if(mTvOutVFd < 0) { + mTvOutVFd = open(TVOUT_DEV_V, O_RDWR); + RETURN_IF(mTvOutVFd); + } + ret = tv20_v4l2_start_overlay(mTvOutVFd); + RETURN_IF(ret); + break; + case S5P_TV_LAYER_GRAPHIC_0 : + ret = ioctl(0/*fp_tvout_g0*/, FBIOBLANK, (void *)FB_BLANK_UNBLANK); + RETURN_IF(ret); + break; + case S5P_TV_LAYER_GRAPHIC_1 : + ret = ioctl(0/*fp_tvout_g1*/, FBIOBLANK, (void *)FB_BLANK_UNBLANK); + RETURN_IF(ret); + break; + default : + RETURN_IF(-1); + } + + mFlagLayerEnable[layer] = true; + + return 0; +} + +int SecHDMI::stopLayer(s5p_tv_layer layer) +{ + int ret; + + if (!mFlagLayerEnable[layer]) { + return 0; + } + + switch (layer) { + case S5P_TV_LAYER_VIDEO: + ret = tv20_v4l2_stop_overlay(mTvOutVFd); + RETURN_IF(ret); + close(mTvOutVFd); + mTvOutVFd = -1; + break; + case S5P_TV_LAYER_GRAPHIC_0 : + ret = ioctl(0/*fp_tvout_g0*/, FBIOBLANK, (void *)FB_BLANK_POWERDOWN); + RETURN_IF(ret); + break; + case S5P_TV_LAYER_GRAPHIC_1 : + ret = ioctl(0/*fp_tvout_g1*/, FBIOBLANK, (void *)FB_BLANK_POWERDOWN); + RETURN_IF(ret); + break; + default : + RETURN_IF(-1); + } + + mFlagLayerEnable[layer] = false; + + return 0; +} + +int SecHDMI::create(int width, int height) +{ + int ret, y_size; + unsigned int addr; + + ALOGV("%s", __func__); + + mTvOutFd = open(TVOUT_DEV, O_RDWR); + RETURN_IF(mTvOutFd); + + memset(&mFimc, 0, sizeof(s5p_fimc_t)); + mFimc.dev_fd = -1; + ret = fimc_open(&mFimc, "/dev/video2"); + RETURN_IF(ret); + + ALOGV("query capabilities"); + ret = tv20_v4l2_querycap(mTvOutFd); + RETURN_IF(ret); + + struct s5p_tv_standart_internal std = + s5p_tv_standards[(int) S5P_TV_STD_PAL_BDGHI]; + + ALOGV("searching for standart: %i", std.index); + if(!tv20_v4l2_enum_standarts(mTvOutFd, std.index)) + return -1; + + ret = tv20_v4l2_s_std(mTvOutFd, std.value); + RETURN_IF(ret); + + ALOGV("searching for output: %i", S5P_TV_OUTPUT_TYPE_COMPOSITE); + if (!tv20_v4l2_enum_output(mTvOutFd, S5P_TV_OUTPUT_TYPE_COMPOSITE)) + return -1; + + ret = tv20_v4l2_s_output(mTvOutFd, S5P_TV_OUTPUT_TYPE_COMPOSITE); + RETURN_IF(ret); + + struct v4l2_window_s5p_tvout* p = + (struct v4l2_window_s5p_tvout*)&mParams.parm.raw_data; + p->win.w.top = 0; + p->win.w.left = 0; + p->win.w.width = width; + p->win.w.height = height; + + ALOGV("searching for format: %i", V4L2_PIX_FMT_NV12); + ret = tv20_v4l2_enum_fmt(mTvOutFd, V4L2_PIX_FMT_NV12); + RETURN_IF(ret); + + addr = (unsigned int) mFimc.out_buf.phys_addr; + y_size = ALIGN_TO_8KB(ALIGN_TO_128B(width) * ALIGN_TO_32B(height)); + ret = tv20_v4l2_s_fmt(mTvOutFd, width, height, V4L2_PIX_FMT_NV12, + (unsigned int) addr, + (unsigned int) addr + y_size); + RETURN_IF(ret); + + return 0; +} + +int SecHDMI::connect() +{ + int ret; + + ALOGV("%s", __func__); + + RETURN_IF(mTvOutFd); + + if(mFlagConnected) { + return 0; + } + +#if 0 + ret = getCableStatus() <= 0 ? -1 : 0; + RETURN_IF(ret); +#endif + + ret = tv20_v4l2_s_parm(mTvOutFd, &mParams); + RETURN_IF(ret); + + ret = tv20_v4l2_streamon(mTvOutFd); + RETURN_IF(ret); + +#if 0 + ret = startLayer(S5P_TV_LAYER_VIDEO); + RETURN_IF(ret); +#endif + + mFlagConnected = true; + + return 0; +} + +int SecHDMI::disconnect() +{ + int ret; + + ALOGV("%s", __func__); + + RETURN_IF(mTvOutFd); + + if(!mFlagConnected) { + return 0; + } + + ret = tv20_v4l2_streamoff(mTvOutFd); + RETURN_IF(ret); + +#if 0 + ret = stopLayer(S5P_TV_LAYER_VIDEO); + RETURN_IF(ret); +#endif + + mFlagConnected = false; + + return 0; +} + +int SecHDMI::flush(int srcW, int srcH, int srcColorFormat, + unsigned int srcYAddr, unsigned int srcCbAddr, unsigned int srcCrAddr, + int dstX, int dstY, + int layer, + int num_of_hwc_layer) +{ + int ret; + +#if 0 + usleep(1000 * 10); +#else + sec_img src_img; + sec_img dst_img; + sec_rect src_rect; + sec_rect dst_rect; + unsigned int phyAddr[3/*MAX_NUM_PLANES*/]; + + if(!srcYAddr) { + struct s3cfb_next_info fb_info; + + if (mLcdFd < 0) { + mLcdFd = fb_open(0); + } + + RETURN_IF(mLcdFd); + + ret = ioctl(mLcdFd, S3CFB_GET_CURR_FB_INFO, &fb_info); + RETURN_IF(ret); + + srcYAddr = fb_info.phy_start_addr; + srcCbAddr = srcYAddr; + } + + memset(&src_img, 0, sizeof(src_img)); + memset(&dst_img, 0, sizeof(src_img)); + memset(&src_rect, 0, sizeof(src_rect)); + memset(&dst_rect, 0, sizeof(src_rect)); + memset(&phyAddr, 0, sizeof(int) * sizeof(phyAddr)); + + phyAddr[0] = srcYAddr; + phyAddr[1] = srcCbAddr; + phyAddr[2] = srcCrAddr; + + src_img.w = srcW; + src_img.h = srcH; + src_img.format = HAL_PIXEL_FORMAT_YCbCr_420_SP/*srcColorFormat*/; + src_img.base = 0; + src_img.offset = 0; + src_img.mem_id = 0; + src_img.mem_type = FIMC_MEM_TYPE_PHYS; + src_img.w = (src_img.w + 15) & (~15); + src_img.h = (src_img.h + 1) & (~1) ; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.w = src_img.w; + src_rect.h = src_img.h; + + struct v4l2_window_s5p_tvout* p = + (struct v4l2_window_s5p_tvout*)&mParams.parm.raw_data; + if (!p) { + return -1; + } + + dst_img.w = p->win.w.width; + dst_img.h = p->win.w.height; + dst_img.format = HAL_PIXEL_FORMAT_YCbCr_420_SP; + dst_img.base = (unsigned int) mFimc.out_buf.phys_addr; + dst_img.offset = 0; + dst_img.mem_id = 0; + dst_img.mem_type = FIMC_MEM_TYPE_PHYS; + + dst_rect.x = p->win.w.top; + dst_rect.y = p->win.w.left; + dst_rect.w = dst_img.w; + dst_rect.h = dst_img.h; + + ALOGV("%s::sr_x %d sr_y %d sr_w %d sr_h %d dr_x %d dr_y %d dr_w %d dr_h %d ", + __func__, src_rect.x, src_rect.y, src_rect.w, src_rect.h, + dst_rect.x, dst_rect.y, dst_rect.w, dst_rect.h); + + ret = fimc_flush(&mFimc, &src_img, &src_rect, &dst_img, &dst_rect, + phyAddr, 0); + RETURN_IF(ret); + +/* + struct fb_var_screeninfo var; + var.xres = srcW; + var.yres = srcH; + var.xres_virtual = var.xres; + var.yres_virtual = var.yres; + var.xoffset = 0; + var.yoffset = 0; + var.width = srcW; + var.height = srcH; + var.activate = FB_ACTIVATE_FORCE; + if (srcColorFormat == HAL_PIXEL_FORMAT_RGB_565) { + var.bits_per_pixel = 16; + var.transp.length = 0; + } + else { + var.bits_per_pixel = 32; + var.transp.length = 8; + } + + ret = tv20_v4l2_s_baseaddr(mTvOutFd, (void *)srcYAddr); + RETURN_IF(ret); + + ret = fb_put_vscreeninfo(mLcdFd, &var); + RETURN_IF(ret); + + ret = tv20_v4l2_s_position(mTvOutFd, dstX, dstY); + RETURN_IF(ret); +*/ +#endif + + return 0; +} diff --git a/exynos3/s5pc110/libhdmi/SecHDMI.h b/exynos3/s5pc110/libhdmi/SecHDMI.h new file mode 100644 index 00000000..c2f6f9b9 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/SecHDMI.h @@ -0,0 +1,113 @@ +/* + * Copyright 2011, Havlena Petr + * + * 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_HARDWARE_SEC_TV_H +#define ANDROID_HARDWARE_SEC_TV_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fimc.h" + +namespace android { + +enum s5p_tv_standart { + S5P_TV_STD_NTSC_M = 0, + S5P_TV_STD_PAL_BDGHI, + S5P_TV_STD_PAL_M, + S5P_TV_STD_PAL_N, + S5P_TV_STD_PAL_Nc, + S5P_TV_STD_PAL_60, + S5P_TV_STD_NTSC_443, + S5P_TV_STD_480P_60_16_9, + S5P_TV_STD_480P_60_4_3, + S5P_TV_STD_576P_50_16_9, + S5P_TV_STD_576P_50_4_3, + S5P_TV_STD_720P_60, + S5P_TV_STD_720P_50 +}; + +// must match with s5p_tv_outputs in s5p_tv_v4l.c +enum s5p_tv_output { + S5P_TV_OUTPUT_TYPE_COMPOSITE = 0, + S5P_TV_OUTPUT_TYPE_SVIDEO, + S5P_TV_OUTPUT_TYPE_YPBPR_INERLACED, + S5P_TV_OUTPUT_TYPE_YPBPR_PROGRESSIVE, + S5P_TV_OUTPUT_TYPE_RGB_PROGRESSIVE, + S5P_TV_OUTPUT_TYPE_HDMI, +}; + +class SecHDMI { +public: + SecHDMI(); + ~SecHDMI(); + + static int getCableStatus(); + + int create(int width, int height); + int destroy(); + + int connect(); + int disconnect(); + + int flush(int srcW, int srcH, int srcColorFormat, + unsigned int srcYAddr, unsigned int srcCbAddr, unsigned int srcCrAddr, + int dstX, int dstY, + int layer, + int num_of_hwc_layer); + + const __u8* getName(int index); + +private: + enum s5p_tv_layer { + S5P_TV_LAYER_BASE = 0, + S5P_TV_LAYER_VIDEO, + S5P_TV_LAYER_GRAPHIC_0, + S5P_TV_LAYER_GRAPHIC_1, + S5P_TV_LAYER_MAX, + }; + + int mTvOutFd; + int mTvOutVFd; + int mLcdFd; + unsigned int mHdcpEnabled; + bool mFlagConnected; + bool mFlagLayerEnable[S5P_TV_LAYER_MAX]; + + s5p_fimc_t mFimc; + v4l2_streamparm mParams; + + int startLayer(s5p_tv_layer layer); + int stopLayer(s5p_tv_layer layer); +}; + +}; // namespace android + +#endif // ANDROID_HARDWARE_SEC_TV_H diff --git a/exynos3/s5pc110/libhdmi/fimc.c b/exynos3/s5pc110/libhdmi/fimc.c new file mode 100644 index 00000000..e229eb79 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/fimc.c @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2010 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 "fimc.h" + +typedef struct sec_img sec_img; +typedef struct sec_rect sec_rect; + +int fimc_v4l2_set_src(int fd, unsigned int hw_ver, s5p_fimc_img_info *src) +{ + struct v4l2_format fmt; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_requestbuffers req; + + /* + * To set size & format for source image (DMA-INPUT) + */ + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt.fmt.pix.width = src->full_width; + fmt.fmt.pix.height = src->full_height; + fmt.fmt.pix.pixelformat = src->color_space; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + + if (ioctl (fd, VIDIOC_S_FMT, &fmt) < 0) { + ALOGE("VIDIOC_S_FMT failed : errno=%d (%s) : fd=%d", errno, + strerror(errno), fd); + return -1; + } + + /* + * crop input size + */ + crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (0x50 == hw_ver) { + crop.c.left = src->start_x; + crop.c.top = src->start_y; + } else { + crop.c.left = 0; + crop.c.top = 0; + } + crop.c.width = src->width; + crop.c.height = src->height; + if (ioctl(fd, VIDIOC_S_CROP, &crop) < 0) { + ALOGE("Error in video VIDIOC_S_CROP (%d, %d, %d, %d)", + crop.c.left, crop.c.top, crop.c.width, crop.c.height); + return -1; + } + + /* + * input buffer type + */ + req.count = 1; + req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + req.memory = V4L2_MEMORY_USERPTR; + + if (ioctl (fd, VIDIOC_REQBUFS, &req) < 0) { + ALOGE("Error in VIDIOC_REQBUFS"); + return -1; + } + + return 0; +} + +int fimc_v4l2_set_dst(int fd, + s5p_fimc_img_info *dst, + int rotation, + int flag_h_flip, + int flag_v_flip, + unsigned int addr) +{ + struct v4l2_format fmt; + struct v4l2_control vc; + struct v4l2_framebuffer fbuf; + + /* + * set rotation configuration + */ + vc.id = V4L2_CID_HFLIP; + vc.value = flag_h_flip; + if (ioctl(fd, VIDIOC_S_CTRL, &vc) < 0) { + ALOGE("Error in video VIDIOC_S_CTRL - flag_h_flip (%d)", flag_h_flip); + return -1; + } + + vc.id = V4L2_CID_VFLIP; + vc.value = flag_v_flip; + if (ioctl(fd, VIDIOC_S_CTRL, &vc) < 0) { + ALOGE("Error in video VIDIOC_S_CTRL - flag_v_flip (%d)", flag_v_flip); + return -1; + } + + vc.id = V4L2_CID_ROTATION; + vc.value = rotation; + if (ioctl(fd, VIDIOC_S_CTRL, &vc) < 0) { + ALOGE("Error in video VIDIOC_S_CTRL - rotation (%d)", rotation); + return -1; + } + + /* + * set size, format & address for destination image (DMA-OUTPUT) + */ + if (ioctl (fd, VIDIOC_G_FBUF, &fbuf) < 0) { + ALOGE("Error in video VIDIOC_G_FBUF"); + return -1; + } + + fbuf.base = (void *)addr; + fbuf.fmt.width = dst->full_width; + fbuf.fmt.height = dst->full_height; + fbuf.fmt.pixelformat = dst->color_space; + if (ioctl (fd, VIDIOC_S_FBUF, &fbuf) < 0) { + ALOGE("Error in video VIDIOC_S_FBUF 0x%x %d %d %d", + (void *)addr, dst->full_width, dst->full_height, + dst->color_space); + return -1; + } + + /* + * set destination window + */ + fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + fmt.fmt.win.w.left = dst->start_x; + fmt.fmt.win.w.top = dst->start_y; + fmt.fmt.win.w.width = dst->width; + fmt.fmt.win.w.height = dst->height; + if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { + ALOGE("Error in video VIDIOC_S_FMT %d %d %d %d", + dst->start_x, dst->start_y, dst->width, dst->height); + return -1; + } + + return 0; +} + +static int fimc_v4l2_stream_on(int fd, enum v4l2_buf_type type) +{ + if (ioctl (fd, VIDIOC_STREAMON, &type) < 0) { + ALOGE("Error in VIDIOC_STREAMON"); + return -1; + } + + return 0; +} + +static int fimc_v4l2_queue(int fd, struct fimc_buf *fimc_buf) +{ + struct v4l2_buffer buf; + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_USERPTR; + buf.m.userptr = (unsigned long)fimc_buf; + buf.length = 0; + buf.index = 0; + + if (ioctl (fd, VIDIOC_QBUF, &buf) < 0) { + ALOGE("Error in VIDIOC_QBUF"); + return -1; + } + + return 0; +} + +static int fimc_v4l2_dequeue(int fd) +{ + struct v4l2_buffer buf; + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_USERPTR; + + if (ioctl (fd, VIDIOC_DQBUF, &buf) < 0) { + ALOGE("Error in VIDIOC_DQBUF"); + return -1; + } + + return buf.index; +} + +static int fimc_v4l2_stream_off(int fd) +{ + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if (ioctl (fd, VIDIOC_STREAMOFF, &type) < 0) { + ALOGE("Error in VIDIOC_STREAMOFF"); + return -1; + } + + return 0; +} + +static int fimc_v4l2_clr_buf(int fd) +{ + struct v4l2_requestbuffers req; + + req.count = 0; + req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + req.memory = V4L2_MEMORY_USERPTR; + + if (ioctl (fd, VIDIOC_REQBUFS, &req) < 0) { + ALOGE("Error in VIDIOC_REQBUFS"); + } + + return 0; +} + +static int fimc_handle_oneshot(int fd, struct fimc_buf *fimc_buf) +{ + int ret =0; + + if (fimc_v4l2_stream_on(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT) < 0) { + ALOGE("Fail : v4l2_stream_on()"); + return -1; + } + + if (fimc_v4l2_queue(fd, fimc_buf) < 0) { + ALOGE("Fail : v4l2_queue()"); + ret = -1; + goto stream_off; + } + + if (fimc_v4l2_dequeue(fd) < 0) { + ALOGE("Fail : v4l2_dequeue()"); + ret = -1; + goto stream_off; + } + +stream_off: + if (fimc_v4l2_stream_off(fd) < 0) { + ALOGE("Fail : v4l2_stream_off()"); + return -1; + } + + if (fimc_v4l2_clr_buf(fd) < 0) { + ALOGE("Fail : v4l2_clr_buf()"); + return -1; + } + + return ret; +} + +static int get_src_phys_addr(s5p_fimc_t *fimc, + sec_img *src_img, + unsigned int *phyAddr) +{ + if(src_img->mem_type == FIMC_MEM_TYPE_PHYS) { + switch(src_img->format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + fimc->params.src.buf_addr_phy_rgb_y = phyAddr[0]; + fimc->params.src.buf_addr_phy_cb = phyAddr[1]; + break; + default: + ALOGE("%s format error (format=0x%x)", __func__, + src_img->format); + return -1; + } + } else { + ALOGE("%s mem_type error (mem_type=%d)", __func__, src_img->mem_type); + return -1; + } + + return 0; +} + +static int get_dst_phys_addr(s5p_fimc_t *fimc, + sec_img *dst_img) +{ + unsigned int dst_phys_addr = 0; + + if (FIMC_MEM_TYPE_PHYS == dst_img->mem_type && 0 != dst_img->base) + dst_phys_addr = dst_img->base; + else { + ALOGE("%s::get_dst_phys_addr fail ", __func__); + dst_phys_addr = 0; + } + return dst_phys_addr; +} + +static inline int rotateValueHAL2PP(unsigned char transform, + int *flag_h_flip, + int *flag_v_flip) +{ + int rotate_result = 0; + int rotate_flag = transform & 0x7; + + switch (rotate_flag) { + case HAL_TRANSFORM_ROT_90: + rotate_result = 90; + break; + case HAL_TRANSFORM_ROT_180: + rotate_result = 180; + break; + case HAL_TRANSFORM_ROT_270: + rotate_result = 270; + break; + } + + switch (rotate_flag) { + case HAL_TRANSFORM_FLIP_H: + *flag_h_flip = 1; + *flag_v_flip = 0; + break; + case HAL_TRANSFORM_FLIP_V: + *flag_h_flip = 0; + *flag_v_flip = 1; + break; + default: + *flag_h_flip = 0; + *flag_v_flip = 0; + break; + } + + return rotate_result; +} + +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; +} + +static inline int widthOfPP(unsigned int ver, + int pp_color_format, + int number) +{ + if (0x50 == ver) { + switch(pp_color_format) { + /* 422 1/2/3 plane */ + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_YUV422P: + + /* 420 2/3 plane */ + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_YUV420: + return multipleOfN(number, 2); + + default : + return number; + } + } else { + switch(pp_color_format) { + case V4L2_PIX_FMT_RGB565: + return multipleOfN(number, 8); + + case V4L2_PIX_FMT_RGB32: + return multipleOfN(number, 4); + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + return multipleOfN(number, 4); + + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV16: + return multipleOfN(number, 8); + + case V4L2_PIX_FMT_YUV422P: + return multipleOfN(number, 16); + + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + return multipleOfN(number, 8); + + case V4L2_PIX_FMT_YUV420: + return multipleOfN(number, 16); + + default : + return number; + } + } + return number; +} + +static inline int heightOfPP(int pp_color_format, + int number) +{ + switch(pp_color_format) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12T: + case V4L2_PIX_FMT_YUV420: + return multipleOfN(number, 2); + + default : + return number; + } + return number; +} + +static int fimc_core(s5p_fimc_t *fimc, + sec_img *src_img, + sec_rect *src_rect, + uint32_t src_color_space, + unsigned int dst_phys_addr, + sec_img *dst_img, + sec_rect *dst_rect, + uint32_t dst_color_space, + int transform) +{ + s5p_fimc_params_t * params = &(fimc->params); + + unsigned int frame_size = 0; + struct fimc_buf fimc_src_buf; + + int src_bpp, src_planes; + int flag_h_flip = 0; + int flag_v_flip = 0; + int rotate_value = rotateValueHAL2PP(transform, &flag_h_flip, &flag_v_flip); + + /* set post processor configuration */ + params->src.full_width = src_img->w; + params->src.full_height = src_img->h; + params->src.start_x = src_rect->x; + params->src.start_y = src_rect->y; + params->src.width = widthOfPP(fimc->hw_ver, src_color_space, src_rect->w); + params->src.height = heightOfPP(src_color_space, src_rect->h); + params->src.color_space = src_color_space; + + + /* check minimum */ + if (src_rect->w < 16 || src_rect->h < 8) { + ALOGE("%s src size is not supported by fimc : f_w=%d f_h=%d x=%d y=%d \ + w=%d h=%d (ow=%d oh=%d) format=0x%x", __func__, + params->src.full_width, params->src.full_height, + params->src.start_x, params->src.start_y, params->src.width, + params->src.height, src_rect->w, src_rect->h, + params->src.color_space); + return -1; + } + + switch (rotate_value) { + case 0: + params->dst.full_width = dst_img->w; + params->dst.full_height = dst_img->h; + + params->dst.start_x = dst_rect->x; + params->dst.start_y = dst_rect->y; + + params->dst.width = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->w); + params->dst.height = heightOfPP(dst_color_space, dst_rect->h); + break; + case 90: + params->dst.full_width = dst_img->h; + params->dst.full_height = dst_img->w; + + params->dst.start_x = dst_rect->y; + params->dst.start_y = dst_img->w - (dst_rect->x + dst_rect->w); + + params->dst.width = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->h); + params->dst.height = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->w); + + if (0x50 > fimc->hw_ver) + params->dst.start_y += (dst_rect->w - params->dst.height); + break; + case 180: + params->dst.full_width = dst_img->w; + params->dst.full_height = dst_img->h; + + params->dst.start_x = dst_img->w - (dst_rect->x + dst_rect->w); + params->dst.start_y = dst_img->h - (dst_rect->y + dst_rect->h); + + params->dst.width = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->w); + params->dst.height = heightOfPP(dst_color_space, dst_rect->h); + break; + case 270: + params->dst.full_width = dst_img->h; + params->dst.full_height = dst_img->w; + + params->dst.start_x = dst_img->h - (dst_rect->y + dst_rect->h); + params->dst.start_y = dst_rect->x; + + params->dst.width = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->h); + params->dst.height = + widthOfPP(fimc->hw_ver, dst_color_space, dst_rect->w); + + if (0x50 > fimc->hw_ver) + params->dst.start_y += (dst_rect->w - params->dst.height); + break; + } + + params->dst.color_space = dst_color_space; + + /* check minimum */ + if (dst_rect->w < 8 || dst_rect->h < 4) { + ALOGE("%s dst size is not supported by fimc : \ + f_w=%d f_h=%d x=%d y=%d w=%d h=%d (ow=%d oh=%d) format=0x%x", + __func__, params->dst.full_width, params->dst.full_height, + params->dst.start_x, params->dst.start_y, params->dst.width, + params->dst.height, dst_rect->w, dst_rect->h, + params->dst.color_space); + return -1; + } + + /* check scaling limit + * the scaling limie must not be more than MAX_RESIZING_RATIO_LIMIT + */ + if (((src_rect->w > dst_rect->w) && + ((src_rect->w / dst_rect->w) > MAX_RESIZING_RATIO_LIMIT)) || + ((dst_rect->w > src_rect->w) && + ((dst_rect->w / src_rect->w) > MAX_RESIZING_RATIO_LIMIT))) { + ALOGE("%s over scaling limit : src.w=%d dst.w=%d (limit=%d)", + __func__, src_rect->w, dst_rect->w, MAX_RESIZING_RATIO_LIMIT); + return -1; + } + + + /* set configuration related to destination (DMA-OUT) + * - set input format & size + * - crop input size + * - set input buffer + * - set buffer type (V4L2_MEMORY_USERPTR) + */ + if (fimc_v4l2_set_dst(fimc->dev_fd, + ¶ms->dst, + rotate_value, + flag_h_flip, + flag_v_flip, + dst_phys_addr) < 0) { + return -1; + } + + /* set configuration related to source (DMA-INPUT) + * - set input format & size + * - crop input size + * - set input buffer + * - set buffer type (V4L2_MEMORY_USERPTR) + */ + if (fimc_v4l2_set_src(fimc->dev_fd, fimc->hw_ver, ¶ms->src) < 0) + return -1; + + /* set input dma address (Y/RGB, Cb, Cr) */ + switch (src_img->format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + /* for video display zero copy case */ + fimc_src_buf.base[0] = params->src.buf_addr_phy_rgb_y; + fimc_src_buf.base[1] = params->src.buf_addr_phy_cb; + break; + + default: + /* set source image */ + fimc_src_buf.base[0] = params->src.buf_addr_phy_rgb_y; + break; + } + + if (fimc_handle_oneshot(fimc->dev_fd, &fimc_src_buf) < 0) { + fimc_v4l2_clr_buf(fimc->dev_fd); + return -1; + } + + return 0; +} + +static +void* fimc_get_reserved_mem_addr(s5p_fimc_t *fimc) +{ + int ret; + struct v4l2_control vc; + + vc.id = V4L2_CID_RESERVED_MEM_BASE_ADDR; + vc.value = 0; + + ret = ioctl(fimc->dev_fd, VIDIOC_G_CTRL, &vc); + if (ret < 0) { + ALOGE("Err(%s) in video VIDIOC_G_CTRL (%d)",ret); + return NULL; + } + + return vc.value; +} + +int fimc_open(s5p_fimc_t *fimc, const char* dev) +{ + struct v4l2_capability cap; + struct v4l2_format fmt; + struct v4l2_control vc; + + /* open device file */ + if(fimc->dev_fd < 0) { + fimc->dev_fd = open(dev, O_RDWR); + if (fimc->dev_fd < 0) { + ALOGE("%s::Post processor open error (%d)", __func__, errno); + goto err; + } + } + + /* check capability */ + if (ioctl(fimc->dev_fd, VIDIOC_QUERYCAP, &cap) < 0) { + ALOGE("VIDIOC_QUERYCAP failed"); + goto err; + } + + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + ALOGE("%d has no streaming support", fimc->dev_fd); + goto err; + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { + ALOGE("%d is no video output", fimc->dev_fd); + goto err; + } + + /* + * malloc fimc_outinfo structure + */ + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (ioctl(fimc->dev_fd, VIDIOC_G_FMT, &fmt) < 0) { + ALOGE("%s::Error in video VIDIOC_G_FMT", __func__); + goto err; + } + + fimc->out_buf.phys_addr = fimc_get_reserved_mem_addr(fimc); + + vc.id = V4L2_CID_FIMC_VERSION; + vc.value = 0; + + if (ioctl(fimc->dev_fd, VIDIOC_G_CTRL, &vc) < 0) { + ALOGE("%s::Error in video VIDIOC_G_CTRL", __func__); + goto err; + } + fimc->hw_ver = vc.value; + + return 0; + +err: + if (0 <= fimc->dev_fd) + close(fimc->dev_fd); + fimc->dev_fd = -1; + + return -1; +} + +void fimc_close(s5p_fimc_t *fimc) +{ + /* close */ + if (0 <= fimc->dev_fd) + close(fimc->dev_fd); + fimc->dev_fd = -1; +} + +int fimc_flush(s5p_fimc_t *fimc, + struct sec_img *src_img, + struct sec_rect *src_rect, + struct sec_img *dst_img, + struct sec_rect *dst_rect, + unsigned int *phyAddr, + uint32_t transform) +{ + unsigned int dst_phys_addr = 0; + int32_t src_color_space; + int32_t dst_color_space; + + /* 1 : source address and size */ + + if(0 > get_src_phys_addr(fimc, src_img, phyAddr)) + return -1; + + /* 2 : destination address and size */ + if(0 == (dst_phys_addr = get_dst_phys_addr(fimc, dst_img))) + return -2; + + /* check whether fimc supports the src format */ + if (0 > (src_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(src_img->format))) + return -3; + + if (0 > (dst_color_space = HAL_PIXEL_FORMAT_2_V4L2_PIX(dst_img->format))) + return -4; + + if(fimc_core(fimc, src_img, src_rect, (uint32_t)src_color_space, + dst_phys_addr, dst_img, dst_rect, (uint32_t)dst_color_space, transform) < 0) + return -5; + + return 0; +} diff --git a/exynos3/s5pc110/libhdmi/fimc.h b/exynos3/s5pc110/libhdmi/fimc.h new file mode 100644 index 00000000..dfd05253 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/fimc.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 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 _FIMC_LIB_ +#define _FIMC_LIB_ + +#include "s5p_fimc.h" +#include "sec_utils.h" + +#define MAX_RESIZING_RATIO_LIMIT (63) + +enum { + FIMC_MEM_TYPE_UNKNOWN = 0, + FIMC_MEM_TYPE_PHYS, + FIMC_MEM_TYPE_VIRT, +}; + +#ifdef __cplusplus +extern "C" { +#endif + +struct sec_rect { + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; +}; + +struct sec_img { + uint32_t w; + uint32_t h; + uint32_t format; + uint32_t base; + uint32_t offset; + int mem_id; + int mem_type; +}; + +inline int SEC_MIN(int x, int y) { + return ((x < y) ? x : y); +} + +inline int SEC_MAX(int x, int y) { + return ((x > y) ? x : y); +} + +int fimc_open(s5p_fimc_t *fimc, const char* dev); + +void fimc_close(s5p_fimc_t *fimc); + +int fimc_flush(s5p_fimc_t *fimc, + struct sec_img *src_img, + struct sec_rect *src_rect, + struct sec_img *dst_img, + struct sec_rect *dst_rect, + unsigned int *phyAddr, + uint32_t transform); + +#ifdef __cplusplus +} +#endif + +#endif // end of _FIMC_LIB_ diff --git a/exynos3/s5pc110/libhdmi/fimd.c b/exynos3/s5pc110/libhdmi/fimd.c new file mode 100644 index 00000000..04865972 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/fimd.c @@ -0,0 +1,231 @@ +/* +* 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.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) + ALOGE("%s: fb[%d] open failed", __func__, win); + + return fp; +} + +int fb_close(int fp) +{ + if (fp) + close(fp); + else + ALOGE("%s: fb is not allocated %d", __func__, fp); + + return 0; +} + +int fb_get_fscreeninfo(int fp, struct fb_fix_screeninfo *fix) +{ + int ret = -1; + + ret = ioctl(fp, FBIOGET_FSCREENINFO, fix); + if (ret) + ALOGE("%s: FBIOGET_FSCREENINFO failed", __func__); + + return ret; +} + +int fb_get_vscreeninfo(int fp, struct fb_var_screeninfo *var) +{ + int ret = -1; + + ret = ioctl(fp, FBIOGET_VSCREENINFO, var); + if (ret) + ALOGE("%s:: FBIOGET_VSCREENINFO failed", __func__); + + return ret; +} + +int fb_put_vscreeninfo(int fp, struct fb_var_screeninfo *var) +{ + int ret = -1; + + ret = ioctl(fp, FBIOPUT_VSCREENINFO, var); + if (ret) + ALOGE("%s:: FBIOPUT_VSCREENINFO failed", __func__); + + return ret; +} + +char* fb_mmap(int fp, __u32 size) +{ + char *buffer; + + buffer = (char *)mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED, fp, 0); + if (!buffer) { + ALOGE("%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) + ALOGE("%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) + ALOGE("%s:: FBIOBLANK failed", __func__); + + return ret; +} + +int fb_off(int fp) +{ + int ret = -1; + + ret = ioctl(fp, FBIOBLANK, FB_BLANK_POWERDOWN); + if (ret) + ALOGE("%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) + ALOGE("%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 */ + fb_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; +} + +#if 0 + +static 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; +} + +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; +} +#endif diff --git a/exynos3/s5pc110/libhdmi/fimd.h b/exynos3/s5pc110/libhdmi/fimd.h new file mode 100644 index 00000000..bb2f36c0 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/fimd.h @@ -0,0 +1,54 @@ +/* + * 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_H__ +#define __FIMD_H__ + +#include + +#include + +#define TOTAL_FB_NUM 5 + +#ifdef __cplusplus +extern "C" { +#endif + +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(int fp, __u32 size); +int fb_get_fscreeninfo(int fp, struct fb_fix_screeninfo *fix); +int fb_get_vscreeninfo(int fp, struct fb_var_screeninfo *var); +int fb_put_vscreeninfo(int fp, struct fb_var_screeninfo *var); + +#if 0 +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); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __FIMD_H__ */ diff --git a/exynos3/s5pc110/libhdmi/hal_module.cpp b/exynos3/s5pc110/libhdmi/hal_module.cpp new file mode 100644 index 00000000..767e6b54 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/hal_module.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2011, Havlena Petr + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include "hardware/hdmi.h" + +#include "SecHDMI.h" + +using namespace android; + +#define RETURN_EINVAL_IF(hw) \ + if(!hw) { \ + ALOGE("%s: %i - Can't obtain hw driver!", __func__, __LINE__); \ + return -EINVAL; \ + } + +struct sec_hdmi_device_t { + hdmi_device_t base; + /* Sec specific "private" data can go here (base.priv) */ + SecHDMI* hw; + int lcd_width; + int lcd_height; +}; + +static SecHDMI* sec_obtain_hw(struct hdmi_device_t* device) +{ + if(!device) { + ALOGE("Can't obtain hdmi base device!"); + return NULL; + } + + struct sec_hdmi_device_t* dev = + (struct sec_hdmi_device_t *) device; + if(!dev) { + ALOGE("Can't obtain SEC hdmi device!"); + return NULL; + } + + return dev->hw; +} + +static int hdmi_connect(struct hdmi_device_t* dev) +{ + ALOGV("connect is called"); + + SecHDMI* hw = sec_obtain_hw(dev); + RETURN_EINVAL_IF(hw); + + return hw->connect(); +} + +static int hdmi_disconnect(struct hdmi_device_t* dev) +{ + ALOGV("disconnect is called"); + + SecHDMI* hw = sec_obtain_hw(dev); + RETURN_EINVAL_IF(hw); + + return hw->disconnect(); +} + +static int hdmi_clear(struct hdmi_device_t* dev, int hdmiLayer) +{ + ALOGV("clear is called"); + + SecHDMI* hw = sec_obtain_hw(dev); + RETURN_EINVAL_IF(hw); + + return 0/*hw->clear(hdmiLayer) ? 0 : -1*/; +} + +static int hdmi_blit(struct hdmi_device_t* dev, int srcW, int srcH, int srcColorFormat, + uint32_t srcYAddr, uint32_t srcCbAddr, uint32_t srcCrAddr, + int dstX, int dstY, + int layer, + int num_of_hwc_layer) +{ + ALOGV("blit is called"); + + SecHDMI* hw = sec_obtain_hw(dev); + RETURN_EINVAL_IF(hw); + + return hw->flush(srcW, srcH, srcColorFormat, srcYAddr, srcCbAddr, srcCrAddr, + dstX, dstY, layer, num_of_hwc_layer); +} + +static int hdmi_close(struct hdmi_device_t *dev) +{ + ALOGV("close is called"); + + if (!dev) { + return 0; + } + + SecHDMI* hw = sec_obtain_hw(dev); + if(hw) { + hw->destroy(); + delete hw; + } + + free(dev); + + return 0; +} + +static int hdmi_get_lcd_size(int* width, int* height) +{ + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + + int fd = -1; + char name[64]; + + for(int i = 0; fd < 0 && device_template[i]; i++) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + } + + if (fd < 0) { + return -1; + } + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) < 0) { + close(fd); + return -2; + } + + *width = info.xres; + *height = info.yres; + + close(fd); + return 0; +} + +static int hdmi_open(const struct hw_module_t *module, char const *name, + struct hw_device_t **device) +{ + int lcdWidth, lcdHeight; + + ALOGV("open: open with %s", name); + + if (strcmp("hdmi-test", name) && + strcmp("hdmi-service", name) && + strcmp("hdmi-composer", name)) { + return -EINVAL; + } + + if(hdmi_get_lcd_size(&lcdWidth, &lcdHeight) < 0) { + return -EINVAL; + } + + struct sec_hdmi_device_t *hdmi_dev = + (struct sec_hdmi_device_t *) malloc(sizeof(struct sec_hdmi_device_t)); + if(!hdmi_dev) { + return -ENOMEM; + } + memset(hdmi_dev, 0, sizeof(*hdmi_dev)); + + hdmi_dev->base.common.tag = HARDWARE_DEVICE_TAG; + hdmi_dev->base.common.version = 0; + hdmi_dev->base.common.module = (struct hw_module_t *)module; + hdmi_dev->base.common.close = (int (*)(struct hw_device_t *))hdmi_close; + hdmi_dev->base.connect = hdmi_connect; + hdmi_dev->base.disconnect = hdmi_disconnect; + hdmi_dev->base.clear = hdmi_clear; + hdmi_dev->base.blit = hdmi_blit; + hdmi_dev->lcd_width = lcdWidth; + hdmi_dev->lcd_height = lcdHeight; + + *device = &hdmi_dev->base.common; + + hdmi_dev->hw = new SecHDMI(); + if(hdmi_dev->hw->create(lcdWidth, lcdHeight) < 0) { + hdmi_close((hdmi_device_t *)hdmi_dev); + return -EINVAL; + } + + ALOGI("initzialized for lcd size: %dx%d", lcdWidth, lcdHeight); + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + open: hdmi_open, +}; + +extern "C" { + struct hw_module_t HAL_MODULE_INFO_SYM = { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: HDMI_HARDWARE_MODULE_ID, + name: "Samsung S5PC11X hdmi module", + author: "Havlena Petr ", + methods: &hal_module_methods, + }; +} diff --git a/exynos3/s5pc110/libhdmi/test/Android.mk b/exynos3/s5pc110/libhdmi/test/Android.mk new file mode 100644 index 00000000..bcdb7879 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/test/Android.mk @@ -0,0 +1,61 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +# --------------------------------------------- # +# test1 binary +# --------------------------------------------- # + +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -fno-short-enums +LOCAL_CFLAGS += -DLOG_TAG=\"test1-hdmi\" -DLOG_TYPE=1 + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../../include + +LOCAL_SRC_FILES := \ + test1.cpp + +LOCAL_MODULE := test1-hdmi +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := liblog libutils libhardware + +include $(BUILD_EXECUTABLE) + +# --------------------------------------------- # +# test2 binary +# --------------------------------------------- # + +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -fno-short-enums +LOCAL_CFLAGS += -DLOG_TAG=\"test2-hdmi\" -DLOG_TYPE=1 + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../ \ + $(LOCAL_PATH)/../../include + +LOCAL_SRC_FILES := \ + ../fimc.c \ + test2.cpp + +LOCAL_MODULE := test2-hdmi +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := liblog libutils + +include $(BUILD_EXECUTABLE) diff --git a/exynos3/s5pc110/libhdmi/test/test.h b/exynos3/s5pc110/libhdmi/test/test.h new file mode 100644 index 00000000..58c934db --- /dev/null +++ b/exynos3/s5pc110/libhdmi/test/test.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Havlena Petr, + * + * 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_TEST_H_ +#define _HDMI_TEST_H_ + +#if LOG_TYPE == 1 +#include + +#define LOGI(fmt, ...) \ + do { \ + printf(LOG_TAG"/I: "fmt"\n", __VA_ARGS__); \ + } while (0) + +#define LOGE(fmt, ...) \ + do { \ + printf(LOG_TAG"/E: "fmt"\n", __VA_ARGS__); \ + } while (0) + +#elif LOG_TYPE == 2 +#include + +#define LOGI(fmt, ...) \ + do { \ + ALOGI(fmt, __VA_ARGS__); \ + } while (0) + +#define LOGE(fmt, ...) \ + do { \ + ALOGE(fmt, __VA_ARGS__); \ + } while (0) + +#endif + +#endif // end of _HDMI_TEST_H_ diff --git a/exynos3/s5pc110/libhdmi/test/test1.cpp b/exynos3/s5pc110/libhdmi/test/test1.cpp new file mode 100644 index 00000000..3e8c911d --- /dev/null +++ b/exynos3/s5pc110/libhdmi/test/test1.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 Havlena Petr, + * + * 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 "hardware/hdmi.h" + +#include +#include +#include +#include +#include +#include + +#include "sec_lcd.h" + +#include "test.h" + +static void dump_fbs(int count) { + char name[64]; + char const * const fb_template = { + "/dev/graphics/fb%u"}; + + for(int i = 0; i < count; i++) { + snprintf(name, 64, fb_template, i); + int fd = open(name, O_RDWR, 0); + if(fd < 0) { + LOGE("%s:: Can't open %s", __func__, name); + continue; + } + + struct s3cfb_next_info fb_info; + int ret = ioctl(fd, S3CFB_GET_CURR_FB_INFO, &fb_info); + if (ret < 0) { + LOGE("%s:: ioctl(S3CFB_GET_FB_PHY__ADDR) fail: %d for %s", + __func__, ret, name); + goto close; + } + + LOGI("%s:: %s addr=0x%08x", __func__, name, fb_info.phy_start_addr); + +close: + close(fd); + } +} + +int main(int argc, char** argv) { + hw_module_t* module; + hdmi_device_t* hdmi; + int ret; + + ret = hw_get_module(HDMI_HARDWARE_MODULE_ID, + (const hw_module_t**)&module); + if(ret) { + LOGE("%s:: Hdmi device not presented", __func__); + goto fail; + } + + ret = module->methods->open(module, "hdmi-test", + (hw_device_t **)&hdmi); + if(ret < 0) { + LOGE("%s:: Can't open hdmi device", __func__); + goto fail; + } + + ret = hdmi->connect(hdmi); + if(ret < 0) { + LOGE("%s:: Can't connect hdmi device", __func__); + goto close; + } + +#if 1 + dump_fbs(5); +#endif + + for(int i = 0; i < 5; i++) { + LOGI("Blit cycle: %d", i); + ret = hdmi->blit(hdmi, + 600, /* default lcd width */ + 1024, /* default lcd height */ + HAL_PIXEL_FORMAT_BGRA_8888, /* our default pixel format */ + 0, 0, 0, /* use default frame buffer */ + 0, 0, + HDMI_MODE_UI, + 0); + if(ret < 0) { + LOGE("%s:: Can't blit to hdmi device", __func__); + break; + } + } + +disconnect: + if(hdmi->disconnect(hdmi) < 0) { + LOGE("%s:: Can't disconnect hdmi device", __func__); + } + +close: + if(hdmi->common.close(&hdmi->common) < 0) { + LOGE("%s:: Can't close hdmi device", __func__); + } + +fail: + LOGI("HDMI result: %d", ret); + return ret; +} diff --git a/exynos3/s5pc110/libhdmi/test/test2.cpp b/exynos3/s5pc110/libhdmi/test/test2.cpp new file mode 100644 index 00000000..9f841e17 --- /dev/null +++ b/exynos3/s5pc110/libhdmi/test/test2.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Havlena Petr, + * + * 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 "test.h" + +int main(int argc, char** argv) { + int ret; + s5p_fimc_t fimc; + /* fimc src and dest objects */ + sec_img src_img; + sec_img dst_img; + sec_rect src_rect; + sec_rect dst_rect; + unsigned int phyAddr[3]; + + memset(&fimc, 0, sizeof(s5p_fimc_t)); + fimc.dev_fd = -1; + ret = fimc_open(&fimc, "/dev/video2"); + if(ret < 0) { + LOGE("%s:: Can't open fimc dev[%d]", __func__, ret); + return ret; + } + + memset(&src_img, 0, sizeof(src_img)); + memset(&dst_img, 0, sizeof(src_img)); + memset(&src_rect, 0, sizeof(src_rect)); + memset(&dst_rect, 0, sizeof(src_rect)); + memset(&phyAddr, 0, sizeof(int) * sizeof(phyAddr)); + + phyAddr[0] = 0/*srcYAddr*/; + phyAddr[1] = 0/*srcCbAddr*/; + phyAddr[2] = 0/*srcCrAddr*/; + + src_img.w = 600; + src_img.h = 1024; + src_img.format = HAL_PIXEL_FORMAT_YCbCr_420_SP; + src_img.base = 0; + src_img.offset = 0; + src_img.mem_id = 0; + src_img.mem_type = FIMC_MEM_TYPE_PHYS; + src_img.w = (src_img.w + 15) & (~15); + src_img.h = (src_img.h + 1) & (~1) ; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.w = src_img.w; + src_rect.h = src_img.h; + + dst_img.w = 600; + dst_img.h = 1024; + dst_img.format = HAL_PIXEL_FORMAT_YCbCr_420_SP; + dst_img.base = (unsigned int) fimc.out_buf.phys_addr; + dst_img.offset = 0; + dst_img.mem_id = 0; + dst_img.mem_type = FIMC_MEM_TYPE_PHYS; + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.w = dst_img.w; + dst_rect.h = dst_img.h; + + LOGI("%s::sr_x %d sr_y %d sr_w %d sr_h %d dr_x %d dr_y %d dr_w %d dr_h %d ", + __func__, src_rect.x, src_rect.y, src_rect.w, src_rect.h, + dst_rect.x, dst_rect.y, dst_rect.w, dst_rect.h); + + for(int i = 0; i < 5 && ret == 0; i++) { + ret = fimc_flush(&fimc, &src_img, &src_rect, &dst_img, &dst_rect, + phyAddr, 0); + if(ret < 0) { + LOGE("%s:: Can't flush to fimc dev[%d]", __func__, ret); + } + } + + fimc_close(&fimc); + return ret; +}