Commit | Line | Data |
---|---|---|
bb5c2d9a | 1 | /* |
8bb0daff | 2 | * drivers/gpu/drm/omapdrm/omap_plane.c |
bb5c2d9a RC |
3 | * |
4 | * Copyright (C) 2011 Texas Instruments | |
5 | * Author: Rob Clark <rob.clark@linaro.org> | |
6 | * | |
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. | |
10 | * | |
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 | |
14 | * more details. | |
15 | * | |
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/>. | |
18 | */ | |
19 | ||
c423bc85 | 20 | #include <drm/drm_atomic.h> |
69a12263 | 21 | #include <drm/drm_atomic_helper.h> |
de8e4100 | 22 | #include <drm/drm_plane_helper.h> |
69a12263 | 23 | |
3c810c61 | 24 | #include "omap_dmm_tiler.h" |
2d278f54 | 25 | #include "omap_drv.h" |
bb5c2d9a RC |
26 | |
27 | /* some hackery because omapdss has an 'enum omap_plane' (which would be | |
28 | * better named omap_plane_id).. and compiler seems unhappy about having | |
29 | * both a 'struct omap_plane' and 'enum omap_plane' | |
30 | */ | |
31 | #define omap_plane _omap_plane | |
32 | ||
33 | /* | |
34 | * plane funcs | |
35 | */ | |
36 | ||
37 | #define to_omap_plane(x) container_of(x, struct omap_plane, base) | |
38 | ||
39 | struct omap_plane { | |
40 | struct drm_plane base; | |
f5f9454c RC |
41 | int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ |
42 | const char *name; | |
bb5c2d9a | 43 | |
a890e662 RC |
44 | uint32_t nformats; |
45 | uint32_t formats[32]; | |
b33f34d3 | 46 | |
f5f9454c | 47 | struct omap_drm_irq error_irq; |
a890e662 | 48 | }; |
bb5c2d9a | 49 | |
afc34932 LP |
50 | struct omap_plane_state { |
51 | struct drm_plane_state base; | |
52 | ||
53 | unsigned int zorder; | |
54 | }; | |
55 | ||
56 | static inline struct omap_plane_state * | |
57 | to_omap_plane_state(struct drm_plane_state *state) | |
bb5c2d9a | 58 | { |
afc34932 LP |
59 | return container_of(state, struct omap_plane_state, base); |
60 | } | |
61 | ||
11ffd031 | 62 | static int omap_plane_prepare_fb(struct drm_plane *plane, |
1832040d | 63 | struct drm_plane_state *new_state) |
11ffd031 | 64 | { |
844f9111 ML |
65 | if (!new_state->fb) |
66 | return 0; | |
67 | ||
68 | return omap_framebuffer_pin(new_state->fb); | |
11ffd031 TV |
69 | } |
70 | ||
71 | static void omap_plane_cleanup_fb(struct drm_plane *plane, | |
1832040d | 72 | struct drm_plane_state *old_state) |
11ffd031 | 73 | { |
844f9111 ML |
74 | if (old_state->fb) |
75 | omap_framebuffer_unpin(old_state->fb); | |
11ffd031 TV |
76 | } |
77 | ||
78 | static void omap_plane_atomic_update(struct drm_plane *plane, | |
79 | struct drm_plane_state *old_state) | |
afc34932 | 80 | { |
edc72557 LP |
81 | struct omap_plane *omap_plane = to_omap_plane(plane); |
82 | struct drm_plane_state *state = plane->state; | |
afc34932 | 83 | struct omap_plane_state *omap_state = to_omap_plane_state(state); |
fb730c9b LP |
84 | struct omap_overlay_info info; |
85 | struct omap_drm_window win; | |
9a0774e0 | 86 | int ret; |
bb5c2d9a | 87 | |
edc72557 | 88 | DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); |
f5f9454c | 89 | |
fb730c9b LP |
90 | memset(&info, 0, sizeof(info)); |
91 | info.rotation_type = OMAP_DSS_ROT_DMA; | |
92 | info.rotation = OMAP_DSS_ROT_0; | |
93 | info.global_alpha = 0xff; | |
94 | info.mirror = 0; | |
95 | info.zorder = omap_state->zorder; | |
96 | ||
97 | memset(&win, 0, sizeof(win)); | |
98 | win.rotation = state->rotation; | |
99 | win.crtc_x = state->crtc_x; | |
100 | win.crtc_y = state->crtc_y; | |
101 | win.crtc_w = state->crtc_w; | |
102 | win.crtc_h = state->crtc_h; | |
103 | ||
104 | /* | |
105 | * src values are in Q16 fixed point, convert to integer. | |
106 | * omap_framebuffer_update_scanout() takes adjusted src. | |
107 | */ | |
108 | win.src_x = state->src_x >> 16; | |
109 | win.src_y = state->src_y >> 16; | |
110 | ||
14152c8d | 111 | switch (state->rotation & DRM_ROTATE_MASK) { |
31ad61e4 JL |
112 | case DRM_ROTATE_90: |
113 | case DRM_ROTATE_270: | |
fb730c9b LP |
114 | win.src_w = state->src_h >> 16; |
115 | win.src_h = state->src_w >> 16; | |
116 | break; | |
117 | default: | |
118 | win.src_w = state->src_w >> 16; | |
119 | win.src_h = state->src_h >> 16; | |
120 | break; | |
121 | } | |
afc34932 | 122 | |
f5f9454c | 123 | /* update scanout: */ |
fb730c9b | 124 | omap_framebuffer_update_scanout(state->fb, &win, &info); |
bb5c2d9a | 125 | |
fb730c9b LP |
126 | DBG("%dx%d -> %dx%d (%d)", info.width, info.height, |
127 | info.out_width, info.out_height, | |
128 | info.screen_width); | |
129 | DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, | |
130 | &info.paddr, &info.p_uv_addr); | |
f5f9454c | 131 | |
a42133a7 | 132 | dispc_ovl_set_channel_out(omap_plane->id, |
afc34932 | 133 | omap_crtc_channel(state->crtc)); |
2dd3887b | 134 | |
f5f9454c | 135 | /* and finally, update omapdss: */ |
fb730c9b | 136 | ret = dispc_ovl_setup(omap_plane->id, &info, false, |
afc34932 | 137 | omap_crtc_timings(state->crtc), false); |
794a65ff TV |
138 | if (WARN_ON(ret)) { |
139 | dispc_ovl_enable(omap_plane->id, false); | |
d9157dfd | 140 | return; |
794a65ff | 141 | } |
f5f9454c RC |
142 | |
143 | dispc_ovl_enable(omap_plane->id, true); | |
de8e4100 LP |
144 | } |
145 | ||
de8e4100 LP |
146 | static void omap_plane_atomic_disable(struct drm_plane *plane, |
147 | struct drm_plane_state *old_state) | |
bb5c2d9a | 148 | { |
afc34932 | 149 | struct omap_plane_state *omap_state = to_omap_plane_state(plane->state); |
3c810c61 | 150 | struct omap_plane *omap_plane = to_omap_plane(plane); |
2debab97 | 151 | |
31ad61e4 | 152 | plane->state->rotation = DRM_ROTATE_0; |
afc34932 LP |
153 | omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY |
154 | ? 0 : omap_plane->id; | |
82e58855 | 155 | |
11ffd031 | 156 | dispc_ovl_enable(omap_plane->id, false); |
bb5c2d9a RC |
157 | } |
158 | ||
c423bc85 TV |
159 | static int omap_plane_atomic_check(struct drm_plane *plane, |
160 | struct drm_plane_state *state) | |
161 | { | |
162 | struct drm_crtc_state *crtc_state; | |
163 | ||
164 | if (!state->crtc) | |
165 | return 0; | |
166 | ||
167 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | |
168 | if (IS_ERR(crtc_state)) | |
169 | return PTR_ERR(crtc_state); | |
170 | ||
171 | if (state->crtc_x < 0 || state->crtc_y < 0) | |
172 | return -EINVAL; | |
173 | ||
174 | if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) | |
175 | return -EINVAL; | |
176 | ||
177 | if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) | |
178 | return -EINVAL; | |
179 | ||
bfeece55 | 180 | if (state->fb) { |
31ad61e4 | 181 | if (state->rotation != DRM_ROTATE_0 && |
bfeece55 TV |
182 | !omap_framebuffer_supports_rotation(state->fb)) |
183 | return -EINVAL; | |
184 | } | |
185 | ||
c423bc85 TV |
186 | return 0; |
187 | } | |
188 | ||
de8e4100 LP |
189 | static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { |
190 | .prepare_fb = omap_plane_prepare_fb, | |
191 | .cleanup_fb = omap_plane_cleanup_fb, | |
c423bc85 | 192 | .atomic_check = omap_plane_atomic_check, |
de8e4100 LP |
193 | .atomic_update = omap_plane_atomic_update, |
194 | .atomic_disable = omap_plane_atomic_disable, | |
195 | }; | |
196 | ||
bb5c2d9a RC |
197 | static void omap_plane_destroy(struct drm_plane *plane) |
198 | { | |
199 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
f5f9454c RC |
200 | |
201 | DBG("%s", omap_plane->name); | |
202 | ||
203 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
204 | ||
bb5c2d9a | 205 | drm_plane_cleanup(plane); |
f5f9454c | 206 | |
bb5c2d9a RC |
207 | kfree(omap_plane); |
208 | } | |
209 | ||
3c810c61 RC |
210 | /* helper to install properties which are common to planes and crtcs */ |
211 | void omap_plane_install_properties(struct drm_plane *plane, | |
212 | struct drm_mode_object *obj) | |
213 | { | |
214 | struct drm_device *dev = plane->dev; | |
215 | struct omap_drm_private *priv = dev->dev_private; | |
3c810c61 | 216 | |
c2a6a552 | 217 | if (priv->has_dmm) { |
e2cd09b2 LP |
218 | struct drm_property *prop = dev->mode_config.rotation_property; |
219 | ||
c2a6a552 | 220 | drm_object_attach_property(obj, prop, 0); |
3c810c61 | 221 | } |
8451b5ad | 222 | |
e2cd09b2 | 223 | drm_object_attach_property(obj, priv->zorder_prop, 0); |
3c810c61 RC |
224 | } |
225 | ||
afc34932 LP |
226 | static struct drm_plane_state * |
227 | omap_plane_atomic_duplicate_state(struct drm_plane *plane) | |
228 | { | |
229 | struct omap_plane_state *state; | |
230 | struct omap_plane_state *copy; | |
231 | ||
232 | if (WARN_ON(!plane->state)) | |
233 | return NULL; | |
234 | ||
235 | state = to_omap_plane_state(plane->state); | |
236 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | |
237 | if (copy == NULL) | |
238 | return NULL; | |
239 | ||
240 | __drm_atomic_helper_plane_duplicate_state(plane, ©->base); | |
241 | ||
242 | return ©->base; | |
243 | } | |
244 | ||
245 | static void omap_plane_atomic_destroy_state(struct drm_plane *plane, | |
246 | struct drm_plane_state *state) | |
247 | { | |
2f701695 | 248 | __drm_atomic_helper_plane_destroy_state(state); |
afc34932 LP |
249 | kfree(to_omap_plane_state(state)); |
250 | } | |
251 | ||
e07323cf TV |
252 | static void omap_plane_reset(struct drm_plane *plane) |
253 | { | |
254 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
255 | struct omap_plane_state *omap_state; | |
256 | ||
f8ef29ee LP |
257 | if (plane->state) { |
258 | omap_plane_atomic_destroy_state(plane, plane->state); | |
259 | plane->state = NULL; | |
260 | } | |
e07323cf TV |
261 | |
262 | omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); | |
263 | if (omap_state == NULL) | |
264 | return; | |
265 | ||
266 | /* | |
267 | * Set defaults depending on whether we are a primary or overlay | |
268 | * plane. | |
269 | */ | |
270 | omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY | |
271 | ? 0 : omap_plane->id; | |
31ad61e4 | 272 | omap_state->base.rotation = DRM_ROTATE_0; |
e07323cf TV |
273 | |
274 | plane->state = &omap_state->base; | |
275 | plane->state->plane = plane; | |
276 | } | |
277 | ||
afc34932 LP |
278 | static int omap_plane_atomic_set_property(struct drm_plane *plane, |
279 | struct drm_plane_state *state, | |
280 | struct drm_property *property, | |
281 | uint64_t val) | |
3c810c61 | 282 | { |
3c810c61 | 283 | struct omap_drm_private *priv = plane->dev->dev_private; |
afc34932 | 284 | struct omap_plane_state *omap_state = to_omap_plane_state(state); |
3c810c61 | 285 | |
afc34932 LP |
286 | if (property == priv->zorder_prop) |
287 | omap_state->zorder = val; | |
288 | else | |
a42133a7 | 289 | return -EINVAL; |
3c810c61 | 290 | |
afc34932 LP |
291 | return 0; |
292 | } | |
a42133a7 | 293 | |
afc34932 LP |
294 | static int omap_plane_atomic_get_property(struct drm_plane *plane, |
295 | const struct drm_plane_state *state, | |
296 | struct drm_property *property, | |
297 | uint64_t *val) | |
298 | { | |
299 | struct omap_drm_private *priv = plane->dev->dev_private; | |
300 | const struct omap_plane_state *omap_state = | |
301 | container_of(state, const struct omap_plane_state, base); | |
302 | ||
303 | if (property == priv->zorder_prop) | |
304 | *val = omap_state->zorder; | |
305 | else | |
306 | return -EINVAL; | |
a42133a7 | 307 | |
afc34932 | 308 | return 0; |
3c810c61 RC |
309 | } |
310 | ||
bb5c2d9a | 311 | static const struct drm_plane_funcs omap_plane_funcs = { |
cef77d40 LP |
312 | .update_plane = drm_atomic_helper_update_plane, |
313 | .disable_plane = drm_atomic_helper_disable_plane, | |
afc34932 | 314 | .reset = omap_plane_reset, |
222025e4 | 315 | .destroy = omap_plane_destroy, |
afc34932 LP |
316 | .set_property = drm_atomic_helper_plane_set_property, |
317 | .atomic_duplicate_state = omap_plane_atomic_duplicate_state, | |
318 | .atomic_destroy_state = omap_plane_atomic_destroy_state, | |
319 | .atomic_set_property = omap_plane_atomic_set_property, | |
320 | .atomic_get_property = omap_plane_atomic_get_property, | |
bb5c2d9a RC |
321 | }; |
322 | ||
f5f9454c RC |
323 | static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
324 | { | |
325 | struct omap_plane *omap_plane = | |
326 | container_of(irq, struct omap_plane, error_irq); | |
3b143fc8 TV |
327 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name, |
328 | irqstatus); | |
f5f9454c RC |
329 | } |
330 | ||
331 | static const char *plane_names[] = { | |
222025e4 LP |
332 | [OMAP_DSS_GFX] = "gfx", |
333 | [OMAP_DSS_VIDEO1] = "vid1", | |
334 | [OMAP_DSS_VIDEO2] = "vid2", | |
335 | [OMAP_DSS_VIDEO3] = "vid3", | |
f5f9454c RC |
336 | }; |
337 | ||
338 | static const uint32_t error_irqs[] = { | |
222025e4 LP |
339 | [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, |
340 | [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, | |
341 | [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, | |
342 | [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, | |
f5f9454c RC |
343 | }; |
344 | ||
bb5c2d9a RC |
345 | /* initialize plane */ |
346 | struct drm_plane *omap_plane_init(struct drm_device *dev, | |
ef6b0e02 | 347 | int id, enum drm_plane_type type) |
bb5c2d9a | 348 | { |
f5f9454c | 349 | struct omap_drm_private *priv = dev->dev_private; |
ef6b0e02 | 350 | struct drm_plane *plane; |
bb5c2d9a | 351 | struct omap_plane *omap_plane; |
ef6b0e02 | 352 | int ret; |
bb5c2d9a | 353 | |
ef6b0e02 | 354 | DBG("%s: type=%d", plane_names[id], type); |
b33f34d3 | 355 | |
bb5c2d9a | 356 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); |
78110bb8 | 357 | if (!omap_plane) |
fb9a35f8 | 358 | return ERR_PTR(-ENOMEM); |
bb5c2d9a | 359 | |
a890e662 RC |
360 | omap_plane->nformats = omap_framebuffer_get_formats( |
361 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), | |
f5f9454c RC |
362 | dss_feat_get_supported_color_modes(id)); |
363 | omap_plane->id = id; | |
364 | omap_plane->name = plane_names[id]; | |
365 | ||
bb5c2d9a RC |
366 | plane = &omap_plane->base; |
367 | ||
f5f9454c RC |
368 | omap_plane->error_irq.irqmask = error_irqs[id]; |
369 | omap_plane->error_irq.irq = omap_plane_error_irq; | |
370 | omap_irq_register(dev, &omap_plane->error_irq); | |
371 | ||
ef6b0e02 LP |
372 | ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, |
373 | &omap_plane_funcs, omap_plane->formats, | |
b0b3b795 | 374 | omap_plane->nformats, type, NULL); |
ef6b0e02 LP |
375 | if (ret < 0) |
376 | goto error; | |
bb5c2d9a | 377 | |
de8e4100 LP |
378 | drm_plane_helper_add(plane, &omap_plane_helper_funcs); |
379 | ||
3c810c61 RC |
380 | omap_plane_install_properties(plane, &plane->base); |
381 | ||
bb5c2d9a | 382 | return plane; |
ef6b0e02 LP |
383 | |
384 | error: | |
385 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
386 | kfree(omap_plane); | |
387 | return NULL; | |
bb5c2d9a | 388 | } |