Kinjal Bhavsar 1b51063e0d liboverlay: Add TV detection and monoscopic display for S3D
Add the state machine for overlay configuration. When connected,
check whether the TV on HDMI is 3D capable or not using the EDID
info. Show L channel on primary panel; and R channel on 2D TV if
non 3DTV is connected via HDMI, else switch TV to 3D mode.

Change-Id: I3109f8b0b81a8f5ad542db038262fd668e225e96
2011-09-19 19:16:35 -05:00

1205 lines
38 KiB
C++

/*
* 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"
/*****************************************************************************/
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;
};
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_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;
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, mHandle.format, 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) {
if (!mHandle.pobjControlChannel[channel]) {
LOGE("%s:Failed got channel %d", __func__, channel);
return false;
}
return mHandle.pobjControlChannel[channel]->getPositionS3D(
channel, mHandle.format3D, 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(0);
closeControlChannel(1);
send3DInfoPacket (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;
}
};
// ****************************************************************************
// 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;
}
LOGD("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
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 NULL;
}
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 NULL;
}
if (!overlay->startControlChannel(FRAMEBUFFER_1, true, VG1_PIPE)) {
error_cleanup_control(ctx, overlay, fd, VG1_PIPE);
return NULL;
}
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 NULL;
}
}
break;
default:
break;
}
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;
}
obj->destroy_overlay();
delete overlay;
}
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;
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_2D_VIDEO_ON_TV:
if(!obj->setPosition(x, y, w, h, VG0_PIPE)) {
LOGE("%s:Failed for channel 0", __func__);
return -1;
}
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__);
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 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;
data.readyToQueue = 1;
memcpy(ctx->sharedMemBase, (void*)&data, sizeof(overlay_shared_data));
}
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;
}
#if 0
static bool overlay_configPipes(overlay_control_context_t *ctx,
overlay_object *obj, int enable,
unsigned int curState) {
bool noRot = true;
overlay_rect rect;
#ifdef USE_MSM_ROTATOR
noRot = false;
#else
noRot = true;
#endif
if(enable) {
if( (ctx->state == OV_2D_VIDEO_ON_PANEL) ||
(ctx->state == OV_3D_VIDEO_2D_PANEL && curState == OV_3D_VIDEO_2D_TV) ) {
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(FRAMEBUFFER_1);
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;
}
} else if( (ctx->state == OV_3D_VIDEO_2D_PANEL && curState == OV_3D_VIDEO_3D_TV) ) {
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(FRAMEBUFFER_0);
//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;
}
}
}
} else {
if ( (ctx->state == OV_2D_VIDEO_ON_TV) ||
(ctx->state == OV_3D_VIDEO_2D_TV && curState == OV_3D_VIDEO_2D_PANEL) ) {
LOGI("2D TV disconnected, close the control channel.");
obj->closeControlChannel(VG1_PIPE);
} else if (ctx->state == OV_3D_VIDEO_3D_TV && curState == OV_3D_VIDEO_2D_PANEL) {
LOGI("3D TV disconnected, close the control channels & open one for panel.");
// Close both the pipes' control channel
obj->closeControlChannel(VG0_PIPE);
obj->closeControlChannel(VG1_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 orienatation for channel 0", __func__);
return -1;
}
}
}
//update the context's state
ctx->state = curState;
return true;
}
#endif
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->readyToQueue = 0;
#if 0
/* 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);
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);
}
}
}
}
#endif
}
// if(param != OVERLAY_HDMI_ENABLE)
{
//Save the panel orientation
if (param == OVERLAY_TRANSFORM)
ctx->orientation = 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:
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:
break;
}
}
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;
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;
}
//setting the crop value
if(!ctx->pobjDataChannel[VG0_PIPE]->setCrop(
ctx->cropRect.x,ctx->cropRect.y,
ctx->cropRect.w,ctx->cropRect.h)) {
LOGE("%s:failed to crop pipe 0", __func__);
}
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 (!ctx->pobjDataChannel[i]->startDataChannel(ovid, rotid, size, i, true)) {
error_cleanup_data(ctx, i);
return -1;
}
//setting the crop value
if(!ctx->pobjDataChannel[i]->setCrop(
ctx->cropRect.x,ctx->cropRect.y,
ctx->cropRect.w,ctx->cropRect.h)) {
LOGE("%s:failed to crop pipe %d", __func__, i);
}
}
break;
case OV_3D_VIDEO_3D_TV:
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_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 channel %d", __func__, 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;
}
int overlay_queueBuffer(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;
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
// Check if readyToQueue is enabled.
overlay_shared_data data;
if(ctx->sharedMemBase != MAP_FAILED)
memcpy(&data, ctx->sharedMemBase, sizeof(data));
else
return false;
if(!data.readyToQueue) {
LOGE("Overlay is not ready to queue buffers");
return -1;
}
#if 0
if(data->state != ctx->state) {
LOGI("Data: State has changed from %d to %d", ctx->state, data->state);
if( (ctx->state == OV_2D_VIDEO_ON_PANEL) ||
(ctx->state == OV_3D_VIDEO_2D_PANEL && data->state == OV_3D_VIDEO_2D_TV) ) {
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;
}
//setting the crop value
if(ctx->format3D) {
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__);
}
} else {
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;
}
} else if( (ctx->state == OV_3D_VIDEO_2D_PANEL && data->state == OV_3D_VIDEO_3D_TV) ) {
LOGI("3D TV connected, close data channel and open both data channels for 3DTV.");
//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);
} else if( (ctx->state == OV_2D_VIDEO_ON_TV) ||
(ctx->state == OV_3D_VIDEO_2D_TV && data->state == OV_3D_VIDEO_2D_PANEL) ) {
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;
} else if (ctx->state == OV_3D_VIDEO_3D_TV && data->state == OV_3D_VIDEO_2D_PANEL) {
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;
}
}
//update the context's state
ctx->state = data->state;
}
#endif
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_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 -1;
}
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_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;
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_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;
}