2 * drivers/staging/omapdrm/omap_plane.c
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <linux/kfifo.h>
24 /* some hackery because omapdss has an 'enum omap_plane' (which would be
25 * better named omap_plane_id).. and compiler seems unhappy about having
26 * both a 'struct omap_plane' and 'enum omap_plane'
28 #define omap_plane _omap_plane
39 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
42 struct drm_plane base
;
43 struct omap_overlay
*ovl
;
44 struct omap_overlay_info info
;
46 /* Source values, converted to integers because we don't support
47 * fractional positions:
49 unsigned int src_x
, src_y
;
51 /* last fb that we pinned: */
52 struct drm_framebuffer
*pinned_fb
;
57 /* for synchronizing access to unpins fifo */
58 struct mutex unpin_mutex
;
60 /* set of bo's pending unpin until next END_WIN irq */
61 DECLARE_KFIFO_PTR(unpin_fifo
, struct drm_gem_object
*);
62 int num_unpins
, pending_num_unpins
;
64 /* for deferred unpin when we need to wait for scanout complete irq */
65 struct work_struct work
;
67 /* callback on next endwin irq */
68 struct callback endwin
;
71 /* map from ovl->id to the irq we are interested in for scanout-done */
72 static const uint32_t id2irq
[] = {
73 [OMAP_DSS_GFX
] = DISPC_IRQ_GFX_END_WIN
,
74 [OMAP_DSS_VIDEO1
] = DISPC_IRQ_VID1_END_WIN
,
75 [OMAP_DSS_VIDEO2
] = DISPC_IRQ_VID2_END_WIN
,
76 [OMAP_DSS_VIDEO3
] = DISPC_IRQ_VID3_END_WIN
,
79 static void dispc_isr(void *arg
, uint32_t mask
)
81 struct drm_plane
*plane
= arg
;
82 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
83 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
85 omap_dispc_unregister_isr(dispc_isr
, plane
,
86 id2irq
[omap_plane
->ovl
->id
]);
88 queue_work(priv
->wq
, &omap_plane
->work
);
91 static void unpin_worker(struct work_struct
*work
)
93 struct omap_plane
*omap_plane
=
94 container_of(work
, struct omap_plane
, work
);
95 struct callback endwin
;
97 mutex_lock(&omap_plane
->unpin_mutex
);
98 DBG("unpinning %d of %d", omap_plane
->num_unpins
,
99 omap_plane
->num_unpins
+ omap_plane
->pending_num_unpins
);
100 while (omap_plane
->num_unpins
> 0) {
101 struct drm_gem_object
*bo
= NULL
;
102 int ret
= kfifo_get(&omap_plane
->unpin_fifo
, &bo
);
104 omap_gem_put_paddr(bo
);
105 drm_gem_object_unreference_unlocked(bo
);
106 omap_plane
->num_unpins
--;
108 endwin
= omap_plane
->endwin
;
109 omap_plane
->endwin
.fxn
= NULL
;
110 mutex_unlock(&omap_plane
->unpin_mutex
);
113 endwin
.fxn(endwin
.arg
);
116 static void install_irq(struct drm_plane
*plane
)
118 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
119 struct omap_overlay
*ovl
= omap_plane
->ovl
;
122 ret
= omap_dispc_register_isr(dispc_isr
, plane
, id2irq
[ovl
->id
]);
125 * omapdss has upper limit on # of registered irq handlers,
126 * which we shouldn't hit.. but if we do the limit should
127 * be raised or bad things happen:
129 WARN_ON(ret
== -EBUSY
);
132 /* push changes down to dss2 */
133 static int commit(struct drm_plane
*plane
)
135 struct drm_device
*dev
= plane
->dev
;
136 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
137 struct omap_overlay
*ovl
= omap_plane
->ovl
;
138 struct omap_overlay_info
*info
= &omap_plane
->info
;
141 DBG("%s", ovl
->name
);
142 DBG("%dx%d -> %dx%d (%d)", info
->width
, info
->height
, info
->out_width
,
143 info
->out_height
, info
->screen_width
);
144 DBG("%d,%d %08x %08x", info
->pos_x
, info
->pos_y
,
145 info
->paddr
, info
->p_uv_addr
);
147 /* NOTE: do we want to do this at all here, or just wait
148 * for dpms(ON) since other CRTC's may not have their mode
149 * set yet, so fb dimensions may still change..
151 ret
= ovl
->set_overlay_info(ovl
, info
);
153 dev_err(dev
->dev
, "could not set overlay info\n");
157 mutex_lock(&omap_plane
->unpin_mutex
);
158 omap_plane
->num_unpins
+= omap_plane
->pending_num_unpins
;
159 omap_plane
->pending_num_unpins
= 0;
160 mutex_unlock(&omap_plane
->unpin_mutex
);
162 /* our encoder doesn't necessarily get a commit() after this, in
163 * particular in the dpms() and mode_set_base() cases, so force the
166 * could this be in the encoder somehow?
169 ret
= ovl
->manager
->apply(ovl
->manager
);
171 dev_err(dev
->dev
, "could not apply settings\n");
176 * NOTE: really this should be atomic w/ mgr->apply() but
177 * omapdss does not expose such an API
179 if (omap_plane
->num_unpins
> 0)
183 struct omap_drm_private
*priv
= dev
->dev_private
;
184 queue_work(priv
->wq
, &omap_plane
->work
);
188 if (ovl
->is_enabled(ovl
)) {
189 omap_framebuffer_flush(plane
->fb
, info
->pos_x
, info
->pos_y
,
190 info
->out_width
, info
->out_height
);
196 /* when CRTC that we are attached to has potentially changed, this checks
197 * if we are attached to proper manager, and if necessary updates.
199 static void update_manager(struct drm_plane
*plane
)
201 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
202 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
203 struct omap_overlay
*ovl
= omap_plane
->ovl
;
204 struct omap_overlay_manager
*mgr
= NULL
;
208 for (i
= 0; i
< priv
->num_encoders
; i
++) {
209 struct drm_encoder
*encoder
= priv
->encoders
[i
];
210 if (encoder
->crtc
== plane
->crtc
) {
211 mgr
= omap_encoder_get_manager(encoder
);
217 if (ovl
->manager
!= mgr
) {
218 bool enabled
= ovl
->is_enabled(ovl
);
220 /* don't switch things around with enabled overlays: */
222 omap_plane_dpms(plane
, DRM_MODE_DPMS_OFF
);
225 DBG("disconnecting %s from %s", ovl
->name
,
227 ovl
->unset_manager(ovl
);
231 DBG("connecting %s to %s", ovl
->name
, mgr
->name
);
232 ovl
->set_manager(ovl
, mgr
);
236 omap_plane_dpms(plane
, DRM_MODE_DPMS_ON
);
240 static void unpin(void *arg
, struct drm_gem_object
*bo
)
242 struct drm_plane
*plane
= arg
;
243 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
245 if (kfifo_put(&omap_plane
->unpin_fifo
,
246 (const struct drm_gem_object
**)&bo
)) {
247 omap_plane
->pending_num_unpins
++;
248 /* also hold a ref so it isn't free'd while pinned */
249 drm_gem_object_reference(bo
);
251 dev_err(plane
->dev
->dev
, "unpin fifo full!\n");
252 omap_gem_put_paddr(bo
);
256 /* update which fb (if any) is pinned for scanout */
257 static int update_pin(struct drm_plane
*plane
, struct drm_framebuffer
*fb
)
259 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
260 struct drm_framebuffer
*pinned_fb
= omap_plane
->pinned_fb
;
262 if (pinned_fb
!= fb
) {
265 DBG("%p -> %p", pinned_fb
, fb
);
267 mutex_lock(&omap_plane
->unpin_mutex
);
268 ret
= omap_framebuffer_replace(pinned_fb
, fb
, plane
, unpin
);
269 mutex_unlock(&omap_plane
->unpin_mutex
);
272 dev_err(plane
->dev
->dev
, "could not swap %p -> %p\n",
273 omap_plane
->pinned_fb
, fb
);
274 omap_plane
->pinned_fb
= NULL
;
278 omap_plane
->pinned_fb
= fb
;
284 /* update parameters that are dependent on the framebuffer dimensions and
285 * position within the fb that this plane scans out from. This is called
286 * when framebuffer or x,y base may have changed.
288 static void update_scanout(struct drm_plane
*plane
)
290 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
291 struct omap_overlay_info
*info
= &omap_plane
->info
;
294 ret
= update_pin(plane
, plane
->fb
);
296 dev_err(plane
->dev
->dev
,
297 "could not pin fb: %d\n", ret
);
298 omap_plane_dpms(plane
, DRM_MODE_DPMS_OFF
);
302 omap_framebuffer_update_scanout(plane
->fb
,
303 omap_plane
->src_x
, omap_plane
->src_y
, info
);
305 DBG("%s: %d,%d: %08x %08x (%d)", omap_plane
->ovl
->name
,
306 omap_plane
->src_x
, omap_plane
->src_y
,
307 (u32
)info
->paddr
, (u32
)info
->p_uv_addr
,
311 int omap_plane_mode_set(struct drm_plane
*plane
,
312 struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
313 int crtc_x
, int crtc_y
,
314 unsigned int crtc_w
, unsigned int crtc_h
,
315 uint32_t src_x
, uint32_t src_y
,
316 uint32_t src_w
, uint32_t src_h
)
318 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
320 /* src values are in Q16 fixed point, convert to integer: */
326 omap_plane
->info
.pos_x
= crtc_x
;
327 omap_plane
->info
.pos_y
= crtc_y
;
328 omap_plane
->info
.out_width
= crtc_w
;
329 omap_plane
->info
.out_height
= crtc_h
;
330 omap_plane
->info
.width
= src_w
;
331 omap_plane
->info
.height
= src_h
;
332 omap_plane
->src_x
= src_x
;
333 omap_plane
->src_y
= src_y
;
335 /* note: this is done after this fxn returns.. but if we need
336 * to do a commit/update_scanout, etc before this returns we
337 * need the current value.
342 update_scanout(plane
);
343 update_manager(plane
);
348 static int omap_plane_update(struct drm_plane
*plane
,
349 struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
350 int crtc_x
, int crtc_y
,
351 unsigned int crtc_w
, unsigned int crtc_h
,
352 uint32_t src_x
, uint32_t src_y
,
353 uint32_t src_w
, uint32_t src_h
)
355 omap_plane_mode_set(plane
, crtc
, fb
, crtc_x
, crtc_y
, crtc_w
, crtc_h
,
356 src_x
, src_y
, src_w
, src_h
);
357 return omap_plane_dpms(plane
, DRM_MODE_DPMS_ON
);
360 static int omap_plane_disable(struct drm_plane
*plane
)
362 return omap_plane_dpms(plane
, DRM_MODE_DPMS_OFF
);
365 static void omap_plane_destroy(struct drm_plane
*plane
)
367 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
368 DBG("%s", omap_plane
->ovl
->name
);
369 omap_plane_disable(plane
);
370 drm_plane_cleanup(plane
);
371 WARN_ON(omap_plane
->pending_num_unpins
+ omap_plane
->num_unpins
> 0);
372 kfifo_free(&omap_plane
->unpin_fifo
);
376 int omap_plane_dpms(struct drm_plane
*plane
, int mode
)
378 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
379 struct omap_overlay
*ovl
= omap_plane
->ovl
;
382 DBG("%s: %d", omap_plane
->ovl
->name
, mode
);
384 if (mode
== DRM_MODE_DPMS_ON
) {
385 update_scanout(plane
);
388 r
= ovl
->enable(ovl
);
390 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
391 r
= ovl
->disable(ovl
);
392 update_pin(plane
, NULL
);
393 queue_work(priv
->wq
, &omap_plane
->work
);
399 void omap_plane_on_endwin(struct drm_plane
*plane
,
400 void (*fxn
)(void *), void *arg
)
402 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
404 mutex_lock(&omap_plane
->unpin_mutex
);
405 omap_plane
->endwin
.fxn
= fxn
;
406 omap_plane
->endwin
.arg
= arg
;
407 mutex_unlock(&omap_plane
->unpin_mutex
);
412 static const struct drm_plane_funcs omap_plane_funcs
= {
413 .update_plane
= omap_plane_update
,
414 .disable_plane
= omap_plane_disable
,
415 .destroy
= omap_plane_destroy
,
418 /* initialize plane */
419 struct drm_plane
*omap_plane_init(struct drm_device
*dev
,
420 struct omap_overlay
*ovl
, unsigned int possible_crtcs
,
423 struct drm_plane
*plane
= NULL
;
424 struct omap_plane
*omap_plane
;
427 DBG("%s: possible_crtcs=%08x, priv=%d", ovl
->name
,
428 possible_crtcs
, priv
);
430 /* friendly reminder to update table for future hw: */
431 WARN_ON(ovl
->id
>= ARRAY_SIZE(id2irq
));
433 omap_plane
= kzalloc(sizeof(*omap_plane
), GFP_KERNEL
);
435 dev_err(dev
->dev
, "could not allocate plane\n");
439 mutex_init(&omap_plane
->unpin_mutex
);
441 ret
= kfifo_alloc(&omap_plane
->unpin_fifo
, 16, GFP_KERNEL
);
443 dev_err(dev
->dev
, "could not allocate unpin FIFO\n");
447 INIT_WORK(&omap_plane
->work
, unpin_worker
);
449 omap_plane
->nformats
= omap_framebuffer_get_formats(
450 omap_plane
->formats
, ARRAY_SIZE(omap_plane
->formats
),
451 ovl
->supported_modes
);
452 omap_plane
->ovl
= ovl
;
453 plane
= &omap_plane
->base
;
455 drm_plane_init(dev
, plane
, possible_crtcs
, &omap_plane_funcs
,
456 omap_plane
->formats
, omap_plane
->nformats
, priv
);
458 /* get our starting configuration, set defaults for parameters
459 * we don't currently use, etc:
461 ovl
->get_overlay_info(ovl
, &omap_plane
->info
);
462 omap_plane
->info
.rotation_type
= OMAP_DSS_ROT_DMA
;
463 omap_plane
->info
.rotation
= OMAP_DSS_ROT_0
;
464 omap_plane
->info
.global_alpha
= 0xff;
465 omap_plane
->info
.mirror
= 0;
466 omap_plane
->info
.mirror
= 0;
468 /* Set defaults depending on whether we are a CRTC or overlay
470 * TODO add ioctl to give userspace an API to change this.. this
471 * will come in a subsequent patch.
474 omap_plane
->info
.zorder
= 0;
476 omap_plane
->info
.zorder
= ovl
->id
;
478 update_manager(plane
);
484 omap_plane_destroy(plane
);