/*
// Copyright (c) 2014 Intel Corporation
//
// 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 <common/utils/HwcTrace.h>
#include <utils/String8.h>
#include <ips/anniedale/AnnPlaneManager.h>
#include <ips/anniedale/AnnRGBPlane.h>
#include <ips/anniedale/AnnOverlayPlane.h>
#include <ips/anniedale/AnnCursorPlane.h>
#include <PlaneCapabilities.h>
namespace android {
namespace intel {
struct PlaneDescription {
char nickname;
int type;
int index;
};
static PlaneDescription PLANE_DESC[] =
{
// nickname must be continous and start with 'A',
// it is used to fast locate plane index and type
{'A', DisplayPlane::PLANE_PRIMARY, 0},
{'B', DisplayPlane::PLANE_PRIMARY, 1},
{'C', DisplayPlane::PLANE_PRIMARY, 2},
{'D', DisplayPlane::PLANE_SPRITE, 0},
{'E', DisplayPlane::PLANE_SPRITE, 1},
{'F', DisplayPlane::PLANE_SPRITE, 2},
{'G', DisplayPlane::PLANE_OVERLAY, 0}, // nickname for Overlay A
{'H', DisplayPlane::PLANE_OVERLAY, 1}, // nickname for Overlay C
{'I', DisplayPlane::PLANE_CURSOR, 0}, // nickname for cursor A
{'J', DisplayPlane::PLANE_CURSOR, 1}, // nickname for cursor B
{'K', DisplayPlane::PLANE_CURSOR, 2} // nickname for cursor C
};
struct ZOrderDescription {
int index; // based on overlay position
const char *zorder;
};
// If overlay is in the bottom of Z order, two legitimate combinations are Oa, D, E, F
// and Oc, D, E, F. However, plane A has to be part of the blending chain as it can't
// be disabled [HW bug]. The only legitimate combinations including overlay and plane A is:
// A, Oa, E, F
// A, Oc, E, F
#define OVERLAY_HW_WORKAROUND
// Cursor plane can be placed on top of any plane below and is intentionally ignored
// in the zorder table.
static ZOrderDescription PIPE_A_ZORDER_DESC[] =
{
{0, "ADEF"}, // no overlay
#ifndef OVERLAY_HW_WORKAROUND
{1, "GDEF"}, // overlay A at bottom (1 << 0)
{1, "HDEF"}, // overlay C at bottom (1 << 0)
#else
{1, "GEF"}, // overlay A at bottom (1 << 0)
{1, "HEF"}, // overlay C at bottom (1 << 0)
#endif
{2, "AGEF"}, // overlay A at next to bottom (1 << 1)
{2, "AHEF"}, // overlay C at next to bottom (1 << 1)
#ifndef OVERLAY_HW_WORKAROUND
{3, "GHEF"}, // overlay A, C at bottom
#else
{3, "GHF"}, // overlay A, C at bottom
#endif
{4, "ADGF"}, // overlay A at next to top (1 << 2)
{4, "ADHF"}, // overlay C at next to top (1 << 2)
{6, "AGHF"}, // overlay A, C in between
{8, "ADEG"}, // overlay A at top (1 << 3)
{8, "ADEH"}, // overlay C at top (1 <<3)
{12, "ADGH"} // overlay A, C at top
};
// use overlay C over overlay A if possible on pipe B
// workaround: use only overlay C on pipe B
static ZOrderDescription PIPE_B_ZORDER_DESC[] =
{
{0, "BD"}, // no overlay
{1, "GBD"}, // overlay A at bottom (1 << 0)
{1, "HBD"}, // overlay C at bottom (1 << 0)
{2, "BGD"}, // overlay A at middle (1 << 1)
{2, "BHD"}, // overlay C at middle (1 << 1)
{3, "GHBD"}, // overlay A and C at bottom ( 1 << 0 + 1 << 1)
{4, "BDG"}, // overlay A at top (1 << 2)
{4, "BDH"}, // overlay C at top (1 << 2)
{6, "BGHD"}, // overlay A/C at middle 1 << 1 + 1 << 2)
{12, "BDGH"} // overlay A/C at top (1 << 2 + 1 << 3)
};
static const int PIPE_A_ZORDER_COMBINATIONS =
sizeof(PIPE_A_ZORDER_DESC)/sizeof(ZOrderDescription);
static const int PIPE_B_ZORDER_COMBINATIONS =
sizeof(PIPE_B_ZORDER_DESC)/sizeof(ZOrderDescription);
AnnPlaneManager::AnnPlaneManager()
: DisplayPlaneManager()
{
}
AnnPlaneManager::~AnnPlaneManager()
{
}
bool AnnPlaneManager::initialize()
{
mSpritePlaneCount = 3; // Sprite D, E, F
mOverlayPlaneCount = 2; // Overlay A, C
mPrimaryPlaneCount = 3; // Primary A, B, C
mCursorPlaneCount = 3;
return DisplayPlaneManager::initialize();
}
void AnnPlaneManager::deinitialize()
{
DisplayPlaneManager::deinitialize();
}
DisplayPlane* AnnPlaneManager::allocPlane(int index, int type)
{
DisplayPlane *plane = NULL;
switch (type) {
case DisplayPlane::PLANE_PRIMARY:
plane = new AnnRGBPlane(index, DisplayPlane::PLANE_PRIMARY, index/*disp*/);
break;
case DisplayPlane::PLANE_SPRITE:
plane = new AnnRGBPlane(index, DisplayPlane::PLANE_SPRITE, 0/*disp*/);
break;
case DisplayPlane::PLANE_OVERLAY:
plane = new AnnOverlayPlane(index, 0/*disp*/);
break;
case DisplayPlane::PLANE_CURSOR:
plane = new AnnCursorPlane(index, index /*disp */);
break;
default:
ELOGTRACE("unsupported type %d", type);
break;
}
if (plane && !plane->initialize(DisplayPlane::MIN_DATA_BUFFER_COUNT)) {
ELOGTRACE("failed to initialize plane.");
DEINIT_AND_DELETE_OBJ(plane);
}
return plane;
}
bool AnnPlaneManager::isValidZOrder(int dsp, ZOrderConfig& config)
{
int size = (int)config.size();
if (size == 0 || size > 5) {
VLOGTRACE("invalid z order config size %d", size);
return false;
}
if (dsp == IDisplayDevice::DEVICE_PRIMARY) {
int firstOverlay = -1;
for (int i = 0; i < size; i++) {
if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY) {
firstOverlay = i;
break;
}
}
int sprites = 0;
for (int i = 0; i < size; i++) {
if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY &&
config[i]->planeType != DisplayPlane::PLANE_CURSOR) {
sprites++;
}
}
if (firstOverlay < 0 && sprites > 4) {
VLOGTRACE("not capable to support more than 4 sprite layers");
return false;
}
#ifdef OVERLAY_HW_WORKAROUND
if (firstOverlay == 0 && sprites > 2) {
VLOGTRACE("not capable to support 3 sprite layers on top of overlay");
return false;
}
#endif
} else if (dsp == IDisplayDevice::DEVICE_EXTERNAL) {
int sprites = 0;
for (int i = 0; i < size; i++) {
if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY &&
config[i]->planeType != DisplayPlane::PLANE_CURSOR) {
sprites++;
}
}
if (sprites > 2) {
ELOGTRACE("number of sprite: %d, maximum 1 sprite and 1 primary supported on pipe 1", sprites);
return false;
}
} else {
ELOGTRACE("invalid display device %d", dsp);
return false;
}
return true;
}
bool AnnPlaneManager::assignPlanes(int dsp, ZOrderConfig& config)
{
if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) {
ELOGTRACE("invalid display device %d", dsp);
return false;
}
int size = (int)config.size();
// calculate index based on overlay Z order position
int index = 0;
for (int i = 0; i < size; i++) {
if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY) {
index += (1 << i);
}
}
int combinations = 0;
if (dsp == IDisplayDevice::DEVICE_PRIMARY)
combinations = PIPE_A_ZORDER_COMBINATIONS;
else
combinations = PIPE_B_ZORDER_COMBINATIONS;
ZOrderDescription *zorderDesc = NULL;
for (int i = 0; i < combinations; i++) {
if (dsp == IDisplayDevice::DEVICE_PRIMARY)
zorderDesc = &PIPE_A_ZORDER_DESC[i];
else
zorderDesc = &PIPE_B_ZORDER_DESC[i];
if (zorderDesc->index != index)
continue;
if (assignPlanes(dsp, config, zorderDesc->zorder)) {
VLOGTRACE("zorder assigned %s", zorderDesc->zorder);
return true;
}
}
return false;
}
bool AnnPlaneManager::assignPlanes(int dsp, ZOrderConfig& config, const char *zorder)
{
// zorder string does not include cursor plane, therefore cursor layer needs to be handled
// in a special way. Cursor layer must be on top of zorder and no more than one cursor layer.
int size = (int)config.size();
if (zorder == NULL || size == 0) {
//DLOGTRACE("invalid zorder or ZOrder config.");
return false;
}
int zorderLen = (int)strlen(zorder);
// test if plane is available
for (int i = 0; i < size; i++) {
if (config[i]->planeType == DisplayPlane::PLANE_CURSOR) {
if (i != size - 1) {
ELOGTRACE("invalid zorder of cursor layer");
return false;
}
PlaneDescription& desc = PLANE_DESC['I' - 'A' + dsp];
if (!isFreePlane(desc.type, desc.index)) {
ELOGTRACE("cursor plane is not available");
return false;
}
continue;
}
if (i >= zorderLen) {
DLOGTRACE("index of ZOrderConfig is out of bound");
return false;
}
char id = *(zorder + i);
PlaneDescription& desc = PLANE_DESC[id - 'A'];
if (!isFreePlane(desc.type, desc.index)) {
DLOGTRACE("plane type %d index %d is not available", desc.type, desc.index);
return false;
}
#if 0
// plane type check
if (config[i]->planeType == DisplayPlane::PLANE_OVERLAY &&
desc.type != DisplayPlane::PLANE_OVERLAY) {
ELOGTRACE("invalid plane type %d, expected %d", desc.type, config[i]->planeType);
return false;
}
if (config[i]->planeType != DisplayPlane::PLANE_OVERLAY) {
if (config[i]->planeType != DisplayPlane::PLANE_PRIMARY &&
config[i]->planeType != DisplayPlane::PLANE_SPRITE) {
ELOGTRACE("invalid plane type %d,", config[i]->planeType);
return false;
}
if (desc.type != DisplayPlane::PLANE_PRIMARY &&
desc.type != DisplayPlane::PLANE_SPRITE) {
ELOGTRACE("invalid plane type %d, expected %d", desc.type, config[i]->planeType);
return false;
}
}
#endif
if (desc.type == DisplayPlane::PLANE_OVERLAY && desc.index == 1 &&
config[i]->hwcLayer->getTransform() != 0) {
DLOGTRACE("overlay C does not support transform");
return false;
}
}
bool primaryPlaneActive = false;
// allocate planes
for (int i = 0; i < size; i++) {
if (config[i]->planeType == DisplayPlane::PLANE_CURSOR) {
PlaneDescription& desc = PLANE_DESC['I' - 'A' + dsp];
ZOrderLayer *zLayer = config.itemAt(i);
zLayer->plane = getPlane(desc.type, desc.index);
if (zLayer->plane == NULL) {
ELOGTRACE("failed to get cursor plane, should never happen!");
}
continue;
}
char id = *(zorder + i);
PlaneDescription& desc = PLANE_DESC[id - 'A'];
ZOrderLayer *zLayer = config.itemAt(i);
zLayer->plane = getPlane(desc.type, desc.index);
if (zLayer->plane == NULL) {
ELOGTRACE("failed to get plane, should never happen!");
}
// override type
zLayer->planeType = desc.type;
if (desc.type == DisplayPlane::PLANE_PRIMARY) {
primaryPlaneActive = true;
}
}
// setup Z order
int slot = 0;
for (int i = 0; i < size; i++) {
slot = i;
#ifdef OVERLAY_HW_WORKAROUND
if (!primaryPlaneActive && config[i]->planeType == DisplayPlane::PLANE_OVERLAY) {
slot += 1;
}
#endif
config[i]->plane->setZOrderConfig(config, (void *)slot);
config[i]->plane->enable();
}
#if 0
DLOGTRACE("config size %d, zorder %s", size, zorder);
for (int i = 0; i < size; i++) {
const ZOrderLayer *l = config.itemAt(i);
ILOGTRACE("%d: plane type %d, index %d, zorder %d",
i, l->planeType, l->plane->getIndex(), l->zorder);
}
#endif
return true;
}
void* AnnPlaneManager::getZOrderConfig() const
{
return NULL;
}
int AnnPlaneManager::getFreePlanes(int dsp, int type)
{
RETURN_NULL_IF_NOT_INIT();
if (type != DisplayPlane::PLANE_SPRITE) {
return DisplayPlaneManager::getFreePlanes(dsp, type);
}
if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) {
ELOGTRACE("invalid display device %d", dsp);
return 0;
}
uint32_t freePlanes = mFreePlanes[type] | mReclaimedPlanes[type];
int start = 0;
int stop = mSpritePlaneCount;
if (dsp == IDisplayDevice::DEVICE_EXTERNAL) {
// only Sprite D (index 0) can be assigned to pipe 1
// Sprites E/F (index 1, 2) are fixed on pipe 0
stop = 1;
}
int count = 0;
for (int i = start; i < stop; i++) {
if ((1 << i) & freePlanes) {
count++;
}
}
return count;
}
} // namespace intel
} // namespace android