1 // SPDX-License-Identifier: MIT 2 /* Copyright © 2024 Intel Corporation */ 3 4 #include <linux/fb.h> 5 6 #include <drm/drm_gem.h> 7 #include <drm/intel/display_parent_interface.h> 8 9 #include "intel_fb.h" 10 #include "xe_bo.h" 11 #include "xe_display_bo.h" 12 #include "xe_pxp.h" 13 #include "xe_ttm_stolen_mgr.h" 14 #include "xe_wa.h" 15 16 #include <generated/xe_device_wa_oob.h> 17 18 static bool xe_display_bo_is_protected(struct drm_gem_object *obj) 19 { 20 return xe_bo_is_protected(gem_to_xe_bo(obj)); 21 } 22 23 static int xe_display_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, int size) 24 { 25 struct xe_bo *bo = gem_to_xe_bo(obj); 26 27 return xe_bo_read(bo, offset, dst, size); 28 } 29 30 static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj, 31 struct drm_mode_fb_cmd2 *mode_cmd) 32 { 33 struct xe_bo *bo = gem_to_xe_bo(obj); 34 struct xe_device *xe = to_xe_device(bo->ttm.base.dev); 35 int ret; 36 37 /* 38 * Some modifiers require physical alignment of 64KiB VRAM pages; 39 * require that the BO in those cases is created correctly. 40 */ 41 if (XE_IOCTL_DBG(xe, intel_fb_needs_64k_phys(mode_cmd->modifier[0]) && 42 !(bo->flags & XE_BO_FLAG_NEEDS_64K))) 43 return -EINVAL; 44 45 xe_bo_get(bo); 46 47 ret = ttm_bo_reserve(&bo->ttm, true, false, NULL); 48 if (ret) 49 goto err; 50 51 if (!(bo->flags & XE_BO_FLAG_FORCE_WC) && 52 bo->ttm.type != ttm_bo_type_sg) { 53 /* 54 * XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is 55 * automatically set when creating FB. We cannot change caching 56 * mode when the bo is VM_BINDed, so we can only set 57 * coherency with display when unbound. 58 */ 59 if (XE_IOCTL_DBG(xe, xe_bo_is_vm_bound(bo))) { 60 ttm_bo_unreserve(&bo->ttm); 61 ret = -EINVAL; 62 goto err; 63 } 64 bo->flags |= XE_BO_FLAG_FORCE_WC; 65 } 66 ttm_bo_unreserve(&bo->ttm); 67 return 0; 68 69 err: 70 xe_bo_put(bo); 71 return ret; 72 } 73 74 static void xe_display_bo_framebuffer_fini(struct drm_gem_object *obj) 75 { 76 struct xe_bo *bo = gem_to_xe_bo(obj); 77 78 if (bo->flags & XE_BO_FLAG_PINNED) { 79 /* Unpin our kernel fb first */ 80 xe_bo_lock(bo, false); 81 xe_bo_unpin(bo); 82 xe_bo_unlock(bo); 83 } 84 xe_bo_put(bo); 85 } 86 87 static struct drm_gem_object * 88 xe_display_bo_framebuffer_lookup(struct drm_device *drm, 89 struct drm_file *filp, 90 const struct drm_mode_fb_cmd2 *mode_cmd) 91 { 92 struct xe_device *xe = to_xe_device(drm); 93 struct xe_bo *bo; 94 struct drm_gem_object *gem = drm_gem_object_lookup(filp, mode_cmd->handles[0]); 95 96 if (!gem) 97 return ERR_PTR(-ENOENT); 98 99 bo = gem_to_xe_bo(gem); 100 /* Require vram placement or dma-buf import */ 101 if (IS_DGFX(xe) && 102 !xe_bo_can_migrate(bo, XE_PL_VRAM0) && 103 bo->ttm.type != ttm_bo_type_sg) { 104 drm_gem_object_put(gem); 105 return ERR_PTR(-EREMOTE); 106 } 107 108 return gem; 109 } 110 111 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 112 /* 113 * FIXME: There shouldn't be any reason to have XE_PAGE_SIZE stride 114 * alignment. The same 64 as i915 uses should be fine, and we shouldn't need to 115 * have driver specific values. However, dropping the stride alignment to 64 116 * leads to underflowing the bo pin count in the atomic cleanup work. 117 */ 118 static u32 xe_display_bo_fbdev_pitch_align(u32 stride) 119 { 120 return ALIGN(stride, XE_PAGE_SIZE); 121 } 122 123 bool xe_display_bo_fbdev_prefer_stolen(struct xe_device *xe, unsigned int size) 124 { 125 struct ttm_resource_manager *stolen; 126 127 stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); 128 if (!stolen) 129 return false; 130 131 if (IS_DGFX(xe)) 132 return false; 133 134 if (XE_DEVICE_WA(xe, 22019338487_display)) 135 return false; 136 137 /* 138 * If the FB is too big, just don't use it since fbdev is not very 139 * important and we should probably use that space with FBC or other 140 * features. 141 */ 142 return stolen->size >= (size * 2) >> PAGE_SHIFT; 143 } 144 145 static struct drm_gem_object *xe_display_bo_fbdev_create(struct drm_device *drm, int size) 146 { 147 struct xe_device *xe = to_xe_device(drm); 148 struct xe_bo *obj; 149 150 obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size, 151 ttm_bo_type_kernel, 152 XE_BO_FLAG_FORCE_WC | 153 XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) | 154 XE_BO_FLAG_GGTT, 155 false); 156 if (IS_ERR(obj)) { 157 drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj); 158 return ERR_PTR(-ENOMEM); 159 } 160 161 return &obj->ttm.base; 162 } 163 164 static void xe_display_bo_fbdev_destroy(struct drm_gem_object *obj) 165 { 166 xe_bo_unpin_map_no_vm(gem_to_xe_bo(obj)); 167 } 168 169 static int xe_display_bo_fbdev_fill_info(struct drm_gem_object *_obj, struct fb_info *info, 170 struct i915_vma *vma) 171 { 172 struct xe_bo *obj = gem_to_xe_bo(_obj); 173 struct pci_dev *pdev = to_pci_dev(_obj->dev->dev); 174 175 if (!(obj->flags & XE_BO_FLAG_SYSTEM)) { 176 if (obj->flags & XE_BO_FLAG_STOLEN) 177 info->fix.smem_start = xe_ttm_stolen_io_offset(obj, 0); 178 else 179 info->fix.smem_start = 180 pci_resource_start(pdev, 2) + 181 xe_bo_addr(obj, 0, XE_PAGE_SIZE); 182 183 info->fix.smem_len = obj->ttm.base.size; 184 } else { 185 /* XXX: Pure fiction, as the BO may not be physically accessible.. */ 186 info->fix.smem_start = 0; 187 info->fix.smem_len = obj->ttm.base.size; 188 } 189 XE_WARN_ON(iosys_map_is_null(&obj->vmap)); 190 191 info->screen_base = obj->vmap.vaddr_iomem; 192 info->screen_size = obj->ttm.base.size; 193 194 return 0; 195 } 196 #endif 197 198 const struct intel_display_bo_interface xe_display_bo_interface = { 199 .is_protected = xe_display_bo_is_protected, 200 .key_check = xe_pxp_obj_key_check, 201 .fb_mmap = drm_gem_prime_mmap, 202 .read_from_page = xe_display_bo_read_from_page, 203 .framebuffer_init = xe_display_bo_framebuffer_init, 204 .framebuffer_fini = xe_display_bo_framebuffer_fini, 205 .framebuffer_lookup = xe_display_bo_framebuffer_lookup, 206 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 207 .fbdev_create = xe_display_bo_fbdev_create, 208 .fbdev_destroy = xe_display_bo_fbdev_destroy, 209 .fbdev_fill_info = xe_display_bo_fbdev_fill_info, 210 .fbdev_pitch_align = xe_display_bo_fbdev_pitch_align, 211 #endif 212 }; 213