diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_txp.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_txp.c | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index bf720206727f..a7c3af0005a0 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -19,6 +19,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> #include <drm/drm_writeback.h> #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; @@ -222,6 +225,13 @@ static const u32 txp_fmts[] = { TXP_FORMAT_BGRA8888, }; +static void vc4_txp_armed(struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + + vc4_state->txp_armed = true; +} + static int vc4_txp_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_state *state) { @@ -256,7 +266,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, if (fb->pitches[0] & GENMASK(3, 0)) return -EINVAL; - vc4_crtc_txp_armed(crtc_state); + vc4_txp_armed(crtc_state); return 0; } @@ -355,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); @@ -381,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; @@ -400,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) |