summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTapani Pälli <tapani.palli@intel.com>2012-10-04 16:01:27 +0300
committerTapani Pälli <tapani.palli@intel.com>2012-10-18 10:52:38 +0300
commit2d2758bca0f962ef21673dbe29df4c042d7f3254 (patch)
treed17d46beb9ccac2406215922182cf8919eeaaac0
parentc878105aa842f6a7fbec5414d50cbf93892b3a1b (diff)
downloadexternal_drm_gralloc-2d2758bca0f962ef21673dbe29df4c042d7f3254.zip
external_drm_gralloc-2d2758bca0f962ef21673dbe29df4c042d7f3254.tar.gz
external_drm_gralloc-2d2758bca0f962ef21673dbe29df4c042d7f3254.tar.bz2
gralloc: hdmi cloned mode support
Patch implements simple support for cloned mode hdmi by introducing primary and hdmi outputs that are updated respectively. Current implementation is rather naive and assumes hdmi can drive same mode as the local lvds. Only cloned mode is supported but can be extended later to support extended mode when hwcomposer is available for controlling this behaviour. HDMI hotplug is implemented as a separate observer thread that listens to uevents and reads hdmi connection state from there, this requires switch support from the kernel hdmi driver. Change-Id: I1d5873214d672563789f4953a892de60539eca88 Signed-off-by: Tapani Pälli <tapani.palli@intel.com> Signed-off-by: Matt Gumbel <matthew.k.gumbel@linux.intel.com>
-rw-r--r--Android.mk1
-rw-r--r--gralloc_drm_intel.c6
-rw-r--r--gralloc_drm_kms.c258
-rw-r--r--gralloc_drm_priv.h29
4 files changed, 238 insertions, 56 deletions
diff --git a/Android.mk b/Android.mk
index 2a61b01..e675a00 100644
--- a/Android.mk
+++ b/Android.mk
@@ -80,6 +80,7 @@ LOCAL_SHARED_LIBRARIES := \
libdrm \
liblog \
libcutils \
+ libhardware_legacy \
# for glFlush/glFinish
LOCAL_SHARED_LIBRARIES += \
diff --git a/gralloc_drm_intel.c b/gralloc_drm_intel.c
index 519827f..6a0d7e0 100644
--- a/gralloc_drm_intel.c
+++ b/gralloc_drm_intel.c
@@ -497,12 +497,12 @@ static void intel_init_kms_features(struct gralloc_drm_drv_t *drv,
struct drm_i915_getparam gp;
int pageflipping, id;
- switch (drm->fb_format) {
+ switch (drm->primary.fb_format) {
case HAL_PIXEL_FORMAT_BGRA_8888:
case HAL_PIXEL_FORMAT_RGB_565:
break;
default:
- drm->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
+ drm->primary.fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
break;
}
@@ -545,7 +545,7 @@ static void intel_init_kms_features(struct gralloc_drm_drv_t *drv,
int pipe;
pipe = drm_intel_get_pipe_from_crtc_id(info->bufmgr,
- drm->crtc_id);
+ drm->primary.crtc_id);
drm->swap_interval = (pipe >= 0) ? 1 : 0;
drm->vblank_secondary = (pipe > 0);
}
diff --git a/gralloc_drm_kms.c b/gralloc_drm_kms.c
index 8607285..57870f7 100644
--- a/gralloc_drm_kms.c
+++ b/gralloc_drm_kms.c
@@ -30,8 +30,10 @@
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
+#include <poll.h>
#include "gralloc_drm.h"
#include "gralloc_drm_priv.h"
+#include <hardware_legacy/uevent.h>
#include <drm_fourcc.h>
@@ -136,14 +138,17 @@ void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo)
/*
* Program CRTC.
*/
-static int drm_kms_set_crtc(struct gralloc_drm_t *drm, int fb_id)
+static int drm_kms_set_crtc(struct gralloc_drm_t *drm,
+ struct gralloc_drm_output *output, int fb_id)
{
int ret;
- ret = drmModeSetCrtc(drm->fd, drm->crtc_id, fb_id,
- 0, 0, &drm->connector_id, 1, &drm->mode);
+ ret = drmModeSetCrtc(drm->fd, output->crtc_id, fb_id,
+ 0, 0, &output->connector_id, 1, &output->mode);
if (ret) {
- ALOGE("failed to set crtc");
+ ALOGE("failed to set crtc (%s) (crtc_id %d, fb_id %d, conn %d, mode %dx%d)",
+ strerror(errno), output->crtc_id, fb_id, output->connector_id,
+ output->mode.hdisplay, output->mode.vdisplay);
return ret;
}
@@ -191,10 +196,23 @@ static int drm_kms_page_flip(struct gralloc_drm_t *drm,
if (!bo)
return 0;
- ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id,
+ pthread_mutex_lock(&drm->hdmi_mutex);
+ if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED) {
+ ret = drmModePageFlip(drm->fd, drm->hdmi.crtc_id, bo->fb_id, 0, NULL);
+ if (ret && errno != EBUSY)
+ ALOGE("failed to perform page flip for hdmi (%s) (crtc %d fb %d))",
+ strerror(errno), drm->hdmi.crtc_id, bo->fb_id);
+ }
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+
+ ret = drmModePageFlip(drm->fd, drm->primary.crtc_id, bo->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
- if (ret)
- ALOGE("failed to perform page flip");
+ if (ret) {
+ ALOGE("failed to perform page flip for primary (%s) (crtc %d fb %d))",
+ strerror(errno), drm->primary.crtc_id, bo->fb_id);
+ /* try to set mode for next frame */
+ drm->first_post = 1;
+ }
else
drm->next_front = bo;
@@ -286,7 +304,7 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
bo = dst;
}
- ret = drm_kms_set_crtc(drm, bo->fb_id);
+ ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
if (!ret) {
drm->first_post = 0;
drm->current_front = bo;
@@ -294,6 +312,11 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
drm->next_front = NULL;
}
+ pthread_mutex_lock(&drm->hdmi_mutex);
+ if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
+ drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+
return ret;
}
@@ -325,7 +348,13 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
break;
case DRM_SWAP_SETCRTC:
drm_kms_wait_for_post(drm, 0);
- ret = drm_kms_set_crtc(drm, bo->fb_id);
+ ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
+
+ pthread_mutex_lock(&drm->hdmi_mutex);
+ if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
+ drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+
drm->current_front = bo;
break;
default:
@@ -388,9 +417,9 @@ static void drm_kms_init_features(struct gralloc_drm_t *drm)
/* create the real front buffer */
front = gralloc_drm_bo_create(drm,
- drm->mode.hdisplay,
- drm->mode.vdisplay,
- drm->fb_format,
+ drm->primary.mode.hdisplay,
+ drm->primary.mode.vdisplay,
+ drm->primary.fb_format,
GRALLOC_USAGE_HW_FB);
if (front && gralloc_drm_bo_add_fb(front)) {
gralloc_drm_bo_decref(front);
@@ -484,7 +513,7 @@ static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
* Initialize KMS with a connector.
*/
static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
- drmModeConnectorPtr connector)
+ struct gralloc_drm_output *output, drmModeConnectorPtr connector)
{
drmModeEncoderPtr encoder;
drmModeModeInfoPtr mode;
@@ -497,16 +526,18 @@ static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
if (!encoder)
return -EINVAL;
+ /* find first possible crtc which is not used yet */
for (i = 0; i < drm->resources->count_crtcs; i++) {
- if (encoder->possible_crtcs & (1 << i))
+ if (encoder->possible_crtcs & (1 << i) &&
+ i != drm->primary.crtc_id)
break;
}
drmModeFreeEncoder(encoder);
if (i == drm->resources->count_crtcs)
return -EINVAL;
- drm->crtc_id = drm->resources->crtcs[i];
- drm->connector_id = connector->connector_id;
+ output->crtc_id = drm->resources->crtcs[i];
+ output->connector_id = connector->connector_id;
/* print connector info */
if (connector->count_modes > 1) {
@@ -526,41 +557,143 @@ static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
ALOGI("the best mode is %s", mode->name);
- drm->mode = *mode;
+ output->mode = *mode;
switch (bpp) {
case 2:
- drm->fb_format = HAL_PIXEL_FORMAT_RGB_565;
+ output->fb_format = HAL_PIXEL_FORMAT_RGB_565;
break;
case 4:
default:
- drm->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
+ output->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
break;
}
if (connector->mmWidth && connector->mmHeight) {
- drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth);
- drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight);
+ output->xdpi = (output->mode.hdisplay * 25.4 / connector->mmWidth);
+ output->ydpi = (output->mode.vdisplay * 25.4 / connector->mmHeight);
}
else {
- drm->xdpi = 75;
- drm->ydpi = 75;
+ output->xdpi = 75;
+ output->ydpi = 75;
}
#ifdef DRM_MODE_FEATURE_DIRTYFB
drm->clip.x1 = 0;
drm->clip.y1 = 0;
- drm->clip.x2 = drm->mode.hdisplay;
- drm->clip.y2 = drm->mode.vdisplay;
+ drm->clip.x2 = output->mode.hdisplay;
+ drm->clip.y2 = output->mode.vdisplay;
#endif
return 0;
}
+
+/*
+ * Fetch a connector of particular type
+ */
+static drmModeConnectorPtr fetch_connector(struct gralloc_drm_t *drm,
+ uint32_t type)
+{
+ int i;
+
+ if (!drm->resources)
+ return NULL;
+
+ for (i = 0; i < drm->resources->count_connectors; i++) {
+ drmModeConnectorPtr connector =
+ connector = drmModeGetConnector(drm->fd,
+ drm->resources->connectors[i]);
+ if (connector) {
+ if (connector->connector_type == type &&
+ connector->connection == DRM_MODE_CONNECTED)
+ return connector;
+ drmModeFreeConnector(connector);
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * Thread that listens to uevents and checks if hdmi state changes
+ */
+static void *hdmi_observer(void *data)
+{
+ static char uevent_desc[4096];
+ drmModeConnectorPtr hdmi;
+ struct gralloc_drm_t *drm =
+ (struct gralloc_drm_t *) data;
+
+ uevent_init();
+
+ memset(uevent_desc, 0, sizeof(uevent_desc));
+
+ while(1) {
+
+ /* this polls */
+ int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2);
+
+ if(len && strstr(uevent_desc, "devices/virtual/switch/hdmi")) {
+
+ /* check what changed */
+ const char *prop = uevent_desc + strlen(uevent_desc) + 1;
+
+ while (*prop) {
+
+ const char *state = strstr(prop, "SWITCH_STATE=");
+ if (state) {
+ unsigned int value = 0;
+ state += strlen("SWITCH_STATE=");
+ value = atoi(state);
+
+ pthread_mutex_lock(&drm->hdmi_mutex);
+
+ if (value) {
+ hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
+ if (hdmi) {
+ drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
+ drmModeFreeConnector(hdmi);
+
+ /* will trigger modeset */
+ drm->first_post = 1;
+
+ /* HACK, assume same mode for now */
+ memcpy(&drm->hdmi.mode, &drm->primary.mode,
+ sizeof(drmModeModeInfo));
+
+ drm->hdmi_mode = HDMI_CLONED;
+ drm->hdmi.active = 1;
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+ }
+ break;
+ } else {
+ drm->hdmi.active = 0;
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+ break;
+ }
+
+ pthread_mutex_unlock(&drm->hdmi_mutex);
+ }
+
+ /* next property/value pair */
+ prop += strlen(prop) + 1;
+ if (prop - uevent_desc >= len)
+ break;
+ }
+ }
+ }
+
+ pthread_exit(NULL);
+ return 0;
+}
+
+
/*
* Initialize KMS.
*/
int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
{
+ drmModeConnectorPtr lvds, hdmi;
int i, ret;
if (drm->resources)
@@ -599,29 +732,58 @@ int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
}
/* find the crtc/connector/mode to use */
- for (i = 0; i < drm->resources->count_connectors; i++) {
- drmModeConnectorPtr connector;
+ lvds = fetch_connector(drm, DRM_MODE_CONNECTOR_LVDS);
+ if (lvds) {
+ drm_kms_init_with_connector(drm, &drm->primary, lvds);
+ drmModeFreeConnector(lvds);
+ drm->primary.active = 1;
+ }
- connector = drmModeGetConnector(drm->fd,
- drm->resources->connectors[i]);
- if (connector) {
- if (connector->connection == DRM_MODE_CONNECTED) {
- if (!drm_kms_init_with_connector(drm,
- connector))
- break;
+ /* if still no connector, find first connected connector and try it */
+ if (!drm->primary.active) {
+
+ for (i = 0; i < drm->resources->count_connectors; i++) {
+ drmModeConnectorPtr connector;
+
+ connector = drmModeGetConnector(drm->fd,
+ drm->resources->connectors[i]);
+ if (connector) {
+ if (connector->connection == DRM_MODE_CONNECTED) {
+ if (!drm_kms_init_with_connector(drm,
+ &drm->primary, connector))
+ break;
+ }
+
+ drmModeFreeConnector(connector);
}
+ }
+ if (i == drm->resources->count_connectors) {
+ ALOGE("failed to find a valid crtc/connector/mode combination");
+ drmModeFreeResources(drm->resources);
+ drm->resources = NULL;
- drmModeFreeConnector(connector);
+ return -EINVAL;
}
}
- if (i == drm->resources->count_connectors) {
- ALOGE("failed to find a valid crtc/connector/mode combination");
- drmModeFreeResources(drm->resources);
- drm->resources = NULL;
- return -EINVAL;
+ /* check if hdmi is connected already */
+ hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
+ if (hdmi) {
+ drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
+ drmModeFreeConnector(hdmi);
+
+ /* HACK, assume same mode for now */
+ memcpy(&drm->hdmi.mode, &drm->primary.mode,
+ sizeof(drmModeModeInfo));
+
+ drm->hdmi_mode = HDMI_CLONED;
+ drm->hdmi.active = 1;
}
+ /* launch hdmi observer thread */
+ pthread_mutex_init(&drm->hdmi_mutex, NULL);
+ pthread_create(&drm->hdmi_hotplug_thread, NULL, hdmi_observer, drm);
+
drm_kms_init_features(drm);
drm->first_post = 1;
@@ -683,14 +845,14 @@ void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm,
struct framebuffer_device_t *fb)
{
*((uint32_t *) &fb->flags) = 0x0;
- *((uint32_t *) &fb->width) = drm->mode.hdisplay;
- *((uint32_t *) &fb->height) = drm->mode.vdisplay;
- *((int *) &fb->stride) = drm->mode.hdisplay;
- *((float *) &fb->fps) = drm->mode.vrefresh;
-
- *((int *) &fb->format) = drm->fb_format;
- *((float *) &fb->xdpi) = drm->xdpi;
- *((float *) &fb->ydpi) = drm->ydpi;
+ *((uint32_t *) &fb->width) = drm->primary.mode.hdisplay;
+ *((uint32_t *) &fb->height) = drm->primary.mode.vdisplay;
+ *((int *) &fb->stride) = drm->primary.mode.hdisplay;
+ *((float *) &fb->fps) = drm->primary.mode.vrefresh;
+
+ *((int *) &fb->format) = drm->primary.fb_format;
+ *((float *) &fb->xdpi) = drm->primary.xdpi;
+ *((float *) &fb->ydpi) = drm->primary.ydpi;
*((int *) &fb->minSwapInterval) = drm->swap_interval;
*((int *) &fb->maxSwapInterval) = drm->swap_interval;
}
diff --git a/gralloc_drm_priv.h b/gralloc_drm_priv.h
index d22fea0..a6dad10 100644
--- a/gralloc_drm_priv.h
+++ b/gralloc_drm_priv.h
@@ -37,10 +37,26 @@ enum drm_swap_mode {
DRM_SWAP_SETCRTC,
};
+enum hdmi_output_mode {
+ HDMI_CLONED,
+ HDMI_EXTENDED,
+};
+
struct gralloc_drm_plane_t {
drmModePlane *drm_plane;
};
+struct gralloc_drm_output
+{
+ uint32_t crtc_id;
+ uint32_t connector_id;
+ drmModeModeInfo mode;
+ int xdpi, ydpi;
+ int fb_format;
+ int bpp;
+ uint32_t active;
+};
+
struct gralloc_drm_t {
/* initialized by gralloc_drm_create */
int fd;
@@ -48,16 +64,19 @@ struct gralloc_drm_t {
/* initialized by gralloc_drm_init_kms */
drmModeResPtr resources;
- uint32_t crtc_id;
- uint32_t connector_id;
- drmModeModeInfo mode;
- int xdpi, ydpi;
+ struct gralloc_drm_output primary;
+ struct gralloc_drm_output hdmi;
+ enum hdmi_output_mode hdmi_mode;
+
+ /* hdmi hotplug */
+ pthread_mutex_t hdmi_mutex;
+ pthread_t hdmi_hotplug_thread;
+
#ifdef DRM_MODE_FEATURE_DIRTYFB
drmModeClip clip;
#endif
/* initialized by drv->init_kms_features */
- int fb_format;
enum drm_swap_mode swap_mode;
int swap_interval;
int mode_quirk_vmwgfx;