mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
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:
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user