summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorAravind Venkateswaran <aravindh@codeaurora.org>2016-12-19 16:04:28 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2016-12-21 23:23:40 -0800
commit1c8ee2be72f6a8cb9a2ce20deb52ac1fb4577237 (patch)
tree1d9817b5b7c01c5764225a4434da99f182c0fd32 /drivers/video
parentc1f984bb5779b9ef2bc1780f47bb2cc67d362626 (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.c140
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c45
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