diff options
| author | Aravind Venkateswaran <aravindh@codeaurora.org> | 2016-12-19 16:04:28 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-12-21 23:23:40 -0800 |
| commit | 1c8ee2be72f6a8cb9a2ce20deb52ac1fb4577237 (patch) | |
| tree | 1d9817b5b7c01c5764225a4434da99f182c0fd32 /drivers/video | |
| parent | c1f984bb5779b9ef2bc1780f47bb2cc67d362626 (diff) | |
msm: mdss: dp: add support for downstream device power management
Implement the necessary programming sequence to configure the
uPacket RX of a connected downstream device in power save mode.
Add a new sysfs node to trigger the configuration as follows:
To enter power save mode:
* echo 1 > /sys/class/graphics/<fbi>/psm
To exit power save mode:
* echo 0 > /sys/class/graphics/<fbi>/psm
where fbi is the framebuffer node corresponding to the display
port device.
CRs-Fixed: 1076516
Change-Id: I306ff4451d56dfa7edcff93fe26842ae9af71b69
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
Diffstat (limited to 'drivers/video')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.c | 140 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.h | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_aux.c | 45 |
3 files changed, 187 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index f81226b6fe15..cd7471b1cd8e 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1235,6 +1235,15 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) dp_drv->power_on = true; + if (dp_drv->psm_enabled) { + ret = mdss_dp_aux_send_psm_request(dp_drv, false); + if (ret) { + pr_err("Failed to exit low power mode, rc=%d\n", + ret); + goto exit; + } + } + ret = mdss_dp_train_main_link(dp_drv); mutex_unlock(&dp_drv->train_mutex); @@ -1302,6 +1311,15 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_configure_source_params(dp_drv, &ln_map); + if (dp_drv->psm_enabled) { + ret = mdss_dp_aux_send_psm_request(dp_drv, false); + if (ret) { + pr_err("Failed to exit low power mode, rc=%d\n", ret); + goto exit; + } + } + + link_training: dp_drv->power_on = true; @@ -1869,6 +1887,124 @@ static ssize_t mdss_dp_sysfs_rda_s3d_mode(struct device *dev, return ret; } +static bool mdss_dp_is_test_ongoing(struct mdss_dp_drv_pdata *dp) +{ + return dp->hpd_irq_clients_notified; +} + +/** + * mdss_dp_psm_config() - Downstream device uPacket RX Power Management + * @dp: Display Port Driver data + * + * Perform required steps to configure the uPacket RX of a downstream + * connected device in a power-save mode. + */ +static int mdss_dp_psm_config(struct mdss_dp_drv_pdata *dp, bool enable) +{ + int ret = 0; + + if (!dp) { + pr_err("invalid data\n"); + return -EINVAL; + } + + if (dp->psm_enabled == enable) { + pr_debug("No change in psm requested\n"); + goto end; + } + + pr_debug("Power save mode %s requested\n", enable ? "entry" : "exit"); + + if (enable) { + ret = mdss_dp_aux_send_psm_request(dp, true); + if (ret) + goto end; + + /* + * If this configuration is requested as part of an + * automated test, then HPD notification has already been + * sent out. Just disable the main-link and turn off DP Tx. + * + * Otherwise, trigger a complete shutdown of the pipeline. + */ + if (mdss_dp_is_test_ongoing(dp)) { + mdss_dp_mainlink_push_idle(&dp->panel_data); + mdss_dp_off_irq(dp); + } else { + mdss_dp_notify_clients(dp, false); + } + } else { + /* + * If this configuration is requested as part of an + * automated test, then just perform a link retraining. + * + * Otherwise, re-initialize the host and setup the complete + * pipeline from scratch by sending a connection notification + * to user modules. + */ + if (mdss_dp_is_test_ongoing(dp)) { + mdss_dp_link_retraining(dp); + } else { + mdss_dp_host_init(&dp->panel_data); + mdss_dp_notify_clients(dp, true); + } + } + +end: + pr_debug("Power save mode %s %s\n", + dp->psm_enabled ? "entry" : "exit", + ret ? "failed" : "successful"); + + return ret; +} + +static ssize_t mdss_dp_wta_psm(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int psm; + int rc; + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid data\n"); + ret = -EINVAL; + goto end; + } + + rc = kstrtoint(buf, 10, &psm); + if (rc) { + pr_err("kstrtoint failed. ret=%d\n", (int)ret); + goto end; + } + + rc = mdss_dp_psm_config(dp, psm ? true : false); + if (rc) { + pr_err("failed to config Power Save Mode\n"); + goto end; + } + +end: + return ret; +} + +static ssize_t mdss_dp_rda_psm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->psm_enabled ? 1 : 0); + pr_debug("psm: %s\n", dp->psm_enabled ? "enabled" : "disabled"); + + return ret; +} + static ssize_t mdss_dp_wta_hpd(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1927,11 +2063,15 @@ static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, mdss_dp_sysfs_rda_s3d_mode, mdss_dp_sysfs_wta_s3d_mode); static DEVICE_ATTR(hpd, S_IRUGO | S_IWUSR, mdss_dp_rda_hpd, mdss_dp_wta_hpd); +static DEVICE_ATTR(psm, S_IRUGO | S_IWUSR, mdss_dp_rda_psm, + mdss_dp_wta_psm); + static struct attribute *mdss_dp_fs_attrs[] = { &dev_attr_connected.attr, &dev_attr_s3d_mode.attr, &dev_attr_hpd.attr, + &dev_attr_psm.attr, NULL, }; diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 9b1fb7fc3dcd..5ab26b466765 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -401,6 +401,7 @@ struct mdss_dp_drv_pdata { bool power_on; bool sink_info_read; bool hpd; + bool psm_enabled; /* dp specific */ unsigned char *base; @@ -683,6 +684,7 @@ void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up); void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep); char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt); int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state); +int mdss_dp_aux_send_psm_request(struct mdss_dp_drv_pdata *dp, bool enable); void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *ep); void *mdss_dp_get_hdcp_data(struct device *dev); int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index efa343315a3c..fb88fb8fc335 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -1026,6 +1026,51 @@ int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len) return len; } +/* + * mdss_dp_aux_send_psm_request() - sends a power save mode messge to sink + * @dp: Display Port Driver data + */ +int mdss_dp_aux_send_psm_request(struct mdss_dp_drv_pdata *dp, bool enable) +{ + u8 psm_request[4]; + int rc = 0; + + psm_request[0] = enable ? 2 : 1; + + pr_debug("sending psm %s request\n", enable ? "entry" : "exit"); + if (enable) { + dp_aux_write_buf(dp, 0x600, psm_request, 1, 0); + } else { + ktime_t timeout = ktime_add_ms(ktime_get(), 20); + + /* + * It could take up to 1ms (20 ms of embedded sinks) till + * the sink is ready to reply to this AUX transaction. It is + * expected that the source keep retrying periodically during + * this time. + */ + for (;;) { + rc = dp_aux_write_buf(dp, 0x600, psm_request, 1, 0); + if ((rc >= 0) || + (ktime_compare(ktime_get(), timeout) > 0)) + break; + usleep_range(100, 120); + } + + /* + * if the aux transmission succeeded, then the function would + * return the number of bytes transmitted. + */ + if (rc > 0) + rc = 0; + } + + if (!rc) + dp->psm_enabled = enable; + + return rc; +} + /** * mdss_dp_aux_send_test_response() - sends a test response to the sink * @dp: Display Port Driver data |
