diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-25 19:46:44 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-25 19:46:44 -0500 |
commit | fffddfd6c8e0c10c42c6e2cc54ba880fcc36ebbb (patch) | |
tree | 71bc5e597124dbaf7550f1e089d675718b3ed5c0 /drivers/gpu/drm/omapdrm/omap_plane.c | |
parent | 69086a78bdc973ec0b722be790b146e84ba8a8c4 (diff) | |
parent | be88298b0a3f771a4802f20c5e66af74bfd1dff1 (diff) |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm merge from Dave Airlie:
"Highlights:
- TI LCD controller KMS driver
- TI OMAP KMS driver merged from staging
- drop gma500 stub driver
- the fbcon locking fixes
- the vgacon dirty like zebra fix.
- open firmware videomode and hdmi common code helpers
- major locking rework for kms object handling - pageflip/cursor
won't block on polling anymore!
- fbcon helper and prime helper cleanups
- i915: all over the map, haswell power well enhancements, valleyview
macro horrors cleaned up, killing lots of legacy GTT code,
- radeon: CS ioctl unification, deprecated UMS support, gpu reset
rework, VM fixes
- nouveau: reworked thermal code, external dp/tmds encoder support
(anx9805), fences sleep instead of polling,
- exynos: all over the driver fixes."
Lovely conflict in radeon/evergreen_cs.c between commit de0babd60d8d
("drm/radeon: enforce use of radeon_get_ib_value when reading user cmd")
and the new changes that modified that evergreen_dma_cs_parse()
function.
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (508 commits)
drm/tilcdc: only build on arm
drm/i915: Revert hdmi HDP pin checks
drm/tegra: Add list of framebuffers to debugfs
drm/tegra: Fix color expansion
drm/tegra: Split DC_CMD_STATE_CONTROL register write
drm/tegra: Implement page-flipping support
drm/tegra: Implement VBLANK support
drm/tegra: Implement .mode_set_base()
drm/tegra: Add plane support
drm/tegra: Remove bogus tegra_framebuffer structure
drm: Add consistency check for page-flipping
drm/radeon: Use generic HDMI infoframe helpers
drm/tegra: Use generic HDMI infoframe helpers
drm: Add EDID helper documentation
drm: Add HDMI infoframe helpers
video: Add generic HDMI infoframe helpers
drm: Add some missing forward declarations
drm: Move mode tables to drm_edid.c
drm: Remove duplicate drm_mode_cea_vic()
gma500: Fix n, m1 and m2 clock limits for sdvo and lvds
...
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_plane.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_plane.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c new file mode 100644 index 000000000000..2882cda6ea19 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_plane.c | |||
@@ -0,0 +1,448 @@ | |||
1 | /* | ||
2 | * drivers/gpu/drm/omapdrm/omap_plane.c | ||
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 <linux/kfifo.h> | ||
21 | |||
22 | #include "omap_drv.h" | ||
23 | #include "omap_dmm_tiler.h" | ||
24 | |||
25 | /* some hackery because omapdss has an 'enum omap_plane' (which would be | ||
26 | * better named omap_plane_id).. and compiler seems unhappy about having | ||
27 | * both a 'struct omap_plane' and 'enum omap_plane' | ||
28 | */ | ||
29 | #define omap_plane _omap_plane | ||
30 | |||
31 | /* | ||
32 | * plane funcs | ||
33 | */ | ||
34 | |||
35 | struct callback { | ||
36 | void (*fxn)(void *); | ||
37 | void *arg; | ||
38 | }; | ||
39 | |||
40 | #define to_omap_plane(x) container_of(x, struct omap_plane, base) | ||
41 | |||
42 | struct omap_plane { | ||
43 | struct drm_plane base; | ||
44 | int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ | ||
45 | const char *name; | ||
46 | struct omap_overlay_info info; | ||
47 | struct omap_drm_apply apply; | ||
48 | |||
49 | /* position/orientation of scanout within the fb: */ | ||
50 | struct omap_drm_window win; | ||
51 | bool enabled; | ||
52 | |||
53 | /* last fb that we pinned: */ | ||
54 | struct drm_framebuffer *pinned_fb; | ||
55 | |||
56 | uint32_t nformats; | ||
57 | uint32_t formats[32]; | ||
58 | |||
59 | struct omap_drm_irq error_irq; | ||
60 | |||
61 | /* set of bo's pending unpin until next post_apply() */ | ||
62 | DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); | ||
63 | |||
64 | // XXX maybe get rid of this and handle vblank in crtc too? | ||
65 | struct callback apply_done_cb; | ||
66 | }; | ||
67 | |||
68 | static void unpin(void *arg, struct drm_gem_object *bo) | ||
69 | { | ||
70 | struct drm_plane *plane = arg; | ||
71 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
72 | |||
73 | if (kfifo_put(&omap_plane->unpin_fifo, | ||
74 | (const struct drm_gem_object **)&bo)) { | ||
75 | /* also hold a ref so it isn't free'd while pinned */ | ||
76 | drm_gem_object_reference(bo); | ||
77 | } else { | ||
78 | dev_err(plane->dev->dev, "unpin fifo full!\n"); | ||
79 | omap_gem_put_paddr(bo); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /* update which fb (if any) is pinned for scanout */ | ||
84 | static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) | ||
85 | { | ||
86 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
87 | struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb; | ||
88 | |||
89 | if (pinned_fb != fb) { | ||
90 | int ret; | ||
91 | |||
92 | DBG("%p -> %p", pinned_fb, fb); | ||
93 | |||
94 | if (fb) | ||
95 | drm_framebuffer_reference(fb); | ||
96 | |||
97 | ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin); | ||
98 | |||
99 | if (pinned_fb) | ||
100 | drm_framebuffer_unreference(pinned_fb); | ||
101 | |||
102 | if (ret) { | ||
103 | dev_err(plane->dev->dev, "could not swap %p -> %p\n", | ||
104 | omap_plane->pinned_fb, fb); | ||
105 | if (fb) | ||
106 | drm_framebuffer_unreference(fb); | ||
107 | omap_plane->pinned_fb = NULL; | ||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | omap_plane->pinned_fb = fb; | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static void omap_plane_pre_apply(struct omap_drm_apply *apply) | ||
118 | { | ||
119 | struct omap_plane *omap_plane = | ||
120 | container_of(apply, struct omap_plane, apply); | ||
121 | struct omap_drm_window *win = &omap_plane->win; | ||
122 | struct drm_plane *plane = &omap_plane->base; | ||
123 | struct drm_device *dev = plane->dev; | ||
124 | struct omap_overlay_info *info = &omap_plane->info; | ||
125 | struct drm_crtc *crtc = plane->crtc; | ||
126 | enum omap_channel channel; | ||
127 | bool enabled = omap_plane->enabled && crtc; | ||
128 | bool ilace, replication; | ||
129 | int ret; | ||
130 | |||
131 | DBG("%s, enabled=%d", omap_plane->name, enabled); | ||
132 | |||
133 | /* if fb has changed, pin new fb: */ | ||
134 | update_pin(plane, enabled ? plane->fb : NULL); | ||
135 | |||
136 | if (!enabled) { | ||
137 | dispc_ovl_enable(omap_plane->id, false); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | channel = omap_crtc_channel(crtc); | ||
142 | |||
143 | /* update scanout: */ | ||
144 | omap_framebuffer_update_scanout(plane->fb, win, info); | ||
145 | |||
146 | DBG("%dx%d -> %dx%d (%d)", info->width, info->height, | ||
147 | info->out_width, info->out_height, | ||
148 | info->screen_width); | ||
149 | DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, | ||
150 | info->paddr, info->p_uv_addr); | ||
151 | |||
152 | /* TODO: */ | ||
153 | ilace = false; | ||
154 | replication = false; | ||
155 | |||
156 | /* and finally, update omapdss: */ | ||
157 | ret = dispc_ovl_setup(omap_plane->id, info, | ||
158 | replication, omap_crtc_timings(crtc), false); | ||
159 | if (ret) { | ||
160 | dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | dispc_ovl_enable(omap_plane->id, true); | ||
165 | dispc_ovl_set_channel_out(omap_plane->id, channel); | ||
166 | } | ||
167 | |||
168 | static void omap_plane_post_apply(struct omap_drm_apply *apply) | ||
169 | { | ||
170 | struct omap_plane *omap_plane = | ||
171 | container_of(apply, struct omap_plane, apply); | ||
172 | struct drm_plane *plane = &omap_plane->base; | ||
173 | struct omap_overlay_info *info = &omap_plane->info; | ||
174 | struct drm_gem_object *bo = NULL; | ||
175 | struct callback cb; | ||
176 | |||
177 | cb = omap_plane->apply_done_cb; | ||
178 | omap_plane->apply_done_cb.fxn = NULL; | ||
179 | |||
180 | while (kfifo_get(&omap_plane->unpin_fifo, &bo)) { | ||
181 | omap_gem_put_paddr(bo); | ||
182 | drm_gem_object_unreference_unlocked(bo); | ||
183 | } | ||
184 | |||
185 | if (cb.fxn) | ||
186 | cb.fxn(cb.arg); | ||
187 | |||
188 | if (omap_plane->enabled) { | ||
189 | omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, | ||
190 | info->out_width, info->out_height); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | static int apply(struct drm_plane *plane) | ||
195 | { | ||
196 | if (plane->crtc) { | ||
197 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
198 | return omap_crtc_apply(plane->crtc, &omap_plane->apply); | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | int omap_plane_mode_set(struct drm_plane *plane, | ||
204 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
205 | int crtc_x, int crtc_y, | ||
206 | unsigned int crtc_w, unsigned int crtc_h, | ||
207 | uint32_t src_x, uint32_t src_y, | ||
208 | uint32_t src_w, uint32_t src_h, | ||
209 | void (*fxn)(void *), void *arg) | ||
210 | { | ||
211 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
212 | struct omap_drm_window *win = &omap_plane->win; | ||
213 | |||
214 | win->crtc_x = crtc_x; | ||
215 | win->crtc_y = crtc_y; | ||
216 | win->crtc_w = crtc_w; | ||
217 | win->crtc_h = crtc_h; | ||
218 | |||
219 | /* src values are in Q16 fixed point, convert to integer: */ | ||
220 | win->src_x = src_x >> 16; | ||
221 | win->src_y = src_y >> 16; | ||
222 | win->src_w = src_w >> 16; | ||
223 | win->src_h = src_h >> 16; | ||
224 | |||
225 | if (fxn) { | ||
226 | /* omap_crtc should ensure that a new page flip | ||
227 | * isn't permitted while there is one pending: | ||
228 | */ | ||
229 | BUG_ON(omap_plane->apply_done_cb.fxn); | ||
230 | |||
231 | omap_plane->apply_done_cb.fxn = fxn; | ||
232 | omap_plane->apply_done_cb.arg = arg; | ||
233 | } | ||
234 | |||
235 | plane->fb = fb; | ||
236 | plane->crtc = crtc; | ||
237 | |||
238 | return apply(plane); | ||
239 | } | ||
240 | |||
241 | static int omap_plane_update(struct drm_plane *plane, | ||
242 | struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
243 | int crtc_x, int crtc_y, | ||
244 | unsigned int crtc_w, unsigned int crtc_h, | ||
245 | uint32_t src_x, uint32_t src_y, | ||
246 | uint32_t src_w, uint32_t src_h) | ||
247 | { | ||
248 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
249 | omap_plane->enabled = true; | ||
250 | return omap_plane_mode_set(plane, crtc, fb, | ||
251 | crtc_x, crtc_y, crtc_w, crtc_h, | ||
252 | src_x, src_y, src_w, src_h, | ||
253 | NULL, NULL); | ||
254 | } | ||
255 | |||
256 | static int omap_plane_disable(struct drm_plane *plane) | ||
257 | { | ||
258 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
259 | omap_plane->win.rotation = BIT(DRM_ROTATE_0); | ||
260 | return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); | ||
261 | } | ||
262 | |||
263 | static void omap_plane_destroy(struct drm_plane *plane) | ||
264 | { | ||
265 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
266 | |||
267 | DBG("%s", omap_plane->name); | ||
268 | |||
269 | omap_irq_unregister(plane->dev, &omap_plane->error_irq); | ||
270 | |||
271 | omap_plane_disable(plane); | ||
272 | drm_plane_cleanup(plane); | ||
273 | |||
274 | WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo)); | ||
275 | kfifo_free(&omap_plane->unpin_fifo); | ||
276 | |||
277 | kfree(omap_plane); | ||
278 | } | ||
279 | |||
280 | int omap_plane_dpms(struct drm_plane *plane, int mode) | ||
281 | { | ||
282 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
283 | bool enabled = (mode == DRM_MODE_DPMS_ON); | ||
284 | int ret = 0; | ||
285 | |||
286 | if (enabled != omap_plane->enabled) { | ||
287 | omap_plane->enabled = enabled; | ||
288 | ret = apply(plane); | ||
289 | } | ||
290 | |||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | /* helper to install properties which are common to planes and crtcs */ | ||
295 | void omap_plane_install_properties(struct drm_plane *plane, | ||
296 | struct drm_mode_object *obj) | ||
297 | { | ||
298 | struct drm_device *dev = plane->dev; | ||
299 | struct omap_drm_private *priv = dev->dev_private; | ||
300 | struct drm_property *prop; | ||
301 | |||
302 | if (priv->has_dmm) { | ||
303 | prop = priv->rotation_prop; | ||
304 | if (!prop) { | ||
305 | const struct drm_prop_enum_list props[] = { | ||
306 | { DRM_ROTATE_0, "rotate-0" }, | ||
307 | { DRM_ROTATE_90, "rotate-90" }, | ||
308 | { DRM_ROTATE_180, "rotate-180" }, | ||
309 | { DRM_ROTATE_270, "rotate-270" }, | ||
310 | { DRM_REFLECT_X, "reflect-x" }, | ||
311 | { DRM_REFLECT_Y, "reflect-y" }, | ||
312 | }; | ||
313 | prop = drm_property_create_bitmask(dev, 0, "rotation", | ||
314 | props, ARRAY_SIZE(props)); | ||
315 | if (prop == NULL) | ||
316 | return; | ||
317 | priv->rotation_prop = prop; | ||
318 | } | ||
319 | drm_object_attach_property(obj, prop, 0); | ||
320 | } | ||
321 | |||
322 | prop = priv->zorder_prop; | ||
323 | if (!prop) { | ||
324 | prop = drm_property_create_range(dev, 0, "zorder", 0, 3); | ||
325 | if (prop == NULL) | ||
326 | return; | ||
327 | priv->zorder_prop = prop; | ||
328 | } | ||
329 | drm_object_attach_property(obj, prop, 0); | ||
330 | } | ||
331 | |||
332 | int omap_plane_set_property(struct drm_plane *plane, | ||
333 | struct drm_property *property, uint64_t val) | ||
334 | { | ||
335 | struct omap_plane *omap_plane = to_omap_plane(plane); | ||
336 | struct omap_drm_private *priv = plane->dev->dev_private; | ||
337 | int ret = -EINVAL; | ||
338 | |||
339 | if (property == priv->rotation_prop) { | ||
340 | DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); | ||
341 | omap_plane->win.rotation = val; | ||
342 | ret = apply(plane); | ||
343 | } else if (property == priv->zorder_prop) { | ||
344 | DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); | ||
345 | omap_plane->info.zorder = val; | ||
346 | ret = apply(plane); | ||
347 | } | ||
348 | |||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static const struct drm_plane_funcs omap_plane_funcs = { | ||
353 | .update_plane = omap_plane_update, | ||
354 | .disable_plane = omap_plane_disable, | ||
355 | .destroy = omap_plane_destroy, | ||
356 | .set_property = omap_plane_set_property, | ||
357 | }; | ||
358 | |||
359 | static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) | ||
360 | { | ||
361 | struct omap_plane *omap_plane = | ||
362 | container_of(irq, struct omap_plane, error_irq); | ||
363 | DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus); | ||
364 | } | ||
365 | |||
366 | static const char *plane_names[] = { | ||
367 | [OMAP_DSS_GFX] = "gfx", | ||
368 | [OMAP_DSS_VIDEO1] = "vid1", | ||
369 | [OMAP_DSS_VIDEO2] = "vid2", | ||
370 | [OMAP_DSS_VIDEO3] = "vid3", | ||
371 | }; | ||
372 | |||
373 | static const uint32_t error_irqs[] = { | ||
374 | [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, | ||
375 | [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, | ||
376 | [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, | ||
377 | [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, | ||
378 | }; | ||
379 | |||
380 | /* initialize plane */ | ||
381 | struct drm_plane *omap_plane_init(struct drm_device *dev, | ||
382 | int id, bool private_plane) | ||
383 | { | ||
384 | struct omap_drm_private *priv = dev->dev_private; | ||
385 | struct drm_plane *plane = NULL; | ||
386 | struct omap_plane *omap_plane; | ||
387 | struct omap_overlay_info *info; | ||
388 | int ret; | ||
389 | |||
390 | DBG("%s: priv=%d", plane_names[id], private_plane); | ||
391 | |||
392 | omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); | ||
393 | if (!omap_plane) | ||
394 | goto fail; | ||
395 | |||
396 | ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); | ||
397 | if (ret) { | ||
398 | dev_err(dev->dev, "could not allocate unpin FIFO\n"); | ||
399 | goto fail; | ||
400 | } | ||
401 | |||
402 | omap_plane->nformats = omap_framebuffer_get_formats( | ||
403 | omap_plane->formats, ARRAY_SIZE(omap_plane->formats), | ||
404 | dss_feat_get_supported_color_modes(id)); | ||
405 | omap_plane->id = id; | ||
406 | omap_plane->name = plane_names[id]; | ||
407 | |||
408 | plane = &omap_plane->base; | ||
409 | |||
410 | omap_plane->apply.pre_apply = omap_plane_pre_apply; | ||
411 | omap_plane->apply.post_apply = omap_plane_post_apply; | ||
412 | |||
413 | omap_plane->error_irq.irqmask = error_irqs[id]; | ||
414 | omap_plane->error_irq.irq = omap_plane_error_irq; | ||
415 | omap_irq_register(dev, &omap_plane->error_irq); | ||
416 | |||
417 | drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs, | ||
418 | omap_plane->formats, omap_plane->nformats, private_plane); | ||
419 | |||
420 | omap_plane_install_properties(plane, &plane->base); | ||
421 | |||
422 | /* get our starting configuration, set defaults for parameters | ||
423 | * we don't currently use, etc: | ||
424 | */ | ||
425 | info = &omap_plane->info; | ||
426 | info->rotation_type = OMAP_DSS_ROT_DMA; | ||
427 | info->rotation = OMAP_DSS_ROT_0; | ||
428 | info->global_alpha = 0xff; | ||
429 | info->mirror = 0; | ||
430 | |||
431 | /* Set defaults depending on whether we are a CRTC or overlay | ||
432 | * layer. | ||
433 | * TODO add ioctl to give userspace an API to change this.. this | ||
434 | * will come in a subsequent patch. | ||
435 | */ | ||
436 | if (private_plane) | ||
437 | omap_plane->info.zorder = 0; | ||
438 | else | ||
439 | omap_plane->info.zorder = id; | ||
440 | |||
441 | return plane; | ||
442 | |||
443 | fail: | ||
444 | if (plane) | ||
445 | omap_plane_destroy(plane); | ||
446 | |||
447 | return NULL; | ||
448 | } | ||