diff options
| author | Ajay Singh Parmar <aparmar@codeaurora.org> | 2016-02-10 15:06:51 -0800 |
|---|---|---|
| committer | Kyle Yan <kyan@codeaurora.org> | 2016-06-09 15:11:46 -0700 |
| commit | a7425ea0ed87b7829ecd4add8aa99ca0eda84751 (patch) | |
| tree | 41c2dd8bbcf74d38a29cdf2160c84acc2cc32bdf | |
| parent | 6883526d9325e3555b0acce54d8fa6966a485d3b (diff) | |
msm: mdss: hdmi: add deep color support
Get deep color support information from sink's EDID (Extended Data
Identification Data). Enable deep color output in hdmi transmitter
if the sink supports RGB 30bpp output format.
CRs-Fixed: 1022772
Change-Id: If65ac051253b4e51f6af5cd60f98eaf908b3bcfd
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
Signed-off-by: Tatenda Chipeperekwa <tatendac@codeaurora.org>
| -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; |
