These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / sti / sti_gdp.c
index 087906f..c85dc7d 100644 (file)
@@ -9,9 +9,12 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
 #include "sti_compositor.h"
 #include "sti_gdp.h"
-#include "sti_layer.h"
+#include "sti_plane.h"
 #include "sti_vtg.h"
 
 #define ALPHASWITCH     BIT(6)
@@ -26,7 +29,7 @@
 #define GDP_XBGR8888    (GDP_RGB888_32 | BIGNOTLITTLE | ALPHASWITCH)
 #define GDP_ARGB8565    0x04
 #define GDP_ARGB8888    0x05
-#define GDP_ABGR8888   (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH)
+#define GDP_ABGR8888    (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH)
 #define GDP_ARGB1555    0x06
 #define GDP_ARGB4444    0x07
 #define GDP_CLUT8       0x0B
@@ -53,8 +56,8 @@
 #define GAM_GDP_PPT_IGNORE      (BIT(1) | BIT(0))
 #define GAM_GDP_SIZE_MAX        0x7FF
 
-#define GDP_NODE_NB_BANK       2
-#define GDP_NODE_PER_FIELD     2
+#define GDP_NODE_NB_BANK        2
+#define GDP_NODE_PER_FIELD      2
 
 struct sti_gdp_node {
        u32 gam_gdp_ctl;
@@ -85,16 +88,20 @@ struct sti_gdp_node_list {
 /**
  * STI GDP structure
  *
- * @layer:             layer structure
+ * @sti_plane:          sti_plane structure
+ * @dev:                driver device
+ * @regs:               gdp registers
  * @clk_pix:            pixel clock for the current gdp
  * @clk_main_parent:    gdp parent clock if main path used
  * @clk_aux_parent:     gdp parent clock if aux path used
  * @vtg_field_nb:       callback for VTG FIELD (top or bottom) notification
  * @is_curr_top:        true if the current node processed is the top field
- * @node_list:         array of node list
+ * @node_list:          array of node list
  */
 struct sti_gdp {
-       struct sti_layer layer;
+       struct sti_plane plane;
+       struct device *dev;
+       void __iomem *regs;
        struct clk *clk_pix;
        struct clk *clk_main_parent;
        struct clk *clk_aux_parent;
@@ -103,7 +110,7 @@ struct sti_gdp {
        struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK];
 };
 
-#define to_sti_gdp(x) container_of(x, struct sti_gdp, layer)
+#define to_sti_gdp(x) container_of(x, struct sti_gdp, plane)
 
 static const uint32_t gdp_supported_formats[] = {
        DRM_FORMAT_XRGB8888,
@@ -120,16 +127,6 @@ static const uint32_t gdp_supported_formats[] = {
        DRM_FORMAT_C8,
 };
 
-static const uint32_t *sti_gdp_get_formats(struct sti_layer *layer)
-{
-       return gdp_supported_formats;
-}
-
-static unsigned int sti_gdp_get_nb_formats(struct sti_layer *layer)
-{
-       return ARRAY_SIZE(gdp_supported_formats);
-}
-
 static int sti_gdp_fourcc2format(int fourcc)
 {
        switch (fourcc) {
@@ -175,20 +172,19 @@ static int sti_gdp_get_alpharange(int format)
 
 /**
  * sti_gdp_get_free_nodes
- * @layer: gdp layer
+ * @gdp: gdp pointer
  *
  * Look for a GDP node list that is not currently read by the HW.
  *
  * RETURNS:
  * Pointer to the free GDP node list
  */
-static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
+static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_gdp *gdp)
 {
        int hw_nvn;
-       struct sti_gdp *gdp = to_sti_gdp(layer);
        unsigned int i;
 
-       hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET);
+       hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
        if (!hw_nvn)
                goto end;
 
@@ -199,7 +195,7 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
 
        /* in hazardious cases restart with the first node */
        DRM_ERROR("inconsistent NVN for %s: 0x%08X\n",
-                       sti_layer_to_str(layer), hw_nvn);
+                       sti_plane_to_str(&gdp->plane), hw_nvn);
 
 end:
        return &gdp->node_list[0];
@@ -207,7 +203,7 @@ end:
 
 /**
  * sti_gdp_get_current_nodes
- * @layer: GDP layer
+ * @gdp: gdp pointer
  *
  * Look for GDP nodes that are currently read by the HW.
  *
@@ -215,13 +211,12 @@ end:
  * Pointer to the current GDP node list
  */
 static
-struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
+struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_gdp *gdp)
 {
        int hw_nvn;
-       struct sti_gdp *gdp = to_sti_gdp(layer);
        unsigned int i;
 
-       hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET);
+       hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
        if (!hw_nvn)
                goto end;
 
@@ -232,205 +227,25 @@ struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
 
 end:
        DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n",
-                               hw_nvn, sti_layer_to_str(layer));
+                               hw_nvn, sti_plane_to_str(&gdp->plane));
 
        return NULL;
 }
 
 /**
- * sti_gdp_prepare_layer
- * @lay: gdp layer
- * @first_prepare: true if it is the first time this function is called
- *
- * Update the free GDP node list according to the layer properties.
- *
- * RETURNS:
- * 0 on success.
- */
-static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare)
-{
-       struct sti_gdp_node_list *list;
-       struct sti_gdp_node *top_field, *btm_field;
-       struct drm_display_mode *mode = layer->mode;
-       struct device *dev = layer->dev;
-       struct sti_gdp *gdp = to_sti_gdp(layer);
-       struct sti_compositor *compo = dev_get_drvdata(dev);
-       int format;
-       unsigned int depth, bpp;
-       int rate = mode->clock * 1000;
-       int res;
-       u32 ydo, xdo, yds, xds;
-
-       list = sti_gdp_get_free_nodes(layer);
-       top_field = list->top_field;
-       btm_field = list->btm_field;
-
-       dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
-                       sti_layer_to_str(layer), top_field, btm_field);
-
-       /* Build the top field from layer params */
-       top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
-       top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
-       format = sti_gdp_fourcc2format(layer->format);
-       if (format == -1) {
-               DRM_ERROR("Format not supported by GDP %.4s\n",
-                         (char *)&layer->format);
-               return 1;
-       }
-       top_field->gam_gdp_ctl |= format;
-       top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
-       top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
-
-       /* pixel memory location */
-       drm_fb_get_bpp_depth(layer->format, &depth, &bpp);
-       top_field->gam_gdp_pml = (u32) layer->paddr + layer->offsets[0];
-       top_field->gam_gdp_pml += layer->src_x * (bpp >> 3);
-       top_field->gam_gdp_pml += layer->src_y * layer->pitches[0];
-
-       /* input parameters */
-       top_field->gam_gdp_pmp = layer->pitches[0];
-       top_field->gam_gdp_size =
-           clamp_val(layer->src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
-           clamp_val(layer->src_w, 0, GAM_GDP_SIZE_MAX);
-
-       /* output parameters */
-       ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
-       yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
-       xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
-       xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1);
-       top_field->gam_gdp_vpo = (ydo << 16) | xdo;
-       top_field->gam_gdp_vps = (yds << 16) | xds;
-
-       /* Same content and chained together */
-       memcpy(btm_field, top_field, sizeof(*btm_field));
-       top_field->gam_gdp_nvn = list->btm_field_paddr;
-       btm_field->gam_gdp_nvn = list->top_field_paddr;
-
-       /* Interlaced mode */
-       if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE)
-               btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
-                   layer->pitches[0];
-
-       if (first_prepare) {
-               /* Register gdp callback */
-               if (sti_vtg_register_client(layer->mixer_id == STI_MIXER_MAIN ?
-                               compo->vtg_main : compo->vtg_aux,
-                               &gdp->vtg_field_nb, layer->mixer_id)) {
-                       DRM_ERROR("Cannot register VTG notifier\n");
-                       return 1;
-               }
-
-               /* Set and enable gdp clock */
-               if (gdp->clk_pix) {
-                       struct clk *clkp;
-                       /* According to the mixer used, the gdp pixel clock
-                        * should have a different parent clock. */
-                       if (layer->mixer_id == STI_MIXER_MAIN)
-                               clkp = gdp->clk_main_parent;
-                       else
-                               clkp = gdp->clk_aux_parent;
-
-                       if (clkp)
-                               clk_set_parent(gdp->clk_pix, clkp);
-
-                       res = clk_set_rate(gdp->clk_pix, rate);
-                       if (res < 0) {
-                               DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
-                                               rate);
-                               return 1;
-                       }
-
-                       if (clk_prepare_enable(gdp->clk_pix)) {
-                               DRM_ERROR("Failed to prepare/enable gdp\n");
-                               return 1;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/**
- * sti_gdp_commit_layer
- * @lay: gdp layer
- *
- * Update the NVN field of the 'right' field of the current GDP node (being
- * used by the HW) with the address of the updated ('free') top field GDP node.
- * - In interlaced mode the 'right' field is the bottom field as we update
- *   frames starting from their top field
- * - In progressive mode, we update both bottom and top fields which are
- *   equal nodes.
- * At the next VSYNC, the updated node list will be used by the HW.
- *
- * RETURNS:
- * 0 on success.
- */
-static int sti_gdp_commit_layer(struct sti_layer *layer)
-{
-       struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(layer);
-       struct sti_gdp_node *updated_top_node = updated_list->top_field;
-       struct sti_gdp_node *updated_btm_node = updated_list->btm_field;
-       struct sti_gdp *gdp = to_sti_gdp(layer);
-       u32 dma_updated_top = updated_list->top_field_paddr;
-       u32 dma_updated_btm = updated_list->btm_field_paddr;
-       struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
-
-       dev_dbg(layer->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__,
-                       sti_layer_to_str(layer),
-                       updated_top_node, updated_btm_node);
-       dev_dbg(layer->dev, "Current NVN:0x%X\n",
-               readl(layer->regs + GAM_GDP_NVN_OFFSET));
-       dev_dbg(layer->dev, "Posted buff: %lx current buff: %x\n",
-               (unsigned long)layer->paddr,
-               readl(layer->regs + GAM_GDP_PML_OFFSET));
-
-       if (curr_list == NULL) {
-               /* First update or invalid node should directly write in the
-                * hw register */
-               DRM_DEBUG_DRIVER("%s first update (or invalid node)",
-                               sti_layer_to_str(layer));
-
-               writel(gdp->is_curr_top == true ?
-                               dma_updated_btm : dma_updated_top,
-                               layer->regs + GAM_GDP_NVN_OFFSET);
-               return 0;
-       }
-
-       if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) {
-               if (gdp->is_curr_top == true) {
-                       /* Do not update in the middle of the frame, but
-                        * postpone the update after the bottom field has
-                        * been displayed */
-                       curr_list->btm_field->gam_gdp_nvn = dma_updated_top;
-               } else {
-                       /* Direct update to avoid one frame delay */
-                       writel(dma_updated_top,
-                               layer->regs + GAM_GDP_NVN_OFFSET);
-               }
-       } else {
-               /* Direct update for progressive to avoid one frame delay */
-               writel(dma_updated_top, layer->regs + GAM_GDP_NVN_OFFSET);
-       }
-
-       return 0;
-}
-
-/**
- * sti_gdp_disable_layer
- * @lay: gdp layer
+ * sti_gdp_disable
+ * @gdp: gdp pointer
  *
  * Disable a GDP.
- *
- * RETURNS:
- * 0 on success.
  */
-static int sti_gdp_disable_layer(struct sti_layer *layer)
+static void sti_gdp_disable(struct sti_gdp *gdp)
 {
+       struct drm_plane *drm_plane = &gdp->plane.drm_plane;
+       struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
+       struct sti_compositor *compo = dev_get_drvdata(gdp->dev);
        unsigned int i;
-       struct sti_gdp *gdp = to_sti_gdp(layer);
-       struct sti_compositor *compo = dev_get_drvdata(layer->dev);
 
-       DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
+       DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane));
 
        /* Set the nodes as 'to be ignored on mixer' */
        for (i = 0; i < GDP_NODE_NB_BANK; i++) {
@@ -438,14 +253,14 @@ static int sti_gdp_disable_layer(struct sti_layer *layer)
                gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
        }
 
-       if (sti_vtg_unregister_client(layer->mixer_id == STI_MIXER_MAIN ?
+       if (sti_vtg_unregister_client(mixer->id == STI_MIXER_MAIN ?
                        compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb))
                DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
 
        if (gdp->clk_pix)
                clk_disable_unprepare(gdp->clk_pix);
 
-       return 0;
+       gdp->plane.status = STI_PLANE_DISABLED;
 }
 
 /**
@@ -464,6 +279,14 @@ int sti_gdp_field_cb(struct notifier_block *nb,
 {
        struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb);
 
+       if (gdp->plane.status == STI_PLANE_FLUSHING) {
+               /* disable need to be synchronize on vsync event */
+               DRM_DEBUG_DRIVER("Vsync event received => disable %s\n",
+                                sti_plane_to_str(&gdp->plane));
+
+               sti_gdp_disable(gdp);
+       }
+
        switch (event) {
        case VTG_TOP_FIELD_EVENT:
                gdp->is_curr_top = true;
@@ -479,10 +302,9 @@ int sti_gdp_field_cb(struct notifier_block *nb,
        return 0;
 }
 
-static void sti_gdp_init(struct sti_layer *layer)
+static void sti_gdp_init(struct sti_gdp *gdp)
 {
-       struct sti_gdp *gdp = to_sti_gdp(layer);
-       struct device_node *np = layer->dev->of_node;
+       struct device_node *np = gdp->dev->of_node;
        dma_addr_t dma_addr;
        void *base;
        unsigned int i, size;
@@ -490,8 +312,8 @@ static void sti_gdp_init(struct sti_layer *layer)
        /* Allocate all the nodes within a single memory page */
        size = sizeof(struct sti_gdp_node) *
            GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK;
-       base = dma_alloc_writecombine(layer->dev,
-                       size, &dma_addr, GFP_KERNEL | GFP_DMA);
+       base = dma_alloc_writecombine(gdp->dev,
+                                     size, &dma_addr, GFP_KERNEL | GFP_DMA);
 
        if (!base) {
                DRM_ERROR("Failed to allocate memory for GDP node\n");
@@ -526,7 +348,7 @@ static void sti_gdp_init(struct sti_layer *layer)
                /* GDP of STiH407 chip have its own pixel clock */
                char *clk_name;
 
-               switch (layer->desc) {
+               switch (gdp->plane.desc) {
                case STI_GDP_0:
                        clk_name = "pix_gdp1";
                        break;
@@ -544,32 +366,249 @@ static void sti_gdp_init(struct sti_layer *layer)
                        return;
                }
 
-               gdp->clk_pix = devm_clk_get(layer->dev, clk_name);
+               gdp->clk_pix = devm_clk_get(gdp->dev, clk_name);
                if (IS_ERR(gdp->clk_pix))
                        DRM_ERROR("Cannot get %s clock\n", clk_name);
 
-               gdp->clk_main_parent = devm_clk_get(layer->dev, "main_parent");
+               gdp->clk_main_parent = devm_clk_get(gdp->dev, "main_parent");
                if (IS_ERR(gdp->clk_main_parent))
                        DRM_ERROR("Cannot get main_parent clock\n");
 
-               gdp->clk_aux_parent = devm_clk_get(layer->dev, "aux_parent");
+               gdp->clk_aux_parent = devm_clk_get(gdp->dev, "aux_parent");
                if (IS_ERR(gdp->clk_aux_parent))
                        DRM_ERROR("Cannot get aux_parent clock\n");
        }
 }
 
-static const struct sti_layer_funcs gdp_ops = {
-       .get_formats = sti_gdp_get_formats,
-       .get_nb_formats = sti_gdp_get_nb_formats,
-       .init = sti_gdp_init,
-       .prepare = sti_gdp_prepare_layer,
-       .commit = sti_gdp_commit_layer,
-       .disable = sti_gdp_disable_layer,
+static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
+                                 struct drm_plane_state *oldstate)
+{
+       struct drm_plane_state *state = drm_plane->state;
+       struct sti_plane *plane = to_sti_plane(drm_plane);
+       struct sti_gdp *gdp = to_sti_gdp(plane);
+       struct drm_crtc *crtc = state->crtc;
+       struct sti_compositor *compo = dev_get_drvdata(gdp->dev);
+       struct drm_framebuffer *fb =  state->fb;
+       bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
+       struct sti_mixer *mixer;
+       struct drm_display_mode *mode;
+       int dst_x, dst_y, dst_w, dst_h;
+       int src_x, src_y, src_w, src_h;
+       struct drm_gem_cma_object *cma_obj;
+       struct sti_gdp_node_list *list;
+       struct sti_gdp_node_list *curr_list;
+       struct sti_gdp_node *top_field, *btm_field;
+       u32 dma_updated_top;
+       u32 dma_updated_btm;
+       int format;
+       unsigned int depth, bpp;
+       u32 ydo, xdo, yds, xds;
+       int res;
+
+       /* Manage the case where crtc is null (disabled) */
+       if (!crtc)
+               return;
+
+       mixer = to_sti_mixer(crtc);
+       mode = &crtc->mode;
+       dst_x = state->crtc_x;
+       dst_y = state->crtc_y;
+       dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
+       dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       /* src_x are in 16.16 format */
+       src_x = state->src_x >> 16;
+       src_y = state->src_y >> 16;
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+
+       DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
+                     crtc->base.id, sti_mixer_to_str(mixer),
+                     drm_plane->base.id, sti_plane_to_str(plane));
+       DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
+                     sti_plane_to_str(plane),
+                     dst_w, dst_h, dst_x, dst_y,
+                     src_w, src_h, src_x, src_y);
+
+       list = sti_gdp_get_free_nodes(gdp);
+       top_field = list->top_field;
+       btm_field = list->btm_field;
+
+       dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
+               sti_plane_to_str(plane), top_field, btm_field);
+
+       /* build the top field */
+       top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
+       top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
+       format = sti_gdp_fourcc2format(fb->pixel_format);
+       if (format == -1) {
+               DRM_ERROR("Format not supported by GDP %.4s\n",
+                         (char *)&fb->pixel_format);
+               return;
+       }
+       top_field->gam_gdp_ctl |= format;
+       top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
+       top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
+
+       cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+       if (!cma_obj) {
+               DRM_ERROR("Can't get CMA GEM object for fb\n");
+               return;
+       }
+
+       DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
+                        (char *)&fb->pixel_format,
+                        (unsigned long)cma_obj->paddr);
+
+       /* pixel memory location */
+       drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
+       top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0];
+       top_field->gam_gdp_pml += src_x * (bpp >> 3);
+       top_field->gam_gdp_pml += src_y * fb->pitches[0];
+
+       /* input parameters */
+       top_field->gam_gdp_pmp = fb->pitches[0];
+       top_field->gam_gdp_size = clamp_val(src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
+                                 clamp_val(src_w, 0, GAM_GDP_SIZE_MAX);
+
+       /* output parameters */
+       ydo = sti_vtg_get_line_number(*mode, dst_y);
+       yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1);
+       xdo = sti_vtg_get_pixel_number(*mode, dst_x);
+       xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1);
+       top_field->gam_gdp_vpo = (ydo << 16) | xdo;
+       top_field->gam_gdp_vps = (yds << 16) | xds;
+
+       /* Same content and chained together */
+       memcpy(btm_field, top_field, sizeof(*btm_field));
+       top_field->gam_gdp_nvn = list->btm_field_paddr;
+       btm_field->gam_gdp_nvn = list->top_field_paddr;
+
+       /* Interlaced mode */
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
+                                        fb->pitches[0];
+
+       if (first_prepare) {
+               /* Register gdp callback */
+               if (sti_vtg_register_client(mixer->id == STI_MIXER_MAIN ?
+                               compo->vtg_main : compo->vtg_aux,
+                               &gdp->vtg_field_nb, crtc)) {
+                       DRM_ERROR("Cannot register VTG notifier\n");
+                       return;
+               }
+
+               /* Set and enable gdp clock */
+               if (gdp->clk_pix) {
+                       struct clk *clkp;
+                       int rate = mode->clock * 1000;
+
+                       /* According to the mixer used, the gdp pixel clock
+                        * should have a different parent clock. */
+                       if (mixer->id == STI_MIXER_MAIN)
+                               clkp = gdp->clk_main_parent;
+                       else
+                               clkp = gdp->clk_aux_parent;
+
+                       if (clkp)
+                               clk_set_parent(gdp->clk_pix, clkp);
+
+                       res = clk_set_rate(gdp->clk_pix, rate);
+                       if (res < 0) {
+                               DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
+                                         rate);
+                               return;
+                       }
+
+                       if (clk_prepare_enable(gdp->clk_pix)) {
+                               DRM_ERROR("Failed to prepare/enable gdp\n");
+                               return;
+                       }
+               }
+       }
+
+       /* Update the NVN field of the 'right' field of the current GDP node
+        * (being used by the HW) with the address of the updated ('free') top
+        * field GDP node.
+        * - In interlaced mode the 'right' field is the bottom field as we
+        *   update frames starting from their top field
+        * - In progressive mode, we update both bottom and top fields which
+        *   are equal nodes.
+        * At the next VSYNC, the updated node list will be used by the HW.
+        */
+       curr_list = sti_gdp_get_current_nodes(gdp);
+       dma_updated_top = list->top_field_paddr;
+       dma_updated_btm = list->btm_field_paddr;
+
+       dev_dbg(gdp->dev, "Current NVN:0x%X\n",
+               readl(gdp->regs + GAM_GDP_NVN_OFFSET));
+       dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n",
+               (unsigned long)cma_obj->paddr,
+               readl(gdp->regs + GAM_GDP_PML_OFFSET));
+
+       if (!curr_list) {
+               /* First update or invalid node should directly write in the
+                * hw register */
+               DRM_DEBUG_DRIVER("%s first update (or invalid node)",
+                                sti_plane_to_str(plane));
+
+               writel(gdp->is_curr_top ?
+                               dma_updated_btm : dma_updated_top,
+                               gdp->regs + GAM_GDP_NVN_OFFSET);
+               goto end;
+       }
+
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               if (gdp->is_curr_top) {
+                       /* Do not update in the middle of the frame, but
+                        * postpone the update after the bottom field has
+                        * been displayed */
+                       curr_list->btm_field->gam_gdp_nvn = dma_updated_top;
+               } else {
+                       /* Direct update to avoid one frame delay */
+                       writel(dma_updated_top,
+                              gdp->regs + GAM_GDP_NVN_OFFSET);
+               }
+       } else {
+               /* Direct update for progressive to avoid one frame delay */
+               writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET);
+       }
+
+end:
+       plane->status = STI_PLANE_UPDATED;
+}
+
+static void sti_gdp_atomic_disable(struct drm_plane *drm_plane,
+                                  struct drm_plane_state *oldstate)
+{
+       struct sti_plane *plane = to_sti_plane(drm_plane);
+       struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc);
+
+       if (!drm_plane->crtc) {
+               DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
+                                drm_plane->base.id);
+               return;
+       }
+
+       DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
+                        drm_plane->crtc->base.id, sti_mixer_to_str(mixer),
+                        drm_plane->base.id, sti_plane_to_str(plane));
+
+       plane->status = STI_PLANE_DISABLING;
+}
+
+static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = {
+       .atomic_update = sti_gdp_atomic_update,
+       .atomic_disable = sti_gdp_atomic_disable,
 };
 
-struct sti_layer *sti_gdp_create(struct device *dev, int id)
+struct drm_plane *sti_gdp_create(struct drm_device *drm_dev,
+                                struct device *dev, int desc,
+                                void __iomem *baseaddr,
+                                unsigned int possible_crtcs,
+                                enum drm_plane_type type)
 {
        struct sti_gdp *gdp;
+       int res;
 
        gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL);
        if (!gdp) {
@@ -577,8 +616,33 @@ struct sti_layer *sti_gdp_create(struct device *dev, int id)
                return NULL;
        }
 
-       gdp->layer.ops = &gdp_ops;
+       gdp->dev = dev;
+       gdp->regs = baseaddr;
+       gdp->plane.desc = desc;
+       gdp->plane.status = STI_PLANE_DISABLED;
+
        gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb;
 
-       return (struct sti_layer *)gdp;
+       sti_gdp_init(gdp);
+
+       res = drm_universal_plane_init(drm_dev, &gdp->plane.drm_plane,
+                                      possible_crtcs,
+                                      &sti_plane_helpers_funcs,
+                                      gdp_supported_formats,
+                                      ARRAY_SIZE(gdp_supported_formats),
+                                      type);
+       if (res) {
+               DRM_ERROR("Failed to initialize universal plane\n");
+               goto err;
+       }
+
+       drm_plane_helper_add(&gdp->plane.drm_plane, &sti_gdp_helpers_funcs);
+
+       sti_plane_init_property(&gdp->plane, type);
+
+       return &gdp->plane.drm_plane;
+
+err:
+       devm_kfree(dev, gdp);
+       return NULL;
 }