diff options
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_edid.c | 52 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_edid.h | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 152 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.h | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.h | 4 | ||||
-rw-r--r-- | include/uapi/linux/msm_mdp.h | 5 |
6 files changed, 186 insertions, 29 deletions
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index ab2c9c0e501f..77d7c04e6f0e 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -126,6 +126,7 @@ struct hdmi_edid_ctrl { u8 it_scan_info; u8 ce_scan_info; u8 cea_blks; + u8 deep_color; u16 physical_address; u32 video_resolution; /* selected by user */ u32 sink_mode; /* HDMI or DVI */ @@ -1301,6 +1302,33 @@ static void hdmi_edid_extract_vendor_id(struct hdmi_edid_ctrl *edid_ctrl) vendor_id[3] = 0; } /* hdmi_edid_extract_vendor_id */ +static void hdmi_edid_extract_dc(struct hdmi_edid_ctrl *edid_ctrl, + const u8 *in_buf) +{ + u8 len; + const u8 *vsd = NULL; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, + VENDOR_SPECIFIC_DATA_BLOCK, &len); + + if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) + return; + + edid_ctrl->deep_color = (vsd[6] >> 0x3) & 0xF; + + DEV_DBG("%s: deep color: Y444|RGB30|RGB36|RGB48: (%d|%d|%d|%d)\n", + __func__, + (int) (edid_ctrl->deep_color & BIT(0)) >> 0, + (int) (edid_ctrl->deep_color & BIT(1)) >> 1, + (int) (edid_ctrl->deep_color & BIT(2)) >> 2, + (int) (edid_ctrl->deep_color & BIT(3)) >> 3); +} + static u32 hdmi_edid_check_header(const u8 *edid_buf) { return (edid_buf[0] == 0x00) && (edid_buf[1] == 0xff) @@ -2198,6 +2226,7 @@ int hdmi_edid_parser(void *input) hdmi_edid_extract_sink_caps(edid_ctrl, edid_buf); hdmi_edid_extract_latency_fields(edid_ctrl, edid_buf); + hdmi_edid_extract_dc(edid_ctrl, edid_buf); hdmi_edid_extract_speaker_allocation_data(edid_ctrl, edid_buf); hdmi_edid_extract_audio_data_blocks(edid_ctrl, edid_buf); hdmi_edid_extract_3d_present(edid_ctrl, edid_buf); @@ -2303,6 +2332,29 @@ u32 hdmi_edid_get_sink_mode(void *input) return sink_mode; } /* hdmi_edid_get_sink_mode */ +/** + * hdmi_edid_get_deep_color() - get deep color info supported by sink + * @input: edid parser data + * + * This API returns deep color for different formats supported by sink. + * Deep color support for Y444 (BIT(0)), RGB30 (BIT(1)), RGB36 (BIT(2), + * RGB 48 (BIT(3)) is provided in a 8 bit integer. The MSB 8 bits are + * not used. + * + * Return: deep color data. + */ +u8 hdmi_edid_get_deep_color(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return 0; + } + + return edid_ctrl->deep_color; +} + bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode) { int i; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 4dd92ed32364..7b4b2dccc802 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -43,5 +43,6 @@ void hdmi_edid_deinit(void *edid_ctrl); void *hdmi_edid_init(struct hdmi_edid_init_data *init_data); bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode); +u8 hdmi_edid_get_deep_color(void *edid_ctrl); #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 81770aaba1ad..09ece57e7909 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -283,6 +283,13 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hdcp_ops; } +static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + return hdmi_ctrl->dc_support && + (hdmi_edid_get_deep_color( + hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)) & BIT(1)); +} + static const char *hdmi_tx_pm_name(enum hdmi_tx_power_module_type module) { switch (module) { @@ -1994,6 +2001,7 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) { int status = 0; void *data; + struct dss_io_data *io; if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -2001,6 +2009,7 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) } data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); + io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; if (!hdmi_tx_is_controller_on(hdmi_ctrl)) { DEV_ERR("%s: failed: HDMI controller is off", __func__); @@ -2008,13 +2017,24 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) goto error; } + if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) { + DEV_ERR("%s: Failed to enable ddc power\n", __func__); + status = -EINVAL; + goto error; + } + + /* Enable SW DDC before EDID read */ + DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, + DSS_REG_R(io, HDMI_DDC_ARBITRATION) & ~(BIT(4))); + if (!hdmi_ctrl->custom_edid && !hdmi_ctrl->sim_mode) { hdmi_ddc_config(&hdmi_ctrl->ddc_ctrl); status = hdmi_tx_read_edid(hdmi_ctrl); if (status) { DEV_ERR("%s: error reading edid\n", __func__); - goto error; + status = -EINVAL; + goto bail; } } @@ -2024,7 +2044,9 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) if (status) DEV_ERR("%s: edid parse failed\n", __func__); } - +bail: + if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false)) + DEV_ERR("%s: Failed to disable ddc power\n", __func__); error: return status; } /* hdmi_tx_read_sink_info */ @@ -2065,17 +2087,49 @@ static void hdmi_tx_update_hdcp_info(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hdcp_ops = ops; } +static void hdmi_tx_update_deep_color(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct mdss_panel_info *pinfo; + u8 deep_color = hdmi_edid_get_deep_color( + hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)); + + pinfo = &hdmi_ctrl->panel_data.panel_info; + + pinfo->deep_color = 0; + hdmi_ctrl->dc_support = false; + pinfo->bpp = 24; + + if (deep_color & BIT(0)) + pinfo->deep_color |= MDP_DEEP_COLOR_YUV444; + + if (deep_color & BIT(1)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB30B; + hdmi_ctrl->dc_support = true; + pinfo->bpp = 30; + } + + if (deep_color & BIT(2)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB36B; + hdmi_ctrl->dc_support = true; + pinfo->bpp = 36; + } + + if (deep_color & BIT(3)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB48B; + hdmi_ctrl->dc_support = true; + pinfo->bpp = 48; + } +} + static void hdmi_tx_hpd_int_work(struct work_struct *work) { struct hdmi_tx_ctrl *hdmi_ctrl = NULL; - struct dss_io_data *io; hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, hpd_int_work); if (!hdmi_ctrl) { DEV_DBG("%s: invalid input\n", __func__); return; } - io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; mutex_lock(&hdmi_ctrl->tx_lock); @@ -2088,19 +2142,8 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) hdmi_ctrl->hpd_state ? "CONNECT" : "DISCONNECT"); if (hdmi_ctrl->hpd_state) { - if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) { - DEV_ERR("%s: Failed to enable ddc power\n", __func__); - goto end; - } - - /* Enable SW DDC before EDID read */ - DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION , - DSS_REG_R(io, HDMI_DDC_ARBITRATION) & ~(BIT(4))); - hdmi_tx_read_sink_info(hdmi_ctrl); - - if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false)) - DEV_ERR("%s: Failed to disable ddc power\n", __func__); + hdmi_tx_update_deep_color(hdmi_ctrl); hdmi_tx_send_cable_notification(hdmi_ctrl, true); } else { @@ -2177,12 +2220,14 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) { struct dss_io_data *io = NULL; /* Defaults: Disable block, HDMI mode */ - u32 reg_val = BIT(1); + u32 hdmi_ctrl_reg = BIT(1); + u32 vbi_pkt_reg; if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); return; } + io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; if (!io->base) { DEV_ERR("%s: Core io is not initialized\n", __func__); @@ -2191,7 +2236,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) if (power_on) { /* Enable the block */ - reg_val |= BIT(0); + hdmi_ctrl_reg |= BIT(0); /** * HDMI Encryption, if HDCP is enabled @@ -2202,24 +2247,45 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) if (hdmi_ctrl->hdmi_tx_ver < 4 && hdmi_tx_is_hdcp_enabled(hdmi_ctrl) && !hdmi_ctrl->pdata.primary) - reg_val |= BIT(2); + hdmi_ctrl_reg |= BIT(2); /* Set transmission mode to DVI based in EDID info */ if (!hdmi_edid_get_sink_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID))) - reg_val &= ~BIT(1); /* DVI mode */ + hdmi_ctrl_reg &= ~BIT(1); /* DVI mode */ /* * Use DATAPATH_MODE as 1 always, the new mode that also * supports scrambler and HDCP 2.2. The legacy mode should no * longer be used */ - reg_val |= BIT(31); + hdmi_ctrl_reg |= BIT(31); + + /* enable deep color if supported */ + if (hdmi_tx_dc_support(hdmi_ctrl)) { + /* GC CD override */ + hdmi_ctrl_reg |= BIT(27); + + /* enable deep color for RGB888 30 bits */ + hdmi_ctrl_reg |= BIT(24); + + /* Enable GC_CONT and GC_SEND in General Control Packet + * (GCP) register so that deep color data is + * transmitted to the sink on every frame, allowing + * the sink to decode the data correctly. + * + * GC_CONT: 0x1 - Send GCP on every frame + * GC_SEND: 0x1 - Enable GCP Transmission + */ + vbi_pkt_reg = DSS_REG_R(io, HDMI_VBI_PKT_CTRL); + vbi_pkt_reg |= BIT(5) | BIT(4); + DSS_REG_W(io, HDMI_VBI_PKT_CTRL, vbi_pkt_reg); + } } - DSS_REG_W(io, HDMI_CTRL, reg_val); + DSS_REG_W(io, HDMI_CTRL, hdmi_ctrl_reg); DEV_DBG("HDMI Core: %s, HDMI_CTRL=0x%08x\n", - power_on ? "Enable" : "Disable", reg_val); + power_on ? "Enable" : "Disable", hdmi_ctrl_reg); } /* hdmi_tx_set_mode */ static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, @@ -2227,7 +2293,6 @@ static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, { struct pinctrl_state *pin_state = NULL; int rc = -EFAULT; - struct dss_module_power *power_data = NULL; u64 cur_pin_states; if (!hdmi_ctrl) { @@ -2238,8 +2303,6 @@ static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, if (IS_ERR_OR_NULL(hdmi_ctrl->pin_res.pinctrl)) return 0; - power_data = &hdmi_ctrl->pdata.power_data[module]; - cur_pin_states = active ? (hdmi_ctrl->pdata.pin_states | BIT(module)) : (hdmi_ctrl->pdata.pin_states & ~BIT(module)); @@ -2861,6 +2924,7 @@ static int hdmi_tx_power_off(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_tx_core_off(hdmi_ctrl); hdmi_ctrl->panel_power_on = false; + hdmi_ctrl->dc_support = false; if (hdmi_ctrl->hpd_off_pending || hdmi_ctrl->panel_suspend) hdmi_tx_hpd_off(hdmi_ctrl); @@ -2876,7 +2940,7 @@ end: static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl) { int ret; - u32 div = 0; + u32 pixel_clk; struct mdss_panel_data *panel_data = &hdmi_ctrl->panel_data; void *pdata = hdmi_tx_get_fd(HDMI_TX_FEAT_PANEL); void *edata = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); @@ -2913,11 +2977,17 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl) if (hdmi_ctrl->panel_ops.on) hdmi_ctrl->panel_ops.on(pdata); + pixel_clk = hdmi_ctrl->timing.pixel_freq * 1000; + if (panel_data->panel_info.out_format == MDP_Y_CBCR_H2V2) - div = 1; + pixel_clk >>= 1; + else if (hdmi_tx_dc_support(hdmi_ctrl)) + pixel_clk += pixel_clk >> 2; + + DEV_DBG("%s: setting pixel clk %d\n", __func__, pixel_clk); hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM].clk_config[0].rate = - (hdmi_ctrl->timing.pixel_freq * 1000) >> div; + pixel_clk; hdmi_edid_set_video_resolution(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), hdmi_ctrl->vic, false); @@ -3659,6 +3729,29 @@ static int hdmi_tx_evt_handle_close(struct hdmi_tx_ctrl *hdmi_ctrl) return 0; } +static int hdmi_tx_evt_handle_deep_color(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + u32 deep_color = (int) (unsigned long) hdmi_ctrl->evt_arg; + struct mdss_panel_info *pinfo = &hdmi_ctrl->panel_data.panel_info; + + hdmi_ctrl->dc_support = true; + + if (deep_color & BIT(1)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB30B; + pinfo->bpp = 30; + } else if (deep_color & BIT(2)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB36B; + pinfo->bpp = 36; + } else if (deep_color & BIT(3)) { + pinfo->deep_color |= MDP_DEEP_COLOR_RGB48B; + pinfo->bpp = 48; + } else { + hdmi_ctrl->dc_support = false; + } + + return 0; +} + static int hdmi_tx_event_handler(struct mdss_panel_data *panel_data, int event, void *arg) { @@ -4310,6 +4403,7 @@ static int hdmi_tx_init_event_handler(struct hdmi_tx_ctrl *hdmi_ctrl) handler[MDSS_EVENT_BLANK] = hdmi_tx_evt_handle_blank; handler[MDSS_EVENT_PANEL_OFF] = hdmi_tx_evt_handle_panel_off; handler[MDSS_EVENT_CLOSE] = hdmi_tx_evt_handle_close; + handler[MDSS_EVENT_DEEP_COLOR] = hdmi_tx_evt_handle_deep_color; return 0; } diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 4654630f8cd4..a83b20d59836 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -124,6 +124,7 @@ struct hdmi_tx_ctrl { bool sim_mode; bool hdcp22_present; bool power_data_enable[HDMI_TX_MAX_PM]; + bool dc_support; void (*hdmi_tx_hpd_done)(void *data); void *downstream_data; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index d3f836032bb6..1b4d7d8c5f82 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -232,6 +232,8 @@ struct mdss_intf_recovery { * the panel. * @MDSS_EVENT_PANEL_TIMING_SWITCH: Panel timing switch is requested. * Argument provided is new panel timing. + * @MDSS_EVENT_DEEP_COLOR: Set deep color. + * Argument provided is bits per pixel (8/10/12) */ enum mdss_intf_events { MDSS_EVENT_RESET = 1, @@ -262,6 +264,7 @@ enum mdss_intf_events { MDSS_EVENT_DSI_RECONFIG_CMD, MDSS_EVENT_DSI_RESET_WRITE_PTR, MDSS_EVENT_PANEL_TIMING_SWITCH, + MDSS_EVENT_DEEP_COLOR, MDSS_EVENT_MAX, }; @@ -600,6 +603,7 @@ struct mdss_panel_info { u32 rst_seq[MDSS_DSI_RST_SEQ_LEN]; u32 rst_seq_len; u32 vic; /* video identification code */ + u32 deep_color; struct mdss_rect roi; int pwm_pmic_gpio; int pwm_lpg_chan; diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index 1c04668ffc98..a1237ed996a5 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -296,6 +296,11 @@ enum mdss_mdp_max_bw_mode { /* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */ #define MDP_NUM_FB_PAGE_PROTECTION_VALUES (5) +#define MDP_DEEP_COLOR_YUV444 0x1 +#define MDP_DEEP_COLOR_RGB30B 0x2 +#define MDP_DEEP_COLOR_RGB36B 0x4 +#define MDP_DEEP_COLOR_RGB48B 0x8 + struct mdp_rect { uint32_t x; uint32_t y; |