summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRay Zhang <rayz@codeaurora.org>2017-06-13 15:54:33 +0800
committerRay Zhang <rayz@codeaurora.org>2017-06-22 09:08:28 +0800
commit2178b8d80dd09db67dae1eb5c2a17040764aeb87 (patch)
tree8aa63857f3135ba9211d48cbd113ada068e36aa4 /drivers
parent560a996da5c0bf398d2fba51e9909cd7323afea8 (diff)
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: Ray Zhang <rayz@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c172
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h18
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c10
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h1
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h1
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c1
7 files changed, 198 insertions, 9 deletions
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 64914585c9a5..d0c3deefabd7 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -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;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index 84d8720969be..47f440d617a3 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -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];
@@ -345,6 +347,22 @@ int sde_hdmi_set_property(struct drm_connector *connector,
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.
*
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index 7af84f5c4229..24d2320683e4 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -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)
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 84b578eaad47..8ca7b36ee0c8 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -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;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index adc1e021b6f7..077caedbffa9 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -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,
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 7538927a4993..e08fe210604c 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -582,6 +582,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
"PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0,
CONNECTOR_PROP_PLL_DELTA);
+ msm_property_install_volatile_range(&c_conn->property_info,
+ "PLL_ENABLE", 0x0, 0, 1, 0,
+ CONNECTOR_PROP_PLL_ENABLE);
+
/* enum/bitmask properties */
msm_property_install_enum(&c_conn->property_info, "topology_name",
DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 45a87456e5ec..a240ed08da89 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -601,6 +601,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.mode_valid = sde_hdmi_mode_valid,
.get_info = sde_hdmi_get_info,
.set_property = sde_hdmi_set_property,
+ .get_property = sde_hdmi_get_property,
};
struct msm_display_info info = {0};
struct drm_encoder *encoder;