diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-09-30 15:14:26 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2008-10-17 17:10:11 -0400 |
commit | 0a3e67a4caac273a3bfc4ced3da364830b1ab241 (patch) | |
tree | 02a2e5e76d9dffcb556d09b0eee4d34ebe5d81cb /drivers/gpu/drm/mga | |
parent | 2df68b439fcb97a4c55f81516206ef4ee325e28d (diff) |
drm: Rework vblank-wait handling to allow interrupt reduction.
Previously, drivers supporting vblank interrupt waits would run the interrupt
all the time, or all the time that any 3d client was running, preventing the
CPU from sleeping for long when the system was otherwise idle. Now, interrupts
are disabled any time that no client is waiting on a vblank event. The new
method uses vblank counters on the chipsets when the interrupts are turned
off, rather than counting interrupts, so that we can continue to present
accurate vblank numbers.
Co-author: Michel Dänzer <michel@tungstengraphics.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/mga')
-rw-r--r-- | drivers/gpu/drm/mga/mga_drv.c | 29 | ||||
-rw-r--r-- | drivers/gpu/drm/mga/mga_drv.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/mga/mga_irq.c | 74 |
3 files changed, 73 insertions, 36 deletions
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 5572939fc7d1..97ee566ef749 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c | |||
@@ -45,15 +45,16 @@ static struct pci_device_id pciidlist[] = { | |||
45 | static struct drm_driver driver = { | 45 | static struct drm_driver driver = { |
46 | .driver_features = | 46 | .driver_features = |
47 | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | | 47 | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | |
48 | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | | 48 | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, |
49 | DRIVER_IRQ_VBL, | ||
50 | .dev_priv_size = sizeof(drm_mga_buf_priv_t), | 49 | .dev_priv_size = sizeof(drm_mga_buf_priv_t), |
51 | .load = mga_driver_load, | 50 | .load = mga_driver_load, |
52 | .unload = mga_driver_unload, | 51 | .unload = mga_driver_unload, |
53 | .lastclose = mga_driver_lastclose, | 52 | .lastclose = mga_driver_lastclose, |
54 | .dma_quiescent = mga_driver_dma_quiescent, | 53 | .dma_quiescent = mga_driver_dma_quiescent, |
55 | .device_is_agp = mga_driver_device_is_agp, | 54 | .device_is_agp = mga_driver_device_is_agp, |
56 | .vblank_wait = mga_driver_vblank_wait, | 55 | .get_vblank_counter = mga_get_vblank_counter, |
56 | .enable_vblank = mga_enable_vblank, | ||
57 | .disable_vblank = mga_disable_vblank, | ||
57 | .irq_preinstall = mga_driver_irq_preinstall, | 58 | .irq_preinstall = mga_driver_irq_preinstall, |
58 | .irq_postinstall = mga_driver_irq_postinstall, | 59 | .irq_postinstall = mga_driver_irq_postinstall, |
59 | .irq_uninstall = mga_driver_irq_uninstall, | 60 | .irq_uninstall = mga_driver_irq_uninstall, |
@@ -64,20 +65,20 @@ static struct drm_driver driver = { | |||
64 | .ioctls = mga_ioctls, | 65 | .ioctls = mga_ioctls, |
65 | .dma_ioctl = mga_dma_buffers, | 66 | .dma_ioctl = mga_dma_buffers, |
66 | .fops = { | 67 | .fops = { |
67 | .owner = THIS_MODULE, | 68 | .owner = THIS_MODULE, |
68 | .open = drm_open, | 69 | .open = drm_open, |
69 | .release = drm_release, | 70 | .release = drm_release, |
70 | .ioctl = drm_ioctl, | 71 | .ioctl = drm_ioctl, |
71 | .mmap = drm_mmap, | 72 | .mmap = drm_mmap, |
72 | .poll = drm_poll, | 73 | .poll = drm_poll, |
73 | .fasync = drm_fasync, | 74 | .fasync = drm_fasync, |
74 | #ifdef CONFIG_COMPAT | 75 | #ifdef CONFIG_COMPAT |
75 | .compat_ioctl = mga_compat_ioctl, | 76 | .compat_ioctl = mga_compat_ioctl, |
76 | #endif | 77 | #endif |
77 | }, | 78 | }, |
78 | .pci_driver = { | 79 | .pci_driver = { |
79 | .name = DRIVER_NAME, | 80 | .name = DRIVER_NAME, |
80 | .id_table = pciidlist, | 81 | .id_table = pciidlist, |
81 | }, | 82 | }, |
82 | 83 | ||
83 | .name = DRIVER_NAME, | 84 | .name = DRIVER_NAME, |
diff --git a/drivers/gpu/drm/mga/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h index f6ebd24bd587..88257c276eb9 100644 --- a/drivers/gpu/drm/mga/mga_drv.h +++ b/drivers/gpu/drm/mga/mga_drv.h | |||
@@ -120,6 +120,7 @@ typedef struct drm_mga_private { | |||
120 | u32 clear_cmd; | 120 | u32 clear_cmd; |
121 | u32 maccess; | 121 | u32 maccess; |
122 | 122 | ||
123 | atomic_t vbl_received; /**< Number of vblanks received. */ | ||
123 | wait_queue_head_t fence_queue; | 124 | wait_queue_head_t fence_queue; |
124 | atomic_t last_fence_retired; | 125 | atomic_t last_fence_retired; |
125 | u32 next_fence_to_post; | 126 | u32 next_fence_to_post; |
@@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv); | |||
181 | extern int mga_warp_init(drm_mga_private_t * dev_priv); | 182 | extern int mga_warp_init(drm_mga_private_t * dev_priv); |
182 | 183 | ||
183 | /* mga_irq.c */ | 184 | /* mga_irq.c */ |
185 | extern int mga_enable_vblank(struct drm_device *dev, int crtc); | ||
186 | extern void mga_disable_vblank(struct drm_device *dev, int crtc); | ||
187 | extern u32 mga_get_vblank_counter(struct drm_device *dev, int crtc); | ||
184 | extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence); | 188 | extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence); |
185 | extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); | 189 | extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); |
186 | extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS); | 190 | extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS); |
187 | extern void mga_driver_irq_preinstall(struct drm_device * dev); | 191 | extern void mga_driver_irq_preinstall(struct drm_device * dev); |
188 | extern void mga_driver_irq_postinstall(struct drm_device * dev); | 192 | extern int mga_driver_irq_postinstall(struct drm_device *dev); |
189 | extern void mga_driver_irq_uninstall(struct drm_device * dev); | 193 | extern void mga_driver_irq_uninstall(struct drm_device * dev); |
190 | extern long mga_compat_ioctl(struct file *filp, unsigned int cmd, | 194 | extern long mga_compat_ioctl(struct file *filp, unsigned int cmd, |
191 | unsigned long arg); | 195 | unsigned long arg); |
diff --git a/drivers/gpu/drm/mga/mga_irq.c b/drivers/gpu/drm/mga/mga_irq.c index 9302cb8f0f83..bab42f41188b 100644 --- a/drivers/gpu/drm/mga/mga_irq.c +++ b/drivers/gpu/drm/mga/mga_irq.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* mga_irq.c -- IRQ handling for radeon -*- linux-c -*- | 1 | /* mga_irq.c -- IRQ handling for radeon -*- linux-c -*- |
2 | * | 2 | */ |
3 | /* | ||
3 | * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. | 4 | * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. |
4 | * | 5 | * |
5 | * The Weather Channel (TM) funded Tungsten Graphics to develop the | 6 | * The Weather Channel (TM) funded Tungsten Graphics to develop the |
@@ -35,6 +36,18 @@ | |||
35 | #include "mga_drm.h" | 36 | #include "mga_drm.h" |
36 | #include "mga_drv.h" | 37 | #include "mga_drv.h" |
37 | 38 | ||
39 | u32 mga_get_vblank_counter(struct drm_device *dev, int crtc) | ||
40 | { | ||
41 | const drm_mga_private_t *const dev_priv = | ||
42 | (drm_mga_private_t *) dev->dev_private; | ||
43 | |||
44 | if (crtc != 0) | ||
45 | return 0; | ||
46 | |||
47 | return atomic_read(&dev_priv->vbl_received); | ||
48 | } | ||
49 | |||
50 | |||
38 | irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) | 51 | irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) |
39 | { | 52 | { |
40 | struct drm_device *dev = (struct drm_device *) arg; | 53 | struct drm_device *dev = (struct drm_device *) arg; |
@@ -47,9 +60,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) | |||
47 | /* VBLANK interrupt */ | 60 | /* VBLANK interrupt */ |
48 | if (status & MGA_VLINEPEN) { | 61 | if (status & MGA_VLINEPEN) { |
49 | MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); | 62 | MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); |
50 | atomic_inc(&dev->vbl_received); | 63 | atomic_inc(&dev_priv->vbl_received); |
51 | DRM_WAKEUP(&dev->vbl_queue); | 64 | drm_handle_vblank(dev, 0); |
52 | drm_vbl_send_signals(dev); | ||
53 | handled = 1; | 65 | handled = 1; |
54 | } | 66 | } |
55 | 67 | ||
@@ -58,6 +70,7 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) | |||
58 | const u32 prim_start = MGA_READ(MGA_PRIMADDRESS); | 70 | const u32 prim_start = MGA_READ(MGA_PRIMADDRESS); |
59 | const u32 prim_end = MGA_READ(MGA_PRIMEND); | 71 | const u32 prim_end = MGA_READ(MGA_PRIMEND); |
60 | 72 | ||
73 | |||
61 | MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR); | 74 | MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR); |
62 | 75 | ||
63 | /* In addition to clearing the interrupt-pending bit, we | 76 | /* In addition to clearing the interrupt-pending bit, we |
@@ -72,28 +85,39 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) | |||
72 | handled = 1; | 85 | handled = 1; |
73 | } | 86 | } |
74 | 87 | ||
75 | if (handled) { | 88 | if (handled) |
76 | return IRQ_HANDLED; | 89 | return IRQ_HANDLED; |
77 | } | ||
78 | return IRQ_NONE; | 90 | return IRQ_NONE; |
79 | } | 91 | } |
80 | 92 | ||
81 | int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) | 93 | int mga_enable_vblank(struct drm_device *dev, int crtc) |
82 | { | 94 | { |
83 | unsigned int cur_vblank; | 95 | drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; |
84 | int ret = 0; | ||
85 | 96 | ||
86 | /* Assume that the user has missed the current sequence number | 97 | if (crtc != 0) { |
87 | * by about a day rather than she wants to wait for years | 98 | DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", |
88 | * using vertical blanks... | 99 | crtc); |
89 | */ | 100 | return 0; |
90 | DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, | 101 | } |
91 | (((cur_vblank = atomic_read(&dev->vbl_received)) | ||
92 | - *sequence) <= (1 << 23))); | ||
93 | 102 | ||
94 | *sequence = cur_vblank; | 103 | MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); |
104 | return 0; | ||
105 | } | ||
95 | 106 | ||
96 | return ret; | 107 | |
108 | void mga_disable_vblank(struct drm_device *dev, int crtc) | ||
109 | { | ||
110 | if (crtc != 0) { | ||
111 | DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", | ||
112 | crtc); | ||
113 | } | ||
114 | |||
115 | /* Do *NOT* disable the vertical refresh interrupt. MGA doesn't have | ||
116 | * a nice hardware counter that tracks the number of refreshes when | ||
117 | * the interrupt is disabled, and the kernel doesn't know the refresh | ||
118 | * rate to calculate an estimate. | ||
119 | */ | ||
120 | /* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */ | ||
97 | } | 121 | } |
98 | 122 | ||
99 | int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence) | 123 | int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence) |
@@ -125,14 +149,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev) | |||
125 | MGA_WRITE(MGA_ICLEAR, ~0); | 149 | MGA_WRITE(MGA_ICLEAR, ~0); |
126 | } | 150 | } |
127 | 151 | ||
128 | void mga_driver_irq_postinstall(struct drm_device * dev) | 152 | int mga_driver_irq_postinstall(struct drm_device *dev) |
129 | { | 153 | { |
130 | drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; | 154 | drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; |
155 | int ret; | ||
156 | |||
157 | ret = drm_vblank_init(dev, 1); | ||
158 | if (ret) | ||
159 | return ret; | ||
131 | 160 | ||
132 | DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); | 161 | DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); |
133 | 162 | ||
134 | /* Turn on vertical blank interrupt and soft trap interrupt. */ | 163 | /* Turn on soft trap interrupt. Vertical blank interrupts are enabled |
135 | MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); | 164 | * in mga_enable_vblank. |
165 | */ | ||
166 | MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN); | ||
167 | return 0; | ||
136 | } | 168 | } |
137 | 169 | ||
138 | void mga_driver_irq_uninstall(struct drm_device * dev) | 170 | void mga_driver_irq_uninstall(struct drm_device * dev) |