msm: rotator: sync point support

Add MSM_ROTATOR_IOCTL_BUFFER_SYNC ioctl interface.
Rotator will create a timeline for each session at START, wait for
input fence and create released fence in this ioctl call,
signal the timeline after rotation is done.

Change-Id: I3738f8287d804ccd94e0a16ac0afb8b41b299c75
Signed-off-by: Ken Zhang <kenz@codeaurora.org>
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index 448d5d2..dd9c8cb 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -29,6 +29,9 @@
 #include <linux/major.h>
 #include <linux/regulator/consumer.h>
 #include <linux/msm_ion.h>
+#include <linux/sync.h>
+#include <linux/sw_sync.h>
+
 #ifdef CONFIG_MSM_BUS_SCALING
 #include <mach/msm_bus.h>
 #include <mach/msm_bus_board.h>
@@ -91,6 +94,10 @@
 #define VERSION_KEY_MASK 0xFFFFFF00
 #define MAX_DOWNSCALE_RATIO 3
 
+#define MAX_TIMELINE_NAME_LEN 16
+#define WAIT_FENCE_FIRST_TIMEOUT MSEC_PER_SEC
+#define WAIT_FENCE_FINAL_TIMEOUT (10 * MSEC_PER_SEC)
+
 #define ROTATOR_REVISION_V0		0
 #define ROTATOR_REVISION_V1		1
 #define ROTATOR_REVISION_V2		2
@@ -131,6 +138,18 @@
 	struct list_head list;
 };
 
+struct rot_sync_info {
+	u32 initialized;
+	struct sync_fence *acq_fen;
+	int cur_rel_fen_fd;
+	struct sync_pt *cur_rel_sync_pt;
+	struct sync_fence *cur_rel_fence;
+	struct sync_fence *last_rel_fence;
+	struct sw_sync_timeline *timeline;
+	int timeline_value;
+	struct mutex sync_mutex;
+};
+
 struct msm_rotator_session {
 	struct msm_rotator_img_info img_info;
 	struct msm_rotator_fd_info fd_info;
@@ -167,6 +186,7 @@
 	#endif
 	u32 sec_mapped;
 	u32 mmu_clk_on;
+	struct rot_sync_info sync_info[MAX_SESSIONS];
 };
 
 #define COMPONENT_5BITS 1
@@ -348,6 +368,164 @@
 	return IRQ_HANDLED;
 }
 
+static void msm_rotator_signal_timeline(u32 session_index)
+{
+	struct rot_sync_info *sync_info;
+	sync_info = &msm_rotator_dev->sync_info[session_index];
+
+	if ((!sync_info->timeline) || (!sync_info->initialized))
+		return;
+
+	mutex_lock(&sync_info->sync_mutex);
+	sw_sync_timeline_inc(sync_info->timeline, 1);
+	sync_info->timeline_value++;
+	sync_info->last_rel_fence = sync_info->cur_rel_fence;
+	sync_info->cur_rel_fence = 0;
+	mutex_unlock(&sync_info->sync_mutex);
+}
+
+static void msm_rotator_release_acq_fence(u32 session_index)
+{
+	struct rot_sync_info *sync_info;
+	sync_info = &msm_rotator_dev->sync_info[session_index];
+
+	if ((!sync_info->timeline) || (!sync_info->initialized))
+		return;
+	mutex_lock(&sync_info->sync_mutex);
+	sync_info->acq_fen = NULL;
+	mutex_unlock(&sync_info->sync_mutex);
+}
+
+static void msm_rotator_release_all_timeline(void)
+{
+	int i;
+	struct rot_sync_info *sync_info;
+	for (i = 0; i < MAX_SESSIONS; i++) {
+		sync_info = &msm_rotator_dev->sync_info[i];
+		if (sync_info->initialized) {
+			msm_rotator_signal_timeline(i);
+			msm_rotator_release_acq_fence(i);
+		}
+	}
+}
+
+static void msm_rotator_wait_for_fence_sub(u32 session_index)
+{
+	struct rot_sync_info *sync_info;
+	int ret;
+	sync_info = &msm_rotator_dev->sync_info[session_index];
+	if (sync_info->acq_fen) {
+		ret = sync_fence_wait(sync_info->acq_fen,
+				WAIT_FENCE_FIRST_TIMEOUT);
+		if (ret == -ETIME) {
+			pr_warn("%s: timeout, wait %ld more ms\n",
+				__func__, WAIT_FENCE_FINAL_TIMEOUT);
+			ret = sync_fence_wait(sync_info->acq_fen,
+				WAIT_FENCE_FINAL_TIMEOUT);
+		}
+		if (ret < 0) {
+			pr_err("%s: sync_fence_wait failed! ret = %x\n",
+				__func__, ret);
+		}
+		sync_fence_put(sync_info->acq_fen);
+		sync_info->acq_fen = NULL;
+	}
+}
+
+static void msm_rotator_wait_for_fence(u32 session_index)
+{
+	struct rot_sync_info *sync_info;
+	sync_info = &msm_rotator_dev->sync_info[session_index];
+	if ((!sync_info->timeline) || (!sync_info->initialized))
+		return;
+
+	mutex_lock(&sync_info->sync_mutex);
+	msm_rotator_wait_for_fence_sub(session_index);
+	mutex_unlock(&sync_info->sync_mutex);
+}
+
+static int  msm_rotator_buf_sync(unsigned long arg)
+{
+	struct msm_rotator_buf_sync buf_sync;
+	int ret = 0;
+	struct sync_fence *fence = NULL;
+	struct rot_sync_info *sync_info;
+	u32 s;
+
+	if (copy_from_user(&buf_sync, (void __user *)arg, sizeof(buf_sync)))
+		return -EFAULT;
+
+	for (s = 0; s < MAX_SESSIONS; s++)
+		if ((msm_rotator_dev->rot_session[s] != NULL) &&
+			(buf_sync.session_id ==
+			(unsigned int)msm_rotator_dev->rot_session[s]
+			))
+			break;
+
+	if (s == MAX_SESSIONS)  {
+		pr_err("%s invalid session id %d", __func__,
+			buf_sync.session_id);
+		return -EINVAL;
+	}
+
+	sync_info = &msm_rotator_dev->sync_info[s];
+
+	if ((sync_info->timeline == NULL) ||
+		(sync_info->initialized == false))
+		return -EINVAL;
+
+	mutex_lock(&sync_info->sync_mutex);
+	if (buf_sync.acq_fen_fd >= 0)
+		fence = sync_fence_fdget(buf_sync.acq_fen_fd);
+
+	sync_info->acq_fen = fence;
+
+	if (sync_info->acq_fen &&
+		(buf_sync.flags & MDP_BUF_SYNC_FLAG_WAIT))
+		msm_rotator_wait_for_fence_sub(s);
+
+	sync_info->cur_rel_sync_pt = sw_sync_pt_create(sync_info->timeline,
+			sync_info->timeline_value + 1);
+	if (sync_info->cur_rel_sync_pt == NULL) {
+		pr_err("%s: cannot create sync point", __func__);
+		ret = -ENOMEM;
+		goto buf_sync_err_1;
+	}
+	/* create fence */
+	sync_info->cur_rel_fence = sync_fence_create("msm_rotator-fence",
+			sync_info->cur_rel_sync_pt);
+	if (sync_info->cur_rel_fence == NULL) {
+		sync_pt_free(sync_info->cur_rel_sync_pt);
+		sync_info->cur_rel_sync_pt = NULL;
+		pr_err("%s: cannot create fence", __func__);
+		ret = -ENOMEM;
+		goto buf_sync_err_1;
+	}
+	/* create fd */
+	sync_info->cur_rel_fen_fd = get_unused_fd_flags(0);
+	if (sync_info->cur_rel_fen_fd < 0) {
+		pr_err("%s: get_unused_fd_flags failed", __func__);
+		ret  = -EIO;
+		goto buf_sync_err_2;
+	}
+	sync_fence_install(sync_info->cur_rel_fence, sync_info->cur_rel_fen_fd);
+	buf_sync.rel_fen_fd = sync_info->cur_rel_fen_fd;
+
+	ret = copy_to_user((void __user *)arg, &buf_sync, sizeof(buf_sync));
+	mutex_unlock(&sync_info->sync_mutex);
+	return ret;
+buf_sync_err_2:
+	sync_fence_put(sync_info->cur_rel_fence);
+	sync_info->cur_rel_fence = NULL;
+	sync_info->cur_rel_fen_fd = 0;
+buf_sync_err_1:
+	if (sync_info->acq_fen)
+		sync_fence_put(sync_info->acq_fen);
+	sync_info->acq_fen = NULL;
+	mutex_unlock(&sync_info->sync_mutex);
+	return ret;
+}
+
 static unsigned int tile_size(unsigned int src_width,
 		unsigned int src_height,
 		const struct tile_parm *tp)
@@ -1246,6 +1424,8 @@
 	if (dst_planes.num_planes >= 3)
 		out_chroma2_paddr = out_chroma_paddr + dst_planes.plane_size[1];
 
+	msm_rotator_wait_for_fence(s);
+
 	cancel_delayed_work(&msm_rotator_dev->rot_clk_work);
 	if (msm_rotator_dev->rot_clk_state != CLK_EN) {
 		enable_rot_clks();
@@ -1373,6 +1553,7 @@
 		fput_light(srcp0_file, ps0_need);
 	else
 		put_img(srcp0_file, srcp0_ihdl, ROTATOR_SRC_DOMAIN, 0);
+	msm_rotator_signal_timeline(s);
 	mutex_unlock(&msm_rotator_dev->rotator_lock);
 	dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
 		__func__, rc);
@@ -1491,6 +1672,7 @@
 	unsigned int dst_w, dst_h;
 	unsigned int is_planar420 = 0;
 	int fast_yuv_en = 0;
+	struct rot_sync_info *sync_info;
 
 	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
 		return -EFAULT;
@@ -1635,6 +1817,7 @@
 		rot_session->img_info =	info;
 		rot_session->fd_info =	*fd_info;
 		rot_session->fast_yuv_enable = fast_yuv_en;
+		s = first_free_idx;
 	} else if (s == MAX_SESSIONS) {
 		dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
 			__func__);
@@ -1645,6 +1828,24 @@
 		rc = -EFAULT;
 	if ((rc == 0) && (info.secure))
 		map_sec_resource(msm_rotator_dev);
+
+	sync_info = &msm_rotator_dev->sync_info[s];
+	if ((rc == 0) && (sync_info->initialized == false)) {
+		char timeline_name[MAX_TIMELINE_NAME_LEN];
+		if (sync_info->timeline == NULL) {
+			snprintf(timeline_name, sizeof(timeline_name),
+				"msm_rot_%d", first_free_idx);
+			sync_info->timeline =
+				sw_sync_timeline_create(timeline_name);
+			if (sync_info->timeline == NULL)
+				pr_err("%s: cannot create %s time line",
+					__func__, timeline_name);
+			sync_info->timeline_value = 0;
+		}
+		mutex_init(&sync_info->sync_mutex);
+		sync_info->initialized = true;
+	}
+	sync_info->acq_fen = NULL;
 rotator_start_exit:
 	mutex_unlock(&msm_rotator_dev->rotator_lock);
 
@@ -1668,6 +1869,8 @@
 			if (msm_rotator_dev->last_session_idx == s)
 				msm_rotator_dev->last_session_idx =
 					INVALID_SESSION;
+			msm_rotator_signal_timeline(s);
+			msm_rotator_release_acq_fence(s);
 			kfree(msm_rotator_dev->rot_session[s]);
 			msm_rotator_dev->rot_session[s] = NULL;
 			break;
@@ -1741,6 +1944,7 @@
 	fd_info = (struct msm_rotator_fd_info *)filp->private_data;
 
 	mutex_lock(&msm_rotator_dev->rotator_lock);
+	msm_rotator_release_all_timeline();
 	if (--fd_info->ref_cnt > 0) {
 		mutex_unlock(&msm_rotator_dev->rotator_lock);
 		return 0;
@@ -1783,6 +1987,8 @@
 		return msm_rotator_do_rotate(arg);
 	case MSM_ROTATOR_IOCTL_FINISH:
 		return msm_rotator_finish(arg);
+	case MSM_ROTATOR_IOCTL_BUFFER_SYNC:
+		return msm_rotator_buf_sync(arg);
 
 	default:
 		dev_dbg(msm_rotator_dev->device,
@@ -2079,6 +2285,7 @@
 		disable_rot_clks();
 		msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
 	}
+	msm_rotator_release_all_timeline();
 	mutex_unlock(&msm_rotator_dev->rotator_lock);
 	return 0;
 }