drm/exynos: added mode_fixup feature and code clean.

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>
This commit is contained in:
Inki Dae 2012-03-16 18:47:04 +09:00 committed by Dave Airlie
parent 3ecd70b18c
commit 1de425b0bd
7 changed files with 157 additions and 13 deletions

View 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,
};

View 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);
}

View 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);

View 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);

View 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,
};

View 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);
};

View 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,
};