diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 6a6cdc8b502c..4f1435d006b2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -216,7 +216,7 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) /* reset HDR related data */ edid_ctrl->hdr_supported = false; edid_ctrl->hdr_data.eotf = 0; - edid_ctrl->hdr_data.descriptor = 0; + edid_ctrl->hdr_data.metadata_type_one = false; edid_ctrl->hdr_data.max_luminance = 0; edid_ctrl->hdr_data.avg_luminance = 0; edid_ctrl->hdr_data.min_luminance = 0; @@ -794,7 +794,7 @@ static ssize_t hdmi_edid_sysfs_rda_hdr_data(struct device *dev, ret = scnprintf(buf, PAGE_SIZE, "%d, %u, %u, %u, %u, %u\n", edid_ctrl->hdr_supported, edid_ctrl->hdr_data.eotf, - edid_ctrl->hdr_data.descriptor, + edid_ctrl->hdr_data.metadata_type_one, edid_ctrl->hdr_data.max_luminance, edid_ctrl->hdr_data.avg_luminance, edid_ctrl->hdr_data.min_luminance); @@ -964,8 +964,8 @@ static void hdmi_edid_parse_hdrdb(struct hdmi_edid_ctrl *edid_ctrl, /* Byte 3: Electro-Optical Transfer Functions */ edid_ctrl->hdr_data.eotf = data_block[2] & 0x3F; - /* Byte 4: Static Metadata Descriptors */ - edid_ctrl->hdr_data.descriptor = data_block[3] & 0x1; + /* Byte 4: Static Metadata Descriptor Type 1 */ + edid_ctrl->hdr_data.metadata_type_one = (data_block[3] & 0x1) & BIT(0); /* Byte 5: Desired Content Maximum Luminance */ if (hdmi_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE)) @@ -2458,16 +2458,16 @@ u8 hdmi_edid_get_deep_color(void *input) * Return: HDR data. */ void hdmi_edid_get_hdr_data(void *input, - struct hdmi_edid_hdr_data *hdr_data) + struct hdmi_edid_hdr_data **hdr_data) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; - if (!edid_ctrl || !hdr_data) { + if (!edid_ctrl) { DEV_ERR("%s: invalid input\n", __func__); return; } - hdr_data = &edid_ctrl->hdr_data; + *hdr_data = &edid_ctrl->hdr_data; } bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode) diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index c818f3fc0d19..ce6cecbb2e03 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -31,14 +31,14 @@ struct hdmi_edid_init_data { /* * struct hdmi_edid_hdr_data - HDR Static Metadata * @eotf: Electro-Optical Transfer Function - * @descriptor: Static Metadata Descriptor + * @metadata_type_one: Static Metadata Type 1 support * @max_luminance: Desired Content Maximum Luminance * @avg_luminance: Desired Content Frame-average Luminance * @min_luminance: Desired Content Minimum Luminance */ struct hdmi_edid_hdr_data { u32 eotf; - u32 descriptor; + bool metadata_type_one; u32 max_luminance; u32 avg_luminance; u32 min_luminance; @@ -61,6 +61,6 @@ bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode); u8 hdmi_edid_get_deep_color(void *edid_ctrl); void hdmi_edid_get_hdr_data(void *edid_ctrl, - struct hdmi_edid_hdr_data *hdr_data); + struct hdmi_edid_hdr_data **hdr_data); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 3d773371713d..10e7a2d1a940 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -79,6 +79,8 @@ #define HDMI_TX_MAX_FPS 120000 #define HDMI_TX_VERSION_403 0x40000003 /* msmcobalt */ +#define HDMI_GET_MSB(x) (x >> 8) +#define HDMI_GET_LSB(x) (x & 0xff) /* Enable HDCP by default */ static bool hdcp_feature_on = true; @@ -111,6 +113,9 @@ static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl, enum hdmi_tx_power_module_type module, int enable); static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl); static void hdmi_tx_fps_work(struct work_struct *work); +static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, + enum hdmi_tx_power_module_type module, bool active); +static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *hdmi_ctrl); static struct mdss_hw hdmi_tx_hw = { .hw_ndx = MDSS_HW_HDMI, @@ -285,6 +290,29 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hdcp_ops; } +/* + * The sink must support at least one electro-optical transfer function for + * HDMI controller to sendi the dynamic range and mastering infoframe. + */ +static inline bool hdmi_tx_is_hdr_supported(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct hdmi_edid_hdr_data *hdr_data; + + hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data); + + return (hdr_data->eotf & BIT(0)) || (hdr_data->eotf & BIT(1)) || + (hdr_data->eotf & BIT(2)); +} + +static inline bool hdmi_tx_metadata_type_one(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct hdmi_edid_hdr_data *hdr_data; + + hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data); + + return hdr_data->metadata_type_one; +} + static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) { return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support && @@ -478,25 +506,30 @@ void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, } /* hdmi_tx_get_featuredata_from_sysfs_dev */ EXPORT_SYMBOL(hdmi_get_featuredata_from_sysfs_dev); -static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *hdmi_ctrl, bool enable) +static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *ctrl, bool enable) { - struct dss_module_power *pd = NULL; int ret = 0; + struct dss_module_power *pd = NULL; - if (!hdmi_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + DEV_ERR("%s: Invalid HDMI ctrl\n", __func__); ret = -EINVAL; goto end; } - pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM]; - if (!pd || !pd->gpio_config) { - DEV_ERR("%s: Error: invalid power data\n", __func__); - ret = -EINVAL; - goto end; + if (ctrl->hdmi_tx_version >= HDMI_TX_VERSION_403) + ret = hdmi_tx_pinctrl_set_state(ctrl, HDMI_TX_HPD_PM, enable); + else { + pd = &ctrl->pdata.power_data[HDMI_TX_HPD_PM]; + if (!pd || !pd->gpio_config) { + DEV_ERR("%s: Invalid power data\n", __func__); + ret = -EINVAL; + goto end; + } + + gpio_set_value(pd->gpio_config->gpio, enable); } - gpio_set_value(pd->gpio_config->gpio, enable); end: return ret; } @@ -1220,12 +1253,6 @@ static ssize_t hdmi_tx_sysfs_wta_5v(struct device *dev, } mutex_lock(&hdmi_ctrl->tx_lock); - pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM]; - if (!pd || !pd->gpio_config) { - DEV_ERR("%s: Error: invalid power data\n", __func__); - ret = -EINVAL; - goto end; - } ret = kstrtoint(buf, 10, &read); if (ret) { @@ -1245,6 +1272,72 @@ end: return ret; } +static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + u32 const hdr_param_count = 13; + struct hdmi_tx_ctrl *ctrl = NULL; + + ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev); + if (!ctrl) { + pr_err("%s: invalid input\n", __func__); + ret = -EINVAL; + goto end; + } + + if (!hdmi_tx_is_hdr_supported(ctrl)) { + pr_err("%s: Sink does not support HDR\n", __func__); + ret = -EINVAL; + goto end; + } + + if (sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u %u", + &ctrl->hdr_data.eotf, + &ctrl->hdr_data.display_primaries_x[0], + &ctrl->hdr_data.display_primaries_y[0], + &ctrl->hdr_data.display_primaries_x[1], + &ctrl->hdr_data.display_primaries_y[1], + &ctrl->hdr_data.display_primaries_x[2], + &ctrl->hdr_data.display_primaries_y[2], + &ctrl->hdr_data.white_point_x, + &ctrl->hdr_data.white_point_y, + &ctrl->hdr_data.max_luminance, + &ctrl->hdr_data.min_luminance, + &ctrl->hdr_data.max_content_light_level, + &ctrl->hdr_data.max_average_light_level) + != hdr_param_count) { + pr_err("%s: Invalid HDR stream data\n", __func__); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", + __func__, + ctrl->hdr_data.eotf, + ctrl->hdr_data.display_primaries_x[0], + ctrl->hdr_data.display_primaries_y[0], + ctrl->hdr_data.display_primaries_x[1], + ctrl->hdr_data.display_primaries_y[1], + ctrl->hdr_data.display_primaries_x[2], + ctrl->hdr_data.display_primaries_y[2]); + + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", + __func__, + ctrl->hdr_data.white_point_x, + ctrl->hdr_data.white_point_y, + ctrl->hdr_data.max_luminance, + ctrl->hdr_data.min_luminance, + ctrl->hdr_data.max_content_light_level, + ctrl->hdr_data.max_average_light_level); + + hdmi_panel_set_hdr_infoframe(ctrl); + + ret = strnlen(buf, PAGE_SIZE); +end: + return ret; +} + static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL); static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb); static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug); @@ -1265,6 +1358,7 @@ static DEVICE_ATTR(avi_cn0_1, S_IWUSR, NULL, hdmi_tx_sysfs_wta_avi_cn_bits); static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_s3d_mode, hdmi_tx_sysfs_wta_s3d_mode); static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v); +static DEVICE_ATTR(hdr_stream, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hdr_stream); static struct attribute *hdmi_tx_fs_attrs[] = { &dev_attr_connected.attr, @@ -1280,6 +1374,7 @@ static struct attribute *hdmi_tx_fs_attrs[] = { &dev_attr_avi_cn0_1.attr, &dev_attr_s3d_mode.attr, &dev_attr_5v.attr, + &dev_attr_hdr_stream.attr, NULL, }; static struct attribute_group hdmi_tx_fs_attrs_group = { @@ -2209,7 +2304,7 @@ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) DEV_DBG("%s: Features \n", __func__, hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON", - hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); + !hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); if (hdmi_disabled) { DEV_ERR("%s: HDMI disabled\n", __func__); @@ -2635,6 +2730,102 @@ static void hdmi_tx_phy_reset(struct hdmi_tx_ctrl *hdmi_ctrl) DSS_REG_W_ND(io, HDMI_PHY_CTRL, val | SW_RESET_PLL); } /* hdmi_tx_phy_reset */ +static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) +{ + u32 packet_payload = 0; + u32 packet_header = 0; + u32 packet_control = 0; + u32 const type_code = 0x87; + u32 const version = 0x01; + u32 const length = 0x1a; + u32 const descriptor_id = 0x00; + struct dss_io_data *io = NULL; + + if (!ctrl) { + pr_err("%s: invalid input\n", __func__); + return; + } + + if (!hdmi_tx_is_hdr_supported(ctrl)) { + pr_err("%s: Sink does not support HDR\n", __func__); + return; + } + + io = &ctrl->pdata.io[HDMI_TX_CORE_IO]; + if (!io->base) { + pr_err("%s: core io not inititalized\n", __func__); + return; + } + + /* Setup Packet header and payload */ + packet_header = type_code | (version << 8) | (length << 16); + DSS_REG_W(io, HDMI_GENERIC0_HDR, packet_header); + + packet_payload = (ctrl->hdr_data.eotf << 8); + if (hdmi_tx_metadata_type_one(ctrl)) { + packet_payload |= (descriptor_id << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[0]) + << 24); + DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload); + } else { + pr_debug("%s: Metadata Type 1 not supported\n", __func__); + DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload); + goto enable_packet_control; + } + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[0])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[0]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[0]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[1]) << 24); + DSS_REG_W(io, HDMI_GENERIC0_1, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[1])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[1]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[1]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[2]) << 24); + DSS_REG_W(io, HDMI_GENERIC0_2, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[2])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[2]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[2]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.white_point_x) << 24); + DSS_REG_W(io, HDMI_GENERIC0_3, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.white_point_x)) + | (HDMI_GET_LSB(ctrl->hdr_data.white_point_y) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.white_point_y) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.max_luminance) << 24); + DSS_REG_W(io, HDMI_GENERIC0_4, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.max_luminance)) + | (HDMI_GET_LSB(ctrl->hdr_data.min_luminance) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.min_luminance) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.max_content_light_level) << 24); + DSS_REG_W(io, HDMI_GENERIC0_5, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.max_content_light_level)) + | (HDMI_GET_LSB(ctrl->hdr_data.max_average_light_level) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.max_average_light_level) << 16); + DSS_REG_W(io, HDMI_GENERIC0_6, packet_payload); + +enable_packet_control: + /* + * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND + * Setup HDMI TX generic packet control + * Enable this packet to transmit every frame + * Enable HDMI TX engine to transmit Generic packet 1 + */ + packet_control = DSS_REG_R_ND(io, HDMI_GEN_PKT_CTRL); + packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16); + DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control); +} + static int hdmi_tx_audio_info_setup(struct platform_device *pdev, struct msm_ext_disp_audio_setup_params *params) { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 462edac31c09..1c306df70c7e 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -20,6 +20,7 @@ #include "mdss_hdmi_audio.h" #define MAX_SWITCH_NAME_SIZE 5 +#define HDR_PRIMARIES_COUNT 3 enum hdmi_tx_io_type { HDMI_TX_CORE_IO, @@ -61,6 +62,30 @@ struct hdmi_tx_pinctrl { struct hdmi_tx_ctrl; typedef int (*hdmi_tx_evt_handler) (struct hdmi_tx_ctrl *); +/* + * struct hdmi_tx_hdr_stream - HDR video stream characteristics + * @eotf: Electro-Optical Transfer Function + * @display_primaries_x: display primaries data for x-coordinate + * @display_primaries_y: display primaries data for y-coordinate + * @white_point_x: white point data for x-coordinate + * @white_point_y: white point data for y-coordinate + * @max_luminance: content maximum luminance + * @min_luminance: content minimum luminance + * @max_content_light_level: content maximum light level + * @max_average_light_level: content average light level + */ +struct hdmi_tx_hdr_stream_data { + u32 eotf; + u32 display_primaries_x[HDR_PRIMARIES_COUNT]; + u32 display_primaries_y[HDR_PRIMARIES_COUNT]; + u32 white_point_x; + u32 white_point_y; + u32 max_luminance; + u32 min_luminance; + u32 max_content_light_level; + u32 max_average_light_level; +}; + struct hdmi_tx_ctrl { struct platform_device *pdev; struct hdmi_tx_platform_data pdata; @@ -88,6 +113,7 @@ struct hdmi_tx_ctrl { struct hdmi_panel_ops panel_ops; struct msm_ext_disp_audio_setup_params audio_params; struct work_struct fps_work; + struct hdmi_tx_hdr_stream_data hdr_data; spinlock_t hpd_state_lock;