diff options
| -rw-r--r-- | drivers/char/drm/drm.h | 33 | ||||
| -rw-r--r-- | drivers/char/drm/drmP.h | 23 | ||||
| -rw-r--r-- | drivers/char/drm/drm_bufs.c | 10 | ||||
| -rw-r--r-- | drivers/char/drm/drm_core.h | 8 | ||||
| -rw-r--r-- | drivers/char/drm/drm_drawable.c | 294 | ||||
| -rw-r--r-- | drivers/char/drm/drm_drv.c | 14 | ||||
| -rw-r--r-- | drivers/char/drm/drm_irq.c | 155 | ||||
| -rw-r--r-- | drivers/char/drm/drm_lock.c | 11 | ||||
| -rw-r--r-- | drivers/char/drm/drm_stub.c | 2 | ||||
| -rw-r--r-- | drivers/char/drm/drm_vm.c | 16 | ||||
| -rw-r--r-- | drivers/char/drm/i915_dma.c | 2 | ||||
| -rw-r--r-- | drivers/char/drm/i915_drm.h | 19 | ||||
| -rw-r--r-- | drivers/char/drm/i915_drv.c | 4 | ||||
| -rw-r--r-- | drivers/char/drm/i915_drv.h | 22 | ||||
| -rw-r--r-- | drivers/char/drm/i915_irq.c | 265 |
15 files changed, 821 insertions, 57 deletions
diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 5642ac43e0f5..8db9041e306c 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h | |||
| @@ -117,6 +117,14 @@ typedef struct drm_clip_rect { | |||
| 117 | } drm_clip_rect_t; | 117 | } drm_clip_rect_t; |
| 118 | 118 | ||
| 119 | /** | 119 | /** |
| 120 | * Drawable information. | ||
| 121 | */ | ||
| 122 | typedef struct drm_drawable_info { | ||
| 123 | unsigned int num_rects; | ||
| 124 | drm_clip_rect_t *rects; | ||
| 125 | } drm_drawable_info_t; | ||
| 126 | |||
| 127 | /** | ||
| 120 | * Texture region, | 128 | * Texture region, |
| 121 | */ | 129 | */ |
| 122 | typedef struct drm_tex_region { | 130 | typedef struct drm_tex_region { |
| @@ -348,7 +356,8 @@ typedef struct drm_buf_desc { | |||
| 348 | _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ | 356 | _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ |
| 349 | _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ | 357 | _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ |
| 350 | _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ | 358 | _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ |
| 351 | _DRM_FB_BUFFER = 0x08 /**< Buffer is in frame buffer */ | 359 | _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ |
| 360 | _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ | ||
| 352 | } flags; | 361 | } flags; |
| 353 | unsigned long agp_start; /**< | 362 | unsigned long agp_start; /**< |
| 354 | * Start address of where the AGP buffers are | 363 | * Start address of where the AGP buffers are |
| @@ -444,6 +453,20 @@ typedef struct drm_draw { | |||
| 444 | } drm_draw_t; | 453 | } drm_draw_t; |
| 445 | 454 | ||
| 446 | /** | 455 | /** |
| 456 | * DRM_IOCTL_UPDATE_DRAW ioctl argument type. | ||
| 457 | */ | ||
| 458 | typedef enum { | ||
| 459 | DRM_DRAWABLE_CLIPRECTS, | ||
| 460 | } drm_drawable_info_type_t; | ||
| 461 | |||
| 462 | typedef struct drm_update_draw { | ||
| 463 | drm_drawable_t handle; | ||
| 464 | unsigned int type; | ||
| 465 | unsigned int num; | ||
| 466 | unsigned long long data; | ||
| 467 | } drm_update_draw_t; | ||
| 468 | |||
| 469 | /** | ||
| 447 | * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. | 470 | * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. |
| 448 | */ | 471 | */ |
| 449 | typedef struct drm_auth { | 472 | typedef struct drm_auth { |
| @@ -465,10 +488,14 @@ typedef struct drm_irq_busid { | |||
| 465 | typedef enum { | 488 | typedef enum { |
| 466 | _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ | 489 | _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ |
| 467 | _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ | 490 | _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ |
| 491 | _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ | ||
| 492 | _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ | ||
| 468 | _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ | 493 | _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ |
| 469 | } drm_vblank_seq_type_t; | 494 | } drm_vblank_seq_type_t; |
| 470 | 495 | ||
| 471 | #define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL | 496 | #define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) |
| 497 | #define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \ | ||
| 498 | _DRM_VBLANK_NEXTONMISS) | ||
| 472 | 499 | ||
| 473 | struct drm_wait_vblank_request { | 500 | struct drm_wait_vblank_request { |
| 474 | drm_vblank_seq_type_t type; | 501 | drm_vblank_seq_type_t type; |
| @@ -623,6 +650,8 @@ typedef struct drm_set_version { | |||
| 623 | 650 | ||
| 624 | #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t) | 651 | #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t) |
| 625 | 652 | ||
| 653 | #define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, drm_update_draw_t) | ||
| 654 | |||
| 626 | /** | 655 | /** |
| 627 | * Device specific ioctls should only be in their respective headers | 656 | * Device specific ioctls should only be in their respective headers |
| 628 | * The device specific ioctl range is from 0x40 to 0x79. | 657 | * The device specific ioctl range is from 0x40 to 0x79. |
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 7690a59ace04..0bbb04f2390f 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h | |||
| @@ -97,6 +97,7 @@ | |||
| 97 | #define DRIVER_IRQ_VBL 0x100 | 97 | #define DRIVER_IRQ_VBL 0x100 |
| 98 | #define DRIVER_DMA_QUEUE 0x200 | 98 | #define DRIVER_DMA_QUEUE 0x200 |
| 99 | #define DRIVER_FB_DMA 0x400 | 99 | #define DRIVER_FB_DMA 0x400 |
| 100 | #define DRIVER_IRQ_VBL2 0x800 | ||
| 100 | 101 | ||
| 101 | /***********************************************************************/ | 102 | /***********************************************************************/ |
| 102 | /** \name Begin the DRM... */ | 103 | /** \name Begin the DRM... */ |
| @@ -430,7 +431,8 @@ typedef struct drm_device_dma { | |||
| 430 | enum { | 431 | enum { |
| 431 | _DRM_DMA_USE_AGP = 0x01, | 432 | _DRM_DMA_USE_AGP = 0x01, |
| 432 | _DRM_DMA_USE_SG = 0x02, | 433 | _DRM_DMA_USE_SG = 0x02, |
| 433 | _DRM_DMA_USE_FB = 0x04 | 434 | _DRM_DMA_USE_FB = 0x04, |
| 435 | _DRM_DMA_USE_PCI_RO = 0x08 | ||
| 434 | } flags; | 436 | } flags; |
| 435 | 437 | ||
| 436 | } drm_device_dma_t; | 438 | } drm_device_dma_t; |
| @@ -562,6 +564,7 @@ struct drm_driver { | |||
| 562 | void (*kernel_context_switch_unlock) (struct drm_device * dev, | 564 | void (*kernel_context_switch_unlock) (struct drm_device * dev, |
| 563 | drm_lock_t *lock); | 565 | drm_lock_t *lock); |
| 564 | int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); | 566 | int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); |
| 567 | int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence); | ||
| 565 | int (*dri_library_name) (struct drm_device *dev, char *buf); | 568 | int (*dri_library_name) (struct drm_device *dev, char *buf); |
| 566 | 569 | ||
| 567 | /** | 570 | /** |
| @@ -708,9 +711,13 @@ typedef struct drm_device { | |||
| 708 | 711 | ||
| 709 | wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ | 712 | wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ |
| 710 | atomic_t vbl_received; | 713 | atomic_t vbl_received; |
| 714 | atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */ | ||
| 711 | spinlock_t vbl_lock; | 715 | spinlock_t vbl_lock; |
| 712 | drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ | 716 | drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ |
| 717 | drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */ | ||
| 713 | unsigned int vbl_pending; | 718 | unsigned int vbl_pending; |
| 719 | spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ | ||
| 720 | void (*locked_tasklet_func)(struct drm_device *dev); | ||
| 714 | 721 | ||
| 715 | /*@} */ | 722 | /*@} */ |
| 716 | cycles_t ctx_start; | 723 | cycles_t ctx_start; |
| @@ -738,6 +745,15 @@ typedef struct drm_device { | |||
| 738 | drm_local_map_t *agp_buffer_map; | 745 | drm_local_map_t *agp_buffer_map; |
| 739 | unsigned int agp_buffer_token; | 746 | unsigned int agp_buffer_token; |
| 740 | drm_head_t primary; /**< primary screen head */ | 747 | drm_head_t primary; /**< primary screen head */ |
| 748 | |||
| 749 | /** \name Drawable information */ | ||
| 750 | /*@{ */ | ||
| 751 | spinlock_t drw_lock; | ||
| 752 | unsigned int drw_bitfield_length; | ||
| 753 | u32 *drw_bitfield; | ||
| 754 | unsigned int drw_info_length; | ||
| 755 | drm_drawable_info_t **drw_info; | ||
| 756 | /*@} */ | ||
| 741 | } drm_device_t; | 757 | } drm_device_t; |
| 742 | 758 | ||
| 743 | static __inline__ int drm_core_check_feature(struct drm_device *dev, | 759 | static __inline__ int drm_core_check_feature(struct drm_device *dev, |
| @@ -885,6 +901,10 @@ extern int drm_adddraw(struct inode *inode, struct file *filp, | |||
| 885 | unsigned int cmd, unsigned long arg); | 901 | unsigned int cmd, unsigned long arg); |
| 886 | extern int drm_rmdraw(struct inode *inode, struct file *filp, | 902 | extern int drm_rmdraw(struct inode *inode, struct file *filp, |
| 887 | unsigned int cmd, unsigned long arg); | 903 | unsigned int cmd, unsigned long arg); |
| 904 | extern int drm_update_drawable_info(struct inode *inode, struct file *filp, | ||
| 905 | unsigned int cmd, unsigned long arg); | ||
| 906 | extern drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, | ||
| 907 | drm_drawable_t id); | ||
| 888 | 908 | ||
| 889 | /* Authentication IOCTL support (drm_auth.h) */ | 909 | /* Authentication IOCTL support (drm_auth.h) */ |
| 890 | extern int drm_getmagic(struct inode *inode, struct file *filp, | 910 | extern int drm_getmagic(struct inode *inode, struct file *filp, |
| @@ -949,6 +969,7 @@ extern int drm_wait_vblank(struct inode *inode, struct file *filp, | |||
| 949 | unsigned int cmd, unsigned long arg); | 969 | unsigned int cmd, unsigned long arg); |
| 950 | extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); | 970 | extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); |
| 951 | extern void drm_vbl_send_signals(drm_device_t * dev); | 971 | extern void drm_vbl_send_signals(drm_device_t * dev); |
| 972 | extern void drm_locked_tasklet(drm_device_t *dev, void(*func)(drm_device_t*)); | ||
| 952 | 973 | ||
| 953 | /* AGP/GART support (drm_agpsupport.h) */ | 974 | /* AGP/GART support (drm_agpsupport.h) */ |
| 954 | extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); | 975 | extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); |
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index 6eafff13dab6..9f65f5697ba8 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c | |||
| @@ -887,6 +887,9 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) | |||
| 887 | request->count = entry->buf_count; | 887 | request->count = entry->buf_count; |
| 888 | request->size = size; | 888 | request->size = size; |
| 889 | 889 | ||
| 890 | if (request->flags & _DRM_PCI_BUFFER_RO) | ||
| 891 | dma->flags = _DRM_DMA_USE_PCI_RO; | ||
| 892 | |||
| 890 | atomic_dec(&dev->buf_alloc); | 893 | atomic_dec(&dev->buf_alloc); |
| 891 | return 0; | 894 | return 0; |
| 892 | 895 | ||
| @@ -1471,9 +1474,10 @@ int drm_freebufs(struct inode *inode, struct file *filp, | |||
| 1471 | * \param arg pointer to a drm_buf_map structure. | 1474 | * \param arg pointer to a drm_buf_map structure. |
| 1472 | * \return zero on success or a negative number on failure. | 1475 | * \return zero on success or a negative number on failure. |
| 1473 | * | 1476 | * |
| 1474 | * Maps the AGP or SG buffer region with do_mmap(), and copies information | 1477 | * Maps the AGP, SG or PCI buffer region with do_mmap(), and copies information |
| 1475 | * about each buffer into user space. The PCI buffers are already mapped on the | 1478 | * about each buffer into user space. For PCI buffers, it calls do_mmap() with |
| 1476 | * addbufs_pci() call. | 1479 | * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls |
| 1480 | * drm_mmap_dma(). | ||
| 1477 | */ | 1481 | */ |
| 1478 | int drm_mapbufs(struct inode *inode, struct file *filp, | 1482 | int drm_mapbufs(struct inode *inode, struct file *filp, |
| 1479 | unsigned int cmd, unsigned long arg) | 1483 | unsigned int cmd, unsigned long arg) |
diff --git a/drivers/char/drm/drm_core.h b/drivers/char/drm/drm_core.h index f4f9db6c7ed4..316739036079 100644 --- a/drivers/char/drm/drm_core.h +++ b/drivers/char/drm/drm_core.h | |||
| @@ -24,11 +24,11 @@ | |||
| 24 | 24 | ||
| 25 | #define CORE_NAME "drm" | 25 | #define CORE_NAME "drm" |
| 26 | #define CORE_DESC "DRM shared core routines" | 26 | #define CORE_DESC "DRM shared core routines" |
| 27 | #define CORE_DATE "20051102" | 27 | #define CORE_DATE "20060810" |
| 28 | 28 | ||
| 29 | #define DRM_IF_MAJOR 1 | 29 | #define DRM_IF_MAJOR 1 |
| 30 | #define DRM_IF_MINOR 2 | 30 | #define DRM_IF_MINOR 3 |
| 31 | 31 | ||
| 32 | #define CORE_MAJOR 1 | 32 | #define CORE_MAJOR 1 |
| 33 | #define CORE_MINOR 0 | 33 | #define CORE_MINOR 1 |
| 34 | #define CORE_PATCHLEVEL 1 | 34 | #define CORE_PATCHLEVEL 0 |
diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c index 7857453c4f48..de37d5f74563 100644 --- a/drivers/char/drm/drm_drawable.c +++ b/drivers/char/drm/drm_drawable.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * | 4 | * |
| 5 | * \author Rickard E. (Rik) Faith <faith@valinux.com> | 5 | * \author Rickard E. (Rik) Faith <faith@valinux.com> |
| 6 | * \author Gareth Hughes <gareth@valinux.com> | 6 | * \author Gareth Hughes <gareth@valinux.com> |
| 7 | * \author Michel Dänzer <michel@tungstengraphics.com> | ||
| 7 | */ | 8 | */ |
| 8 | 9 | ||
| 9 | /* | 10 | /* |
| @@ -11,6 +12,7 @@ | |||
| 11 | * | 12 | * |
| 12 | * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. | 13 | * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. |
| 13 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. | 14 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
| 15 | * Copyright 2006 Tungsten Graphics, Inc., Bismarck, North Dakota. | ||
| 14 | * All Rights Reserved. | 16 | * All Rights Reserved. |
| 15 | * | 17 | * |
| 16 | * Permission is hereby granted, free of charge, to any person obtaining a | 18 | * Permission is hereby granted, free of charge, to any person obtaining a |
| @@ -35,22 +37,294 @@ | |||
| 35 | 37 | ||
| 36 | #include "drmP.h" | 38 | #include "drmP.h" |
| 37 | 39 | ||
| 38 | /** No-op. */ | 40 | /** |
| 39 | int drm_adddraw(struct inode *inode, struct file *filp, | 41 | * Allocate drawable ID and memory to store information about it. |
| 40 | unsigned int cmd, unsigned long arg) | 42 | */ |
| 43 | int drm_adddraw(DRM_IOCTL_ARGS) | ||
| 41 | { | 44 | { |
| 45 | DRM_DEVICE; | ||
| 46 | unsigned long irqflags; | ||
| 47 | int i, j; | ||
| 48 | u32 *bitfield = dev->drw_bitfield; | ||
| 49 | unsigned int bitfield_length = dev->drw_bitfield_length; | ||
| 50 | drm_drawable_info_t **info = dev->drw_info; | ||
| 51 | unsigned int info_length = dev->drw_info_length; | ||
| 42 | drm_draw_t draw; | 52 | drm_draw_t draw; |
| 43 | 53 | ||
| 44 | draw.handle = 0; /* NOOP */ | 54 | for (i = 0, j = 0; i < bitfield_length; i++) { |
| 55 | if (bitfield[i] == ~0) | ||
| 56 | continue; | ||
| 57 | |||
| 58 | for (; j < 8 * sizeof(*bitfield); j++) | ||
| 59 | if (!(bitfield[i] & (1 << j))) | ||
| 60 | goto done; | ||
| 61 | } | ||
| 62 | done: | ||
| 63 | |||
| 64 | if (i == bitfield_length) { | ||
| 65 | bitfield_length++; | ||
| 66 | |||
| 67 | bitfield = drm_alloc(bitfield_length * sizeof(*bitfield), | ||
| 68 | DRM_MEM_BUFS); | ||
| 69 | |||
| 70 | if (!bitfield) { | ||
| 71 | DRM_ERROR("Failed to allocate new drawable bitfield\n"); | ||
| 72 | return DRM_ERR(ENOMEM); | ||
| 73 | } | ||
| 74 | |||
| 75 | if (8 * sizeof(*bitfield) * bitfield_length > info_length) { | ||
| 76 | info_length += 8 * sizeof(*bitfield); | ||
| 77 | |||
| 78 | info = drm_alloc(info_length * sizeof(*info), | ||
| 79 | DRM_MEM_BUFS); | ||
| 80 | |||
| 81 | if (!info) { | ||
| 82 | DRM_ERROR("Failed to allocate new drawable info" | ||
| 83 | " array\n"); | ||
| 84 | |||
| 85 | drm_free(bitfield, | ||
| 86 | bitfield_length * sizeof(*bitfield), | ||
| 87 | DRM_MEM_BUFS); | ||
| 88 | return DRM_ERR(ENOMEM); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | bitfield[i] = 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | draw.handle = i * 8 * sizeof(*bitfield) + j + 1; | ||
| 45 | DRM_DEBUG("%d\n", draw.handle); | 96 | DRM_DEBUG("%d\n", draw.handle); |
| 46 | if (copy_to_user((drm_draw_t __user *) arg, &draw, sizeof(draw))) | 97 | |
| 47 | return -EFAULT; | 98 | spin_lock_irqsave(&dev->drw_lock, irqflags); |
| 99 | |||
| 100 | bitfield[i] |= 1 << j; | ||
| 101 | info[draw.handle - 1] = NULL; | ||
| 102 | |||
| 103 | if (bitfield != dev->drw_bitfield) { | ||
| 104 | memcpy(bitfield, dev->drw_bitfield, dev->drw_bitfield_length * | ||
| 105 | sizeof(*bitfield)); | ||
| 106 | drm_free(dev->drw_bitfield, sizeof(*bitfield) * | ||
| 107 | dev->drw_bitfield_length, DRM_MEM_BUFS); | ||
| 108 | dev->drw_bitfield = bitfield; | ||
| 109 | dev->drw_bitfield_length = bitfield_length; | ||
| 110 | } | ||
| 111 | |||
| 112 | if (info != dev->drw_info) { | ||
| 113 | memcpy(info, dev->drw_info, dev->drw_info_length * | ||
| 114 | sizeof(*info)); | ||
| 115 | drm_free(dev->drw_info, sizeof(*info) * dev->drw_info_length, | ||
| 116 | DRM_MEM_BUFS); | ||
| 117 | dev->drw_info = info; | ||
| 118 | dev->drw_info_length = info_length; | ||
| 119 | } | ||
| 120 | |||
| 121 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 122 | |||
| 123 | DRM_COPY_TO_USER_IOCTL((drm_draw_t __user *)data, draw, sizeof(draw)); | ||
| 124 | |||
| 48 | return 0; | 125 | return 0; |
| 49 | } | 126 | } |
| 50 | 127 | ||
| 51 | /** No-op. */ | 128 | /** |
| 52 | int drm_rmdraw(struct inode *inode, struct file *filp, | 129 | * Free drawable ID and memory to store information about it. |
| 53 | unsigned int cmd, unsigned long arg) | 130 | */ |
| 131 | int drm_rmdraw(DRM_IOCTL_ARGS) | ||
| 54 | { | 132 | { |
| 55 | return 0; /* NOOP */ | 133 | DRM_DEVICE; |
| 134 | drm_draw_t draw; | ||
| 135 | int id, idx; | ||
| 136 | unsigned int shift; | ||
| 137 | unsigned long irqflags; | ||
| 138 | u32 *bitfield = dev->drw_bitfield; | ||
| 139 | unsigned int bitfield_length = dev->drw_bitfield_length; | ||
| 140 | drm_drawable_info_t **info = dev->drw_info; | ||
| 141 | unsigned int info_length = dev->drw_info_length; | ||
| 142 | |||
| 143 | DRM_COPY_FROM_USER_IOCTL(draw, (drm_draw_t __user *) data, | ||
| 144 | sizeof(draw)); | ||
| 145 | |||
| 146 | id = draw.handle - 1; | ||
| 147 | idx = id / (8 * sizeof(*bitfield)); | ||
| 148 | shift = id % (8 * sizeof(*bitfield)); | ||
| 149 | |||
| 150 | if (idx < 0 || idx >= bitfield_length || | ||
| 151 | !(bitfield[idx] & (1 << shift))) { | ||
| 152 | DRM_DEBUG("No such drawable %d\n", draw.handle); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | spin_lock_irqsave(&dev->drw_lock, irqflags); | ||
| 157 | |||
| 158 | bitfield[idx] &= ~(1 << shift); | ||
| 159 | |||
| 160 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 161 | |||
| 162 | if (info[id]) { | ||
| 163 | drm_free(info[id]->rects, info[id]->num_rects * | ||
| 164 | sizeof(drm_clip_rect_t), DRM_MEM_BUFS); | ||
| 165 | drm_free(info[id], sizeof(**info), DRM_MEM_BUFS); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Can we shrink the arrays? */ | ||
| 169 | if (idx == bitfield_length - 1) { | ||
| 170 | while (idx >= 0 && !bitfield[idx]) | ||
| 171 | --idx; | ||
| 172 | |||
| 173 | bitfield_length = idx + 1; | ||
| 174 | |||
| 175 | if (idx != id / (8 * sizeof(*bitfield))) | ||
| 176 | bitfield = drm_alloc(bitfield_length * | ||
| 177 | sizeof(*bitfield), DRM_MEM_BUFS); | ||
| 178 | |||
| 179 | if (!bitfield && bitfield_length) { | ||
| 180 | bitfield = dev->drw_bitfield; | ||
| 181 | bitfield_length = dev->drw_bitfield_length; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | if (bitfield != dev->drw_bitfield) { | ||
| 186 | info_length = 8 * sizeof(*bitfield) * bitfield_length; | ||
| 187 | |||
| 188 | info = drm_alloc(info_length * sizeof(*info), DRM_MEM_BUFS); | ||
| 189 | |||
| 190 | if (!info && info_length) { | ||
| 191 | info = dev->drw_info; | ||
| 192 | info_length = dev->drw_info_length; | ||
| 193 | } | ||
| 194 | |||
| 195 | spin_lock_irqsave(&dev->drw_lock, irqflags); | ||
| 196 | |||
| 197 | memcpy(bitfield, dev->drw_bitfield, bitfield_length * | ||
| 198 | sizeof(*bitfield)); | ||
| 199 | drm_free(dev->drw_bitfield, sizeof(*bitfield) * | ||
| 200 | dev->drw_bitfield_length, DRM_MEM_BUFS); | ||
| 201 | dev->drw_bitfield = bitfield; | ||
| 202 | dev->drw_bitfield_length = bitfield_length; | ||
| 203 | |||
| 204 | if (info != dev->drw_info) { | ||
| 205 | memcpy(info, dev->drw_info, info_length * | ||
| 206 | sizeof(*info)); | ||
| 207 | drm_free(dev->drw_info, sizeof(*info) * | ||
| 208 | dev->drw_info_length, DRM_MEM_BUFS); | ||
| 209 | dev->drw_info = info; | ||
| 210 | dev->drw_info_length = info_length; | ||
| 211 | } | ||
| 212 | |||
| 213 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 214 | } | ||
| 215 | |||
| 216 | DRM_DEBUG("%d\n", draw.handle); | ||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | int drm_update_drawable_info(DRM_IOCTL_ARGS) { | ||
| 221 | DRM_DEVICE; | ||
| 222 | drm_update_draw_t update; | ||
| 223 | unsigned int id, idx, shift; | ||
| 224 | u32 *bitfield = dev->drw_bitfield; | ||
| 225 | unsigned long irqflags, bitfield_length = dev->drw_bitfield_length; | ||
| 226 | drm_drawable_info_t *info; | ||
| 227 | drm_clip_rect_t *rects; | ||
| 228 | int err; | ||
| 229 | |||
| 230 | DRM_COPY_FROM_USER_IOCTL(update, (drm_update_draw_t __user *) data, | ||
| 231 | sizeof(update)); | ||
| 232 | |||
| 233 | id = update.handle - 1; | ||
| 234 | idx = id / (8 * sizeof(*bitfield)); | ||
| 235 | shift = id % (8 * sizeof(*bitfield)); | ||
| 236 | |||
| 237 | if (idx < 0 || idx >= bitfield_length || | ||
| 238 | !(bitfield[idx] & (1 << shift))) { | ||
| 239 | DRM_ERROR("No such drawable %d\n", update.handle); | ||
| 240 | return DRM_ERR(EINVAL); | ||
| 241 | } | ||
| 242 | |||
| 243 | info = dev->drw_info[id]; | ||
| 244 | |||
| 245 | if (!info) { | ||
| 246 | info = drm_calloc(1, sizeof(drm_drawable_info_t), DRM_MEM_BUFS); | ||
| 247 | |||
| 248 | if (!info) { | ||
| 249 | DRM_ERROR("Failed to allocate drawable info memory\n"); | ||
| 250 | return DRM_ERR(ENOMEM); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | switch (update.type) { | ||
| 255 | case DRM_DRAWABLE_CLIPRECTS: | ||
| 256 | if (update.num != info->num_rects) { | ||
| 257 | rects = drm_alloc(update.num * sizeof(drm_clip_rect_t), | ||
| 258 | DRM_MEM_BUFS); | ||
| 259 | } else | ||
| 260 | rects = info->rects; | ||
| 261 | |||
| 262 | if (update.num && !rects) { | ||
| 263 | DRM_ERROR("Failed to allocate cliprect memory\n"); | ||
| 264 | err = DRM_ERR(ENOMEM); | ||
| 265 | goto error; | ||
| 266 | } | ||
| 267 | |||
| 268 | if (update.num && DRM_COPY_FROM_USER(rects, | ||
| 269 | (drm_clip_rect_t __user *) | ||
| 270 | (unsigned long)update.data, | ||
| 271 | update.num * | ||
| 272 | sizeof(*rects))) { | ||
| 273 | DRM_ERROR("Failed to copy cliprects from userspace\n"); | ||
| 274 | err = DRM_ERR(EFAULT); | ||
| 275 | goto error; | ||
| 276 | } | ||
| 277 | |||
| 278 | spin_lock_irqsave(&dev->drw_lock, irqflags); | ||
| 279 | |||
| 280 | if (rects != info->rects) { | ||
| 281 | drm_free(info->rects, info->num_rects * | ||
| 282 | sizeof(drm_clip_rect_t), DRM_MEM_BUFS); | ||
| 283 | } | ||
| 284 | |||
| 285 | info->rects = rects; | ||
| 286 | info->num_rects = update.num; | ||
| 287 | dev->drw_info[id] = info; | ||
| 288 | |||
| 289 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 290 | |||
| 291 | DRM_DEBUG("Updated %d cliprects for drawable %d\n", | ||
| 292 | info->num_rects, id); | ||
| 293 | break; | ||
| 294 | default: | ||
| 295 | DRM_ERROR("Invalid update type %d\n", update.type); | ||
| 296 | return DRM_ERR(EINVAL); | ||
| 297 | } | ||
| 298 | |||
| 299 | return 0; | ||
| 300 | |||
| 301 | error: | ||
| 302 | if (!dev->drw_info[id]) | ||
| 303 | drm_free(info, sizeof(*info), DRM_MEM_BUFS); | ||
| 304 | else if (rects != dev->drw_info[id]->rects) | ||
| 305 | drm_free(rects, update.num * | ||
| 306 | sizeof(drm_clip_rect_t), DRM_MEM_BUFS); | ||
| 307 | |||
| 308 | return err; | ||
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Caller must hold the drawable spinlock! | ||
| 313 | */ | ||
| 314 | drm_drawable_info_t *drm_get_drawable_info(drm_device_t *dev, drm_drawable_t id) { | ||
| 315 | u32 *bitfield = dev->drw_bitfield; | ||
| 316 | unsigned int idx, shift; | ||
| 317 | |||
| 318 | id--; | ||
| 319 | idx = id / (8 * sizeof(*bitfield)); | ||
| 320 | shift = id % (8 * sizeof(*bitfield)); | ||
| 321 | |||
| 322 | if (idx < 0 || idx >= dev->drw_bitfield_length || | ||
| 323 | !(bitfield[idx] & (1 << shift))) { | ||
| 324 | DRM_DEBUG("No such drawable %d\n", id); | ||
| 325 | return NULL; | ||
| 326 | } | ||
| 327 | |||
| 328 | return dev->drw_info[id]; | ||
| 56 | } | 329 | } |
| 330 | EXPORT_SYMBOL(drm_get_drawable_info); | ||
diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c index b366c5b1bd16..a70af0de4453 100644 --- a/drivers/char/drm/drm_drv.c +++ b/drivers/char/drm/drm_drv.c | |||
| @@ -116,6 +116,8 @@ static drm_ioctl_desc_t drm_ioctls[] = { | |||
| 116 | [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = {drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, | 116 | [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = {drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, |
| 117 | 117 | ||
| 118 | [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0}, | 118 | [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0}, |
| 119 | |||
| 120 | [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, | ||
| 119 | }; | 121 | }; |
| 120 | 122 | ||
| 121 | #define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) | 123 | #define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) |
| @@ -151,6 +153,18 @@ int drm_lastclose(drm_device_t * dev) | |||
| 151 | if (dev->irq_enabled) | 153 | if (dev->irq_enabled) |
| 152 | drm_irq_uninstall(dev); | 154 | drm_irq_uninstall(dev); |
| 153 | 155 | ||
| 156 | /* Free drawable information memory */ | ||
| 157 | for (i = 0; i < dev->drw_bitfield_length / sizeof(*dev->drw_bitfield); | ||
| 158 | i++) { | ||
| 159 | drm_drawable_info_t *info = drm_get_drawable_info(dev, i); | ||
| 160 | |||
| 161 | if (info) { | ||
| 162 | drm_free(info->rects, info->num_rects * | ||
| 163 | sizeof(drm_clip_rect_t), DRM_MEM_BUFS); | ||
| 164 | drm_free(info, sizeof(*info), DRM_MEM_BUFS); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 154 | mutex_lock(&dev->struct_mutex); | 168 | mutex_lock(&dev->struct_mutex); |
| 155 | del_timer(&dev->timer); | 169 | del_timer(&dev->timer); |
| 156 | 170 | ||
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 4553a3a1e496..9d00c51fe2c4 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c | |||
| @@ -121,6 +121,7 @@ static int drm_irq_install(drm_device_t * dev) | |||
| 121 | spin_lock_init(&dev->vbl_lock); | 121 | spin_lock_init(&dev->vbl_lock); |
| 122 | 122 | ||
| 123 | INIT_LIST_HEAD(&dev->vbl_sigs.head); | 123 | INIT_LIST_HEAD(&dev->vbl_sigs.head); |
| 124 | INIT_LIST_HEAD(&dev->vbl_sigs2.head); | ||
| 124 | 125 | ||
| 125 | dev->vbl_pending = 0; | 126 | dev->vbl_pending = 0; |
| 126 | } | 127 | } |
| @@ -175,6 +176,8 @@ int drm_irq_uninstall(drm_device_t * dev) | |||
| 175 | 176 | ||
| 176 | free_irq(dev->irq, dev); | 177 | free_irq(dev->irq, dev); |
| 177 | 178 | ||
| 179 | dev->locked_tasklet_func = NULL; | ||
| 180 | |||
| 178 | return 0; | 181 | return 0; |
| 179 | } | 182 | } |
| 180 | 183 | ||
| @@ -247,10 +250,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) | |||
| 247 | drm_wait_vblank_t vblwait; | 250 | drm_wait_vblank_t vblwait; |
| 248 | struct timeval now; | 251 | struct timeval now; |
| 249 | int ret = 0; | 252 | int ret = 0; |
| 250 | unsigned int flags; | 253 | unsigned int flags, seq; |
| 251 | |||
| 252 | if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL)) | ||
| 253 | return -EINVAL; | ||
| 254 | 254 | ||
| 255 | if (!dev->irq) | 255 | if (!dev->irq) |
| 256 | return -EINVAL; | 256 | return -EINVAL; |
| @@ -258,9 +258,26 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) | |||
| 258 | if (copy_from_user(&vblwait, argp, sizeof(vblwait))) | 258 | if (copy_from_user(&vblwait, argp, sizeof(vblwait))) |
| 259 | return -EFAULT; | 259 | return -EFAULT; |
| 260 | 260 | ||
| 261 | switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) { | 261 | if (vblwait.request.type & |
| 262 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { | ||
| 263 | DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", | ||
| 264 | vblwait.request.type, | ||
| 265 | (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)); | ||
| 266 | return -EINVAL; | ||
| 267 | } | ||
| 268 | |||
| 269 | flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; | ||
| 270 | |||
| 271 | if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? | ||
| 272 | DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) | ||
| 273 | return -EINVAL; | ||
| 274 | |||
| 275 | seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 | ||
| 276 | : &dev->vbl_received); | ||
| 277 | |||
| 278 | switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { | ||
| 262 | case _DRM_VBLANK_RELATIVE: | 279 | case _DRM_VBLANK_RELATIVE: |
| 263 | vblwait.request.sequence += atomic_read(&dev->vbl_received); | 280 | vblwait.request.sequence += seq; |
| 264 | vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; | 281 | vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; |
| 265 | case _DRM_VBLANK_ABSOLUTE: | 282 | case _DRM_VBLANK_ABSOLUTE: |
| 266 | break; | 283 | break; |
| @@ -268,26 +285,30 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) | |||
| 268 | return -EINVAL; | 285 | return -EINVAL; |
| 269 | } | 286 | } |
| 270 | 287 | ||
| 271 | flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; | 288 | if ((flags & _DRM_VBLANK_NEXTONMISS) && |
| 289 | (seq - vblwait.request.sequence) <= (1<<23)) { | ||
| 290 | vblwait.request.sequence = seq + 1; | ||
| 291 | } | ||
| 272 | 292 | ||
| 273 | if (flags & _DRM_VBLANK_SIGNAL) { | 293 | if (flags & _DRM_VBLANK_SIGNAL) { |
| 274 | unsigned long irqflags; | 294 | unsigned long irqflags; |
| 295 | drm_vbl_sig_t *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) | ||
| 296 | ? &dev->vbl_sigs2 : &dev->vbl_sigs; | ||
| 275 | drm_vbl_sig_t *vbl_sig; | 297 | drm_vbl_sig_t *vbl_sig; |
| 276 | 298 | ||
| 277 | vblwait.reply.sequence = atomic_read(&dev->vbl_received); | ||
| 278 | |||
| 279 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 299 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
| 280 | 300 | ||
| 281 | /* Check if this task has already scheduled the same signal | 301 | /* Check if this task has already scheduled the same signal |
| 282 | * for the same vblank sequence number; nothing to be done in | 302 | * for the same vblank sequence number; nothing to be done in |
| 283 | * that case | 303 | * that case |
| 284 | */ | 304 | */ |
| 285 | list_for_each_entry(vbl_sig, &dev->vbl_sigs.head, head) { | 305 | list_for_each_entry(vbl_sig, &vbl_sigs->head, head) { |
| 286 | if (vbl_sig->sequence == vblwait.request.sequence | 306 | if (vbl_sig->sequence == vblwait.request.sequence |
| 287 | && vbl_sig->info.si_signo == vblwait.request.signal | 307 | && vbl_sig->info.si_signo == vblwait.request.signal |
| 288 | && vbl_sig->task == current) { | 308 | && vbl_sig->task == current) { |
| 289 | spin_unlock_irqrestore(&dev->vbl_lock, | 309 | spin_unlock_irqrestore(&dev->vbl_lock, |
| 290 | irqflags); | 310 | irqflags); |
| 311 | vblwait.reply.sequence = seq; | ||
| 291 | goto done; | 312 | goto done; |
| 292 | } | 313 | } |
| 293 | } | 314 | } |
| @@ -315,11 +336,16 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) | |||
| 315 | 336 | ||
| 316 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 337 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
| 317 | 338 | ||
| 318 | list_add_tail((struct list_head *)vbl_sig, &dev->vbl_sigs.head); | 339 | list_add_tail((struct list_head *)vbl_sig, &vbl_sigs->head); |
| 319 | 340 | ||
| 320 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 341 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
| 342 | |||
| 343 | vblwait.reply.sequence = seq; | ||
| 321 | } else { | 344 | } else { |
| 322 | if (dev->driver->vblank_wait) | 345 | if (flags & _DRM_VBLANK_SECONDARY) { |
| 346 | if (dev->driver->vblank_wait2) | ||
| 347 | ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence); | ||
| 348 | } else if (dev->driver->vblank_wait) | ||
| 323 | ret = | 349 | ret = |
| 324 | dev->driver->vblank_wait(dev, | 350 | dev->driver->vblank_wait(dev, |
| 325 | &vblwait.request.sequence); | 351 | &vblwait.request.sequence); |
| @@ -347,25 +373,32 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) | |||
| 347 | */ | 373 | */ |
| 348 | void drm_vbl_send_signals(drm_device_t * dev) | 374 | void drm_vbl_send_signals(drm_device_t * dev) |
| 349 | { | 375 | { |
| 350 | struct list_head *list, *tmp; | ||
| 351 | drm_vbl_sig_t *vbl_sig; | ||
| 352 | unsigned int vbl_seq = atomic_read(&dev->vbl_received); | ||
| 353 | unsigned long flags; | 376 | unsigned long flags; |
| 377 | int i; | ||
| 354 | 378 | ||
| 355 | spin_lock_irqsave(&dev->vbl_lock, flags); | 379 | spin_lock_irqsave(&dev->vbl_lock, flags); |
| 356 | 380 | ||
| 357 | list_for_each_safe(list, tmp, &dev->vbl_sigs.head) { | 381 | for (i = 0; i < 2; i++) { |
| 358 | vbl_sig = list_entry(list, drm_vbl_sig_t, head); | 382 | struct list_head *list, *tmp; |
| 359 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | 383 | drm_vbl_sig_t *vbl_sig; |
| 360 | vbl_sig->info.si_code = vbl_seq; | 384 | drm_vbl_sig_t *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; |
| 361 | send_sig_info(vbl_sig->info.si_signo, &vbl_sig->info, | 385 | unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : |
| 362 | vbl_sig->task); | 386 | &dev->vbl_received); |
| 387 | |||
| 388 | list_for_each_safe(list, tmp, &vbl_sigs->head) { | ||
| 389 | vbl_sig = list_entry(list, drm_vbl_sig_t, head); | ||
| 390 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | ||
| 391 | vbl_sig->info.si_code = vbl_seq; | ||
| 392 | send_sig_info(vbl_sig->info.si_signo, | ||
| 393 | &vbl_sig->info, vbl_sig->task); | ||
| 363 | 394 | ||
| 364 | list_del(list); | 395 | list_del(list); |
| 365 | 396 | ||
| 366 | drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); | 397 | drm_free(vbl_sig, sizeof(*vbl_sig), |
| 398 | DRM_MEM_DRIVER); | ||
| 367 | 399 | ||
| 368 | dev->vbl_pending--; | 400 | dev->vbl_pending--; |
| 401 | } | ||
| 369 | } | 402 | } |
| 370 | } | 403 | } |
| 371 | 404 | ||
| @@ -373,3 +406,77 @@ void drm_vbl_send_signals(drm_device_t * dev) | |||
| 373 | } | 406 | } |
| 374 | 407 | ||
| 375 | EXPORT_SYMBOL(drm_vbl_send_signals); | 408 | EXPORT_SYMBOL(drm_vbl_send_signals); |
| 409 | |||
| 410 | /** | ||
| 411 | * Tasklet wrapper function. | ||
| 412 | * | ||
| 413 | * \param data DRM device in disguise. | ||
| 414 | * | ||
| 415 | * Attempts to grab the HW lock and calls the driver callback on success. On | ||
| 416 | * failure, leave the lock marked as contended so the callback can be called | ||
| 417 | * from drm_unlock(). | ||
| 418 | */ | ||
| 419 | static void drm_locked_tasklet_func(unsigned long data) | ||
| 420 | { | ||
| 421 | drm_device_t *dev = (drm_device_t*)data; | ||
| 422 | unsigned long irqflags; | ||
| 423 | |||
| 424 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); | ||
| 425 | |||
| 426 | if (!dev->locked_tasklet_func || | ||
| 427 | !drm_lock_take(&dev->lock.hw_lock->lock, | ||
| 428 | DRM_KERNEL_CONTEXT)) { | ||
| 429 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
| 430 | return; | ||
| 431 | } | ||
| 432 | |||
| 433 | dev->lock.lock_time = jiffies; | ||
| 434 | atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); | ||
| 435 | |||
| 436 | dev->locked_tasklet_func(dev); | ||
| 437 | |||
| 438 | drm_lock_free(dev, &dev->lock.hw_lock->lock, | ||
| 439 | DRM_KERNEL_CONTEXT); | ||
| 440 | |||
| 441 | dev->locked_tasklet_func = NULL; | ||
| 442 | |||
| 443 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
| 444 | } | ||
| 445 | |||
| 446 | /** | ||
| 447 | * Schedule a tasklet to call back a driver hook with the HW lock held. | ||
| 448 | * | ||
| 449 | * \param dev DRM device. | ||
| 450 | * \param func Driver callback. | ||
| 451 | * | ||
| 452 | * This is intended for triggering actions that require the HW lock from an | ||
| 453 | * interrupt handler. The lock will be grabbed ASAP after the interrupt handler | ||
| 454 | * completes. Note that the callback may be called from interrupt or process | ||
| 455 | * context, it must not make any assumptions about this. Also, the HW lock will | ||
| 456 | * be held with the kernel context or any client context. | ||
| 457 | */ | ||
| 458 | void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*)) | ||
| 459 | { | ||
| 460 | unsigned long irqflags; | ||
| 461 | static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0); | ||
| 462 | |||
| 463 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) || | ||
| 464 | test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state)) | ||
| 465 | return; | ||
| 466 | |||
| 467 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); | ||
| 468 | |||
| 469 | if (dev->locked_tasklet_func) { | ||
| 470 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
| 471 | return; | ||
| 472 | } | ||
| 473 | |||
| 474 | dev->locked_tasklet_func = func; | ||
| 475 | |||
| 476 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
| 477 | |||
| 478 | drm_tasklet.data = (unsigned long)dev; | ||
| 479 | |||
| 480 | tasklet_hi_schedule(&drm_tasklet); | ||
| 481 | } | ||
| 482 | EXPORT_SYMBOL(drm_locked_tasklet); | ||
diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c index f9e45303498d..116ed0f2ac09 100644 --- a/drivers/char/drm/drm_lock.c +++ b/drivers/char/drm/drm_lock.c | |||
| @@ -155,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp, | |||
| 155 | drm_file_t *priv = filp->private_data; | 155 | drm_file_t *priv = filp->private_data; |
| 156 | drm_device_t *dev = priv->head->dev; | 156 | drm_device_t *dev = priv->head->dev; |
| 157 | drm_lock_t lock; | 157 | drm_lock_t lock; |
| 158 | unsigned long irqflags; | ||
| 158 | 159 | ||
| 159 | if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) | 160 | if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) |
| 160 | return -EFAULT; | 161 | return -EFAULT; |
| @@ -165,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp, | |||
| 165 | return -EINVAL; | 166 | return -EINVAL; |
| 166 | } | 167 | } |
| 167 | 168 | ||
| 169 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); | ||
| 170 | |||
| 171 | if (dev->locked_tasklet_func) { | ||
| 172 | dev->locked_tasklet_func(dev); | ||
| 173 | |||
| 174 | dev->locked_tasklet_func = NULL; | ||
| 175 | } | ||
| 176 | |||
| 177 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
| 178 | |||
| 168 | atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); | 179 | atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); |
| 169 | 180 | ||
| 170 | /* kernel_context_switch isn't used by any of the x86 drm | 181 | /* kernel_context_switch isn't used by any of the x86 drm |
diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 7b1d4e8659ba..5fd6dc0870cf 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c | |||
| @@ -60,6 +60,8 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, | |||
| 60 | int retcode; | 60 | int retcode; |
| 61 | 61 | ||
| 62 | spin_lock_init(&dev->count_lock); | 62 | spin_lock_init(&dev->count_lock); |
| 63 | spin_lock_init(&dev->drw_lock); | ||
| 64 | spin_lock_init(&dev->tasklet_lock); | ||
| 63 | init_timer(&dev->timer); | 65 | init_timer(&dev->timer); |
| 64 | mutex_init(&dev->struct_mutex); | 66 | mutex_init(&dev->struct_mutex); |
| 65 | mutex_init(&dev->ctxlist_mutex); | 67 | mutex_init(&dev->ctxlist_mutex); |
diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c index ae2691942ddb..b9cfc077f6bc 100644 --- a/drivers/char/drm/drm_vm.c +++ b/drivers/char/drm/drm_vm.c | |||
| @@ -473,6 +473,22 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) | |||
| 473 | } | 473 | } |
| 474 | unlock_kernel(); | 474 | unlock_kernel(); |
| 475 | 475 | ||
| 476 | if (!capable(CAP_SYS_ADMIN) && | ||
| 477 | (dma->flags & _DRM_DMA_USE_PCI_RO)) { | ||
| 478 | vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE); | ||
| 479 | #if defined(__i386__) || defined(__x86_64__) | ||
| 480 | pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; | ||
| 481 | #else | ||
| 482 | /* Ye gads this is ugly. With more thought | ||
| 483 | we could move this up higher and use | ||
| 484 | `protection_map' instead. */ | ||
| 485 | vma->vm_page_prot = | ||
| 486 | __pgprot(pte_val | ||
| 487 | (pte_wrprotect | ||
| 488 | (__pte(pgprot_val(vma->vm_page_prot))))); | ||
| 489 | #endif | ||
| 490 | } | ||
| 491 | |||
| 476 | vma->vm_ops = &drm_vm_dma_ops; | 492 | vma->vm_ops = &drm_vm_dma_ops; |
| 477 | 493 | ||
| 478 | vma->vm_flags |= VM_RESERVED; /* Don't swap */ | 494 | vma->vm_flags |= VM_RESERVED; /* Don't swap */ |
diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index fb7913ff5286..9354ce3b0093 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c | |||
| @@ -162,6 +162,7 @@ static int i915_initialize(drm_device_t * dev, | |||
| 162 | 162 | ||
| 163 | dev_priv->ring.virtual_start = dev_priv->ring.map.handle; | 163 | dev_priv->ring.virtual_start = dev_priv->ring.map.handle; |
| 164 | 164 | ||
| 165 | dev_priv->cpp = init->cpp; | ||
| 165 | dev_priv->back_offset = init->back_offset; | 166 | dev_priv->back_offset = init->back_offset; |
| 166 | dev_priv->front_offset = init->front_offset; | 167 | dev_priv->front_offset = init->front_offset; |
| 167 | dev_priv->current_page = 0; | 168 | dev_priv->current_page = 0; |
| @@ -782,6 +783,7 @@ drm_ioctl_desc_t i915_ioctls[] = { | |||
| 782 | [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = { i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, | 783 | [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = { i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, |
| 783 | [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = { i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, | 784 | [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = { i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, |
| 784 | [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = { i915_vblank_pipe_get, DRM_AUTH }, | 785 | [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = { i915_vblank_pipe_get, DRM_AUTH }, |
| 786 | [DRM_IOCTL_NR(DRM_I915_VBLANK_SWAP)] = {i915_vblank_swap, DRM_AUTH}, | ||
| 785 | }; | 787 | }; |
| 786 | 788 | ||
| 787 | int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); | 789 | int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); |
diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 6af83e613f27..96a468886a7a 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h | |||
| @@ -104,6 +104,15 @@ typedef struct _drm_i915_sarea { | |||
| 104 | unsigned int depth_tiled; | 104 | unsigned int depth_tiled; |
| 105 | unsigned int rotated_tiled; | 105 | unsigned int rotated_tiled; |
| 106 | unsigned int rotated2_tiled; | 106 | unsigned int rotated2_tiled; |
| 107 | |||
| 108 | int pipeA_x; | ||
| 109 | int pipeA_y; | ||
| 110 | int pipeA_w; | ||
| 111 | int pipeA_h; | ||
| 112 | int pipeB_x; | ||
| 113 | int pipeB_y; | ||
| 114 | int pipeB_w; | ||
| 115 | int pipeB_h; | ||
| 107 | } drm_i915_sarea_t; | 116 | } drm_i915_sarea_t; |
| 108 | 117 | ||
| 109 | /* Flags for perf_boxes | 118 | /* Flags for perf_boxes |
| @@ -132,6 +141,7 @@ typedef struct _drm_i915_sarea { | |||
| 132 | #define DRM_I915_DESTROY_HEAP 0x0c | 141 | #define DRM_I915_DESTROY_HEAP 0x0c |
| 133 | #define DRM_I915_SET_VBLANK_PIPE 0x0d | 142 | #define DRM_I915_SET_VBLANK_PIPE 0x0d |
| 134 | #define DRM_I915_GET_VBLANK_PIPE 0x0e | 143 | #define DRM_I915_GET_VBLANK_PIPE 0x0e |
| 144 | #define DRM_I915_VBLANK_SWAP 0x0f | ||
| 135 | 145 | ||
| 136 | #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) | 146 | #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) |
| 137 | #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) | 147 | #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) |
| @@ -148,6 +158,7 @@ typedef struct _drm_i915_sarea { | |||
| 148 | #define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) | 158 | #define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) |
| 149 | #define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) | 159 | #define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) |
| 150 | #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) | 160 | #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) |
| 161 | #define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) | ||
| 151 | 162 | ||
| 152 | /* Allow drivers to submit batchbuffers directly to hardware, relying | 163 | /* Allow drivers to submit batchbuffers directly to hardware, relying |
| 153 | * on the security mechanisms provided by hardware. | 164 | * on the security mechanisms provided by hardware. |
| @@ -243,4 +254,12 @@ typedef struct drm_i915_vblank_pipe { | |||
| 243 | int pipe; | 254 | int pipe; |
| 244 | } drm_i915_vblank_pipe_t; | 255 | } drm_i915_vblank_pipe_t; |
| 245 | 256 | ||
| 257 | /* Schedule buffer swap at given vertical blank: | ||
| 258 | */ | ||
| 259 | typedef struct drm_i915_vblank_swap { | ||
| 260 | drm_drawable_t drawable; | ||
| 261 | drm_vblank_seq_type_t seqtype; | ||
| 262 | unsigned int sequence; | ||
| 263 | } drm_i915_vblank_swap_t; | ||
| 264 | |||
| 246 | #endif /* _I915_DRM_H_ */ | 265 | #endif /* _I915_DRM_H_ */ |
diff --git a/drivers/char/drm/i915_drv.c b/drivers/char/drm/i915_drv.c index 8e2e6095c4b3..85bcc276f804 100644 --- a/drivers/char/drm/i915_drv.c +++ b/drivers/char/drm/i915_drv.c | |||
| @@ -44,12 +44,14 @@ static struct drm_driver driver = { | |||
| 44 | */ | 44 | */ |
| 45 | .driver_features = | 45 | .driver_features = |
| 46 | DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ | 46 | DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ |
| 47 | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, | 47 | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | |
| 48 | DRIVER_IRQ_VBL2, | ||
| 48 | .load = i915_driver_load, | 49 | .load = i915_driver_load, |
| 49 | .lastclose = i915_driver_lastclose, | 50 | .lastclose = i915_driver_lastclose, |
| 50 | .preclose = i915_driver_preclose, | 51 | .preclose = i915_driver_preclose, |
| 51 | .device_is_agp = i915_driver_device_is_agp, | 52 | .device_is_agp = i915_driver_device_is_agp, |
| 52 | .vblank_wait = i915_driver_vblank_wait, | 53 | .vblank_wait = i915_driver_vblank_wait, |
| 54 | .vblank_wait2 = i915_driver_vblank_wait2, | ||
| 53 | .irq_preinstall = i915_driver_irq_preinstall, | 55 | .irq_preinstall = i915_driver_irq_preinstall, |
| 54 | .irq_postinstall = i915_driver_irq_postinstall, | 56 | .irq_postinstall = i915_driver_irq_postinstall, |
| 55 | .irq_uninstall = i915_driver_irq_uninstall, | 57 | .irq_uninstall = i915_driver_irq_uninstall, |
diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index fdc2bf192714..93cdcfe6aa84 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h | |||
| @@ -46,9 +46,11 @@ | |||
| 46 | * 1.3: Add vblank support | 46 | * 1.3: Add vblank support |
| 47 | * 1.4: Fix cmdbuffer path, add heap destroy | 47 | * 1.4: Fix cmdbuffer path, add heap destroy |
| 48 | * 1.5: Add vblank pipe configuration | 48 | * 1.5: Add vblank pipe configuration |
| 49 | * 1.6: - New ioctl for scheduling buffer swaps on vertical blank | ||
| 50 | * - Support vertical blank on secondary display pipe | ||
| 49 | */ | 51 | */ |
| 50 | #define DRIVER_MAJOR 1 | 52 | #define DRIVER_MAJOR 1 |
| 51 | #define DRIVER_MINOR 5 | 53 | #define DRIVER_MINOR 6 |
| 52 | #define DRIVER_PATCHLEVEL 0 | 54 | #define DRIVER_PATCHLEVEL 0 |
| 53 | 55 | ||
| 54 | typedef struct _drm_i915_ring_buffer { | 56 | typedef struct _drm_i915_ring_buffer { |
| @@ -71,6 +73,13 @@ struct mem_block { | |||
| 71 | DRMFILE filp; /* 0: free, -1: heap, other: real files */ | 73 | DRMFILE filp; /* 0: free, -1: heap, other: real files */ |
| 72 | }; | 74 | }; |
| 73 | 75 | ||
| 76 | typedef struct _drm_i915_vbl_swap { | ||
| 77 | struct list_head head; | ||
| 78 | drm_drawable_t drw_id; | ||
| 79 | unsigned int pipe; | ||
| 80 | unsigned int sequence; | ||
| 81 | } drm_i915_vbl_swap_t; | ||
| 82 | |||
| 74 | typedef struct drm_i915_private { | 83 | typedef struct drm_i915_private { |
| 75 | drm_local_map_t *sarea; | 84 | drm_local_map_t *sarea; |
| 76 | drm_local_map_t *mmio_map; | 85 | drm_local_map_t *mmio_map; |
| @@ -83,6 +92,7 @@ typedef struct drm_i915_private { | |||
| 83 | dma_addr_t dma_status_page; | 92 | dma_addr_t dma_status_page; |
| 84 | unsigned long counter; | 93 | unsigned long counter; |
| 85 | 94 | ||
| 95 | unsigned int cpp; | ||
| 86 | int back_offset; | 96 | int back_offset; |
| 87 | int front_offset; | 97 | int front_offset; |
| 88 | int current_page; | 98 | int current_page; |
| @@ -98,6 +108,10 @@ typedef struct drm_i915_private { | |||
| 98 | struct mem_block *agp_heap; | 108 | struct mem_block *agp_heap; |
| 99 | unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; | 109 | unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; |
| 100 | int vblank_pipe; | 110 | int vblank_pipe; |
| 111 | |||
| 112 | spinlock_t swaps_lock; | ||
| 113 | drm_i915_vbl_swap_t vbl_swaps; | ||
| 114 | unsigned int swaps_pending; | ||
| 101 | } drm_i915_private_t; | 115 | } drm_i915_private_t; |
| 102 | 116 | ||
| 103 | extern drm_ioctl_desc_t i915_ioctls[]; | 117 | extern drm_ioctl_desc_t i915_ioctls[]; |
| @@ -117,12 +131,14 @@ extern int i915_irq_emit(DRM_IOCTL_ARGS); | |||
| 117 | extern int i915_irq_wait(DRM_IOCTL_ARGS); | 131 | extern int i915_irq_wait(DRM_IOCTL_ARGS); |
| 118 | 132 | ||
| 119 | extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence); | 133 | extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence); |
| 134 | extern int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence); | ||
| 120 | extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); | 135 | extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); |
| 121 | extern void i915_driver_irq_preinstall(drm_device_t * dev); | 136 | extern void i915_driver_irq_preinstall(drm_device_t * dev); |
| 122 | extern void i915_driver_irq_postinstall(drm_device_t * dev); | 137 | extern void i915_driver_irq_postinstall(drm_device_t * dev); |
| 123 | extern void i915_driver_irq_uninstall(drm_device_t * dev); | 138 | extern void i915_driver_irq_uninstall(drm_device_t * dev); |
| 124 | extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); | 139 | extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); |
| 125 | extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); | 140 | extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); |
| 141 | extern int i915_vblank_swap(DRM_IOCTL_ARGS); | ||
| 126 | 142 | ||
| 127 | /* i915_mem.c */ | 143 | /* i915_mem.c */ |
| 128 | extern int i915_mem_alloc(DRM_IOCTL_ARGS); | 144 | extern int i915_mem_alloc(DRM_IOCTL_ARGS); |
| @@ -256,6 +272,10 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); | |||
| 256 | 272 | ||
| 257 | #define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) | 273 | #define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) |
| 258 | 274 | ||
| 275 | #define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) | ||
| 276 | #define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) | ||
| 277 | #define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) | ||
| 278 | |||
| 259 | #define MI_BATCH_BUFFER ((0x30<<23)|1) | 279 | #define MI_BATCH_BUFFER ((0x30<<23)|1) |
| 260 | #define MI_BATCH_BUFFER_START (0x31<<23) | 280 | #define MI_BATCH_BUFFER_START (0x31<<23) |
| 261 | #define MI_BATCH_BUFFER_END (0xA<<23) | 281 | #define MI_BATCH_BUFFER_END (0xA<<23) |
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 0d4a162aa385..e5463b111fc0 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c | |||
| @@ -37,6 +37,99 @@ | |||
| 37 | 37 | ||
| 38 | #define MAX_NOPID ((u32)~0) | 38 | #define MAX_NOPID ((u32)~0) |
| 39 | 39 | ||
| 40 | /** | ||
| 41 | * Emit blits for scheduled buffer swaps. | ||
| 42 | * | ||
| 43 | * This function will be called with the HW lock held. | ||
| 44 | */ | ||
| 45 | static void i915_vblank_tasklet(drm_device_t *dev) | ||
| 46 | { | ||
| 47 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
| 48 | unsigned long irqflags; | ||
| 49 | struct list_head *list, *tmp; | ||
| 50 | |||
| 51 | DRM_DEBUG("\n"); | ||
| 52 | |||
| 53 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
| 54 | |||
| 55 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { | ||
| 56 | drm_i915_vbl_swap_t *vbl_swap = | ||
| 57 | list_entry(list, drm_i915_vbl_swap_t, head); | ||
| 58 | atomic_t *counter = vbl_swap->pipe ? &dev->vbl_received2 : | ||
| 59 | &dev->vbl_received; | ||
| 60 | |||
| 61 | if ((atomic_read(counter) - vbl_swap->sequence) <= (1<<23)) { | ||
| 62 | drm_drawable_info_t *drw; | ||
| 63 | |||
| 64 | spin_unlock(&dev_priv->swaps_lock); | ||
| 65 | |||
| 66 | spin_lock(&dev->drw_lock); | ||
| 67 | |||
| 68 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); | ||
| 69 | |||
| 70 | if (drw) { | ||
| 71 | int i, num_rects = drw->num_rects; | ||
| 72 | drm_clip_rect_t *rect = drw->rects; | ||
| 73 | drm_i915_sarea_t *sarea_priv = | ||
| 74 | dev_priv->sarea_priv; | ||
| 75 | u32 cpp = dev_priv->cpp; | ||
| 76 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | | ||
| 77 | XY_SRC_COPY_BLT_WRITE_ALPHA | | ||
| 78 | XY_SRC_COPY_BLT_WRITE_RGB) | ||
| 79 | : XY_SRC_COPY_BLT_CMD; | ||
| 80 | u32 pitchropcpp = (sarea_priv->pitch * cpp) | | ||
| 81 | (0xcc << 16) | (cpp << 23) | | ||
| 82 | (1 << 24); | ||
| 83 | RING_LOCALS; | ||
| 84 | |||
| 85 | i915_kernel_lost_context(dev); | ||
| 86 | |||
| 87 | BEGIN_LP_RING(6); | ||
| 88 | |||
| 89 | OUT_RING(GFX_OP_DRAWRECT_INFO); | ||
| 90 | OUT_RING(0); | ||
| 91 | OUT_RING(0); | ||
| 92 | OUT_RING(sarea_priv->width | | ||
| 93 | sarea_priv->height << 16); | ||
| 94 | OUT_RING(sarea_priv->width | | ||
| 95 | sarea_priv->height << 16); | ||
| 96 | OUT_RING(0); | ||
| 97 | |||
| 98 | ADVANCE_LP_RING(); | ||
| 99 | |||
| 100 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | ||
| 101 | |||
| 102 | for (i = 0; i < num_rects; i++, rect++) { | ||
| 103 | BEGIN_LP_RING(8); | ||
| 104 | |||
| 105 | OUT_RING(cmd); | ||
| 106 | OUT_RING(pitchropcpp); | ||
| 107 | OUT_RING((rect->y1 << 16) | rect->x1); | ||
| 108 | OUT_RING((rect->y2 << 16) | rect->x2); | ||
| 109 | OUT_RING(sarea_priv->front_offset); | ||
| 110 | OUT_RING((rect->y1 << 16) | rect->x1); | ||
| 111 | OUT_RING(pitchropcpp & 0xffff); | ||
| 112 | OUT_RING(sarea_priv->back_offset); | ||
| 113 | |||
| 114 | ADVANCE_LP_RING(); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | spin_unlock(&dev->drw_lock); | ||
| 119 | |||
| 120 | spin_lock(&dev_priv->swaps_lock); | ||
| 121 | |||
| 122 | list_del(list); | ||
| 123 | |||
| 124 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); | ||
| 125 | |||
| 126 | dev_priv->swaps_pending--; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
| 131 | } | ||
| 132 | |||
| 40 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | 133 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
| 41 | { | 134 | { |
| 42 | drm_device_t *dev = (drm_device_t *) arg; | 135 | drm_device_t *dev = (drm_device_t *) arg; |
| @@ -60,9 +153,26 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
| 60 | DRM_WAKEUP(&dev_priv->irq_queue); | 153 | DRM_WAKEUP(&dev_priv->irq_queue); |
| 61 | 154 | ||
| 62 | if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { | 155 | if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { |
| 63 | atomic_inc(&dev->vbl_received); | 156 | int vblank_pipe = dev_priv->vblank_pipe; |
| 157 | |||
| 158 | if ((vblank_pipe & | ||
| 159 | (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) | ||
| 160 | == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { | ||
| 161 | if (temp & VSYNC_PIPEA_FLAG) | ||
| 162 | atomic_inc(&dev->vbl_received); | ||
| 163 | if (temp & VSYNC_PIPEB_FLAG) | ||
| 164 | atomic_inc(&dev->vbl_received2); | ||
| 165 | } else if (((temp & VSYNC_PIPEA_FLAG) && | ||
| 166 | (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || | ||
| 167 | ((temp & VSYNC_PIPEB_FLAG) && | ||
| 168 | (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) | ||
| 169 | atomic_inc(&dev->vbl_received); | ||
| 170 | |||
| 64 | DRM_WAKEUP(&dev->vbl_queue); | 171 | DRM_WAKEUP(&dev->vbl_queue); |
| 65 | drm_vbl_send_signals(dev); | 172 | drm_vbl_send_signals(dev); |
| 173 | |||
| 174 | if (dev_priv->swaps_pending > 0) | ||
| 175 | drm_locked_tasklet(dev, i915_vblank_tasklet); | ||
| 66 | } | 176 | } |
| 67 | 177 | ||
| 68 | return IRQ_HANDLED; | 178 | return IRQ_HANDLED; |
| @@ -120,7 +230,8 @@ static int i915_wait_irq(drm_device_t * dev, int irq_nr) | |||
| 120 | return ret; | 230 | return ret; |
| 121 | } | 231 | } |
| 122 | 232 | ||
| 123 | int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) | 233 | static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence, |
| 234 | atomic_t *counter) | ||
| 124 | { | 235 | { |
| 125 | drm_i915_private_t *dev_priv = dev->dev_private; | 236 | drm_i915_private_t *dev_priv = dev->dev_private; |
| 126 | unsigned int cur_vblank; | 237 | unsigned int cur_vblank; |
| @@ -132,7 +243,7 @@ int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) | |||
| 132 | } | 243 | } |
| 133 | 244 | ||
| 134 | DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, | 245 | DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, |
| 135 | (((cur_vblank = atomic_read(&dev->vbl_received)) | 246 | (((cur_vblank = atomic_read(counter)) |
| 136 | - *sequence) <= (1<<23))); | 247 | - *sequence) <= (1<<23))); |
| 137 | 248 | ||
| 138 | *sequence = cur_vblank; | 249 | *sequence = cur_vblank; |
| @@ -141,6 +252,16 @@ int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) | |||
| 141 | } | 252 | } |
| 142 | 253 | ||
| 143 | 254 | ||
| 255 | int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) | ||
| 256 | { | ||
| 257 | return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); | ||
| 258 | } | ||
| 259 | |||
| 260 | int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence) | ||
| 261 | { | ||
| 262 | return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); | ||
| 263 | } | ||
| 264 | |||
| 144 | /* Needs the lock as it touches the ring. | 265 | /* Needs the lock as it touches the ring. |
| 145 | */ | 266 | */ |
| 146 | int i915_irq_emit(DRM_IOCTL_ARGS) | 267 | int i915_irq_emit(DRM_IOCTL_ARGS) |
| @@ -189,7 +310,7 @@ int i915_irq_wait(DRM_IOCTL_ARGS) | |||
| 189 | return i915_wait_irq(dev, irqwait.irq_seq); | 310 | return i915_wait_irq(dev, irqwait.irq_seq); |
| 190 | } | 311 | } |
| 191 | 312 | ||
| 192 | static int i915_enable_interrupt (drm_device_t *dev) | 313 | static void i915_enable_interrupt (drm_device_t *dev) |
| 193 | { | 314 | { |
| 194 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 315 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
| 195 | u16 flag; | 316 | u16 flag; |
| @@ -199,13 +320,8 @@ static int i915_enable_interrupt (drm_device_t *dev) | |||
| 199 | flag |= VSYNC_PIPEA_FLAG; | 320 | flag |= VSYNC_PIPEA_FLAG; |
| 200 | if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) | 321 | if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) |
| 201 | flag |= VSYNC_PIPEB_FLAG; | 322 | flag |= VSYNC_PIPEB_FLAG; |
| 202 | if (dev_priv->vblank_pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { | 323 | |
| 203 | DRM_ERROR("%s called with invalid pipe 0x%x\n", | ||
| 204 | __FUNCTION__, dev_priv->vblank_pipe); | ||
| 205 | return DRM_ERR(EINVAL); | ||
| 206 | } | ||
| 207 | I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag); | 324 | I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag); |
| 208 | return 0; | ||
| 209 | } | 325 | } |
| 210 | 326 | ||
| 211 | /* Set the vblank monitor pipe | 327 | /* Set the vblank monitor pipe |
| @@ -224,8 +340,17 @@ int i915_vblank_pipe_set(DRM_IOCTL_ARGS) | |||
| 224 | DRM_COPY_FROM_USER_IOCTL(pipe, (drm_i915_vblank_pipe_t __user *) data, | 340 | DRM_COPY_FROM_USER_IOCTL(pipe, (drm_i915_vblank_pipe_t __user *) data, |
| 225 | sizeof(pipe)); | 341 | sizeof(pipe)); |
| 226 | 342 | ||
| 343 | if (pipe.pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { | ||
| 344 | DRM_ERROR("%s called with invalid pipe 0x%x\n", | ||
| 345 | __FUNCTION__, pipe.pipe); | ||
| 346 | return DRM_ERR(EINVAL); | ||
| 347 | } | ||
| 348 | |||
| 227 | dev_priv->vblank_pipe = pipe.pipe; | 349 | dev_priv->vblank_pipe = pipe.pipe; |
| 228 | return i915_enable_interrupt (dev); | 350 | |
| 351 | i915_enable_interrupt (dev); | ||
| 352 | |||
| 353 | return 0; | ||
| 229 | } | 354 | } |
| 230 | 355 | ||
| 231 | int i915_vblank_pipe_get(DRM_IOCTL_ARGS) | 356 | int i915_vblank_pipe_get(DRM_IOCTL_ARGS) |
| @@ -251,6 +376,118 @@ int i915_vblank_pipe_get(DRM_IOCTL_ARGS) | |||
| 251 | return 0; | 376 | return 0; |
| 252 | } | 377 | } |
| 253 | 378 | ||
| 379 | /** | ||
| 380 | * Schedule buffer swap at given vertical blank. | ||
| 381 | */ | ||
| 382 | int i915_vblank_swap(DRM_IOCTL_ARGS) | ||
| 383 | { | ||
| 384 | DRM_DEVICE; | ||
| 385 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 386 | drm_i915_vblank_swap_t swap; | ||
| 387 | drm_i915_vbl_swap_t *vbl_swap; | ||
| 388 | unsigned int pipe, seqtype, curseq; | ||
| 389 | unsigned long irqflags; | ||
| 390 | struct list_head *list; | ||
| 391 | |||
| 392 | if (!dev_priv) { | ||
| 393 | DRM_ERROR("%s called with no initialization\n", __func__); | ||
| 394 | return DRM_ERR(EINVAL); | ||
| 395 | } | ||
| 396 | |||
| 397 | if (dev_priv->sarea_priv->rotation) { | ||
| 398 | DRM_DEBUG("Rotation not supported\n"); | ||
| 399 | return DRM_ERR(EINVAL); | ||
| 400 | } | ||
| 401 | |||
| 402 | DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data, | ||
| 403 | sizeof(swap)); | ||
| 404 | |||
| 405 | if (swap.seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | | ||
| 406 | _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { | ||
| 407 | DRM_ERROR("Invalid sequence type 0x%x\n", swap.seqtype); | ||
| 408 | return DRM_ERR(EINVAL); | ||
| 409 | } | ||
| 410 | |||
| 411 | pipe = (swap.seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; | ||
| 412 | |||
| 413 | seqtype = swap.seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); | ||
| 414 | |||
| 415 | if (!(dev_priv->vblank_pipe & (1 << pipe))) { | ||
| 416 | DRM_ERROR("Invalid pipe %d\n", pipe); | ||
| 417 | return DRM_ERR(EINVAL); | ||
| 418 | } | ||
| 419 | |||
| 420 | spin_lock_irqsave(&dev->drw_lock, irqflags); | ||
| 421 | |||
| 422 | if (!drm_get_drawable_info(dev, swap.drawable)) { | ||
| 423 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 424 | DRM_ERROR("Invalid drawable ID %d\n", swap.drawable); | ||
| 425 | return DRM_ERR(EINVAL); | ||
| 426 | } | ||
| 427 | |||
| 428 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
| 429 | |||
| 430 | curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); | ||
| 431 | |||
| 432 | if (seqtype == _DRM_VBLANK_RELATIVE) | ||
| 433 | swap.sequence += curseq; | ||
| 434 | |||
| 435 | if ((curseq - swap.sequence) <= (1<<23)) { | ||
| 436 | if (swap.seqtype & _DRM_VBLANK_NEXTONMISS) { | ||
| 437 | swap.sequence = curseq + 1; | ||
| 438 | } else { | ||
| 439 | DRM_DEBUG("Missed target sequence\n"); | ||
| 440 | return DRM_ERR(EINVAL); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
| 445 | |||
| 446 | list_for_each(list, &dev_priv->vbl_swaps.head) { | ||
| 447 | vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); | ||
| 448 | |||
| 449 | if (vbl_swap->drw_id == swap.drawable && | ||
| 450 | vbl_swap->pipe == pipe && | ||
| 451 | vbl_swap->sequence == swap.sequence) { | ||
| 452 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
| 453 | DRM_DEBUG("Already scheduled\n"); | ||
| 454 | return 0; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
| 459 | |||
| 460 | if (dev_priv->swaps_pending >= 100) { | ||
| 461 | DRM_DEBUG("Too many swaps queued\n"); | ||
| 462 | return DRM_ERR(EBUSY); | ||
| 463 | } | ||
| 464 | |||
| 465 | vbl_swap = drm_calloc(1, sizeof(vbl_swap), DRM_MEM_DRIVER); | ||
| 466 | |||
| 467 | if (!vbl_swap) { | ||
| 468 | DRM_ERROR("Failed to allocate memory to queue swap\n"); | ||
| 469 | return DRM_ERR(ENOMEM); | ||
| 470 | } | ||
| 471 | |||
| 472 | DRM_DEBUG("\n"); | ||
| 473 | |||
| 474 | vbl_swap->drw_id = swap.drawable; | ||
| 475 | vbl_swap->pipe = pipe; | ||
| 476 | vbl_swap->sequence = swap.sequence; | ||
| 477 | |||
| 478 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
| 479 | |||
| 480 | list_add_tail((struct list_head *)vbl_swap, &dev_priv->vbl_swaps.head); | ||
| 481 | dev_priv->swaps_pending++; | ||
| 482 | |||
| 483 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
| 484 | |||
| 485 | DRM_COPY_TO_USER_IOCTL((drm_i915_vblank_swap_t __user *) data, swap, | ||
| 486 | sizeof(swap)); | ||
| 487 | |||
| 488 | return 0; | ||
| 489 | } | ||
| 490 | |||
| 254 | /* drm_dma.h hooks | 491 | /* drm_dma.h hooks |
| 255 | */ | 492 | */ |
| 256 | void i915_driver_irq_preinstall(drm_device_t * dev) | 493 | void i915_driver_irq_preinstall(drm_device_t * dev) |
| @@ -266,6 +503,12 @@ void i915_driver_irq_postinstall(drm_device_t * dev) | |||
| 266 | { | 503 | { |
| 267 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 504 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
| 268 | 505 | ||
| 506 | dev_priv->swaps_lock = SPIN_LOCK_UNLOCKED; | ||
| 507 | INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); | ||
| 508 | dev_priv->swaps_pending = 0; | ||
| 509 | |||
| 510 | if (!dev_priv->vblank_pipe) | ||
| 511 | dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; | ||
| 269 | i915_enable_interrupt(dev); | 512 | i915_enable_interrupt(dev); |
| 270 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); | 513 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); |
| 271 | } | 514 | } |
