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