fbdev: add dma-buf support

Add support for the dma-buf exporter role to the frame buffer API. The
importer role isn't meaningful for frame buffer devices, as the frame
buffer device model doesn't allow using externally allocated memory.

Taken from an RFC on the linaro-mm-sig mailing list:
http://lists.linaro.org/pipermail/linaro-mm-sig/2012-June/002167.html

Signed-off-by: Guillaume Tucker <guillaume.tucker@arm.com>
[Ported to gem5's 4.3 kernel]
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
[Ported to gem5's 4.9 kernel. Added dep. on CONFIG_DMA_SHARED_BUFFER]
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 5e58f5e..22182c2 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -4,6 +4,7 @@
 
 menuconfig FB
 	tristate "Support for frame buffer devices"
+	select DMA_SHARED_BUFFER
 	select FB_CMDLINE
 	select FB_NOTIFY
 	---help---
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index f741ba8..6761376 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -15,6 +15,7 @@
 
 #include <linux/compat.h>
 #include <linux/types.h>
+#include <linux/dma-buf.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
@@ -1086,6 +1087,20 @@
 }
 EXPORT_SYMBOL(fb_blank);
 
+int fb_get_dmabuf(struct fb_info *info, int flags)
+{
+	struct dma_buf *dmabuf;
+
+	if (info->fbops->fb_dmabuf_export == NULL)
+		return -ENOTTY;
+
+	dmabuf = info->fbops->fb_dmabuf_export(info);
+	if (IS_ERR(dmabuf))
+		return PTR_ERR(dmabuf);
+
+	return dma_buf_fd(dmabuf, flags);
+}
+
 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
 			unsigned long arg)
 {
@@ -1096,6 +1111,7 @@
 	struct fb_cmap cmap_from;
 	struct fb_cmap_user cmap;
 	struct fb_event event;
+	struct fb_dmabuf_export dmaexp;
 	void __user *argp = (void __user *)arg;
 	long ret = 0;
 
@@ -1213,6 +1229,21 @@
 		unlock_fb_info(info);
 		console_unlock();
 		break;
+	case FBIOGET_DMABUF:
+		if (copy_from_user(&dmaexp, argp, sizeof(dmaexp)))
+			return -EFAULT;
+
+		if (!lock_fb_info(info))
+			return -ENODEV;
+		dmaexp.fd = fb_get_dmabuf(info, dmaexp.flags);
+		unlock_fb_info(info);
+
+		if (dmaexp.fd < 0)
+			return dmaexp.fd;
+
+		ret = copy_to_user(argp, &dmaexp, sizeof(dmaexp))
+		    ? -EFAULT : 0;
+		break;
 	default:
 		if (!lock_fb_info(info))
 			return -ENODEV;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index bc24e48..1f3e763 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -321,6 +321,9 @@
 	/* called at KDB enter and leave time to prepare the console */
 	int (*fb_debug_enter)(struct fb_info *info);
 	int (*fb_debug_leave)(struct fb_info *info);
+
+	/* Export the frame buffer as a dmabuf object */
+	struct dma_buf *(*fb_dmabuf_export)(struct fb_info *info);
 };
 
 #ifdef CONFIG_FB_TILEBLITTING
diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
index 6cd9b19..64a46b1 100644
--- a/include/uapi/linux/fb.h
+++ b/include/uapi/linux/fb.h
@@ -35,6 +35,7 @@
 #define FBIOPUT_MODEINFO        0x4617
 #define FBIOGET_DISPINFO        0x4618
 #define FBIO_WAITFORVSYNC	_IOW('F', 0x20, __u32)
+#define FBIOGET_DMABUF		_IOR('F', 0x21, struct fb_dmabuf_export)
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@@ -399,5 +400,10 @@
 #define FB_BACKLIGHT_MAX	0xFF
 #endif
 
+struct fb_dmabuf_export {
+	__u32 fd;
+	__u32 flags;
+};
+
 
 #endif /* _UAPI_LINUX_FB_H */