sdm: HDMI as primary and headless display support
Add support for a NULL display that closes all fences and allows
SurfaceFlinger to work without a display connected.
Also add support for ability to connect/disconnect and suspend/resume
HDMI primary display by using NULL display.
CRs-Fixed: 814136
Change-Id: I6ec1149de2a6e5e34aeae0a10e9bc226edd4aa04
diff --git a/sdm/include/core/core_interface.h b/sdm/include/core/core_interface.h
index 582222f..8f97cb4 100644
--- a/sdm/include/core/core_interface.h
+++ b/sdm/include/core/core_interface.h
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -88,6 +88,18 @@
kBwModeMax, //!< Limiter for maximum available bandwidth modes.
};
+
+/*! @brief Information on hardware for the first display
+
+ @details This structure returns the display type of the first display on the device
+ (internal display or HDMI etc) and whether it is currently connected,
+
+*/
+struct HWDisplayInterfaceInfo {
+ DisplayType type;
+ bool is_connected;
+};
+
/*! @brief Display core interface.
@details This class defines display core interfaces. It contains methods which client shall use
@@ -172,6 +184,18 @@
*/
virtual DisplayError SetMaxBandwidthMode(HWBwModes mode) = 0;
+ /*! @brief Method to get characteristics of the first display.
+
+ @details Client shall use this method to determine if the first display is HDMI, and whether
+ it is currently connected.
+
+ @param[in] hw_disp_info structure that this method will fill up with info.
+
+ @return \link DisplayError \endlink
+
+ */
+ virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) = 0;
+
protected:
virtual ~CoreInterface() { }
diff --git a/sdm/libs/core/core_impl.cpp b/sdm/libs/core/core_impl.cpp
index 424be43..82f578e 100644
--- a/sdm/libs/core/core_impl.cpp
+++ b/sdm/libs/core/core_impl.cpp
@@ -212,5 +212,9 @@
return comp_mgr_.SetMaxBandwidthMode(mode);
}
+DisplayError CoreImpl::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) {
+ return hw_info_intf_->GetFirstDisplayInterfaceType(hw_disp_info);
+}
+
} // namespace sdm
diff --git a/sdm/libs/core/core_impl.h b/sdm/libs/core/core_impl.h
index 06ca3d9..97e8655 100644
--- a/sdm/libs/core/core_impl.h
+++ b/sdm/libs/core/core_impl.h
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
@@ -58,6 +58,7 @@
DisplayInterface **intf);
virtual DisplayError DestroyDisplay(DisplayInterface *intf);
virtual DisplayError SetMaxBandwidthMode(HWBwModes mode);
+ virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
protected:
Locker locker_;
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 9fce6a6..c230674 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -747,4 +747,15 @@
return kErrorNotSupported;
}
+DisplayError DisplayBase::SetVSyncState(bool enable) {
+ DisplayError error = kErrorNone;
+ if (vsync_enable_ != enable) {
+ error = hw_intf_->SetVSyncState(enable);
+ if (error == kErrorNone) {
+ vsync_enable_ = enable;
+ }
+ }
+ return error;
+}
+
} // namespace sdm
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 27d10f6..7242156 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -74,6 +74,7 @@
virtual DisplayError SetCursorPosition(int x, int y);
virtual DisplayError GetRefreshRateRange(uint32_t *min_refresh_rate, uint32_t *max_refresh_rate);
virtual DisplayError GetPanelBrightness(int *level);
+ virtual DisplayError SetVSyncState(bool enable);
protected:
// DumpImpl method
diff --git a/sdm/libs/core/display_hdmi.cpp b/sdm/libs/core/display_hdmi.cpp
index 331ddcc..12ddd51 100644
--- a/sdm/libs/core/display_hdmi.cpp
+++ b/sdm/libs/core/display_hdmi.cpp
@@ -166,7 +166,7 @@
DisplayError DisplayHDMI::SetVSyncState(bool enable) {
SCOPE_LOCK(locker_);
- return kErrorNotSupported;
+ return DisplayBase::SetVSyncState(enable);
}
void DisplayHDMI::SetIdleTimeoutMs(uint32_t timeout_ms) { }
@@ -368,5 +368,15 @@
event_handler_->CECMessage(message);
}
+DisplayError DisplayHDMI::VSync(int64_t timestamp) {
+ if (vsync_enable_) {
+ DisplayEventVSync vsync;
+ vsync.timestamp = timestamp;
+ event_handler_->VSync(vsync);
+ }
+
+ return kErrorNone;
+}
+
} // namespace sdm
diff --git a/sdm/libs/core/display_hdmi.h b/sdm/libs/core/display_hdmi.h
index 15a0019..88e553d 100644
--- a/sdm/libs/core/display_hdmi.h
+++ b/sdm/libs/core/display_hdmi.h
@@ -67,7 +67,7 @@
virtual DisplayError SetCursorPosition(int x, int y);
// Implement the HWEventHandlers
- virtual DisplayError VSync(int64_t timestamp) { return kErrorNone; }
+ virtual DisplayError VSync(int64_t timestamp);
virtual DisplayError Blank(bool blank) { return kErrorNone; }
virtual void IdleTimeout() { }
virtual void ThermalEvent(int64_t thermal_level) { }
@@ -81,7 +81,8 @@
Locker locker_;
HWScanSupport scan_support_;
std::map<LayerBufferS3DFormat, HWS3DMode> s3d_format_to_mode_;
- std::vector<const char *> event_list_ = {"cec/rd_msg", "thread_exit"};
+ std::vector<const char *> event_list_ = {"vsync_event", "idle_notify", "cec/rd_msg",
+ "thread_exit"};
};
} // namespace sdm
diff --git a/sdm/libs/core/display_primary.cpp b/sdm/libs/core/display_primary.cpp
index 9e12c8a..e5751df 100644
--- a/sdm/libs/core/display_primary.cpp
+++ b/sdm/libs/core/display_primary.cpp
@@ -193,15 +193,7 @@
DisplayError DisplayPrimary::SetVSyncState(bool enable) {
SCOPE_LOCK(locker_);
- DisplayError error = kErrorNone;
- if (vsync_enable_ != enable) {
- error = hw_intf_->SetVSyncState(enable);
- if (error == kErrorNone) {
- vsync_enable_ = enable;
- }
- }
-
- return error;
+ return DisplayBase::SetVSyncState(enable);
}
void DisplayPrimary::SetIdleTimeoutMs(uint32_t timeout_ms) {
diff --git a/sdm/libs/core/fb/hw_device.cpp b/sdm/libs/core/fb/hw_device.cpp
index 33974bf..48560cb 100644
--- a/sdm/libs/core/fb/hw_device.cpp
+++ b/sdm/libs/core/fb/hw_device.cpp
@@ -1030,7 +1030,12 @@
}
DisplayError HWDevice::SetVSyncState(bool enable) {
- return kErrorNotSupported;
+ int vsync_on = enable ? 1 : 0;
+ if (Sys::ioctl_(device_fd_, MSMFB_OVERLAY_VSYNC_CTRL, &vsync_on) < 0) {
+ IOCTL_LOGE(MSMFB_OVERLAY_VSYNC_CTRL, device_type_);
+ return kErrorHardware;
+ }
+ return kErrorNone;
}
void HWDevice::SetIdleTimeoutMs(uint32_t timeout_ms) {
diff --git a/sdm/libs/core/fb/hw_info.cpp b/sdm/libs/core/fb/hw_info.cpp
index ac8b8a6..f6f664a 100644
--- a/sdm/libs/core/fb/hw_info.cpp
+++ b/sdm/libs/core/fb/hw_info.cpp
@@ -524,5 +524,57 @@
hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats));
}
+DisplayError HWInfo::GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) {
+ char *stringbuffer = reinterpret_cast<char *>(malloc(kMaxStringLength));
+ if (stringbuffer == NULL) {
+ DLOGE("Failed to allocate Stringbuffer");
+ return kErrorMemory;
+ }
+
+ char *line = stringbuffer;
+ size_t len = kMaxStringLength;
+ ssize_t read;
+
+ FILE *fileptr = Sys::fopen_("/sys/class/graphics/fb0/msm_fb_type", "r");
+ if (!fileptr) {
+ free(stringbuffer);
+ return kErrorHardware;
+ }
+
+ if ((read = Sys::getline_(&line, &len, fileptr)) != -1) {
+ if (!strncmp(line, "dtv panel", strlen("dtv panel"))) {
+ hw_disp_info->type = kHDMI;
+ DLOGI("First display is HDMI");
+ } else {
+ hw_disp_info->type = kPrimary;
+ DLOGI("First display is internal display");
+ }
+ } else {
+ free(stringbuffer);
+ fclose(fileptr);
+ return kErrorHardware;
+ }
+
+ fclose(fileptr);
+
+ fileptr = Sys::fopen_("/sys/class/graphics/fb0/connected", "r");
+ if (!fileptr) {
+ // If fb0 is for a DSI/connected panel, then connected node will not exist
+ hw_disp_info->is_connected = true;
+ } else {
+ if ((read = Sys::getline_(&line, &len, fileptr)) != -1) {
+ hw_disp_info->is_connected = (!strncmp(line, "1", strlen("1")));
+ } else {
+ fclose(fileptr);
+ free(stringbuffer);
+ return kErrorHardware;
+ }
+ fclose(fileptr);
+ }
+
+ free(stringbuffer);
+ return kErrorNone;
+}
+
} // namespace sdm
diff --git a/sdm/libs/core/fb/hw_info.h b/sdm/libs/core/fb/hw_info.h
index ba98f87..f22acb0 100644
--- a/sdm/libs/core/fb/hw_info.h
+++ b/sdm/libs/core/fb/hw_info.h
@@ -26,6 +26,7 @@
#define __HW_INFO_H__
#include <core/sdm_types.h>
+#include <core/core_interface.h>
#include <private/hw_info_types.h>
#include <linux/msm_mdp.h>
#include "hw_info_interface.h"
@@ -39,6 +40,7 @@
class HWInfo: public HWInfoInterface {
public:
virtual DisplayError GetHWResourceInfo(HWResourceInfo *hw_resource);
+ virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
private:
virtual DisplayError GetHWRotatorInfo(HWResourceInfo *hw_resource);
diff --git a/sdm/libs/core/fb/hw_primary.cpp b/sdm/libs/core/fb/hw_primary.cpp
index da88623..84eea2a 100644
--- a/sdm/libs/core/fb/hw_primary.cpp
+++ b/sdm/libs/core/fb/hw_primary.cpp
@@ -487,14 +487,7 @@
DisplayError HWPrimary::SetVSyncState(bool enable) {
DTRACE_SCOPED();
-
- int vsync_on = enable ? 1 : 0;
- if (Sys::ioctl_(device_fd_, MSMFB_OVERLAY_VSYNC_CTRL, &vsync_on) < 0) {
- IOCTL_LOGE(MSMFB_OVERLAY_VSYNC_CTRL, device_type_);
- return kErrorHardware;
- }
-
- return kErrorNone;
+ return HWDevice::SetVSyncState(enable);
}
DisplayError HWPrimary::SetDisplayMode(const HWDisplayMode hw_display_mode) {
diff --git a/sdm/libs/core/fb/hw_virtual.h b/sdm/libs/core/fb/hw_virtual.h
index f587230..330a067 100644
--- a/sdm/libs/core/fb/hw_virtual.h
+++ b/sdm/libs/core/fb/hw_virtual.h
@@ -34,6 +34,7 @@
static DisplayError Create(HWInterface **intf, HWInfoInterface *hw_info_intf,
BufferSyncHandler *buffer_sync_handler);
static DisplayError Destroy(HWInterface *intf);
+ virtual DisplayError SetVSyncState(bool enable) { return kErrorNotSupported; }
protected:
HWVirtual(BufferSyncHandler *buffer_sync_handler, HWInfoInterface *hw_info_intf);
diff --git a/sdm/libs/core/hw_info_interface.h b/sdm/libs/core/hw_info_interface.h
index 014ab0d..401c8bf 100644
--- a/sdm/libs/core/hw_info_interface.h
+++ b/sdm/libs/core/hw_info_interface.h
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
@@ -26,6 +26,7 @@
#define __HW_INFO_INTERFACE_H__
#include <inttypes.h>
+#include <core/core_interface.h>
#include <private/hw_info_types.h>
namespace sdm {
@@ -35,6 +36,7 @@
static DisplayError Create(HWInfoInterface **intf);
static DisplayError Destroy(HWInfoInterface *intf);
virtual DisplayError GetHWResourceInfo(HWResourceInfo *hw_resource) = 0;
+ virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info) = 0;
protected:
virtual ~HWInfoInterface() { }
diff --git a/sdm/libs/hwc/Android.mk b/sdm/libs/hwc/Android.mk
index 8ce3757..795ba39 100644
--- a/sdm/libs/hwc/Android.mk
+++ b/sdm/libs/hwc/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES := hwc_session.cpp \
hwc_display.cpp \
+ hwc_display_null.cpp \
hwc_display_primary.cpp \
hwc_display_external.cpp \
hwc_display_virtual.cpp \
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index 081ad37..5dd0e6b 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -74,9 +74,10 @@
}
HWCDisplay::HWCDisplay(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, DisplayType type,
- int id, bool needs_blit, qService::QService *qservice)
+ int id, bool needs_blit, qService::QService *qservice,
+ DisplayClass display_class)
: core_intf_(core_intf), hwc_procs_(hwc_procs), type_(type), id_(id), needs_blit_(needs_blit),
- qservice_(qservice) {
+ qservice_(qservice), display_class_(display_class) {
}
int HWCDisplay::Init() {
@@ -1581,4 +1582,8 @@
return refresh_rate;
}
+DisplayClass HWCDisplay::GetDisplayClass() {
+ return display_class_;
+}
+
} // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display.h b/sdm/libs/hwc/hwc_display.h
index 9466633..8a4d5b8 100644
--- a/sdm/libs/hwc/hwc_display.h
+++ b/sdm/libs/hwc/hwc_display.h
@@ -36,6 +36,16 @@
class BlitEngine;
+// Subclasses set this to their type. This has to be different from DisplayType.
+// This is to avoid RTTI and dynamic_cast
+enum DisplayClass {
+ DISPLAY_CLASS_PRIMARY,
+ DISPLAY_CLASS_EXTERNAL,
+ DISPLAY_CLASS_VIRTUAL,
+ DISPLAY_CLASS_NULL
+};
+
+
class HWCDisplay : public DisplayEventHandler {
public:
virtual ~HWCDisplay() { }
@@ -91,6 +101,7 @@
PPDisplayAPIPayload *out_payload,
PPPendingParams *pending_action);
int GetVisibleDisplayRect(hwc_rect_t* rect);
+ DisplayClass GetDisplayClass();
protected:
enum DisplayStatus {
@@ -133,7 +144,7 @@
};
HWCDisplay(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, DisplayType type, int id,
- bool needs_blit, qService::QService *qservice);
+ bool needs_blit, qService::QService *qservice, DisplayClass display_class);
// DisplayEventHandler methods
virtual DisplayError VSync(const DisplayEventVSync &vsync);
@@ -215,6 +226,7 @@
void ResetLayerCacheStack();
BlitEngine *blit_engine_ = NULL;
qService::QService *qservice_ = NULL;
+ DisplayClass display_class_;
};
inline int HWCDisplay::Perform(uint32_t operation, ...) {
diff --git a/sdm/libs/hwc/hwc_display_external.cpp b/sdm/libs/hwc/hwc_display_external.cpp
index c36582f..813b123 100644
--- a/sdm/libs/hwc/hwc_display_external.cpp
+++ b/sdm/libs/hwc/hwc_display_external.cpp
@@ -39,8 +39,14 @@
namespace sdm {
int HWCDisplayExternal::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
- uint32_t primary_width, uint32_t primary_height,
qService::QService *qservice, HWCDisplay **hwc_display) {
+ return Create(core_intf, hwc_procs, 0, 0, qservice, false, hwc_display);
+}
+
+int HWCDisplayExternal::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+ uint32_t primary_width, uint32_t primary_height,
+ qService::QService *qservice, bool use_primary_res,
+ HWCDisplay **hwc_display) {
uint32_t external_width = 0;
uint32_t external_height = 0;
@@ -53,10 +59,19 @@
hwc_display_external->GetPanelResolution(&external_width, &external_height);
- int downscale_enabled = 0;
- HWCDebugHandler::Get()->GetProperty("sdm.debug.downscale_external", &downscale_enabled);
- if (downscale_enabled) {
- GetDownscaleResolution(primary_width, primary_height, &external_width, &external_height);
+ if (primary_width && primary_height) {
+ // use_primary_res means HWCDisplayExternal should directly set framebuffer resolution to the
+ // provided primary_width and primary_height
+ if (use_primary_res) {
+ external_width = primary_width;
+ external_height = primary_height;
+ } else {
+ int downscale_enabled = 0;
+ HWCDebugHandler::Get()->GetProperty("sdm.debug.downscale_external", &downscale_enabled);
+ if (downscale_enabled) {
+ GetDownscaleResolution(primary_width, primary_height, &external_width, &external_height);
+ }
+ }
}
status = hwc_display_external->SetFrameBufferResolution(external_width, external_height);
@@ -77,7 +92,8 @@
HWCDisplayExternal::HWCDisplayExternal(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
qService::QService *qservice)
- : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice) {
+ : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice,
+ DISPLAY_CLASS_EXTERNAL) {
}
int HWCDisplayExternal::Prepare(hwc_display_contents_1_t *content_list) {
diff --git a/sdm/libs/hwc/hwc_display_external.h b/sdm/libs/hwc/hwc_display_external.h
index d9f894d..436190d 100644
--- a/sdm/libs/hwc/hwc_display_external.h
+++ b/sdm/libs/hwc/hwc_display_external.h
@@ -32,8 +32,10 @@
class HWCDisplayExternal : public HWCDisplay {
public:
static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs, uint32_t primary_width,
- uint32_t primary_height, qService::QService *qservice,
+ uint32_t primary_height, qService::QService *qservice, bool use_primary_res,
HWCDisplay **hwc_display);
+ static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+ qService::QService *qservice, HWCDisplay **hwc_display);
static void Destroy(HWCDisplay *hwc_display);
virtual int Prepare(hwc_display_contents_1_t *content_list);
virtual int Commit(hwc_display_contents_1_t *content_list);
diff --git a/sdm/libs/hwc/hwc_display_null.cpp b/sdm/libs/hwc/hwc_display_null.cpp
new file mode 100644
index 0000000..11cf47a
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_null.cpp
@@ -0,0 +1,120 @@
+/*
+* Copyright (c) 2016, The Linux Foundation. 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 The Linux Foundation 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.
+*/
+
+#include <hardware/hwcomposer_defs.h>
+#include <utils/constants.h>
+#include <utils/debug.h>
+#include "hwc_display_null.h"
+
+#define __CLASS__ "HWCDisplayNull"
+
+namespace sdm {
+
+int HWCDisplayNull::Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+ HWCDisplay **hwc_display) {
+ int status;
+
+ DLOGI("Null display is being created");
+ HWCDisplayNull *hwc_display_null = new HWCDisplayNull(core_intf, hwc_procs);
+
+ status = hwc_display_null->Init();
+ if (status) {
+ delete hwc_display_null;
+ return status;
+ }
+
+ *hwc_display = hwc_display_null;
+
+ return 0;
+}
+
+void HWCDisplayNull::Destroy(HWCDisplay *hwc_display) {
+ DLOGI("Null display is being destroyed");
+ hwc_display->Deinit();
+ delete hwc_display;
+}
+
+// We pass the display type as HWC_DISPLAY_PRIMARY to HWCDisplay, but since we override
+// and don't chain to HWCDisplay::Init(), that type does not actually get used.
+HWCDisplayNull::HWCDisplayNull(CoreInterface *core_intf, hwc_procs_t const **hwc_procs)
+ : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, false, NULL,
+ DISPLAY_CLASS_NULL) {
+}
+
+int HWCDisplayNull::Init() {
+ // Don't call HWCDisplay::Init() for null display, we don't want the chain of
+ // DisplayPrimary / HWPrimary etc objects to be created.
+ return 0;
+}
+
+int HWCDisplayNull::Deinit() {
+ return 0;
+}
+
+int HWCDisplayNull::Prepare(hwc_display_contents_1_t *content_list) {
+ for (size_t i = 0; i < content_list->numHwLayers; i++) {
+ if (content_list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET ||
+ content_list->hwLayers[i].compositionType == HWC_BACKGROUND) {
+ continue;
+ }
+
+ content_list->hwLayers[i].compositionType = HWC_OVERLAY;
+ }
+
+ return 0;
+}
+
+int HWCDisplayNull::Commit(hwc_display_contents_1_t *content_list) {
+ // HWCSession::Commit (from where this is called) already closes all the acquire
+ // fences once we return from here. So no need to close acquire fences here.
+ for (size_t i = 0; i < content_list->numHwLayers; i++) {
+ content_list->hwLayers[i].releaseFenceFd = -1;
+ }
+
+ return 0;
+}
+
+#define NULL_DISPLAY_FPS 60
+
+int HWCDisplayNull::GetDisplayAttributes(uint32_t config, const uint32_t *attributes,
+ int32_t *values) {
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+ // We fake display resolution as 1080P by default, though it can be overriden through a call to
+ // SetResolution(), and DPI as 160, though what the DPI value does is not clear
+ switch (attributes[i]) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ values[i] = INT32(1000000000L / NULL_DISPLAY_FPS);
+ break;
+ case HWC_DISPLAY_WIDTH:
+ values[i] = static_cast<int32_t>(x_res_);
+ break;
+ case HWC_DISPLAY_HEIGHT:
+ values[i] = static_cast<int32_t>(y_res_);
+ break;
+ }
+ }
+ return 0;
+}
+
+} // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display_null.h b/sdm/libs/hwc/hwc_display_null.h
new file mode 100644
index 0000000..2f6438a
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_null.h
@@ -0,0 +1,104 @@
+/*
+* Copyright (c) 2016, The Linux Foundation. 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 The Linux Foundation 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 __HWC_DISPLAY_NULL_H__
+#define __HWC_DISPLAY_NULL_H__
+
+#include <qdMetaData.h>
+#include <gralloc_priv.h>
+#include "hwc_display.h"
+
+namespace sdm {
+
+class HWCDisplayNull : public HWCDisplay {
+ public:
+ static int Create(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
+ HWCDisplay **hwc_display);
+ static void Destroy(HWCDisplay *hwc_display);
+ virtual int Init();
+ virtual int Deinit();
+ virtual int Prepare(hwc_display_contents_1_t *content_list);
+ virtual int Commit(hwc_display_contents_1_t *content_list);
+ virtual int EventControl(int event, int enable) { return 0; }
+ virtual int SetPowerMode(int mode) { return 0; }
+
+ // Framebuffer configurations
+ virtual int GetDisplayConfigs(uint32_t *configs, size_t *num_configs) {
+ return HWCDisplay::GetDisplayConfigs(configs, num_configs);
+ }
+
+ virtual int GetDisplayAttributes(uint32_t config, const uint32_t *attributes, int32_t *values);
+ virtual int GetActiveConfig() { return 0; }
+ virtual int SetActiveConfig(int index) { return -1; }
+
+ virtual void SetIdleTimeoutMs(uint32_t timeout_ms) { return; }
+ virtual void SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type) { return; }
+ virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages) { return kErrorNone; }
+ virtual DisplayError ControlPartialUpdate(bool enable, uint32_t *pending) { return kErrorNone; }
+ virtual uint32_t GetLastPowerMode() { return 0; }
+ virtual int SetFrameBufferResolution(uint32_t x_pixels, uint32_t y_pixels) { return 0; }
+
+ virtual void GetFrameBufferResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+ *x_pixels = x_res_;
+ *y_pixels = y_res_;
+ }
+
+ virtual void GetPanelResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+ *x_pixels = x_res_;
+ *y_pixels = y_res_;
+ }
+
+ virtual int SetDisplayStatus(uint32_t display_status) { return 0; }
+ virtual int OnMinHdcpEncryptionLevelChange(uint32_t min_enc_level) { return 0; }
+ virtual int Perform(uint32_t operation, ...) { return 0; }
+ virtual int SetCursorPosition(int x, int y) { return 0; }
+ virtual void SetSecureDisplay(bool secure_display_active) { return; }
+
+ // Display Configurations
+ virtual int SetActiveDisplayConfig(int config) { return 0; }
+ virtual int GetActiveDisplayConfig(uint32_t *config) { return -1; }
+ virtual int GetDisplayConfigCount(uint32_t *count) { return -1; }
+ virtual int GetDisplayAttributesForConfig(int config, DisplayConfigVariableInfo *attributes) {
+ return -1;
+ }
+ virtual bool IsValidContentList(hwc_display_contents_1_t *content_list) {
+ return true;
+ }
+
+ void SetResolution(uint32_t x_res, uint32_t y_res) {
+ x_res_ = x_res;
+ y_res_ = y_res;
+ }
+
+
+ private:
+ HWCDisplayNull(CoreInterface *core_intf, hwc_procs_t const **hwc_procs);
+ uint32_t x_res_ = 1920;
+ uint32_t y_res_ = 1080;
+};
+
+} // namespace sdm
+
+#endif // __HWC_DISPLAY_NULL_H__
+
diff --git a/sdm/libs/hwc/hwc_display_primary.cpp b/sdm/libs/hwc/hwc_display_primary.cpp
index 2a3270e..8915bba 100644
--- a/sdm/libs/hwc/hwc_display_primary.cpp
+++ b/sdm/libs/hwc/hwc_display_primary.cpp
@@ -85,8 +85,8 @@
BufferAllocator *buffer_allocator,
hwc_procs_t const **hwc_procs,
qService::QService *qservice)
- : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, true, qservice),
- buffer_allocator_(buffer_allocator), cpu_hint_(NULL) {
+ : HWCDisplay(core_intf, hwc_procs, kPrimary, HWC_DISPLAY_PRIMARY, true, qservice,
+ DISPLAY_CLASS_PRIMARY), buffer_allocator_(buffer_allocator), cpu_hint_(NULL) {
}
int HWCDisplayPrimary::Init() {
diff --git a/sdm/libs/hwc/hwc_display_virtual.cpp b/sdm/libs/hwc/hwc_display_virtual.cpp
index 5b0caab..0d3cdf2 100644
--- a/sdm/libs/hwc/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc/hwc_display_virtual.cpp
@@ -96,7 +96,8 @@
}
HWCDisplayVirtual::HWCDisplayVirtual(CoreInterface *core_intf, hwc_procs_t const **hwc_procs)
- : HWCDisplay(core_intf, hwc_procs, kVirtual, HWC_DISPLAY_VIRTUAL, false, NULL),
+ : HWCDisplay(core_intf, hwc_procs, kVirtual, HWC_DISPLAY_VIRTUAL, false, NULL,
+ DISPLAY_CLASS_VIRTUAL),
dump_output_layer_(false), output_buffer_(NULL) {
}
diff --git a/sdm/libs/hwc/hwc_session.cpp b/sdm/libs/hwc/hwc_session.cpp
index ffd8cc1..7928765 100644
--- a/sdm/libs/hwc/hwc_session.cpp
+++ b/sdm/libs/hwc/hwc_session.cpp
@@ -49,6 +49,7 @@
#include "hwc_buffer_sync_handler.h"
#include "hwc_session.h"
#include "hwc_debugger.h"
+#include "hwc_display_null.h"
#include "hwc_display_primary.h"
#include "hwc_display_virtual.h"
@@ -146,9 +147,33 @@
return -EINVAL;
}
- // Create and power on primary display
- status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
- &hwc_display_[HWC_DISPLAY_PRIMARY]);
+ // Read which display is first, and create it and store it in primary slot
+ HWDisplayInterfaceInfo hw_disp_info;
+ error = core_intf_->GetFirstDisplayInterfaceType(&hw_disp_info);
+ if (error == kErrorNone) {
+ if (hw_disp_info.type == kHDMI) {
+ // HDMI is primary display. If already connected, then create it and store in
+ // primary display slot. If not connected, create a NULL display for now.
+ if (hw_disp_info.is_connected) {
+ status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, qservice_,
+ &hwc_display_[HWC_DISPLAY_PRIMARY]);
+ } else {
+ // NullDisplay simply closes all its fences, and advertizes a standard
+ // resolution to SurfaceFlinger
+ status = HWCDisplayNull::Create(core_intf_, &hwc_procs_,
+ &hwc_display_[HWC_DISPLAY_PRIMARY]);
+ }
+ } else {
+ // Create and power on primary display
+ status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
+ &hwc_display_[HWC_DISPLAY_PRIMARY]);
+ }
+ } else {
+ // Create and power on primary display
+ status = HWCDisplayPrimary::Create(core_intf_, buffer_allocator_, &hwc_procs_, qservice_,
+ &hwc_display_[HWC_DISPLAY_PRIMARY]);
+ }
+
if (status) {
CoreInterface::DestroyCore();
return status;
@@ -597,7 +622,7 @@
if (disp == HWC_DISPLAY_EXTERNAL) {
status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
- qservice_, &hwc_display_[disp]);
+ qservice_, false, &hwc_display_[disp]);
} else if (disp == HWC_DISPLAY_VIRTUAL) {
status = HWCDisplayVirtual::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
content_list, &hwc_display_[disp]);
@@ -1307,6 +1332,7 @@
int HWCSession::HotPlugHandler(bool connected) {
int status = 0;
bool notify_hotplug = false;
+ bool hdmi_primary = false;
// To prevent sending events to client while a lock is held, acquire scope locks only within
// below scope so that those get automatically unlocked after the scope ends.
@@ -1318,36 +1344,112 @@
return -1;
}
- if (connected) {
- if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
- DLOGE("HDMI is already connected");
- return -1;
- }
- // Connect external display if virtual display is not connected.
- // Else, defer external display connection and process it when virtual display
- // tears down; Do not notify SurfaceFlinger since connection is deferred now.
- if (!hwc_display_[HWC_DISPLAY_VIRTUAL]) {
- status = ConnectDisplay(HWC_DISPLAY_EXTERNAL, NULL);
+ HWCDisplay *primary_display = hwc_display_[HWC_DISPLAY_PRIMARY];
+ HWCDisplay *external_display = NULL;
+ HWCDisplay *null_display = NULL;
+
+ if (primary_display->GetDisplayClass() == DISPLAY_CLASS_EXTERNAL) {
+ external_display = static_cast<HWCDisplayExternal *>(hwc_display_[HWC_DISPLAY_PRIMARY]);
+ } else if (primary_display->GetDisplayClass() == DISPLAY_CLASS_NULL) {
+ null_display = static_cast<HWCDisplayNull *>(hwc_display_[HWC_DISPLAY_PRIMARY]);
+ }
+
+ if (external_display || null_display) {
+ hdmi_primary = true;
+ }
+
+ // If primary display connected is a NULL display, then replace it with the external display
+ if (connected) {
+ // If we are in HDMI as primary and the primary display just got plugged in
+ if (null_display) {
+ assert(hdmi_primary);
+ uint32_t primary_width, primary_height;
+ null_display->GetFrameBufferResolution(&primary_width, &primary_height);
+ delete null_display;
+ hwc_display_[HWC_DISPLAY_PRIMARY] = NULL;
+
+ // Create external display with a forced framebuffer resolution to that of what the NULL
+ // display had. This is necessary because SurfaceFlinger does not dynamically update
+ // framebuffer resolution once it reads it at bootup. So we always have to have the NULL
+ // display/external display both at the bootup resolution.
+ int status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width,
+ primary_height, qservice_, true,
+ &hwc_display_[HWC_DISPLAY_PRIMARY]);
if (status) {
- return status;
+ DLOGE("Could not create external display");
+ return -1;
}
- notify_hotplug = true;
+
+ // Next, go ahead and enable vsync on external display. This is expliclity required
+ // because in HDMI as primary case, SurfaceFlinger may not be aware of underlying
+ // changing display. and thus may not explicitly enable vsync
+
+ status = hwc_display_[HWC_DISPLAY_PRIMARY]->EventControl(HWC_EVENT_VSYNC, true);
+ if (status) {
+ DLOGE("Error enabling vsync for HDMI as primary case");
+ }
+ // Don't do hotplug notification for HDMI as primary case for now
+ notify_hotplug = false;
} else {
- DLOGI("Virtual display is connected, pending connection");
- external_pending_connect_ = true;
+ if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+ DLOGE("HDMI is already connected");
+ return -1;
+ }
+
+ // Connect external display if virtual display is not connected.
+ // Else, defer external display connection and process it when virtual display
+ // tears down; Do not notify SurfaceFlinger since connection is deferred now.
+ if (!hwc_display_[HWC_DISPLAY_VIRTUAL]) {
+ status = ConnectDisplay(HWC_DISPLAY_EXTERNAL, NULL);
+ if (status) {
+ return status;
+ }
+ notify_hotplug = true;
+ } else {
+ DLOGI("Virtual display is connected, pending connection");
+ external_pending_connect_ = true;
+ }
}
} else {
// Do not return error if external display is not in connected status.
// Due to virtual display concurrency, external display connection might be still pending
// but hdmi got disconnected before pending connection could be processed.
- if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
- status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
- notify_hotplug = true;
+
+ if (hdmi_primary) {
+ assert(external_display != NULL);
+ uint32_t x_res, y_res;
+ external_display->GetFrameBufferResolution(&x_res, &y_res);
+ // Need to manually disable VSYNC as SF is not aware of connect/disconnect cases
+ // for HDMI as primary
+ external_display->EventControl(HWC_EVENT_VSYNC, false);
+ HWCDisplayExternal::Destroy(external_display);
+
+ HWCDisplayNull *null_display;
+
+ int status = HWCDisplayNull::Create(core_intf_, &hwc_procs_,
+ reinterpret_cast<HWCDisplay **>(&null_display));
+
+ if (status) {
+ DLOGE("Could not create Null display when primary got disconnected");
+ return -1;
+ }
+
+ null_display->SetResolution(x_res, y_res);
+ hwc_display_[HWC_DISPLAY_PRIMARY] = null_display;
+
+ // Don't do hotplug notification for HDMI as primary case for now
+ notify_hotplug = false;
+ } else {
+ if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+ status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
+ notify_hotplug = true;
+ }
+ external_pending_connect_ = false;
}
- external_pending_connect_ = false;
}
}
+
if (connected && notify_hotplug) {
// trigger screen refresh to ensure sufficient resources are available to process new
// new display connection.