diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_crtc.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 91 |
1 files changed, 59 insertions, 32 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 18f5009ce90e..287dbc89ad64 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -32,6 +32,7 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -42,6 +43,7 @@ #include <drm/drm_vblank.h> #include "vc4_drv.h" +#include "vc4_hdmi.h" #include "vc4_regs.h" #define HVS_FIFO_LATENCY_PIX 6 @@ -279,27 +281,15 @@ 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, - struct drm_atomic_state *state, - struct drm_connector_state *(*get_state)(struct drm_atomic_state *state, - struct drm_connector *connector)) +struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; + struct drm_encoder *encoder; - drm_connector_list_iter_begin(crtc->dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - struct drm_connector_state *conn_state = get_state(state, connector); + WARN_ON(hweight32(state->encoder_mask) > 1); - if (!conn_state) - continue; - - if (conn_state->crtc == crtc) { - drm_connector_list_iter_end(&conn_iter); - return connector->encoder; - } - } - drm_connector_list_iter_end(&conn_iter); + drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) + return encoder; return NULL; } @@ -313,12 +303,11 @@ 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, struct drm_atomic_state *state) +static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encoder, + 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, 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); @@ -496,8 +485,10 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) enum vc4_encoder_type encoder_type; const struct vc4_pv_data *pv_data; struct drm_encoder *encoder; + struct vc4_hdmi *vc4_hdmi; unsigned encoder_sel; int channel; + int ret; if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node, "brcm,bcm2711-pixelvalve2") || @@ -525,7 +516,20 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) if (WARN_ON(!encoder)) return 0; - return vc4_crtc_disable(crtc, encoder, NULL, channel); + vc4_hdmi = encoder_to_vc4_hdmi(encoder); + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); + if (ret) + return ret; + + ret = vc4_crtc_disable(crtc, encoder, NULL, channel); + if (ret) + return ret; + + ret = pm_runtime_put(&vc4_hdmi->pdev->dev); + if (ret) + return ret; + + return 0; } static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, @@ -534,10 +538,12 @@ 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_encoder *encoder = vc4_get_crtc_encoder(crtc, old_state); struct drm_device *dev = crtc->dev; + drm_dbg(dev, "Disabling CRTC %s (%u) connected to Encoder %s (%u)", + crtc->name, crtc->base.id, encoder->name, encoder->base.id); + require_hvs_enabled(dev); /* Disable vblank irq handling before crtc is disabled. */ @@ -562,12 +568,16 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, + 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, state, - drm_atomic_get_new_connector_state); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + drm_dbg(dev, "Enabling CRTC %s (%u) connected to Encoder %s (%u)", + crtc->name, crtc->base.id, encoder->name, encoder->base.id); + require_hvs_enabled(dev); /* Enable vblank irq handling before crtc is started otherwise @@ -580,7 +590,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, state); + vc4_crtc_config_pv(crtc, encoder, state); CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN); @@ -649,12 +659,27 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); struct drm_connector *conn; struct drm_connector_state *conn_state; + struct drm_encoder *encoder; int ret, i; ret = vc4_hvs_atomic_check(crtc, state); if (ret) return ret; + encoder = vc4_get_crtc_encoder(crtc, crtc_state); + if (encoder) { + const struct drm_display_mode *mode = &crtc_state->adjusted_mode; + struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + + mode = &crtc_state->adjusted_mode; + if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) { + vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 1000, + mode->clock * 9 / 10) * 1000; + } else { + vc4_state->hvs_load = mode->clock * 1000; + } + } + for_each_new_connector_in_state(state, conn, conn_state, i) { if (conn_state->crtc != crtc) @@ -691,14 +716,14 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) struct drm_crtc *crtc = &vc4_crtc->base; struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - u32 chan = vc4_state->assigned_channel; + u32 chan = vc4_crtc->current_hvs_channel; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); + spin_lock(&vc4_crtc->irq_lock); if (vc4_crtc->event && - (vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)) || - vc4_state->feed_txp)) { + (vc4_crtc->current_dlist == HVS_READ(SCALER_DISPLACTX(chan)) || + vc4_crtc->feeds_txp)) { drm_crtc_send_vblank_event(crtc, vc4_crtc->event); vc4_crtc->event = NULL; drm_crtc_vblank_put(crtc); @@ -711,6 +736,7 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) */ vc4_hvs_unmask_underrun(dev, chan); } + spin_unlock(&vc4_crtc->irq_lock); spin_unlock_irqrestore(&dev->event_lock, flags); } @@ -876,7 +902,6 @@ struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) return NULL; old_vc4_state = to_vc4_crtc_state(crtc->state); - vc4_state->feed_txp = old_vc4_state->feed_txp; vc4_state->margins = old_vc4_state->margins; vc4_state->assigned_channel = old_vc4_state->assigned_channel; @@ -937,6 +962,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = { static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .mode_valid = vc4_crtc_mode_valid, .atomic_check = vc4_crtc_atomic_check, + .atomic_begin = vc4_hvs_atomic_begin, .atomic_flush = vc4_hvs_atomic_flush, .atomic_enable = vc4_crtc_atomic_enable, .atomic_disable = vc4_crtc_atomic_disable, @@ -1111,6 +1137,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, return PTR_ERR(primary_plane); } + spin_lock_init(&vc4_crtc->irq_lock); drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, crtc_funcs, NULL); drm_crtc_helper_add(crtc, crtc_helper_funcs); |