Hi Maxime, Am Donnerstag, 7. März 2024, 14:38:47 CET schrieb Maxime Ripard: > Infoframes in KMS is usually handled by a bunch of low-level helpers > that require quite some boilerplate for drivers. This leads to > discrepancies with how drivers generate them, and which are actually > sent. > > Now that we have everything needed to generate them in the HDMI > connector state, we can generate them in our common logic so that > drivers can simply reuse what we precomputed. > > Signed-off-by: Maxime Ripard <mripard@xxxxxxxxxx> > --- > drivers/gpu/drm/Kconfig | 1 + > drivers/gpu/drm/drm_atomic_state_helper.c | 327 +++++++++++++++++++++ > drivers/gpu/drm/drm_connector.c | 14 + > .../gpu/drm/tests/drm_atomic_state_helper_test.c | 1 + > drivers/gpu/drm/tests/drm_connector_test.c | 12 + > include/drm/drm_atomic_state_helper.h | 8 + > include/drm/drm_connector.h | 133 +++++++++ > 7 files changed, 496 insertions(+) > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 872edb47bb53..ad9c467e20ce 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -97,10 +97,11 @@ config DRM_KUNIT_TEST > If in doubt, say "N". > > config DRM_KMS_HELPER > tristate > depends on DRM > + select DRM_DISPLAY_HDMI_HELPER > help > CRTC helpers for KMS drivers. > > config DRM_DEBUG_DP_MST_TOPOLOGY_REFS > bool "Enable refcount backtrace history in the DP MST helpers" > diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c > index e66272c0d006..46d9fd2ea8fa 100644 > --- a/drivers/gpu/drm/drm_atomic_state_helper.c > +++ b/drivers/gpu/drm/drm_atomic_state_helper.c > [snip] > @@ -958,10 +1100,195 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, > > return 0; > } > EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); > > +#define HDMI_MAX_INFOFRAME_SIZE 29 > + > +static int clear_device_infoframe(struct drm_connector *connector, > + enum hdmi_infoframe_type type) > +{ > + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; > + > + if (!funcs || !funcs->clear_infoframe) > + return 0; > + > + return funcs->clear_infoframe(connector, type); > +} > + > +static int clear_infoframe(struct drm_connector *connector, > + struct drm_connector_hdmi_infoframe *conn_frame, > + struct drm_connector_hdmi_infoframe *old_frame) > +{ > + int ret; > + > + ret = clear_device_infoframe(connector, old_frame->data.any.type); > + if (ret) > + return ret; > + > + memset(old_frame, 0, sizeof(*old_frame)); > + > + return 0; > +} > + > +static int write_device_infoframe(struct drm_connector *connector, > + union hdmi_infoframe *frame) > +{ > + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; > + u8 buffer[HDMI_MAX_INFOFRAME_SIZE]; > + int len; > + > + if (!funcs || !funcs->write_infoframe) > + return -ENOSYS; > + > + len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); > + if (len < 0) > + return len; > + > + return funcs->write_infoframe(connector, frame->any.type, buffer, len); > +} > + > +static int write_infoframe(struct drm_connector *connector, > + struct drm_connector_hdmi_infoframe *conn_frame, > + struct drm_connector_hdmi_infoframe *new_frame) > +{ > + int ret; > + > + ret = write_device_infoframe(connector, &new_frame->data); > + if (ret) > + return ret; > + > + if (conn_frame) > + memcpy(conn_frame, new_frame, sizeof(*conn_frame)); > + > + return 0; > +} > + > +static int write_or_clear_infoframe(struct drm_connector *connector, > + struct drm_connector_hdmi_infoframe *conn_frame, > + struct drm_connector_hdmi_infoframe *old_frame, > + struct drm_connector_hdmi_infoframe *new_frame) > +{ > + if (new_frame->set) > + return write_infoframe(connector, conn_frame, new_frame); > + > + if (old_frame->set && !new_frame->set) > + return clear_infoframe(connector, conn_frame, old_frame); > + > + return 0; > +} > + > +#define UPDATE_INFOFRAME(c, os, ns, i) \ > + write_or_clear_infoframe(c, \ > + &(c)->hdmi.infoframes.i, \ > + &(os)->hdmi.infoframes.i, \ > + &(ns)->hdmi.infoframes.i) > + > +/** > + * drm_atomic_helper_connector_hdmi_update_infoframes - Update the Infoframes > + * @connector: A pointer to the HDMI connector > + * @state: The HDMI connector state to generate the infoframe from > + * > + * This function is meant for HDMI connector drivers to write their > + * infoframes. It will typically be used in a > + * @drm_connector_helper_funcs.atomic_enable implementation. > + * > + * Returns: > + * Zero on success, error code on failure. > + */ > +int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, > + struct drm_atomic_state *state) > +{ > + struct drm_connector_state *old_state = > + drm_atomic_get_old_connector_state(state, connector); > + struct drm_connector_state *new_state = > + drm_atomic_get_new_connector_state(state, connector); > + struct drm_display_info *info = &connector->display_info; > + int ret; > + > + if (!info->is_hdmi) > + return 0; > + > + if (!info->has_hdmi_infoframe) > + return 0; > + > + mutex_lock(&connector->hdmi.infoframes.lock); > + > + ret = UPDATE_INFOFRAME(connector, old_state, new_state, avi); > + if (ret) > + goto out; > + > + if (connector->hdmi.infoframes.audio.set) { > + ret = write_infoframe(connector, > + NULL, > + &connector->hdmi.infoframes.audio); > + if (ret) > + goto out; > + } > + > + ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdr_drm); > + if (ret) > + goto out; > + > + ret = UPDATE_INFOFRAME(connector, old_state, new_state, spd); > + if (ret) > + goto out; > + > + ret = UPDATE_INFOFRAME(connector, old_state, new_state, hdmi); > + if (ret) > + goto out; > + > +out: > + mutex_unlock(&connector->hdmi.infoframes.lock); > + return ret; > +} > +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_infoframes); > + > +#undef UPDATE_INFOFRAME > +#undef UPDATE_INFOFRAME_TOGGLE UPDATE_INFOFRAME_TOGGLE seems to never be defined. Best regards, Alexadner > + > +/** > + * drm_atomic_helper_connector_hdmi_update_audio_infoframe - Update the Audio Infoframe > + * @connector: A pointer to the HDMI connector > + * @frame: A pointer to the audio infoframe to write > + * > + * This function is meant for HDMI connector drivers to update their > + * audio infoframe. It will typically be used in one of the ALSA hooks > + * (most likely prepare). > + * > + * Returns: > + * Zero on success, error code on failure. > + */ > +int > +drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector, > + struct hdmi_audio_infoframe *frame) > +{ > + struct drm_connector_hdmi_infoframe infoframe = {}; > + struct drm_display_info *info = &connector->display_info; > + int ret; > + > + if (!info->is_hdmi) > + return 0; > + > + if (!info->has_hdmi_infoframe) > + return 0; > + > + memcpy(&infoframe.data, frame, sizeof(infoframe.data)); > + infoframe.set = true; > + > + mutex_lock(&connector->hdmi.infoframes.lock); > + > + ret = write_infoframe(connector, > + &connector->hdmi.infoframes.audio, > + &infoframe); > + > + mutex_unlock(&connector->hdmi.infoframes.lock); > + > + return ret; > +} > +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe); > + > /** > * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state > * @connector: connector object > * @state: atomic connector state > * > [snip] -- TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany Amtsgericht München, HRB 105018 Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider http://www.tq-group.com/