diff options
| author | Ray Zhang <rayz@codeaurora.org> | 2017-06-13 15:54:33 +0800 |
|---|---|---|
| committer | Ray Zhang <rayz@codeaurora.org> | 2017-06-22 09:08:28 +0800 |
| commit | 2178b8d80dd09db67dae1eb5c2a17040764aeb87 (patch) | |
| tree | 8aa63857f3135ba9211d48cbd113ada068e36aa4 /drivers | |
| parent | 560a996da5c0bf398d2fba51e9909cd7323afea8 (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.c | 172 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 18 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c | 10 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi/hdmi.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_connector.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_kms.c | 1 |
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; |
