tve200
    vc4
    bridge/dw-hdmi
+   xen-front
 
 .. only::  subproject and html
 
 
--- /dev/null
+====================================================
+ drm/xen-front Xen para-virtualized frontend driver
+====================================================
+
+This frontend driver implements Xen para-virtualized display
+according to the display protocol described at
+include/xen/interface/io/displif.h
+
+Driver modes of operation in terms of display buffers used
+==========================================================
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: Driver modes of operation in terms of display buffers used
+
+Buffers allocated by the frontend driver
+----------------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: Buffers allocated by the frontend driver
+
+With GEM CMA helpers
+~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: With GEM CMA helpers
+
+Without GEM CMA helpers
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: Without GEM CMA helpers
+
+Buffers allocated by the backend
+--------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: Buffers allocated by the backend
+
+Driver limitations
+==================
+
+.. kernel-doc:: drivers/gpu/drm/xen/xen_drm_front.h
+   :doc: Driver limitations
 
 
 source "drivers/gpu/drm/tve200/Kconfig"
 
+source "drivers/gpu/drm/xen/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
 
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
 obj-$(CONFIG_DRM_PL111) += pl111/
 obj-$(CONFIG_DRM_TVE200) += tve200/
+obj-$(CONFIG_DRM_XEN) += xen/
 
--- /dev/null
+config DRM_XEN
+       bool "DRM Support for Xen guest OS"
+       depends on XEN
+       help
+         Choose this option if you want to enable DRM support
+         for Xen.
+
+config DRM_XEN_FRONTEND
+       tristate "Para-virtualized frontend driver for Xen guest OS"
+       depends on DRM_XEN
+       depends on DRM
+       select DRM_KMS_HELPER
+       select VIDEOMODE_HELPERS
+       select XEN_XENBUS_FRONTEND
+       help
+         Choose this option if you want to enable a para-virtualized
+         frontend DRM/KMS driver for Xen guest OSes.
+
+config DRM_XEN_FRONTEND_CMA
+       bool "Use DRM CMA to allocate dumb buffers"
+       depends on DRM_XEN_FRONTEND
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       help
+         Use DRM CMA helpers to allocate display buffers.
+         This is useful for the use-cases when guest driver needs to
+         share or export buffers to other drivers which only expect
+         contiguous buffers.
+         Note: in this mode driver cannot use buffers allocated
+         by the backend.
 
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+
+drm_xen_front-objs := xen_drm_front.o \
+                     xen_drm_front_kms.o \
+                     xen_drm_front_conn.o \
+                     xen_drm_front_evtchnl.o \
+                     xen_drm_front_shbuf.o \
+                     xen_drm_front_cfg.o
+
+ifeq ($(CONFIG_DRM_XEN_FRONTEND_CMA),y)
+       drm_xen_front-objs += xen_drm_front_gem_cma.o
+else
+       drm_xen_front-objs += xen_drm_front_gem.o
+endif
+
+obj-$(CONFIG_DRM_XEN_FRONTEND) += drm_xen_front.o
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/of_device.h>
+
+#include <xen/platform_pci.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include <xen/interface/io/displif.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_cfg.h"
+#include "xen_drm_front_evtchnl.h"
+#include "xen_drm_front_gem.h"
+#include "xen_drm_front_kms.h"
+#include "xen_drm_front_shbuf.h"
+
+struct xen_drm_front_dbuf {
+       struct list_head list;
+       u64 dbuf_cookie;
+       u64 fb_cookie;
+       struct xen_drm_front_shbuf *shbuf;
+};
+
+static int dbuf_add_to_list(struct xen_drm_front_info *front_info,
+                           struct xen_drm_front_shbuf *shbuf, u64 dbuf_cookie)
+{
+       struct xen_drm_front_dbuf *dbuf;
+
+       dbuf = kzalloc(sizeof(*dbuf), GFP_KERNEL);
+       if (!dbuf)
+               return -ENOMEM;
+
+       dbuf->dbuf_cookie = dbuf_cookie;
+       dbuf->shbuf = shbuf;
+       list_add(&dbuf->list, &front_info->dbuf_list);
+       return 0;
+}
+
+static struct xen_drm_front_dbuf *dbuf_get(struct list_head *dbuf_list,
+                                          u64 dbuf_cookie)
+{
+       struct xen_drm_front_dbuf *buf, *q;
+
+       list_for_each_entry_safe(buf, q, dbuf_list, list)
+               if (buf->dbuf_cookie == dbuf_cookie)
+                       return buf;
+
+       return NULL;
+}
+
+static void dbuf_flush_fb(struct list_head *dbuf_list, u64 fb_cookie)
+{
+       struct xen_drm_front_dbuf *buf, *q;
+
+       list_for_each_entry_safe(buf, q, dbuf_list, list)
+               if (buf->fb_cookie == fb_cookie)
+                       xen_drm_front_shbuf_flush(buf->shbuf);
+}
+
+static void dbuf_free(struct list_head *dbuf_list, u64 dbuf_cookie)
+{
+       struct xen_drm_front_dbuf *buf, *q;
+
+       list_for_each_entry_safe(buf, q, dbuf_list, list)
+               if (buf->dbuf_cookie == dbuf_cookie) {
+                       list_del(&buf->list);
+                       xen_drm_front_shbuf_unmap(buf->shbuf);
+                       xen_drm_front_shbuf_free(buf->shbuf);
+                       kfree(buf);
+                       break;
+               }
+}
+
+static void dbuf_free_all(struct list_head *dbuf_list)
+{
+       struct xen_drm_front_dbuf *buf, *q;
+
+       list_for_each_entry_safe(buf, q, dbuf_list, list) {
+               list_del(&buf->list);
+               xen_drm_front_shbuf_unmap(buf->shbuf);
+               xen_drm_front_shbuf_free(buf->shbuf);
+               kfree(buf);
+       }
+}
+
+static struct xendispl_req *
+be_prepare_req(struct xen_drm_front_evtchnl *evtchnl, u8 operation)
+{
+       struct xendispl_req *req;
+
+       req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                              evtchnl->u.req.ring.req_prod_pvt);
+       req->operation = operation;
+       req->id = evtchnl->evt_next_id++;
+       evtchnl->evt_id = req->id;
+       return req;
+}
+
+static int be_stream_do_io(struct xen_drm_front_evtchnl *evtchnl,
+                          struct xendispl_req *req)
+{
+       reinit_completion(&evtchnl->u.req.completion);
+       if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+               return -EIO;
+
+       xen_drm_front_evtchnl_flush(evtchnl);
+       return 0;
+}
+
+static int be_stream_wait_io(struct xen_drm_front_evtchnl *evtchnl)
+{
+       if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+                       msecs_to_jiffies(XEN_DRM_FRONT_WAIT_BACK_MS)) <= 0)
+               return -ETIMEDOUT;
+
+       return evtchnl->u.req.resp_status;
+}
+
+int xen_drm_front_mode_set(struct xen_drm_front_drm_pipeline *pipeline,
+                          u32 x, u32 y, u32 width, u32 height,
+                          u32 bpp, u64 fb_cookie)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xen_drm_front_info *front_info;
+       struct xendispl_req *req;
+       unsigned long flags;
+       int ret;
+
+       front_info = pipeline->drm_info->front_info;
+       evtchnl = &front_info->evt_pairs[pipeline->index].req;
+       if (unlikely(!evtchnl))
+               return -EIO;
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_SET_CONFIG);
+       req->op.set_config.x = x;
+       req->op.set_config.y = y;
+       req->op.set_config.width = width;
+       req->op.set_config.height = height;
+       req->op.set_config.bpp = bpp;
+       req->op.set_config.fb_cookie = fb_cookie;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret == 0)
+               ret = be_stream_wait_io(evtchnl);
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return ret;
+}
+
+static int be_dbuf_create_int(struct xen_drm_front_info *front_info,
+                             u64 dbuf_cookie, u32 width, u32 height,
+                             u32 bpp, u64 size, struct page **pages,
+                             struct sg_table *sgt)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xen_drm_front_shbuf *shbuf;
+       struct xendispl_req *req;
+       struct xen_drm_front_shbuf_cfg buf_cfg;
+       unsigned long flags;
+       int ret;
+
+       evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req;
+       if (unlikely(!evtchnl))
+               return -EIO;
+
+       memset(&buf_cfg, 0, sizeof(buf_cfg));
+       buf_cfg.xb_dev = front_info->xb_dev;
+       buf_cfg.pages = pages;
+       buf_cfg.size = size;
+       buf_cfg.sgt = sgt;
+       buf_cfg.be_alloc = front_info->cfg.be_alloc;
+
+       shbuf = xen_drm_front_shbuf_alloc(&buf_cfg);
+       if (!shbuf)
+               return -ENOMEM;
+
+       ret = dbuf_add_to_list(front_info, shbuf, dbuf_cookie);
+       if (ret < 0) {
+               xen_drm_front_shbuf_free(shbuf);
+               return ret;
+       }
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_CREATE);
+       req->op.dbuf_create.gref_directory =
+                       xen_drm_front_shbuf_get_dir_start(shbuf);
+       req->op.dbuf_create.buffer_sz = size;
+       req->op.dbuf_create.dbuf_cookie = dbuf_cookie;
+       req->op.dbuf_create.width = width;
+       req->op.dbuf_create.height = height;
+       req->op.dbuf_create.bpp = bpp;
+       if (buf_cfg.be_alloc)
+               req->op.dbuf_create.flags |= XENDISPL_DBUF_FLG_REQ_ALLOC;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret < 0)
+               goto fail;
+
+       ret = be_stream_wait_io(evtchnl);
+       if (ret < 0)
+               goto fail;
+
+       ret = xen_drm_front_shbuf_map(shbuf);
+       if (ret < 0)
+               goto fail;
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return 0;
+
+fail:
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       dbuf_free(&front_info->dbuf_list, dbuf_cookie);
+       return ret;
+}
+
+int xen_drm_front_dbuf_create_from_sgt(struct xen_drm_front_info *front_info,
+                                      u64 dbuf_cookie, u32 width, u32 height,
+                                      u32 bpp, u64 size, struct sg_table *sgt)
+{
+       return be_dbuf_create_int(front_info, dbuf_cookie, width, height,
+                                 bpp, size, NULL, sgt);
+}
+
+int xen_drm_front_dbuf_create_from_pages(struct xen_drm_front_info *front_info,
+                                        u64 dbuf_cookie, u32 width, u32 height,
+                                        u32 bpp, u64 size, struct page **pages)
+{
+       return be_dbuf_create_int(front_info, dbuf_cookie, width, height,
+                                 bpp, size, pages, NULL);
+}
+
+static int xen_drm_front_dbuf_destroy(struct xen_drm_front_info *front_info,
+                                     u64 dbuf_cookie)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xendispl_req *req;
+       unsigned long flags;
+       bool be_alloc;
+       int ret;
+
+       evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req;
+       if (unlikely(!evtchnl))
+               return -EIO;
+
+       be_alloc = front_info->cfg.be_alloc;
+
+       /*
+        * For the backend allocated buffer release references now, so backend
+        * can free the buffer.
+        */
+       if (be_alloc)
+               dbuf_free(&front_info->dbuf_list, dbuf_cookie);
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_DESTROY);
+       req->op.dbuf_destroy.dbuf_cookie = dbuf_cookie;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret == 0)
+               ret = be_stream_wait_io(evtchnl);
+
+       /*
+        * Do this regardless of communication status with the backend:
+        * if we cannot remove remote resources remove what we can locally.
+        */
+       if (!be_alloc)
+               dbuf_free(&front_info->dbuf_list, dbuf_cookie);
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return ret;
+}
+
+int xen_drm_front_fb_attach(struct xen_drm_front_info *front_info,
+                           u64 dbuf_cookie, u64 fb_cookie, u32 width,
+                           u32 height, u32 pixel_format)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xen_drm_front_dbuf *buf;
+       struct xendispl_req *req;
+       unsigned long flags;
+       int ret;
+
+       evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req;
+       if (unlikely(!evtchnl))
+               return -EIO;
+
+       buf = dbuf_get(&front_info->dbuf_list, dbuf_cookie);
+       if (!buf)
+               return -EINVAL;
+
+       buf->fb_cookie = fb_cookie;
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_FB_ATTACH);
+       req->op.fb_attach.dbuf_cookie = dbuf_cookie;
+       req->op.fb_attach.fb_cookie = fb_cookie;
+       req->op.fb_attach.width = width;
+       req->op.fb_attach.height = height;
+       req->op.fb_attach.pixel_format = pixel_format;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret == 0)
+               ret = be_stream_wait_io(evtchnl);
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return ret;
+}
+
+int xen_drm_front_fb_detach(struct xen_drm_front_info *front_info,
+                           u64 fb_cookie)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xendispl_req *req;
+       unsigned long flags;
+       int ret;
+
+       evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req;
+       if (unlikely(!evtchnl))
+               return -EIO;
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_FB_DETACH);
+       req->op.fb_detach.fb_cookie = fb_cookie;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret == 0)
+               ret = be_stream_wait_io(evtchnl);
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return ret;
+}
+
+int xen_drm_front_page_flip(struct xen_drm_front_info *front_info,
+                           int conn_idx, u64 fb_cookie)
+{
+       struct xen_drm_front_evtchnl *evtchnl;
+       struct xendispl_req *req;
+       unsigned long flags;
+       int ret;
+
+       if (unlikely(conn_idx >= front_info->num_evt_pairs))
+               return -EINVAL;
+
+       dbuf_flush_fb(&front_info->dbuf_list, fb_cookie);
+       evtchnl = &front_info->evt_pairs[conn_idx].req;
+
+       mutex_lock(&evtchnl->u.req.req_io_lock);
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       req = be_prepare_req(evtchnl, XENDISPL_OP_PG_FLIP);
+       req->op.pg_flip.fb_cookie = fb_cookie;
+
+       ret = be_stream_do_io(evtchnl, req);
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+       if (ret == 0)
+               ret = be_stream_wait_io(evtchnl);
+
+       mutex_unlock(&evtchnl->u.req.req_io_lock);
+       return ret;
+}
+
+void xen_drm_front_on_frame_done(struct xen_drm_front_info *front_info,
+                                int conn_idx, u64 fb_cookie)
+{
+       struct xen_drm_front_drm_info *drm_info = front_info->drm_info;
+
+       if (unlikely(conn_idx >= front_info->cfg.num_connectors))
+               return;
+
+       xen_drm_front_kms_on_frame_done(&drm_info->pipeline[conn_idx],
+                                       fb_cookie);
+}
+
+static int xen_drm_drv_dumb_create(struct drm_file *filp,
+                                  struct drm_device *dev,
+                                  struct drm_mode_create_dumb *args)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct drm_gem_object *obj;
+       int ret;
+
+       /*
+        * Dumb creation is a two stage process: first we create a fully
+        * constructed GEM object which is communicated to the backend, and
+        * only after that we can create GEM's handle. This is done so,
+        * because of the possible races: once you create a handle it becomes
+        * immediately visible to user-space, so the latter can try accessing
+        * object without pages etc.
+        * For details also see drm_gem_handle_create
+        */
+       args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       args->size = args->pitch * args->height;
+
+       obj = xen_drm_front_gem_create(dev, args->size);
+       if (IS_ERR_OR_NULL(obj)) {
+               ret = PTR_ERR(obj);
+               goto fail;
+       }
+
+       /*
+        * In case of CONFIG_DRM_XEN_FRONTEND_CMA gem_obj is constructed
+        * via DRM CMA helpers and doesn't have ->pages allocated
+        * (xendrm_gem_get_pages will return NULL), but instead can provide
+        * sg table
+        */
+       if (xen_drm_front_gem_get_pages(obj))
+               ret = xen_drm_front_dbuf_create_from_pages(drm_info->front_info,
+                               xen_drm_front_dbuf_to_cookie(obj),
+                               args->width, args->height, args->bpp,
+                               args->size,
+                               xen_drm_front_gem_get_pages(obj));
+       else
+               ret = xen_drm_front_dbuf_create_from_sgt(drm_info->front_info,
+                               xen_drm_front_dbuf_to_cookie(obj),
+                               args->width, args->height, args->bpp,
+                               args->size,
+                               xen_drm_front_gem_get_sg_table(obj));
+       if (ret)
+               goto fail_backend;
+
+       /* This is the tail of GEM object creation */
+       ret = drm_gem_handle_create(filp, obj, &args->handle);
+       if (ret)
+               goto fail_handle;
+
+       /* Drop reference from allocate - handle holds it now */
+       drm_gem_object_put_unlocked(obj);
+       return 0;
+
+fail_handle:
+       xen_drm_front_dbuf_destroy(drm_info->front_info,
+                                  xen_drm_front_dbuf_to_cookie(obj));
+fail_backend:
+       /* drop reference from allocate */
+       drm_gem_object_put_unlocked(obj);
+fail:
+       DRM_ERROR("Failed to create dumb buffer: %d\n", ret);
+       return ret;
+}
+
+static void xen_drm_drv_free_object_unlocked(struct drm_gem_object *obj)
+{
+       struct xen_drm_front_drm_info *drm_info = obj->dev->dev_private;
+       int idx;
+
+       if (drm_dev_enter(obj->dev, &idx)) {
+               xen_drm_front_dbuf_destroy(drm_info->front_info,
+                                          xen_drm_front_dbuf_to_cookie(obj));
+               drm_dev_exit(idx);
+       } else {
+               dbuf_free(&drm_info->front_info->dbuf_list,
+                         xen_drm_front_dbuf_to_cookie(obj));
+       }
+
+       xen_drm_front_gem_free_object_unlocked(obj);
+}
+
+static void xen_drm_drv_release(struct drm_device *dev)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct xen_drm_front_info *front_info = drm_info->front_info;
+
+       xen_drm_front_kms_fini(drm_info);
+
+       drm_atomic_helper_shutdown(dev);
+       drm_mode_config_cleanup(dev);
+
+       drm_dev_fini(dev);
+       kfree(dev);
+
+       if (front_info->cfg.be_alloc)
+               xenbus_switch_state(front_info->xb_dev,
+                                   XenbusStateInitialising);
+
+       kfree(drm_info);
+}
+
+static const struct file_operations xen_drm_dev_fops = {
+       .owner          = THIS_MODULE,
+       .open           = drm_open,
+       .release        = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = drm_compat_ioctl,
+#endif
+       .poll           = drm_poll,
+       .read           = drm_read,
+       .llseek         = no_llseek,
+#ifdef CONFIG_DRM_XEN_FRONTEND_CMA
+       .mmap           = drm_gem_cma_mmap,
+#else
+       .mmap           = xen_drm_front_gem_mmap,
+#endif
+};
+
+static const struct vm_operations_struct xen_drm_drv_vm_ops = {
+       .open           = drm_gem_vm_open,
+       .close          = drm_gem_vm_close,
+};
+
+static struct drm_driver xen_drm_driver = {
+       .driver_features           = DRIVER_GEM | DRIVER_MODESET |
+                                    DRIVER_PRIME | DRIVER_ATOMIC,
+       .release                   = xen_drm_drv_release,
+       .gem_vm_ops                = &xen_drm_drv_vm_ops,
+       .gem_free_object_unlocked  = xen_drm_drv_free_object_unlocked,
+       .prime_handle_to_fd        = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle        = drm_gem_prime_fd_to_handle,
+       .gem_prime_import          = drm_gem_prime_import,
+       .gem_prime_export          = drm_gem_prime_export,
+       .gem_prime_import_sg_table = xen_drm_front_gem_import_sg_table,
+       .gem_prime_get_sg_table    = xen_drm_front_gem_get_sg_table,
+       .dumb_create               = xen_drm_drv_dumb_create,
+       .fops                      = &xen_drm_dev_fops,
+       .name                      = "xendrm-du",
+       .desc                      = "Xen PV DRM Display Unit",
+       .date                      = "20180221",
+       .major                     = 1,
+       .minor                     = 0,
+
+#ifdef CONFIG_DRM_XEN_FRONTEND_CMA
+       .gem_prime_vmap            = drm_gem_cma_prime_vmap,
+       .gem_prime_vunmap          = drm_gem_cma_prime_vunmap,
+       .gem_prime_mmap            = drm_gem_cma_prime_mmap,
+#else
+       .gem_prime_vmap            = xen_drm_front_gem_prime_vmap,
+       .gem_prime_vunmap          = xen_drm_front_gem_prime_vunmap,
+       .gem_prime_mmap            = xen_drm_front_gem_prime_mmap,
+#endif
+};
+
+static int xen_drm_drv_init(struct xen_drm_front_info *front_info)
+{
+       struct device *dev = &front_info->xb_dev->dev;
+       struct xen_drm_front_drm_info *drm_info;
+       struct drm_device *drm_dev;
+       int ret;
+
+       DRM_INFO("Creating %s\n", xen_drm_driver.desc);
+
+       drm_info = kzalloc(sizeof(*drm_info), GFP_KERNEL);
+       if (!drm_info) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       drm_info->front_info = front_info;
+       front_info->drm_info = drm_info;
+
+       drm_dev = drm_dev_alloc(&xen_drm_driver, dev);
+       if (!drm_dev) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       drm_info->drm_dev = drm_dev;
+
+       drm_dev->dev_private = drm_info;
+
+       ret = xen_drm_front_kms_init(drm_info);
+       if (ret) {
+               DRM_ERROR("Failed to initialize DRM/KMS, ret %d\n", ret);
+               goto fail_modeset;
+       }
+
+       ret = drm_dev_register(drm_dev, 0);
+       if (ret)
+               goto fail_register;
+
+       DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+                xen_drm_driver.name, xen_drm_driver.major,
+                xen_drm_driver.minor, xen_drm_driver.patchlevel,
+                xen_drm_driver.date, drm_dev->primary->index);
+
+       return 0;
+
+fail_register:
+       drm_dev_unregister(drm_dev);
+fail_modeset:
+       drm_kms_helper_poll_fini(drm_dev);
+       drm_mode_config_cleanup(drm_dev);
+fail:
+       kfree(drm_info);
+       return ret;
+}
+
+static void xen_drm_drv_fini(struct xen_drm_front_info *front_info)
+{
+       struct xen_drm_front_drm_info *drm_info = front_info->drm_info;
+       struct drm_device *dev;
+
+       if (!drm_info)
+               return;
+
+       dev = drm_info->drm_dev;
+       if (!dev)
+               return;
+
+       /* Nothing to do if device is already unplugged */
+       if (drm_dev_is_unplugged(dev))
+               return;
+
+       drm_kms_helper_poll_fini(dev);
+       drm_dev_unplug(dev);
+
+       front_info->drm_info = NULL;
+
+       xen_drm_front_evtchnl_free_all(front_info);
+       dbuf_free_all(&front_info->dbuf_list);
+
+       /*
+        * If we are not using backend allocated buffers, then tell the
+        * backend we are ready to (re)initialize. Otherwise, wait for
+        * drm_driver.release.
+        */
+       if (!front_info->cfg.be_alloc)
+               xenbus_switch_state(front_info->xb_dev,
+                                   XenbusStateInitialising);
+}
+
+static int displback_initwait(struct xen_drm_front_info *front_info)
+{
+       struct xen_drm_front_cfg *cfg = &front_info->cfg;
+       int ret;
+
+       cfg->front_info = front_info;
+       ret = xen_drm_front_cfg_card(front_info, cfg);
+       if (ret < 0)
+               return ret;
+
+       DRM_INFO("Have %d conector(s)\n", cfg->num_connectors);
+       /* Create event channels for all connectors and publish */
+       ret = xen_drm_front_evtchnl_create_all(front_info);
+       if (ret < 0)
+               return ret;
+
+       return xen_drm_front_evtchnl_publish_all(front_info);
+}
+
+static int displback_connect(struct xen_drm_front_info *front_info)
+{
+       xen_drm_front_evtchnl_set_state(front_info, EVTCHNL_STATE_CONNECTED);
+       return xen_drm_drv_init(front_info);
+}
+
+static void displback_disconnect(struct xen_drm_front_info *front_info)
+{
+       if (!front_info->drm_info)
+               return;
+
+       /* Tell the backend to wait until we release the DRM driver. */
+       xenbus_switch_state(front_info->xb_dev, XenbusStateReconfiguring);
+
+       xen_drm_drv_fini(front_info);
+}
+
+static void displback_changed(struct xenbus_device *xb_dev,
+                             enum xenbus_state backend_state)
+{
+       struct xen_drm_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
+       int ret;
+
+       DRM_DEBUG("Backend state is %s, front is %s\n",
+                 xenbus_strstate(backend_state),
+                 xenbus_strstate(xb_dev->state));
+
+       switch (backend_state) {
+       case XenbusStateReconfiguring:
+               /* fall through */
+       case XenbusStateReconfigured:
+               /* fall through */
+       case XenbusStateInitialised:
+               break;
+
+       case XenbusStateInitialising:
+               if (xb_dev->state == XenbusStateReconfiguring)
+                       break;
+
+               /* recovering after backend unexpected closure */
+               displback_disconnect(front_info);
+               break;
+
+       case XenbusStateInitWait:
+               if (xb_dev->state == XenbusStateReconfiguring)
+                       break;
+
+               /* recovering after backend unexpected closure */
+               displback_disconnect(front_info);
+               if (xb_dev->state != XenbusStateInitialising)
+                       break;
+
+               ret = displback_initwait(front_info);
+               if (ret < 0)
+                       xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
+               else
+                       xenbus_switch_state(xb_dev, XenbusStateInitialised);
+               break;
+
+       case XenbusStateConnected:
+               if (xb_dev->state != XenbusStateInitialised)
+                       break;
+
+               ret = displback_connect(front_info);
+               if (ret < 0) {
+                       displback_disconnect(front_info);
+                       xenbus_dev_fatal(xb_dev, ret, "connecting backend");
+               } else {
+                       xenbus_switch_state(xb_dev, XenbusStateConnected);
+               }
+               break;
+
+       case XenbusStateClosing:
+               /*
+                * in this state backend starts freeing resources,
+                * so let it go into closed state, so we can also
+                * remove ours
+                */
+               break;
+
+       case XenbusStateUnknown:
+               /* fall through */
+       case XenbusStateClosed:
+               if (xb_dev->state == XenbusStateClosed)
+                       break;
+
+               displback_disconnect(front_info);
+               break;
+       }
+}
+
+static int xen_drv_probe(struct xenbus_device *xb_dev,
+                        const struct xenbus_device_id *id)
+{
+       struct xen_drm_front_info *front_info;
+       struct device *dev = &xb_dev->dev;
+       int ret;
+
+       /*
+        * The device is not spawn from a device tree, so arch_setup_dma_ops
+        * is not called, thus leaving the device with dummy DMA ops.
+        * This makes the device return error on PRIME buffer import, which
+        * is not correct: to fix this call of_dma_configure() with a NULL
+        * node to set default DMA ops.
+        */
+       dev->bus->force_dma = true;
+       dev->coherent_dma_mask = DMA_BIT_MASK(32);
+       ret = of_dma_configure(dev, NULL);
+       if (ret < 0) {
+               DRM_ERROR("Cannot setup DMA ops, ret %d", ret);
+               return ret;
+       }
+
+       front_info = devm_kzalloc(&xb_dev->dev,
+                                 sizeof(*front_info), GFP_KERNEL);
+       if (!front_info)
+               return -ENOMEM;
+
+       front_info->xb_dev = xb_dev;
+       spin_lock_init(&front_info->io_lock);
+       INIT_LIST_HEAD(&front_info->dbuf_list);
+       dev_set_drvdata(&xb_dev->dev, front_info);
+
+       return xenbus_switch_state(xb_dev, XenbusStateInitialising);
+}
+
+static int xen_drv_remove(struct xenbus_device *dev)
+{
+       struct xen_drm_front_info *front_info = dev_get_drvdata(&dev->dev);
+       int to = 100;
+
+       xenbus_switch_state(dev, XenbusStateClosing);
+
+       /*
+        * On driver removal it is disconnected from XenBus,
+        * so no backend state change events come via .otherend_changed
+        * callback. This prevents us from exiting gracefully, e.g.
+        * signaling the backend to free event channels, waiting for its
+        * state to change to XenbusStateClosed and cleaning at our end.
+        * Normally when front driver removed backend will finally go into
+        * XenbusStateInitWait state.
+        *
+        * Workaround: read backend's state manually and wait with time-out.
+        */
+       while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state",
+                                    XenbusStateUnknown) != XenbusStateInitWait) &&
+                                    to--)
+               msleep(10);
+
+       if (!to) {
+               unsigned int state;
+
+               state = xenbus_read_unsigned(front_info->xb_dev->otherend,
+                                            "state", XenbusStateUnknown);
+               DRM_ERROR("Backend state is %s while removing driver\n",
+                         xenbus_strstate(state));
+       }
+
+       xen_drm_drv_fini(front_info);
+       xenbus_frontend_closed(dev);
+       return 0;
+}
+
+static const struct xenbus_device_id xen_driver_ids[] = {
+       { XENDISPL_DRIVER_NAME },
+       { "" }
+};
+
+static struct xenbus_driver xen_driver = {
+       .ids = xen_driver_ids,
+       .probe = xen_drv_probe,
+       .remove = xen_drv_remove,
+       .otherend_changed = displback_changed,
+};
+
+static int __init xen_drv_init(void)
+{
+       /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+       if (XEN_PAGE_SIZE != PAGE_SIZE) {
+               DRM_ERROR(XENDISPL_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+                         XEN_PAGE_SIZE, PAGE_SIZE);
+               return -ENODEV;
+       }
+
+       if (!xen_domain())
+               return -ENODEV;
+
+       if (!xen_has_pv_devices())
+               return -ENODEV;
+
+       DRM_INFO("Registering XEN PV " XENDISPL_DRIVER_NAME "\n");
+       return xenbus_register_frontend(&xen_driver);
+}
+
+static void __exit xen_drv_fini(void)
+{
+       DRM_INFO("Unregistering XEN PV " XENDISPL_DRIVER_NAME "\n");
+       xenbus_unregister_driver(&xen_driver);
+}
+
+module_init(xen_drv_init);
+module_exit(xen_drv_fini);
+
+MODULE_DESCRIPTION("Xen para-virtualized display device frontend");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:" XENDISPL_DRIVER_NAME);
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_H_
+#define __XEN_DRM_FRONT_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <linux/scatterlist.h>
+
+#include "xen_drm_front_cfg.h"
+
+/**
+ * DOC: Driver modes of operation in terms of display buffers used
+ *
+ * Depending on the requirements for the para-virtualized environment, namely
+ * requirements dictated by the accompanying DRM/(v)GPU drivers running in both
+ * host and guest environments, number of operating modes of para-virtualized
+ * display driver are supported:
+ *
+ * - display buffers can be allocated by either frontend driver or backend
+ * - display buffers can be allocated to be contiguous in memory or not
+ *
+ * Note! Frontend driver itself has no dependency on contiguous memory for
+ * its operation.
+ */
+
+/**
+ * DOC: Buffers allocated by the frontend driver
+ *
+ * The below modes of operation are configured at compile-time via
+ * frontend driver's kernel configuration:
+ */
+
+/**
+ * DOC: With GEM CMA helpers
+ *
+ * This use-case is useful when used with accompanying DRM/vGPU driver in
+ * guest domain which was designed to only work with contiguous buffers,
+ * e.g. DRM driver based on GEM CMA helpers: such drivers can only import
+ * contiguous PRIME buffers, thus requiring frontend driver to provide
+ * such. In order to implement this mode of operation para-virtualized
+ * frontend driver can be configured to use GEM CMA helpers.
+ */
+
+/**
+ * DOC: Without GEM CMA helpers
+ *
+ * If accompanying drivers can cope with non-contiguous memory then, to
+ * lower pressure on CMA subsystem of the kernel, driver can allocate
+ * buffers from system memory.
+ *
+ * Note! If used with accompanying DRM/(v)GPU drivers this mode of operation
+ * may require IOMMU support on the platform, so accompanying DRM/vGPU
+ * hardware can still reach display buffer memory while importing PRIME
+ * buffers from the frontend driver.
+ */
+
+/**
+ * DOC: Buffers allocated by the backend
+ *
+ * This mode of operation is run-time configured via guest domain configuration
+ * through XenStore entries.
+ *
+ * For systems which do not provide IOMMU support, but having specific
+ * requirements for display buffers it is possible to allocate such buffers
+ * at backend side and share those with the frontend.
+ * For example, if host domain is 1:1 mapped and has DRM/GPU hardware expecting
+ * physically contiguous memory, this allows implementing zero-copying
+ * use-cases.
+ *
+ * Note, while using this scenario the following should be considered:
+ *
+ * #. If guest domain dies then pages/grants received from the backend
+ *    cannot be claimed back
+ *
+ * #. Misbehaving guest may send too many requests to the
+ *    backend exhausting its grant references and memory
+ *    (consider this from security POV)
+ */
+
+/**
+ * DOC: Driver limitations
+ *
+ * #. Only primary plane without additional properties is supported.
+ *
+ * #. Only one video mode per connector supported which is configured
+ *    via XenStore.
+ *
+ * #. All CRTCs operate at fixed frequency of 60Hz.
+ */
+
+/* timeout in ms to wait for backend to respond */
+#define XEN_DRM_FRONT_WAIT_BACK_MS     3000
+
+#ifndef GRANT_INVALID_REF
+/*
+ * Note on usage of grant reference 0 as invalid grant reference:
+ * grant reference 0 is valid, but never exposed to a PV driver,
+ * because of the fact it is already in use/reserved by the PV console.
+ */
+#define GRANT_INVALID_REF      0
+#endif
+
+struct xen_drm_front_info {
+       struct xenbus_device *xb_dev;
+       struct xen_drm_front_drm_info *drm_info;
+
+       /* to protect data between backend IO code and interrupt handler */
+       spinlock_t io_lock;
+
+       int num_evt_pairs;
+       struct xen_drm_front_evtchnl_pair *evt_pairs;
+       struct xen_drm_front_cfg cfg;
+
+       /* display buffers */
+       struct list_head dbuf_list;
+};
+
+struct xen_drm_front_drm_pipeline {
+       struct xen_drm_front_drm_info *drm_info;
+
+       int index;
+
+       struct drm_simple_display_pipe pipe;
+
+       struct drm_connector conn;
+       /* These are only for connector mode checking */
+       int width, height;
+
+       struct drm_pending_vblank_event *pending_event;
+
+       struct delayed_work pflip_to_worker;
+
+       bool conn_connected;
+};
+
+struct xen_drm_front_drm_info {
+       struct xen_drm_front_info *front_info;
+       struct drm_device *drm_dev;
+
+       struct xen_drm_front_drm_pipeline pipeline[XEN_DRM_FRONT_MAX_CRTCS];
+};
+
+static inline u64 xen_drm_front_fb_to_cookie(struct drm_framebuffer *fb)
+{
+       return (u64)fb;
+}
+
+static inline u64 xen_drm_front_dbuf_to_cookie(struct drm_gem_object *gem_obj)
+{
+       return (u64)gem_obj;
+}
+
+int xen_drm_front_mode_set(struct xen_drm_front_drm_pipeline *pipeline,
+                          u32 x, u32 y, u32 width, u32 height,
+                          u32 bpp, u64 fb_cookie);
+
+int xen_drm_front_dbuf_create_from_sgt(struct xen_drm_front_info *front_info,
+                                      u64 dbuf_cookie, u32 width, u32 height,
+                                      u32 bpp, u64 size, struct sg_table *sgt);
+
+int xen_drm_front_dbuf_create_from_pages(struct xen_drm_front_info *front_info,
+                                        u64 dbuf_cookie, u32 width, u32 height,
+                                        u32 bpp, u64 size, struct page **pages);
+
+int xen_drm_front_fb_attach(struct xen_drm_front_info *front_info,
+                           u64 dbuf_cookie, u64 fb_cookie, u32 width,
+                           u32 height, u32 pixel_format);
+
+int xen_drm_front_fb_detach(struct xen_drm_front_info *front_info,
+                           u64 fb_cookie);
+
+int xen_drm_front_page_flip(struct xen_drm_front_info *front_info,
+                           int conn_idx, u64 fb_cookie);
+
+void xen_drm_front_on_frame_done(struct xen_drm_front_info *front_info,
+                                int conn_idx, u64 fb_cookie);
+
+#endif /* __XEN_DRM_FRONT_H_ */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drmP.h>
+
+#include <linux/device.h>
+
+#include <xen/interface/io/displif.h>
+#include <xen/xenbus.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_cfg.h"
+
+static int cfg_connector(struct xen_drm_front_info *front_info,
+                        struct xen_drm_front_cfg_connector *connector,
+                        const char *path, int index)
+{
+       char *connector_path;
+
+       connector_path = devm_kasprintf(&front_info->xb_dev->dev,
+                                       GFP_KERNEL, "%s/%d", path, index);
+       if (!connector_path)
+               return -ENOMEM;
+
+       if (xenbus_scanf(XBT_NIL, connector_path, XENDISPL_FIELD_RESOLUTION,
+                        "%d" XENDISPL_RESOLUTION_SEPARATOR "%d",
+                        &connector->width, &connector->height) < 0) {
+               /* either no entry configured or wrong resolution set */
+               connector->width = 0;
+               connector->height = 0;
+               return -EINVAL;
+       }
+
+       connector->xenstore_path = connector_path;
+
+       DRM_INFO("Connector %s: resolution %dx%d\n",
+                connector_path, connector->width, connector->height);
+       return 0;
+}
+
+int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info,
+                          struct xen_drm_front_cfg *cfg)
+{
+       struct xenbus_device *xb_dev = front_info->xb_dev;
+       int ret, i;
+
+       if (xenbus_read_unsigned(front_info->xb_dev->nodename,
+                                XENDISPL_FIELD_BE_ALLOC, 0)) {
+               DRM_INFO("Backend can provide display buffers\n");
+               cfg->be_alloc = true;
+       }
+
+       cfg->num_connectors = 0;
+       for (i = 0; i < ARRAY_SIZE(cfg->connectors); i++) {
+               ret = cfg_connector(front_info, &cfg->connectors[i],
+                                   xb_dev->nodename, i);
+               if (ret < 0)
+                       break;
+               cfg->num_connectors++;
+       }
+
+       if (!cfg->num_connectors) {
+               DRM_ERROR("No connector(s) configured at %s\n",
+                         xb_dev->nodename);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_CFG_H_
+#define __XEN_DRM_FRONT_CFG_H_
+
+#include <linux/types.h>
+
+#define XEN_DRM_FRONT_MAX_CRTCS        4
+
+struct xen_drm_front_cfg_connector {
+       int width;
+       int height;
+       char *xenstore_path;
+};
+
+struct xen_drm_front_cfg {
+       struct xen_drm_front_info *front_info;
+       /* number of connectors in this configuration */
+       int num_connectors;
+       /* connector configurations */
+       struct xen_drm_front_cfg_connector connectors[XEN_DRM_FRONT_MAX_CRTCS];
+       /* set if dumb buffers are allocated externally on backend side */
+       bool be_alloc;
+};
+
+int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info,
+                          struct xen_drm_front_cfg *cfg);
+
+#endif /* __XEN_DRM_FRONT_CFG_H_ */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <video/videomode.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_conn.h"
+#include "xen_drm_front_kms.h"
+
+static struct xen_drm_front_drm_pipeline *
+to_xen_drm_pipeline(struct drm_connector *connector)
+{
+       return container_of(connector, struct xen_drm_front_drm_pipeline, conn);
+}
+
+static const u32 plane_formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ARGB1555,
+};
+
+const u32 *xen_drm_front_conn_get_formats(int *format_count)
+{
+       *format_count = ARRAY_SIZE(plane_formats);
+       return plane_formats;
+}
+
+static int connector_detect(struct drm_connector *connector,
+                           struct drm_modeset_acquire_ctx *ctx,
+                           bool force)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(connector);
+
+       if (drm_dev_is_unplugged(connector->dev))
+               pipeline->conn_connected = false;
+
+       return pipeline->conn_connected ? connector_status_connected :
+                       connector_status_disconnected;
+}
+
+#define XEN_DRM_CRTC_VREFRESH_HZ       60
+
+static int connector_get_modes(struct drm_connector *connector)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(connector);
+       struct drm_display_mode *mode;
+       struct videomode videomode;
+       int width, height;
+
+       mode = drm_mode_create(connector->dev);
+       if (!mode)
+               return 0;
+
+       memset(&videomode, 0, sizeof(videomode));
+       videomode.hactive = pipeline->width;
+       videomode.vactive = pipeline->height;
+       width = videomode.hactive + videomode.hfront_porch +
+                       videomode.hback_porch + videomode.hsync_len;
+       height = videomode.vactive + videomode.vfront_porch +
+                       videomode.vback_porch + videomode.vsync_len;
+       videomode.pixelclock = width * height * XEN_DRM_CRTC_VREFRESH_HZ;
+       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+
+       drm_display_mode_from_videomode(&videomode, mode);
+       drm_mode_probed_add(connector, mode);
+       return 1;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = connector_get_modes,
+       .detect_ctx = connector_detect,
+};
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+int xen_drm_front_conn_init(struct xen_drm_front_drm_info *drm_info,
+                           struct drm_connector *connector)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(connector);
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+
+       pipeline->conn_connected = true;
+
+       connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+                       DRM_CONNECTOR_POLL_DISCONNECT;
+
+       return drm_connector_init(drm_info->drm_dev, connector,
+                                 &connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_CONN_H_
+#define __XEN_DRM_FRONT_CONN_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_encoder.h>
+
+#include <linux/wait.h>
+
+struct xen_drm_front_drm_info;
+
+int xen_drm_front_conn_init(struct xen_drm_front_drm_info *drm_info,
+                           struct drm_connector *connector);
+
+const u32 *xen_drm_front_conn_get_formats(int *format_count);
+
+#endif /* __XEN_DRM_FRONT_CONN_H_ */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drmP.h>
+
+#include <linux/errno.h>
+#include <linux/irq.h>
+
+#include <xen/xenbus.h>
+#include <xen/events.h>
+#include <xen/grant_table.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_ctrl(int irq, void *dev_id)
+{
+       struct xen_drm_front_evtchnl *evtchnl = dev_id;
+       struct xen_drm_front_info *front_info = evtchnl->front_info;
+       struct xendispl_resp *resp;
+       RING_IDX i, rp;
+       unsigned long flags;
+
+       if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+               return IRQ_HANDLED;
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+       rp = evtchnl->u.req.ring.sring->rsp_prod;
+       /* ensure we see queued responses up to rp */
+       virt_rmb();
+
+       for (i = evtchnl->u.req.ring.rsp_cons; i != rp; i++) {
+               resp = RING_GET_RESPONSE(&evtchnl->u.req.ring, i);
+               if (unlikely(resp->id != evtchnl->evt_id))
+                       continue;
+
+               switch (resp->operation) {
+               case XENDISPL_OP_PG_FLIP:
+               case XENDISPL_OP_FB_ATTACH:
+               case XENDISPL_OP_FB_DETACH:
+               case XENDISPL_OP_DBUF_CREATE:
+               case XENDISPL_OP_DBUF_DESTROY:
+               case XENDISPL_OP_SET_CONFIG:
+                       evtchnl->u.req.resp_status = resp->status;
+                       complete(&evtchnl->u.req.completion);
+                       break;
+
+               default:
+                       DRM_ERROR("Operation %d is not supported\n",
+                                 resp->operation);
+                       break;
+               }
+       }
+
+       evtchnl->u.req.ring.rsp_cons = i;
+
+       if (i != evtchnl->u.req.ring.req_prod_pvt) {
+               int more_to_do;
+
+               RING_FINAL_CHECK_FOR_RESPONSES(&evtchnl->u.req.ring,
+                                              more_to_do);
+               if (more_to_do)
+                       goto again;
+       } else {
+               evtchnl->u.req.ring.sring->rsp_event = i + 1;
+       }
+
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+       struct xen_drm_front_evtchnl *evtchnl = dev_id;
+       struct xen_drm_front_info *front_info = evtchnl->front_info;
+       struct xendispl_event_page *page = evtchnl->u.evt.page;
+       u32 cons, prod;
+       unsigned long flags;
+
+       if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+               return IRQ_HANDLED;
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+
+       prod = page->in_prod;
+       /* ensure we see ring contents up to prod */
+       virt_rmb();
+       if (prod == page->in_cons)
+               goto out;
+
+       for (cons = page->in_cons; cons != prod; cons++) {
+               struct xendispl_evt *event;
+
+               event = &XENDISPL_IN_RING_REF(page, cons);
+               if (unlikely(event->id != evtchnl->evt_id++))
+                       continue;
+
+               switch (event->type) {
+               case XENDISPL_EVT_PG_FLIP:
+                       xen_drm_front_on_frame_done(front_info, evtchnl->index,
+                                                   event->op.pg_flip.fb_cookie);
+                       break;
+               }
+       }
+       page->in_cons = cons;
+       /* ensure ring contents */
+       virt_wmb();
+
+out:
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+       return IRQ_HANDLED;
+}
+
+static void evtchnl_free(struct xen_drm_front_info *front_info,
+                        struct xen_drm_front_evtchnl *evtchnl)
+{
+       unsigned long page = 0;
+
+       if (evtchnl->type == EVTCHNL_TYPE_REQ)
+               page = (unsigned long)evtchnl->u.req.ring.sring;
+       else if (evtchnl->type == EVTCHNL_TYPE_EVT)
+               page = (unsigned long)evtchnl->u.evt.page;
+       if (!page)
+               return;
+
+       evtchnl->state = EVTCHNL_STATE_DISCONNECTED;
+
+       if (evtchnl->type == EVTCHNL_TYPE_REQ) {
+               /* release all who still waits for response if any */
+               evtchnl->u.req.resp_status = -EIO;
+               complete_all(&evtchnl->u.req.completion);
+       }
+
+       if (evtchnl->irq)
+               unbind_from_irqhandler(evtchnl->irq, evtchnl);
+
+       if (evtchnl->port)
+               xenbus_free_evtchn(front_info->xb_dev, evtchnl->port);
+
+       /* end access and free the page */
+       if (evtchnl->gref != GRANT_INVALID_REF)
+               gnttab_end_foreign_access(evtchnl->gref, 0, page);
+
+       memset(evtchnl, 0, sizeof(*evtchnl));
+}
+
+static int evtchnl_alloc(struct xen_drm_front_info *front_info, int index,
+                        struct xen_drm_front_evtchnl *evtchnl,
+                        enum xen_drm_front_evtchnl_type type)
+{
+       struct xenbus_device *xb_dev = front_info->xb_dev;
+       unsigned long page;
+       grant_ref_t gref;
+       irq_handler_t handler;
+       int ret;
+
+       memset(evtchnl, 0, sizeof(*evtchnl));
+       evtchnl->type = type;
+       evtchnl->index = index;
+       evtchnl->front_info = front_info;
+       evtchnl->state = EVTCHNL_STATE_DISCONNECTED;
+       evtchnl->gref = GRANT_INVALID_REF;
+
+       page = get_zeroed_page(GFP_NOIO | __GFP_HIGH);
+       if (!page) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       if (type == EVTCHNL_TYPE_REQ) {
+               struct xen_displif_sring *sring;
+
+               init_completion(&evtchnl->u.req.completion);
+               mutex_init(&evtchnl->u.req.req_io_lock);
+               sring = (struct xen_displif_sring *)page;
+               SHARED_RING_INIT(sring);
+               FRONT_RING_INIT(&evtchnl->u.req.ring, sring, XEN_PAGE_SIZE);
+
+               ret = xenbus_grant_ring(xb_dev, sring, 1, &gref);
+               if (ret < 0) {
+                       evtchnl->u.req.ring.sring = NULL;
+                       free_page(page);
+                       goto fail;
+               }
+
+               handler = evtchnl_interrupt_ctrl;
+       } else {
+               ret = gnttab_grant_foreign_access(xb_dev->otherend_id,
+                                                 virt_to_gfn((void *)page), 0);
+               if (ret < 0) {
+                       free_page(page);
+                       goto fail;
+               }
+
+               evtchnl->u.evt.page = (struct xendispl_event_page *)page;
+               gref = ret;
+               handler = evtchnl_interrupt_evt;
+       }
+       evtchnl->gref = gref;
+
+       ret = xenbus_alloc_evtchn(xb_dev, &evtchnl->port);
+       if (ret < 0)
+               goto fail;
+
+       ret = bind_evtchn_to_irqhandler(evtchnl->port,
+                                       handler, 0, xb_dev->devicetype,
+                                       evtchnl);
+       if (ret < 0)
+               goto fail;
+
+       evtchnl->irq = ret;
+       return 0;
+
+fail:
+       DRM_ERROR("Failed to allocate ring: %d\n", ret);
+       return ret;
+}
+
+int xen_drm_front_evtchnl_create_all(struct xen_drm_front_info *front_info)
+{
+       struct xen_drm_front_cfg *cfg;
+       int ret, conn;
+
+       cfg = &front_info->cfg;
+
+       front_info->evt_pairs =
+                       kcalloc(cfg->num_connectors,
+                               sizeof(struct xen_drm_front_evtchnl_pair),
+                               GFP_KERNEL);
+       if (!front_info->evt_pairs) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       for (conn = 0; conn < cfg->num_connectors; conn++) {
+               ret = evtchnl_alloc(front_info, conn,
+                                   &front_info->evt_pairs[conn].req,
+                                   EVTCHNL_TYPE_REQ);
+               if (ret < 0) {
+                       DRM_ERROR("Error allocating control channel\n");
+                       goto fail;
+               }
+
+               ret = evtchnl_alloc(front_info, conn,
+                                   &front_info->evt_pairs[conn].evt,
+                                   EVTCHNL_TYPE_EVT);
+               if (ret < 0) {
+                       DRM_ERROR("Error allocating in-event channel\n");
+                       goto fail;
+               }
+       }
+       front_info->num_evt_pairs = cfg->num_connectors;
+       return 0;
+
+fail:
+       xen_drm_front_evtchnl_free_all(front_info);
+       return ret;
+}
+
+static int evtchnl_publish(struct xenbus_transaction xbt,
+                          struct xen_drm_front_evtchnl *evtchnl,
+                          const char *path, const char *node_ring,
+                          const char *node_chnl)
+{
+       struct xenbus_device *xb_dev = evtchnl->front_info->xb_dev;
+       int ret;
+
+       /* write control channel ring reference */
+       ret = xenbus_printf(xbt, path, node_ring, "%u", evtchnl->gref);
+       if (ret < 0) {
+               xenbus_dev_error(xb_dev, ret, "writing ring-ref");
+               return ret;
+       }
+
+       /* write event channel ring reference */
+       ret = xenbus_printf(xbt, path, node_chnl, "%u", evtchnl->port);
+       if (ret < 0) {
+               xenbus_dev_error(xb_dev, ret, "writing event channel");
+               return ret;
+       }
+
+       return 0;
+}
+
+int xen_drm_front_evtchnl_publish_all(struct xen_drm_front_info *front_info)
+{
+       struct xenbus_transaction xbt;
+       struct xen_drm_front_cfg *plat_data;
+       int ret, conn;
+
+       plat_data = &front_info->cfg;
+
+again:
+       ret = xenbus_transaction_start(&xbt);
+       if (ret < 0) {
+               xenbus_dev_fatal(front_info->xb_dev, ret,
+                                "starting transaction");
+               return ret;
+       }
+
+       for (conn = 0; conn < plat_data->num_connectors; conn++) {
+               ret = evtchnl_publish(xbt, &front_info->evt_pairs[conn].req,
+                                     plat_data->connectors[conn].xenstore_path,
+                                     XENDISPL_FIELD_REQ_RING_REF,
+                                     XENDISPL_FIELD_REQ_CHANNEL);
+               if (ret < 0)
+                       goto fail;
+
+               ret = evtchnl_publish(xbt, &front_info->evt_pairs[conn].evt,
+                                     plat_data->connectors[conn].xenstore_path,
+                                     XENDISPL_FIELD_EVT_RING_REF,
+                                     XENDISPL_FIELD_EVT_CHANNEL);
+               if (ret < 0)
+                       goto fail;
+       }
+
+       ret = xenbus_transaction_end(xbt, 0);
+       if (ret < 0) {
+               if (ret == -EAGAIN)
+                       goto again;
+
+               xenbus_dev_fatal(front_info->xb_dev, ret,
+                                "completing transaction");
+               goto fail_to_end;
+       }
+
+       return 0;
+
+fail:
+       xenbus_transaction_end(xbt, 1);
+
+fail_to_end:
+       xenbus_dev_fatal(front_info->xb_dev, ret, "writing Xen store");
+       return ret;
+}
+
+void xen_drm_front_evtchnl_flush(struct xen_drm_front_evtchnl *evtchnl)
+{
+       int notify;
+
+       evtchnl->u.req.ring.req_prod_pvt++;
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&evtchnl->u.req.ring, notify);
+       if (notify)
+               notify_remote_via_irq(evtchnl->irq);
+}
+
+void xen_drm_front_evtchnl_set_state(struct xen_drm_front_info *front_info,
+                                    enum xen_drm_front_evtchnl_state state)
+{
+       unsigned long flags;
+       int i;
+
+       if (!front_info->evt_pairs)
+               return;
+
+       spin_lock_irqsave(&front_info->io_lock, flags);
+       for (i = 0; i < front_info->num_evt_pairs; i++) {
+               front_info->evt_pairs[i].req.state = state;
+               front_info->evt_pairs[i].evt.state = state;
+       }
+       spin_unlock_irqrestore(&front_info->io_lock, flags);
+}
+
+void xen_drm_front_evtchnl_free_all(struct xen_drm_front_info *front_info)
+{
+       int i;
+
+       if (!front_info->evt_pairs)
+               return;
+
+       for (i = 0; i < front_info->num_evt_pairs; i++) {
+               evtchnl_free(front_info, &front_info->evt_pairs[i].req);
+               evtchnl_free(front_info, &front_info->evt_pairs[i].evt);
+       }
+
+       kfree(front_info->evt_pairs);
+       front_info->evt_pairs = NULL;
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_EVTCHNL_H_
+#define __XEN_DRM_FRONT_EVTCHNL_H_
+
+#include <linux/completion.h>
+#include <linux/types.h>
+
+#include <xen/interface/io/ring.h>
+#include <xen/interface/io/displif.h>
+
+/*
+ * All operations which are not connector oriented use this ctrl event channel,
+ * e.g. fb_attach/destroy which belong to a DRM device, not to a CRTC.
+ */
+#define GENERIC_OP_EVT_CHNL    0
+
+enum xen_drm_front_evtchnl_state {
+       EVTCHNL_STATE_DISCONNECTED,
+       EVTCHNL_STATE_CONNECTED,
+};
+
+enum xen_drm_front_evtchnl_type {
+       EVTCHNL_TYPE_REQ,
+       EVTCHNL_TYPE_EVT,
+};
+
+struct xen_drm_front_drm_info;
+
+struct xen_drm_front_evtchnl {
+       struct xen_drm_front_info *front_info;
+       int gref;
+       int port;
+       int irq;
+       int index;
+       enum xen_drm_front_evtchnl_state state;
+       enum xen_drm_front_evtchnl_type type;
+       /* either response id or incoming event id */
+       u16 evt_id;
+       /* next request id or next expected event id */
+       u16 evt_next_id;
+       union {
+               struct {
+                       struct xen_displif_front_ring ring;
+                       struct completion completion;
+                       /* latest response status */
+                       int resp_status;
+                       /* serializer for backend IO: request/response */
+                       struct mutex req_io_lock;
+               } req;
+               struct {
+                       struct xendispl_event_page *page;
+               } evt;
+       } u;
+};
+
+struct xen_drm_front_evtchnl_pair {
+       struct xen_drm_front_evtchnl req;
+       struct xen_drm_front_evtchnl evt;
+};
+
+int xen_drm_front_evtchnl_create_all(struct xen_drm_front_info *front_info);
+
+int xen_drm_front_evtchnl_publish_all(struct xen_drm_front_info *front_info);
+
+void xen_drm_front_evtchnl_flush(struct xen_drm_front_evtchnl *evtchnl);
+
+void xen_drm_front_evtchnl_set_state(struct xen_drm_front_info *front_info,
+                                    enum xen_drm_front_evtchnl_state state);
+
+void xen_drm_front_evtchnl_free_all(struct xen_drm_front_info *front_info);
+
+#endif /* __XEN_DRM_FRONT_EVTCHNL_H_ */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include "xen_drm_front_gem.h"
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem.h>
+
+#include <linux/dma-buf.h>
+#include <linux/scatterlist.h>
+#include <linux/shmem_fs.h>
+
+#include <xen/balloon.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_shbuf.h"
+
+struct xen_gem_object {
+       struct drm_gem_object base;
+
+       size_t num_pages;
+       struct page **pages;
+
+       /* set for buffers allocated by the backend */
+       bool be_alloc;
+
+       /* this is for imported PRIME buffer */
+       struct sg_table *sgt_imported;
+};
+
+static inline struct xen_gem_object *
+to_xen_gem_obj(struct drm_gem_object *gem_obj)
+{
+       return container_of(gem_obj, struct xen_gem_object, base);
+}
+
+static int gem_alloc_pages_array(struct xen_gem_object *xen_obj,
+                                size_t buf_size)
+{
+       xen_obj->num_pages = DIV_ROUND_UP(buf_size, PAGE_SIZE);
+       xen_obj->pages = kvmalloc_array(xen_obj->num_pages,
+                                       sizeof(struct page *), GFP_KERNEL);
+       return !xen_obj->pages ? -ENOMEM : 0;
+}
+
+static void gem_free_pages_array(struct xen_gem_object *xen_obj)
+{
+       kvfree(xen_obj->pages);
+       xen_obj->pages = NULL;
+}
+
+static struct xen_gem_object *gem_create_obj(struct drm_device *dev,
+                                            size_t size)
+{
+       struct xen_gem_object *xen_obj;
+       int ret;
+
+       xen_obj = kzalloc(sizeof(*xen_obj), GFP_KERNEL);
+       if (!xen_obj)
+               return ERR_PTR(-ENOMEM);
+
+       ret = drm_gem_object_init(dev, &xen_obj->base, size);
+       if (ret < 0) {
+               kfree(xen_obj);
+               return ERR_PTR(ret);
+       }
+
+       return xen_obj;
+}
+
+static struct xen_gem_object *gem_create(struct drm_device *dev, size_t size)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct xen_gem_object *xen_obj;
+       int ret;
+
+       size = round_up(size, PAGE_SIZE);
+       xen_obj = gem_create_obj(dev, size);
+       if (IS_ERR_OR_NULL(xen_obj))
+               return xen_obj;
+
+       if (drm_info->front_info->cfg.be_alloc) {
+               /*
+                * backend will allocate space for this buffer, so
+                * only allocate array of pointers to pages
+                */
+               ret = gem_alloc_pages_array(xen_obj, size);
+               if (ret < 0)
+                       goto fail;
+
+               /*
+                * allocate ballooned pages which will be used to map
+                * grant references provided by the backend
+                */
+               ret = alloc_xenballooned_pages(xen_obj->num_pages,
+                                              xen_obj->pages);
+               if (ret < 0) {
+                       DRM_ERROR("Cannot allocate %zu ballooned pages: %d\n",
+                                 xen_obj->num_pages, ret);
+                       gem_free_pages_array(xen_obj);
+                       goto fail;
+               }
+
+               xen_obj->be_alloc = true;
+               return xen_obj;
+       }
+       /*
+        * need to allocate backing pages now, so we can share those
+        * with the backend
+        */
+       xen_obj->num_pages = DIV_ROUND_UP(size, PAGE_SIZE);
+       xen_obj->pages = drm_gem_get_pages(&xen_obj->base);
+       if (IS_ERR_OR_NULL(xen_obj->pages)) {
+               ret = PTR_ERR(xen_obj->pages);
+               xen_obj->pages = NULL;
+               goto fail;
+       }
+
+       return xen_obj;
+
+fail:
+       DRM_ERROR("Failed to allocate buffer with size %zu\n", size);
+       return ERR_PTR(ret);
+}
+
+struct drm_gem_object *xen_drm_front_gem_create(struct drm_device *dev,
+                                               size_t size)
+{
+       struct xen_gem_object *xen_obj;
+
+       xen_obj = gem_create(dev, size);
+       if (IS_ERR_OR_NULL(xen_obj))
+               return ERR_CAST(xen_obj);
+
+       return &xen_obj->base;
+}
+
+void xen_drm_front_gem_free_object_unlocked(struct drm_gem_object *gem_obj)
+{
+       struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj);
+
+       if (xen_obj->base.import_attach) {
+               drm_prime_gem_destroy(&xen_obj->base, xen_obj->sgt_imported);
+               gem_free_pages_array(xen_obj);
+       } else {
+               if (xen_obj->pages) {
+                       if (xen_obj->be_alloc) {
+                               free_xenballooned_pages(xen_obj->num_pages,
+                                                       xen_obj->pages);
+                               gem_free_pages_array(xen_obj);
+                       } else {
+                               drm_gem_put_pages(&xen_obj->base,
+                                                 xen_obj->pages, true, false);
+                       }
+               }
+       }
+       drm_gem_object_release(gem_obj);
+       kfree(xen_obj);
+}
+
+struct page **xen_drm_front_gem_get_pages(struct drm_gem_object *gem_obj)
+{
+       struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj);
+
+       return xen_obj->pages;
+}
+
+struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj)
+{
+       struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj);
+
+       if (!xen_obj->pages)
+               return NULL;
+
+       return drm_prime_pages_to_sg(xen_obj->pages, xen_obj->num_pages);
+}
+
+struct drm_gem_object *
+xen_drm_front_gem_import_sg_table(struct drm_device *dev,
+                                 struct dma_buf_attachment *attach,
+                                 struct sg_table *sgt)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct xen_gem_object *xen_obj;
+       size_t size;
+       int ret;
+
+       size = attach->dmabuf->size;
+       xen_obj = gem_create_obj(dev, size);
+       if (IS_ERR_OR_NULL(xen_obj))
+               return ERR_CAST(xen_obj);
+
+       ret = gem_alloc_pages_array(xen_obj, size);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       xen_obj->sgt_imported = sgt;
+
+       ret = drm_prime_sg_to_page_addr_arrays(sgt, xen_obj->pages,
+                                              NULL, xen_obj->num_pages);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       /*
+        * N.B. Although we have an API to create display buffer from sgt
+        * we use pages API, because we still need those for GEM handling,
+        * e.g. for mapping etc.
+        */
+       ret = xen_drm_front_dbuf_create_from_pages(drm_info->front_info,
+                                                  xen_drm_front_dbuf_to_cookie(&xen_obj->base),
+                                                  0, 0, 0, size,
+                                                  xen_obj->pages);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       DRM_DEBUG("Imported buffer of size %zu with nents %u\n",
+                 size, sgt->nents);
+
+       return &xen_obj->base;
+}
+
+static int gem_mmap_obj(struct xen_gem_object *xen_obj,
+                       struct vm_area_struct *vma)
+{
+       unsigned long addr = vma->vm_start;
+       int i;
+
+       /*
+        * clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
+        * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
+        * the whole buffer.
+        */
+       vma->vm_flags &= ~VM_PFNMAP;
+       vma->vm_flags |= VM_MIXEDMAP;
+       vma->vm_pgoff = 0;
+       vma->vm_page_prot =
+                       pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+       /*
+        * vm_operations_struct.fault handler will be called if CPU access
+        * to VM is here. For GPUs this isn't the case, because CPU
+        * doesn't touch the memory. Insert pages now, so both CPU and GPU are
+        * happy.
+        * FIXME: as we insert all the pages now then no .fault handler must
+        * be called, so don't provide one
+        */
+       for (i = 0; i < xen_obj->num_pages; i++) {
+               int ret;
+
+               ret = vm_insert_page(vma, addr, xen_obj->pages[i]);
+               if (ret < 0) {
+                       DRM_ERROR("Failed to insert pages into vma: %d\n", ret);
+                       return ret;
+               }
+
+               addr += PAGE_SIZE;
+       }
+       return 0;
+}
+
+int xen_drm_front_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct xen_gem_object *xen_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret < 0)
+               return ret;
+
+       gem_obj = vma->vm_private_data;
+       xen_obj = to_xen_gem_obj(gem_obj);
+       return gem_mmap_obj(xen_obj, vma);
+}
+
+void *xen_drm_front_gem_prime_vmap(struct drm_gem_object *gem_obj)
+{
+       struct xen_gem_object *xen_obj = to_xen_gem_obj(gem_obj);
+
+       if (!xen_obj->pages)
+               return NULL;
+
+       return vmap(xen_obj->pages, xen_obj->num_pages,
+                   VM_MAP, pgprot_writecombine(PAGE_KERNEL));
+}
+
+void xen_drm_front_gem_prime_vunmap(struct drm_gem_object *gem_obj,
+                                   void *vaddr)
+{
+       vunmap(vaddr);
+}
+
+int xen_drm_front_gem_prime_mmap(struct drm_gem_object *gem_obj,
+                                struct vm_area_struct *vma)
+{
+       struct xen_gem_object *xen_obj;
+       int ret;
+
+       ret = drm_gem_mmap_obj(gem_obj, gem_obj->size, vma);
+       if (ret < 0)
+               return ret;
+
+       xen_obj = to_xen_gem_obj(gem_obj);
+       return gem_mmap_obj(xen_obj, vma);
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_GEM_H
+#define __XEN_DRM_FRONT_GEM_H
+
+#include <drm/drmP.h>
+
+struct drm_gem_object *xen_drm_front_gem_create(struct drm_device *dev,
+                                               size_t size);
+
+struct drm_gem_object *
+xen_drm_front_gem_import_sg_table(struct drm_device *dev,
+                                 struct dma_buf_attachment *attach,
+                                 struct sg_table *sgt);
+
+struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj);
+
+struct page **xen_drm_front_gem_get_pages(struct drm_gem_object *obj);
+
+void xen_drm_front_gem_free_object_unlocked(struct drm_gem_object *gem_obj);
+
+#ifndef CONFIG_DRM_XEN_FRONTEND_CMA
+
+int xen_drm_front_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+void *xen_drm_front_gem_prime_vmap(struct drm_gem_object *gem_obj);
+
+void xen_drm_front_gem_prime_vunmap(struct drm_gem_object *gem_obj,
+                                   void *vaddr);
+
+int xen_drm_front_gem_prime_mmap(struct drm_gem_object *gem_obj,
+                                struct vm_area_struct *vma);
+#endif
+
+#endif /* __XEN_DRM_FRONT_GEM_H */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_gem.h"
+
+struct drm_gem_object *
+xen_drm_front_gem_import_sg_table(struct drm_device *dev,
+                                 struct dma_buf_attachment *attach,
+                                 struct sg_table *sgt)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct drm_gem_object *gem_obj;
+       struct drm_gem_cma_object *cma_obj;
+       int ret;
+
+       gem_obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
+       if (IS_ERR_OR_NULL(gem_obj))
+               return gem_obj;
+
+       cma_obj = to_drm_gem_cma_obj(gem_obj);
+
+       ret = xen_drm_front_dbuf_create_from_sgt(drm_info->front_info,
+                                                xen_drm_front_dbuf_to_cookie(gem_obj),
+                                                0, 0, 0, gem_obj->size,
+                                                drm_gem_cma_prime_get_sg_table(gem_obj));
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       DRM_DEBUG("Imported CMA buffer of size %zu\n", gem_obj->size);
+
+       return gem_obj;
+}
+
+struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj)
+{
+       return drm_gem_cma_prime_get_sg_table(gem_obj);
+}
+
+struct drm_gem_object *xen_drm_front_gem_create(struct drm_device *dev,
+                                               size_t size)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       struct drm_gem_cma_object *cma_obj;
+
+       if (drm_info->front_info->cfg.be_alloc) {
+               /* This use-case is not yet supported and probably won't be */
+               DRM_ERROR("Backend allocated buffers and CMA helpers are not supported at the same time\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       cma_obj = drm_gem_cma_create(dev, size);
+       if (IS_ERR_OR_NULL(cma_obj))
+               return ERR_CAST(cma_obj);
+
+       return &cma_obj->base;
+}
+
+void xen_drm_front_gem_free_object_unlocked(struct drm_gem_object *gem_obj)
+{
+       drm_gem_cma_free_object(gem_obj);
+}
+
+struct page **xen_drm_front_gem_get_pages(struct drm_gem_object *gem_obj)
+{
+       return NULL;
+}
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include "xen_drm_front_kms.h"
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_conn.h"
+
+/*
+ * Timeout in ms to wait for frame done event from the backend:
+ * must be a bit more than IO time-out
+ */
+#define FRAME_DONE_TO_MS       (XEN_DRM_FRONT_WAIT_BACK_MS + 100)
+
+static struct xen_drm_front_drm_pipeline *
+to_xen_drm_pipeline(struct drm_simple_display_pipe *pipe)
+{
+       return container_of(pipe, struct xen_drm_front_drm_pipeline, pipe);
+}
+
+static void fb_destroy(struct drm_framebuffer *fb)
+{
+       struct xen_drm_front_drm_info *drm_info = fb->dev->dev_private;
+       int idx;
+
+       if (drm_dev_enter(fb->dev, &idx)) {
+               xen_drm_front_fb_detach(drm_info->front_info,
+                                       xen_drm_front_fb_to_cookie(fb));
+               drm_dev_exit(idx);
+       }
+       drm_gem_fb_destroy(fb);
+}
+
+static struct drm_framebuffer_funcs fb_funcs = {
+       .destroy = fb_destroy,
+};
+
+static struct drm_framebuffer *
+fb_create(struct drm_device *dev, struct drm_file *filp,
+         const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct xen_drm_front_drm_info *drm_info = dev->dev_private;
+       static struct drm_framebuffer *fb;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       fb = drm_gem_fb_create_with_funcs(dev, filp, mode_cmd, &fb_funcs);
+       if (IS_ERR_OR_NULL(fb))
+               return fb;
+
+       gem_obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
+       if (!gem_obj) {
+               DRM_ERROR("Failed to lookup GEM object\n");
+               ret = -ENOENT;
+               goto fail;
+       }
+
+       drm_gem_object_put_unlocked(gem_obj);
+
+       ret = xen_drm_front_fb_attach(drm_info->front_info,
+                                     xen_drm_front_dbuf_to_cookie(gem_obj),
+                                     xen_drm_front_fb_to_cookie(fb),
+                                     fb->width, fb->height,
+                                     fb->format->format);
+       if (ret < 0) {
+               DRM_ERROR("Back failed to attach FB %p: %d\n", fb, ret);
+               goto fail;
+       }
+
+       return fb;
+
+fail:
+       drm_gem_fb_destroy(fb);
+       return ERR_PTR(ret);
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+       .fb_create = fb_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void send_pending_event(struct xen_drm_front_drm_pipeline *pipeline)
+{
+       struct drm_crtc *crtc = &pipeline->pipe.crtc;
+       struct drm_device *dev = crtc->dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (pipeline->pending_event)
+               drm_crtc_send_vblank_event(crtc, pipeline->pending_event);
+       pipeline->pending_event = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void display_enable(struct drm_simple_display_pipe *pipe,
+                          struct drm_crtc_state *crtc_state,
+                          struct drm_plane_state *plane_state)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(pipe);
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_framebuffer *fb = plane_state->fb;
+       int ret, idx;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
+       ret = xen_drm_front_mode_set(pipeline, crtc->x, crtc->y,
+                                    fb->width, fb->height,
+                                    fb->format->cpp[0] * 8,
+                                    xen_drm_front_fb_to_cookie(fb));
+
+       if (ret) {
+               DRM_ERROR("Failed to enable display: %d\n", ret);
+               pipeline->conn_connected = false;
+       }
+
+       drm_dev_exit(idx);
+}
+
+static void display_disable(struct drm_simple_display_pipe *pipe)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(pipe);
+       int ret = 0, idx;
+
+       if (drm_dev_enter(pipe->crtc.dev, &idx)) {
+               ret = xen_drm_front_mode_set(pipeline, 0, 0, 0, 0, 0,
+                                            xen_drm_front_fb_to_cookie(NULL));
+               drm_dev_exit(idx);
+       }
+       if (ret)
+               DRM_ERROR("Failed to disable display: %d\n", ret);
+
+       /* Make sure we can restart with enabled connector next time */
+       pipeline->conn_connected = true;
+
+       /* release stalled event if any */
+       send_pending_event(pipeline);
+}
+
+void xen_drm_front_kms_on_frame_done(struct xen_drm_front_drm_pipeline *pipeline,
+                                    u64 fb_cookie)
+{
+       /*
+        * This runs in interrupt context, e.g. under
+        * drm_info->front_info->io_lock, so we cannot call _sync version
+        * to cancel the work
+        */
+       cancel_delayed_work(&pipeline->pflip_to_worker);
+
+       send_pending_event(pipeline);
+}
+
+static void pflip_to_worker(struct work_struct *work)
+{
+       struct delayed_work *delayed_work = to_delayed_work(work);
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       container_of(delayed_work,
+                                    struct xen_drm_front_drm_pipeline,
+                                    pflip_to_worker);
+
+       DRM_ERROR("Frame done timed-out, releasing");
+       send_pending_event(pipeline);
+}
+
+static bool display_send_page_flip(struct drm_simple_display_pipe *pipe,
+                                  struct drm_plane_state *old_plane_state)
+{
+       struct drm_plane_state *plane_state =
+                       drm_atomic_get_new_plane_state(old_plane_state->state,
+                                                      &pipe->plane);
+
+       /*
+        * If old_plane_state->fb is NULL and plane_state->fb is not,
+        * then this is an atomic commit which will enable display.
+        * If old_plane_state->fb is not NULL and plane_state->fb is,
+        * then this is an atomic commit which will disable display.
+        * Ignore these and do not send page flip as this framebuffer will be
+        * sent to the backend as a part of display_set_config call.
+        */
+       if (old_plane_state->fb && plane_state->fb) {
+               struct xen_drm_front_drm_pipeline *pipeline =
+                               to_xen_drm_pipeline(pipe);
+               struct xen_drm_front_drm_info *drm_info = pipeline->drm_info;
+               int ret;
+
+               schedule_delayed_work(&pipeline->pflip_to_worker,
+                                     msecs_to_jiffies(FRAME_DONE_TO_MS));
+
+               ret = xen_drm_front_page_flip(drm_info->front_info,
+                                             pipeline->index,
+                                             xen_drm_front_fb_to_cookie(plane_state->fb));
+               if (ret) {
+                       DRM_ERROR("Failed to send page flip request to backend: %d\n", ret);
+
+                       pipeline->conn_connected = false;
+                       /*
+                        * Report the flip not handled, so pending event is
+                        * sent, unblocking user-space.
+                        */
+                       return false;
+               }
+               /*
+                * Signal that page flip was handled, pending event will be sent
+                * on frame done event from the backend.
+                */
+               return true;
+       }
+
+       return false;
+}
+
+static int display_prepare_fb(struct drm_simple_display_pipe *pipe,
+                             struct drm_plane_state *plane_state)
+{
+       return drm_gem_fb_prepare_fb(&pipe->plane, plane_state);
+}
+
+static void display_update(struct drm_simple_display_pipe *pipe,
+                          struct drm_plane_state *old_plane_state)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       to_xen_drm_pipeline(pipe);
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_pending_vblank_event *event;
+       int idx;
+
+       event = crtc->state->event;
+       if (event) {
+               struct drm_device *dev = crtc->dev;
+               unsigned long flags;
+
+               WARN_ON(pipeline->pending_event);
+
+               spin_lock_irqsave(&dev->event_lock, flags);
+               crtc->state->event = NULL;
+
+               pipeline->pending_event = event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx)) {
+               send_pending_event(pipeline);
+               return;
+       }
+
+       /*
+        * Send page flip request to the backend *after* we have event cached
+        * above, so on page flip done event from the backend we can
+        * deliver it and there is no race condition between this code and
+        * event from the backend.
+        * If this is not a page flip, e.g. no flip done event from the backend
+        * is expected, then send now.
+        */
+       if (!display_send_page_flip(pipe, old_plane_state))
+               send_pending_event(pipeline);
+
+       drm_dev_exit(idx);
+}
+
+static enum drm_mode_status
+display_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
+{
+       struct xen_drm_front_drm_pipeline *pipeline =
+                       container_of(crtc, struct xen_drm_front_drm_pipeline,
+                                    pipe.crtc);
+
+       if (mode->hdisplay != pipeline->width)
+               return MODE_ERROR;
+
+       if (mode->vdisplay != pipeline->height)
+               return MODE_ERROR;
+
+       return MODE_OK;
+}
+
+static const struct drm_simple_display_pipe_funcs display_funcs = {
+       .mode_valid = display_mode_valid,
+       .enable = display_enable,
+       .disable = display_disable,
+       .prepare_fb = display_prepare_fb,
+       .update = display_update,
+};
+
+static int display_pipe_init(struct xen_drm_front_drm_info *drm_info,
+                            int index, struct xen_drm_front_cfg_connector *cfg,
+                            struct xen_drm_front_drm_pipeline *pipeline)
+{
+       struct drm_device *dev = drm_info->drm_dev;
+       const u32 *formats;
+       int format_count;
+       int ret;
+
+       pipeline->drm_info = drm_info;
+       pipeline->index = index;
+       pipeline->height = cfg->height;
+       pipeline->width = cfg->width;
+
+       INIT_DELAYED_WORK(&pipeline->pflip_to_worker, pflip_to_worker);
+
+       ret = xen_drm_front_conn_init(drm_info, &pipeline->conn);
+       if (ret)
+               return ret;
+
+       formats = xen_drm_front_conn_get_formats(&format_count);
+
+       return drm_simple_display_pipe_init(dev, &pipeline->pipe,
+                                           &display_funcs, formats,
+                                           format_count, NULL,
+                                           &pipeline->conn);
+}
+
+int xen_drm_front_kms_init(struct xen_drm_front_drm_info *drm_info)
+{
+       struct drm_device *dev = drm_info->drm_dev;
+       int i, ret;
+
+       drm_mode_config_init(dev);
+
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+       dev->mode_config.max_width = 4095;
+       dev->mode_config.max_height = 2047;
+       dev->mode_config.funcs = &mode_config_funcs;
+
+       for (i = 0; i < drm_info->front_info->cfg.num_connectors; i++) {
+               struct xen_drm_front_cfg_connector *cfg =
+                               &drm_info->front_info->cfg.connectors[i];
+               struct xen_drm_front_drm_pipeline *pipeline =
+                               &drm_info->pipeline[i];
+
+               ret = display_pipe_init(drm_info, i, cfg, pipeline);
+               if (ret) {
+                       drm_mode_config_cleanup(dev);
+                       return ret;
+               }
+       }
+
+       drm_mode_config_reset(dev);
+       drm_kms_helper_poll_init(dev);
+       return 0;
+}
+
+void xen_drm_front_kms_fini(struct xen_drm_front_drm_info *drm_info)
+{
+       int i;
+
+       for (i = 0; i < drm_info->front_info->cfg.num_connectors; i++) {
+               struct xen_drm_front_drm_pipeline *pipeline =
+                               &drm_info->pipeline[i];
+
+               cancel_delayed_work_sync(&pipeline->pflip_to_worker);
+
+               send_pending_event(pipeline);
+       }
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_KMS_H_
+#define __XEN_DRM_FRONT_KMS_H_
+
+#include <linux/types.h>
+
+struct xen_drm_front_drm_info;
+struct xen_drm_front_drm_pipeline;
+
+int xen_drm_front_kms_init(struct xen_drm_front_drm_info *drm_info);
+
+void xen_drm_front_kms_fini(struct xen_drm_front_drm_info *drm_info);
+
+void xen_drm_front_kms_on_frame_done(struct xen_drm_front_drm_pipeline *pipeline,
+                                    u64 fb_cookie);
+
+#endif /* __XEN_DRM_FRONT_KMS_H_ */
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#include <drm/drmP.h>
+
+#if defined(CONFIG_X86)
+#include <drm/drm_cache.h>
+#endif
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <asm/xen/hypervisor.h>
+#include <xen/balloon.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+#include <xen/interface/io/ring.h>
+#include <xen/interface/io/displif.h>
+
+#include "xen_drm_front.h"
+#include "xen_drm_front_shbuf.h"
+
+struct xen_drm_front_shbuf_ops {
+       /*
+        * Calculate number of grefs required to handle this buffer,
+        * e.g. if grefs are required for page directory only or the buffer
+        * pages as well.
+        */
+       void (*calc_num_grefs)(struct xen_drm_front_shbuf *buf);
+       /* Fill page directory according to para-virtual display protocol. */
+       void (*fill_page_dir)(struct xen_drm_front_shbuf *buf);
+       /* Claim grant references for the pages of the buffer. */
+       int (*grant_refs_for_buffer)(struct xen_drm_front_shbuf *buf,
+                                    grant_ref_t *priv_gref_head, int gref_idx);
+       /* Map grant references of the buffer. */
+       int (*map)(struct xen_drm_front_shbuf *buf);
+       /* Unmap grant references of the buffer. */
+       int (*unmap)(struct xen_drm_front_shbuf *buf);
+};
+
+grant_ref_t xen_drm_front_shbuf_get_dir_start(struct xen_drm_front_shbuf *buf)
+{
+       if (!buf->grefs)
+               return GRANT_INVALID_REF;
+
+       return buf->grefs[0];
+}
+
+int xen_drm_front_shbuf_map(struct xen_drm_front_shbuf *buf)
+{
+       if (buf->ops->map)
+               return buf->ops->map(buf);
+
+       /* no need to map own grant references */
+       return 0;
+}
+
+int xen_drm_front_shbuf_unmap(struct xen_drm_front_shbuf *buf)
+{
+       if (buf->ops->unmap)
+               return buf->ops->unmap(buf);
+
+       /* no need to unmap own grant references */
+       return 0;
+}
+
+void xen_drm_front_shbuf_flush(struct xen_drm_front_shbuf *buf)
+{
+#if defined(CONFIG_X86)
+       drm_clflush_pages(buf->pages, buf->num_pages);
+#endif
+}
+
+void xen_drm_front_shbuf_free(struct xen_drm_front_shbuf *buf)
+{
+       if (buf->grefs) {
+               int i;
+
+               for (i = 0; i < buf->num_grefs; i++)
+                       if (buf->grefs[i] != GRANT_INVALID_REF)
+                               gnttab_end_foreign_access(buf->grefs[i],
+                                                         0, 0UL);
+       }
+       kfree(buf->grefs);
+       kfree(buf->directory);
+       if (buf->sgt) {
+               sg_free_table(buf->sgt);
+               kvfree(buf->pages);
+       }
+       kfree(buf);
+}
+
+/*
+ * number of grefs a page can hold with respect to the
+ * struct xendispl_page_directory header
+ */
+#define XEN_DRM_NUM_GREFS_PER_PAGE ((PAGE_SIZE - \
+               offsetof(struct xendispl_page_directory, gref)) / \
+               sizeof(grant_ref_t))
+
+static int get_num_pages_dir(struct xen_drm_front_shbuf *buf)
+{
+       /* number of pages the page directory consumes itself */
+       return DIV_ROUND_UP(buf->num_pages, XEN_DRM_NUM_GREFS_PER_PAGE);
+}
+
+static void backend_calc_num_grefs(struct xen_drm_front_shbuf *buf)
+{
+       /* only for pages the page directory consumes itself */
+       buf->num_grefs = get_num_pages_dir(buf);
+}
+
+static void guest_calc_num_grefs(struct xen_drm_front_shbuf *buf)
+{
+       /*
+        * number of pages the page directory consumes itself
+        * plus grefs for the buffer pages
+        */
+       buf->num_grefs = get_num_pages_dir(buf) + buf->num_pages;
+}
+
+#define xen_page_to_vaddr(page) \
+               ((phys_addr_t)pfn_to_kaddr(page_to_xen_pfn(page)))
+
+static int backend_unmap(struct xen_drm_front_shbuf *buf)
+{
+       struct gnttab_unmap_grant_ref *unmap_ops;
+       int i, ret;
+
+       if (!buf->pages || !buf->backend_map_handles || !buf->grefs)
+               return 0;
+
+       unmap_ops = kcalloc(buf->num_pages, sizeof(*unmap_ops),
+                           GFP_KERNEL);
+       if (!unmap_ops) {
+               DRM_ERROR("Failed to get memory while unmapping\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < buf->num_pages; i++) {
+               phys_addr_t addr;
+
+               addr = xen_page_to_vaddr(buf->pages[i]);
+               gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map,
+                                   buf->backend_map_handles[i]);
+       }
+
+       ret = gnttab_unmap_refs(unmap_ops, NULL, buf->pages,
+                               buf->num_pages);
+
+       for (i = 0; i < buf->num_pages; i++) {
+               if (unlikely(unmap_ops[i].status != GNTST_okay))
+                       DRM_ERROR("Failed to unmap page %d: %d\n",
+                                 i, unmap_ops[i].status);
+       }
+
+       if (ret)
+               DRM_ERROR("Failed to unmap grant references, ret %d", ret);
+
+       kfree(unmap_ops);
+       kfree(buf->backend_map_handles);
+       buf->backend_map_handles = NULL;
+       return ret;
+}
+
+static int backend_map(struct xen_drm_front_shbuf *buf)
+{
+       struct gnttab_map_grant_ref *map_ops = NULL;
+       unsigned char *ptr;
+       int ret, cur_gref, cur_dir_page, cur_page, grefs_left;
+
+       map_ops = kcalloc(buf->num_pages, sizeof(*map_ops), GFP_KERNEL);
+       if (!map_ops)
+               return -ENOMEM;
+
+       buf->backend_map_handles = kcalloc(buf->num_pages,
+                                          sizeof(*buf->backend_map_handles),
+                                          GFP_KERNEL);
+       if (!buf->backend_map_handles) {
+               kfree(map_ops);
+               return -ENOMEM;
+       }
+
+       /*
+        * read page directory to get grefs from the backend: for external
+        * buffer we only allocate buf->grefs for the page directory,
+        * so buf->num_grefs has number of pages in the page directory itself
+        */
+       ptr = buf->directory;
+       grefs_left = buf->num_pages;
+       cur_page = 0;
+       for (cur_dir_page = 0; cur_dir_page < buf->num_grefs; cur_dir_page++) {
+               struct xendispl_page_directory *page_dir =
+                               (struct xendispl_page_directory *)ptr;
+               int to_copy = XEN_DRM_NUM_GREFS_PER_PAGE;
+
+               if (to_copy > grefs_left)
+                       to_copy = grefs_left;
+
+               for (cur_gref = 0; cur_gref < to_copy; cur_gref++) {
+                       phys_addr_t addr;
+
+                       addr = xen_page_to_vaddr(buf->pages[cur_page]);
+                       gnttab_set_map_op(&map_ops[cur_page], addr,
+                                         GNTMAP_host_map,
+                                         page_dir->gref[cur_gref],
+                                         buf->xb_dev->otherend_id);
+                       cur_page++;
+               }
+
+               grefs_left -= to_copy;
+               ptr += PAGE_SIZE;
+       }
+       ret = gnttab_map_refs(map_ops, NULL, buf->pages, buf->num_pages);
+
+       /* save handles even if error, so we can unmap */
+       for (cur_page = 0; cur_page < buf->num_pages; cur_page++) {
+               buf->backend_map_handles[cur_page] = map_ops[cur_page].handle;
+               if (unlikely(map_ops[cur_page].status != GNTST_okay))
+                       DRM_ERROR("Failed to map page %d: %d\n",
+                                 cur_page, map_ops[cur_page].status);
+       }
+
+       if (ret) {
+               DRM_ERROR("Failed to map grant references, ret %d", ret);
+               backend_unmap(buf);
+       }
+
+       kfree(map_ops);
+       return ret;
+}
+
+static void backend_fill_page_dir(struct xen_drm_front_shbuf *buf)
+{
+       struct xendispl_page_directory *page_dir;
+       unsigned char *ptr;
+       int i, num_pages_dir;
+
+       ptr = buf->directory;
+       num_pages_dir = get_num_pages_dir(buf);
+
+       /* fill only grefs for the page directory itself */
+       for (i = 0; i < num_pages_dir - 1; i++) {
+               page_dir = (struct xendispl_page_directory *)ptr;
+
+               page_dir->gref_dir_next_page = buf->grefs[i + 1];
+               ptr += PAGE_SIZE;
+       }
+       /* last page must say there is no more pages */
+       page_dir = (struct xendispl_page_directory *)ptr;
+       page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+}
+
+static void guest_fill_page_dir(struct xen_drm_front_shbuf *buf)
+{
+       unsigned char *ptr;
+       int cur_gref, grefs_left, to_copy, i, num_pages_dir;
+
+       ptr = buf->directory;
+       num_pages_dir = get_num_pages_dir(buf);
+
+       /*
+        * while copying, skip grefs at start, they are for pages
+        * granted for the page directory itself
+        */
+       cur_gref = num_pages_dir;
+       grefs_left = buf->num_pages;
+       for (i = 0; i < num_pages_dir; i++) {
+               struct xendispl_page_directory *page_dir =
+                               (struct xendispl_page_directory *)ptr;
+
+               if (grefs_left <= XEN_DRM_NUM_GREFS_PER_PAGE) {
+                       to_copy = grefs_left;
+                       page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+               } else {
+                       to_copy = XEN_DRM_NUM_GREFS_PER_PAGE;
+                       page_dir->gref_dir_next_page = buf->grefs[i + 1];
+               }
+               memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+                      to_copy * sizeof(grant_ref_t));
+               ptr += PAGE_SIZE;
+               grefs_left -= to_copy;
+               cur_gref += to_copy;
+       }
+}
+
+static int guest_grant_refs_for_buffer(struct xen_drm_front_shbuf *buf,
+                                      grant_ref_t *priv_gref_head,
+                                      int gref_idx)
+{
+       int i, cur_ref, otherend_id;
+
+       otherend_id = buf->xb_dev->otherend_id;
+       for (i = 0; i < buf->num_pages; i++) {
+               cur_ref = gnttab_claim_grant_reference(priv_gref_head);
+               if (cur_ref < 0)
+                       return cur_ref;
+
+               gnttab_grant_foreign_access_ref(cur_ref, otherend_id,
+                                               xen_page_to_gfn(buf->pages[i]),
+                                               0);
+               buf->grefs[gref_idx++] = cur_ref;
+       }
+       return 0;
+}
+
+static int grant_references(struct xen_drm_front_shbuf *buf)
+{
+       grant_ref_t priv_gref_head;
+       int ret, i, j, cur_ref;
+       int otherend_id, num_pages_dir;
+
+       ret = gnttab_alloc_grant_references(buf->num_grefs, &priv_gref_head);
+       if (ret < 0) {
+               DRM_ERROR("Cannot allocate grant references\n");
+               return ret;
+       }
+
+       otherend_id = buf->xb_dev->otherend_id;
+       j = 0;
+       num_pages_dir = get_num_pages_dir(buf);
+       for (i = 0; i < num_pages_dir; i++) {
+               unsigned long frame;
+
+               cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+               if (cur_ref < 0)
+                       return cur_ref;
+
+               frame = xen_page_to_gfn(virt_to_page(buf->directory +
+                                       PAGE_SIZE * i));
+               gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+               buf->grefs[j++] = cur_ref;
+       }
+
+       if (buf->ops->grant_refs_for_buffer) {
+               ret = buf->ops->grant_refs_for_buffer(buf, &priv_gref_head, j);
+               if (ret)
+                       return ret;
+       }
+
+       gnttab_free_grant_references(priv_gref_head);
+       return 0;
+}
+
+static int alloc_storage(struct xen_drm_front_shbuf *buf)
+{
+       if (buf->sgt) {
+               buf->pages = kvmalloc_array(buf->num_pages,
+                                           sizeof(struct page *), GFP_KERNEL);
+               if (!buf->pages)
+                       return -ENOMEM;
+
+               if (drm_prime_sg_to_page_addr_arrays(buf->sgt, buf->pages,
+                                                    NULL, buf->num_pages) < 0)
+                       return -EINVAL;
+       }
+
+       buf->grefs = kcalloc(buf->num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+       if (!buf->grefs)
+               return -ENOMEM;
+
+       buf->directory = kcalloc(get_num_pages_dir(buf), PAGE_SIZE, GFP_KERNEL);
+       if (!buf->directory)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/*
+ * For be allocated buffers we don't need grant_refs_for_buffer as those
+ * grant references are allocated at backend side
+ */
+static const struct xen_drm_front_shbuf_ops backend_ops = {
+       .calc_num_grefs = backend_calc_num_grefs,
+       .fill_page_dir = backend_fill_page_dir,
+       .map = backend_map,
+       .unmap = backend_unmap
+};
+
+/* For locally granted references we do not need to map/unmap the references */
+static const struct xen_drm_front_shbuf_ops local_ops = {
+       .calc_num_grefs = guest_calc_num_grefs,
+       .fill_page_dir = guest_fill_page_dir,
+       .grant_refs_for_buffer = guest_grant_refs_for_buffer,
+};
+
+struct xen_drm_front_shbuf *
+xen_drm_front_shbuf_alloc(struct xen_drm_front_shbuf_cfg *cfg)
+{
+       struct xen_drm_front_shbuf *buf;
+       int ret;
+
+       /* either pages or sgt, not both */
+       if (unlikely(cfg->pages && cfg->sgt)) {
+               DRM_ERROR("Cannot handle buffer allocation with both pages and sg table provided\n");
+               return NULL;
+       }
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       if (cfg->be_alloc)
+               buf->ops = &backend_ops;
+       else
+               buf->ops = &local_ops;
+
+       buf->xb_dev = cfg->xb_dev;
+       buf->num_pages = DIV_ROUND_UP(cfg->size, PAGE_SIZE);
+       buf->sgt = cfg->sgt;
+       buf->pages = cfg->pages;
+
+       buf->ops->calc_num_grefs(buf);
+
+       ret = alloc_storage(buf);
+       if (ret)
+               goto fail;
+
+       ret = grant_references(buf);
+       if (ret)
+               goto fail;
+
+       buf->ops->fill_page_dir(buf);
+
+       return buf;
+
+fail:
+       xen_drm_front_shbuf_free(buf);
+       return ERR_PTR(ret);
+}
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ *  Xen para-virtual DRM device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
+ */
+
+#ifndef __XEN_DRM_FRONT_SHBUF_H_
+#define __XEN_DRM_FRONT_SHBUF_H_
+
+#include <linux/kernel.h>
+#include <linux/scatterlist.h>
+
+#include <xen/grant_table.h>
+
+struct xen_drm_front_shbuf {
+       /*
+        * number of references granted for the backend use:
+        *  - for allocated/imported dma-buf's this holds number of grant
+        *    references for the page directory and pages of the buffer
+        *  - for the buffer provided by the backend this holds number of
+        *    grant references for the page directory as grant references for
+        *    the buffer will be provided by the backend
+        */
+       int num_grefs;
+       grant_ref_t *grefs;
+       unsigned char *directory;
+
+       /*
+        * there are 2 ways to provide backing storage for this shared buffer:
+        * either pages or sgt. if buffer created from sgt then we own
+        * the pages and must free those ourselves on closure
+        */
+       int num_pages;
+       struct page **pages;
+
+       struct sg_table *sgt;
+
+       struct xenbus_device *xb_dev;
+
+       /* these are the ops used internally depending on be_alloc mode */
+       const struct xen_drm_front_shbuf_ops *ops;
+
+       /* Xen map handles for the buffer allocated by the backend */
+       grant_handle_t *backend_map_handles;
+};
+
+struct xen_drm_front_shbuf_cfg {
+       struct xenbus_device *xb_dev;
+       size_t size;
+       struct page **pages;
+       struct sg_table *sgt;
+       bool be_alloc;
+};
+
+struct xen_drm_front_shbuf *
+xen_drm_front_shbuf_alloc(struct xen_drm_front_shbuf_cfg *cfg);
+
+grant_ref_t xen_drm_front_shbuf_get_dir_start(struct xen_drm_front_shbuf *buf);
+
+int xen_drm_front_shbuf_map(struct xen_drm_front_shbuf *buf);
+
+int xen_drm_front_shbuf_unmap(struct xen_drm_front_shbuf *buf);
+
+void xen_drm_front_shbuf_flush(struct xen_drm_front_shbuf *buf);
+
+void xen_drm_front_shbuf_free(struct xen_drm_front_shbuf *buf);
+
+#endif /* __XEN_DRM_FRONT_SHBUF_H_ */