hwc: Add support for HDMI as Primary display
We are adding support for HDMI as Primary to the display HAL.
The HAL must be able to support boot-up use cases and cable
connect/disconnect events as follows:
1. Boot up with HDMI cable connected
For this scenario we read the best mode supported by the
HDMI TV and set that as the current and default mode for
subsequent boot up.
2. Boot up without HDMI cable connected
We read a default resolution from the driver by reading vscreen
and reporting this as the display resolution to SF.
3. Switch the display to active state when we receive first frame
When HDMI is primary we should rely on the first valid
draw call in order to activate the display
4. Update handling of uevents when HDMI is primary
a) Do not send hot plug when the cable is connected/disconnected.
b) Use the correct display ID in uevents when HDMI is primary
5. Handle display timeout when HDMI is Primary
When HDMI is connected as primary we clean up resources
and call commit to generate a black frame on the interface.
However, we do not call blank since we need the timing
generator and HDMI core to remain turned on.
6. Clear pipe resource when HDMI is disconnected
When HDMI is primary, we need to make sure that SF/HWC does
not have any open fd's when the cable is disconnected.
We clear all pipe resources and call a display commit to ensure
that all the fd's are closed. This will ensure that the HDMI
core turns off and that we receive an event the next time the
cable is connected.
Change-Id: Ice70add583a3859f99bfa2e384fbbb6df4df92e1
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 288b2e0..b5ba073 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -249,7 +249,23 @@
const int dpy = HWC_DISPLAY_PRIMARY;
bool fbComp = false;
if (LIKELY(list && list->numHwLayers > 1) &&
- ctx->dpyAttr[dpy].isActive) {
+ (ctx->dpyAttr[dpy].isActive ||
+ ctx->mHDMIDisplay->isHDMIPrimaryDisplay())) {
+
+ // When HDMI is primary we should rely on the first valid
+ // draw call in order to activate the display
+ if (!ctx->dpyAttr[dpy].isActive) {
+ // If the cable is connected after HWC initialization and before
+ // the UEvent thread is initialized then we will miss the ONLINE
+ // event. We need to update the display appropriately when we get
+ // the first valid frame.
+ int cableConnected = ctx->mHDMIDisplay->getConnectedState();
+ if ((cableConnected == 1) && !ctx->dpyAttr[dpy].connected) {
+ qhwc::handle_online(ctx, dpy);
+ }
+ ctx->mHDMIDisplay->activateDisplay();
+ ctx->dpyAttr[dpy].isActive = true;
+ }
if (ctx->dpyAttr[dpy].customFBSize &&
list->flags & HWC_GEOMETRY_CHANGED)
@@ -433,19 +449,33 @@
switch(dpy) {
case HWC_DISPLAY_PRIMARY:
- if(ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, value) < 0 ) {
- ALOGE("%s: ioctl FBIOBLANK failed for Primary with error %s"
- " value %d", __FUNCTION__, strerror(errno), value);
- return -errno;
- }
+ if(ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ if(ctx->dpyAttr[dpy].connected) {
+ // When HDMI is connected as primary we clean up resources
+ // and call commit to generate a black frame on the interface.
+ // However, we do not call blank since we need the timing
+ // generator and HDMI core to remain turned on.
+ if((mode == HWC_POWER_MODE_OFF) &&
+ (!Overlay::displayCommit(ctx->dpyAttr[dpy].fd))) {
+ ALOGE("%s: display commit fail for %d", __FUNCTION__, dpy);
+ ret = -1;
+ }
+ }
+ } else {
+ if(ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, value) < 0 ) {
+ ALOGE("%s: ioctl FBIOBLANK failed for Primary with error %s"
+ " value %d", __FUNCTION__, strerror(errno), value);
+ return -errno;
+ }
- if(mode == HWC_POWER_MODE_NORMAL) {
- // Enable HPD here, as during bootup POWER_MODE_NORMAL is set
- // when SF is completely initialized
- ctx->mHDMIDisplay->setHPD(1);
- }
+ if(mode == HWC_POWER_MODE_NORMAL) {
+ // Enable HPD here, as during bootup POWER_MODE_NORMAL is set
+ // when SF is completely initialized
+ ctx->mHDMIDisplay->setHPD(1);
+ }
- ctx->dpyAttr[dpy].isActive = not(mode == HWC_POWER_MODE_OFF);
+ ctx->dpyAttr[dpy].isActive = not(mode == HWC_POWER_MODE_OFF);
+ }
//Deliberate fall through since there is no explicit power mode for
//virtual displays.
case HWC_DISPLAY_VIRTUAL:
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index 6b2316b..d1c68a5 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -39,11 +39,18 @@
#define HWC_UEVENT_THREAD_NAME "hwcUeventThread"
/* Parse uevent data for devices which we are interested */
-static int getConnectedDisplay(const char* strUdata)
+static int getConnectedDisplay(hwc_context_t* ctx, const char* strUdata)
{
- if(strcasestr("change@/devices/virtual/switch/hdmi", strUdata))
- return HWC_DISPLAY_EXTERNAL;
- return -1;
+ int ret = -1;
+ // Switch node for HDMI as PRIMARY/EXTERNAL
+ if(strcasestr("change@/devices/virtual/switch/hdmi", strUdata)) {
+ if (ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ ret = HWC_DISPLAY_PRIMARY;
+ } else {
+ ret = HWC_DISPLAY_EXTERNAL;
+ }
+ }
+ return ret;
}
static bool getPanelResetStatus(hwc_context_t* ctx, const char* strUdata, int len)
@@ -86,7 +93,7 @@
return;
}
- int dpy = getConnectedDisplay(udata);
+ int dpy = getConnectedDisplay(ctx, udata);
if(dpy < 0) {
ALOGD_IF(UEVENT_DEBUG, "%s: Not disp Event ", __FUNCTION__);
return;
@@ -108,16 +115,16 @@
}
ctx->mDrawLock.lock();
- destroyCompositionResources(ctx, dpy);
- ctx->mHDMIDisplay->teardown();
- resetDisplayInfo(ctx, dpy);
+ handle_offline(ctx, dpy);
ctx->mDrawLock.unlock();
/* We need to send hotplug to SF only when we are disconnecting
- * HDMI */
- ALOGE_IF(UEVENT_DEBUG,"%s:Sending EXTERNAL OFFLINE hotplug"
- "event", __FUNCTION__);
- ctx->proc->hotplug(ctx->proc, dpy, EXTERNAL_OFFLINE);
+ * HDMI as an external display. */
+ if(dpy == HWC_DISPLAY_EXTERNAL) {
+ ALOGE_IF(UEVENT_DEBUG,"%s:Sending EXTERNAL OFFLINE hotplug"
+ "event", __FUNCTION__);
+ ctx->proc->hotplug(ctx->proc, dpy, EXTERNAL_OFFLINE);
+ }
break;
}
case EXTERNAL_ONLINE:
@@ -128,19 +135,29 @@
"for display: %d", __FUNCTION__, dpy);
break;
}
- ctx->mDrawLock.lock();
- //Force composition to give up resources like pipes and
- //close fb. For example if assertive display is going on,
- //fb2 could be open, thus connecting Layer Mixer#0 to
- //WriteBack module. If HDMI attempts to open fb1, the driver
- //will try to attach Layer Mixer#0 to HDMI INT, which will
- //fail, since Layer Mixer#0 is still connected to WriteBack.
- //This block will force composition to close fb2 in above
- //example.
- ctx->dpyAttr[dpy].isConfiguring = true;
- ctx->mDrawLock.unlock();
- ctx->proc->invalidate(ctx->proc);
+ if (ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ ctx->mDrawLock.lock();
+ handle_online(ctx, dpy);
+ ctx->mDrawLock.unlock();
+
+ ctx->proc->invalidate(ctx->proc);
+ break;
+ } else {
+ ctx->mDrawLock.lock();
+ //Force composition to give up resources like pipes and
+ //close fb. For example if assertive display is going on,
+ //fb2 could be open, thus connecting Layer Mixer#0 to
+ //WriteBack module. If HDMI attempts to open fb1, the driver
+ //will try to attach Layer Mixer#0 to HDMI INT, which will
+ //fail, since Layer Mixer#0 is still connected to WriteBack.
+ //This block will force composition to close fb2 in above
+ //example.
+ ctx->dpyAttr[dpy].isConfiguring = true;
+ ctx->mDrawLock.unlock();
+
+ ctx->proc->invalidate(ctx->proc);
+ }
//2 cycles for slower content
usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
* 2 / 1000);
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index c409db4..4718ed4 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -259,12 +259,39 @@
void initContext(hwc_context_t *ctx)
{
- openFramebufferDevice(ctx);
+ overlay::Overlay::initOverlay();
+ ctx->mHDMIDisplay = new HDMIDisplay();
+ uint32_t priW = 0, priH = 0;
+ // 1. HDMI as Primary
+ // -If HDMI cable is connected, read display configs from edid data
+ // -If HDMI cable is not connected then use default data in vscreeninfo
+ // 2. HDMI as External
+ // -Initialize HDMI class for use with external display
+ // -Use vscreeninfo to populate display configs
+ if(ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ int connected = ctx->mHDMIDisplay->getConnectedState();
+ if(connected == 1) {
+ ctx->mHDMIDisplay->configure();
+ updateDisplayInfo(ctx, HWC_DISPLAY_PRIMARY);
+ ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
+ } else {
+ openFramebufferDevice(ctx);
+ ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = false;
+ }
+ } else {
+ openFramebufferDevice(ctx);
+ ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
+ // Send the primary resolution to the hdmi display class
+ // to be used for MDP scaling functionality
+ priW = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
+ priH = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
+ ctx->mHDMIDisplay->setPrimaryAttributes(priW, priH);
+ }
+
char value[PROPERTY_VALUE_MAX];
ctx->mMDP.version = qdutils::MDPVersion::getInstance().getMDPVersion();
ctx->mMDP.hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
ctx->mMDP.panel = qdutils::MDPVersion::getInstance().getPanelType();
- overlay::Overlay::initOverlay();
ctx->mOverlay = overlay::Overlay::getInstance();
ctx->mRotMgr = RotMgr::getInstance();
@@ -291,12 +318,6 @@
HWC_DISPLAY_PRIMARY);
}
- ctx->mHDMIDisplay = new HDMIDisplay();
- // Send the primary resolution to the hdmi display class
- // to be used for MDP scaling functionality
- uint32_t priW = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
- uint32_t priH = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
- ctx->mHDMIDisplay->setPrimaryAttributes(priW, priH);
ctx->mHWCVirtual = new HWCVirtualVDS();
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = false;
@@ -306,7 +327,6 @@
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].mMDPScalingMode = false;
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].mMDPScalingMode = false;
- ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
//Initialize the primary display viewFrame info
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].left = 0;
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].top = 0;
@@ -2530,4 +2550,54 @@
return;
}
+void clearPipeResources(hwc_context_t* ctx, int dpy) {
+ if(ctx->mOverlay) {
+ ctx->mOverlay->configBegin();
+ ctx->mOverlay->configDone();
+ }
+ if(ctx->mRotMgr) {
+ ctx->mRotMgr->clear();
+ }
+ // Call a display commit to ensure that pipes and associated
+ // fd's are cleaned up.
+ if(!Overlay::displayCommit(ctx->dpyAttr[dpy].fd)) {
+ ALOGE("%s: display commit failed for %d", __FUNCTION__, dpy);
+ }
+}
+
+// Handles online events when HDMI is the primary display. In particular,
+// online events for hdmi connected before AND after boot up and HWC init.
+void handle_online(hwc_context_t* ctx, int dpy) {
+ // Close the current fd if it was opened earlier on when HWC
+ // was initialized.
+ if (ctx->dpyAttr[dpy].fd >= 0) {
+ close(ctx->dpyAttr[dpy].fd);
+ ctx->dpyAttr[dpy].fd = -1;
+ }
+ // TODO: If HDMI is connected after the display has booted up,
+ // and the best configuration is different from the default
+ // then we need to deal with this appropriately.
+ ctx->mHDMIDisplay->configure();
+ updateDisplayInfo(ctx, dpy);
+ initCompositionResources(ctx, dpy);
+ ctx->dpyAttr[dpy].connected = true;
+}
+
+// Handles offline events for HDMI. This can be used for offline events
+// initiated by the HDMI driver and the CEC framework.
+void handle_offline(hwc_context_t* ctx, int dpy) {
+ destroyCompositionResources(ctx, dpy);
+ // Clear all pipe resources and call a display commit to ensure
+ // that all the fd's are closed. This will ensure that the HDMI
+ // core turns off and that we receive an event the next time the
+ // cable is connected.
+ if (ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ clearPipeResources(ctx, dpy);
+ }
+ ctx->mHDMIDisplay->teardown();
+ resetDisplayInfo(ctx, dpy);
+ ctx->dpyAttr[dpy].connected = false;
+ ctx->dpyAttr[dpy].isActive = false;
+}
+
};//namespace qhwc
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 3d97319..96a7bb2 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -288,6 +288,7 @@
void resetDisplayInfo(hwc_context_t* ctx, int dpy);
void initCompositionResources(hwc_context_t* ctx, int dpy);
void destroyCompositionResources(hwc_context_t* ctx, int dpy);
+void clearPipeResources(hwc_context_t* ctx, int dpy);
//Helper function to dump logs
void dumpsys_log(android::String8& buf, const char* fmt, ...);
@@ -344,6 +345,10 @@
void handle_pause(hwc_context_t *ctx, int dpy);
void handle_resume(hwc_context_t *ctx, int dpy);
+// Handle ONLINE/OFFLINE for HDMI display
+void handle_online(hwc_context_t* ctx, int dpy);
+void handle_offline(hwc_context_t* ctx, int dpy);
+
//Close acquireFenceFds of all layers of incoming list
void closeAcquireFds(hwc_display_contents_1_t* list);
diff --git a/libhwcomposer/hwc_vsync.cpp b/libhwcomposer/hwc_vsync.cpp
index 07d1e4e..7b97c6c 100644
--- a/libhwcomposer/hwc_vsync.cpp
+++ b/libhwcomposer/hwc_vsync.cpp
@@ -27,6 +27,7 @@
#include <sys/prctl.h>
#include <poll.h>
#include "hwc_utils.h"
+#include "hdmi.h"
#include "qd_utils.h"
#include "string.h"
#include "overlay.h"
@@ -73,7 +74,9 @@
if (!strncmp(data, PANEL_ON_STR, strlen(PANEL_ON_STR))) {
unsigned long int poweron = strtoul(data + strlen(PANEL_ON_STR), NULL, 0);
ALOGI("%s: dpy:%d panel power state: %ld", __FUNCTION__, dpy, poweron);
- ctx->dpyAttr[dpy].isActive = poweron ? true: false;
+ if (!ctx->mHDMIDisplay->isHDMIPrimaryDisplay()) {
+ ctx->dpyAttr[dpy].isActive = poweron ? true: false;
+ }
}
}