diff options
-rw-r--r-- | hwc/Android.mk | 5 | ||||
-rw-r--r-- | hwc/hwc.c | 262 |
2 files changed, 264 insertions, 3 deletions
diff --git a/hwc/Android.mk b/hwc/Android.mk index 489d739..60b421d 100644 --- a/hwc/Android.mk +++ b/hwc/Android.mk @@ -16,6 +16,11 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := hwcomposer.omap4 LOCAL_CFLAGS := -DLOG_TAG=\"ti_hwc\" LOCAL_C_INCLUDES += external/libpng external/zlib + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/../edid/inc +LOCAL_SHARED_LIBRARIES += libedid + # LOG_NDEBUG=0 means verbose logging enabled # LOCAL_CFLAGS += -DLOG_NDEBUG=0 include $(BUILD_SHARED_LIBRARY) @@ -25,6 +25,7 @@ #include <linux/omapfb.h> #include <sys/mman.h> #include <sys/resource.h> +#include <stdbool.h> #include <cutils/properties.h> #include <cutils/log.h> @@ -37,6 +38,9 @@ #include <utils/Timers.h> #include <system/graphics.h> +#include <ui/S3DFormat.h> +#include <edid_parser.h> + #include <linux/bltsville.h> #define MAX_HWC_LAYERS 32 @@ -100,6 +104,11 @@ struct omap4_hwc_ext { __u32 yres; float m[2][3]; /* external transformation matrix */ hwc_rect_t mirror_region; /* region of screen to mirror */ + + bool s3d_enabled; + bool s3d_capable; + enum S3DLayoutType s3d_type; + enum S3DLayoutOrder s3d_order; }; typedef struct omap4_hwc_ext omap4_hwc_ext_t; @@ -193,6 +202,9 @@ struct omap4_hwc_device { int last_ext_ovls; /* # of overlays on external/internal display for last composition */ int last_int_ovls; + enum S3DLayoutType s3d_input_type; + enum S3DLayoutOrder s3d_input_order; + enum bltmode blt_mode; enum bltpolicy blt_policy; @@ -403,6 +415,17 @@ static int omap4_hwc_is_valid_format(int format) } } +static __u32 get_s3d_layout_type(hwc_layer_t *layer) +{ + return (layer->flags & S3DLayoutTypeMask) >> S3DLayoutTypeShift; +} + +static __u32 get_s3d_layout_order(hwc_layer_t *layer) +{ + return (layer->flags & S3DLayoutOrderMask) >> S3DLayoutOrderShift; +} + + static int scaled(hwc_layer_t *layer) { int w = WIDTH(layer->sourceCrop); @@ -411,7 +434,9 @@ static int scaled(hwc_layer_t *layer) if (layer->transform & HWC_TRANSFORM_ROT_90) swap(w, h); - return WIDTH(layer->displayFrame) != w || HEIGHT(layer->displayFrame) != h; + /* An S3D layer also needs scaling due to subsampling */ + return WIDTH(layer->displayFrame) != w || HEIGHT(layer->displayFrame) != h + || get_s3d_layout_type(layer) != eMono; } static int is_protected(hwc_layer_t *layer) @@ -1073,10 +1098,28 @@ static void gather_layer_statistics(omap4_hwc_device_t *hwc_dev, struct counts * for (i = 0; list && i < list->numHwLayers; i++) { hwc_layer_t *layer = &list->hwLayers[i]; IMG_native_handle_t *handle = (IMG_native_handle_t *)layer->handle; + __u32 s3d_layout_type = get_s3d_layout_type(layer); layer->compositionType = HWC_FRAMEBUFFER; if (omap4_hwc_is_valid_layer(hwc_dev, layer, handle)) { + + if (s3d_layout_type != eMono) { + /* For now we can only handle 1 S3D layer, skip any additional ones */ + if (num->s3d > 0 || !hwc_dev->ext.dock.enabled || !hwc_dev->ext.s3d_capable) { + layer->flags |= HWC_SKIP_LAYER; + continue; + } else if (num->s3d == 0) { + /* For now, S3D layer is made a dockable layer to trigger docking logic. */ + if (!dockable(layer)) { + num->dockable++; + } + num->s3d++; + hwc_dev->s3d_input_type = s3d_layout_type; + hwc_dev->s3d_input_order = get_s3d_layout_order(layer); + } + } + num->possible_overlay_layers++; /* NV12 layers can only be rendered on scaling overlays */ @@ -1118,6 +1161,11 @@ static void decide_supported_cloning(omap4_hwc_device_t *hwc_dev, struct counts /* reserve just a video pipeline for HDMI if docking */ hwc_dev->ext_ovls = (num->dockable || ext->force_dock) ? 1 : 0; + + if (num->s3d && (hwc_dev->ext.s3d_type != hwc_dev->s3d_input_type)) { + /* S3D layers are dockable, and they need two overlays */ + hwc_dev->ext_ovls += 1; + } num->max_hw_overlays -= max(hwc_dev->ext_ovls, hwc_dev->last_ext_ovls); /* use mirroring transform if we are auto-switching to docking mode while mirroring*/ @@ -1293,6 +1341,152 @@ static int clone_external_layer(omap4_hwc_device_t *hwc_dev, int ix) { return clone_layer(hwc_dev, ix); } + +const char hdmiS3DTypePath[] = "/sys/devices/platform/omapdss/display1/s3d_type"; +const char hdmiS3DEnablePath[] = "/sys/devices/platform/omapdss/display1/s3d_enable"; + +static void +omap4_hwc_s3d_hdmi_enable(omap4_hwc_device_t *hwc_dev, bool enable) +{ + size_t bytesWritten; + char data; + int fd; + + if (hwc_dev->ext.s3d_enabled == enable) { + return; + } + + if (enable) { + char type[2]; + + switch(hwc_dev->ext.s3d_type) { + case eSideBySide: + snprintf(type, sizeof(type), "%d", HDMI_SIDE_BY_SIDE_HALF); + break; + case eTopBottom: + snprintf(type, sizeof(type), "%d", HDMI_TOPBOTTOM); + break; + default: + return; + } + + fd = open(hdmiS3DTypePath, O_WRONLY); + if (fd < 0) { + ALOGE("Failed to open sysfs %s", hdmiS3DTypePath); + return; + } + bytesWritten = write(fd, type, sizeof(type)); + close(fd); + + if (bytesWritten != sizeof(type)) { + ALOGE("Failed to write (%s) to sysfs %s", type, hdmiS3DTypePath); + return; + } + } + data = enable ? '1' : '0'; + + fd = open(hdmiS3DEnablePath, O_WRONLY); + if (fd < 0) { + ALOGE("Failed to open sysfs %s", hdmiS3DEnablePath); + return; + } + bytesWritten = write(fd, &data, 1); + close(fd); + + if (bytesWritten != 1) { + ALOGE("Failed to write(%d) to sysfs %s", enable, hdmiS3DEnablePath); + return; + } + + hwc_dev->ext.s3d_enabled = enable; +} + +static void +omap4_hwc_adjust_ext_s3d_layer(omap4_hwc_device_t *hwc_dev, + struct dss2_ovl_info *ovl, + bool leftView) +{ + struct dss2_ovl_cfg *oc = &ovl->cfg; + float x, y, w, h; + + switch (hwc_dev->s3d_input_type) { + case eSideBySide: + oc->crop.w = oc->crop.w/2; + if ((leftView && hwc_dev->s3d_input_order == eRightViewFirst) || + (!leftView && hwc_dev->s3d_input_order == eLeftViewFirst)) { + oc->crop.x = oc->crop.x + oc->crop.w; + } + break; + case eTopBottom: + oc->crop.h = oc->crop.h/2; + if ((leftView && hwc_dev->s3d_input_order == eRightViewFirst) || + (!leftView && hwc_dev->s3d_input_order == eLeftViewFirst)) { + oc->crop.y = oc->crop.y + oc->crop.h; + } + break; + default: + /* Should never fall here! */ + ALOGE("Unsupported S3D layer type!"); + break; + } + + switch (hwc_dev->ext.s3d_type) { + case eSideBySide: + oc->win.w = oc->win.w/2; + if ((leftView && hwc_dev->ext.s3d_order == eRightViewFirst) || + (!leftView && hwc_dev->ext.s3d_order == eLeftViewFirst)) { + oc->win.x = oc->win.x/2 + hwc_dev->ext.xres/2; + } else { + oc->win.x = oc->win.x/2; + } + break; + case eTopBottom: + oc->win.h = oc->win.h/2; + if ((leftView && hwc_dev->ext.s3d_order == eRightViewFirst) || + (!leftView && hwc_dev->ext.s3d_order == eLeftViewFirst)) { + oc->win.y = oc->win.y/2 + hwc_dev->ext.yres/2; + } else { + oc->win.y = oc->win.y/2; + } + break; + default: + /* Currently unhandled!!! */ + ALOGE("Unsupported S3D display type!"); + break; + } +} + +static int +clone_s3d_external_layer(omap4_hwc_device_t *hwc_dev, int ix_s3d) +{ + struct dsscomp_setup_dispc_data *dsscomp = &hwc_dev->comp_data.dsscomp_data; + int r; + + /* S3D layers are forced into docking layers. If the display layout and + * the layer layout don't match, we have to use 2 overlay pipelines */ + r = clone_external_layer(hwc_dev, ix_s3d); + if (r) { + ALOGE("Failed to clone s3d layer (%d)", r); + return r; + } + + r = clone_layer(hwc_dev, ix_s3d); + if (r) { + ALOGE("Failed to clone s3d layer (%d)", r); + return r; + } + + if (dsscomp->num_ovls < 2) { + ALOGE("Number of overlays is inconsistent (%d)", dsscomp->num_ovls); + return -EINVAL; + } + + omap4_hwc_adjust_ext_s3d_layer(hwc_dev, &dsscomp->ovls[dsscomp->num_ovls - 1], true); + omap4_hwc_adjust_ext_s3d_layer(hwc_dev, &dsscomp->ovls[dsscomp->num_ovls - 2], false); + + return 0; +} + static int setup_mirroring(omap4_hwc_device_t *hwc_dev) { omap4_hwc_ext_t *ext = &hwc_dev->ext; @@ -1508,6 +1702,7 @@ static int omap4_hwc_prepare(struct hwc_composer_device *dev, hwc_layer_list_t* int fb_z = -1; int scaled_gfx = 0; int ix_docking = -1; + int ix_s3d = -1; int blit_all = 0; blit_reset(hwc_dev, list ? list->flags : 0); @@ -1593,6 +1788,10 @@ static int omap4_hwc_prepare(struct hwc_composer_device *dev, hwc_layer_list_t* display_area(&dsscomp->ovls[dsscomp->num_ovls]) > display_area(&dsscomp->ovls[ix_docking]))) ix_docking = dsscomp->num_ovls; + /* remember the ix for s3d layer */ + if (get_s3d_layout_type(layer) != eMono) { + ix_s3d = dsscomp->num_ovls; + } dsscomp->num_ovls++; z++; } else if (hwc_dev->use_sgx) { @@ -1652,8 +1851,27 @@ static int omap4_hwc_prepare(struct hwc_composer_device *dev, hwc_layer_list_t* hwc_dev->post2_layers = dsscomp->num_ovls; omap4_hwc_ext_t *ext = &hwc_dev->ext; - if (ext->current.enabled && hwc_dev->ext_ovls) { - if (ext->current.docking && ix_docking >= 0) { + if (ext->current.enabled && ((!num.protected && hwc_dev->ext_ovls) || + (hwc_dev->ext_ovls_wanted && hwc_dev->ext_ovls >= hwc_dev->ext_ovls_wanted))) { + if (ext->current.docking && ix_s3d >= 0) { + if (clone_s3d_external_layer(hwc_dev, ix_s3d) == 0) { + dsscomp->ovls[dsscomp->num_ovls - 2].cfg.zorder = z++; + dsscomp->ovls[dsscomp->num_ovls - 1].cfg.zorder = z++; + /* For now, show only the left view of an S3D layer + * in the local display while we have hdmi attached */ + switch (hwc_dev->s3d_input_type) { + case eSideBySide: + dsscomp->ovls[ix_s3d].cfg.crop.w = dsscomp->ovls[ix_s3d].cfg.crop.w/2; + break; + case eTopBottom: + dsscomp->ovls[ix_s3d].cfg.crop.h = dsscomp->ovls[ix_s3d].cfg.crop.h/2; + break; + default: + ALOGE("Unsupported S3D input type"); + break; + } + } + } else if (ext->current.docking && ix_docking >= 0) { if (clone_external_layer(hwc_dev, ix_docking) == 0) dsscomp->ovls[dsscomp->num_ovls - 1].cfg.zorder = z++; } else if (ext->current.docking && ix_docking < 0 && ext->force_dock) { @@ -1690,6 +1908,9 @@ static int omap4_hwc_prepare(struct hwc_composer_device *dev, hwc_layer_list_t* omap4_hwc_adjust_primary_display_layer(hwc_dev, &dsscomp->ovls[i]); } + + omap4_hwc_s3d_hdmi_enable(hwc_dev, ix_s3d >= 0); + ext->last = ext->current; if (z != dsscomp->num_ovls || dsscomp->num_ovls > MAX_HW_OVERLAYS) @@ -2086,6 +2307,38 @@ static void set_primary_display_transform_matrix(omap4_hwc_device_t *hwc_dev) } + +static void handle_s3d_hotplug(omap4_hwc_ext_t *ext, int state) +{ + struct edid_t *edid = NULL; + if (state) { + int fd = open("/sys/devices/platform/omapdss/display1/edid", O_RDONLY); + if (!fd) + return; + uint8_t edid_data[EDID_SIZE]; + size_t bytes_read = read(fd, edid_data, EDID_SIZE); + close(fd); + if (bytes_read < EDID_SIZE) + return; + if (edid_parser_init(&edid, edid_data)) + return; + } + + ext->s3d_enabled = false; + ext->s3d_capable = false; + ext->s3d_type = eMono; + ext->s3d_order = eLeftViewFirst; + + if (edid) { + ext->s3d_capable = edid_s3d_capable(edid); + /* For now assume Side-by-Side half support applies to all modes */ + ext->s3d_type = eSideBySide; + ext->s3d_order = eLeftViewFirst; + edid_parser_deinit(edid); + } +} + + static void handle_hotplug(omap4_hwc_device_t *hwc_dev) { omap4_hwc_ext_t *ext = &hwc_dev->ext; @@ -2115,6 +2368,9 @@ static void handle_hotplug(omap4_hwc_device_t *hwc_dev) } pthread_mutex_lock(&hwc_dev->lock); + + handle_s3d_hotplug(ext, state); + ext->dock.enabled = ext->mirror.enabled = 0; if (state) { /* check whether we can clone and/or dock */ |