diff options
author | Sunita Nadampalli <sunitan@ti.com> | 2014-12-01 14:31:40 -0600 |
---|---|---|
committer | Gerrit Code Review <gerrit2@git.omapzoom.org> | 2015-01-19 16:18:43 -0600 |
commit | eb6a5023f72386cdf59db101bce9d4ce2777a418 (patch) | |
tree | 3e51b409da66aa6f7c96c2355e27a7bcdd61c032 | |
parent | e77c0df775e1138ec42e76f23084883e302b7e4c (diff) | |
download | dra7xx-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.c | 243 | ||||
-rw-r--r-- | hwcomposer/display.h | 29 | ||||
-rw-r--r-- | hwcomposer/hwc.c | 75 |
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); |