#include "sun4i_lvds.h"
 #include "sun4i_rgb.h"
 #include "sun4i_tcon.h"
+#include "sun6i_mipi_dsi.h"
 #include "sunxi_engine.h"
 
 static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
        case DRM_MODE_ENCODER_LVDS:
                is_lvds = true;
                /* Fallthrough */
+       case DRM_MODE_ENCODER_DSI:
        case DRM_MODE_ENCODER_NONE:
                channel = 0;
                break;
                     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
 }
 
+static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
+                                    struct mipi_dsi_device *device,
+                                    const struct drm_display_mode *mode)
+{
+       u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format);
+       u8 lanes = device->lanes;
+       u32 block_space, start_delay;
+       u32 tcon_div;
+
+       tcon->dclk_min_div = 4;
+       tcon->dclk_max_div = 127;
+
+       sun4i_tcon0_mode_set_common(tcon, mode);
+
+       regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
+                          SUN4I_TCON0_CTL_IF_MASK,
+                          SUN4I_TCON0_CTL_IF_8080);
+
+       regmap_write(tcon->regs, SUN4I_TCON_ECC_FIFO_REG,
+                    SUN4I_TCON_ECC_FIFO_EN);
+
+       regmap_write(tcon->regs, SUN4I_TCON0_CPU_IF_REG,
+                    SUN4I_TCON0_CPU_IF_MODE_DSI |
+                    SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH |
+                    SUN4I_TCON0_CPU_IF_TRI_FIFO_EN |
+                    SUN4I_TCON0_CPU_IF_TRI_EN);
+
+       /*
+        * This looks suspicious, but it works...
+        *
+        * The datasheet says that this should be set higher than 20 *
+        * pixel cycle, but it's not clear what a pixel cycle is.
+        */
+       regmap_read(tcon->regs, SUN4I_TCON0_DCLK_REG, &tcon_div);
+       tcon_div &= GENMASK(6, 0);
+       block_space = mode->htotal * bpp / (tcon_div * lanes);
+       block_space -= mode->hdisplay + 40;
+
+       regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI0_REG,
+                    SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(block_space) |
+                    SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(mode->hdisplay));
+
+       regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI1_REG,
+                    SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(mode->vdisplay));
+
+       start_delay = (mode->crtc_vtotal - mode->crtc_vdisplay - 10 - 1);
+       start_delay = start_delay * mode->crtc_htotal * 149;
+       start_delay = start_delay / (mode->crtc_clock / 1000) / 8;
+       regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI2_REG,
+                    SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(10) |
+                    SUN4I_TCON0_CPU_TRI2_START_DELAY(start_delay));
+
+       /*
+        * The Allwinner BSP has a comment that the period should be
+        * the display clock * 15, but uses an hardcoded 3000...
+        */
+       regmap_write(tcon->regs, SUN4I_TCON_SAFE_PERIOD_REG,
+                    SUN4I_TCON_SAFE_PERIOD_NUM(3000) |
+                    SUN4I_TCON_SAFE_PERIOD_MODE(3));
+
+       /* Enable the output on the pins */
+       regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG,
+                    0xe0000000);
+}
+
 static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
                                      const struct drm_encoder *encoder,
                                      const struct drm_display_mode *mode)
                         const struct drm_encoder *encoder,
                         const struct drm_display_mode *mode)
 {
+       struct sun6i_dsi *dsi;
+
        switch (encoder->encoder_type) {
+       case DRM_MODE_ENCODER_DSI:
+               /*
+                * This is not really elegant, but it's the "cleaner"
+                * way I could think of...
+                */
+               dsi = encoder_to_sun6i_dsi(encoder);
+               sun4i_tcon0_mode_set_cpu(tcon, dsi->device, mode);
+               break;
        case DRM_MODE_ENCODER_LVDS:
                sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
                break;
 
 #define SUN4I_TCON_GINT0_TCON0_TRI_COUNTER_INT         BIT(10)
 
 #define SUN4I_TCON_GINT1_REG                   0x8
+
 #define SUN4I_TCON_FRM_CTL_REG                 0x10
+#define SUN4I_TCON_FRM_CTL_EN                          BIT(31)
+
+#define SUN4I_TCON_FRM_SEED_PR_REG             0x14
+#define SUN4I_TCON_FRM_SEED_PG_REG             0x18
+#define SUN4I_TCON_FRM_SEED_PB_REG             0x1c
+#define SUN4I_TCON_FRM_SEED_LR_REG             0x20
+#define SUN4I_TCON_FRM_SEED_LG_REG             0x24
+#define SUN4I_TCON_FRM_SEED_LB_REG             0x28
+#define SUN4I_TCON_FRM_TBL0_REG                        0x2c
+#define SUN4I_TCON_FRM_TBL1_REG                        0x30
+#define SUN4I_TCON_FRM_TBL2_REG                        0x34
+#define SUN4I_TCON_FRM_TBL3_REG                        0x38
 
 #define SUN4I_TCON0_CTL_REG                    0x40
 #define SUN4I_TCON0_CTL_TCON_ENABLE                    BIT(31)
+#define SUN4I_TCON0_CTL_IF_MASK                                GENMASK(25, 24)
+#define SUN4I_TCON0_CTL_IF_8080                                (1 << 24)
 #define SUN4I_TCON0_CTL_CLK_DELAY_MASK                 GENMASK(8, 4)
 #define SUN4I_TCON0_CTL_CLK_DELAY(delay)               ((delay << 4) & SUN4I_TCON0_CTL_CLK_DELAY_MASK)
 #define SUN4I_TCON0_CTL_SRC_SEL_MASK                   GENMASK(2, 0)
 #define SUN4I_TCON0_BASIC3_V_SYNC(height)              (((height) - 1) & 0x7ff)
 
 #define SUN4I_TCON0_HV_IF_REG                  0x58
+
 #define SUN4I_TCON0_CPU_IF_REG                 0x60
+#define SUN4I_TCON0_CPU_IF_MODE_MASK                   GENMASK(31, 28)
+#define SUN4I_TCON0_CPU_IF_MODE_DSI                    (1 << 28)
+#define SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH              BIT(16)
+#define SUN4I_TCON0_CPU_IF_TRI_FIFO_EN                 BIT(2)
+#define SUN4I_TCON0_CPU_IF_TRI_EN                      BIT(0)
+
 #define SUN4I_TCON0_CPU_WR_REG                 0x64
 #define SUN4I_TCON0_CPU_RD0_REG                        0x68
 #define SUN4I_TCON0_CPU_RDA_REG                        0x6c
 
 #define SUN4I_TCON1_IO_POL_REG                 0xf0
 #define SUN4I_TCON1_IO_TRI_REG                 0xf4
+
+#define SUN4I_TCON_ECC_FIFO_REG                        0xf8
+#define SUN4I_TCON_ECC_FIFO_EN                         BIT(3)
+
 #define SUN4I_TCON_CEU_CTL_REG                 0x100
 #define SUN4I_TCON_CEU_MUL_RR_REG              0x110
 #define SUN4I_TCON_CEU_MUL_RG_REG              0x114
 #define SUN4I_TCON_CEU_RANGE_R_REG             0x140
 #define SUN4I_TCON_CEU_RANGE_G_REG             0x144
 #define SUN4I_TCON_CEU_RANGE_B_REG             0x148
+
+#define SUN4I_TCON0_CPU_TRI0_REG               0x160
+#define SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(space)                ((((space) - 1) & 0xfff) << 16)
+#define SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(size)          (((size) - 1) & 0xfff)
+
+#define SUN4I_TCON0_CPU_TRI1_REG               0x164
+#define SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(num)            (((num) - 1) & 0xffff)
+
+#define SUN4I_TCON0_CPU_TRI2_REG               0x168
+#define SUN4I_TCON0_CPU_TRI2_START_DELAY(delay)                (((delay) & 0xffff) << 16)
+#define SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(set)      ((set) & 0xfff)
+
+#define SUN4I_TCON_SAFE_PERIOD_REG             0x1f0
+#define SUN4I_TCON_SAFE_PERIOD_NUM(num)                        (((num) & 0xfff) << 16)
+#define SUN4I_TCON_SAFE_PERIOD_MODE(mode)              ((mode) & 0x3)
+
 #define SUN4I_TCON_MUX_CTRL_REG                        0x200
 
 #define SUN4I_TCON0_LVDS_ANA0_REG              0x220