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 diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d6a9322462405c..a0b5503928dfb6 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. * @@ -292,24 +279,24 @@ static struct drm_encoder *vc4_get_connector_encoder(struct drm_connector *conne * 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) { - struct drm_encoder *encoder; - struct vc4_encoder *vc4_encoder; + struct drm_connector_state *conn_state = get_state(state, connector); - encoder = vc4_get_connector_encoder(connector); - if (!encoder) + if (!conn_state) continue; - vc4_encoder = to_vc4_encoder(encoder); - if (vc4_encoder->crtc == crtc) { + if (conn_state->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); - return encoder; + return connector->encoder; } } drm_connector_list_iter_end(&conn_iter); @@ -326,16 +313,17 @@ 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); - 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); - 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 || @@ -443,10 +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); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -487,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, @@ -509,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, @@ -518,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); @@ -525,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 @@ -546,7 +565,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); @@ -561,7 +581,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); 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; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 0a786803c3203c..13886a5367369b 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) @@ -1175,36 +1184,21 @@ 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; - 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; } @@ -2145,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) { @@ -2744,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;