Skip to content

Commit 2e54ff0

Browse files
bparrottomba
authored andcommitted
drm/omap: dynamically assign hw overlays to planes
(re)assign the hw overlays to planes based on required caps, and to handle situations where we could not modify an in-use plane. This means all planes advertise the superset of formats and properties. Userspace must (as always) use atomic TEST_ONLY step for atomic updates, as not all planes may be available for use on every frame. The mapping of hwoverlays to plane is stored in omap_global_state, so that state updates are atomically committed in the same way that plane/etc state updates are managed. This is needed because the omap_plane_state keeps a pointer to the hwoverlay, and we don't want global state to become out of sync with the plane state if an atomic update fails, we hit deadlock/ backoff scenario, etc. The use of global_state_lock keeps multiple parallel updates which both re-assign hwoverlays properly serialized. Signed-off-by: Benoit Parrot <[email protected]> Signed-off-by: Neil Armstrong <[email protected]> Reviewed-by: Tomi Valkeinen <[email protected]> Signed-off-by: Tomi Valkeinen <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 6e42201 commit 2e54ff0

File tree

4 files changed

+220
-23
lines changed

4 files changed

+220
-23
lines changed

drivers/gpu/drm/omapdrm/omap_drv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ struct omap_drm_pipeline {
5050

5151
struct omap_global_state {
5252
struct drm_private_state base;
53+
54+
/* global atomic state of assignment between overlays and planes */
55+
struct drm_plane *hwoverlay_to_plane[8];
5356
};
5457

5558
struct omap_drm_private {

drivers/gpu/drm/omapdrm/omap_overlay.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,115 @@ static const char * const overlay_id_to_name[] = {
2121
[OMAP_DSS_VIDEO3] = "vid3",
2222
};
2323

24+
/*
25+
* Find a free overlay with the required caps and supported fourcc
26+
*/
27+
static struct omap_hw_overlay *
28+
omap_plane_find_free_overlay(struct drm_device *dev, struct drm_plane *hwoverlay_to_plane[],
29+
u32 caps, u32 fourcc)
30+
{
31+
struct omap_drm_private *priv = dev->dev_private;
32+
int i;
33+
34+
DBG("caps: %x fourcc: %x", caps, fourcc);
35+
36+
for (i = 0; i < priv->num_ovls; i++) {
37+
struct omap_hw_overlay *cur = priv->overlays[i];
38+
39+
DBG("%d: id: %d cur->caps: %x",
40+
cur->idx, cur->id, cur->caps);
41+
42+
/* skip if already in-use */
43+
if (hwoverlay_to_plane[cur->idx])
44+
continue;
45+
46+
/* skip if doesn't support some required caps: */
47+
if (caps & ~cur->caps)
48+
continue;
49+
50+
/* check supported format */
51+
if (!dispc_ovl_color_mode_supported(priv->dispc,
52+
cur->id, fourcc))
53+
continue;
54+
55+
return cur;
56+
}
57+
58+
DBG("no match");
59+
return NULL;
60+
}
61+
62+
/*
63+
* Assign a new overlay to a plane with the required caps and supported fourcc
64+
* If a plane need a new overlay, the previous one should have been released
65+
* with omap_overlay_release()
66+
* This should be called from the plane atomic_check() in order to prepare the
67+
* next global overlay_map to be enabled when atomic transaction is valid.
68+
*/
69+
int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane,
70+
u32 caps, u32 fourcc, struct omap_hw_overlay **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;
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+
DBG("%s: assign to plane %s caps %x", ovl->name, plane->name, caps);
85+
86+
return 0;
87+
}
88+
89+
/*
90+
* Release an overlay from a plane if the plane gets not visible or the plane
91+
* need a new overlay if overlay caps changes.
92+
* This should be called from the plane atomic_check() in order to prepare the
93+
* next global overlay_map to be enabled when atomic transaction is valid.
94+
*/
95+
void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay)
96+
{
97+
/* Get the global state of the current atomic transaction */
98+
struct omap_global_state *state = omap_get_global_state(s);
99+
struct drm_plane **overlay_map = state->hwoverlay_to_plane;
100+
101+
if (!overlay)
102+
return;
103+
104+
if (WARN_ON(!overlay_map[overlay->idx]))
105+
return;
106+
107+
DBG("%s: release from plane %s", overlay->name, overlay_map[overlay->idx]->name);
108+
109+
overlay_map[overlay->idx] = NULL;
110+
}
111+
112+
/*
113+
* Update an overlay state that was attached to a plane before the current atomic state.
114+
* This should be called from the plane atomic_update() or atomic_disable(),
115+
* where an overlay association to a plane could have changed between the old and current
116+
* atomic state.
117+
*/
118+
void omap_overlay_update_state(struct omap_drm_private *priv,
119+
struct omap_hw_overlay *overlay)
120+
{
121+
struct omap_global_state *state = omap_get_existing_global_state(priv);
122+
struct drm_plane **overlay_map = state->hwoverlay_to_plane;
123+
124+
/* Check if this overlay is not used anymore, then disable it */
125+
if (!overlay_map[overlay->idx]) {
126+
DBG("%s: disabled", overlay->name);
127+
128+
/* disable the overlay */
129+
dispc_ovl_enable(priv->dispc, overlay->id, false);
130+
}
131+
}
132+
24133
static void omap_overlay_destroy(struct omap_hw_overlay *overlay)
25134
{
26135
kfree(overlay);

drivers/gpu/drm/omapdrm/omap_overlay.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ struct omap_hw_overlay {
2727

2828
int omap_hwoverlays_init(struct omap_drm_private *priv);
2929
void omap_hwoverlays_destroy(struct omap_drm_private *priv);
30+
int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane,
31+
u32 caps, u32 fourcc, struct omap_hw_overlay **overlay);
32+
void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay);
33+
void omap_overlay_update_state(struct omap_drm_private *priv, struct omap_hw_overlay *overlay);
3034
#endif /* __OMAPDRM_OVERLAY_H__ */

drivers/gpu/drm/omapdrm/omap_plane.c

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <drm/drm_atomic_helper.h>
99
#include <drm/drm_gem_atomic_helper.h>
1010
#include <drm/drm_plane_helper.h>
11+
#include <drm/drm_fourcc.h>
1112

1213
#include "omap_dmm_tiler.h"
1314
#include "omap_drv.h"
@@ -21,15 +22,15 @@
2122
struct omap_plane_state {
2223
/* Must be first. */
2324
struct drm_plane_state base;
25+
26+
struct omap_hw_overlay *overlay;
2427
};
2528

2629
#define to_omap_plane(x) container_of(x, struct omap_plane, base)
2730

2831
struct omap_plane {
2932
struct drm_plane base;
3033
enum omap_plane_id id;
31-
32-
struct omap_hw_overlay *overlay;
3334
};
3435

3536
static int omap_plane_prepare_fb(struct drm_plane *plane,
@@ -54,13 +55,29 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
5455
struct drm_atomic_state *state)
5556
{
5657
struct omap_drm_private *priv = plane->dev->dev_private;
57-
struct omap_plane *omap_plane = to_omap_plane(plane);
5858
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
5959
plane);
60-
enum omap_plane_id ovl_id = omap_plane->overlay->id;
60+
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
61+
plane);
62+
struct omap_plane_state *new_omap_state;
63+
struct omap_plane_state *old_omap_state;
6164
struct omap_overlay_info info;
65+
enum omap_plane_id ovl_id;
6266
int ret;
6367

68+
new_omap_state = to_omap_plane_state(new_state);
69+
old_omap_state = to_omap_plane_state(old_state);
70+
71+
/* Cleanup previously held overlay if needed */
72+
if (old_omap_state->overlay)
73+
omap_overlay_update_state(priv, old_omap_state->overlay);
74+
75+
if (!new_omap_state->overlay) {
76+
DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
77+
return;
78+
}
79+
80+
ovl_id = new_omap_state->overlay->id;
6481
DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
6582
new_state->fb);
6683

@@ -79,9 +96,9 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
7996
/* update scanout: */
8097
omap_framebuffer_update_scanout(new_state->fb, new_state, &info);
8198

82-
DBG("%dx%d -> %dx%d (%d)", info.width, info.height,
83-
info.out_width, info.out_height,
84-
info.screen_width);
99+
DBG("%s: %dx%d -> %dx%d (%d)",
100+
new_omap_state->overlay->name, info.width, info.height,
101+
info.out_width, info.out_height, info.screen_width);
85102
DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
86103
&info.paddr, &info.p_uv_addr);
87104

@@ -102,16 +119,26 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
102119
static void omap_plane_atomic_disable(struct drm_plane *plane,
103120
struct drm_atomic_state *state)
104121
{
105-
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
106-
plane);
107122
struct omap_drm_private *priv = plane->dev->dev_private;
108123
struct omap_plane *omap_plane = to_omap_plane(plane);
109-
enum omap_plane_id ovl_id = omap_plane->overlay->id;
124+
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
125+
plane);
126+
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
127+
plane);
128+
struct omap_plane_state *new_omap_state;
129+
struct omap_plane_state *old_omap_state;
130+
131+
new_omap_state = to_omap_plane_state(new_state);
132+
old_omap_state = to_omap_plane_state(old_state);
133+
134+
if (!old_omap_state->overlay)
135+
return;
110136

111137
new_state->rotation = DRM_MODE_ROTATE_0;
112138
new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
113139

114-
dispc_ovl_enable(priv->dispc, ovl_id, false);
140+
omap_overlay_update_state(priv, old_omap_state->overlay);
141+
new_omap_state->overlay = NULL;
115142
}
116143

117144
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
@@ -121,32 +148,37 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
121148
{
122149
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
123150
plane);
151+
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
152+
plane);
124153
struct omap_drm_private *priv = plane->dev->dev_private;
154+
struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
155+
struct omap_global_state *omap_overlay_global_state;
125156
struct drm_crtc_state *crtc_state;
157+
bool new_hw_overlay = false;
126158
u32 max_width, max_height;
159+
struct drm_crtc *crtc;
127160
u16 width, height;
161+
u32 caps = 0;
162+
u32 fourcc;
128163
int ret;
129164

130-
if (!new_plane_state->fb)
131-
return 0;
165+
omap_overlay_global_state = omap_get_global_state(state);
166+
if (IS_ERR(omap_overlay_global_state))
167+
return PTR_ERR(omap_overlay_global_state);
132168

133169
dispc_ovl_get_max_size(priv->dispc, &width, &height);
134170
max_width = width << 16;
135171
max_height = height << 16;
136172

137-
/* crtc should only be NULL when disabling (i.e., !new_plane_state->fb) */
138-
if (WARN_ON(!new_plane_state->crtc))
173+
crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;
174+
if (!crtc)
139175
return 0;
140176

141-
crtc_state = drm_atomic_get_existing_crtc_state(state,
142-
new_plane_state->crtc);
177+
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
143178
/* we should have a crtc state if the plane is attached to a crtc */
144179
if (WARN_ON(!crtc_state))
145180
return 0;
146181

147-
if (!crtc_state->enable)
148-
return 0;
149-
150182
/*
151183
* Note: these are just sanity checks to filter out totally bad scaling
152184
* factors. The real limits must be calculated case by case, and
@@ -159,6 +191,15 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
159191
if (ret)
160192
return ret;
161193

194+
DBG("%s: visible %d -> %d", plane->name,
195+
old_plane_state->visible, new_plane_state->visible);
196+
197+
if (!new_plane_state->visible) {
198+
omap_overlay_release(state, omap_state->overlay);
199+
omap_state->overlay = NULL;
200+
return 0;
201+
}
202+
162203
if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)
163204
return -EINVAL;
164205

@@ -179,6 +220,43 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
179220
!omap_framebuffer_supports_rotation(new_plane_state->fb))
180221
return -EINVAL;
181222

223+
if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
224+
(new_plane_state->src_h >> 16) != new_plane_state->crtc_h)
225+
caps |= OMAP_DSS_OVL_CAP_SCALE;
226+
227+
fourcc = new_plane_state->fb->format->format;
228+
229+
/*
230+
* (re)allocate hw overlay if we don't have one or
231+
* there is a caps mismatch
232+
*/
233+
if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {
234+
new_hw_overlay = true;
235+
} else {
236+
/* check supported format */
237+
if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,
238+
fourcc))
239+
new_hw_overlay = true;
240+
}
241+
242+
if (new_hw_overlay) {
243+
struct omap_hw_overlay *old_ovl = omap_state->overlay;
244+
struct omap_hw_overlay *new_ovl = NULL;
245+
246+
omap_overlay_release(state, old_ovl);
247+
248+
ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl);
249+
if (ret) {
250+
DBG("%s: failed to assign hw_overlay", plane->name);
251+
omap_state->overlay = NULL;
252+
return ret;
253+
}
254+
255+
omap_state->overlay = new_ovl;
256+
}
257+
258+
DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
259+
182260
return 0;
183261
}
184262

@@ -252,17 +330,21 @@ static void omap_plane_reset(struct drm_plane *plane)
252330
static struct drm_plane_state *
253331
omap_plane_atomic_duplicate_state(struct drm_plane *plane)
254332
{
255-
struct omap_plane_state *state;
333+
struct omap_plane_state *state, *current_state;
256334

257335
if (WARN_ON(!plane->state))
258336
return NULL;
259337

338+
current_state = to_omap_plane_state(plane->state);
339+
260340
state = kmalloc(sizeof(*state), GFP_KERNEL);
261341
if (!state)
262342
return NULL;
263343

264344
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
265345

346+
state->overlay = current_state->overlay;
347+
266348
return &state->base;
267349
}
268350

@@ -344,12 +426,11 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
344426
return ERR_PTR(-ENOMEM);
345427

346428
omap_plane->id = idx;
347-
omap_plane->overlay = priv->overlays[idx];
348429

349430
DBG("%d: type=%d", omap_plane->id, type);
350431
DBG(" crtc_mask: 0x%04x", possible_crtcs);
351432

352-
formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->overlay->id);
433+
formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
353434
for (nformats = 0; formats[nformats]; ++nformats)
354435
;
355436

0 commit comments

Comments
 (0)