]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/omapdrm/omap_plane.c
drm: omapdrm: Drop manual framebuffer pin handling
[karo-tx-linux.git] / drivers / gpu / drm / omapdrm / omap_plane.c
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 <drm/drm_atomic_helper.h>
21 #include <drm/drm_plane_helper.h>
22
23 #include "omap_dmm_tiler.h"
24 #include "omap_drv.h"
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;
40         int id;  /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
41         const char *name;
42         struct omap_overlay_info info;
43
44         /* position/orientation of scanout within the fb: */
45         struct omap_drm_window win;
46         bool enabled;
47
48         uint32_t nformats;
49         uint32_t formats[32];
50
51         struct omap_drm_irq error_irq;
52 };
53
54 static int __omap_plane_setup(struct omap_plane *omap_plane,
55                               struct drm_crtc *crtc,
56                               struct drm_framebuffer *fb)
57 {
58         struct omap_overlay_info *info = &omap_plane->info;
59         struct drm_device *dev = omap_plane->base.dev;
60         int ret;
61
62         DBG("%s, enabled=%d", omap_plane->name, omap_plane->enabled);
63
64         if (!omap_plane->enabled) {
65                 dispc_ovl_enable(omap_plane->id, false);
66                 return 0;
67         }
68
69         /* update scanout: */
70         omap_framebuffer_update_scanout(fb, &omap_plane->win, info);
71
72         DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
73                         info->out_width, info->out_height,
74                         info->screen_width);
75         DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
76                         &info->paddr, &info->p_uv_addr);
77
78         dispc_ovl_set_channel_out(omap_plane->id,
79                                   omap_crtc_channel(crtc));
80
81         /* and finally, update omapdss: */
82         ret = dispc_ovl_setup(omap_plane->id, info, false,
83                               omap_crtc_timings(crtc), false);
84         if (ret) {
85                 dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
86                 return ret;
87         }
88
89         dispc_ovl_enable(omap_plane->id, true);
90
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
99         dispc_runtime_get();
100         ret = __omap_plane_setup(omap_plane, plane->crtc, plane->fb);
101         dispc_runtime_put();
102
103         return ret;
104 }
105
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;
112
113         omap_plane->enabled = enable;
114         return omap_plane_setup(omap_plane);
115 }
116
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)
133 {
134         struct omap_plane *omap_plane = to_omap_plane(plane);
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;
139
140         if (!state->fb || !state->crtc)
141                 return;
142
143         /* omap_framebuffer_update_scanout() takes adjusted src */
144         switch (omap_plane->win.rotation & 0xf) {
145         case BIT(DRM_ROTATE_90):
146         case BIT(DRM_ROTATE_270):
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;
153                 break;
154         }
155
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;
161
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);
169 }
170
171 static void omap_plane_atomic_disable(struct drm_plane *plane,
172                                       struct drm_plane_state *old_state)
173 {
174         struct omap_plane *omap_plane = to_omap_plane(plane);
175
176         omap_plane->win.rotation = BIT(DRM_ROTATE_0);
177         omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
178                                 ? 0 : omap_plane->id;
179
180         if (!omap_plane->enabled)
181                 return;
182
183         omap_plane->enabled = false;
184         __omap_plane_setup(omap_plane, NULL, NULL);
185 }
186
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
194 static void omap_plane_destroy(struct drm_plane *plane)
195 {
196         struct omap_plane *omap_plane = to_omap_plane(plane);
197
198         DBG("%s", omap_plane->name);
199
200         omap_irq_unregister(plane->dev, &omap_plane->error_irq);
201
202         drm_plane_cleanup(plane);
203
204         kfree(omap_plane);
205 }
206
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;
213
214         if (priv->has_dmm) {
215                 struct drm_property *prop = dev->mode_config.rotation_property;
216
217                 drm_object_attach_property(obj, prop, 0);
218         }
219
220         drm_object_attach_property(obj, priv->zorder_prop, 0);
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;
228         int ret;
229
230         if (property == plane->dev->mode_config.rotation_property) {
231                 DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
232                 omap_plane->win.rotation = val;
233         } else if (property == priv->zorder_prop) {
234                 DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
235                 omap_plane->info.zorder = val;
236         } else {
237                 return -EINVAL;
238         }
239
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);
252 }
253
254 static const struct drm_plane_funcs omap_plane_funcs = {
255         .update_plane = drm_atomic_helper_update_plane,
256         .disable_plane = drm_atomic_helper_disable_plane,
257         .reset = drm_atomic_helper_plane_reset,
258         .destroy = omap_plane_destroy,
259         .set_property = omap_plane_set_property,
260         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
261         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
262 };
263
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);
268         DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
269                 irqstatus);
270 }
271
272 static const char *plane_names[] = {
273         [OMAP_DSS_GFX] = "gfx",
274         [OMAP_DSS_VIDEO1] = "vid1",
275         [OMAP_DSS_VIDEO2] = "vid2",
276         [OMAP_DSS_VIDEO3] = "vid3",
277 };
278
279 static const uint32_t error_irqs[] = {
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,
284 };
285
286 /* initialize plane */
287 struct drm_plane *omap_plane_init(struct drm_device *dev,
288                 int id, enum drm_plane_type type)
289 {
290         struct omap_drm_private *priv = dev->dev_private;
291         struct drm_plane *plane;
292         struct omap_plane *omap_plane;
293         struct omap_overlay_info *info;
294         int ret;
295
296         DBG("%s: type=%d", plane_names[id], type);
297
298         omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
299         if (!omap_plane)
300                 return ERR_PTR(-ENOMEM);
301
302         omap_plane->nformats = omap_framebuffer_get_formats(
303                         omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
304                         dss_feat_get_supported_color_modes(id));
305         omap_plane->id = id;
306         omap_plane->name = plane_names[id];
307
308         plane = &omap_plane->base;
309
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
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;
319
320         drm_plane_helper_add(plane, &omap_plane_helper_funcs);
321
322         omap_plane_install_properties(plane, &plane->base);
323
324         /* get our starting configuration, set defaults for parameters
325          * we don't currently use, etc:
326          */
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;
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          */
338         if (type == DRM_PLANE_TYPE_PRIMARY)
339                 omap_plane->info.zorder = 0;
340         else
341                 omap_plane->info.zorder = id;
342
343         return plane;
344
345 error:
346         omap_irq_unregister(plane->dev, &omap_plane->error_irq);
347         kfree(omap_plane);
348         return NULL;
349 }