drm/exynos: added mode_fixup feature and code clean.
authorInki Dae <inki.dae@samsung.com>
Fri, 16 Mar 2012 09:47:04 +0000 (18:47 +0900)
committerDave Airlie <airlied@redhat.com>
Tue, 20 Mar 2012 09:40:21 +0000 (09:40 +0000)
this patch adds mode_fixup feature for hdmi module that
specific driver changes current mode to driver desired mode
properly.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/exynos/exynos_drm_connector.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_encoder.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.h
drivers/gpu/drm/exynos/exynos_hdmi.c

index 618bd4d..ebdd71d 100644 (file)
@@ -225,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
        .best_encoder   = exynos_drm_best_encoder,
 };
 
+static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
+                               unsigned int max_width, unsigned int max_height)
+{
+       struct exynos_drm_connector *exynos_connector =
+                                       to_exynos_connector(connector);
+       struct exynos_drm_manager *manager = exynos_connector->manager;
+       struct exynos_drm_manager_ops *ops = manager->ops;
+       unsigned int width, height;
+
+       width = max_width;
+       height = max_height;
+
+       /*
+        * if specific driver want to find desired_mode using maxmum
+        * resolution then get max width and height from that driver.
+        */
+       if (ops && ops->get_max_resol)
+               ops->get_max_resol(manager->dev, &width, &height);
+
+       return drm_helper_probe_single_connector_modes(connector, width,
+                                                       height);
+}
+
 /* get detection status of display device. */
 static enum drm_connector_status
 exynos_drm_connector_detect(struct drm_connector *connector, bool force)
@@ -262,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
 
 static struct drm_connector_funcs exynos_connector_funcs = {
        .dpms           = drm_helper_connector_dpms,
-       .fill_modes     = drm_helper_probe_single_connector_modes,
+       .fill_modes     = exynos_drm_connector_fill_modes,
        .detect         = exynos_drm_connector_detect,
        .destroy        = exynos_drm_connector_destroy,
 };
index de81883..2d9a0e6 100644 (file)
@@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 {
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       mode = adjusted_mode;
+       /*
+        * copy the mode data adjusted by mode_fixup() into crtc->mode
+        * so that hardware can be seet to proper mode.
+        */
+       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
 
        return exynos_drm_crtc_update(crtc);
 }
index 13540de..4115a9f 100644 (file)
@@ -155,8 +155,10 @@ struct exynos_drm_display_ops {
  *
  * @dpms: control device power.
  * @apply: set timing, vblank and overlay data to registers.
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
  * @mode_set: convert drm_display_mode to hw specific display mode and
  *           would be called by encoder->mode_set().
+ * @get_max_resol: get maximum resolution to specific hardware.
  * @commit: set current hw specific display mode to hw.
  * @enable_vblank: specific driver callback for enabling vblank interrupt.
  * @disable_vblank: specific driver callback for disabling vblank interrupt.
@@ -164,7 +166,13 @@ struct exynos_drm_display_ops {
 struct exynos_drm_manager_ops {
        void (*dpms)(struct device *subdrv_dev, int mode);
        void (*apply)(struct device *subdrv_dev);
+       void (*mode_fixup)(struct device *subdrv_dev,
+                               struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode);
        void (*mode_set)(struct device *subdrv_dev, void *mode);
+       void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
+                               unsigned int *height);
        void (*commit)(struct device *subdrv_dev);
        int (*enable_vblank)(struct device *subdrv_dev);
        void (*disable_vblank)(struct device *subdrv_dev);
index ef4754f..45ca732 100644 (file)
@@ -111,9 +111,19 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode)
 {
+       struct drm_device *dev = encoder->dev;
+       struct drm_connector *connector;
+       struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+       struct exynos_drm_manager_ops *manager_ops = manager->ops;
+
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       /* drm framework doesn't check NULL. */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder)
+                       if (manager_ops && manager_ops->mode_fixup)
+                               manager_ops->mode_fixup(manager->dev, connector,
+                                                       mode, adjusted_mode);
+       }
 
        return true;
 }
@@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       mode = adjusted_mode;
-
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder) {
                        if (manager_ops && manager_ops->mode_set)
-                               manager_ops->mode_set(manager->dev, mode);
+                               manager_ops->mode_set(manager->dev,
+                                                       adjusted_mode);
 
                        if (overlay_ops && overlay_ops->mode_set)
                                overlay_ops->mode_set(manager->dev, overlay);
index ed8a319..ed86bdd 100644 (file)
@@ -155,6 +155,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
                return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
 }
 
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
+                               struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup)
+               hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector,
+                                               mode, adjusted_mode);
+}
+
 static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -165,6 +179,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
                hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
 }
 
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
+                               unsigned int *width, unsigned int *height)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol)
+               hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width,
+                                                       height);
+}
+
 static void drm_hdmi_commit(struct device *subdrv_dev)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -200,7 +226,9 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
        .dpms = drm_hdmi_dpms,
        .enable_vblank = drm_hdmi_enable_vblank,
        .disable_vblank = drm_hdmi_disable_vblank,
+       .mode_fixup = drm_hdmi_mode_fixup,
        .mode_set = drm_hdmi_mode_set,
+       .get_max_resol = drm_hdmi_get_max_resol,
        .commit = drm_hdmi_commit,
 };
 
index 3c29f79..44497cf 100644 (file)
@@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops {
 };
 
 struct exynos_hdmi_manager_ops {
+       void (*mode_fixup)(void *ctx, struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode);
        void (*mode_set)(void *ctx, void *mode);
+       void (*get_max_resol)(void *ctx, unsigned int *width,
+                               unsigned int *height);
        void (*commit)(void *ctx);
        void (*disable)(void *ctx);
 };
index 1cfe86e..6fe1e89 100644 (file)
@@ -41,6 +41,8 @@
 #include "exynos_hdmi.h"
 
 #define HDMI_OVERLAY_NUMBER    3
+#define MAX_WIDTH              1920
+#define MAX_HEIGHT             1080
 #define get_hdmi_context(dev)  platform_get_drvdata(to_platform_device(dev))
 
 /* HDMI Version 1.3 */
@@ -1126,7 +1128,7 @@ static int hdmi_v13_conf_index(struct drm_display_mode *mode)
                                 true : false))
                        return i;
 
-       return -1;
+       return -EINVAL;
 }
 
 static int hdmi_v14_conf_index(struct drm_display_mode *mode)
@@ -1142,7 +1144,7 @@ static int hdmi_v14_conf_index(struct drm_display_mode *mode)
                                 true : false))
                        return i;
 
-       return -1;
+       return -EINVAL;
 }
 
 static int hdmi_conf_index(struct hdmi_context *hdata,
@@ -1150,8 +1152,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata,
 {
        if (hdata->is_v13)
                return hdmi_v13_conf_index(mode);
-       else
-               return hdmi_v14_conf_index(mode);
+
+       return hdmi_v14_conf_index(mode);
 }
 
 static bool hdmi_is_connected(void *ctx)
@@ -1193,6 +1195,11 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
 {
        int i;
 
+       DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+                       check_timing->xres, check_timing->yres,
+                       check_timing->refresh, (check_timing->vmode &
+                       FB_VMODE_INTERLACED) ? true : false);
+
        for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
                if (hdmi_v13_confs[i].width == check_timing->xres &&
                        hdmi_v13_confs[i].height == check_timing->yres &&
@@ -1200,7 +1207,9 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
                        hdmi_v13_confs[i].interlace ==
                        ((check_timing->vmode & FB_VMODE_INTERLACED) ?
                         true : false))
-                       return 0;
+                               return 0;
+
+       /* TODO */
 
        return -EINVAL;
 }
@@ -1209,14 +1218,21 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
+       DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+                       check_timing->xres, check_timing->yres,
+                       check_timing->refresh, (check_timing->vmode &
+                       FB_VMODE_INTERLACED) ? true : false);
+
+       for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++)
                if (hdmi_confs[i].width == check_timing->xres &&
                        hdmi_confs[i].height == check_timing->yres &&
                        hdmi_confs[i].vrefresh == check_timing->refresh &&
                        hdmi_confs[i].interlace ==
                        ((check_timing->vmode & FB_VMODE_INTERLACED) ?
                         true : false))
-                       return 0;
+                               return 0;
+
+       /* TODO */
 
        return -EINVAL;
 }
@@ -1692,6 +1708,46 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
        hdmi_regs_dump(hdata, "start");
 }
 
+static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       struct drm_display_mode *m;
+       struct hdmi_context *hdata = (struct hdmi_context *)ctx;
+       int index;
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+       if (hdata->is_v13)
+               index = hdmi_v13_conf_index(adjusted_mode);
+       else
+               index = hdmi_v14_conf_index(adjusted_mode);
+
+       /* just return if user desired mode exists. */
+       if (index >= 0)
+               return;
+
+       /*
+        * otherwise, find the most suitable mode among modes and change it
+        * to adjusted_mode.
+        */
+       list_for_each_entry(m, &connector->modes, head) {
+               if (hdata->is_v13)
+                       index = hdmi_v13_conf_index(m);
+               else
+                       index = hdmi_v14_conf_index(m);
+
+               if (index >= 0) {
+                       DRM_INFO("desired mode doesn't exist so\n");
+                       DRM_INFO("use the most suitable mode among modes.\n");
+                       memcpy(adjusted_mode, m, sizeof(*m));
+                       break;
+               }
+       }
+}
+
 static void hdmi_mode_set(void *ctx, void *mode)
 {
        struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -1706,6 +1762,15 @@ static void hdmi_mode_set(void *ctx, void *mode)
                DRM_DEBUG_KMS("not supported mode\n");
 }
 
+static void hdmi_get_max_resol(void *ctx, unsigned int *width,
+                                       unsigned int *height)
+{
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+       *width = MAX_WIDTH;
+       *height = MAX_HEIGHT;
+}
+
 static void hdmi_commit(void *ctx)
 {
        struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -1730,7 +1795,9 @@ static void hdmi_disable(void *ctx)
 }
 
 static struct exynos_hdmi_manager_ops manager_ops = {
+       .mode_fixup     = hdmi_mode_fixup,
        .mode_set       = hdmi_mode_set,
+       .get_max_resol  = hdmi_get_max_resol,
        .commit         = hdmi_commit,
        .disable        = hdmi_disable,
 };