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 <algea.cao@rock-chips.com>
Change-Id: I0f5a6fa324c6727c37414f017b4839b3ec910206
This commit is contained in:
Algea Cao
2022-09-09 19:20:37 +08:00
committed by Tao Huang
parent 5ebe93e6bf
commit 1bf8b6d4b4
6 changed files with 164 additions and 30 deletions

View File

@@ -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");

View File

@@ -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)

View File

@@ -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",

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,