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 * |
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(dispc: priv->dispc, |
51 | plane: 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 | */ |
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(dev: s->dev, hwoverlay_to_plane: 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(dev: s->dev, hwoverlay_to_plane: 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 | */ |
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 | */ |
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(dispc: priv->dispc, plane: overlay->id, enable: false); |
148 | } |
149 | } |
150 | |
151 | static void omap_overlay_destroy(struct omap_hw_overlay *overlay) |
152 | { |
153 | kfree(objp: overlay); |
154 | } |
155 | |
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(size: sizeof(*overlay), GFP_KERNEL); |
162 | if (!overlay) |
163 | return ERR_PTR(error: -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 | |
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(dispc: 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(dispc: priv->dispc, plane: hw_plane_ids[i]); |
186 | overlay = omap_overlay_init(overlay_id: hw_plane_ids[i], caps); |
187 | if (IS_ERR(ptr: overlay)) { |
188 | ret = PTR_ERR(ptr: 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 | |
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(overlay: priv->overlays[i]); |
207 | priv->overlays[i] = NULL; |
208 | } |
209 | |
210 | priv->num_ovls = 0; |
211 | } |
212 | |