diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 6211e8abdc848159ce54db0d6f3959ad1c7e2abf..1a9c7a80f37f7faf188205af0b143e8a161c2ac9 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -474,17 +474,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, if (conn_state->crtc != crtc) continue; - /* The writeback connector is implemented using the transposer - * block which is directly taking its data from the HVS FIFO. - */ - if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { - state->no_vblank = true; - vc4_state->feed_txp = true; - } else { - state->no_vblank = false; - vc4_state->feed_txp = false; - } - vc4_state->margins.left = conn_state->tv.margins.left; vc4_state->margins.right = conn_state->tv.margins.right; vc4_state->margins.top = conn_state->tv.margins.top; @@ -826,7 +815,6 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc); const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); const enum vc4_encoder_type *encoder_types = pv_data->encoder_types; struct drm_encoder *encoder; @@ -835,13 +823,6 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct vc4_encoder *vc4_encoder; int i; - /* HVS FIFO2 can feed the TXP IP. */ - if (crtc_data->hvs_channel == 2 && - encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { - encoder->possible_crtcs |= drm_crtc_mask(crtc); - continue; - } - vc4_encoder = to_vc4_encoder(encoder); for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index d9a8ab87ad254512c4f3894dcb9c57bbf34791f4..a7c3af0005a0aa0908ff0c46a0d312b95eeb6a96 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "vc4_drv.h" @@ -145,6 +146,8 @@ #define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) struct vc4_txp { + struct vc4_crtc base; + struct platform_device *pdev; struct drm_writeback_connector connector; @@ -362,23 +365,105 @@ static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { .disable = vc4_txp_encoder_disable, }; +static int vc4_txp_enable_vblank(struct drm_crtc *crtc) +{ + return 0; +} + +static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {} + +static const struct drm_crtc_funcs vc4_txp_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, + .page_flip = vc4_page_flip, + .reset = vc4_crtc_reset, + .atomic_duplicate_state = vc4_crtc_duplicate_state, + .atomic_destroy_state = vc4_crtc_destroy_state, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .enable_vblank = vc4_txp_enable_vblank, + .disable_vblank = vc4_txp_disable_vblank, +}; + +static int vc4_txp_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + int ret; + + ret = vc4_hvs_atomic_check(crtc, state); + if (ret) + return ret; + + state->no_vblank = true; + vc4_state->feed_txp = true; + + return 0; +} + +static void vc4_txp_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + drm_crtc_vblank_on(crtc); + vc4_hvs_atomic_enable(crtc, old_state); +} + +static void vc4_txp_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + + /* Disable vblank irq handling before crtc is disabled. */ + drm_crtc_vblank_off(crtc); + + vc4_hvs_atomic_disable(crtc, old_state); + + /* + * Make sure we issue a vblank event after disabling the CRTC if + * someone was waiting it. + */ + if (crtc->state->event) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} + +static const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = { + .atomic_check = vc4_txp_atomic_check, + .atomic_flush = vc4_hvs_atomic_flush, + .atomic_enable = vc4_txp_atomic_enable, + .atomic_disable = vc4_txp_atomic_disable, + .mode_set_nofb = vc4_hvs_mode_set_nofb, +}; + static irqreturn_t vc4_txp_interrupt(int irq, void *data) { struct vc4_txp *txp = data; + struct vc4_crtc *vc4_crtc = &txp->base; TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); - vc4_crtc_handle_vblank(to_vc4_crtc(txp->connector.base.state->crtc)); + vc4_crtc_handle_vblank(vc4_crtc); drm_writeback_signal_completion(&txp->connector, 0); return IRQ_HANDLED; } +static const struct vc4_crtc_data vc4_txp_crtc_data = { + .hvs_channel = 2, +}; + static int vc4_txp_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_crtc *vc4_crtc; struct vc4_txp *txp; + struct drm_crtc *crtc; + struct drm_encoder *encoder; int ret, irq; irq = platform_get_irq(pdev, 0); @@ -388,6 +473,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL); if (!txp) return -ENOMEM; + vc4_crtc = &txp->base; + crtc = &vc4_crtc->base; + + vc4_crtc->pdev = pdev; + vc4_crtc->data = &vc4_txp_crtc_data; txp->pdev = pdev; @@ -407,6 +497,14 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; + ret = vc4_crtc_init(drm, vc4_crtc, + &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs); + if (ret) + return ret; + + encoder = &txp->connector.encoder; + encoder->possible_crtcs |= drm_crtc_mask(crtc); + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, dev_name(dev), txp); if (ret)