summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSunita Nadampalli <sunitan@ti.com>2014-12-01 14:31:40 -0600
committerGerrit Code Review <gerrit2@git.omapzoom.org>2015-01-19 16:18:43 -0600
commiteb6a5023f72386cdf59db101bce9d4ce2777a418 (patch)
tree3e51b409da66aa6f7c96c2355e27a7bcdd61c032
parente77c0df775e1138ec42e76f23084883e302b7e4c (diff)
downloaddra7xx-eb6a5023f72386cdf59db101bce9d4ce2777a418.tar.gz
hwcomposer: Added support for HDMI and external display
Change-Id: If9d6e5298abda444eae837606274b5f081c90560 Signed-off-by: Sunita Nadampalli <sunitan@ti.com>
-rw-r--r--hwcomposer/display.c243
-rw-r--r--hwcomposer/display.h29
-rw-r--r--hwcomposer/hwc.c75
3 files changed, 345 insertions, 2 deletions
diff --git a/hwcomposer/display.c b/hwcomposer/display.c
index c4ccefd..5a7cde1 100644
--- a/hwcomposer/display.c
+++ b/hwcomposer/display.c
@@ -341,6 +341,11 @@ static void setup_lcd_config(display_config_t *config, int xres, int yres, struc
setup_config(config, xres, yres, info, LCD_DISPLAY_FPS, LCD_DISPLAY_DEFAULT_DPI);
}
+static void setup_hdmi_config(display_config_t *config, int xres, int yres, struct dsscomp_display_info *info)
+{
+ setup_config(config, xres, yres, info, HDMI_DISPLAY_FPS, HDMI_DISPLAY_DEFAULT_DPI);
+}
+
static int init_primary_lcd_display(omap_hwc_device_t *hwc_dev, uint32_t xres, uint32_t yres, struct dsscomp_display_info *info)
{
int err;
@@ -399,6 +404,106 @@ static void set_primary_display_transform_matrix(omap_hwc_device_t *hwc_dev)
translate_matrix(transform->matrix, lcd_w >> 1, lcd_h >> 1);
}
+static void set_external_display_transform_matrix(omap_hwc_device_t *hwc_dev, int disp)
+{
+ display_t *display = hwc_dev->displays[disp];
+ display_transform_t *transform = &display->transform;
+ int orig_xres = WIDTH(transform->region);
+ int orig_yres = HEIGHT(transform->region);
+ float orig_center_x = transform->region.left + orig_xres / 2.0f;
+ float orig_center_y = transform->region.top + orig_yres / 2.0f;
+
+ /* Reorientation matrix is:
+ m = (center-from-target-center) * (scale-to-target) * (mirror) * (rotate) * (center-to-original-center) */
+
+ memcpy(transform->matrix, unit_matrix, sizeof(unit_matrix));
+ translate_matrix(transform->matrix, -orig_center_x, -orig_center_y);
+ rotate_matrix(transform->matrix, transform->rotation);
+ if (transform->hflip)
+ scale_matrix(transform->matrix, 1, -1, 1, 1);
+
+ primary_display_t *primary = get_primary_display_info(hwc_dev);
+ if (!primary)
+ return;
+
+ float xpy = primary->xpy;
+
+ if (transform->rotation & 1) {
+ SWAP(orig_xres, orig_yres);
+ xpy = 1.0f / xpy;
+ }
+
+ /* get target size */
+ uint32_t adj_xres, adj_yres;
+ uint32_t width, height;
+ int xres, yres;
+
+ if (is_hdmi_display(hwc_dev, disp)) {
+ hdmi_display_t *hdmi = &((external_hdmi_display_t*)display)->hdmi;
+ width = hdmi->width;
+ height = hdmi->height;
+ xres = display->disp_link.mode->hdisplay;;
+ yres = display->disp_link.mode->hdisplay;;
+ } else {
+ display_config_t *config = &display->configs[display->active_config_ix];
+ width = 0;
+ height = 0;
+ xres = config->xres;
+ yres = config->yres;
+ }
+
+ display->transform.scaling = ((xres != orig_xres) || (yres != orig_yres));
+
+ get_max_dimensions(orig_xres, orig_yres, xpy,
+ xres, yres, width, height,
+ &adj_xres, &adj_yres);
+
+ scale_matrix(transform->matrix, orig_xres, adj_xres, orig_yres, adj_yres);
+ translate_matrix(transform->matrix, xres >> 1, yres >> 1);
+}
+
+static int setup_external_display_transform(omap_hwc_device_t *hwc_dev, int disp)
+{
+ display_t *display = hwc_dev->displays[disp];
+
+ if (display->opmode == DISP_MODE_PRESENTATION) {
+ display_config_t *config = &display->configs[display->active_config_ix];
+ struct hwc_rect src_region = { .right = config->xres, .bottom = config->yres };
+ display->transform.region = src_region;
+ } else {
+ primary_display_t *primary = get_primary_display_info(hwc_dev);
+ if (!primary)
+ return -ENODEV;
+
+ display->transform.region = primary->mirroring_region;
+ }
+
+ uint32_t xres = WIDTH(display->transform.region);
+ uint32_t yres = HEIGHT(display->transform.region);
+
+ if (!(xres && yres))
+ return -EINVAL;
+
+ int rot_flip = (yres > xres) ? 3 : 0;
+ display->transform.rotation = rot_flip & EXT_ROTATION;
+ display->transform.hflip = (rot_flip & EXT_HFLIP) > 0;
+
+ if (display->transform.rotation & 1)
+ SWAP(xres, yres);
+
+ set_external_display_transform_matrix(hwc_dev, disp);
+
+ return 0;
+}
+
+static int init_hdmi_display(omap_hwc_device_t *hwc_dev, int disp)
+{
+ hdmi_display_t *hdmi = (hdmi_display_t*)hwc_dev->displays[disp];
+ if (!hdmi)
+ return -ENODEV;
+
+ return 0;
+}
static void vblank_handler(int fd, unsigned int frame, unsigned int sec,
unsigned int usec, void *data)
@@ -647,7 +752,121 @@ primary_display_t *get_primary_display_info(omap_hwc_device_t *hwc_dev)
return primary;
}
+int add_external_hdmi_display(omap_hwc_device_t *hwc_dev)
+{
+ if (hwc_dev->displays[HWC_DISPLAY_EXTERNAL]) {
+ ALOGE("Display %d is already connected", HWC_DISPLAY_EXTERNAL);
+ return -EBUSY;
+ }
+
+ drmModeConnector *connector;
+ drmModeEncoder *encoder;
+ drmModeModeInfoPtr mode;
+ uint32_t possible_crtcs;
+
+ int err, i, n, j;
+
+ drmSetMaster(hwc_dev->drm_fd);
+ connector = drmModeGetConnector(hwc_dev->drm_fd, hwc_dev->drm_resources->connectors[HWC_DISPLAY_EXTERNAL]);
+ if (!connector) {
+ ALOGE("No connector for DISPLAY %d\n", HWC_DISPLAY_EXTERNAL);
+ return -1;
+ }
+
+ //set the mode for connector to fix 640x480 resolution
+ mode = &connector->modes[0];
+
+ encoder = drmModeGetEncoder(hwc_dev->drm_fd, connector->encoders[0]);
+ if (!encoder) {
+ ALOGE("Failed to get encoder\n");
+ goto free_connector;
+ }
+
+ drmDropMaster(hwc_dev->drm_fd);
+
+ i = 0;
+ n = encoder->possible_crtcs;
+ while (!(n & 1)) {
+ n >>= 1;
+ i++;
+ }
+
+ err = allocate_display(sizeof(external_hdmi_display_t), HDMI_DISPLAY_CONFIGS, &hwc_dev->displays[HWC_DISPLAY_EXTERNAL]);
+ if (err)
+ return err;
+
+ //allocate the display object
+ err = init_hdmi_display(hwc_dev, HWC_DISPLAY_EXTERNAL);
+ if (err) {
+ remove_external_hdmi_display(hwc_dev);
+ return err;
+ }
+
+ display_t *display = hwc_dev->displays[HWC_DISPLAY_EXTERNAL];
+ display->disp_link.con = connector;
+ display->disp_link.enc = encoder;
+ display->disp_link.crtc_id = hwc_dev->drm_resources->crtcs[i];
+
+ //Change the mode to PREFERRED_HDMI_RESOLUTION
+
+ for (j = 0; j < connector->count_modes; j++) {
+ mode = &connector->modes[j];
+ if (!strcmp(mode->name, PREFERRED_HDMI_RESOLUTION)) {
+ break;
+ }
+ }
+
+ display->disp_link.mode = mode;
+ display->disp_link.evctx.version = DRM_EVENT_CONTEXT_VERSION;
+ display->disp_link.evctx.vblank_handler = vblank_handler;
+ display->disp_link.ctx = hwc_dev;
+ display->role = DISP_ROLE_EXTERNAL;
+ display->opmode = DISP_MODE_PRESENTATION;
+ display->type = DISP_TYPE_HDMI;
+ display->mgr_ix = 1;
+ display->blanked = true;
+
+ /* SurfaceFlinger currently doesn't unblank external display on reboot.
+ * Unblank HDMI display by default.
+ * See SurfaceFlinger::readyToRun() function.
+ */
+ display->update_transform = true;
+
+ //IMG_framebuffer_device_public_t *fb_dev = hwc_dev->fb_dev[HWC_DISPLAY_EXTERNAL];
+ uint32_t xres = HDMI_FB_WIDTH;
+ uint32_t yres = HDMI_FB_HEIGHT;
+
+ // TODO: Verify that HDMI supports xres x yres
+ // TODO: Set HDMI resolution? What if we need to do docking of 1080p i.s.o. Presentation?
+
+ setup_hdmi_config(&display->configs[0], xres, yres, &display->disp_link);
+
+ external_hdmi_display_t *ext_hdmi = (external_hdmi_display_t*)display;
+
+ /* check set props */
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.hwc.avoid_mode_change", value, "1");
+ ext_hdmi->avoid_mode_change = atoi(value) > 0;
+
+ return 0;
+
+
+free_connector:
+ drmModeFreeConnector(connector);
+ return -1;
+}
+
+void remove_external_hdmi_display(omap_hwc_device_t *hwc_dev)
+{
+ display_t *display = hwc_dev->displays[HWC_DISPLAY_EXTERNAL];
+ if (!display) {
+ ALOGW("Failed to remove non-existent display %d", HWC_DISPLAY_EXTERNAL);
+ return;
+ }
+
+ remove_display(hwc_dev, HWC_DISPLAY_EXTERNAL);
+}
static int get_layer_stack(omap_hwc_device_t *hwc_dev, int disp, uint32_t *stack)
{
@@ -726,6 +945,21 @@ void set_display_contents(omap_hwc_device_t *hwc_dev, size_t num_displays, hwc_d
update_primary_display_orientation(hwc_dev);
}
+int get_external_display_id(omap_hwc_device_t *hwc_dev)
+{
+ size_t i;
+ int disp = -1;
+
+ for (i = HWC_DISPLAY_EXTERNAL; i < MAX_DISPLAYS; i++) {
+ if (hwc_dev->displays[i] && hwc_dev->displays[i]->type != DISP_TYPE_UNKNOWN) {
+ disp = i;
+ break;
+ }
+ }
+
+ return disp;
+}
+
int get_display_configs(omap_hwc_device_t *hwc_dev, int disp, uint32_t *configs, size_t *numConfigs)
{
if (!numConfigs)
@@ -818,6 +1052,10 @@ bool is_lcd_display(omap_hwc_device_t *hwc_dev, int disp)
return is_valid_display(hwc_dev, disp) && hwc_dev->displays[disp]->type == DISP_TYPE_LCD;
}
+bool is_hdmi_display(omap_hwc_device_t *hwc_dev, int disp)
+{
+ return is_valid_display(hwc_dev, disp) && hwc_dev->displays[disp]->type == DISP_TYPE_HDMI;
+}
int blank_display(omap_hwc_device_t *hwc_dev, int disp)
{
@@ -895,6 +1133,8 @@ int setup_display_tranfsorm(omap_hwc_device_t *hwc_dev, int disp)
if (display->role == DISP_ROLE_PRIMARY)
set_primary_display_transform_matrix(hwc_dev);
+ else
+ err = setup_external_display_transform(hwc_dev, disp);
if (!err)
display->update_transform = false;
@@ -913,7 +1153,8 @@ int apply_display_transform(omap_hwc_device_t *hwc_dev, int disp)
if (!transform->rotation && !transform->hflip && !transform->scaling)
return 0;
- composition_t *comp = &display->composition;
+ composition_t *comp;
+ comp = &display->composition;
uint32_t i;
diff --git a/hwcomposer/display.h b/hwcomposer/display.h
index 40733df..22b1b47 100644
--- a/hwcomposer/display.h
+++ b/hwcomposer/display.h
@@ -43,6 +43,12 @@
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
#endif
+
+/* The three below parameters should be in sync, no scaling supported */
+#define PREFERRED_HDMI_RESOLUTION "1280x720"
+#define HDMI_FB_WIDTH 1280
+#define HDMI_FB_HEIGHT 720
+
typedef struct omap_hwc_device omap_hwc_device_t;
typedef struct dss_platform_info {
@@ -171,6 +177,23 @@ struct primary_lcd_display {
};
typedef struct primary_lcd_display primary_lcd_display_t;
+struct hdmi_display {
+ display_t base;
+
+ uint16_t width; /* external screen dimensions */
+ uint16_t height;
+ uint32_t video_mode_ix; /* TWO's complement of video mode index */
+};
+typedef struct hdmi_display hdmi_display_t;
+
+struct external_hdmi_display {
+ hdmi_display_t hdmi;
+
+ /* attributes */
+ bool avoid_mode_change; /* use HDMI mode used for mirroring if possible */
+};
+typedef struct external_hdmi_display external_hdmi_display_t;
+
int init_primary_display(omap_hwc_device_t *hwc_dev);
void reset_primary_display(omap_hwc_device_t *hwc_dev);
primary_display_t *get_primary_display_info(omap_hwc_device_t *hwc_dev);
@@ -204,4 +227,10 @@ bool can_dss_scale(omap_hwc_device_t *hwc_dev, uint32_t src_w, uint32_t src_h,
uint32_t dst_w, uint32_t dst_h, bool is_2d,
dss_platform_info_t *limits, uint32_t pclk);
+int add_external_hdmi_display(omap_hwc_device_t *hwc_dev);
+void remove_external_hdmi_display(omap_hwc_device_t *hwc_dev);
+int get_external_display_id(omap_hwc_device_t *hwc_dev);
+bool is_hdmi_display(omap_hwc_device_t *hwc_dev, int disp);
+bool is_external_display_mirroring(omap_hwc_device_t *hwc_dev, int disp);
+
#endif
diff --git a/hwcomposer/hwc.c b/hwcomposer/hwc.c
index c9d113c..6ccde76 100644
--- a/hwcomposer/hwc.c
+++ b/hwcomposer/hwc.c
@@ -106,6 +106,13 @@ static void reserve_overlays_for_displays(omap_hwc_device_t *hwc_dev)
primary_comp->scaling_ovls = primary_comp->avail_ovls - num_nonscaling_overlays;
primary_comp->used_ovls = 0;
+ int ext_disp = get_external_display_id(hwc_dev);
+
+ if (ext_disp < 0)
+ return;
+
+ display_t *ext_display = hwc_dev->displays[ext_disp];
+
/*
* For primary display we must reserve at least one overlay for FB, plus an extra
* overlay for each protected layer.
@@ -118,6 +125,20 @@ static void reserve_overlays_for_displays(omap_hwc_device_t *hwc_dev)
primary_comp->wanted_ovls = MAX(max_overlays / 2, min_primary_overlays);
primary_comp->avail_ovls = MIN(max_primary_overlays, primary_comp->wanted_ovls);
+ /*
+ * We may not have enough overlays on the external display. We "reserve" them here but
+ * may not do external composition for the first frame while the overlays required for
+ * it are cleared.
+ */
+ composition_t *ext_comp = &ext_display->composition;
+
+ ext_comp->tiler1d_slot_size = hwc_dev->tiler1d_slot_size;
+ ext_comp->wanted_ovls = max_overlays - primary_comp->wanted_ovls;
+ ext_comp->avail_ovls = MIN(max_external_overlays, ext_comp->wanted_ovls);
+ ext_comp->scaling_ovls = ext_comp->avail_ovls;
+ ext_comp->used_ovls = 0;
+ ext_comp->ovl_ix_base = MAX_DSS_OVERLAYS - ext_comp->avail_ovls;
+
}
@@ -160,6 +181,7 @@ static int hwc_prepare_for_display(omap_hwc_device_t *hwc_dev, int disp)
return 0;
display_t *display = hwc_dev->displays[disp];
+ hdmi_display_t *hdmi = is_hdmi_display(hwc_dev, disp) ? (hdmi_display_t*)display : NULL;
hwc_display_contents_1_t *list = display->contents;
composition_t *comp = &display->composition;
layer_statistics_t *layer_stats = &display->layer_stats;
@@ -290,7 +312,7 @@ static int hwc_prepare_for_display(omap_hwc_device_t *hwc_dev, int disp)
/* If display is blanked or not configured drop compositions */
- if (display->blanked)
+ if (display->blanked || (hdmi && !is_hdmi_display(hwc_dev, HWC_DISPLAY_PRIMARY) && hdmi->video_mode_ix == 0))
hwc_dev->num_ovls = 0;
return 0;
@@ -484,6 +506,55 @@ static int hwc_device_close(hw_device_t* device)
return 0;
}
+
+static void handle_hotplug(omap_hwc_device_t *hwc_dev)
+{
+ /* WA: till Hotplug is integrated */
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.hwc.hdmi.force.init", value, "0");
+ if (atoi(value)) {
+ hwc_dev->ext_disp_state = 1;
+ }
+
+ bool state = hwc_dev->ext_disp_state;
+ bool hotplug = false;
+
+ pthread_mutex_lock(&hwc_dev->ctx_mutex);
+
+ if (state) {
+ int ext_disp = get_external_display_id(hwc_dev);
+ int err = add_external_hdmi_display(hwc_dev);
+ if (err) {
+ remove_external_hdmi_display(hwc_dev);
+ pthread_mutex_unlock(&hwc_dev->ctx_mutex);
+ return;
+ }
+ } else
+ remove_external_hdmi_display(hwc_dev);
+
+ display_t *ext_display = hwc_dev->displays[HWC_DISPLAY_EXTERNAL];
+ ALOGI("external display changed (state=%d, config={tform=%ddeg%s}, tv=%d", state,
+ ext_display ? ext_display->transform.rotation * 90 : -1,
+ ext_display ? ext_display->transform.hflip ? "+hflip" : "" : "",
+ is_hdmi_display(hwc_dev, HWC_DISPLAY_EXTERNAL));
+
+ hotplug = true;
+
+ pthread_mutex_unlock(&hwc_dev->ctx_mutex);
+
+ /* hwc_dev->cb_procs is set right after the device is opened, but there is
+ * still a race condition where a hotplug event might occur after the open
+ * but before the procs are registered. */
+ if (hwc_dev->cb_procs) {
+ if (hotplug && hwc_dev->cb_procs->hotplug) {
+ hwc_dev->cb_procs->hotplug(hwc_dev->cb_procs, HWC_DISPLAY_EXTERNAL, state);
+ } else {
+ if (hwc_dev->cb_procs->invalidate)
+ hwc_dev->cb_procs->invalidate(hwc_dev->cb_procs);
+ }
+ }
+}
+
static void hwc_registerProcs(struct hwc_composer_device_1* dev,
hwc_procs_t const* procs)
{
@@ -638,6 +709,8 @@ static int hwc_device_open(const hw_module_t* module, const char* name, hw_devic
property_get("debug.hwc.idle", value, "250");
hwc_dev->idle = atoi(value);
+ handle_hotplug(hwc_dev);
+
ALOGI("open_device(rgb_order=%d nv12_only=%d)",
hwc_dev->flags_rgb_order, hwc_dev->flags_nv12_only);