/*
// 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 <Hwcomposer.h>
#include <common/base/Drm.h>
#include <PhysicalDevice.h>
namespace android {
namespace intel {
PhysicalDevice::PhysicalDevice(uint32_t disp, uint32_t type, Hwcomposer& hwc, DisplayPlaneManager& dpm)
: mDisp(disp),
mType(type),
mHwc(hwc),
mDisplayPlaneManager(dpm),
mActiveDisplayConfig(-1),
mBlankControl(NULL),
mVsyncObserver(NULL),
mLayerList(NULL),
mConnected(false),
mBlank(false),
mDisplayState(DEVICE_DISPLAY_ON),
mInitialized(false)
{
CTRACE();
switch (type) {
case DEVICE_PRIMARY:
mName = "Primary";
break;
case DEVICE_EXTERNAL:
mName = "External";
break;
default:
mName = "Unknown";
}
mDisplayConfigs.setCapacity(DEVICE_COUNT);
}
PhysicalDevice::~PhysicalDevice()
{
WARN_IF_NOT_DEINIT();
}
void PhysicalDevice::onGeometryChanged(hwc_display_contents_1_t *list)
{
if (!list) {
ELOGTRACE("list is NULL");
return;
}
ALOGTRACE("disp = %d, layer number = %d", mDisp, list->numHwLayers);
// NOTE: should NOT be here
if (mLayerList) {
WLOGTRACE("mLayerList exists");
DEINIT_AND_DELETE_OBJ(mLayerList);
}
// create a new layer list
mLayerList = new HwcLayerList(list, mType);
if (!mLayerList) {
WLOGTRACE("failed to create layer list");
}
}
bool PhysicalDevice::prePrepare(hwc_display_contents_1_t *display)
{
RETURN_FALSE_IF_NOT_INIT();
// for a null list, delete hwc list
if (!mConnected || !display || mBlank) {
if (mLayerList) {
DEINIT_AND_DELETE_OBJ(mLayerList);
}
return true;
}
// check if geometry is changed, if changed delete list
if ((display->flags & HWC_GEOMETRY_CHANGED) && mLayerList) {
DEINIT_AND_DELETE_OBJ(mLayerList);
}
return true;
}
bool PhysicalDevice::prepare(hwc_display_contents_1_t *display)
{
RETURN_FALSE_IF_NOT_INIT();
if (!mConnected || !display || mBlank)
return true;
// check if geometry is changed
if (display->flags & HWC_GEOMETRY_CHANGED) {
onGeometryChanged(display);
}
if (!mLayerList) {
WLOGTRACE("null HWC layer list");
return true;
}
// update list with new list
return mLayerList->update(display);
}
bool PhysicalDevice::commit(hwc_display_contents_1_t *display, IDisplayContext *context)
{
RETURN_FALSE_IF_NOT_INIT();
if (!display || !context || !mLayerList || mBlank) {
return true;
}
/* Sync the arguments of Frame Buffer Target layer updated in SurfaceFlinger. */
mLayerList->updateFBT(display);
return context->commitContents(display, mLayerList);
}
bool PhysicalDevice::vsyncControl(bool enabled)
{
RETURN_FALSE_IF_NOT_INIT();
ALOGTRACE("disp = %d, enabled = %d", mDisp, enabled);
return mVsyncObserver->control(enabled);
}
bool PhysicalDevice::blank(bool blank)
{
RETURN_FALSE_IF_NOT_INIT();
mBlank = blank;
bool ret = mBlankControl->blank(mDisp, blank);
if (ret == false) {
ELOGTRACE("failed to blank device");
return false;
}
return true;
}
bool PhysicalDevice::getDisplaySize(int *width, int *height)
{
RETURN_FALSE_IF_NOT_INIT();
Mutex::Autolock _l(mLock);
if (!width || !height) {
ELOGTRACE("invalid parameters");
return false;
}
*width = 0;
*height = 0;
drmModeModeInfo mode;
Drm *drm = Hwcomposer::getInstance().getDrm();
bool ret = drm->getModeInfo(mType, mode);
if (!ret) {
return false;
}
*width = mode.hdisplay;
*height = mode.vdisplay;
return true;
}
template <typename T>
static inline T min(T a, T b) {
return a<b ? a : b;
}
bool PhysicalDevice::getDisplayConfigs(uint32_t *configs,
size_t *numConfigs)
{
RETURN_FALSE_IF_NOT_INIT();
Mutex::Autolock _l(mLock);
if (!mConnected) {
ILOGTRACE("device is not connected");
return false;
}
if (!configs || !numConfigs || *numConfigs < 1) {
ELOGTRACE("invalid parameters");
return false;
}
// fill in all config handles
*numConfigs = min(*numConfigs, mDisplayConfigs.size());
for (int i = 0; i < static_cast<int>(*numConfigs); i++) {
configs[i] = i;
}
return true;
}
bool PhysicalDevice::getDisplayAttributes(uint32_t config,
const uint32_t *attributes,
int32_t *values)
{
RETURN_FALSE_IF_NOT_INIT();
Mutex::Autolock _l(mLock);
if (!mConnected) {
ILOGTRACE("device is not connected");
return false;
}
if (!attributes || !values) {
ELOGTRACE("invalid parameters");
return false;
}
DisplayConfig *configChosen = mDisplayConfigs.itemAt(config);
if (!configChosen) {
WLOGTRACE("failed to get display config");
return false;
}
int i = 0;
while (attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE) {
switch (attributes[i]) {
case HWC_DISPLAY_VSYNC_PERIOD:
if (configChosen->getRefreshRate()) {
values[i] = 1e9 / configChosen->getRefreshRate();
} else {
ELOGTRACE("refresh rate is 0!!!");
values[i] = 0;
}
break;
case HWC_DISPLAY_WIDTH:
values[i] = configChosen->getWidth();
break;
case HWC_DISPLAY_HEIGHT:
values[i] = configChosen->getHeight();
break;
case HWC_DISPLAY_DPI_X:
values[i] = configChosen->getDpiX() * 1000.0f;
break;
case HWC_DISPLAY_DPI_Y:
values[i] = configChosen->getDpiY() * 1000.0f;
break;
default:
ELOGTRACE("unknown attribute %d", attributes[i]);
break;
}
i++;
}
return true;
}
bool PhysicalDevice::compositionComplete()
{
CTRACE();
// do nothing by default
return true;
}
void PhysicalDevice::removeDisplayConfigs()
{
for (size_t i = 0; i < mDisplayConfigs.size(); i++) {
DisplayConfig *config = mDisplayConfigs.itemAt(i);
delete config;
}
mDisplayConfigs.clear();
mActiveDisplayConfig = -1;
}
bool PhysicalDevice::detectDisplayConfigs()
{
Mutex::Autolock _l(mLock);
Drm *drm = Hwcomposer::getInstance().getDrm();
if (!drm->detect(mType)) {
ELOGTRACE("drm detection on device %d failed ", mType);
return false;
}
return updateDisplayConfigs();
}
bool PhysicalDevice::updateDisplayConfigs()
{
bool ret;
Drm *drm = Hwcomposer::getInstance().getDrm();
// reset display configs
removeDisplayConfigs();
// update device connection status
mConnected = drm->isConnected(mType);
if (!mConnected) {
return true;
}
// reset the number of display configs
mDisplayConfigs.setCapacity(1);
drmModeModeInfo mode;
ret = drm->getModeInfo(mType, mode);
if (!ret) {
ELOGTRACE("failed to get mode info");
mConnected = false;
return false;
}
uint32_t mmWidth, mmHeight;
ret = drm->getPhysicalSize(mType, mmWidth, mmHeight);
if (!ret) {
ELOGTRACE("failed to get physical size");
mConnected = false;
return false;
}
float physWidthInch = (float)mmWidth * 0.039370f;
float physHeightInch = (float)mmHeight * 0.039370f;
// use current drm mode, likely it's preferred mode
int dpiX = 0;
int dpiY = 0;
if (physWidthInch && physHeightInch) {
dpiX = mode.hdisplay / physWidthInch;
dpiY = mode.vdisplay / physHeightInch;
} else {
ELOGTRACE("invalid physical size, EDID read error?");
// don't bail out as it is not a fatal error
}
// use active fb dimension as config width/height
DisplayConfig *config = new DisplayConfig(mode.vrefresh,
mode.hdisplay,
mode.vdisplay,
dpiX, dpiY);
// add it to the front of other configs
mDisplayConfigs.push_front(config);
// init the active display config
mActiveDisplayConfig = 0;
drmModeModeInfoPtr modes;
drmModeModeInfoPtr compatMode;
int modeCount = 0;
modes = drm->detectAllConfigs(mType, &modeCount);
for (int i = 0; i < modeCount; i++) {
if (modes) {
compatMode = &modes[i];
if (!compatMode)
continue;
if (compatMode->hdisplay == mode.hdisplay &&
compatMode->vdisplay == mode.vdisplay &&
compatMode->vrefresh != mode.vrefresh) {
bool found = false;
for (size_t j = 0; j < mDisplayConfigs.size(); j++) {
DisplayConfig *config = mDisplayConfigs.itemAt(j);
if (config->getRefreshRate() == (int)compatMode->vrefresh) {
found = true;
break;
}
}
if (found) {
continue;
}
DisplayConfig *config = new DisplayConfig(compatMode->vrefresh,
compatMode->hdisplay,
compatMode->vdisplay,
dpiX, dpiY);
// add it to the end of configs
mDisplayConfigs.push_back(config);
}
}
}
return true;
}
bool PhysicalDevice::initialize()
{
CTRACE();
if (mType != DEVICE_PRIMARY && mType != DEVICE_EXTERNAL) {
ELOGTRACE("invalid device type");
return false;
}
// detect display configs
bool ret = detectDisplayConfigs();
if (ret == false) {
DEINIT_AND_RETURN_FALSE("failed to detect display config");
}
// create blank control
mBlankControl = createBlankControl();
if (!mBlankControl) {
DEINIT_AND_RETURN_FALSE("failed to create blank control");
}
// create vsync event observer
mVsyncObserver = new VsyncEventObserver(*this);
if (!mVsyncObserver || !mVsyncObserver->initialize()) {
DEINIT_AND_RETURN_FALSE("failed to create vsync observer");
}
mInitialized = true;
return true;
}
void PhysicalDevice::deinitialize()
{
if (mLayerList) {
DEINIT_AND_DELETE_OBJ(mLayerList);
}
DEINIT_AND_DELETE_OBJ(mVsyncObserver);
// destroy blank control
if (mBlankControl) {
delete mBlankControl;
mBlankControl = 0;
}
// remove configs
removeDisplayConfigs();
mInitialized = false;
}
bool PhysicalDevice::isConnected() const
{
RETURN_FALSE_IF_NOT_INIT();
return mConnected;
}
const char* PhysicalDevice::getName() const
{
return mName;
}
int PhysicalDevice::getType() const
{
return mType;
}
void PhysicalDevice::onVsync(int64_t timestamp)
{
RETURN_VOID_IF_NOT_INIT();
ALOGTRACE("timestamp = %lld", timestamp);
if (!mConnected)
return;
// notify hwc
mHwc.vsync(mType, timestamp);
}
void PhysicalDevice::dump(Dump& d)
{
d.append("-------------------------------------------------------------\n");
d.append("Device Name: %s (%s)\n", mName,
mConnected ? "connected" : "disconnected");
d.append("Display configs (count = %d):\n", mDisplayConfigs.size());
d.append(" CONFIG | VSYNC_PERIOD | WIDTH | HEIGHT | DPI_X | DPI_Y \n");
d.append("--------+--------------+-------+--------+-------+-------\n");
for (size_t i = 0; i < mDisplayConfigs.size(); i++) {
DisplayConfig *config = mDisplayConfigs.itemAt(i);
if (config) {
d.append("%s %2d | %4d | %5d | %4d | %3d | %3d \n",
(i == (size_t)mActiveDisplayConfig) ? "* " : " ",
i,
config->getRefreshRate(),
config->getWidth(),
config->getHeight(),
config->getDpiX(),
config->getDpiY());
}
}
// dump layer list
if (mLayerList)
mLayerList->dump(d);
}
bool PhysicalDevice::setPowerMode(int mode)
{
// TODO: set proper blanking modes for HWC 1.4 modes
switch (mode) {
case HWC_POWER_MODE_OFF:
case HWC_POWER_MODE_DOZE:
return blank(true);
case HWC_POWER_MODE_NORMAL:
case HWC_POWER_MODE_DOZE_SUSPEND:
return blank(false);
default:
return false;
}
return false;
}
int PhysicalDevice::getActiveConfig()
{
return mActiveDisplayConfig;
}
bool PhysicalDevice::setActiveConfig(int index)
{
// TODO: for now only implement in external
if (index == 0)
return true;
return false;
}
} // namespace intel
} // namespace android