diff options
author | Rob Clark <rob@ti.com> | 2012-03-05 11:48:35 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-03-07 16:38:07 -0500 |
commit | b33f34d3d10b9b00ca568740e7099da107d525b9 (patch) | |
tree | f63a9d59ae626af10018a38eb63b7f318c4a4800 /drivers | |
parent | 9f18c95abba6dbfd13c86e0b8f3e7219df753f7e (diff) |
staging: drm/omap: defer unpin until scanout completes
When flipping, defer unpinning until scanout completes, as indicated
by the appropriate END_WIN irq.
This also re-organizes things a bit, in replacing omap_fb_{pin,unpin}
with omap_fb_replace(), to make it easier to add support for scanout
synchronized DMM refill mode (flipping by just reprogramming DMM
synchronized with DSS scanout).
Signed-off-by: Rob Clark <rob@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/omapdrm/omap_drv.h | 5 | ||||
-rw-r--r-- | drivers/staging/omapdrm/omap_fb.c | 84 | ||||
-rw-r--r-- | drivers/staging/omapdrm/omap_plane.c | 136 |
3 files changed, 181 insertions, 44 deletions
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index a84547c2464..fe4766e27b9 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h | |||
@@ -101,8 +101,9 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, | |||
101 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, | 101 | struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, |
102 | struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); | 102 | struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); |
103 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p); | 103 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p); |
104 | int omap_framebuffer_pin(struct drm_framebuffer *fb); | 104 | int omap_framebuffer_replace(struct drm_framebuffer *a, |
105 | void omap_framebuffer_unpin(struct drm_framebuffer *fb); | 105 | struct drm_framebuffer *b, void *arg, |
106 | void (*unpin)(void *arg, struct drm_gem_object *bo)); | ||
106 | void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, | 107 | void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, |
107 | struct omap_overlay_info *info); | 108 | struct omap_overlay_info *info); |
108 | struct drm_connector *omap_framebuffer_get_next_connector( | 109 | struct drm_connector *omap_framebuffer_get_next_connector( |
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 08e2e356b58..fcb248f4cb0 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c | |||
@@ -137,41 +137,6 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { | |||
137 | .dirty = omap_framebuffer_dirty, | 137 | .dirty = omap_framebuffer_dirty, |
138 | }; | 138 | }; |
139 | 139 | ||
140 | /* pins buffer in preparation for scanout */ | ||
141 | int omap_framebuffer_pin(struct drm_framebuffer *fb) | ||
142 | { | ||
143 | struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); | ||
144 | int ret, i, n = drm_format_num_planes(omap_fb->format->pixel_format); | ||
145 | |||
146 | for (i = 0; i < n; i++) { | ||
147 | struct plane *plane = &omap_fb->planes[i]; | ||
148 | ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true); | ||
149 | if (ret) | ||
150 | goto fail; | ||
151 | } | ||
152 | |||
153 | return 0; | ||
154 | |||
155 | fail: | ||
156 | while (--i > 0) { | ||
157 | struct plane *plane = &omap_fb->planes[i]; | ||
158 | omap_gem_put_paddr(plane->bo); | ||
159 | } | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | /* releases buffer when done with scanout */ | ||
164 | void omap_framebuffer_unpin(struct drm_framebuffer *fb) | ||
165 | { | ||
166 | struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); | ||
167 | int i, n = drm_format_num_planes(omap_fb->format->pixel_format); | ||
168 | |||
169 | for (i = 0; i < n; i++) { | ||
170 | struct plane *plane = &omap_fb->planes[i]; | ||
171 | omap_gem_put_paddr(plane->bo); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /* update ovl info for scanout, handles cases of multi-planar fb's, etc. | 140 | /* update ovl info for scanout, handles cases of multi-planar fb's, etc. |
176 | */ | 141 | */ |
177 | void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, | 142 | void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, |
@@ -201,6 +166,55 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, | |||
201 | } | 166 | } |
202 | } | 167 | } |
203 | 168 | ||
169 | /* Call for unpin 'a' (if not NULL), and pin 'b' (if not NULL). Although | ||
170 | * buffers to unpin are just just pushed to the unpin fifo so that the | ||
171 | * caller can defer unpin until vblank. | ||
172 | * | ||
173 | * Note if this fails (ie. something went very wrong!), all buffers are | ||
174 | * unpinned, and the caller disables the overlay. We could have tried | ||
175 | * to revert back to the previous set of pinned buffers but if things are | ||
176 | * hosed there is no guarantee that would succeed. | ||
177 | */ | ||
178 | int omap_framebuffer_replace(struct drm_framebuffer *a, | ||
179 | struct drm_framebuffer *b, void *arg, | ||
180 | void (*unpin)(void *arg, struct drm_gem_object *bo)) | ||
181 | { | ||
182 | int ret = 0, i, na, nb; | ||
183 | struct omap_framebuffer *ofba = to_omap_framebuffer(a); | ||
184 | struct omap_framebuffer *ofbb = to_omap_framebuffer(b); | ||
185 | |||
186 | na = a ? drm_format_num_planes(a->pixel_format) : 0; | ||
187 | nb = b ? drm_format_num_planes(b->pixel_format) : 0; | ||
188 | |||
189 | for (i = 0; i < max(na, nb); i++) { | ||
190 | struct plane *pa, *pb; | ||
191 | |||
192 | pa = (i < na) ? &ofba->planes[i] : NULL; | ||
193 | pb = (i < nb) ? &ofbb->planes[i] : NULL; | ||
194 | |||
195 | if (pa) { | ||
196 | unpin(arg, pa->bo); | ||
197 | pa->paddr = 0; | ||
198 | } | ||
199 | |||
200 | if (pb && !ret) | ||
201 | ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true); | ||
202 | } | ||
203 | |||
204 | if (ret) { | ||
205 | /* something went wrong.. unpin what has been pinned */ | ||
206 | for (i = 0; i < nb; i++) { | ||
207 | struct plane *pb = &ofba->planes[i]; | ||
208 | if (pb->paddr) { | ||
209 | unpin(arg, pb->bo); | ||
210 | pb->paddr = 0; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
204 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p) | 218 | struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p) |
205 | { | 219 | { |
206 | struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); | 220 | struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); |
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c index c5625e3461b..55ddc580fe6 100644 --- a/drivers/staging/omapdrm/omap_plane.c +++ b/drivers/staging/omapdrm/omap_plane.c | |||
@@ -17,6 +17,8 @@ | |||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/kfifo.h> | ||
21 | |||
20 | #include "omap_drv.h" | 22 | #include "omap_drv.h" |
21 | 23 | ||
22 | /* some hackery because omapdss has an 'enum omap_plane' (which would be | 24 | /* some hackery because omapdss has an 'enum omap_plane' (which would be |
@@ -46,8 +48,57 @@ struct omap_plane { | |||
46 | 48 | ||
47 | uint32_t nformats; | 49 | uint32_t nformats; |
48 | uint32_t formats[32]; | 50 | uint32_t formats[32]; |
51 | |||
52 | /* for synchronizing access to unpins fifo */ | ||
53 | struct mutex unpin_mutex; | ||
54 | |||
55 | /* set of bo's pending unpin until next END_WIN irq */ | ||
56 | DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); | ||
57 | int num_unpins, pending_num_unpins; | ||
58 | |||
59 | /* for deferred unpin when we need to wait for scanout complete irq */ | ||
60 | struct work_struct work; | ||
61 | }; | ||
62 | |||
63 | /* map from ovl->id to the irq we are interested in for scanout-done */ | ||
64 | static const uint32_t id2irq[] = { | ||
65 | [OMAP_DSS_GFX] = DISPC_IRQ_GFX_END_WIN, | ||
66 | [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN, | ||
67 | [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN, | ||
68 | [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN, | ||
49 | }; | 69 | }; |
50 | 70 | ||
71 | static void dispc_isr(void *arg, uint32_t mask) | ||
72 | { | ||
73 | struct drm_plane *plane = arg; | ||
74 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
75 | struct omap_drm_private *priv = plane->dev->dev_private; | ||
76 | |||
77 | omap_dispc_unregister_isr(dispc_isr, plane, | ||
78 | id2irq[omap_plane->ovl->id]); | ||
79 | |||
80 | queue_work(priv->wq, &omap_plane->work); | ||
81 | } | ||
82 | |||
83 | static void unpin_worker(struct work_struct *work) | ||
84 | { | ||
85 | struct omap_plane *omap_plane = | ||
86 | container_of(work, struct omap_plane, work); | ||
87 | |||
88 | mutex_lock(&omap_plane->unpin_mutex); | ||
89 | DBG("unpinning %d of %d", omap_plane->num_unpins, | ||
90 | omap_plane->num_unpins + omap_plane->pending_num_unpins); | ||
91 | while (omap_plane->num_unpins > 0) { | ||
92 | struct drm_gem_object *bo = NULL; | ||
93 | int ret = kfifo_get(&omap_plane->unpin_fifo, &bo); | ||
94 | WARN_ON(!ret); | ||
95 | omap_gem_put_paddr(bo); | ||
96 | drm_gem_object_unreference_unlocked(bo); | ||
97 | omap_plane->num_unpins--; | ||
98 | } | ||
99 | mutex_unlock(&omap_plane->unpin_mutex); | ||
100 | } | ||
101 | |||
51 | /* push changes down to dss2 */ | 102 | /* push changes down to dss2 */ |
52 | static int commit(struct drm_plane *plane) | 103 | static int commit(struct drm_plane *plane) |
53 | { | 104 | { |
@@ -73,6 +124,11 @@ static int commit(struct drm_plane *plane) | |||
73 | return ret; | 124 | return ret; |
74 | } | 125 | } |
75 | 126 | ||
127 | mutex_lock(&omap_plane->unpin_mutex); | ||
128 | omap_plane->num_unpins += omap_plane->pending_num_unpins; | ||
129 | omap_plane->pending_num_unpins = 0; | ||
130 | mutex_unlock(&omap_plane->unpin_mutex); | ||
131 | |||
76 | /* our encoder doesn't necessarily get a commit() after this, in | 132 | /* our encoder doesn't necessarily get a commit() after this, in |
77 | * particular in the dpms() and mode_set_base() cases, so force the | 133 | * particular in the dpms() and mode_set_base() cases, so force the |
78 | * manager to update: | 134 | * manager to update: |
@@ -85,8 +141,29 @@ static int commit(struct drm_plane *plane) | |||
85 | dev_err(dev->dev, "could not apply settings\n"); | 141 | dev_err(dev->dev, "could not apply settings\n"); |
86 | return ret; | 142 | return ret; |
87 | } | 143 | } |
144 | |||
145 | /* | ||
146 | * NOTE: really this should be atomic w/ mgr->apply() but | ||
147 | * omapdss does not expose such an API | ||
148 | */ | ||
149 | if (omap_plane->num_unpins > 0) { | ||
150 | ret = omap_dispc_register_isr(dispc_isr, | ||
151 | plane, id2irq[ovl->id]); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * omapdss has upper limit on # of registered irq handlers, | ||
156 | * which we shouldn't hit.. but if we do the limit should | ||
157 | * be raised or bad things happen: | ||
158 | */ | ||
159 | WARN_ON(ret == -EBUSY); | ||
160 | |||
161 | } else { | ||
162 | struct omap_drm_private *priv = dev->dev_private; | ||
163 | queue_work(priv->wq, &omap_plane->work); | ||
88 | } | 164 | } |
89 | 165 | ||
166 | |||
90 | if (ovl->is_enabled(ovl)) { | 167 | if (ovl->is_enabled(ovl)) { |
91 | omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, | 168 | omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, |
92 | info->out_width, info->out_height); | 169 | info->out_width, info->out_height); |
@@ -139,21 +216,48 @@ static void update_manager(struct drm_plane *plane) | |||
139 | } | 216 | } |
140 | } | 217 | } |
141 | 218 | ||
219 | static void unpin(void *arg, struct drm_gem_object *bo) | ||
220 | { | ||
221 | struct drm_plane *plane = arg; | ||
222 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
223 | |||
224 | if (kfifo_put(&omap_plane->unpin_fifo, | ||
225 | (const struct drm_gem_object **)&bo)) { | ||
226 | omap_plane->pending_num_unpins++; | ||
227 | /* also hold a ref so it isn't free'd while pinned */ | ||
228 | drm_gem_object_reference(bo); | ||
229 | } else { | ||
230 | dev_err(plane->dev->dev, "unpin fifo full!\n"); | ||
231 | omap_gem_put_paddr(bo); | ||
232 | } | ||
233 | } | ||
234 | |||
142 | /* update which fb (if any) is pinned for scanout */ | 235 | /* update which fb (if any) is pinned for scanout */ |
143 | static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) | 236 | static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) |
144 | { | 237 | { |
145 | struct omap_plane *omap_plane = to_omap_plane(plane); | 238 | struct omap_plane *omap_plane = to_omap_plane(plane); |
146 | int ret = 0; | 239 | struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb; |
240 | |||
241 | if (pinned_fb != fb) { | ||
242 | int ret; | ||
243 | |||
244 | DBG("%p -> %p", pinned_fb, fb); | ||
245 | |||
246 | mutex_lock(&omap_plane->unpin_mutex); | ||
247 | ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin); | ||
248 | mutex_unlock(&omap_plane->unpin_mutex); | ||
249 | |||
250 | if (ret) { | ||
251 | dev_err(plane->dev->dev, "could not swap %p -> %p\n", | ||
252 | omap_plane->pinned_fb, fb); | ||
253 | omap_plane->pinned_fb = NULL; | ||
254 | return ret; | ||
255 | } | ||
147 | 256 | ||
148 | if (omap_plane->pinned_fb != fb) { | ||
149 | if (omap_plane->pinned_fb) | ||
150 | omap_framebuffer_unpin(omap_plane->pinned_fb); | ||
151 | omap_plane->pinned_fb = fb; | 257 | omap_plane->pinned_fb = fb; |
152 | if (fb) | ||
153 | ret = omap_framebuffer_pin(fb); | ||
154 | } | 258 | } |
155 | 259 | ||
156 | return ret; | 260 | return 0; |
157 | } | 261 | } |
158 | 262 | ||
159 | /* update parameters that are dependent on the framebuffer dimensions and | 263 | /* update parameters that are dependent on the framebuffer dimensions and |
@@ -243,6 +347,8 @@ static void omap_plane_destroy(struct drm_plane *plane) | |||
243 | DBG("%s", omap_plane->ovl->name); | 347 | DBG("%s", omap_plane->ovl->name); |
244 | omap_plane_disable(plane); | 348 | omap_plane_disable(plane); |
245 | drm_plane_cleanup(plane); | 349 | drm_plane_cleanup(plane); |
350 | WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0); | ||
351 | kfifo_free(&omap_plane->unpin_fifo); | ||
246 | kfree(omap_plane); | 352 | kfree(omap_plane); |
247 | } | 353 | } |
248 | 354 | ||
@@ -260,8 +366,10 @@ int omap_plane_dpms(struct drm_plane *plane, int mode) | |||
260 | if (!r) | 366 | if (!r) |
261 | r = ovl->enable(ovl); | 367 | r = ovl->enable(ovl); |
262 | } else { | 368 | } else { |
369 | struct omap_drm_private *priv = plane->dev->dev_private; | ||
263 | r = ovl->disable(ovl); | 370 | r = ovl->disable(ovl); |
264 | update_pin(plane, NULL); | 371 | update_pin(plane, NULL); |
372 | queue_work(priv->wq, &omap_plane->work); | ||
265 | } | 373 | } |
266 | 374 | ||
267 | return r; | 375 | return r; |
@@ -280,16 +388,30 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, | |||
280 | { | 388 | { |
281 | struct drm_plane *plane = NULL; | 389 | struct drm_plane *plane = NULL; |
282 | struct omap_plane *omap_plane; | 390 | struct omap_plane *omap_plane; |
391 | int ret; | ||
283 | 392 | ||
284 | DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, | 393 | DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, |
285 | possible_crtcs, priv); | 394 | possible_crtcs, priv); |
286 | 395 | ||
396 | /* friendly reminder to update table for future hw: */ | ||
397 | WARN_ON(ovl->id >= ARRAY_SIZE(id2irq)); | ||
398 | |||
287 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); | 399 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); |
288 | if (!omap_plane) { | 400 | if (!omap_plane) { |
289 | dev_err(dev->dev, "could not allocate plane\n"); | 401 | dev_err(dev->dev, "could not allocate plane\n"); |
290 | goto fail; | 402 | goto fail; |
291 | } | 403 | } |
292 | 404 | ||
405 | mutex_init(&omap_plane->unpin_mutex); | ||
406 | |||
407 | ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); | ||
408 | if (ret) { | ||
409 | dev_err(dev->dev, "could not allocate unpin FIFO\n"); | ||
410 | goto fail; | ||
411 | } | ||
412 | |||
413 | INIT_WORK(&omap_plane->work, unpin_worker); | ||
414 | |||
293 | omap_plane->nformats = omap_framebuffer_get_formats( | 415 | omap_plane->nformats = omap_framebuffer_get_formats( |
294 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), | 416 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), |
295 | ovl->supported_modes); | 417 | ovl->supported_modes); |