mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
drm/bridge: dw-hdmi-qp: Provide dw_hdmi_qp_hpd_notify() helper
Export dw_hdmi_qp_hpd_notify() for platform drivers to report hot-plug detection events. Unlike drm_helper_hpd_irq_event() which polls all connectors, this helper targets only the affected connector and ensures .detect_ctx() is invoked on reconnection events to trigger SCDC state recovery. Track the connector in use in curr_conn, set and cleared by the atomic enable/disable hooks. Since the helper runs asynchronously from IRQ/work context, hold a reference on that connector for as long as the bridge is enabled, so that the pointer is always safe to dereference. Otherwise validity would rest on the platform glue quiescing HPD before the DRM-managed connector is freed during teardown, risking a use-after-free on a late HPD event. The helper must take its own temporary reference while running, because the long-lived reference above only guarantees the pointer is valid at the time it is read, not that the connector survives a concurrent atomic_disable dropping that reference while the helper is still using it. Moreover, reading the pointer and acquiring the temporary reference must be atomic with respect to atomic_disable, otherwise a window exists where drm_connector_get() is called on a connector whose refcount already reached zero, hence guard curr_conn with a mutex for this purpose. Holding the mutex across the whole helper instead is not an option, as it takes modeset locks while atomic_disable is called with them held. Also note that this helper may sleep, so it must only be called from a threaded HPD interrupt handler or a work function. Tested-by: Maud Spierings <maud_spierings@hotmail.com> Tested-by: Diederik de Haas <diederik@cknow-tech.com> # NanoPC-T6 LTS, Rock 5B Tested-by: Robin Räber <robin.raebi@gmail.com> Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com> Signed-off-by: Heiko Stuebner <heiko@sntech.de> Link: https://patch.msgid.link/20261005-dw-hdmi-qp-scramb-v12-5-195157ba0b40@collabora.com
This commit is contained in:
parent
046dbe428a
commit
a58efc1aa0
|
|
@ -28,6 +28,7 @@
|
|||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
|
|
@ -81,6 +82,9 @@ struct dw_hdmi_qp {
|
|||
struct regmap *regm;
|
||||
int main_irq;
|
||||
|
||||
/* Guards curr_conn between atomic hooks and HPD notify readers */
|
||||
struct mutex mutex;
|
||||
struct drm_connector *curr_conn;
|
||||
unsigned long tmds_char_rate;
|
||||
bool no_hpd;
|
||||
};
|
||||
|
|
@ -564,6 +568,10 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
|||
dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
|
||||
}
|
||||
|
||||
drm_connector_get(connector);
|
||||
scoped_guard(mutex, &hdmi->mutex)
|
||||
hdmi->curr_conn = connector;
|
||||
|
||||
hdmi->phy.ops->init(hdmi, hdmi->phy.data);
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
||||
|
|
@ -578,6 +586,13 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
|||
struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
||||
struct drm_connector *connector;
|
||||
|
||||
scoped_guard(mutex, &hdmi->mutex) {
|
||||
if (hdmi->curr_conn) {
|
||||
drm_connector_put(hdmi->curr_conn);
|
||||
hdmi->curr_conn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
hdmi->tmds_char_rate = 0;
|
||||
|
||||
connector = drm_atomic_get_old_connector_for_encoder(state, bridge->encoder);
|
||||
|
|
@ -1099,6 +1114,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|||
if (IS_ERR(hdmi))
|
||||
return ERR_CAST(hdmi);
|
||||
|
||||
ret = devm_mutex_init(dev, &hdmi->mutex);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
hdmi->dev = dev;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
|
|
@ -1194,6 +1213,46 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
|
||||
|
||||
/**
|
||||
* dw_hdmi_qp_hpd_notify() - Notify a hot-plug detection event
|
||||
* @hdmi: pointer to the DW HDMI QP controller
|
||||
*
|
||||
* Platform drivers should call this from their threaded IRQ handler or work
|
||||
* function to notify the bridge of a connection status change.
|
||||
* The bridge's .read_hpd() phy_ops callback is used to read the current
|
||||
* connection status.
|
||||
*/
|
||||
void dw_hdmi_qp_hpd_notify(struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
enum drm_connector_status status;
|
||||
struct drm_connector *conn;
|
||||
|
||||
scoped_guard(mutex, &hdmi->mutex) {
|
||||
conn = hdmi->curr_conn;
|
||||
if (conn)
|
||||
drm_connector_get(conn);
|
||||
}
|
||||
|
||||
status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
|
||||
dev_dbg(hdmi->dev, "%s status=%d\n", __func__, status);
|
||||
|
||||
/*
|
||||
* When the display pipeline has been already active, switch to
|
||||
* drm_connector_helper_hpd_irq_event() to ensure .detect_ctx()
|
||||
* gets invoked, i.e. via drm_helper_probe_detect(), because
|
||||
* drm_bridge_hpd_notify() defers to a delayed hotplug path in
|
||||
* this case.
|
||||
*/
|
||||
if (conn && status == connector_status_connected)
|
||||
drm_connector_helper_hpd_irq_event(conn);
|
||||
else
|
||||
drm_bridge_hpd_notify(&hdmi->bridge, status);
|
||||
|
||||
if (conn)
|
||||
drm_connector_put(conn);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_qp_hpd_notify);
|
||||
|
||||
void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
disable_irq(hdmi->main_irq);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ struct dw_hdmi_qp_plat_data {
|
|||
struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||||
struct drm_encoder *encoder,
|
||||
const struct dw_hdmi_qp_plat_data *plat_data);
|
||||
void dw_hdmi_qp_hpd_notify(struct dw_hdmi_qp *hdmi);
|
||||
void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi);
|
||||
void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi);
|
||||
#endif /* __DW_HDMI_QP__ */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user