diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..0360739 --- /dev/null +++ b/Android.mk @@ -0,0 +1 @@ +# Empty Android.mk diff --git a/libcopybit/Android.mk b/libcopybit/Android.mk new file mode 100644 index 0000000..9e425e0 --- /dev/null +++ b/libcopybit/Android.mk @@ -0,0 +1,73 @@ +# 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) +# HAL module implemenation, not prelinked and stored in +# hw/<COPYPIX_HARDWARE_MODULE_ID>.<ro.board.platform>.so + +ifeq ($(TARGET_USES_C2D_COMPOSITION),true) + include $(CLEAR_VARS) + LOCAL_PRELINK_MODULE := false + LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + LOCAL_SHARED_LIBRARIES := liblog libdl + LOCAL_SRC_FILES := copybit_c2d.cpp + LOCAL_MODULE := copybit.$(TARGET_BOARD_PLATFORM) + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += hardware/msm7k/libgralloc-qsd8k + LOCAL_CFLAGS += -DCOPYBIT_Z180=1 -DC2D_SUPPORT_DISPLAY=1 + include $(BUILD_SHARED_LIBRARY) +else + ifneq "$(findstring msm7630,$(TARGET_PRODUCT))" "msm7630" + ifeq ($(TARGET_BOARD_PLATFORM),msm7k) + include $(CLEAR_VARS) + ifeq ($(TARGET_GRALLOC_USES_ASHMEM),true) + LOCAL_CFLAGS += -DUSE_ASHMEM + ifeq "$(findstring msm7627,$(TARGET_PRODUCT))" "msm7627" + LOCAL_CFLAGS += -DTARGET_7x27 + endif + endif + + LOCAL_PRELINK_MODULE := false + LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + LOCAL_SHARED_LIBRARIES := liblog + LOCAL_SRC_FILES := copybit.cpp + LOCAL_MODULE := copybit.msm7k + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += hardware/msm7k/libgralloc-qsd8k + LOCAL_CFLAGS += -DCOPYBIT_MSM7K=1 + include $(BUILD_SHARED_LIBRARY) + endif + + ifeq ($(TARGET_BOARD_PLATFORM),qsd8k) + include $(CLEAR_VARS) + ifeq ($(TARGET_GRALLOC_USES_ASHMEM),true) + LOCAL_CFLAGS += -DUSE_ASHMEM + endif + + LOCAL_PRELINK_MODULE := false + LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + LOCAL_SHARED_LIBRARIES := liblog + LOCAL_SRC_FILES := copybit.cpp + LOCAL_MODULE := copybit.qsd8k + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += hardware/msm7k/libgralloc-qsd8k + LOCAL_CFLAGS += -DCOPYBIT_QSD8K=1 + include $(BUILD_SHARED_LIBRARY) + endif + endif +endif diff --git a/libcopybit/MODULE_LICENSE_APACHE2 b/libcopybit/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/libcopybit/NOTICE b/libcopybit/NOTICE new file mode 100644 index 0000000..7340b9e --- /dev/null +++ b/libcopybit/NOTICE @@ -0,0 +1,190 @@ + + 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libcopybit/c2d2.h b/libcopybit/c2d2.h new file mode 100644 index 0000000..10c94be --- /dev/null +++ b/libcopybit/c2d2.h @@ -0,0 +1,618 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __c2d2_h_ +#define __c2d2_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <EGL/egl.h> /* for EGL surfaces */ + +#ifndef C2D_API +#define C2D_API /* define API export as needed */ +#endif +#ifndef int32 +typedef int int32; +#endif +#ifndef uint32 +typedef unsigned int uint32; +#endif + +/*****************************************************************************/ +/*********************** Blit definitions *****************************/ +/*****************************************************************************/ + +/* Status codes, returned by any blit function */ +typedef enum { + C2D_STATUS_OK = 0, + C2D_STATUS_NOT_SUPPORTED = 1, + C2D_STATUS_OUT_OF_MEMORY = 2, + C2D_STATUS_INVALID_PARAM = 3, + C2D_STATUS_SURFACE_IN_USE = 4, +} C2D_STATUS; + + +/* Definitions of color format modes, used together with color formats */ +typedef enum { + C2D_FORMAT_PACK_INTO_32BIT = (1 << 8), /* pack into dword if set */ + C2D_FORMAT_SWAP_ENDIANNESS = (1 << 9), /* swaps the order */ + C2D_FORMAT_LINEAR_SPACE = (1 << 10), /* linear color space */ + C2D_FORMAT_PREMULTIPLIED = (1 << 11), /* alpha premultiplied */ + C2D_FORMAT_INVERT_ALPHA = (1 << 12), /* inverts alpha */ + C2D_FORMAT_DISABLE_ALPHA = (1 << 13), /* disables alpha */ + C2D_FORMAT_INTERLACED = (1 << 14), /* YUV line-interlaced */ + C2D_FORMAT_TRANSPARENT = (1 << 15), /* YUV 1-bit alpha in Y */ + C2D_FORMAT_MACROTILED = (1 << 16), /* tiled in macro level */ + C2D_FORMAT_TILED_4x4 = (1 << 17), /* 4x4 tiled format */ + C2D_FORMAT_SWAP_RB = (1 << 18), /* Swap R & B color components */ +} C2D_FORMAT_MODE; + +/* Definitions of supported RGB formats, used in C2D_RGB_SURFACE_DEF. + * The bits of each color channel are packed into a machine word + * representing a single pixel from left to right (MSB to LSB) in the + * order indicated by format name. For the sub-byte formats the pixels + * are packed into bytes from left to right (MSbit to LSBit). + * If the C2D_FORMAT_PACK_INTO_32BIT bit is set, the minimal + * machine word used for pixel storage is 32-bit and the whole word + * is reversed if endianness is swapped. + * If the C2D_FORMAT_SWAP_ENDIANNESS bit is set, the order within a + * minimal machine word representing a pixel + * is reversed for both sub-byte and multi-byte formats. + * If the C2D_FORMAT_LINEAR_SPACE bit is set, the color space of + * the formats below is considered linear, if applicable. + * If the C2D_FORMAT_PREMULTIPLIED bit is set, the color channels + * are premultiplied with the alpha, if applicable. + * If the C2D_FORMAT_INVERT_ALPHA bit is set, the alpha interpretation + * is inverted: 0 - opaque, 1 - transparent, if applicable. + * If the C2D_FORMAT_DISABLE_ALPHA bit is set, the alpha channel serves + * as a placeholder and is ignored during blit, if applicable. + * If the COMP_FORMAT_MACROTILED bit is set, the surface is in the + * tiled format : 64x32 for 8bpp, 32x32 for 16bpp formats */ +typedef enum { + C2D_COLOR_FORMAT_1 = 0, /* 1-bit alpha/color expansion */ + + C2D_COLOR_FORMAT_2_PALETTE = 1, /* 2-bit indices for palette */ + C2D_COLOR_FORMAT_4_PALETTE = 2, /* 4-bit indices for palette */ + C2D_COLOR_FORMAT_8_PALETTE = 3, /* 8-bit indices for palette */ + + C2D_COLOR_FORMAT_2_L = 4, /* 2-bit grayscale */ + C2D_COLOR_FORMAT_4_L = 5, /* 4-bit grayscale */ + C2D_COLOR_FORMAT_8_L = 6, /* 8-bit grayscale */ + + C2D_COLOR_FORMAT_2_A = 7, /* 2-bit alpha only */ + C2D_COLOR_FORMAT_4_A = 8, /* 4-bit alpha only */ + C2D_COLOR_FORMAT_8_A = 9, /* 8-bit alpha only */ + + C2D_COLOR_FORMAT_444_RGB = 10, /* 12-bit colors */ + C2D_COLOR_FORMAT_565_RGB = 11, /* 16-bit colors */ + C2D_COLOR_FORMAT_888_RGB = 12, /* 24-bit colors */ + + C2D_COLOR_FORMAT_1555_ARGB = 13, /* 16-bit colors (1-bit alpha) */ + C2D_COLOR_FORMAT_4444_ARGB = 14, /* 16-bit colors (4-bit alpha) */ + C2D_COLOR_FORMAT_8565_ARGB = 15, /* 24-bit colors (8-bit alpha) */ + C2D_COLOR_FORMAT_8888_ARGB = 16, /* 32-bit colors (8-bit alpha) */ + + C2D_COLOR_FORMAT_5551_RGBA = 17, /* 16-bit colors (1-bit alpha) */ + C2D_COLOR_FORMAT_4444_RGBA = 18, /* 16-bit colors (4-bit alpha) */ + C2D_COLOR_FORMAT_5658_RGBA = 19, /* 24-bit colors (8-bit alpha) */ + C2D_COLOR_FORMAT_8888_RGBA = 20, /* 32-bit colors (8-bit alpha) */ + + /* derived RGB color formats (base format + mode bits) */ + +} C2D_RGB_FORMAT; + +/* Definitions of supported YUV formats, used in C2D_YUV_SURFACE_DEF. + * Each of Y,U,V channels usually takes 1 byte and therefore is + * individually addressable. The definitions below show how Y,U,V + * channels are packed into macropixels for each particular format. + * The order is from left (smaller byte addresses) to right (larger + * byte addresses). The first three digits (4xx) denote the chroma + * subsampling in standard YUV notation. The digits in the macropixel + * denote that the whole block (from the previous digit or from the + * beginning) has to be repeated the number of times. Underscores + * between Y,U,V channels are used to describe separate planes for + * planar YUV formats. Formats are mapped to numbers so that future + * versions with various YUV permutations are easy to add. + * If the C2D_FORMAT_INTERLACED bit is set, the line order is + * interlaced: 0,2,4,...1,3,5... if applicable. + * If the C2D_FORMAT_TRANSPARENT bit is set, the least significant + * bit of Y channel serves as alpha: 0 - transparent, 1 - opaque. */ +typedef enum { + C2D_COLOR_FORMAT_411_YYUYYV = 110, /* packed, 12-bit */ + C2D_COLOR_FORMAT_411_YUYYVY = 111, /* packed, 12-bit */ + C2D_COLOR_FORMAT_411_UYYVYY = 112, /* packed, 12-bit, "Y411" */ + C2D_COLOR_FORMAT_411_YUYV2Y4 = 116, /* packed, 12-bit */ + C2D_COLOR_FORMAT_411_UYVY2Y4 = 117, /* packed, 12-bit, "Y41P" */ + + C2D_COLOR_FORMAT_422_YUYV = 120, /* packed, 16-bit, "YUY2" */ + C2D_COLOR_FORMAT_422_UYVY = 121, /* packed, 16-bit, "UYVY" */ + C2D_COLOR_FORMAT_422_YVYU = 122, /* packed, 16-bit, "YVYU" */ + C2D_COLOR_FORMAT_422_VYUY = 123, /* packed, 16-bit */ + + C2D_COLOR_FORMAT_444_YUV = 130, /* packed, 24-bit */ + C2D_COLOR_FORMAT_444_UYV = 131, /* packed, 24-bit, "IYU2" */ + C2D_COLOR_FORMAT_444_AYUV = 136, /* packed, 24-bit, "AYUV" */ + + C2D_COLOR_FORMAT_410_Y_UV = 150, /* planar, Y + interleaved UV */ + C2D_COLOR_FORMAT_411_Y_UV = 151, /* planar, Y + interleaved UV */ + C2D_COLOR_FORMAT_420_Y_UV = 152, /* planar, Y + interleaved UV */ + C2D_COLOR_FORMAT_422_Y_UV = 153, /* planar, Y + interleaved UV */ + C2D_COLOR_FORMAT_444_Y_UV = 154, /* planar, Y + interleaved UV */ + + C2D_COLOR_FORMAT_410_Y_VU = 160, /* planar, Y + interleaved VU */ + C2D_COLOR_FORMAT_411_Y_VU = 161, /* planar, Y + interleaved VU */ + C2D_COLOR_FORMAT_420_Y_VU = 162, /* planar, Y + interleaved VU */ + C2D_COLOR_FORMAT_422_Y_VU = 163, /* planar, Y + interleaved VU */ + C2D_COLOR_FORMAT_444_Y_VU = 164, /* planar, Y + interleaved VU */ + + C2D_COLOR_FORMAT_410_Y_U_V = 170, /* planar, Y + U + V separate */ + C2D_COLOR_FORMAT_411_Y_U_V = 171, /* planar, Y + U + V separate */ + C2D_COLOR_FORMAT_420_Y_V_U = 172, /* planar, Y + V + U separate */ + C2D_COLOR_FORMAT_420_Y_U_V = 173, /* planar, Y + U + V separate */ + C2D_COLOR_FORMAT_422_Y_U_V = 174, /* planar, Y + U + V separate */ + C2D_COLOR_FORMAT_444_Y_U_V = 175, /* planar, Y + U + V separate */ + + C2D_COLOR_FORMAT_800_Y = 190, /* planar, Y only, grayscale */ + + /* derived YUV color formats (base format + mode bits), FOURCC */ + + C2D_COLOR_FORMAT_411_Y411 = 112, + C2D_COLOR_FORMAT_411_Y41P = 117, + C2D_COLOR_FORMAT_411_IY41 = 117 | (1 << 14), + C2D_COLOR_FORMAT_411_Y41T = 117 | (1 << 15), + + C2D_COLOR_FORMAT_422_YUY2 = 120, + C2D_COLOR_FORMAT_422_IUYV = 121 | (1 << 14), + C2D_COLOR_FORMAT_422_Y42T = 121 | (1 << 15), + C2D_COLOR_FORMAT_444_IYU2 = 131, + + C2D_COLOR_FORMAT_420_NV12 = 152, + C2D_COLOR_FORMAT_420_NV21 = 162, + + C2D_COLOR_FORMAT_410_YUV9 = 170, + C2D_COLOR_FORMAT_410_YVU9 = 170, + C2D_COLOR_FORMAT_411_Y41B = 171, + C2D_COLOR_FORMAT_420_YV12 = 172, + C2D_COLOR_FORMAT_420_IYUV = 173, + C2D_COLOR_FORMAT_420_I420 = 173, + C2D_COLOR_FORMAT_422_YV16 = 174, + C2D_COLOR_FORMAT_422_Y42B = 174, + + C2D_COLOR_FORMAT_800_Y800 = 190, + +} C2D_YUV_FORMAT; + + +/* Configuration bits, used in the config_mask field of C2D_OBJECT struct */ +typedef enum { + C2D_SOURCE_RECT_BIT = (1 << 0), /* enables source_rect field */ + C2D_MIRROR_H_BIT = (1 << 1), /* enables horizontal flipping */ + C2D_MIRROR_V_BIT = (1 << 2), /* enables vertical flipping */ + C2D_SOURCE_TILE_BIT = (1 << 3), /* enables source surface tiling */ + C2D_TARGET_RECT_BIT = (1 << 4), /* enables target_rect field */ + C2D_ROTATE_BIT = (1 << 5), /* enables all rotation fields */ + C2D_SCISSOR_RECT_BIT = (1 << 6), /* enables scissor_rect field */ + C2D_MASK_SURFACE_BIT = (1 << 7), /* enables mask_surface_id field */ + C2D_MASK_ALIGN_BIT = (1 << 8), /* aligns mask to source_rect */ + C2D_MASK_SCALE_BIT = (1 << 9), /* enables mask surface scaling */ + C2D_MASK_TILE_BIT = (1 << 10), /* enables mask surface tiling */ + C2D_GLOBAL_ALPHA_BIT = (1 << 11), /* enables global_alpha field */ + C2D_COLOR_KEY_BIT = (1 << 12), /* enables color_key field */ + C2D_NO_PIXEL_ALPHA_BIT = (1 << 13), /* disables source alpha channel */ + C2D_NO_BILINEAR_BIT = (1 << 14), /* disables bilinear on scaling */ + C2D_NO_ANTIALIASING_BIT = (1 << 15), /* disables antialiasing on edges */ + C2D_DRAW_LINE_BIT = (1 << 16), /* enables line drawing with source rectangle */ + C2D_DRAW_LINE_NOLAST = (1 << 17), /* disable last pixel draw for line */ +} C2D_SOURCE_CONFIG; + +/* Target configuration bits, defines rotation + mirroring. + * Mirror is applied prior to rotation if enabled. */ +typedef enum { + C2D_TARGET_MIRROR_H = (1 << 0), /* horizontal flip */ + C2D_TARGET_MIRROR_V = (1 << 1), /* vertical flip */ + C2D_TARGET_ROTATE_0 = (0 << 2), /* no rotation */ + C2D_TARGET_ROTATE_90 = (1 << 2), /* 90 degree rotation */ + C2D_TARGET_ROTATE_180 = (2 << 2), /* 180 degree rotation */ + C2D_TARGET_ROTATE_270 = (3 << 2), /* 270 degree rotation, 90 + 180 */ + C2D_TARGET_MASK_ALIGN = (1 << 4), /* aligns mask to target scissor */ + C2D_TARGET_MASK_SCALE = (1 << 5), /* enables mask scaling */ + C2D_TARGET_MASK_TILE = (1 << 6), /* enables mask tiling */ + C2D_TARGET_COLOR_KEY = (1 << 7), /* enables target_color_key */ + C2D_TARGET_NO_PIXEL_ALPHA = (1 << 8), /* disables target alpha channel */ +} C2D_TARGET_CONFIG; + +#define C2D_TARGET_ROTATION_MASK (C2D_TARGET_ROTATE_90*3) + +/* Additional blend modes, can be used with both source and target configs. + If none of the below is set, the default "SRC over DST" is applied. */ +typedef enum { + C2D_ALPHA_BLEND_SRC_OVER = (0 << 20), /* Default, Porter-Duff "SRC over DST" */ + C2D_ALPHA_BLEND_SRC = (1 << 20), /* Porter-Duff "SRC" */ + C2D_ALPHA_BLEND_SRC_IN = (2 << 20), /* Porter-Duff "SRC in DST" */ + C2D_ALPHA_BLEND_DST_IN = (3 << 20), /* Porter-Duff "DST in SRC" */ + C2D_ALPHA_BLEND_SRC_OUT = (4 << 20), /* Porter-Duff "SRC out DST" */ + C2D_ALPHA_BLEND_DST_OUT = (5 << 20), /* Porter-Duff "DST out SRC" */ + C2D_ALPHA_BLEND_DST_OVER = (6 << 20), /* Porter-Duff "DST over SRC" */ + C2D_ALPHA_BLEND_SRC_ATOP = (7 << 20), /* Porter-Duff "SRC ATOP" */ + C2D_ALPHA_BLEND_DST_ATOP = (8 << 20), /* Porter-Duff "DST ATOP" */ + C2D_ALPHA_BLEND_XOR = (9 << 20), /* Xor */ + C2D_ALPHA_BLEND_MULTIPLY = (10 << 20), /* OpenVG "MULTIPLY" */ + C2D_ALPHA_BLEND_SCREEN = (11 << 20), /* OpenVG "SCREEN" */ + C2D_ALPHA_BLEND_DARKEN = (12 << 20), /* OpenVG "DARKEN" */ + C2D_ALPHA_BLEND_LIGHTEN = (13 << 20), /* OpenVG "LIGHTEN" */ + C2D_ALPHA_BLEND_ADDITIVE = (14 << 20), /* OpenVG "ADDITIVE" */ + C2D_ALPHA_BLEND_DIRECT = (15 << 20), /* Direct alpha blitting */ + C2D_ALPHA_BLEND_INVERTC = (16 << 20), /* Invert color */ + C2D_ALPHA_BLEND_NONE = (1 << 25), /* disables alpha blending */ +} C2D_ALPHA_BLEND_MODE; + + +/* Surface caps enumeration */ +typedef enum { + C2D_SOURCE = (1 << 0), /* allows to use as a source */ + C2D_TARGET = (1 << 1), /* allows to use as a target */ + C2D_MASK = (1 << 2), /* allows to use as a mask */ + C2D_PALETTE = (1 << 3), /* allows to use as a palette */ +} C2D_SURFACE_BITS; + +/* Surface type enumeration */ +typedef enum { + C2D_SURFACE_EGL = 0, /* Arbitrary EGL surface */ + C2D_SURFACE_RGB_HOST = 1, /* Host memory RGB surface */ + C2D_SURFACE_RGB_EXT = 2, /* External memory RGB surface */ + C2D_SURFACE_YUV_HOST = 3, /* Host memory YUV surface */ + C2D_SURFACE_YUV_EXT = 4, /* External memory YUV surface */ + C2D_SURFACE_WITH_PHYS = (1<<3), /* physical address allready mapped */ + /* this bit is valid with HOST types */ +} C2D_SURFACE_TYPE; + +/* Structure for registering an EGL surface as a blit surface */ +typedef struct { + EGLDisplay display; /* EGL display */ + EGLContext context; /* EGL context, reserved - pass EGL_NO_CONTEXT */ + EGLSurface surface; /* EGL surface */ +} C2D_EGL_SURFACE_DEF; + +/* Structure for registering a RGB buffer as a blit surface */ +typedef struct { + uint32 format; /* RGB color format plus additional mode bits */ + uint32 width; /* defines width in pixels */ + uint32 height; /* defines height in pixels */ + void *buffer; /* pointer to the RGB buffer */ + void *phys; /* physical address */ + int32 stride; /* defines stride in bytes, negative stride is allowed */ +} C2D_RGB_SURFACE_DEF; + +/* Structure for registering a YUV plane(s) as a blit surface */ +typedef struct { + uint32 format; /* YUV color format plus additional mode bits */ + uint32 width; /* defines width in pixels */ + uint32 height; /* defines height in pixels */ + void *plane0; /* holds the whole buffer if YUV format is not planar */ + void *phys0; /* physical address */ + int32 stride0; /* stride in bytes if YUV format is not planar */ + void *plane1; /* holds UV or VU plane for planar interleaved */ + void *phys1; /* physical address */ + int32 stride1; /* stride for UV or VU plane for planar interleaved */ + void *plane2; /* holds the 3. plane, ignored if YUV format is not planar */ + void *phys2; /* physical address */ + int32 stride2; /* stride for the 3. plane, ignored if YUV format is not planar */ +} C2D_YUV_SURFACE_DEF; + + +/* Rectangle definition */ +typedef struct { + int32 x; /* upper-left x */ + int32 y; /* upper-left y */ + int32 width; /* width */ + int32 height; /* height */ +} C2D_RECT; + +/* C2D_OBJECT encapsulates the blit parameters for a source surface. + * The fg_color defines color in target format for bits equal to 1 + * in the source C2D_COLOR_FORMAT_1 format. It also defines rendering + * color for all alpha-only source formats. If the surface_id is 0 + * the fg_color defines a constant fill color used instead of the surface. + * The bg_color defines color in target format for bits equal to 0 + * in the source C2D_COLOR_FORMAT_1 format, otherwise both are ignored. + * The palette_id is used for all palette source formats, otherwise ignored. + + * The source_rect first defines the content of the source surface, + * it is then horizontally/vertically flipped if C2D_MIRROR_*_BIT is set, + * then scaled with bilinear interpolation to exactly fit target_rect + * or repeated across target_rect if C2D_SOURCE_TILE_BIT is set, + * target_rect is then rotated clockwise by an arbitrary angle in degrees + * around the rot_orig_x/y, defined relative to target_rect's top left point, + * and then clipped to scissor_rect defined in target coordinate system. + + * Finally alpha blending is applied before pixels get written into the target. + * Surface's pixel alpha is combined with mask alpha and with global alpha. + * Mask surface follows all transformations applied to the source surface. + * Source color key defines transparent color, applied together with alpha. */ +typedef struct C2D_OBJECT_STR { + uint32 surface_id; /* source surface */ + + uint32 fg_color; /* foreground color */ + uint32 bg_color; /* background color */ + uint32 palette_id; /* one-dimensional horizontal palette surface */ + + uint32 config_mask; /* defines which fields below are enabled */ + + C2D_RECT source_rect; /* region of the source surface, 16.16 fp */ + C2D_RECT target_rect; /* position and scaling in target, 16.16 fp */ + + int32 rot_orig_x; /* rotation origin relative to target_rect's... */ + int32 rot_orig_y; /* ...top left point, both are 16.16 fp */ + int32 rotation; /* clock-wise rotation in degrees, 16.16 fp */ + + C2D_RECT scissor_rect; /* defines the clip rectangle in target surface */ + + uint32 mask_surface_id; /* source alpha-mask surface */ + uint32 global_alpha; /* 0 = fully transparent, 255 = fully opaque */ + uint32 color_key; /* transparent color for the source surface */ + + struct C2D_OBJECT_STR *next; /* pointer to the next object or NULL */ +} C2D_OBJECT; + + +/*****************************************************************************/ +/**************************** C2D API 2.0 ********************************/ +/*****************************************************************************/ + +/****************************************************************************** + * Functions to create/destroy surfaces */ + +/* Creates a generic blit surface according to its type. + * Pass a combination of desired surface bits according to planned usage. + * Accepted values for surface_bits may include bits from C2D_SURFACE_BITS, + * and also from C2D_DISPLAY for compatibility with HW display controller. + * For host memory types the memory is preallocated outside the API + * and should remain valid until surface is destroyed. + * For external memory types the memory is allocated within API. + * On success, the non-zero surface identifier is returned. + * All numbers greater that 0 are valid surface identifiers, 0 is invalid. + + * arbitrary EGL surface (including proprietary Command List Surface): + * surface_type = C2D_SURFACE_EGL + * surface_definition = C2D_EGL_SURFACE_DEF + * all fields in definition structure should be set + * context field is reserved and can be ignored + + * Host memory RGB surface: + * surface_type = C2D_SURFACE_RGB_HOST + * surface_definition = C2D_RGB_SURFACE_DEF + * all fields in definition structure should be set + + * External memory RGB surface: + * surface_type = C2D_SURFACE_RGB_EXT + * surface_definition = C2D_RGB_SURFACE_DEF + * buffer field in definition structure is ignored + + * Host memory YUV surface: + * surface_type = C2D_SURFACE_YUV_HOST + * surface_definition = C2D_YUV_SURFACE_DEF + * one or all plane and stride fields in definition structure + * should be set depending on whether the format is planar or not + + * External memory YUV surface: + * surface_type = C2D_SURFACE_YUV_EXT + * surface_definition = C2D_YUV_SURFACE_DEF + * all plane and stride fields in definition structure are ignored */ +C2D_API C2D_STATUS c2dCreateSurface( uint32 *surface_id, + uint32 surface_bits, + C2D_SURFACE_TYPE surface_type, + void *surface_definition ); + +/* Requests properties of the specified surface. */ +C2D_API C2D_STATUS c2dQuerySurface( uint32 surface_id, + uint32 *surface_bits, + C2D_SURFACE_TYPE *surface_type, + uint32 *width, uint32 *height, + uint32 *format ); + +/* Destroys a generic blit surface. + * For external memory surfaces also deallocates the memory. + * It is safe to free any external resources associated with a given + * surface on c2dCreateSurface call after this function returns. */ +C2D_API C2D_STATUS c2dDestroySurface( uint32 surface_id ); + + +/****************************************************************************** + * Functions to modify/exchange surface data */ + +/* The format of fill_color is the same as color format being used + * for specified surface. If fill_rect is NULL the whole surface is filled. + * Alpha-blending is not performed while filling. + * The operation is complete when function returns. */ +C2D_API C2D_STATUS c2dFillSurface( uint32 surface_id, + uint32 fill_color, + C2D_RECT *fill_rect ); + +/* Writes data located in host memory into the specified surface. + * The chunk of host memory is identified with surface_type and + * surface_definition, no surface registration needed in this case. + * Only C2D_SURFACE_RGB_HOST, C2D_SURFACE_YUV_HOST are accepted. + * If only part of the host memory buffer should be loaded, it should + * be configured in surface_definition using width, height and stride. + * The x and y are defined in target surface coordinate space. + * Color conversion has to be done, if color formats differ. + * Alpha-blending is not performed while writing. + * The operation is complete when function returns. */ +C2D_API C2D_STATUS c2dWriteSurface( uint32 surface_id, + C2D_SURFACE_TYPE surface_type, + void *surface_definition, + int32 x, int32 y ); + +/* Reads data from the specified surface into the host memory. + * The chunk of host memory is identified with surface_type and + * surface_definition, no surface registration needed in this case. + * Only C2D_SURFACE_RGB_HOST, C2D_SURFACE_YUV_HOST are accepted. + * If only part of the surface should be read, it should + * be configured in surface_definition using width, height and stride. + * The x and y are defined in source surface coordinate space. + * Color conversion has to be done, if color formats differ. + * Alpha-blending is not performed while reading. + * The operation is complete when function returns. */ +C2D_API C2D_STATUS c2dReadSurface( uint32 surface_id, + C2D_SURFACE_TYPE surface_type, + void *surface_definition, + int32 x, int32 y ); + +/* Notifies c2d imlementation that surface has been updated from outside the API, + * if updated_rect is NULL then the whole surface has been updated. */ +C2D_API C2D_STATUS c2dSurfaceUpdated( uint32 surface_id, + C2D_RECT *updated_rect ); + +/* Updates surface information. + * Could be called only for host surfaces set with parameter "C2D_SURFACE_WITH_PHYS". + * Count for surface planes have to be same than for already allocated surface */ +C2D_API C2D_STATUS c2dUpdateSurface( uint32 surface_id, + uint32 surface_bits, + C2D_SURFACE_TYPE surface_type, + void *surface_definition ); + +/****************************************************************************** + * Functions to do actual blit */ + +/* Draw a list of blit objects into the given target. + * The target_config is a bitwise OR of values from C2D_TARGET_CONFIG. + * The target transformation creates the effect that target surface + * is transformed before the blit and then transformed back + * after blit, however no physical target transform is performed. + * The objects_list is a linked list of blit objects, no more + * than num_objects is drawn from the given list. + * If num_objects is 0, the whole list is drawn. + * The blit is not guaranteed to complete after function returns. */ +C2D_API C2D_STATUS c2dDraw( uint32 target_id, + uint32 target_config, C2D_RECT *target_scissor, + uint32 target_mask_id, uint32 target_color_key, + C2D_OBJECT *objects_list, uint32 num_objects ); + + +/* timstamp set in the blit commands flush */ +typedef void* c2d_ts_handle; + +/* Forces any pending blit to complete for a given target. + * Non-blocking. All input surfaces for this target except those + * which are shared with other targets are expected to be immediately + * writable after client has been waiting returned timestamp with + * c2dWaitTimestamp funtion or c2dFinish has been called for same target */ +C2D_API C2D_STATUS c2dFlush( uint32 target_id, c2d_ts_handle *timestamp); + + +/* Waits the pending timestamp */ +C2D_API C2D_STATUS c2dWaitTimestamp( c2d_ts_handle timestamp ); + + +/* Forces any pending blit to complete for a given target. + * Blocking version, returns when blit is done. + * All input surfaces for this target except those which are shared with + * other targets are expected to be immediately + * writable after this function returns. */ +C2D_API C2D_STATUS c2dFinish( uint32 target_id ); + + +/*****************************************************************************/ +/****************************** Display API **********************************/ +/*****************************************************************************/ + + +/* Display input enumeration */ +typedef enum { + C2D_DISPLAY_INPUT_0 = 0, /*!< default input */ + C2D_DISPLAY_INPUT_1 = (1<<16), /*!< Overlay 1 */ + C2D_DISPLAY_INPUT_2 = (1<<17), /*!< Overlay 2... */ +} C2D_DISPLAY_INPUT; + + +/****************************************************************************** + * Functions for display output. */ + +/* Functionality described in this section is optional and is + * provided only for the cases when blit HW + * is tightly bound to the display controller. */ + +/* Display enumeration, may also be used in surface caps */ +typedef enum { + C2D_DISPLAY_MAIN = (1 << 10), /* main display */ + C2D_DISPLAY_SECONDARY = (1 << 11), /* secondary display */ + C2D_DISPLAY_TV_OUT = (1 << 12), /* tv-out */ +} C2D_DISPLAY; + +/* Display window enumeration */ +typedef enum { + C2D_DISPLAY_OVERLAY = C2D_DISPLAY_INPUT_1, /*!< Overlay window bit. This defines display input. + When defined the surface is set on the overlay window + otherwise the surface is set on the background window. */ +} C2D_DISPLAY_WINDOW; /*!< Window bit set with display parameter */ + + +/* Display update modes */ +typedef enum { + C2D_DISPLAY_MODE_TEAR_SYNC = (1 << 0), /* enables tearing sync */ + C2D_DISPLAY_MODE_SURF_REMOVE = (1 << 1), /* Remove surface from given display + input */ +} C2D_DISPLAY_MODE; + + +/* Sets the given surface as a current display front buffer. + * Several displays can be specified as an output if supported. + * Still only one input can be specified at a time fro display/displays. + * The surface remains shown until it gets replaced with another one. */ +C2D_API C2D_STATUS c2dDisplaySetSurface( uint32 display, + uint32 surface_id, uint32 mode ); + +/* Returns the current surface for a particular display. + * Only one display can be specified at a time. + * The latest surface set with compDisplaySetSurface or + * the default pre-allocated surface is returned. */ +C2D_API C2D_STATUS c2dDisplayGetSurface( uint32 display, + uint32 *surface_id ); + +/* Returns the properties for a particular display. + * Only one display can be specified at a time. */ +C2D_API C2D_STATUS c2dDisplayGetProperties( uint32 display, + uint32 *width, uint32 *height, + uint32 *format ); + +/* Sets the properties for a particular display input. + * Only one display + input can be specified at a time. + * C2D_OBJECT used to set input rect(target rect), + * blending operations, rotation...etc for display source */ +C2D_API C2D_STATUS c2dDisplaySetObject( uint32 display, + uint32 target_config, uint32 target_color_key, + C2D_OBJECT * c2dObject, uint32 mode); + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* __c2d2_h_ */ diff --git a/libcopybit/copybit.cpp b/libcopybit/copybit.cpp new file mode 100644 index 0000000..6f6fca5 --- /dev/null +++ b/libcopybit/copybit.cpp @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#define LOG_TAG "copybit" + +#include <cutils/log.h> + +#include <linux/msm_mdp.h> +#include <linux/fb.h> + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <hardware/copybit.h> + +#include "gralloc_priv.h" + +#define DEBUG_MDP_ERRORS 1 + +/******************************************************************************/ + +#if defined(COPYBIT_MSM7K) +#define MAX_SCALE_FACTOR (4) +#define MAX_DIMENSION (4096) +#elif defined(COPYBIT_QSD8K) +#define MAX_SCALE_FACTOR (8) +#define MAX_DIMENSION (2048) +#else +#error "Unsupported MDP version" +#endif + +/******************************************************************************/ + +/** State information for each device instance */ +struct copybit_context_t { + struct copybit_device_t device; + int mFD; + uint8_t mAlpha; + uint8_t mFlags; +}; + +/** + * Common hardware methods + */ + +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device); + +static struct hw_module_methods_t copybit_module_methods = { + open: open_copybit +}; + +/* + * The COPYBIT Module + */ +struct copybit_module_t HAL_MODULE_INFO_SYM = { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: COPYBIT_HARDWARE_MODULE_ID, + name: "QCT MSM7K COPYBIT Module", + author: "Google, Inc.", + methods: ©bit_module_methods + } +}; + +/******************************************************************************/ + +/** min of int a, b */ +static inline int min(int a, int b) { + return (a<b) ? a : b; +} + +/** max of int a, b */ +static inline int max(int a, int b) { + return (a>b) ? a : b; +} + +/** scale each parameter by mul/div. Assume div isn't 0 */ +static inline void MULDIV(uint32_t *a, uint32_t *b, int mul, int div) { + if (mul != div) { + *a = (mul * *a) / div; + *b = (mul * *b) / div; + } +} + +/** Determine the intersection of lhs & rhs store in out */ +static void intersect(struct copybit_rect_t *out, + const struct copybit_rect_t *lhs, + const struct copybit_rect_t *rhs) { + out->l = max(lhs->l, rhs->l); + out->t = max(lhs->t, rhs->t); + out->r = min(lhs->r, rhs->r); + out->b = min(lhs->b, rhs->b); +} + +/** convert COPYBIT_FORMAT to MDP format */ +static int get_format(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_RGB_565: return MDP_RGB_565; + case HAL_PIXEL_FORMAT_RGBX_8888: return MDP_RGBX_8888; + case HAL_PIXEL_FORMAT_RGB_888: return MDP_RGB_888; + case HAL_PIXEL_FORMAT_RGBA_8888: return MDP_RGBA_8888; + case HAL_PIXEL_FORMAT_BGRA_8888: return MDP_BGRA_8888; + case HAL_PIXEL_FORMAT_YCrCb_422_SP: return MDP_Y_CBCR_H2V1; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: return MDP_Y_CBCR_H2V2; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: return MDP_Y_CRCB_H2V1; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: return MDP_Y_CRCB_H2V2; + } + return -1; +} + +/** convert from copybit image to mdp image structure */ +static void set_image(struct mdp_img *img, const struct copybit_image_t *rhs) +{ + private_handle_t* hnd = (private_handle_t*)rhs->handle; + img->width = rhs->w; + img->height = rhs->h; + img->format = get_format(rhs->format); + img->offset = hnd->offset; + #if defined(COPYBIT_MSM7K) + #if defined(USE_ASHMEM) && (TARGET_7x27) + img->memory_id = hnd->fd; + #else //USE_ASHMEM not defined + img->memory_id = hnd->fd; + #endif //end USE_ASHMEM + #else + img->memory_id = hnd->fd; + #endif +} +/** setup rectangles */ +static void set_rects(struct copybit_context_t *dev, + struct mdp_blit_req *e, + const struct copybit_rect_t *dst, + const struct copybit_rect_t *src, + const struct copybit_rect_t *scissor, + uint32_t horiz_padding, + uint32_t vert_padding) { + struct copybit_rect_t clip; + intersect(&clip, scissor, dst); + + e->dst_rect.x = clip.l; + e->dst_rect.y = clip.t; + e->dst_rect.w = clip.r - clip.l; + e->dst_rect.h = clip.b - clip.t; + + uint32_t W, H; + if (dev->mFlags & COPYBIT_TRANSFORM_ROT_90) { + e->src_rect.x = (clip.t - dst->t) + src->t; + e->src_rect.y = (dst->r - clip.r) + src->l; + e->src_rect.w = (clip.b - clip.t); + e->src_rect.h = (clip.r - clip.l); + W = dst->b - dst->t; + H = dst->r - dst->l; + } else { + e->src_rect.x = (clip.l - dst->l) + src->l; + e->src_rect.y = (clip.t - dst->t) + src->t; + e->src_rect.w = (clip.r - clip.l); + e->src_rect.h = (clip.b - clip.t); + W = dst->r - dst->l; + H = dst->b - dst->t; + } + MULDIV(&e->src_rect.x, &e->src_rect.w, src->r - src->l, W); + MULDIV(&e->src_rect.y, &e->src_rect.h, src->b - src->t, H); + + if (dev->mFlags & COPYBIT_TRANSFORM_FLIP_V) { + if (dev->mFlags & COPYBIT_TRANSFORM_ROT_90) { + e->src_rect.x = e->src.width - (e->src_rect.x + e->src_rect.w) - horiz_padding; + }else{ + e->src_rect.y = e->src.height - (e->src_rect.y + e->src_rect.h) - vert_padding; + } + } + + if (dev->mFlags & COPYBIT_TRANSFORM_FLIP_H) { + if (dev->mFlags & COPYBIT_TRANSFORM_ROT_90) { + e->src_rect.y = e->src.height - (e->src_rect.y + e->src_rect.h) - vert_padding; + }else{ + e->src_rect.x = e->src.width - (e->src_rect.x + e->src_rect.w) - horiz_padding; + } + } +} + +/** setup mdp request */ +static void set_infos(struct copybit_context_t *dev, struct mdp_blit_req *req, int flags) { + req->alpha = dev->mAlpha; + req->transp_mask = MDP_TRANSP_NOP; + req->flags = dev->mFlags | flags; +#if defined(COPYBIT_QSD8K) + req->flags |= MDP_BLEND_FG_PREMULT; +#endif +} + +/** copy the bits */ +static int msm_copybit(struct copybit_context_t *dev, void const *list) +{ + int err = ioctl(dev->mFD, MSMFB_BLIT, + (struct mdp_blit_req_list const*)list); + LOGE_IF(err<0, "copyBits failed (%s)", strerror(errno)); + if (err == 0) { + return 0; + } else { +#if DEBUG_MDP_ERRORS + struct mdp_blit_req_list const* l = (struct mdp_blit_req_list const*)list; + for (int i=0 ; i<l->count ; i++) { + LOGD("%d: src={w=%d, h=%d, f=%d, rect={%d,%d,%d,%d}}\n" + " dst={w=%d, h=%d, f=%d, rect={%d,%d,%d,%d}}\n" + " flags=%08lx" + , + i, + l->req[i].src.width, + l->req[i].src.height, + l->req[i].src.format, + l->req[i].src_rect.x, + l->req[i].src_rect.y, + l->req[i].src_rect.w, + l->req[i].src_rect.h, + l->req[i].dst.width, + l->req[i].dst.height, + l->req[i].dst.format, + l->req[i].dst_rect.x, + l->req[i].dst_rect.y, + l->req[i].dst_rect.w, + l->req[i].dst_rect.h, + l->req[i].flags + ); + } +#endif + return -errno; + } +} + +/*****************************************************************************/ + +/** Set a parameter to value */ +static int set_parameter_copybit( + struct copybit_device_t *dev, + int name, + int value) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int status = 0; + if (ctx) { + switch(name) { + case COPYBIT_ROTATION_DEG: + switch (value) { + case 0: + ctx->mFlags &= ~0x7; + break; + case 90: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_90; + break; + case 180: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_180; + break; + case 270: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_270; + break; + default: + LOGE("Invalid value for COPYBIT_ROTATION_DEG"); + status = -EINVAL; + break; + } + break; + case COPYBIT_PLANE_ALPHA: + if (value < 0) value = 0; + if (value >= 256) value = 255; + ctx->mAlpha = value; + break; + case COPYBIT_DITHER: + if (value == COPYBIT_ENABLE) { + ctx->mFlags |= MDP_DITHER; + } else if (value == COPYBIT_DISABLE) { + ctx->mFlags &= ~MDP_DITHER; + } + break; + case COPYBIT_BLUR: + if (value == COPYBIT_ENABLE) { + ctx->mFlags |= MDP_BLUR; + } else if (value == COPYBIT_DISABLE) { + ctx->mFlags &= ~MDP_BLUR; + } + break; + case COPYBIT_TRANSFORM: + ctx->mFlags &= ~0x7; + ctx->mFlags |= value & 0x7; + break; + default: + status = -EINVAL; + break; + } + } else { + status = -EINVAL; + } + return status; +} + +/** Get a static info value */ +static int get(struct copybit_device_t *dev, int name) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int value; + if (ctx) { + switch(name) { + case COPYBIT_MINIFICATION_LIMIT: + value = MAX_SCALE_FACTOR; + break; + case COPYBIT_MAGNIFICATION_LIMIT: + value = MAX_SCALE_FACTOR; + break; + case COPYBIT_SCALING_FRAC_BITS: + value = 32; + break; + case COPYBIT_ROTATION_STEP_DEG: + value = 90; + break; + default: + value = -EINVAL; + } + } else { + value = -EINVAL; + } + return value; +} + +/** do a stretch blit type operation */ +static int stretch_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_rect_t const *dst_rect, + struct copybit_rect_t const *src_rect, + struct copybit_region_t const *region) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int status = 0; + if (ctx) { + struct { + uint32_t count; + struct mdp_blit_req req[12]; + } list; + + if (ctx->mAlpha < 255) { + switch (src->format) { + // we don't support plane alpha with RGBA formats + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + return -EINVAL; + } + } + + if (src_rect->l < 0 || src_rect->r > src->w || + src_rect->t < 0 || src_rect->b > src->h) { + // this is always invalid + return -EINVAL; + } + + if (src->w > MAX_DIMENSION || src->h > MAX_DIMENSION) + return -EINVAL; + + if (dst->w > MAX_DIMENSION || dst->h > MAX_DIMENSION) + return -EINVAL; + + const uint32_t maxCount = sizeof(list.req)/sizeof(list.req[0]); + const struct copybit_rect_t bounds = { 0, 0, dst->w, dst->h }; + struct copybit_rect_t clip; + list.count = 0; + status = 0; + while ((status == 0) && region->next(region, &clip)) { + intersect(&clip, &bounds, &clip); + mdp_blit_req* req = &list.req[list.count]; + int flags = 0; + + set_infos(ctx, req, flags); + set_image(&req->dst, dst); + set_image(&req->src, src); + set_rects(ctx, req, dst_rect, src_rect, &clip, src->horiz_padding, src->vert_padding); + + if (req->src_rect.w<=0 || req->src_rect.h<=0) + continue; + + if (req->dst_rect.w<=0 || req->dst_rect.h<=0) + continue; + + if (++list.count == maxCount) { + status = msm_copybit(ctx, &list); + list.count = 0; + } + } + if ((status == 0) && list.count) { + status = msm_copybit(ctx, &list); + } + } else { + status = -EINVAL; + } + return status; +} + +/** Perform a blit type operation */ +static int blit_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_region_t const *region) +{ + struct copybit_rect_t dr = { 0, 0, dst->w, dst->h }; + struct copybit_rect_t sr = { 0, 0, src->w, src->h }; + return stretch_copybit(dev, dst, src, &dr, &sr, region); +} + +/*****************************************************************************/ + +/** Close the copybit device */ +static int close_copybit(struct hw_device_t *dev) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + if (ctx) { + close(ctx->mFD); + free(ctx); + } + return 0; +} + +/** Open a new instance of a copybit device using name */ +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) +{ + int status = -EINVAL; + copybit_context_t *ctx; + ctx = (copybit_context_t *)malloc(sizeof(copybit_context_t)); + memset(ctx, 0, sizeof(*ctx)); + + ctx->device.common.tag = HARDWARE_DEVICE_TAG; + ctx->device.common.version = 1; + ctx->device.common.module = const_cast<hw_module_t*>(module); + ctx->device.common.close = close_copybit; + ctx->device.set_parameter = set_parameter_copybit; + ctx->device.get = get; + ctx->device.blit = blit_copybit; + ctx->device.stretch = stretch_copybit; + ctx->mAlpha = MDP_ALPHA_NOP; + ctx->mFlags = 0; + ctx->mFD = open("/dev/graphics/fb0", O_RDWR, 0); + + if (ctx->mFD < 0) { + status = errno; + LOGE("Error opening frame buffer errno=%d (%s)", + status, strerror(status)); + status = -status; + } else { + struct fb_fix_screeninfo finfo; + if (ioctl(ctx->mFD, FBIOGET_FSCREENINFO, &finfo) == 0) { + if (strncmp(finfo.id, "msmfb", 5) == 0) { + /* Success */ + status = 0; + } else { + LOGE("Error not msm frame buffer"); + status = -EINVAL; + } + } else { + LOGE("Error executing ioctl for screen info"); + status = -errno; + } + } + + if (status == 0) { + *device = &ctx->device.common; + } else { + close_copybit(&ctx->device.common); + } + return status; +} diff --git a/libcopybit/copybit_c2d.cpp b/libcopybit/copybit_c2d.cpp new file mode 100644 index 0000000..0995345 --- /dev/null +++ b/libcopybit/copybit_c2d.cpp @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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_TAG "copybit_c2d" + +#include <cutils/log.h> + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <linux/msm_kgsl.h> +#include <linux/ioctl.h> + +#include <ui/egl/android_natives.h> +#include <ui/egl/android_natives.h> +#include <private/ui/android_natives_priv.h> +#include <cutils/native_handle.h> +#include <gralloc_priv.h> + +#include <hardware/copybit.h> + +#include "c2d2.h" + +#include <dlfcn.h> +C2D_STATUS (*LINK_c2dCreateSurface)( uint32 *surface_id, + uint32 surface_bits, + C2D_SURFACE_TYPE surface_type, + void *surface_definition ); + +C2D_STATUS (*LINK_c2dUpdateSurface)( uint32 surface_id, + uint32 surface_bits, + C2D_SURFACE_TYPE surface_type, + void *surface_definition ); + +C2D_STATUS (*LINK_c2dReadSurface)( uint32 surface_id, + C2D_SURFACE_TYPE surface_type, + void *surface_definition, + int32 x, int32 y ); + +C2D_STATUS (*LINK_c2dDraw)( uint32 target_id, + uint32 target_config, C2D_RECT *target_scissor, + uint32 target_mask_id, uint32 target_color_key, + C2D_OBJECT *objects_list, uint32 num_objects ); + +C2D_STATUS (*LINK_c2dFinish)( uint32 target_id); + +C2D_STATUS (*LINK_c2dFlush)( uint32 target_id, c2d_ts_handle *timestamp); + +C2D_STATUS (*LINK_c2dWaitTimestamp)( c2d_ts_handle timestamp ); + +C2D_STATUS (*LINK_c2dDestroySurface)( uint32 surface_id ); + + +/******************************************************************************/ + +#if defined(COPYBIT_Z180) +#define MAX_SCALE_FACTOR (4096) +#define MAX_DIMENSION (4096) +#else +#error "Unsupported HW version" +#endif + +#define G12_DEVICE_NAME "/dev/kgsl-2d0" + +#define COPYBIT_SUCCESS 0 +#define COPYBIT_FAILURE -1 + +#define RGB_SURFACE 0 +#define YUV_SURFACE 1 +#define NUM_SRC_SURFACES 2 +#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) +/******************************************************************************/ + +/** State information for each device instance */ +struct copybit_context_t { + struct copybit_device_t device; + unsigned int src[NUM_SRC_SURFACES]; + unsigned int dst; /* dst surface */ + unsigned int trg_transform; /* target transform */ + C2D_OBJECT blitState; + void *libc2d2; + int g12_device_fd; + int fb_width; + int fb_height; + bool isPremultipliedAlpha; +}; + +struct blitlist{ + uint32_t count; + C2D_OBJECT blitObjects[12]; +}; + +/** + * Common hardware methods + */ + +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device); + +static struct hw_module_methods_t copybit_module_methods = { + open: open_copybit +}; + +/* + * The COPYBIT Module + */ +struct copybit_module_t HAL_MODULE_INFO_SYM = { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: COPYBIT_HARDWARE_MODULE_ID, + name: "QCT COPYBIT C2D 2.0 Module", + author: "Qualcomm", + methods: ©bit_module_methods + } +}; + + +/* convert COPYBIT_FORMAT to C2D format */ +static int get_format(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_RGB_565: return C2D_COLOR_FORMAT_565_RGB; + case HAL_PIXEL_FORMAT_RGBX_8888: return C2D_COLOR_FORMAT_8888_ARGB | C2D_FORMAT_SWAP_RB | C2D_FORMAT_DISABLE_ALPHA; + case HAL_PIXEL_FORMAT_RGBA_8888: return C2D_COLOR_FORMAT_8888_ARGB | C2D_FORMAT_SWAP_RB; + case HAL_PIXEL_FORMAT_BGRA_8888: return C2D_COLOR_FORMAT_8888_ARGB; + case HAL_PIXEL_FORMAT_RGBA_5551: return C2D_COLOR_FORMAT_5551_RGBA; + case HAL_PIXEL_FORMAT_RGBA_4444: return C2D_COLOR_FORMAT_4444_RGBA; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: return C2D_COLOR_FORMAT_420_NV12; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: return C2D_COLOR_FORMAT_420_NV21; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: return C2D_COLOR_FORMAT_420_NV12 | C2D_FORMAT_MACROTILED; + default: return -EINVAL; + } + return -EINVAL; +} + +/* ------------------------------------------------------------------- *//*! + * \internal + * \brief Get the bpp for a particular color format + * \param color format + * \return bits per pixel +*//* ------------------------------------------------------------------- */ +int c2diGetBpp(int32 colorformat) +{ + + int c2dBpp = 0; + + switch(colorformat&0xFF) + { + case C2D_COLOR_FORMAT_4444_RGBA: + case C2D_COLOR_FORMAT_4444_ARGB: + case C2D_COLOR_FORMAT_1555_ARGB: + case C2D_COLOR_FORMAT_565_RGB: + case C2D_COLOR_FORMAT_5551_RGBA: + c2dBpp = 16; + break; + case C2D_COLOR_FORMAT_8888_RGBA: + case C2D_COLOR_FORMAT_8888_ARGB: + c2dBpp = 32; + break; + case C2D_COLOR_FORMAT_8_L: + case C2D_COLOR_FORMAT_8_A: + c2dBpp = 8; + break; + case C2D_COLOR_FORMAT_4_A: + c2dBpp = 4; + break; + case C2D_COLOR_FORMAT_1: + c2dBpp = 1; + break; + default: + LOGE("%s ERROR", __func__); + break; + } + return c2dBpp; +} + +static uint32 c2d_get_gpuaddr(int device_fd, struct private_handle_t *handle) +{ + if(!handle) + return 0; + + struct kgsl_map_user_mem param; + memset(¶m, 0, sizeof(param)); + param.fd = handle->fd; + param.len = handle->size; + param.offset = handle->offset; + param.hostptr = handle->base; + + if (handle->flags & (private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP)) + param.memtype = KGSL_USER_MEM_TYPE_PMEM; + else if (handle->flags & private_handle_t::PRIV_FLAGS_USES_ASHMEM) + param.memtype = KGSL_USER_MEM_TYPE_ASHMEM; + else if (handle->flags & private_handle_t::PRIV_FLAGS_USES_ION) + param.memtype = KGSL_USER_MEM_TYPE_ION; + else { + LOGE("Invalid handle flags: 0x%x", handle->flags); + return 0; + } + + if (!ioctl(device_fd, IOCTL_KGSL_MAP_USER_MEM, (void *)¶m, sizeof(param))) { + return param.gpuaddr; + } + + return 0; +} + +static uint32 c2d_unmap_gpuaddr(int device_fd, unsigned int gpuaddr) +{ + struct kgsl_sharedmem_free param; + + memset(¶m, 0, sizeof(param)); + param.gpuaddr = gpuaddr; + + ioctl(device_fd, IOCTL_KGSL_SHAREDMEM_FREE, (void *)¶m, sizeof(param)); + return COPYBIT_SUCCESS; +} + +static int is_supported_rgb_format(int format) +{ + switch(format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: { + return COPYBIT_SUCCESS; + } + default: + return COPYBIT_FAILURE; + } +} + +static int is_supported_yuv_format(int format) +{ + switch(format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: { + return COPYBIT_SUCCESS; + } + default: + return COPYBIT_FAILURE; + } +} + +static int calculate_yuv_offset_and_stride(int format, int width, int height, int *offset, int *yStride, int *uvStride) +{ + int aligned_height = 0; + int aligned_width = 0, size = 0; + + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: { + /* NV12 Tile buffers have their luma height aligned to 32bytes and width + * aligned to 128 bytes. The chroma offset starts at an 8K boundary + */ + aligned_height = ALIGN(height, 32); + aligned_width = ALIGN(width, 128); + size = aligned_width * aligned_height; + *offset = ALIGN(size,8192); + *yStride = aligned_width; + *uvStride = aligned_width; + break; + } + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: { + aligned_width = ALIGN(width, 32); + *offset = aligned_width * height; + *yStride = aligned_width; + *uvStride = aligned_width; + break; + } + default: { + return COPYBIT_FAILURE; + } + } + return COPYBIT_SUCCESS; +} + +/** create C2D surface from copybit image */ +static int set_image(int device_fd, uint32 surfaceId, const struct copybit_image_t *rhs, int *cformat, uint32_t *mapped, + const bool isPremultipliedAlpha) +{ + struct private_handle_t* handle = (struct private_handle_t*)rhs->handle; + C2D_SURFACE_TYPE surfaceType; + int status = COPYBIT_SUCCESS; + + *cformat = get_format(rhs->format); + if(*cformat == -EINVAL) { + LOGE("%s: invalid format", __func__); + return -EINVAL; + } + + if(handle == NULL) { + LOGE("%s: invalid handle", __func__); + return -EINVAL; + } + + if (handle->gpuaddr == 0) { + handle->gpuaddr = c2d_get_gpuaddr(device_fd, handle); + if(!handle->gpuaddr) { + return COPYBIT_FAILURE; + } + *mapped = 1; + } + + /* create C2D surface */ + if(is_supported_rgb_format(rhs->format) == COPYBIT_SUCCESS) { + /* RGB */ + C2D_RGB_SURFACE_DEF surfaceDef; + + surfaceType = (C2D_SURFACE_TYPE) (C2D_SURFACE_RGB_HOST | C2D_SURFACE_WITH_PHYS); + + surfaceDef.phys = (void*) handle->gpuaddr; + surfaceDef.buffer = (void*) (handle->base); + + surfaceDef.format = *cformat | (isPremultipliedAlpha ? C2D_FORMAT_PREMULTIPLIED : 0); + surfaceDef.width = rhs->w; + surfaceDef.height = rhs->h; + surfaceDef.stride = ALIGN(((surfaceDef.width * c2diGetBpp(surfaceDef.format))>>3), 32); + + if(LINK_c2dUpdateSurface( surfaceId,C2D_TARGET | C2D_SOURCE, surfaceType, &surfaceDef)) { + LOGE("%s: RGB Surface c2dUpdateSurface ERROR", __func__); + goto error; + status = COPYBIT_FAILURE; + } + } else if (is_supported_yuv_format(rhs->format) == COPYBIT_SUCCESS) { + C2D_YUV_SURFACE_DEF surfaceDef; + int offset = 0; + int yStride = 0; + int uvStride = 0; + memset(&surfaceDef, 0, sizeof(surfaceDef)); + surfaceType = (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS); + surfaceDef.format = *cformat; + + status = calculate_yuv_offset_and_stride(rhs->format, rhs->w, rhs->h, &offset, &yStride, &uvStride); + if(status != COPYBIT_SUCCESS) { + LOGE("calculate_yuv_offset_and_stride error"); + goto error; + } + + surfaceDef.width = rhs->w; + surfaceDef.height = rhs->h; + surfaceDef.plane0 = (void*) (handle->base); + surfaceDef.phys0 = (void*) (handle->gpuaddr); + surfaceDef.stride0 = yStride; + + surfaceDef.plane1 = (void*) (handle->base + offset); + surfaceDef.phys1 = (void*) (handle->gpuaddr + offset); + surfaceDef.stride1 = uvStride; + + if(LINK_c2dUpdateSurface( surfaceId,C2D_TARGET | C2D_SOURCE, surfaceType, &surfaceDef)) { + LOGE("%s: YUV Surface c2dUpdateSurface ERROR", __func__); + goto error; + status = COPYBIT_FAILURE; + } + } else { + LOGE("%s: invalid format %x", __func__, rhs->format); + goto error; + status = COPYBIT_FAILURE; + } + + return status; + +error: + if(*mapped == 1) { + c2d_unmap_gpuaddr(device_fd, handle->gpuaddr); + handle->gpuaddr = 0; + *mapped = 0; + } + return status; +} + +static int set_src_image(int device_fd, uint32 *surfaceId, const struct copybit_image_t *rhs, int *cformat, uint32 *mapped) +{ + struct private_handle_t* handle = (struct private_handle_t*)rhs->handle; + *cformat = get_format(rhs->format); + C2D_SURFACE_TYPE surfaceType; + uint32 gpuaddr = (uint32)handle->gpuaddr; + int status = COPYBIT_SUCCESS; + + if (handle->gpuaddr == 0) + { + handle->gpuaddr = c2d_get_gpuaddr(device_fd, handle); + if(!handle->gpuaddr) + return COPYBIT_FAILURE; + + *mapped = 1; + } + + /* create C2D surface */ + if(is_supported_rgb_format(rhs->format) == COPYBIT_SUCCESS) { + /* RGB */ + C2D_RGB_SURFACE_DEF surfaceDef; + surfaceType = (C2D_SURFACE_TYPE) (C2D_SURFACE_RGB_HOST | C2D_SURFACE_WITH_PHYS); + + surfaceDef.phys = (void*) handle->gpuaddr; + surfaceDef.buffer = (void*) (handle->base); + surfaceDef.buffer = (void*) (handle->base + handle->offset); + + surfaceDef.format = get_format(rhs->format); + surfaceDef.width = rhs->w; + surfaceDef.height = rhs->h; + surfaceDef.stride = ALIGN(((surfaceDef.width * c2diGetBpp(surfaceDef.format))>>3), 32); + + if(LINK_c2dCreateSurface( surfaceId, C2D_TARGET, surfaceType,(void*)&surfaceDef)) { + LOGE("%s: LINK_c2dCreateSurface error", __func__); + status = COPYBIT_FAILURE; + goto error; + } + } else if(is_supported_yuv_format(rhs->format) == COPYBIT_SUCCESS) { + /* YUV */ + C2D_YUV_SURFACE_DEF surfaceDef; + int offset = 0; + int yStride = 0; + int uvStride = 0; + memset(&surfaceDef, 0, sizeof(surfaceDef)); + + surfaceType = (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS); + surfaceDef.format = get_format(rhs->format); + status = calculate_yuv_offset_and_stride(rhs->format, rhs->w, rhs->h, &offset, &yStride, &uvStride); + if(status != COPYBIT_SUCCESS) { + LOGE("calculate_yuv_offset_and_stride error"); + goto error; + } + + surfaceDef.width = rhs->w; + surfaceDef.height = rhs->h; + surfaceDef.plane0 = (void*) (handle->base); + surfaceDef.phys0 = (void*) handle->gpuaddr; + surfaceDef.stride0 = yStride; + + surfaceDef.plane1 = (void*) (handle->base + offset); + surfaceDef.phys1 = (void*) (handle->gpuaddr + offset); + surfaceDef.stride1 = uvStride; + + if(LINK_c2dCreateSurface( surfaceId, C2D_TARGET | C2D_SOURCE, surfaceType, (void*)&surfaceDef)) { + LOGE("%s: YUV surface LINK_c2dCreateSurface error", __func__); + status = COPYBIT_FAILURE; + goto error; + } + } else { + LOGE("%s: Invalid format %x", __func__, rhs->format); + status = COPYBIT_FAILURE; + } + + return COPYBIT_SUCCESS; + +error: + if(*mapped == 1) { + c2d_unmap_gpuaddr(device_fd, handle->gpuaddr); + handle->gpuaddr = 0; + *mapped = 0; + } + return status; +} + +void unset_image(int device_fd, uint32 surfaceId, const struct copybit_image_t *rhs, uint32 mmapped) +{ + struct private_handle_t* handle = (struct private_handle_t*)rhs->handle; + + if (mmapped && handle->gpuaddr) { + // Unmap this gpuaddr + c2d_unmap_gpuaddr(device_fd, handle->gpuaddr); + handle->gpuaddr = 0; + } +} + +static int blit_to_target(int device_fd, uint32 surfaceId, const struct copybit_image_t *rhs) +{ + struct private_handle_t* handle = (struct private_handle_t*)rhs->handle; + uint32 cformat = get_format(rhs->format); + C2D_SURFACE_TYPE surfaceType; + uint32 memoryMapped = 0; + int status = COPYBIT_SUCCESS; + + if (!handle->gpuaddr) { + handle->gpuaddr = c2d_get_gpuaddr(device_fd,handle); + if(!handle->gpuaddr) + return COPYBIT_FAILURE; + + memoryMapped = 1; + } + + /* create C2D surface */ + + if(cformat) { + /* RGB */ + C2D_RGB_SURFACE_DEF surfaceDef; + memset(&surfaceDef, 0, sizeof(surfaceDef)); + + surfaceDef.buffer = (void*) handle->base; + surfaceDef.phys = (void*) handle->gpuaddr; + + surfaceType = C2D_SURFACE_RGB_HOST; + surfaceDef.format = get_format(rhs->format); + surfaceDef.width = rhs->w; + surfaceDef.height = rhs->h; + surfaceDef.stride = ALIGN(((surfaceDef.width * c2diGetBpp(surfaceDef.format))>>3), 32); + + if(LINK_c2dReadSurface(surfaceId, surfaceType, (void*)&surfaceDef, 0, 0)) { + LOGE("%s: LINK_c2dReadSurface ERROR", __func__); + status = COPYBIT_FAILURE; + goto done; + } + } + else { + /* YUV */ + /* TODO */ + } + +done: + if (memoryMapped) { + c2d_unmap_gpuaddr(device_fd, handle->gpuaddr); + handle->gpuaddr = 0; + } + return status; +} + +/** setup rectangles */ +static void set_rects(struct copybit_context_t *ctx, + C2D_OBJECT *c2dObject, + const struct copybit_rect_t *dst, + const struct copybit_rect_t *src, + const struct copybit_rect_t *scissor) +{ + + if((ctx->trg_transform & C2D_TARGET_ROTATE_90) && + (ctx->trg_transform & C2D_TARGET_ROTATE_180)) { + /* target rotation is 270 */ + c2dObject->target_rect.x = (dst->t)<<16; + c2dObject->target_rect.y = (ALIGN(ctx->fb_width,32) - (dst->r))<<16; + c2dObject->target_rect.height = ((dst->r) - (dst->l))<<16; + c2dObject->target_rect.width = ((dst->b) - (dst->t))<<16; + } else if(ctx->trg_transform & C2D_TARGET_ROTATE_90) { + c2dObject->target_rect.x = (ctx->fb_height - dst->b)<<16; + c2dObject->target_rect.y = (dst->l)<<16; + c2dObject->target_rect.height = ((dst->r) - (dst->l))<<16; + c2dObject->target_rect.width = ((dst->b) - (dst->t))<<16; + } else if(ctx->trg_transform & C2D_TARGET_ROTATE_180) { + c2dObject->target_rect.y = (ctx->fb_height - dst->b)<<16; + c2dObject->target_rect.x = (ALIGN(ctx->fb_width,32) - dst->r)<<16; + c2dObject->target_rect.height = ((dst->b) - (dst->t))<<16; + c2dObject->target_rect.width = ((dst->r) - (dst->l))<<16; + } else { + c2dObject->target_rect.x = (dst->l)<<16; + c2dObject->target_rect.y = (dst->t)<<16; + c2dObject->target_rect.height = ((dst->b) - (dst->t))<<16; + c2dObject->target_rect.width = ((dst->r) - (dst->l))<<16; + } + c2dObject->config_mask |= C2D_TARGET_RECT_BIT; + + c2dObject->source_rect.x = (src->l)<<16; + c2dObject->source_rect.y = (src->t)<<16; + c2dObject->source_rect.height = ((src->b) - (src->t))<<16; + c2dObject->source_rect.width = ((src->r) - (src->l))<<16; + c2dObject->config_mask |= C2D_SOURCE_RECT_BIT; + + c2dObject->scissor_rect.x = scissor->l; + c2dObject->scissor_rect.y = scissor->t; + c2dObject->scissor_rect.height = (scissor->b) - (scissor->t); + c2dObject->scissor_rect.width = (scissor->r) - (scissor->l); + + c2dObject->config_mask |= C2D_SCISSOR_RECT_BIT; +} + +/** copy the bits */ +static int msm_copybit(struct copybit_context_t *dev, blitlist *list, uint32 target) +{ + int objects; + + for(objects = 0; objects < list->count; objects++) { + list->blitObjects[objects].next = &(list->blitObjects[objects+1]); + } + + if(LINK_c2dDraw(target,dev->trg_transform, 0x0, 0, 0, list->blitObjects, + list->count)) { + LOGE("%s: LINK_c2dDraw ERROR"); + return COPYBIT_FAILURE; + } + + return COPYBIT_SUCCESS; +} + +/*****************************************************************************/ + +/** Set a parameter to value */ +static int set_parameter_copybit( + struct copybit_device_t *dev, + int name, + int value) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + if (!ctx) { + LOGE("%s: null context", __func__); + return -EINVAL; + } + + switch(name) { + case COPYBIT_ROTATION_DEG: + ctx->blitState.rotation = value<<16; + /* SRC rotation */ + if(!value) + ctx->blitState.config_mask &=~C2D_ROTATE_BIT;; + break; + case COPYBIT_PLANE_ALPHA: + if (value < 0) value = 0; + if (value >= 256) value = 255; + + ctx->blitState.global_alpha = value; + + if(ctx->blitState.global_alpha<255) + ctx->blitState.config_mask |= C2D_GLOBAL_ALPHA_BIT; + else + ctx->blitState.config_mask &=~C2D_GLOBAL_ALPHA_BIT; + break; + case COPYBIT_DITHER: + /* TODO */ + break; + case COPYBIT_BLUR: + /* TODO */ + break; + case COPYBIT_TRANSFORM: + ctx->blitState.config_mask &=~C2D_ROTATE_BIT; + ctx->blitState.config_mask &=~C2D_MIRROR_H_BIT; + ctx->blitState.config_mask &=~C2D_MIRROR_V_BIT; + ctx->trg_transform = C2D_TARGET_ROTATE_0; + + if((value&0x7) == COPYBIT_TRANSFORM_ROT_180) + ctx->trg_transform = C2D_TARGET_ROTATE_180; + else if((value&0x7) == COPYBIT_TRANSFORM_ROT_270) + ctx->trg_transform = C2D_TARGET_ROTATE_90; + else { + if(value©BIT_TRANSFORM_FLIP_H) + ctx->blitState.config_mask |= C2D_MIRROR_H_BIT; + if(value©BIT_TRANSFORM_FLIP_V) + ctx->blitState.config_mask |= C2D_MIRROR_V_BIT; + if(value©BIT_TRANSFORM_ROT_90) + ctx->trg_transform = C2D_TARGET_ROTATE_270; + } + break; + case COPYBIT_PREMULTIPLIED_ALPHA: + (value == COPYBIT_ENABLE) ? ctx->isPremultipliedAlpha = true : + ctx->isPremultipliedAlpha = false; + break; + default: + LOGE("%s: default case", __func__); + return -EINVAL; + break; + } + + return COPYBIT_SUCCESS; +} + +/** Get a static info value */ +static int get(struct copybit_device_t *dev, int name) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int value; + + if (!ctx) { + LOGE("%s: null context error", __func__); + return -EINVAL; + } + + switch(name) { + case COPYBIT_MINIFICATION_LIMIT: + value = MAX_SCALE_FACTOR; + break; + case COPYBIT_MAGNIFICATION_LIMIT: + value = MAX_SCALE_FACTOR; + break; + case COPYBIT_SCALING_FRAC_BITS: + value = 32; + break; + case COPYBIT_ROTATION_STEP_DEG: + value = 1; + break; + default: + LOGE("%s: default case", __func__); + value = -EINVAL; + } + return value; +} + +static int is_alpha(int cformat) +{ + int alpha = 0; + switch (cformat & 0xFF) { + case C2D_COLOR_FORMAT_8888_ARGB: + case C2D_COLOR_FORMAT_8888_RGBA: + case C2D_COLOR_FORMAT_5551_RGBA: + case C2D_COLOR_FORMAT_4444_ARGB: + alpha = 1; + break; + default: + alpha = 0; + break; + } + + if(alpha && (cformat&C2D_FORMAT_DISABLE_ALPHA)) + alpha = 0; + + return alpha; +} + +/** do a stretch blit type operation */ +static int stretch_copybit_internal( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_rect_t const *dst_rect, + struct copybit_rect_t const *src_rect, + struct copybit_region_t const *region, + bool enableBlend) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int status = COPYBIT_SUCCESS; + uint32 maxCount; + uint32 src_mapped = 0, trg_mapped = 0; + blitlist list; + C2D_OBJECT *req; + memset(&list, 0, sizeof(list)); + int cformat; + c2d_ts_handle timestamp; + uint32 surface_index = 0; + + if (!ctx) { + LOGE("%s: null context error", __func__); + return -EINVAL; + } + + if (src->w > MAX_DIMENSION || src->h > MAX_DIMENSION) { + LOGE("%s: src dimension error", __func__); + return -EINVAL; + } + + if (dst->w > MAX_DIMENSION || dst->h > MAX_DIMENSION) { + LOGE("%s : dst dimension error dst w %d h %d", __func__, dst->w, dst->h); + return -EINVAL; + } + + maxCount = sizeof(list.blitObjects)/sizeof(C2D_OBJECT); + + struct copybit_rect_t clip; + list.count = 0; + + status = set_image(ctx->g12_device_fd, ctx->dst, dst, &cformat, &trg_mapped, ctx->isPremultipliedAlpha); + if(status) { + LOGE("%s: set_image error", __func__); + return COPYBIT_FAILURE; + } + + if(is_supported_rgb_format(src->format) == COPYBIT_SUCCESS) { + surface_index = RGB_SURFACE; + } else if (is_supported_yuv_format(src->format) == COPYBIT_SUCCESS) { + surface_index = YUV_SURFACE; + } else { + LOGE("%s: Invalid source surface format %x", __func__, src->format); + return -EINVAL; + } + + status = set_image(ctx->g12_device_fd, ctx->src[surface_index], src, &cformat, &src_mapped, ctx->isPremultipliedAlpha); + if(status) { + LOGE("%s: set_src_image error", __func__); + return COPYBIT_FAILURE; + } + + if (enableBlend) { + if(ctx->blitState.config_mask & C2D_GLOBAL_ALPHA_BIT) { + ctx->blitState.config_mask &= ~C2D_ALPHA_BLEND_NONE; + if(!(ctx->blitState.global_alpha)) { + // src alpha is zero + unset_image(ctx->g12_device_fd, ctx->src[surface_index], src, src_mapped); + unset_image(ctx->g12_device_fd, ctx->dst, dst, trg_mapped); + return status; + } + } else { + if(is_alpha(cformat)) + ctx->blitState.config_mask &= ~C2D_ALPHA_BLEND_NONE; + else + ctx->blitState.config_mask |= C2D_ALPHA_BLEND_NONE; + } + } else { + ctx->blitState.config_mask |= C2D_ALPHA_BLEND_NONE; + } + + ctx->blitState.surface_id = ctx->src[surface_index]; + + while ((status == 0) && region->next(region, &clip)) { + req = &(list.blitObjects[list.count]); + memcpy(req,&ctx->blitState,sizeof(C2D_OBJECT)); + + set_rects(ctx, req, dst_rect, src_rect, &clip); + + if (++list.count == maxCount) { + status = msm_copybit(ctx, &list, ctx->dst); + list.count = 0; + } + } + if ((status == 0) && list.count) { + status = msm_copybit(ctx, &list, ctx->dst); + } + + if(LINK_c2dFinish(ctx->dst)) { + LOGE("%s: LINK_c2dFinish ERROR", __func__); + } + + + unset_image(ctx->g12_device_fd, ctx->src[surface_index], src, src_mapped); + unset_image(ctx->g12_device_fd, ctx->dst, dst, trg_mapped); + ctx->isPremultipliedAlpha = false; + return status; +} + +static int stretch_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_rect_t const *dst_rect, + struct copybit_rect_t const *src_rect, + struct copybit_region_t const *region) +{ + return stretch_copybit_internal(dev, dst, src, dst_rect, src_rect, region, true); +} + +/** Perform a blit type operation */ +static int blit_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_region_t const *region) +{ + struct copybit_rect_t dr = { 0, 0, dst->w, dst->h }; + struct copybit_rect_t sr = { 0, 0, src->w, src->h }; + return stretch_copybit_internal(dev, dst, src, &dr, &sr, region, false); +} + +/*****************************************************************************/ + +/** Close the copybit device */ +static int close_copybit(struct hw_device_t *dev) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + if (ctx) { + LINK_c2dDestroySurface(ctx->dst); + for(int i = 0; i <NUM_SRC_SURFACES; i++) { + LINK_c2dDestroySurface(ctx->src[i]); + } + + if (ctx->libc2d2) { + ::dlclose(ctx->libc2d2); + LOGV("dlclose(libc2d2)"); + } + + if(ctx->g12_device_fd) + close(ctx->g12_device_fd); + free(ctx); + } + + return 0; +} + +/** Open a new instance of a copybit device using name */ +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) +{ + int status = COPYBIT_SUCCESS; + C2D_RGB_SURFACE_DEF surfDefinition = {0}; + C2D_YUV_SURFACE_DEF yuvSurfaceDef = {0} ; + struct copybit_context_t *ctx; + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + + int fd = -1; + int i=0; + char fbName[64]; + + ctx = (struct copybit_context_t *)malloc(sizeof(struct copybit_context_t)); + if(!ctx) { + LOGE("%s: malloc failed", __func__); + return COPYBIT_FAILURE; + } + + /* initialize drawstate */ + memset(ctx, 0, sizeof(*ctx)); + + ctx->libc2d2 = ::dlopen("libC2D2.so", RTLD_NOW); + if (!ctx->libc2d2) { + LOGE("FATAL ERROR: could not dlopen libc2d2.so: %s", dlerror()); + goto error; + } + *(void **)&LINK_c2dCreateSurface = ::dlsym(ctx->libc2d2, + "c2dCreateSurface"); + *(void **)&LINK_c2dUpdateSurface = ::dlsym(ctx->libc2d2, + "c2dUpdateSurface"); + *(void **)&LINK_c2dReadSurface = ::dlsym(ctx->libc2d2, + "c2dReadSurface"); + *(void **)&LINK_c2dDraw = ::dlsym(ctx->libc2d2, "c2dDraw"); + *(void **)&LINK_c2dFlush = ::dlsym(ctx->libc2d2, "c2dFlush"); + *(void **)&LINK_c2dFinish = ::dlsym(ctx->libc2d2, "c2dFinish"); + *(void **)&LINK_c2dWaitTimestamp = ::dlsym(ctx->libc2d2, + "c2dWaitTimestamp"); + *(void **)&LINK_c2dDestroySurface = ::dlsym(ctx->libc2d2, + "c2dDestroySurface"); + + if(!LINK_c2dCreateSurface || !LINK_c2dUpdateSurface || !LINK_c2dReadSurface + || !LINK_c2dDraw || !LINK_c2dFlush || !LINK_c2dWaitTimestamp || !LINK_c2dFinish + || !LINK_c2dDestroySurface) { + LOGE("%s: dlsym ERROR", __func__); + goto error1; + } + + ctx->device.common.tag = HARDWARE_DEVICE_TAG; + ctx->device.common.version = 1; + ctx->device.common.module = (hw_module_t*)(module); + ctx->device.common.close = close_copybit; + ctx->device.set_parameter = set_parameter_copybit; + ctx->device.get = get; + ctx->device.blit = blit_copybit; + ctx->device.stretch = stretch_copybit; + ctx->blitState.config_mask = C2D_NO_BILINEAR_BIT | C2D_NO_ANTIALIASING_BIT; + ctx->trg_transform = C2D_TARGET_ROTATE_0; + ctx->g12_device_fd = open(G12_DEVICE_NAME, O_RDWR | O_SYNC); + if(ctx->g12_device_fd < 0) { + LOGE("%s: g12_device_fd open failed", __func__); + goto error1; + } + + /* Create RGB Surface */ + surfDefinition.buffer = (void*)0xdddddddd; + surfDefinition.phys = (void*)0xdddddddd; + surfDefinition.stride = 1 * 4; + surfDefinition.width = 1; + surfDefinition.height = 1; + surfDefinition.format = C2D_COLOR_FORMAT_8888_ARGB; + + if(LINK_c2dCreateSurface(&ctx->dst,C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_RGB_HOST | + C2D_SURFACE_WITH_PHYS), &surfDefinition)) { + LOGE("%s: create ctx->dst failed", __func__); + goto error2; + } + + if(LINK_c2dCreateSurface(&(ctx->src[RGB_SURFACE]), C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_RGB_HOST | + C2D_SURFACE_WITH_PHYS), &surfDefinition)) { + LOGE("%s: create ctx->src[RGB_SURFACE] failed", __func__); + goto error3; + } + + /* Create YUV source surface */ + yuvSurfaceDef.format = C2D_COLOR_FORMAT_420_NV12; + + yuvSurfaceDef.width = 4; + yuvSurfaceDef.height = 4; + yuvSurfaceDef.plane0 = (void*)0xaaaaaaaa; + yuvSurfaceDef.phys0 = (void*) 0xaaaaaaaa; + yuvSurfaceDef.stride0 = 4; + + yuvSurfaceDef.plane1 = (void*)0xaaaaaaaa; + yuvSurfaceDef.phys1 = (void*) 0xaaaaaaaa; + yuvSurfaceDef.stride1 = 4; + + if(LINK_c2dCreateSurface(&(ctx->src[YUV_SURFACE]),C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS), + &yuvSurfaceDef)) { + LOGE("%s: create ctx->src[YUV_SURFACE] failed", __func__); + goto error4; + } + + *device = &ctx->device.common; + + while ((fd==-1) && device_template[i]) { + snprintf(fbName, 64, device_template[i], 0); + fd = open(fbName, O_RDWR, 0); + i++; + } + if (fd < 0) + goto error5; + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + goto error6; + + ctx->fb_width = info.xres; + ctx->fb_height = info.yres; + close(fd); + ctx->isPremultipliedAlpha = false; + return status; + +error6: + close(fd); + fd = -1; +error5: + LINK_c2dDestroySurface(ctx->src[YUV_SURFACE]); +error4: + LINK_c2dDestroySurface(ctx->src[RGB_SURFACE]); +error3: + LINK_c2dDestroySurface(ctx->dst); +error2: + close(ctx->g12_device_fd); +error1: + ::dlclose(ctx->libc2d2); +error: + free(ctx); + status = COPYBIT_FAILURE; + *device = NULL; + + return status; +} diff --git a/libgralloc-qsd8k/Android.mk b/libgralloc-qsd8k/Android.mk new file mode 100644 index 0000000..1157e9f --- /dev/null +++ b/libgralloc-qsd8k/Android.mk @@ -0,0 +1,66 @@ +# 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. +ifneq ($(TARGET_USES_ION),true) +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation, not prelinked and stored in +# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libGLESv1_CM libutils + +LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include \ + hardware/qcom/display/libgralloc +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +LOCAL_SRC_FILES := \ + allocator.cpp \ + framebuffer.cpp \ + gpu.cpp \ + gralloc.cpp \ + mapper.cpp \ + pmemalloc.cpp + +LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM) +LOCAL_CFLAGS:= -DLOG_TAG=\"$(TARGET_BOARD_PLATFORM).gralloc\" -DHOST -DDEBUG_CALC_FPS + +ifneq (, $(filter msm7625_ffa msm7625_surf msm7627_ffa msm7627_surf msm7627_7x_ffa msm7627_7x_surf, $(QCOM_TARGET_PRODUCT))) +LOCAL_CFLAGS += -DTARGET_MSM7x27 +endif + +ifeq ($(TARGET_HAVE_HDMI_OUT),true) +LOCAL_CFLAGS += -DHDMI_DUAL_DISPLAY +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../liboverlay +LOCAL_SHARED_LIBRARIES += liboverlay +endif + +ifeq ($(TARGET_GRALLOC_USES_ASHMEM),true) +LOCAL_CFLAGS += -DUSE_ASHMEM +endif +include $(BUILD_SHARED_LIBRARY) + + +# Build a host library for testing +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + gpu.cpp \ + pmemalloc.cpp + +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := libgralloc_qsd8k_host +LOCAL_CFLAGS:= -DLOG_TAG=\"gralloc-qsd8k\" +include $(BUILD_HOST_STATIC_LIBRARY) +endif +endif diff --git a/libgralloc-qsd8k/MODULE_LICENSE_APACHE2 b/libgralloc-qsd8k/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/libgralloc-qsd8k/NOTICE b/libgralloc-qsd8k/NOTICE new file mode 100644 index 0000000..3237da6 --- /dev/null +++ b/libgralloc-qsd8k/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2008-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libgralloc-qsd8k/allocator.cpp b/libgralloc-qsd8k/allocator.cpp new file mode 100644 index 0000000..e7645b1 --- /dev/null +++ b/libgralloc-qsd8k/allocator.cpp @@ -0,0 +1,165 @@ +/* + * 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 <cutils/log.h> + +#include "allocator.h" + + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator() + : mHeapSize(0) +{ +} + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) + : mHeapSize(0) +{ + setSize(size); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +ssize_t SimpleBestFitAllocator::setSize(size_t size) +{ + Locker::Autolock _l(mLock); + if (mHeapSize != 0) return -EINVAL; + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); + return size; +} + + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +ssize_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Locker::Autolock _l(mLock); + if (mHeapSize == 0) return -EINVAL; + ssize_t offset = alloc(size, flags); + return offset; +} + +ssize_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Locker::Autolock _l(mLock); + if (mHeapSize == 0) return -EINVAL; + chunk_t const * const freed = dealloc(offset); + if (freed) { + return 0; + } + return -ENOENT; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF(((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return -ENOMEM; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} diff --git a/libgralloc-qsd8k/allocator.h b/libgralloc-qsd8k/allocator.h new file mode 100644 index 0000000..dc81f51 --- /dev/null +++ b/libgralloc-qsd8k/allocator.h @@ -0,0 +1,130 @@ +/* + * 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 GRALLOC_ALLOCATOR_H_ +#define GRALLOC_ALLOCATOR_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include "gr.h" +#include "pmemalloc.h" + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template <typename NODE> +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + newNode->prev = 0; + newNode->next = mFirst; + mFirst->prev = newNode; + mFirst = newNode; + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) { + insertHead(newNode); + } else { + newNode->prev = mLast; + newNode->next = 0; + mLast->next = newNode; + mLast = newNode; + } + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + +class SimpleBestFitAllocator : public PmemUserspaceAllocator::Deps::Allocator +{ +public: + + SimpleBestFitAllocator(); + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual ssize_t setSize(size_t size); + + virtual ssize_t allocate(size_t size, uint32_t flags = 0); + virtual ssize_t deallocate(size_t offset); + virtual size_t size() const; + +private: + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + + static const int kMemoryAlign; + mutable Locker mLock; + LinkedList<chunk_t> mList; + size_t mHeapSize; +}; + +#endif /* GRALLOC_ALLOCATOR_H_ */ diff --git a/libgralloc-qsd8k/framebuffer.cpp b/libgralloc-qsd8k/framebuffer.cpp new file mode 100644 index 0000000..dc51e08 --- /dev/null +++ b/libgralloc-qsd8k/framebuffer.cpp @@ -0,0 +1,1130 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved. + * + * 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 <sys/mman.h> + +#include <dlfcn.h> + +#include <cutils/ashmem.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include <utils/Timers.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> +#include <stdlib.h> +#include <pthread.h> +#include <utils/Timers.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> + +#include <linux/fb.h> +#include <linux/msm_mdp.h> + +#include <GLES/gl.h> + +#include "gralloc_priv.h" +#include "gr.h" +#ifdef NO_SURFACEFLINGER_SWAPINTERVAL +#include <cutils/properties.h> +#endif + +#define FB_DEBUG 0 + +#if defined(HDMI_DUAL_DISPLAY) +#define AS_1080_RATIO_H (4.25/100) // Default Action Safe vertical limit for 1080p +#define AS_1080_RATIO_W (4.25/100) // Default Action Safe horizontal limit for 1080p +#define AS_720_RATIO_H (6.0/100) // Default Action Safe vertical limit for 720p +#define AS_720_RATIO_W (4.25/100) // Default Action Safe horizontal limit for 720p +#define AS_480_RATIO_H (8.0/100) // Default Action Safe vertical limit for 480p +#define AS_480_RATIO_W (5.0/100) // Default Action Safe horizontal limit for 480p +#define HEIGHT_1080P 1080 +#define HEIGHT_720P 720 +#define HEIGHT_480P 480 +#define EVEN_OUT(x) if (x & 0x0001) {x--;} +using overlay::Overlay; +/** min of int a, b */ +static inline int min(int a, int b) { + return (a<b) ? a : b; +} +/** max of int a, b */ +static inline int max(int a, int b) { + return (a>b) ? a : b; +} +/** align */ +static inline size_t ALIGN(size_t x, size_t align) { + return (x + align-1) & ~(align-1); +} +#endif + +char framebufferStateName[] = {'S', 'R', 'A'}; + +#ifdef DEBUG_CALC_FPS + +#define MAX_FPS_CALC_PERIOD_IN_FRAMES 128 +#define MAX_FRAMARRIVAL_STEPS 50 +#define MAX_DEBUG_FPS_LEVEL 2 + +struct debug_fps_metadata_t { + /*fps calculation based on time or number of frames*/ + enum DfmType { + DFM_FRAMES = 0, + DFM_TIME = 1, + }; + + DfmType type; + + /* indicates how much time do we wait till we calculate FPS */ + unsigned long time_period; + + /*indicates how much time elapsed since we report fps*/ + float time_elapsed; + + /* indicates how many frames do we wait till we calculate FPS */ + unsigned int period; + /* current frame, will go upto period, and then reset */ + unsigned int curr_frame; + /* frame will arrive at a multiple of 16666 us at the display. + This indicates how many steps to consider for our calculations. + For example, if framearrival_steps = 10, then the frame that arrived + after 166660 us or more will be ignored. + */ + unsigned int framearrival_steps; + /* ignorethresh_us = framearrival_steps * 16666 */ + nsecs_t ignorethresh_us; + /* used to calculate the actual frame arrival step, the times might not be + accurate + */ + unsigned int margin_us; + /* actual data storage */ + nsecs_t framearrivals[MAX_FPS_CALC_PERIOD_IN_FRAMES]; + nsecs_t accum_framearrivals[MAX_FRAMARRIVAL_STEPS]; +}; + +#endif + +/*****************************************************************************/ + +enum { + MDDI_PANEL = '1', + EBI2_PANEL = '2', + LCDC_PANEL = '3', + EXT_MDDI_PANEL = '4', + TV_PANEL = '5' +}; + +enum { + PAGE_FLIP = 0x00000001, + LOCKED = 0x00000002 +}; + +struct fb_context_t { + framebuffer_device_t device; +}; + +static int neworientation; + +#ifdef DEBUG_CALC_FPS +static debug_fps_metadata_t debug_fps_metadata; +static unsigned int debug_fps_level = 0; +#endif + +/*****************************************************************************/ + +static void +msm_copy_buffer(buffer_handle_t handle, int fd, + int width, int height, int format, + int x, int y, int w, int h); + +static int fb_setSwapInterval(struct framebuffer_device_t* dev, + int interval) +{ + fb_context_t* ctx = (fb_context_t*)dev; + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) + return -EINVAL; + + m->swapInterval = interval; + return 0; +} + +static int fb_setUpdateRect(struct framebuffer_device_t* dev, + int l, int t, int w, int h) +{ + if (((w|h) <= 0) || ((l|t)<0)) + return -EINVAL; + + fb_context_t* ctx = (fb_context_t*)dev; + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + m->info.reserved[0] = 0x54445055; // "UPDT"; + m->info.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); + m->info.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); + return 0; +} + +#ifdef DEBUG_CALC_FPS + +static void populate_debug_fps_metadata(void) +{ + char prop[PROPERTY_VALUE_MAX]; + + /*defaults calculation of fps to based on number of frames*/ + property_get("debug.gr.calcfps.type", prop, "0"); + debug_fps_metadata.type = (debug_fps_metadata_t::DfmType) atoi(prop); + + /*defaults to 1000ms*/ + property_get("debug.gr.calcfps.timeperiod", prop, "1000"); + debug_fps_metadata.time_period = atoi(prop); + + property_get("debug.gr.calcfps.period", prop, "10"); + debug_fps_metadata.period = atoi(prop); + + if (debug_fps_metadata.period > MAX_FPS_CALC_PERIOD_IN_FRAMES) { + debug_fps_metadata.period = MAX_FPS_CALC_PERIOD_IN_FRAMES; + } + + /* default ignorethresh_us: 500 milli seconds */ + property_get("debug.gr.calcfps.ignorethresh_us", prop, "500000"); + debug_fps_metadata.ignorethresh_us = atoi(prop); + + debug_fps_metadata.framearrival_steps = + (debug_fps_metadata.ignorethresh_us / 16666); + + if (debug_fps_metadata.framearrival_steps > MAX_FRAMARRIVAL_STEPS) { + debug_fps_metadata.framearrival_steps = MAX_FRAMARRIVAL_STEPS; + debug_fps_metadata.ignorethresh_us = + debug_fps_metadata.framearrival_steps * 16666; + } + + /* 2ms margin of error for the gettimeofday */ + debug_fps_metadata.margin_us = 2000; + + for (int i = 0; i < MAX_FRAMARRIVAL_STEPS; i++) + debug_fps_metadata.accum_framearrivals[i] = 0; + + LOGE("period: %d", debug_fps_metadata.period); + LOGE("ignorethresh_us: %lld", debug_fps_metadata.ignorethresh_us); +} + +static void print_fps(float fps) +{ + if (debug_fps_metadata_t::DFM_FRAMES == debug_fps_metadata.type) + LOGE("FPS for last %d frames: %3.2f", debug_fps_metadata.period, fps); + else + LOGE("FPS for last (%f ms, %d frames): %3.2f", + debug_fps_metadata.time_elapsed, + debug_fps_metadata.curr_frame, fps); + + debug_fps_metadata.curr_frame = 0; + debug_fps_metadata.time_elapsed = 0.0; + + if (debug_fps_level > 1) { + LOGE("Frame Arrival Distribution:"); + for (unsigned int i = 0; + i < ((debug_fps_metadata.framearrival_steps / 6) + 1); + i++) { + LOGE("%lld %lld %lld %lld %lld %lld", + debug_fps_metadata.accum_framearrivals[i*6], + debug_fps_metadata.accum_framearrivals[i*6+1], + debug_fps_metadata.accum_framearrivals[i*6+2], + debug_fps_metadata.accum_framearrivals[i*6+3], + debug_fps_metadata.accum_framearrivals[i*6+4], + debug_fps_metadata.accum_framearrivals[i*6+5]); + } + + /* We are done with displaying, now clear the stats */ + for (unsigned int i = 0; + i < debug_fps_metadata.framearrival_steps; + i++) + debug_fps_metadata.accum_framearrivals[i] = 0; + } + return; +} + +static void calc_fps(nsecs_t currtime_us) +{ + static nsecs_t oldtime_us = 0; + + nsecs_t diff = currtime_us - oldtime_us; + + oldtime_us = currtime_us; + + if (debug_fps_metadata_t::DFM_FRAMES == debug_fps_metadata.type && + diff > debug_fps_metadata.ignorethresh_us) { + return; + } + + if (debug_fps_metadata.curr_frame < MAX_FPS_CALC_PERIOD_IN_FRAMES) { + debug_fps_metadata.framearrivals[debug_fps_metadata.curr_frame++] = diff; + } + + if (debug_fps_level > 1) { + unsigned int currstep = (diff + debug_fps_metadata.margin_us) / 16666; + + if (currstep < debug_fps_metadata.framearrival_steps) { + debug_fps_metadata.accum_framearrivals[currstep-1]++; + } + } + + if (debug_fps_metadata_t::DFM_FRAMES == debug_fps_metadata.type) { + if (debug_fps_metadata.curr_frame == debug_fps_metadata.period) { + /* time to calculate and display FPS */ + nsecs_t sum = 0; + for (unsigned int i = 0; i < debug_fps_metadata.period; i++) + sum += debug_fps_metadata.framearrivals[i]; + print_fps((debug_fps_metadata.period * float(1000000))/float(sum)); + } + } + else if (debug_fps_metadata_t::DFM_TIME == debug_fps_metadata.type) { + debug_fps_metadata.time_elapsed += ((float)diff/1000.0); + if (debug_fps_metadata.time_elapsed >= debug_fps_metadata.time_period) { + float fps = (1000.0 * debug_fps_metadata.curr_frame)/ + (float)debug_fps_metadata.time_elapsed; + print_fps(fps); + } + } + return; +} + +#endif // DEBUG_CALC_FPS + +static void *disp_loop(void *ptr) +{ + struct qbuf_t nxtBuf; + static int cur_buf=-1; + private_module_t *m = reinterpret_cast<private_module_t*>(ptr); + + while (1) { + pthread_mutex_lock(&(m->qlock)); + + // wait (sleep) while display queue is empty; + if (m->disp.isEmpty()) { + pthread_cond_wait(&(m->qpost),&(m->qlock)); + } + + // dequeue next buff to display and lock it + nxtBuf = m->disp.getHeadValue(); + m->disp.pop(); + pthread_mutex_unlock(&(m->qlock)); + + // post buf out to display synchronously + private_handle_t const* hnd = reinterpret_cast<private_handle_t const*> + (nxtBuf.buf); + const size_t offset = hnd->base - m->framebuffer->base; + m->info.activate = FB_ACTIVATE_VBL; + m->info.yoffset = offset / m->finfo.line_length; + +#if defined(HDMI_DUAL_DISPLAY) + pthread_mutex_lock(&m->overlayLock); + m->orientation = neworientation; + m->currentOffset = offset; + m->hdmiStateChanged = true; + pthread_cond_signal(&(m->overlayPost)); + pthread_mutex_unlock(&m->overlayLock); +#endif + if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) { + LOGE("ERROR FBIOPUT_VSCREENINFO failed; frame not displayed"); + } + +#ifdef DEBUG_CALC_FPS + if (debug_fps_level > 0) calc_fps(ns2us(systemTime())); +#endif + + if (cur_buf == -1) { + int nxtAvail = ((nxtBuf.idx + 1) % m->numBuffers); + pthread_mutex_lock(&(m->avail[nxtBuf.idx].lock)); + m->avail[nxtBuf.idx].is_avail = true; + m->avail[nxtBuf.idx].state = REF; + pthread_cond_broadcast(&(m->avail[nxtBuf.idx].cond)); + pthread_mutex_unlock(&(m->avail[nxtBuf.idx].lock)); + } else { + pthread_mutex_lock(&(m->avail[nxtBuf.idx].lock)); + if (m->avail[nxtBuf.idx].state != SUB) { + LOGE_IF(m->swapInterval != 0, "[%d] state %c, expected %c", nxtBuf.idx, + framebufferStateName[m->avail[nxtBuf.idx].state], + framebufferStateName[SUB]); + } + m->avail[nxtBuf.idx].state = REF; + pthread_mutex_unlock(&(m->avail[nxtBuf.idx].lock)); + + pthread_mutex_lock(&(m->avail[cur_buf].lock)); + m->avail[cur_buf].is_avail = true; + if (m->avail[cur_buf].state != REF) { + LOGE_IF(m->swapInterval != 0, "[%d] state %c, expected %c", cur_buf, + framebufferStateName[m->avail[cur_buf].state], + framebufferStateName[REF]); + } + m->avail[cur_buf].state = AVL; + pthread_cond_broadcast(&(m->avail[cur_buf].cond)); + pthread_mutex_unlock(&(m->avail[cur_buf].lock)); + } + cur_buf = nxtBuf.idx; + } + return NULL; +} + +#if defined(HDMI_DUAL_DISPLAY) +static void *hdmi_ui_loop(void *ptr) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + ptr); + while (1) { + pthread_mutex_lock(&m->overlayLock); + while(!(m->hdmiStateChanged)) + pthread_cond_wait(&(m->overlayPost), &(m->overlayLock)); + m->hdmiStateChanged = false; + if (m->exitHDMIUILoop) { + pthread_mutex_unlock(&m->overlayLock); + return NULL; + } + float asWidthRatio = m->actionsafeWidthRatio/100.0f; + float asHeightRatio = m->actionsafeHeightRatio/100.0f; + + if (m->pobjOverlay) { + Overlay* pTemp = m->pobjOverlay; + if (!m->enableHDMIOutput) + pTemp->closeChannel(); + else if (m->enableHDMIOutput && !m->videoOverlay) { + if (!pTemp->isChannelUP()) { + int alignedW = ALIGN(m->info.xres, 32); + + private_handle_t const* hnd = + reinterpret_cast<private_handle_t const*>(m->framebuffer); + overlay_buffer_info info; + info.width = alignedW; + info.height = hnd->height; + info.format = hnd->format; + info.size = hnd->size; + + if (pTemp->startChannel(info, 1, + false, true, 0, VG0_PIPE, true)) { + pTemp->setFd(m->framebuffer->fd); + pTemp->setCrop(0, 0, m->info.xres, m->info.yres); + } else + pTemp->closeChannel(); + } + + if (pTemp->isChannelUP()) { + int width = pTemp->getFBWidth(); + int height = pTemp->getFBHeight(); + int aswidth = width, asheight = height; + int asX = 0, asY = 0; // Action safe x, y co-ordinates + int fbwidth = m->info.xres, fbheight = m->info.yres; + float defaultASWidthRatio = 0.0f, defaultASHeightRatio = 0.0f; + if(HEIGHT_1080P == height) { + defaultASHeightRatio = AS_1080_RATIO_H; + defaultASWidthRatio = AS_1080_RATIO_W; + } else if(HEIGHT_720P == height) { + defaultASHeightRatio = AS_720_RATIO_H; + defaultASWidthRatio = AS_720_RATIO_W; + } else if(HEIGHT_480P == height) { + defaultASHeightRatio = AS_480_RATIO_H; + defaultASWidthRatio = AS_480_RATIO_W; + } + if(asWidthRatio <= 0.0f) + asWidthRatio = defaultASWidthRatio; + if(asHeightRatio <= 0.0f) + asHeightRatio = defaultASHeightRatio; + + aswidth = (int)((float)width - (float)(width * asWidthRatio)); + asheight = (int)((float)height - (float)(height * asHeightRatio)); + asX = (width - aswidth) / 2; + asY = (height - asheight) / 2; + int rot = m->orientation; + if (fbwidth < fbheight) { + switch(rot) { + // ROT_0 + case 0: + // ROT_180 + case HAL_TRANSFORM_ROT_180: { + aswidth = (asheight * fbwidth) / fbheight; + asX = (width - aswidth) / 2; + if(rot == HAL_TRANSFORM_ROT_180) + rot = OVERLAY_TRANSFORM_ROT_180; + else + rot = 0; + } + break; + // ROT_90 + case HAL_TRANSFORM_ROT_90: + rot = OVERLAY_TRANSFORM_ROT_270; + break; + // ROT_270 + case HAL_TRANSFORM_ROT_270: + rot = OVERLAY_TRANSFORM_ROT_90; + break; + } + } + else if (fbwidth > fbheight) { + switch(rot) { + // ROT_0 + case 0: + rot = 0; + break; + // ROT_180 + case HAL_TRANSFORM_ROT_180: + rot = OVERLAY_TRANSFORM_ROT_180; + break; + // ROT_90 + case HAL_TRANSFORM_ROT_90: + // ROT_270 + case HAL_TRANSFORM_ROT_270: { + //Swap width and height + int t = fbwidth; + fbwidth = fbheight; + fbheight = t; + aswidth = (asheight * fbwidth) / fbheight; + asX = (width - aswidth) / 2; + if(rot == HAL_TRANSFORM_ROT_90) + rot = OVERLAY_TRANSFORM_ROT_270; + else + rot = OVERLAY_TRANSFORM_ROT_90; + } + break; + } + } + int currOrientation = 0; + pTemp->getOrientation(currOrientation); + if(rot != currOrientation) { + pTemp->setParameter(OVERLAY_TRANSFORM, + rot); + } + EVEN_OUT(asX); + EVEN_OUT(asY); + EVEN_OUT(aswidth); + EVEN_OUT(asheight); + int currentX = 0, currentY = 0; + uint32_t currentW = width, currentH = height; + if (pTemp->getPosition(currentX, currentY, currentW, currentH)) { + if ((currentX != asX) || (currentY != asY) || (currentW != aswidth) + || (currentH != asheight)) { + pTemp->setPosition(asX, asY, aswidth, asheight); + } + } + pTemp->queueBuffer(m->currentOffset); + } + } + else + pTemp->closeChannel(); + } + pthread_mutex_unlock(&m->overlayLock); + } + return NULL; +} + +static int fb_videoOverlayStarted(struct framebuffer_device_t* dev, int started) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + pthread_mutex_lock(&m->overlayLock); + Overlay* pTemp = m->pobjOverlay; + if(started != m->videoOverlay) { + m->hdmiStateChanged = true; + if (started && pTemp) { + pTemp->closeChannel(); + m->videoOverlay = true; + pthread_cond_signal(&(m->overlayPost)); + } + else { + m->videoOverlay = false; + pthread_cond_signal(&(m->overlayPost)); + } + } + pthread_mutex_unlock(&m->overlayLock); + return 0; +} + +static int fb_enableHDMIOutput(struct framebuffer_device_t* dev, int enable) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + pthread_mutex_lock(&m->overlayLock); + Overlay* pTemp = m->pobjOverlay; + if (!enable && pTemp) + pTemp->closeChannel(); + m->enableHDMIOutput = enable; + m->hdmiStateChanged = true; + pthread_cond_signal(&(m->overlayPost)); + pthread_mutex_unlock(&m->overlayLock); + return 0; +} + +static int fb_setActionSafeWidthRatio(struct framebuffer_device_t* dev, float asWidthRatio) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + pthread_mutex_lock(&m->overlayLock); + m->actionsafeWidthRatio = asWidthRatio; + pthread_mutex_unlock(&m->overlayLock); + return 0; +} + +static int fb_setActionSafeHeightRatio(struct framebuffer_device_t* dev, float asHeightRatio) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + pthread_mutex_lock(&m->overlayLock); + m->actionsafeHeightRatio = asHeightRatio; + pthread_mutex_unlock(&m->overlayLock); + return 0; +} +static int fb_orientationChanged(struct framebuffer_device_t* dev, int orientation) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + pthread_mutex_lock(&m->overlayLock); + neworientation = orientation; + pthread_mutex_unlock(&m->overlayLock); + return 0; +} +#endif + +static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer) +{ + if (private_handle_t::validate(buffer) < 0) + return -EINVAL; + + int nxtIdx, futureIdx = -1; + bool reuse; + struct qbuf_t qb; + fb_context_t* ctx = (fb_context_t*)dev; + + private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer); + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + + if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { + + reuse = false; + nxtIdx = (m->currentIdx + 1) % m->numBuffers; + futureIdx = (nxtIdx + 1) % m->numBuffers; + + if (m->swapInterval == 0) { + // if SwapInterval = 0 and no buffers available then reuse + // current buf for next rendering so don't post new buffer + if (pthread_mutex_trylock(&(m->avail[nxtIdx].lock))) { + reuse = true; + } else { + if (! m->avail[nxtIdx].is_avail) + reuse = true; + pthread_mutex_unlock(&(m->avail[nxtIdx].lock)); + } + } + + if(!reuse){ + // unlock previous ("current") Buffer and lock the new buffer + m->base.lock(&m->base, buffer, + private_module_t::PRIV_USAGE_LOCKED_FOR_POST, + 0,0, m->info.xres, m->info.yres, NULL); + + // post/queue the new buffer + pthread_mutex_lock(&(m->avail[nxtIdx].lock)); + if (m->avail[nxtIdx].is_avail != true) { + LOGE_IF(m->swapInterval != 0, "Found %d buf to be not avail", nxtIdx); + } + + m->avail[nxtIdx].is_avail = false; + + if (m->avail[nxtIdx].state != AVL) { + LOGD("[%d] state %c, expected %c", nxtIdx, + framebufferStateName[m->avail[nxtIdx].state], + framebufferStateName[AVL]); + } + + m->avail[nxtIdx].state = SUB; + pthread_mutex_unlock(&(m->avail[nxtIdx].lock)); + + qb.idx = nxtIdx; + qb.buf = buffer; + pthread_mutex_lock(&(m->qlock)); + m->disp.push(qb); + pthread_cond_signal(&(m->qpost)); + pthread_mutex_unlock(&(m->qlock)); + + if (m->currentBuffer) + m->base.unlock(&m->base, m->currentBuffer); + + m->currentBuffer = buffer; + m->currentIdx = nxtIdx; + } else { + if (m->currentBuffer) + m->base.unlock(&m->base, m->currentBuffer); + m->base.lock(&m->base, buffer, + private_module_t::PRIV_USAGE_LOCKED_FOR_POST, + 0,0, m->info.xres, m->info.yres, NULL); + m->currentBuffer = buffer; + } + + } else { + void* fb_vaddr; + void* buffer_vaddr; + + m->base.lock(&m->base, m->framebuffer, + GRALLOC_USAGE_SW_WRITE_RARELY, + 0, 0, m->info.xres, m->info.yres, + &fb_vaddr); + + m->base.lock(&m->base, buffer, + GRALLOC_USAGE_SW_READ_RARELY, + 0, 0, m->info.xres, m->info.yres, + &buffer_vaddr); + + //memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres); + + msm_copy_buffer( + m->framebuffer, m->framebuffer->fd, + m->info.xres, m->info.yres, m->fbFormat, + m->info.xoffset, m->info.yoffset, + m->info.width, m->info.height); + + m->base.unlock(&m->base, buffer); + m->base.unlock(&m->base, m->framebuffer); + } + + LOGD_IF(FB_DEBUG, "Framebuffer state: [0] = %c [1] = %c [2] = %c", + framebufferStateName[m->avail[0].state], + framebufferStateName[m->avail[1].state], + framebufferStateName[m->avail[2].state]); + return 0; +} + +static int fb_compositionComplete(struct framebuffer_device_t* dev) +{ + // TODO: Properly implement composition complete callback + glFinish(); + + return 0; +} + +static int fb_lockBuffer(struct framebuffer_device_t* dev, int index) +{ + private_module_t* m = reinterpret_cast<private_module_t*>( + dev->common.module); + + // Return immediately if the buffer is available + if ((m->avail[index].state == AVL) || (m->swapInterval == 0)) + return 0; + + pthread_mutex_lock(&(m->avail[index].lock)); + while (m->avail[index].state != AVL) { + pthread_cond_wait(&(m->avail[index].cond), + &(m->avail[index].lock)); + } + pthread_mutex_unlock(&(m->avail[index].lock)); + + return 0; +} + +/*****************************************************************************/ + +int mapFrameBufferLocked(struct private_module_t* module) +{ + // already initialized... + if (module->framebuffer) { + return 0; + } + + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + + int fd = -1; + int i=0; + char name[64]; + char property[PROPERTY_VALUE_MAX]; + + while ((fd==-1) && device_template[i]) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + i++; + } + if (fd < 0) + return -errno; + + struct fb_fix_screeninfo finfo; + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + info.reserved[0] = 0; + info.reserved[1] = 0; + info.reserved[2] = 0; + info.xoffset = 0; + info.yoffset = 0; + info.activate = FB_ACTIVATE_NOW; + + /* Interpretation of offset for color fields: All offsets are from the right, + * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you + * can use the offset as right argument to <<). A pixel afterwards is a bit + * stream and is written to video memory as that unmodified. This implies + * big-endian byte order if bits_per_pixel is greater than 8. + */ + + if(info.bits_per_pixel == 32) { + /* + * Explicitly request RGBA_8888 + */ + info.bits_per_pixel = 32; + info.red.offset = 24; + info.red.length = 8; + info.green.offset = 16; + info.green.length = 8; + info.blue.offset = 8; + info.blue.length = 8; + info.transp.offset = 0; + info.transp.length = 8; + + /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do + * not use the MDP for composition (i.e. hw composition == 0), ask for + * RGBA instead of RGBX. */ + if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0) + module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; + else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0)) + module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; + else + module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888; + } else { + /* + * Explicitly request 5/6/5 + */ + info.bits_per_pixel = 16; + info.red.offset = 11; + info.red.length = 5; + info.green.offset = 5; + info.green.length = 6; + info.blue.offset = 0; + info.blue.length = 5; + info.transp.offset = 0; + info.transp.length = 0; + module->fbFormat = HAL_PIXEL_FORMAT_RGB_565; + } + /* + * Request NUM_BUFFERS screens (at lest 2 for page flipping) + */ + int numberOfBuffers = (int)(finfo.smem_len/(info.yres * info.xres * (info.bits_per_pixel/8))); + LOGV("num supported framebuffers in kernel = %d", numberOfBuffers); + + if (property_get("debug.gr.numframebuffers", property, NULL) > 0) { + int num = atoi(property); + if ((num >= NUM_FRAMEBUFFERS_MIN) && (num <= NUM_FRAMEBUFFERS_MAX)) { + numberOfBuffers = num; + } + } + + if (numberOfBuffers > NUM_FRAMEBUFFERS_MAX) + numberOfBuffers = NUM_FRAMEBUFFERS_MAX; + + LOGE("We support %d buffers", numberOfBuffers); + + info.yres_virtual = info.yres * numberOfBuffers; + + uint32_t flags = PAGE_FLIP; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); + } + + if (info.yres_virtual < info.yres * 2) { + // we need at least 2 for page-flipping + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", + info.yres_virtual, info.yres*2); + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + if (int(info.width) <= 0 || int(info.height) <= 0) { + // the driver doesn't return that information + // default to 160 dpi + info.width = ((info.xres * 25.4f)/160.0f + 0.5f); + info.height = ((info.yres * 25.4f)/160.0f + 0.5f); + } + + float xdpi = (info.xres * 25.4f) / info.width; + float ydpi = (info.yres * 25.4f) / info.height; + //The reserved[4] field is used to store FPS by the driver. + float fps = info.reserved[4]; + + LOGI( "using (fd=%d)\n" + "id = %s\n" + "xres = %d px\n" + "yres = %d px\n" + "xres_virtual = %d px\n" + "yres_virtual = %d px\n" + "bpp = %d\n" + "r = %2u:%u\n" + "g = %2u:%u\n" + "b = %2u:%u\n", + fd, + finfo.id, + info.xres, + info.yres, + info.xres_virtual, + info.yres_virtual, + info.bits_per_pixel, + info.red.offset, info.red.length, + info.green.offset, info.green.length, + info.blue.offset, info.blue.length + ); + + LOGI( "width = %d mm (%f dpi)\n" + "height = %d mm (%f dpi)\n" + "refresh rate = %.2f Hz\n", + info.width, xdpi, + info.height, ydpi, + fps + ); + + + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + if (finfo.smem_len <= 0) + return -errno; + + module->flags = flags; + module->info = info; + module->finfo = finfo; + module->xdpi = xdpi; + module->ydpi = ydpi; + module->fps = fps; + +#ifdef NO_SURFACEFLINGER_SWAPINTERVAL + char pval[PROPERTY_VALUE_MAX]; + property_get("debug.gr.swapinterval", pval, "1"); + module->swapInterval = atoi(pval); + if (module->swapInterval < private_module_t::PRIV_MIN_SWAP_INTERVAL || + module->swapInterval > private_module_t::PRIV_MAX_SWAP_INTERVAL) { + module->swapInterval = 1; + LOGW("Out of range (%d to %d) value for debug.gr.swapinterval, using 1", + private_module_t::PRIV_MIN_SWAP_INTERVAL, + private_module_t::PRIV_MAX_SWAP_INTERVAL); + } + +#else + /* when surfaceflinger supports swapInterval then can just do this */ + module->swapInterval = 1; +#endif + +#ifdef DEBUG_CALC_FPS + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.gr.calcfps", prop, "0"); + debug_fps_level = atoi(prop); + if (debug_fps_level > MAX_DEBUG_FPS_LEVEL) { + LOGW("out of range value for debug.gr.calcfps, using 0"); + debug_fps_level = 0; + } + + LOGE("DEBUG_CALC_FPS: %d", debug_fps_level); + populate_debug_fps_metadata(); +#endif + + module->currentIdx = -1; + pthread_cond_init(&(module->qpost), NULL); + pthread_mutex_init(&(module->qlock), NULL); + for (i = 0; i < NUM_FRAMEBUFFERS_MAX; i++) { + pthread_mutex_init(&(module->avail[i].lock), NULL); + pthread_cond_init(&(module->avail[i].cond), NULL); + module->avail[i].is_avail = true; + module->avail[i].state = AVL; + } + + /* create display update thread */ + pthread_t thread1; + if (pthread_create(&thread1, NULL, &disp_loop, (void *) module)) { + return -errno; + } + + /* + * map the framebuffer + */ + + int err; + size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual); + module->framebuffer = new private_handle_t(dup(fd), fbSize, + private_handle_t::PRIV_FLAGS_USES_PMEM, BUFFER_TYPE_UI, module->fbFormat, info.xres, info.yres); + + module->numBuffers = info.yres_virtual / info.yres; + module->bufferMask = 0; + + void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (vaddr == MAP_FAILED) { + LOGE("Error mapping the framebuffer (%s)", strerror(errno)); + return -errno; + } + module->framebuffer->base = intptr_t(vaddr); + memset(vaddr, 0, fbSize); + +#if defined(HDMI_DUAL_DISPLAY) + /* Overlay for HDMI*/ + pthread_mutex_init(&(module->overlayLock), NULL); + pthread_cond_init(&(module->overlayPost), NULL); + module->pobjOverlay = new Overlay(); + module->currentOffset = 0; + module->exitHDMIUILoop = false; + module->hdmiStateChanged = false; + pthread_t hdmiUIThread; + pthread_create(&hdmiUIThread, NULL, &hdmi_ui_loop, (void *) module); +#endif + + return 0; +} + +static int mapFrameBuffer(struct private_module_t* module) +{ + pthread_mutex_lock(&module->lock); + int err = mapFrameBufferLocked(module); + pthread_mutex_unlock(&module->lock); + return err; +} + +/*****************************************************************************/ + +static int fb_close(struct hw_device_t *dev) +{ + fb_context_t* ctx = (fb_context_t*)dev; +#if defined(HDMI_DUAL_DISPLAY) + private_module_t* m = reinterpret_cast<private_module_t*>( + ctx->device.common.module); + pthread_mutex_lock(&m->overlayLock); + m->exitHDMIUILoop = true; + pthread_cond_signal(&(m->overlayPost)); + pthread_mutex_unlock(&m->overlayLock); +#endif + if (ctx) { + free(ctx); + } + return 0; +} + +int fb_device_open(hw_module_t const* module, const char* name, + hw_device_t** device) +{ + int status = -EINVAL; + if (!strcmp(name, GRALLOC_HARDWARE_FB0)) { + alloc_device_t* gralloc_device; + status = gralloc_open(module, &gralloc_device); + if (status < 0) + return status; + + /* initialize our state here */ + fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev)); + memset(dev, 0, sizeof(*dev)); + + /* initialize the procs */ + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast<hw_module_t*>(module); + dev->device.common.close = fb_close; + dev->device.setSwapInterval = fb_setSwapInterval; + dev->device.post = fb_post; + dev->device.setUpdateRect = 0; + dev->device.compositionComplete = fb_compositionComplete; + dev->device.lockBuffer = fb_lockBuffer; +#if defined(HDMI_DUAL_DISPLAY) + dev->device.orientationChanged = fb_orientationChanged; + dev->device.videoOverlayStarted = fb_videoOverlayStarted; + dev->device.enableHDMIOutput = fb_enableHDMIOutput; + dev->device.setActionSafeWidthRatio = fb_setActionSafeWidthRatio; + dev->device.setActionSafeHeightRatio = fb_setActionSafeHeightRatio; +#endif + + private_module_t* m = (private_module_t*)module; + status = mapFrameBuffer(m); + if (status >= 0) { + int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3); + const_cast<uint32_t&>(dev->device.flags) = 0; + const_cast<uint32_t&>(dev->device.width) = m->info.xres; + const_cast<uint32_t&>(dev->device.height) = m->info.yres; + const_cast<int&>(dev->device.stride) = stride; + const_cast<int&>(dev->device.format) = m->fbFormat; + const_cast<float&>(dev->device.xdpi) = m->xdpi; + const_cast<float&>(dev->device.ydpi) = m->ydpi; + const_cast<float&>(dev->device.fps) = m->fps; + const_cast<int&>(dev->device.minSwapInterval) = private_module_t::PRIV_MIN_SWAP_INTERVAL; + const_cast<int&>(dev->device.maxSwapInterval) = private_module_t::PRIV_MAX_SWAP_INTERVAL; + const_cast<int&>(dev->device.numFramebuffers) = m->numBuffers; + + if (m->finfo.reserved[0] == 0x5444 && + m->finfo.reserved[1] == 0x5055) { + dev->device.setUpdateRect = fb_setUpdateRect; + LOGD("UPDATE_ON_DEMAND supported"); + } + + *device = &dev->device.common; + } + + // Close the gralloc module + gralloc_close(gralloc_device); + } + return status; +} + +/* Copy a pmem buffer to the framebuffer */ + +static void +msm_copy_buffer(buffer_handle_t handle, int fd, + int width, int height, int format, + int x, int y, int w, int h) +{ + struct { + unsigned int count; + mdp_blit_req req; + } blit; + private_handle_t *priv = (private_handle_t*) handle; + + memset(&blit, 0, sizeof(blit)); + blit.count = 1; + + blit.req.flags = 0; + blit.req.alpha = 0xff; + blit.req.transp_mask = 0xffffffff; + + blit.req.src.width = width; + blit.req.src.height = height; + blit.req.src.offset = 0; + blit.req.src.memory_id = priv->fd; + + blit.req.dst.width = width; + blit.req.dst.height = height; + blit.req.dst.offset = 0; + blit.req.dst.memory_id = fd; + blit.req.dst.format = format; + + blit.req.src_rect.x = blit.req.dst_rect.x = x; + blit.req.src_rect.y = blit.req.dst_rect.y = y; + blit.req.src_rect.w = blit.req.dst_rect.w = w; + blit.req.src_rect.h = blit.req.dst_rect.h = h; + + if (ioctl(fd, MSMFB_BLIT, &blit)) + LOGE("MSMFB_BLIT failed = %d", -errno); +} diff --git a/libgralloc-qsd8k/gpu.cpp b/libgralloc-qsd8k/gpu.cpp new file mode 100755 index 0000000..cabcda9 --- /dev/null +++ b/libgralloc-qsd8k/gpu.cpp @@ -0,0 +1,505 @@ +/* + * 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 <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/mman.h> + +#include <cutils/properties.h> +#ifdef HOST +#include <linux/ashmem.h> +#endif + +#include "gr.h" +#include "gpu.h" + +static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; +static const int QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03; +static const int QOMX_INTERLACE_FLAG = 0x49283654; +static const int QOMX_3D_VIDEO_FLAG = 0x23784238; + +gpu_context_t::gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module) : + deps(deps), + pmemAllocator(pmemAllocator), + pmemAdspAllocator(pmemAdspAllocator) +{ + // Zero out the alloc_device_t + memset(static_cast<alloc_device_t*>(this), 0, sizeof(alloc_device_t)); + + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.sf.hw", property, NULL) > 0) { + if(atoi(property) == 0) { + //debug.sf.hw = 0 + compositionType = CPU_COMPOSITION; + } else { //debug.sf.hw = 1 + // Get the composition type + property_get("debug.composition.type", property, NULL); + if (property == NULL) { + compositionType = GPU_COMPOSITION; + } else if ((strncmp(property, "mdp", 3)) == 0) { + compositionType = MDP_COMPOSITION; + } else if ((strncmp(property, "c2d", 3)) == 0) { + compositionType = C2D_COMPOSITION; + } else { + compositionType = GPU_COMPOSITION; + } + } + } else { //debug.sf.hw is not set. Use cpu composition + compositionType = CPU_COMPOSITION; + } + + // Initialize the procs + common.tag = HARDWARE_DEVICE_TAG; + common.version = 0; + common.module = const_cast<hw_module_t*>(&module->base.common); + common.close = gralloc_close; + alloc = gralloc_alloc; + allocSize = gralloc_alloc_size; + free = gralloc_free; +} + +int gpu_context_t::gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast<private_module_t*>(common.module); + + // we don't support allocations with both the FB and PMEM_ADSP flags + if (usage & GRALLOC_USAGE_PRIVATE_ADSP_HEAP) { + return -EINVAL; + } + + // allocate the framebuffer + if (m->framebuffer == NULL) { + // initialize the framebuffer, the framebuffer is mapped once + // and forever. + int err = deps.mapFrameBufferLocked(m); + if (err < 0) { + return err; + } + } + + const uint32_t bufferMask = m->bufferMask; + const uint32_t numBuffers = m->numBuffers; + const size_t bufferSize = m->finfo.line_length * m->info.yres; + if (numBuffers == 1) { + // If we have only one buffer, we never use page-flipping. Instead, + // we return a regular buffer which will be memcpy'ed to the main + // screen when post is called. + int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; + return gralloc_alloc_buffer(bufferSize, newUsage, pHandle, BUFFER_TYPE_UI, m->fbFormat, m->info.xres, m->info.yres); + } + + if (bufferMask >= ((1LU<<numBuffers)-1)) { + // We ran out of buffers. + return -ENOMEM; + } + + // create a "fake" handles for it + intptr_t vaddr = intptr_t(m->framebuffer->base); + private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), bufferSize, + private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_FRAMEBUFFER, + BUFFER_TYPE_UI, m->fbFormat, m->info.xres, m->info.yres); + + // find a free slot + for (uint32_t i=0 ; i<numBuffers ; i++) { + if ((bufferMask & (1LU<<i)) == 0) { + m->bufferMask |= (1LU<<i); + break; + } + vaddr += bufferSize; + } + + hnd->base = vaddr; + hnd->offset = vaddr - intptr_t(m->framebuffer->base); + *pHandle = hnd; + + return 0; +} + + +int gpu_context_t::gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast<private_module_t*>(common.module); + pthread_mutex_lock(&m->lock); + int err = gralloc_alloc_framebuffer_locked(size, usage, pHandle); + pthread_mutex_unlock(&m->lock); + return err; +} + +int gpu_context_t::alloc_ashmem_buffer(size_t size, unsigned int postfix, void** pBase, + int* pOffset, int* pFd) +{ + int err = 0; + int fd = -1; + void* base = 0; + int offset = 0; + + char name[ASHMEM_NAME_LEN]; + snprintf(name, ASHMEM_NAME_LEN, "gralloc-buffer-%x", postfix); + int prot = PROT_READ | PROT_WRITE; + fd = ashmem_create_region(name, size); + if (fd < 0) { + LOGE("couldn't create ashmem (%s)", strerror(errno)); + err = -errno; + } else { + if (ashmem_set_prot_region(fd, prot) < 0) { + LOGE("ashmem_set_prot_region(fd=%d, prot=%x) failed (%s)", + fd, prot, strerror(errno)); + close(fd); + err = -errno; + } else { + base = mmap(0, size, prot, MAP_SHARED|MAP_POPULATE|MAP_LOCKED, fd, 0); + if (base == MAP_FAILED) { + LOGE("alloc mmap(fd=%d, size=%d, prot=%x) failed (%s)", + fd, size, prot, strerror(errno)); + close(fd); + err = -errno; + } else { + memset((char*)base + offset, 0, size); + } + } + } + if(err == 0) { + *pFd = fd; + *pBase = base; + *pOffset = offset; +#ifdef HOST + if (ioctl(fd, ASHMEM_CACHE_INV_RANGE, NULL)) { + LOGE("ASHMEM_CACHE_INV_RANGE failed fd = %d", fd); + } +#endif + } + return err; +} + +int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle, + int bufferType, int format, int width, int height) +{ + int err = 0; + int flags = 0; + + int fd = -1; + void* base = 0; // XXX JMG: This should change to just get an address from + // the PmemAllocator rather than getting the base & offset separately + int offset = 0; + int lockState = 0; + + size = roundUpToPageSize(size); +#ifndef USE_ASHMEM + if (usage & GRALLOC_USAGE_HW_TEXTURE) { + // enable pmem in that case, so our software GL can fallback to + // the copybit module. + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_HW_2D) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } +#else + // Enable use of PMEM only when MDP composition is used (and other conditions apply). + // Else fall back on using ASHMEM + if ((get_composition_type() == MDP_COMPOSITION) && + ((usage & GRALLOC_USAGE_HW_TEXTURE) || (usage & GRALLOC_USAGE_HW_2D)) ) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_PRIVATE_EBI_HEAP) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } +#endif + if ((usage & GRALLOC_USAGE_PRIVATE_ADSP_HEAP) || (usage & GRALLOC_USAGE_PRIVATE_SMI_HEAP) + || (usage & GRALLOC_USAGE_EXTERNAL_DISP) || (usage & GRALLOC_USAGE_PROTECTED)) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + private_module_t* m = reinterpret_cast<private_module_t*>(common.module); + if((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) == 0 && + (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) == 0) { + flags |= private_handle_t::PRIV_FLAGS_USES_ASHMEM; + err = alloc_ashmem_buffer(size, (unsigned int)pHandle, &base, &offset, &fd); + if(err >= 0) + lockState |= private_handle_t::LOCK_STATE_MAPPED; + } + else if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0 || + (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + + PmemAllocator* pma = 0; + + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0) { + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + LOGE("attempting to allocate a gralloc buffer with both the " + "USES_PMEM and USES_PMEM_ADSP flags. Unsetting the " + "USES_PMEM_ADSP flag."); + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + } + pma = &pmemAllocator; + } else { // (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0 + pma = &pmemAdspAllocator; + } + + // PMEM buffers are always mmapped + lockState |= private_handle_t::LOCK_STATE_MAPPED; + + err = pma->alloc_pmem_buffer(size, usage, &base, &offset, &fd, format); + if (err < 0) { + // Pmem allocation failed. Try falling back to ashmem iff we are: + // a. not using MDP composition + // b. not allocating memory for a buffer to be used by overlays + // c. The client has not explicitly requested a PMEM buffer + if ((get_composition_type() != MDP_COMPOSITION) && + (bufferType != BUFFER_TYPE_VIDEO) && + ((usage & GRALLOC_USAGE_PRIVATE_EBI_HEAP) == 0) && + ((usage & GRALLOC_USAGE_PRIVATE_ADSP_HEAP) == 0)) { + // the caller didn't request PMEM, so we can try something else + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + err = 0; + LOGE("Pmem allocation failed. Trying ashmem"); + goto try_ashmem; + } else { + LOGE("couldn't open pmem (%s)", strerror(errno)); + } + } + } else { +try_ashmem: + err = alloc_ashmem_buffer(size, (unsigned int)pHandle, &base, &offset, &fd); + if (err >= 0) { + lockState |= private_handle_t::LOCK_STATE_MAPPED; + flags |= private_handle_t::PRIV_FLAGS_USES_ASHMEM; + } else { + LOGE("Ashmem fallback failed"); + } + } + + if (err == 0) { + private_handle_t* hnd = new private_handle_t(fd, size, flags, bufferType, format, width, height); + hnd->offset = offset; + hnd->base = int(base)+offset; + hnd->lockState = lockState; + *pHandle = hnd; + } + + LOGE_IF(err, "gralloc failed err=%s", strerror(-err)); + + return err; +} + +static inline size_t ALIGN(size_t x, size_t align) { + return (x + align-1) & ~(align-1); +} + +void gpu_context_t::getGrallocInformationFromFormat(int inputFormat, int *colorFormat, int *bufferType) +{ + *bufferType = BUFFER_TYPE_VIDEO; + *colorFormat = inputFormat; + + if (inputFormat == HAL_PIXEL_FORMAT_YV12) { + *bufferType = BUFFER_TYPE_VIDEO; + } else if (inputFormat & S3D_FORMAT_MASK) { + // S3D format + *colorFormat = COLOR_FORMAT(inputFormat); + } else if (inputFormat & INTERLACE_MASK) { + // Interlaced + *colorFormat = inputFormat ^ HAL_PIXEL_FORMAT_INTERLACE; + } else if (inputFormat < 0x7) { + // RGB formats + *colorFormat = inputFormat; + *bufferType = BUFFER_TYPE_UI; + } else if ((inputFormat == HAL_PIXEL_FORMAT_R_8) || + (inputFormat == HAL_PIXEL_FORMAT_RG_88)) { + *colorFormat = inputFormat; + *bufferType = BUFFER_TYPE_UI; + } +} + +int gpu_context_t::alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride, int bufferSize) { + if (!pHandle || !pStride) + return -EINVAL; + + size_t size, alignedw, alignedh; + + alignedw = ALIGN(w, 32); + alignedh = ALIGN(h, 32); + int colorFormat, bufferType; + getGrallocInformationFromFormat(format, &colorFormat, &bufferType); + + switch (colorFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + size = alignedw * alignedh * 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + size = alignedw * alignedh * 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + size = alignedw * alignedh * 2; + break; + + // adreno formats + case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO: // NV21 + size = ALIGN(alignedw*alignedh, 4096); + size += ALIGN(2 * ALIGN(w/2, 32) * ALIGN(h/2, 32), 4096); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: // NV12 + // The chroma plane is subsampled, + // but the pitch in bytes is unchanged + // The GPU needs 4K alignment, but the video decoder needs 8K + alignedw = ALIGN(w, 128); + size = ALIGN( alignedw * alignedh, 8192); + size += ALIGN( alignedw * ALIGN(h/2, 32), 8192); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YV12: + if ((w&1) || (h&1)) { + LOGE("w or h is odd for the YUV format"); + return -EINVAL; + } + alignedw = ALIGN(w, 16); + alignedh = h; + size = alignedw*alignedh + + (ALIGN(alignedw/2, 16) * (alignedh/2))*2; + size = ALIGN(size, 4096); + break; + + default: + LOGE("unrecognized pixel format: %d", format); + return -EINVAL; + } + + if ((ssize_t)size <= 0) + return -EINVAL; + + size = (bufferSize >= size)? bufferSize : size; + + // All buffers marked as protected or for external + // display need to go to overlay + if ((usage & GRALLOC_USAGE_EXTERNAL_DISP) || + (usage & GRALLOC_USAGE_PROTECTED)) { + bufferType = BUFFER_TYPE_VIDEO; + } + int err; + if (usage & GRALLOC_USAGE_HW_FB) { + err = gralloc_alloc_framebuffer(size, usage, pHandle); + } else { + err = gralloc_alloc_buffer(size, usage, pHandle, bufferType, format, alignedw, alignedh); + } + + if (err < 0) { + return err; + } + + *pStride = alignedw; + return 0; +} + +int gpu_context_t::free_impl(private_handle_t const* hnd) { + private_module_t* m = reinterpret_cast<private_module_t*>(common.module); + if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { + // free this buffer + const size_t bufferSize = m->finfo.line_length * m->info.yres; + int index = (hnd->base - m->framebuffer->base) / bufferSize; + m->bufferMask &= ~(1<<index); + } else { + PmemAllocator* pmem_allocator = 0; + if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM) { + pmem_allocator = &pmemAllocator; + } else if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) { + pmem_allocator = &pmemAdspAllocator; + } else if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_ASHMEM) { + // free ashmem + if (hnd->fd >= 0) { + if (hnd->base) { + int err = munmap((void*)hnd->base, hnd->size); + LOGE_IF(err<0, "ASHMEM_UNMAP failed (%s), " + "fd=%d, sub.offset=%d, sub.size=%d", + strerror(errno), hnd->fd, hnd->offset, hnd->size); + } + } + } + if (pmem_allocator) { + pmem_allocator->free_pmem_buffer(hnd->size, (void*)hnd->base, + hnd->offset, hnd->fd); + } + + deps.terminateBuffer(&m->base, const_cast<private_handle_t*>(hnd)); + } + + deps.close(hnd->fd); + delete hnd; // XXX JMG: move this to the deps + return 0; +} + +/****************************************************************************** + * Static functions + *****************************************************************************/ + +int gpu_context_t::gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride) +{ + if (!dev) { + return -EINVAL; + } + gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev); + return gpu->alloc_impl(w, h, format, usage, pHandle, pStride, 0); +} + +int gpu_context_t::gralloc_alloc_size(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride, int bufferSize) +{ + if (!dev) { + return -EINVAL; + } + gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev); + return gpu->alloc_impl(w, h, format, usage, pHandle, pStride, bufferSize); +} + +int gpu_context_t::gralloc_free(alloc_device_t* dev, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(handle); + gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev); + return gpu->free_impl(hnd); +} + +/*****************************************************************************/ + +int gpu_context_t::gralloc_close(struct hw_device_t *dev) +{ + gpu_context_t* ctx = reinterpret_cast<gpu_context_t*>(dev); + if (ctx) { + /* TODO: keep a list of all buffer_handle_t created, and free them + * all here. + */ + delete ctx; + } + return 0; +} + + +gpu_context_t::Deps::~Deps() {} diff --git a/libgralloc-qsd8k/gpu.h b/libgralloc-qsd8k/gpu.h new file mode 100755 index 0000000..3ef57d3 --- /dev/null +++ b/libgralloc-qsd8k/gpu.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef GRALLOC_QSD8K_GPU_H_ +#define GRALLOC_QSD8K_GPU_H_ + +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include <cutils/log.h> +#include <cutils/ashmem.h> + +#include "gralloc_priv.h" +#include "pmemalloc.h" + + +class gpu_context_t : public alloc_device_t { + public: + + class Deps { + public: + + virtual ~Deps(); + + // ashmem + virtual int ashmem_create_region(const char *name, size_t size) = 0; + + // POSIX + virtual int close(int fd) = 0; + + // Framebuffer (locally defined) + virtual int mapFrameBufferLocked(struct private_module_t* module) = 0; + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) = 0; + }; + + gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module); + + int gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle, int bufferType, int format, + int width, int height); + int free_impl(private_handle_t const* hnd); + int alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride, int bufferSize = 0); + + static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride); + static int gralloc_free(alloc_device_t* dev, buffer_handle_t handle); + static int gralloc_alloc_size(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride, int bufferSize); + static int gralloc_close(struct hw_device_t *dev); + int get_composition_type() const { return compositionType; } + + private: + + Deps& deps; + PmemAllocator& pmemAllocator; + PmemAllocator& pmemAdspAllocator; + int compositionType; + int alloc_ashmem_buffer(size_t size, unsigned int postfix, void** pBase, + int* pOffset, int* pFd); + void getGrallocInformationFromFormat(int inputFormat, int *colorFormat, int *bufferType); +}; + +#endif // GRALLOC_QSD8K_GPU_H diff --git a/libgralloc-qsd8k/gr.h b/libgralloc-qsd8k/gr.h new file mode 100644 index 0000000..49a0513 --- /dev/null +++ b/libgralloc-qsd8k/gr.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef GR_H_ +#define GR_H_ + +#include <stdint.h> +#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define +# include <asm/page.h> +#else +# include <sys/user.h> +#endif +#include <limits.h> +#include <sys/cdefs.h> +#include <hardware/gralloc.h> +#include <pthread.h> +#include <errno.h> + +#include <cutils/native_handle.h> + +/*****************************************************************************/ + +struct private_module_t; +struct private_handle_t; + +inline size_t roundUpToPageSize(size_t x) { + return (x + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); +} + +#define FALSE 0 +#define TRUE 1 + +int mapFrameBufferLocked(struct private_module_t* module); +int terminateBuffer(gralloc_module_t const* module, private_handle_t* hnd); +size_t calculateBufferSize(int width, int height, int format); +int decideBufferHandlingMechanism(int format, const char *compositionUsed, + int hasBlitEngine, int *needConversion, + int *useBufferDirectly); +/*****************************************************************************/ + +class Locker { + pthread_mutex_t mutex; +public: + class Autolock { + Locker& locker; + public: + inline Autolock(Locker& locker) : locker(locker) { locker.lock(); } + inline ~Autolock() { locker.unlock(); } + }; + inline Locker() { pthread_mutex_init(&mutex, 0); } + inline ~Locker() { pthread_mutex_destroy(&mutex); } + inline void lock() { pthread_mutex_lock(&mutex); } + inline void unlock() { pthread_mutex_unlock(&mutex); } +}; + +#endif /* GR_H_ */ diff --git a/libgralloc-qsd8k/gralloc.cpp b/libgralloc-qsd8k/gralloc.cpp new file mode 100755 index 0000000..bfade0a --- /dev/null +++ b/libgralloc-qsd8k/gralloc.cpp @@ -0,0 +1,231 @@ +/* + * 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. + */ + +#include <unistd.h> +#include <fcntl.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <linux/android_pmem.h> + +#include "allocator.h" +#include "gr.h" +#include "gpu.h" + +/*****************************************************************************/ + +static int gralloc_alloc_buffer(alloc_device_t* dev, + size_t size, int usage, buffer_handle_t* pHandle); + +/*****************************************************************************/ + +int fb_device_open(const hw_module_t* module, const char* name, + hw_device_t** device); + +static int gralloc_device_open(const hw_module_t* module, const char* name, + hw_device_t** device); + +extern int gralloc_lock(gralloc_module_t const* module, + buffer_handle_t handle, int usage, + int l, int t, int w, int h, + void** vaddr); + +extern int gralloc_unlock(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_register_buffer(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_unregister_buffer(gralloc_module_t const* module, + buffer_handle_t handle); + +extern int gralloc_perform(struct gralloc_module_t const* module, + int operation, ... ); + +/*****************************************************************************/ + +/* On-device dependency implementation */ +class PmemAllocatorDepsDeviceImpl : public PmemUserspaceAllocator::Deps, + public PmemKernelAllocator::Deps { + + const private_module_t* module; + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + int err = 0; +#ifndef TARGET_MSM7x27 + pmem_region region; + err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®ion); + if (err == 0) { + *size = region.len; + } +#else +#ifdef USE_ASHMEM + if(module != NULL) + *size = module->info.xres * module->info.yres * 2 * 2; + else + return -ENOMEM; +#else + *size = 23<<20; //23MB for 7x27 +#endif +#endif + return err; + } + + virtual int connectPmem(int fd, int master_fd) { + return ioctl(fd, PMEM_CONNECT, master_fd); + } + + virtual int mapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_MAP, &sub); + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_UNMAP, &sub); + } + + virtual int alignPmem(int fd, size_t size, int align) { + struct pmem_allocation allocation; + allocation.size = size; + allocation.align = align; + return ioctl(fd, PMEM_ALLOCATE_ALIGNED, &allocation); + } + + virtual int cleanPmem(int fd, unsigned long base, int offset, size_t size) { + struct pmem_addr pmem_addr; + pmem_addr.vaddr = base; + pmem_addr.offset = offset; + pmem_addr.length = size; + return ioctl(fd, PMEM_CLEAN_INV_CACHES, &pmem_addr); + } + + virtual int getErrno() { + return errno; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return ::mmap(start, length, prot, flags, fd, offset); + } + + virtual int munmap(void* start, size_t length) { + return ::munmap(start, length); + } + + virtual int open(const char* pathname, int flags, int mode) { + return ::open(pathname, flags, mode); + } + + virtual int close(int fd) { + return ::close(fd); + } + +public: + void setModule(const private_module_t* m) { + module = m; + } + +}; + +class GpuContextDepsDeviceImpl : public gpu_context_t::Deps { + + public: + + virtual int ashmem_create_region(const char *name, size_t size) { + return ::ashmem_create_region(name, size); + } + + virtual int mapFrameBufferLocked(struct private_module_t* module) { + return ::mapFrameBufferLocked(module); + } + + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) { + return ::terminateBuffer(module, hnd); + } + + virtual int close(int fd) { + return ::close(fd); + } +}; + +static PmemAllocatorDepsDeviceImpl pmemAllocatorDeviceDepsImpl; +static GpuContextDepsDeviceImpl gpuContextDeviceDepsImpl; + +/*****************************************************************************/ + +static SimpleBestFitAllocator pmemAllocMgr; +static PmemUserspaceAllocator pmemAllocator(pmemAllocatorDeviceDepsImpl, pmemAllocMgr, + "/dev/pmem"); + +static PmemKernelAllocator pmemAdspAllocator(pmemAllocatorDeviceDepsImpl); + +/*****************************************************************************/ + +static struct hw_module_methods_t gralloc_module_methods = { + open: gralloc_device_open +}; + +struct private_module_t HAL_MODULE_INFO_SYM = { + base: { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: GRALLOC_HARDWARE_MODULE_ID, + name: "Graphics Memory Allocator Module", + author: "The Android Open Source Project", + methods: &gralloc_module_methods + }, + registerBuffer: gralloc_register_buffer, + unregisterBuffer: gralloc_unregister_buffer, + lock: gralloc_lock, + unlock: gralloc_unlock, + perform: gralloc_perform, + }, + framebuffer: 0, + fbFormat: 0, + flags: 0, + numBuffers: 0, + bufferMask: 0, + lock: PTHREAD_MUTEX_INITIALIZER, + currentBuffer: 0, +}; + +/*****************************************************************************/ + +int gralloc_device_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + int status = -EINVAL; + if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { + const private_module_t* m = reinterpret_cast<const private_module_t*>( + module); + pmemAllocatorDeviceDepsImpl.setModule(m); + gpu_context_t *dev; + dev = new gpu_context_t(gpuContextDeviceDepsImpl, pmemAllocator, + pmemAdspAllocator, m); + *device = &dev->common; + status = 0; + } else { + status = fb_device_open(module, name, device); + } + return status; +} diff --git a/libgralloc-qsd8k/gralloc_priv.h b/libgralloc-qsd8k/gralloc_priv.h new file mode 100755 index 0000000..8037812 --- /dev/null +++ b/libgralloc-qsd8k/gralloc_priv.h @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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 GRALLOC_PRIV_H_ +#define GRALLOC_PRIV_H_ + +#include <stdint.h> +#include <limits.h> +#include <sys/cdefs.h> +#include <hardware/gralloc.h> +#include <pthread.h> +#include <errno.h> +#include <unistd.h> + +#include <cutils/native_handle.h> + +#include <linux/fb.h> + +#if defined(__cplusplus) && defined(HDMI_DUAL_DISPLAY) +#include "overlayLib.h" +using namespace overlay; +#endif + +enum { + /* gralloc usage bits indicating the type + * of allocation that should be used */ + GRALLOC_USAGE_PRIVATE_ADSP_HEAP = GRALLOC_USAGE_PRIVATE_0, + GRALLOC_USAGE_PRIVATE_EBI_HEAP = GRALLOC_USAGE_PRIVATE_1, + GRALLOC_USAGE_PRIVATE_SMI_HEAP = GRALLOC_USAGE_PRIVATE_2, + GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP = GRALLOC_USAGE_PRIVATE_3, + /* Set this for allocating uncached memory (using O_DSYNC) + * cannot be used with the system heap */ + GRALLOC_USAGE_PRIVATE_UNCACHED = 0x00010000, +}; + +enum { + GPU_COMPOSITION, + C2D_COMPOSITION, + MDP_COMPOSITION, + CPU_COMPOSITION, +}; + +/* numbers of max buffers for page flipping */ +#define NUM_FRAMEBUFFERS_MIN 2 +#define NUM_FRAMEBUFFERS_MAX 3 + +/* number of default bufers for page flipping */ +#define NUM_DEF_FRAME_BUFFERS 2 +#define NO_SURFACEFLINGER_SWAPINTERVAL +#define INTERLACE_MASK 0x80 +#define S3D_FORMAT_MASK 0xFF000 +#define COLOR_FORMAT(x) (x & 0xFFF) // Max range for colorFormats is 0 - FFF +#define DEVICE_PMEM_ADSP "/dev/pmem_adsp" +#define DEVICE_PMEM_SMIPOOL "/dev/pmem_smipool" +/*****************************************************************************/ +#ifdef __cplusplus +template <class T> +struct Node +{ + T data; + Node<T> *next; +}; + +template <class T> +class Queue +{ +public: + Queue(): front(NULL), back(NULL), len(0) {dummy = new T;} + ~Queue() + { + clear(); + delete dummy; + } + void push(const T& item) //add an item to the back of the queue + { + if(len != 0) { //if the queue is not empty + back->next = new Node<T>; //create a new node + back = back->next; //set the new node as the back node + back->data = item; + back->next = NULL; + } else { + back = new Node<T>; + back->data = item; + back->next = NULL; + front = back; + } + len++; + } + void pop() //remove the first item from the queue + { + if (isEmpty()) + return; //if the queue is empty, no node to dequeue + T item = front->data; + Node<T> *tmp = front; + front = front->next; + delete tmp; + if(front == NULL) //if the queue is empty, update the back pointer + back = NULL; + len--; + return; + } + T& getHeadValue() const //return the value of the first item in the queue + { //without modification to the structure + if (isEmpty()) { + LOGE("Error can't get head of empty queue"); + return *dummy; + } + return front->data; + } + + bool isEmpty() const //returns true if no elements are in the queue + { + return (front == NULL); + } + + size_t size() const //returns the amount of elements in the queue + { + return len; + } + +private: + Node<T> *front; + Node<T> *back; + size_t len; + void clear() + { + while (!isEmpty()) + pop(); + } + T *dummy; +}; +#endif + +enum { + /* OEM specific HAL formats */ + //HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x100, // defined in hardware.h + //HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x101, // defined in hardware.h + HAL_PIXEL_FORMAT_YCbCr_422_P = 0x102, + HAL_PIXEL_FORMAT_YCbCr_420_P = 0x103, + //HAL_PIXEL_FORMAT_YCbCr_422_I = 0x104, // defined in hardware.h + HAL_PIXEL_FORMAT_YCbCr_420_I = 0x105, + HAL_PIXEL_FORMAT_CbYCrY_422_I = 0x106, + HAL_PIXEL_FORMAT_CbYCrY_420_I = 0x107, + HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED = 0x108, + HAL_PIXEL_FORMAT_YCbCr_420_SP = 0x109, + HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO = 0x10A, + HAL_PIXEL_FORMAT_YCrCb_422_SP = 0x10B, + HAL_PIXEL_FORMAT_R_8 = 0x10D, + HAL_PIXEL_FORMAT_RG_88 = 0x10E, + HAL_PIXEL_FORMAT_INTERLACE = 0x180, + +}; + +/* possible formats for 3D content*/ +enum { + HAL_NO_3D = 0x0000, + HAL_3D_IN_SIDE_BY_SIDE_L_R = 0x10000, + HAL_3D_IN_TOP_BOTTOM = 0x20000, + HAL_3D_IN_INTERLEAVE = 0x40000, + HAL_3D_IN_SIDE_BY_SIDE_R_L = 0x80000, + HAL_3D_OUT_SIDE_BY_SIDE = 0x1000, + HAL_3D_OUT_TOP_BOTTOM = 0x2000, + HAL_3D_OUT_INTERLEAVE = 0x4000, + HAL_3D_OUT_MONOSCOPIC = 0x8000 +}; + +enum { + BUFFER_TYPE_UI = 0, + BUFFER_TYPE_VIDEO +}; +/*****************************************************************************/ + +struct private_module_t; +struct private_handle_t; +struct PmemAllocator; + +struct qbuf_t { + buffer_handle_t buf; + int idx; +}; + +enum buf_state { + SUB, + REF, + AVL +}; + +struct avail_t { + pthread_mutex_t lock; + pthread_cond_t cond; +#ifdef __cplusplus + bool is_avail; + buf_state state; +#endif +}; + +struct private_module_t { + gralloc_module_t base; + + struct private_handle_t* framebuffer; + uint32_t fbFormat; + uint32_t flags; + uint32_t numBuffers; + uint32_t bufferMask; + pthread_mutex_t lock; + buffer_handle_t currentBuffer; + + struct fb_var_screeninfo info; + struct fb_fix_screeninfo finfo; + float xdpi; + float ydpi; + float fps; + int swapInterval; +#ifdef __cplusplus + Queue<struct qbuf_t> disp; // non-empty when buffer is ready for display +#endif + int currentIdx; + struct avail_t avail[NUM_FRAMEBUFFERS_MAX]; + pthread_mutex_t qlock; + pthread_cond_t qpost; + + enum { + // flag to indicate we'll post this buffer + PRIV_USAGE_LOCKED_FOR_POST = 0x80000000, + PRIV_MIN_SWAP_INTERVAL = 0, + PRIV_MAX_SWAP_INTERVAL = 1, + }; +#if defined(__cplusplus) && defined(HDMI_DUAL_DISPLAY) + Overlay* pobjOverlay; + int orientation; + bool videoOverlay; + uint32_t currentOffset; + bool enableHDMIOutput; + bool exitHDMIUILoop; + float actionsafeWidthRatio; + float actionsafeHeightRatio; + bool hdmiStateChanged; + pthread_mutex_t overlayLock; + pthread_cond_t overlayPost; +#endif +}; + +/*****************************************************************************/ + +#ifdef __cplusplus +struct private_handle_t : public native_handle { +#else +struct private_handle_t { + native_handle_t nativeHandle; +#endif + + enum { + PRIV_FLAGS_FRAMEBUFFER = 0x00000001, + PRIV_FLAGS_USES_PMEM = 0x00000002, + PRIV_FLAGS_USES_PMEM_ADSP = 0x00000004, + PRIV_FLAGS_USES_ION = 0x00000008, + PRIV_FLAGS_USES_ASHMEM = 0x00000010, + PRIV_FLAGS_NEEDS_FLUSH = 0x00000020, + PRIV_FLAGS_DO_NOT_FLUSH = 0x00000040, + }; + + enum { + LOCK_STATE_WRITE = 1<<31, + LOCK_STATE_MAPPED = 1<<30, + LOCK_STATE_READ_MASK = 0x3FFFFFFF + }; + + // file-descriptors + int fd; + // ints + int magic; + int flags; + int size; + int offset; + int bufferType; + + // FIXME: the attributes below should be out-of-line + int base; + int lockState; + int writeOwner; + int gpuaddr; // The gpu address mapped into the mmu. If using ashmem, set to 0 They don't care + int pid; + int format; + int width; + int height; + +#ifdef __cplusplus + static const int sNumInts = 13; + static const int sNumFds = 1; + static const int sMagic = 'gmsm'; + + private_handle_t(int fd, int size, int flags, int bufferType, int format, int width, int height) : + fd(fd), magic(sMagic), flags(flags), size(size), offset(0), bufferType(bufferType), + base(0), lockState(0), writeOwner(0), gpuaddr(0), pid(getpid()), format(format), width(width), + height(height) + { + version = sizeof(native_handle); + numInts = sNumInts; + numFds = sNumFds; + } + ~private_handle_t() { + magic = 0; + } + + bool usesPhysicallyContiguousMemory() { + return (flags & PRIV_FLAGS_USES_PMEM) != 0; + } + + static int validate(const native_handle* h) { + const private_handle_t* hnd = (const private_handle_t*)h; + if (!h || h->version != sizeof(native_handle) || + h->numInts != sNumInts || h->numFds != sNumFds || + hnd->magic != sMagic) + { + LOGE("invalid gralloc handle (at %p)", h); + return -EINVAL; + } + return 0; + } + + static private_handle_t* dynamicCast(const native_handle* in) { + if (validate(in) == 0) { + return (private_handle_t*) in; + } + return NULL; + } +#endif +}; + +#endif /* GRALLOC_PRIV_H_ */ diff --git a/libgralloc-qsd8k/mapper.cpp b/libgralloc-qsd8k/mapper.cpp new file mode 100755 index 0000000..0e495be --- /dev/null +++ b/libgralloc-qsd8k/mapper.cpp @@ -0,0 +1,386 @@ +/* + * 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. + */ + +#include <limits.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <string.h> +#include <stdarg.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <linux/ashmem.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <cutils/ashmem.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +#include <linux/android_pmem.h> + +#include "gralloc_priv.h" +#include "gr.h" + +// we need this for now because pmem cannot mmap at an offset +#define PMEM_HACK 1 + +/* desktop Linux needs a little help with gettid() */ +#if defined(ARCH_X86) && !defined(HAVE_ANDROID_OS) +#define __KERNEL__ +# include <linux/unistd.h> +pid_t gettid() { return syscall(__NR_gettid);} +#undef __KERNEL__ +#endif + +/*****************************************************************************/ + +static int gralloc_map(gralloc_module_t const* module, + buffer_handle_t handle, + void** vaddr) +{ + private_handle_t* hnd = (private_handle_t*)handle; + void *mappedAddress; + if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + size_t size = hnd->size; +#if PMEM_HACK + size += hnd->offset; +#endif + if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_ASHMEM) { + mappedAddress = mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED | MAP_POPULATE, hnd->fd, 0); + } else { + mappedAddress = mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0); + } + if (mappedAddress == MAP_FAILED) { + LOGE("Could not mmap handle %p, fd=%d (%s)", + handle, hnd->fd, strerror(errno)); + hnd->base = 0; + return -errno; + } + hnd->base = intptr_t(mappedAddress) + hnd->offset; + //LOGD("gralloc_map() succeeded fd=%d, off=%d, size=%d, vaddr=%p", + // hnd->fd, hnd->offset, hnd->size, mappedAddress); + } + *vaddr = (void*)hnd->base; + return 0; +} + +static int gralloc_unmap(gralloc_module_t const* module, + buffer_handle_t handle) +{ + private_handle_t* hnd = (private_handle_t*)handle; + if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + void* base = (void*)hnd->base; + size_t size = hnd->size; +#if PMEM_HACK + base = (void*)(intptr_t(base) - hnd->offset); + size += hnd->offset; +#endif + //LOGD("unmapping from %p, size=%d", base, size); + if (munmap(base, size) < 0) { + LOGE("Could not unmap %s", strerror(errno)); + } + } + hnd->base = 0; + return 0; +} + +/*****************************************************************************/ + +static pthread_mutex_t sMapLock = PTHREAD_MUTEX_INITIALIZER; + +/*****************************************************************************/ + +int gralloc_register_buffer(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + // In this implementation, we don't need to do anything here + + /* NOTE: we need to initialize the buffer as not mapped/not locked + * because it shouldn't when this function is called the first time + * in a new process. Ideally these flags shouldn't be part of the + * handle, but instead maintained in the kernel or at least + * out-of-line + */ + + // if this handle was created in this process, then we keep it as is. + private_handle_t* hnd = (private_handle_t*)handle; + if (hnd->pid != getpid()) { + hnd->base = 0; + hnd->lockState = 0; + hnd->writeOwner = 0; + } + return 0; +} + +int gralloc_unregister_buffer(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + /* + * If the buffer has been mapped during a lock operation, it's time + * to un-map it. It's an error to be here with a locked buffer. + * NOTE: the framebuffer is handled differently and is never unmapped. + */ + + private_handle_t* hnd = (private_handle_t*)handle; + + LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, + "[unregister] handle %p still locked (state=%08x)", + hnd, hnd->lockState); + + // never unmap buffers that were created in this process + if (hnd->pid != getpid()) { + if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + gralloc_unmap(module, handle); + } + hnd->base = 0; + hnd->lockState = 0; + hnd->writeOwner = 0; + } + return 0; +} + +int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) +{ + /* + * If the buffer has been mapped during a lock operation, it's time + * to un-map it. It's an error to be here with a locked buffer. + */ + + LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, + "[terminate] handle %p still locked (state=%08x)", + hnd, hnd->lockState); + + if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + // this buffer was mapped, unmap it now + if (hnd->flags & (private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP | + private_handle_t::PRIV_FLAGS_USES_ASHMEM)) { + if (hnd->pid != getpid()) { + // ... unless it's a "master" pmem buffer, that is a buffer + // mapped in the process it's been allocated. + // (see gralloc_alloc_buffer()) + gralloc_unmap(module, hnd); + } + } else { + LOGE("terminateBuffer: unmapping a non pmem/ashmem buffer flags = 0x%x", hnd->flags); + gralloc_unmap(module, hnd); + } + } + + return 0; +} + +int gralloc_lock(gralloc_module_t const* module, + buffer_handle_t handle, int usage, + int l, int t, int w, int h, + void** vaddr) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + int err = 0; + private_handle_t* hnd = (private_handle_t*)handle; + int32_t current_value, new_value; + int retry; + + do { + current_value = hnd->lockState; + new_value = current_value; + + if (current_value & private_handle_t::LOCK_STATE_WRITE) { + // already locked for write + LOGE("handle %p already locked for write", handle); + return -EBUSY; + } else if (current_value & private_handle_t::LOCK_STATE_READ_MASK) { + // already locked for read + if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { + LOGE("handle %p already locked for read", handle); + return -EBUSY; + } else { + // this is not an error + //LOGD("%p already locked for read... count = %d", + // handle, (current_value & ~(1<<31))); + } + } + + // not currently locked + if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { + // locking for write + new_value |= private_handle_t::LOCK_STATE_WRITE; + } + new_value++; + + retry = android_atomic_cmpxchg(current_value, new_value, + (volatile int32_t*)&hnd->lockState); + } while (retry); + + if (new_value & private_handle_t::LOCK_STATE_WRITE) { + // locking for write, store the tid + hnd->writeOwner = gettid(); + } + + // if requesting sw write for non-framebuffer handles, flag for + // flushing at unlock + + if ((usage & GRALLOC_USAGE_SW_WRITE_MASK) && + !(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + hnd->flags |= private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; + } + + if (usage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) { + if (!(current_value & private_handle_t::LOCK_STATE_MAPPED)) { + // we need to map for real + pthread_mutex_t* const lock = &sMapLock; + pthread_mutex_lock(lock); + if (!(hnd->lockState & private_handle_t::LOCK_STATE_MAPPED)) { + err = gralloc_map(module, handle, vaddr); + if (err == 0) { + android_atomic_or(private_handle_t::LOCK_STATE_MAPPED, + (volatile int32_t*)&(hnd->lockState)); + } + } + pthread_mutex_unlock(lock); + } + *vaddr = (void*)hnd->base; + } + + return err; +} + +int gralloc_unlock(gralloc_module_t const* module, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + private_handle_t* hnd = (private_handle_t*)handle; + int32_t current_value, new_value; + + if (hnd->flags & private_handle_t::PRIV_FLAGS_NEEDS_FLUSH) { + int err; + if (hnd->flags & (private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP)) { + struct pmem_addr pmem_addr; + pmem_addr.vaddr = hnd->base; + pmem_addr.offset = hnd->offset; + pmem_addr.length = hnd->size; + err = ioctl( hnd->fd, PMEM_CLEAN_CACHES, &pmem_addr); + } else if ((hnd->flags & private_handle_t::PRIV_FLAGS_USES_ASHMEM)) { + unsigned long addr = hnd->base + hnd->offset; + err = ioctl(hnd->fd, ASHMEM_CACHE_FLUSH_RANGE, NULL); + } + + LOGE_IF(err < 0, "cannot flush handle %p (offs=%x len=%x, flags = 0x%x) err=%s\n", + hnd, hnd->offset, hnd->size, hnd->flags, strerror(errno)); + hnd->flags &= ~private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; + } + + do { + current_value = hnd->lockState; + new_value = current_value; + + if (current_value & private_handle_t::LOCK_STATE_WRITE) { + // locked for write + if (hnd->writeOwner == gettid()) { + hnd->writeOwner = 0; + new_value &= ~private_handle_t::LOCK_STATE_WRITE; + } + } + + if ((new_value & private_handle_t::LOCK_STATE_READ_MASK) == 0) { + LOGE("handle %p not locked", handle); + return -EINVAL; + } + + new_value--; + + } while (android_atomic_cmpxchg(current_value, new_value, + (volatile int32_t*)&hnd->lockState)); + + return 0; +} + +/*****************************************************************************/ + +int gralloc_perform(struct gralloc_module_t const* module, + int operation, ... ) +{ + int res = -EINVAL; + va_list args; + va_start(args, operation); + + switch (operation) { + case GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER: { + int fd = va_arg(args, int); + size_t size = va_arg(args, size_t); + size_t offset = va_arg(args, size_t); + void* base = va_arg(args, void*); + + // validate that it's indeed a pmem buffer + pmem_region region; + if (ioctl(fd, PMEM_GET_SIZE, ®ion) < 0) { + break; + } + + native_handle_t** handle = va_arg(args, native_handle_t**); + private_handle_t* hnd = (private_handle_t*)native_handle_create( + private_handle_t::sNumFds, private_handle_t::sNumInts); + hnd->magic = private_handle_t::sMagic; + hnd->fd = fd; + hnd->flags = private_handle_t::PRIV_FLAGS_USES_PMEM; + hnd->size = size; + hnd->offset = offset; + hnd->base = intptr_t(base) + offset; + hnd->lockState = private_handle_t::LOCK_STATE_MAPPED; + hnd->gpuaddr = 0; + *handle = (native_handle_t *)hnd; + res = 0; + break; + } + case GRALLOC_MODULE_PERFORM_UPDATE_BUFFER_HANDLE: { + native_handle_t* handle = va_arg(args, native_handle_t*); + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + private_handle_t* hnd = (private_handle_t*)handle; + hnd->width = w; + hnd->height = h; + if (hnd->format != f) { + hnd->format = f; + } + break; + } + default: + break; + } + + va_end(args); + return res; +} diff --git a/libgralloc-qsd8k/pmemalloc.cpp b/libgralloc-qsd8k/pmemalloc.cpp new file mode 100755 index 0000000..fce2d87 --- /dev/null +++ b/libgralloc-qsd8k/pmemalloc.cpp @@ -0,0 +1,360 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 + +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/mman.h> + +#include <cutils/log.h> +#include <cutils/ashmem.h> + +#include "gralloc_priv.h" +#include "pmemalloc.h" + + +#define BEGIN_FUNC LOGV("%s begin", __PRETTY_FUNCTION__) +#define END_FUNC LOGV("%s end", __PRETTY_FUNCTION__) + + +static int get_open_flags(int usage) { + int openFlags = O_RDWR | O_SYNC; + uint32_t uread = usage & GRALLOC_USAGE_SW_READ_MASK; + uint32_t uwrite = usage & GRALLOC_USAGE_SW_WRITE_MASK; + if (uread == GRALLOC_USAGE_SW_READ_OFTEN || + uwrite == GRALLOC_USAGE_SW_WRITE_OFTEN) { + openFlags &= ~O_SYNC; + } + return openFlags; +} + +PmemAllocator::~PmemAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +PmemUserspaceAllocator::PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev): + deps(deps), + allocator(allocator), + pmemdev(pmemdev), + master_fd(MASTER_FD_INIT) +{ + BEGIN_FUNC; + pthread_mutex_init(&lock, NULL); + END_FUNC; +} + + +PmemUserspaceAllocator::~PmemUserspaceAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +void* PmemUserspaceAllocator::get_base_address() { + BEGIN_FUNC; + END_FUNC; + return master_base; +} + + +int PmemUserspaceAllocator::init_pmem_area_locked() +{ + BEGIN_FUNC; + int err = 0; + int fd = deps.open(pmemdev, O_RDWR, 0); + if (fd >= 0) { + size_t size = 0; + err = deps.getPmemTotalSize(fd, &size); + if (err < 0) { + LOGE("%s: PMEM_GET_TOTAL_SIZE failed (%d), limp mode", pmemdev, + err); + size = 8<<20; // 8 MiB + } + allocator.setSize(size); + + void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, + 0); + if (base == MAP_FAILED) { + LOGE("%s: failed to map pmem master fd: %s", pmemdev, + strerror(deps.getErrno())); + err = -deps.getErrno(); + base = 0; + deps.close(fd); + fd = -1; + } else { + master_fd = fd; + master_base = base; + } + } else { + LOGE("%s: failed to open pmem device: %s", pmemdev, + strerror(deps.getErrno())); + err = -deps.getErrno(); + } + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::init_pmem_area() +{ + BEGIN_FUNC; + pthread_mutex_lock(&lock); + int err = master_fd; + if (err == MASTER_FD_INIT) { + // first time, try to initialize pmem + err = init_pmem_area_locked(); + if (err) { + LOGE("%s: failed to initialize pmem area", pmemdev); + master_fd = err; + } + } else if (err < 0) { + // pmem couldn't be initialized, never use it + } else { + // pmem OK + err = 0; + } + pthread_mutex_unlock(&lock); + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::alloc_pmem_buffer(size_t size, int usage, + void** pBase, int* pOffset, int* pFd, int format) +{ + BEGIN_FUNC; + int err = init_pmem_area(); + if (err == 0) { + void* base = master_base; + int offset = allocator.allocate(size); + if (offset < 0) { + // no more pmem memory + LOGE("%s: no more pmem available", pmemdev); + err = -ENOMEM; + } else { + int openFlags = get_open_flags(usage); + + //LOGD("%s: allocating pmem at offset 0x%p", pmemdev, offset); + + // now create the "sub-heap" + int fd = deps.open(pmemdev, openFlags, 0); + err = fd < 0 ? fd : 0; + + // and connect to it + if (err == 0) + err = deps.connectPmem(fd, master_fd); + + // and make it available to the client process + if (err == 0) + err = deps.mapPmem(fd, offset, size); + + if (err < 0) { + LOGE("%s: failed to initialize pmem sub-heap: %d", pmemdev, + err); + err = -deps.getErrno(); + deps.close(fd); + allocator.deallocate(offset); + fd = -1; + } else { + LOGV("%s: mapped fd %d at offset %d, size %d", pmemdev, fd, offset, size); + memset((char*)base + offset, 0, size); + //Clean cache before flushing to ensure pmem is properly flushed + err = deps.cleanPmem(fd, (unsigned long) base + offset, offset, size); + if (err < 0) { + LOGE("cleanPmem failed: (%s)", strerror(deps.getErrno())); + } +#ifdef HOST + cacheflush(intptr_t(base) + offset, intptr_t(base) + offset + size, 0); +#endif + *pBase = base; + *pOffset = offset; + *pFd = fd; + } + //LOGD_IF(!err, "%s: allocating pmem size=%d, offset=%d", pmemdev, size, offset); + } + } + END_FUNC; + return err; +} + + +int PmemUserspaceAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) +{ + BEGIN_FUNC; + int err = 0; + if (fd >= 0) { + int err = deps.unmapPmem(fd, offset, size); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), fd=%d, sub.offset=%u, " + "sub.size=%u", strerror(deps.getErrno()), fd, offset, size); + if (err == 0) { + // we can't deallocate the memory in case of UNMAP failure + // because it would give that process access to someone else's + // surfaces, which would be a security breach. + allocator.deallocate(offset); + } + } + END_FUNC; + return err; +} + +PmemUserspaceAllocator::Deps::Allocator::~Allocator() +{ + BEGIN_FUNC; + END_FUNC; +} + +PmemUserspaceAllocator::Deps::~Deps() +{ + BEGIN_FUNC; + END_FUNC; +} + +PmemKernelAllocator::PmemKernelAllocator(Deps& deps): + deps(deps) +{ + BEGIN_FUNC; + END_FUNC; +} + + +PmemKernelAllocator::~PmemKernelAllocator() +{ + BEGIN_FUNC; + END_FUNC; +} + + +void* PmemKernelAllocator::get_base_address() { + BEGIN_FUNC; + END_FUNC; + return 0; +} + + +static unsigned clp2(unsigned x) { + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >>16); + return x + 1; +} + + +int PmemKernelAllocator::alloc_pmem_buffer(size_t size, int usage, + void** pBase,int* pOffset, int* pFd, int format) +{ + BEGIN_FUNC; + + *pBase = 0; + *pOffset = 0; + *pFd = -1; + + int err, offset = 0; + int openFlags = get_open_flags(usage); + const char *device; + + if (usage & GRALLOC_USAGE_PRIVATE_ADSP_HEAP) { + device = DEVICE_PMEM_ADSP; + } else if (usage & GRALLOC_USAGE_PRIVATE_SMI_HEAP) { + device = DEVICE_PMEM_SMIPOOL; + } else if ((usage & GRALLOC_USAGE_EXTERNAL_DISP) || + (usage & GRALLOC_USAGE_PROTECTED)) { + int tempFd = deps.open(DEVICE_PMEM_SMIPOOL, openFlags, 0); + if (tempFd < 0) { + device = DEVICE_PMEM_ADSP; + } else { + close(tempFd); + device = DEVICE_PMEM_SMIPOOL; + } + } else { + LOGE("Invalid device"); + return -EINVAL; + } + + int fd = deps.open(device, openFlags, 0); + if (fd < 0) { + err = -deps.getErrno(); + END_FUNC; + LOGE("Error opening %s", device); + return err; + } + + // The size should already be page aligned, now round it up to a power of 2. + //size = clp2(size); + + if (format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED) { + // Tile format buffers need physical alignment to 8K + err = deps.alignPmem(fd, size, 8192); + if (err < 0) { + LOGE("alignPmem failed"); + } + } + + void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("%s: failed to map pmem fd: %s", device, + strerror(deps.getErrno())); + err = -deps.getErrno(); + deps.close(fd); + END_FUNC; + return err; + } + memset(base, 0, size); + + *pBase = base; + *pOffset = 0; + *pFd = fd; + + END_FUNC; + return 0; +} + + +int PmemKernelAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) +{ + BEGIN_FUNC; + // The size should already be page aligned, now round it up to a power of 2 + // like we did when allocating. + //size = clp2(size); + + int err = deps.munmap(base, size); + if (err < 0) { + err = deps.getErrno(); + LOGW("error unmapping pmem fd: %s", strerror(err)); + return -err; + } + + END_FUNC; + return 0; +} + +PmemKernelAllocator::Deps::~Deps() +{ + BEGIN_FUNC; + END_FUNC; +} diff --git a/libgralloc-qsd8k/pmemalloc.h b/libgralloc-qsd8k/pmemalloc.h new file mode 100755 index 0000000..2a1c6fd --- /dev/null +++ b/libgralloc-qsd8k/pmemalloc.h @@ -0,0 +1,161 @@ +/* + * 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 GRALLOC_QSD8K_PMEMALLOC_H +#define GRALLOC_QSD8K_PMEMALLOC_H + +#include <limits.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> + + +/** + * An interface to the PMEM allocators. + */ +class PmemAllocator { + + public: + + virtual ~PmemAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address() = 0; + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd, int format) = 0; + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd) = 0; +}; + + +/** + * A PMEM allocator that allocates the entire pmem memory from the kernel and + * then uses a user-space allocator to suballocate from that. This requires + * that the PMEM device driver have kernel allocation disabled. + */ +class PmemUserspaceAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + class Allocator { + public: + virtual ~Allocator(); + virtual ssize_t setSize(size_t size) = 0; + virtual size_t size() const = 0; + virtual ssize_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual ssize_t deallocate(size_t offset) = 0; + }; + + virtual ~Deps(); + + // pmem + virtual size_t getPmemTotalSize(int fd, size_t* size) = 0; + virtual int connectPmem(int fd, int master_fd) = 0; + virtual int mapPmem(int fd, int offset, size_t size) = 0; + virtual int unmapPmem(int fd, int offset, size_t size) = 0; + virtual int cleanPmem(int fd, unsigned long base, int offset, size_t size) = 0; + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + }; + + PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev); + virtual ~PmemUserspaceAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int init_pmem_area_locked(); + virtual int init_pmem_area(); + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd, int format); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + +#ifndef ANDROID_OS + // DO NOT USE: For testing purposes only. + void set_master_values(int fd, void* base) { + master_fd = fd; + master_base = base; + } +#endif // ANDROID_OS + + private: + + enum { + MASTER_FD_INIT = -1, + }; + + Deps& deps; + Deps::Allocator& allocator; + + pthread_mutex_t lock; + const char* pmemdev; + int master_fd; + void* master_base; +}; + + +/** + * A PMEM allocator that allocates each individual allocation from the kernel + * (using the kernel's allocator). This requires the kernel driver for the + * particular PMEM device being allocated from to support kernel allocation. + */ +class PmemKernelAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + virtual ~Deps(); + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int munmap(void* start, size_t length) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + virtual int alignPmem(int fd, size_t size, int align) = 0; + }; + + PmemKernelAllocator(Deps& deps); + virtual ~PmemKernelAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd, int format); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + + private: + + Deps& deps; +}; + +#endif // GRALLOC_QSD8K_PMEMALLOC_H diff --git a/libgralloc-qsd8k/tests/Android.mk b/libgralloc-qsd8k/tests/Android.mk new file mode 100644 index 0000000..b9a7459 --- /dev/null +++ b/libgralloc-qsd8k/tests/Android.mk @@ -0,0 +1,55 @@ +# 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) + +# you can use EXTRA_CFLAGS to indicate additional CFLAGS to use +# in the build. The variables will be cleaned on exit +# +# + +libgralloc_test_includes:= \ + bionic/libstdc++/include \ + external/astl/include \ + external/gtest/include \ + $(LOCAL_PATH)/.. + +libgralloc_test_static_libs := \ + libgralloc_qsd8k_host \ + libgtest_main_host \ + libgtest_host \ + libastl_host \ + liblog + +define host-test + $(foreach file,$(1), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_CPP_EXTENSION := .cpp) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_C_INCLUDES := $(libgralloc_test_includes)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_CFLAGS += $(EXTRA_CFLAGS)) \ + $(eval LOCAL_LDLIBS += $(EXTRA_LDLIBS)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(libgralloc_test_static_libs)) \ + $(eval LOCAL_MODULE_TAGS := eng tests) \ + $(eval include $(BUILD_HOST_EXECUTABLE)) \ + ) \ + $(eval EXTRA_CFLAGS :=) \ + $(eval EXTRA_LDLIBS :=) +endef + +TEST_SRC_FILES := \ + pmemalloc_test.cpp + +$(call host-test, $(TEST_SRC_FILES)) diff --git a/libgralloc-qsd8k/tests/pmemalloc_test.cpp b/libgralloc-qsd8k/tests/pmemalloc_test.cpp new file mode 100644 index 0000000..94e86bf --- /dev/null +++ b/libgralloc-qsd8k/tests/pmemalloc_test.cpp @@ -0,0 +1,601 @@ +/* + * 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 <gtest/gtest.h> + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include "pmemalloc.h" + +class DepsStub : public PmemUserspaceAllocator::Deps, public PmemKernelAllocator::Deps { + + public: + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + return 0; + } + + virtual int connectPmem(int fd, int master_fd) { + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int getErrno() { + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return 0; + } + + virtual int munmap(void* start, size_t length) { + return 0; + } + + virtual int open(const char* pathname, int flags, int mode) { + return 0; + } + + virtual int close(int fd) { + return 0; + } +}; + +/******************************************************************************/ + +class AllocatorStub : public PmemUserspaceAllocator::Deps::Allocator { + virtual ssize_t setSize(size_t size) { + return 0; + } + + virtual size_t size() const { + return 0; + } + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + return 0; + } + + virtual ssize_t deallocate(size_t offset) { + return 0; + } +}; + +/******************************************************************************/ + +static const char* fakePmemDev = "/foo/bar"; + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletion : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(1234, fd); + return (void*)0x87654321; + } + +}; + +struct Allocator_InitPmemAreaLockedWithSuccessfulCompletion : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithSuccessfulCompletion) { + Deps_InitPmemAreaLockedWithSuccessfulCompletion depsMock; + Allocator_InitPmemAreaLockedWithSuccessfulCompletion allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMmap : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual int getErrno() { + return ENOMEM; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + +}; + +struct Allocator_InitPmemAreaLockedWithEnomemOnMmap : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEnomemOnMmap) { + Deps_InitPmemAreaLockedWithEnomemOnMmap depsMock; + Allocator_InitPmemAreaLockedWithEnomemOnMmap allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + return -EACCES; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEaccesOnGetPmemTotalSize) { + Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnOpen : public DepsStub { + + virtual int getErrno() { + return EACCES; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return -1; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithEaccesOnOpenMaster) { + Deps_InitPmemAreaLockedWithEaccesOnOpen depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletion Deps_InitPmemAreaWithSuccessfulInitialCompletion; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithSuccessfulInitialCompletion) { + Deps_InitPmemAreaWithSuccessfulInitialCompletion depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithEaccesOnOpen Deps_InitPmemAreaWithEaccesOnInitLocked; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithEaccesOnInitLocked) { + Deps_InitPmemAreaWithEaccesOnInitLocked depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterSuccessfulInitialCompletion) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(1234, 0); // Indicate that the pma has been successfully init'd + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); + //XXX JMG: Add this back in maybe? ASSERT_EQ(1234, pmi.master); // Make sure the master fd wasn't changed +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterFailedInit) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(-EACCES, 0); // Indicate that the pma has failed init + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return 0; + } +}; + + +struct Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags : public AllocatorStub { + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + EXPECT_EQ(size_t(0x100), size); + EXPECT_EQ(uint32_t(0x0), flags); + return 0x300; + } +}; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(uint8_t(0), buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnodevOnOpen : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENODEV; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnodevOnOpen; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnodevOnOpen) { + Deps_InitPmemAreaLockedWithEnodevOnOpen depsMock; + Allocator_AllocPmemBufferWithEnodevOnOpen allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENODEV, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnConnectPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnConnectPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnomemOnConnectPmem) { + Deps_InitPmemAreaLockedWithEnomemOnConnectPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnConnectPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMapPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnMapPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithEnomemOnMapPmem) { + Deps_InitPmemAreaLockedWithEnomemOnMapPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnMapPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags : public DepsStub { + + void* mmapResult; + + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags(void* mmapResult) : + mmapResult(mmapResult) {} + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(5678, fd); + return mmapResult; + } +}; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEpermOnOpen : public DepsStub { + + virtual int getErrno() { + return EPERM; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEpermOnOpen) { + Deps_KernelAllocPmemBufferWithEpermOnOpen depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-EPERM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEnomemOnMmap : DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + + virtual int getErrno() { + return ENOMEM; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEnomemOnMmap) { + Deps_KernelAllocPmemBufferWithEnomemOnMmap depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/ diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk new file mode 100644 index 0000000..ffc30bd --- /dev/null +++ b/libhwcomposer/Android.mk @@ -0,0 +1,28 @@ +LOCAL_PATH := $(call my-dir) + +# HAL module implemenation, not prelinked and stored in +# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog libcutils libEGL libhardware libutils liboverlay + +LOCAL_SRC_FILES := \ + hwcomposer.cpp + +LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM) +LOCAL_CFLAGS:= -DLOG_TAG=\"$(TARGET_BOARD_PLATFORM).hwcomposer\" +LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc +LOCAL_C_INCLUDES += hardware/msm7k/liboverlay +LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +ifeq ($(TARGET_HAVE_HDMI_OUT),true) +LOCAL_CFLAGS += -DHDMI_DUAL_DISPLAY +endif +ifeq ($(TARGET_HAVE_BYPASS),true) +LOCAL_CFLAGS += -DCOMPOSITION_BYPASS +endif +ifeq ($(TARGET_USE_HDMI_AS_PRIMARY),true) +LOCAL_CFLAGS += -DHDMI_AS_PRIMARY +endif +include $(BUILD_SHARED_LIBRARY) diff --git a/libhwcomposer/hwcomposer.cpp b/libhwcomposer/hwcomposer.cpp new file mode 100644 index 0000000..034d74d --- /dev/null +++ b/libhwcomposer/hwcomposer.cpp @@ -0,0 +1,1162 @@ +/* + * 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 <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <hardware/hardware.h> +#include <hardware/overlay.h> + +#include <fcntl.h> +#include <errno.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> + +#include <hardware/hwcomposer.h> +#include <hardware/overlay.h> +#include <hardware/copybit.h> +#include <overlayLib.h> +#include <overlayLibUI.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <ui/android_native_buffer.h> +#include <gralloc_priv.h> + +/*****************************************************************************/ +#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// Enum containing the supported composition types +enum { + COMPOSITION_TYPE_GPU = 0, + COMPOSITION_TYPE_MDP = 0x1, + COMPOSITION_TYPE_C2D = 0x2, + COMPOSITION_TYPE_CPU = 0x4, + COMPOSITION_TYPE_DYN = 0x8 +}; + +enum HWCCompositionType { + HWC_USE_GPU = HWC_FRAMEBUFFER, // This layer is to be handled by Surfaceflinger + HWC_USE_OVERLAY = HWC_OVERLAY, // This layer is to be handled by the overlay + HWC_USE_COPYBIT // This layer is to be handled by copybit +}; + +enum HWCPrivateFlags { + HWC_USE_ORIGINAL_RESOLUTION = HWC_FLAGS_PRIVATE_0, // This layer is to be drawn using overlays + HWC_DO_NOT_USE_OVERLAY = HWC_FLAGS_PRIVATE_1, // Do not use overlays to draw this layer + HWC_COMP_BYPASS = HWC_FLAGS_PRIVATE_3, // Layer "might" use or have used bypass +}; + +enum HWCLayerType{ + HWC_SINGLE_VIDEO = 0x1, + HWC_ORIG_RESOLUTION = 0x2, + HWC_S3D_LAYER = 0x4, + HWC_STOP_UI_MIRRORING_MASK = 0xF +}; + +#ifdef COMPOSITION_BYPASS +enum BypassState { + BYPASS_ON, + BYPASS_OFF, + BYPASS_OFF_PENDING, +}; + +enum { + MAX_BYPASS_LAYERS = 2, + ANIM_FRAME_COUNT = 30, +}; +#endif + +enum eHWCOverlayStatus { + HWC_OVERLAY_OPEN, + HWC_OVERLAY_PREPARE_TO_CLOSE, + HWC_OVERLAY_CLOSED +}; + +struct hwc_context_t { + hwc_composer_device_t device; + /* our private state goes below here */ + overlay::Overlay* mOverlayLibObject; +#ifdef COMPOSITION_BYPASS + overlay::OverlayUI* mOvUI[MAX_BYPASS_LAYERS]; + int animCount; + BypassState bypassState; +#endif +#if defined HDMI_DUAL_DISPLAY + bool mHDMIEnabled; + bool pendingHDMI; +#endif + int previousLayerCount; + eHWCOverlayStatus hwcOverlayStatus; +}; + +static int hwc_device_open(const struct hw_module_t* module, const char* name, + struct hw_device_t** device); + +static struct hw_module_methods_t hwc_module_methods = { + open: hwc_device_open +}; + + +struct private_hwc_module_t { + hwc_module_t base; + overlay_control_device_t *overlayEngine; + copybit_device_t *copybitEngine; + framebuffer_device_t *fbDevice; + int compositionType; + bool isBypassEnabled; //from build.prop debug.compbypass.enable +}; + +struct private_hwc_module_t HAL_MODULE_INFO_SYM = { + base: { + common: { + tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: HWC_HARDWARE_MODULE_ID, + name: "Hardware Composer Module", + author: "The Android Open Source Project", + methods: &hwc_module_methods, + } + }, + overlayEngine: NULL, + copybitEngine: NULL, + fbDevice: NULL, + compositionType: 0, + isBypassEnabled: false, +}; + +/*****************************************************************************/ + +static void dump_layer(hwc_layer_t const* l) { + LOGD("\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, {%d,%d,%d,%d}, {%d,%d,%d,%d}", + l->compositionType, l->flags, l->handle, l->transform, l->blending, + l->sourceCrop.left, + l->sourceCrop.top, + l->sourceCrop.right, + l->sourceCrop.bottom, + l->displayFrame.left, + l->displayFrame.top, + l->displayFrame.right, + l->displayFrame.bottom); +} + +static inline int min(const int& a, const int& b) { + return (a < b) ? a : b; +} + +static inline int max(const int& a, const int& b) { + return (a > b) ? a : b; +} + +static int setVideoOverlayStatusInGralloc(hwc_context_t* ctx, const bool enable) { +#if defined HDMI_DUAL_DISPLAY + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + ctx->device.common.module); + if(!hwcModule) { + LOGE("%s: invalid params", __FUNCTION__); + return -1; + } + + framebuffer_device_t *fbDev = hwcModule->fbDevice; + if (!fbDev) { + LOGE("%s: fbDev is NULL", __FUNCTION__); + return -1; + } + + // Inform the gralloc to stop or start UI mirroring + fbDev->videoOverlayStarted(fbDev, enable); +#endif + return 0; +} + +static void setHWCOverlayStatus(hwc_context_t *ctx, bool isVideoPresent) { + + switch (ctx->hwcOverlayStatus) { + case HWC_OVERLAY_OPEN: + ctx->hwcOverlayStatus = + isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_PREPARE_TO_CLOSE; + break; + case HWC_OVERLAY_PREPARE_TO_CLOSE: + ctx->hwcOverlayStatus = + isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_CLOSED; + break; + case HWC_OVERLAY_CLOSED: + ctx->hwcOverlayStatus = + isVideoPresent ? HWC_OVERLAY_OPEN : HWC_OVERLAY_CLOSED; + break; + default: + LOGE("%s: Invalid hwcOverlayStatus (status =%d)", __FUNCTION__, + ctx->hwcOverlayStatus); + break; + } +} + +static int hwc_closeOverlayChannels(hwc_context_t* ctx) { + overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; + if(!ovLibObject) { + LOGE("%s: invalid params", __FUNCTION__); + return -1; + } + + if (HWC_OVERLAY_PREPARE_TO_CLOSE == ctx->hwcOverlayStatus) { + // Video mirroring is going on, and we do not have any layers to + // mirror directly. Close the current video channel and inform the + // gralloc to start UI mirroring + ovLibObject->closeChannel(); + // Inform the gralloc that video overlay has stopped. + setVideoOverlayStatusInGralloc(ctx, false); + } + return 0; +} + +/* + * Configures mdp pipes + */ +static int prepareOverlay(hwc_context_t *ctx, hwc_layer_t *layer, const bool waitForVsync) { + int ret = 0; + if (LIKELY(ctx && ctx->mOverlayLibObject)) { + private_hwc_module_t* hwcModule = + reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module); + if (UNLIKELY(!hwcModule)) { + LOGE("prepareOverlay null module "); + return -1; + } + + private_handle_t *hnd = (private_handle_t *)layer->handle; + overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; + overlay_buffer_info info; + info.width = hnd->width; + info.height = hnd->height; + info.format = hnd->format; + info.size = hnd->size; + + ret = ovLibObject->setSource(info, layer->transform, + (ovLibObject->getHDMIStatus()?true:false), waitForVsync); + if (!ret) { + LOGE("prepareOverlay setSource failed"); + return -1; + } + + ret = ovLibObject->setParameter(OVERLAY_TRANSFORM, layer->transform); + if (!ret) { + LOGE("prepareOverlay setParameter failed transform %x", + layer->transform); + return -1; + } + + hwc_rect_t sourceCrop = layer->sourceCrop; + ret = ovLibObject->setCrop(sourceCrop.left, sourceCrop.top, + (sourceCrop.right - sourceCrop.left), + (sourceCrop.bottom - sourceCrop.top)); + if (!ret) { + LOGE("prepareOverlay setCrop failed"); + return -1; + } + + if (layer->flags == HWC_USE_ORIGINAL_RESOLUTION) { + framebuffer_device_t* fbDev = hwcModule->fbDevice; + ret = ovLibObject->setPosition(0, 0, + fbDev->width, fbDev->height); + } else { + hwc_rect_t displayFrame = layer->displayFrame; + ret = ovLibObject->setPosition(displayFrame.left, displayFrame.top, + (displayFrame.right - displayFrame.left), + (displayFrame.bottom - displayFrame.top)); + } + if (!ret) { + LOGE("prepareOverlay setPosition failed"); + return -1; + } + } + return 0; +} + +bool canSkipComposition(hwc_context_t* ctx, int yuvBufferCount, int currentLayerCount, + int numLayersNotUpdating) +{ + if (!ctx) { + LOGE("canSkipComposition invalid context"); + return false; + } + + bool compCountChanged = false; + if (yuvBufferCount == 1) { + if (currentLayerCount != ctx->previousLayerCount) { + compCountChanged = true; + ctx->previousLayerCount = currentLayerCount; + } + + if (!compCountChanged) { + if ((currentLayerCount == 1) || + ((currentLayerCount-1) == numLayersNotUpdating)) { + // We either have only one overlay layer or we have + // all the non-UI layers not updating. In this case + // we can skip the composition of the UI layers. + return true; + } + } + } else { + ctx->previousLayerCount = -1; + } + return false; +} + +static bool isFullScreenUpdate(const framebuffer_device_t* fbDev, const hwc_layer_list_t* list) { + + if(!fbDev) { + LOGE("ERROR: %s : fb device is invalid",__func__); + return false; + } + + int fb_w = fbDev->width; + int fb_h = fbDev->height; + + /* + * We have full screen condition when + * 1. We have 1 layer to compose + * a. layers dest rect equals display resolution. + * 2. We have 2 layers to compose + * a. Sum of their dest rects equals display resolution. + */ + + if(list->numHwLayers == 1) + { + hwc_rect_t rect = list->hwLayers[0].displayFrame; + + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + + int transform = list->hwLayers[0].transform; + + if(transform & (HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_ROT_270)) + return ((fb_w == h) && (fb_h == w)); + else + return ((fb_h == h) && (fb_w == w)); + } + + if(list->numHwLayers == 2) { + + hwc_rect_t rect_1 = list->hwLayers[0].displayFrame; + hwc_rect_t rect_2 = list->hwLayers[1].displayFrame; + + int transform_1 = list->hwLayers[0].transform; + int transform_2 = list->hwLayers[1].transform; + + int w1 = rect_1.right - rect_1.left; + int h1 = rect_1.bottom - rect_1.top; + int w2 = rect_2.right - rect_2.left; + int h2 = rect_2.bottom - rect_2.top; + + if(transform_1 == transform_2) { + if(transform_1 & (HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_ROT_270)) { + if((fb_w == (w1 + w2)) && (fb_h == h1) && (fb_h == h2)) + return true; + } else { + if((fb_w == w1) && (fb_w == w2) && (fb_h == (h1 + h2))) + return true; + } + } + } + return false; +} + +#ifdef COMPOSITION_BYPASS +/* + * Configures pipe(s) for composition bypass + */ +static int prepareBypass(hwc_context_t *ctx, hwc_layer_t *layer, int index, + int lastLayerIndex) { + if (ctx && ctx->mOvUI[index]) { + private_hwc_module_t* hwcModule = reinterpret_cast< + private_hwc_module_t*>(ctx->device.common.module); + if (!hwcModule) { + LOGE("prepareBypass null module "); + return -1; + } + private_handle_t *hnd = (private_handle_t *)layer->handle; + if(!hnd) { + LOGE("prepareBypass handle null"); + return -1; + } + hwc_rect_t sourceCrop = layer->sourceCrop; + if((sourceCrop.right - sourceCrop.left) > hwcModule->fbDevice->width || + (sourceCrop.bottom - sourceCrop.top) > hwcModule->fbDevice->height) { + ctx->animCount = ANIM_FRAME_COUNT; + return -1; + } + overlay::OverlayUI *ovUI = ctx->mOvUI[index]; + int ret = 0; + int orientation = layer->transform; + overlay_buffer_info info; + info.width = sourceCrop.right - sourceCrop.left; + info.height = sourceCrop.bottom - sourceCrop.top; + info.format = hnd->format; + info.size = hnd->size; + const bool useVGPipe = true; + //only last layer should wait for vsync + const bool waitForVsync = (index == lastLayerIndex); + const int fbnum = 0; + //Just to differentiate zorders for different layers + const int zorder = index; + ret = ovUI->setSource(info, orientation, useVGPipe, waitForVsync, + fbnum, zorder); + if (ret) { + LOGE("prepareBypass setSource failed"); + return -1; + } + + hwc_rect_t displayFrame = layer->displayFrame; + ret = ovUI->setPosition(displayFrame.left, displayFrame.top, + (displayFrame.right - displayFrame.left), + (displayFrame.bottom - displayFrame.top)); + if (ret) { + LOGE("prepareBypass setPosition failed"); + return -1; + } + } + return 0; +} + +static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer, + int index) { + if (ctx && ctx->mOvUI[index]) { + overlay::OverlayUI *ovUI = ctx->mOvUI[index]; + int ret = 0; + private_handle_t *hnd = (private_handle_t *)layer->handle; + ret = ovUI->queueBuffer(hnd); + if (ret) { + LOGE("drawLayerUsingBypass queueBuffer failed"); + return -1; + } + } + return 0; +} + +/* Checks if 2 layers intersect */ +static bool isIntersect(const hwc_rect_t& one, const hwc_rect_t& two) { + hwc_rect_t result; + result.left = max(one.left, two.left); + result.top = max(one.top, two.top); + result.right = min(one.right, two.right); + result.bottom = min(one.bottom, two.bottom); + const int width = result.right - result.left; + const int height = result.bottom - result.top; + const bool isEmpty = width <= 0 || height <= 0; + return !isEmpty; +} + +/* Check if layers are disjoint */ +static bool isDisjoint(const hwc_layer_list_t* list) { + //Validate supported layer range + if(list->numHwLayers <= 0 || list->numHwLayers > MAX_BYPASS_LAYERS) { + return false; + } + for(int i = 0; i < (list->numHwLayers) - 1; i++) { + for(int j = i + 1; j < list->numHwLayers; j++) { + if(isIntersect(list->hwLayers[i].displayFrame, + list->hwLayers[j].displayFrame)) { + return false; + } + } + } + return true; +} + +/* + * Checks if doing comp. bypass is possible. If video is not on and there + * are 2 layers then its doable. + */ +inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount, + const hwc_layer_list_t* list) { + hwc_context_t* ctx = (hwc_context_t*)(dev); + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + dev->common.module); + //Check if enabled in build.prop + if(hwcModule->isBypassEnabled == false) { + return false; + } + //Disable bypass during animation + if(UNLIKELY(ctx->animCount)) { + --(ctx->animCount); + return false; + } +#if defined HDMI_DUAL_DISPLAY + //Disable bypass when HDMI is enabled + if(ctx->mHDMIEnabled || ctx->pendingHDMI) { + return false; + } +#endif + return (yuvCount == 0) && isDisjoint(list); +} + +/* + * Bypass is not efficient if area is greater than 1280x720 + * AND rotation is necessary, since the rotator consumes + * time greater than 1 Vsync and is sequential. + */ +inline static bool isBypassEfficient(const framebuffer_device_t* fbDev, + const hwc_layer_list_t* list, hwc_context_t* ctx) { + bool rotationNeeded = false; + for(int i = 0; i < list->numHwLayers; ++i) { + if(list->hwLayers[i].transform) { + rotationNeeded = true; + break; + } + } + return !(rotationNeeded); +} + +bool setupBypass(hwc_context_t* ctx, hwc_layer_list_t* list) { + for (int index = 0 ; index < list->numHwLayers; index++) { + if(prepareBypass(ctx, &(list->hwLayers[index]), index, + list->numHwLayers - 1) != 0) { + return false; + } + } + return true; +} + +void setBypassLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list) { + for (int index = 0 ; index < list->numHwLayers; index++) { + list->hwLayers[index].flags = HWC_COMP_BYPASS; + list->hwLayers[index].compositionType = HWC_USE_OVERLAY; + #ifdef DEBUG + LOGE("%s: layer = %d", __FUNCTION__, index); + #endif + } +} + +void unsetBypassLayerFlags(hwc_layer_list_t* list) { + for (int index = 0 ; index < list->numHwLayers; index++) { + if(list->hwLayers[index].flags == HWC_COMP_BYPASS) { + list->hwLayers[index].flags = 0; + } + } +} + +void closeBypass(hwc_context_t* ctx) { + for (int index = 0 ; index < MAX_BYPASS_LAYERS; index++) { + ctx->mOvUI[index]->closeChannel(); + #ifdef DEBUG + LOGE("%s", __FUNCTION__); + #endif + } +} +#endif //COMPOSITION_BYPASS + + +static void handleHDMIStateChange(hwc_composer_device_t *dev) { +#if defined HDMI_DUAL_DISPLAY + hwc_context_t* ctx = (hwc_context_t*)(dev); + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + dev->common.module); + framebuffer_device_t *fbDev = hwcModule->fbDevice; + if (fbDev) { + fbDev->enableHDMIOutput(fbDev, ctx->mHDMIEnabled); + } + + if(ctx && ctx->mOverlayLibObject) { + overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; + ovLibObject->setHDMIStatus(ctx->mHDMIEnabled); + if (!(ctx->mHDMIEnabled)) { + // Close the overlay channels if HDMI is disconnected + ovLibObject->closeChannel(); + } + } +#endif +} + + +/* Just mark flags and do stuff after eglSwapBuffers */ +static void hwc_enableHDMIOutput(hwc_composer_device_t *dev, bool enable) { +#if defined HDMI_DUAL_DISPLAY + hwc_context_t* ctx = (hwc_context_t*)(dev); + ctx->mHDMIEnabled = enable; + if(enable) { //On connect, allow bypass to draw once to FB + ctx->pendingHDMI = true; + } else { //On disconnect, close immediately (there will be no bypass) + handleHDMIStateChange(dev); + } +#endif +} + +static bool isValidDestination(const framebuffer_device_t* fbDev, const hwc_rect_t& rect) +{ + if (!fbDev) { + LOGE("%s: fbDev is null", __FUNCTION__); + return false; + } + + int dest_width = (rect.right - rect.left); + int dest_height = (rect.bottom - rect.top); + + if (rect.left < 0 || rect.right < 0 || rect.top < 0 || rect.bottom < 0 + || dest_width <= 0 || dest_height <= 0) { + LOGE("%s: destination: left=%d right=%d top=%d bottom=%d width=%d" + "height=%d", __FUNCTION__, rect.left, rect.right, rect.top, + rect.bottom, dest_width, dest_height); + return false; + } + + if ((rect.left+dest_width) > fbDev->width || (rect.top+dest_height) > fbDev->height) { + LOGE("%s: destination out of bound params", __FUNCTION__); + return false; + } + + return true; +} + +static int getYUVBufferCount (const hwc_layer_list_t* list) { + int yuvBufferCount = 0; + if (list) { + for (size_t i=0 ; i<list->numHwLayers; i++) { + private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; + if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && + !(list->hwLayers[i].flags & HWC_DO_NOT_USE_OVERLAY)) { + yuvBufferCount++; + if (yuvBufferCount > 1) { + break; + } + } + } + } + return yuvBufferCount; +} + +static int getS3DVideoFormat (const hwc_layer_list_t* list) { + int s3dFormat = 0; + if (list) { + for (size_t i=0; i<list->numHwLayers; i++) { + private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; + if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO)) + s3dFormat = FORMAT_3D_INPUT(hnd->format); + if (s3dFormat) + break; + } + } + return s3dFormat; +} + +static bool isS3DCompositionRequired() { +#ifdef HDMI_AS_PRIMARY + return overlay::is3DTV(); +#endif + return false; +} + +static void markUILayerForS3DComposition (hwc_layer_t &layer, int s3dVideoFormat) { +#ifdef HDMI_AS_PRIMARY + layer.compositionType = HWC_FRAMEBUFFER; + switch(s3dVideoFormat) { + case HAL_3D_IN_SIDE_BY_SIDE_L_R: + case HAL_3D_IN_SIDE_BY_SIDE_R_L: + layer.hints |= HWC_HINT_DRAW_S3D_SIDE_BY_SIDE; + break; + case HAL_3D_IN_TOP_BOTTOM: + layer.hints |= HWC_HINT_DRAW_S3D_TOP_BOTTOM; + break; + default: + LOGE("%s: Unknown S3D input format 0x%x", __FUNCTION__, s3dVideoFormat); + break; + } +#endif + return; +} + +static int getLayersNotUpdatingCount(const hwc_layer_list_t* list) { + int numLayersNotUpdating = 0; + if (list) { + for (size_t i=0 ; i<list->numHwLayers; i++) { + private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; + if (hnd && (hnd->bufferType != BUFFER_TYPE_VIDEO) && + list->hwLayers[i].flags & HWC_LAYER_NOT_UPDATING) + numLayersNotUpdating++; + } + } + return numLayersNotUpdating; +} + +static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { + + hwc_context_t* ctx = (hwc_context_t*)(dev); + + if(!ctx || !list) { + LOGE("hwc_prepare invalid context or list"); + return -1; + } + + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + dev->common.module); + if(!hwcModule) { + LOGE("hwc_prepare null module "); + return -1; + } + + int yuvBufferCount = 0; + int layerType = 0; + bool isS3DCompositionNeeded = false; + int s3dVideoFormat = 0; + int numLayersNotUpdating = 0; + bool fullscreen = false; + + if (list) { + fullscreen = isFullScreenUpdate(hwcModule->fbDevice, list); + yuvBufferCount = getYUVBufferCount(list); + + bool skipComposition = false; + if (yuvBufferCount == 1) { + numLayersNotUpdating = getLayersNotUpdatingCount(list); + skipComposition = canSkipComposition(ctx, yuvBufferCount, + list->numHwLayers, numLayersNotUpdating); + s3dVideoFormat = getS3DVideoFormat(list); + if (s3dVideoFormat) + isS3DCompositionNeeded = isS3DCompositionRequired(); + } + + if (list->flags & HWC_GEOMETRY_CHANGED) { + if (yuvBufferCount == 1) { + // Inform the gralloc of the current video overlay status + setVideoOverlayStatusInGralloc(ctx, true); + } + } + + for (size_t i=0 ; i<list->numHwLayers ; i++) { + private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle; + // If there is a single Fullscreen layer, we can bypass it - TBD + // If there is only one video/camera buffer, we can bypass itn + if (list->hwLayers[i].flags & HWC_SKIP_LAYER) { + // During the animaton UI layers are marked as SKIP + // need to still mark the layer for S3D composition + if (isS3DCompositionNeeded) + markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat); + continue; + } + if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && (yuvBufferCount == 1)) { + bool waitForVsync = skipComposition ? true:false; + if (!isValidDestination(hwcModule->fbDevice, list->hwLayers[i].displayFrame)) { + list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; + skipComposition = false; + } else if(prepareOverlay(ctx, &(list->hwLayers[i]), waitForVsync) == 0) { + list->hwLayers[i].compositionType = HWC_USE_OVERLAY; + list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; + // We've opened the channel. Set the state to open. + ctx->hwcOverlayStatus = HWC_OVERLAY_OPEN; + } else if (hwcModule->compositionType & (COMPOSITION_TYPE_C2D)) { + //Fail safe path: If drawing with overlay fails, + + //Use C2D if available. + list->hwLayers[i].compositionType = HWC_USE_COPYBIT; + skipComposition = false; + } else { + //If C2D is not enabled fall back to GPU. + list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; + skipComposition = false; + } + } else if (isS3DCompositionNeeded) { + markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat); + } else if (list->hwLayers[i].flags == HWC_USE_ORIGINAL_RESOLUTION) { + list->hwLayers[i].compositionType = HWC_USE_OVERLAY; + list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; + layerType |= HWC_ORIG_RESOLUTION; + } else if (hnd && (hwcModule->compositionType & + (COMPOSITION_TYPE_C2D|COMPOSITION_TYPE_MDP))) { + list->hwLayers[i].compositionType = HWC_USE_COPYBIT; + } else if ((hwcModule->compositionType == COMPOSITION_TYPE_DYN) + && fullscreen) { + list->hwLayers[i].compositionType = HWC_USE_COPYBIT; + } else { + list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; + } + } + + if (skipComposition) { + list->flags |= HWC_SKIP_COMPOSITION; + } else { + list->flags &= ~HWC_SKIP_COMPOSITION; + } + +#ifdef COMPOSITION_BYPASS + //Check if bypass is feasible + if(isBypassDoable(dev, yuvBufferCount, list) && + isBypassEfficient(hwcModule->fbDevice, list, ctx)) { + //Setup bypass + if(setupBypass(ctx, list)) { + //Overwrite layer flags only if setup succeeds. + setBypassLayerFlags(ctx, list); + list->flags |= HWC_SKIP_COMPOSITION; + ctx->bypassState = BYPASS_ON; + } + } else { + unsetBypassLayerFlags(list); + if(ctx->bypassState == BYPASS_ON) { + ctx->bypassState = BYPASS_OFF_PENDING; + } + } +#endif + } + + return 0; +} +// --------------------------------------------------------------------------- +struct range { + int current; + int end; +}; +struct region_iterator : public copybit_region_t { + + region_iterator(hwc_region_t region) { + mRegion = region; + r.end = region.numRects; + r.current = 0; + this->next = iterate; + } + +private: + static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { + if (!self || !rect) { + LOGE("iterate invalid parameters"); + return 0; + } + + region_iterator const* me = static_cast<region_iterator const*>(self); + if (me->r.current != me->r.end) { + rect->l = me->mRegion.rects[me->r.current].left; + rect->t = me->mRegion.rects[me->r.current].top; + rect->r = me->mRegion.rects[me->r.current].right; + rect->b = me->mRegion.rects[me->r.current].bottom; + me->r.current++; + return 1; + } + return 0; + } + + hwc_region_t mRegion; + mutable range r; +}; + + +static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, EGLDisplay dpy, + EGLSurface surface) +{ + hwc_context_t* ctx = (hwc_context_t*)(dev); + if(!ctx) { + LOGE("drawLayerUsingCopybit null context "); + return -1; + } + + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(dev->common.module); + if(!hwcModule) { + LOGE("drawLayerUsingCopybit null module "); + return -1; + } + + private_handle_t *hnd = (private_handle_t *)layer->handle; + if(!hnd) { + LOGE("drawLayerUsingCopybit invalid handle"); + return -1; + } + + // Set the copybit source: + copybit_image_t src; + src.w = ALIGN(hnd->width, 32); + src.h = hnd->height; + src.format = hnd->format; + src.base = (void *)hnd->base; + src.handle = (native_handle_t *)layer->handle; + + // Copybit source rect + hwc_rect_t sourceCrop = layer->sourceCrop; + copybit_rect_t srcRect = {sourceCrop.left, sourceCrop.top, + sourceCrop.right, + sourceCrop.bottom}; + + // Copybit destination rect + hwc_rect_t displayFrame = layer->displayFrame; + copybit_rect_t dstRect = {displayFrame.left, displayFrame.top, + displayFrame.right, + displayFrame.bottom}; + + // Copybit dst + copybit_image_t dst; + android_native_buffer_t *renderBuffer = (android_native_buffer_t *)eglGetRenderBufferANDROID(dpy, surface); + if (!renderBuffer) { + LOGE("eglGetRenderBufferANDROID returned NULL buffer"); + return -1; + } + private_handle_t *fbHandle = (private_handle_t *)renderBuffer->handle; + if(!fbHandle) { + LOGE("Framebuffer handle is NULL"); + return -1; + } + dst.w = ALIGN(fbHandle->width,32); + dst.h = fbHandle->height; + dst.format = fbHandle->format; + dst.base = (void *)fbHandle->base; + dst.handle = (native_handle_t *)renderBuffer->handle; + + // Copybit region + hwc_region_t region = layer->visibleRegionScreen; + region_iterator copybitRegion(region); + + copybit_device_t *copybit = hwcModule->copybitEngine; + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, layer->transform); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, + (layer->blending == HWC_BLENDING_NONE) ? 0xFF : layer->alpha); + copybit->set_parameter(copybit, COPYBIT_PREMULTIPLIED_ALPHA, + (layer->blending == HWC_BLENDING_PREMULT)? COPYBIT_ENABLE : COPYBIT_DISABLE); + int err = copybit->stretch(copybit, &dst, &src, &dstRect, &srcRect, ©bitRegion); + + if(err < 0) + LOGE("copybit stretch failed"); + + return err; +} + +static int drawLayerUsingOverlay(hwc_context_t *ctx, hwc_layer_t *layer) +{ + if (ctx && ctx->mOverlayLibObject) { + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module); + if (!hwcModule) { + LOGE("drawLayerUsingLayer null module "); + return -1; + } + private_handle_t *hnd = (private_handle_t *)layer->handle; + overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; + int ret = 0; + + ret = ovLibObject->queueBuffer(hnd); + if (!ret) { + LOGE("drawLayerUsingOverlay queueBuffer failed"); + return -1; + } + } + return 0; +} + +static int hwc_set(hwc_composer_device_t *dev, + hwc_display_t dpy, + hwc_surface_t sur, + hwc_layer_list_t* list) +{ + hwc_context_t* ctx = (hwc_context_t*)(dev); + if(!ctx || !list) { + LOGE("hwc_set invalid context or list"); + return -1; + } + + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + dev->common.module); + if(!hwcModule) { + LOGE("hwc_set null module "); + return -1; + } + + int ret = 0; + for (size_t i=0; i<list->numHwLayers; i++) { + if (list->hwLayers[i].flags == HWC_SKIP_LAYER) { + continue; +#ifdef COMPOSITION_BYPASS + } else if (list->hwLayers[i].flags == HWC_COMP_BYPASS) { + drawLayerUsingBypass(ctx, &(list->hwLayers[i]), i); +#endif + } else if (list->hwLayers[i].compositionType == HWC_USE_OVERLAY) { + drawLayerUsingOverlay(ctx, &(list->hwLayers[i])); + } else if (list->flags & HWC_SKIP_COMPOSITION) { + break; + } else if (list->hwLayers[i].compositionType == HWC_USE_COPYBIT) { + drawLayerUsingCopybit(dev, &(list->hwLayers[i]), (EGLDisplay)dpy, (EGLSurface)sur); + } + } + + // Do not call eglSwapBuffers if we the skip composition flag is set on the list. + if (!(list->flags & HWC_SKIP_COMPOSITION)) { + EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur); + if (!sucess) { + ret = HWC_EGL_ERROR; + } + } +#ifdef COMPOSITION_BYPASS + if(ctx->bypassState == BYPASS_OFF_PENDING) { + closeBypass(ctx); + ctx->bypassState = BYPASS_OFF; + } +#endif +#if defined HDMI_DUAL_DISPLAY + if(ctx->pendingHDMI) { + handleHDMIStateChange(dev); + ctx->pendingHDMI = false; + } +#endif + + hwc_closeOverlayChannels(ctx); + int yuvBufferCount = getYUVBufferCount(list); + setHWCOverlayStatus(ctx, yuvBufferCount); + return ret; +} + +static int hwc_device_close(struct hw_device_t *dev) +{ + if(!dev) { + LOGE("hwc_device_close null device pointer"); + return -1; + } + + struct hwc_context_t* ctx = (struct hwc_context_t*)dev; + + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>( + ctx->device.common.module); + + // Close the overlay and copybit modules + if(hwcModule->copybitEngine) { + copybit_close(hwcModule->copybitEngine); + hwcModule->copybitEngine = NULL; + } + if(hwcModule->overlayEngine) { + overlay_control_close(hwcModule->overlayEngine); + hwcModule->overlayEngine = NULL; + } + if(hwcModule->fbDevice) { + framebuffer_close(hwcModule->fbDevice); + hwcModule->fbDevice = NULL; + } + + if (ctx) { + delete ctx->mOverlayLibObject; + ctx->mOverlayLibObject = NULL; +#ifdef COMPOSITION_BYPASS + for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { + delete ctx->mOvUI[i]; + } +#endif + free(ctx); + } + return 0; +} + +/*****************************************************************************/ +static int hwc_module_initialize(struct private_hwc_module_t* hwcModule) +{ + + // Open the overlay and copybit modules + hw_module_t const *module; + + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &(hwcModule->copybitEngine)); + } + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &(hwcModule->overlayEngine)); + } + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { + framebuffer_open(module, &(hwcModule->fbDevice)); + } + + // get the current composition type + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.sf.hw", property, NULL) > 0) { + if(atoi(property) == 0) { + //debug.sf.hw = 0 + hwcModule->compositionType = COMPOSITION_TYPE_CPU; + } else { //debug.sf.hw = 1 + // Get the composition type + property_get("debug.composition.type", property, NULL); + if (property == NULL) { + hwcModule->compositionType = COMPOSITION_TYPE_GPU; + } else if ((strncmp(property, "mdp", 3)) == 0) { + hwcModule->compositionType = COMPOSITION_TYPE_MDP; + } else if ((strncmp(property, "c2d", 3)) == 0) { + hwcModule->compositionType = COMPOSITION_TYPE_C2D; + } else if ((strncmp(property, "dyn", 3)) == 0) { + hwcModule->compositionType = COMPOSITION_TYPE_DYN; + } else { + hwcModule->compositionType = COMPOSITION_TYPE_GPU; + } + + if(!hwcModule->copybitEngine) + hwcModule->compositionType = COMPOSITION_TYPE_GPU; + } + } else { //debug.sf.hw is not set. Use cpu composition + hwcModule->compositionType = COMPOSITION_TYPE_CPU; + } + + //Check if composition bypass is enabled + if(property_get("debug.compbypass.enable", property, NULL) > 0) { + if(atoi(property) == 1) { + hwcModule->isBypassEnabled = true; + } + } + + return 0; +} + + +static int hwc_device_open(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) +{ + int status = -EINVAL; + if (!strcmp(name, HWC_HARDWARE_COMPOSER)) { + private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*> + (const_cast<hw_module_t*>(module)); + + hwc_module_initialize(hwcModule); + struct hwc_context_t *dev; + dev = (hwc_context_t*)malloc(sizeof(*dev)); + + /* initialize our state here */ + memset(dev, 0, sizeof(*dev)); + if(hwcModule->overlayEngine) { + dev->mOverlayLibObject = new overlay::Overlay(); +#ifdef COMPOSITION_BYPASS + for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { + dev->mOvUI[i] = new overlay::OverlayUI(); + } + dev->animCount = 0; + dev->bypassState = BYPASS_OFF; +#endif + } else { + dev->mOverlayLibObject = NULL; +#ifdef COMPOSITION_BYPASS + for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { + dev->mOvUI[i] = NULL; + } +#endif + } +#if defined HDMI_DUAL_DISPLAY + dev->mHDMIEnabled = false; + dev->pendingHDMI = false; +#endif + dev->hwcOverlayStatus = HWC_OVERLAY_CLOSED; + /* initialize the procs */ + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast<hw_module_t*>(module); + dev->device.common.close = hwc_device_close; + + dev->device.prepare = hwc_prepare; + dev->device.set = hwc_set; + dev->device.enableHDMIOutput = hwc_enableHDMIOutput; + *device = &dev->device.common; + + status = 0; + } + return status; +} diff --git a/liboverlay/Android.mk b/liboverlay/Android.mk new file mode 100644 index 0000000..0f3512c --- /dev/null +++ b/liboverlay/Android.mk @@ -0,0 +1,53 @@ +# Copyright (C) 2008 The Android Open Source Project +# Copyright (c) 2011, Code Aurora Forum. All rights reserved. +# +# 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_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) +LOCAL_SHARED_LIBRARIES := liblog libcutils libutils +LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc +LOCAL_SRC_FILES := \ + overlayLib.cpp \ + overlayLibUI.cpp \ +LOCAL_CFLAGS:= -DLOG_TAG=\"OverlayLib\" + +ifeq ($(TARGET_USES_ION),true) + LOCAL_CFLAGS += -DUSE_ION + LOCAL_SHARED_LIBRARIES += libmemalloc +endif + +ifeq ($(TARGET_USE_HDMI_AS_PRIMARY),true) +LOCAL_CFLAGS += -DHDMI_AS_PRIMARY +endif +LOCAL_MODULE := liboverlay +include $(BUILD_SHARED_LIBRARY) + +# HAL module implemenation, not prelinked and stored in +# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog liboverlay libcutils +LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc +LOCAL_SRC_FILES := overlay.cpp +LOCAL_MODULE := overlay.default +include $(BUILD_SHARED_LIBRARY) diff --git a/liboverlay/overlay.cpp b/liboverlay/overlay.cpp new file mode 100644 index 0000000..aa2828b --- /dev/null +++ b/liboverlay/overlay.cpp @@ -0,0 +1,1197 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * 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_TAG "Overlay" + +#include <hardware/hardware.h> +#include "overlayLib.h" +#include <cutils/properties.h> +#include <cutils/ashmem.h> +#include <utils/threads.h> +#include <linux/ashmem.h> +#include <gralloc_priv.h> + +using android::Mutex; + +#define USE_MSM_ROTATOR +#define EVEN_OUT(x) if (x & 0x0001) {x--;} + +#define SHARED_MEMORY_REGION_NAME "overlay_shared_memory" + +#define OVERLAY_HDMI_ENABLE 8 + +/*****************************************************************************/ + + +using namespace overlay; + +struct overlay_control_context_t { + struct overlay_control_device_t device; + void *sharedMemBase; + unsigned int format3D; //input and output 3D format, zero means no 3D + unsigned int state; + unsigned int orientation; + overlay_rect posPanel; +}; + +struct overlay_data_context_t { + struct overlay_data_device_t device; + OverlayDataChannel* pobjDataChannel[2]; + unsigned int format3D; + unsigned int state; + bool setCrop; + overlay_rect cropRect; + int srcFD; //store the FD as it will needed for fb1 + int size; //size of the overlay created + void *sharedMemBase; +}; + +/////////////////////////////////////////////////////////////////////////////////// + +/* Overlay State func FIXME move to a separate module */ +class overlay_object; +int setParameterHandleState(overlay_control_context_t *ctx, + overlay_object *obj, + int param, int value); +int createOverlayHandleState(overlay_control_context_t *ctx, bool noRot, + overlay_object* overlay, int fd); +int setPositionHandleState(overlay_control_context_t *ctx, + overlay_object *obj, overlay_rect& rect, + int x, int y, uint32_t w, uint32_t h); +int configPipes_OV_2D_VIDEO_ON_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +int configPipes_OV_3D_VIDEO_2D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +int configPipes_OV_3D_VIDEO_3D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +int configPipes_OV_2D_VIDEO_ON_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +int configPipes_OV_3D_VIDEO_2D_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +int configPipes_OV_3D_VIDEO_3D_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect); + +/* queue buffer */ +int queueBuffer_OV_2D_VIDEO_ON_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + +int queueBuffer_OV_3D_VIDEO_2D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + + +int queueBuffer_OV_3D_VIDEO_3D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + +int queueBuffer_OV_2D_VIDEO_ON_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + +int queueBuffer_OV_3D_VIDEO_2D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + +int queueBuffer_OV_3D_VIDEO_3D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot); + +/////////////////////////////////////////////////////////////////////////////////// + + +static int overlay_device_open(const struct hw_module_t* module, const char* name, + struct hw_device_t** device); + +static struct hw_module_methods_t overlay_module_methods = { +open: overlay_device_open +}; + +struct private_overlay_module_t { + overlay_module_t base; + Mutex *pobjMutex; +}; + +struct private_overlay_module_t HAL_MODULE_INFO_SYM = { +base: { +common: { +tag: HARDWARE_MODULE_TAG, + version_major: 1, + version_minor: 0, + id: OVERLAY_HARDWARE_MODULE_ID, + name: "QCT MSM OVERLAY module", + author: "QuIC, Inc.", + methods: &overlay_module_methods, + } + }, +pobjMutex: NULL, +}; + +struct handle_t : public native_handle { + int sharedMemoryFd; + int ovid[2]; + int rotid[2]; + int size; + int w; + int h; + int format; + unsigned int format3D; + OverlayControlChannel *pobjControlChannel[2]; +}; + +static int handle_get_ovId(const overlay_handle_t overlay, int index = 0) { + return static_cast<const struct handle_t *>(overlay)->ovid[index]; +} + +static int handle_get_rotId(const overlay_handle_t overlay, int index = 0) { + return static_cast<const struct handle_t *>(overlay)->rotid[index]; +} + +static int handle_get_size(const overlay_handle_t overlay) { + return static_cast<const struct handle_t *>(overlay)->size; +} + +static int handle_get_width(const overlay_handle_t overlay) { + return static_cast<const struct handle_t *>(overlay)->w; +} + +static int handle_get_height(const overlay_handle_t overlay) { + return static_cast<const struct handle_t *>(overlay)->h; +} + +static int handle_get_shared_fd(const overlay_handle_t overlay) { + return static_cast<const struct handle_t *>(overlay)->sharedMemoryFd; +} + +static int handle_get_format3D(const overlay_handle_t overlay) { + return static_cast<const struct handle_t *>(overlay)->format3D; +} + +/* + * This is the overlay_t object, it is returned to the user and represents + * an overlay. + * This handles will be passed across processes and possibly given to other + * HAL modules (for instance video decode modules). + */ +class overlay_object : public overlay_t { + handle_t mHandle; + + static overlay_handle_t getHandleRef(struct overlay_t* overlay) { + /* returns a reference to the handle, caller doesn't take ownership */ + return &(static_cast<overlay_object *>(overlay)->mHandle); + } + + public: + overlay_object(int w, int h, int format, int fd, unsigned int format3D = 0) { + this->overlay_t::getHandleRef = getHandleRef; + this->overlay_t::w = w; + this->overlay_t::h = h; + mHandle.version = sizeof(native_handle); + mHandle.sharedMemoryFd = fd; + mHandle.numFds = 1; + mHandle.numInts = (sizeof(mHandle) - sizeof(native_handle)) / 4; + mHandle.ovid[0] = -1; + mHandle.ovid[1] = -1; + mHandle.rotid[0] = -1; + mHandle.rotid[1] = -1; + mHandle.size = -1; + mHandle.w = w; + mHandle.h = h; + mHandle.format = format; + mHandle.format3D = format3D; + mHandle.pobjControlChannel[0] = 0; + mHandle.pobjControlChannel[1] = 0; + } + + ~overlay_object() { + destroy_overlay(); + } + + int getHwOvId(int index = 0) { return mHandle.ovid[index]; } + int getRotSessionId(int index = 0) { return mHandle.rotid[index]; } + int getSharedMemoryFD() {return mHandle.sharedMemoryFd;} + + bool startControlChannel(int fbnum, bool norot = false, int zorder = 0) { + int index = fbnum; + if (mHandle.format3D) + index = zorder; + if (!mHandle.pobjControlChannel[index]) + mHandle.pobjControlChannel[index] = new OverlayControlChannel(); + else { + mHandle.pobjControlChannel[index]->closeControlChannel(); + mHandle.pobjControlChannel[index] = new OverlayControlChannel(); + } + bool ret = mHandle.pobjControlChannel[index]->startControlChannel( + mHandle.w, mHandle.h, mHandle.format, fbnum, norot, false, + mHandle.format3D, zorder, true); + if (ret) { + if (!(mHandle.pobjControlChannel[index]-> + getOvSessionID(mHandle.ovid[index]) && + mHandle.pobjControlChannel[index]-> + getRotSessionID(mHandle.rotid[index]) && + mHandle.pobjControlChannel[index]-> + getSize(mHandle.size))) + ret = false; + } + + if (!ret) { + closeControlChannel(index); + } + + return ret; + } + + bool setPosition(int x, int y, uint32_t w, uint32_t h, int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->setPosition( + x, y, w, h); + } + + bool getAspectRatioPosition(overlay_rect *rect, int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->getAspectRatioPosition(mHandle.w, + mHandle.h, rect); + } + + bool setParameter(int param, int value, int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->setParameter( + param, value); + } + + bool closeControlChannel(int channel) { + if (!mHandle.pobjControlChannel[channel]) + return true; + bool ret = mHandle.pobjControlChannel[channel]-> + closeControlChannel(); + delete mHandle.pobjControlChannel[channel]; + mHandle.pobjControlChannel[channel] = 0; + return ret; + } + + bool getPositionS3D(overlay_rect *rect, int channel, bool useVFB = false) { + if (!mHandle.pobjControlChannel[channel]) { + LOGE("%s:Failed got channel %d", __func__, channel); + return false; + } + int format = useVFB ? HAL_3D_OUT_SIDE_BY_SIDE_MASK : mHandle.format3D; + return mHandle.pobjControlChannel[channel]->getPositionS3D( + channel, format, rect); + } + + bool getPosition(int *x, int *y, uint32_t *w, uint32_t *h, int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->getPosition( + *x, *y, *w, *h); + } + + bool getOrientation(int *orientation, int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->getOrientation( + *orientation); + } + + void destroy_overlay() { + close(mHandle.sharedMemoryFd); + closeControlChannel(VG1_PIPE); + closeControlChannel(VG0_PIPE); + if(mHandle.format3D) { + send3DInfoPacket (0); + enableBarrier(0); + } + } + + int getFBWidth(int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->getFBWidth(); + } + + int getFBHeight(int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->getFBHeight(); + } + + inline void setFormat3D(unsigned int format3D) { + mHandle.format3D = format3D; + } + + inline bool useVirtualFB(int channel) { + if (!mHandle.pobjControlChannel[channel]) + return false; + return mHandle.pobjControlChannel[channel]->useVirtualFB(); + } +}; + +// **************************************************************************** +// Control module +// **************************************************************************** + +static int overlay_get(struct overlay_control_device_t *dev, int name) { + int result = -1; + switch (name) { + case OVERLAY_MINIFICATION_LIMIT: + result = HW_OVERLAY_MINIFICATION_LIMIT; + break; + case OVERLAY_MAGNIFICATION_LIMIT: + result = HW_OVERLAY_MAGNIFICATION_LIMIT; + break; + case OVERLAY_SCALING_FRAC_BITS: + result = 32; + break; + case OVERLAY_ROTATION_STEP_DEG: + result = 90; // 90 rotation steps (for instance) + break; + case OVERLAY_HORIZONTAL_ALIGNMENT: + result = 1; // 1-pixel alignment + break; + case OVERLAY_VERTICAL_ALIGNMENT: + result = 1; // 1-pixel alignment + break; + case OVERLAY_WIDTH_ALIGNMENT: + result = 1; // 1-pixel alignment + break; + case OVERLAY_HEIGHT_ALIGNMENT: + result = 1; // 1-pixel alignment + break; + } + return result; +} + +static void error_cleanup_control(overlay_control_context_t *ctx, overlay_object *overlay, int fd, int index) { + LOGE("Failed to start control channel %d", index); + for (int i = 0; i < index; i++) + overlay->closeControlChannel(i); + if(ctx && (ctx->sharedMemBase != MAP_FAILED)) { + munmap(ctx->sharedMemBase, sizeof(overlay_shared_data)); + ctx->sharedMemBase = MAP_FAILED; + } + if(fd > 0) + close(fd); + delete overlay; +} + +static overlay_t* overlay_createOverlay(struct overlay_control_device_t *dev, + uint32_t w, uint32_t h, int32_t format) { + overlay_object *overlay = NULL; + overlay_control_context_t *ctx = (overlay_control_context_t *)dev; + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + + // Open shared memory to store shared data + int size = sizeof(overlay_shared_data); + void *base; + int fd = ashmem_create_region(SHARED_MEMORY_REGION_NAME, + size); + if(fd < 0) { + LOGE("%s: create shared memory failed", __func__); + return NULL; + } + if (ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE) < 0) { + LOGE("ashmem_set_prot_region(fd=%d, failed (%s)", + fd, strerror(-errno)); + close(fd); + fd = -1; + return NULL; + } else { + base = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED|MAP_POPULATE, fd, 0); + if (base == MAP_FAILED) { + LOGE("alloc mmap(fd=%d, size=%d) failed (%s)", + fd, size, strerror(-errno)); + close(fd); + fd = -1; + return NULL; + } + } + + // Separate the color format from the 3D format. + // If there is 3D content; the effective format passed by the client is: + // effectiveFormat = 3D_IN | 3D_OUT | ColorFormat + unsigned int format3D = FORMAT_3D(format); + format = COLOR_FORMAT(format); + int fIn3D = FORMAT_3D_INPUT(format3D); // MSB 2 bytes are input format + int fOut3D = FORMAT_3D_OUTPUT(format3D); // LSB 2 bytes are output format + format3D = fIn3D | fOut3D; + // Use the same in/out format if not mentioned + if (!fIn3D) { + format3D |= fOut3D << SHIFT_3D; //Set the input format + } + if(!fOut3D) { + switch (fIn3D) { + case HAL_3D_IN_SIDE_BY_SIDE_L_R: + case HAL_3D_IN_SIDE_BY_SIDE_R_L: + // For all side by side formats, set the output + // format as Side-by-Side i.e 0x1 + format3D |= HAL_3D_IN_SIDE_BY_SIDE_L_R >> SHIFT_3D; + break; + default: + format3D |= fIn3D >> SHIFT_3D; //Set the output format + break; + } + } + unsigned int curState = overlay::getOverlayConfig(format3D); + if (curState == OV_3D_VIDEO_2D_PANEL || curState == OV_3D_VIDEO_2D_TV) { + LOGI("3D content on 2D display: set the output format as monoscopic"); + format3D = FORMAT_3D_INPUT(format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + } + LOGW("createOverlay: creating overlay with format3D: 0x%x, curState: %d", format3D, curState); + ctx->sharedMemBase = base; + ctx->format3D = format3D; + ctx->state = curState; + memset(ctx->sharedMemBase, 0, size); + + /* number of buffer is not being used as overlay buffers are coming from client */ + overlay = new overlay_object(w, h, format, fd, format3D); + if (overlay == NULL) { + LOGE("%s: can't create overlay object!", __FUNCTION__); + if(ctx && (ctx->sharedMemBase != MAP_FAILED)) { + munmap(ctx->sharedMemBase, size); + ctx->sharedMemBase = MAP_FAILED; + } + if(fd > 0) + close(fd); + return NULL; + } + bool noRot; +#ifdef USE_MSM_ROTATOR + noRot = false; +#else + noRot = true; +#endif + if(-1 == createOverlayHandleState(ctx, noRot, overlay, fd)) + return 0;// NULL + overlay_shared_data* data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + data->state = ctx->state; + for (int i=0; i<NUM_CHANNELS; i++) { + data->ovid[i] = overlay->getHwOvId(i); + data->rotid[i] = overlay->getRotSessionId(i); + } + return overlay; +} + +static void overlay_destroyOverlay(struct overlay_control_device_t *dev, + overlay_t* overlay) +{ + overlay_control_context_t *ctx = (overlay_control_context_t *)dev; + overlay_object * obj = static_cast<overlay_object *>(overlay); + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + if(ctx && (ctx->sharedMemBase != MAP_FAILED)) { + munmap(ctx->sharedMemBase, sizeof(overlay_shared_data)); + ctx->sharedMemBase = MAP_FAILED; + } + // ~overlay_object calls destroy_overlay + delete obj; +} + +static int overlay_setPosition(struct overlay_control_device_t *dev, + overlay_t* overlay, + int x, int y, uint32_t w, uint32_t h) { + /* set this overlay's position (talk to the h/w) */ + overlay_control_context_t *ctx = (overlay_control_context_t *)dev; + overlay_object * obj = static_cast<overlay_object *>(overlay); + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + bool ret; + overlay_rect rect; + // saving the position for the disconnection event + ctx->posPanel.x = x; + ctx->posPanel.y = y; + ctx->posPanel.w = w; + ctx->posPanel.h = h; + + if(-1 == setPositionHandleState(ctx, obj, rect, x, y, w, h)) + return -1; + + return 0; +} + +static int overlay_commit(struct overlay_control_device_t *dev, + overlay_t* overlay) +{ + overlay_control_context_t *ctx = (overlay_control_context_t *)dev; + overlay_object *obj = static_cast<overlay_object *>(overlay); + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + + Mutex::Autolock objLock(m->pobjMutex); + if (obj && (obj->getSharedMemoryFD() > 0) && + (ctx->sharedMemBase != MAP_FAILED)) { + overlay_shared_data* data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + data->isControlSetup = true; + } + return 0; +} + +static int overlay_getPosition(struct overlay_control_device_t *dev, + overlay_t* overlay, + int* x, int* y, uint32_t* w, uint32_t* h) { + + /* get this overlay's position */ + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + overlay_object * obj = static_cast<overlay_object *>(overlay); + return obj->getPosition(x, y, w, h, 0) ? 0 : -1; +} + +static bool overlay_configPipes(overlay_control_context_t *ctx, + overlay_object *obj, int enable, + unsigned int newState) { + bool noRot = true; + overlay_rect rect; +#ifdef USE_MSM_ROTATOR + noRot = false; +#else + noRot = true; +#endif + switch (ctx->state) + { + case OV_2D_VIDEO_ON_PANEL: + if(-1 == configPipes_OV_2D_VIDEO_ON_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + case OV_3D_VIDEO_2D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_2D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_3D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + case OV_2D_VIDEO_ON_TV: + if(-1 == configPipes_OV_2D_VIDEO_ON_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + case OV_3D_VIDEO_2D_TV: + if(-1 == configPipes_OV_3D_VIDEO_2D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == configPipes_OV_3D_VIDEO_3D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return false; + break; + default: + LOGE("Unknown state in configPipes"); + abort(); + } + //update the context's state + ctx->state = newState; + return true; +} + +static int overlay_setParameter(struct overlay_control_device_t *dev, + overlay_t* overlay, int param, int value) { + + overlay_control_context_t *ctx = (overlay_control_context_t *)dev; + overlay_object *obj = static_cast<overlay_object *>(overlay); + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + + if (obj && (obj->getSharedMemoryFD() > 0) && + (ctx->sharedMemBase != MAP_FAILED)) { + overlay_shared_data* data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + data->isControlSetup = false; + /* SF will inform Overlay HAL the HDMI cable connection. + This avoids polling on the system property hw.hdmiON */ + if(param == OVERLAY_HDMI_ENABLE) { + unsigned int curState = getOverlayConfig(ctx->format3D, false, value); + if(ctx->state != curState) { + LOGI("Overlay Configured for : %d Current state: %d", ctx->state, curState); + if(!overlay_configPipes(ctx, obj, value, curState)) { + LOGE("In overlay_setParameter: reconfiguring of Overlay failed !!"); + return -1; + } + else { + data->state = ctx->state; + for (int i=0; i<NUM_CHANNELS; i++) { + data->ovid[i] = obj->getHwOvId(i); + data->rotid[i] = obj->getRotSessionId(i); + } + } + } + } + } + if(param != OVERLAY_HDMI_ENABLE) { + //Save the panel orientation + if (param == OVERLAY_TRANSFORM) { + ctx->orientation = value; + if(ctx->state == OV_3D_VIDEO_3D_PANEL) { + int barrier = 0; + switch(ctx->orientation) { + case HAL_TRANSFORM_ROT_90: + case HAL_TRANSFORM_ROT_270: + barrier = BARRIER_LANDSCAPE; + break; + default: + barrier = BARRIER_PORTRAIT; + break; + } + if(!enableBarrier(barrier)) + LOGE("%s:failed to enable barriers for 3D video", __func__); + } + } + if (-1 == setParameterHandleState(ctx, obj, param, value)) + return -1; + } + return 0; +} + +static int overlay_control_close(struct hw_device_t *dev) +{ + struct overlay_control_context_t* ctx = (struct overlay_control_context_t*)dev; + if (ctx) { + /* free all resources associated with this device here + * in particular the overlay_handle_t, outstanding overlay_t, etc... + */ + free(ctx); + } + return 0; +} + +// **************************************************************************** +// Data module +// **************************************************************************** + +static void error_cleanup_data(struct overlay_data_context_t* ctx, int index) +{ + LOGE("Couldn't start data channel %d", index); + for (int i = 0; i<index; i++) { + delete ctx->pobjDataChannel[i]; + ctx->pobjDataChannel[i] = NULL; + } +} + +int overlay_initialize(struct overlay_data_device_t *dev, + overlay_handle_t handle) +{ + /* + * overlay_handle_t should contain all the information to "inflate" this + * overlay. Typically it'll have a file descriptor, informations about + * how many buffers are there, etc... + * It is also the place to mmap all buffers associated with this overlay + * (see getBufferAddress). + * + * NOTE: this function doesn't take ownership of overlay_handle_t + * + */ + + struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; + int ovid = handle_get_ovId(handle); + int rotid = handle_get_rotId(handle); + int size = handle_get_size(handle); + int sharedFd = handle_get_shared_fd(handle); + unsigned int format3D = handle_get_format3D(handle); + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + bool noRot = true; +#ifdef USE_MSM_ROTATOR + noRot = false; +#else + noRot = true; +#endif + //default: set crop info to src size. + ctx->cropRect.x = 0; + ctx->cropRect.y = 0; + ctx->cropRect.w = handle_get_width(handle); + ctx->cropRect.h = handle_get_height(handle); + + ctx->sharedMemBase = MAP_FAILED; + ctx->format3D = format3D; + //Store the size, needed for HDMI mirroring + ctx->size = size; + + if(sharedFd > 0) { + void *base = mmap(0, sizeof(overlay_shared_data), PROT_READ, + MAP_SHARED|MAP_POPULATE, sharedFd, 0); + if(base == MAP_FAILED) { + LOGE("%s: map region failed %d", __func__, -errno); + return -1; + } + ctx->sharedMemBase = base; + } else { + LOGE("Received invalid shared memory fd"); + return -1; + } + overlay_shared_data* data = static_cast<overlay_shared_data*> + (ctx->sharedMemBase); + if (data == NULL){ + LOGE("%s:Shared data is NULL!!", __func__); + return -1; + } + ctx->state = data->state; + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + ctx->pobjDataChannel[VG0_PIPE] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[VG0_PIPE]->startDataChannel(ovid, rotid, size, FRAMEBUFFER_0, noRot)) { + error_cleanup_data(ctx, VG0_PIPE); + return -1; + } + break; + case OV_3D_VIDEO_3D_PANEL: + overlay_rect rect; + for (int i = 0; i < NUM_CHANNELS; i++) { + ovid = handle_get_ovId(handle, i); + rotid = handle_get_rotId(handle, i); + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[i]->startDataChannel(ovid, rotid, size, FRAMEBUFFER_0, noRot)) { + error_cleanup_data(ctx, i); + return -1; + } + } + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + for (int i = 0; i < NUM_CHANNELS; i++) { + ovid = handle_get_ovId(handle, i); + rotid = handle_get_rotId(handle, i); + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (FRAMEBUFFER_1 == i) + noRot = true; + if (!ctx->pobjDataChannel[i]->startDataChannel(ovid, rotid, size, i, noRot)) { + error_cleanup_data(ctx, i); + return -1; + } + } + break; + case OV_3D_VIDEO_3D_TV: + for (int i = 0; i < NUM_CHANNELS; i++) { + ovid = handle_get_ovId(handle, i); + rotid = handle_get_rotId(handle, i); + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[i]->startDataChannel(ovid, rotid, size, FRAMEBUFFER_1, true)) { + error_cleanup_data(ctx, i); + return -1; + } + } + if(!send3DInfoPacket(ctx->format3D & OUTPUT_MASK_3D)) + LOGI("%s:Error setting the 3D mode for TV", __func__); + break; + default: + break; + } + return 0; +} + +int overlay_dequeueBuffer(struct overlay_data_device_t *dev, + overlay_buffer_t* buf) +{ + /* blocks until a buffer is available and return an opaque structure + * representing this buffer. + */ + + /* no internal overlay buffer to dequeue */ + LOGE("%s: no buffer to dequeue ...\n", __FUNCTION__); + + return 0; +} + +//Called with Mutex::Autolock objLock(m->pobjMutex); already held +int overlay_queueBufferCheckStateChange(struct overlay_data_device_t *dev, + overlay_buffer_t buffer) +{ + /* Mark this buffer for posting and recycle or free overlay_buffer_t. */ + struct overlay_data_context_t *ctx = (struct overlay_data_context_t*)dev; + overlay_shared_data* data = 0; + if(ctx->sharedMemBase != MAP_FAILED) { + data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + if(0 == data){ + LOGE("ctx->sharedMemBase is NULL"); + return false; + } + } + else{ + LOGE("ctx->sharedMemBase == MAP_FAILED"); + return false; + } + + bool noRot = true; +#ifdef USE_MSM_ROTATOR + noRot = false; +#else + noRot = true; +#endif + + unsigned int newState = data->state; + + switch (ctx->state) + { + case OV_2D_VIDEO_ON_PANEL: + if(-1 == queueBuffer_OV_2D_VIDEO_ON_PANEL(ctx, data, + newState, + noRot)) + return -1; + break; + case OV_3D_VIDEO_2D_PANEL: + if(-1 == queueBuffer_OV_3D_VIDEO_2D_PANEL(ctx, data, + newState, + noRot)) + return -1; + break; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_PANEL(ctx, data, + newState, + noRot)) + return -1; + break; + case OV_2D_VIDEO_ON_TV: + if(-1 == queueBuffer_OV_2D_VIDEO_ON_TV(ctx, data, + newState, + noRot)) + return -1; + break; + case OV_3D_VIDEO_2D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_2D_TV(ctx, data, + newState, + noRot)) + return -1; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_TV(ctx, data, + newState, + noRot)) + return -1; + break; + default: + LOGE("Unknown state in configPipes"); + abort(); + } + //update the context's state + ctx->state = newState; + return 0; +} + +int overlay_queueBuffer(struct overlay_data_device_t *dev, + overlay_buffer_t buffer) +{ + struct overlay_data_context_t *ctx = (struct overlay_data_context_t*)dev; + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + Mutex::Autolock objLock(m->pobjMutex); + + // Check if control channel is setup. + overlay_shared_data* data = NULL; + if(ctx->sharedMemBase != MAP_FAILED) { + data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + if(data == NULL) + return false; + } + else + return false; + + if(false == data->isControlSetup) { + LOGE("Overlay Control Channel is not fully setup yet"); + return -1; + } + + // check any state-changing related events + if(-1 == overlay_queueBufferCheckStateChange(dev, buffer)){ + return -1; + } + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + if (ctx->setCrop) { + if(!ctx->pobjDataChannel[VG0_PIPE]->setCrop(ctx->cropRect.x, ctx->cropRect.y, ctx->cropRect.w, ctx->cropRect.h)) { + LOGE("%s: failed for pipe 0", __func__); + } + ctx->setCrop = false; + } + if(!ctx->pobjDataChannel[VG0_PIPE]->queueBuffer((uint32_t) buffer)) { + LOGE("%s: failed for VG pipe 0", __func__); + return -1; + } + break; + case OV_3D_VIDEO_2D_PANEL: + if (ctx->setCrop) { + overlay_rect rect; + ctx->pobjDataChannel[VG0_PIPE]->getCropS3D(&ctx->cropRect, VG0_PIPE, ctx->format3D, &rect); + if(!ctx->pobjDataChannel[VG0_PIPE]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: failed for pipe 0", __func__); + } + ctx->setCrop = false; + } + if(!ctx->pobjDataChannel[VG0_PIPE]->queueBuffer((uint32_t) buffer)) { + LOGE("%s: failed for VG pipe 0", __func__); + return -1; + } + break; + case OV_3D_VIDEO_3D_PANEL: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!ctx->pobjDataChannel[i]->queueBuffer((uint32_t) buffer)) { + LOGE("%s: failed for VG pipe %d", __func__, i); + return -1; + } + } + break; + default: + break; + } + return 0; +} + +int overlay_setFd(struct overlay_data_device_t *dev, int fd) +{ + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; + Mutex::Autolock objLock(m->pobjMutex); + ctx->srcFD = fd; + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + if(!ctx->pobjDataChannel[VG0_PIPE]->setFd(fd)) { + LOGE("%s: failed for VG pipe 0", __func__); + return -1; + } + break; + case OV_3D_VIDEO_3D_PANEL: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!ctx->pobjDataChannel[i]->setFd(fd)) { + LOGE("%s: failed for pipe %d", __func__, i); + return -1; + } + } + break; + default: + break; + } + return 0; +} + +static int overlay_setCrop(struct overlay_data_device_t *dev, uint32_t x, + uint32_t y, uint32_t w, uint32_t h) +{ + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*>( + dev->common.module); + struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; + overlay_shared_data* data = static_cast<overlay_shared_data*>(ctx->sharedMemBase); + //Yield processor until control channel is fully set up i.e commit happens. + while(false == data->isControlSetup) { + sched_yield(); + } + Mutex::Autolock objLock(m->pobjMutex); + overlay_rect rect; + ctx->cropRect.x = x; + ctx->cropRect.y = y; + ctx->cropRect.w = w; + ctx->cropRect.h = h; + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + ctx->setCrop = true; + break; + case OV_2D_VIDEO_ON_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!ctx->pobjDataChannel[i]->setCrop(x, y, w, h)) { + LOGE("%s: failed for pipe %d", __func__, i); + return -1; + } + } + break; + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i]->getCropS3D(&ctx->cropRect, i, ctx->format3D, &rect); + if(!ctx->pobjDataChannel[i]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: failed for pipe %d", __func__, i); + return -1; + } + } + break; + default: + break; + } + return 0; +} + +void *overlay_getBufferAddress(struct overlay_data_device_t *dev, + overlay_buffer_t buffer) +{ + /* overlay buffers are coming from client */ + return( NULL ); +} + +int overlay_getBufferCount(struct overlay_data_device_t *dev) +{ + return 0; +} + + +static int overlay_data_close(struct hw_device_t *dev) +{ + struct overlay_data_context_t* ctx = (struct overlay_data_context_t*)dev; + if (ctx) { + /* free all resources associated with this device here + * in particular all pending overlay_buffer_t if needed. + * + * NOTE: overlay_handle_t passed in initialize() is NOT freed and + * its file descriptors are not closed (this is the responsibility + * of the caller). + */ + + if (ctx->pobjDataChannel[0]) { + ctx->pobjDataChannel[0]->closeDataChannel(); + delete ctx->pobjDataChannel[0]; + ctx->pobjDataChannel[0] = 0; + } + + if (ctx->pobjDataChannel[1]) { + ctx->pobjDataChannel[1]->closeDataChannel(); + delete ctx->pobjDataChannel[1]; + ctx->pobjDataChannel[1] = 0; + } + + if(ctx->sharedMemBase != MAP_FAILED) { + munmap(ctx->sharedMemBase, sizeof(overlay_shared_data)); + ctx->sharedMemBase = MAP_FAILED; + } + + free(ctx); + } + return 0; +} + +/*****************************************************************************/ + +static int overlay_device_open(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) +{ + int status = -EINVAL; + + private_overlay_module_t* m = reinterpret_cast<private_overlay_module_t*> + (const_cast<hw_module_t*>(module)); + if (!m->pobjMutex) + m->pobjMutex = new Mutex(); + + if (!strcmp(name, OVERLAY_HARDWARE_CONTROL)) { + struct overlay_control_context_t *dev; + dev = (overlay_control_context_t*)malloc(sizeof(*dev)); + + if (!dev) + return status; + + /* initialize our state here */ + memset(dev, 0, sizeof(*dev)); + + /* initialize the procs */ + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast<hw_module_t*>(module); + dev->device.common.close = overlay_control_close; + + dev->device.get = overlay_get; + dev->device.createOverlay = overlay_createOverlay; + dev->device.destroyOverlay = overlay_destroyOverlay; + dev->device.setPosition = overlay_setPosition; + dev->device.getPosition = overlay_getPosition; + dev->device.setParameter = overlay_setParameter; + dev->device.commit = overlay_commit; + + *device = &dev->device.common; + status = 0; + } else if (!strcmp(name, OVERLAY_HARDWARE_DATA)) { + struct overlay_data_context_t *dev; + dev = (overlay_data_context_t*)malloc(sizeof(*dev)); + + if (!dev) + return status; + + /* initialize our state here */ + memset(dev, 0, sizeof(*dev)); + + /* initialize the procs */ + dev->device.common.tag = HARDWARE_DEVICE_TAG; + dev->device.common.version = 0; + dev->device.common.module = const_cast<hw_module_t*>(module); + dev->device.common.close = overlay_data_close; + + dev->device.initialize = overlay_initialize; + dev->device.setCrop = overlay_setCrop; + dev->device.dequeueBuffer = overlay_dequeueBuffer; + dev->device.queueBuffer = overlay_queueBuffer; + dev->device.setFd = overlay_setFd; + dev->device.getBufferAddress = overlay_getBufferAddress; + dev->device.getBufferCount = overlay_getBufferCount; + + *device = &dev->device.common; + status = 0; + } + return status; +} + +#include "overlayState.cpp" diff --git a/liboverlay/overlayLib.cpp b/liboverlay/overlayLib.cpp new file mode 100644 index 0000000..f0db551 --- /dev/null +++ b/liboverlay/overlayLib.cpp @@ -0,0 +1,1799 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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 "overlayLib.h" +#include "gralloc_priv.h" + +#define INTERLACE_MASK 0x80 +#define DEBUG_OVERLAY true +/* Helper functions */ +static inline size_t ALIGN(size_t x, size_t align) { + return (x + align-1) & ~(align-1); +} + +using namespace overlay; +using android::sp; +using gralloc::IMemAlloc; +using gralloc::IonController; +using gralloc::alloc_data; + +#ifdef HDMI_AS_PRIMARY +bool Overlay::sHDMIAsPrimary = true; +#else +bool Overlay::sHDMIAsPrimary = false; +#endif + +int overlay::get_mdp_format(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888 : + return MDP_RGBA_8888; + case HAL_PIXEL_FORMAT_BGRA_8888: + return MDP_BGRA_8888; + case HAL_PIXEL_FORMAT_RGB_565: + return MDP_RGB_565; + case HAL_PIXEL_FORMAT_RGBX_8888: + return MDP_RGBX_8888; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + return MDP_Y_CBCR_H2V1; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + return MDP_Y_CRCB_H2V2; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return MDP_Y_CBCR_H2V2; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: + return MDP_Y_CRCB_H2V2_TILE; + case HAL_PIXEL_FORMAT_YV12: + return MDP_Y_CR_CB_GH2V2; + default: + LOGE("%s: unknown color format [0x%x]", __FUNCTION__, format); + return -1; + } + return -1; +} + +int overlay::get_mdp_orientation(int value) { + switch(value) { + case 0: return 0; + case HAL_TRANSFORM_FLIP_V: return MDP_FLIP_UD; + case HAL_TRANSFORM_FLIP_H: return MDP_FLIP_LR; + case HAL_TRANSFORM_ROT_90: return MDP_ROT_90; + case HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V: + return MDP_ROT_90|MDP_FLIP_LR; + case HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H: + return MDP_ROT_90|MDP_FLIP_UD; + case HAL_TRANSFORM_ROT_180: return MDP_ROT_180; + case HAL_TRANSFORM_ROT_270: return MDP_ROT_270; + default: + LOGE("%s: invalid rotation value (value = 0x%x", + __FUNCTION__, value); + return -1; + } + return -1; +} + +// Rotator - input to output mapping +int overlay::get_rot_output_format(int format) { + switch (format) { + case MDP_Y_CRCB_H2V2_TILE: + return MDP_Y_CRCB_H2V2; + case MDP_Y_CB_CR_H2V2: + case MDP_Y_CR_CB_GH2V2: + return MDP_Y_CBCR_H2V2; + default: + return format; + } + return -1; +} + +// This function normalizes the crop values to be all even +void overlay::normalize_crop(uint32_t& xy, uint32_t& wh) { + + if (xy & 0x0001) { + // x or y is odd, increment it's value + xy += 1; + // Since we've incremented x(y), we need to decrement + // w(h) accordingly + if (wh & 0x0001) { + // w or h is odd, decrement it by 1, to make it even + EVEN_OUT(wh); + } else { + // w(h) is already even, hence we decrement by 2 + wh -=2; + } + } else { + EVEN_OUT(wh); + } +} + +#define LOG_TAG "OverlayLIB" +static void reportError(const char* message) { + LOGE( "%s", message); +} + +void overlay::dump(mdp_overlay& mOVInfo) { + if (!DEBUG_OVERLAY) + return; + LOGE("mOVInfo:"); + LOGE("src: width %d height %d format %s user_data[0] %d", mOVInfo.src.width, + mOVInfo.src.height, getFormatString(mOVInfo.src.format), + mOVInfo.user_data[0]); + LOGE("src_rect: x %d y %d w %d h %d", mOVInfo.src_rect.x, + mOVInfo.src_rect.y, mOVInfo.src_rect.w, mOVInfo.src_rect.h); + LOGE("dst_rect: x %d y %d w %d h %d", mOVInfo.dst_rect.x, + mOVInfo.dst_rect.y, mOVInfo.dst_rect.w, mOVInfo.dst_rect.h); + LOGE("z_order %d is_fg %d alpha %d transp_mask %d flags %x id %d", + mOVInfo.z_order, mOVInfo.is_fg, mOVInfo.alpha, mOVInfo.transp_mask, + mOVInfo.flags, mOVInfo.id); +} + +void overlay::dump(msm_rotator_img_info& mRotInfo) { + if (!DEBUG_OVERLAY) + return; + LOGE("mRotInfo:"); + LOGE("session_id %d dst_x %d dst_y %d rotations %d enable %d", + mRotInfo.session_id, mRotInfo.dst_x, mRotInfo.dst_y, + mRotInfo.rotations, mRotInfo.enable); + LOGE("src: width %d height %d format %s", mRotInfo.src.width, + mRotInfo.src.height, getFormatString(mRotInfo.src.format)); + LOGE("dst: width %d height %d format %s", mRotInfo.dst.width, + mRotInfo.dst.height, getFormatString(mRotInfo.src.format)); + LOGE("src_rect: x %d y %d w %d h %d", mRotInfo.src_rect.x, + mRotInfo.src_rect.y, mRotInfo.src_rect.w, mRotInfo.src_rect.h); +} + +const char* overlay::getFormatString(int format){ + static const char* formats[] = { + "MDP_RGB_565", + "MDP_XRGB_8888", + "MDP_Y_CBCR_H2V2", + "MDP_ARGB_8888", + "MDP_RGB_888", + "MDP_Y_CRCB_H2V2", + "MDP_YCRYCB_H2V1", + "MDP_Y_CRCB_H2V1", + "MDP_Y_CBCR_H2V1", + "MDP_RGBA_8888", + "MDP_BGRA_8888", + "MDP_RGBX_8888", + "MDP_Y_CRCB_H2V2_TILE", + "MDP_Y_CBCR_H2V2_TILE", + "MDP_Y_CR_CB_H2V2", + "MDP_Y_CR_CB_GH2V2", + "MDP_Y_CB_CR_H2V2", + "MDP_Y_CRCB_H1V1", + "MDP_Y_CBCR_H1V1", + "MDP_IMGTYPE_LIMIT", + "MDP_BGR_565", + "MDP_FB_FORMAT", + "MDP_IMGTYPE_LIMIT2" + }; + return formats[format]; +} + +bool overlay::isHDMIConnected () { + char value[PROPERTY_VALUE_MAX]; + property_get("hw.hdmiON", value, "0"); + int isHDMI = atoi(value); + return isHDMI ? true : false; +} + +bool overlay::is3DTV() { + char is3DTV = '0'; + FILE *fp = fopen(EDID_3D_INFO_FILE, "r"); + if (fp) { + fread(&is3DTV, 1, 1, fp); + fclose(fp); + } + LOGI("3DTV EDID flag: %c", is3DTV); + return (is3DTV == '0') ? false : true; +} + +bool overlay::isPanel3D() { + int fd = open("/dev/graphics/fb0", O_RDWR, 0); + if (fd < 0) { + reportError("Can't open framebuffer 0"); + return false; + } + fb_fix_screeninfo finfo; + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) { + reportError("FBIOGET_FSCREENINFO on fb0 failed"); + close(fd); + fd = -1; + return false; + } + close(fd); + return (FB_TYPE_3D_PANEL == finfo.type) ? true : false; +} + +bool overlay::usePanel3D() { + if (Overlay::sHDMIAsPrimary) + return is3DTV(); + + if(!isPanel3D()) + return false; + char value[PROPERTY_VALUE_MAX]; + property_get("persist.user.panel3D", value, "0"); + int usePanel3D = atoi(value); + return usePanel3D ? true : false; +} + +bool overlay::send3DInfoPacket (unsigned int format3D) { + FILE *fp = fopen(FORMAT_3D_FILE, "wb"); + if (fp) { + fprintf(fp, "%d", format3D); + fclose(fp); + fp = NULL; + return true; + } + LOGE("%s:no sysfs entry for setting 3d mode!", __FUNCTION__); + return false; +} + +bool overlay::enableBarrier (unsigned int orientation) { + FILE *fp = fopen(BARRIER_FILE, "wb"); + if (fp) { + fprintf(fp, "%d", orientation); + fclose(fp); + fp = NULL; + return true; + } + LOGE("%s:no sysfs entry for enabling barriers on 3D panel!", __FUNCTION__); + return false; +} + +int overlay::getColorFormat(int format) +{ + return (format == HAL_PIXEL_FORMAT_YV12) ? + format : COLOR_FORMAT(format); +} + +unsigned int overlay::getOverlayConfig (unsigned int format3D, bool poll, + bool isHDMI) { + bool isTV3D = false; + unsigned int curState = 0; + if (poll) + isHDMI = isHDMIConnected(); + if (isHDMI) { + LOGD("%s: HDMI connected... checking the TV type", __FUNCTION__); + if (format3D) { + if (is3DTV()) + curState = OV_3D_VIDEO_3D_TV; + else + curState = OV_3D_VIDEO_2D_TV; + } else + curState = OV_2D_VIDEO_ON_TV; + } else { + LOGD("%s: HDMI not connected...", __FUNCTION__); + if(format3D) { + if (usePanel3D()) + curState = OV_3D_VIDEO_3D_PANEL; + else + curState = OV_3D_VIDEO_2D_PANEL; + } + else + curState = OV_2D_VIDEO_ON_PANEL; + } + return curState; +} + +Overlay::Overlay() : mChannelUP(false), mHDMIConnected(false), + mS3DFormat(0), mCroppedSrcWidth(0), + mCroppedSrcHeight(0), mState(-1) { + mOVBufferInfo.width = mOVBufferInfo.height = 0; + mOVBufferInfo.format = mOVBufferInfo.size = 0; +} + +Overlay::~Overlay() { + closeChannel(); +} + +int Overlay::getFBWidth(int channel) const { + return objOvCtrlChannel[channel].getFBWidth(); +} + +int Overlay::getFBHeight(int channel) const { + return objOvCtrlChannel[channel].getFBHeight(); +} + +bool Overlay::startChannel(const overlay_buffer_info& info, int fbnum, + bool norot, bool uichannel, + unsigned int format3D, int channel, + bool ignoreFB, int num_buffers) { + int zorder = 0; + int format = getColorFormat(info.format); + mCroppedSrcWidth = info.width; + mCroppedSrcHeight = info.height; + if (format3D) + zorder = channel; + if (mState == -1) + mState = OV_UI_MIRROR_TV; + + mChannelUP = objOvCtrlChannel[channel].startControlChannel(info.width, + info.height, format, fbnum, + norot, uichannel, + format3D, zorder, ignoreFB); + if (!mChannelUP) { + LOGE("startChannel for fb%d failed", fbnum); + return mChannelUP; + } + objOvCtrlChannel[channel].setSize(info.size); + return objOvDataChannel[channel].startDataChannel(objOvCtrlChannel[channel], fbnum, + norot, uichannel, num_buffers); +} + +bool Overlay::closeChannel() { + + if (!mChannelUP) + return true; + + if(mS3DFormat) { + if (mHDMIConnected) + overlay::send3DInfoPacket(0); + else if (mState == OV_3D_VIDEO_3D_PANEL) { + if (sHDMIAsPrimary) + overlay::send3DInfoPacket(0); + else + enableBarrier(0); + } + } + for (int i = 0; i < NUM_CHANNELS; i++) { + objOvCtrlChannel[i].closeControlChannel(); + objOvDataChannel[i].closeDataChannel(); + } + mChannelUP = false; + mS3DFormat = 0; + mOVBufferInfo.width = 0; + mOVBufferInfo.height = 0; + mOVBufferInfo.format = 0; + mOVBufferInfo.size = 0; + mState = -1; + return true; +} + +bool Overlay::getPosition(int& x, int& y, uint32_t& w, uint32_t& h, int channel) { + return objOvCtrlChannel[channel].getPosition(x, y, w, h); +} + +bool Overlay::getOrientation(int& orientation, int channel) const { + return objOvCtrlChannel[channel].getOrientation(orientation); +} + +bool Overlay::setPosition(int x, int y, uint32_t w, uint32_t h) { + bool ret = false; + overlay_rect rect; + switch (mState) { + case OV_UI_MIRROR_TV: + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + return setChannelPosition(x, y, w, h, VG0_PIPE); + break; + case OV_2D_VIDEO_ON_TV: + objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(mCroppedSrcWidth, + mCroppedSrcHeight, &rect); + setChannelPosition(rect.x, rect.y, rect.w, rect.h, VG1_PIPE); + return setChannelPosition(x, y, w, h, VG0_PIPE); + break; + case OV_3D_VIDEO_3D_PANEL: + for (int i = 0; i < NUM_CHANNELS; i++) { + if (sHDMIAsPrimary) + objOvCtrlChannel[i].getPositionS3D(i, mS3DFormat, &rect); + else { + if (!objOvCtrlChannel[i].useVirtualFB()) { + LOGE("%s: failed virtual fb for channel %d", __FUNCTION__, i); + return false; + } + objOvCtrlChannel[i].getPositionS3D(i, 0x1, &rect); + } + if(!setChannelPosition(rect.x, rect.y, rect.w, rect.h, i)) { + LOGE("%s: failed for channel %d", __FUNCTION__, i); + return false; + } + } + break; + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i = 0; i < NUM_CHANNELS; i++) { + ret = objOvCtrlChannel[i].getPositionS3D(i, mS3DFormat, &rect); + if (!ret) + ret = setChannelPosition(x, y, w, h, i); + else + ret = setChannelPosition(rect.x, rect.y, rect.w, rect.h, i); + if (!ret) { + LOGE("%s: failed for channel %d", __FUNCTION__, i); + return ret; + } + } + break; + default: + LOGE("%s:Unknown state %d", __FUNCTION__, mState); + break; + } + return true; +} + +bool Overlay::setChannelPosition(int x, int y, uint32_t w, uint32_t h, int channel) { + return objOvCtrlChannel[channel].setPosition(x, y, w, h); +} + +bool Overlay::updateOverlaySource(const overlay_buffer_info& info, int orientation, + bool waitForVsync) { + bool ret = false; + int currentFlags = 0; + if (objOvCtrlChannel[0].isChannelUP()) { + currentFlags = objOvCtrlChannel[0].getOverlayFlags(); + } + + bool needUpdateFlags = false; + if (waitForVsync) { + if (currentFlags & MDP_OV_PLAY_NOWAIT) { + needUpdateFlags = true; + } + } else { + if (!(currentFlags & MDP_OV_PLAY_NOWAIT)) { + needUpdateFlags = true; + } + } + + bool geometryChanged = true; + if (info.width == mOVBufferInfo.width && + info.height == mOVBufferInfo.height && + info.format == mOVBufferInfo.format) { + geometryChanged = false; + } + + if (sHDMIAsPrimary) + needUpdateFlags = false; + + if ((false == needUpdateFlags) && (false == geometryChanged)) { + objOvDataChannel[0].updateDataChannel(0, 0); + return true; + } + + // Disable rotation for the HDMI channels + int orientHdmi = 0; + int orientPrimary = sHDMIAsPrimary ? 0 : orientation; + int orient[2] = {orientPrimary, orientHdmi}; + // enable waitForVsync on HDMI + bool waitForHDMI = true; + bool waitForPrimary = sHDMIAsPrimary ? true : waitForVsync; + bool waitCond[2] = {waitForPrimary, waitForHDMI}; + + switch(mState) { + case OV_3D_VIDEO_3D_PANEL: + orient[1] = sHDMIAsPrimary ? 0 : orientation; + break; + case OV_3D_VIDEO_3D_TV: + orient[0] = 0; + break; + default: + break; + } + + int numChannelsToUpdate = NUM_CHANNELS; + if (!geometryChanged) { + // Only update the primary channel - we only need to update the + // wait/no-wait flags + if (objOvCtrlChannel[0].isChannelUP()) { + return objOvCtrlChannel[0].updateWaitForVsyncFlags(waitForVsync); + } + } + + // Set the overlay source info + for (int i = 0; i < NUM_CHANNELS; i++) { + if (objOvCtrlChannel[i].isChannelUP()) { + ret = objOvCtrlChannel[i].updateOverlaySource(info, orient[i], waitCond[i]); + if (!ret) { + LOGE("objOvCtrlChannel[%d].updateOverlaySource failed", i); + return false; + } + objOvCtrlChannel[i].setSize(info.size); + int updateDataChannel = orientation ? 1:0; + ret = objOvDataChannel[i].updateDataChannel(updateDataChannel, info.size); + } + } + if (ret) { + mOVBufferInfo = info; + } else + LOGE("update failed"); + return ret; +} + +int Overlay::getS3DFormat(int format) { + // The S3D is part of the HAL_PIXEL_FORMAT_YV12 value. Add + // an explicit check for the format + if (format == HAL_PIXEL_FORMAT_YV12) { + return 0; + } + int format3D = FORMAT_3D(format); + int fIn3D = FORMAT_3D_INPUT(format3D); // MSB 2 bytes are input format + int fOut3D = FORMAT_3D_OUTPUT(format3D); // LSB 2 bytes are output format + format3D = fIn3D | fOut3D; + if (!fIn3D) { + format3D |= fOut3D << SHIFT_3D; //Set the input format + } + if (!fOut3D) { + format3D |= fIn3D >> SHIFT_3D; //Set the output format + } + return format3D; +} + +bool Overlay::setSource(const overlay_buffer_info& info, int orientation, + bool hdmiConnected, bool waitForVsync, int num_buffers) { + // Separate the color format from the 3D format. + // If there is 3D content; the effective format passed by the client is: + // effectiveFormat = 3D_IN | 3D_OUT | ColorFormat + int newState = mState; + bool stateChange = false, ret = false; + unsigned int format3D = getS3DFormat(info.format); + int colorFormat = getColorFormat(info.format); + if (-1 == mState) { + newState = getOverlayConfig (format3D, false, hdmiConnected); + stateChange = (mState == newState) ? false : true; + } + + if (stateChange) { + closeChannel(); + mHDMIConnected = hdmiConnected; + mState = newState; + mS3DFormat = format3D; + if (mState == OV_3D_VIDEO_2D_PANEL || mState == OV_3D_VIDEO_2D_TV) { + LOGI("3D content on 2D display: set the output format as monoscopic"); + mS3DFormat = FORMAT_3D_INPUT(format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + } + // We always enable the rotator for the primary. + bool noRot = false; + bool uiChannel = false; + switch(mState) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + return startChannel(info, FRAMEBUFFER_0, noRot, false, + mS3DFormat, VG0_PIPE, waitForVsync, num_buffers); + break; + case OV_3D_VIDEO_3D_PANEL: + if (sHDMIAsPrimary) { + noRot = true; + waitForVsync = true; + send3DInfoPacket(mS3DFormat & OUTPUT_MASK_3D); + } + for (int i=0; i<NUM_CHANNELS; i++) { + if(!startChannel(info, FRAMEBUFFER_0, noRot, uiChannel, + mS3DFormat, i, waitForVsync, num_buffers)) { + LOGE("%s:failed to open channel %d", __FUNCTION__, i); + return false; + } + } + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if (FRAMEBUFFER_1 == i) { + // Disable rotation for HDMI + noRot = true; + waitForVsync = true; + } + if(!startChannel(info, i, noRot, false, mS3DFormat, + i, waitForVsync, num_buffers)) { + LOGE("%s:failed to open channel %d", __FUNCTION__, i); + return false; + } + } + overlay_rect rect; + objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(info.width, info.height, &rect); + return setChannelPosition(rect.x, rect.y, rect.w, rect.h, VG1_PIPE); + break; + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!startChannel(info, FRAMEBUFFER_1, true, false, + mS3DFormat, i, waitForVsync, num_buffers)) { + LOGE("%s:failed to open channel %d", __FUNCTION__, i); + return false; + } + send3DInfoPacket(mS3DFormat & OUTPUT_MASK_3D); + } + break; + default: + LOGE("%s:Unknown state %d", __FUNCTION__, mState); + break; + } + } else { + ret = updateOverlaySource(info, orientation, waitForVsync); + } + return true; +} + +bool Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) { + if (!mChannelUP) { + LOGE("%s: channel not set", __FUNCTION__); + return false; + } + overlay_rect rect, inRect; + inRect.x = x; inRect.y = y; inRect.w = w; inRect.h = h; + mCroppedSrcWidth = w; + mCroppedSrcHeight = h; + + switch (mState) { + case OV_UI_MIRROR_TV: + case OV_2D_VIDEO_ON_PANEL: + return setChannelCrop(x, y, w, h, VG0_PIPE); + break; + case OV_3D_VIDEO_2D_PANEL: + objOvDataChannel[VG0_PIPE].getCropS3D(&inRect, VG0_PIPE, mS3DFormat, &rect); + return setChannelCrop(rect.x, rect.y, rect.w, rect.h, VG0_PIPE); + break; + case OV_2D_VIDEO_ON_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!setChannelCrop(x, y, w, h, i)) { + LOGE("%s: failed for pipe %d", __FUNCTION__, i); + return false; + } + } + break; + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + objOvDataChannel[i].getCropS3D(&inRect, i, mS3DFormat, &rect); + if(!setChannelCrop(rect.x, rect.y, rect.w, rect.h, i)) { + LOGE("%s: failed for pipe %d", __FUNCTION__, i); + return false; + } + } + break; + default: + LOGE("%s:Unknown state %d", __FUNCTION__, mState); + break; + } + return true; +} + +bool Overlay::setChannelCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h, int channel) { + return objOvDataChannel[channel].setCrop(x, y, w, h); +} + +bool Overlay::setParameter(int param, int value) { + int currentOrientation = -1; + getOrientation(currentOrientation); + + switch (mState) { + case OV_UI_MIRROR_TV: + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + return objOvCtrlChannel[VG0_PIPE].setParameter(param, value); + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!objOvCtrlChannel[i].setParameter(param, value)) { + LOGE("%s:failed for channel %d", __FUNCTION__, i); + return false; + } + } + break; + case OV_3D_VIDEO_3D_PANEL: + if (!sHDMIAsPrimary && param == OVERLAY_TRANSFORM) { + int barrier = 0; + switch (value) { + case HAL_TRANSFORM_ROT_90: + case HAL_TRANSFORM_ROT_270: + barrier = BARRIER_LANDSCAPE; + break; + default: + barrier = BARRIER_PORTRAIT; + break; + } + if(!enableBarrier(barrier)) + LOGE("%s:failed to enable barriers for 3D video", __FUNCTION__); + } + for (int i=0; i<NUM_CHANNELS; i++) { + if(!objOvCtrlChannel[i].setParameter(param, value)) { + LOGE("%s:failed for channel %d", __FUNCTION__, i); + return false; + } + } + break; + default: + LOGE("%s:Unknown state %d", __FUNCTION__, mState); + break; + } + return true; +} + +bool Overlay::setOrientation(int value, int channel) { + return objOvCtrlChannel[channel].setParameter(OVERLAY_TRANSFORM, value); +} + +bool Overlay::setFd(int fd, int channel) { + return objOvDataChannel[channel].setFd(fd); +} + +bool Overlay::queueBuffer(uint32_t offset, int channel) { + return objOvDataChannel[channel].queueBuffer(offset); +} + +bool Overlay::queueBuffer(buffer_handle_t buffer) { + private_handle_t const* hnd = reinterpret_cast + <private_handle_t const*>(buffer); + if (!hnd) { + LOGE("Overlay::queueBuffer invalid handle"); + return false; + } + const size_t offset = hnd->offset; + const int fd = hnd->fd; + switch (mState) { + case OV_UI_MIRROR_TV: + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + if(!queueBuffer(fd, offset, VG0_PIPE)) { + LOGE("%s:failed for channel 0", __FUNCTION__); + return false; + } + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!queueBuffer(fd, offset, i)) { + LOGE("%s:failed for channel %d", __FUNCTION__, i); + return false; + } + } + break; + default: + LOGE("%s:Unknown state %d", __FUNCTION__, mState); + break; + } + return true; +} + +bool Overlay::queueBuffer(int fd, uint32_t offset, int channel) { + bool ret = false; + ret = setFd(fd, channel); + if(!ret) { + LOGE("Overlay::queueBuffer channel %d setFd failed", channel); + return false; + } + ret = queueBuffer(offset, channel); + if(!ret) { + LOGE("Overlay::queueBuffer channel %d queueBuffer failed", channel); + return false; + } + return ret; +} + +OverlayControlChannel::OverlayControlChannel() : mNoRot(false), mFD(-1), mRotFD(-1), + mFormat3D(0), mIsChannelUpdated(true) { + memset(&mOVInfo, 0, sizeof(mOVInfo)); + memset(&m3DOVInfo, 0, sizeof(m3DOVInfo)); + memset(&mRotInfo, 0, sizeof(mRotInfo)); +} + + +OverlayControlChannel::~OverlayControlChannel() { + closeControlChannel(); +} + +bool OverlayControlChannel::getAspectRatioPosition(int w, int h, overlay_rect *rect) +{ + int width = w, height = h, x, y; + int fbWidth = getFBWidth(); + int fbHeight = getFBHeight(); + // width and height for YUV TILE format + int tempWidth = w, tempHeight = h; + /* Calculate the width and height if it is YUV TILE format*/ + if(getFormat() == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED) { + tempWidth = w - ( (((w-1)/64 +1)*64) - w); + tempHeight = h - ((((h-1)/32 +1)*32) - h); + } + if (width * fbHeight > fbWidth * height) { + height = fbWidth * height / width; + EVEN_OUT(height); + width = fbWidth; + } else if (width * fbHeight < fbWidth * height) { + width = fbHeight * width / height; + EVEN_OUT(width); + height = fbHeight; + } else { + width = fbWidth; + height = fbHeight; + } + /* Scaling of upto a max of 8 times supported */ + if(width >(tempWidth * HW_OVERLAY_MAGNIFICATION_LIMIT)){ + width = HW_OVERLAY_MAGNIFICATION_LIMIT * tempWidth; + } + if(height >(tempHeight*HW_OVERLAY_MAGNIFICATION_LIMIT)) { + height = HW_OVERLAY_MAGNIFICATION_LIMIT * tempHeight; + } + if (width > fbWidth) width = fbWidth; + if (height > fbHeight) height = fbHeight; + x = (fbWidth - width) / 2; + y = (fbHeight - height) / 2; + rect->x = x; + rect->y = y; + rect->w = width; + rect->h = height; + return true; +} + +bool OverlayControlChannel::getPositionS3D(int channel, int format, overlay_rect *rect) { + int wDisp = getFBWidth(); + int hDisp = getFBHeight(); + switch (format & OUTPUT_MASK_3D) { + case HAL_3D_OUT_SIDE_BY_SIDE_MASK: + if (channel == VG0_PIPE) { + rect->x = 0; + rect->y = 0; + rect->w = wDisp/2; + rect->h = hDisp; + } else { + rect->x = wDisp/2; + rect->y = 0; + rect->w = wDisp/2; + rect->h = hDisp; + } + break; + case HAL_3D_OUT_TOP_BOTTOM_MASK: + if (channel == VG0_PIPE) { + rect->x = 0; + rect->y = 0; + rect->w = wDisp; + rect->h = hDisp/2; + } else { + rect->x = 0; + rect->y = hDisp/2; + rect->w = wDisp; + rect->h = hDisp/2; + } + break; + case HAL_3D_OUT_MONOSCOPIC_MASK: + if (channel == VG1_PIPE) { + rect->x = 0; + rect->y = 0; + rect->w = wDisp; + rect->h = hDisp; + } + else + return false; + break; + case HAL_3D_OUT_INTERLEAVE_MASK: + break; + default: + reportError("Unsupported 3D output format"); + break; + } + return true; +} + +bool OverlayControlChannel::openDevices(int fbnum) { + if (fbnum < 0) + return false; + + char const * const device_template = + "/dev/graphics/fb%u"; + char dev_name[64]; + snprintf(dev_name, 64, device_template, fbnum); + + mFD = open(dev_name, O_RDWR, 0); + if (mFD < 0) { + reportError("Cant open framebuffer "); + return false; + } + + fb_fix_screeninfo finfo; + if (ioctl(mFD, FBIOGET_FSCREENINFO, &finfo) == -1) { + reportError("FBIOGET_FSCREENINFO on fb1 failed"); + close(mFD); + mFD = -1; + return false; + } + + fb_var_screeninfo vinfo; + if (ioctl(mFD, FBIOGET_VSCREENINFO, &vinfo) == -1) { + reportError("FBIOGET_VSCREENINFO on fb1 failed"); + close(mFD); + mFD = -1; + return false; + } + mFBWidth = vinfo.xres; + mFBHeight = vinfo.yres; + mFBbpp = vinfo.bits_per_pixel; + mFBystride = finfo.line_length; + + if (!mNoRot) { + mRotFD = open("/dev/msm_rotator", O_RDWR, 0); + if (mRotFD < 0) { + reportError("Cant open rotator device"); + close(mFD); + mFD = -1; + return false; + } + } + + return true; +} + +bool OverlayControlChannel::setOverlayInformation(const overlay_buffer_info& info, + int flags, int orientation, int zorder, + bool ignoreFB, int requestType) { + int w = info.width; + int h = info.height; + int format = info.format; + + mOVInfo.src.width = w; + mOVInfo.src.height = h; + mOVInfo.src_rect.x = 0; + mOVInfo.src_rect.y = 0; + mOVInfo.dst_rect.x = 0; + mOVInfo.dst_rect.y = 0; + mOVInfo.dst_rect.w = w; + mOVInfo.dst_rect.h = h; + if(format == MDP_Y_CRCB_H2V2_TILE) { + if (!orientation) { + mOVInfo.src_rect.w = w - ((((w-1)/64 +1)*64) - w); + mOVInfo.src_rect.h = h - ((((h-1)/32 +1)*32) - h); + } else { + mOVInfo.src_rect.w = w; + mOVInfo.src_rect.h = h; + mOVInfo.src.width = (((w-1)/64 +1)*64); + mOVInfo.src.height = (((h-1)/32 +1)*32); + mOVInfo.src_rect.x = mOVInfo.src.width - w; + mOVInfo.src_rect.y = mOVInfo.src.height - h; + } + } else { + mOVInfo.src_rect.w = w; + mOVInfo.src_rect.h = h; + } + + mOVInfo.src.format = format; + if (w > mFBWidth) + mOVInfo.dst_rect.w = mFBWidth; + if (h > mFBHeight) + mOVInfo.dst_rect.h = mFBHeight; + + mOVInfo.user_data[0] = 0; + if (requestType == NEW_REQUEST) { + mOVInfo.id = MSMFB_NEW_REQUEST; + mOVInfo.z_order = zorder; + mOVInfo.alpha = 0xff; + mOVInfo.transp_mask = 0xffffffff; + mOVInfo.flags = flags; + } + if (!ignoreFB) + mOVInfo.flags |= MDP_OV_PLAY_NOWAIT; + else + mOVInfo.flags &= ~MDP_OV_PLAY_NOWAIT; + + return true; +} + +bool OverlayControlChannel::startOVRotatorSessions( + const overlay_buffer_info& info, + int orientation, int requestType) { + bool ret = true; + int w = info.width; + int h = info.height; + int format = info.format; + + if (orientation) { + mRotInfo.src.format = format; + mRotInfo.src.width = w; + mRotInfo.src.height = h; + mRotInfo.src_rect.w = w; + mRotInfo.src_rect.h = h; + mRotInfo.dst.width = w; + mRotInfo.dst.height = h; + if(format == MDP_Y_CRCB_H2V2_TILE) { + mRotInfo.src.width = (((w-1)/64 +1)*64); + mRotInfo.src.height = (((h-1)/32 +1)*32); + mRotInfo.src_rect.w = (((w-1)/64 +1)*64); + mRotInfo.src_rect.h = (((h-1)/32 +1)*32); + mRotInfo.dst.width = (((w-1)/64 +1)*64); + mRotInfo.dst.height = (((h-1)/32 +1)*32); + mRotInfo.dst.format = MDP_Y_CRCB_H2V2; + } + mRotInfo.dst.format = get_rot_output_format(format); + mRotInfo.dst_x = 0; + mRotInfo.dst_y = 0; + mRotInfo.src_rect.x = 0; + mRotInfo.src_rect.y = 0; + mRotInfo.rotations = 0; + + if (requestType == NEW_REQUEST) { + mRotInfo.enable = 0; + if(mUIChannel) + mRotInfo.enable = 1; + mRotInfo.session_id = 0; + } else + mRotInfo.enable = 1; + + int result = ioctl(mRotFD, MSM_ROTATOR_IOCTL_START, &mRotInfo); + if (result) { + reportError("Rotator session failed"); + dump(mRotInfo); + ret = false; + } + } + + if (ret && ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) { + reportError("startOVRotatorSessions, Overlay set failed"); + dump(mOVInfo); + ret = false; + } + + if (!ret) + closeControlChannel(); + else + mIsChannelUpdated = true; + return ret; +} + +bool OverlayControlChannel::updateOverlaySource(const overlay_buffer_info& info, + int orientation, bool waitForVsync) +{ + int colorFormat = getColorFormat(info.format); + int hw_format = get_mdp_format(colorFormat); + overlay_buffer_info ovBufInfo; + ovBufInfo.width = info.width; + ovBufInfo.height = info.height; + ovBufInfo.format = hw_format; + + if (!setOverlayInformation(ovBufInfo, 0, orientation, 0, waitForVsync, UPDATE_REQUEST)) + return false; + + return startOVRotatorSessions(ovBufInfo, orientation, UPDATE_REQUEST); +} + +bool OverlayControlChannel::startControlChannel(int w, int h, + int format, int fbnum, bool norot, + bool uichannel, + unsigned int format3D, int zorder, + bool ignoreFB) { + mNoRot = norot; + mFormat = format; + mUIChannel = uichannel; + fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo; + int hw_format; + int flags = 0; + int colorFormat = format; + // The interlace mask is part of the HAL_PIXEL_FORMAT_YV12 value. Add + // an explicit check for the format + if ((format != HAL_PIXEL_FORMAT_YV12) && (format & INTERLACE_MASK)) { + flags |= MDP_DEINTERLACE; + + // Get the actual format + colorFormat = format ^ HAL_PIXEL_FORMAT_INTERLACE; + } + hw_format = get_mdp_format(colorFormat); + if (hw_format < 0) { + reportError("Unsupported format"); + return false; + } + + mFormat3D = format3D; + if ( !mFormat3D || (mFormat3D & HAL_3D_OUT_MONOSCOPIC_MASK) ) { + // Set the share bit for sharing the VG pipe + flags |= MDP_OV_PIPE_SHARE; + } + if (!openDevices(fbnum)) + return false; + + int orientation = mNoRot ? 0: 1; + overlay_buffer_info ovBufInfo; + ovBufInfo.width = w; + ovBufInfo.height = h; + ovBufInfo.format = hw_format; + if (!setOverlayInformation(ovBufInfo, flags, orientation, zorder, ignoreFB, NEW_REQUEST)) + return false; + + return startOVRotatorSessions(ovBufInfo, orientation, NEW_REQUEST); +} + +bool OverlayControlChannel::closeControlChannel() { + if (!isChannelUP()) + return true; + + if (!mNoRot && mRotFD > 0) { + ioctl(mRotFD, MSM_ROTATOR_IOCTL_FINISH, &(mRotInfo.session_id)); + close(mRotFD); + mRotFD = -1; + } + + int ovid = mOVInfo.id; + ioctl(mFD, MSMFB_OVERLAY_UNSET, &ovid); + if (m3DOVInfo.is_3d) { + m3DOVInfo.is_3d = 0; + ioctl(mFD, MSMFB_OVERLAY_3D, &m3DOVInfo); + } + + close(mFD); + memset(&mOVInfo, 0, sizeof(mOVInfo)); + memset(&mRotInfo, 0, sizeof(mRotInfo)); + memset(&m3DOVInfo, 0, sizeof(m3DOVInfo)); + mFD = -1; + + return true; +} + +bool OverlayControlChannel::updateWaitForVsyncFlags(bool waitForVsync) { + if (!waitForVsync) + mOVInfo.flags |= MDP_OV_PLAY_NOWAIT; + else + mOVInfo.flags &= ~MDP_OV_PLAY_NOWAIT; + + if (ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) { + LOGE("%s: OVERLAY_SET failed", __FUNCTION__); + dump(mOVInfo); + return false; + } + return true; +} + +bool OverlayControlChannel::setPosition(int x, int y, uint32_t w, uint32_t h) { + + if (!isChannelUP() || + (x < 0) || (y < 0) || ((x + w) > mFBWidth) || + ((y + h) > mFBHeight)) { + reportError("setPosition failed"); + LOGW("x %d y %d (x+w) %d (y+h) %d FBWidth %d FBHeight %d", x, y, x+w, y+h, + mFBWidth,mFBHeight); + return false; + } + if( x != mOVInfo.dst_rect.x || y != mOVInfo.dst_rect.y || + w != mOVInfo.dst_rect.w || h != mOVInfo.dst_rect.h ) { + mdp_overlay ov; + ov.id = mOVInfo.id; + if (ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) { + reportError("setPosition, overlay GET failed"); + return false; + } + + /* Scaling of upto a max of 8 times supported */ + if(w >(ov.src_rect.w * HW_OVERLAY_MAGNIFICATION_LIMIT)){ + w = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.w; + x = (mFBWidth - w) / 2; + } + if(h >(ov.src_rect.h * HW_OVERLAY_MAGNIFICATION_LIMIT)) { + h = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.h; + y = (mFBHeight - h) / 2; + } + ov.dst_rect.x = x; + ov.dst_rect.y = y; + ov.dst_rect.w = w; + ov.dst_rect.h = h; + if (ioctl(mFD, MSMFB_OVERLAY_SET, &ov)) { + reportError("setPosition, Overlay SET failed"); + dump(ov); + return false; + } + mOVInfo = ov; + } + return true; +} + +void OverlayControlChannel::swapOVRotWidthHeight() { + int tmp = mOVInfo.src.width; + mOVInfo.src.width = mOVInfo.src.height; + mOVInfo.src.height = tmp; + + tmp = mOVInfo.src_rect.h; + mOVInfo.src_rect.h = mOVInfo.src_rect.w; + mOVInfo.src_rect.w = tmp; + + tmp = mRotInfo.dst.width; + mRotInfo.dst.width = mRotInfo.dst.height; + mRotInfo.dst.height = tmp; +} + +bool OverlayControlChannel::useVirtualFB() { + if(!m3DOVInfo.is_3d) { + m3DOVInfo.is_3d = 1; + mFBWidth *= 2; + mFBHeight /= 2; + m3DOVInfo.width = mFBWidth; + m3DOVInfo.height = mFBHeight; + return ioctl(mFD, MSMFB_OVERLAY_3D, &m3DOVInfo) ? false : true; + } + return true; +} + +bool OverlayControlChannel::setParameter(int param, int value, bool fetch) { + if (!isChannelUP()) { + LOGE("%s: channel is not up", __FUNCTION__); + return false; + } + + mdp_overlay ov = mOVInfo; + if (fetch && ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) { + reportError("setParameter, overlay GET failed"); + return false; + } + mOVInfo = ov; + if (!mIsChannelUpdated && (OVERLAY_TRANSFORM == param)) { + int orientation = get_mdp_orientation(value); + if (orientation == mOVInfo.user_data[0]) { + return true; + } + } + mIsChannelUpdated = false; + + switch (param) { + case OVERLAY_DITHER: + break; + case OVERLAY_TRANSFORM: + { + int val = mOVInfo.user_data[0]; + if (mNoRot) + return true; + + int rot = value; + + switch(rot) { + case 0: + case HAL_TRANSFORM_FLIP_H: + case HAL_TRANSFORM_FLIP_V: + { + if (val == MDP_ROT_90) { + int tmp = mOVInfo.src_rect.y; + mOVInfo.src_rect.y = mOVInfo.src.width - + (mOVInfo.src_rect.x + mOVInfo.src_rect.w); + mOVInfo.src_rect.x = tmp; + swapOVRotWidthHeight(); + } + else if (val == MDP_ROT_270) { + int tmp = mOVInfo.src_rect.x; + mOVInfo.src_rect.x = mOVInfo.src.height - ( + mOVInfo.src_rect.y + mOVInfo.src_rect.h); + mOVInfo.src_rect.y = tmp; + swapOVRotWidthHeight(); + } + break; + } + case HAL_TRANSFORM_ROT_90: + case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H): + case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V): + { + if (val == MDP_ROT_270) { + mOVInfo.src_rect.x = mOVInfo.src.width - ( + mOVInfo.src_rect.x + mOVInfo.src_rect.w); + mOVInfo.src_rect.y = mOVInfo.src.height - ( + mOVInfo.src_rect.y + mOVInfo.src_rect.h); + } + else if (val == MDP_ROT_NOP || val == MDP_ROT_180) { + int tmp = mOVInfo.src_rect.x; + mOVInfo.src_rect.x = mOVInfo.src.height - + (mOVInfo.src_rect.y + mOVInfo.src_rect.h); + mOVInfo.src_rect.y = tmp; + swapOVRotWidthHeight(); + } + break; + } + case HAL_TRANSFORM_ROT_180: + { + if (val == MDP_ROT_270) { + int tmp = mOVInfo.src_rect.y; + mOVInfo.src_rect.y = mOVInfo.src.width - + (mOVInfo.src_rect.x + mOVInfo.src_rect.w); + mOVInfo.src_rect.x = tmp; + swapOVRotWidthHeight(); + } + else if (val == MDP_ROT_90) { + int tmp = mOVInfo.src_rect.x; + mOVInfo.src_rect.x = mOVInfo.src.height - ( + mOVInfo.src_rect.y + mOVInfo.src_rect.h); + mOVInfo.src_rect.y = tmp; + swapOVRotWidthHeight(); + } + break; + } + case HAL_TRANSFORM_ROT_270: + { + if (val == MDP_ROT_90) { + mOVInfo.src_rect.y = mOVInfo.src.height - + (mOVInfo.src_rect.y + mOVInfo.src_rect.h); + mOVInfo.src_rect.x = mOVInfo.src.width - + (mOVInfo.src_rect.x + mOVInfo.src_rect.w); + } + else if (val == MDP_ROT_NOP || val == MDP_ROT_180) { + int tmp = mOVInfo.src_rect.y; + mOVInfo.src_rect.y = mOVInfo.src.width - ( + mOVInfo.src_rect.x + mOVInfo.src_rect.w); + mOVInfo.src_rect.x = tmp; + swapOVRotWidthHeight(); + } + break; + } + default: return false; + } + int mdp_rotation = get_mdp_orientation(rot); + if (mdp_rotation == -1) + return false; + + mOVInfo.user_data[0] = mdp_rotation; + mRotInfo.rotations = mOVInfo.user_data[0]; + + /* Rotator always outputs non-tiled formats. + If rotator is used, set Overlay input to non-tiled + Else, overlay input remains tiled */ + + if (mOVInfo.user_data[0]) { + if (mRotInfo.src.format == MDP_Y_CRCB_H2V2_TILE) + mOVInfo.src.format = MDP_Y_CRCB_H2V2; + mRotInfo.enable = 1; + } + else { + if(mRotInfo.src.format == MDP_Y_CRCB_H2V2_TILE) + mOVInfo.src.format = MDP_Y_CRCB_H2V2_TILE; + mRotInfo.enable = 0; + if(mUIChannel) + mRotInfo.enable = 1; + } + if (ioctl(mRotFD, MSM_ROTATOR_IOCTL_START, &mRotInfo)) { + reportError("setParameter, rotator start failed"); + return false; + } + + if ((mOVInfo.user_data[0] == MDP_ROT_90) || + (mOVInfo.user_data[0] == MDP_ROT_270)) + mOVInfo.flags |= MDP_SOURCE_ROTATED_90; + else + mOVInfo.flags &= ~MDP_SOURCE_ROTATED_90; + + if (ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) { + reportError("setParameter, overlay set failed"); + dump(mOVInfo); + return false; + } + break; + } + default: + reportError("Unsupproted param"); + return false; + } + + return true; +} + +bool OverlayControlChannel::getPosition(int& x, int& y, + uint32_t& w, uint32_t& h) { + if (!isChannelUP()) + return false; + //mOVInfo has the current Overlay Position + x = mOVInfo.dst_rect.x; + y = mOVInfo.dst_rect.y; + w = mOVInfo.dst_rect.w; + h = mOVInfo.dst_rect.h; + + return true; +} + +bool OverlayControlChannel::getOrientation(int& orientation) const { + if (!isChannelUP()) + return false; + // mOVInfo has the current orientation + orientation = mOVInfo.user_data[0]; + return true; +} +bool OverlayControlChannel::getOvSessionID(int& sessionID) const { + if (!isChannelUP()) + return false; + sessionID = mOVInfo.id; + return true; +} + +bool OverlayControlChannel::getRotSessionID(int& sessionID) const { + if (!isChannelUP()) + return false; + sessionID = mRotInfo.session_id; + return true; +} + +bool OverlayControlChannel::getSize(int& size) const { + if (!isChannelUP()) + return false; + size = mSize; + return true; +} + +OverlayDataChannel::OverlayDataChannel() : mNoRot(false), mFD(-1), mRotFD(-1), + mPmemFD(-1), mPmemAddr(0), mUpdateDataChannel(0) +{ +#ifdef USE_ION + mAlloc = gralloc::IAllocController::getInstance(); +#endif +} + +OverlayDataChannel::~OverlayDataChannel() { + closeDataChannel(); +} + +bool OverlayDataChannel::startDataChannel( + const OverlayControlChannel& objOvCtrlChannel, + int fbnum, bool norot, bool uichannel, int num_buffers) { + int ovid, rotid, size; + mNoRot = norot; + memset(&mOvData, 0, sizeof(mOvData)); + memset(&mOvDataRot, 0, sizeof(mOvDataRot)); + memset(&mRotData, 0, sizeof(mRotData)); + if (objOvCtrlChannel.getOvSessionID(ovid) && + objOvCtrlChannel.getRotSessionID(rotid) && + objOvCtrlChannel.getSize(size)) { + return startDataChannel(ovid, rotid, size, fbnum, + norot, uichannel, num_buffers); + } + else + return false; +} + +bool OverlayDataChannel::openDevices(int fbnum, bool uichannel, int num_buffers) { + if (fbnum < 0) + return false; + char const * const device_template = + "/dev/graphics/fb%u"; + char dev_name[64]; + snprintf(dev_name, 64, device_template, fbnum); + + mFD = open(dev_name, O_RDWR, 0); + if (mFD < 0) { + reportError("Cant open framebuffer "); + return false; + } + if (!mNoRot) { + mRotFD = open("/dev/msm_rotator", O_RDWR, 0); + if (mRotFD < 0) { + reportError("Cant open rotator device"); + close(mFD); + mFD = -1; + return false; + } + + return mapRotatorMemory(num_buffers, uichannel, NEW_REQUEST); + } + return true; +} + +bool OverlayDataChannel::mapRotatorMemory(int num_buffers, bool uiChannel, int requestType) +{ + mPmemAddr = MAP_FAILED; + +#ifdef USE_ION + alloc_data data; + data.base = 0; + data.fd = -1; + data.offset = 0; + data.size = mPmemOffset * num_buffers; + data.align = getpagesize(); + data.uncached = true; + + int err = mAlloc->allocate(data, GRALLOC_USAGE_PRIVATE_ADSP_HEAP| + GRALLOC_USAGE_PRIVATE_SMI_HEAP, 0); + if(err) { + reportError("Cant allocate from ION"); + close(mFD); + mFD = -1; + close(mRotFD); + mRotFD = -1; + return false; + } + mPmemFD = data.fd; + mPmemAddr = data.base; + mBufferType = data.allocType; +#else + + if((requestType == NEW_REQUEST) && !uiChannel) { + mPmemFD = open("/dev/pmem_smipool", O_RDWR | O_SYNC); + if(mPmemFD >= 0) + mPmemAddr = (void *) mmap(NULL, mPmemOffset * num_buffers, PROT_READ | PROT_WRITE, + MAP_SHARED, mPmemFD, 0); + } + + if (mPmemAddr == MAP_FAILED) { + mPmemFD = open("/dev/pmem_adsp", O_RDWR | O_SYNC); + if (mPmemFD < 0) { + reportError("Cant open pmem_adsp "); + close(mFD); + mFD = -1; + close(mRotFD); + mRotFD = -1; + return false; + } else { + mPmemAddr = (void *) mmap(NULL, mPmemOffset * num_buffers, PROT_READ | PROT_WRITE, + MAP_SHARED, mPmemFD, 0); + if (mPmemAddr == MAP_FAILED) { + reportError("Cant map pmem_adsp "); + close(mFD); + mFD = -1; + close(mPmemFD); + mPmemFD = -1; + close(mRotFD); + mRotFD = -1; + return false; + } + } + } +#endif + // Set this flag if source memory is fb + if(uiChannel) + mRotData.src.flags |= MDP_MEMORY_ID_TYPE_FB; + + mOvDataRot.data.memory_id = mPmemFD; + mRotData.dst.memory_id = mPmemFD; + mRotData.dst.offset = 0; + mNumBuffers = num_buffers; + mCurrentItem = 0; + for (int i = 0; i < num_buffers; i++) + mRotOffset[i] = i * mPmemOffset; + + return true; +} + +bool OverlayDataChannel::updateDataChannel(int updateStatus, int size) { + mUpdateDataChannel = updateStatus; + mNewPmemOffset = size; + return true; +} + +bool OverlayDataChannel::startDataChannel(int ovid, int rotid, int size, + int fbnum, bool norot, + bool uichannel, int num_buffers) { + memset(&mOvData, 0, sizeof(mOvData)); + memset(&mOvDataRot, 0, sizeof(mOvDataRot)); + memset(&mRotData, 0, sizeof(mRotData)); + mNoRot = norot; + mOvData.data.memory_id = -1; + mOvData.id = ovid; + mOvDataRot = mOvData; + mPmemOffset = size; + mRotData.session_id = rotid; + mNumBuffers = 0; + mCurrentItem = 0; + + return openDevices(fbnum, uichannel, num_buffers); +} + +bool OverlayDataChannel::closeDataChannel() { + if (!isChannelUP()) + return true; + + if (!mNoRot && mRotFD > 0) { +#ifdef USE_ION + sp<IMemAlloc> memalloc = mAlloc->getAllocator(mBufferType); + memalloc->free_buffer(mPmemAddr, mPmemOffset * mNumBuffers, 0, mPmemFD); +#else + munmap(mPmemAddr, mPmemOffset * mNumBuffers); + close(mPmemFD); +#endif + close(mPmemFD); + mPmemFD = -1; + close(mRotFD); + mRotFD = -1; + } + close(mFD); + mFD = -1; + memset(&mOvData, 0, sizeof(mOvData)); + memset(&mOvDataRot, 0, sizeof(mOvDataRot)); + memset(&mRotData, 0, sizeof(mRotData)); + + mNumBuffers = 0; + mCurrentItem = 0; + + return true; +} + +bool OverlayDataChannel::setFd(int fd) { + mOvData.data.memory_id = fd; + return true; +} + +bool OverlayDataChannel::queueBuffer(uint32_t offset) { + if ((!isChannelUP()) || mOvData.data.memory_id < 0) { + reportError("QueueBuffer failed, either channel is not set or no file descriptor to read from"); + return false; + } + + int oldPmemFD = -1; + void* oldPmemAddr = MAP_FAILED; + uint32_t oldPmemOffset = -1; + bool result; + if (!mNoRot) { + if (mUpdateDataChannel) { + oldPmemFD = mPmemFD; + oldPmemAddr = mPmemAddr; + oldPmemOffset = mPmemOffset; + mPmemOffset = mNewPmemOffset; + mNewPmemOffset = -1; + // Map the new PMEM memory + result = mapRotatorMemory(mNumBuffers, 0, UPDATE_REQUEST); + if (!result) { + LOGE("queueBuffer: mapRotatorMemory failed"); + return false; + } + } + } + + result = queue(offset); + + // Unmap the old PMEM memory after the queueBuffer has returned + if (oldPmemFD != -1 && oldPmemAddr != MAP_FAILED) { +#ifdef USE_ION + sp<IMemAlloc> memalloc = mAlloc->getAllocator(mBufferType); + memalloc->free_buffer(oldPmemAddr, oldPmemOffset * mNumBuffers, 0, oldPmemFD); +#else + munmap(oldPmemAddr, oldPmemOffset * mNumBuffers); + close(oldPmemFD); +#endif + + oldPmemFD = -1; + } + return result; +} + +bool OverlayDataChannel::queue(uint32_t offset) { + msmfb_overlay_data *odPtr; + mOvData.data.offset = offset; + odPtr = &mOvData; + if (!mNoRot) { + mRotData.src.memory_id = mOvData.data.memory_id; + mRotData.src.offset = offset; + mRotData.dst.offset = (mRotData.dst.offset) ? 0 : mPmemOffset; + mRotData.dst.offset = mRotOffset[mCurrentItem]; + mCurrentItem = (mCurrentItem + 1) % mNumBuffers; + + int result = ioctl(mRotFD, + MSM_ROTATOR_IOCTL_ROTATE, &mRotData); + + if (!result) { + mOvDataRot.data.offset = (uint32_t) mRotData.dst.offset; + odPtr = &mOvDataRot; + } + } + + if (ioctl(mFD, MSMFB_OVERLAY_PLAY, odPtr)) { + reportError("overlay play failed."); + return false; + } + return true; +} + +bool OverlayDataChannel::getCropS3D(overlay_rect *inRect, int channel, int format, + overlay_rect *rect) { + // for the 3D usecase extract channels from a frame + switch (format & INPUT_MASK_3D) { + case HAL_3D_IN_SIDE_BY_SIDE_L_R: + if(channel == 0) { + rect->x = 0; + rect->y = 0; + rect->w = inRect->w/2; + rect->h = inRect->h; + } else { + rect->x = inRect->w/2; + rect->y = 0; + rect->w = inRect->w/2; + rect->h = inRect->h; + } + break; + case HAL_3D_IN_SIDE_BY_SIDE_R_L: + if(channel == 1) { + rect->x = 0; + rect->y = 0; + rect->w = inRect->w/2; + rect->h = inRect->h; + } else { + rect->x = inRect->w/2; + rect->y = 0; + rect->w = inRect->w/2; + rect->h = inRect->h; + } + break; + case HAL_3D_IN_TOP_BOTTOM: + if(channel == 0) { + rect->x = 0; + rect->y = 0; + rect->w = inRect->w; + rect->h = inRect->h/2; + } else { + rect->x = 0; + rect->y = inRect->h/2; + rect->w = inRect->w; + rect->h = inRect->h/2; + } + break; + case HAL_3D_IN_INTERLEAVE: + break; + default: + reportError("Unsupported 3D format..."); + break; + } + return true; +} + +bool OverlayDataChannel::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) { + if (!isChannelUP()) { + reportError("Channel not set"); + return false; + } + + mdp_overlay ov; + ov.id = mOvData.id; + if (ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) { + reportError("setCrop, overlay GET failed"); + return false; + } + + if ((ov.user_data[0] == MDP_ROT_90) || + (ov.user_data[0] == (MDP_ROT_90 | MDP_FLIP_UD)) || + (ov.user_data[0] == (MDP_ROT_90 | MDP_FLIP_LR))){ + if (ov.src.width < (y + h)) + return false; + + uint32_t tmp = x; + x = ov.src.width - (y + h); + y = tmp; + + tmp = w; + w = h; + h = tmp; + } + else if (ov.user_data[0] == MDP_ROT_270) { + if (ov.src.height < (x + w)) + return false; + + uint32_t tmp = y; + y = ov.src.height - (x + w); + x = tmp; + + tmp = w; + w = h; + h = tmp; + } + else if(ov.user_data[0] == MDP_ROT_180) { + if ((ov.src.height < (y + h)) || (ov.src.width < ( x + w))) + return false; + + x = ov.src.width - (x + w); + y = ov.src.height - (y + h); + } + + + if ((ov.src_rect.x == x) && + (ov.src_rect.y == y) && + (ov.src_rect.w == w) && + (ov.src_rect.h == h)) + return true; + + normalize_crop(x, w); + normalize_crop(y, h); + + ov.src_rect.x = x; + ov.src_rect.y = y; + ov.src_rect.w = w; + ov.src_rect.h = h; + + /* Scaling of upto a max of 8 times supported */ + if(ov.dst_rect.w >(ov.src_rect.w * HW_OVERLAY_MAGNIFICATION_LIMIT)){ + ov.dst_rect.w = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.w; + } + if(ov.dst_rect.h >(ov.src_rect.h * HW_OVERLAY_MAGNIFICATION_LIMIT)) { + ov.dst_rect.h = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.h; + } + if (ioctl(mFD, MSMFB_OVERLAY_SET, &ov)) { + reportError("setCrop, overlay set error"); + return false; + } + + return true; +} diff --git a/liboverlay/overlayLib.h b/liboverlay/overlayLib.h new file mode 100644 index 0000000..0ad92ce --- /dev/null +++ b/liboverlay/overlayLib.h @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * 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 INCLUDE_OVERLAY_LIB +#define INCLUDE_OVERLAY_LIB + +#include <cutils/log.h> +#include <cutils/properties.h> +#include <cutils/atomic.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +#include <dlfcn.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <pthread.h> + +#include <linux/fb.h> +#include <linux/msm_mdp.h> +#include <linux/msm_rotator.h> +#include <linux/android_pmem.h> + +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <hardware/overlay.h> +#include <utils/RefBase.h> +#include <alloc_controller.h> +#include <memalloc.h> + +#define HW_OVERLAY_MAGNIFICATION_LIMIT 8 +#define HW_OVERLAY_MINIFICATION_LIMIT HW_OVERLAY_MAGNIFICATION_LIMIT + +#define EVEN_OUT(x) if (x & 0x0001) {x--;} +#define VG0_PIPE 0 +#define VG1_PIPE 1 +#define NUM_CHANNELS 2 +#define FRAMEBUFFER_0 0 +#define FRAMEBUFFER_1 1 + +enum { + HDMI_OFF, + HDMI_ON +}; + +enum { + OVERLAY_CHANNEL_DOWN, + OVERLAY_CHANNEL_UP +}; + +enum { + NEW_REQUEST, + UPDATE_REQUEST +}; +/* ------------------------------- 3D defines ---------------------------------------*/ +// The compound format passed to the overlay is +// ABCCC where A is the input 3D format, +// B is the output 3D format +// CCC is the color format e.g YCbCr420SP YCrCb420SP etc. +#define FORMAT_3D(x) (x & 0xFF000) +#define COLOR_FORMAT(x) (x & 0xFFF) +// in the final 3D format, the MSB 2Bytes are the input format and the +// LSB 2bytes are the output format. Shift the output byte 12 bits. +#define SHIFT_OUTPUT_3D 12 +#define FORMAT_3D_OUTPUT(x) ((x & 0xF000) >> SHIFT_OUTPUT_3D) +#define FORMAT_3D_INPUT(x) (x & 0xF0000) +#define INPUT_MASK_3D 0xFFFF0000 +#define OUTPUT_MASK_3D 0x0000FFFF +#define SHIFT_3D 16 +// The output format is the 2MSB bytes. Shift the format by 12 to reflect this +#define HAL_3D_OUT_SIDE_BY_SIDE_MASK (HAL_3D_OUT_SIDE_BY_SIDE >> SHIFT_OUTPUT_3D) +#define HAL_3D_OUT_TOP_BOTTOM_MASK (HAL_3D_OUT_TOP_BOTTOM >> SHIFT_OUTPUT_3D) +#define HAL_3D_OUT_INTERLEAVE_MASK (HAL_3D_OUT_INTERLEAVE >> SHIFT_OUTPUT_3D) +#define HAL_3D_OUT_MONOSCOPIC_MASK (HAL_3D_OUT_MONOSCOPIC >> SHIFT_OUTPUT_3D) + +// 3D panel barrier orientation +#define BARRIER_LANDSCAPE 1 +#define BARRIER_PORTRAIT 2 + +#ifdef HDMI_AS_PRIMARY +#define FORMAT_3D_FILE "/sys/class/graphics/fb0/format_3d" +#define EDID_3D_INFO_FILE "/sys/class/graphics/fb0/3d_present" +#else +#define FORMAT_3D_FILE "/sys/class/graphics/fb1/format_3d" +#define EDID_3D_INFO_FILE "/sys/class/graphics/fb1/3d_present" +#endif +#define BARRIER_FILE "/sys/devices/platform/mipi_novatek.0/enable_3d_barrier" +/* -------------------------- end 3D defines ----------------------------------------*/ + +// Struct to hold the buffer info: geometry and size +struct overlay_buffer_info { + int width; + int height; + int format; + int size; +}; + +namespace overlay { + +enum { + OV_UI_MIRROR_TV = 0, + OV_2D_VIDEO_ON_PANEL, + OV_2D_VIDEO_ON_TV, + OV_3D_VIDEO_2D_PANEL, + OV_3D_VIDEO_2D_TV, + OV_3D_VIDEO_3D_PANEL, + OV_3D_VIDEO_3D_TV +}; +bool isHDMIConnected(); +bool is3DTV(); +bool isPanel3D(); +bool usePanel3D(); +bool send3DInfoPacket(unsigned int format3D); +bool enableBarrier(unsigned int orientation); +unsigned int getOverlayConfig (unsigned int format3D, bool poll = true, + bool isHDMI = false); +int getColorFormat(int format); +int get_mdp_format(int format); +int get_size(int format, int w, int h); +int get_rot_output_format(int format); +int get_mdp_orientation(int value); +void normalize_crop(uint32_t& xy, uint32_t& wh); + +/* Print values being sent to driver in case of ioctl failures + These logs are enabled only if DEBUG_OVERLAY is true */ +void dump(msm_rotator_img_info& mRotInfo); +void dump(mdp_overlay& mOvInfo); +const char* getFormatString(int format); + +const int max_num_buffers = 3; +typedef struct mdp_rect overlay_rect; + +class OverlayControlChannel { + + bool mNoRot; + + int mFBWidth; + int mFBHeight; + int mFBbpp; + int mFBystride; + int mFormat; + int mFD; + int mRotFD; + int mSize; + int mOrientation; + unsigned int mFormat3D; + bool mUIChannel; + mdp_overlay mOVInfo; + msm_rotator_img_info mRotInfo; + msmfb_overlay_3d m3DOVInfo; + bool mIsChannelUpdated; + bool openDevices(int fbnum = -1); + bool setOverlayInformation(const overlay_buffer_info& info, + int flags, int orientation, int zorder = 0, bool ignoreFB = false, + int requestType = NEW_REQUEST); + bool startOVRotatorSessions(const overlay_buffer_info& info, int orientation, int requestType); + void swapOVRotWidthHeight(); + +public: + OverlayControlChannel(); + ~OverlayControlChannel(); + bool startControlChannel(int w, int h, int format, + int fbnum, bool norot = false, + bool uichannel = false, + unsigned int format3D = 0, int zorder = 0, + bool ignoreFB = false); + bool closeControlChannel(); + bool setPosition(int x, int y, uint32_t w, uint32_t h); + bool setParameter(int param, int value, bool fetch = true); + void setSize (int size) { mSize = size; } + bool getPosition(int& x, int& y, uint32_t& w, uint32_t& h); + bool getOvSessionID(int& sessionID) const; + bool getRotSessionID(int& sessionID) const; + bool getSize(int& size) const; + bool isChannelUP() const { return (mFD > 0); } + int getFBWidth() const { return mFBWidth; } + int getFBHeight() const { return mFBHeight; } + int getFormat3D() const { return mFormat3D; } + bool getOrientation(int& orientation) const; + bool updateWaitForVsyncFlags(bool waitForVsync); + bool getAspectRatioPosition(int w, int h, overlay_rect *rect); + bool getPositionS3D(int channel, int format, overlay_rect *rect); + bool updateOverlaySource(const overlay_buffer_info& info, int orientation, bool waitForVsync); + bool getFormat() const { return mFormat; } + bool useVirtualFB (); + int getOverlayFlags() const { return mOVInfo.flags; } +}; + +class OverlayDataChannel { + + bool mNoRot; + int mFD; + int mRotFD; + int mPmemFD; + void* mPmemAddr; + uint32_t mPmemOffset; + uint32_t mNewPmemOffset; + msmfb_overlay_data mOvData; + msmfb_overlay_data mOvDataRot; + msm_rotator_data_info mRotData; + int mRotOffset[max_num_buffers]; + int mCurrentItem; + int mNumBuffers; + int mUpdateDataChannel; + android::sp<gralloc::IAllocController> mAlloc; + int mBufferType; + + bool openDevices(int fbnum = -1, bool uichannel = false, int num_buffers = 2); + bool mapRotatorMemory(int num_buffers, bool uiChannel, int requestType); + bool queue(uint32_t offset); + +public: + OverlayDataChannel(); + ~OverlayDataChannel(); + bool startDataChannel(const OverlayControlChannel& objOvCtrlChannel, + int fbnum, bool norot = false, + bool uichannel = false, int num_buffers = 2); + bool startDataChannel(int ovid, int rotid, int size, + int fbnum, bool norot = false, bool uichannel = false, + int num_buffers = 2); + bool closeDataChannel(); + bool setFd(int fd); + bool queueBuffer(uint32_t offset); + bool setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + bool getCropS3D(overlay_rect *inRect, int channel, int format, overlay_rect *rect); + bool isChannelUP() const { return (mFD > 0); } + bool updateDataChannel(int updateStatus, int size); +}; + +/* + * Overlay class for single thread application + * A multiple thread/process application need to use Overlay HAL + */ +class Overlay { + + bool mChannelUP; + bool mHDMIConnected; + unsigned int mS3DFormat; + //Actual cropped source width and height of overlay + int mCroppedSrcWidth; + int mCroppedSrcHeight; + overlay_buffer_info mOVBufferInfo; + int mState; + OverlayControlChannel objOvCtrlChannel[2]; + OverlayDataChannel objOvDataChannel[2]; + +public: + Overlay(); + ~Overlay(); + + static bool sHDMIAsPrimary; + bool startChannel(const overlay_buffer_info& info, int fbnum, bool norot = false, + bool uichannel = false, unsigned int format3D = 0, + int channel = 0, bool ignoreFB = false, + int num_buffers = 2); + bool closeChannel(); + bool setPosition(int x, int y, uint32_t w, uint32_t h); + bool setParameter(int param, int value); + bool setOrientation(int value, int channel = 0); + bool setFd(int fd, int channel = 0); + bool queueBuffer(uint32_t offset, int channel = 0); + bool getPosition(int& x, int& y, uint32_t& w, uint32_t& h, int channel = 0); + bool isChannelUP() const { return mChannelUP; } + int getFBWidth(int channel = 0) const; + int getFBHeight(int channel = 0) const; + bool getOrientation(int& orientation, int channel = 0) const; + bool queueBuffer(buffer_handle_t buffer); + bool setSource(const overlay_buffer_info& info, int orientation, bool hdmiConnected, + bool ignoreFB = false, int numBuffers = 2); + bool setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + int getChannelStatus() const { return (mChannelUP ? OVERLAY_CHANNEL_UP: OVERLAY_CHANNEL_DOWN); } + void setHDMIStatus (bool isHDMIConnected) { mHDMIConnected = isHDMIConnected; mState = -1; } + int getHDMIStatus() const {return (mHDMIConnected ? HDMI_ON : HDMI_OFF); } + +private: + bool setChannelPosition(int x, int y, uint32_t w, uint32_t h, int channel = 0); + bool setChannelCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h, int channel); + bool queueBuffer(int fd, uint32_t offset, int channel); + bool updateOverlaySource(const overlay_buffer_info& info, int orientation, bool waitForVsync); + int getS3DFormat(int format); +}; + +struct overlay_shared_data { + volatile bool isControlSetup; + unsigned int state; + int rotid[2]; + int ovid[2]; +}; +}; +#endif diff --git a/liboverlay/overlayLibUI.cpp b/liboverlay/overlayLibUI.cpp new file mode 100644 index 0000000..dd19cd9 --- /dev/null +++ b/liboverlay/overlayLibUI.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * 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 "overlayLibUI.h" +#include "gralloc_priv.h" +#define LOG_TAG "OverlayUI" + +using android::sp; +using gralloc::IMemAlloc; +using gralloc::alloc_data; + +namespace { +/* helper functions */ +bool checkOVState(int w, int h, int format, int orientation, + int zorder, const mdp_overlay& ov) { + switch(orientation) { + case OVERLAY_TRANSFORM_ROT_90: + case OVERLAY_TRANSFORM_ROT_270: { + int tmp = w; + w = h; + h = tmp; + break; + } + default: + break; + } + + int srcw = (w + 31) & ~31; + int srch = (h + 31) & ~31; + bool displayAttrsCheck = ((srcw == ov.src.width) && (srch == ov.src.height) && + (format == ov.src.format)); + bool zOrderCheck = (ov.z_order == zorder); + + if (displayAttrsCheck && zorder == overlay::NO_INIT) + return true; + + if (displayAttrsCheck && zorder != overlay::NO_INIT + && ov.z_order == zorder) + return true; + return false; +} + +void swapOVRotWidthHeight(msm_rotator_img_info& rotInfo, + mdp_overlay& ovInfo) { + int srcWidth = ovInfo.src.width; + ovInfo.src.width = ovInfo.src.height; + ovInfo.src.height = srcWidth; + + int srcRectWidth = ovInfo.src_rect.w; + ovInfo.src_rect.w = ovInfo.src_rect.h; + ovInfo.src_rect.h = srcRectWidth; + + int dstWidth = rotInfo.dst.width; + rotInfo.dst.width = rotInfo.dst.height; + rotInfo.dst.height = dstWidth; +} + +void setupOvRotInfo(int w, int h, int format, int orientation, + mdp_overlay& ovInfo, msm_rotator_img_info& rotInfo) { + memset(&ovInfo, 0, sizeof(ovInfo)); + memset(&rotInfo, 0, sizeof(rotInfo)); + ovInfo.id = MSMFB_NEW_REQUEST; + int srcw = (w + 31) & ~31; + int srch = (h + 31) & ~31; + ovInfo.src.width = srcw; + ovInfo.src.height = srch; + ovInfo.src.format = format; + ovInfo.src_rect.w = w; + ovInfo.src_rect.h = h; + ovInfo.alpha = 0xff; + ovInfo.transp_mask = 0xffffffff; + rotInfo.src.format = format; + rotInfo.dst.format = format; + rotInfo.src.width = srcw; + rotInfo.src.height = srch; + rotInfo.src_rect.w = srcw; + rotInfo.src_rect.h = srch; + rotInfo.dst.width = srcw; + rotInfo.dst.height = srch; + + int rot = orientation; + switch(rot) { + case 0: + case HAL_TRANSFORM_FLIP_H: + case HAL_TRANSFORM_FLIP_V: + rot = 0; + break; + case HAL_TRANSFORM_ROT_90: + case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H): + case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V): { + int tmp = ovInfo.src_rect.x; + ovInfo.src_rect.x = ovInfo.src.height - + (ovInfo.src_rect.y + ovInfo.src_rect.h); + ovInfo.src_rect.y = tmp; + swapOVRotWidthHeight(rotInfo, ovInfo); + rot = HAL_TRANSFORM_ROT_90; + break; + } + case HAL_TRANSFORM_ROT_180: + break; + case HAL_TRANSFORM_ROT_270: { + int tmp = ovInfo.src_rect.y; + ovInfo.src_rect.y = ovInfo.src.width - + (ovInfo.src_rect.x + ovInfo.src_rect.w); + ovInfo.src_rect.x = tmp; + swapOVRotWidthHeight(rotInfo, ovInfo); + break; + } + default: + break; + } + + int mdp_rotation = overlay::get_mdp_orientation(rot); + if (mdp_rotation < 0) + mdp_rotation = 0; + ovInfo.user_data[0] = mdp_rotation; + rotInfo.rotations = ovInfo.user_data[0]; + if (mdp_rotation) + rotInfo.enable = 1; + ovInfo.dst_rect.w = ovInfo.src_rect.w; + ovInfo.dst_rect.h = ovInfo.src_rect.h; +} + +bool isRGBType(int format) { + bool ret = false; + switch(format) { + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + case MDP_RGB_565: + ret = true; + break; + default: + ret = false; + break; + } + return ret; +} + +int getRGBBpp(int format) { + int ret = -1; + switch(format) { + case MDP_RGBA_8888: + case MDP_BGRA_8888: + case MDP_RGBX_8888: + ret = 4; + break; + case MDP_RGB_565: + ret = 2; + break; + default: + ret = -1; + break; + } + + return ret; +} + +bool turnOFFVSync() { + static int swapIntervalPropVal = -1; + if (swapIntervalPropVal == -1) { + char pval[PROPERTY_VALUE_MAX]; + property_get("debug.gr.swapinterval", pval, "1"); + swapIntervalPropVal = atoi(pval); + } + return (swapIntervalPropVal == 0); +} + +}; + +namespace overlay { + +status_t Display::openDisplay(int fbnum) { + if (mFD != NO_INIT) + return ALREADY_EXISTS; + + status_t ret = NO_INIT; + char const * const device_template = + "/dev/graphics/fb%u"; + char dev_name[64]; + snprintf(dev_name, 64, device_template, fbnum); + + mFD = open(dev_name, O_RDWR, 0); + if (mFD < 0) { + LOGE("Failed to open FB %d", fbnum); + return ret; + } + + fb_var_screeninfo vinfo; + if (ioctl(mFD, FBIOGET_VSCREENINFO, &vinfo)) { + LOGE("FBIOGET_VSCREENINFO on failed on FB %d", fbnum); + close(mFD); + mFD = NO_INIT; + return ret; + } + + mFBWidth = vinfo.xres; + mFBHeight = vinfo.yres; + mFBBpp = vinfo.bits_per_pixel; + ret = NO_ERROR; + + return ret; +} + +status_t OVHelper::startOVSession(mdp_overlay& ovInfo, int fbnum) { + status_t ret = NO_INIT; + + if (mSessionID == NO_INIT) { + ret = mobjDisplay.openDisplay(fbnum); + if (ret != NO_ERROR) + return ret; + + if (ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_SET, &ovInfo)) { + LOGE("OVerlay set failed.."); + mobjDisplay.closeDisplay(); + ret = BAD_VALUE; + } + else { + mSessionID = ovInfo.id; + mOVInfo = ovInfo; + ret = NO_ERROR; + } + } + else + ret = ALREADY_EXISTS; + + return ret; +} + +status_t OVHelper::queueBuffer(msmfb_overlay_data ovData) { + if (mSessionID == NO_INIT) + return NO_INIT; + + ovData.id = mSessionID; + if (ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_PLAY, &ovData)) + return BAD_VALUE; + + return NO_ERROR; +} + +status_t OVHelper::closeOVSession() { + if (mSessionID != NO_INIT) { + ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_UNSET, &mSessionID); + mobjDisplay.closeDisplay(); + } + + mSessionID = NO_INIT; + + return NO_ERROR; +} + +status_t OVHelper::setPosition(int x, int y, int w, int h) { + status_t ret = BAD_VALUE; + if (mSessionID != NO_INIT) { + int fd = mobjDisplay.getFD(); + if (x < 0 || y < 0 || ((x + w) > getFBWidth())) { + LOGE("set position failed, invalid argument"); + return ret; + } + + mdp_overlay ov; + ov.id = mSessionID; + if (!ioctl(fd, MSMFB_OVERLAY_GET, &ov)) { + if (x != ov.dst_rect.x || y != ov.dst_rect.y || + w != ov.dst_rect.w || h != ov.dst_rect.h) { + ov.dst_rect.x = x; + ov.dst_rect.y = y; + ov.dst_rect.w = w; + ov.dst_rect.h = h; + if (ioctl(fd, MSMFB_OVERLAY_SET, &ov)) { + LOGE("set position failed"); + return ret; + } + } + mOVInfo = ov; + return NO_ERROR; + } + return ret; + } + + return NO_INIT; +} + +status_t OVHelper::getOVInfo(mdp_overlay& ovInfo) { + if (mSessionID == NO_INIT) + return NO_INIT; + ovInfo = mOVInfo; + return NO_ERROR; +} + +status_t Rotator::startRotSession(msm_rotator_img_info& rotInfo, + int size, int numBuffers) { + status_t ret = ALREADY_EXISTS; + if (mSessionID == NO_INIT && mFD == NO_INIT) { + mNumBuffers = numBuffers; + mFD = open("/dev/msm_rotator", O_RDWR, 0); + if (mFD < 0) { + LOGE("Couldnt open rotator device"); + return NO_INIT; + } + + if (int check = ioctl(mFD, MSM_ROTATOR_IOCTL_START, &rotInfo)) { + close(mFD); + mFD = NO_INIT; + return NO_INIT; + } + +#ifdef USE_ION + alloc_data data; + data.base = 0; + data.fd = -1; + data.offset = 0; + data.size = mSize * mNumBuffers; + data.align = getpagesize(); + data.uncached = true; + + int err = mAlloc->allocate(data, GRALLOC_USAGE_PRIVATE_ADSP_HEAP| + GRALLOC_USAGE_PRIVATE_SMI_HEAP, 0); + + if(err) { + LOGE("Cant allocate from ION"); + closeRotSession(); + return NO_INIT; + } + mPmemFD = data.fd; + mPmemAddr = data.base; + mBufferType = data.allocType; +#else + mSessionID = rotInfo.session_id; + mPmemFD = open("/dev/pmem_adsp", O_RDWR | O_SYNC); + if (mPmemFD < 0) { + closeRotSession(); + return NO_INIT; + } + + mSize = size; + mPmemAddr = (void *) mmap(NULL, mSize* mNumBuffers, PROT_READ | PROT_WRITE, + MAP_SHARED, mPmemFD, 0); + if (mPmemAddr == MAP_FAILED) { + closeRotSession(); + return NO_INIT; + } +#endif + + mCurrentItem = 0; + for (int i = 0; i < mNumBuffers; i++) + mRotOffset[i] = i * mSize; + ret = NO_ERROR; + } + + return ret; +} + +status_t Rotator::closeRotSession() { + if (mSessionID != NO_INIT && mFD != NO_INIT) { + ioctl(mFD, MSM_ROTATOR_IOCTL_FINISH, &mSessionID); + close(mFD); +#ifdef USE_ION + sp<IMemAlloc> memalloc = mAlloc->getAllocator(mBufferType); + memalloc->free_buffer(mPmemAddr, mSize * mNumBuffers, 0, mPmemFD); +#else + munmap(mPmemAddr, mSize * mNumBuffers); + close(mPmemFD); +#endif + close(mPmemFD); + } + + mFD = NO_INIT; + mSessionID = NO_INIT; + mPmemFD = NO_INIT; + mPmemAddr = MAP_FAILED; + + return NO_ERROR; +} + +status_t Rotator::rotateBuffer(msm_rotator_data_info& rotData) { + status_t ret = NO_INIT; + if (mSessionID != NO_INIT) { + rotData.dst.memory_id = mPmemFD; + rotData.dst.offset = mRotOffset[mCurrentItem]; + rotData.session_id = mSessionID; + mCurrentItem = (mCurrentItem + 1) % mNumBuffers; + if (ioctl(mFD, MSM_ROTATOR_IOCTL_ROTATE, &rotData)) { + LOGE("Rotator failed to rotate"); + return BAD_VALUE; + } + return NO_ERROR; + } + + return ret; +} + +status_t OverlayUI::closeChannel() { + mobjOVHelper.closeOVSession(); + mobjRotator.closeRotSession(); + mChannelState = CLOSED; + return NO_ERROR; +} + +status_t OverlayUI::setSource(const overlay_buffer_info& info, int orientation, + bool useVGPipe, bool ignoreFB, + int fbnum, int zorder) { + status_t ret = NO_INIT; + + int format3D = FORMAT_3D(info.format); + int colorFormat = COLOR_FORMAT(info.format); + int format = get_mdp_format(colorFormat); + + if (format3D || !isRGBType(format)) + return ret; + + if (mChannelState == PENDING_CLOSE) + closeChannel(); + + if (mChannelState == UP) { + mdp_overlay ov; + if (mobjOVHelper.getOVInfo(ov) == NO_ERROR) { + if (mOrientation == orientation && + mFBNum == fbnum && + checkOVState(info.width, info.height, format, orientation, zorder, ov)) + return NO_ERROR; + else + mChannelState = PENDING_CLOSE; + } + else + mChannelState = PENDING_CLOSE; + return ret; + } + + mOrientation = orientation; + mdp_overlay ovInfo; + msm_rotator_img_info rotInfo; + setupOvRotInfo(info.width, info.height, format, orientation, ovInfo, rotInfo); + + int flags = 0; + if (ignoreFB) + ovInfo.is_fg = 1; + else + flags |= MDP_OV_PLAY_NOWAIT; + + if (turnOFFVSync()) + flags |= MDP_OV_PLAY_NOWAIT; + + if (useVGPipe || + (fbnum == FB0 && getRGBBpp(format) != mobjOVHelper.getFBBpp())) + flags |= MDP_OV_PIPE_SHARE; + + ovInfo.flags = flags; + if (zorder != NO_INIT) + ovInfo.z_order = zorder; + + ret = startChannel(fbnum, ovInfo, rotInfo, info.size); + return ret; +} + +status_t OverlayUI::startChannel(int fbnum, mdp_overlay& ovInfo, + msm_rotator_img_info& rotInfo, int size) { + status_t ret = BAD_VALUE; + if (mChannelState == UP) + return ret; + + ret = mobjOVHelper.startOVSession(ovInfo, fbnum); + if (ret == NO_ERROR && mOrientation) { + ret = mobjRotator.startRotSession(rotInfo, size); + } + + if (ret == NO_ERROR) { + mChannelState = UP; + mFBNum = fbnum; + } + else + LOGE("start channel failed."); + + return ret; +} + +status_t OverlayUI::queueBuffer(buffer_handle_t buffer) { + status_t ret = NO_INIT; + + if (mChannelState != UP) + return ret; + + msmfb_overlay_data ovData; + memset(&ovData, 0, sizeof(ovData)); + + private_handle_t const* hnd = reinterpret_cast + <private_handle_t const*>(buffer); + ovData.data.memory_id = hnd->fd; + ovData.data.offset = hnd->offset; + if (mOrientation) { + msm_rotator_data_info rotData; + memset(&rotData, 0, sizeof(rotData)); + rotData.src.memory_id = hnd->fd; + rotData.src.offset = hnd->offset; + if (mobjRotator.rotateBuffer(rotData) != NO_ERROR) { + LOGE("Rotator failed.. "); + return BAD_VALUE; + } + ovData.data.memory_id = rotData.dst.memory_id; + ovData.data.offset = rotData.dst.offset; + } + + ret = mobjOVHelper.queueBuffer(ovData); + if (ret != NO_ERROR) + LOGE("Queuebuffer failed "); + + return ret; +} + +}; diff --git a/liboverlay/overlayLibUI.h b/liboverlay/overlayLibUI.h new file mode 100644 index 0000000..2cb1ffe --- /dev/null +++ b/liboverlay/overlayLibUI.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * 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 INCLUDE_OVERLAY_LIB_UI +#define INCLUDE_OVERLAY_LIB_UI + +#include <errno.h> + +#include "overlayLib.h" + +namespace overlay { + +enum channel_state_t { UP, CLOSED, PENDING_CLOSE }; +enum status_t { + NO_ERROR, + INVALID_OPERATION = -ENOSYS, + BAD_VALUE = -EINVAL, + NO_INIT = -ENODEV, + ALREADY_EXISTS = -EEXIST + }; + +/* + * Display class provides following services + * Open FB + * FB information (Width, Height and Bpp) + */ + +class Display { + int mFD; + int mFBWidth; + int mFBHeight; + int mFBBpp; + Display(const Display& objDisplay); + Display& operator=(const Display& objDisplay); + +public: + explicit Display() : mFD(NO_INIT) { }; + ~Display() { close(mFD); }; + int getFD() const { return mFD; }; + int getFBWidth() const { return mFBWidth; }; + int getFBHeight() const { return mFBHeight; }; + int getFBBpp() const { return mFBBpp; }; + status_t openDisplay(int fbnum); + void closeDisplay() { close(mFD); mFD = NO_INIT; }; +}; + +/* + * OVHelper class, provides apis related to Overlay + * It communicates with MDP driver, provides following services + * Start overlay session + * Set position of the destination on to display + */ + +class OVHelper { + int mSessionID; + Display mobjDisplay; + mdp_overlay mOVInfo; + OVHelper(const OVHelper& objOVHelper); + OVHelper& operator=(const OVHelper& objOVHelper); + +public: + explicit OVHelper() : mSessionID(NO_INIT) { }; + ~OVHelper() { closeOVSession(); }; + status_t startOVSession(mdp_overlay& ovInfo, int fbnum); + status_t closeOVSession(); + status_t queueBuffer(msmfb_overlay_data ovData); + int getFBWidth() const { return mobjDisplay.getFBWidth(); }; + int getFBHeight() const { return mobjDisplay.getFBHeight(); }; + int getFBBpp() const { return mobjDisplay.getFBBpp(); }; + status_t setPosition(int x, int y, int w, int h); + status_t getOVInfo(mdp_overlay& ovInfo); +}; + +/* + * Rotator class, manages rotation of the buffers + * It communicates with Rotator driver, provides following services + * Start rotator session + * Rotate buffer + */ + +class Rotator { + int mFD; + int mSessionID; + int mPmemFD; + void* mPmemAddr; + int mRotOffset[max_num_buffers]; + int mCurrentItem; + int mNumBuffers; + int mSize; + android::sp<gralloc::IAllocController> mAlloc; + int mBufferType; + Rotator(const Rotator& objROtator); + Rotator& operator=(const Rotator& objRotator); + +public: + explicit Rotator() : mFD(NO_INIT), mSessionID(NO_INIT), mPmemFD(-1) + { +#ifdef USE_ION + mAlloc = gralloc::IAllocController::getInstance(); +#endif + } + ~Rotator() { closeRotSession(); } + status_t startRotSession(msm_rotator_img_info& rotInfo, int size, + int numBuffers = max_num_buffers); + status_t closeRotSession(); + status_t rotateBuffer(msm_rotator_data_info& rotData); +}; + +/* + * Overlay class for Comp. Bypass + * We merge control and data channel classes. + */ + +class OverlayUI { + channel_state_t mChannelState; + int mOrientation; + int mFBNum; + OVHelper mobjOVHelper; + Rotator mobjRotator; + + OverlayUI(const OverlayUI& objOverlay); + OverlayUI& operator=(const OverlayUI& objOverlay); + + status_t startChannel(int fbnum, mdp_overlay& ovInfo, + msm_rotator_img_info& rotInfo, int size); +public: + + enum fbnum_t { FB0, FB1 }; + + explicit OverlayUI() : mChannelState(CLOSED), mOrientation(NO_INIT), mFBNum(NO_INIT) { }; + ~OverlayUI() { closeChannel(); }; + status_t setSource(const overlay_buffer_info& info, int orientation, + bool useVGPipe = false, bool ignoreFB = true, + int fbnum = FB0, int zorder = NO_INIT); + status_t setPosition(int x, int y, int w, int h) { + return mobjOVHelper.setPosition(x, y, w, h); + }; + status_t closeChannel(); + channel_state_t isChannelUP() const { return mChannelState; }; + int getFBWidth() const { return mobjOVHelper.getFBWidth(); }; + int getFBHeight() const { return mobjOVHelper.getFBHeight(); }; + status_t queueBuffer(buffer_handle_t buffer); +}; + +}; +#endif diff --git a/liboverlay/overlayState.cpp b/liboverlay/overlayState.cpp new file mode 100644 index 0000000..f4398d4 --- /dev/null +++ b/liboverlay/overlayState.cpp @@ -0,0 +1,1109 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2009 - 2011, Code Aurora Forum. All rights reserved. + * + * 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. + */ + +int setParameterHandleState(overlay_control_context_t *ctx, + overlay_object *obj, + int param, int value) +{ + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + if(!obj->setParameter(param, value, VG0_PIPE)) { + LOGE("%s: Failed for channel 0", __func__); + return -1; + } + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + case OV_3D_VIDEO_3D_PANEL: + for (int i=0; i<NUM_CHANNELS; i++) { + if(!obj->setParameter(param, value, i)) { + LOGE("%s: Failed for channel %d", __func__, i); + return -1; + } + } + break; + default: + LOGE("Unknown state in setParameter"); + abort(); + break; + } + return 0; +} + +int createOverlayHandleState(overlay_control_context_t *ctx, bool noRot, + overlay_object* overlay, int fd) +{ + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + if (!overlay->startControlChannel(FRAMEBUFFER_0, noRot)) { + error_cleanup_control(ctx, overlay, fd, FRAMEBUFFER_0); + return -1; + } + break; + case OV_3D_VIDEO_3D_PANEL: + for (int i=0; i<NUM_CHANNELS; i++) { + if (!overlay->startControlChannel(FRAMEBUFFER_0, noRot, i)) { + error_cleanup_control(ctx, overlay, fd, i); + return -1; + } + } + break; + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + if (!overlay->startControlChannel(FRAMEBUFFER_0, noRot, VG0_PIPE)) { + error_cleanup_control(ctx, overlay, fd, VG0_PIPE); + return -1; + } + if (!overlay->startControlChannel(FRAMEBUFFER_1, true, VG1_PIPE)) { + error_cleanup_control(ctx, overlay, fd, VG1_PIPE); + return -1; + } + break; + case OV_3D_VIDEO_3D_TV: + for (int i=0; i<NUM_CHANNELS; i++) { + if (!overlay->startControlChannel(FRAMEBUFFER_1, true, i)) { + error_cleanup_control(ctx, overlay, fd, i); + return -1; + } + } + break; + default: + break; + } + return 0; +} + +int setPositionHandleState(overlay_control_context_t *ctx, + overlay_object *obj, overlay_rect& rect, + int x, int y, uint32_t w, uint32_t h) +{ + int ret = 0; + switch (ctx->state) { + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + if(!obj->setPosition(x, y, w, h, VG0_PIPE)) { + LOGE("%s:Failed for channel 0", __func__); + return -1; + } + break; + case OV_3D_VIDEO_3D_PANEL: + for (int i = 0; i < NUM_CHANNELS; i++) { + if (!obj->useVirtualFB(i)) { + LOGE("can't use the virtual fb for line interleaving!"); + } + obj->getPositionS3D(&rect, i, true); + if(!obj->setPosition(rect.x, rect.y, rect.w, rect.h, i)) { + LOGE("%s:Failed for channel %d", __func__, i); + return -1; + } + } + break; + case OV_2D_VIDEO_ON_TV: + obj->getAspectRatioPosition(&rect, VG1_PIPE); + if(!obj->setPosition(rect.x, rect.y, rect.w, rect.h, VG1_PIPE)) { + LOGE("%s:Failed for channel 1", __func__); + } + if(!obj->setPosition(x, y, w, h, VG0_PIPE)) { + LOGE("%s:Failed for channel 0", __func__); + return -1; + } + break; + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + for (int i = 0; i < NUM_CHANNELS; i++) { + if (!obj->getPositionS3D(&rect, i)) + ret = obj->setPosition(x, y, w, h, i); + else + ret = obj->setPosition(rect.x, rect.y, rect.w, rect.h, i); + if (!ret) { + LOGE("%s:Failed for channel %d", __func__, i); + return -1; + } + } + break; + default: + break; + } + return ret; +} + +//////////////////////// configPipes /////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////// + +bool TV2Dconn(overlay_control_context_t *ctx, + overlay_object *obj, bool noRot, + overlay_rect& rect) +{ + LOGI("2D TV connected, Open a new control channel for TV."); + //Start a new channel for mirroring on HDMI + if (!obj->startControlChannel(FRAMEBUFFER_1, true, VG1_PIPE)) { + obj->closeControlChannel(VG1_PIPE); + return false; + } + if (ctx->format3D) + obj->getPositionS3D(&rect, FRAMEBUFFER_1); + else + obj->getAspectRatioPosition(&rect, FRAMEBUFFER_1); + if(!obj->setPosition(rect.x, rect.y, rect.w, rect.h, FRAMEBUFFER_1)) { + LOGE("%s:Failed to set position for framebuffer 1", __func__); + return false; + } + return true; +} + +bool TV3Dconn(overlay_control_context_t *ctx, + overlay_object *obj, bool noRot, + overlay_rect& rect) +{ + LOGI("3D TV connected, close old ctl channel and open two ctl channels for 3DTV."); + //close the channel 0 as it is configured for panel + obj->closeControlChannel(VG0_PIPE); + //update the output from monoscopic to stereoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | ctx->format3D >> SHIFT_3D; + obj->setFormat3D(ctx->format3D); + LOGI("Control: new S3D format : 0x%x", ctx->format3D); + //now open both the channels + for (int i = 0; i < NUM_CHANNELS; i++) { + if (!obj->startControlChannel(FRAMEBUFFER_1, true, i)) { + LOGE("%s:Failed to open control channel for pipe %d", __func__, i); + return false; + } + obj->getPositionS3D(&rect, i); + if(!obj->setPosition(rect.x, rect.y, rect.w, rect.h, i)) { + LOGE("%s: failed for channel %d", __func__, i); + return false; + } + } + return true; +} + +bool TV3DSetup(overlay_control_context_t *ctx, overlay_object *obj, int i, + int fbnum, overlay_rect& rect) +{ + bool noRot = fbnum ? true : false; + if (!obj->startControlChannel(fbnum, noRot, i)) { + LOGE("%s:Failed to open control channel for pipe %d", __func__, i); + return false; + } + bool ret=true; + if (!obj->getPositionS3D(&rect, i)) + ret = obj->setPosition(ctx->posPanel.x, ctx->posPanel.y, + ctx->posPanel.w, ctx->posPanel.h, i); + else + ret = obj->setPosition(rect.x, rect.y, rect.w, rect.h, i); + if(!ret) { + LOGE("%s: failed for channel %d", __func__, i); + return false; + } + return true; +} + +int configPipes_OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) // HDMI connected + { + if(!TV2Dconn(ctx, obj, noRot, rect)) + return -1; + return 0; + } + LOGE("%s Error cannot disconnect HDMI in that state", __func__); + abort(); + return -1; +} + +int configPipes_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_2D_TV (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + // same as OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV + return configPipes_OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV(ctx, + obj, + newState, + enable, noRot, rect); +} + +int configPipes_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_3D_TV (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) // HDMI connected + { + if(!TV3Dconn(ctx, obj, noRot, rect)) + return -1; + return 0; + } + // HDMI disconnected + LOGE("%s Error cannot disconnect HDMI in that state", __func__); + abort(); + return -1; +} + +int configPipes_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_2D_TV (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(!enable){ // HDMI disconnect + LOGE("%s Error cannot disconnect HDMI in that state", __func__); + abort(); + return -1; + } + obj->closeControlChannel(VG1_PIPE); + obj->closeControlChannel(VG0_PIPE); + //disable the panel barriers + enableBarrier(0); + //now open both the channels + //update the output from stereoscopic to monoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + obj->setFormat3D(ctx->format3D); + LOGI("Control: new S3D format : 0x%x", ctx->format3D); + int fbnum = 0; + bool ret = true; + //now open both the channels + for (int i = 0; i < NUM_CHANNELS; i++) { + fbnum = i; + if(!TV3DSetup(ctx, obj, i, fbnum, rect)) + return -1; + } + return 0; +} + +int configPipes_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_3D_TV (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(!enable){ // HDMI disconnect + LOGE("%s Error cannot disconnect HDMI in that state", __func__); + abort(); + return -1; + } + obj->closeControlChannel(VG1_PIPE); + obj->closeControlChannel(VG0_PIPE); + //disable the panel barrier + enableBarrier(0); + //now open both the channels + for (int i = 0; i < NUM_CHANNELS; i++) { + if(!TV3DSetup(ctx, obj, i, FRAMEBUFFER_1, rect)) + return -1; + } + return 0; +} + + +///// HDMI Disconnect //// +int configPipes_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) // HDMI connected + { + LOGE("%s Error cannot connect HDMI in that state", __func__); + abort(); + return -1; + } + LOGI("2D TV disconnected, close the control channel."); + obj->closeControlChannel(VG1_PIPE); + return 0; +} + +int configPipes_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_2D_PANEL (overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + // same as OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL + return configPipes_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL(ctx, + obj, + newState, + enable, noRot, rect); +} + +int configPipes_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_2D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) // HDMI connected + { + LOGE("%s Error cannot connect HDMI in that state", __func__); + abort(); + return -1; + } + LOGI("3D TV disconnected, close the control channels & open one for panel."); + // Close both the pipes' control channel + obj->closeControlChannel(VG1_PIPE); + obj->closeControlChannel(VG0_PIPE); + //update the format3D as monoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + obj->setFormat3D(ctx->format3D); + LOGI("Control: New format3D: 0x%x", ctx->format3D); + //now open the channel 0 + if (!obj->startControlChannel(FRAMEBUFFER_0, noRot)) { + LOGE("%s:Failed to open control channel for pipe 0", __func__); + return false; + } + if(!obj->setPosition(ctx->posPanel.x, ctx->posPanel.y, + ctx->posPanel.w, ctx->posPanel.h, FRAMEBUFFER_0)) { + LOGE("%s:Failed to set position for framebuffer 0", __func__); + return false; + } + if (!obj->setParameter(OVERLAY_TRANSFORM, ctx->orientation, VG0_PIPE)) { + LOGE("%s: Failed to set orientation for channel 0", __func__); + return -1; + } + return 0; +} + +int TVto3DPanel(overlay_control_context_t *ctx, overlay_object *obj, + bool noRot, overlay_rect& rect) +{ + for (int i = 0; i < NUM_CHANNELS; i++) { + if (!obj->startControlChannel(FRAMEBUFFER_0, noRot, i)) { + LOGE("%s:Failed to open control channel for pipe %d", __func__, i); + return false; + } + if (!obj->useVirtualFB(i)) { + LOGE("can't use the virtual fb for line interleaving!"); + } + obj->getPositionS3D(&rect, i); + if(!obj->setPosition(rect.x, rect.y, rect.w, rect.h, i)) { + LOGE("%s:Failed for channel %d", __func__, i); + return -1; + } + if (!obj->setParameter(OVERLAY_TRANSFORM, ctx->orientation, i)) { + LOGE("%s: Failed to set orientation for channel 0", __func__); + return -1; + } + } + return 0; +} + +int configPipes_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_3D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) { // HDMI connect + LOGE("%s Error cannot connect HDMI in that state", __func__); + abort(); + return -1; + } + // disconnect TV + // Close both the pipes' control channel + obj->closeControlChannel(VG0_PIPE); + obj->closeControlChannel(VG1_PIPE); + //update the output from monoscopic to stereoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | ctx->format3D >> SHIFT_3D; + obj->setFormat3D(ctx->format3D); + LOGI("Control: new S3D format : 0x%x", ctx->format3D); + return TVto3DPanel(ctx, obj, noRot, rect); +} + +int configPipes_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_3D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + if(enable) { // HDMI connect + LOGE("%s Error cannot connect HDMI in that state", __func__); + abort(); + return -1; + } + + // disconnect TV + // Close both the pipes' control channel + obj->closeControlChannel(VG0_PIPE); + obj->closeControlChannel(VG1_PIPE); + return TVto3DPanel(ctx, obj, noRot, rect); +} + + +//// On Panel //// + +int configPipes_OV_2D_VIDEO_ON_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_3D_TV: + case OV_3D_VIDEO_2D_TV: + LOGE("ctl: Error in handling OV_2D_VIDEO_ON_PANEL newstate=%d", newState); + abort(); + return -1; + break; + case OV_2D_VIDEO_ON_TV: + LOGI("TV connected: open a new VG control channel"); + if(-1 == configPipes_OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + break; + } + return 0; +} + +int configPipes_OV_3D_VIDEO_2D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_2D_VIDEO_ON_TV: + LOGE("Error in handling OV_3D_VIDEO_2D_PANEL newstate=%d", newState); + abort(); + return -1; + case OV_3D_VIDEO_2D_TV: + if(-1 == configPipes_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_2D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == configPipes_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_3D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + } + return 0; +} + +int configPipes_OV_3D_VIDEO_3D_PANEL(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_2D_VIDEO_ON_TV: + LOGE("Error in handling OV_3D_VIDEO_3D_PANEL newstate=%d", newState); + abort(); + return -1; + case OV_3D_VIDEO_2D_TV: + if(-1 == configPipes_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_2D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == configPipes_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_3D_TV(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + } + return 0; +} + +/// OV on TV //// + +int configPipes_OV_2D_VIDEO_ON_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + if(-1 == configPipes_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_TV: + case OV_3D_VIDEO_2D_TV: + case OV_2D_VIDEO_ON_TV: + LOGE("Error in handling OV_2D_VIDEO_ON_TV newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + } + return 0; +} + +int configPipes_OV_3D_VIDEO_2D_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_3D_TV: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + LOGE("Error in handling OV_3D_VIDEO_2D_TV newstate=%d", newState); + abort(); + return -1; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_3D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + break; + case OV_3D_VIDEO_2D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_2D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + } + return 0; +} + +int configPipes_OV_3D_VIDEO_3D_TV(overlay_control_context_t *ctx, + overlay_object *obj, + unsigned int newState, + int enable, bool noRot, + overlay_rect& rect) +{ + switch(newState){ + case OV_3D_VIDEO_2D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_2D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_3D_TV: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + LOGE("Error in handling OV_3D_VIDEO_2D_TV newstate=%d", newState); + abort(); + return -1; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == configPipes_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_3D_PANEL(ctx, + obj, + newState, + enable, noRot, rect)) + return -1; + break; + default: + LOGE("%s Unknown state in configPipes %d", __func__, newState); + abort(); + } + return 0; +} + + +//////////////////////////////////// Queue Buffer /////////////////////////////////// + +///////////////////////// Helper func /////////////////////////////// + +int queueBuffer_OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV(overlay_data_context_t *ctx, + overlay_shared_data* data) +{ + LOGI("2D TV connected, Open a new data channel for TV."); + //Start a new channel for mirroring on HDMI + ctx->pobjDataChannel[VG1_PIPE] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[VG1_PIPE]->startDataChannel( + data->ovid[VG1_PIPE], data->rotid[VG1_PIPE], ctx->size, + FRAMEBUFFER_1, true)) { + delete ctx->pobjDataChannel[VG1_PIPE]; + ctx->pobjDataChannel[VG1_PIPE] = NULL; + return -1; + } + if(!ctx->pobjDataChannel[VG1_PIPE]->setCrop( + ctx->cropRect.x,ctx->cropRect.y, + ctx->cropRect.w,ctx->cropRect.h)) { + LOGE("%s:failed to crop pipe 1", __func__); + } + //setting the srcFD + if (!ctx->pobjDataChannel[VG1_PIPE]->setFd(ctx->srcFD)) { + LOGE("%s: Failed to set fd for pipe 1", __func__); + return -1; + } + return 0; +} + +int queueBuffer_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_2D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data) +{ + LOGI("2D TV connected, Open a new data channel for TV."); + //Start a new channel for mirroring on HDMI + ctx->pobjDataChannel[VG1_PIPE] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[VG1_PIPE]->startDataChannel( + data->ovid[VG1_PIPE], data->rotid[VG1_PIPE], ctx->size, + FRAMEBUFFER_1, true)) { + delete ctx->pobjDataChannel[VG1_PIPE]; + ctx->pobjDataChannel[VG1_PIPE] = NULL; + return -1; + } + overlay_rect rect; + ctx->pobjDataChannel[VG1_PIPE]->getCropS3D(&ctx->cropRect, VG1_PIPE, ctx->format3D, &rect); + if (!ctx->pobjDataChannel[VG1_PIPE]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: Failed to crop pipe 1", __func__); + return -1; + } + //setting the srcFD + if (!ctx->pobjDataChannel[VG1_PIPE]->setFd(ctx->srcFD)) { + LOGE("%s: Failed to set fd for pipe 1", __func__); + return -1; + } + return 0; +} + +int queueBuffer_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_3D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data) +{ + //close the channel 0 as it is configured for panel + ctx->pobjDataChannel[VG0_PIPE]->closeDataChannel(); + delete ctx->pobjDataChannel[VG0_PIPE]; + ctx->pobjDataChannel[VG0_PIPE] = NULL; + //update the output from monoscopic to stereoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | ctx->format3D >> SHIFT_3D; + LOGI("Data: New S3D format : 0x%x", ctx->format3D); + //now open both the channels + overlay_rect rect; + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[i]->startDataChannel( + data->ovid[i], data->rotid[i], ctx->size, + FRAMEBUFFER_1, true)) { + error_cleanup_data(ctx, i); + return -1; + } + ctx->pobjDataChannel[i]->getCropS3D(&ctx->cropRect, i, ctx->format3D, &rect); + if (!ctx->pobjDataChannel[i]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: Failed to crop pipe %d", __func__, i); + return -1; + } + if (!ctx->pobjDataChannel[i]->setFd(ctx->srcFD)) { + LOGE("%s: Failed to set fd for pipe %d", __func__, i); + return -1; + } + } + send3DInfoPacket(ctx->format3D & OUTPUT_MASK_3D); + return 0; +} +int queueBuffer_3D_to_2D_TV_common(overlay_data_context_t *ctx, + overlay_shared_data* data, + overlay_rect& rect, + int i, int fbnum) +{ + bool noRot = fbnum ? true : false; + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[i]->startDataChannel( + data->ovid[i], data->rotid[i], ctx->size, fbnum, noRot)) { + error_cleanup_data(ctx, i); + return -1; + } + ctx->pobjDataChannel[i]->getCropS3D(&ctx->cropRect, i, ctx->format3D, &rect); + if (!ctx->pobjDataChannel[i]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: Failed to crop pipe %d", __func__, i); + return -1; + } + if (!ctx->pobjDataChannel[i]->setFd(ctx->srcFD)) { + LOGE("%s: Failed to set fd for pipe %d", __func__, i); + return -1; + } + return 0; +} + +int queueBuffer_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_2D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data) +{ + // Close both the pipes' data channel + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i]->closeDataChannel(); + delete ctx->pobjDataChannel[i]; + ctx->pobjDataChannel[i] = NULL; + } + //now open both the channels + overlay_rect rect; + for (int i = 0; i < NUM_CHANNELS; i++) { + int fbnum = i; + //update the output from stereoscopic to monoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + LOGI("Data: New S3D format : 0x%x", ctx->format3D); + if(-1 == queueBuffer_3D_to_2D_TV_common(ctx, data, rect, i, fbnum)) + return -1; + } + return 0; +} + +int queueBuffer_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_3D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data) +{ + // Close both the pipes' data channel + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i]->closeDataChannel(); + delete ctx->pobjDataChannel[i]; + ctx->pobjDataChannel[i] = NULL; + } + //now open both the channels + overlay_rect rect; + int fbnum = 1; + for (int i = 0; i < NUM_CHANNELS; i++) { + if(-1 == queueBuffer_3D_to_2D_TV_common(ctx, data, rect, i, fbnum)) + return -1; + } + send3DInfoPacket(ctx->format3D & OUTPUT_MASK_3D); + return 0; +} + +void queueBuffer_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL(overlay_data_context_t *ctx) +{ + LOGI("2D TV disconnected, close the data channel for TV."); + ctx->pobjDataChannel[VG1_PIPE]->closeDataChannel(); + delete ctx->pobjDataChannel[VG1_PIPE]; + ctx->pobjDataChannel[VG1_PIPE] = NULL; +} + +void queueBuffer_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_2D_PANEL(overlay_data_context_t *ctx) +{ + // same as queueBuffer_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL + queueBuffer_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL(ctx); +} + +int queueBuffer_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_2D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, bool noRot) +{ + LOGI("3D TV disconnected, close the data channels for 3DTV and open one for panel."); + // Close both the pipes' data channel + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i]->closeDataChannel(); + delete ctx->pobjDataChannel[i]; + ctx->pobjDataChannel[i] = NULL; + } + send3DInfoPacket(0); + //update the format3D as monoscopic + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | HAL_3D_OUT_MONOSCOPIC_MASK; + //now open the channel 0 + ctx->pobjDataChannel[VG0_PIPE] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[VG0_PIPE]->startDataChannel( + data->ovid[VG0_PIPE], data->rotid[VG0_PIPE], ctx->size, + FRAMEBUFFER_0, noRot)) { + error_cleanup_data(ctx, VG0_PIPE); + return -1; + } + overlay_rect rect; + ctx->pobjDataChannel[VG0_PIPE]->getCropS3D(&ctx->cropRect, VG0_PIPE, + ctx->format3D, &rect); + //setting the crop value + if(!ctx->pobjDataChannel[VG0_PIPE]->setCrop( rect.x, rect.y,rect.w, rect.h)) { + LOGE("%s:failed to crop pipe 0", __func__); + } + //setting the srcFD + if (!ctx->pobjDataChannel[VG0_PIPE]->setFd(ctx->srcFD)) { + LOGE("%s: Failed set fd for pipe 0", __func__); + return -1; + } + return 0; +} + +void queueBuffer_3D_Panel_common_pre(overlay_data_context_t *ctx) +{ + // Close both the pipes' data channel + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i]->closeDataChannel(); + delete ctx->pobjDataChannel[i]; + ctx->pobjDataChannel[i] = NULL; + } + send3DInfoPacket(0); +} + + +int queueBuffer_3D_Panel_common_post(overlay_data_context_t *ctx, + overlay_shared_data* data, + bool noRot) +{ + overlay_rect rect; + for (int i = 0; i < NUM_CHANNELS; i++) { + ctx->pobjDataChannel[i] = new OverlayDataChannel(); + if (!ctx->pobjDataChannel[i]->startDataChannel( + data->ovid[i], data->rotid[i], ctx->size, FRAMEBUFFER_0, noRot)) { + error_cleanup_data(ctx, i); + return -1; + } + ctx->pobjDataChannel[i]->getCropS3D(&ctx->cropRect, i, + ctx->format3D, &rect); + if (!ctx->pobjDataChannel[i]->setCrop(rect.x, rect.y, rect.w, rect.h)) { + LOGE("%s: Failed to crop pipe %d", __func__, i); + return -1; + } + if (!ctx->pobjDataChannel[i]->setFd(ctx->srcFD)) { + LOGE("%s: Failed to set fd for pipe %d", __func__, i); + return -1; + } + } + return 0; +} + +int queueBuffer_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_3D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + bool noRot) +{ + queueBuffer_3D_Panel_common_pre(ctx); + + if(-1 == queueBuffer_3D_Panel_common_post(ctx, data, noRot)) + return -1; + + return 0; +} + +int queueBuffer_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_3D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + bool noRot) +{ + queueBuffer_3D_Panel_common_pre(ctx); + ctx->format3D = FORMAT_3D_INPUT(ctx->format3D) | + ctx->format3D >> SHIFT_3D; + if(-1 == queueBuffer_3D_Panel_common_post(ctx, data, noRot)) + return -1; + + return 0; +} + +////////////////// Queue buffer state machine handling ///////////////////// + +int queueBuffer_OV_2D_VIDEO_ON_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_2D_VIDEO_ON_PANEL: + // nothing to do here + break; + case OV_2D_VIDEO_ON_TV: + LOGI("TV connected, open a new data channel"); + if(-1 == queueBuffer_OV_2D_VIDEO_ON_PANEL_to_OV_2D_VIDEO_ON_TV(ctx, data)) + return -1; + break; + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + LOGE("data: Error in handling OV_2D_VIDEO_ON_PANEL newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + +int queueBuffer_OV_3D_VIDEO_2D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_3D_VIDEO_2D_PANEL: + // nothing to do here + break; + case OV_3D_VIDEO_2D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_2D_TV(ctx, data)) + return -1; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_2D_PANEL_to_OV_3D_VIDEO_3D_TV(ctx, data)) + return -1; + break; + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_2D_VIDEO_ON_TV: + LOGE("Error in handling OV_3D_VIDEO_2D_PANEL newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + +int queueBuffer_OV_3D_VIDEO_3D_PANEL(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_3D_VIDEO_3D_PANEL: + // nothing to do here + break; + case OV_2D_VIDEO_ON_PANEL: + case OV_3D_VIDEO_2D_PANEL: + case OV_2D_VIDEO_ON_TV: + LOGE("Error in handling OV_3D_VIDEO_3D_PANEL newstate=%d", newState); + abort(); + return -1; + case OV_3D_VIDEO_2D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_2D_TV(ctx, data)) + return -1; + break; + case OV_3D_VIDEO_3D_TV: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_PANEL_to_OV_3D_VIDEO_3D_TV(ctx, data)) + return -1; + break; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + +int queueBuffer_OV_2D_VIDEO_ON_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_2D_VIDEO_ON_TV: + // nothing to see here + break; + case OV_2D_VIDEO_ON_PANEL: + queueBuffer_OV_2D_VIDEO_ON_TV_to_OV_2D_VIDEO_ON_PANEL(ctx); + break; + case OV_3D_VIDEO_2D_PANEL: + case OV_3D_VIDEO_3D_PANEL: + case OV_3D_VIDEO_2D_TV: + case OV_3D_VIDEO_3D_TV: + LOGE("Error in handling OV_2D_VIDEO_ON_TV newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + +int queueBuffer_OV_3D_VIDEO_2D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_3D_VIDEO_2D_TV: + // nothing to see here + break; + case OV_3D_VIDEO_2D_PANEL: + queueBuffer_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_2D_PANEL(ctx); + break; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == queueBuffer_OV_3D_VIDEO_2D_TV_to_OV_3D_VIDEO_3D_PANEL(ctx, data, noRot)) + return -1; + break; + case OV_2D_VIDEO_ON_PANEL: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_3D_TV: + LOGE("Error in handling OV_3D_VIDEO_2D_TV newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + +int queueBuffer_OV_3D_VIDEO_3D_TV(overlay_data_context_t *ctx, + overlay_shared_data* data, + unsigned int newState, bool noRot) +{ + switch(newState){ + case OV_3D_VIDEO_3D_TV: + // nothing to see here + break; + case OV_3D_VIDEO_2D_PANEL: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_2D_PANEL(ctx, data, noRot)) + return -1; + break; + case OV_3D_VIDEO_3D_PANEL: + if(-1 == queueBuffer_OV_3D_VIDEO_3D_TV_to_OV_3D_VIDEO_3D_PANEL(ctx, data, noRot)) + return -1; + break; + case OV_2D_VIDEO_ON_PANEL: + case OV_2D_VIDEO_ON_TV: + case OV_3D_VIDEO_2D_TV: + LOGE("Error in handling OV_3D_VIDEO_3D_TV newstate=%d", newState); + abort(); + return -1; + default: + LOGE("%s Unknown state in queueBuffer %d", __func__, newState); + abort(); + } + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////// +