virtio: Allocate bounce buffers for devices with VIRTIO_F_IOMMU_PLATFORM

In preparation for bouncing virtio data for devices advertising the
VIRTIO_F_IOMMU_PLATFORM feature, allocate an array of bounce buffer
structures in the vring, one per descriptor.

Signed-off-by: Will Deacon <willdeacon@google.com>
[ Paul: pick from the Android tree. Rebase to the upstream ]
Signed-off-by: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
Link: https://android.googlesource.com/platform/external/u-boot/+/3e052749e7c50c4c1a6014e645ae3b9be3710c07
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 1bd19ad..de75786 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -6,6 +6,7 @@
  * virtio ring implementation
  */
 
+#include <bouncebuf.h>
 #include <common.h>
 #include <dm.h>
 #include <log.h>
@@ -292,6 +293,7 @@
 	struct udevice *vdev = uc_priv->vdev;
 	struct virtqueue *vq;
 	void *queue = NULL;
+	struct bounce_buffer *bbs = NULL;
 	struct vring vring;
 
 	/* We assume num is a power of 2 */
@@ -320,17 +322,29 @@
 		return NULL;
 
 	memset(queue, 0, vring_size(num, vring_align));
-	vring_init(&vring, num, queue, vring_align);
+
+	if (virtio_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
+		bbs = calloc(num, sizeof(*bbs));
+		if (!bbs)
+			goto err_free_queue;
+	}
+
+	vring_init(&vring, num, queue, vring_align, bbs);
 
 	vq = __vring_new_virtqueue(index, vring, udev);
-	if (!vq) {
-		virtio_free_pages(vdev, queue, DIV_ROUND_UP(vring.size, PAGE_SIZE));
-		return NULL;
-	}
+	if (!vq)
+		goto err_free_bbs;
+
 	debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
 	      queue, vq, num);
 
 	return vq;
+
+err_free_bbs:
+	free(bbs);
+err_free_queue:
+	virtio_free_pages(vdev, queue, DIV_ROUND_UP(vring.size, PAGE_SIZE));
+	return NULL;
 }
 
 void vring_del_virtqueue(struct virtqueue *vq)
@@ -339,6 +353,7 @@
 			  DIV_ROUND_UP(vq->vring.size, PAGE_SIZE));
 	free(vq->vring_desc_shadow);
 	list_del(&vq->list);
+	free(vq->vring.bouncebufs);
 	free(vq);
 }