From 3f0820fff63814d4db055be52f03d35111bb8e68 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 5 May 2021 15:35:34 +0200 Subject: [PATCH 1/9] clk: Always clamp the rounded rate The current core while setting the min and max rate properly in the clk_request structure will not make sure that the requested rate is within these boundaries, leaving it to each and every driver to make sure it is. Add a clamp call to make sure it's always done. Signed-off-by: Maxime Ripard --- drivers/clk/clk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1b9a76c7dafa85..f4695bac4cecc4 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1316,6 +1316,8 @@ static int clk_core_determine_round_nolock(struct clk_core *core, if (!core) return 0; + req->rate = clamp(req->rate, req->min_rate, req->max_rate); + /* * At this point, core protection will be disabled if * - if the provider is not protected at all From 73077709c6a032c1270e789d28be3ff50e52839d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 5 May 2021 14:06:21 +0200 Subject: [PATCH 2/9] Revert "drm/vc4: Rework the encoder retrieval code" This reverts commit 3cf3d39b754a78c67134da7b8109eb968129ef8d. This commit was making the assumption that we had a 1:1 mapping between the encoders and their CRTC. While this is true for the HDMI controllers on the BCM2711, this isn't true for the other encoders (DSI0 and DPI share the PixelValve 0, and DSI1 and SMI share the PixelValve1), and this isn't true at all on the older SoCs, effectively breaking the encoder retrieval logic. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_crtc.c | 25 ++--------------------- drivers/gpu/drm/vc4/vc4_drv.c | 36 ---------------------------------- drivers/gpu/drm/vc4/vc4_drv.h | 10 ---------- 3 files changed, 2 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d6a9322462405c..1d1ae0d9e48f5d 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -272,19 +272,6 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc, PV_CONTROL_FIFO_LEVEL); } -static struct drm_encoder *vc4_get_connector_encoder(struct drm_connector *connector) -{ - struct drm_encoder *encoder; - - if (drm_WARN_ON(connector->dev, hweight32(connector->possible_encoders) != 1)) - return NULL; - - drm_connector_for_each_possible_encoder(connector, encoder) - return encoder; - - return NULL; -} - /* * Returns the encoder attached to the CRTC. * @@ -299,17 +286,9 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { - struct drm_encoder *encoder; - struct vc4_encoder *vc4_encoder; - - encoder = vc4_get_connector_encoder(connector); - if (!encoder) - continue; - - vc4_encoder = to_vc4_encoder(encoder); - if (vc4_encoder->crtc == crtc) { + if (connector->state->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); - return encoder; + return connector->encoder; } } drm_connector_list_iter_end(&conn_iter); diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index c404ef49f42057..3683780b9de8e1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -226,41 +226,6 @@ static int compare_dev(struct device *dev, void *data) return dev == data; } -static struct drm_crtc *vc4_drv_find_crtc(struct drm_device *drm, - struct drm_encoder *encoder) -{ - struct drm_crtc *crtc; - - if (WARN_ON(hweight32(encoder->possible_crtcs) != 1)) - return NULL; - - drm_for_each_crtc(crtc, drm) { - if (!drm_encoder_crtc_ok(encoder, crtc)) - continue; - - return crtc; - } - - return NULL; -} - -static void vc4_drv_set_encoder_data(struct drm_device *drm) -{ - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, drm) { - struct vc4_encoder *vc4_encoder; - struct drm_crtc *crtc; - - crtc = vc4_drv_find_crtc(drm, encoder); - if (WARN_ON(!crtc)) - return; - - vc4_encoder = to_vc4_encoder(encoder); - vc4_encoder->crtc = crtc; - } -} - static void vc4_match_add_drivers(struct device *dev, struct component_match **match, struct platform_driver *const *drivers, @@ -343,7 +308,6 @@ static int vc4_drm_bind(struct device *dev) ret = component_bind_all(dev, drm); if (ret) return ret; - vc4_drv_set_encoder_data(drm); if (!vc4->firmware_kms) { ret = vc4_plane_create_additional_planes(drm); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index ff4e71f79e4941..7a70838595b2f4 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -445,16 +445,6 @@ enum vc4_encoder_type { struct vc4_encoder { struct drm_encoder base; - - /* - * At boot time, we need to be able to retrieve the CRTC for a given - * connector in order to run the disable hooks below to avoid the stuck - * pixel issue. Unfortunately the drm_connector->encoder pointer is - * NULL at that time so we can't move up the chain, so we'll store it - * ourselves here. - */ - struct drm_crtc *crtc; - enum vc4_encoder_type type; u32 clock_select; From ec37d67c2bdfd9a1a7febb8fb80cf0156ccb81f2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 May 2021 17:01:46 +0200 Subject: [PATCH 3/9] drm/vc4: crtc: Pass the drm_atomic_state to config_pv The vc4_crtc_config_pv will need to access the drm_atomic_state structure and its only parent function, vc4_crtc_atomic_enable already has access to it. Let's pass it as a parameter. Fixes: 792c3132bc1b ("drm/vc4: encoder: Add finer-grained encoder callbacks") Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_crtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 1d1ae0d9e48f5d..513c7528813188 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -305,7 +305,7 @@ static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc) CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR); } -static void vc4_crtc_config_pv(struct drm_crtc *crtc) +static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -313,8 +313,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc) struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); - struct drm_crtc_state *state = crtc->state; - struct drm_display_mode *mode = &state->adjusted_mode; + struct drm_crtc_state *crtc_state = crtc->state; + struct drm_display_mode *mode = &crtc_state->adjusted_mode; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || @@ -540,7 +540,7 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, if (vc4_encoder->pre_crtc_configure) vc4_encoder->pre_crtc_configure(encoder, state); - vc4_crtc_config_pv(crtc); + vc4_crtc_config_pv(crtc, state); CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN); From 700ea766c0c581db9408fc435355edb41fc84254 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 May 2021 17:07:07 +0200 Subject: [PATCH 4/9] drm/vc4: crtc: Fix vc4_get_crtc_encoder logic The vc4_get_crtc_encoder function currently only works when the connector->state->crtc pointer is set, which is only true when the connector is currently enabled. However, we use it as part of the disable path as well, and our lookup will fail in that case, resulting in it returning a null pointer we can't act on. We can access the connector that used to be connected to that crtc though using the old connector state in the disable path. Since we want to support both the enable and disable path, we can support it by passing the state accessor variant as a function pointer, together with the atomic state. Fixes: 792c3132bc1b ("drm/vc4: encoder: Add finer-grained encoder callbacks") Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_crtc.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 513c7528813188..a813b7de60ef69 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -279,14 +279,22 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc, * allows drivers to push pixels to more than one encoder from the * same CRTC. */ -static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) +static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, + struct drm_atomic_state *state, + struct drm_connector_state *(*get_state)(struct drm_atomic_state *state, + struct drm_connector *connector)) { struct drm_connector *connector; struct drm_connector_list_iter conn_iter; drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->state->crtc == crtc) { + struct drm_connector_state *conn_state = get_state(state, connector); + + if (!conn_state) + continue; + + if (conn_state->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); return connector->encoder; } @@ -309,7 +317,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_atomic_state *s { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_new_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); @@ -425,7 +434,8 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, struct drm_atomic_state *state, unsigned int channel) { - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_old_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -525,7 +535,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_new_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); require_hvs_enabled(dev); From 5447ea0d811059d709d53653b038323e7bf0832b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 May 2021 17:15:57 +0200 Subject: [PATCH 5/9] drm/vc4: crtc: Lookup the encoder from the register at boot At boot, we can't rely on the vc4_get_crtc_encoder since we don't have a state yet and thus will not be able to figure out which connector is attached to our CRTC. However, we have a muxing bit in the CRTC register we can use to get the encoder currently connected to the pixelvalve. We can thus read that register, lookup the associated register through the vc4_pv_data structure, and then pass it to vc4_crtc_disable so that we can perform the proper operations. Fixes: 875a4d536842 ("drm/vc4: drv: Disable the CRTC at boot time") Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_crtc.c | 38 ++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index a813b7de60ef69..a0b5503928dfb6 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -431,11 +431,10 @@ static void require_hvs_enabled(struct drm_device *dev) } static int vc4_crtc_disable(struct drm_crtc *crtc, + struct drm_encoder *encoder, struct drm_atomic_state *state, unsigned int channel) { - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, - drm_atomic_get_old_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -476,10 +475,29 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, return 0; } +static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc, + enum vc4_encoder_type type) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, crtc->dev) { + struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + + if (vc4_encoder->type == type) + return encoder; + } + + return NULL; +} + int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) { struct drm_device *drm = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + enum vc4_encoder_type encoder_type; + const struct vc4_pv_data *pv_data; + struct drm_encoder *encoder; + unsigned encoder_sel; int channel; if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node, @@ -498,7 +516,17 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) if (channel < 0) return 0; - return vc4_crtc_disable(crtc, NULL, channel); + encoder_sel = VC4_GET_FIELD(CRTC_READ(PV_CONTROL), PV_CONTROL_CLK_SELECT); + if (WARN_ON(encoder_sel != 0)) + return 0; + + pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); + encoder_type = pv_data->encoder_types[encoder_sel]; + encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type); + if (WARN_ON(!encoder)) + return 0; + + return vc4_crtc_disable(crtc, encoder, NULL, channel); } static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, @@ -507,6 +535,8 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_old_connector_state); struct drm_device *dev = crtc->dev; require_hvs_enabled(dev); @@ -514,7 +544,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, /* Disable vblank irq handling before crtc is disabled. */ drm_crtc_vblank_off(crtc); - vc4_crtc_disable(crtc, state, old_vc4_state->assigned_channel); + vc4_crtc_disable(crtc, encoder, state, old_vc4_state->assigned_channel); /* * Make sure we issue a vblank event after disabling the CRTC if From a05cd8824314164dbaf4ee6de052afa678550e6a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 May 2021 16:37:26 +0200 Subject: [PATCH 6/9] drm/vc4: hdmi: Switch to pm_runtime_resume_and_get pm_runtime_get_sync increases the PM usage counter even if it fails, and forgetting to do so will result in a reference leak. We can't really do anything in atomic_enable in case of a failure though, and we probably can't recover either, but at least switching to pm_runtime_resume_and_get makes us play nice with the PM subsystem. Fixes: 4f6e3d66ac52 ("drm/vc4: Add runtime PM support to the HDMI encoder driver") Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 0a786803c3203c..2db4af7a1cb318 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1203,8 +1203,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, unsigned long bvb_rate, pixel_rate, hsm_rate; int ret; - ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); - if (ret < 0) { + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); + if (ret) { DRM_ERROR("Failed to retain power domain: %d\n", ret); return; } From 776ed37d6c7cbd08988d00f8098fc9815b85a7cd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 May 2021 16:40:19 +0200 Subject: [PATCH 7/9] drm/vc4: hdmi: Make sure the controller is powered in detect If the HPD GPIO is not available and drm_probe_ddc fails, we end up reading the HDMI_HOTPLUG register, but the controller might be powered off resulting in a CPU hang. Make sure we have the power domain and the HSM clock powered during the detect cycle to prevent the hang from happening. Fixes: 4f6e3d66ac52 ("drm/vc4: Add runtime PM support to the HDMI encoder driver") Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 2db4af7a1cb318..5db4971a922035 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -477,8 +477,12 @@ static enum drm_connector_status vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) { struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); + enum drm_connector_status ret = connector_status_disconnected; bool connected = false; + WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev)); + WARN_ON(clk_prepare_enable(vc4_hdmi->hsm_clock)); + if (vc4_hdmi->hpd_gpio) { if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^ vc4_hdmi->hpd_active_low) @@ -500,11 +504,16 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) } } - return connector_status_connected; + ret = connector_status_connected; + goto out; } cec_phys_addr_invalidate(vc4_hdmi->cec_adap); - return connector_status_disconnected; + +out: + clk_disable_unprepare(vc4_hdmi->hsm_clock); + pm_runtime_put(&vc4_hdmi->pdev->dev); + return ret; } static void vc4_hdmi_connector_destroy(struct drm_connector *connector) From fa199911af27ec11151df02b2e755f6041ef51c8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 29 Apr 2021 21:58:27 +0200 Subject: [PATCH 8/9] drm/vc4: hdmi: Simplify the connector state retrieval When we have the entire DRM state, retrieving the connector state only requires the drm_connector pointer. Fortunately for us, we have allocated it as a part of the vc4_hdmi structure, so we can retrieve get a pointer by simply accessing our field in that structure. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 5db4971a922035..81c2c957183f81 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1184,31 +1184,16 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) "VC4_HDMI_FIFO_CTL_RECENTER_DONE"); } -static struct drm_connector_state * -vc4_hdmi_encoder_get_connector_state(struct drm_encoder *encoder, - struct drm_atomic_state *state) -{ - struct drm_connector_state *conn_state; - struct drm_connector *connector; - unsigned int i; - - for_each_new_connector_in_state(state, connector, conn_state, i) { - if (conn_state->best_encoder == encoder) - return conn_state; - } - - return NULL; -} - static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, struct drm_atomic_state *state) { + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *conn_state = - vc4_hdmi_encoder_get_connector_state(encoder, state); + drm_atomic_get_new_connector_state(state, connector); struct vc4_hdmi_connector_state *vc4_conn_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); unsigned long bvb_rate, pixel_rate, hsm_rate; int ret; From 671a8068ee5feae1d92e6d48027fa8de062e2af2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 29 Apr 2021 11:08:52 +0200 Subject: [PATCH 9/9] drm/vc4: hdmi: Rely on interrupts to handle hotplug DRM currently polls for the HDMI connector status every 10s, which can be an issue when we connect/disconnect a display quickly or the device on the other end only issues a hotplug pulse (for example on EDID change). Switch the driver to rely on the internal controller logic for the BCM2711/RPi4. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 81c2c957183f81..13886a5367369b 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2139,6 +2139,46 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) } +static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv) +{ + struct vc4_hdmi *vc4_hdmi = priv; + struct drm_device *dev = vc4_hdmi->connector.dev; + + if (dev) + drm_kms_helper_hotplug_event(dev); + + return IRQ_HANDLED; +} + +static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi) +{ + struct platform_device *pdev = vc4_hdmi->pdev; + struct device *dev = &pdev->dev; + int ret; + + if (vc4_hdmi->variant->external_irq_controller) { + ret = devm_request_threaded_irq(dev, + platform_get_irq_byname(pdev, "hpd-connected"), + NULL, + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, + "vc4 hdmi hpd connected", vc4_hdmi); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, + platform_get_irq_byname(pdev, "hpd-removed"), + NULL, + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, + "vc4 hdmi hpd disconnected", vc4_hdmi); + if (ret) + return ret; + + connector->polled = DRM_CONNECTOR_POLL_HPD; + } + + return 0; +} + #ifdef CONFIG_DRM_VC4_HDMI_CEC static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv) { @@ -2738,6 +2778,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_destroy_encoder; + ret = vc4_hdmi_hotplug_init(vc4_hdmi); + if (ret) + goto err_destroy_conn; + ret = vc4_hdmi_cec_init(vc4_hdmi); if (ret) goto err_destroy_conn;