Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] drm/mediatek: NULL mtk_dp->conn in mtk_dp_report_link_train() on hotplug - [Medium] drm/mediatek: mtk_dp_bridge_atomic_enable() reports untrained DP link state - [Medium] drm/mediatek: unsynchronized DP link state reporting in mtk_dp_hpd_event_thread() --
commit da87270e7bdd066871fe7e9da8a36687e44518d5 Author: Kory Maincent <[email protected]> drm/mediatek: Use dp_connector helpers to expose link training state This commit adopts the DRM core DisplayPort helpers in mtk_dp by setting DRM_BRIDGE_OP_DP and populating dp_link_caps at probe time. It also updates the hotplug, detect, and atomic enable/disable paths to synchronize sink capabilities and report link training parameters. > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c > b/drivers/gpu/drm/mediatek/mtk_dp.c > index f656b85b84212..b87e171d88d83 100644 > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > @@ -1812,6 +1812,17 @@ static void mtk_dp_train_change_mode(struct mtk_dp > *mtk_dp) > mtk_dp_reset_swing_pre_emphasis(mtk_dp); > } > > +static void mtk_dp_report_link_train(struct mtk_dp *mtk_dp) > +{ > + u32 rate, nlanes; > + bool dsc = false; > + > + rate = drm_dp_bw_code_to_link_rate(mtk_dp->train_info.link_rate); > + nlanes = mtk_dp->train_info.lane_count; > + > + drm_dp_set_cur_link_params(mtk_dp->conn, rate, nlanes, dsc); > +} [Severity: Medium] Will mtk_dp->conn be NULL when mtk_dp_report_link_train() is called from mtk_dp_hpd_event_thread() on initial DisplayPort hotplug? In drivers/gpu/drm/mediatek/mtk_dp.c, mtk_dp->conn is only assigned in mtk_dp_bridge_atomic_enable(): mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); Neither mtk_dp_bdg_detect() nor mtk_dp_bridge_attach() initializes mtk_dp->conn. Moreover, on initial hotplug, mtk_dp_bridge_mode_valid() rejects all modes until mtk_dp_training() sets train_info.link_rate and train_info.lane_count in mtk_dp_hpd_event_thread(), so mtk_dp_bridge_atomic_enable() cannot have run prior to the first mtk_dp_report_link_train() call in mtk_dp_hpd_event_thread(). When mtk_dp_hpd_event_thread() runs link training and calls mtk_dp_report_link_train() -> drm_dp_set_cur_link_params() before a modeset has enabled the bridge, mtk_dp->conn is still NULL, causing drm_dp_set_cur_link_params() to return early without recording cur_link_info or updating sysfs. > > static int mtk_dp_training(struct mtk_dp *mtk_dp) > { > int ret; > @@ -2015,6 +2026,8 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, > void *dev) > ret = mtk_dp_training(mtk_dp); > if (ret) > drm_err(mtk_dp->drm_dev, "Training failed, > %d\n", ret); > + else > + mtk_dp_report_link_train(mtk_dp); > > mtk_dp->enabled = true; > } > @@ -2145,9 +2158,12 @@ mtk_dp_bdg_detect(struct drm_bridge *bridge, struct > drm_connector *connector) > struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); > enum drm_connector_status ret = connector_status_disconnected; > bool enabled = mtk_dp->enabled; > + int err; > > - if (!mtk_dp->train_info.cable_plugged_in) > + if (!mtk_dp->train_info.cable_plugged_in) { > + drm_dp_sink_reset_caps(connector); > return ret; > + } > > if (!enabled) > mtk_dp_aux_panel_poweron(mtk_dp, true); > @@ -2160,9 +2176,18 @@ mtk_dp_bdg_detect(struct drm_bridge *bridge, struct > drm_connector *connector) > * whether we connect to a sink device. > */ > > - if (drm_dp_read_sink_count(&mtk_dp->aux) > 0) > + if (drm_dp_read_sink_count(&mtk_dp->aux) > 0) { > ret = connector_status_connected; > > + err = drm_dp_sink_sync_caps(connector, &mtk_dp->aux, NULL); > + if (err) > + drm_dbg_kms(mtk_dp->aux.drm_dev, > + "%s: error sink caps synchronisation > (%d)\n", > + mtk_dp->aux.name, err); > + } else { > + drm_dp_sink_reset_caps(connector); > + } > + > if (!enabled) > mtk_dp_aux_panel_poweron(mtk_dp, false); [Severity: Medium] Can mtk_dp_bdg_detect() race with mtk_dp_hpd_event_thread() during hotplug processing and corrupt the link training or reported link state? In mtk_dp_hpd_event_thread(), drm_helper_hpd_irq_event() is called before mtk_dp_training(), mtk_dp_report_link_train(), and mtk_dp->enabled = true: if (status & MTK_DP_THREAD_CABLE_STATE_CHG) { if (mtk_dp->bridge.dev) drm_helper_hpd_irq_event(mtk_dp->bridge.dev); ... } else { mtk_dp_aux_panel_poweron(mtk_dp, true); ... ret = mtk_dp_training(mtk_dp); if (ret) drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret); else mtk_dp_report_link_train(mtk_dp); mtk_dp->enabled = true; } } Inside drm_helper_hpd_irq_event(), mtk_dp_bdg_detect() runs under mode_config.mutex, syncs or resets sink capabilities, and then drm_helper_hpd_irq_event() drops mode_config.mutex and sends a hotplug uevent to userspace. Because mtk_dp_hpd_event_thread() performs capability parsing, mtk_dp_training(), and mtk_dp_report_link_train() without holding connection_mutex, mode_config.mutex, or any driver lock while mtk_dp->enabled is still false, a concurrent userspace probe calling mtk_dp_bdg_detect() sees enabled == false, runs drm_dp_sink_sync_caps() over AUX concurrently with mtk_dp_training(), and then calls mtk_dp_aux_panel_poweron(mtk_dp, false), powering down the panel and AUX in the middle of link training. In addition, if a branch device asserts HPD with no downstream sink (cable_plugged_in is true and drm_dp_read_sink_count(&mtk_dp->aux) <= 0) after mtk_dp->conn has been set, mtk_dp_bdg_detect() calls drm_dp_sink_reset_caps(connector), after which mtk_dp_hpd_event_thread() runs mtk_dp_report_link_train(mtk_dp) and repopulates cur_link_info on a disconnected connector. > @@ -2390,6 +2415,12 @@ static void mtk_dp_bridge_atomic_enable(struct > drm_bridge *bridge, > if (mtk_dp->data->bridge_type == DRM_MODE_CONNECTOR_eDP) { > mtk_dp_aux_panel_poweron(mtk_dp, true); > > + ret = drm_dp_sink_sync_caps(mtk_dp->conn, &mtk_dp->aux, NULL); > + if (ret) > + drm_dbg_kms(mtk_dp->aux.drm_dev, > + "%s: error sink caps synchronisation > (%d)\n", > + mtk_dp->aux.name, ret); > + > /* Training */ > ret = mtk_dp_training(mtk_dp); > if (ret) { > @@ -2398,6 +2429,8 @@ static void mtk_dp_bridge_atomic_enable(struct > drm_bridge *bridge, > } > } > > + mtk_dp_report_link_train(mtk_dp); > + > ret = mtk_dp_video_config(mtk_dp); > if (ret) > goto power_off_aux; [Severity: Medium] Should mtk_dp_report_link_train() in mtk_dp_bridge_atomic_enable() be restricted to eDP, or guarded on successful DisplayPort link training? In mtk_dp_initialize_priv_data(), mtk_dp->train_info.link_rate is initialized to DP_LINK_BW_5_4 and mtk_dp->train_info.lane_count is initialized to mtk_dp->max_lanes. For full DisplayPort (DRM_MODE_CONNECTOR_DisplayPort), mtk_dp_training() is called in mtk_dp_hpd_event_thread() rather than in mtk_dp_bridge_atomic_enable(). However, in mtk_dp_hpd_event_thread(), drm_helper_hpd_irq_event() is called before mtk_dp_training(), and if mtk_dp_training() fails, train_info.link_rate and train_info.lane_count retain their initial defaults or stale values from a previous sink while mtk_dp->enabled is still set to true. When a modeset is committed (either via drm_helper_hpd_irq_event() -> drm_client_dev_hotplug() before mtk_dp_training() runs, or by userspace after mtk_dp_training() fails), mtk_dp_bridge_atomic_enable() unconditionally calls mtk_dp_report_link_train(mtk_dp) outside the eDP branch, exposing untrained or stale train_info parameters via drm_dp_set_cur_link_params(). [ ... ] -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=8
