input: misc: hbtp_input: Add support for multiple AFE
Add support for both HI04 and JDI/Rohm with DDIC synchronization. Change-Id: Ib6bddd5f43b41660304d240db39f4e273ede3af0 Signed-off-by: Alex Sarraf <asarraf@codeaurora.org>
This commit is contained in:
parent
c6b7d9674c
commit
c38f93d110
3 changed files with 711 additions and 16 deletions
|
@ -20,6 +20,19 @@ Optional properties:
|
|||
- qcom,dig-vtg-max : Digital supply maximum voltage in uV
|
||||
- qcom,display-resolution : Display resolution - maxX, maxY
|
||||
- qcom,use-scale : boolean, enables the scaling for touch coordinates
|
||||
- pinctrl-names : defines pinctrl names
|
||||
"pmx_ts_active" : Required pinctrl name.
|
||||
This should specify active config of TS RST gpio
|
||||
"pmx_ts_suspend" : Required pinctrl name
|
||||
This should specify suspend config of TS RST gpio
|
||||
"ddic_rst_active" : Required pinctrl name
|
||||
This should specify active config of DDIC RST gpio
|
||||
"ddic_rst_suspend" : Required pinctrl name
|
||||
This should specify suspend config of DDIC RST gpio
|
||||
- pinctrl-0 : pin control to be used for TS active config
|
||||
- pinctrl-1 : pin control to be used for TS suspend config
|
||||
- pinctrl-2 : pin control to be used for DDIC active config
|
||||
- pinctrl-3 : pin control to be used for DDIC suspend config
|
||||
|
||||
Optional properties if qcom,use-scale DT property is defined:
|
||||
- qcom,def-maxx : default X-resolution of the touch panel.
|
||||
|
@ -29,6 +42,20 @@ Optional properties if qcom,use-scale DT property is defined:
|
|||
- qcom,des-maxy : desired Y-resolution of the touch panel.
|
||||
(Above two properties should be defined in pairs only)
|
||||
|
||||
Optional Properties if pinctrl names are defined:
|
||||
- qcom,pmx-ts-on-seq-delay-us : unsigned integer type for
|
||||
delay after active TS RST gpio is changed
|
||||
- qcom,fb-resume-delay-us : unsigned integer type for
|
||||
delay in early resume framebuffer callback
|
||||
- qcom,ddic-rst-on-seq-delay-us : array of unsigned integer type for
|
||||
delay of each step in series of DDIC RST gpio control
|
||||
|
||||
Optional Properties if qcom,afe-vtg and qcom,dig-vtg are defined
|
||||
- qcom,afe-power-on-delay-us : unsigned integer type for
|
||||
delay between turning on analog and digital power supply
|
||||
- qcom,afe-power-off-delay-us : unsigned integer type for
|
||||
delay between turning off digital and analog power supply
|
||||
|
||||
Example:
|
||||
&soc {
|
||||
hbtp {
|
||||
|
@ -47,5 +74,16 @@ Example:
|
|||
qcom,default-max-y = <1920>;
|
||||
qcom,desired-max-x = <720>;
|
||||
qcom,desired-max-y = <1280>;
|
||||
pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
|
||||
"ddic_rst_active", "ddic_rst_suspend";
|
||||
pinctrl-0 = <&ts_rst_active>;
|
||||
pinctrl-1 = <&ts_rst_suspend>;
|
||||
pinctrl-2 = <&ddic_rst_active>;
|
||||
pinctrl-3 = <&ddic_rst_suspend>;
|
||||
qcom,pmx-ts-on-seq-delay-us = <1000>;
|
||||
qcom,ddic-rst-on-seq-delay-us = <10000 10000 10000 10000>;
|
||||
qcom,fb-resume-delay-us = <90000>;
|
||||
qcom,afe-power-on-delay-us = <1000>;
|
||||
qcom,afe-power-off-delay-us = <6>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -23,6 +23,12 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <uapi/linux/hbtp_input.h>
|
||||
#include "../input-compat.h"
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#if defined(CONFIG_FB)
|
||||
#include <linux/notifier.h>
|
||||
|
@ -32,6 +38,10 @@
|
|||
#define HBTP_INPUT_NAME "hbtp_input"
|
||||
#define DISP_COORDS_SIZE 2
|
||||
|
||||
#define HBTP_PINCTRL_VALID_STATE_CNT (2)
|
||||
#define HBTP_HOLD_DURATION_US (10)
|
||||
#define HBTP_PINCTRL_DDIC_SEQ_NUM (4)
|
||||
|
||||
struct hbtp_data {
|
||||
struct platform_device *pdev;
|
||||
struct input_dev *input_dev;
|
||||
|
@ -41,6 +51,20 @@ struct hbtp_data {
|
|||
#if defined(CONFIG_FB)
|
||||
struct notifier_block fb_notif;
|
||||
#endif
|
||||
struct pinctrl *ts_pinctrl;
|
||||
struct pinctrl_state *gpio_state_active;
|
||||
struct pinctrl_state *gpio_state_suspend;
|
||||
struct pinctrl_state *ddic_rst_state_active;
|
||||
struct pinctrl_state *ddic_rst_state_suspend;
|
||||
u32 ts_pinctrl_seq_delay;
|
||||
u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM];
|
||||
u32 fb_resume_seq_delay;
|
||||
bool lcd_on;
|
||||
bool power_suspended;
|
||||
bool power_sync_enabled;
|
||||
bool power_sig_enabled;
|
||||
struct completion power_resume_sig;
|
||||
struct completion power_suspend_sig;
|
||||
struct regulator *vcc_ana;
|
||||
struct regulator *vcc_dig;
|
||||
int afe_load_ua;
|
||||
|
@ -59,10 +83,19 @@ struct hbtp_data {
|
|||
bool override_disp_coords;
|
||||
bool manage_afe_power_ana;
|
||||
bool manage_power_dig;
|
||||
u32 power_on_delay;
|
||||
u32 power_off_delay;
|
||||
bool manage_pin_ctrl;
|
||||
};
|
||||
|
||||
static struct hbtp_data *hbtp;
|
||||
|
||||
#if defined(CONFIG_FB)
|
||||
static int hbtp_fb_suspend(struct hbtp_data *ts);
|
||||
static int hbtp_fb_early_resume(struct hbtp_data *ts);
|
||||
static int hbtp_fb_resume(struct hbtp_data *ts);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FB)
|
||||
static int fb_notifier_callback(struct notifier_block *self,
|
||||
unsigned long event, void *data)
|
||||
|
@ -70,20 +103,48 @@ static int fb_notifier_callback(struct notifier_block *self,
|
|||
int blank;
|
||||
struct fb_event *evdata = data;
|
||||
struct hbtp_data *hbtp_data =
|
||||
container_of(self, struct hbtp_data, fb_notif);
|
||||
char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
|
||||
container_of(self, struct hbtp_data, fb_notif);
|
||||
|
||||
if (evdata && evdata->data && event == FB_EVENT_BLANK &&
|
||||
hbtp_data && hbtp_data->input_dev) {
|
||||
if (evdata && evdata->data && hbtp_data &&
|
||||
(event == FB_EARLY_EVENT_BLANK ||
|
||||
event == FB_R_EARLY_EVENT_BLANK)) {
|
||||
blank = *(int *)(evdata->data);
|
||||
if (blank == FB_BLANK_UNBLANK)
|
||||
kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
|
||||
KOBJ_ONLINE, envp);
|
||||
else if (blank == FB_BLANK_POWERDOWN)
|
||||
kobject_uevent_env(&hbtp_data->input_dev->dev.kobj,
|
||||
KOBJ_OFFLINE, envp);
|
||||
if (event == FB_EARLY_EVENT_BLANK) {
|
||||
if (blank == FB_BLANK_UNBLANK) {
|
||||
pr_debug("%s: receives EARLY_BLANK:UNBLANK\n",
|
||||
__func__);
|
||||
hbtp_data->lcd_on = true;
|
||||
hbtp_fb_early_resume(hbtp_data);
|
||||
} else if (blank == FB_BLANK_POWERDOWN) {
|
||||
pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
|
||||
__func__);
|
||||
hbtp_data->lcd_on = false;
|
||||
}
|
||||
} else if (event == FB_R_EARLY_EVENT_BLANK) {
|
||||
if (blank == FB_BLANK_UNBLANK) {
|
||||
pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n",
|
||||
__func__);
|
||||
hbtp_data->lcd_on = false;
|
||||
hbtp_fb_suspend(hbtp_data);
|
||||
} else if (blank == FB_BLANK_POWERDOWN) {
|
||||
pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n",
|
||||
__func__);
|
||||
hbtp_data->lcd_on = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (evdata && evdata->data && hbtp_data &&
|
||||
event == FB_EVENT_BLANK) {
|
||||
blank = *(int *)(evdata->data);
|
||||
if (blank == FB_BLANK_POWERDOWN) {
|
||||
pr_debug("%s: receives BLANK:POWERDOWN\n", __func__);
|
||||
hbtp_fb_suspend(hbtp_data);
|
||||
} else if (blank == FB_BLANK_UNBLANK) {
|
||||
pr_debug("%s: receives BLANK:UNBLANK\n", __func__);
|
||||
hbtp_fb_resume(hbtp_data);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -111,6 +172,8 @@ static int hbtp_input_release(struct inode *inode, struct file *file)
|
|||
return -ENOTTY;
|
||||
}
|
||||
hbtp->count--;
|
||||
if (hbtp->power_sig_enabled)
|
||||
hbtp->power_sig_enabled = false;
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
|
||||
return 0;
|
||||
|
@ -278,6 +341,14 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on)
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (hbtp->power_on_delay) {
|
||||
pr_debug("%s: power-on-delay = %u\n", __func__,
|
||||
hbtp->power_on_delay);
|
||||
usleep_range(hbtp->power_on_delay,
|
||||
hbtp->power_on_delay + HBTP_HOLD_DURATION_US);
|
||||
}
|
||||
|
||||
if (hbtp->vcc_dig) {
|
||||
ret = reg_set_load_check(hbtp->vcc_dig,
|
||||
hbtp->dig_load_ua);
|
||||
|
@ -299,17 +370,171 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on)
|
|||
return 0;
|
||||
|
||||
reg_off:
|
||||
if (hbtp->vcc_ana) {
|
||||
reg_set_load_check(hbtp->vcc_ana, 0);
|
||||
regulator_disable(hbtp->vcc_ana);
|
||||
}
|
||||
if (hbtp->vcc_dig) {
|
||||
reg_set_load_check(hbtp->vcc_dig, 0);
|
||||
regulator_disable(hbtp->vcc_dig);
|
||||
}
|
||||
|
||||
if (hbtp->power_off_delay) {
|
||||
pr_debug("%s: power-off-delay = %u\n", __func__,
|
||||
hbtp->power_off_delay);
|
||||
usleep_range(hbtp->power_off_delay,
|
||||
hbtp->power_off_delay + HBTP_HOLD_DURATION_US);
|
||||
}
|
||||
|
||||
if (hbtp->vcc_ana) {
|
||||
reg_set_load_check(hbtp->vcc_ana, 0);
|
||||
regulator_disable(hbtp->vcc_ana);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hbtp_gpio_select(struct hbtp_data *data, bool on)
|
||||
{
|
||||
struct pinctrl_state *pins_state;
|
||||
int ret = 0;
|
||||
|
||||
pins_state = on ? data->gpio_state_active : data->gpio_state_suspend;
|
||||
if (!IS_ERR_OR_NULL(pins_state)) {
|
||||
ret = pinctrl_select_state(data->ts_pinctrl, pins_state);
|
||||
if (ret) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"can not set %s pins\n",
|
||||
on ? "ts_active" : "ts_suspend");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (on) {
|
||||
if (data->ts_pinctrl_seq_delay) {
|
||||
usleep_range(data->ts_pinctrl_seq_delay,
|
||||
data->ts_pinctrl_seq_delay +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
dev_dbg(&data->pdev->dev, "ts_pinctrl_seq_delay = %u\n",
|
||||
data->ts_pinctrl_seq_delay);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dev_warn(&data->pdev->dev,
|
||||
"not a valid '%s' pinstate\n",
|
||||
on ? "ts_active" : "ts_suspend");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hbtp_ddic_rst_select(struct hbtp_data *data, bool on)
|
||||
{
|
||||
struct pinctrl_state *active, *suspend;
|
||||
int ret = 0;
|
||||
|
||||
active = data->ddic_rst_state_active;
|
||||
if (IS_ERR_OR_NULL(active)) {
|
||||
dev_warn(&data->pdev->dev,
|
||||
"not a valid ddic_rst_active pinstate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
suspend = data->ddic_rst_state_suspend;
|
||||
if (IS_ERR_OR_NULL(suspend)) {
|
||||
dev_warn(&data->pdev->dev,
|
||||
"not a valid ddic_rst_suspend pinstate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (on) {
|
||||
if (data->ddic_pinctrl_seq_delay[0]) {
|
||||
usleep_range(data->ddic_pinctrl_seq_delay[0],
|
||||
data->ddic_pinctrl_seq_delay[0] +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
dev_dbg(&data->pdev->dev, "ddic_seq_delay[0] = %u\n",
|
||||
data->ddic_pinctrl_seq_delay[0]);
|
||||
}
|
||||
|
||||
ret = pinctrl_select_state(data->ts_pinctrl, active);
|
||||
if (ret) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"can not set ddic_rst_active pins\n");
|
||||
return ret;
|
||||
}
|
||||
if (data->ddic_pinctrl_seq_delay[1]) {
|
||||
usleep_range(data->ddic_pinctrl_seq_delay[1],
|
||||
data->ddic_pinctrl_seq_delay[1] +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
dev_dbg(&data->pdev->dev, "ddic_seq_delay[1] = %u\n",
|
||||
data->ddic_pinctrl_seq_delay[1]);
|
||||
}
|
||||
ret = pinctrl_select_state(data->ts_pinctrl, suspend);
|
||||
if (ret) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"can not set ddic_rst_suspend pins\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->ddic_pinctrl_seq_delay[2]) {
|
||||
usleep_range(data->ddic_pinctrl_seq_delay[2],
|
||||
data->ddic_pinctrl_seq_delay[2] +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
dev_dbg(&data->pdev->dev, "ddic_seq_delay[2] = %u\n",
|
||||
data->ddic_pinctrl_seq_delay[2]);
|
||||
}
|
||||
|
||||
ret = pinctrl_select_state(data->ts_pinctrl, active);
|
||||
if (ret) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"can not set ddic_rst_active pins\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->ddic_pinctrl_seq_delay[3]) {
|
||||
usleep_range(data->ddic_pinctrl_seq_delay[3],
|
||||
data->ddic_pinctrl_seq_delay[3] +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
dev_dbg(&data->pdev->dev, "ddic_seq_delay[3] = %u\n",
|
||||
data->ddic_pinctrl_seq_delay[3]);
|
||||
}
|
||||
} else {
|
||||
ret = pinctrl_select_state(data->ts_pinctrl, suspend);
|
||||
if (ret) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"can not set ddic_rst_suspend pins\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hbtp_pinctrl_enable(struct hbtp_data *ts, bool on)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!ts->manage_pin_ctrl) {
|
||||
pr_info("%s: pinctrl info is not available\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!on)
|
||||
goto pinctrl_suspend;
|
||||
|
||||
rc = hbtp_gpio_select(ts, true);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rc = hbtp_ddic_rst_select(ts, true);
|
||||
if (rc < 0)
|
||||
goto err_ddic_rst_pinctrl_enable;
|
||||
|
||||
return rc;
|
||||
|
||||
pinctrl_suspend:
|
||||
if (ts->ddic_rst_state_suspend)
|
||||
hbtp_ddic_rst_select(ts, true);
|
||||
err_ddic_rst_pinctrl_enable:
|
||||
hbtp_gpio_select(ts, false);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
|
||||
unsigned long arg, void __user *p)
|
||||
{
|
||||
|
@ -318,6 +543,8 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
|
|||
struct hbtp_input_absinfo absinfo[ABS_MT_LAST - ABS_MT_FIRST + 1];
|
||||
struct hbtp_input_key key_data;
|
||||
enum hbtp_afe_power_cmd power_cmd;
|
||||
enum hbtp_afe_signal afe_signal;
|
||||
enum hbtp_afe_power_ctrl afe_power_ctrl;
|
||||
|
||||
switch (cmd) {
|
||||
case HBTP_SET_ABSPARAM:
|
||||
|
@ -408,6 +635,112 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd,
|
|||
input_sync(hbtp->input_dev);
|
||||
break;
|
||||
|
||||
case HBTP_SET_SYNCSIGNAL:
|
||||
if (!hbtp || !hbtp->input_dev) {
|
||||
pr_err("%s: The input device hasn't been created\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!hbtp->power_sig_enabled) {
|
||||
pr_err("%s: power_signal is not enabled", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (copy_from_user(&afe_signal, (void *)arg,
|
||||
sizeof(enum hbtp_afe_signal))) {
|
||||
pr_err("%s: Error copying data\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_debug("%s: receives %d signal\n", __func__, afe_signal);
|
||||
|
||||
switch (afe_signal) {
|
||||
case HBTP_AFE_SIGNAL_ON_RESUME:
|
||||
mutex_lock(&hbtp->mutex);
|
||||
if (!hbtp->power_suspended) {
|
||||
complete(&hbtp->power_resume_sig);
|
||||
} else {
|
||||
pr_err("%s: resume signal in wrong state\n",
|
||||
__func__);
|
||||
}
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
break;
|
||||
case HBTP_AFE_SIGNAL_ON_SUSPEND:
|
||||
mutex_lock(&hbtp->mutex);
|
||||
if (hbtp->power_suspended) {
|
||||
complete(&hbtp->power_suspend_sig);
|
||||
} else {
|
||||
pr_err("%s: suspend signal in wrong state\n",
|
||||
__func__);
|
||||
}
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unsupported command for afe signal, %d\n",
|
||||
__func__, afe_signal);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case HBTP_SET_POWER_CTRL:
|
||||
if (!hbtp || !hbtp->input_dev) {
|
||||
pr_err("%s: The input device hasn't been created\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (copy_from_user(&afe_power_ctrl, (void *)arg,
|
||||
sizeof(enum hbtp_afe_power_ctrl))) {
|
||||
pr_err("%s: Error copying data\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
switch (afe_power_ctrl) {
|
||||
case HBTP_AFE_POWER_ENABLE_SYNC:
|
||||
pr_debug("%s: power_sync is enabled\n", __func__);
|
||||
if (!hbtp->manage_pin_ctrl || !hbtp->manage_power_dig ||
|
||||
!hbtp->manage_afe_power_ana) {
|
||||
pr_err("%s: power/pin is not available\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
mutex_lock(&hbtp->mutex);
|
||||
error = hbtp_pdev_power_on(hbtp, true);
|
||||
if (error) {
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
pr_err("%s: failed to power on\n", __func__);
|
||||
return error;
|
||||
}
|
||||
error = hbtp_pinctrl_enable(hbtp, true);
|
||||
if (error) {
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
pr_err("%s: failed to enable pins\n", __func__);
|
||||
hbtp_pdev_power_on(hbtp, false);
|
||||
return error;
|
||||
}
|
||||
hbtp->power_sync_enabled = true;
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
pr_debug("%s: power_sync option is enabled\n",
|
||||
__func__);
|
||||
break;
|
||||
case HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL:
|
||||
if (!hbtp->power_sync_enabled) {
|
||||
pr_err("%s: power_sync is not enabled\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
mutex_lock(&hbtp->mutex);
|
||||
init_completion(&hbtp->power_resume_sig);
|
||||
init_completion(&hbtp->power_suspend_sig);
|
||||
hbtp->power_sig_enabled = true;
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
pr_err("%s: sync_signal option is enabled\n", __func__);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unsupported power ctrl, %d\n",
|
||||
__func__, afe_power_ctrl);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd);
|
||||
error = -EINVAL;
|
||||
|
@ -514,6 +847,25 @@ static int hbtp_parse_dt(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
if (hbtp->manage_power_dig && hbtp->manage_afe_power_ana) {
|
||||
rc = of_property_read_u32(np,
|
||||
"qcom,afe-power-on-delay-us", &temp_val);
|
||||
if (!rc)
|
||||
hbtp->power_on_delay = (u32)temp_val;
|
||||
else
|
||||
dev_info(dev, "Power-On Delay is not specified\n");
|
||||
|
||||
rc = of_property_read_u32(np,
|
||||
"qcom,afe-power-off-delay-us", &temp_val);
|
||||
if (!rc)
|
||||
hbtp->power_off_delay = (u32)temp_val;
|
||||
else
|
||||
dev_info(dev, "Power-Off Delay is not specified\n");
|
||||
|
||||
dev_dbg(dev, "power-on-delay = %u, power-off-delay = %u\n",
|
||||
hbtp->power_on_delay, hbtp->power_off_delay);
|
||||
}
|
||||
|
||||
prop = of_find_property(np, "qcom,display-resolution", NULL);
|
||||
if (prop != NULL) {
|
||||
if (!prop->value)
|
||||
|
@ -605,11 +957,292 @@ static int hbtp_parse_dt(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int hbtp_pinctrl_init(struct hbtp_data *data)
|
||||
{
|
||||
const char *statename;
|
||||
int rc;
|
||||
int state_cnt, i;
|
||||
struct device_node *np = data->pdev->dev.of_node;
|
||||
bool pinctrl_state_act_found = false;
|
||||
bool pinctrl_state_sus_found = false;
|
||||
bool pinctrl_ddic_act_found = false;
|
||||
bool pinctrl_ddic_sus_found = false;
|
||||
int count = 0;
|
||||
|
||||
data->ts_pinctrl = devm_pinctrl_get(&(data->pdev->dev));
|
||||
if (IS_ERR_OR_NULL(data->ts_pinctrl)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Target does not use pinctrl\n");
|
||||
rc = PTR_ERR(data->ts_pinctrl);
|
||||
data->ts_pinctrl = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
state_cnt = of_property_count_strings(np, "pinctrl-names");
|
||||
if (state_cnt < HBTP_PINCTRL_VALID_STATE_CNT) {
|
||||
/*
|
||||
*if pinctrl names are not available then,
|
||||
*power_sync can't be enabled
|
||||
*/
|
||||
dev_info(&data->pdev->dev,
|
||||
"pinctrl names are not available\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < state_cnt; i++) {
|
||||
rc = of_property_read_string_index(np,
|
||||
"pinctrl-names", i, &statename);
|
||||
if (rc) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"failed to read pinctrl states by index\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!strcmp(statename, "pmx_ts_active")) {
|
||||
data->gpio_state_active
|
||||
= pinctrl_lookup_state(data->ts_pinctrl,
|
||||
statename);
|
||||
if (IS_ERR_OR_NULL(data->gpio_state_active)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Can not get ts default state\n");
|
||||
rc = PTR_ERR(data->gpio_state_active);
|
||||
goto error;
|
||||
}
|
||||
pinctrl_state_act_found = true;
|
||||
} else if (!strcmp(statename, "pmx_ts_suspend")) {
|
||||
data->gpio_state_suspend
|
||||
= pinctrl_lookup_state(data->ts_pinctrl,
|
||||
statename);
|
||||
if (IS_ERR_OR_NULL(data->gpio_state_suspend)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Can not get ts sleep state\n");
|
||||
rc = PTR_ERR(data->gpio_state_suspend);
|
||||
goto error;
|
||||
}
|
||||
pinctrl_state_sus_found = true;
|
||||
} else if (!strcmp(statename, "ddic_rst_active")) {
|
||||
data->ddic_rst_state_active
|
||||
= pinctrl_lookup_state(data->ts_pinctrl,
|
||||
statename);
|
||||
if (IS_ERR(data->ddic_rst_state_active)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Can not get DDIC rst act state\n");
|
||||
rc = PTR_ERR(data->ddic_rst_state_active);
|
||||
goto error;
|
||||
}
|
||||
pinctrl_ddic_act_found = true;
|
||||
} else if (!strcmp(statename, "ddic_rst_suspend")) {
|
||||
data->ddic_rst_state_suspend
|
||||
= pinctrl_lookup_state(data->ts_pinctrl,
|
||||
statename);
|
||||
if (IS_ERR(data->ddic_rst_state_suspend)) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"Can not get DDIC rst sleep state\n");
|
||||
rc = PTR_ERR(data->ddic_rst_state_suspend);
|
||||
goto error;
|
||||
}
|
||||
pinctrl_ddic_sus_found = true;
|
||||
} else {
|
||||
dev_err(&data->pdev->dev, "invalid pinctrl state\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pinctrl_state_act_found || !pinctrl_state_sus_found) {
|
||||
dev_err(&data->pdev->dev,
|
||||
"missing required pinctrl states\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "qcom,pmx-ts-on-seq-delay-us",
|
||||
&data->ts_pinctrl_seq_delay)) {
|
||||
dev_warn(&data->pdev->dev, "Can not find ts seq delay\n");
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "qcom,fb-resume-delay-us",
|
||||
&data->fb_resume_seq_delay)) {
|
||||
dev_warn(&data->pdev->dev, "Can not find fb resume seq delay\n");
|
||||
}
|
||||
|
||||
if (pinctrl_ddic_act_found && pinctrl_ddic_sus_found) {
|
||||
count = of_property_count_u32_elems(np,
|
||||
"qcom,ddic-rst-on-seq-delay-us");
|
||||
if (count == HBTP_PINCTRL_DDIC_SEQ_NUM) {
|
||||
of_property_read_u32_array(np,
|
||||
"qcom,ddic-rst-on-seq-delay-us",
|
||||
data->ddic_pinctrl_seq_delay, count);
|
||||
} else {
|
||||
dev_err(&data->pdev->dev, "count(%u) is not same as %u\n",
|
||||
(u32)count, HBTP_PINCTRL_DDIC_SEQ_NUM);
|
||||
}
|
||||
} else {
|
||||
dev_err(&data->pdev->dev, "ddic pinctrl act/sus not found\n");
|
||||
}
|
||||
|
||||
data->manage_pin_ctrl = true;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
devm_pinctrl_put(data->ts_pinctrl);
|
||||
data->ts_pinctrl = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hbtp_fb_suspend(struct hbtp_data *ts)
|
||||
{
|
||||
int rc;
|
||||
char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
|
||||
|
||||
mutex_lock(&hbtp->mutex);
|
||||
if (ts->pdev && ts->power_sync_enabled) {
|
||||
pr_debug("%s: power_sync is enabled\n", __func__);
|
||||
if (ts->power_suspended) {
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
pr_err("%s: power is not resumed\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
rc = hbtp_pinctrl_enable(ts, false);
|
||||
if (rc) {
|
||||
pr_err("%s: failed to disable GPIO pins\n", __func__);
|
||||
goto err_pin_disable;
|
||||
}
|
||||
|
||||
rc = hbtp_pdev_power_on(ts, false);
|
||||
if (rc) {
|
||||
pr_err("%s: failed to disable power\n", __func__);
|
||||
goto err_power_disable;
|
||||
}
|
||||
ts->power_suspended = true;
|
||||
}
|
||||
|
||||
if (ts->input_dev) {
|
||||
kobject_uevent_env(&ts->input_dev->dev.kobj,
|
||||
KOBJ_OFFLINE, envp);
|
||||
|
||||
if (ts->power_sig_enabled) {
|
||||
pr_debug("%s: power_sig is enabled, wait for signal\n",
|
||||
__func__);
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
rc = wait_for_completion_interruptible(
|
||||
&hbtp->power_suspend_sig);
|
||||
if (rc != 0) {
|
||||
pr_err("%s: wait for suspend is interrupted\n",
|
||||
__func__);
|
||||
}
|
||||
mutex_lock(&hbtp->mutex);
|
||||
pr_debug("%s: Wait is done for suspend\n", __func__);
|
||||
} else {
|
||||
pr_debug("%s: power_sig is NOT enabled", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return 0;
|
||||
err_power_disable:
|
||||
hbtp_pinctrl_enable(ts, true);
|
||||
err_pin_disable:
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hbtp_fb_early_resume(struct hbtp_data *ts)
|
||||
{
|
||||
char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
|
||||
int rc;
|
||||
|
||||
mutex_lock(&hbtp->mutex);
|
||||
|
||||
pr_debug("%s: hbtp_fb_early_resume\n", __func__);
|
||||
|
||||
if (ts->pdev && ts->power_sync_enabled) {
|
||||
pr_debug("%s: power_sync is enabled\n", __func__);
|
||||
if (!ts->power_suspended) {
|
||||
pr_err("%s: power is not suspended\n", __func__);
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return 0;
|
||||
}
|
||||
rc = hbtp_pdev_power_on(ts, true);
|
||||
if (rc) {
|
||||
pr_err("%s: failed to enable panel power\n", __func__);
|
||||
goto err_power_on;
|
||||
}
|
||||
|
||||
rc = hbtp_pinctrl_enable(ts, true);
|
||||
|
||||
if (rc) {
|
||||
pr_err("%s: failed to enable pin\n", __func__);
|
||||
goto err_pin_enable;
|
||||
}
|
||||
|
||||
ts->power_suspended = false;
|
||||
|
||||
if (ts->input_dev) {
|
||||
|
||||
kobject_uevent_env(&ts->input_dev->dev.kobj,
|
||||
KOBJ_ONLINE, envp);
|
||||
|
||||
if (ts->power_sig_enabled) {
|
||||
pr_err("%s: power_sig is enabled, wait for signal\n",
|
||||
__func__);
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
rc = wait_for_completion_interruptible(
|
||||
&hbtp->power_resume_sig);
|
||||
if (rc != 0) {
|
||||
pr_err("%s: wait for resume is interrupted\n",
|
||||
__func__);
|
||||
}
|
||||
mutex_lock(&hbtp->mutex);
|
||||
pr_debug("%s: wait is done\n", __func__);
|
||||
} else {
|
||||
pr_debug("%s: power_sig is NOT enabled\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (ts->fb_resume_seq_delay) {
|
||||
usleep_range(ts->fb_resume_seq_delay,
|
||||
ts->fb_resume_seq_delay +
|
||||
HBTP_HOLD_DURATION_US);
|
||||
pr_err("%s: fb_resume_seq_delay = %u\n",
|
||||
__func__, ts->fb_resume_seq_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return 0;
|
||||
|
||||
err_pin_enable:
|
||||
hbtp_pdev_power_on(ts, false);
|
||||
err_power_on:
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hbtp_fb_resume(struct hbtp_data *ts)
|
||||
{
|
||||
char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
|
||||
|
||||
mutex_lock(&hbtp->mutex);
|
||||
if (!ts->power_sync_enabled) {
|
||||
pr_debug("%s: power_sync is disabled, send uevent\n", __func__);
|
||||
if (ts->input_dev) {
|
||||
kobject_uevent_env(&ts->input_dev->dev.kobj,
|
||||
KOBJ_ONLINE, envp);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&hbtp->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hbtp_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error;
|
||||
struct regulator *vcc_ana, *vcc_dig;
|
||||
|
||||
hbtp->pdev = pdev;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
error = hbtp_parse_dt(&pdev->dev);
|
||||
if (error) {
|
||||
|
@ -618,6 +1251,14 @@ static int hbtp_pdev_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hbtp);
|
||||
|
||||
error = hbtp_pinctrl_init(hbtp);
|
||||
if (error) {
|
||||
pr_info("%s: pinctrl isn't available, rc=%d\n", __func__,
|
||||
error);
|
||||
}
|
||||
|
||||
if (hbtp->manage_afe_power_ana) {
|
||||
vcc_ana = regulator_get(&pdev->dev, "vcc_ana");
|
||||
if (IS_ERR(vcc_ana)) {
|
||||
|
@ -662,8 +1303,6 @@ static int hbtp_pdev_probe(struct platform_device *pdev)
|
|||
hbtp->vcc_dig = vcc_dig;
|
||||
}
|
||||
|
||||
hbtp->pdev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -677,6 +1316,9 @@ static int hbtp_pdev_remove(struct platform_device *pdev)
|
|||
regulator_put(hbtp->vcc_dig);
|
||||
}
|
||||
|
||||
if (hbtp->ts_pinctrl)
|
||||
devm_pinctrl_put(hbtp->ts_pinctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,17 @@ struct hbtp_input_key {
|
|||
__s32 value;
|
||||
};
|
||||
|
||||
enum hbtp_afe_signal {
|
||||
HBTP_AFE_SIGNAL_ON_RESUME,
|
||||
HBTP_AFE_SIGNAL_ON_SUSPEND,
|
||||
};
|
||||
|
||||
enum hbtp_afe_power_ctrl {
|
||||
HBTP_AFE_POWER_ENABLE_SYNC,
|
||||
HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL,
|
||||
};
|
||||
|
||||
|
||||
/* ioctl */
|
||||
#define HBTP_INPUT_IOCTL_BASE 'T'
|
||||
#define HBTP_SET_ABSPARAM _IOW(HBTP_INPUT_IOCTL_BASE, 201, \
|
||||
|
@ -53,6 +64,10 @@ struct hbtp_input_key {
|
|||
enum hbtp_afe_power_cmd)
|
||||
#define HBTP_SET_KEYDATA _IOW(HBTP_INPUT_IOCTL_BASE, 204, \
|
||||
struct hbtp_input_key)
|
||||
#define HBTP_SET_SYNCSIGNAL _IOW(HBTP_INPUT_IOCTL_BASE, 205, \
|
||||
enum hbtp_afe_signal)
|
||||
#define HBTP_SET_POWER_CTRL _IOW(HBTP_INPUT_IOCTL_BASE, 206, \
|
||||
enum hbtp_afe_power_ctrl)
|
||||
|
||||
#endif /* _UAPI_HBTP_INPUT_H */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue