Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 144221df authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

msm: mdss: hdmi: fix 4k@60 scrambling issue



Enable scrambling for all the pixel clocks where scrambling is mandatory.

Also, do not process multiple interrupts of same HDMI cable status as
this can lead to spin multiple threads, one modifying scrambling data
while other accessing it, resulting in wrong interpretation of scrambling
related data.

Change-Id: I31f80315a7d1b70bc6a0a84f5cd2990021bb8025
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent c049a981
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -989,43 +989,47 @@ 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;
	u8 len = 0, i = 0;
	const u8 *vsd = NULL;
	u32 vsd_offset = 0;
	u32 vsd_offset = DBC_START_OFFSET;
	u32 hf_ieee_oui = 0;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return;
	}

	/*
	 * Find the first vsdb block and then search for the 2nd vsdb block.
	 * HF-VSDB block should always follow the H14b vsdb
	 */
	vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET,
	/* Find HF-VSDB with HF-OUI */
	do {
		vsd = hdmi_edid_find_block(in_buf, vsd_offset,
			   VENDOR_SPECIFIC_DATA_BLOCK, &len);

	if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
		DEV_ERR("%s: EDID: No H14b VSDB present\n", __func__);
		return;
		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;
		}
	vsd_offset = vsd - in_buf;
	len = 0;

	vsd = hdmi_edid_find_block(in_buf, vsd_offset,
				   VENDOR_SPECIFIC_DATA_BLOCK, &len);
		hf_ieee_oui = (vsd[1] << 16) | (vsd[2] << 8) | vsd[3];

	if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) {
		DEV_ERR("%s: EDID: No HF VSDB present\n", __func__);
		return;
		if (hf_ieee_oui == HDMI_FORUM_IEEE_OUI) {
			DEV_DBG("%s: found HF-VSDB\n", __func__);
			break;
		}

	hf_ieee_oui = (vsd[1] << 16) | (vsd[2] << 8) | vsd[3];
	DEV_DBG("%s: HF IEEE OUI = 0x%x", __func__, hf_ieee_oui);
	if (hf_ieee_oui != HDMI_FORUM_IEEE_OUI) {
		DEV_ERR("%s: Not a HF OUI", __func__);
		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;
	}

	/* Max pixel clock is in  multiples of 5Mhz. */
	edid_ctrl->sink_caps.max_pclk_in_hz =
			vsd[5]*5000000;
+74 −70
Original line number Diff line number Diff line
@@ -2968,23 +2968,21 @@ static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
	DEV_INFO("HDMI Audio: Disabled\n");
} /* hdmi_tx_audio_off */

static int hdmi_tx_setup_tmds_clk_ratio(struct hdmi_tx_ctrl *hdmi_ctrl)
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl)
{
	int rc = 0;
	u32 rate = 0;
	struct msm_hdmi_mode_timing_info *timing = NULL;
	u32 tmds_clock_ratio = 0;
	u32 rate_ratio;

	if (!hdmi_ctrl) {
		DEV_ERR("%s: Bad input parameters\n", __func__);
		return -EINVAL;
		goto end;
	}

	timing = &hdmi_ctrl->vid_cfg.timing;
	if (!timing) {
		DEV_ERR("%s: Invalid timing info\n", __func__);
		return -EINVAL;
		goto end;
	}

	switch (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format) {
@@ -3001,16 +2999,8 @@ static int hdmi_tx_setup_tmds_clk_ratio(struct hdmi_tx_ctrl *hdmi_ctrl)

	rate = timing->pixel_freq / rate_ratio;

	if (rate > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
		DEV_DBG("%s: TMDS CLOCK PERIOD RATIO: 1\n", __func__);
		tmds_clock_ratio = 1;
	}

	rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
			     HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
			     tmds_clock_ratio);

	return rc;
end:
	return rate;
}

int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
@@ -3018,9 +3008,11 @@ int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
	int rc = 0;
	u32 rate = 0;
	u32 reg_val = 0;
	u32 tmds_clock_ratio = 0;
	bool scrambler_on = false;
	struct dss_io_data *io = NULL;
	struct msm_hdmi_mode_timing_info *timing = NULL;
	void *edid_data = NULL;

	if (!hdmi_ctrl) {
		DEV_ERR("%s: Bad input parameters\n", __func__);
@@ -3033,6 +3025,8 @@ int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
		return -EINVAL;
	}

	edid_data = hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID];

	timing = &hdmi_ctrl->vid_cfg.timing;
	if (!timing) {
		DEV_ERR("%s: Invalid timing info\n", __func__);
@@ -3045,35 +3039,31 @@ int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
		return 0;
	}

	if (!hdmi_edid_get_scdc_support(
			hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])) {
		DEV_DBG("%s: HDMI TX does not support scdc\n", __func__);
		return 0;
	}
	switch (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format) {
	case MDP_Y_CBCR_H2V2:
		rate = timing->pixel_freq /
			HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;
		break;
	case MDP_Y_CBCR_H2V1:
		rate = timing->pixel_freq /
			HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO;
		break;
	default:
		rate = timing->pixel_freq /
			HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;
		break;
	}
	if (!hdmi_edid_get_scdc_support(edid_data))
		DEV_WARN("%s: sink didn't provide scdc support\n", __func__);

	rate = hdmi_tx_setup_tmds_clk_rate(hdmi_ctrl);

	if (rate > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
		scrambler_on = true;
		tmds_clock_ratio = 1;
	} else {
		if (hdmi_edid_get_sink_scrambler_support(
				hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]))
		if (hdmi_edid_get_sink_scrambler_support(edid_data))
			scrambler_on = true;
	}

	if (scrambler_on) {
		u64 mult;
		u64 div;

		rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
			HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE,
			tmds_clock_ratio);
		if (rc) {
			DEV_ERR("%s: TMDS CLK RATIO ERR\n", __func__);
			return rc;
		}

		reg_val = DSS_REG_R(io, HDMI_CTRL);
		reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */
		reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */
@@ -3081,28 +3071,20 @@ int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
		DSS_REG_W(io, HDMI_CTRL, reg_val);

		rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
				     HDMI_TX_SCDC_SCRAMBLING_ENABLE,
				     0x1);
			HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x1);
		if (!rc) {
			hdmi_ctrl->scrambler_enabled = true;
		} else {
		rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
				     HDMI_TX_SCDC_SCRAMBLING_ENABLE,
				     0x0);
	}

	if (rc) {
		DEV_ERR("%s: SCDC write failed\n", __func__);
			DEV_ERR("%s: failed to enable scrambling\n",
				__func__);
			return rc;
		}

		/*
	 * Setup hardware to periodically check for scrambler status bit on the
	 * sink. Sink should set this bit with in 200ms after scrambler is
	 * enabled.
		 * Setup hardware to periodically check for scrambler
		 * status bit on the sink. Sink should set this bit
		 * with in 200ms after scrambler is enabled.
		 */
	if (scrambler_on) {
		u64 mult;
		u64 div;
		/* calculate number of lines sent in 200ms */
		mult = HDMI_TX_SCRAMBLER_TIMEOUT_USEC *
			((u64)timing->pixel_freq * HDMI_TX_KHZ_TO_HZ);
		div = hdmi_tx_get_v_total(timing) * HDMI_TX_MHZ_TO_HZ;
@@ -3112,8 +3094,14 @@ int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl)
			mult = 0;

		rc = hdmi_setup_ddc_timers(&hdmi_ctrl->ddc_ctrl,
					   HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS,
					   (u32)mult);
			HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, (u32)mult);
	} else {
		if (hdmi_ctrl->scrambler_enabled) {
			hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl,
				HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0);

			hdmi_ctrl->scrambler_enabled = false;
		}
	}

	return rc;
@@ -3311,15 +3299,7 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data)
			goto end;
		}
	}
	if (hdmi_edid_get_scdc_support(
			hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])) {

		rc = hdmi_tx_setup_tmds_clk_ratio(hdmi_ctrl);
		if (rc) {
			DEV_ERR("%s: failed to set TMDS CLK RATIO\n", __func__);
			return rc;
		}
	}
	rc = hdmi_tx_core_on(hdmi_ctrl);
	if (rc) {
		DEV_ERR("%s: hdmi_msm_core_on failed\n", __func__);
@@ -3555,25 +3535,49 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data)
	struct dss_io_data *io = NULL;
	struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)data;
	unsigned long flags;
	u32 hpd_current_state;
	u32 reg_val = 0;

	if (!hdmi_ctrl) {
		DEV_WARN("%s: invalid input data, ISR ignored\n", __func__);
		return IRQ_HANDLED;
		goto end;
	}

	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
	if (!io->base) {
		DEV_WARN("%s: core io not initialized, ISR ignored\n",
			__func__);
		return IRQ_HANDLED;
		goto end;
	}

	if (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(0)) {
		spin_lock_irqsave(&hdmi_ctrl->hpd_state_lock, flags);
		hpd_current_state = hdmi_ctrl->hpd_state;
		hdmi_ctrl->hpd_state =
			(DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
		spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags);

		/*
		 * check if this is a spurious interrupt, if yes, reset
		 * interrupts and return
		 */
		if (hpd_current_state == hdmi_ctrl->hpd_state) {
			DEV_DBG("%s: spurious interrupt %d\n", __func__,
				hpd_current_state);

			/* enable interrupts */
			reg_val |= BIT(2);

			/* set polarity, reverse of current state */
			reg_val |= (~hpd_current_state << 1) & BIT(1);

			/* ack interrupt */
			reg_val |= BIT(0);

			DSS_REG_W(io, HDMI_HPD_INT_CTRL, reg_val);
			goto end;
		}

		/*
		 * Ack the current hpd interrupt and stop listening to
		 * new hpd interrupt.
@@ -3602,7 +3606,7 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data)
		if (hdmi_ctrl->hdcp_ops->hdmi_hdcp_isr(
					hdmi_ctrl->hdcp_feature_data))
			DEV_ERR("%s: hdmi_hdcp_isr failed\n", __func__);

end:
	return IRQ_HANDLED;
} /* hdmi_tx_isr */

+1 −0
Original line number Diff line number Diff line
@@ -170,6 +170,7 @@ struct hdmi_tx_ctrl {
	bool hdcp_feature_on;
	bool hpd_disabled;
	bool ds_registered;
	bool scrambler_enabled;
	u32 hdcp14_present;
	bool audio_ack_enabled;
	atomic_t audio_ack_pending;