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>
This commit is contained in:
Aravind Venkateswaran 2016-12-19 16:04:28 -08:00 committed by Gerrit - the friendly Code Review server
parent c1f984bb57
commit 1c8ee2be72
3 changed files with 187 additions and 0 deletions

View file

@ -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,
};

View file

@ -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);

View file

@ -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