hwc: Add support for video overlay

Graphic Layer 1 is always used for the FB layer and Graphic
Layer 0 for the Video layer

Bug: 7123196
Change-Id: Ia4d4bbb10598cd32beac44324025dcae7b6fac19
diff --git a/libhwc/hwc.cpp b/libhwc/hwc.cpp
index e4ce96b..920a0ce 100644
--- a/libhwc/hwc.cpp
+++ b/libhwc/hwc.cpp
@@ -123,7 +123,9 @@
     bool hdmi_blanked;
     int  hdmi_w;
     int  hdmi_h;
-    hdmi_layer_t hdmi_layers[2];
+
+    exynos5_gsc_data_t      hdmi_gsc;
+    hdmi_layer_t            hdmi_layers[2];
 
     exynos5_gsc_data_t      gsc[NUM_GSC_UNITS];
 
@@ -445,6 +447,14 @@
         return -1;
     }
 
+    if (hl.id == 1) {
+        if (exynos_v4l2_s_ctrl(hl.fd, V4L2_CID_TV_PIXEL_BLEND_ENABLE, 1) < 0) {
+            ALOGE("%s: layer%d: PIXEL_BLEND_ENABLE failed %d", __func__,
+                                                                hl.id, errno);
+            return -1;
+        }
+    }
+
     ALOGV("%s: layer%d enabled", __func__, hl.id);
     hl.enabled = true;
     return 0;
@@ -529,7 +539,7 @@
         return -1;
     }
 
-    hdmi_enable_layer(dev, dev->hdmi_layers[0]);
+    hdmi_enable_layer(dev, dev->hdmi_layers[1]);
 
     dev->hdmi_enabled = true;
     return 0;
@@ -541,22 +551,26 @@
         return;
 
     hdmi_disable_layer(dev, dev->hdmi_layers[0]);
+    hdmi_disable_layer(dev, dev->hdmi_layers[1]);
+
+    exynos5_gsc_data_t *gsc_data = &dev->hdmi_gsc;
+    for (size_t i = 0; i < NUM_GSC_DST_BUFS; i++) {
+        if (gsc_data->dst_buf[i])
+            dev->alloc_device->free(dev->alloc_device, gsc_data->dst_buf[i]);
+    }
+
+    memset(gsc_data, 0, sizeof(*gsc_data));
 
     dev->hdmi_enabled = false;
 }
 
 static int hdmi_output(struct exynos5_hwc_composer_device_1_t *dev,
                        hdmi_layer_t &hl,
-                       hwc_layer_1_t &layer)
+                       hwc_layer_1_t &layer,
+                       private_handle_t *h)
 {
-    private_handle_t *h = private_handle_t::dynamicCast(layer.handle);
     int ret = 0;
 
-    if (!hl.enabled) {
-        ret = -1;
-        goto err;
-    }
-
     exynos_gsc_img cfg;
     memset(&cfg, 0, sizeof(cfg));
     cfg.x = layer.displayFrame.left;
@@ -565,6 +579,8 @@
     cfg.h = HEIGHT(layer.displayFrame);
 
     if (gsc_src_cfg_changed(hl.cfg, cfg)) {
+        hdmi_disable_layer(dev, hl);
+
         struct v4l2_format fmt;
         memset(&fmt, 0, sizeof(fmt));
         fmt.type  = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
@@ -579,6 +595,24 @@
             goto err;
         }
 
+        struct v4l2_subdev_crop sd_crop;
+        memset(&sd_crop, 0, sizeof(sd_crop));
+        if (hl.id == 0)
+            sd_crop.pad   = MIXER_G0_SUBDEV_PAD_SOURCE;
+        else
+            sd_crop.pad   = MIXER_G1_SUBDEV_PAD_SOURCE;
+        sd_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+        sd_crop.rect.left   = cfg.x;
+        sd_crop.rect.top    = cfg.y;
+        sd_crop.rect.width  = cfg.w;
+        sd_crop.rect.height = cfg.h;
+        if (exynos_subdev_s_crop(dev->hdmi_mixer0, &sd_crop) < 0) {
+            ALOGE("%s: s_crop failed pad=%d", __func__, sd_crop.pad);
+            goto err;
+        }
+
+        hdmi_enable_layer(dev, hl);
+
         ALOGV("HDMI layer%d configuration:", hl.id);
         dump_gsc_img(cfg);
         hl.cfg = cfg;
@@ -928,6 +962,7 @@
         hwc_display_contents_1_t* contents)
 {
     ALOGV("preparing %u layers for HDMI", contents->numHwLayers);
+    hwc_layer_1_t *video_layer = NULL;
 
     for (size_t i = 0; i < contents->numHwLayers; i++) {
         hwc_layer_1_t &layer = contents->hwLayers[i];
@@ -943,6 +978,19 @@
             continue;
         }
 
+        if (layer.handle) {
+            private_handle_t *h = private_handle_t::dynamicCast(layer.handle);
+            if (h->flags & GRALLOC_USAGE_PROTECTED) {
+                if (!video_layer) {
+                    video_layer = &layer;
+                    layer.compositionType = HWC_OVERLAY;
+                    ALOGV("\tlayer %u: video layer", i);
+                    dump_layer(&layer);
+                    continue;
+                }
+            }
+        }
+
         layer.compositionType = HWC_FRAMEBUFFER;
         dump_layer(&layer);
     }
@@ -986,7 +1034,7 @@
         alloc_device_t* alloc_device, exynos5_gsc_data_t *gsc_data,
         int gsc_idx)
 {
-    ALOGV("configuring gscaler %u for memory-to-memory", gsc_idx);
+    ALOGV("configuring gscaler %u for memory-to-memory", AVAILABLE_GSC_UNITS[gsc_idx]);
 
     private_handle_t *src_handle = private_handle_t::dynamicCast(layer.handle);
     buffer_handle_t dst_buf;
@@ -1332,6 +1380,9 @@
 static int exynos5_set_hdmi(exynos5_hwc_composer_device_1_t *pdev,
         hwc_display_contents_1_t* contents)
 {
+    hwc_layer_1_t *fb_layer = NULL;
+    hwc_layer_1_t *video_layer = NULL;
+
     if (!pdev->hdmi_enabled) {
         for (size_t i = 0; i < contents->numHwLayers; i++) {
             hwc_layer_1_t &layer = contents->hwLayers[i];
@@ -1349,6 +1400,41 @@
             continue;
         }
 
+        if (layer.compositionType == HWC_OVERLAY) {
+             if (!layer.handle)
+                continue;
+
+            ALOGV("HDMI video layer:");
+            dump_layer(&layer);
+
+            if (layer.acquireFenceFd != -1) {
+                int err = sync_wait(layer.acquireFenceFd, 100);
+                if (err != 0)
+                    ALOGW("fence for layer %zu didn't signal in 100 ms: %s",
+                                                    i, strerror(errno));
+                close(layer.acquireFenceFd);
+                layer.acquireFenceFd = -1;
+            }
+
+            exynos5_gsc_data_t &gsc = pdev->hdmi_gsc;
+            exynos5_config_gsc_m2m(layer, pdev->alloc_device, &gsc, 1);
+
+            int err = exynos_gsc_stop_exclusive(gsc.gsc);
+            exynos_gsc_destroy(gsc.gsc);
+            gsc.gsc = NULL;
+            if (err < 0) {
+                ALOGE("failed to dequeue gscaler output for layer");
+                continue;
+            }
+
+            buffer_handle_t dst_buf = gsc.dst_buf[gsc.current_buf];
+            gsc.current_buf = (gsc.current_buf + 1) % NUM_GSC_DST_BUFS;
+            private_handle_t *h = private_handle_t::dynamicCast(dst_buf);
+
+            hdmi_output(pdev, pdev->hdmi_layers[0], layer, h);
+            video_layer = &layer;
+        }
+
         if (layer.compositionType == HWC_FRAMEBUFFER_TARGET) {
             if (!layer.handle)
                 continue;
@@ -1356,10 +1442,17 @@
             ALOGV("HDMI FB layer:");
             dump_layer(&layer);
 
-            hdmi_output(pdev, pdev->hdmi_layers[0], layer);
+            private_handle_t *h = private_handle_t::dynamicCast(layer.handle);
+            hdmi_output(pdev, pdev->hdmi_layers[1], layer, h);
+            fb_layer = &layer;
         }
     }
 
+    if (!video_layer)
+        hdmi_disable_layer(pdev, pdev->hdmi_layers[0]);
+    if (!fb_layer)
+        hdmi_disable_layer(pdev, pdev->hdmi_layers[1]);
+
     return 0;
 }