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....@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_connector.c |   25 +++++++-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c      |    6 ++-
 drivers/gpu/drm/exynos/exynos_drm_drv.h       |    8 +++
 drivers/gpu/drm/exynos/exynos_drm_encoder.c   |   17 ++++-
 drivers/gpu/drm/exynos/exynos_drm_hdmi.c      |   28 +++++++++
 drivers/gpu/drm/exynos/exynos_drm_hdmi.h      |    5 ++
 drivers/gpu/drm/exynos/exynos_hdmi.c          |   81 ++++++++++++++++++++++--
 7 files changed, 157 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c 
b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index 99d5527..303af60 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -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,
 };
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c 
b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index de81883..2d9a0e6 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -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);
 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h 
b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 13540de..4115a9f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -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);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c 
b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index ef4754f..45ca732 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -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);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c 
b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
index ed8a319e..ed86bdd 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -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,
 };
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h 
b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
index 3c29f79..44497cf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
@@ -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);
 };
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c 
b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 1cfe86e..6fe1e89 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -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,
 };
-- 
1.7.4.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to