/* // 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