From 1bf8b6d4b41d058fc0d5fa90b108cd61992816e6 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Fri, 9 Sep 2022 19:20:37 +0800 Subject: [PATCH] drm/bridge: synopsys: dw-hdmi-qp: Support ALLM Add two drm properties for ALLM: allm_capacity: values: 0 1 0: sink not support ALLM 1: sink support ALLM allm_enable: 0: disable ALLM 1: enable ALLM Signed-off-by: Algea Cao Change-Id: I0f5a6fa324c6727c37414f017b4839b3ec910206 --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 112 +++++++++++++++---- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 2 +- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 62 +++++++++- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 10 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 +- include/drm/bridge/dw_hdmi.h | 6 +- 6 files changed, 164 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 2cc24007498f..6e7335f2104f 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -252,6 +252,7 @@ struct dw_hdmi_qp { bool dclk_en; bool frl_switch; bool cec_enable; + bool allm_enable; struct mutex mutex; /* for state below and previous_mode */ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ @@ -1299,6 +1300,12 @@ static void hdmi_config_AVI(struct dw_hdmi_qp *hdmi, hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN, PKTSCHED_AVI_TX_EN, PKTSCHED_PKT_EN); } +#define VSI_PKT_TYPE 0x81 +#define VSI_PKT_VERSION 1 +#define HDMI_FORUM_OUI 0xc45dd8 +#define ALLM_MODE BIT(1) +#define HDMI_FORUM_LEN 9 + static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi_qp *hdmi, const struct drm_connector *connector, const struct drm_display_mode *mode) @@ -1308,24 +1315,47 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi_qp *hdmi, u32 val; ssize_t err; int i, reg; + struct dw_hdmi_link_config *link_cfg = NULL; + void *data = hdmi->plat_data->phy_data; + + if (hdmi->plat_data->get_link_cfg) + link_cfg = hdmi->plat_data->get_link_cfg(data); hdmi_modb(hdmi, 0, PKTSCHED_VSI_TX_EN, PKTSCHED_PKT_EN); - err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector, - mode); - if (err < 0) - /* - * Going into that statement does not means vendor infoframe - * fails. It just informed us that vendor infoframe is not - * needed for the selected mode. Only 4k or stereoscopic 3D - * mode requires vendor infoframe. So just simply return. - */ - return; + for (i = 0; i <= 7; i++) + hdmi_writel(hdmi, 0, PKT_VSI_CONTENTS0 + i * 4); - err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (err < 0) { - dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", - err); - return; + if (hdmi->allm_enable && (link_cfg->add_func & SUPPORT_HDMI_ALLM)) { + buffer[0] = VSI_PKT_TYPE; + buffer[1] = VSI_PKT_VERSION; + buffer[2] = 5; + buffer[4] = HDMI_FORUM_OUI & 0xff; + buffer[5] = (HDMI_FORUM_OUI >> 8) & 0xff; + buffer[6] = (HDMI_FORUM_OUI >> 16) & 0xff; + buffer[7] = VSI_PKT_VERSION; + buffer[8] = ALLM_MODE; + + hdmi_infoframe_set_checksum(buffer, HDMI_FORUM_LEN); + + err = 9; + } else { + err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector, + mode); + if (err < 0) + /* + * Going into that statement does not means vendor infoframe + * fails. It just informed us that vendor infoframe is not + * needed for the selected mode. Only 4k or stereoscopic 3D + * mode requires vendor infoframe. So just simply return. + */ + return; + + err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n", + err); + return; + } } /* vsi header */ @@ -2044,6 +2074,37 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector) return ret; } +void dw_hdmi_qp_set_allm_enable(struct dw_hdmi_qp *hdmi, bool enable) +{ + struct dw_hdmi_link_config *link_cfg = NULL; + void *data; + + if (!hdmi || !hdmi->curr_conn) + return; + + data = hdmi->plat_data->phy_data; + + if (hdmi->plat_data->get_link_cfg) + link_cfg = hdmi->plat_data->get_link_cfg(data); + + if (!link_cfg) + return; + + if (enable == hdmi->allm_enable) + return; + + hdmi->allm_enable = enable; + + if (enable && !(link_cfg->add_func & SUPPORT_HDMI_ALLM)) { + hdmi->allm_enable = false; + dev_err(hdmi->dev, "sink don't support allm, allm won't be enabled\n"); + return; + } + + hdmi_config_vendor_specific_infoframe(hdmi, hdmi->curr_conn, &hdmi->previous_mode); +} +EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_allm_enable); + static int dw_hdmi_atomic_connector_set_property(struct drm_connector *connector, struct drm_connector_state *state, @@ -2088,6 +2149,7 @@ dw_hdmi_connector_set_property(struct drm_connector *connector, static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) { + u32 val; u64 color = MEDIA_BUS_FMT_YUV8_1X24; const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops; @@ -2095,14 +2157,21 @@ static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi) enum drm_connector_status connect_status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); - if (hdmi->plat_data->get_grf_color_fmt && - (connect_status == connector_status_connected) && - hdmi->initialized) - color = hdmi->plat_data->get_grf_color_fmt(data); + if ((connect_status == connector_status_connected) && + hdmi->initialized) { + if (hdmi->plat_data->get_grf_color_fmt) + color = hdmi->plat_data->get_grf_color_fmt(data); + + val = (hdmi_readl(hdmi, PKT_VSI_CONTENTS1) >> 8) & 0xffffff; + if (val == HDMI_FORUM_OUI) + hdmi->allm_enable = true; + else + hdmi->allm_enable = false; + } if (ops && ops->attach_properties) return ops->attach_properties(&hdmi->connector, color, 0, - hdmi->plat_data->phy_data); + hdmi->plat_data->phy_data, hdmi->allm_enable); } static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi) @@ -2188,6 +2257,7 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, * drm_display_mode and set phy status to enabled. */ if (!vmode->mpixelclock) { + hdmi->curr_conn = connector; if (hdmi->plat_data->get_enc_in_encoding) hdmi->hdmi_data.enc_in_encoding = hdmi->plat_data->get_enc_in_encoding(data); @@ -2805,7 +2875,7 @@ static int dw_hdmi_status_show(struct seq_file *s, void *v) seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n", hdmi->hdmi_data.video_mode.mpixelclock, val); } - + seq_printf(s, "ALLM: %d\n", hdmi->allm_enable); seq_puts(s, "Color Format: "); if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) seq_puts(s, "RGB"); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 28a3bc53f192..088b638a9dfd 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3240,7 +3240,7 @@ static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi) if (ops && ops->attach_properties) return ops->attach_properties(&hdmi->connector, color, hdmi->version, - hdmi->plat_data->phy_data); + hdmi->plat_data->phy_data, 0); } static void dw_hdmi_destroy_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 cfa96940c0e2..31eebd779955 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -196,6 +196,8 @@ struct rockchip_hdmi { struct drm_property *output_hdmi_dvi; struct drm_property *output_type_capacity; struct drm_property *user_split_mode_prop; + struct drm_property *allm_capacity; + struct drm_property *allm_enable; struct drm_property_blob *hdr_panel_blob_ptr; struct drm_property_blob *next_hdr_data_ptr; @@ -204,11 +206,13 @@ struct rockchip_hdmi { unsigned int colorimetry; unsigned int hdmi_quant_range; unsigned int phy_bus_width; + unsigned int enable_allm; enum rk_if_color_format hdmi_output; struct rockchip_drm_sub_dev sub_dev; u8 max_frl_rate_per_lane; u8 max_lanes; + u8 add_func; struct rockchip_drm_dsc_cap dsc_cap; struct next_hdr_sink_data next_hdr_data; struct dw_hdmi_link_config link_cfg; @@ -800,6 +804,7 @@ static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, hdmi->link_cfg.dsc_mode = false; hdmi->link_cfg.frl_lanes = max_lanes; hdmi->link_cfg.rate_per_lane = max_rate_per_lane; + hdmi->link_cfg.add_func = hdmi->add_func; if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) { dev_info(hdmi->dev, "use tmds mode\n"); @@ -2167,9 +2172,14 @@ dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid) if (!edid) return -EINVAL; + memset(&hdmi->dsc_cap, 0, sizeof(hdmi->dsc_cap)); + hdmi->max_frl_rate_per_lane = 0; + hdmi->max_lanes = 0; + hdmi->add_func = 0; + return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap, &hdmi->max_frl_rate_per_lane, - &hdmi->max_lanes, edid); + &hdmi->max_lanes, &hdmi->add_func, edid); } static int @@ -2296,10 +2306,15 @@ static const struct drm_prop_enum_list output_type_cap_list[] = { { 1, "HDMI" }, }; +static const struct drm_prop_enum_list allm_enable_list[] = { + { 0, "disable" }, + { 1, "enable" }, +}; + static void dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, unsigned int color, int version, - void *data) + void *data, bool allm_en) { struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; struct drm_property *prop; @@ -2414,6 +2429,23 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector, hdmi->user_split_mode ? 1 : 0); } + prop = drm_property_create_bool(connector->dev, 0, "allm_capacity"); + if (prop) { + hdmi->allm_capacity = prop; + drm_object_attach_property(&connector->base, prop, + !!(hdmi->add_func & SUPPORT_HDMI_ALLM)); + } + + prop = drm_property_create_enum(connector->dev, 0, + "allm_enable", + allm_enable_list, + ARRAY_SIZE(allm_enable_list)); + if (prop) { + hdmi->allm_enable = prop; + drm_object_attach_property(&connector->base, prop, 0); + } + hdmi->enable_allm = allm_en; + if (!hdmi->is_hdmi_qp) { prop = drm_property_create_enum(connector->dev, 0, "output_hdmi_dvi", @@ -2518,6 +2550,17 @@ dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector, hdmi->user_split_mode_prop); hdmi->user_split_mode_prop = NULL; } + + if (hdmi->allm_capacity) { + drm_property_destroy(connector->dev, + hdmi->allm_capacity); + hdmi->allm_capacity = NULL; + } + + if (hdmi->allm_enable) { + drm_property_destroy(connector->dev, hdmi->allm_enable); + hdmi->allm_enable = NULL; + } } static int @@ -2566,6 +2609,15 @@ dw_hdmi_rockchip_set_property(struct drm_connector *connector, return 0; } else if (property == hdmi->output_type_capacity) { return 0; + } else if (property == hdmi->allm_capacity) { + return 0; + } else if (property == hdmi->allm_enable) { + u64 allm_enable = hdmi->enable_allm; + + hdmi->enable_allm = val; + if (allm_enable != hdmi->enable_allm) + dw_hdmi_qp_set_allm_enable(hdmi->hdmi_qp, hdmi->enable_allm); + return 0; } DRM_ERROR("Unknown property [PROP:%d:%s]\n", @@ -2635,6 +2687,12 @@ dw_hdmi_rockchip_get_property(struct drm_connector *connector, } else if (property == hdmi->user_split_mode_prop) { *val = hdmi->user_split_mode; return 0; + } else if (property == hdmi->allm_capacity) { + *val = !!(hdmi->add_func & SUPPORT_HDMI_ALLM); + return 0; + } else if (property == hdmi->allm_enable) { + *val = hdmi->enable_allm; + return 0; } DRM_ERROR("Unknown property [PROP:%d:%s]\n", diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 286a9db986c7..185f0387da81 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -620,7 +620,7 @@ void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane) static void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, - u8 *max_frl_rate_per_lane, u8 *max_lanes, + u8 *max_frl_rate_per_lane, u8 *max_lanes, u8 *add_func, const u8 *hf_vsdb) { u8 max_frl_rate; @@ -635,6 +635,8 @@ void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap, get_max_frl_rate(max_frl_rate, max_lanes, max_frl_rate_per_lane); + *add_func = hf_vsdb[8]; + if (cea_db_payload_len(hf_vsdb) < 13) return; @@ -844,13 +846,13 @@ void parse_next_hdr_block(struct next_hdr_sink_data *sink_data, } int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, - u8 *max_frl_rate_per_lane, u8 *max_lanes, + u8 *max_frl_rate_per_lane, u8 *max_lanes, u8 *add_func, const struct edid *edid) { const u8 *edid_ext; int i, start, end; - if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid) + if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid || !add_func) return -EINVAL; edid_ext = find_cea_extension(edid); @@ -865,7 +867,7 @@ int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, if (cea_db_is_hdmi_forum_vsdb(db)) parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane, - max_lanes, db); + max_lanes, add_func, db); } return 0; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index b19fcf3363fc..424273d61ae4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -486,7 +486,7 @@ uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info); int rockchip_drm_get_yuv422_format(struct drm_connector *connector, struct edid *edid); int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap, - u8 *max_frl_rate_per_lane, u8 *max_lanes, + u8 *max_frl_rate_per_lane, u8 *max_lanes, u8 *add_func, const struct edid *edid); int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data, const struct edid *edid); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 600c87339ecf..35b4889b01dc 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -78,6 +78,8 @@ struct platform_device; * +----------------------+----------------------------------+------------------------------+ */ +#define SUPPORT_HDMI_ALLM BIT(1) + enum { DW_HDMI_RES_8, DW_HDMI_RES_10, @@ -135,6 +137,7 @@ struct dw_hdmi_link_config { int frl_lanes; int rate_per_lane; int hcactive; + u8 add_func; u8 pps_payload[128]; }; @@ -165,7 +168,7 @@ struct dw_hdmi_qp_phy_ops { struct dw_hdmi_property_ops { void (*attach_properties)(struct drm_connector *connector, unsigned int color, int version, - void *data); + void *data, bool allm_en); void (*destroy_properties)(struct drm_connector *connector, void *data); int (*set_property)(struct drm_connector *connector, @@ -296,6 +299,7 @@ void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val); bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi); int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi); void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap); +void dw_hdmi_qp_set_allm_enable(struct dw_hdmi_qp *hdmi_qp, bool enable); void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi); struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,