Merge branch 'exynos-drm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-fixes
This pull-request fixes hdmi power-off order issue, mixer issues related to power on/off, and includes trivial fixups. * 'exynos-drm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: drm/exynos: enable vsync interrupt while waiting for vblank drm/exynos: soft reset mixer before reconfigure after power-on drm/exynos: allow multiple layer updates per vsync for mixer drm/exynos: stop mixer before gating clocks during poweroff drm/exynos: set power state variable after enabling clocks and power drm/exynos: disable unused windows on apply drm/exynos: Fix de-registration ordering drm/exynos: change zero to NULL for sparse drm/exynos: dpi: Fix NULL pointer dereference with legacy bindings drm/exynos: hdmi: fix power order issue
This commit is contained in:
commit
b5f4843c67
|
@ -40,7 +40,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
|
||||||
{
|
{
|
||||||
struct exynos_dpi *ctx = connector_to_dpi(connector);
|
struct exynos_dpi *ctx = connector_to_dpi(connector);
|
||||||
|
|
||||||
if (!ctx->panel->connector)
|
if (ctx->panel && !ctx->panel->connector)
|
||||||
drm_panel_attach(ctx->panel, &ctx->connector);
|
drm_panel_attach(ctx->panel, &ctx->connector);
|
||||||
|
|
||||||
return connector_status_connected;
|
return connector_status_connected;
|
||||||
|
|
|
@ -765,24 +765,24 @@ static int exynos_drm_init(void)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_unregister_pd:
|
|
||||||
platform_device_unregister(exynos_drm_pdev);
|
|
||||||
|
|
||||||
err_remove_vidi:
|
err_remove_vidi:
|
||||||
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
||||||
exynos_drm_remove_vidi();
|
exynos_drm_remove_vidi();
|
||||||
|
|
||||||
|
err_unregister_pd:
|
||||||
#endif
|
#endif
|
||||||
|
platform_device_unregister(exynos_drm_pdev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos_drm_exit(void)
|
static void exynos_drm_exit(void)
|
||||||
{
|
{
|
||||||
|
platform_driver_unregister(&exynos_drm_platform_driver);
|
||||||
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
#ifdef CONFIG_DRM_EXYNOS_VIDI
|
||||||
exynos_drm_remove_vidi();
|
exynos_drm_remove_vidi();
|
||||||
#endif
|
#endif
|
||||||
platform_device_unregister(exynos_drm_pdev);
|
platform_device_unregister(exynos_drm_pdev);
|
||||||
platform_driver_unregister(&exynos_drm_platform_driver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(exynos_drm_init);
|
module_init(exynos_drm_init);
|
||||||
|
|
|
@ -343,7 +343,7 @@ struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
|
||||||
int exynos_dpi_remove(struct device *dev);
|
int exynos_dpi_remove(struct device *dev);
|
||||||
#else
|
#else
|
||||||
static inline struct exynos_drm_display *
|
static inline struct exynos_drm_display *
|
||||||
exynos_dpi_probe(struct device *dev) { return 0; }
|
exynos_dpi_probe(struct device *dev) { return NULL; }
|
||||||
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
|
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -741,6 +741,8 @@ static void fimd_apply(struct exynos_drm_manager *mgr)
|
||||||
win_data = &ctx->win_data[i];
|
win_data = &ctx->win_data[i];
|
||||||
if (win_data->enabled)
|
if (win_data->enabled)
|
||||||
fimd_win_commit(mgr, i);
|
fimd_win_commit(mgr, i);
|
||||||
|
else
|
||||||
|
fimd_win_disable(mgr, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
fimd_commit(mgr);
|
fimd_commit(mgr);
|
||||||
|
|
|
@ -2090,6 +2090,11 @@ out:
|
||||||
|
|
||||||
static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
||||||
{
|
{
|
||||||
|
struct hdmi_context *hdata = display->ctx;
|
||||||
|
struct drm_encoder *encoder = hdata->encoder;
|
||||||
|
struct drm_crtc *crtc = encoder->crtc;
|
||||||
|
struct drm_crtc_helper_funcs *funcs = NULL;
|
||||||
|
|
||||||
DRM_DEBUG_KMS("mode %d\n", mode);
|
DRM_DEBUG_KMS("mode %d\n", mode);
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
@ -2099,6 +2104,20 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
||||||
case DRM_MODE_DPMS_STANDBY:
|
case DRM_MODE_DPMS_STANDBY:
|
||||||
case DRM_MODE_DPMS_SUSPEND:
|
case DRM_MODE_DPMS_SUSPEND:
|
||||||
case DRM_MODE_DPMS_OFF:
|
case DRM_MODE_DPMS_OFF:
|
||||||
|
/*
|
||||||
|
* The SFRs of VP and Mixer are updated by Vertical Sync of
|
||||||
|
* Timing generator which is a part of HDMI so the sequence
|
||||||
|
* to disable TV Subsystem should be as following,
|
||||||
|
* VP -> Mixer -> HDMI
|
||||||
|
*
|
||||||
|
* Below codes will try to disable Mixer and VP(if used)
|
||||||
|
* prior to disabling HDMI.
|
||||||
|
*/
|
||||||
|
if (crtc)
|
||||||
|
funcs = crtc->helper_private;
|
||||||
|
if (funcs && funcs->dpms)
|
||||||
|
(*funcs->dpms)(crtc, mode);
|
||||||
|
|
||||||
hdmi_poweroff(display);
|
hdmi_poweroff(display);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -377,6 +377,20 @@ static void mixer_run(struct mixer_context *ctx)
|
||||||
mixer_regs_dump(ctx);
|
mixer_regs_dump(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mixer_stop(struct mixer_context *ctx)
|
||||||
|
{
|
||||||
|
struct mixer_resources *res = &ctx->mixer_res;
|
||||||
|
int timeout = 20;
|
||||||
|
|
||||||
|
mixer_reg_writemask(res, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
|
||||||
|
|
||||||
|
while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
|
||||||
|
--timeout)
|
||||||
|
usleep_range(10000, 12000);
|
||||||
|
|
||||||
|
mixer_regs_dump(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static void vp_video_buffer(struct mixer_context *ctx, int win)
|
static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||||
{
|
{
|
||||||
struct mixer_resources *res = &ctx->mixer_res;
|
struct mixer_resources *res = &ctx->mixer_res;
|
||||||
|
@ -497,13 +511,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
|
||||||
static void mixer_layer_update(struct mixer_context *ctx)
|
static void mixer_layer_update(struct mixer_context *ctx)
|
||||||
{
|
{
|
||||||
struct mixer_resources *res = &ctx->mixer_res;
|
struct mixer_resources *res = &ctx->mixer_res;
|
||||||
u32 val;
|
|
||||||
|
|
||||||
val = mixer_reg_read(res, MXR_CFG);
|
mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
|
||||||
|
|
||||||
/* allow one update per vsync only */
|
|
||||||
if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK))
|
|
||||||
mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
static void mixer_graph_buffer(struct mixer_context *ctx, int win)
|
||||||
|
@ -1010,6 +1019,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||||
}
|
}
|
||||||
mutex_unlock(&mixer_ctx->mixer_mutex);
|
mutex_unlock(&mixer_ctx->mixer_mutex);
|
||||||
|
|
||||||
|
drm_vblank_get(mgr->crtc->dev, mixer_ctx->pipe);
|
||||||
|
|
||||||
atomic_set(&mixer_ctx->wait_vsync_event, 1);
|
atomic_set(&mixer_ctx->wait_vsync_event, 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1020,6 +1031,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
|
||||||
!atomic_read(&mixer_ctx->wait_vsync_event),
|
!atomic_read(&mixer_ctx->wait_vsync_event),
|
||||||
HZ/20))
|
HZ/20))
|
||||||
DRM_DEBUG_KMS("vblank wait timed out.\n");
|
DRM_DEBUG_KMS("vblank wait timed out.\n");
|
||||||
|
|
||||||
|
drm_vblank_put(mgr->crtc->dev, mixer_ctx->pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mixer_window_suspend(struct exynos_drm_manager *mgr)
|
static void mixer_window_suspend(struct exynos_drm_manager *mgr)
|
||||||
|
@ -1061,7 +1074,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
|
||||||
mutex_unlock(&ctx->mixer_mutex);
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx->powered = true;
|
|
||||||
mutex_unlock(&ctx->mixer_mutex);
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
|
|
||||||
pm_runtime_get_sync(ctx->dev);
|
pm_runtime_get_sync(ctx->dev);
|
||||||
|
@ -1072,6 +1085,12 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
|
||||||
clk_prepare_enable(res->sclk_mixer);
|
clk_prepare_enable(res->sclk_mixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_lock(&ctx->mixer_mutex);
|
||||||
|
ctx->powered = true;
|
||||||
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
|
|
||||||
|
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
|
||||||
|
|
||||||
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
|
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
|
||||||
mixer_win_reset(ctx);
|
mixer_win_reset(ctx);
|
||||||
|
|
||||||
|
@ -1084,14 +1103,21 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr)
|
||||||
struct mixer_resources *res = &ctx->mixer_res;
|
struct mixer_resources *res = &ctx->mixer_res;
|
||||||
|
|
||||||
mutex_lock(&ctx->mixer_mutex);
|
mutex_lock(&ctx->mixer_mutex);
|
||||||
if (!ctx->powered)
|
if (!ctx->powered) {
|
||||||
goto out;
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
mutex_unlock(&ctx->mixer_mutex);
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
|
|
||||||
|
mixer_stop(ctx);
|
||||||
mixer_window_suspend(mgr);
|
mixer_window_suspend(mgr);
|
||||||
|
|
||||||
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
|
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
|
||||||
|
|
||||||
|
mutex_lock(&ctx->mixer_mutex);
|
||||||
|
ctx->powered = false;
|
||||||
|
mutex_unlock(&ctx->mixer_mutex);
|
||||||
|
|
||||||
clk_disable_unprepare(res->mixer);
|
clk_disable_unprepare(res->mixer);
|
||||||
if (ctx->vp_enabled) {
|
if (ctx->vp_enabled) {
|
||||||
clk_disable_unprepare(res->vp);
|
clk_disable_unprepare(res->vp);
|
||||||
|
@ -1099,12 +1125,6 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_put_sync(ctx->dev);
|
pm_runtime_put_sync(ctx->dev);
|
||||||
|
|
||||||
mutex_lock(&ctx->mixer_mutex);
|
|
||||||
ctx->powered = false;
|
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&ctx->mixer_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
|
static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
#define MXR_STATUS_BIG_ENDIAN (1 << 3)
|
#define MXR_STATUS_BIG_ENDIAN (1 << 3)
|
||||||
#define MXR_STATUS_ENDIAN_MASK (1 << 3)
|
#define MXR_STATUS_ENDIAN_MASK (1 << 3)
|
||||||
#define MXR_STATUS_SYNC_ENABLE (1 << 2)
|
#define MXR_STATUS_SYNC_ENABLE (1 << 2)
|
||||||
|
#define MXR_STATUS_REG_IDLE (1 << 1)
|
||||||
#define MXR_STATUS_REG_RUN (1 << 0)
|
#define MXR_STATUS_REG_RUN (1 << 0)
|
||||||
|
|
||||||
/* bits for MXR_CFG */
|
/* bits for MXR_CFG */
|
||||||
|
|
Loading…
Reference in New Issue