From 67a83c3bef456c5351336b817d2f53b89eb0bc6a Mon Sep 17 00:00:00 2001 From: Konstantin Makariev Date: Mon, 28 Jun 2021 17:32:31 -0500 Subject: [PATCH] pct1812_mmi: pct1812ff kernel driver Kernel driver to support PixArt PCT1812FF touch IC Change-Id: I017e5b2c34b8c833d1350494a621bf1b20b2f05d Signed-off-by: Konstantin Makariev Reviewed-on: https://gerrit.mot.com/1994779 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Igor Kovalenko Submit-Approved: Jira Key --- .../input/touchscreen/pixart_mmi/Android.mk | 21 + drivers/input/touchscreen/pixart_mmi/Kbuild | 11 + drivers/input/touchscreen/pixart_mmi/Kconfig | 13 + drivers/input/touchscreen/pixart_mmi/Makefile | 14 + .../touchscreen/pixart_mmi/pct1812_captouch.c | 749 ++++++++++++++++++ 5 files changed, 808 insertions(+) create mode 100644 drivers/input/touchscreen/pixart_mmi/Android.mk create mode 100644 drivers/input/touchscreen/pixart_mmi/Kbuild create mode 100644 drivers/input/touchscreen/pixart_mmi/Kconfig create mode 100644 drivers/input/touchscreen/pixart_mmi/Makefile create mode 100644 drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c diff --git a/drivers/input/touchscreen/pixart_mmi/Android.mk b/drivers/input/touchscreen/pixart_mmi/Android.mk new file mode 100644 index 000000000000..dcfcda829293 --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(BUILD_PIXART_TOUCHPAD),true) +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_PIXART_TS=y +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := pct1812_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +#LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +#ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) +# KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +# LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +#endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk +endif diff --git a/drivers/input/touchscreen/pixart_mmi/Kbuild b/drivers/input/touchscreen/pixart_mmi/Kbuild new file mode 100644 index 000000000000..de6944798f46 --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we canss +# EXTRA_CFLAGS += -Wallss +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/pixart_mmi + +obj-m := pct1812_mmi.o +pct1812_mmi-objs := pct1812_captouch.o + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif diff --git a/drivers/input/touchscreen/pixart_mmi/Kconfig b/drivers/input/touchscreen/pixart_mmi/Kconfig new file mode 100644 index 000000000000..c25eaa4dccca --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Kconfig @@ -0,0 +1,13 @@ +# +# PixArt TOUCH driver configuration +# + +config TOUCHSCREEN_PIXART_TS + tristate "PixArt Touchscreen" + depends on I2C + help + Say Y here if you want support for PCT1812 touchscreen controller. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pct1812_mmi. diff --git a/drivers/input/touchscreen/pixart_mmi/Makefile b/drivers/input/touchscreen/pixart_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c b/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c new file mode 100644 index 000000000000..5445f077e669 --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c @@ -0,0 +1,749 @@ +// +// Description: I2C Adapter for PixArt Imaging PCT1812FF Capacitance Touch Controller Driver on Linux Kernel +// +// Copyright (c) 2021 Lenovo, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#define MYNAME "pct1812_mmi" +#define pr_fmt(fmt) "%s: %s: " fmt, MYNAME, __func__ + +#define DEBUG +//#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RESET_CODE 0xaa +#define RESUME_CODE 0xbb +#define SUSPEND_CODE 0x99 + +#define FRAME_NUM_REG 0x00 +#define GEST_TYPE_REG 0x60 +#define GEST_X0_REG 0x62 +#define GEST_X1_REG 0x63 +#define BOOT_STATUS_REG 0x70 +#define STATUS_REG 0x71 +#define USER_BANK_REG 0x73 +#define USER_ADDR_REG 0x74 +#define USER_DAA_REG 0x75 +#define SW_RESET_REG 0x7a +#define DEEP_SLEEP_REG 0x7c + +#define STAT_BIT_ERR (1 << 0) +#define STAT_BIT_EVENT (1 << 3) +#define STAT_BIT_WDOG (1 << 7) + +#define BOOT_COMPLETE 1 +#define TOUCH_RESET_DELAY 10 +#define CMD_WAIT_DELAY 10 +#define WDOG_INTERVAL 1000 +#define PON_DELAY 200 + +#define CNT_I2C_RETRY 3 +#define CNT_WAIT_RETRY 50 + +#define I2C_WRITE_BUFFER_SIZE 4 +#define I2C_READ_BUFFER_SIZE 4 + +#define PINCTRL_STATE_ACTIVE "touchpad_active" +#define PINCTRL_STATE_SUSPEND "touchpad_suspend" + +enum pwr_modes { + PWR_MODE_OFF, + PWR_MODE_RUN, + PWR_MODE_LPM, + PWR_MODE_DEEP_SLEEP, + PWR_MODE_SHUTDOWN, + PWR_MODE_MAX +}; + +enum cmds { + CMD_INIT, + CMD_RESET, + CMD_SUSPEND, + CMD_RESUME, + CMD_RECOVER, + CMD_WDOG +}; + +struct pct1812_platform { + int max_x; + int max_y; + + unsigned rst_gpio; + unsigned irq_gpio; + + struct pinctrl *pinctrl; + + const char *vdd; + const char *vio; +}; + +struct pct1812_data { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct pct1812_platform *plat_data; + + struct regulator *reg_vdd; + struct regulator *reg_vio; + + int tx_count; + int rx_count; + + struct mutex i2c_mutex; + struct mutex eventlock; + struct mutex cmdlock; + struct delayed_work worker; + + bool irq_enabled; + struct kfifo cmd_pipe; + atomic_t touch_stopped; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; +}; +#if 0 +static int inline get_cmd(void) +{ + int cmd = 0; + + mutex_lock(&ts->cmdlock); + cmd = ts->cmd; + mutex_unloc(&ts->cmdlock); + + return cmd; +} + +static void inline set_cmd(int cmd) +{ + mutex_lock(&ts->cmdlock); + ts->cmd = cmd; + mutex_unloc(&ts->cmdlock); +} +#endif +static void inline pct1812_fifo_cmd_add(struct pct1812_data *ts, enum cmds command, unsigned int delay) +{ + kfifo_put(&ts->cmd_pipe, command); + schedule_delayed_work(&ts->worker, msecs_to_jiffies(delay)); +} + +static void inline pct1812_delay_ms(unsigned int ms) +{ + if (ms < 20) + usleep_range(ms * 1000, ms * 1000); + else + msleep(ms); +} + +static int pct1812_i2c_read(struct pct1812_data *ts, unsigned char reg, unsigned char *data, int len) +{ + unsigned char retry, buf[I2C_READ_BUFFER_SIZE]; + int ret; + struct i2c_msg msg[2]; +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + return -EIO; + } +#endif + buf[0] = reg; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < CNT_I2C_RETRY; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret == 2) + break; +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + return -EIO; + } +#endif + pct1812_delay_ms(1); + if (retry > 1) { + dev_err(&ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + //ts->comm_err_count++; + } + } + mutex_unlock(&ts->i2c_mutex); + + if (retry == CNT_I2C_RETRY) { + dev_err(&ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; + } +#if 0 + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_READ_CMD) { + pr_info("sec_input:i2c_cmd: R: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } +#endif + return ret; +} + +static int pct1812_i2c_write(struct pct1812_data *ts, unsigned char reg, unsigned char *data, int len) +{ + unsigned char retry, buf[I2C_WRITE_BUFFER_SIZE + 1]; + int ret; + struct i2c_msg msg; + + if (len > I2C_WRITE_BUFFER_SIZE) { + dev_err(&ts->client->dev, "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + return -EIO; + } +#endif + buf[0] = reg; + memcpy(buf + 1, data, len); + + msg.addr = ts->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < CNT_I2C_RETRY; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) + break; +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + return -EIO; + } +#endif + pct1812_delay_ms(1); + if (retry > 1) { + dev_err(&ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + //ts->comm_err_count++; + } + } + mutex_unlock(&ts->i2c_mutex); + + if (retry == CNT_I2C_RETRY) { + dev_err(&ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; + } +#if 0 + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_WRITE_CMD) { + pr_info("sec_input:i2c_cmd: W: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } +#endif + return (ret == 1) ? 0 : ret; +} + +static int pct1812_parse_dt(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pct1812_platform *pdata = dev->platform_data; + struct device_node *np = dev->of_node; + unsigned int coords[2]; + int ret = 0; + + if (of_property_read_string(np, "pct1812,regulator_vdd", &pdata->vdd)) { + dev_err(dev, "%s: Failed to get VDD name property\n", __func__); + return -EINVAL; + } + + if (of_property_read_string(np, "pct1812,regulator_vio", &pdata->vio)) { + dev_err(dev, "%s: Failed to get VIO name property\n", __func__); + return -EINVAL; + } + + pdata->irq_gpio = of_get_named_gpio(np, "pct1812,irq_gpio", 0); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "pct1812,irq"); + if (ret) { + dev_err(&client->dev, "%s: Unable to request gpio [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + dev_info(&client->dev, "%s: using irq gpio %d\n", __func__, pdata->irq_gpio); + } else { + dev_err(&client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } +#if 0 + pdata->rst_gpio = of_get_named_gpio(np, "pct1812,rst_gpio", 0); + if (gpio_is_valid(pdata->rst_gpio)) { + ret = gpio_request_one(pdata->rst_gpio, GPIOF_DIR_OUT, "pct1812,reset"); + if (ret) { + dev_err(&client->dev, "%s: Unable to request gpio [%d]\n", __func__, pdata->rst_gpio); + return -EINVAL; + } + dev_info(&client->dev, "%s: using reset gpio %d\n", __func__, pdata->rst_gpio); + } +#endif + client->irq = gpio_to_irq(pdata->irq_gpio); + + if (!of_property_read_u32_array(np, "pct1812,max_coords", coords, 2)) { + pdata->max_x = coords[0] - 1; + pdata->max_y = coords[1] - 1; + dev_info(dev, "%s: max_coords:(%d,%d)\n", __func__, pdata->max_x, pdata->max_y); + } + + return ret; +} +static int pct1812_read_touch_event(struct pct1812_data *ts) +{ + return 0; +} + +static irqreturn_t pct1812_irq_handler(int irq, void *ptr) +{ + struct pct1812_data *ts = (struct pct1812_data *)ptr; + unsigned char status = 0; + bool ack = true; + int ret; + + mutex_lock(&ts->eventlock); + ret = pct1812_i2c_read(ts, STATUS_REG, &status, sizeof(status)); + if (status & STAT_BIT_EVENT) { + pct1812_read_touch_event(ts); + } else if ((status & STAT_BIT_ERR) || (status & STAT_BIT_WDOG)) { + ack = false; + pct1812_fifo_cmd_add(ts, CMD_RECOVER, TOUCH_RESET_DELAY); + } + if (ack) { + status = 0; // ACK interrupt + ret = pct1812_i2c_write(ts, STATUS_REG, &status, sizeof(status)); + } + mutex_unlock(&ts->eventlock); + + return IRQ_HANDLED; +} + +static int pct1812_wait4ready(struct pct1812_data *ts, unsigned char reg, unsigned char vok) +{ + unsigned char status = 0; + int retry, ret; + + for (retry = 0; retry < CNT_WAIT_RETRY; retry++) { + ret = pct1812_i2c_read(ts, reg, &status, sizeof(status)); + if (ret || status == vok) + break; + pct1812_delay_ms(CMD_WAIT_DELAY); + } + if (ret == CNT_WAIT_RETRY) + ret = -ETIME; + return (status == vok) ? 0 : ret; +} + +static int pct1812_regulator(struct pct1812_data *ts, bool get) +{ + struct pct1812_platform *pdata = ts->plat_data; + struct regulator *rvdd = NULL; + struct regulator *rvio = NULL; + + if (get) { + rvdd = regulator_get(ts->dev, pdata->vdd); + if (IS_ERR_OR_NULL(rvdd)) { + dev_err(ts->dev, "%s: Failed to get %s regulator\n", + __func__, pdata->vdd); + rvdd = NULL; + } + if (pdata->vio) + rvio = regulator_get(ts->dev, pdata->vio); + if (IS_ERR_OR_NULL(rvio)) { + dev_err(ts->dev, "%s: Failed to get %s regulator.\n", + __func__, pdata->vio); + rvio = NULL; + } + } else { + regulator_put(ts->reg_vdd); + if (pdata->vio) + regulator_put(ts->reg_vio); + } + + ts->reg_vdd = rvdd; + ts->reg_vio = rvio; + + return 0; +} + +static int pct1812_pinctrl_state(struct pct1812_data *info, bool on) +{ + struct pinctrl_state *state_ptr; + const char *state_name; + int error = 0; + + if (!info->ts_pinctrl) + return 0; + + if (on) { + state_name = PINCTRL_STATE_ACTIVE; + state_ptr =info->pinctrl_state_active; + } else { + state_name = PINCTRL_STATE_SUSPEND; + state_ptr =info->pinctrl_state_suspend; + } + + error = pinctrl_select_state(info->ts_pinctrl, state_ptr); + if (error < 0) + dev_err(info->dev, "%s: Failed to select %s\n", + __func__, state_name); + return error; +} + +static int pct1812_pinctrl_init(struct pct1812_data *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(info->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + dev_err(info->dev, "%s Target not using pinctrl %d\n", + __func__, retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + dev_err(info->dev, "%s Can not lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + dev_err(info->dev, "%s Can not lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + return retval; +} + +static int pct1812_power(struct pct1812_data *ts, bool on) +{ + struct pct1812_platform *pdata = ts->plat_data; + + if (on) { + if (pdata->vio) + regulator_enable(ts->reg_vio); + regulator_enable(ts->reg_vdd); + pct1812_delay_ms(PON_DELAY); + } else { + regulator_disable(ts->reg_vdd); + if (pdata->vio) + regulator_disable(ts->reg_vio); + } + + dev_info(ts->dev,"%s: %s: %s:%s\n", __func__, on ? "on" : "off", + pdata->vdd, regulator_is_enabled(ts->reg_vdd) ? "on" : "off"); + if (pdata->vio) + dev_info(ts->dev,"%s: %s: %s:%s\n", __func__, on ? "on" : "off", + pdata->vio, + regulator_is_enabled(ts->reg_vio) ? "on" : "off"); + + return 0; +} +#if 0 +static int pct1812_power_mode(struct pct1812_data *ts, enum pwr_modes mode) +{ + return 0; +} +#endif +static int pct1812_run_cmd(struct pct1812_data *ts, enum cmds command) +{ + unsigned char code; + int ret, counter = 0; + + switch (command) { + case CMD_RESET: + code = RESET_CODE; + break; + case CMD_RESUME: + code = RESUME_CODE; + break; + case CMD_SUSPEND: + code = SUSPEND_CODE; + break; + default: return -EINVAL; + } +run_once_again: + ret = pct1812_i2c_write(ts, SW_RESET_REG, &code, sizeof(code)); + if (ret) { + dev_err(ts->dev, "%s: Error sending cmd: %d (%d)\n", __func__, command, ret); + return ret; + } + if (!counter++) + pct1812_delay_ms(10); + if (code == RESET_CODE) { + code = RESUME_CODE; + goto run_once_again; + } + + return ret; +} + +static int pct1812_queued_resume(struct pct1812_data *ts) +{ + int ret; + + if (atomic_cmpxchg(&ts->touch_stopped, 1, 0) == 0) + return 0; + + pct1812_run_cmd(ts, CMD_RESUME); + ret = pct1812_wait4ready(ts, BOOT_STATUS_REG, BOOT_COMPLETE); + if (!ret) { // set active flag and irq + } + + return ret; +} + +static void pct1812_work(struct work_struct *work) +{ + struct pct1812_data *ts = container_of(work, struct pct1812_data, worker.work); + unsigned char status = 0; + static unsigned char prev; + int cmd, ret; + + while (kfifo_get(&ts->cmd_pipe, &cmd)) { + //cmd = get_cmd(); + switch (cmd) { + case CMD_WDOG: + ret = pct1812_i2c_read(ts, FRAME_NUM_REG, &status, sizeof(status)); + if (ret || (prev == status)) { + dev_warn(ts->dev, "%s: Possible lockup\n", __func__); + } + break; + case CMD_RECOVER: + ret = pct1812_run_cmd(ts, CMD_RESET); + if (ret) { + dev_err(ts->dev, "%s: Failed to reset\n", __func__); + } else { + ret = pct1812_wait4ready(ts, BOOT_STATUS_REG, BOOT_COMPLETE); + if (ret == -ETIME) + dev_err(ts->dev, "%s: Failed to init\n", __func__); + } + break; + case CMD_RESUME: + ret = pct1812_queued_resume(ts); + if (ret) + dev_err(ts->dev, "%s: Failed to resume\n", __func__); + break; + } + } + + pct1812_fifo_cmd_add(ts, CMD_WDOG, WDOG_INTERVAL); +} +#if 0 +static int pct1812_suspend(struct device *dev) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + int ret; + + if (atomic_cmpxchg(&ts->touch_stopped, 0, 1) == 1) + return 0; + + ret = pct1812_run_cmd(ts, CMD_SUSPEND); + if (!ret) { // set active flag and irq + } + + return ret; +} + +static int pct1812_resume(struct device *dev) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + + //atomic_set(&ts->resume_should_stop, 0); + kfifo_put(&ts->cmd_pipe, CMD_RESUME); + /* schedule_delayed_work returns true if work has been scheduled */ + /* and false otherwise, thus return 0 on success to comply POSIX */ + return schedule_delayed_work(&ts->worker, 0) == false; +} +#endif + +static int pct1812_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct pct1812_data *ts; + struct pct1812_platform *pdata; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "%s: EIO err!!!\n", __func__); + return -EIO; + } + + ts = kzalloc(sizeof(struct pct1812_data), GFP_KERNEL); + if (!ts) + goto error_allocate_mem; + + if (!client->dev.of_node) { + dev_err(&client->dev, "%s: Failed to locate dt\n", __func__); + goto error_allocate_mem; + } + + pdata = devm_kzalloc(&client->dev, sizeof(struct pct1812_platform), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "%s: Failed to allocate platform data\n", __func__); + goto error_allocate_pdata; + } + + client->dev.platform_data = pdata; + ret = pct1812_parse_dt(client); + if (ret) { + dev_err(&client->dev, "%s: Failed to parse dt\n", __func__); + goto error_allocate_mem; + } + + ts->client = client; + ts->dev = &client->dev; + ts->plat_data = pdata; + + i2c_set_clientdata(client, ts); + dev_set_drvdata(&client->dev, ts); + + pdata->pinctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR(pdata->pinctrl)) { + dev_warn(&client->dev, "%s: no pinctrl found\n", __func__); + } else { + struct pinctrl_state *state; + + state = pinctrl_lookup_state(pdata->pinctrl, "on_state"); + if (IS_ERR(state)) + dev_err(&client->dev, "%s: could not get active pinstate\n", __func__); + } + + mutex_init(&ts->i2c_mutex); + mutex_init(&ts->cmdlock); + mutex_init(&ts->eventlock); + + INIT_DELAYED_WORK(&ts->worker, pct1812_work); + + pct1812_pinctrl_init(ts); + pct1812_pinctrl_state(ts, true); + + pct1812_regulator(ts, true); + pct1812_power(ts, true); + pct1812_delay_ms(TOUCH_RESET_DELAY); + + ret = pct1812_wait4ready(ts, BOOT_STATUS_REG, BOOT_COMPLETE); + if (ret == -ETIME) { + dev_err(&client->dev, "%s: Failed to init\n", __func__); + goto error_init; + } + + ret = kfifo_alloc(&ts->cmd_pipe, sizeof(unsigned int)* 10, GFP_KERNEL); + if (ret) + goto error_init; + + ret = request_threaded_irq(client->irq, NULL, pct1812_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, MYNAME, ts); + if (ret < 0) { + dev_err(&client->dev, "%s: Unable to request threaded irq\n", __func__); + goto error_fifo_alloc; + } + + /* prevent unbalanced irq enable */ + ts->irq_enabled = true; + dev_info(&client->dev, "%s: done\n", __func__); + + pct1812_fifo_cmd_add(ts, CMD_WDOG, WDOG_INTERVAL); + + return 0; + +error_fifo_alloc: + kfifo_free(&ts->cmd_pipe); + +error_init: + pct1812_power(ts, false); + pct1812_regulator(ts, false); + +error_allocate_mem: +error_allocate_pdata: + + dev_err(&client->dev, "%s: failed(%d)\n", __func__, ret); + + return ret; +} + +static const struct i2c_device_id pct1812_id[] = { + { MYNAME, 0 }, + { }, +}; + +static const struct of_device_id pct1812_match_table[] = { + { .compatible = "pixart,pct1812_ts",}, + { }, +}; + +static struct i2c_driver pct1812_driver = { + .driver = { + .owner = THIS_MODULE, + .name = MYNAME, + .of_match_table = pct1812_match_table, + }, + .probe = pct1812_probe, + .id_table = pct1812_id, +}; + +module_i2c_driver(pct1812_driver); + +MODULE_AUTHOR("Konstantin Makariev "); +MODULE_DESCRIPTION("I2C Driver for PixArt Imaging PCT1812FF"); +MODULE_LICENSE("GPL v2");