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

Commit 2178b8d8 authored by Ray Zhang's avatar Ray Zhang
Browse files

drm/msm: add PLL_ENABLE property to support clock recovery



PLL_ENABLE property is used to enable or disable the PLL
update function. With this property PLL update function
only works when PLL_ENABLE is set, and all changes done
to hardware will be discarded once PLL_ENABLE is cleared.

CRs-Fixed: 2042852
Change-Id: Ia321918382b8622101cff566049284810833f63e
Signed-off-by: default avatarRay Zhang <rayz@codeaurora.org>
parent 560a996d
Loading
Loading
Loading
Loading
+163 −9
Original line number Diff line number Diff line
@@ -705,13 +705,22 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)
	u64 clip_pclk;
	int rc = 0;

	mutex_lock(&display->display_lock);

	if (!hdmi->power_on || !display->connected) {
		SDE_ERROR("HDMI display is not ready\n");
		mutex_unlock(&display->display_lock);
		return -EINVAL;
	}

	if (!display->pll_update_enable) {
		SDE_ERROR("PLL update function is not enabled\n");
		mutex_unlock(&display->display_lock);
		return -EINVAL;
	}

	/* get current pclk */
	cur_pclk = hdmi->pixclock;
	cur_pclk = hdmi->actual_pixclock;
	/* get desired pclk */
	dst_pclk = cur_pclk * (1000000000 + ppm);
	do_div(dst_pclk, 1000000000);
@@ -725,13 +734,16 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm)

		rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk);
		if (rc < 0) {
			SDE_ERROR("PLL update failed, reset clock rate\n");
			SDE_ERROR("HDMI PLL update failed\n");
			mutex_unlock(&display->display_lock);
			return rc;
		}

		hdmi->pixclock = clip_pclk;
		hdmi->actual_pixclock = clip_pclk;
	}

	mutex_unlock(&display->display_lock);

	return rc;
}

@@ -767,14 +779,119 @@ static const struct file_operations pll_delta_fops = {
	.write = _sde_hdmi_debugfs_pll_delta_write,
};

/**
 * _sde_hdmi_enable_pll_update() - Enable the HDMI PLL update function
 *
 * @enable: non-zero to enable PLL update function, 0 to disable.
 * return: 0 on success, non-zero in case of failure.
 *
 */
static int _sde_hdmi_enable_pll_update(struct sde_hdmi *display, s32 enable)
{
	struct hdmi *hdmi = display->ctrl.ctrl;
	int rc = 0;

	mutex_lock(&display->display_lock);

	if (!hdmi->power_on || !display->connected) {
		SDE_ERROR("HDMI display is not ready\n");
		mutex_unlock(&display->display_lock);
		return -EINVAL;
	}

	if (!enable && hdmi->actual_pixclock != hdmi->pixclock) {
		/* reset pixel clock when disable */
		rc = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);
		if (rc < 0) {
			SDE_ERROR("reset clock rate failed\n");
			mutex_unlock(&display->display_lock);
			return rc;
		}
	}
	hdmi->actual_pixclock = hdmi->pixclock;

	display->pll_update_enable = !!enable;

	mutex_unlock(&display->display_lock);

	SDE_DEBUG("HDMI PLL update: %s\n",
			display->pll_update_enable ? "enable" : "disable");

	return rc;
}

static ssize_t _sde_hdmi_debugfs_pll_enable_read(struct file *file,
		char __user *buff, size_t count, loff_t *ppos)
{
	struct sde_hdmi *display = file->private_data;
	char *buf;
	u32 len = 0;

	if (!display)
		return -ENODEV;

	if (*ppos)
		return 0;

	buf = kzalloc(SZ_1K, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	len += snprintf(buf, SZ_4K, "%s\n",
			display->pll_update_enable ? "enable" : "disable");

	if (copy_to_user(buff, buf, len)) {
		kfree(buf);
		return -EFAULT;
	}

	*ppos += len;

	kfree(buf);
	return len;
}

static ssize_t _sde_hdmi_debugfs_pll_enable_write(struct file *file,
		const char __user *user_buf, size_t count, loff_t *ppos)
{
	struct sde_hdmi *display = file->private_data;
	char buf[10];
	int enable = 0;

	if (!display)
		return -ENODEV;

	if (count >= sizeof(buf))
		return -EFAULT;

	if (copy_from_user(buf, user_buf, count))
		return -EFAULT;

	buf[count] = 0; /* end of string */

	if (kstrtoint(buf, 0, &enable))
		return -EFAULT;

	_sde_hdmi_enable_pll_update(display, enable);

	return count;
}

static const struct file_operations pll_enable_fops = {
	.open = simple_open,
	.read = _sde_hdmi_debugfs_pll_enable_read,
	.write = _sde_hdmi_debugfs_pll_enable_write,
};

static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
{
	int rc = 0;
	struct dentry *dir, *dump_file, *edid_modes;
	struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info;
	struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file;
	struct dentry *edid_vcdb_info, *edid_vendor_name;
	struct dentry *src_hdcp14_support, *src_hdcp22_support;
	struct dentry *sink_hdcp22_support, *hdmi_hdcp_state;
	struct dentry *pll_delta_file, *pll_enable_file;

	dir = debugfs_create_dir(display->name, NULL);
	if (!dir) {
@@ -796,18 +913,30 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display)
		goto error_remove_dir;
	}

	pll_file = debugfs_create_file("pll_delta",
	pll_delta_file = debugfs_create_file("pll_delta",
					0644,
					dir,
					display,
					&pll_delta_fops);
	if (IS_ERR_OR_NULL(pll_file)) {
		rc = PTR_ERR(pll_file);
	if (IS_ERR_OR_NULL(pll_delta_file)) {
		rc = PTR_ERR(pll_delta_file);
		SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n",
		       display->name, rc);
		goto error_remove_dir;
	}

	pll_enable_file = debugfs_create_file("pll_enable",
					0644,
					dir,
					display,
					&pll_enable_fops);
	if (IS_ERR_OR_NULL(pll_enable_file)) {
		rc = PTR_ERR(pll_enable_file);
		SDE_ERROR("[%s]debugfs create pll_enable file failed, rc=%d\n",
		       display->name, rc);
		goto error_remove_dir;
	}

	edid_modes = debugfs_create_file("edid_modes",
					0444,
					dir,
@@ -1749,17 +1878,42 @@ int sde_hdmi_set_property(struct drm_connector *connector,
	if (!connector || !display) {
		SDE_ERROR("connector=%pK or display=%pK is NULL\n",
			connector, display);
		return 0;
		return -EINVAL;
	}

	SDE_DEBUG("\n");

	if (property_index == CONNECTOR_PROP_PLL_DELTA)
	if (property_index == CONNECTOR_PROP_PLL_ENABLE)
		rc = _sde_hdmi_enable_pll_update(display, value);
	else if (property_index == CONNECTOR_PROP_PLL_DELTA)
		rc = _sde_hdmi_update_pll_delta(display, value);

	return rc;
}

int sde_hdmi_get_property(struct drm_connector *connector,
	struct drm_connector_state *state,
	int property_index,
	uint64_t *value,
	void *display)
{
	struct sde_hdmi *hdmi_display = display;
	int rc = 0;

	if (!connector || !hdmi_display) {
		SDE_ERROR("connector=%pK or display=%pK is NULL\n",
			connector, hdmi_display);
		return -EINVAL;
	}

	mutex_lock(&hdmi_display->display_lock);
	if (property_index == CONNECTOR_PROP_PLL_ENABLE)
		*value = hdmi_display->pll_update_enable ? 1 : 0;
	mutex_unlock(&hdmi_display->display_lock);

	return rc;
}

u32 sde_hdmi_get_num_of_displays(void)
{
	u32 count = 0;
+18 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ enum hdmi_tx_feature_type {
 * @codec_ready:      If audio codec is ready.
 * @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.
 * @notifier:         CEC notifider to convey physical address information.
 * @root:             Debug fs root entry.
 */
@@ -159,6 +160,7 @@ struct sde_hdmi {

	struct irq_domain *irq_domain;
	struct cec_notifier *notifier;
	bool pll_update_enable;

	struct delayed_work hdcp_cb_work;
	struct dss_io_data io[HDMI_TX_MAX_IO];
@@ -344,6 +346,22 @@ int sde_hdmi_set_property(struct drm_connector *connector,
			uint64_t value,
			void *display);

/**
 * sde_hdmi_get_property() - get the connector properties
 * @connector:        Handle to the connector.
 * @state:            Handle to the connector state.
 * @property_index:   property index.
 * @value:            property value.
 * @display:          Handle to the display.
 *
 * Return: error code.
 */
int sde_hdmi_get_property(struct drm_connector *connector,
			struct drm_connector_state *state,
			int property_index,
			uint64_t *value,
			void *display);

/**
 * sde_hdmi_bridge_init() - init sde hdmi bridge
 * @hdmi:          Handle to the hdmi.
+10 −0
Original line number Diff line number Diff line
@@ -494,6 +494,16 @@ static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge)

static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge)
{
	struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge);
	struct hdmi *hdmi = sde_hdmi_bridge->hdmi;
	struct sde_connector *c_conn = to_sde_connector(hdmi->connector);
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;

	mutex_lock(&display->display_lock);

	display->pll_update_enable = false;

	mutex_unlock(&display->display_lock);
}

static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ struct hdmi {
	/* video state: */
	bool power_on;
	unsigned long int pixclock;
	unsigned long int actual_pixclock;

	void __iomem *mmio;
	void __iomem *qfprom_mmio;
+1 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ enum msm_mdp_conn_property {
	CONNECTOR_PROP_DST_W,
	CONNECTOR_PROP_DST_H,
	CONNECTOR_PROP_PLL_DELTA,
	CONNECTOR_PROP_PLL_ENABLE,

	/* enum/bitmask properties */
	CONNECTOR_PROP_TOPOLOGY_NAME,
Loading