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

Commit a019f26a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm: add RGB 30-bit deep color support for HDMI"

parents a09e027f fcef1e04
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -556,13 +556,13 @@ static const struct file_operations sde_hdmi_hdcp_state_fops = {
	.read = _sde_hdmi_hdcp_state_read,
};

static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
static u64 _sde_hdmi_clip_valid_pclk(struct hdmi *hdmi, u64 pclk_in)
{
	u32 pclk_delta, pclk;
	u64 pclk_clip = pclk_in;

	/* as per standard, 0.5% of deviation is allowed */
	pclk = mode->clock * HDMI_KHZ_TO_HZ;
	pclk = hdmi->pixclock;
	pclk_delta = pclk * 5 / 1000;

	if (pclk_in < (pclk - pclk_delta))
@@ -700,7 +700,6 @@ static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work)
static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
{
	struct hdmi *hdmi = display->ctrl.ctrl;
	struct drm_display_mode *current_mode = &display->mode;
	u64 cur_pclk, dst_pclk;
	u64 clip_pclk;
	int rc = 0;
@@ -725,7 +724,7 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
	dst_pclk = cur_pclk * (1000000000 + ppm);
	do_div(dst_pclk, 1000000000);

	clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk);
	clip_pclk = _sde_hdmi_clip_valid_pclk(hdmi, dst_pclk);

	/* update pclk */
	if (clip_pclk != cur_pclk) {
@@ -2126,9 +2125,13 @@ static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi)
		}
	}

	SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__,
	if (sde_hdmi->hdmi_tx_major_version >= HDMI_TX_VERSION_4)
		sde_hdmi->dc_feature_supported = true;

	SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__,
			hdmi_disabled ? "OFF" : "ON",
			hdcp_disabled ? "OFF" : "ON");
			hdcp_disabled ? "OFF" : "ON",
			sde_hdmi->dc_feature_supported ? "ON" : "OFF");

	if (hdmi_disabled) {
		DEV_ERR("%s: HDMI disabled\n", __func__);
+9 −0
Original line number Diff line number Diff line
@@ -33,6 +33,9 @@
#include "sde_hdmi_util.h"
#include "sde_hdcp.h"

#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...)   SDE_ERROR(fmt, ##args)
#else
@@ -110,6 +113,8 @@ enum hdmi_tx_feature_type {
 * @client_notify_pending: If there is client notification pending.
 * @irq_domain:       IRQ domain structure.
 * @pll_update_enable: if it's allowed to update HDMI PLL ppm.
 * @dc_enable:        If deep color is enabled. Only DC_30 so far.
 * @dc_feature_supported: If deep color feature is supported.
 * @notifier:         CEC notifider to convey physical address information.
 * @root:             Debug fs root entry.
 */
@@ -161,6 +166,8 @@ struct sde_hdmi {
	struct irq_domain *irq_domain;
	struct cec_notifier *notifier;
	bool pll_update_enable;
	bool dc_enable;
	bool dc_feature_supported;

	struct delayed_work hdcp_cb_work;
	struct dss_io_data io[HDMI_TX_MAX_IO];
@@ -188,6 +195,8 @@ enum hdmi_tx_scdc_access_type {

#define HDMI_KHZ_TO_HZ 1000
#define HDMI_MHZ_TO_HZ 1000000
#define HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2
#define HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1

/* Maximum pixel clock rates for hdmi tx */
#define HDMI_DEFAULT_MAX_PCLK_RATE	148500
+85 −2
Original line number Diff line number Diff line
@@ -345,7 +345,9 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
		return 0;
	}

	if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) {
	/* use actual clock instead of mode clock */
	if (hdmi->pixclock >
		HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ * HDMI_KHZ_TO_HZ) {
		scrambler_on = true;
		tmds_clock_ratio = 1;
	} else {
@@ -402,6 +404,38 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
	return rc;
}

static void _sde_hdmi_bridge_setup_deep_color(struct hdmi *hdmi)
{
	struct drm_connector *connector = hdmi->connector;
	struct sde_connector *c_conn = to_sde_connector(connector);
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
	u32 hdmi_ctrl_reg, vbi_pkt_reg;

	SDE_DEBUG("Deep Color: %s\n", display->dc_enable ? "On" : "Off");

	if (display->dc_enable) {
		hdmi_ctrl_reg = hdmi_read(hdmi, REG_HDMI_CTRL);

		/* GC CD override */
		hdmi_ctrl_reg |= BIT(27);

		/* enable deep color for RGB888/YUV444/YUV420 30 bits */
		hdmi_ctrl_reg |= BIT(24);
		hdmi_write(hdmi, REG_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 = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL);
		vbi_pkt_reg |= BIT(5) | BIT(4);
		hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_reg);
	}
}

static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
{
	struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
@@ -660,18 +694,52 @@ static inline void _sde_hdmi_save_mode(struct hdmi *hdmi,
	drm_mode_copy(&display->mode, mode);
}

static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi,
	struct drm_display_mode *mode)
{
	/*
	 * choose priority:
	 * 1. DC + RGB
	 * 2. DC + YUV
	 * 3. RGB
	 * 4. YUV
	 */
	int dc_format;
	struct drm_connector *connector = hdmi->connector;

	dc_format = sde_hdmi_sink_dc_support(connector, mode);
	if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE)
		return (MSM_MODE_FLAG_COLOR_FORMAT_RGB444
			| MSM_MODE_FLAG_RGB444_DC_ENABLE);

	return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
}

static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
		 struct drm_display_mode *mode,
		 struct drm_display_mode *adjusted_mode)
{
	struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
	struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
	struct drm_connector *connector = hdmi->connector;
	struct sde_connector *c_conn = to_sde_connector(connector);
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
	int hstart, hend, vstart, vend;
	uint32_t frame_ctrl;

	mode = adjusted_mode;

	hdmi->pixclock = mode->clock * 1000;
	display->dc_enable =  mode->private_flags &
				(MSM_MODE_FLAG_RGB444_DC_ENABLE |
				 MSM_MODE_FLAG_YUV420_DC_ENABLE);

	/* compute pixclock as per color format and bit depth */
	hdmi->pixclock = sde_hdmi_calc_pixclk(
				mode->clock * HDMI_KHZ_TO_HZ,
				mode->private_flags,
				display->dc_enable);
	SDE_DEBUG("Actual PCLK: %lu, Mode PCLK: %d\n",
		hdmi->pixclock, mode->clock);

	hstart = mode->htotal - mode->hsync_start;
	hend   = mode->htotal - mode->hsync_start + mode->hdisplay;
@@ -734,6 +802,20 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,

	_sde_hdmi_save_mode(hdmi, mode);
	_sde_hdmi_bridge_setup_scrambler(hdmi, mode);
	_sde_hdmi_bridge_setup_deep_color(hdmi);
}

static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
	 const struct drm_display_mode *mode,
	 struct drm_display_mode *adjusted_mode)
{
	struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
	struct hdmi *hdmi = sde_hdmi_bridge->hdmi;

	adjusted_mode->private_flags |=
		_sde_hdmi_choose_best_format(hdmi, adjusted_mode);

	return true;
}

static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
@@ -742,6 +824,7 @@ static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
		.disable = _sde_hdmi_bridge_disable,
		.post_disable = _sde_hdmi_bridge_post_disable,
		.mode_set = _sde_hdmi_bridge_mode_set,
		.mode_fixup = _sde_hdmi_bridge_mode_fixup,
};


+73 −0
Original line number Diff line number Diff line
@@ -825,3 +825,76 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display)
	}
	return rc;
}

unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq,
	u32 out_format, bool dc_enable)
{
	u32 rate_ratio = HDMI_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO;

	if (out_format & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
		rate_ratio = HDMI_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO;

	pixel_freq /= rate_ratio;

	if (dc_enable)
		pixel_freq += pixel_freq >> 2;

	return pixel_freq;

}

bool sde_hdmi_validate_pixclk(struct drm_connector *connector,
	unsigned long pclk)
{
	struct sde_connector *c_conn = to_sde_connector(connector);
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
	unsigned long max_pclk = display->max_pclk_khz * HDMI_KHZ_TO_HZ;

	if (connector->max_tmds_char)
		max_pclk = MIN(max_pclk,
			connector->max_tmds_char * HDMI_MHZ_TO_HZ);
	else if (connector->max_tmds_clock)
		max_pclk = MIN(max_pclk,
			connector->max_tmds_clock * HDMI_MHZ_TO_HZ);

	SDE_DEBUG("MAX PCLK = %ld, PCLK = %ld\n", max_pclk, pclk);

	return pclk < max_pclk;
}

static bool sde_hdmi_check_dc_clock(struct drm_connector *connector,
	struct drm_display_mode *mode, u32 format)
{
	struct sde_connector *c_conn = to_sde_connector(connector);
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;

	 u32 tmds_clk_with_dc = sde_hdmi_calc_pixclk(
					mode->clock * HDMI_KHZ_TO_HZ,
					format,
					true);

	return (display->dc_feature_supported &&
		 sde_hdmi_validate_pixclk(connector, tmds_clk_with_dc));
}

int sde_hdmi_sink_dc_support(struct drm_connector *connector,
	struct drm_display_mode *mode)
{
	int dc_format = 0;

	if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV) &&
	    (connector->display_info.edid_hdmi_dc_modes
	     & DRM_EDID_YCBCR420_DC_30))
		if (sde_hdmi_check_dc_clock(connector, mode,
				MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420))
			dc_format |= MSM_MODE_FLAG_YUV420_DC_ENABLE;

	if ((mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB) &&
	    (connector->display_info.edid_hdmi_dc_modes
	     & DRM_EDID_HDMI_DC_30))
		if (sde_hdmi_check_dc_clock(connector, mode,
				MSM_MODE_FLAG_COLOR_FORMAT_RGB444))
			dc_format |= MSM_MODE_FLAG_RGB444_DC_ENABLE;

	return dc_format;
}
+6 −0
Original line number Diff line number Diff line
@@ -164,4 +164,10 @@ int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display);
void sde_hdmi_ddc_config(void *hdmi_display);
int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display);
void sde_hdmi_dump_regs(void *hdmi_display);
unsigned long sde_hdmi_calc_pixclk(unsigned long pixel_freq,
	u32 out_format, bool dc_enable);
bool sde_hdmi_validate_pixclk(struct drm_connector *connector,
	unsigned long pclk);
int sde_hdmi_sink_dc_support(struct drm_connector *connector,
	struct drm_display_mode *mode);
#endif /* _SDE_HDMI_UTIL_H_ */
Loading