diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index a3255467c428..1bb727fe7229 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -324,6 +324,8 @@ struct dw_hdmi { bool sink_is_hdmi; bool sink_has_audio; bool hpd_state; + bool support_hdmi; + int force_output; struct delayed_work work; struct workqueue_struct *workqueue; @@ -390,6 +392,16 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, hdmi_modb(hdmi, data << shift, mask, reg); } +static void dw_hdmi_check_output_type(struct dw_hdmi *hdmi) +{ + if (hdmi->force_output == 1) + hdmi->sink_is_hdmi = true; + else if (hdmi->force_output == 2) + hdmi->sink_is_hdmi = false; + else + hdmi->sink_is_hdmi = hdmi->support_hdmi; +} + static void repo_hpd_event(struct work_struct *p_work) { struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work); @@ -2740,7 +2752,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", edid->width_cm, edid->height_cm); - hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->support_hdmi = drm_detect_hdmi_monitor(edid); hdmi->sink_has_audio = drm_detect_monitor_audio(edid); drm_connector_update_edid_property(connector, edid); cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); @@ -2748,7 +2760,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) dw_hdmi_update_hdr_property(connector); kfree(edid); } else { - hdmi->sink_is_hdmi = true; + hdmi->support_hdmi = true; hdmi->sink_has_audio = true; for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) { @@ -2772,6 +2784,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) dev_info(hdmi->dev, "failed to get edid\n"); } + dw_hdmi_check_output_type(hdmi); return ret; } @@ -2916,6 +2929,24 @@ void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi) } EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range); +void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val) +{ + hdmi->force_output = val; + + dw_hdmi_check_output_type(hdmi); + + hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP); + dw_hdmi_setup(hdmi, &hdmi->previous_mode); + hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type); + +bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi) +{ + return hdmi->sink_is_hdmi; +} +EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi); + static void dw_hdmi_connector_force(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 094061cd7b7d..b15ceefa6795 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -116,6 +116,7 @@ struct rockchip_hdmi { bool unsupported_yuv_input; bool unsupported_deep_color; bool mode_changed; + u8 force_output; u8 id; unsigned long bus_format; @@ -130,6 +131,7 @@ struct rockchip_hdmi { struct drm_property *colorimetry_property; struct drm_property *quant_range; struct drm_property *hdr_panel_metadata_property; + struct drm_property *output_hdmi_dvi; struct drm_property_blob *hdr_panel_blob_ptr; @@ -642,6 +644,7 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, unsigned long tmdsclock, pixclock = mode->crtc_clock; unsigned int color_depth; bool support_dc = false; + bool sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi); int max_tmds_clock = info->max_tmds_clock; int output_eotf; @@ -702,6 +705,11 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, else color_depth = 8; + if (!sink_is_hdmi) { + *color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB; + color_depth = 8; + } + *eotf = TRADITIONAL_GAMMA_SDR; if (conn_state->hdr_output_metadata) { hdr_metadata = (struct hdr_output_metadata *) @@ -986,6 +994,12 @@ static const struct drm_prop_enum_list colorimetry_enum_list[] = { { RK_HDMI_COLORIMETRY_BT2020, "ITU_2020" }, }; +static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = { + { 0, "auto" }, + { 1, "force_hdmi" }, + { 2, "force_dvi" }, +}; + static void dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, unsigned int color, int version, @@ -1106,6 +1120,15 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, drm_object_attach_property(&connector->base, prop, 0); } + prop = drm_property_create_enum(connector->dev, 0, + "output_hdmi_dvi", + output_hdmi_dvi_enum_list, + ARRAY_SIZE(output_hdmi_dvi_enum_list)); + if (prop) { + hdmi->output_hdmi_dvi = prop; + drm_object_attach_property(&connector->base, prop, 0); + } + prop = connector->dev->mode_config.hdr_output_metadata_property; if (version >= 0x211a) drm_object_attach_property(&connector->base, prop, 0); @@ -1159,6 +1182,12 @@ dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, hdmi->hdr_panel_metadata_property); hdmi->hdr_panel_metadata_property = NULL; } + + if (hdmi->output_hdmi_dvi) { + drm_property_destroy(connector->dev, + hdmi->output_hdmi_dvi); + hdmi->output_hdmi_dvi = NULL; + } } static int @@ -1195,6 +1224,12 @@ dw_hdmi_rockchip_set_property(struct drm_connector *connector, } else if (property == hdmi->colorimetry_property) { hdmi->colorimetry = val; return 0; + } else if (property == hdmi->output_hdmi_dvi) { + if (hdmi->force_output != val) + hdmi->color_changed++; + hdmi->force_output = val; + dw_hdmi_set_output_type(hdmi->hdmi, val); + return 0; } DRM_ERROR("failed to set rockchip hdmi connector property %s\n", property->name); @@ -1260,6 +1295,9 @@ dw_hdmi_rockchip_get_property(struct drm_connector *connector, } else if (property == private->connector_id_prop) { *val = hdmi->id; return 0; + } else if (property == hdmi->output_hdmi_dvi) { + *val = hdmi->force_output; + return 0; } DRM_ERROR("failed to get rockchip hdmi connector property %s\n", property->name); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 5e3e592d5bd6..46cd16575473 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -217,5 +217,7 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, bool force, bool disabled, bool rxsense); void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data); void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi); +void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); +bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); #endif /* __IMX_HDMI_H__ */