1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
4  * Author: Benoit Parrot <bparrot@ti.com>
5  */
6 
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 
10 #include "omap_dmm_tiler.h"
11 #include "omap_drv.h"
12 
13 /*
14  * overlay funcs
15  */
16 static const char * const overlay_id_to_name[] = {
17 	[OMAP_DSS_GFX] = "gfx",
18 	[OMAP_DSS_VIDEO1] = "vid1",
19 	[OMAP_DSS_VIDEO2] = "vid2",
20 	[OMAP_DSS_VIDEO3] = "vid3",
21 };
22 
23 /*
24  * Find a free overlay with the required caps and supported fourcc
25  */
26 static struct omap_hw_overlay *
omap_plane_find_free_overlay(struct drm_device * dev,struct drm_plane * hwoverlay_to_plane[],u32 caps,u32 fourcc)27 omap_plane_find_free_overlay(struct drm_device *dev, struct drm_plane *hwoverlay_to_plane[],
28 			     u32 caps, u32 fourcc)
29 {
30 	struct omap_drm_private *priv = dev->dev_private;
31 	int i;
32 
33 	DBG("caps: %x fourcc: %x", caps, fourcc);
34 
35 	for (i = 0; i < priv->num_ovls; i++) {
36 		struct omap_hw_overlay *cur = priv->overlays[i];
37 
38 		DBG("%d: id: %d cur->caps: %x",
39 		    cur->idx, cur->id, cur->caps);
40 
41 		/* skip if already in-use */
42 		if (hwoverlay_to_plane[cur->idx])
43 			continue;
44 
45 		/* skip if doesn't support some required caps: */
46 		if (caps & ~cur->caps)
47 			continue;
48 
49 		/* check supported format */
50 		if (!dispc_ovl_color_mode_supported(priv->dispc,
51 						    cur->id, fourcc))
52 			continue;
53 
54 		return cur;
55 	}
56 
57 	DBG("no match");
58 	return NULL;
59 }
60 
61 /*
62  * Assign a new overlay to a plane with the required caps and supported fourcc
63  * If a plane need a new overlay, the previous one should have been released
64  * with omap_overlay_release()
65  * This should be called from the plane atomic_check() in order to prepare the
66  * next global overlay_map to be enabled when atomic transaction is valid.
67  */
omap_overlay_assign(struct drm_atomic_state * s,struct drm_plane * plane,u32 caps,u32 fourcc,struct omap_hw_overlay ** overlay,struct omap_hw_overlay ** r_overlay)68 int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane,
69 			u32 caps, u32 fourcc, struct omap_hw_overlay **overlay,
70 			struct omap_hw_overlay **r_overlay)
71 {
72 	/* Get the global state of the current atomic transaction */
73 	struct omap_global_state *state = omap_get_global_state(s);
74 	struct drm_plane **overlay_map = state->hwoverlay_to_plane;
75 	struct omap_hw_overlay *ovl, *r_ovl;
76 
77 	ovl = omap_plane_find_free_overlay(s->dev, overlay_map, caps, fourcc);
78 	if (!ovl)
79 		return -ENOMEM;
80 
81 	overlay_map[ovl->idx] = plane;
82 	*overlay = ovl;
83 
84 	if (r_overlay) {
85 		r_ovl = omap_plane_find_free_overlay(s->dev, overlay_map,
86 						     caps, fourcc);
87 		if (!r_ovl) {
88 			overlay_map[ovl->idx] = NULL;
89 			*overlay = NULL;
90 			return -ENOMEM;
91 		}
92 
93 		overlay_map[r_ovl->idx] = plane;
94 		*r_overlay = r_ovl;
95 	}
96 
97 	DBG("%s: assign to plane %s caps %x", ovl->name, plane->name, caps);
98 
99 	if (r_overlay) {
100 		DBG("%s: assign to right of plane %s caps %x",
101 		    r_ovl->name, plane->name, caps);
102 	}
103 
104 	return 0;
105 }
106 
107 /*
108  * Release an overlay from a plane if the plane gets not visible or the plane
109  * need a new overlay if overlay caps changes.
110  * This should be called from the plane atomic_check() in order to prepare the
111  * next global overlay_map to be enabled when atomic transaction is valid.
112  */
omap_overlay_release(struct drm_atomic_state * s,struct omap_hw_overlay * overlay)113 void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay)
114 {
115 	/* Get the global state of the current atomic transaction */
116 	struct omap_global_state *state = omap_get_global_state(s);
117 	struct drm_plane **overlay_map = state->hwoverlay_to_plane;
118 
119 	if (!overlay)
120 		return;
121 
122 	if (WARN_ON(!overlay_map[overlay->idx]))
123 		return;
124 
125 	DBG("%s: release from plane %s", overlay->name, overlay_map[overlay->idx]->name);
126 
127 	overlay_map[overlay->idx] = NULL;
128 }
129 
130 /*
131  * Update an overlay state that was attached to a plane before the current atomic state.
132  * This should be called from the plane atomic_update() or atomic_disable(),
133  * where an overlay association to a plane could have changed between the old and current
134  * atomic state.
135  */
omap_overlay_update_state(struct omap_drm_private * priv,struct omap_hw_overlay * overlay)136 void omap_overlay_update_state(struct omap_drm_private *priv,
137 			       struct omap_hw_overlay *overlay)
138 {
139 	struct omap_global_state *state = omap_get_existing_global_state(priv);
140 	struct drm_plane **overlay_map = state->hwoverlay_to_plane;
141 
142 	/* Check if this overlay is not used anymore, then disable it */
143 	if (!overlay_map[overlay->idx]) {
144 		DBG("%s: disabled", overlay->name);
145 
146 		/* disable the overlay */
147 		dispc_ovl_enable(priv->dispc, overlay->id, false);
148 	}
149 }
150 
omap_overlay_destroy(struct omap_hw_overlay * overlay)151 static void omap_overlay_destroy(struct omap_hw_overlay *overlay)
152 {
153 	kfree(overlay);
154 }
155 
omap_overlay_init(enum omap_plane_id overlay_id,enum omap_overlay_caps caps)156 static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id,
157 						 enum omap_overlay_caps caps)
158 {
159 	struct omap_hw_overlay *overlay;
160 
161 	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
162 	if (!overlay)
163 		return ERR_PTR(-ENOMEM);
164 
165 	overlay->name = overlay_id_to_name[overlay_id];
166 	overlay->id = overlay_id;
167 	overlay->caps = caps;
168 
169 	return overlay;
170 }
171 
omap_hwoverlays_init(struct omap_drm_private * priv)172 int omap_hwoverlays_init(struct omap_drm_private *priv)
173 {
174 	static const enum omap_plane_id hw_plane_ids[] = {
175 			OMAP_DSS_GFX, OMAP_DSS_VIDEO1,
176 			OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3,
177 	};
178 	u32 num_overlays = dispc_get_num_ovls(priv->dispc);
179 	enum omap_overlay_caps caps;
180 	int i, ret;
181 
182 	for (i = 0; i < num_overlays; i++) {
183 		struct omap_hw_overlay *overlay;
184 
185 		caps = dispc_ovl_get_caps(priv->dispc, hw_plane_ids[i]);
186 		overlay = omap_overlay_init(hw_plane_ids[i], caps);
187 		if (IS_ERR(overlay)) {
188 			ret = PTR_ERR(overlay);
189 			dev_err(priv->dev, "failed to construct overlay for %s (%d)\n",
190 				overlay_id_to_name[i], ret);
191 			omap_hwoverlays_destroy(priv);
192 			return ret;
193 		}
194 		overlay->idx = priv->num_ovls;
195 		priv->overlays[priv->num_ovls++] = overlay;
196 	}
197 
198 	return 0;
199 }
200 
omap_hwoverlays_destroy(struct omap_drm_private * priv)201 void omap_hwoverlays_destroy(struct omap_drm_private *priv)
202 {
203 	int i;
204 
205 	for (i = 0; i < priv->num_ovls; i++) {
206 		omap_overlay_destroy(priv->overlays[i]);
207 		priv->overlays[i] = NULL;
208 	}
209 
210 	priv->num_ovls = 0;
211 }
212