summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c52
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c152
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h4
-rw-r--r--include/uapi/linux/msm_mdp.h5
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;