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, |
11ffd031 TV |
63 | const struct drm_plane_state *new_state) |
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, | |
11ffd031 TV |
72 | const struct drm_plane_state *old_state) |
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) { |
fb730c9b LP |
112 | case BIT(DRM_ROTATE_90): |
113 | case BIT(DRM_ROTATE_270): | |
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 | |
afc34932 LP |
152 | plane->state->rotation = BIT(DRM_ROTATE_0); |
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 | ||
180 | return 0; | |
181 | } | |
182 | ||
de8e4100 LP |
183 | static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { |
184 | .prepare_fb = omap_plane_prepare_fb, | |
185 | .cleanup_fb = omap_plane_cleanup_fb, | |
c423bc85 | 186 | .atomic_check = omap_plane_atomic_check, |
de8e4100 LP |
187 | .atomic_update = omap_plane_atomic_update, |
188 | .atomic_disable = omap_plane_atomic_disable, | |
189 | }; | |
190 | ||
bb5c2d9a RC |
191 | static void omap_plane_destroy(struct drm_plane *plane) |
192 | { | |
193 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
f5f9454c RC |
194 | |
195 | DBG("%s", omap_plane->name); | |
196 | ||
197 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
198 | ||
bb5c2d9a | 199 | drm_plane_cleanup(plane); |
f5f9454c | 200 | |
bb5c2d9a RC |
201 | kfree(omap_plane); |
202 | } | |
203 | ||
3c810c61 RC |
204 | /* helper to install properties which are common to planes and crtcs */ |
205 | void omap_plane_install_properties(struct drm_plane *plane, | |
206 | struct drm_mode_object *obj) | |
207 | { | |
208 | struct drm_device *dev = plane->dev; | |
209 | struct omap_drm_private *priv = dev->dev_private; | |
3c810c61 | 210 | |
c2a6a552 | 211 | if (priv->has_dmm) { |
e2cd09b2 LP |
212 | struct drm_property *prop = dev->mode_config.rotation_property; |
213 | ||
c2a6a552 | 214 | drm_object_attach_property(obj, prop, 0); |
3c810c61 | 215 | } |
8451b5ad | 216 | |
e2cd09b2 | 217 | drm_object_attach_property(obj, priv->zorder_prop, 0); |
3c810c61 RC |
218 | } |
219 | ||
afc34932 LP |
220 | static struct drm_plane_state * |
221 | omap_plane_atomic_duplicate_state(struct drm_plane *plane) | |
222 | { | |
223 | struct omap_plane_state *state; | |
224 | struct omap_plane_state *copy; | |
225 | ||
226 | if (WARN_ON(!plane->state)) | |
227 | return NULL; | |
228 | ||
229 | state = to_omap_plane_state(plane->state); | |
230 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | |
231 | if (copy == NULL) | |
232 | return NULL; | |
233 | ||
234 | __drm_atomic_helper_plane_duplicate_state(plane, ©->base); | |
235 | ||
236 | return ©->base; | |
237 | } | |
238 | ||
239 | static void omap_plane_atomic_destroy_state(struct drm_plane *plane, | |
240 | struct drm_plane_state *state) | |
241 | { | |
242 | __drm_atomic_helper_plane_destroy_state(plane, state); | |
243 | kfree(to_omap_plane_state(state)); | |
244 | } | |
245 | ||
e07323cf TV |
246 | static void omap_plane_reset(struct drm_plane *plane) |
247 | { | |
248 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
249 | struct omap_plane_state *omap_state; | |
250 | ||
f8ef29ee LP |
251 | if (plane->state) { |
252 | omap_plane_atomic_destroy_state(plane, plane->state); | |
253 | plane->state = NULL; | |
254 | } | |
e07323cf TV |
255 | |
256 | omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); | |
257 | if (omap_state == NULL) | |
258 | return; | |
259 | ||
260 | /* | |
261 | * Set defaults depending on whether we are a primary or overlay | |
262 | * plane. | |
263 | */ | |
264 | omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY | |
265 | ? 0 : omap_plane->id; | |
266 | omap_state->base.rotation = BIT(DRM_ROTATE_0); | |
267 | ||
268 | plane->state = &omap_state->base; | |
269 | plane->state->plane = plane; | |
270 | } | |
271 | ||
afc34932 LP |
272 | static int omap_plane_atomic_set_property(struct drm_plane *plane, |
273 | struct drm_plane_state *state, | |
274 | struct drm_property *property, | |
275 | uint64_t val) | |
3c810c61 | 276 | { |
3c810c61 | 277 | struct omap_drm_private *priv = plane->dev->dev_private; |
afc34932 | 278 | struct omap_plane_state *omap_state = to_omap_plane_state(state); |
3c810c61 | 279 | |
afc34932 LP |
280 | if (property == priv->zorder_prop) |
281 | omap_state->zorder = val; | |
282 | else | |
a42133a7 | 283 | return -EINVAL; |
3c810c61 | 284 | |
afc34932 LP |
285 | return 0; |
286 | } | |
a42133a7 | 287 | |
afc34932 LP |
288 | static int omap_plane_atomic_get_property(struct drm_plane *plane, |
289 | const struct drm_plane_state *state, | |
290 | struct drm_property *property, | |
291 | uint64_t *val) | |
292 | { | |
293 | struct omap_drm_private *priv = plane->dev->dev_private; | |
294 | const struct omap_plane_state *omap_state = | |
295 | container_of(state, const struct omap_plane_state, base); | |
296 | ||
297 | if (property == priv->zorder_prop) | |
298 | *val = omap_state->zorder; | |
299 | else | |
300 | return -EINVAL; | |
a42133a7 | 301 | |
afc34932 | 302 | return 0; |
3c810c61 RC |
303 | } |
304 | ||
bb5c2d9a | 305 | static const struct drm_plane_funcs omap_plane_funcs = { |
cef77d40 LP |
306 | .update_plane = drm_atomic_helper_update_plane, |
307 | .disable_plane = drm_atomic_helper_disable_plane, | |
afc34932 | 308 | .reset = omap_plane_reset, |
222025e4 | 309 | .destroy = omap_plane_destroy, |
afc34932 LP |
310 | .set_property = drm_atomic_helper_plane_set_property, |
311 | .atomic_duplicate_state = omap_plane_atomic_duplicate_state, | |
312 | .atomic_destroy_state = omap_plane_atomic_destroy_state, | |
313 | .atomic_set_property = omap_plane_atomic_set_property, | |
314 | .atomic_get_property = omap_plane_atomic_get_property, | |
bb5c2d9a RC |
315 | }; |
316 | ||
f5f9454c RC |
317 | static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
318 | { | |
319 | struct omap_plane *omap_plane = | |
320 | container_of(irq, struct omap_plane, error_irq); | |
3b143fc8 TV |
321 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name, |
322 | irqstatus); | |
f5f9454c RC |
323 | } |
324 | ||
325 | static const char *plane_names[] = { | |
222025e4 LP |
326 | [OMAP_DSS_GFX] = "gfx", |
327 | [OMAP_DSS_VIDEO1] = "vid1", | |
328 | [OMAP_DSS_VIDEO2] = "vid2", | |
329 | [OMAP_DSS_VIDEO3] = "vid3", | |
f5f9454c RC |
330 | }; |
331 | ||
332 | static const uint32_t error_irqs[] = { | |
222025e4 LP |
333 | [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, |
334 | [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, | |
335 | [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, | |
336 | [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, | |
f5f9454c RC |
337 | }; |
338 | ||
bb5c2d9a RC |
339 | /* initialize plane */ |
340 | struct drm_plane *omap_plane_init(struct drm_device *dev, | |
ef6b0e02 | 341 | int id, enum drm_plane_type type) |
bb5c2d9a | 342 | { |
f5f9454c | 343 | struct omap_drm_private *priv = dev->dev_private; |
ef6b0e02 | 344 | struct drm_plane *plane; |
bb5c2d9a | 345 | struct omap_plane *omap_plane; |
ef6b0e02 | 346 | int ret; |
bb5c2d9a | 347 | |
ef6b0e02 | 348 | DBG("%s: type=%d", plane_names[id], type); |
b33f34d3 | 349 | |
bb5c2d9a | 350 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); |
78110bb8 | 351 | if (!omap_plane) |
fb9a35f8 | 352 | return ERR_PTR(-ENOMEM); |
bb5c2d9a | 353 | |
a890e662 RC |
354 | omap_plane->nformats = omap_framebuffer_get_formats( |
355 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), | |
f5f9454c RC |
356 | dss_feat_get_supported_color_modes(id)); |
357 | omap_plane->id = id; | |
358 | omap_plane->name = plane_names[id]; | |
359 | ||
bb5c2d9a RC |
360 | plane = &omap_plane->base; |
361 | ||
f5f9454c RC |
362 | omap_plane->error_irq.irqmask = error_irqs[id]; |
363 | omap_plane->error_irq.irq = omap_plane_error_irq; | |
364 | omap_irq_register(dev, &omap_plane->error_irq); | |
365 | ||
ef6b0e02 LP |
366 | ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, |
367 | &omap_plane_funcs, omap_plane->formats, | |
b0b3b795 | 368 | omap_plane->nformats, type, NULL); |
ef6b0e02 LP |
369 | if (ret < 0) |
370 | goto error; | |
bb5c2d9a | 371 | |
de8e4100 LP |
372 | drm_plane_helper_add(plane, &omap_plane_helper_funcs); |
373 | ||
3c810c61 RC |
374 | omap_plane_install_properties(plane, &plane->base); |
375 | ||
bb5c2d9a | 376 | return plane; |
ef6b0e02 LP |
377 | |
378 | error: | |
379 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
380 | kfree(omap_plane); | |
381 | return NULL; | |
bb5c2d9a | 382 | } |