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:
parent
3ecd70b18c
commit
1de425b0bd
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue