Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/sun4i: Fix layer zpos change/atomic modesetting

Identical configurations of planes can lead to different (and wrong)
layer -> pipe routing at HW level, depending on the order of atomic
plane changes.

For example:

- Layer 1 is configured to zpos 0 and thus uses pipe 0. No other layer
is enabled. This is a typical situation at boot.

- When a compositor takes over and layer 3 is enabled,
sun8i_ui_layer_enable() will get called with old_zpos=0 zpos=1, which
will lead to incorrect disabling of pipe 0 and enabling of pipe 1.

What happens is that sun8i_ui_layer_enable() function may disable
blender pipes even if it is no longer assigned to its layer.

To correct this, move the routing setup out of individual plane's
atomic_update into crtc's atomic_update, where it can be calculated
and updated all at once.

Remove the atomic_disable callback because it is no longer needed.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://lore.kernel.org/r/20240224150604.3855534-4-megi@xff.cz
Signed-off-by: Maxime Ripard <mripard@kernel.org>

authored by

Ondrej Jirman and committed by
Maxime Ripard
f8d59fac aa0b4a69

+71 -143
+61
drivers/gpu/drm/sun4i/sun8i_mixer.c
··· 250 250 return -EINVAL; 251 251 } 252 252 253 + static void sun8i_layer_enable(struct sun8i_layer *layer, bool enable) 254 + { 255 + u32 ch_base = sun8i_channel_base(layer->mixer, layer->channel); 256 + u32 val, reg, mask; 257 + 258 + if (layer->type == SUN8I_LAYER_TYPE_UI) { 259 + val = enable ? SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN : 0; 260 + mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; 261 + reg = SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay); 262 + } else { 263 + val = enable ? SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN : 0; 264 + mask = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 265 + reg = SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay); 266 + } 267 + 268 + regmap_update_bits(layer->mixer->engine.regs, reg, mask, val); 269 + } 270 + 253 271 static void sun8i_mixer_commit(struct sunxi_engine *engine, 254 272 struct drm_crtc *crtc, 255 273 struct drm_atomic_state *state) 256 274 { 275 + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); 276 + u32 bld_base = sun8i_blender_base(mixer); 277 + struct drm_plane_state *plane_state; 278 + struct drm_plane *plane; 279 + u32 route = 0, pipe_en = 0; 280 + 257 281 DRM_DEBUG_DRIVER("Committing changes\n"); 282 + 283 + drm_for_each_plane(plane, state->dev) { 284 + struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 285 + bool enable; 286 + int zpos; 287 + 288 + if (!(plane->possible_crtcs & drm_crtc_mask(crtc)) || layer->mixer != mixer) 289 + continue; 290 + 291 + plane_state = drm_atomic_get_new_plane_state(state, plane); 292 + if (!plane_state) 293 + plane_state = plane->state; 294 + 295 + enable = plane_state->crtc && plane_state->visible; 296 + zpos = plane_state->normalized_zpos; 297 + 298 + DRM_DEBUG_DRIVER(" plane %d: chan=%d ovl=%d en=%d zpos=%d\n", 299 + plane->base.id, layer->channel, layer->overlay, 300 + enable, zpos); 301 + 302 + /* 303 + * We always update the layer enable bit, because it can clear 304 + * spontaneously for unknown reasons. 305 + */ 306 + sun8i_layer_enable(layer, enable); 307 + 308 + if (!enable) 309 + continue; 310 + 311 + /* Route layer to pipe based on zpos */ 312 + route |= layer->channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 313 + pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 314 + } 315 + 316 + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route); 317 + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 318 + pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); 258 319 259 320 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, 260 321 SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
+6
drivers/gpu/drm/sun4i/sun8i_mixer.h
··· 186 186 struct clk *mod_clk; 187 187 }; 188 188 189 + enum { 190 + SUN8I_LAYER_TYPE_UI, 191 + SUN8I_LAYER_TYPE_VI, 192 + }; 193 + 189 194 struct sun8i_layer { 190 195 struct drm_plane plane; 191 196 struct sun8i_mixer *mixer; 197 + int type; 192 198 int channel; 193 199 int overlay; 194 200 };
+2 -71
drivers/gpu/drm/sun4i/sun8i_ui_layer.c
··· 24 24 #include "sun8i_ui_layer.h" 25 25 #include "sun8i_ui_scaler.h" 26 26 27 - static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, 28 - int overlay, bool enable, unsigned int zpos, 29 - unsigned int old_zpos) 30 - { 31 - u32 val, bld_base, ch_base; 32 - 33 - bld_base = sun8i_blender_base(mixer); 34 - ch_base = sun8i_channel_base(mixer, channel); 35 - 36 - DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n", 37 - enable ? "En" : "Dis", channel, overlay); 38 - 39 - if (enable) 40 - val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; 41 - else 42 - val = 0; 43 - 44 - regmap_update_bits(mixer->engine.regs, 45 - SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), 46 - SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); 47 - 48 - if (!enable || zpos != old_zpos) { 49 - regmap_update_bits(mixer->engine.regs, 50 - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 51 - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 52 - 0); 53 - 54 - regmap_update_bits(mixer->engine.regs, 55 - SUN8I_MIXER_BLEND_ROUTE(bld_base), 56 - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 57 - 0); 58 - } 59 - 60 - if (enable) { 61 - val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 62 - 63 - regmap_update_bits(mixer->engine.regs, 64 - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 65 - val, val); 66 - 67 - val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 68 - 69 - regmap_update_bits(mixer->engine.regs, 70 - SUN8I_MIXER_BLEND_ROUTE(bld_base), 71 - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 72 - val); 73 - } 74 - } 75 - 76 27 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel, 77 28 int overlay, struct drm_plane *plane) 78 29 { ··· 210 259 true, true); 211 260 } 212 261 213 - static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, 214 - struct drm_atomic_state *state) 215 - { 216 - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 217 - plane); 218 - struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 219 - unsigned int old_zpos = old_state->normalized_zpos; 220 - struct sun8i_mixer *mixer = layer->mixer; 221 - 222 - sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 223 - old_zpos); 224 - } 225 262 226 263 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, 227 264 struct drm_atomic_state *state) 228 265 { 229 - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 230 - plane); 231 266 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 232 267 plane); 233 268 struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 234 269 unsigned int zpos = new_state->normalized_zpos; 235 - unsigned int old_zpos = old_state->normalized_zpos; 236 270 struct sun8i_mixer *mixer = layer->mixer; 237 271 238 - if (!new_state->visible) { 239 - sun8i_ui_layer_enable(mixer, layer->channel, 240 - layer->overlay, false, 0, old_zpos); 272 + if (!new_state->crtc || !new_state->visible) 241 273 return; 242 - } 243 274 244 275 sun8i_ui_layer_update_coord(mixer, layer->channel, 245 276 layer->overlay, plane, zpos); ··· 231 298 layer->overlay, plane); 232 299 sun8i_ui_layer_update_buffer(mixer, layer->channel, 233 300 layer->overlay, plane); 234 - sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, 235 - true, zpos, old_zpos); 236 301 } 237 302 238 303 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { 239 304 .atomic_check = sun8i_ui_layer_atomic_check, 240 - .atomic_disable = sun8i_ui_layer_atomic_disable, 241 305 .atomic_update = sun8i_ui_layer_atomic_update, 242 306 }; 243 307 ··· 320 390 321 391 drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs); 322 392 layer->mixer = mixer; 393 + layer->type = SUN8I_LAYER_TYPE_UI; 323 394 layer->channel = channel; 324 395 layer->overlay = 0; 325 396
+2 -72
drivers/gpu/drm/sun4i/sun8i_vi_layer.c
··· 18 18 #include "sun8i_vi_layer.h" 19 19 #include "sun8i_vi_scaler.h" 20 20 21 - static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, 22 - int overlay, bool enable, unsigned int zpos, 23 - unsigned int old_zpos) 24 - { 25 - u32 val, bld_base, ch_base; 26 - 27 - bld_base = sun8i_blender_base(mixer); 28 - ch_base = sun8i_channel_base(mixer, channel); 29 - 30 - DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", 31 - enable ? "En" : "Dis", channel, overlay); 32 - 33 - if (enable) 34 - val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 35 - else 36 - val = 0; 37 - 38 - regmap_update_bits(mixer->engine.regs, 39 - SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 40 - SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); 41 - 42 - if (!enable || zpos != old_zpos) { 43 - regmap_update_bits(mixer->engine.regs, 44 - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 45 - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 46 - 0); 47 - 48 - regmap_update_bits(mixer->engine.regs, 49 - SUN8I_MIXER_BLEND_ROUTE(bld_base), 50 - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 51 - 0); 52 - } 53 - 54 - if (enable) { 55 - val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 56 - 57 - regmap_update_bits(mixer->engine.regs, 58 - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 59 - val, val); 60 - 61 - val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 62 - 63 - regmap_update_bits(mixer->engine.regs, 64 - SUN8I_MIXER_BLEND_ROUTE(bld_base), 65 - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 66 - val); 67 - } 68 - } 69 - 70 21 static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, 71 22 int overlay, struct drm_plane *plane) 72 23 { ··· 344 393 true, true); 345 394 } 346 395 347 - static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, 348 - struct drm_atomic_state *state) 349 - { 350 - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 351 - plane); 352 - struct sun8i_layer *layer = plane_to_sun8i_vi_layer(plane); 353 - unsigned int old_zpos = old_state->normalized_zpos; 354 - struct sun8i_mixer *mixer = layer->mixer; 355 - 356 - sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 357 - old_zpos); 358 - } 359 - 360 396 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, 361 397 struct drm_atomic_state *state) 362 398 { 363 - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 364 - plane); 365 399 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 366 400 plane); 367 401 struct sun8i_layer *layer = plane_to_sun8i_layer(plane); 368 402 unsigned int zpos = new_state->normalized_zpos; 369 - unsigned int old_zpos = old_state->normalized_zpos; 370 403 struct sun8i_mixer *mixer = layer->mixer; 371 404 372 - if (!new_state->visible) { 373 - sun8i_vi_layer_enable(mixer, layer->channel, 374 - layer->overlay, false, 0, old_zpos); 405 + if (!new_state->crtc || !new_state->visible) 375 406 return; 376 - } 377 407 378 408 sun8i_vi_layer_update_coord(mixer, layer->channel, 379 409 layer->overlay, plane, zpos); ··· 364 432 layer->overlay, plane); 365 433 sun8i_vi_layer_update_buffer(mixer, layer->channel, 366 434 layer->overlay, plane); 367 - sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, 368 - true, zpos, old_zpos); 369 435 } 370 436 371 437 static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { 372 438 .atomic_check = sun8i_vi_layer_atomic_check, 373 - .atomic_disable = sun8i_vi_layer_atomic_disable, 374 439 .atomic_update = sun8i_vi_layer_atomic_update, 375 440 }; 376 441 ··· 542 613 543 614 drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); 544 615 layer->mixer = mixer; 616 + layer->type = SUN8I_LAYER_TYPE_VI; 545 617 layer->channel = index; 546 618 layer->overlay = 0; 547 619