A panel driver cannot reject a configuration it does not support, nor compute its state for the configuration that is being requested: none of its callbacks is called in the check phase of an atomic update.
Add an optional atomic_check callback, called from the atomic_check of the bridge embedded in the panel. Following the other atomic_check callbacks it receives the whole commit rather than individual object states. Add helpers returning the new CRTC and connector states for a panel, so that panel drivers don't have to open-code the walk through the encoder. The callback is only meant for the panels using atomic state, warn about the panels implementing it without the state callbacks. Assisted-by: LLM Signed-off-by: Dmitry Baryshkov <[email protected]> --- drivers/gpu/drm/drm_panel.c | 84 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_panel.h | 30 ++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 481caa294243..3a154be128a0 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -243,6 +243,19 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge, panel->funcs->debugfs_init(panel, root); } +static int panel_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + + if (!panel->funcs->atomic_check) + return 0; + + return panel->funcs->atomic_check(panel, crtc_state->state); +} + static struct drm_bridge_state * panel_bridge_atomic_create_state(struct drm_bridge *bridge) { @@ -297,6 +310,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .atomic_disable = panel_bridge_atomic_disable, .atomic_post_disable = panel_bridge_atomic_post_disable, .get_modes = panel_bridge_get_modes, + .atomic_check = panel_bridge_atomic_check, .atomic_create_state = panel_bridge_atomic_create_state, .atomic_duplicate_state = panel_bridge_atomic_duplicate_state, .atomic_destroy_state = panel_bridge_atomic_destroy_state, @@ -654,6 +668,9 @@ static void drm_panel_init(struct drm_panel *panel, struct device *dev, !funcs->atomic_create_state != !funcs->atomic_destroy_state, "Panel state callbacks have to be implemented together\n"); + dev_WARN_ONCE(dev, !funcs->atomic_create_state && funcs->atomic_check, + "Atomic callbacks require the panel state callbacks\n"); + INIT_LIST_HEAD(&panel->list); INIT_LIST_HEAD(&panel->followers); mutex_init(&panel->follower_lock); @@ -1185,6 +1202,73 @@ struct drm_panel_state *drm_panel_get_current_state(struct drm_panel *panel) } EXPORT_SYMBOL(drm_panel_get_current_state); +/** + * drm_atomic_get_new_crtc_state_for_panel() - get new CRTC state for a panel + * @commit: global atomic state object + * @panel: panel to get the CRTC state for + * + * This function returns the new state of the CRTC that drives @panel as of + * @commit. It is meant to be used from &drm_panel_funcs.atomic_check and the + * enable path. In the disable path the panel might not be driven by any CRTC + * anymore. + * + * Return: + * + * The new CRTC state, or NULL if the panel is not driven by a CRTC that is + * part of the global atomic state. + */ +struct drm_crtc_state * +drm_atomic_get_new_crtc_state_for_panel(struct drm_atomic_commit *commit, + struct drm_panel *panel) +{ + struct drm_encoder *encoder = panel->bridge.encoder; + struct drm_crtc *crtc; + + if (!encoder) + return NULL; + + crtc = drm_atomic_get_new_crtc_for_encoder(commit, encoder); + if (!crtc) + return NULL; + + return drm_atomic_get_new_crtc_state(commit, crtc); +} +EXPORT_SYMBOL(drm_atomic_get_new_crtc_state_for_panel); + +/** + * drm_atomic_get_new_connector_state_for_panel() - get new connector state for + * a panel + * @commit: global atomic state object + * @panel: panel to get the connector state for + * + * This function returns the new state of the connector that @panel is routed + * to as of @commit. It is meant to be used from &drm_panel_funcs.atomic_check + * and the enable path. In the disable path the panel might not be routed to + * any connector anymore. + * + * Return: + * + * The new connector state, or NULL if the panel is not routed to a connector + * that is part of the global atomic state. + */ +struct drm_connector_state * +drm_atomic_get_new_connector_state_for_panel(const struct drm_atomic_commit *commit, + struct drm_panel *panel) +{ + struct drm_encoder *encoder = panel->bridge.encoder; + struct drm_connector *connector; + + if (!encoder) + return NULL; + + connector = drm_atomic_get_new_connector_for_encoder(commit, encoder); + if (!connector) + return NULL; + + return drm_atomic_get_new_connector_state(commit, connector); +} +EXPORT_SYMBOL(drm_atomic_get_new_connector_state_for_panel); + /** * drm_panel_get - Acquire a panel reference * @panel: DRM panel diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index ff63bed934fa..a5879b7b986b 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -38,6 +38,8 @@ struct device_node; struct drm_atomic_commit; struct drm_bridge; struct drm_connector; +struct drm_connector_state; +struct drm_crtc_state; struct drm_device; struct drm_panel_follower; struct drm_panel; @@ -168,6 +170,28 @@ struct drm_panel_funcs { */ void (*debugfs_init)(struct drm_panel *panel, struct dentry *root); + /** + * @atomic_check: + * + * Check that the configuration requested in @commit can be handled by + * the panel and compute the new panel state. The new panel state can + * be retrieved with drm_atomic_get_new_panel_state(), the CRTC and + * connector states with drm_atomic_get_new_crtc_state_for_panel() and + * drm_atomic_get_new_connector_state_for_panel(). + * + * This function is called in the check phase of an atomic update, it + * must not touch the hardware or any persistent driver data. + * + * This function is optional. It can only be implemented together with + * @atomic_create_state. + * + * Return: + * + * Zero if the check passed, a negative error code otherwise. + */ + int (*atomic_check)(struct drm_panel *panel, + struct drm_atomic_commit *commit); + /** * @atomic_create_state: * @@ -438,6 +462,12 @@ drm_atomic_get_old_panel_state(const struct drm_atomic_commit *commit, struct drm_panel_state * drm_atomic_get_new_panel_state(const struct drm_atomic_commit *commit, struct drm_panel *panel); +struct drm_crtc_state * +drm_atomic_get_new_crtc_state_for_panel(struct drm_atomic_commit *commit, + struct drm_panel *panel); +struct drm_connector_state * +drm_atomic_get_new_connector_state_for_panel(const struct drm_atomic_commit *commit, + struct drm_panel *panel); #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL) struct drm_panel *of_drm_find_panel(const struct device_node *np); -- 2.47.3
