drm/mali-dp: Add writeback support for DP500.
authorLiviu Dudau <Liviu.Dudau@arm.com>
Tue, 10 Apr 2018 16:25:57 +0000 (17:25 +0100)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Thu, 5 Jul 2018 14:19:02 +0000 (15:19 +0100)
Mali DP500 behaves differently from the rest of the Mali DP IP,
in that it does not have a one-shot mode and keeps writing the
content of the current frame to the provided memory area until
stopped. As a way of emulating the one-shot behaviour, we are
going to use the CVAL interrupt that is being raised at the
start of each frame, during prefetch phase, to act as End-of-Write
signal, but with a twist: we are going to disable the memory
write engine right after we're notified that it has been enabled,
using the knowledge that the bit controlling the enabling will
only be acted upon on the next vblank/prefetch.

CVAL interrupt will fire durint the next prefetch phase every time
the global CVAL bit gets set, so we need a state byte to track
the memory write enabling. We also need to pay attention during the
disabling of the memory write engine as that requires the CVAL bit
to be set in the control register, but we don't want to do that
during an atomic commit, as it will write into the hardware a partial
state.

Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/arm/malidp_drv.h
drivers/gpu/drm/arm/malidp_hw.c
drivers/gpu/drm/arm/malidp_hw.h
drivers/gpu/drm/arm/malidp_regs.h

index 0a788d7..ab2d27e 100644 (file)
@@ -170,14 +170,15 @@ static int malidp_set_and_wait_config_valid(struct drm_device *drm)
        struct malidp_hw_device *hwdev = malidp->dev;
        int ret;
 
-       atomic_set(&malidp->config_valid, 0);
        hwdev->hw->set_config_valid(hwdev);
        /* don't wait for config_valid flag if we are in config mode */
-       if (hwdev->hw->in_config_mode(hwdev))
+       if (hwdev->hw->in_config_mode(hwdev)) {
+               atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE);
                return 0;
+       }
 
        ret = wait_event_interruptible_timeout(malidp->wq,
-                       atomic_read(&malidp->config_valid) == 1,
+                       atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE,
                        msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT));
 
        return (ret > 0) ? 0 : -ETIMEDOUT;
@@ -216,12 +217,19 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state)
 static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
 {
        struct drm_device *drm = state->dev;
+       struct malidp_drm *malidp = drm->dev_private;
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
        int i;
 
        pm_runtime_get_sync(drm->dev);
 
+       /*
+        * set config_valid to a special value to let IRQ handlers
+        * know that we are updating registers
+        */
+       atomic_set(&malidp->config_valid, MALIDP_CONFIG_START);
+
        drm_atomic_helper_commit_modeset_disables(drm, state);
 
        for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
@@ -588,7 +596,7 @@ static int malidp_bind(struct device *dev)
                out_depth = (out_depth << 8) | (output_width[i] & 0xf);
        malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base);
 
-       atomic_set(&malidp->config_valid, 0);
+       atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT);
        init_waitqueue_head(&malidp->wq);
 
        ret = malidp_init(drm);
index c70989b..e8c41cf 100644 (file)
 #include <drm/drmP.h>
 #include "malidp_hw.h"
 
+#define MALIDP_CONFIG_VALID_INIT       0
+#define MALIDP_CONFIG_VALID_DONE       1
+#define MALIDP_CONFIG_START            0xd0
+
 struct malidp_drm {
        struct malidp_hw_device *dev;
        struct drm_crtc crtc;
index c98b3e0..0f6290b 100644 (file)
 #include "malidp_drv.h"
 #include "malidp_hw.h"
 
+enum {
+       MW_NOT_ENABLED = 0,     /* SE writeback not enabled */
+       MW_ONESHOT,             /* SE in one-shot mode for writeback */
+       MW_START,               /* SE started writeback */
+       MW_STOP,                /* SE finished writeback */
+};
+
 static const struct malidp_format_id malidp500_de_formats[] = {
        /*    fourcc,   layers supporting the format,     internal id  */
        { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2,  0 },
@@ -368,6 +375,51 @@ static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
        return ret;
 }
 
+static int malidp500_enable_memwrite(struct malidp_hw_device *hwdev,
+                                    dma_addr_t *addrs, s32 *pitches,
+                                    int num_planes, u16 w, u16 h, u32 fmt_id)
+{
+       u32 base = MALIDP500_SE_MEMWRITE_BASE;
+       u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+
+       /* enable the scaling engine block */
+       malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
+
+       hwdev->mw_state = MW_START;
+
+       malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
+       switch (num_planes) {
+       case 2:
+               malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW);
+               malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH);
+               malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE);
+               /* fall through */
+       case 1:
+               malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW);
+               malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH);
+               malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE);
+               break;
+       default:
+               WARN(1, "Invalid number of planes");
+       }
+
+       malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h),
+                       MALIDP500_SE_MEMWRITE_OUT_SIZE);
+       malidp_hw_setbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
+
+       return 0;
+}
+
+static void malidp500_disable_memwrite(struct malidp_hw_device *hwdev)
+{
+       u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+
+       if (hwdev->mw_state == MW_START)
+               hwdev->mw_state = MW_STOP;
+       malidp_hw_clearbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
+       malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC);
+}
+
 static int malidp550_query_hw(struct malidp_hw_device *hwdev)
 {
        u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -598,6 +650,8 @@ static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev,
        /* enable the scaling engine block */
        malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
 
+       hwdev->mw_state = MW_ONESHOT;
+
        malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
        switch (num_planes) {
        case 2:
@@ -678,8 +732,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
                        },
                        .se_irq_map = {
                                .irq_mask = MALIDP500_SE_IRQ_CONF_MODE |
+                                           MALIDP500_SE_IRQ_CONF_VALID |
                                            MALIDP500_SE_IRQ_GLOBAL,
-                               .vsync_irq = 0,
+                               .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID,
                        },
                        .dc_irq_map = {
                                .irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
@@ -698,6 +753,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
                .rotmem_required = malidp500_rotmem_required,
                .se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
                .se_calc_mclk = malidp500_se_calc_mclk,
+               .enable_memwrite = malidp500_enable_memwrite,
+               .disable_memwrite = malidp500_disable_memwrite,
                .features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
        },
        [MALIDP_550] = {
@@ -842,7 +899,7 @@ static irqreturn_t malidp_de_irq(int irq, void *arg)
                        malidp->event = NULL;
                        spin_unlock(&drm->event_lock);
                }
-               atomic_set(&malidp->config_valid, 1);
+               atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE);
                ret = IRQ_WAKE_THREAD;
        }
 
@@ -936,7 +993,25 @@ static irqreturn_t malidp_se_irq(int irq, void *arg)
        mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ);
        status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
        status &= mask;
-       /* ToDo: status decoding and firing up of VSYNC and page flip events */
+
+       if (status & se->vsync_irq) {
+               switch (hwdev->mw_state) {
+               case MW_STOP:
+                       /* disable writeback after stop */
+                       hwdev->mw_state = MW_NOT_ENABLED;
+                       break;
+               case MW_START:
+                       /* writeback started, need to emulate one-shot mode */
+                       hw->disable_memwrite(hwdev);
+                       /*
+                        * only set config_valid HW bit if there is no
+                        * other update in progress
+                        */
+                       if (atomic_read(&malidp->config_valid) != MALIDP_CONFIG_START)
+                               hw->set_config_valid(hwdev);
+                       break;
+               }
+       }
 
        malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status);
 
@@ -966,6 +1041,7 @@ int malidp_se_irq_init(struct drm_device *drm, int irq)
                return ret;
        }
 
+       hwdev->mw_state = MW_NOT_ENABLED;
        malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK,
                             hwdev->hw->map.se_irq_map.irq_mask);
 
index a242e97..c479738 100644 (file)
@@ -178,7 +178,7 @@ struct malidp_hw {
        long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
                             struct malidp_se_config *se_config,
                             struct videomode *vm);
-       /**
+       /*
         * Enable writing to memory the content of the next frame
         * @param hwdev - malidp_hw_device structure containing the HW description
         * @param addrs - array of addresses for each plane
@@ -232,6 +232,9 @@ struct malidp_hw_device {
        /* track the device PM state */
        bool pm_suspended;
 
+       /* track the SE memory writeback state */
+       u8 mw_state;
+
        /* size of memory used for rotating layers, up to two banks available */
        u32 rotation_memory[2];
 };
index e2b2c49..93b198f 100644 (file)
 #define MALIDP500_DE_LG2_PTR_BASE      0x0031c
 #define MALIDP500_SE_BASE              0x00c00
 #define MALIDP500_SE_CONTROL           0x00c0c
-#define MALIDP500_SE_PTR_BASE          0x00e0c
+#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
+#define MALIDP500_SE_MEMWRITE_BASE     0x00e00
 #define MALIDP500_DC_IRQ_BASE          0x00f00
 #define MALIDP500_CONFIG_VALID         0x00f00
 #define MALIDP500_CONFIG_ID            0x00fd4