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/via | |
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/via')
-rw-r--r-- | drivers/gpu/drm/via/via_drv.c | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/via/via_drv.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/via/via_irq.c | 102 |
3 files changed, 83 insertions, 61 deletions
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index 80c01cdfa37d..0993b441fc42 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c | |||
@@ -40,11 +40,13 @@ static struct pci_device_id pciidlist[] = { | |||
40 | static struct drm_driver driver = { | 40 | static struct drm_driver driver = { |
41 | .driver_features = | 41 | .driver_features = |
42 | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | | 42 | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | |
43 | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, | 43 | DRIVER_IRQ_SHARED, |
44 | .load = via_driver_load, | 44 | .load = via_driver_load, |
45 | .unload = via_driver_unload, | 45 | .unload = via_driver_unload, |
46 | .context_dtor = via_final_context, | 46 | .context_dtor = via_final_context, |
47 | .vblank_wait = via_driver_vblank_wait, | 47 | .get_vblank_counter = via_get_vblank_counter, |
48 | .enable_vblank = via_enable_vblank, | ||
49 | .disable_vblank = via_disable_vblank, | ||
48 | .irq_preinstall = via_driver_irq_preinstall, | 50 | .irq_preinstall = via_driver_irq_preinstall, |
49 | .irq_postinstall = via_driver_irq_postinstall, | 51 | .irq_postinstall = via_driver_irq_postinstall, |
50 | .irq_uninstall = via_driver_irq_uninstall, | 52 | .irq_uninstall = via_driver_irq_uninstall, |
@@ -59,17 +61,17 @@ static struct drm_driver driver = { | |||
59 | .get_reg_ofs = drm_core_get_reg_ofs, | 61 | .get_reg_ofs = drm_core_get_reg_ofs, |
60 | .ioctls = via_ioctls, | 62 | .ioctls = via_ioctls, |
61 | .fops = { | 63 | .fops = { |
62 | .owner = THIS_MODULE, | 64 | .owner = THIS_MODULE, |
63 | .open = drm_open, | 65 | .open = drm_open, |
64 | .release = drm_release, | 66 | .release = drm_release, |
65 | .ioctl = drm_ioctl, | 67 | .ioctl = drm_ioctl, |
66 | .mmap = drm_mmap, | 68 | .mmap = drm_mmap, |
67 | .poll = drm_poll, | 69 | .poll = drm_poll, |
68 | .fasync = drm_fasync, | 70 | .fasync = drm_fasync, |
69 | }, | 71 | }, |
70 | .pci_driver = { | 72 | .pci_driver = { |
71 | .name = DRIVER_NAME, | 73 | .name = DRIVER_NAME, |
72 | .id_table = pciidlist, | 74 | .id_table = pciidlist, |
73 | }, | 75 | }, |
74 | 76 | ||
75 | .name = DRIVER_NAME, | 77 | .name = DRIVER_NAME, |
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 2daae81874cd..cafcb844a223 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h | |||
@@ -75,6 +75,7 @@ typedef struct drm_via_private { | |||
75 | struct timeval last_vblank; | 75 | struct timeval last_vblank; |
76 | int last_vblank_valid; | 76 | int last_vblank_valid; |
77 | unsigned usec_per_vblank; | 77 | unsigned usec_per_vblank; |
78 | atomic_t vbl_received; | ||
78 | drm_via_state_t hc_state; | 79 | drm_via_state_t hc_state; |
79 | char pci_buf[VIA_PCI_BUF_SIZE]; | 80 | char pci_buf[VIA_PCI_BUF_SIZE]; |
80 | const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; | 81 | const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; |
@@ -130,21 +131,24 @@ extern int via_init_context(struct drm_device * dev, int context); | |||
130 | extern int via_final_context(struct drm_device * dev, int context); | 131 | extern int via_final_context(struct drm_device * dev, int context); |
131 | 132 | ||
132 | extern int via_do_cleanup_map(struct drm_device * dev); | 133 | extern int via_do_cleanup_map(struct drm_device * dev); |
133 | extern int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); | 134 | extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc); |
135 | extern int via_enable_vblank(struct drm_device *dev, int crtc); | ||
136 | extern void via_disable_vblank(struct drm_device *dev, int crtc); | ||
134 | 137 | ||
135 | extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS); | 138 | extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS); |
136 | extern void via_driver_irq_preinstall(struct drm_device * dev); | 139 | extern void via_driver_irq_preinstall(struct drm_device * dev); |
137 | extern void via_driver_irq_postinstall(struct drm_device * dev); | 140 | extern int via_driver_irq_postinstall(struct drm_device *dev); |
138 | extern void via_driver_irq_uninstall(struct drm_device * dev); | 141 | extern void via_driver_irq_uninstall(struct drm_device * dev); |
139 | 142 | ||
140 | extern int via_dma_cleanup(struct drm_device * dev); | 143 | extern int via_dma_cleanup(struct drm_device * dev); |
141 | extern void via_init_command_verifier(void); | 144 | extern void via_init_command_verifier(void); |
142 | extern int via_driver_dma_quiescent(struct drm_device * dev); | 145 | extern int via_driver_dma_quiescent(struct drm_device * dev); |
143 | extern void via_init_futex(drm_via_private_t * dev_priv); | 146 | extern void via_init_futex(drm_via_private_t *dev_priv); |
144 | extern void via_cleanup_futex(drm_via_private_t * dev_priv); | 147 | extern void via_cleanup_futex(drm_via_private_t *dev_priv); |
145 | extern void via_release_futex(drm_via_private_t * dev_priv, int context); | 148 | extern void via_release_futex(drm_via_private_t *dev_priv, int context); |
146 | 149 | ||
147 | extern void via_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv); | 150 | extern void via_reclaim_buffers_locked(struct drm_device *dev, |
151 | struct drm_file *file_priv); | ||
148 | extern void via_lastclose(struct drm_device *dev); | 152 | extern void via_lastclose(struct drm_device *dev); |
149 | 153 | ||
150 | extern void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq); | 154 | extern void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq); |
diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index c6bb978a1106..1b966fbdb49f 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c | |||
@@ -43,7 +43,7 @@ | |||
43 | #define VIA_REG_INTERRUPT 0x200 | 43 | #define VIA_REG_INTERRUPT 0x200 |
44 | 44 | ||
45 | /* VIA_REG_INTERRUPT */ | 45 | /* VIA_REG_INTERRUPT */ |
46 | #define VIA_IRQ_GLOBAL (1 << 31) | 46 | #define VIA_IRQ_GLOBAL (1 << 31) |
47 | #define VIA_IRQ_VBLANK_ENABLE (1 << 19) | 47 | #define VIA_IRQ_VBLANK_ENABLE (1 << 19) |
48 | #define VIA_IRQ_VBLANK_PENDING (1 << 3) | 48 | #define VIA_IRQ_VBLANK_PENDING (1 << 3) |
49 | #define VIA_IRQ_HQV0_ENABLE (1 << 11) | 49 | #define VIA_IRQ_HQV0_ENABLE (1 << 11) |
@@ -68,16 +68,15 @@ | |||
68 | 68 | ||
69 | static maskarray_t via_pro_group_a_irqs[] = { | 69 | static maskarray_t via_pro_group_a_irqs[] = { |
70 | {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, | 70 | {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, |
71 | 0x00000000}, | 71 | 0x00000000 }, |
72 | {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, | 72 | {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, |
73 | 0x00000000}, | 73 | 0x00000000 }, |
74 | {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, | 74 | {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, |
75 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, | 75 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, |
76 | {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, | 76 | {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, |
77 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, | 77 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, |
78 | }; | 78 | }; |
79 | static int via_num_pro_group_a = | 79 | static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs); |
80 | sizeof(via_pro_group_a_irqs) / sizeof(maskarray_t); | ||
81 | static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; | 80 | static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; |
82 | 81 | ||
83 | static maskarray_t via_unichrome_irqs[] = { | 82 | static maskarray_t via_unichrome_irqs[] = { |
@@ -86,14 +85,24 @@ static maskarray_t via_unichrome_irqs[] = { | |||
86 | {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, | 85 | {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, |
87 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} | 86 | VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} |
88 | }; | 87 | }; |
89 | static int via_num_unichrome = sizeof(via_unichrome_irqs) / sizeof(maskarray_t); | 88 | static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs); |
90 | static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; | 89 | static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; |
91 | 90 | ||
91 | |||
92 | static unsigned time_diff(struct timeval *now, struct timeval *then) | 92 | static unsigned time_diff(struct timeval *now, struct timeval *then) |
93 | { | 93 | { |
94 | return (now->tv_usec >= then->tv_usec) ? | 94 | return (now->tv_usec >= then->tv_usec) ? |
95 | now->tv_usec - then->tv_usec : | 95 | now->tv_usec - then->tv_usec : |
96 | 1000000 - (then->tv_usec - now->tv_usec); | 96 | 1000000 - (then->tv_usec - now->tv_usec); |
97 | } | ||
98 | |||
99 | u32 via_get_vblank_counter(struct drm_device *dev, int crtc) | ||
100 | { | ||
101 | drm_via_private_t *dev_priv = dev->dev_private; | ||
102 | if (crtc != 0) | ||
103 | return 0; | ||
104 | |||
105 | return atomic_read(&dev_priv->vbl_received); | ||
97 | } | 106 | } |
98 | 107 | ||
99 | irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) | 108 | irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) |
@@ -108,23 +117,22 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) | |||
108 | 117 | ||
109 | status = VIA_READ(VIA_REG_INTERRUPT); | 118 | status = VIA_READ(VIA_REG_INTERRUPT); |
110 | if (status & VIA_IRQ_VBLANK_PENDING) { | 119 | if (status & VIA_IRQ_VBLANK_PENDING) { |
111 | atomic_inc(&dev->vbl_received); | 120 | atomic_inc(&dev_priv->vbl_received); |
112 | if (!(atomic_read(&dev->vbl_received) & 0x0F)) { | 121 | if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { |
113 | do_gettimeofday(&cur_vblank); | 122 | do_gettimeofday(&cur_vblank); |
114 | if (dev_priv->last_vblank_valid) { | 123 | if (dev_priv->last_vblank_valid) { |
115 | dev_priv->usec_per_vblank = | 124 | dev_priv->usec_per_vblank = |
116 | time_diff(&cur_vblank, | 125 | time_diff(&cur_vblank, |
117 | &dev_priv->last_vblank) >> 4; | 126 | &dev_priv->last_vblank) >> 4; |
118 | } | 127 | } |
119 | dev_priv->last_vblank = cur_vblank; | 128 | dev_priv->last_vblank = cur_vblank; |
120 | dev_priv->last_vblank_valid = 1; | 129 | dev_priv->last_vblank_valid = 1; |
121 | } | 130 | } |
122 | if (!(atomic_read(&dev->vbl_received) & 0xFF)) { | 131 | if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { |
123 | DRM_DEBUG("US per vblank is: %u\n", | 132 | DRM_DEBUG("US per vblank is: %u\n", |
124 | dev_priv->usec_per_vblank); | 133 | dev_priv->usec_per_vblank); |
125 | } | 134 | } |
126 | DRM_WAKEUP(&dev->vbl_queue); | 135 | drm_handle_vblank(dev, 0); |
127 | drm_vbl_send_signals(dev); | ||
128 | handled = 1; | 136 | handled = 1; |
129 | } | 137 | } |
130 | 138 | ||
@@ -145,6 +153,7 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) | |||
145 | /* Acknowlege interrupts */ | 153 | /* Acknowlege interrupts */ |
146 | VIA_WRITE(VIA_REG_INTERRUPT, status); | 154 | VIA_WRITE(VIA_REG_INTERRUPT, status); |
147 | 155 | ||
156 | |||
148 | if (handled) | 157 | if (handled) |
149 | return IRQ_HANDLED; | 158 | return IRQ_HANDLED; |
150 | else | 159 | else |
@@ -163,31 +172,34 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) | |||
163 | } | 172 | } |
164 | } | 173 | } |
165 | 174 | ||
166 | int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) | 175 | int via_enable_vblank(struct drm_device *dev, int crtc) |
167 | { | 176 | { |
168 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | 177 | drm_via_private_t *dev_priv = dev->dev_private; |
169 | unsigned int cur_vblank; | 178 | u32 status; |
170 | int ret = 0; | ||
171 | 179 | ||
172 | DRM_DEBUG("\n"); | 180 | if (crtc != 0) { |
173 | if (!dev_priv) { | 181 | DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); |
174 | DRM_ERROR("called with no initialization\n"); | ||
175 | return -EINVAL; | 182 | return -EINVAL; |
176 | } | 183 | } |
177 | 184 | ||
178 | viadrv_acknowledge_irqs(dev_priv); | 185 | status = VIA_READ(VIA_REG_INTERRUPT); |
186 | VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); | ||
187 | |||
188 | VIA_WRITE8(0x83d4, 0x11); | ||
189 | VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); | ||
179 | 190 | ||
180 | /* Assume that the user has missed the current sequence number | 191 | return 0; |
181 | * by about a day rather than she wants to wait for years | 192 | } |
182 | * using vertical blanks... | ||
183 | */ | ||
184 | 193 | ||
185 | DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, | 194 | void via_disable_vblank(struct drm_device *dev, int crtc) |
186 | (((cur_vblank = atomic_read(&dev->vbl_received)) - | 195 | { |
187 | *sequence) <= (1 << 23))); | 196 | drm_via_private_t *dev_priv = dev->dev_private; |
188 | 197 | ||
189 | *sequence = cur_vblank; | 198 | VIA_WRITE8(0x83d4, 0x11); |
190 | return ret; | 199 | VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); |
200 | |||
201 | if (crtc != 0) | ||
202 | DRM_ERROR("%s: bad crtc %d\n", __func__, crtc); | ||
191 | } | 203 | } |
192 | 204 | ||
193 | static int | 205 | static int |
@@ -239,6 +251,7 @@ via_driver_irq_wait(struct drm_device * dev, unsigned int irq, int force_sequenc | |||
239 | return ret; | 251 | return ret; |
240 | } | 252 | } |
241 | 253 | ||
254 | |||
242 | /* | 255 | /* |
243 | * drm_dma.h hooks | 256 | * drm_dma.h hooks |
244 | */ | 257 | */ |
@@ -292,23 +305,25 @@ void via_driver_irq_preinstall(struct drm_device * dev) | |||
292 | } | 305 | } |
293 | } | 306 | } |
294 | 307 | ||
295 | void via_driver_irq_postinstall(struct drm_device * dev) | 308 | int via_driver_irq_postinstall(struct drm_device *dev) |
296 | { | 309 | { |
297 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | 310 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; |
298 | u32 status; | 311 | u32 status; |
299 | 312 | ||
300 | DRM_DEBUG("\n"); | 313 | DRM_DEBUG("via_driver_irq_postinstall\n"); |
301 | if (dev_priv) { | 314 | if (!dev_priv) |
302 | status = VIA_READ(VIA_REG_INTERRUPT); | 315 | return -EINVAL; |
303 | VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL | ||
304 | | dev_priv->irq_enable_mask); | ||
305 | 316 | ||
306 | /* Some magic, oh for some data sheets ! */ | 317 | drm_vblank_init(dev, 1); |
318 | status = VIA_READ(VIA_REG_INTERRUPT); | ||
319 | VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL | ||
320 | | dev_priv->irq_enable_mask); | ||
307 | 321 | ||
308 | VIA_WRITE8(0x83d4, 0x11); | 322 | /* Some magic, oh for some data sheets ! */ |
309 | VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); | 323 | VIA_WRITE8(0x83d4, 0x11); |
324 | VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); | ||
310 | 325 | ||
311 | } | 326 | return 0; |
312 | } | 327 | } |
313 | 328 | ||
314 | void via_driver_irq_uninstall(struct drm_device * dev) | 329 | void via_driver_irq_uninstall(struct drm_device * dev) |
@@ -352,7 +367,8 @@ int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) | |||
352 | 367 | ||
353 | switch (irqwait->request.type & ~VIA_IRQ_FLAGS_MASK) { | 368 | switch (irqwait->request.type & ~VIA_IRQ_FLAGS_MASK) { |
354 | case VIA_IRQ_RELATIVE: | 369 | case VIA_IRQ_RELATIVE: |
355 | irqwait->request.sequence += atomic_read(&cur_irq->irq_received); | 370 | irqwait->request.sequence += |
371 | atomic_read(&cur_irq->irq_received); | ||
356 | irqwait->request.type &= ~_DRM_VBLANK_RELATIVE; | 372 | irqwait->request.type &= ~_DRM_VBLANK_RELATIVE; |
357 | case VIA_IRQ_ABSOLUTE: | 373 | case VIA_IRQ_ABSOLUTE: |
358 | break; | 374 | break; |