Merge "msm: mdss: refine the calculation of tmds clock rate"

This commit is contained in:
Linux Build Service Account 2016-11-25 17:46:55 -08:00 committed by Gerrit - the friendly Code Review server
commit c7690d6901
7 changed files with 205 additions and 133 deletions

View file

@ -134,6 +134,7 @@ struct hdmi_edid_ctrl {
u8 it_scan_info;
u8 ce_scan_info;
u8 cea_blks;
/* DC: MSB -> LSB: Y420_48|Y420_36|Y420_30|RGB48|RGB36|RGB30|Y444 */
u8 deep_color;
u16 physical_address;
u32 video_resolution; /* selected by user */
@ -858,6 +859,43 @@ static const u8 *hdmi_edid_find_block(const u8 *in_buf, u32 start_offset,
return NULL;
} /* hdmi_edid_find_block */
static const u8 *hdmi_edid_find_hfvsdb(const u8 *in_buf)
{
u8 len = 0, i = 0;
const u8 *vsd = NULL;
u32 vsd_offset = DBC_START_OFFSET;
u32 hf_ieee_oui = 0;
/* Find HF-VSDB with HF-OUI */
do {
vsd = hdmi_edid_find_block(in_buf, vsd_offset,
VENDOR_SPECIFIC_DATA_BLOCK, &len);
if (!vsd || !len || len > MAX_DATA_BLOCK_SIZE) {
if (i == 0)
pr_debug("%s: VSDB not found\n", __func__);
else
pr_debug("%s: no more VSDB found\n", __func__);
return NULL;
}
hf_ieee_oui = (vsd[1] << 16) | (vsd[2] << 8) | vsd[3];
if (hf_ieee_oui == HDMI_FORUM_IEEE_OUI) {
pr_debug("%s: found HF-VSDB\n", __func__);
break;
}
pr_debug("%s: Not a HF OUI 0x%x\n", __func__, hf_ieee_oui);
i++;
vsd_offset = vsd - in_buf + len + 1;
} while (1);
return vsd;
}
static void hdmi_edid_set_y420_support(struct hdmi_edid_ctrl *edid_ctrl,
u32 video_format)
{
@ -1251,62 +1289,32 @@ static void hdmi_edid_extract_speaker_allocation_data(
static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl,
const u8 *in_buf)
{
u8 len = 0, i = 0;
const u8 *vsd = NULL;
u32 vsd_offset = DBC_START_OFFSET;
u32 hf_ieee_oui = 0;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
pr_err("%s: invalid input\n", __func__);
return;
}
/* Find HF-VSDB with HF-OUI */
do {
vsd = hdmi_edid_find_block(in_buf, vsd_offset,
VENDOR_SPECIFIC_DATA_BLOCK, &len);
vsd = hdmi_edid_find_hfvsdb(in_buf);
if (!vsd || !len || len > MAX_DATA_BLOCK_SIZE) {
if (i == 0)
DEV_ERR("%s: VSDB not found\n", __func__);
else
DEV_DBG("%s: no more VSDB found\n", __func__);
break;
}
hf_ieee_oui = (vsd[1] << 16) | (vsd[2] << 8) | vsd[3];
if (hf_ieee_oui == HDMI_FORUM_IEEE_OUI) {
DEV_DBG("%s: found HF-VSDB\n", __func__);
break;
}
DEV_DBG("%s: Not a HF OUI 0x%x\n", __func__, hf_ieee_oui);
i++;
vsd_offset = vsd - in_buf + len + 1;
} while (1);
if (!vsd) {
DEV_DBG("%s: HF-VSDB not found\n", __func__);
return;
if (vsd) {
/* Max pixel clock is in multiples of 5Mhz. */
edid_ctrl->sink_caps.max_pclk_in_hz =
vsd[5]*5000000;
edid_ctrl->sink_caps.scdc_present =
(vsd[6] & 0x80) ? true : false;
edid_ctrl->sink_caps.scramble_support =
(vsd[6] & 0x08) ? true : false;
edid_ctrl->sink_caps.read_req_support =
(vsd[6] & 0x40) ? true : false;
edid_ctrl->sink_caps.osd_disparity =
(vsd[6] & 0x01) ? true : false;
edid_ctrl->sink_caps.dual_view_support =
(vsd[6] & 0x02) ? true : false;
edid_ctrl->sink_caps.ind_view_support =
(vsd[6] & 0x04) ? true : false;
}
/* Max pixel clock is in multiples of 5Mhz. */
edid_ctrl->sink_caps.max_pclk_in_hz =
vsd[5]*5000000;
edid_ctrl->sink_caps.scdc_present =
(vsd[6] & 0x80) ? true : false;
edid_ctrl->sink_caps.scramble_support =
(vsd[6] & 0x08) ? true : false;
edid_ctrl->sink_caps.read_req_support =
(vsd[6] & 0x40) ? true : false;
edid_ctrl->sink_caps.osd_disparity =
(vsd[6] & 0x01) ? true : false;
edid_ctrl->sink_caps.dual_view_support =
(vsd[6] & 0x02) ? true : false;
edid_ctrl->sink_caps.ind_view_support =
(vsd[6] & 0x04) ? true : false;
}
static void hdmi_edid_extract_latency_fields(struct hdmi_edid_ctrl *edid_ctrl,
@ -1404,12 +1412,19 @@ static void hdmi_edid_extract_dc(struct hdmi_edid_ctrl *edid_ctrl,
edid_ctrl->deep_color = (vsd[6] >> 0x3) & 0xF;
DEV_DBG("%s: deep color: Y444|RGB30|RGB36|RGB48: (%d|%d|%d|%d)\n",
__func__,
vsd = hdmi_edid_find_hfvsdb(in_buf);
if (vsd)
edid_ctrl->deep_color |= (vsd[7] & 0x07) << 4;
pr_debug("deep color: Y444|RGB30|RGB36|RGB48|Y420_30|Y420_36|Y420_48: (%d|%d|%d|%d|%d|%d|%d)\n",
(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);
(int) (edid_ctrl->deep_color & BIT(3)) >> 3,
(int) (edid_ctrl->deep_color & BIT(4)) >> 4,
(int) (edid_ctrl->deep_color & BIT(5)) >> 5,
(int) (edid_ctrl->deep_color & BIT(6)) >> 6);
}
static u32 hdmi_edid_check_header(const u8 *edid_buf)
@ -2398,8 +2413,8 @@ u32 hdmi_edid_get_sink_mode(void *input)
*
* 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.
* RGB 48 (BIT(3)), Y420_30 (BIT(4)), Y420_36 (BIT(5)), Y420_48 (BIT(6))
* is provided in a 8 bit integer. The MSB 8 bits are not used.
*
* Return: deep color data.
*/
@ -2415,6 +2430,25 @@ u8 hdmi_edid_get_deep_color(void *input)
return edid_ctrl->deep_color;
}
/**
* hdmi_edid_get_max_pclk() - get max pclk supported. Sink side's limitation
* should be concerned as well.
* @input: edid parser data
*
* Return: max pclk rate
*/
u32 hdmi_edid_get_max_pclk(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->init_data.max_pclk_khz;
}
/**
* hdmi_edid_get_hdr_data() - get the HDR capabiliies of the sink
* @input: edid parser data

View file

@ -60,6 +60,7 @@ 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);
u32 hdmi_edid_get_max_pclk(void *edid_ctrl);
void hdmi_edid_get_hdr_data(void *edid_ctrl,
struct hdmi_edid_hdr_data **hdr_data);

View file

@ -602,14 +602,51 @@ end:
return rc;
}
static int hdmi_panel_setup_dc(struct hdmi_panel *panel)
{
u32 hdmi_ctrl_reg;
u32 vbi_pkt_reg;
int rc = 0;
pr_debug("Deep Color: %s\n", panel->data->dc_enable ? "ON" : "OFF");
/* enable deep color if supported */
if (panel->data->dc_enable) {
hdmi_ctrl_reg = DSS_REG_R(panel->io, HDMI_CTRL);
/* GC CD override */
hdmi_ctrl_reg |= BIT(27);
/* enable deep color for RGB888 30 bits */
hdmi_ctrl_reg |= BIT(24);
DSS_REG_W(panel->io, HDMI_CTRL, hdmi_ctrl_reg);
/* 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(panel->io, HDMI_VBI_PKT_CTRL);
vbi_pkt_reg |= BIT(5) | BIT(4);
DSS_REG_W(panel->io, HDMI_VBI_PKT_CTRL, vbi_pkt_reg);
}
return rc;
}
static int hdmi_panel_setup_scrambler(struct hdmi_panel *panel)
{
int rc = 0;
int timeout_hsync;
u32 reg_val = 0;
u32 tmds_clock_ratio = 0;
u32 tmds_clock = 0;
bool scrambler_on = false;
struct msm_hdmi_mode_timing_info *timing = NULL;
struct mdss_panel_info *pinfo = NULL;
if (!panel) {
pr_err("invalid input\n");
@ -622,13 +659,22 @@ static int hdmi_panel_setup_scrambler(struct hdmi_panel *panel)
return -EINVAL;
}
pinfo = panel->data->pinfo;
if (!pinfo) {
pr_err("invalid panel data\n");
return -EINVAL;
}
/* Scrambling is supported from HDMI TX 4.0 */
if (panel->version < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) {
pr_debug("scrambling not supported by tx\n");
return 0;
}
if (timing->pixel_freq > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
tmds_clock = hdmi_tx_setup_tmds_clk_rate(timing->pixel_freq,
pinfo->out_format, panel->data->dc_enable);
if (tmds_clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
scrambler_on = true;
tmds_clock_ratio = 1;
} else {
@ -798,6 +844,12 @@ static int hdmi_panel_power_on(void *input)
pr_err("scrambler setup failed. rc=%d\n", rc);
goto err;
}
rc = hdmi_panel_setup_dc(panel);
if (rc) {
pr_err("Deep Color setup failed. rc=%d\n", rc);
goto err;
}
end:
panel->on = true;

View file

@ -28,6 +28,7 @@
* @infoframe: set to true if infoframes should be sent to sink
* @is_it_content: set to true if content is IT
* @scrambler: set to true if scrambler needs to be enabled
* @dc_enable: set to true if deep color is enabled
*/
struct hdmi_panel_data {
struct mdss_panel_info *pinfo;
@ -39,6 +40,7 @@ struct hdmi_panel_data {
bool infoframe;
bool is_it_content;
bool scrambler;
bool dc_enable;
};
/**

View file

@ -35,6 +35,7 @@
#include "mdss.h"
#include "mdss_panel.h"
#include "mdss_hdmi_mhl.h"
#include "mdss_hdmi_util.h"
#define DRV_NAME "hdmi-tx"
#define COMPATIBLE_NAME "qcom,hdmi-tx"
@ -58,13 +59,6 @@
#define AUDIO_POLL_SLEEP_US (5 * 1000)
#define AUDIO_POLL_TIMEOUT_US (AUDIO_POLL_SLEEP_US * 1000)
#define HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2
#define HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
#define HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
/* Maximum pixel clock rates for hdmi tx */
#define HDMI_DEFAULT_MAX_PCLK_RATE 148500
#define HDMI_TX_3_MAX_PCLK_RATE 297000
@ -111,7 +105,6 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data);
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
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);
@ -318,11 +311,29 @@ static inline bool hdmi_tx_metadata_type_one(struct hdmi_tx_ctrl *hdmi_ctrl)
return hdr_data->metadata_type_one;
}
static inline bool hdmix_tx_sink_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl)
{
void *edid_fd = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
if (hdmi_ctrl->panel_data.panel_info.out_format == MDP_Y_CBCR_H2V2)
return (hdmi_edid_get_deep_color(edid_fd) & BIT(4));
else
return (hdmi_edid_get_deep_color(edid_fd) & BIT(1));
}
static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support &&
(hdmi_edid_get_deep_color(
hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)) & BIT(1));
/* actual pixel clock if deep color is enabled */
void *edid_fd = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
u32 tmds_clk_with_dc = hdmi_tx_setup_tmds_clk_rate(
hdmi_ctrl->timing.pixel_freq,
hdmi_ctrl->panel.pinfo->out_format,
true);
return hdmi_ctrl->dc_feature_on &&
hdmi_ctrl->dc_support &&
hdmix_tx_sink_dc_support(hdmi_ctrl) &&
(tmds_clk_with_dc <= hdmi_edid_get_max_pclk(edid_fd));
}
static const char *hdmi_tx_pm_name(enum hdmi_tx_power_module_type module)
@ -349,7 +360,10 @@ static const char *hdmi_tx_io_name(u32 type)
static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
{
if (hdmi_ctrl && hdmi_ctrl->audio_ops.on) {
u32 pclk = hdmi_tx_setup_tmds_clk_rate(hdmi_ctrl);
u32 pclk = hdmi_tx_setup_tmds_clk_rate(
hdmi_ctrl->timing.pixel_freq,
hdmi_ctrl->panel.pinfo->out_format,
hdmi_ctrl->panel.dc_enable);
hdmi_ctrl->audio_ops.on(hdmi_ctrl->audio_data,
pclk, &hdmi_ctrl->audio_params);
@ -2345,7 +2359,6 @@ 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 hdmi_ctrl_reg = BIT(1);
u32 vbi_pkt_reg;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@ -2383,27 +2396,6 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
* longer be used
*/
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, hdmi_ctrl_reg);
@ -2973,44 +2965,6 @@ static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote)
return hpd;
}
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl)
{
u32 rate = 0;
struct msm_hdmi_mode_timing_info *timing = NULL;
u32 rate_ratio;
if (!hdmi_ctrl) {
DEV_ERR("%s: Bad input parameters\n", __func__);
goto end;
}
timing = &hdmi_ctrl->timing;
if (!timing) {
DEV_ERR("%s: Invalid timing info\n", __func__);
goto end;
}
switch (hdmi_ctrl->panel_data.panel_info.out_format) {
case MDP_Y_CBCR_H2V2:
rate_ratio = HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
case MDP_Y_CBCR_H2V1:
rate_ratio = HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
default:
rate_ratio = HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
}
rate = timing->pixel_freq / rate_ratio;
if (hdmi_tx_dc_support(hdmi_ctrl))
rate += rate >> 2;
end:
return rate;
}
static inline bool hdmi_tx_hw_is_cable_connected(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return DSS_REG_R(&hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO],
@ -3117,7 +3071,7 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl)
void *pdata = hdmi_tx_get_fd(HDMI_TX_FEAT_PANEL);
void *edata = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
hdmi_panel_get_vic(&panel_data->panel_info,
hdmi_ctrl->vic = hdmi_panel_get_vic(&panel_data->panel_info,
&hdmi_ctrl->ds_data);
if (hdmi_ctrl->vic <= 0) {
@ -3144,16 +3098,14 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl)
hdmi_ctrl->vic);
hdmi_ctrl->panel.scrambler = hdmi_edid_get_sink_scrambler_support(
edata);
hdmi_ctrl->panel.dc_enable = hdmi_tx_dc_support(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)
pixel_clk >>= 1;
else if (hdmi_tx_dc_support(hdmi_ctrl))
pixel_clk += pixel_clk >> 2;
pixel_clk = hdmi_tx_setup_tmds_clk_rate(hdmi_ctrl->timing.pixel_freq,
hdmi_ctrl->panel.pinfo->out_format,
hdmi_ctrl->panel.dc_enable) * 1000;
DEV_DBG("%s: setting pixel clk %d\n", __func__, pixel_clk);

View file

@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/msm_mdp.h>
#include "mdss_hdmi_util.h"
#define RESOLUTION_NAME_STR_LEN 30
@ -26,6 +27,10 @@
#define HDMI_SCDC_UNKNOWN_REGISTER "Unknown register"
#define HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2
#define HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
#define HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1
static char res_buf[RESOLUTION_NAME_STR_LEN];
enum trigger_mode {
@ -738,6 +743,30 @@ ssize_t hdmi_get_video_3d_fmt_2string(u32 format, char *buf, u32 size)
return len;
} /* hdmi_get_video_3d_fmt_2string */
int hdmi_tx_setup_tmds_clk_rate(u32 pixel_freq, u32 out_format, bool dc_enable)
{
u32 rate_ratio;
switch (out_format) {
case MDP_Y_CBCR_H2V2:
rate_ratio = HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
case MDP_Y_CBCR_H2V1:
rate_ratio = HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
default:
rate_ratio = HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;
break;
}
pixel_freq /= rate_ratio;
if (dc_enable)
pixel_freq += pixel_freq >> 2;
return pixel_freq;
}
static void hdmi_ddc_trigger(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
enum trigger_mode mode, bool seg)
{

View file

@ -16,6 +16,7 @@
#include "video/msm_hdmi_modes.h"
#include "mdss_panel.h"
#include "mdss_hdmi_panel.h"
/* HDMI_TX Registers */
#define HDMI_CTRL (0x00000000)
@ -495,6 +496,7 @@ bool hdmi_is_valid_resv_timing(int mode);
void hdmi_reset_resv_timing_info(void);
int hdmi_panel_get_vic(struct mdss_panel_info *pinfo,
struct hdmi_util_ds_data *ds_data);
int hdmi_tx_setup_tmds_clk_rate(u32 pixel_freq, u32 out_format, bool dc_enable);
/* todo: Fix this. Right now this is defined in mdss_hdmi_tx.c */
void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, u32 type);