diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index f934c1a448e7..ab1cde197b72 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -325,6 +325,7 @@ struct dw_hdmi { struct device *codec_dev; enum drm_connector_status last_connector_result; bool initialized; /* hdmi is enabled before bind */ + bool update; }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -2119,7 +2120,11 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2); /* AVI data byte 4 differences: none */ - val = frame.video_code & 0x7f; + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || + hdmi->connector.display_info.hdmi.scdc.supported) + val = hdmi->vic; + else + val = frame.video_code & 0x7f; hdmi_writeb(hdmi, val, HDMI_FC_AVIVID); /* AVI Data Byte 5- set up input and output pixel repetition */ @@ -2158,7 +2163,16 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, u8 buffer[10]; ssize_t err; - err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector, + /* if sink support hdmi2.0, don't send vsi */ + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) || + hdmi->connector.display_info.hdmi.scdc.supported) { + hdmi_mask_writeb(hdmi, 0, HDMI_FC_DATAUTO0, HDMI_FC_DATAUTO0_VSD_OFFSET, + HDMI_FC_DATAUTO0_VSD_MASK); + return; + } + + err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, + &hdmi->connector, mode); if (err < 0) /* @@ -2319,6 +2333,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, vmode->mtmdsclock /= 2; dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock); + if (hdmi->update) + return; + /* Set up HDMI_FC_INVIDCONF * Some display equipments require that the interval * between Video Data and Data island must be at least 58 pixels, @@ -2473,11 +2490,13 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM); /* Enable pixel clock and tmds data path */ - hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | - HDMI_MC_CLKDIS_CSCCLK_DISABLE | - HDMI_MC_CLKDIS_AUDCLK_DISABLE | - HDMI_MC_CLKDIS_PREPCLK_DISABLE | - HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + + if (!hdmi->update) + hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE | + HDMI_MC_CLKDIS_CSCCLK_DISABLE | + HDMI_MC_CLKDIS_AUDCLK_DISABLE | + HDMI_MC_CLKDIS_PREPCLK_DISABLE | + HDMI_MC_CLKDIS_TMDSCLK_DISABLE; hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); @@ -2513,6 +2532,8 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) unsigned int i; u8 val; + if (hdmi->update) + return; /* * Under some circumstances the Frame Composer arithmetic unit can miss * an FC register write due to being busy processing the previous one. @@ -2556,6 +2577,8 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) { + if (hdmi->update) + return; hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, HDMI_IH_MUTE_FC_STAT2); } @@ -2999,6 +3022,18 @@ static bool hdr_metadata_equal(const struct drm_connector_state *old_state, return !memcmp(old_blob->data, new_blob->data, old_blob->length); } +static bool check_hdr_color_change(struct drm_connector_state *old_state, + struct drm_connector_state *new_state, + struct dw_hdmi *hdmi) +{ + void *data = hdmi->plat_data->phy_data; + + if (!hdr_metadata_equal(old_state, new_state)) + return hdmi->plat_data->check_hdr_color_change(new_state, data); + + return false; +} + static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, struct drm_atomic_state *state) { @@ -3013,19 +3048,21 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *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; - bool color_changed = false; if (!crtc) return 0; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + mode = &crtc_state->mode; + /* * 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); @@ -3039,28 +3076,49 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, 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->previous_pixelclock = mode->clock * 1000; + vmode->previous_tmdsclock = mode->clock * 1000; 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) - color_changed = true; } - if (!hdr_metadata_equal(old_state, new_state) || - dw_hdmi_color_changed(connector) || color_changed) { - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); + if (check_hdr_color_change(old_state, new_state, hdmi) || + dw_hdmi_color_changed(connector)) { + u32 mtmdsclk; - crtc_state->mode_changed = true; + if (hdmi->plat_data->update_color_format) + hdmi->plat_data->update_color_format(new_state, data); + 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); + + mtmdsclk = hdmi_get_tmdsclock(hdmi, mode->clock); + + if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) + mtmdsclk /= 2; + + if (hdmi->hdmi_data.video_mode.mpixelclock == (mode->clock * 1000) && + hdmi->hdmi_data.video_mode.mtmdsclock == (mtmdsclk * 1000)) { + hdmi->update = true; + hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); + mdelay(50); + } else { + hdmi->update = false; + crtc_state->mode_changed = true; + } } return 0; @@ -3110,6 +3168,20 @@ dw_hdmi_connector_set_property(struct drm_connector *connector, property, val); } +static void dw_hdmi_connector_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *state) +{ + struct dw_hdmi *hdmi = + container_of(connector, struct dw_hdmi, connector); + + if (hdmi->update) { + dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode); + mdelay(50); + hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); + hdmi->update = false; + } +} + void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) { if (!hdmi->bridge_is_on) @@ -3197,6 +3269,7 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .get_modes = dw_hdmi_connector_get_modes, .best_encoder = dw_hdmi_connector_best_encoder, .atomic_check = dw_hdmi_connector_atomic_check, + .atomic_commit = dw_hdmi_connector_atomic_commit, }; static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index d13159e50451..6c2666fdb7a5 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -1575,6 +1575,9 @@ static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) struct drm_crtc *crtc = encoder->crtc; struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); + if (WARN_ON(!crtc || !crtc->state)) + return; + if (crtc->state->active_changed) { if (hdmi->plat_data->split_mode) { s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1); @@ -1937,7 +1940,7 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, else *enc_out_encoding = V4L2_YCBCR_ENC_709; - if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) { + if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020 && color_depth == 8) { /* BT2020 require color depth at lest 10bit */ color_depth = 10; /* We prefer use YCbCr422 to send 10bit */ @@ -2226,6 +2229,14 @@ dw_hdmi_rockchip_get_hdr_blob(void *data) return hdmi->hdr_panel_blob_ptr; } +static void dw_hdmi_rockchip_update_color_format(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + dw_hdmi_rockchip_check_color(conn_state, hdmi); +} + static bool dw_hdmi_rockchip_get_color_changed(void *data) { @@ -2367,6 +2378,21 @@ static int dw_hdmi_link_clk_set(void *data, bool enable) return 0; } +static bool +dw_hdmi_rockchip_check_hdr_color_change(struct drm_connector_state *conn_state, + void *data) +{ + struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; + + if (!conn_state || !data) + return false; + + if (dw_hdmi_rockchip_check_color(conn_state, hdmi)) + return true; + + return false; +} + static const struct drm_prop_enum_list color_depth_enum_list[] = { { 0, "Automatic" }, /* Prefer highest color depth */ { 8, "24bit" }, @@ -3295,7 +3321,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, plat_data->dclk_set = dw_hdmi_dclk_set; plat_data->link_clk_set = dw_hdmi_link_clk_set; plat_data->get_vp_id = dw_hdmi_rockchip_get_vp_id; - + plat_data->update_color_format = + dw_hdmi_rockchip_update_color_format; + plat_data->check_hdr_color_change = + dw_hdmi_rockchip_check_hdr_color_change; plat_data->property_ops = &dw_hdmi_rockchip_property_ops; secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index f667486ccd62..761687a457fe 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -252,6 +252,8 @@ struct dw_hdmi_plat_data { int (*dclk_set)(void *data, bool enable, int vp_id); int (*link_clk_set)(void *data, bool enable); int (*get_vp_id)(struct drm_crtc_state *crtc_state); + void (*update_color_format)(struct drm_connector_state *conn_state, void *data); + bool (*check_hdr_color_change)(struct drm_connector_state *conn_state, void *data); /* Vendor Property support */ const struct dw_hdmi_property_ops *property_ops;