mirror of https://gitee.com/openkylin/linux.git
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,
|
.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. */
|
/* get detection status of display device. */
|
||||||
static enum drm_connector_status
|
static enum drm_connector_status
|
||||||
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
|
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 = {
|
static struct drm_connector_funcs exynos_connector_funcs = {
|
||||||
.dpms = drm_helper_connector_dpms,
|
.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,
|
.detect = exynos_drm_connector_detect,
|
||||||
.destroy = exynos_drm_connector_destroy,
|
.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__);
|
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);
|
return exynos_drm_crtc_update(crtc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,8 +155,10 @@ struct exynos_drm_display_ops {
|
||||||
*
|
*
|
||||||
* @dpms: control device power.
|
* @dpms: control device power.
|
||||||
* @apply: set timing, vblank and overlay data to registers.
|
* @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
|
* @mode_set: convert drm_display_mode to hw specific display mode and
|
||||||
* would be called by encoder->mode_set().
|
* 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.
|
* @commit: set current hw specific display mode to hw.
|
||||||
* @enable_vblank: specific driver callback for enabling vblank interrupt.
|
* @enable_vblank: specific driver callback for enabling vblank interrupt.
|
||||||
* @disable_vblank: specific driver callback for disabling 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 {
|
struct exynos_drm_manager_ops {
|
||||||
void (*dpms)(struct device *subdrv_dev, int mode);
|
void (*dpms)(struct device *subdrv_dev, int mode);
|
||||||
void (*apply)(struct device *subdrv_dev);
|
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 (*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);
|
void (*commit)(struct device *subdrv_dev);
|
||||||
int (*enable_vblank)(struct device *subdrv_dev);
|
int (*enable_vblank)(struct device *subdrv_dev);
|
||||||
void (*disable_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 *mode,
|
||||||
struct drm_display_mode *adjusted_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_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
|
||||||
|
|
||||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||||
|
|
||||||
mode = adjusted_mode;
|
|
||||||
|
|
||||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||||
if (connector->encoder == encoder) {
|
if (connector->encoder == encoder) {
|
||||||
if (manager_ops && manager_ops->mode_set)
|
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)
|
if (overlay_ops && overlay_ops->mode_set)
|
||||||
overlay_ops->mode_set(manager->dev, overlay);
|
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);
|
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)
|
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
|
||||||
{
|
{
|
||||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
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);
|
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)
|
static void drm_hdmi_commit(struct device *subdrv_dev)
|
||||||
{
|
{
|
||||||
struct drm_hdmi_context *ctx = to_context(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,
|
.dpms = drm_hdmi_dpms,
|
||||||
.enable_vblank = drm_hdmi_enable_vblank,
|
.enable_vblank = drm_hdmi_enable_vblank,
|
||||||
.disable_vblank = drm_hdmi_disable_vblank,
|
.disable_vblank = drm_hdmi_disable_vblank,
|
||||||
|
.mode_fixup = drm_hdmi_mode_fixup,
|
||||||
.mode_set = drm_hdmi_mode_set,
|
.mode_set = drm_hdmi_mode_set,
|
||||||
|
.get_max_resol = drm_hdmi_get_max_resol,
|
||||||
.commit = drm_hdmi_commit,
|
.commit = drm_hdmi_commit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct exynos_hdmi_manager_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 (*mode_set)(void *ctx, void *mode);
|
||||||
|
void (*get_max_resol)(void *ctx, unsigned int *width,
|
||||||
|
unsigned int *height);
|
||||||
void (*commit)(void *ctx);
|
void (*commit)(void *ctx);
|
||||||
void (*disable)(void *ctx);
|
void (*disable)(void *ctx);
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#include "exynos_hdmi.h"
|
#include "exynos_hdmi.h"
|
||||||
|
|
||||||
#define HDMI_OVERLAY_NUMBER 3
|
#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))
|
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
|
||||||
|
|
||||||
/* HDMI Version 1.3 */
|
/* HDMI Version 1.3 */
|
||||||
|
@ -1126,7 +1128,7 @@ static int hdmi_v13_conf_index(struct drm_display_mode *mode)
|
||||||
true : false))
|
true : false))
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hdmi_v14_conf_index(struct drm_display_mode *mode)
|
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))
|
true : false))
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hdmi_conf_index(struct hdmi_context *hdata,
|
static int hdmi_conf_index(struct hdmi_context *hdata,
|
||||||
|
@ -1150,7 +1152,7 @@ static int hdmi_conf_index(struct hdmi_context *hdata,
|
||||||
{
|
{
|
||||||
if (hdata->is_v13)
|
if (hdata->is_v13)
|
||||||
return hdmi_v13_conf_index(mode);
|
return hdmi_v13_conf_index(mode);
|
||||||
else
|
|
||||||
return hdmi_v14_conf_index(mode);
|
return hdmi_v14_conf_index(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1193,6 +1195,11 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
|
||||||
{
|
{
|
||||||
int i;
|
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)
|
for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
|
||||||
if (hdmi_v13_confs[i].width == check_timing->xres &&
|
if (hdmi_v13_confs[i].width == check_timing->xres &&
|
||||||
hdmi_v13_confs[i].height == check_timing->yres &&
|
hdmi_v13_confs[i].height == check_timing->yres &&
|
||||||
|
@ -1202,6 +1209,8 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
|
||||||
true : false))
|
true : false))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1209,7 +1218,12 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
|
||||||
{
|
{
|
||||||
int i;
|
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 &&
|
if (hdmi_confs[i].width == check_timing->xres &&
|
||||||
hdmi_confs[i].height == check_timing->yres &&
|
hdmi_confs[i].height == check_timing->yres &&
|
||||||
hdmi_confs[i].vrefresh == check_timing->refresh &&
|
hdmi_confs[i].vrefresh == check_timing->refresh &&
|
||||||
|
@ -1218,6 +1232,8 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
|
||||||
true : false))
|
true : false))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1692,6 +1708,46 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
|
||||||
hdmi_regs_dump(hdata, "start");
|
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)
|
static void hdmi_mode_set(void *ctx, void *mode)
|
||||||
{
|
{
|
||||||
struct hdmi_context *hdata = (struct hdmi_context *)ctx;
|
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");
|
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)
|
static void hdmi_commit(void *ctx)
|
||||||
{
|
{
|
||||||
struct hdmi_context *hdata = (struct hdmi_context *)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 = {
|
static struct exynos_hdmi_manager_ops manager_ops = {
|
||||||
|
.mode_fixup = hdmi_mode_fixup,
|
||||||
.mode_set = hdmi_mode_set,
|
.mode_set = hdmi_mode_set,
|
||||||
|
.get_max_resol = hdmi_get_max_resol,
|
||||||
.commit = hdmi_commit,
|
.commit = hdmi_commit,
|
||||||
.disable = hdmi_disable,
|
.disable = hdmi_disable,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue