162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2017 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 1362306a36Sopenharmony_ci * Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2162306a36Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: 2462306a36Sopenharmony_ci * Zhiyuan Lv <zhiyuan.lv@intel.com> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Contributors: 2762306a36Sopenharmony_ci * Xiaoguang Chen 2862306a36Sopenharmony_ci * Tina Zhang <tina.zhang@intel.com> 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/dma-buf.h> 3262306a36Sopenharmony_ci#include <linux/mdev.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3562306a36Sopenharmony_ci#include <drm/drm_plane.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "gem/i915_gem_dmabuf.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "i915_drv.h" 4062306a36Sopenharmony_ci#include "i915_reg.h" 4162306a36Sopenharmony_ci#include "gvt.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define GEN8_DECODE_PTE(pte) (pte & GENMASK_ULL(63, 12)) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int vgpu_gem_get_pages(struct drm_i915_gem_object *obj) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(obj->base.dev); 4862306a36Sopenharmony_ci struct intel_vgpu *vgpu; 4962306a36Sopenharmony_ci struct sg_table *st; 5062306a36Sopenharmony_ci struct scatterlist *sg; 5162306a36Sopenharmony_ci int i, j, ret; 5262306a36Sopenharmony_ci gen8_pte_t __iomem *gtt_entries; 5362306a36Sopenharmony_ci struct intel_vgpu_fb_info *fb_info; 5462306a36Sopenharmony_ci unsigned int page_num; /* limited by sg_alloc_table */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (overflows_type(obj->base.size >> PAGE_SHIFT, page_num)) 5762306a36Sopenharmony_ci return -E2BIG; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci page_num = obj->base.size >> PAGE_SHIFT; 6062306a36Sopenharmony_ci fb_info = (struct intel_vgpu_fb_info *)obj->gvt_info; 6162306a36Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !fb_info)) 6262306a36Sopenharmony_ci return -ENODEV; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci vgpu = fb_info->obj->vgpu; 6562306a36Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !vgpu)) 6662306a36Sopenharmony_ci return -ENODEV; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci st = kmalloc(sizeof(*st), GFP_KERNEL); 6962306a36Sopenharmony_ci if (unlikely(!st)) 7062306a36Sopenharmony_ci return -ENOMEM; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = sg_alloc_table(st, page_num, GFP_KERNEL); 7362306a36Sopenharmony_ci if (ret) { 7462306a36Sopenharmony_ci kfree(st); 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci gtt_entries = (gen8_pte_t __iomem *)to_gt(dev_priv)->ggtt->gsm + 7862306a36Sopenharmony_ci (fb_info->start >> PAGE_SHIFT); 7962306a36Sopenharmony_ci for_each_sg(st->sgl, sg, page_num, i) { 8062306a36Sopenharmony_ci dma_addr_t dma_addr = 8162306a36Sopenharmony_ci GEN8_DECODE_PTE(readq(>t_entries[i])); 8262306a36Sopenharmony_ci if (intel_gvt_dma_pin_guest_page(vgpu, dma_addr)) { 8362306a36Sopenharmony_ci ret = -EINVAL; 8462306a36Sopenharmony_ci goto out; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci sg->offset = 0; 8862306a36Sopenharmony_ci sg->length = PAGE_SIZE; 8962306a36Sopenharmony_ci sg_dma_len(sg) = PAGE_SIZE; 9062306a36Sopenharmony_ci sg_dma_address(sg) = dma_addr; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci __i915_gem_object_set_pages(obj, st); 9462306a36Sopenharmony_ciout: 9562306a36Sopenharmony_ci if (ret) { 9662306a36Sopenharmony_ci dma_addr_t dma_addr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci for_each_sg(st->sgl, sg, i, j) { 9962306a36Sopenharmony_ci dma_addr = sg_dma_address(sg); 10062306a36Sopenharmony_ci if (dma_addr) 10162306a36Sopenharmony_ci intel_gvt_dma_unmap_guest_page(vgpu, dma_addr); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci sg_free_table(st); 10462306a36Sopenharmony_ci kfree(st); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void vgpu_gem_put_pages(struct drm_i915_gem_object *obj, 11262306a36Sopenharmony_ci struct sg_table *pages) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct scatterlist *sg; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (obj->base.dma_buf) { 11762306a36Sopenharmony_ci struct intel_vgpu_fb_info *fb_info = obj->gvt_info; 11862306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *obj = fb_info->obj; 11962306a36Sopenharmony_ci struct intel_vgpu *vgpu = obj->vgpu; 12062306a36Sopenharmony_ci int i; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for_each_sg(pages->sgl, sg, fb_info->size, i) 12362306a36Sopenharmony_ci intel_gvt_dma_unmap_guest_page(vgpu, 12462306a36Sopenharmony_ci sg_dma_address(sg)); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci sg_free_table(pages); 12862306a36Sopenharmony_ci kfree(pages); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void dmabuf_gem_object_free(struct kref *kref) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *obj = 13462306a36Sopenharmony_ci container_of(kref, struct intel_vgpu_dmabuf_obj, kref); 13562306a36Sopenharmony_ci struct intel_vgpu *vgpu = obj->vgpu; 13662306a36Sopenharmony_ci struct list_head *pos; 13762306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (vgpu && test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status) && 14062306a36Sopenharmony_ci !list_empty(&vgpu->dmabuf_obj_list_head)) { 14162306a36Sopenharmony_ci list_for_each(pos, &vgpu->dmabuf_obj_list_head) { 14262306a36Sopenharmony_ci dmabuf_obj = list_entry(pos, struct intel_vgpu_dmabuf_obj, list); 14362306a36Sopenharmony_ci if (dmabuf_obj == obj) { 14462306a36Sopenharmony_ci list_del(pos); 14562306a36Sopenharmony_ci idr_remove(&vgpu->object_idr, 14662306a36Sopenharmony_ci dmabuf_obj->dmabuf_id); 14762306a36Sopenharmony_ci kfree(dmabuf_obj->info); 14862306a36Sopenharmony_ci kfree(dmabuf_obj); 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci /* Free the orphan dmabuf_objs here */ 15462306a36Sopenharmony_ci kfree(obj->info); 15562306a36Sopenharmony_ci kfree(obj); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline void dmabuf_obj_get(struct intel_vgpu_dmabuf_obj *obj) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci kref_get(&obj->kref); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic inline void dmabuf_obj_put(struct intel_vgpu_dmabuf_obj *obj) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci kref_put(&obj->kref, dmabuf_gem_object_free); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void vgpu_gem_release(struct drm_i915_gem_object *gem_obj) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci struct intel_vgpu_fb_info *fb_info = gem_obj->gvt_info; 17462306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *obj = fb_info->obj; 17562306a36Sopenharmony_ci struct intel_vgpu *vgpu = obj->vgpu; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (vgpu) { 17862306a36Sopenharmony_ci mutex_lock(&vgpu->dmabuf_lock); 17962306a36Sopenharmony_ci gem_obj->base.dma_buf = NULL; 18062306a36Sopenharmony_ci dmabuf_obj_put(obj); 18162306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci /* vgpu is NULL, as it has been removed already */ 18462306a36Sopenharmony_ci gem_obj->base.dma_buf = NULL; 18562306a36Sopenharmony_ci dmabuf_obj_put(obj); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct drm_i915_gem_object_ops intel_vgpu_gem_ops = { 19062306a36Sopenharmony_ci .name = "i915_gem_object_vgpu", 19162306a36Sopenharmony_ci .flags = I915_GEM_OBJECT_IS_PROXY, 19262306a36Sopenharmony_ci .get_pages = vgpu_gem_get_pages, 19362306a36Sopenharmony_ci .put_pages = vgpu_gem_put_pages, 19462306a36Sopenharmony_ci .release = vgpu_gem_release, 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct drm_i915_gem_object *vgpu_create_gem(struct drm_device *dev, 19862306a36Sopenharmony_ci struct intel_vgpu_fb_info *info) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci static struct lock_class_key lock_class; 20162306a36Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(dev); 20262306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci obj = i915_gem_object_alloc(); 20562306a36Sopenharmony_ci if (obj == NULL) 20662306a36Sopenharmony_ci return NULL; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci drm_gem_private_object_init(dev, &obj->base, 20962306a36Sopenharmony_ci roundup(info->size, PAGE_SIZE)); 21062306a36Sopenharmony_ci i915_gem_object_init(obj, &intel_vgpu_gem_ops, &lock_class, 0); 21162306a36Sopenharmony_ci i915_gem_object_set_readonly(obj); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci obj->read_domains = I915_GEM_DOMAIN_GTT; 21462306a36Sopenharmony_ci obj->write_domain = 0; 21562306a36Sopenharmony_ci if (GRAPHICS_VER(dev_priv) >= 9) { 21662306a36Sopenharmony_ci unsigned int tiling_mode = 0; 21762306a36Sopenharmony_ci unsigned int stride = 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci switch (info->drm_format_mod) { 22062306a36Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 22162306a36Sopenharmony_ci tiling_mode = I915_TILING_NONE; 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case I915_FORMAT_MOD_X_TILED: 22462306a36Sopenharmony_ci tiling_mode = I915_TILING_X; 22562306a36Sopenharmony_ci stride = info->stride; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case I915_FORMAT_MOD_Y_TILED: 22862306a36Sopenharmony_ci case I915_FORMAT_MOD_Yf_TILED: 22962306a36Sopenharmony_ci tiling_mode = I915_TILING_Y; 23062306a36Sopenharmony_ci stride = info->stride; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci default: 23362306a36Sopenharmony_ci gvt_dbg_core("invalid drm_format_mod %llx for tiling\n", 23462306a36Sopenharmony_ci info->drm_format_mod); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci obj->tiling_and_stride = tiling_mode | stride; 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci obj->tiling_and_stride = info->drm_format_mod ? 23962306a36Sopenharmony_ci I915_TILING_X : 0; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return obj; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic bool validate_hotspot(struct intel_vgpu_cursor_plane_format *c) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (c && c->x_hot <= c->width && c->y_hot <= c->height) 24862306a36Sopenharmony_ci return true; 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci return false; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int vgpu_get_plane_info(struct drm_device *dev, 25462306a36Sopenharmony_ci struct intel_vgpu *vgpu, 25562306a36Sopenharmony_ci struct intel_vgpu_fb_info *info, 25662306a36Sopenharmony_ci int plane_id) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct intel_vgpu_primary_plane_format p; 25962306a36Sopenharmony_ci struct intel_vgpu_cursor_plane_format c; 26062306a36Sopenharmony_ci int ret, tile_height = 1; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci memset(info, 0, sizeof(*info)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (plane_id == DRM_PLANE_TYPE_PRIMARY) { 26562306a36Sopenharmony_ci ret = intel_vgpu_decode_primary_plane(vgpu, &p); 26662306a36Sopenharmony_ci if (ret) 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci info->start = p.base; 26962306a36Sopenharmony_ci info->start_gpa = p.base_gpa; 27062306a36Sopenharmony_ci info->width = p.width; 27162306a36Sopenharmony_ci info->height = p.height; 27262306a36Sopenharmony_ci info->stride = p.stride; 27362306a36Sopenharmony_ci info->drm_format = p.drm_format; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci switch (p.tiled) { 27662306a36Sopenharmony_ci case PLANE_CTL_TILED_LINEAR: 27762306a36Sopenharmony_ci info->drm_format_mod = DRM_FORMAT_MOD_LINEAR; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case PLANE_CTL_TILED_X: 28062306a36Sopenharmony_ci info->drm_format_mod = I915_FORMAT_MOD_X_TILED; 28162306a36Sopenharmony_ci tile_height = 8; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case PLANE_CTL_TILED_Y: 28462306a36Sopenharmony_ci info->drm_format_mod = I915_FORMAT_MOD_Y_TILED; 28562306a36Sopenharmony_ci tile_height = 32; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case PLANE_CTL_TILED_YF: 28862306a36Sopenharmony_ci info->drm_format_mod = I915_FORMAT_MOD_Yf_TILED; 28962306a36Sopenharmony_ci tile_height = 32; 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci default: 29262306a36Sopenharmony_ci gvt_vgpu_err("invalid tiling mode: %x\n", p.tiled); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci } else if (plane_id == DRM_PLANE_TYPE_CURSOR) { 29562306a36Sopenharmony_ci ret = intel_vgpu_decode_cursor_plane(vgpu, &c); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci info->start = c.base; 29962306a36Sopenharmony_ci info->start_gpa = c.base_gpa; 30062306a36Sopenharmony_ci info->width = c.width; 30162306a36Sopenharmony_ci info->height = c.height; 30262306a36Sopenharmony_ci info->stride = c.width * (c.bpp / 8); 30362306a36Sopenharmony_ci info->drm_format = c.drm_format; 30462306a36Sopenharmony_ci info->drm_format_mod = 0; 30562306a36Sopenharmony_ci info->x_pos = c.x_pos; 30662306a36Sopenharmony_ci info->y_pos = c.y_pos; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (validate_hotspot(&c)) { 30962306a36Sopenharmony_ci info->x_hot = c.x_hot; 31062306a36Sopenharmony_ci info->y_hot = c.y_hot; 31162306a36Sopenharmony_ci } else { 31262306a36Sopenharmony_ci info->x_hot = UINT_MAX; 31362306a36Sopenharmony_ci info->y_hot = UINT_MAX; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci gvt_vgpu_err("invalid plane id:%d\n", plane_id); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci info->size = info->stride * roundup(info->height, tile_height); 32162306a36Sopenharmony_ci if (info->size == 0) { 32262306a36Sopenharmony_ci gvt_vgpu_err("fb size is zero\n"); 32362306a36Sopenharmony_ci return -EINVAL; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (info->start & (PAGE_SIZE - 1)) { 32762306a36Sopenharmony_ci gvt_vgpu_err("Not aligned fb address:0x%llx\n", info->start); 32862306a36Sopenharmony_ci return -EFAULT; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!intel_gvt_ggtt_validate_range(vgpu, info->start, info->size)) { 33262306a36Sopenharmony_ci gvt_vgpu_err("invalid gma addr\n"); 33362306a36Sopenharmony_ci return -EFAULT; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic struct intel_vgpu_dmabuf_obj * 34062306a36Sopenharmony_cipick_dmabuf_by_info(struct intel_vgpu *vgpu, 34162306a36Sopenharmony_ci struct intel_vgpu_fb_info *latest_info) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct list_head *pos; 34462306a36Sopenharmony_ci struct intel_vgpu_fb_info *fb_info; 34562306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj = NULL; 34662306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *ret = NULL; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci list_for_each(pos, &vgpu->dmabuf_obj_list_head) { 34962306a36Sopenharmony_ci dmabuf_obj = list_entry(pos, struct intel_vgpu_dmabuf_obj, list); 35062306a36Sopenharmony_ci if (!dmabuf_obj->info) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci fb_info = (struct intel_vgpu_fb_info *)dmabuf_obj->info; 35462306a36Sopenharmony_ci if ((fb_info->start == latest_info->start) && 35562306a36Sopenharmony_ci (fb_info->start_gpa == latest_info->start_gpa) && 35662306a36Sopenharmony_ci (fb_info->size == latest_info->size) && 35762306a36Sopenharmony_ci (fb_info->drm_format_mod == latest_info->drm_format_mod) && 35862306a36Sopenharmony_ci (fb_info->drm_format == latest_info->drm_format) && 35962306a36Sopenharmony_ci (fb_info->width == latest_info->width) && 36062306a36Sopenharmony_ci (fb_info->height == latest_info->height)) { 36162306a36Sopenharmony_ci ret = dmabuf_obj; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic struct intel_vgpu_dmabuf_obj * 37062306a36Sopenharmony_cipick_dmabuf_by_num(struct intel_vgpu *vgpu, u32 id) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct list_head *pos; 37362306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj = NULL; 37462306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *ret = NULL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci list_for_each(pos, &vgpu->dmabuf_obj_list_head) { 37762306a36Sopenharmony_ci dmabuf_obj = list_entry(pos, struct intel_vgpu_dmabuf_obj, list); 37862306a36Sopenharmony_ci if (dmabuf_obj->dmabuf_id == id) { 37962306a36Sopenharmony_ci ret = dmabuf_obj; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return ret; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void update_fb_info(struct vfio_device_gfx_plane_info *gvt_dmabuf, 38862306a36Sopenharmony_ci struct intel_vgpu_fb_info *fb_info) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci gvt_dmabuf->drm_format = fb_info->drm_format; 39162306a36Sopenharmony_ci gvt_dmabuf->drm_format_mod = fb_info->drm_format_mod; 39262306a36Sopenharmony_ci gvt_dmabuf->width = fb_info->width; 39362306a36Sopenharmony_ci gvt_dmabuf->height = fb_info->height; 39462306a36Sopenharmony_ci gvt_dmabuf->stride = fb_info->stride; 39562306a36Sopenharmony_ci gvt_dmabuf->size = fb_info->size; 39662306a36Sopenharmony_ci gvt_dmabuf->x_pos = fb_info->x_pos; 39762306a36Sopenharmony_ci gvt_dmabuf->y_pos = fb_info->y_pos; 39862306a36Sopenharmony_ci gvt_dmabuf->x_hot = fb_info->x_hot; 39962306a36Sopenharmony_ci gvt_dmabuf->y_hot = fb_info->y_hot; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciint intel_vgpu_query_plane(struct intel_vgpu *vgpu, void *args) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct drm_device *dev = &vgpu->gvt->gt->i915->drm; 40562306a36Sopenharmony_ci struct vfio_device_gfx_plane_info *gfx_plane_info = args; 40662306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj; 40762306a36Sopenharmony_ci struct intel_vgpu_fb_info fb_info; 40862306a36Sopenharmony_ci int ret = 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (gfx_plane_info->flags == (VFIO_GFX_PLANE_TYPE_DMABUF | 41162306a36Sopenharmony_ci VFIO_GFX_PLANE_TYPE_PROBE)) 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci else if ((gfx_plane_info->flags & ~VFIO_GFX_PLANE_TYPE_DMABUF) || 41462306a36Sopenharmony_ci (!gfx_plane_info->flags)) 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = vgpu_get_plane_info(dev, vgpu, &fb_info, 41862306a36Sopenharmony_ci gfx_plane_info->drm_plane_type); 41962306a36Sopenharmony_ci if (ret != 0) 42062306a36Sopenharmony_ci goto out; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci mutex_lock(&vgpu->dmabuf_lock); 42362306a36Sopenharmony_ci /* If exists, pick up the exposed dmabuf_obj */ 42462306a36Sopenharmony_ci dmabuf_obj = pick_dmabuf_by_info(vgpu, &fb_info); 42562306a36Sopenharmony_ci if (dmabuf_obj) { 42662306a36Sopenharmony_ci update_fb_info(gfx_plane_info, &fb_info); 42762306a36Sopenharmony_ci gfx_plane_info->dmabuf_id = dmabuf_obj->dmabuf_id; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* This buffer may be released between query_plane ioctl and 43062306a36Sopenharmony_ci * get_dmabuf ioctl. Add the refcount to make sure it won't 43162306a36Sopenharmony_ci * be released between the two ioctls. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci if (!dmabuf_obj->initref) { 43462306a36Sopenharmony_ci dmabuf_obj->initref = true; 43562306a36Sopenharmony_ci dmabuf_obj_get(dmabuf_obj); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci ret = 0; 43862306a36Sopenharmony_ci gvt_dbg_dpy("vgpu%d: re-use dmabuf_obj ref %d, id %d\n", 43962306a36Sopenharmony_ci vgpu->id, kref_read(&dmabuf_obj->kref), 44062306a36Sopenharmony_ci gfx_plane_info->dmabuf_id); 44162306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 44262306a36Sopenharmony_ci goto out; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Need to allocate a new one*/ 44862306a36Sopenharmony_ci dmabuf_obj = kmalloc(sizeof(struct intel_vgpu_dmabuf_obj), GFP_KERNEL); 44962306a36Sopenharmony_ci if (unlikely(!dmabuf_obj)) { 45062306a36Sopenharmony_ci gvt_vgpu_err("alloc dmabuf_obj failed\n"); 45162306a36Sopenharmony_ci ret = -ENOMEM; 45262306a36Sopenharmony_ci goto out; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci dmabuf_obj->info = kmalloc(sizeof(struct intel_vgpu_fb_info), 45662306a36Sopenharmony_ci GFP_KERNEL); 45762306a36Sopenharmony_ci if (unlikely(!dmabuf_obj->info)) { 45862306a36Sopenharmony_ci gvt_vgpu_err("allocate intel vgpu fb info failed\n"); 45962306a36Sopenharmony_ci ret = -ENOMEM; 46062306a36Sopenharmony_ci goto out_free_dmabuf; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci memcpy(dmabuf_obj->info, &fb_info, sizeof(struct intel_vgpu_fb_info)); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ((struct intel_vgpu_fb_info *)dmabuf_obj->info)->obj = dmabuf_obj; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci dmabuf_obj->vgpu = vgpu; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ret = idr_alloc(&vgpu->object_idr, dmabuf_obj, 1, 0, GFP_NOWAIT); 46962306a36Sopenharmony_ci if (ret < 0) 47062306a36Sopenharmony_ci goto out_free_info; 47162306a36Sopenharmony_ci gfx_plane_info->dmabuf_id = ret; 47262306a36Sopenharmony_ci dmabuf_obj->dmabuf_id = ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci dmabuf_obj->initref = true; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci kref_init(&dmabuf_obj->kref); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci update_fb_info(gfx_plane_info, &fb_info); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci INIT_LIST_HEAD(&dmabuf_obj->list); 48162306a36Sopenharmony_ci mutex_lock(&vgpu->dmabuf_lock); 48262306a36Sopenharmony_ci list_add_tail(&dmabuf_obj->list, &vgpu->dmabuf_obj_list_head); 48362306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci gvt_dbg_dpy("vgpu%d: %s new dmabuf_obj ref %d, id %d\n", vgpu->id, 48662306a36Sopenharmony_ci __func__, kref_read(&dmabuf_obj->kref), ret); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciout_free_info: 49162306a36Sopenharmony_ci kfree(dmabuf_obj->info); 49262306a36Sopenharmony_ciout_free_dmabuf: 49362306a36Sopenharmony_ci kfree(dmabuf_obj); 49462306a36Sopenharmony_ciout: 49562306a36Sopenharmony_ci /* ENODEV means plane isn't ready, which might be a normal case. */ 49662306a36Sopenharmony_ci return (ret == -ENODEV) ? 0 : ret; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* To associate an exposed dmabuf with the dmabuf_obj */ 50062306a36Sopenharmony_ciint intel_vgpu_get_dmabuf(struct intel_vgpu *vgpu, unsigned int dmabuf_id) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct drm_device *dev = &vgpu->gvt->gt->i915->drm; 50362306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj; 50462306a36Sopenharmony_ci struct drm_i915_gem_object *obj; 50562306a36Sopenharmony_ci struct dma_buf *dmabuf; 50662306a36Sopenharmony_ci int dmabuf_fd; 50762306a36Sopenharmony_ci int ret = 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci mutex_lock(&vgpu->dmabuf_lock); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci dmabuf_obj = pick_dmabuf_by_num(vgpu, dmabuf_id); 51262306a36Sopenharmony_ci if (dmabuf_obj == NULL) { 51362306a36Sopenharmony_ci gvt_vgpu_err("invalid dmabuf id:%d\n", dmabuf_id); 51462306a36Sopenharmony_ci ret = -EINVAL; 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci obj = vgpu_create_gem(dev, dmabuf_obj->info); 51962306a36Sopenharmony_ci if (obj == NULL) { 52062306a36Sopenharmony_ci gvt_vgpu_err("create gvt gem obj failed\n"); 52162306a36Sopenharmony_ci ret = -ENOMEM; 52262306a36Sopenharmony_ci goto out; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci obj->gvt_info = dmabuf_obj->info; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dmabuf = i915_gem_prime_export(&obj->base, DRM_CLOEXEC | DRM_RDWR); 52862306a36Sopenharmony_ci if (IS_ERR(dmabuf)) { 52962306a36Sopenharmony_ci gvt_vgpu_err("export dma-buf failed\n"); 53062306a36Sopenharmony_ci ret = PTR_ERR(dmabuf); 53162306a36Sopenharmony_ci goto out_free_gem; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ret = dma_buf_fd(dmabuf, DRM_CLOEXEC | DRM_RDWR); 53562306a36Sopenharmony_ci if (ret < 0) { 53662306a36Sopenharmony_ci gvt_vgpu_err("create dma-buf fd failed ret:%d\n", ret); 53762306a36Sopenharmony_ci goto out_free_dmabuf; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci dmabuf_fd = ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci dmabuf_obj_get(dmabuf_obj); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (dmabuf_obj->initref) { 54462306a36Sopenharmony_ci dmabuf_obj->initref = false; 54562306a36Sopenharmony_ci dmabuf_obj_put(dmabuf_obj); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci gvt_dbg_dpy("vgpu%d: dmabuf:%d, dmabuf ref %d, fd:%d\n" 55162306a36Sopenharmony_ci " file count: %ld, GEM ref: %d\n", 55262306a36Sopenharmony_ci vgpu->id, dmabuf_obj->dmabuf_id, 55362306a36Sopenharmony_ci kref_read(&dmabuf_obj->kref), 55462306a36Sopenharmony_ci dmabuf_fd, 55562306a36Sopenharmony_ci file_count(dmabuf->file), 55662306a36Sopenharmony_ci kref_read(&obj->base.refcount)); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci i915_gem_object_put(obj); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return dmabuf_fd; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciout_free_dmabuf: 56362306a36Sopenharmony_ci dma_buf_put(dmabuf); 56462306a36Sopenharmony_ciout_free_gem: 56562306a36Sopenharmony_ci i915_gem_object_put(obj); 56662306a36Sopenharmony_ciout: 56762306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 56862306a36Sopenharmony_ci return ret; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_civoid intel_vgpu_dmabuf_cleanup(struct intel_vgpu *vgpu) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct list_head *pos, *n; 57462306a36Sopenharmony_ci struct intel_vgpu_dmabuf_obj *dmabuf_obj; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci mutex_lock(&vgpu->dmabuf_lock); 57762306a36Sopenharmony_ci list_for_each_safe(pos, n, &vgpu->dmabuf_obj_list_head) { 57862306a36Sopenharmony_ci dmabuf_obj = list_entry(pos, struct intel_vgpu_dmabuf_obj, list); 57962306a36Sopenharmony_ci dmabuf_obj->vgpu = NULL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci idr_remove(&vgpu->object_idr, dmabuf_obj->dmabuf_id); 58262306a36Sopenharmony_ci list_del(pos); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* dmabuf_obj might be freed in dmabuf_obj_put */ 58562306a36Sopenharmony_ci if (dmabuf_obj->initref) { 58662306a36Sopenharmony_ci dmabuf_obj->initref = false; 58762306a36Sopenharmony_ci dmabuf_obj_put(dmabuf_obj); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci mutex_unlock(&vgpu->dmabuf_lock); 59262306a36Sopenharmony_ci} 593