#include "vmwgfx_binding.h"
 #include "device_include/svga3d_surfacedefs.h"
 
+#define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32)
+#define SVGA3D_FLAGS_UPPER_32(svga3d_flags) (svga3d_flags >> 32)
+#define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \
+       (svga3d_flags & ((uint64_t)U32_MAX))
 
 /**
  * struct vmw_user_surface - User-space visible surface resource
                                 bool readback,
                                 struct ttm_validate_buffer *val_buf);
 static int vmw_gb_surface_destroy(struct vmw_resource *res);
-
+static int
+vmw_gb_surface_define_internal(struct drm_device *dev,
+                              struct drm_vmw_gb_surface_create_ext_req *req,
+                              struct drm_vmw_gb_surface_create_rep *rep,
+                              struct drm_file *file_priv);
+static int
+vmw_gb_surface_reference_internal(struct drm_device *dev,
+                                 struct drm_vmw_surface_arg *req,
+                                 struct drm_vmw_gb_surface_ref_ext_rep *rep,
+                                 struct drm_file *file_priv);
 
 static const struct vmw_user_resource_conv user_surface_conv = {
        .object_type = VMW_RES_SURFACE,
 
 /**
  * vmw_gb_surface_define_ioctl - Ioctl function implementing
- *                               the user surface define functionality.
+ * the user surface define functionality.
  *
- * @dev:            Pointer to a struct drm_device.
- * @data:           Pointer to data copied from / to user-space.
- * @file_priv:      Pointer to a drm file private structure.
+ * @dev: Pointer to a struct drm_device.
+ * @data: Pointer to data copied from / to user-space.
+ * @file_priv: Pointer to a drm file private structure.
  */
 int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data,
                                struct drm_file *file_priv)
 {
-       struct vmw_private *dev_priv = vmw_priv(dev);
-       struct vmw_user_surface *user_srf;
-       struct vmw_surface *srf;
-       struct vmw_resource *res;
-       struct vmw_resource *tmp;
        union drm_vmw_gb_surface_create_arg *arg =
            (union drm_vmw_gb_surface_create_arg *)data;
-       struct drm_vmw_gb_surface_create_req *req = &arg->req;
        struct drm_vmw_gb_surface_create_rep *rep = &arg->rep;
-       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
-       int ret;
-       uint32_t size;
-       uint32_t backup_handle = 0;
-
-       if (req->multisample_count != 0)
-               return -EINVAL;
-
-       if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS)
-               return -EINVAL;
-
-       if (unlikely(vmw_user_surface_size == 0))
-               vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
-                       128;
-
-       size = vmw_user_surface_size + 128;
-
-       /* Define a surface based on the parameters. */
-       ret = vmw_surface_gb_priv_define(dev,
-                       size,
-                       (SVGA3dSurfaceAllFlags)req->svga3d_flags,
-                       req->format,
-                       req->drm_surface_flags & drm_vmw_surface_flag_scanout,
-                       req->mip_levels,
-                       req->multisample_count,
-                       req->array_size,
-                       req->base_size,
-                       &srf);
-       if (unlikely(ret != 0))
-               return ret;
-
-       user_srf = container_of(srf, struct vmw_user_surface, srf);
-       if (drm_is_primary_client(file_priv))
-               user_srf->master = drm_master_get(file_priv->master);
-
-       ret = ttm_read_lock(&dev_priv->reservation_sem, true);
-       if (unlikely(ret != 0))
-               return ret;
-
-       res = &user_srf->srf.res;
-
-
-       if (req->buffer_handle != SVGA3D_INVALID_ID) {
-               ret = vmw_user_bo_lookup(tfile, req->buffer_handle,
-                                        &res->backup,
-                                        &user_srf->backup_base);
-               if (ret == 0) {
-                       if (res->backup->base.num_pages * PAGE_SIZE <
-                           res->backup_size) {
-                               DRM_ERROR("Surface backup buffer is too small.\n");
-                               vmw_bo_unreference(&res->backup);
-                               ret = -EINVAL;
-                               goto out_unlock;
-                       } else {
-                               backup_handle = req->buffer_handle;
-                       }
-               }
-       } else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer)
-               ret = vmw_user_bo_alloc(dev_priv, tfile,
-                                       res->backup_size,
-                                       req->drm_surface_flags &
-                                       drm_vmw_surface_flag_shareable,
-                                       &backup_handle,
-                                       &res->backup,
-                                       &user_srf->backup_base);
-
-       if (unlikely(ret != 0)) {
-               vmw_resource_unreference(&res);
-               goto out_unlock;
-       }
-
-       tmp = vmw_resource_reference(res);
-       ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime,
-                                   req->drm_surface_flags &
-                                   drm_vmw_surface_flag_shareable,
-                                   VMW_RES_SURFACE,
-                                   &vmw_user_surface_base_release, NULL);
-
-       if (unlikely(ret != 0)) {
-               vmw_resource_unreference(&tmp);
-               vmw_resource_unreference(&res);
-               goto out_unlock;
-       }
-
-       rep->handle      = user_srf->prime.base.hash.key;
-       rep->backup_size = res->backup_size;
-       if (res->backup) {
-               rep->buffer_map_handle =
-                       drm_vma_node_offset_addr(&res->backup->base.vma_node);
-               rep->buffer_size = res->backup->base.num_pages * PAGE_SIZE;
-               rep->buffer_handle = backup_handle;
-       } else {
-               rep->buffer_map_handle = 0;
-               rep->buffer_size = 0;
-               rep->buffer_handle = SVGA3D_INVALID_ID;
-       }
+       struct drm_vmw_gb_surface_create_ext_req req_ext;
 
-       vmw_resource_unreference(&res);
+       req_ext.base = arg->req;
+       req_ext.version = drm_vmw_gb_surface_v1;
+       req_ext.svga3d_flags_upper_32_bits = 0;
+       req_ext.multisample_pattern = SVGA3D_MS_PATTERN_NONE;
+       req_ext.quality_level = SVGA3D_MS_QUALITY_NONE;
+       req_ext.must_be_zero = 0;
 
-out_unlock:
-       ttm_read_unlock(&dev_priv->reservation_sem);
-       return ret;
+       return vmw_gb_surface_define_internal(dev, &req_ext, rep, file_priv);
 }
 
 /**
  * vmw_gb_surface_reference_ioctl - Ioctl function implementing
- *                                  the user surface reference functionality.
+ * the user surface reference functionality.
  *
- * @dev:            Pointer to a struct drm_device.
- * @data:           Pointer to data copied from / to user-space.
- * @file_priv:      Pointer to a drm file private structure.
+ * @dev: Pointer to a struct drm_device.
+ * @data: Pointer to data copied from / to user-space.
+ * @file_priv: Pointer to a drm file private structure.
  */
 int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data,
                                   struct drm_file *file_priv)
 {
-       struct vmw_private *dev_priv = vmw_priv(dev);
        union drm_vmw_gb_surface_reference_arg *arg =
            (union drm_vmw_gb_surface_reference_arg *)data;
        struct drm_vmw_surface_arg *req = &arg->req;
        struct drm_vmw_gb_surface_ref_rep *rep = &arg->rep;
-       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
-       struct vmw_surface *srf;
-       struct vmw_user_surface *user_srf;
-       struct ttm_base_object *base;
-       uint32_t backup_handle;
-       int ret = -EINVAL;
+       struct drm_vmw_gb_surface_ref_ext_rep rep_ext;
+       int ret;
+
+       ret = vmw_gb_surface_reference_internal(dev, req, &rep_ext, file_priv);
 
-       ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid,
-                                          req->handle_type, &base);
        if (unlikely(ret != 0))
                return ret;
 
-       user_srf = container_of(base, struct vmw_user_surface, prime.base);
-       srf = &user_srf->srf;
-       if (!srf->res.backup) {
-               DRM_ERROR("Shared GB surface is missing a backup buffer.\n");
-               goto out_bad_resource;
-       }
-
-       mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */
-       ret = vmw_user_bo_reference(tfile, srf->res.backup, &backup_handle);
-       mutex_unlock(&dev_priv->cmdbuf_mutex);
-
-       if (unlikely(ret != 0)) {
-               DRM_ERROR("Could not add a reference to a GB surface "
-                         "backup buffer.\n");
-               (void) ttm_ref_object_base_unref(tfile, base->hash.key,
-                                                TTM_REF_USAGE);
-               goto out_bad_resource;
-       }
-
-       rep->creq.svga3d_flags = (uint32_t)srf->flags;
-       rep->creq.format = srf->format;
-       rep->creq.mip_levels = srf->mip_levels[0];
-       rep->creq.drm_surface_flags = 0;
-       rep->creq.multisample_count = srf->multisample_count;
-       rep->creq.autogen_filter = srf->autogen_filter;
-       rep->creq.array_size = srf->array_size;
-       rep->creq.buffer_handle = backup_handle;
-       rep->creq.base_size = srf->base_size;
-       rep->crep.handle = user_srf->prime.base.hash.key;
-       rep->crep.backup_size = srf->res.backup_size;
-       rep->crep.buffer_handle = backup_handle;
-       rep->crep.buffer_map_handle =
-               drm_vma_node_offset_addr(&srf->res.backup->base.vma_node);
-       rep->crep.buffer_size = srf->res.backup->base.num_pages * PAGE_SIZE;
-
-out_bad_resource:
-       ttm_base_object_unref(&base);
+       rep->creq = rep_ext.creq.base;
+       rep->crep = rep_ext.crep;
 
        return ret;
 }
  * @multisample_count:
  * @array_size: Surface array size.
  * @size: width, heigh, depth of the surface requested
+ * @multisample_pattern: Multisampling pattern when msaa is supported
+ * @quality_level: Precision settings
  * @user_srf_out: allocated user_srf.  Set to NULL on failure.
  *
  * GB surfaces allocated by this function will not have a user mode handle, and
                               uint32_t multisample_count,
                               uint32_t array_size,
                               struct drm_vmw_size size,
+                              SVGA3dMSPattern multisample_pattern,
+                              SVGA3dMSQualityLevel quality_level,
                               struct vmw_surface **srf_out)
 {
        struct vmw_private *dev_priv = vmw_priv(dev);
        };
        struct vmw_surface *srf;
        int ret;
-       u32 num_layers;
+       u32 num_layers = 1;
 
        *srf_out = NULL;
 
        srf->autogen_filter    = SVGA3D_TEX_FILTER_NONE;
        srf->array_size        = array_size;
        srf->multisample_count = multisample_count;
-       srf->multisample_pattern = SVGA3D_MS_PATTERN_NONE;
-       srf->quality_level = SVGA3D_MS_QUALITY_NONE;
+       srf->multisample_pattern = multisample_pattern;
+       srf->quality_level = quality_level;
 
        if (array_size)
                num_layers = array_size;
        else if (svga3d_flags & SVGA3D_SURFACE_CUBEMAP)
                num_layers = SVGA3D_MAX_SURFACE_FACES;
-       else
-               num_layers = 1;
 
        srf->res.backup_size   =
                svga3dsurface_get_serialized_size(srf->format,
        ttm_read_unlock(&dev_priv->reservation_sem);
        return ret;
 }
+
+/**
+ * vmw_gb_surface_define_ext_ioctl - Ioctl function implementing
+ * the user surface define functionality.
+ *
+ * @dev: Pointer to a struct drm_device.
+ * @data: Pointer to data copied from / to user-space.
+ * @file_priv: Pointer to a drm file private structure.
+ */
+int vmw_gb_surface_define_ext_ioctl(struct drm_device *dev, void *data,
+                               struct drm_file *file_priv)
+{
+       union drm_vmw_gb_surface_create_ext_arg *arg =
+           (union drm_vmw_gb_surface_create_ext_arg *)data;
+       struct drm_vmw_gb_surface_create_ext_req *req = &arg->req;
+       struct drm_vmw_gb_surface_create_rep *rep = &arg->rep;
+
+       return vmw_gb_surface_define_internal(dev, req, rep, file_priv);
+}
+
+/**
+ * vmw_gb_surface_reference_ext_ioctl - Ioctl function implementing
+ * the user surface reference functionality.
+ *
+ * @dev: Pointer to a struct drm_device.
+ * @data: Pointer to data copied from / to user-space.
+ * @file_priv: Pointer to a drm file private structure.
+ */
+int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data,
+                                  struct drm_file *file_priv)
+{
+       union drm_vmw_gb_surface_reference_ext_arg *arg =
+           (union drm_vmw_gb_surface_reference_ext_arg *)data;
+       struct drm_vmw_surface_arg *req = &arg->req;
+       struct drm_vmw_gb_surface_ref_ext_rep *rep = &arg->rep;
+
+       return vmw_gb_surface_reference_internal(dev, req, rep, file_priv);
+}
+
+/**
+ * vmw_gb_surface_define_internal - Ioctl function implementing
+ * the user surface define functionality.
+ *
+ * @dev: Pointer to a struct drm_device.
+ * @req: Request argument from user-space.
+ * @rep: Response argument to user-space.
+ * @file_priv: Pointer to a drm file private structure.
+ */
+static int
+vmw_gb_surface_define_internal(struct drm_device *dev,
+                              struct drm_vmw_gb_surface_create_ext_req *req,
+                              struct drm_vmw_gb_surface_create_rep *rep,
+                              struct drm_file *file_priv)
+{
+       struct vmw_private *dev_priv = vmw_priv(dev);
+       struct vmw_user_surface *user_srf;
+       struct vmw_surface *srf;
+       struct vmw_resource *res;
+       struct vmw_resource *tmp;
+       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+       int ret;
+       uint32_t size;
+       uint32_t backup_handle = 0;
+       SVGA3dSurfaceAllFlags svga3d_flags_64 =
+               SVGA3D_FLAGS_64(req->svga3d_flags_upper_32_bits,
+                               req->base.svga3d_flags);
+
+       if (!dev_priv->has_sm4_1) {
+               /*
+                * If SM4_1 is not support then cannot send 64-bit flag to
+                * device.
+                */
+               if (req->svga3d_flags_upper_32_bits != 0)
+                       return -EINVAL;
+
+               if (req->base.multisample_count != 0)
+                       return -EINVAL;
+
+               if (req->multisample_pattern != SVGA3D_MS_PATTERN_NONE)
+                       return -EINVAL;
+
+               if (req->quality_level != SVGA3D_MS_QUALITY_NONE)
+                       return -EINVAL;
+       }
+
+       if (req->base.mip_levels > DRM_VMW_MAX_MIP_LEVELS)
+               return -EINVAL;
+
+       if (unlikely(vmw_user_surface_size == 0))
+               vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
+                       128;
+
+       size = vmw_user_surface_size + 128;
+
+       /* Define a surface based on the parameters. */
+       ret = vmw_surface_gb_priv_define(dev,
+                                        size,
+                                        svga3d_flags_64,
+                                        req->base.format,
+                                        req->base.drm_surface_flags &
+                                        drm_vmw_surface_flag_scanout,
+                                        req->base.mip_levels,
+                                        req->base.multisample_count,
+                                        req->base.array_size,
+                                        req->base.base_size,
+                                        req->multisample_pattern,
+                                        req->quality_level,
+                                        &srf);
+       if (unlikely(ret != 0))
+               return ret;
+
+       user_srf = container_of(srf, struct vmw_user_surface, srf);
+       if (drm_is_primary_client(file_priv))
+               user_srf->master = drm_master_get(file_priv->master);
+
+       ret = ttm_read_lock(&dev_priv->reservation_sem, true);
+       if (unlikely(ret != 0))
+               return ret;
+
+       res = &user_srf->srf.res;
+
+       if (req->base.buffer_handle != SVGA3D_INVALID_ID) {
+               ret = vmw_user_bo_lookup(tfile, req->base.buffer_handle,
+                                        &res->backup,
+                                        &user_srf->backup_base);
+               if (ret == 0) {
+                       if (res->backup->base.num_pages * PAGE_SIZE <
+                           res->backup_size) {
+                               DRM_ERROR("Surface backup buffer too small.\n");
+                               vmw_bo_unreference(&res->backup);
+                               ret = -EINVAL;
+                               goto out_unlock;
+                       } else {
+                               backup_handle = req->base.buffer_handle;
+                       }
+               }
+       } else if (req->base.drm_surface_flags &
+                  drm_vmw_surface_flag_create_buffer)
+               ret = vmw_user_bo_alloc(dev_priv, tfile,
+                                       res->backup_size,
+                                       req->base.drm_surface_flags &
+                                       drm_vmw_surface_flag_shareable,
+                                       &backup_handle,
+                                       &res->backup,
+                                       &user_srf->backup_base);
+
+       if (unlikely(ret != 0)) {
+               vmw_resource_unreference(&res);
+               goto out_unlock;
+       }
+
+       tmp = vmw_resource_reference(res);
+       ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime,
+                                   req->base.drm_surface_flags &
+                                   drm_vmw_surface_flag_shareable,
+                                   VMW_RES_SURFACE,
+                                   &vmw_user_surface_base_release, NULL);
+
+       if (unlikely(ret != 0)) {
+               vmw_resource_unreference(&tmp);
+               vmw_resource_unreference(&res);
+               goto out_unlock;
+       }
+
+       rep->handle      = user_srf->prime.base.hash.key;
+       rep->backup_size = res->backup_size;
+       if (res->backup) {
+               rep->buffer_map_handle =
+                       drm_vma_node_offset_addr(&res->backup->base.vma_node);
+               rep->buffer_size = res->backup->base.num_pages * PAGE_SIZE;
+               rep->buffer_handle = backup_handle;
+       } else {
+               rep->buffer_map_handle = 0;
+               rep->buffer_size = 0;
+               rep->buffer_handle = SVGA3D_INVALID_ID;
+       }
+
+       vmw_resource_unreference(&res);
+
+out_unlock:
+       ttm_read_unlock(&dev_priv->reservation_sem);
+       return ret;
+}
+
+/**
+ * vmw_gb_surface_reference_internal - Ioctl function implementing
+ * the user surface reference functionality.
+ *
+ * @dev: Pointer to a struct drm_device.
+ * @req: Pointer to user-space request surface arg.
+ * @rep: Pointer to response to user-space.
+ * @file_priv: Pointer to a drm file private structure.
+ */
+static int
+vmw_gb_surface_reference_internal(struct drm_device *dev,
+                                 struct drm_vmw_surface_arg *req,
+                                 struct drm_vmw_gb_surface_ref_ext_rep *rep,
+                                 struct drm_file *file_priv)
+{
+       struct vmw_private *dev_priv = vmw_priv(dev);
+       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+       struct vmw_surface *srf;
+       struct vmw_user_surface *user_srf;
+       struct ttm_base_object *base;
+       uint32_t backup_handle;
+       int ret = -EINVAL;
+
+       ret = vmw_surface_handle_reference(dev_priv, file_priv, req->sid,
+                                          req->handle_type, &base);
+       if (unlikely(ret != 0))
+               return ret;
+
+       user_srf = container_of(base, struct vmw_user_surface, prime.base);
+       srf = &user_srf->srf;
+       if (!srf->res.backup) {
+               DRM_ERROR("Shared GB surface is missing a backup buffer.\n");
+               goto out_bad_resource;
+       }
+
+       mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */
+       ret = vmw_user_bo_reference(tfile, srf->res.backup, &backup_handle);
+       mutex_unlock(&dev_priv->cmdbuf_mutex);
+
+       if (unlikely(ret != 0)) {
+               DRM_ERROR("Could not add a reference to a GB surface "
+                         "backup buffer.\n");
+               (void) ttm_ref_object_base_unref(tfile, base->hash.key,
+                                                TTM_REF_USAGE);
+               goto out_bad_resource;
+       }
+
+       rep->creq.base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(srf->flags);
+       rep->creq.base.format = srf->format;
+       rep->creq.base.mip_levels = srf->mip_levels[0];
+       rep->creq.base.drm_surface_flags = 0;
+       rep->creq.base.multisample_count = srf->multisample_count;
+       rep->creq.base.autogen_filter = srf->autogen_filter;
+       rep->creq.base.array_size = srf->array_size;
+       rep->creq.base.buffer_handle = backup_handle;
+       rep->creq.base.base_size = srf->base_size;
+       rep->crep.handle = user_srf->prime.base.hash.key;
+       rep->crep.backup_size = srf->res.backup_size;
+       rep->crep.buffer_handle = backup_handle;
+       rep->crep.buffer_map_handle =
+               drm_vma_node_offset_addr(&srf->res.backup->base.vma_node);
+       rep->crep.buffer_size = srf->res.backup->base.num_pages * PAGE_SIZE;
+
+       rep->creq.version = drm_vmw_gb_surface_v1;
+       rep->creq.svga3d_flags_upper_32_bits =
+               SVGA3D_FLAGS_UPPER_32(srf->flags);
+       rep->creq.multisample_pattern = srf->multisample_pattern;
+       rep->creq.quality_level = srf->quality_level;
+       rep->creq.must_be_zero = 0;
+
+out_bad_resource:
+       ttm_base_object_unref(&base);
+
+       return ret;
+}