Merge tag 'iommu-fix-v5.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/joro...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / omapdrm / omap_plane.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4  * Author: Rob Clark <rob.clark@linaro.org>
5  */
6
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_plane_helper.h>
10
11 #include "omap_dmm_tiler.h"
12 #include "omap_drv.h"
13
14 /*
15  * plane funcs
16  */
17
18 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
19
20 struct omap_plane {
21         struct drm_plane base;
22         enum omap_plane_id id;
23         const char *name;
24 };
25
26 static int omap_plane_prepare_fb(struct drm_plane *plane,
27                                  struct drm_plane_state *new_state)
28 {
29         if (!new_state->fb)
30                 return 0;
31
32         return omap_framebuffer_pin(new_state->fb);
33 }
34
35 static void omap_plane_cleanup_fb(struct drm_plane *plane,
36                                   struct drm_plane_state *old_state)
37 {
38         if (old_state->fb)
39                 omap_framebuffer_unpin(old_state->fb);
40 }
41
42 static void omap_plane_atomic_update(struct drm_plane *plane,
43                                      struct drm_plane_state *old_state)
44 {
45         struct omap_drm_private *priv = plane->dev->dev_private;
46         struct omap_plane *omap_plane = to_omap_plane(plane);
47         struct drm_plane_state *state = plane->state;
48         struct omap_overlay_info info;
49         int ret;
50
51         DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb);
52
53         memset(&info, 0, sizeof(info));
54         info.rotation_type = OMAP_DSS_ROT_NONE;
55         info.rotation = DRM_MODE_ROTATE_0;
56         info.global_alpha = 0xff;
57         info.zorder = state->normalized_zpos;
58
59         /* update scanout: */
60         omap_framebuffer_update_scanout(state->fb, state, &info);
61
62         DBG("%dx%d -> %dx%d (%d)", info.width, info.height,
63                         info.out_width, info.out_height,
64                         info.screen_width);
65         DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
66                         &info.paddr, &info.p_uv_addr);
67
68         /* and finally, update omapdss: */
69         ret = priv->dispc_ops->ovl_setup(priv->dispc, omap_plane->id, &info,
70                               omap_crtc_timings(state->crtc), false,
71                               omap_crtc_channel(state->crtc));
72         if (ret) {
73                 dev_err(plane->dev->dev, "Failed to setup plane %s\n",
74                         omap_plane->name);
75                 priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false);
76                 return;
77         }
78
79         priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, true);
80 }
81
82 static void omap_plane_atomic_disable(struct drm_plane *plane,
83                                       struct drm_plane_state *old_state)
84 {
85         struct omap_drm_private *priv = plane->dev->dev_private;
86         struct omap_plane *omap_plane = to_omap_plane(plane);
87
88         plane->state->rotation = DRM_MODE_ROTATE_0;
89         plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
90                            ? 0 : omap_plane->id;
91
92         priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false);
93 }
94
95 static int omap_plane_atomic_check(struct drm_plane *plane,
96                                    struct drm_plane_state *state)
97 {
98         struct drm_crtc_state *crtc_state;
99
100         if (!state->fb)
101                 return 0;
102
103         /* crtc should only be NULL when disabling (i.e., !state->fb) */
104         if (WARN_ON(!state->crtc))
105                 return 0;
106
107         crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
108         /* we should have a crtc state if the plane is attached to a crtc */
109         if (WARN_ON(!crtc_state))
110                 return 0;
111
112         if (!crtc_state->enable)
113                 return 0;
114
115         if (state->crtc_x < 0 || state->crtc_y < 0)
116                 return -EINVAL;
117
118         if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay)
119                 return -EINVAL;
120
121         if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay)
122                 return -EINVAL;
123
124         if (state->rotation != DRM_MODE_ROTATE_0 &&
125             !omap_framebuffer_supports_rotation(state->fb))
126                 return -EINVAL;
127
128         return 0;
129 }
130
131 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = {
132         .prepare_fb = omap_plane_prepare_fb,
133         .cleanup_fb = omap_plane_cleanup_fb,
134         .atomic_check = omap_plane_atomic_check,
135         .atomic_update = omap_plane_atomic_update,
136         .atomic_disable = omap_plane_atomic_disable,
137 };
138
139 static void omap_plane_destroy(struct drm_plane *plane)
140 {
141         struct omap_plane *omap_plane = to_omap_plane(plane);
142
143         DBG("%s", omap_plane->name);
144
145         drm_plane_cleanup(plane);
146
147         kfree(omap_plane);
148 }
149
150 /* helper to install properties which are common to planes and crtcs */
151 void omap_plane_install_properties(struct drm_plane *plane,
152                 struct drm_mode_object *obj)
153 {
154         struct drm_device *dev = plane->dev;
155         struct omap_drm_private *priv = dev->dev_private;
156
157         if (priv->has_dmm) {
158                 if (!plane->rotation_property)
159                         drm_plane_create_rotation_property(plane,
160                                                            DRM_MODE_ROTATE_0,
161                                                            DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
162                                                            DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
163                                                            DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
164
165                 /* Attach the rotation property also to the crtc object */
166                 if (plane->rotation_property && obj != &plane->base)
167                         drm_object_attach_property(obj, plane->rotation_property,
168                                                    DRM_MODE_ROTATE_0);
169         }
170
171         drm_object_attach_property(obj, priv->zorder_prop, 0);
172 }
173
174 static void omap_plane_reset(struct drm_plane *plane)
175 {
176         struct omap_plane *omap_plane = to_omap_plane(plane);
177
178         drm_atomic_helper_plane_reset(plane);
179         if (!plane->state)
180                 return;
181
182         /*
183          * Set the zpos default depending on whether we are a primary or overlay
184          * plane.
185          */
186         plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
187                            ? 0 : omap_plane->id;
188 }
189
190 static int omap_plane_atomic_set_property(struct drm_plane *plane,
191                                           struct drm_plane_state *state,
192                                           struct drm_property *property,
193                                           u64 val)
194 {
195         struct omap_drm_private *priv = plane->dev->dev_private;
196
197         if (property == priv->zorder_prop)
198                 state->zpos = val;
199         else
200                 return -EINVAL;
201
202         return 0;
203 }
204
205 static int omap_plane_atomic_get_property(struct drm_plane *plane,
206                                           const struct drm_plane_state *state,
207                                           struct drm_property *property,
208                                           u64 *val)
209 {
210         struct omap_drm_private *priv = plane->dev->dev_private;
211
212         if (property == priv->zorder_prop)
213                 *val = state->zpos;
214         else
215                 return -EINVAL;
216
217         return 0;
218 }
219
220 static const struct drm_plane_funcs omap_plane_funcs = {
221         .update_plane = drm_atomic_helper_update_plane,
222         .disable_plane = drm_atomic_helper_disable_plane,
223         .reset = omap_plane_reset,
224         .destroy = omap_plane_destroy,
225         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
226         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
227         .atomic_set_property = omap_plane_atomic_set_property,
228         .atomic_get_property = omap_plane_atomic_get_property,
229 };
230
231 static const char *plane_id_to_name[] = {
232         [OMAP_DSS_GFX] = "gfx",
233         [OMAP_DSS_VIDEO1] = "vid1",
234         [OMAP_DSS_VIDEO2] = "vid2",
235         [OMAP_DSS_VIDEO3] = "vid3",
236 };
237
238 static const enum omap_plane_id plane_idx_to_id[] = {
239         OMAP_DSS_GFX,
240         OMAP_DSS_VIDEO1,
241         OMAP_DSS_VIDEO2,
242         OMAP_DSS_VIDEO3,
243 };
244
245 /* initialize plane */
246 struct drm_plane *omap_plane_init(struct drm_device *dev,
247                 int idx, enum drm_plane_type type,
248                 u32 possible_crtcs)
249 {
250         struct omap_drm_private *priv = dev->dev_private;
251         unsigned int num_planes = priv->dispc_ops->get_num_ovls(priv->dispc);
252         struct drm_plane *plane;
253         struct omap_plane *omap_plane;
254         enum omap_plane_id id;
255         int ret;
256         u32 nformats;
257         const u32 *formats;
258
259         if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id)))
260                 return ERR_PTR(-EINVAL);
261
262         id = plane_idx_to_id[idx];
263
264         DBG("%s: type=%d", plane_id_to_name[id], type);
265
266         omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
267         if (!omap_plane)
268                 return ERR_PTR(-ENOMEM);
269
270         formats = priv->dispc_ops->ovl_get_color_modes(priv->dispc, id);
271         for (nformats = 0; formats[nformats]; ++nformats)
272                 ;
273         omap_plane->id = id;
274         omap_plane->name = plane_id_to_name[id];
275
276         plane = &omap_plane->base;
277
278         ret = drm_universal_plane_init(dev, plane, possible_crtcs,
279                                        &omap_plane_funcs, formats,
280                                        nformats, NULL, type, NULL);
281         if (ret < 0)
282                 goto error;
283
284         drm_plane_helper_add(plane, &omap_plane_helper_funcs);
285
286         omap_plane_install_properties(plane, &plane->base);
287         drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1);
288
289         return plane;
290
291 error:
292         dev_err(dev->dev, "%s(): could not create plane: %s\n",
293                 __func__, plane_id_to_name[id]);
294
295         kfree(omap_plane);
296         return NULL;
297 }