layer: check always commit handler list when checking commit possible
[platform/core/uifw/libtdm.git] / src / tdm_layer.c
index d677f16..abf2a91 100644 (file)
@@ -79,6 +79,8 @@ static void _tdm_layer_cb_wait_vblank(tdm_vblank *vblank, tdm_error error, unsig
                                                                          unsigned int tv_sec, unsigned int tv_usec, void *user_data);
 static void _tbm_layer_queue_acquirable_cb(tbm_surface_queue_h surface_queue, void *data);
 static void _tbm_layer_queue_destroy_cb(tbm_surface_queue_h surface_queue, void *data);
+static void _tdm_layer_cb_output_commit(tdm_output *output, unsigned int sequence,
+                                                                               unsigned int tv_sec, unsigned int tv_usec, void *user_data);
 
 EXTERN tdm_error
 tdm_layer_get_capabilities(tdm_layer *layer, tdm_layer_capability *capabilities)
@@ -359,6 +361,7 @@ _tdm_layer_free_all_buffers(tdm_private_layer *private_layer)
 
        LIST_FOR_EACH_ENTRY_SAFE(lm, lmm, &clone_list, link) {
                LIST_DEL(&lm->link);
+               tdm_output_remove_commit_handler_internal(private_output, _tdm_layer_cb_output_commit, lm);
                _tdm_layer_free_buffer(private_layer, lm->committed_buffer);
                free(lm);
        }
@@ -520,7 +523,7 @@ _tdm_layer_got_output_vblank(tdm_private_output *private_output, unsigned int se
        tdm_private_layer_commit_handler *lm = NULL, *lmm = NULL;
        tdm_private_display *private_display;
        struct list_head clone_list, pending_clone_list;
-       tdm_error ret;
+       tdm_error ret = TDM_ERROR_NONE;
 
        private_display = private_output->private_display;
 
@@ -568,13 +571,15 @@ _tdm_layer_got_output_vblank(tdm_private_output *private_output, unsigned int se
        if (tdm_debug_module & TDM_DEBUG_COMMIT)
                TDM_INFO("layer commit: output(%d) commit", private_output->pipe);
 
-       /* tdm_vblank APIs is for server. it should be called in unlock status*/
-       if (!private_output->layer_waiting_vblank) {
-               _pthread_mutex_unlock(&private_display->lock);
-               ret = tdm_vblank_wait(private_output->vblank, 0, 0, 1, _tdm_layer_cb_wait_vblank, private_output);
-               _pthread_mutex_lock(&private_display->lock);
-               TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
-               private_output->layer_waiting_vblank = 1;
+       if (private_output->current_dpms_value == TDM_OUTPUT_DPMS_ON) {
+               /* tdm_vblank APIs is for server. it should be called in unlock status*/
+               if (!private_output->layer_waiting_vblank) {
+                       _pthread_mutex_unlock(&private_display->lock);
+                       ret = tdm_vblank_wait(private_output->vblank, 0, 0, 1, _tdm_layer_cb_wait_vblank, private_output);
+                       _pthread_mutex_lock(&private_display->lock);
+                       TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed);
+                       private_output->layer_waiting_vblank = 1;
+               }
        }
 
        if (tdm_debug_module & TDM_DEBUG_COMMIT)
@@ -585,13 +590,30 @@ _tdm_layer_got_output_vblank(tdm_private_output *private_output, unsigned int se
                LIST_ADDTAIL(&lm->link, &private_output->layer_commit_handler_list);
        }
 
+       if (private_output->current_dpms_value != TDM_OUTPUT_DPMS_ON) {
+               TDM_WRN("TDM_OUTPUT_DPMS_OFF. Directly call vblank callback.");
+               _pthread_mutex_unlock(&private_display->lock);
+               _tdm_layer_cb_wait_vblank(private_output->vblank, 0, 0, 0, 0, private_output);
+               _pthread_mutex_lock(&private_display->lock);
+       }
+
        return;
 wait_failed:
+       /* CAUTION: Once _tdm_layer_commit returns success, the layer commit handler MUST be called always.
+        * That is, even if we get error in _tdm_layer_got_output_vblank() function for some reasons,
+        * the layer commit handler MUST be called.
+        */
        LIST_FOR_EACH_ENTRY_SAFE(lm, lmm, &pending_clone_list, link) {
+               TDM_WRN("Directly call layer commit handlers: ret(%d)\n", ret);
                LIST_DEL(&lm->link);
+               _pthread_mutex_unlock(&private_display->lock);
+               if (lm->func)
+                       lm->func(lm->private_layer, sequence, tv_sec, tv_usec, lm->user_data);
+               _pthread_mutex_lock(&private_display->lock);
                _tdm_layer_free_buffer(lm->private_layer, lm->committed_buffer);
                free(lm);
        }
+
        return;
 }
 
@@ -678,27 +700,44 @@ _tdm_lauer_get_output_used_layer_count(tdm_private_output *private_output)
        return count;
 }
 
+/* commit_per_vblank == 1: we can commit if
+ *                           - there is no previous commit request
+ *                           - only 1 layer is used
+ * commit_per_vblank == 2: we can commit if
+ *                           - there is no previous commit request
+ */
 static int
 _tdm_layer_commit_possible(tdm_private_layer *private_layer)
 {
        tdm_private_output *private_output = private_layer->private_output;
        tdm_private_display *private_display = private_output->private_display;
 
-       if (private_display->commit_per_vblank == 1 && _tdm_lauer_get_output_used_layer_count(private_output) == 1) {
+       TDM_RETURN_VAL_IF_FAIL(!(private_display->commit_per_vblank > 0), 1);
+
+       /* There is a previous commit request which is not done and displayed on screen yet.
+        * We can't commit at this time.
+        */
+       if (!LIST_IS_EMPTY(&private_output->layer_commit_handler_list)) {
                if (tdm_debug_module & TDM_DEBUG_COMMIT)
-                       TDM_INFO("layer(%p) commit: 1 layer", private_layer);
-               return 1;
+                       TDM_INFO("layer(%p) commit: not possible(previous commit)", private_layer);
+               return 0;
        }
 
-       if (private_display->commit_per_vblank == 2 && LIST_IS_EMPTY(&private_output->layer_commit_handler_list)) {
+       if (private_display->commit_per_vblank == 1 && _tdm_lauer_get_output_used_layer_count(private_output) > 1) {
                if (tdm_debug_module & TDM_DEBUG_COMMIT)
-                       TDM_INFO("layer(%p) commit: non previous commit", private_layer);
-               return 1;
+                       TDM_INFO("layer(%p) commit: not possible(more than 2 layers)", private_layer);
+               return 0;
        }
 
-       return 0;
+       TDM_INFO("layer(%p) commit: possible", private_layer);
+
+       return 1;
 }
 
+/* CAUTION: Once _tdm_layer_commit returns success, the layer commit handler MUST be called always.
+ * That is, even if we get error in _tdm_layer_got_output_vblank() function for some reasons,
+ * the layer commit handler MUST be called.
+ */
 static tdm_error
 _tdm_layer_commit(tdm_layer *layer, tdm_layer_commit_handler func, void *user_data)
 {
@@ -836,6 +875,51 @@ tdm_layer_is_committing(tdm_layer *layer, unsigned int *committing)
        return ret;
 }
 
+INTERN void
+tdm_layer_remove_commit_handler_internal(tdm_layer *layer, tdm_layer_commit_handler func, void *user_data)
+{
+       tdm_private_layer *private_layer = (tdm_private_layer*)layer;
+       tdm_private_output *private_output = private_layer->private_output;
+       tdm_private_layer_commit_handler *lm = NULL, *lmm = NULL;
+
+       TDM_RETURN_IF_FAIL(private_layer != NULL);
+       TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
+
+       LIST_FOR_EACH_ENTRY_SAFE(lm, lmm, &private_output->layer_commit_handler_list, link) {
+               if (lm->func == func && lm->user_data == user_data) {
+                       LIST_DEL(&lm->link);
+                       tdm_output_remove_commit_handler_internal(private_output, _tdm_layer_cb_output_commit, lm);
+                       _tdm_layer_free_buffer(private_layer, lm->committed_buffer);
+                       free(lm);
+                       break;
+               }
+       }
+
+       LIST_FOR_EACH_ENTRY_SAFE(lm, lmm, &private_output->pending_commit_handler_list, link) {
+               if (lm->func == func && lm->user_data == user_data) {
+                       LIST_DEL(&lm->link);
+                       tdm_output_remove_commit_handler_internal(private_output, _tdm_layer_cb_output_commit, lm);
+                       _tdm_layer_free_buffer(private_layer, lm->committed_buffer);
+                       free(lm);
+                       break;
+               }
+       }
+}
+
+EXTERN tdm_error
+tdm_layer_remove_commit_handler(tdm_layer *layer, tdm_layer_commit_handler func, void *user_data)
+{
+       LAYER_FUNC_ENTRY();
+
+       _pthread_mutex_lock(&private_display->lock);
+
+       tdm_layer_remove_commit_handler_internal(layer, func, user_data);
+
+       _pthread_mutex_unlock(&private_display->lock);
+
+       return ret;
+}
+
 EXTERN tbm_surface_h
 tdm_layer_get_displaying_buffer(tdm_layer *layer, tdm_error *error)
 {