diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 6e9273330394..ec75328c5a59 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -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); diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 856561f8389b..c2aff08c7f8e 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -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; diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index fca0aa96e7f2..b3b0148471ad 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -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;