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;
}