drm/vc4: txp: Turn the TXP into a CRTC of its own
authorMaxime Ripard <maxime@cerno.tech>
Thu, 11 Jun 2020 13:36:53 +0000 (15:36 +0200)
committerMaxime Ripard <maxime@cerno.tech>
Tue, 7 Jul 2020 08:51:21 +0000 (10:51 +0200)
The TXP so far has been leveraging the PixelValve infrastructure in the
driver, that was really two things: the interaction with DRM's CRTC
concept, the setup of the underlying pixelvalve and the setup of the shared
HVS, the pixelvalve part being irrelevant to the TXP since it accesses the
HVS directly.

Now that we have a clear separation between the three parts, we can
represent the TXP as a CRTC of its own, leveraging the common CRTC and HVS
code, but leaving aside the pixelvalve setup.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Eric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20f387f881b57f3474fa42d94cfd8bc1b7b80595.1591882579.git-series.maxime@cerno.tech
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_txp.c

index 6211e8a..1a9c7a8 100644 (file)
@@ -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]) {
index d9a8ab8..a7c3af0 100644 (file)
@@ -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"
 #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)