+static void dispc_irq_vsync_on_frame_handler(void *data, u32 mask)
+{
+ struct completion *completion;
+ unsigned int i;
+ u32 diff;
+ int ret;
+
+ spin_lock(&dispc.irq_lock);
+
+ dispc.frame_counter++;
+
+ diff = dispc.frame_counter - dispc.fc_last_use;
+ if (diff > 5 * 60 && dispc.fc_isr_registered) {
+ ret = omap_dispc_unregister_isr_unlocked(
+ dispc_irq_vsync_on_frame_handler,
+ data, DISPC_IRQ_VSYNC);
+ if (ret == 0)
+ dispc.fc_isr_registered = false;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dispc.fc_complete); i++) {
+ completion = xchg(&dispc.fc_complete[i], NULL);
+ if (completion != NULL)
+ complete(completion);
+ }
+
+ spin_unlock(&dispc.irq_lock);
+}
+
+int omap_dispc_wait_for_vsync_on_frame(u32 *frame,
+ unsigned long timeout, bool force)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+ bool need_to_wait = force;
+ unsigned long flags;
+ unsigned int i;
+ long time;
+ int ret;
+
+ spin_lock_irqsave(&dispc.irq_lock, flags);
+
+ if (!dispc.fc_isr_registered) {
+ ret = omap_dispc_register_isr_unlocked(
+ dispc_irq_vsync_on_frame_handler,
+ NULL, DISPC_IRQ_VSYNC);
+ if (ret)
+ goto out_unlock;
+ dispc.fc_isr_registered = true;
+ }
+ else {
+ need_to_wait |= *frame == dispc.frame_counter;
+ }
+ dispc.fc_last_use = dispc.frame_counter;
+
+ if (need_to_wait) {
+ for (i = 0; i < ARRAY_SIZE(dispc.fc_complete); i++) {
+ if (dispc.fc_complete[i] == NULL) {
+ dispc.fc_complete[i] = &completion;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(dispc.fc_complete)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ }
+
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+ ret = 0;
+ if (need_to_wait) {
+ time = wait_for_completion_interruptible_timeout(
+ &completion, msecs_to_jiffies(17 * 2));
+ if (time == 0)
+ ret = -ETIMEDOUT;
+ else if (time < 0)
+ ret = time;
+ }
+ if (ret != 0) {
+ spin_lock(&dispc.irq_lock);
+
+ for (i = 0; i < ARRAY_SIZE(dispc.fc_complete); i++) {
+ if (dispc.fc_complete[i] == &completion) {
+ dispc.fc_complete[i] = NULL;
+ break;
+ }
+ }
+
+ spin_unlock(&dispc.irq_lock);
+ }
+
+ *frame = dispc.frame_counter;
+ return ret;
+
+out_unlock:
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+ return ret;
+}
+
+int omap_dispc_get_line_status(void)
+{
+ int r;
+
+ r = dispc_runtime_get();
+ if (r < 0)
+ return r;
+
+ r = dispc_read_reg(DISPC_LINE_STATUS);
+
+ dispc_runtime_put();
+
+ return r;
+}
+