#include "meson_registers.h"
 #include "meson_venc.h"
 #include "meson_viu.h"
+#include "meson_rdma.h"
 #include "meson_vpp.h"
+#include "meson_osd_afbcd.h"
 
 #define MESON_G12A_VIU_OFFSET  0x5ec0
 
        struct meson_drm *priv;
        void (*enable_osd1)(struct meson_drm *priv);
        void (*enable_vd1)(struct meson_drm *priv);
+       void (*enable_osd1_afbc)(struct meson_drm *priv);
+       void (*disable_osd1_afbc)(struct meson_drm *priv);
        unsigned int viu_offset;
+       bool vsync_forced;
+       bool vsync_disabled;
 };
 #define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
 
        struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
        struct meson_drm *priv = meson_crtc->priv;
 
+       meson_crtc->vsync_disabled = false;
        meson_venc_enable_vsync(priv);
 
        return 0;
        struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
        struct meson_drm *priv = meson_crtc->priv;
 
-       meson_venc_disable_vsync(priv);
+       if (!meson_crtc->vsync_forced) {
+               meson_crtc->vsync_disabled = true;
+               meson_venc_disable_vsync(priv);
+       }
 }
 
 static const struct drm_crtc_funcs meson_crtc_funcs = {
                            priv->io_base + _REG(VPP_MISC));
 }
 
+static void meson_crtc_g12a_enable_osd1_afbc(struct meson_drm *priv)
+{
+       writel_relaxed(priv->viu.osd1_blk2_cfg4,
+                      priv->io_base + _REG(VIU_OSD1_BLK2_CFG_W4));
+
+       writel_bits_relaxed(OSD_MEM_LINEAR_ADDR, OSD_MEM_LINEAR_ADDR,
+                           priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
+
+       writel_relaxed(priv->viu.osd1_blk1_cfg4,
+                      priv->io_base + _REG(VIU_OSD1_BLK1_CFG_W4));
+
+       meson_viu_g12a_enable_osd1_afbc(priv);
+
+       writel_bits_relaxed(OSD_MEM_LINEAR_ADDR, OSD_MEM_LINEAR_ADDR,
+                           priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
+
+       writel_bits_relaxed(OSD_MALI_SRC_EN, OSD_MALI_SRC_EN,
+                           priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0));
+}
+
 static void meson_g12a_crtc_enable_osd1(struct meson_drm *priv)
 {
        writel_relaxed(priv->viu.osd_blend_din0_scope_h,
                                priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3));
                writel_relaxed(priv->viu.osd1_blk0_cfg[4],
                                priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4));
+
+               if (priv->viu.osd1_afbcd) {
+                       if (meson_crtc->enable_osd1_afbc)
+                               meson_crtc->enable_osd1_afbc(priv);
+               } else {
+                       if (meson_crtc->disable_osd1_afbc)
+                               meson_crtc->disable_osd1_afbc(priv);
+                       if (priv->afbcd.ops) {
+                               priv->afbcd.ops->reset(priv);
+                               priv->afbcd.ops->disable(priv);
+                       }
+                       meson_crtc->vsync_forced = false;
+               }
+
                writel_relaxed(priv->viu.osd_sc_ctrl0,
                                priv->io_base + _REG(VPP_OSD_SC_CTRL0));
                writel_relaxed(priv->viu.osd_sc_i_wh_m1,
                writel_relaxed(priv->viu.osd_sc_v_ctrl0,
                                priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
 
-               meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
-                               priv->viu.osd1_addr, priv->viu.osd1_stride,
-                               priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
-                               MESON_CANVAS_BLKMODE_LINEAR, 0);
+               if (!priv->viu.osd1_afbcd)
+                       meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
+                                           priv->viu.osd1_addr,
+                                           priv->viu.osd1_stride,
+                                           priv->viu.osd1_height,
+                                           MESON_CANVAS_WRAP_NONE,
+                                           MESON_CANVAS_BLKMODE_LINEAR, 0);
 
                /* Enable OSD1 */
                if (meson_crtc->enable_osd1)
                        meson_crtc->enable_osd1(priv);
 
+               if (priv->viu.osd1_afbcd) {
+                       priv->afbcd.ops->reset(priv);
+                       priv->afbcd.ops->setup(priv);
+                       priv->afbcd.ops->enable(priv);
+                       meson_crtc->vsync_forced = true;
+               }
+
                priv->viu.osd1_commit = false;
        }
 
                priv->viu.vd1_commit = false;
        }
 
+       if (meson_crtc->vsync_disabled)
+               return;
+
        drm_crtc_handle_vblank(priv->crtc);
 
        spin_lock_irqsave(&priv->drm->event_lock, flags);
                meson_crtc->enable_osd1 = meson_g12a_crtc_enable_osd1;
                meson_crtc->enable_vd1 = meson_g12a_crtc_enable_vd1;
                meson_crtc->viu_offset = MESON_G12A_VIU_OFFSET;
+               meson_crtc->enable_osd1_afbc =
+                                       meson_crtc_g12a_enable_osd1_afbc;
+               meson_crtc->disable_osd1_afbc =
+                                       meson_viu_g12a_disable_osd1_afbc;
                drm_crtc_helper_add(crtc, &meson_g12a_crtc_helper_funcs);
        } else {
                meson_crtc->enable_osd1 = meson_crtc_enable_osd1;
                meson_crtc->enable_vd1 = meson_crtc_enable_vd1;
+               if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM)) {
+                       meson_crtc->enable_osd1_afbc =
+                                       meson_viu_gxm_enable_osd1_afbc;
+                       meson_crtc->disable_osd1_afbc =
+                                       meson_viu_gxm_disable_osd1_afbc;
+               }
                drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs);
        }