#include "sun4i_backend.h"
 #include "sun4i_drv.h"
+#include "sun4i_frontend.h"
 #include "sun4i_layer.h"
 #include "sunxi_engine.h"
 
        return 0;
 }
 
+int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
+                                       int layer, uint32_t fmt)
+{
+       u32 val;
+       int ret;
+
+       ret = sun4i_backend_drm_format_to_layer(NULL, fmt, &val);
+       if (ret) {
+               DRM_DEBUG_DRIVER("Invalid format\n");
+               return ret;
+       }
+
+       regmap_update_bits(backend->engine.regs,
+                          SUN4I_BACKEND_ATTCTL_REG0(layer),
+                          SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN,
+                          SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN);
+
+       regmap_update_bits(backend->engine.regs,
+                          SUN4I_BACKEND_ATTCTL_REG1(layer),
+                          SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
+
+       return 0;
+}
+
 int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
                                      int layer, struct drm_plane *plane)
 {
        return 0;
 }
 
+static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine)
+{
+       struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
+       struct sun4i_frontend *frontend = backend->frontend;
+
+       if (!frontend)
+               return;
+
+       /*
+        * In a teardown scenario with the frontend involved, we have
+        * to keep the frontend enabled until the next vblank, and
+        * only then disable it.
+        *
+        * This is due to the fact that the backend will not take into
+        * account the new configuration (with the plane that used to
+        * be fed by the frontend now disabled) until we write to the
+        * commit bit and the hardware fetches the new configuration
+        * during the next vblank.
+        *
+        * So we keep the frontend around in order to prevent any
+        * visual artifacts.
+        */
+       spin_lock(&backend->frontend_lock);
+       if (backend->frontend_teardown) {
+               sun4i_frontend_exit(frontend);
+               backend->frontend_teardown = false;
+       }
+       spin_unlock(&backend->frontend_lock);
+};
+
 static int sun4i_backend_init_sat(struct device *dev) {
        struct sun4i_backend *backend = dev_get_drvdata(dev);
        int ret;
        return ret;
 }
 
+/* TODO: This needs to take multiple pipelines into account */
+static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv,
+                                                         struct device_node *node)
+{
+       struct device_node *port, *ep, *remote;
+       struct sun4i_frontend *frontend;
+
+       port = of_graph_get_port_by_id(node, 0);
+       if (!port)
+               return ERR_PTR(-EINVAL);
+
+       for_each_available_child_of_node(port, ep) {
+               remote = of_graph_get_remote_port_parent(ep);
+               if (!remote)
+                       continue;
+
+               /* does this node match any registered engines? */
+               list_for_each_entry(frontend, &drv->frontend_list, list) {
+                       if (remote == frontend->node) {
+                               of_node_put(remote);
+                               of_node_put(port);
+                               return frontend;
+                       }
+               }
+       }
+
+       return ERR_PTR(-EINVAL);
+}
+
 static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
        .commit                         = sun4i_backend_commit,
        .layers_init                    = sun4i_layers_init,
        .apply_color_correction         = sun4i_backend_apply_color_correction,
        .disable_color_correction       = sun4i_backend_disable_color_correction,
+       .vblank_quirk                   = sun4i_backend_vblank_quirk,
 };
 
 static struct regmap_config sun4i_backend_regmap_config = {
        if (!backend)
                return -ENOMEM;
        dev_set_drvdata(dev, backend);
+       spin_lock_init(&backend->frontend_lock);
 
        backend->engine.node = dev->of_node;
        backend->engine.ops = &sun4i_backend_engine_ops;
        if (backend->engine.id < 0)
                return backend->engine.id;
 
+       backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node);
+       if (IS_ERR(backend->frontend))
+               dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n");
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(regs))
 
 #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x)               ((x) << 15)
 #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK      GENMASK(11, 10)
 #define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x)                        ((x) << 10)
+#define SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN            BIT(1)
 
 #define SUN4I_BACKEND_ATTCTL_REG1(l)           (0x8a0 + (0x4 * (l)))
 #define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT          GENMASK(15, 14)
 
 struct sun4i_backend {
        struct sunxi_engine     engine;
+       struct sun4i_frontend   *frontend;
 
        struct reset_control    *reset;
 
 
        struct clk              *sat_clk;
        struct reset_control    *sat_reset;
+
+       /* Protects against races in the frontend teardown */
+       spinlock_t              frontend_lock;
+       bool                    frontend_teardown;
 };
 
 static inline struct sun4i_backend *
                                       int layer, struct drm_plane *plane);
 int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
                                      int layer, struct drm_plane *plane);
+int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
+                                       int layer, uint32_t in_fmt);
 
 #endif /* _SUN4I_BACKEND_H_ */
 
 #include <drm/drmP.h>
 
 #include "sun4i_backend.h"
+#include "sun4i_frontend.h"
 #include "sun4i_layer.h"
 #include "sunxi_engine.h"
 
 static struct drm_plane_state *
 sun4i_backend_layer_duplicate_state(struct drm_plane *plane)
 {
+       struct sun4i_layer_state *orig = state_to_sun4i_layer_state(plane->state);
        struct sun4i_layer_state *copy;
 
        copy = kzalloc(sizeof(*copy), GFP_KERNEL);
                return NULL;
 
        __drm_atomic_helper_plane_duplicate_state(plane, ©->state);
+       copy->uses_frontend = orig->uses_frontend;
 
        return ©->state;
 }
 static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
                                               struct drm_plane_state *old_state)
 {
+       struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(old_state);
        struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
        struct sun4i_backend *backend = layer->backend;
 
        sun4i_backend_layer_enable(backend, layer->id, false);
+
+       if (layer_state->uses_frontend) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&backend->frontend_lock, flags);
+               backend->frontend_teardown = true;
+               spin_unlock_irqrestore(&backend->frontend_lock, flags);
+       }
 }
 
 static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
                                              struct drm_plane_state *old_state)
 {
+       struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(plane->state);
        struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
        struct sun4i_backend *backend = layer->backend;
+       struct sun4i_frontend *frontend = backend->frontend;
+
+       if (layer_state->uses_frontend) {
+               sun4i_frontend_init(frontend);
+               sun4i_frontend_update_coord(frontend, plane);
+               sun4i_frontend_update_buffer(frontend, plane);
+               sun4i_frontend_update_formats(frontend, plane,
+                                             DRM_FORMAT_ARGB8888);
+               sun4i_backend_update_layer_frontend(backend, layer->id,
+                                                   DRM_FORMAT_ARGB8888);
+               sun4i_backend_update_layer_coord(backend, layer->id, plane);
+               sun4i_frontend_enable(frontend);
+       } else {
+               sun4i_backend_update_layer_coord(backend, layer->id, plane);
+               sun4i_backend_update_layer_formats(backend, layer->id, plane);
+               sun4i_backend_update_layer_buffer(backend, layer->id, plane);
+       }
 
-       sun4i_backend_update_layer_coord(backend, layer->id, plane);
-       sun4i_backend_update_layer_formats(backend, layer->id, plane);
-       sun4i_backend_update_layer_buffer(backend, layer->id, plane);
        sun4i_backend_layer_enable(backend, layer->id, true);
 }