drm/bridge: synopsys: dw-hdmi: Check whether color has changed in connector atomic_check

For compatibility with GKI, connector atomic_begin/atomic_flush should
be removed. If hdmi color format has changed, set flag mode_changed
true and start a modeset to config hdmi.

We check whether color format has changed in
dw_hdmi_connector_atomic_check(), but color format variable update in
dw_hdmi_rockchip_encoder_atomic_check(), It runs after
dw_hdmi_connector_atomic_check(). That will lead to misjudgments when
determining whether color format has changed.

To solve this problem, we introduce get_color_changed(). When color
properties are set and color format is changed, get_color_changed()
return true.

Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Change-Id: Id1fbe80171856f91efa5ae40a0e0608a92ebcbf7
This commit is contained in:
Algea Cao
2020-08-17 17:59:19 +08:00
parent d2e5de5116
commit 299266141e
3 changed files with 179 additions and 50 deletions

View File

@@ -2840,6 +2840,19 @@ dw_hdmi_connector_best_encoder(struct drm_connector *connector)
return hdmi->bridge.encoder;
}
static bool dw_hdmi_color_changed(struct drm_connector *connector)
{
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
connector);
void *data = hdmi->plat_data->phy_data;
bool ret = false;
if (hdmi->plat_data->get_color_changed)
ret = hdmi->plat_data->get_color_changed(data);
return ret;
}
static bool hdr_metadata_equal(const struct drm_connector_state *old_state,
const struct drm_connector_state *new_state)
{
@@ -2864,11 +2877,54 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
drm_atomic_get_new_connector_state(state, connector);
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *crtc_state;
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
connector);
struct drm_display_mode *mode = NULL;
void *data = hdmi->plat_data->phy_data;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
unsigned int in_bus_format = hdmi->hdmi_data.enc_in_bus_format;
unsigned int out_bus_format = hdmi->hdmi_data.enc_out_bus_format;
if (!crtc)
return 0;
if (!hdr_metadata_equal(old_state, new_state)) {
/*
* If HDMI is enabled in uboot, it's need to record
* drm_display_mode and set phy status to enabled.
*/
if (!vmode->mpixelclock) {
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (hdmi->plat_data->get_enc_in_encoding)
hdmi->hdmi_data.enc_in_encoding =
hdmi->plat_data->get_enc_in_encoding(data);
if (hdmi->plat_data->get_enc_out_encoding)
hdmi->hdmi_data.enc_out_encoding =
hdmi->plat_data->get_enc_out_encoding(data);
if (hdmi->plat_data->get_input_bus_format)
hdmi->hdmi_data.enc_in_bus_format =
hdmi->plat_data->get_input_bus_format(data);
if (hdmi->plat_data->get_output_bus_format)
hdmi->hdmi_data.enc_out_bus_format =
hdmi->plat_data->get_output_bus_format(data);
mode = &crtc_state->mode;
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
vmode->mpixelclock = mode->crtc_clock * 1000;
vmode->previous_pixelclock = mode->clock;
vmode->previous_tmdsclock = mode->clock;
vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi,
vmode->mpixelclock);
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
vmode->mtmdsclock /= 2;
if (in_bus_format != hdmi->hdmi_data.enc_in_bus_format ||
out_bus_format != hdmi->hdmi_data.enc_out_bus_format)
dw_hdmi_setup(hdmi, hdmi->curr_conn,
&hdmi->previous_mode);
}
if (!hdr_metadata_equal(old_state, new_state) ||
dw_hdmi_color_changed(connector)) {
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);

View File

@@ -117,6 +117,7 @@ struct rockchip_hdmi {
unsigned long bus_format;
unsigned long output_bus_format;
unsigned long enc_out_encoding;
int color_changed;
struct drm_property *color_depth_property;
struct drm_property *hdmi_output_property;
@@ -589,7 +590,9 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
struct drm_crtc_state *crtc_state,
struct rockchip_hdmi *hdmi,
unsigned int *color_format,
unsigned int *color_depth,
unsigned int *output_mode,
unsigned long *bus_format,
unsigned int *bus_width,
unsigned long *enc_out_encoding,
unsigned int *eotf)
{
@@ -598,6 +601,7 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
struct hdr_static_metadata *hdr_metadata;
u32 vic = drm_match_cea_mode(mode);
unsigned long tmdsclock, pixclock = mode->crtc_clock;
unsigned int color_depth;
bool support_dc = false;
u32 max_tmds_clock = info->max_tmds_clock;
@@ -654,9 +658,9 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
support_dc = true;
if (hdmi->colordepth > 8 && support_dc)
*color_depth = 10;
color_depth = 10;
else
*color_depth = 8;
color_depth = 8;
*eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
if (conn_state->hdr_output_metadata) {
@@ -680,7 +684,7 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
/* BT2020 require color depth at lest 10bit */
*color_depth = 10;
color_depth = 10;
/* We prefer use YCbCr422 to send 10bit */
if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
*color_format = DRM_HDMI_OUTPUT_YCBCR422;
@@ -692,10 +696,10 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
DRM_MODE_FLAG_3D_FRAME_PACKING)
pixclock *= 2;
if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || *color_depth == 8)
if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8)
tmdsclock = pixclock;
else
tmdsclock = pixclock * (*color_depth) / 8;
tmdsclock = pixclock * (color_depth) / 8;
if (*color_format == DRM_HDMI_OUTPUT_YCBCR420)
tmdsclock /= 2;
@@ -708,16 +712,81 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
if (tmdsclock > max_tmds_clock) {
if (max_tmds_clock >= 594000) {
*color_depth = 8;
color_depth = 8;
} else if (max_tmds_clock > 340000) {
if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
*color_format = DRM_HDMI_OUTPUT_YCBCR420;
} else {
*color_depth = 8;
color_depth = 8;
if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
*color_format = DRM_HDMI_OUTPUT_YCBCR420;
}
}
if (*color_format == DRM_HDMI_OUTPUT_YCBCR420) {
*output_mode = ROCKCHIP_OUT_MODE_YUV420;
if (color_depth > 8)
*bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
else
*bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
*bus_width = color_depth / 2;
} else {
*output_mode = ROCKCHIP_OUT_MODE_AAAA;
if (color_depth > 8) {
if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
!hdmi->unsupported_yuv_input)
*bus_format = MEDIA_BUS_FMT_YUV10_1X30;
else
*bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
} else {
if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
!hdmi->unsupported_yuv_input)
*bus_format = MEDIA_BUS_FMT_YUV8_1X24;
else
*bus_format = MEDIA_BUS_FMT_RGB888_1X24;
}
if (*color_format == DRM_HDMI_OUTPUT_YCBCR422)
*bus_width = 8;
else
*bus_width = color_depth;
}
hdmi->bus_format = *bus_format;
if (*color_format == DRM_HDMI_OUTPUT_YCBCR422) {
if (color_depth == 12)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
else if (color_depth == 10)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
else
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
} else {
hdmi->output_bus_format = *bus_format;
}
}
static bool
dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state,
struct rockchip_hdmi *hdmi)
{
struct drm_crtc_state *crtc_state = conn_state->crtc->state;
unsigned int colorformat;
unsigned long bus_format;
unsigned long output_bus_format = hdmi->output_bus_format;
unsigned long enc_out_encoding = hdmi->enc_out_encoding;
unsigned int eotf, bus_width;
unsigned int output_mode;
dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
&colorformat,
&output_mode, &bus_format, &bus_width,
&hdmi->enc_out_encoding, &eotf);
if (output_bus_format != hdmi->output_bus_format ||
enc_out_encoding != hdmi->enc_out_encoding)
return true;
else
return false;
}
static int
@@ -727,40 +796,15 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
unsigned int colordepth, colorformat, bus_width;
unsigned int colorformat, bus_width;
unsigned int output_mode;
unsigned long bus_format;
dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
&colorformat, &colordepth,
&colorformat,
&output_mode, &bus_format, &bus_width,
&hdmi->enc_out_encoding, &s->eotf);
if (colorformat == DRM_HDMI_OUTPUT_YCBCR420) {
s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
if (colordepth > 8)
s->bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
else
s->bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
bus_width = colordepth / 2;
} else {
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
if (colordepth > 8) {
if (colorformat != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
!hdmi->unsupported_yuv_input)
s->bus_format = MEDIA_BUS_FMT_YUV10_1X30;
else
s->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
} else {
if (colorformat != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
!hdmi->unsupported_yuv_input)
s->bus_format = MEDIA_BUS_FMT_YUV8_1X24;
else
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
}
if (colorformat == DRM_HDMI_OUTPUT_YCBCR422)
bus_width = 8;
else
bus_width = colordepth;
}
hdmi->phy_bus_width = bus_width;
if (hdmi->phy)
phy_set_bus_width(hdmi->phy, bus_width);
@@ -768,19 +812,10 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
s->output_if = VOP_OUTPUT_IF_HDMI0;
s->output_mode = output_mode;
s->bus_format = bus_format;
hdmi->bus_format = s->bus_format;
if (colorformat == DRM_HDMI_OUTPUT_YCBCR422) {
if (colordepth == 12)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
else if (colordepth == 10)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
else
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
} else {
hdmi->output_bus_format = s->bus_format;
}
if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
s->color_space = V4L2_COLORSPACE_BT2020;
else if (colorformat == DRM_HDMI_OUTPUT_DEFAULT_RGB)
@@ -834,6 +869,19 @@ dw_hdmi_rockchip_get_quant_range(void *data)
return hdmi->hdmi_quant_range;
}
static bool
dw_hdmi_rockchip_get_color_changed(void *data)
{
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
bool ret = false;
if (hdmi->color_changed)
ret = true;
hdmi->color_changed = 0;
return ret;
}
static const struct drm_prop_enum_list color_depth_enum_list[] = {
{ 0, "Automatic" }, /* Prefer highest color depth */
{ 8, "24bit" },
@@ -903,6 +951,19 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
hdmi->colordepth = 8;
}
hdmi->bus_format = color;
if (hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR422) {
if (hdmi->colordepth == 12)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
else if (hdmi->colordepth == 10)
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
else
hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
} else {
hdmi->output_bus_format = hdmi->bus_format;
}
/* RK3368 does not support deep color mode */
if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) {
prop = drm_property_create_enum(connector->dev, 0,
@@ -1017,9 +1078,18 @@ dw_hdmi_rockchip_set_property(struct drm_connector *connector,
if (property == hdmi->color_depth_property) {
hdmi->colordepth = val;
/* If hdmi is disconnected, state->crtc is null */
if (!state->crtc)
return 0;
if (dw_hdmi_rockchip_check_color(state, hdmi))
hdmi->color_changed++;
return 0;
} else if (property == hdmi->hdmi_output_property) {
hdmi->hdmi_output = val;
if (!state->crtc)
return 0;
if (dw_hdmi_rockchip_check_color(state, hdmi))
hdmi->color_changed++;
return 0;
} else if (property == hdmi->quant_range) {
hdmi->hdmi_quant_range = val;
@@ -1381,6 +1451,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
dw_hdmi_rockchip_get_enc_out_encoding;
plat_data->get_quant_range =
dw_hdmi_rockchip_get_quant_range;
plat_data->get_color_changed =
dw_hdmi_rockchip_get_color_changed;
plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
encoder = &hdmi->encoder;

View File

@@ -193,6 +193,7 @@ struct dw_hdmi_plat_data {
unsigned long (*get_enc_in_encoding)(void *data);
unsigned long (*get_enc_out_encoding)(void *data);
unsigned long (*get_quant_range)(void *data);
bool (*get_color_changed)(void *data);
/* Vendor Property support */
const struct dw_hdmi_property_ops *property_ops;