Merge "msm: wfd: Don't enforce FIFO ordering when buffers are returned"
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index e0a46cc..6ffaffa 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -462,8 +462,16 @@
 static long vsg_return_ip_buffer(struct v4l2_subdev *sd, void *arg)
 {
 	struct vsg_context *context = NULL;
-	struct vsg_buf_info *buf_info = NULL, *last_buffer = NULL,
-			*expected_buffer = NULL;
+	struct vsg_buf_info *buf_info = NULL, *temp = NULL,
+			/* last buffer sent for encoding */
+			*last_buffer = NULL,
+			/* buffer we expected to get back, ideally ==
+			 * last_buffer, but might not be if sequence is
+			 * encode, encode, return */
+			*expected_buffer = NULL,
+			/* buffer that we've sent for encoding at some point */
+			*known_buffer = NULL;
+	bool is_last_buffer = false;
 	int rc = 0;
 
 	if (!arg || !sd) {
@@ -477,41 +485,47 @@
 	buf_info = (struct vsg_buf_info *)arg;
 	last_buffer = context->last_buffer;
 
+	WFD_MSG_DBG("Return frame with paddr %p\n",
+			(void *)buf_info->mdp_buf_info.paddr);
+
 	if (!list_empty(&context->busy_queue.node)) {
 		expected_buffer = list_first_entry(&context->busy_queue.node,
 				struct vsg_buf_info, node);
 	}
 
-	WFD_MSG_DBG("Return frame with paddr %p\n",
-			(void *)buf_info->mdp_buf_info.paddr);
-
-	if (!expected_buffer) {
-		WFD_MSG_ERR("Unexpectedly received buffer from enc with "
-			"paddr %p\n", (void *)buf_info->mdp_buf_info.paddr);
-		goto return_ip_buf_bad_buf;
+	list_for_each_entry(temp, &context->busy_queue.node, node) {
+		if (mdp_buf_info_equals(&temp->mdp_buf_info,
+				&buf_info->mdp_buf_info)) {
+			known_buffer = temp;
+			break;
+		}
 	}
 
-	expected_buffer->flags &= ~VSG_BUF_BEING_ENCODED;
-	if (mdp_buf_info_equals(&expected_buffer->mdp_buf_info,
-				&buf_info->mdp_buf_info)) {
-		bool is_same_buffer = context->last_buffer &&
-			mdp_buf_info_equals(
-					&context->last_buffer->mdp_buf_info,
-					&expected_buffer->mdp_buf_info);
-
-		list_del(&expected_buffer->node);
-		if (!is_same_buffer &&
-			!(expected_buffer->flags & VSG_NEVER_RELEASE)) {
-			vsg_release_input_buffer(context, expected_buffer);
-			kfree(expected_buffer);
-		}
-	} else {
-		WFD_MSG_ERR("Returned buffer %p is not latest buffer, "
-				"expected %p\n",
-				(void *)buf_info->mdp_buf_info.paddr,
-				(void *)expected_buffer->mdp_buf_info.paddr);
-		rc = -EINVAL;
+	if (!expected_buffer || !known_buffer) {
+		WFD_MSG_ERR("Unexpectedly received buffer from enc with "
+			"paddr %p\n", (void *)buf_info->mdp_buf_info.paddr);
+		rc = -EBADHANDLE;
 		goto return_ip_buf_bad_buf;
+	} else if (known_buffer != expected_buffer) {
+		/* Buffers can come back out of order if encoder decides to drop
+		 * a frame */
+		WFD_MSG_DBG(
+				"Got a buffer (%p) out of order. Preferred to get %p\n",
+				(void *)known_buffer->mdp_buf_info.paddr,
+				(void *)expected_buffer->mdp_buf_info.paddr);
+	}
+
+	known_buffer->flags &= ~VSG_BUF_BEING_ENCODED;
+	is_last_buffer = context->last_buffer &&
+		mdp_buf_info_equals(
+				&context->last_buffer->mdp_buf_info,
+				&known_buffer->mdp_buf_info);
+
+	list_del(&known_buffer->node);
+	if (!is_last_buffer &&
+			!(known_buffer->flags & VSG_NEVER_RELEASE)) {
+		vsg_release_input_buffer(context, known_buffer);
+		kfree(known_buffer);
 	}
 
 return_ip_buf_bad_buf: