Merge tag 'ovl-update-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs
[linux-2.6-microblaze.git] / drivers / gpu / drm / mxsfb / lcdif_kms.c
index 262bc43..2541d2d 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_color_mgmt.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_dma_helper.h>
 #include "lcdif_drv.h"
 #include "lcdif_regs.h"
 
+struct lcdif_crtc_state {
+       struct drm_crtc_state   base;   /* always be the first member */
+       u32                     bus_format;
+       u32                     bus_flags;
+};
+
+static inline struct lcdif_crtc_state *
+to_lcdif_crtc_state(struct drm_crtc_state *s)
+{
+       return container_of(s, struct lcdif_crtc_state, base);
+}
+
 /* -----------------------------------------------------------------------------
  * CRTC
  */
@@ -385,48 +398,108 @@ static void lcdif_reset_block(struct lcdif_drm_private *lcdif)
        readl(lcdif->base + LCDC_V8_CTRL);
 }
 
-static void lcdif_crtc_mode_set_nofb(struct lcdif_drm_private *lcdif,
-                                    struct drm_plane_state *plane_state,
-                                    struct drm_bridge_state *bridge_state,
-                                    const u32 bus_format)
+static void lcdif_crtc_mode_set_nofb(struct drm_crtc_state *crtc_state,
+                                    struct drm_plane_state *plane_state)
 {
-       struct drm_device *drm = lcdif->crtc.dev;
-       struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
-       u32 bus_flags = 0;
-
-       if (lcdif->bridge && lcdif->bridge->timings)
-               bus_flags = lcdif->bridge->timings->input_bus_flags;
-       else if (bridge_state)
-               bus_flags = bridge_state->input_bus_cfg.flags;
+       struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(crtc_state);
+       struct drm_device *drm = crtc_state->crtc->dev;
+       struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm);
+       struct drm_display_mode *m = &crtc_state->adjusted_mode;
 
        DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
                             m->crtc_clock,
                             (int)(clk_get_rate(lcdif->clk) / 1000));
        DRM_DEV_DEBUG_DRIVER(drm->dev, "Bridge bus_flags: 0x%08X\n",
-                            bus_flags);
+                            lcdif_crtc_state->bus_flags);
        DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
 
        /* Mandatory eLCDIF reset as per the Reference Manual */
        lcdif_reset_block(lcdif);
 
-       lcdif_set_formats(lcdif, plane_state, bus_format);
+       lcdif_set_formats(lcdif, plane_state, lcdif_crtc_state->bus_format);
 
-       lcdif_set_mode(lcdif, bus_flags);
+       lcdif_set_mode(lcdif, lcdif_crtc_state->bus_flags);
 }
 
 static int lcdif_crtc_atomic_check(struct drm_crtc *crtc,
                                   struct drm_atomic_state *state)
 {
+       struct drm_device *drm = crtc->dev;
        struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
                                                                          crtc);
+       struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(crtc_state);
        bool has_primary = crtc_state->plane_mask &
                           drm_plane_mask(crtc->primary);
+       struct drm_connector_state *connector_state;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_bridge_state *bridge_state;
+       struct drm_bridge *bridge;
+       u32 bus_format, bus_flags;
+       bool format_set = false, flags_set = false;
+       int ret, i;
 
        /* The primary plane has to be enabled when the CRTC is active. */
        if (crtc_state->active && !has_primary)
                return -EINVAL;
 
-       return drm_atomic_add_affected_planes(state, crtc);
+       ret = drm_atomic_add_affected_planes(state, crtc);
+       if (ret)
+               return ret;
+
+       /* Try to find consistent bus format and flags across first bridges. */
+       for_each_new_connector_in_state(state, connector, connector_state, i) {
+               if (!connector_state->crtc)
+                       continue;
+
+               encoder = connector_state->best_encoder;
+
+               bridge = drm_bridge_chain_get_first_bridge(encoder);
+               if (!bridge)
+                       continue;
+
+               bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+               if (!bridge_state)
+                       bus_format = MEDIA_BUS_FMT_FIXED;
+               else
+                       bus_format = bridge_state->input_bus_cfg.format;
+
+               if (bus_format == MEDIA_BUS_FMT_FIXED) {
+                       dev_warn(drm->dev,
+                                "[ENCODER:%d:%s]'s bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"
+                                "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n",
+                                encoder->base.id, encoder->name);
+                       bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+               } else if (!bus_format) {
+                       /* If all else fails, default to RGB888_1X24 */
+                       bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+               }
+
+               if (!format_set) {
+                       lcdif_crtc_state->bus_format = bus_format;
+                       format_set = true;
+               } else if (lcdif_crtc_state->bus_format != bus_format) {
+                       DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus format\n");
+                       return -EINVAL;
+               }
+
+               if (bridge->timings)
+                       bus_flags = bridge->timings->input_bus_flags;
+               else if (bridge_state)
+                       bus_flags = bridge_state->input_bus_cfg.flags;
+               else
+                       bus_flags = 0;
+
+               if (!flags_set) {
+                       lcdif_crtc_state->bus_flags = bus_flags;
+                       flags_set = true;
+               } else if (lcdif_crtc_state->bus_flags != bus_flags) {
+                       DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus flags\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
 }
 
 static void lcdif_crtc_atomic_flush(struct drm_crtc *crtc,
@@ -458,41 +531,18 @@ static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
                                     struct drm_atomic_state *state)
 {
        struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
+       struct drm_crtc_state *new_cstate = drm_atomic_get_new_crtc_state(state, crtc);
        struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
                                                                            crtc->primary);
        struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode;
-       struct drm_bridge_state *bridge_state = NULL;
        struct drm_device *drm = lcdif->drm;
-       u32 bus_format = 0;
        dma_addr_t paddr;
 
-       /* If there is a bridge attached to the LCDIF, use its bus format */
-       if (lcdif->bridge) {
-               bridge_state =
-                       drm_atomic_get_new_bridge_state(state,
-                                                       lcdif->bridge);
-               if (!bridge_state)
-                       bus_format = MEDIA_BUS_FMT_FIXED;
-               else
-                       bus_format = bridge_state->input_bus_cfg.format;
-
-               if (bus_format == MEDIA_BUS_FMT_FIXED) {
-                       dev_warn_once(drm->dev,
-                                     "Bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"
-                                     "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n");
-                       bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-               }
-       }
-
-       /* If all else fails, default to RGB888_1X24 */
-       if (!bus_format)
-               bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-
        clk_set_rate(lcdif->clk, m->crtc_clock * 1000);
 
        pm_runtime_get_sync(drm->dev);
 
-       lcdif_crtc_mode_set_nofb(lcdif, new_pstate, bridge_state, bus_format);
+       lcdif_crtc_mode_set_nofb(new_cstate, new_pstate);
 
        /* Write cur_buf as well to avoid an initial corrupt frame */
        paddr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
@@ -529,6 +579,48 @@ static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
        pm_runtime_put_sync(drm->dev);
 }
 
+static void lcdif_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *state)
+{
+       __drm_atomic_helper_crtc_destroy_state(state);
+       kfree(to_lcdif_crtc_state(state));
+}
+
+static void lcdif_crtc_reset(struct drm_crtc *crtc)
+{
+       struct lcdif_crtc_state *state;
+
+       if (crtc->state)
+               lcdif_crtc_atomic_destroy_state(crtc, crtc->state);
+
+       crtc->state = NULL;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state)
+               __drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static struct drm_crtc_state *
+lcdif_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+       struct lcdif_crtc_state *old = to_lcdif_crtc_state(crtc->state);
+       struct lcdif_crtc_state *new;
+
+       if (WARN_ON(!crtc->state))
+               return NULL;
+
+       new = kzalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               return NULL;
+
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);
+
+       new->bus_format = old->bus_format;
+       new->bus_flags = old->bus_flags;
+
+       return &new->base;
+}
+
 static int lcdif_crtc_enable_vblank(struct drm_crtc *crtc)
 {
        struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev);
@@ -557,24 +649,16 @@ static const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = {
 };
 
 static const struct drm_crtc_funcs lcdif_crtc_funcs = {
-       .reset = drm_atomic_helper_crtc_reset,
+       .reset = lcdif_crtc_reset,
        .destroy = drm_crtc_cleanup,
        .set_config = drm_atomic_helper_set_config,
        .page_flip = drm_atomic_helper_page_flip,
-       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+       .atomic_duplicate_state = lcdif_crtc_atomic_duplicate_state,
+       .atomic_destroy_state = lcdif_crtc_atomic_destroy_state,
        .enable_vblank = lcdif_crtc_enable_vblank,
        .disable_vblank = lcdif_crtc_disable_vblank,
 };
 
-/* -----------------------------------------------------------------------------
- * Encoder
- */
-
-static const struct drm_encoder_funcs lcdif_encoder_funcs = {
-       .destroy = drm_encoder_cleanup,
-};
-
 /* -----------------------------------------------------------------------------
  * Planes
  */
@@ -667,7 +751,6 @@ int lcdif_kms_init(struct lcdif_drm_private *lcdif)
                                        BIT(DRM_COLOR_YCBCR_BT2020);
        const u32 supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
                                     BIT(DRM_COLOR_YCBCR_FULL_RANGE);
-       struct drm_encoder *encoder = &lcdif->encoder;
        struct drm_crtc *crtc = &lcdif->crtc;
        int ret;
 
@@ -691,13 +774,7 @@ int lcdif_kms_init(struct lcdif_drm_private *lcdif)
                return ret;
 
        drm_crtc_helper_add(crtc, &lcdif_crtc_helper_funcs);
-       ret = drm_crtc_init_with_planes(lcdif->drm, crtc,
-                                       &lcdif->planes.primary, NULL,
-                                       &lcdif_crtc_funcs, NULL);
-       if (ret)
-               return ret;
-
-       encoder->possible_crtcs = drm_crtc_mask(crtc);
-       return drm_encoder_init(lcdif->drm, encoder, &lcdif_encoder_funcs,
-                               DRM_MODE_ENCODER_NONE, NULL);
+       return drm_crtc_init_with_planes(lcdif->drm, crtc,
+                                        &lcdif->planes.primary, NULL,
+                                        &lcdif_crtc_funcs, NULL);
 }