]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blobdiff - drivers/gpu/drm/vc4/vc4_crtc.c
Merge drm-upstream/drm-next into drm-misc-next
[mirror_ubuntu-eoan-kernel.git] / drivers / gpu / drm / vc4 / vc4_crtc.c
index bf4667481935281526def8786619cd0a537c76f7..dcadf793ee80697cae097733eb387470c6423594 100644 (file)
 #include "vc4_drv.h"
 #include "vc4_regs.h"
 
-struct vc4_crtc {
-       struct drm_crtc base;
-       const struct vc4_crtc_data *data;
-       void __iomem *regs;
-
-       /* Timestamp at start of vblank irq - unaffected by lock delays. */
-       ktime_t t_vblank;
-
-       /* Which HVS channel we're using for our CRTC. */
-       int channel;
-
-       u8 lut_r[256];
-       u8 lut_g[256];
-       u8 lut_b[256];
-       /* Size in pixels of the COB memory allocated to this CRTC. */
-       u32 cob_size;
-
-       struct drm_pending_vblank_event *event;
-};
-
 struct vc4_crtc_state {
        struct drm_crtc_state base;
        /* Dlist area for this CRTC configuration. */
        struct drm_mm_node mm;
 };
 
-static inline struct vc4_crtc *
-to_vc4_crtc(struct drm_crtc *crtc)
-{
-       return (struct vc4_crtc *)crtc;
-}
-
 static inline struct vc4_crtc_state *
 to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
 {
        return (struct vc4_crtc_state *)crtc_state;
 }
 
-struct vc4_crtc_data {
-       /* Which channel of the HVS this pixelvalve sources from. */
-       int hvs_channel;
-
-       enum vc4_encoder_type encoder_types[4];
-};
-
 #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
 #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
 
@@ -298,23 +265,21 @@ vc4_crtc_lut_load(struct drm_crtc *crtc)
                HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
 }
 
-static int
-vc4_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
-                  uint32_t size,
-                  struct drm_modeset_acquire_ctx *ctx)
+static void
+vc4_crtc_update_gamma_lut(struct drm_crtc *crtc)
 {
        struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+       struct drm_color_lut *lut = crtc->state->gamma_lut->data;
+       u32 length = drm_color_lut_size(crtc->state->gamma_lut);
        u32 i;
 
-       for (i = 0; i < size; i++) {
-               vc4_crtc->lut_r[i] = r[i] >> 8;
-               vc4_crtc->lut_g[i] = g[i] >> 8;
-               vc4_crtc->lut_b[i] = b[i] >> 8;
+       for (i = 0; i < length; i++) {
+               vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8);
+               vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8);
+               vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8);
        }
 
        vc4_crtc_lut_load(crtc);
-
-       return 0;
 }
 
 static u32 vc4_get_fifo_full_level(u32 format)
@@ -699,6 +664,22 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
        if (crtc->state->active && old_state->active)
                vc4_crtc_update_dlist(crtc);
 
+       if (crtc->state->color_mgmt_changed) {
+               u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel));
+
+               if (crtc->state->gamma_lut) {
+                       vc4_crtc_update_gamma_lut(crtc);
+                       dispbkgndx |= SCALER_DISPBKGND_GAMMA;
+               } else {
+                       /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
+                        * in hardware, which is the same as a linear lut that
+                        * DRM expects us to use in absence of a user lut.
+                        */
+                       dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+               }
+               HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx);
+       }
+
        if (debug_dump_regs) {
                DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
                vc4_hvs_dump_state(dev);
@@ -760,6 +741,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
 struct vc4_async_flip_state {
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb;
+       struct drm_framebuffer *old_fb;
        struct drm_pending_vblank_event *event;
 
        struct vc4_seqno_cb cb;
@@ -789,6 +771,23 @@ vc4_async_page_flip_complete(struct vc4_seqno_cb *cb)
 
        drm_crtc_vblank_put(crtc);
        drm_framebuffer_put(flip_state->fb);
+
+       /* Decrement the BO usecnt in order to keep the inc/dec calls balanced
+        * when the planes are updated through the async update path.
+        * FIXME: we should move to generic async-page-flip when it's
+        * available, so that we can get rid of this hand-made cleanup_fb()
+        * logic.
+        */
+       if (flip_state->old_fb) {
+               struct drm_gem_cma_object *cma_bo;
+               struct vc4_bo *bo;
+
+               cma_bo = drm_fb_cma_get_gem_obj(flip_state->old_fb, 0);
+               bo = to_vc4_bo(&cma_bo->base);
+               vc4_bo_dec_usecnt(bo);
+               drm_framebuffer_put(flip_state->old_fb);
+       }
+
        kfree(flip_state);
 
        up(&vc4->async_modeset);
@@ -813,9 +812,22 @@ static int vc4_async_page_flip(struct drm_crtc *crtc,
        struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0);
        struct vc4_bo *bo = to_vc4_bo(&cma_bo->base);
 
+       /* Increment the BO usecnt here, so that we never end up with an
+        * unbalanced number of vc4_bo_{dec,inc}_usecnt() calls when the
+        * plane is later updated through the non-async path.
+        * FIXME: we should move to generic async-page-flip when it's
+        * available, so that we can get rid of this hand-made prepare_fb()
+        * logic.
+        */
+       ret = vc4_bo_inc_usecnt(bo);
+       if (ret)
+               return ret;
+
        flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL);
-       if (!flip_state)
+       if (!flip_state) {
+               vc4_bo_dec_usecnt(bo);
                return -ENOMEM;
+       }
 
        drm_framebuffer_get(fb);
        flip_state->fb = fb;
@@ -826,10 +838,23 @@ static int vc4_async_page_flip(struct drm_crtc *crtc,
        ret = down_interruptible(&vc4->async_modeset);
        if (ret) {
                drm_framebuffer_put(fb);
+               vc4_bo_dec_usecnt(bo);
                kfree(flip_state);
                return ret;
        }
 
+       /* Save the current FB before it's replaced by the new one in
+        * drm_atomic_set_fb_for_plane(). We'll need the old FB in
+        * vc4_async_page_flip_complete() to decrement the BO usecnt and keep
+        * it consistent.
+        * FIXME: we should move to generic async-page-flip when it's
+        * available, so that we can get rid of this hand-made cleanup_fb()
+        * logic.
+        */
+       flip_state->old_fb = plane->state->fb;
+       if (flip_state->old_fb)
+               drm_framebuffer_get(flip_state->old_fb);
+
        WARN_ON(drm_crtc_vblank_get(crtc) != 0);
 
        /* Immediately update the plane's legacy fb pointer, so that later
@@ -837,7 +862,6 @@ static int vc4_async_page_flip(struct drm_crtc *crtc,
         * is released.
         */
        drm_atomic_set_fb_for_plane(plane->state, fb);
-       plane->fb = fb;
 
        vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno,
                           vc4_async_page_flip_complete);
@@ -909,7 +933,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
        .reset = vc4_crtc_reset,
        .atomic_duplicate_state = vc4_crtc_duplicate_state,
        .atomic_destroy_state = vc4_crtc_destroy_state,
-       .gamma_set = vc4_crtc_gamma_set,
+       .gamma_set = drm_atomic_helper_legacy_gamma_set,
        .enable_vblank = vc4_enable_vblank,
        .disable_vblank = vc4_disable_vblank,
 };
@@ -1032,9 +1056,14 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
        drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
                                  &vc4_crtc_funcs, NULL);
        drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
-       primary_plane->crtc = crtc;
        vc4_crtc->channel = vc4_crtc->data->hvs_channel;
        drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+       drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+
+       /* We support CTM, but only for one CRTC at a time. It's therefore
+        * implemented as private driver state in vc4_kms, not here.
+        */
+       drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
 
        /* Set up some arbitrary number of planes.  We're not limited
         * by a set number of physical registers, just the space in
@@ -1062,7 +1091,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
        cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
        if (!IS_ERR(cursor_plane)) {
                cursor_plane->possible_crtcs = 1 << drm_crtc_index(crtc);
-               cursor_plane->crtc = crtc;
                crtc->cursor = cursor_plane;
        }