diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig
index eff3047052d4..9c18aace38ed 100644
--- a/drivers/gpu/drm/virtio/Kconfig
+++ b/drivers/gpu/drm/virtio/Kconfig
@@ -9,3 +9,12 @@ config DRM_VIRTIO_GPU
 	   QEMU based VMMs (like KVM or Xen).
 
 	   If unsure say M.
+
+config DRM_VIRTIO_GPU_DUMMY
+	tristate "Virtio dummy GPU driver"
+	depends on DRM_VIRTIO_GPU
+	help
+	   This add a new virtio GPU device which handles the virtio ring buffers
+	   inline so it doesn't rely on VMM to provide the virtio GPU device.
+	   Currently it only handle VIRTIO_GPU_CMD_GET_DISPLAY_INFO which is enough
+	   for a dummy 2D VGA device.
diff --git a/drivers/gpu/drm/virtio/Makefile b/drivers/gpu/drm/virtio/Makefile
index 92aa2b3d349d..26d8fee1bc41 100644
--- a/drivers/gpu/drm/virtio/Makefile
+++ b/drivers/gpu/drm/virtio/Makefile
@@ -8,4 +8,7 @@ virtio-gpu-y := virtgpu_drv.o virtgpu_kms.o virtgpu_gem.o \
 	virtgpu_fence.o virtgpu_object.o virtgpu_debugfs.o virtgpu_plane.o \
 	virtgpu_ioctl.o virtgpu_prime.o virtgpu_trace_points.o
 
+virtio-gpu-dummy-y := virtgpu_dummy.o
+
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio-gpu.o
+obj-$(CONFIG_DRM_VIRTIO_GPU_DUMMY) += virtio-gpu-dummy.o
diff --git a/drivers/gpu/drm/virtio/virtgpu_dummy.c b/drivers/gpu/drm/virtio/virtgpu_dummy.c
new file mode 100644
index 000000000000..8c2eb6fea47c
--- /dev/null
+++ b/drivers/gpu/drm/virtio/virtgpu_dummy.c
@@ -0,0 +1,161 @@
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_gpu.h>
+
+#include "virtgpu_drv.h"
+
+static int virtgpu_dummy_width = 1024;
+static int virtgpu_dummy_height = 768;
+
+MODULE_PARM_DESC(width, "Dummy VGA width");
+module_param_named(width, virtgpu_dummy_width, int, 0400);
+MODULE_PARM_DESC(height, "Dummy VGA height");
+module_param_named(height, virtgpu_dummy_height, int, 0400);
+
+static struct bus_type dummy_bus = {
+	.name = "",
+};
+
+static struct dummy_gpu {
+	struct device *root;
+	struct virtio_device vdev;
+	unsigned char status;
+} dummy;
+
+static u64 dummy_get_features(struct virtio_device *vdev)
+{
+	return 1ULL << VIRTIO_F_VERSION_1;
+}
+
+static int dummy_finalize_features(struct virtio_device *vdev)
+{
+	return 0;
+}
+
+static void dummy_get(struct virtio_device *vdev, unsigned int offset,
+		      void *buf, unsigned len)
+{
+	static struct virtio_gpu_config config = {
+		.num_scanouts = 1,
+	};
+	BUG_ON(offset + len > sizeof(config));
+	memcpy(buf, (char *)&config + offset, len);
+}
+
+static u8 dummy_get_status(struct virtio_device *vdev)
+{
+	struct dummy_gpu*  gpu = container_of(vdev, struct dummy_gpu, vdev);
+	return gpu->status;
+}
+
+static void dummy_set_status(struct virtio_device *vdev, u8 status)
+{
+	struct dummy_gpu*  gpu = container_of(vdev, struct dummy_gpu, vdev);
+	BUG_ON(!status);
+	gpu->status = status;
+}
+
+void process_cmd(struct vring_desc *desc, int idx)
+{
+	// FIXME, use chain to get resp buffer addr
+	char *buf = __va(desc[idx].addr);
+	struct virtio_gpu_vbuffer *vbuf =
+	    (struct virtio_gpu_vbuffer *)(buf - sizeof(*vbuf));
+	struct virtio_gpu_ctrl_hdr *cmd_p = (struct virtio_gpu_ctrl_hdr *)buf;
+	struct virtio_gpu_resp_display_info *resp;
+	BUG_ON(vbuf->buf != buf);
+	if (cmd_p->type != cpu_to_le32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO))
+		return;
+	BUG_ON(vbuf->resp_size != sizeof(struct virtio_gpu_resp_display_info));
+	resp = (struct virtio_gpu_resp_display_info *)vbuf->resp_buf;
+	resp->pmodes[0].r.width = virtgpu_dummy_width;
+	resp->pmodes[0].r.height = virtgpu_dummy_height;
+	resp->pmodes[0].enabled = 1;
+}
+
+static bool dummy_notify(struct virtqueue *vq)
+{
+	struct vring *r = (struct vring *)(vq + 1);
+	int used, avail;
+	// FIXME, handle multiple avail and also fix for big endian.
+	used = r->used->idx & (r->num - 1);
+	avail = (r->avail->idx - 1) & (r->num - 1);
+	r->used->ring[used].id = r->avail->ring[avail];
+	r->used->idx++;
+	if (!strcmp(vq->name, "control"))
+		process_cmd(r->desc, r->avail->ring[avail]);
+	vq->callback(vq);
+	return true;
+}
+
+static int dummy_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+			  struct virtqueue *vqs[],
+			  vq_callback_t * callbacks[],
+			  const char *const names[],
+			  const bool *ctx, struct irq_affinity *desc)
+{
+	int i, j;
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = vring_create_virtqueue(i, 256, SMP_CACHE_BYTES, vdev,
+						true, false, false,
+						dummy_notify, callbacks[i],
+						names[i]);
+		if (!vqs[i])
+			goto err;
+	}
+	return 0;
+err:
+	for (j = 0; j < i; ++j) {
+		vring_del_virtqueue(vqs[j]);
+		vqs[j] = NULL;
+	}
+	return -ENOMEM;
+}
+
+static void dummy_reset(struct virtio_device *vdev)
+{
+}
+
+static const struct virtio_config_ops dummy_vq_ops = {
+	.get_features = dummy_get_features,
+	.finalize_features = dummy_finalize_features,
+	.get = dummy_get,
+	.get_status = dummy_get_status,
+	.set_status = dummy_set_status,
+	.reset = dummy_reset,
+	.find_vqs = dummy_find_vqs,
+};
+
+static int __init virtio_gpu_dummy_init(void)
+{
+	int ret;
+	struct device * root = root_device_register("dummy");
+	if (PTR_ERR_OR_ZERO(root))
+		return PTR_ERR(root);
+	root->bus = &dummy_bus;
+	dummy.vdev.dev.parent = root;
+	dummy.vdev.id.device = VIRTIO_ID_GPU;
+	dummy.vdev.config = &dummy_vq_ops;
+	ret = register_virtio_device(&dummy.vdev);
+	if (ret) {
+		pr_err("Failed to register virtio device %d", ret);
+		root_device_unregister(root);
+		return ret;
+	}
+	dummy.root = root;
+	return 0;
+}
+
+static void virtio_gpu_dummy_exit(void)
+{
+	if (!dummy.root)
+		return;
+	unregister_virtio_device(&dummy.vdev);
+	root_device_unregister(dummy.root);
+}
+
+module_init(virtio_gpu_dummy_init);
+module_exit(virtio_gpu_dummy_exit);
