#include "atmel_hlcdc_dc.h"
 
+/**
+ * Atmel HLCDC CRTC state structure
+ *
+ * @base: base CRTC state
+ * @output_mode: RGBXXX output mode
+ */
+struct atmel_hlcdc_crtc_state {
+       struct drm_crtc_state base;
+       unsigned int output_mode;
+};
+
+static inline struct atmel_hlcdc_crtc_state *
+drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state)
+{
+       return container_of(state, struct atmel_hlcdc_crtc_state, base);
+}
+
 /**
  * Atmel HLCDC CRTC structure
  *
        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
        struct regmap *regmap = crtc->dc->hlcdc->regmap;
        struct drm_display_mode *adj = &c->state->adjusted_mode;
+       struct atmel_hlcdc_crtc_state *state;
        unsigned long mode_rate;
        struct videomode vm;
        unsigned long prate;
        if (adj->flags & DRM_MODE_FLAG_NHSYNC)
                cfg |= ATMEL_HLCDC_HSPOL;
 
+       state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
+       cfg |= state->output_mode << 8;
+
        regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
                           ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
                           ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
                           ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
                           ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
-                          ATMEL_HLCDC_GUARDTIME_MASK,
+                          ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK,
                           cfg);
 }
 
        }
 }
 
+#define ATMEL_HLCDC_RGB444_OUTPUT      BIT(0)
+#define ATMEL_HLCDC_RGB565_OUTPUT      BIT(1)
+#define ATMEL_HLCDC_RGB666_OUTPUT      BIT(2)
+#define ATMEL_HLCDC_RGB888_OUTPUT      BIT(3)
+#define ATMEL_HLCDC_OUTPUT_MODE_MASK   GENMASK(3, 0)
+
+static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
+{
+       unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK;
+       struct atmel_hlcdc_crtc_state *hstate;
+       struct drm_connector_state *cstate;
+       struct drm_connector *connector;
+       struct atmel_hlcdc_crtc *crtc;
+       int i;
+
+       crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
+
+       for_each_connector_in_state(state->state, connector, cstate, i) {
+               struct drm_display_info *info = &connector->display_info;
+               unsigned int supported_fmts = 0;
+               int j;
+
+               if (!cstate->crtc)
+                       continue;
+
+               for (j = 0; j < info->num_bus_formats; j++) {
+                       switch (info->bus_formats[j]) {
+                       case MEDIA_BUS_FMT_RGB444_1X12:
+                               supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
+                               break;
+                       case MEDIA_BUS_FMT_RGB565_1X16:
+                               supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
+                               break;
+                       case MEDIA_BUS_FMT_RGB666_1X18:
+                               supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
+                               break;
+                       case MEDIA_BUS_FMT_RGB888_1X24:
+                               supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               if (crtc->dc->desc->conflicting_output_formats)
+                       output_fmts &= supported_fmts;
+               else
+                       output_fmts |= supported_fmts;
+       }
+
+       if (!output_fmts)
+               return -EINVAL;
+
+       hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
+       hstate->output_mode = fls(output_fmts) - 1;
+
+       return 0;
+}
+
 static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
                                         struct drm_crtc_state *s)
 {
        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+       int ret;
 
        if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK)
                return -EINVAL;
 
+       ret = atmel_hlcdc_crtc_select_output_mode(s);
+       if (ret)
+               return ret;
+
        return atmel_hlcdc_plane_prepare_disc_area(s);
 }
 
        atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
 }
 
+void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
+{
+       struct atmel_hlcdc_crtc_state *state;
+
+       if (crtc->state && crtc->state->mode_blob)
+               drm_property_unreference_blob(crtc->state->mode_blob);
+
+       if (crtc->state) {
+               state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
+               kfree(state);
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state) {
+               crtc->state = &state->base;
+               crtc->state->crtc = crtc;
+       }
+}
+
+static struct drm_crtc_state *
+atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+       struct atmel_hlcdc_crtc_state *state, *cur;
+
+       if (WARN_ON(!crtc->state))
+               return NULL;
+
+       state = kmalloc(sizeof(*state), GFP_KERNEL);
+       if (state)
+               __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+       cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
+       state->output_mode = cur->output_mode;
+
+       return &state->base;
+}
+
+static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
+                                          struct drm_crtc_state *s)
+{
+       struct atmel_hlcdc_crtc_state *state;
+
+       state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
+       __drm_atomic_helper_crtc_destroy_state(crtc, s);
+       kfree(state);
+}
+
 static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
        .page_flip = drm_atomic_helper_page_flip,
        .set_config = drm_atomic_helper_set_config,
        .destroy = atmel_hlcdc_crtc_destroy,
-       .reset = drm_atomic_helper_crtc_reset,
-       .atomic_duplicate_state =  drm_atomic_helper_crtc_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+       .reset = atmel_hlcdc_crtc_reset,
+       .atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
+       .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
 };
 
 int atmel_hlcdc_crtc_create(struct drm_device *dev)
 
 
 #include "atmel_hlcdc_dc.h"
 
-/**
- * Atmel HLCDC RGB output mode
- */
-enum atmel_hlcdc_connector_rgb_mode {
-       ATMEL_HLCDC_CONNECTOR_RGB444,
-       ATMEL_HLCDC_CONNECTOR_RGB565,
-       ATMEL_HLCDC_CONNECTOR_RGB666,
-       ATMEL_HLCDC_CONNECTOR_RGB888,
-};
-
 /**
  * Atmel HLCDC RGB connector structure
  *
        struct atmel_hlcdc_rgb_output *rgb =
                        drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
        struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
-
        drm_panel_enable(panel->panel);
 }
 
        drm_panel_disable(panel->panel);
 }
 
-static void
-atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
-                                struct drm_display_mode *mode,
-                                struct drm_display_mode *adjusted)
-{
-       struct atmel_hlcdc_rgb_output *rgb =
-                       drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
-       struct drm_display_info *info = &rgb->connector.display_info;
-       unsigned int cfg;
-
-       cfg = 0;
-
-       if (info->num_bus_formats) {
-               switch (info->bus_formats[0]) {
-               case MEDIA_BUS_FMT_RGB565_1X16:
-                       cfg |= ATMEL_HLCDC_CONNECTOR_RGB565 << 8;
-                       break;
-               case MEDIA_BUS_FMT_RGB666_1X18:
-                       cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8;
-                       break;
-               case MEDIA_BUS_FMT_RGB888_1X24:
-                       cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8;
-                       break;
-               case MEDIA_BUS_FMT_RGB444_1X12:
-               default:
-                       break;
-               }
-       }
-
-       regmap_update_bits(rgb->dc->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
-                          ATMEL_HLCDC_MODE_MASK,
-                          cfg);
-}
-
 static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
-       .mode_set = atmel_hlcdc_rgb_encoder_mode_set,
        .disable = atmel_hlcdc_panel_encoder_disable,
        .enable = atmel_hlcdc_panel_encoder_enable,
 };