aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
author=?utf-8?q?Michel_D=C3=A4nzer?= <michel@tungstengraphics.com>2006-12-11 02:32:27 -0500
committerDave Airlie <airlied@linux.ie>2006-12-11 02:32:27 -0500
commit3188a24c256bae0ed93d81d82db1f1bb6060d727 (patch)
tree8161deb13ff3c135eb274b08295920fb00ad8bf0 /drivers/char
parent2c3f0eddfbd7f5c7a5450de287bad805722888c3 (diff)
i915_vblank_tasklet: Try harder to avoid tearing.
Previously, if there were several buffer swaps scheduled for the same vertical blank, all but the first blit emitted stood a chance of exhibiting tearing. In order to avoid this, split the blits along slices of each output top to bottom. Signed-off-by: Dave Airlie <airlied@linux.ie>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/drm/i915_irq.c199
1 files changed, 139 insertions, 60 deletions
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c
index e5463b111fc0..e2c4b3a41b1e 100644
--- a/drivers/char/drm/i915_irq.c
+++ b/drivers/char/drm/i915_irq.c
@@ -46,88 +46,167 @@ static void i915_vblank_tasklet(drm_device_t *dev)
46{ 46{
47 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 47 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
48 unsigned long irqflags; 48 unsigned long irqflags;
49 struct list_head *list, *tmp; 49 struct list_head *list, *tmp, hits, *hit;
50 int nhits, nrects, slice[2], upper[2], lower[2], i;
51 unsigned counter[2] = { atomic_read(&dev->vbl_received),
52 atomic_read(&dev->vbl_received2) };
53 drm_drawable_info_t *drw;
54 drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
55 u32 cpp = dev_priv->cpp;
56 u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
57 XY_SRC_COPY_BLT_WRITE_ALPHA |
58 XY_SRC_COPY_BLT_WRITE_RGB)
59 : XY_SRC_COPY_BLT_CMD;
60 u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) |
61 (cpp << 23) | (1 << 24);
62 RING_LOCALS;
50 63
51 DRM_DEBUG("\n"); 64 DRM_DEBUG("\n");
52 65
66 INIT_LIST_HEAD(&hits);
67
68 nhits = nrects = 0;
69
53 spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); 70 spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
54 71
72 /* Find buffer swaps scheduled for this vertical blank */
55 list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { 73 list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
56 drm_i915_vbl_swap_t *vbl_swap = 74 drm_i915_vbl_swap_t *vbl_swap =
57 list_entry(list, drm_i915_vbl_swap_t, head); 75 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 76
98 ADVANCE_LP_RING(); 77 if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23))
78 continue;
79
80 list_del(list);
81 dev_priv->swaps_pending--;
99 82
100 sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; 83 spin_unlock(&dev_priv->swaps_lock);
84 spin_lock(&dev->drw_lock);
101 85
102 for (i = 0; i < num_rects; i++, rect++) { 86 drw = drm_get_drawable_info(dev, vbl_swap->drw_id);
103 BEGIN_LP_RING(8); 87
88 if (!drw) {
89 spin_unlock(&dev->drw_lock);
90 drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
91 spin_lock(&dev_priv->swaps_lock);
92 continue;
93 }
104 94
105 OUT_RING(cmd); 95 list_for_each(hit, &hits) {
106 OUT_RING(pitchropcpp); 96 drm_i915_vbl_swap_t *swap_cmp =
107 OUT_RING((rect->y1 << 16) | rect->x1); 97 list_entry(hit, drm_i915_vbl_swap_t, head);
108 OUT_RING((rect->y2 << 16) | rect->x2); 98 drm_drawable_info_t *drw_cmp =
109 OUT_RING(sarea_priv->front_offset); 99 drm_get_drawable_info(dev, swap_cmp->drw_id);
110 OUT_RING((rect->y1 << 16) | rect->x1);
111 OUT_RING(pitchropcpp & 0xffff);
112 OUT_RING(sarea_priv->back_offset);
113 100
114 ADVANCE_LP_RING(); 101 if (drw_cmp &&
115 } 102 drw_cmp->rects[0].y1 > drw->rects[0].y1) {
103 list_add_tail(list, hit);
104 break;
116 } 105 }
106 }
117 107
118 spin_unlock(&dev->drw_lock); 108 spin_unlock(&dev->drw_lock);
119 109
120 spin_lock(&dev_priv->swaps_lock); 110 /* List of hits was empty, or we reached the end of it */
111 if (hit == &hits)
112 list_add_tail(list, hits.prev);
121 113
122 list_del(list); 114 nhits++;
123 115
124 drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); 116 spin_lock(&dev_priv->swaps_lock);
117 }
118
119 if (nhits == 0) {
120 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
121 return;
122 }
123
124 spin_unlock(&dev_priv->swaps_lock);
125 125
126 dev_priv->swaps_pending--; 126 i915_kernel_lost_context(dev);
127
128 BEGIN_LP_RING(6);
129
130 OUT_RING(GFX_OP_DRAWRECT_INFO);
131 OUT_RING(0);
132 OUT_RING(0);
133 OUT_RING(sarea_priv->width | sarea_priv->height << 16);
134 OUT_RING(sarea_priv->width | sarea_priv->height << 16);
135 OUT_RING(0);
136
137 ADVANCE_LP_RING();
138
139 sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
140
141 upper[0] = upper[1] = 0;
142 slice[0] = max(sarea_priv->pipeA_h / nhits, 1);
143 slice[1] = max(sarea_priv->pipeB_h / nhits, 1);
144 lower[0] = sarea_priv->pipeA_y + slice[0];
145 lower[1] = sarea_priv->pipeB_y + slice[0];
146
147 spin_lock(&dev->drw_lock);
148
149 /* Emit blits for buffer swaps, partitioning both outputs into as many
150 * slices as there are buffer swaps scheduled in order to avoid tearing
151 * (based on the assumption that a single buffer swap would always
152 * complete before scanout starts).
153 */
154 for (i = 0; i++ < nhits;
155 upper[0] = lower[0], lower[0] += slice[0],
156 upper[1] = lower[1], lower[1] += slice[1]) {
157 if (i == nhits)
158 lower[0] = lower[1] = sarea_priv->height;
159
160 list_for_each(hit, &hits) {
161 drm_i915_vbl_swap_t *swap_hit =
162 list_entry(hit, drm_i915_vbl_swap_t, head);
163 drm_clip_rect_t *rect;
164 int num_rects, pipe;
165 unsigned short top, bottom;
166
167 drw = drm_get_drawable_info(dev, swap_hit->drw_id);
168
169 if (!drw)
170 continue;
171
172 rect = drw->rects;
173 pipe = swap_hit->pipe;
174 top = upper[pipe];
175 bottom = lower[pipe];
176
177 for (num_rects = drw->num_rects; num_rects--; rect++) {
178 int y1 = max(rect->y1, top);
179 int y2 = min(rect->y2, bottom);
180
181 if (y1 >= y2)
182 continue;
183
184 BEGIN_LP_RING(8);
185
186 OUT_RING(cmd);
187 OUT_RING(pitchropcpp);
188 OUT_RING((y1 << 16) | rect->x1);
189 OUT_RING((y2 << 16) | rect->x2);
190 OUT_RING(sarea_priv->front_offset);
191 OUT_RING((y1 << 16) | rect->x1);
192 OUT_RING(pitchropcpp & 0xffff);
193 OUT_RING(sarea_priv->back_offset);
194
195 ADVANCE_LP_RING();
196 }
127 } 197 }
128 } 198 }
129 199
130 spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); 200 spin_unlock_irqrestore(&dev->drw_lock, irqflags);
201
202 list_for_each_safe(hit, tmp, &hits) {
203 drm_i915_vbl_swap_t *swap_hit =
204 list_entry(hit, drm_i915_vbl_swap_t, head);
205
206 list_del(hit);
207
208 drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
209 }
131} 210}
132 211
133irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) 212irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)