]>
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 | ||
69a12263 | 20 | #include <drm/drm_atomic_helper.h> |
de8e4100 | 21 | #include <drm/drm_plane_helper.h> |
69a12263 | 22 | |
3c810c61 | 23 | #include "omap_dmm_tiler.h" |
2d278f54 | 24 | #include "omap_drv.h" |
bb5c2d9a RC |
25 | |
26 | /* some hackery because omapdss has an 'enum omap_plane' (which would be | |
27 | * better named omap_plane_id).. and compiler seems unhappy about having | |
28 | * both a 'struct omap_plane' and 'enum omap_plane' | |
29 | */ | |
30 | #define omap_plane _omap_plane | |
31 | ||
32 | /* | |
33 | * plane funcs | |
34 | */ | |
35 | ||
36 | #define to_omap_plane(x) container_of(x, struct omap_plane, base) | |
37 | ||
38 | struct omap_plane { | |
39 | struct drm_plane base; | |
f5f9454c RC |
40 | int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ |
41 | const char *name; | |
bb5c2d9a RC |
42 | struct omap_overlay_info info; |
43 | ||
3c810c61 RC |
44 | /* position/orientation of scanout within the fb: */ |
45 | struct omap_drm_window win; | |
f5f9454c | 46 | bool enabled; |
9a0774e0 | 47 | |
a890e662 RC |
48 | uint32_t nformats; |
49 | uint32_t formats[32]; | |
b33f34d3 | 50 | |
f5f9454c | 51 | struct omap_drm_irq error_irq; |
a890e662 | 52 | }; |
bb5c2d9a | 53 | |
de8e4100 LP |
54 | static int __omap_plane_setup(struct omap_plane *omap_plane, |
55 | struct drm_crtc *crtc, | |
56 | struct drm_framebuffer *fb) | |
bb5c2d9a | 57 | { |
a42133a7 | 58 | struct omap_overlay_info *info = &omap_plane->info; |
de8e4100 | 59 | struct drm_device *dev = omap_plane->base.dev; |
9a0774e0 | 60 | int ret; |
bb5c2d9a | 61 | |
a42133a7 | 62 | DBG("%s, enabled=%d", omap_plane->name, omap_plane->enabled); |
f5f9454c | 63 | |
a42133a7 | 64 | if (!omap_plane->enabled) { |
f5f9454c | 65 | dispc_ovl_enable(omap_plane->id, false); |
de8e4100 | 66 | return 0; |
9a0774e0 | 67 | } |
bb5c2d9a | 68 | |
f5f9454c | 69 | /* update scanout: */ |
de8e4100 | 70 | omap_framebuffer_update_scanout(fb, &omap_plane->win, info); |
bb5c2d9a | 71 | |
f5f9454c RC |
72 | DBG("%dx%d -> %dx%d (%d)", info->width, info->height, |
73 | info->out_width, info->out_height, | |
9a0774e0 | 74 | info->screen_width); |
2d31ca3a RK |
75 | DBG("%d,%d %pad %pad", info->pos_x, info->pos_y, |
76 | &info->paddr, &info->p_uv_addr); | |
f5f9454c | 77 | |
a42133a7 LP |
78 | dispc_ovl_set_channel_out(omap_plane->id, |
79 | omap_crtc_channel(crtc)); | |
2dd3887b | 80 | |
f5f9454c | 81 | /* and finally, update omapdss: */ |
9c660b7c LP |
82 | ret = dispc_ovl_setup(omap_plane->id, info, false, |
83 | omap_crtc_timings(crtc), false); | |
f5f9454c RC |
84 | if (ret) { |
85 | dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret); | |
de8e4100 | 86 | return ret; |
f5f9454c RC |
87 | } |
88 | ||
89 | dispc_ovl_enable(omap_plane->id, true); | |
f5f9454c | 90 | |
de8e4100 LP |
91 | return 0; |
92 | } | |
93 | ||
94 | static int omap_plane_setup(struct omap_plane *omap_plane) | |
95 | { | |
96 | struct drm_plane *plane = &omap_plane->base; | |
97 | int ret; | |
98 | ||
de8e4100 LP |
99 | dispc_runtime_get(); |
100 | ret = __omap_plane_setup(omap_plane, plane->crtc, plane->fb); | |
a42133a7 | 101 | dispc_runtime_put(); |
de8e4100 | 102 | |
a42133a7 | 103 | return ret; |
bb5c2d9a RC |
104 | } |
105 | ||
a42133a7 LP |
106 | int omap_plane_set_enable(struct drm_plane *plane, bool enable) |
107 | { | |
108 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
109 | ||
110 | if (enable == omap_plane->enabled) | |
111 | return 0; | |
f5f9454c | 112 | |
a42133a7 LP |
113 | omap_plane->enabled = enable; |
114 | return omap_plane_setup(omap_plane); | |
bb5c2d9a RC |
115 | } |
116 | ||
de8e4100 LP |
117 | static int omap_plane_prepare_fb(struct drm_plane *plane, |
118 | struct drm_framebuffer *fb, | |
119 | const struct drm_plane_state *new_state) | |
120 | { | |
121 | return omap_framebuffer_pin(fb); | |
122 | } | |
123 | ||
124 | static void omap_plane_cleanup_fb(struct drm_plane *plane, | |
125 | struct drm_framebuffer *fb, | |
126 | const struct drm_plane_state *old_state) | |
127 | { | |
128 | omap_framebuffer_unpin(fb); | |
129 | } | |
130 | ||
131 | static void omap_plane_atomic_update(struct drm_plane *plane, | |
132 | struct drm_plane_state *old_state) | |
2f53700d | 133 | { |
f5f9454c | 134 | struct omap_plane *omap_plane = to_omap_plane(plane); |
de8e4100 LP |
135 | struct omap_drm_window *win = &omap_plane->win; |
136 | struct drm_plane_state *state = plane->state; | |
137 | uint32_t src_w; | |
138 | uint32_t src_h; | |
a42133a7 | 139 | |
de8e4100 LP |
140 | if (!state->fb || !state->crtc) |
141 | return; | |
b03e14fd | 142 | |
de8e4100 | 143 | /* omap_framebuffer_update_scanout() takes adjusted src */ |
d4586604 GI |
144 | switch (omap_plane->win.rotation & 0xf) { |
145 | case BIT(DRM_ROTATE_90): | |
146 | case BIT(DRM_ROTATE_270): | |
de8e4100 LP |
147 | src_w = state->src_h; |
148 | src_h = state->src_w; | |
149 | break; | |
150 | default: | |
151 | src_w = state->src_w; | |
152 | src_h = state->src_h; | |
d4586604 GI |
153 | break; |
154 | } | |
155 | ||
de8e4100 LP |
156 | /* src values are in Q16 fixed point, convert to integer. */ |
157 | win->crtc_x = state->crtc_x; | |
158 | win->crtc_y = state->crtc_y; | |
159 | win->crtc_w = state->crtc_w; | |
160 | win->crtc_h = state->crtc_h; | |
a42133a7 | 161 | |
de8e4100 LP |
162 | win->src_x = state->src_x >> 16; |
163 | win->src_y = state->src_y >> 16; | |
164 | win->src_w = src_w >> 16; | |
165 | win->src_h = src_h >> 16; | |
166 | ||
167 | omap_plane->enabled = true; | |
168 | __omap_plane_setup(omap_plane, state->crtc, state->fb); | |
2f53700d RC |
169 | } |
170 | ||
de8e4100 LP |
171 | static void omap_plane_atomic_disable(struct drm_plane *plane, |
172 | struct drm_plane_state *old_state) | |
bb5c2d9a | 173 | { |
3c810c61 | 174 | struct omap_plane *omap_plane = to_omap_plane(plane); |
2debab97 | 175 | |
3c810c61 | 176 | omap_plane->win.rotation = BIT(DRM_ROTATE_0); |
82e58855 LP |
177 | omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY |
178 | ? 0 : omap_plane->id; | |
179 | ||
a42133a7 | 180 | if (!omap_plane->enabled) |
de8e4100 | 181 | return; |
a42133a7 | 182 | |
a42133a7 | 183 | omap_plane->enabled = false; |
de8e4100 | 184 | __omap_plane_setup(omap_plane, NULL, NULL); |
bb5c2d9a RC |
185 | } |
186 | ||
de8e4100 LP |
187 | static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { |
188 | .prepare_fb = omap_plane_prepare_fb, | |
189 | .cleanup_fb = omap_plane_cleanup_fb, | |
190 | .atomic_update = omap_plane_atomic_update, | |
191 | .atomic_disable = omap_plane_atomic_disable, | |
192 | }; | |
193 | ||
bb5c2d9a RC |
194 | static void omap_plane_destroy(struct drm_plane *plane) |
195 | { | |
196 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
f5f9454c RC |
197 | |
198 | DBG("%s", omap_plane->name); | |
199 | ||
200 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
201 | ||
bb5c2d9a | 202 | drm_plane_cleanup(plane); |
f5f9454c | 203 | |
bb5c2d9a RC |
204 | kfree(omap_plane); |
205 | } | |
206 | ||
3c810c61 RC |
207 | /* helper to install properties which are common to planes and crtcs */ |
208 | void omap_plane_install_properties(struct drm_plane *plane, | |
209 | struct drm_mode_object *obj) | |
210 | { | |
211 | struct drm_device *dev = plane->dev; | |
212 | struct omap_drm_private *priv = dev->dev_private; | |
3c810c61 | 213 | |
c2a6a552 | 214 | if (priv->has_dmm) { |
e2cd09b2 LP |
215 | struct drm_property *prop = dev->mode_config.rotation_property; |
216 | ||
c2a6a552 | 217 | drm_object_attach_property(obj, prop, 0); |
3c810c61 | 218 | } |
8451b5ad | 219 | |
e2cd09b2 | 220 | drm_object_attach_property(obj, priv->zorder_prop, 0); |
3c810c61 RC |
221 | } |
222 | ||
223 | int omap_plane_set_property(struct drm_plane *plane, | |
224 | struct drm_property *property, uint64_t val) | |
225 | { | |
226 | struct omap_plane *omap_plane = to_omap_plane(plane); | |
227 | struct omap_drm_private *priv = plane->dev->dev_private; | |
a42133a7 | 228 | int ret; |
3c810c61 | 229 | |
e2cd09b2 | 230 | if (property == plane->dev->mode_config.rotation_property) { |
f5f9454c | 231 | DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); |
3c810c61 | 232 | omap_plane->win.rotation = val; |
8451b5ad | 233 | } else if (property == priv->zorder_prop) { |
f5f9454c | 234 | DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); |
8451b5ad | 235 | omap_plane->info.zorder = val; |
a42133a7 LP |
236 | } else { |
237 | return -EINVAL; | |
3c810c61 RC |
238 | } |
239 | ||
a42133a7 LP |
240 | /* |
241 | * We're done if the plane is disabled, properties will be applied the | |
242 | * next time it becomes enabled. | |
243 | */ | |
244 | if (!omap_plane->enabled) | |
245 | return 0; | |
246 | ||
247 | ret = omap_plane_setup(omap_plane); | |
248 | if (ret < 0) | |
249 | return ret; | |
250 | ||
251 | return omap_crtc_flush(plane->crtc); | |
3c810c61 RC |
252 | } |
253 | ||
bb5c2d9a | 254 | static const struct drm_plane_funcs omap_plane_funcs = { |
cef77d40 LP |
255 | .update_plane = drm_atomic_helper_update_plane, |
256 | .disable_plane = drm_atomic_helper_disable_plane, | |
69a12263 | 257 | .reset = drm_atomic_helper_plane_reset, |
222025e4 LP |
258 | .destroy = omap_plane_destroy, |
259 | .set_property = omap_plane_set_property, | |
69a12263 LP |
260 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
261 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
bb5c2d9a RC |
262 | }; |
263 | ||
f5f9454c RC |
264 | static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
265 | { | |
266 | struct omap_plane *omap_plane = | |
267 | container_of(irq, struct omap_plane, error_irq); | |
3b143fc8 TV |
268 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name, |
269 | irqstatus); | |
f5f9454c RC |
270 | } |
271 | ||
272 | static const char *plane_names[] = { | |
222025e4 LP |
273 | [OMAP_DSS_GFX] = "gfx", |
274 | [OMAP_DSS_VIDEO1] = "vid1", | |
275 | [OMAP_DSS_VIDEO2] = "vid2", | |
276 | [OMAP_DSS_VIDEO3] = "vid3", | |
f5f9454c RC |
277 | }; |
278 | ||
279 | static const uint32_t error_irqs[] = { | |
222025e4 LP |
280 | [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, |
281 | [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, | |
282 | [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, | |
283 | [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, | |
f5f9454c RC |
284 | }; |
285 | ||
bb5c2d9a RC |
286 | /* initialize plane */ |
287 | struct drm_plane *omap_plane_init(struct drm_device *dev, | |
ef6b0e02 | 288 | int id, enum drm_plane_type type) |
bb5c2d9a | 289 | { |
f5f9454c | 290 | struct omap_drm_private *priv = dev->dev_private; |
ef6b0e02 | 291 | struct drm_plane *plane; |
bb5c2d9a | 292 | struct omap_plane *omap_plane; |
f5f9454c | 293 | struct omap_overlay_info *info; |
ef6b0e02 | 294 | int ret; |
bb5c2d9a | 295 | |
ef6b0e02 | 296 | DBG("%s: type=%d", plane_names[id], type); |
b33f34d3 | 297 | |
bb5c2d9a | 298 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); |
78110bb8 | 299 | if (!omap_plane) |
fb9a35f8 | 300 | return ERR_PTR(-ENOMEM); |
bb5c2d9a | 301 | |
a890e662 RC |
302 | omap_plane->nformats = omap_framebuffer_get_formats( |
303 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), | |
f5f9454c RC |
304 | dss_feat_get_supported_color_modes(id)); |
305 | omap_plane->id = id; | |
306 | omap_plane->name = plane_names[id]; | |
307 | ||
bb5c2d9a RC |
308 | plane = &omap_plane->base; |
309 | ||
f5f9454c RC |
310 | omap_plane->error_irq.irqmask = error_irqs[id]; |
311 | omap_plane->error_irq.irq = omap_plane_error_irq; | |
312 | omap_irq_register(dev, &omap_plane->error_irq); | |
313 | ||
ef6b0e02 LP |
314 | ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, |
315 | &omap_plane_funcs, omap_plane->formats, | |
316 | omap_plane->nformats, type); | |
317 | if (ret < 0) | |
318 | goto error; | |
bb5c2d9a | 319 | |
de8e4100 LP |
320 | drm_plane_helper_add(plane, &omap_plane_helper_funcs); |
321 | ||
3c810c61 RC |
322 | omap_plane_install_properties(plane, &plane->base); |
323 | ||
bb5c2d9a RC |
324 | /* get our starting configuration, set defaults for parameters |
325 | * we don't currently use, etc: | |
326 | */ | |
f5f9454c RC |
327 | info = &omap_plane->info; |
328 | info->rotation_type = OMAP_DSS_ROT_DMA; | |
329 | info->rotation = OMAP_DSS_ROT_0; | |
330 | info->global_alpha = 0xff; | |
331 | info->mirror = 0; | |
bb5c2d9a RC |
332 | |
333 | /* Set defaults depending on whether we are a CRTC or overlay | |
334 | * layer. | |
335 | * TODO add ioctl to give userspace an API to change this.. this | |
336 | * will come in a subsequent patch. | |
337 | */ | |
ef6b0e02 | 338 | if (type == DRM_PLANE_TYPE_PRIMARY) |
bb5c2d9a RC |
339 | omap_plane->info.zorder = 0; |
340 | else | |
f5f9454c | 341 | omap_plane->info.zorder = id; |
bb5c2d9a RC |
342 | |
343 | return plane; | |
ef6b0e02 LP |
344 | |
345 | error: | |
346 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | |
347 | kfree(omap_plane); | |
348 | return NULL; | |
bb5c2d9a | 349 | } |