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