1 /************************************************************************** 2 * 3 * Copyright 2009, VMware, Inc. 4 * All Rights Reserved. 5 * Copyright 2010 George Sapountzis <gsapountzis (at) gmail.com> 6 * 2013 Red Hat, Inc. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 23 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stddef.h> 33 #include <stdint.h> 34 #include <string.h> 35 #include <limits.h> 36 37 #include <sys/types.h> 38 #include <sys/mman.h> 39 #include <unistd.h> 40 #include <dlfcn.h> 41 #include <fcntl.h> 42 #include <xf86drm.h> 43 44 #include "pipe/p_compiler.h" 45 #include "pipe/p_format.h" 46 #include "util/u_inlines.h" 47 #include "util/u_format.h" 48 #include "util/u_math.h" 49 #include "util/u_memory.h" 50 #include "util/list.h" 51 52 #include "state_tracker/sw_winsys.h" 53 #include "state_tracker/drm_driver.h" 54 #include "kms_dri_sw_winsys.h" 55 56 #ifdef DEBUG 57 #define DEBUG_PRINT(msg, ...) fprintf(stderr, msg, __VA_ARGS__) 58 #else 59 #define DEBUG_PRINT(msg, ...) 60 #endif 61 62 63 struct kms_sw_displaytarget 64 { 65 enum pipe_format format; 66 unsigned width; 67 unsigned height; 68 unsigned stride; 69 unsigned size; 70 71 uint32_t handle; 72 void *mapped; 73 74 int ref_count; 75 struct list_head link; 76 }; 77 78 struct kms_sw_winsys 79 { 80 struct sw_winsys base; 81 82 int fd; 83 struct list_head bo_list; 84 }; 85 86 static inline struct kms_sw_displaytarget * 87 kms_sw_displaytarget( struct sw_displaytarget *dt ) 88 { 89 return (struct kms_sw_displaytarget *)dt; 90 } 91 92 static inline struct kms_sw_winsys * 93 kms_sw_winsys( struct sw_winsys *ws ) 94 { 95 return (struct kms_sw_winsys *)ws; 96 } 97 98 99 static boolean 100 kms_sw_is_displaytarget_format_supported( struct sw_winsys *ws, 101 unsigned tex_usage, 102 enum pipe_format format ) 103 { 104 /* TODO: check visuals or other sensible thing here */ 105 return TRUE; 106 } 107 108 static struct sw_displaytarget * 109 kms_sw_displaytarget_create(struct sw_winsys *ws, 110 unsigned tex_usage, 111 enum pipe_format format, 112 unsigned width, unsigned height, 113 unsigned alignment, 114 const void *front_private, 115 unsigned *stride) 116 { 117 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws); 118 struct kms_sw_displaytarget *kms_sw_dt; 119 struct drm_mode_create_dumb create_req; 120 struct drm_mode_destroy_dumb destroy_req; 121 int ret; 122 123 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget); 124 if (!kms_sw_dt) 125 goto no_dt; 126 127 kms_sw_dt->ref_count = 1; 128 129 kms_sw_dt->format = format; 130 kms_sw_dt->width = width; 131 kms_sw_dt->height = height; 132 133 memset(&create_req, 0, sizeof(create_req)); 134 create_req.bpp = 32; 135 create_req.width = width; 136 create_req.height = height; 137 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req); 138 if (ret) 139 goto free_bo; 140 141 kms_sw_dt->stride = create_req.pitch; 142 kms_sw_dt->size = create_req.size; 143 kms_sw_dt->handle = create_req.handle; 144 145 list_add(&kms_sw_dt->link, &kms_sw->bo_list); 146 147 DEBUG_PRINT("KMS-DEBUG: created buffer %u (size %u)\n", kms_sw_dt->handle, kms_sw_dt->size); 148 149 *stride = kms_sw_dt->stride; 150 return (struct sw_displaytarget *)kms_sw_dt; 151 152 free_bo: 153 memset(&destroy_req, 0, sizeof destroy_req); 154 destroy_req.handle = create_req.handle; 155 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req); 156 FREE(kms_sw_dt); 157 no_dt: 158 return NULL; 159 } 160 161 static void 162 kms_sw_displaytarget_destroy(struct sw_winsys *ws, 163 struct sw_displaytarget *dt) 164 { 165 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws); 166 struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt); 167 struct drm_mode_destroy_dumb destroy_req; 168 169 kms_sw_dt->ref_count --; 170 if (kms_sw_dt->ref_count > 0) 171 return; 172 173 memset(&destroy_req, 0, sizeof destroy_req); 174 destroy_req.handle = kms_sw_dt->handle; 175 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req); 176 177 list_del(&kms_sw_dt->link); 178 179 DEBUG_PRINT("KMS-DEBUG: destroyed buffer %u\n", kms_sw_dt->handle); 180 181 FREE(kms_sw_dt); 182 } 183 184 static void * 185 kms_sw_displaytarget_map(struct sw_winsys *ws, 186 struct sw_displaytarget *dt, 187 unsigned flags) 188 { 189 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws); 190 struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt); 191 struct drm_mode_map_dumb map_req; 192 int prot, ret; 193 194 memset(&map_req, 0, sizeof map_req); 195 map_req.handle = kms_sw_dt->handle; 196 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req); 197 if (ret) 198 return NULL; 199 200 prot = (flags == PIPE_TRANSFER_READ) ? PROT_READ : (PROT_READ | PROT_WRITE); 201 kms_sw_dt->mapped = mmap(0, kms_sw_dt->size, prot, MAP_SHARED, 202 kms_sw->fd, map_req.offset); 203 204 if (kms_sw_dt->mapped == MAP_FAILED) 205 return NULL; 206 207 DEBUG_PRINT("KMS-DEBUG: mapped buffer %u (size %u) at %p\n", 208 kms_sw_dt->handle, kms_sw_dt->size, kms_sw_dt->mapped); 209 210 return kms_sw_dt->mapped; 211 } 212 213 static struct kms_sw_displaytarget * 214 kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys *kms_sw, 215 unsigned int kms_handle) 216 { 217 struct kms_sw_displaytarget *kms_sw_dt; 218 219 LIST_FOR_EACH_ENTRY(kms_sw_dt, &kms_sw->bo_list, link) { 220 if (kms_sw_dt->handle == kms_handle) { 221 kms_sw_dt->ref_count++; 222 223 DEBUG_PRINT("KMS-DEBUG: imported buffer %u (size %u)\n", 224 kms_sw_dt->handle, kms_sw_dt->size); 225 226 return kms_sw_dt; 227 } 228 } 229 230 return NULL; 231 } 232 233 static struct kms_sw_displaytarget * 234 kms_sw_displaytarget_add_from_prime(struct kms_sw_winsys *kms_sw, int fd, 235 unsigned width, unsigned height, 236 unsigned stride) 237 { 238 uint32_t handle = -1; 239 struct kms_sw_displaytarget * kms_sw_dt; 240 int ret; 241 242 ret = drmPrimeFDToHandle(kms_sw->fd, fd, &handle); 243 244 if (ret) 245 return NULL; 246 247 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, handle); 248 if (kms_sw_dt) 249 return kms_sw_dt; 250 251 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget); 252 if (!kms_sw_dt) 253 return NULL; 254 255 off_t lseek_ret = lseek(fd, 0, SEEK_END); 256 if (lseek_ret == -1) { 257 FREE(kms_sw_dt); 258 return NULL; 259 } 260 kms_sw_dt->size = lseek_ret; 261 kms_sw_dt->ref_count = 1; 262 kms_sw_dt->handle = handle; 263 kms_sw_dt->width = width; 264 kms_sw_dt->height = height; 265 kms_sw_dt->stride = stride; 266 267 lseek(fd, 0, SEEK_SET); 268 269 list_add(&kms_sw_dt->link, &kms_sw->bo_list); 270 271 return kms_sw_dt; 272 } 273 274 static void 275 kms_sw_displaytarget_unmap(struct sw_winsys *ws, 276 struct sw_displaytarget *dt) 277 { 278 struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt); 279 280 DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->mapped); 281 282 munmap(kms_sw_dt->mapped, kms_sw_dt->size); 283 kms_sw_dt->mapped = NULL; 284 } 285 286 static struct sw_displaytarget * 287 kms_sw_displaytarget_from_handle(struct sw_winsys *ws, 288 const struct pipe_resource *templ, 289 struct winsys_handle *whandle, 290 unsigned *stride) 291 { 292 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws); 293 struct kms_sw_displaytarget *kms_sw_dt; 294 295 assert(whandle->type == DRM_API_HANDLE_TYPE_KMS || 296 whandle->type == DRM_API_HANDLE_TYPE_FD); 297 298 if (whandle->offset != 0) { 299 DEBUG_PRINT("KMS-DEBUG: attempt to import unsupported winsys offset %d\n", 300 whandle->offset); 301 return NULL; 302 } 303 304 switch(whandle->type) { 305 case DRM_API_HANDLE_TYPE_FD: 306 kms_sw_dt = kms_sw_displaytarget_add_from_prime(kms_sw, whandle->handle, 307 templ->width0, 308 templ->height0, 309 whandle->stride); 310 if (kms_sw_dt) 311 *stride = kms_sw_dt->stride; 312 return (struct sw_displaytarget *)kms_sw_dt; 313 case DRM_API_HANDLE_TYPE_KMS: 314 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, whandle->handle); 315 if (kms_sw_dt) { 316 *stride = kms_sw_dt->stride; 317 return (struct sw_displaytarget *)kms_sw_dt; 318 } 319 /* fallthrough */ 320 default: 321 break; 322 } 323 324 assert(0); 325 return NULL; 326 } 327 328 static boolean 329 kms_sw_displaytarget_get_handle(struct sw_winsys *winsys, 330 struct sw_displaytarget *dt, 331 struct winsys_handle *whandle) 332 { 333 struct kms_sw_winsys *kms_sw = kms_sw_winsys(winsys); 334 struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt); 335 336 switch(whandle->type) { 337 case DRM_API_HANDLE_TYPE_KMS: 338 whandle->handle = kms_sw_dt->handle; 339 whandle->stride = kms_sw_dt->stride; 340 whandle->offset = 0; 341 return TRUE; 342 case DRM_API_HANDLE_TYPE_FD: 343 if (!drmPrimeHandleToFD(kms_sw->fd, kms_sw_dt->handle, 344 DRM_CLOEXEC, (int*)&whandle->handle)) { 345 whandle->stride = kms_sw_dt->stride; 346 whandle->offset = 0; 347 return TRUE; 348 } 349 /* fallthrough */ 350 default: 351 whandle->handle = 0; 352 whandle->stride = 0; 353 whandle->offset = 0; 354 return FALSE; 355 } 356 } 357 358 static void 359 kms_sw_displaytarget_display(struct sw_winsys *ws, 360 struct sw_displaytarget *dt, 361 void *context_private, 362 struct pipe_box *box) 363 { 364 /* This function should not be called, instead the dri2 loader should 365 handle swap buffers internally. 366 */ 367 assert(0); 368 } 369 370 371 static void 372 kms_destroy_sw_winsys(struct sw_winsys *winsys) 373 { 374 FREE(winsys); 375 } 376 377 struct sw_winsys * 378 kms_dri_create_winsys(int fd) 379 { 380 struct kms_sw_winsys *ws; 381 382 ws = CALLOC_STRUCT(kms_sw_winsys); 383 if (!ws) 384 return NULL; 385 386 ws->fd = fd; 387 list_inithead(&ws->bo_list); 388 389 ws->base.destroy = kms_destroy_sw_winsys; 390 391 ws->base.is_displaytarget_format_supported = kms_sw_is_displaytarget_format_supported; 392 393 /* screen texture functions */ 394 ws->base.displaytarget_create = kms_sw_displaytarget_create; 395 ws->base.displaytarget_destroy = kms_sw_displaytarget_destroy; 396 ws->base.displaytarget_from_handle = kms_sw_displaytarget_from_handle; 397 ws->base.displaytarget_get_handle = kms_sw_displaytarget_get_handle; 398 399 /* texture functions */ 400 ws->base.displaytarget_map = kms_sw_displaytarget_map; 401 ws->base.displaytarget_unmap = kms_sw_displaytarget_unmap; 402 403 ws->base.displaytarget_display = kms_sw_displaytarget_display; 404 405 return &ws->base; 406 } 407 408 /* vim: set sw=3 ts=8 sts=3 expandtab: */ 409