]> code.ossystems Code Review - openembedded-core.git/blob
70f91194e47be3bdf4699d3c367ae65b2a7cf03d
[openembedded-core.git] /
1 From: Eric Anholt <eric@anholt.net>
2 Date: Tue, 29 Jul 2008 19:10:39 +0000 (-0700)
3 Subject: i915: Add support for MSI and interrupt mitigation.
4 X-Git-Tag: v2.6.12-rc2
5 X-Git-Url: http://gitweb.freedesktop.org/?p=users/anholt/anholt/linux-2.6.git;a=commitdiff;h=aae4223e2fd3b29ae8e070b7a16d8cfc70c6a0c0
6
7 i915: Add support for MSI and interrupt mitigation.
8
9 Previous attempts at interrupt mitigation had been foiled by i915_wait_irq's
10 failure to update the sarea seqno value when the status page indicated that
11 the seqno had already been passed.  MSI support has been seen to cut CPU
12 costs by up to 40% in some workloads by avoiding other expensive interrupt
13 handlers for frequent graphics interrupts.
14
15 Signed-off-by: Eric Anholt <eric@anholt.net>
16 ---
17
18 --- a/drivers/gpu/drm/drm_irq.c
19 +++ b/drivers/gpu/drm/drm_irq.c
20 @@ -63,7 +63,7 @@ int drm_irq_by_busid(struct drm_device *
21             p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))
22                 return -EINVAL;
23  
24 -       p->irq = dev->irq;
25 +       p->irq = dev->pdev->irq;
26  
27         DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
28                   p->irq);
29 @@ -89,7 +89,7 @@ static int drm_irq_install(struct drm_de
30         if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
31                 return -EINVAL;
32  
33 -       if (dev->irq == 0)
34 +       if (dev->pdev->irq == 0)
35                 return -EINVAL;
36  
37         mutex_lock(&dev->struct_mutex);
38 @@ -107,7 +107,7 @@ static int drm_irq_install(struct drm_de
39         dev->irq_enabled = 1;
40         mutex_unlock(&dev->struct_mutex);
41  
42 -       DRM_DEBUG("irq=%d\n", dev->irq);
43 +       DRM_DEBUG("irq=%d\n", dev->pdev->irq);
44  
45         if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
46                 init_waitqueue_head(&dev->vbl_queue);
47 @@ -127,8 +127,12 @@ static int drm_irq_install(struct drm_de
48         if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
49                 sh_flags = IRQF_SHARED;
50  
51 -       ret = request_irq(dev->irq, dev->driver->irq_handler,
52 +       ret = request_irq(dev->pdev->irq, dev->driver->irq_handler,
53                           sh_flags, dev->devname, dev);
54 +       /* Expose the device irq number to drivers that want to export it for
55 +        * whatever reason.
56 +        */
57 +       dev->irq = dev->pdev->irq;
58         if (ret < 0) {
59                 mutex_lock(&dev->struct_mutex);
60                 dev->irq_enabled = 0;
61 @@ -164,11 +168,11 @@ int drm_irq_uninstall(struct drm_device 
62         if (!irq_enabled)
63                 return -EINVAL;
64  
65 -       DRM_DEBUG("irq=%d\n", dev->irq);
66 +       DRM_DEBUG("irq=%d\n", dev->pdev->irq);
67  
68         dev->driver->irq_uninstall(dev);
69  
70 -       free_irq(dev->irq, dev);
71 +       free_irq(dev->pdev->irq, dev);
72  
73         dev->locked_tasklet_func = NULL;
74  
75 @@ -201,7 +205,7 @@ int drm_control(struct drm_device *dev, 
76                 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
77                         return 0;
78                 if (dev->if_version < DRM_IF_VERSION(1, 2) &&
79 -                   ctl->irq != dev->irq)
80 +                   ctl->irq != dev->pdev->irq)
81                         return -EINVAL;
82                 return drm_irq_install(dev);
83         case DRM_UNINST_HANDLER:
84 @@ -239,7 +243,7 @@ int drm_wait_vblank(struct drm_device *d
85         int ret = 0;
86         unsigned int flags, seq;
87  
88 -       if ((!dev->irq) || (!dev->irq_enabled))
89 +       if ((!dev->pdev->irq) || (!dev->irq_enabled))
90                 return -EINVAL;
91  
92         if (vblwait->request.type &
93 --- a/drivers/gpu/drm/i915/i915_dma.c
94 +++ b/drivers/gpu/drm/i915/i915_dma.c
95 @@ -84,7 +84,7 @@ static int i915_dma_cleanup(struct drm_d
96          * may not have been called from userspace and after dev_private
97          * is freed, it's too late.
98          */
99 -       if (dev->irq)
100 +       if (dev->irq_enabled)
101                 drm_irq_uninstall(dev);
102  
103         if (dev_priv->ring.virtual_start) {
104 @@ -644,7 +644,7 @@ static int i915_getparam(struct drm_devi
105  
106         switch (param->param) {
107         case I915_PARAM_IRQ_ACTIVE:
108 -               value = dev->irq ? 1 : 0;
109 +               value = dev->irq_enabled;
110                 break;
111         case I915_PARAM_ALLOW_BATCHBUFFER:
112                 value = dev_priv->allow_batchbuffer ? 1 : 0;
113 @@ -763,6 +763,20 @@ int i915_driver_load(struct drm_device *
114         ret = drm_addmap(dev, base, size, _DRM_REGISTERS,
115                          _DRM_KERNEL | _DRM_DRIVER,
116                          &dev_priv->mmio_map);
117 +
118 +
119 +       /* On the 945G/GM, the chipset reports the MSI capability on the
120 +        * integrated graphics even though the support isn't actually there
121 +        * according to the published specs.  It doesn't appear to function
122 +        * correctly in testing on 945G.
123 +        * This may be a side effect of MSI having been made available for PEG
124 +        * and the registers being closely associated.
125 +        */
126 +       if (!IS_I945G(dev) && !IS_I945GM(dev))
127 +               pci_enable_msi(dev->pdev);
128 +
129 +       spin_lock_init(&dev_priv->user_irq_lock);
130 +
131         return ret;
132  }
133  
134 @@ -770,6 +784,9 @@ int i915_driver_unload(struct drm_device
135  {
136         struct drm_i915_private *dev_priv = dev->dev_private;
137  
138 +       if (dev->pdev->msi_enabled)
139 +               pci_disable_msi(dev->pdev);
140 +
141         if (dev_priv->mmio_map)
142                 drm_rmmap(dev, dev_priv->mmio_map);
143  
144 --- a/drivers/gpu/drm/i915/i915_drv.h
145 +++ b/drivers/gpu/drm/i915/i915_drv.h
146 @@ -105,6 +105,12 @@ typedef struct drm_i915_private {
147         wait_queue_head_t irq_queue;
148         atomic_t irq_received;
149         atomic_t irq_emitted;
150 +       /** Protects user_irq_refcount and irq_mask_reg */
151 +       spinlock_t user_irq_lock;
152 +       /** Refcount for i915_user_irq_get() versus i915_user_irq_put(). */
153 +       int user_irq_refcount;
154 +       /** Cached value of IMR to avoid reads in updating the bitfield */
155 +       u32 irq_mask_reg;
156  
157         int tex_lru_log_granularity;
158         int allow_batchbuffer;
159 --- a/drivers/gpu/drm/i915/i915_irq.c
160 +++ b/drivers/gpu/drm/i915/i915_irq.c
161 @@ -33,6 +33,31 @@
162  
163  #define MAX_NOPID ((u32)~0)
164  
165 +/** These are the interrupts used by the driver */
166 +#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT |              \
167 +                                   I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
168 +                                   I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
169 +
170 +static inline void
171 +i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
172 +{
173 +       if ((dev_priv->irq_mask_reg & mask) != 0) {
174 +               dev_priv->irq_mask_reg &= ~mask;
175 +               I915_WRITE(IMR, dev_priv->irq_mask_reg);
176 +               (void) I915_READ(IMR);
177 +       }
178 +}
179 +
180 +static inline void
181 +i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
182 +{
183 +       if ((dev_priv->irq_mask_reg & mask) != mask) {
184 +               dev_priv->irq_mask_reg |= mask;
185 +               I915_WRITE(IMR, dev_priv->irq_mask_reg);
186 +               (void) I915_READ(IMR);
187 +       }
188 +}
189 +
190  /**
191   * Emit blits for scheduled buffer swaps.
192   *
193 @@ -229,46 +254,50 @@ irqreturn_t i915_driver_irq_handler(DRM_
194  {
195         struct drm_device *dev = (struct drm_device *) arg;
196         drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
197 -       u16 temp;
198         u32 pipea_stats, pipeb_stats;
199 +       u32 iir;
200  
201         pipea_stats = I915_READ(PIPEASTAT);
202         pipeb_stats = I915_READ(PIPEBSTAT);
203  
204 -       temp = I915_READ16(IIR);
205 -
206 -       temp &= (I915_USER_INTERRUPT |
207 -                I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
208 -                I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT);
209 -
210 -       DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
211 -
212 -       if (temp == 0)
213 +       if (dev->pdev->msi_enabled)
214 +               I915_WRITE(IMR, ~0);
215 +       iir = I915_READ(IIR);
216 +
217 +       DRM_DEBUG("iir=%08x\n", iir);
218 +
219 +       if (iir == 0) {
220 +               if (dev->pdev->msi_enabled) {
221 +                       I915_WRITE(IMR, dev_priv->irq_mask_reg);
222 +                       (void) I915_READ(IMR);
223 +               }
224                 return IRQ_NONE;
225 +       }
226  
227 -       I915_WRITE16(IIR, temp);
228 -       (void) I915_READ16(IIR);
229 -       DRM_READMEMORYBARRIER();
230 +       I915_WRITE(IIR, iir);
231 +       if (dev->pdev->msi_enabled)
232 +               I915_WRITE(IMR, dev_priv->irq_mask_reg);
233 +       (void) I915_READ(IIR); /* Flush posted writes */
234  
235         dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
236  
237 -       if (temp & I915_USER_INTERRUPT)
238 +       if (iir & I915_USER_INTERRUPT)
239                 DRM_WAKEUP(&dev_priv->irq_queue);
240  
241 -       if (temp & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
242 -                   I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
243 +       if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
244 +                  I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
245                 int vblank_pipe = dev_priv->vblank_pipe;
246  
247                 if ((vblank_pipe &
248                      (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
249                     == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
250 -                       if (temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
251 +                       if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
252                                 atomic_inc(&dev->vbl_received);
253 -                       if (temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
254 +                       if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
255                                 atomic_inc(&dev->vbl_received2);
256 -               } else if (((temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
257 +               } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
258                             (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
259 -                          ((temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
260 +                          ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
261                             (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
262                         atomic_inc(&dev->vbl_received);
263  
264 @@ -314,6 +343,27 @@ static int i915_emit_irq(struct drm_devi
265         return dev_priv->counter;
266  }
267  
268 +static void i915_user_irq_get(struct drm_device *dev)
269 +{
270 +       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
271 +
272 +       spin_lock(&dev_priv->user_irq_lock);
273 +       if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
274 +               i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
275 +       spin_unlock(&dev_priv->user_irq_lock);
276 +}
277 +
278 +static void i915_user_irq_put(struct drm_device *dev)
279 +{
280 +       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
281 +
282 +       spin_lock(&dev_priv->user_irq_lock);
283 +       BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
284 +       if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
285 +               i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
286 +       spin_unlock(&dev_priv->user_irq_lock);
287 +}
288 +
289  static int i915_wait_irq(struct drm_device * dev, int irq_nr)
290  {
291         drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
292 @@ -322,13 +372,17 @@ static int i915_wait_irq(struct drm_devi
293         DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
294                   READ_BREADCRUMB(dev_priv));
295  
296 -       if (READ_BREADCRUMB(dev_priv) >= irq_nr)
297 +       if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
298 +               dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
299                 return 0;
300 +       }
301  
302         dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
303  
304 +       i915_user_irq_get(dev);
305         DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
306                     READ_BREADCRUMB(dev_priv) >= irq_nr);
307 +       i915_user_irq_put(dev);
308  
309         if (ret == -EBUSY) {
310                 DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
311 @@ -413,20 +467,6 @@ int i915_irq_wait(struct drm_device *dev
312         return i915_wait_irq(dev, irqwait->irq_seq);
313  }
314  
315 -static void i915_enable_interrupt (struct drm_device *dev)
316 -{
317 -       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
318 -       u16 flag;
319 -
320 -       flag = 0;
321 -       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
322 -               flag |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
323 -       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
324 -               flag |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
325 -
326 -       I915_WRITE16(IER, I915_USER_INTERRUPT | flag);
327 -}
328 -
329  /* Set the vblank monitor pipe
330   */
331  int i915_vblank_pipe_set(struct drm_device *dev, void *data,
332 @@ -434,6 +474,7 @@ int i915_vblank_pipe_set(struct drm_devi
333  {
334         drm_i915_private_t *dev_priv = dev->dev_private;
335         drm_i915_vblank_pipe_t *pipe = data;
336 +       u32 enable_mask = 0, disable_mask = 0;
337  
338         if (!dev_priv) {
339                 DRM_ERROR("called with no initialization\n");
340 @@ -445,9 +486,20 @@ int i915_vblank_pipe_set(struct drm_devi
341                 return -EINVAL;
342         }
343  
344 -       dev_priv->vblank_pipe = pipe->pipe;
345 +       if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
346 +               enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
347 +       else
348 +               disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
349 +
350 +       if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
351 +               enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
352 +       else
353 +               disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
354  
355 -       i915_enable_interrupt (dev);
356 +       i915_enable_irq(dev_priv, enable_mask);
357 +       i915_disable_irq(dev_priv, disable_mask);
358 +
359 +       dev_priv->vblank_pipe = pipe->pipe;
360  
361         return 0;
362  }
363 @@ -464,7 +516,7 @@ int i915_vblank_pipe_get(struct drm_devi
364                 return -EINVAL;
365         }
366  
367 -       flag = I915_READ(IER);
368 +       flag = I915_READ(IMR);
369         pipe->pipe = 0;
370         if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
371                 pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
372 @@ -586,9 +638,9 @@ void i915_driver_irq_preinstall(struct d
373  {
374         drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
375  
376 -       I915_WRITE16(HWSTAM, 0xfffe);
377 -       I915_WRITE16(IMR, 0x0);
378 -       I915_WRITE16(IER, 0x0);
379 +       I915_WRITE(HWSTAM, 0xfffe);
380 +       I915_WRITE(IMR, 0x0);
381 +       I915_WRITE(IER, 0x0);
382  }
383  
384  void i915_driver_irq_postinstall(struct drm_device * dev)
385 @@ -601,7 +653,18 @@ void i915_driver_irq_postinstall(struct 
386  
387         if (!dev_priv->vblank_pipe)
388                 dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
389 -       i915_enable_interrupt(dev);
390 +
391 +       /* Set initial unmasked IRQs to just the selected vblank pipes. */
392 +       dev_priv->irq_mask_reg = ~0;
393 +       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
394 +               dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
395 +       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
396 +               dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
397 +
398 +       I915_WRITE(IMR, dev_priv->irq_mask_reg);
399 +       I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
400 +       (void) I915_READ(IER);
401 +
402         DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
403  }
404  
405 @@ -613,10 +676,10 @@ void i915_driver_irq_uninstall(struct dr
406         if (!dev_priv)
407                 return;
408  
409 -       I915_WRITE16(HWSTAM, 0xffff);
410 -       I915_WRITE16(IMR, 0xffff);
411 -       I915_WRITE16(IER, 0x0);
412 +       I915_WRITE(HWSTAM, 0xffff);
413 +       I915_WRITE(IMR, 0xffff);
414 +       I915_WRITE(IER, 0x0);
415  
416 -       temp = I915_READ16(IIR);
417 -       I915_WRITE16(IIR, temp);
418 +       temp = I915_READ(IIR);
419 +       I915_WRITE(IIR, temp);
420  }
421