2 * drivers/gpu/drm/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 "drm_flip_work.h"
23 #include "omap_dmm_tiler.h"
25 /* some hackery because omapdss has an 'enum omap_plane' (which would be
26 * better named omap_plane_id).. and compiler seems unhappy about having
27 * both a 'struct omap_plane' and 'enum omap_plane'
29 #define omap_plane _omap_plane
40 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
43 struct drm_plane base
;
44 int id
; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
46 struct omap_overlay_info info
;
47 struct omap_drm_apply apply
;
49 /* position/orientation of scanout within the fb: */
50 struct omap_drm_window win
;
53 /* last fb that we pinned: */
54 struct drm_framebuffer
*pinned_fb
;
59 struct omap_drm_irq error_irq
;
61 /* for deferring bo unpin's until next post_apply(): */
62 struct drm_flip_work unpin_work
;
64 // XXX maybe get rid of this and handle vblank in crtc too?
65 struct callback apply_done_cb
;
68 static void unpin_worker(struct drm_flip_work
*work
, void *val
)
70 struct omap_plane
*omap_plane
=
71 container_of(work
, struct omap_plane
, unpin_work
);
72 struct drm_device
*dev
= omap_plane
->base
.dev
;
74 omap_framebuffer_unpin(val
);
75 mutex_lock(&dev
->mode_config
.mutex
);
76 drm_framebuffer_unreference(val
);
77 mutex_unlock(&dev
->mode_config
.mutex
);
80 /* update which fb (if any) is pinned for scanout */
81 static int update_pin(struct drm_plane
*plane
, struct drm_framebuffer
*fb
)
83 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
84 struct drm_framebuffer
*pinned_fb
= omap_plane
->pinned_fb
;
86 if (pinned_fb
!= fb
) {
89 DBG("%p -> %p", pinned_fb
, fb
);
92 drm_framebuffer_reference(fb
);
93 ret
= omap_framebuffer_pin(fb
);
97 drm_flip_work_queue(&omap_plane
->unpin_work
, pinned_fb
);
100 dev_err(plane
->dev
->dev
, "could not swap %p -> %p\n",
101 omap_plane
->pinned_fb
, fb
);
102 drm_framebuffer_unreference(fb
);
103 omap_plane
->pinned_fb
= NULL
;
107 omap_plane
->pinned_fb
= fb
;
113 static void omap_plane_pre_apply(struct omap_drm_apply
*apply
)
115 struct omap_plane
*omap_plane
=
116 container_of(apply
, struct omap_plane
, apply
);
117 struct omap_drm_window
*win
= &omap_plane
->win
;
118 struct drm_plane
*plane
= &omap_plane
->base
;
119 struct drm_device
*dev
= plane
->dev
;
120 struct omap_overlay_info
*info
= &omap_plane
->info
;
121 struct drm_crtc
*crtc
= plane
->crtc
;
122 enum omap_channel channel
;
123 bool enabled
= omap_plane
->enabled
&& crtc
;
124 bool ilace
, replication
;
127 DBG("%s, enabled=%d", omap_plane
->name
, enabled
);
129 /* if fb has changed, pin new fb: */
130 update_pin(plane
, enabled
? plane
->fb
: NULL
);
133 dispc_ovl_enable(omap_plane
->id
, false);
137 channel
= omap_crtc_channel(crtc
);
139 /* update scanout: */
140 omap_framebuffer_update_scanout(plane
->fb
, win
, info
);
142 DBG("%dx%d -> %dx%d (%d)", info
->width
, info
->height
,
143 info
->out_width
, info
->out_height
,
145 DBG("%d,%d %08x %08x", info
->pos_x
, info
->pos_y
,
146 info
->paddr
, info
->p_uv_addr
);
152 /* and finally, update omapdss: */
153 ret
= dispc_ovl_setup(omap_plane
->id
, info
,
154 replication
, omap_crtc_timings(crtc
), false);
156 dev_err(dev
->dev
, "dispc_ovl_setup failed: %d\n", ret
);
160 dispc_ovl_enable(omap_plane
->id
, true);
161 dispc_ovl_set_channel_out(omap_plane
->id
, channel
);
164 static void omap_plane_post_apply(struct omap_drm_apply
*apply
)
166 struct omap_plane
*omap_plane
=
167 container_of(apply
, struct omap_plane
, apply
);
168 struct drm_plane
*plane
= &omap_plane
->base
;
169 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
170 struct omap_overlay_info
*info
= &omap_plane
->info
;
173 cb
= omap_plane
->apply_done_cb
;
174 omap_plane
->apply_done_cb
.fxn
= NULL
;
176 drm_flip_work_commit(&omap_plane
->unpin_work
, priv
->wq
);
181 if (omap_plane
->enabled
) {
182 omap_framebuffer_flush(plane
->fb
, info
->pos_x
, info
->pos_y
,
183 info
->out_width
, info
->out_height
);
187 static int apply(struct drm_plane
*plane
)
190 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
191 return omap_crtc_apply(plane
->crtc
, &omap_plane
->apply
);
196 int omap_plane_mode_set(struct drm_plane
*plane
,
197 struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
198 int crtc_x
, int crtc_y
,
199 unsigned int crtc_w
, unsigned int crtc_h
,
200 uint32_t src_x
, uint32_t src_y
,
201 uint32_t src_w
, uint32_t src_h
,
202 void (*fxn
)(void *), void *arg
)
204 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
205 struct omap_drm_window
*win
= &omap_plane
->win
;
207 win
->crtc_x
= crtc_x
;
208 win
->crtc_y
= crtc_y
;
209 win
->crtc_w
= crtc_w
;
210 win
->crtc_h
= crtc_h
;
212 /* src values are in Q16 fixed point, convert to integer: */
213 win
->src_x
= src_x
>> 16;
214 win
->src_y
= src_y
>> 16;
215 win
->src_w
= src_w
>> 16;
216 win
->src_h
= src_h
>> 16;
219 /* omap_crtc should ensure that a new page flip
220 * isn't permitted while there is one pending:
222 BUG_ON(omap_plane
->apply_done_cb
.fxn
);
224 omap_plane
->apply_done_cb
.fxn
= fxn
;
225 omap_plane
->apply_done_cb
.arg
= arg
;
234 static int omap_plane_update(struct drm_plane
*plane
,
235 struct drm_crtc
*crtc
, struct drm_framebuffer
*fb
,
236 int crtc_x
, int crtc_y
,
237 unsigned int crtc_w
, unsigned int crtc_h
,
238 uint32_t src_x
, uint32_t src_y
,
239 uint32_t src_w
, uint32_t src_h
)
241 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
242 omap_plane
->enabled
= true;
245 drm_framebuffer_unreference(plane
->fb
);
247 drm_framebuffer_reference(fb
);
249 return omap_plane_mode_set(plane
, crtc
, fb
,
250 crtc_x
, crtc_y
, crtc_w
, crtc_h
,
251 src_x
, src_y
, src_w
, src_h
,
255 static int omap_plane_disable(struct drm_plane
*plane
)
257 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
258 omap_plane
->win
.rotation
= BIT(DRM_ROTATE_0
);
259 return omap_plane_dpms(plane
, DRM_MODE_DPMS_OFF
);
262 static void omap_plane_destroy(struct drm_plane
*plane
)
264 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
266 DBG("%s", omap_plane
->name
);
268 omap_irq_unregister(plane
->dev
, &omap_plane
->error_irq
);
270 omap_plane_disable(plane
);
271 drm_plane_cleanup(plane
);
273 drm_flip_work_cleanup(&omap_plane
->unpin_work
);
278 int omap_plane_dpms(struct drm_plane
*plane
, int mode
)
280 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
281 bool enabled
= (mode
== DRM_MODE_DPMS_ON
);
284 if (enabled
!= omap_plane
->enabled
) {
285 omap_plane
->enabled
= enabled
;
292 /* helper to install properties which are common to planes and crtcs */
293 void omap_plane_install_properties(struct drm_plane
*plane
,
294 struct drm_mode_object
*obj
)
296 struct drm_device
*dev
= plane
->dev
;
297 struct omap_drm_private
*priv
= dev
->dev_private
;
298 struct drm_property
*prop
;
301 prop
= priv
->rotation_prop
;
303 const struct drm_prop_enum_list props
[] = {
304 { DRM_ROTATE_0
, "rotate-0" },
305 { DRM_ROTATE_90
, "rotate-90" },
306 { DRM_ROTATE_180
, "rotate-180" },
307 { DRM_ROTATE_270
, "rotate-270" },
308 { DRM_REFLECT_X
, "reflect-x" },
309 { DRM_REFLECT_Y
, "reflect-y" },
311 prop
= drm_property_create_bitmask(dev
, 0, "rotation",
312 props
, ARRAY_SIZE(props
));
315 priv
->rotation_prop
= prop
;
317 drm_object_attach_property(obj
, prop
, 0);
320 prop
= priv
->zorder_prop
;
322 prop
= drm_property_create_range(dev
, 0, "zorder", 0, 3);
325 priv
->zorder_prop
= prop
;
327 drm_object_attach_property(obj
, prop
, 0);
330 int omap_plane_set_property(struct drm_plane
*plane
,
331 struct drm_property
*property
, uint64_t val
)
333 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
334 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
337 if (property
== priv
->rotation_prop
) {
338 DBG("%s: rotation: %02x", omap_plane
->name
, (uint32_t)val
);
339 omap_plane
->win
.rotation
= val
;
341 } else if (property
== priv
->zorder_prop
) {
342 DBG("%s: zorder: %02x", omap_plane
->name
, (uint32_t)val
);
343 omap_plane
->info
.zorder
= val
;
350 static const struct drm_plane_funcs omap_plane_funcs
= {
351 .update_plane
= omap_plane_update
,
352 .disable_plane
= omap_plane_disable
,
353 .destroy
= omap_plane_destroy
,
354 .set_property
= omap_plane_set_property
,
357 static void omap_plane_error_irq(struct omap_drm_irq
*irq
, uint32_t irqstatus
)
359 struct omap_plane
*omap_plane
=
360 container_of(irq
, struct omap_plane
, error_irq
);
361 DRM_ERROR("%s: errors: %08x\n", omap_plane
->name
, irqstatus
);
364 static const char *plane_names
[] = {
365 [OMAP_DSS_GFX
] = "gfx",
366 [OMAP_DSS_VIDEO1
] = "vid1",
367 [OMAP_DSS_VIDEO2
] = "vid2",
368 [OMAP_DSS_VIDEO3
] = "vid3",
371 static const uint32_t error_irqs
[] = {
372 [OMAP_DSS_GFX
] = DISPC_IRQ_GFX_FIFO_UNDERFLOW
,
373 [OMAP_DSS_VIDEO1
] = DISPC_IRQ_VID1_FIFO_UNDERFLOW
,
374 [OMAP_DSS_VIDEO2
] = DISPC_IRQ_VID2_FIFO_UNDERFLOW
,
375 [OMAP_DSS_VIDEO3
] = DISPC_IRQ_VID3_FIFO_UNDERFLOW
,
378 /* initialize plane */
379 struct drm_plane
*omap_plane_init(struct drm_device
*dev
,
380 int id
, bool private_plane
)
382 struct omap_drm_private
*priv
= dev
->dev_private
;
383 struct drm_plane
*plane
= NULL
;
384 struct omap_plane
*omap_plane
;
385 struct omap_overlay_info
*info
;
388 DBG("%s: priv=%d", plane_names
[id
], private_plane
);
390 omap_plane
= kzalloc(sizeof(*omap_plane
), GFP_KERNEL
);
394 ret
= drm_flip_work_init(&omap_plane
->unpin_work
, 16,
395 "unpin", unpin_worker
);
397 dev_err(dev
->dev
, "could not allocate unpin FIFO\n");
401 omap_plane
->nformats
= omap_framebuffer_get_formats(
402 omap_plane
->formats
, ARRAY_SIZE(omap_plane
->formats
),
403 dss_feat_get_supported_color_modes(id
));
405 omap_plane
->name
= plane_names
[id
];
407 plane
= &omap_plane
->base
;
409 omap_plane
->apply
.pre_apply
= omap_plane_pre_apply
;
410 omap_plane
->apply
.post_apply
= omap_plane_post_apply
;
412 omap_plane
->error_irq
.irqmask
= error_irqs
[id
];
413 omap_plane
->error_irq
.irq
= omap_plane_error_irq
;
414 omap_irq_register(dev
, &omap_plane
->error_irq
);
416 drm_plane_init(dev
, plane
, (1 << priv
->num_crtcs
) - 1, &omap_plane_funcs
,
417 omap_plane
->formats
, omap_plane
->nformats
, private_plane
);
419 omap_plane_install_properties(plane
, &plane
->base
);
421 /* get our starting configuration, set defaults for parameters
422 * we don't currently use, etc:
424 info
= &omap_plane
->info
;
425 info
->rotation_type
= OMAP_DSS_ROT_DMA
;
426 info
->rotation
= OMAP_DSS_ROT_0
;
427 info
->global_alpha
= 0xff;
430 /* Set defaults depending on whether we are a CRTC or overlay
432 * TODO add ioctl to give userspace an API to change this.. this
433 * will come in a subsequent patch.
436 omap_plane
->info
.zorder
= 0;
438 omap_plane
->info
.zorder
= id
;
444 omap_plane_destroy(plane
);