drm: bridge: dw-hdmi: Implement connector atomic_begin

To avoid screen flash when updating CSC, we introduce connector
atomic_begin. Before flush crtc and connector, it's need to send
AVMUTE flag to make screen black, and clear flag after CSC updated.

AVMUTE -> Update CRTC -> Update HDMI -> Clear AVMUTE

Change-Id: Id47caac1e25fcce5a5aa7b879da4a6b9a9bab8a1
Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
This commit is contained in:
Zheng Yang
2017-12-05 16:30:40 +08:00
committed by Tao Huang
parent 401fc0060c
commit 5975fbc62e
2 changed files with 56 additions and 18 deletions

View File

@@ -170,6 +170,7 @@ struct hdmi_data_info {
unsigned int pix_repet_factor;
unsigned int hdcp_enable;
struct hdmi_vmode video_mode;
bool update;
};
struct dw_hdmi_i2c {
@@ -2015,6 +2016,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
{
int ret;
void *data = hdmi->plat_data->phy_data;
bool need_delay = false;
hdmi_disable_overflow_interrupts(hdmi);
@@ -2098,6 +2100,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
if (ret)
return ret;
hdmi->phy.enabled = true;
} else {
need_delay = true;
}
/* HDMI Initialization Step B.3 */
dw_hdmi_enable_video_path(hdmi);
@@ -2128,6 +2132,9 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
dw_hdmi_clear_overflow(hdmi);
/* XXX: Add delay to make csc work before unmute video. */
if (need_delay)
msleep(50);
return 0;
}
@@ -2294,13 +2301,12 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
}
static void
dw_hdmi_connector_atomic_flush(struct drm_connector *connector,
dw_hdmi_connector_atomic_begin(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
connector);
connector);
void *data = hdmi->plat_data->phy_data;
struct drm_display_mode *mode = NULL;
unsigned int enc_in_bus_format;
unsigned int enc_out_bus_format;
unsigned int enc_in_encoding;
@@ -2309,21 +2315,8 @@ dw_hdmi_connector_atomic_flush(struct drm_connector *connector,
if (!hdmi->hpd_state || !conn_state->crtc)
return;
DRM_DEBUG("%s\n", __func__);
/*
* If HDMI is enabled in uboot, it's need to record
* drm_display_mode and set phy status to enabled.
*/
if (!hdmi->hdmi_data.video_mode.mpixelclock) {
mode = &conn_state->crtc->mode;
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
hdmi->hdmi_data.video_mode.mpixelclock = mode->clock;
hdmi->hdmi_data.video_mode.previous_pixelclock = mode->clock;
hdmi->hdmi_data.video_mode.previous_tmdsclock = mode->clock;
hdmi->phy.enabled = true;
if (!hdmi->hdmi_data.video_mode.mpixelclock)
return;
}
if (hdmi->plat_data->get_enc_in_encoding)
enc_in_encoding = hdmi->plat_data->get_enc_in_encoding(data);
@@ -2347,8 +2340,48 @@ dw_hdmi_connector_atomic_flush(struct drm_connector *connector,
if (enc_in_encoding != hdmi->hdmi_data.enc_in_encoding ||
enc_out_encoding != hdmi->hdmi_data.enc_out_encoding ||
enc_in_bus_format != hdmi->hdmi_data.enc_in_bus_format ||
enc_out_bus_format != hdmi->hdmi_data.enc_out_bus_format)
enc_out_bus_format != hdmi->hdmi_data.enc_out_bus_format) {
hdmi->hdmi_data.update = true;
hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
/* XXX: Add delay to make av mute work on sink*/
msleep(50);
} else {
hdmi->hdmi_data.update = false;
}
}
static void
dw_hdmi_connector_atomic_flush(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
connector);
struct drm_display_mode *mode = NULL;
if (!hdmi->hpd_state || !conn_state->crtc)
return;
DRM_DEBUG("%s\n", __func__);
/*
* If HDMI is enabled in uboot, it's need to record
* drm_display_mode and set phy status to enabled.
*/
if (!hdmi->hdmi_data.video_mode.mpixelclock) {
mode = &conn_state->crtc->mode;
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
hdmi->hdmi_data.video_mode.mpixelclock = mode->clock;
hdmi->hdmi_data.video_mode.previous_pixelclock = mode->clock;
hdmi->hdmi_data.video_mode.previous_tmdsclock = mode->clock;
hdmi->phy.enabled = true;
return;
}
if (hdmi->hdmi_data.update) {
dw_hdmi_setup(hdmi, &hdmi->previous_mode);
hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
hdmi->hdmi_data.update = false;
}
}
static int
@@ -2423,6 +2456,7 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
.get_modes = dw_hdmi_connector_get_modes,
.best_encoder = drm_atomic_helper_best_encoder,
.atomic_begin = dw_hdmi_connector_atomic_begin,
.atomic_flush = dw_hdmi_connector_atomic_flush,
};

View File

@@ -853,6 +853,10 @@ enum {
HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
/* HDMI_FC_GCP */
HDMI_FC_GCP_SET_AVMUTE = 0x2,
HDMI_FC_GCP_CLEAR_AVMUTE = 0x1,
/* FC_DBGFORCE field values */
HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,