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