msm: mdss: add multiple register support in ESD check

Current implementation cannot support multiple registers
in ESD check, and it does not work for many cases. For
example, some panels need check multiple registers to get
its status, and some panels might return several possible
values for one register read.

To support this kind of behaviors, a new property in dtsi
is added as "qcom,mdss-dsi-panel-status-valid-params" which
specifies the valid value length we should check in the
payload returned by panel, and the payload length panel
should return is specified by the property "qcom,mdss-dsi-
panel-status-read-length". "qcom,mdss-dsi-panel-status-value"
is also extended to an array which specifies all the possible
return values from panel.

Change-Id: I098d04281b819581f53c7c509778e7b594aa499a
Signed-off-by: Ray Zhang <rayz@codeaurora.org>
This commit is contained in:
Ray Zhang 2015-12-03 14:52:58 +08:00 committed by David Keitel
parent 37563b3b8a
commit 1f05cf89cf
4 changed files with 170 additions and 43 deletions

View file

@ -362,11 +362,18 @@ Optional properties:
"reg_read_nt35596" = Reads panel status register to check the panel
status for NT35596 panel.
"te_signal_check" = Uses TE signal behaviour to check the panel status
- qcom,mdss-dsi-panel-status-read-length: Integer value that specifies the expected read-back length of the
panel register.
- qcom,mdss-dsi-panel-status-value: An integer array that specifies the values of the panel status register
which is used to check the panel status. The size of this array
is specified by qcom,mdss-dsi-panel-status-read-length.
- qcom,mdss-dsi-panel-status-read-length: Integer array that specify the expected read-back length of values
for each of panel registers. Each length is corresponding to number of
returned parameters of register introduced in specification.
- qcom,mdss-dsi-panel-status-valid-params: Integer array that specify the valid returned values which need to check
for each of register.
Some panel need only check the first few values returned from panel.
So: if this property is the same to qcom,mdss-dsi-panel-status-read-length,
then just ignore this one.
- qcom,mdss-dsi-panel-status-value: Multiple integer arrays, each specifies the values of the panel status register
which is used to check the panel status. The size of each array is the sum of
length specified in qcom,mdss-dsi-panel-status-read-length, and must be equal.
This can cover that Some panel may return several alternative values.
- qcom,mdss-dsi-panel-max-error-count: Integer value that specifies the maximum number of errors from register
read that can be ignored before treating that the panel has gone bad.
- qcom,dynamic-mode-switch-enabled: Boolean used to mention whether panel supports

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -451,8 +451,11 @@ struct mdss_dsi_ctrl_pdata {
struct dsi_panel_cmds post_panel_on_cmds;
struct dsi_panel_cmds off_cmds;
struct dsi_panel_cmds status_cmds;
u32 status_cmds_rlen;
u32 *status_valid_params;
u32 *status_cmds_rlen;
u32 *status_value;
unsigned char *return_buf;
u32 groups; /* several alternative values to compare */
u32 status_error_count;
u32 max_status_error_count;

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -1018,22 +1018,39 @@ void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata)
static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
int i, rc, *lenp;
int start = 0;
struct dcs_cmd_req cmdreq;
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = ctrl->status_cmds.cmds;
cmdreq.cmds_cnt = ctrl->status_cmds.cmd_cnt;
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_RX;
cmdreq.rlen = ctrl->status_cmds_rlen;
cmdreq.cb = NULL;
cmdreq.rbuf = ctrl->status_buf.data;
rc = 1;
lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen;
if (ctrl->status_cmds.link_state == DSI_LP_MODE)
cmdreq.flags |= CMD_REQ_LP_MODE;
else if (ctrl->status_cmds.link_state == DSI_HS_MODE)
cmdreq.flags |= CMD_REQ_HS_MODE;
for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) {
memset(&cmdreq, 0, sizeof(cmdreq));
cmdreq.cmds = ctrl->status_cmds.cmds + i;
cmdreq.cmds_cnt = 1;
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_RX;
cmdreq.rlen = ctrl->status_cmds_rlen[i];
cmdreq.cb = NULL;
cmdreq.rbuf = ctrl->status_buf.data;
return mdss_dsi_cmdlist_put(ctrl, &cmdreq);
if (ctrl->status_cmds.link_state == DSI_LP_MODE)
cmdreq.flags |= CMD_REQ_LP_MODE;
else if (ctrl->status_cmds.link_state == DSI_HS_MODE)
cmdreq.flags |= CMD_REQ_HS_MODE;
rc = mdss_dsi_cmdlist_put(ctrl, &cmdreq);
if (rc <= 0) {
pr_err("%s: get status: fail\n", __func__);
return rc;
}
memcpy(ctrl->return_buf + start,
ctrl->status_buf.data, lenp[i]);
start += lenp[i];
}
return rc;
}

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -1328,10 +1328,35 @@ static int mdss_dsi_parse_reset_seq(struct device_node *np,
return 0;
}
static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl)
{
int i, j;
int len = 0, *lenp;
int group = 0;
lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen;
for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++)
len += lenp[i];
for (j = 0; j < ctrl->groups; ++j) {
for (i = 0; i < len; ++i) {
if (ctrl->return_buf[i] !=
ctrl->status_value[group + i])
break;
}
if (i == len)
return true;
group += len;
}
return false;
}
static int mdss_dsi_gen_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf,
ctrl_pdata->status_value, 0)) {
if (!mdss_dsi_cmp_panel_reg_v2(ctrl_pdata)) {
pr_err("%s: Read back value from panel is incorrect\n",
__func__);
return -EINVAL;
@ -1466,10 +1491,61 @@ exit:
return;
}
/* the length of all the valid values to be checked should not be great
* than the length of returned data from read command.
*/
static bool
mdss_dsi_parse_esd_check_valid_params(struct mdss_dsi_ctrl_pdata *ctrl)
{
int i;
for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) {
if (ctrl->status_valid_params[i] > ctrl->status_cmds_rlen[i]) {
pr_debug("%s: ignore valid params!\n", __func__);
return false;
}
}
return true;
}
static bool mdss_dsi_parse_esd_status_len(struct device_node *np,
char *prop_key, u32 **target, u32 cmd_cnt)
{
int tmp;
if (!of_find_property(np, prop_key, &tmp))
return false;
tmp /= sizeof(u32);
if (tmp != cmd_cnt) {
pr_err("%s: request property number(%d) not match command count(%d)\n",
__func__, tmp, cmd_cnt);
return false;
}
*target = kcalloc(tmp, sizeof(u32), GFP_KERNEL);
if (IS_ERR_OR_NULL(*target)) {
pr_err("%s: Error allocating memory for property\n",
__func__);
return false;
}
if (of_property_read_u32_array(np, prop_key, *target, tmp)) {
pr_err("%s: cannot get values from dts\n", __func__);
kfree(*target);
*target = NULL;
return false;
}
return true;
}
static void mdss_dsi_parse_esd_params(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 tmp;
u32 i, status_len, *lenp;
int rc;
struct property *data;
const char *string;
@ -1485,37 +1561,55 @@ static void mdss_dsi_parse_esd_params(struct device_node *np,
"qcom,mdss-dsi-panel-status-command",
"qcom,mdss-dsi-panel-status-command-state");
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-status-read-length",
&tmp);
ctrl->status_cmds_rlen = (!rc ? tmp : 1);
rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-max-error-count",
&tmp);
ctrl->max_status_error_count = (!rc ? tmp : 0);
ctrl->status_value = kzalloc(sizeof(u32) * ctrl->status_cmds_rlen,
GFP_KERNEL);
if (!ctrl->status_value) {
pr_err("%s: Error allocating memory for status buffer\n",
__func__);
if (!mdss_dsi_parse_esd_status_len(np,
"qcom,mdss-dsi-panel-status-read-length",
&ctrl->status_cmds_rlen, ctrl->status_cmds.cmd_cnt)) {
pinfo->esd_check_enabled = false;
return;
}
if (mdss_dsi_parse_esd_status_len(np,
"qcom,mdss-dsi-panel-status-valid-params",
&ctrl->status_valid_params, ctrl->status_cmds.cmd_cnt)) {
if (!mdss_dsi_parse_esd_check_valid_params(ctrl))
goto error1;
}
status_len = 0;
lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen;
for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i)
status_len += lenp[i];
data = of_find_property(np, "qcom,mdss-dsi-panel-status-value", &tmp);
tmp /= sizeof(u32);
if (!data || (tmp != ctrl->status_cmds_rlen)) {
pr_debug("%s: Panel status values not found\n", __func__);
memset(ctrl->status_value, 0, ctrl->status_cmds_rlen);
if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) {
ctrl->groups = tmp / status_len;
} else {
rc = of_property_read_u32_array(np,
"qcom,mdss-dsi-panel-status-value",
ctrl->status_value, tmp);
if (rc) {
pr_debug("%s: Error reading panel status values\n",
__func__);
memset(ctrl->status_value, 0, ctrl->status_cmds_rlen);
}
pr_err("%s: Error parse panel-status-value\n", __func__);
goto error1;
}
ctrl->status_value = kzalloc(sizeof(u32) * status_len * ctrl->groups,
GFP_KERNEL);
if (!ctrl->status_value)
goto error1;
ctrl->return_buf = kcalloc(status_len * ctrl->groups,
sizeof(unsigned char), GFP_KERNEL);
if (!ctrl->return_buf)
goto error2;
rc = of_property_read_u32_array(np,
"qcom,mdss-dsi-panel-status-value",
ctrl->status_value, ctrl->groups * status_len);
if (rc) {
pr_debug("%s: Error reading panel status values\n",
__func__);
memset(ctrl->status_value, 0, ctrl->groups * status_len);
}
ctrl->status_mode = ESD_MAX;
@ -1545,10 +1639,16 @@ static void mdss_dsi_parse_esd_params(struct device_node *np,
goto error;
}
}
return;
error:
kfree(ctrl->return_buf);
error2:
kfree(ctrl->status_value);
error1:
kfree(ctrl->status_valid_params);
kfree(ctrl->status_cmds_rlen);
pinfo->esd_check_enabled = false;
}