diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 189 |
1 files changed, 187 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1384d6686555..96316fd47233 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c | |||
| @@ -55,6 +55,9 @@ static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, | |||
| 55 | static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj); | 55 | static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj); |
| 56 | static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); | 56 | static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); |
| 57 | static int i915_gem_evict_something(struct drm_device *dev); | 57 | static int i915_gem_evict_something(struct drm_device *dev); |
| 58 | static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | ||
| 59 | struct drm_i915_gem_pwrite *args, | ||
| 60 | struct drm_file *file_priv); | ||
| 58 | 61 | ||
| 59 | int i915_gem_do_init(struct drm_device *dev, unsigned long start, | 62 | int i915_gem_do_init(struct drm_device *dev, unsigned long start, |
| 60 | unsigned long end) | 63 | unsigned long end) |
| @@ -386,8 +389,10 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, | |||
| 386 | * pread/pwrite currently are reading and writing from the CPU | 389 | * pread/pwrite currently are reading and writing from the CPU |
| 387 | * perspective, requiring manual detiling by the client. | 390 | * perspective, requiring manual detiling by the client. |
| 388 | */ | 391 | */ |
| 389 | if (obj_priv->tiling_mode == I915_TILING_NONE && | 392 | if (obj_priv->phys_obj) |
| 390 | dev->gtt_total != 0) | 393 | ret = i915_gem_phys_pwrite(dev, obj, args, file_priv); |
| 394 | else if (obj_priv->tiling_mode == I915_TILING_NONE && | ||
| 395 | dev->gtt_total != 0) | ||
| 391 | ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv); | 396 | ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv); |
| 392 | else | 397 | else |
| 393 | ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv); | 398 | ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv); |
| @@ -2858,6 +2863,9 @@ void i915_gem_free_object(struct drm_gem_object *obj) | |||
| 2858 | while (obj_priv->pin_count > 0) | 2863 | while (obj_priv->pin_count > 0) |
| 2859 | i915_gem_object_unpin(obj); | 2864 | i915_gem_object_unpin(obj); |
| 2860 | 2865 | ||
| 2866 | if (obj_priv->phys_obj) | ||
| 2867 | i915_gem_detach_phys_object(dev, obj); | ||
| 2868 | |||
| 2861 | i915_gem_object_unbind(obj); | 2869 | i915_gem_object_unbind(obj); |
| 2862 | 2870 | ||
| 2863 | list = &obj->map_list; | 2871 | list = &obj->map_list; |
| @@ -3293,3 +3301,180 @@ i915_gem_load(struct drm_device *dev) | |||
| 3293 | 3301 | ||
| 3294 | i915_gem_detect_bit_6_swizzle(dev); | 3302 | i915_gem_detect_bit_6_swizzle(dev); |
| 3295 | } | 3303 | } |
| 3304 | |||
| 3305 | /* | ||
| 3306 | * Create a physically contiguous memory object for this object | ||
| 3307 | * e.g. for cursor + overlay regs | ||
| 3308 | */ | ||
| 3309 | int i915_gem_init_phys_object(struct drm_device *dev, | ||
| 3310 | int id, int size) | ||
| 3311 | { | ||
| 3312 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 3313 | struct drm_i915_gem_phys_object *phys_obj; | ||
| 3314 | int ret; | ||
| 3315 | |||
| 3316 | if (dev_priv->mm.phys_objs[id - 1] || !size) | ||
| 3317 | return 0; | ||
| 3318 | |||
| 3319 | phys_obj = drm_calloc(1, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER); | ||
| 3320 | if (!phys_obj) | ||
| 3321 | return -ENOMEM; | ||
| 3322 | |||
| 3323 | phys_obj->id = id; | ||
| 3324 | |||
| 3325 | phys_obj->handle = drm_pci_alloc(dev, size, 0, 0xffffffff); | ||
| 3326 | if (!phys_obj->handle) { | ||
| 3327 | ret = -ENOMEM; | ||
| 3328 | goto kfree_obj; | ||
| 3329 | } | ||
| 3330 | #ifdef CONFIG_X86 | ||
| 3331 | set_memory_wc((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE); | ||
| 3332 | #endif | ||
| 3333 | |||
| 3334 | dev_priv->mm.phys_objs[id - 1] = phys_obj; | ||
| 3335 | |||
| 3336 | return 0; | ||
| 3337 | kfree_obj: | ||
| 3338 | drm_free(phys_obj, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER); | ||
| 3339 | return ret; | ||
| 3340 | } | ||
| 3341 | |||
| 3342 | void i915_gem_free_phys_object(struct drm_device *dev, int id) | ||
| 3343 | { | ||
| 3344 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 3345 | struct drm_i915_gem_phys_object *phys_obj; | ||
| 3346 | |||
| 3347 | if (!dev_priv->mm.phys_objs[id - 1]) | ||
| 3348 | return; | ||
| 3349 | |||
| 3350 | phys_obj = dev_priv->mm.phys_objs[id - 1]; | ||
| 3351 | if (phys_obj->cur_obj) { | ||
| 3352 | i915_gem_detach_phys_object(dev, phys_obj->cur_obj); | ||
| 3353 | } | ||
| 3354 | |||
| 3355 | #ifdef CONFIG_X86 | ||
| 3356 | set_memory_wb((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE); | ||
| 3357 | #endif | ||
| 3358 | drm_pci_free(dev, phys_obj->handle); | ||
| 3359 | kfree(phys_obj); | ||
| 3360 | dev_priv->mm.phys_objs[id - 1] = NULL; | ||
| 3361 | } | ||
| 3362 | |||
| 3363 | void i915_gem_free_all_phys_object(struct drm_device *dev) | ||
| 3364 | { | ||
| 3365 | int i; | ||
| 3366 | |||
| 3367 | for (i = 0; i < I915_MAX_PHYS_OBJECT; i++) | ||
| 3368 | i915_gem_free_phys_object(dev, i); | ||
| 3369 | } | ||
| 3370 | |||
| 3371 | void i915_gem_detach_phys_object(struct drm_device *dev, | ||
| 3372 | struct drm_gem_object *obj) | ||
| 3373 | { | ||
| 3374 | struct drm_i915_gem_object *obj_priv; | ||
| 3375 | int i; | ||
| 3376 | int ret; | ||
| 3377 | int page_count; | ||
| 3378 | |||
| 3379 | obj_priv = obj->driver_private; | ||
| 3380 | if (!obj_priv->phys_obj) | ||
| 3381 | return; | ||
| 3382 | |||
| 3383 | ret = i915_gem_object_get_page_list(obj); | ||
| 3384 | if (ret) | ||
| 3385 | goto out; | ||
| 3386 | |||
| 3387 | page_count = obj->size / PAGE_SIZE; | ||
| 3388 | |||
| 3389 | for (i = 0; i < page_count; i++) { | ||
| 3390 | char *dst = kmap_atomic(obj_priv->page_list[i], KM_USER0); | ||
| 3391 | char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE); | ||
| 3392 | |||
| 3393 | memcpy(dst, src, PAGE_SIZE); | ||
| 3394 | kunmap_atomic(dst, KM_USER0); | ||
| 3395 | } | ||
| 3396 | drm_clflush_pages(obj_priv->page_list, page_count); | ||
| 3397 | drm_agp_chipset_flush(dev); | ||
| 3398 | out: | ||
| 3399 | obj_priv->phys_obj->cur_obj = NULL; | ||
| 3400 | obj_priv->phys_obj = NULL; | ||
| 3401 | } | ||
| 3402 | |||
| 3403 | int | ||
| 3404 | i915_gem_attach_phys_object(struct drm_device *dev, | ||
| 3405 | struct drm_gem_object *obj, int id) | ||
| 3406 | { | ||
| 3407 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 3408 | struct drm_i915_gem_object *obj_priv; | ||
| 3409 | int ret = 0; | ||
| 3410 | int page_count; | ||
| 3411 | int i; | ||
| 3412 | |||
| 3413 | if (id > I915_MAX_PHYS_OBJECT) | ||
| 3414 | return -EINVAL; | ||
| 3415 | |||
| 3416 | obj_priv = obj->driver_private; | ||
| 3417 | |||
| 3418 | if (obj_priv->phys_obj) { | ||
| 3419 | if (obj_priv->phys_obj->id == id) | ||
| 3420 | return 0; | ||
| 3421 | i915_gem_detach_phys_object(dev, obj); | ||
| 3422 | } | ||
| 3423 | |||
| 3424 | |||
| 3425 | /* create a new object */ | ||
| 3426 | if (!dev_priv->mm.phys_objs[id - 1]) { | ||
| 3427 | ret = i915_gem_init_phys_object(dev, id, | ||
| 3428 | obj->size); | ||
| 3429 | if (ret) { | ||
| 3430 | DRM_ERROR("failed to init phys object %d size: %d\n", id, obj->size); | ||
| 3431 | goto out; | ||
| 3432 | } | ||
| 3433 | } | ||
| 3434 | |||
| 3435 | /* bind to the object */ | ||
| 3436 | obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1]; | ||
| 3437 | obj_priv->phys_obj->cur_obj = obj; | ||
| 3438 | |||
| 3439 | ret = i915_gem_object_get_page_list(obj); | ||
| 3440 | if (ret) { | ||
| 3441 | DRM_ERROR("failed to get page list\n"); | ||
| 3442 | goto out; | ||
| 3443 | } | ||
| 3444 | |||
| 3445 | page_count = obj->size / PAGE_SIZE; | ||
| 3446 | |||
| 3447 | for (i = 0; i < page_count; i++) { | ||
| 3448 | char *src = kmap_atomic(obj_priv->page_list[i], KM_USER0); | ||
| 3449 | char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE); | ||
| 3450 | |||
| 3451 | memcpy(dst, src, PAGE_SIZE); | ||
| 3452 | kunmap_atomic(src, KM_USER0); | ||
| 3453 | } | ||
| 3454 | |||
| 3455 | return 0; | ||
| 3456 | out: | ||
| 3457 | return ret; | ||
| 3458 | } | ||
| 3459 | |||
| 3460 | static int | ||
| 3461 | i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, | ||
| 3462 | struct drm_i915_gem_pwrite *args, | ||
| 3463 | struct drm_file *file_priv) | ||
| 3464 | { | ||
| 3465 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | ||
| 3466 | void *obj_addr; | ||
| 3467 | int ret; | ||
| 3468 | char __user *user_data; | ||
| 3469 | |||
| 3470 | user_data = (char __user *) (uintptr_t) args->data_ptr; | ||
| 3471 | obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset; | ||
| 3472 | |||
| 3473 | DRM_ERROR("obj_addr %p, %lld\n", obj_addr, args->size); | ||
| 3474 | ret = copy_from_user(obj_addr, user_data, args->size); | ||
| 3475 | if (ret) | ||
| 3476 | return -EFAULT; | ||
| 3477 | |||
| 3478 | drm_agp_chipset_flush(dev); | ||
| 3479 | return 0; | ||
| 3480 | } | ||
