3156 lines
83 KiB
C
3156 lines
83 KiB
C
/*
|
|
* wacom_i2c.c - Wacom Digitizer Controller (I2C bus)
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/input.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/sec_sysfs.h>
|
|
#include <linux/usb/manager/usb_typec_manager_notifier.h>
|
|
|
|
#include "wacom.h"
|
|
|
|
#define WACOM_FW_SIZE 131072
|
|
|
|
#define COM_QUERY_RETRY 3
|
|
#define WACOM_I2C_RETRY 3
|
|
|
|
#define WACOM_FW_PATH_SDCARD "/sdcard/FIRMWARE/WACOM/wacom_firm.fw"
|
|
|
|
|
|
static struct wacom_features wacom_feature_EMR = {
|
|
.comstat = COM_QUERY,
|
|
.fw_version = 0x0,
|
|
.update_status = FW_UPDATE_PASS,
|
|
};
|
|
|
|
struct wacom_i2c *wacom_get_drv_data(void *data)
|
|
{
|
|
static void *drv_data;
|
|
|
|
if (unlikely(data))
|
|
drv_data = data;
|
|
|
|
return (struct wacom_i2c *)drv_data;
|
|
}
|
|
|
|
int coordc;
|
|
|
|
void forced_release(struct wacom_i2c *wac_i2c)
|
|
{
|
|
input_info(true, &wac_i2c->client->dev, "%s\n", __func__);
|
|
|
|
if (wac_i2c->dex_mode & DEX_MODE_MOUSE) {
|
|
input_report_key(wac_i2c->input_dev, BTN_LEFT, 0);
|
|
input_report_rel(wac_i2c->input_dev, REL_X, 0);
|
|
input_report_rel(wac_i2c->input_dev, REL_Y, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_RIGHT, 0);
|
|
} else {
|
|
input_report_abs(wac_i2c->input_dev, ABS_X, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_Y, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
|
|
}
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOOL_RUBBER, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOOL_PEN, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
wac_i2c->pen_prox = 0;
|
|
wac_i2c->pen_pressed = 0;
|
|
wac_i2c->side_pressed = 0;
|
|
wac_i2c->mcount = 0;
|
|
wac_i2c->virtual_tracking = EPEN_POS_NONE;
|
|
/*wac_i2c->pen_pdct = PDCT_NOSIGNAL; */
|
|
}
|
|
|
|
int wacom_i2c_send(struct wacom_i2c *wac_i2c,
|
|
const char *buf, int count, bool mode)
|
|
{
|
|
struct i2c_client *client = mode ? wac_i2c->client_boot : wac_i2c->client;
|
|
int retry = WACOM_I2C_RETRY;
|
|
int ret;
|
|
|
|
do {
|
|
if (!wac_i2c->power_enable) {
|
|
input_err(true, &wac_i2c->client->dev, "%s: Power status off\n",
|
|
__func__);
|
|
return -EIO;
|
|
}
|
|
|
|
ret = i2c_master_send(client, buf, count);
|
|
if (ret == count)
|
|
break;
|
|
|
|
if (retry < WACOM_I2C_RETRY) {
|
|
input_err(true, &wac_i2c->client->dev, "%s: I2C retry(%d)\n",
|
|
__func__, WACOM_I2C_RETRY - retry);
|
|
wac_i2c->i2c_fail_count++;
|
|
}
|
|
} while (--retry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wacom_i2c_recv(struct wacom_i2c *wac_i2c, char *buf, int count, bool mode)
|
|
{
|
|
struct i2c_client *client = mode ? wac_i2c->client_boot : wac_i2c->client;
|
|
int retry = WACOM_I2C_RETRY;
|
|
int ret;
|
|
|
|
do {
|
|
if (!wac_i2c->power_enable) {
|
|
input_err(true, &wac_i2c->client->dev, "%s: Power status off\n",
|
|
__func__);
|
|
return -EIO;
|
|
}
|
|
|
|
ret = i2c_master_recv(client, buf, count);
|
|
if (ret == count)
|
|
break;
|
|
|
|
if (retry < WACOM_I2C_RETRY) {
|
|
input_err(true, &wac_i2c->client->dev, "%s: I2C retry(%d)\n",
|
|
__func__, WACOM_I2C_RETRY - retry);
|
|
wac_i2c->i2c_fail_count++;
|
|
}
|
|
} while (--retry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wacom_checksum(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int ret = 0, retry = 10;
|
|
int i = 0;
|
|
u8 buf[5] = { 0, };
|
|
|
|
buf[0] = COM_CHECKSUM;
|
|
|
|
while (retry--) {
|
|
ret = wacom_i2c_send(wac_i2c, &buf[0], 1,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "i2c fail, retry, %d\n",
|
|
__LINE__);
|
|
continue;
|
|
}
|
|
|
|
msleep(200);
|
|
|
|
ret = wacom_i2c_recv(wac_i2c, buf, 5, WACOM_I2C_MODE_NORMAL);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "i2c fail, retry, %d\n",
|
|
__LINE__);
|
|
continue;
|
|
}
|
|
|
|
if (buf[0] == 0x1f)
|
|
break;
|
|
|
|
input_info(true, &client->dev, "buf[0]: 0x%x, checksum retry\n",
|
|
buf[0]);
|
|
}
|
|
|
|
if (ret >= 0) {
|
|
input_info(true, &client->dev,
|
|
"received checksum %x, %x, %x, %x, %x\n", buf[0],
|
|
buf[1], buf[2], buf[3], buf[4]);
|
|
}
|
|
|
|
for (i = 0; i < 5; ++i) {
|
|
if (buf[i] != wac_i2c->fw_chksum[i]) {
|
|
input_info(true, &client->dev,
|
|
"checksum fail %dth %x %x\n", i, buf[i],
|
|
wac_i2c->fw_chksum[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
wac_i2c->checksum_result = (i == 5);
|
|
|
|
return wac_i2c->checksum_result;
|
|
}
|
|
|
|
int wacom_i2c_query(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
struct wacom_features *wac_feature = wac_i2c->wac_feature;
|
|
struct i2c_client *client = wac_i2c->client;
|
|
u8 data[COM_QUERY_BUFFER] = { 0, };
|
|
u8 *query = data + COM_QUERY_POS;
|
|
int read_size = COM_QUERY_BUFFER;
|
|
int ret;
|
|
int i;
|
|
int max_x, max_y, pressure, height, x_tilt, y_tilt;
|
|
|
|
for (i = 0; i < COM_QUERY_RETRY; i++) {
|
|
ret = wacom_i2c_recv(wac_i2c, data, read_size,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "%s: failed to recv\n",
|
|
__func__);
|
|
continue;
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"%s: %dth ret of wacom query=%d\n", __func__, i,
|
|
ret);
|
|
|
|
if (read_size != ret) {
|
|
input_err(true, &client->dev,
|
|
"%s: read size error %d of %d\n", __func__,
|
|
ret, read_size);
|
|
continue;
|
|
}
|
|
|
|
if (query[EPEN_REG_HEADER] == 0x0f) {
|
|
wac_feature->fw_version =
|
|
((u16)query[EPEN_REG_FWVER1] << 8) +
|
|
query[EPEN_REG_FWVER2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"%X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X\n",
|
|
query[0], query[1], query[2], query[3], query[4],
|
|
query[5], query[6], query[7], query[8], query[9], query[10],
|
|
query[11], query[12], query[13], query[14]);
|
|
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "%s: failed to read query\n",
|
|
__func__);
|
|
wac_feature->fw_version = 0;
|
|
wac_i2c->query_status = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
wac_i2c->query_status = true;
|
|
|
|
max_x = ((u16)query[EPEN_REG_X1] << 8) + query[EPEN_REG_X2];
|
|
max_y = ((u16)query[EPEN_REG_Y1] << 8) + query[EPEN_REG_Y2];
|
|
pressure =
|
|
((u16)query[EPEN_REG_PRESSURE1] << 8) + query[EPEN_REG_PRESSURE2];
|
|
x_tilt = query[EPEN_REG_TILT_X];
|
|
y_tilt = query[EPEN_REG_TILT_Y];
|
|
height = query[EPEN_REG_HEIGHT];
|
|
|
|
if (!pdata->use_dt_coord) {
|
|
pdata->max_x = max_x;
|
|
pdata->max_y = max_y;
|
|
}
|
|
pdata->max_pressure = pressure;
|
|
pdata->max_x_tilt = x_tilt;
|
|
pdata->max_y_tilt = y_tilt;
|
|
pdata->max_height = height;
|
|
|
|
input_info(true, &client->dev, "use_dt_coord=%d, max_x=%d max_y=%d, max_pressure=%d\n",
|
|
pdata->use_dt_coord, pdata->max_x, pdata->max_y, pdata->max_pressure);
|
|
input_info(true, &client->dev, "fw_version=0x%X\n",
|
|
wac_feature->fw_version);
|
|
input_info(true, &client->dev, "mpu %#x, bl %#x, tx %d, ty %d, h %d\n",
|
|
query[EPEN_REG_MPUVER], query[EPEN_REG_BLVER], x_tilt,
|
|
y_tilt, height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wacom_i2c_set_sense_mode(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int retval;
|
|
char data[4] = { 0, 0, 0, 0 };
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s cmd mod(%d)\n", __func__,
|
|
wac_i2c->wcharging_mode);
|
|
|
|
if (wac_i2c->wcharging_mode == 1)
|
|
data[0] = COM_LOW_SENSE_MODE;
|
|
else if (wac_i2c->wcharging_mode == 3)
|
|
data[0] = COM_LOW_SENSE_MODE2;
|
|
else {
|
|
/* it must be 0 */
|
|
data[0] = COM_NORMAL_SENSE_MODE;
|
|
}
|
|
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to send wacom i2c mode, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
msleep(60);
|
|
|
|
data[0] = COM_SAMPLERATE_STOP;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send1, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
msleep(60);
|
|
|
|
#if 0 /* temp block not to receive gabage irq by cmd */
|
|
data[1] = COM_REQUEST_SENSE_MODE;
|
|
retval = wacom_i2c_send(wac_i2c, &data[1], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send2, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
msleep(60);
|
|
|
|
retval = wacom_i2c_recv(wac_i2c, &data[2], 2, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 2) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c recv, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
input_info(true, &client->dev, "%s: mode:%X, %X\n", __func__, data[2],
|
|
data[3]);
|
|
|
|
data[0] = COM_SAMPLERATE_STOP;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send3, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
msleep(60);
|
|
#endif
|
|
data[0] = COM_SAMPLERATE_START;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send4, %d\n", __func__,
|
|
retval);
|
|
return retval;
|
|
}
|
|
|
|
msleep(60);
|
|
|
|
return data[3];
|
|
}
|
|
|
|
void wacom_select_survey_mode(struct wacom_i2c *wac_i2c, bool enable)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
|
|
if (enable) {
|
|
if (wac_i2c->epen_blocked ||
|
|
(wac_i2c->battery_saving_mode && !(wac_i2c->function_result & EPEN_EVENT_PEN_OUT))) {
|
|
if (wac_i2c->pdata->use_garage) {
|
|
input_info(true, &client->dev,
|
|
"%s: %s & garage on. garage only mode\n",
|
|
__func__,
|
|
wac_i2c->epen_blocked ? "epen blocked" : "ps on & pen in");
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_GARAGE_ONLY);
|
|
} else {
|
|
input_info(true, &client->dev,
|
|
"%s: %s & garage off. power off\n", __func__,
|
|
wac_i2c->epen_blocked ? "epen blocked" : "ps on & pen in");
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
wacom_power(wac_i2c, false);
|
|
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
}
|
|
} else if (wac_i2c->survey_mode) {
|
|
input_info(true, &client->dev, "%s: exit aop mode\n",
|
|
__func__);
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_NONE);
|
|
} else {
|
|
input_info(true, &client->dev, "%s: power on\n",
|
|
__func__);
|
|
|
|
wacom_power(wac_i2c, true);
|
|
msleep(100);
|
|
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
}
|
|
} else {
|
|
if (wac_i2c->epen_blocked ||
|
|
(wac_i2c->battery_saving_mode && !(wac_i2c->function_result & EPEN_EVENT_PEN_OUT))) {
|
|
if (wac_i2c->pdata->use_garage) {
|
|
input_info(true, &client->dev,
|
|
"%s: %s & garage on. garage only mode\n",
|
|
__func__,
|
|
wac_i2c->epen_blocked ? "epen blocked" : "ps on & pen in");
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_GARAGE_ONLY);
|
|
} else {
|
|
input_info(true, &client->dev,
|
|
"%s: %s & garage off. power off\n", __func__,
|
|
wac_i2c->epen_blocked ? "epen blocked" : "ps on & pen in");
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
wacom_power(wac_i2c, false);
|
|
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
}
|
|
} else if (!(wac_i2c->function_set & EPEN_SETMODE_AOP)) {
|
|
if (wac_i2c->pdata->use_garage) {
|
|
input_info(true, &client->dev,
|
|
"%s: aop off & garage on. garage only mode\n",
|
|
__func__);
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_GARAGE_ONLY);
|
|
} else {
|
|
input_info(true, &client->dev,
|
|
"%s: aop off & garage off. power off\n",
|
|
__func__);
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
wacom_power(wac_i2c, false);
|
|
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
}
|
|
} else {
|
|
/* aop on => (aod : screen off memo = 1:1 or 1:0 or 0:1)
|
|
* double tab & hover + button event will be occurred,
|
|
* but some of them will be skipped at reporting by mode
|
|
*/
|
|
input_info(true, &client->dev,
|
|
"%s: aop on. aop mode\n", __func__);
|
|
|
|
if (!wac_i2c->power_enable) {
|
|
input_info(true, &client->dev, "%s: power on\n",
|
|
__func__);
|
|
|
|
wacom_power(wac_i2c, true);
|
|
msleep(100);
|
|
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
}
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_GARAGE_AOP);
|
|
}
|
|
}
|
|
|
|
if (wac_i2c->power_enable) {
|
|
input_info(true, &client->dev, "%s: screen %s, survey mode:%d, result:%d\n",
|
|
__func__, enable ? "on" : "off",
|
|
wac_i2c->survey_mode,
|
|
wac_i2c->function_result & EPEN_EVENT_SURVEY);
|
|
|
|
if ((wac_i2c->function_result & EPEN_EVENT_SURVEY) != wac_i2c->survey_mode) {
|
|
input_err(true, &client->dev, "%s: survey mode cmd failed\n",
|
|
__func__);
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
wac_i2c->survey_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wacom_i2c_set_survey_mode(struct wacom_i2c *wac_i2c, int mode)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int retval;
|
|
char data[4] = { 0, 0, 0, 0 };
|
|
|
|
switch (mode) {
|
|
case EPEN_SURVEY_MODE_NONE:
|
|
data[0] = COM_SURVEYEXIT;
|
|
break;
|
|
case EPEN_SURVEY_MODE_GARAGE_ONLY:
|
|
if (!wac_i2c->pdata->use_garage) {
|
|
input_err(true, &client->dev,
|
|
"%s: garage mode is not supported\n", __func__);
|
|
return;
|
|
}
|
|
|
|
data[0] = COM_SURVEY_TARGET_GARAGEONLY;
|
|
break;
|
|
case EPEN_SURVEY_MODE_GARAGE_AOP:
|
|
data[0] = COM_SURVEYSCAN;
|
|
break;
|
|
default:
|
|
input_err(true, &client->dev,
|
|
"%s: wrong param %d\n", __func__, mode);
|
|
return;
|
|
}
|
|
|
|
wac_i2c->survey_mode = mode;
|
|
input_info(true, &client->dev, "%s: ps %s & mode : %d cmd(0x%2X)\n", __func__,
|
|
wac_i2c->battery_saving_mode ? "on" : "off", mode, data[0]);
|
|
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send survey, %d\n",
|
|
__func__, retval);
|
|
wac_i2c->reset_flag = true;
|
|
|
|
return;
|
|
}
|
|
|
|
if (mode)
|
|
msleep(35);
|
|
|
|
wac_i2c->reset_flag = false;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
wac_i2c->function_result |= mode;
|
|
|
|
return;
|
|
}
|
|
|
|
static int keycode[] = {
|
|
KEY_RECENT, KEY_BACK,
|
|
};
|
|
|
|
void forced_release_key(struct wacom_i2c *wac_i2c)
|
|
{
|
|
input_info(true, &wac_i2c->client->dev, "%s : [%x/%x]!\n",
|
|
__func__, wac_i2c->soft_key_pressed[0],
|
|
wac_i2c->soft_key_pressed[1]);
|
|
|
|
input_report_key(wac_i2c->input_dev, KEY_RECENT, 0);
|
|
input_report_key(wac_i2c->input_dev, KEY_BACK, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
}
|
|
|
|
void wacom_i2c_softkey(struct wacom_i2c *wac_i2c, s16 key, s16 pressed)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
if (wac_i2c->block_softkey && pressed) {
|
|
cancel_delayed_work_sync(&wac_i2c->softkey_block_work);
|
|
input_info(true, &client->dev, "block p\n");
|
|
return;
|
|
} else if (wac_i2c->block_softkey && !pressed) {
|
|
input_info(true, &client->dev, "block r\n");
|
|
wac_i2c->block_softkey = false;
|
|
return;
|
|
}
|
|
#endif
|
|
input_report_key(wac_i2c->input_dev, keycode[key], pressed);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
wac_i2c->soft_key_pressed[key] = pressed;
|
|
if (pressed)
|
|
cancel_delayed_work_sync(&wac_i2c->fullscan_check_work);
|
|
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
input_info(true, &client->dev, "%d %s\n",
|
|
keycode[key], !(!pressed) ? "PRESS" : "RELEASE");
|
|
#else
|
|
input_info(true, &client->dev, "softkey %s\n",
|
|
!(!pressed) ? "PRESS" : "RELEASE");
|
|
#endif
|
|
}
|
|
|
|
void forced_release_fullscan(struct wacom_i2c *wac_i2c)
|
|
{
|
|
input_info(true, &wac_i2c->client->dev, "%s full scan OUT\n", __func__);
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
wac_i2c->fullscan_mode = false;
|
|
}
|
|
|
|
int wacom_get_scan_mode(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int retval;
|
|
char data[COM_COORD_NUM + 1] = { 0, };
|
|
int i, retry = 3, temp = 0, ret = 0;
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s\n", __func__);
|
|
|
|
data[0] = COM_SAMPLERATE_STOP;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send (stop), %d\n",
|
|
__func__, retval);
|
|
return -EIO;
|
|
}
|
|
|
|
data[0] = COM_REQUESTSCANMODE;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send (request data), %d\n",
|
|
__func__, retval);
|
|
return -EIO;
|
|
}
|
|
msleep(35);
|
|
|
|
while (retry--) {
|
|
retval = wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM + 1,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (retval < 0) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send survey, %d\n",
|
|
__func__, retval);
|
|
}
|
|
temp = 0;
|
|
for (i = 0; i <= COM_COORD_NUM; i++)
|
|
temp += data[i];
|
|
|
|
input_info(true, &client->dev,
|
|
"%x %x %x %x %x, %x %x %x %x %x, %x %x %x\n",
|
|
data[0], data[1], data[2], data[3], data[4], data[5],
|
|
data[6], data[7], data[8], data[9], data[10],
|
|
data[11], data[12]);
|
|
|
|
if (temp == 0) { /* unlock */
|
|
ret = EPEN_GLOBAL_SCAN_MODE;
|
|
break;
|
|
} else if (data[12] == 0x01) { /* send noise mode to tsp */
|
|
ret = EPEN_HIGH_NOISE_MODE;
|
|
break;
|
|
}
|
|
msleep(10);
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"data[0] = %x, data[12] =%x, retry(%d) ret(%d)\n", data[0],
|
|
data[12], 3 - retry, ret);
|
|
|
|
data[0] = COM_SAMPLERATE_133;
|
|
retval = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (retval != 1) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read wacom i2c send (start), %d\n",
|
|
__func__, retval);
|
|
return -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool wacom_get_status_data(struct wacom_i2c *wac_i2c, char *data)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int retval;
|
|
int retry = 5;
|
|
bool ret = false;
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s\n", __func__);
|
|
|
|
while (retry--) {
|
|
retval =
|
|
wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM + 1,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (retval < 0) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to read status data, %d\n",
|
|
__func__, retval);
|
|
}
|
|
|
|
if (data[10] != data[11]) {
|
|
input_err(true, &client->dev,
|
|
"%s: invalid status data\n", __func__);
|
|
break;
|
|
}
|
|
|
|
/* AOP status : BB BB = Button+Hover, DD DD = Double Tab gesture
|
|
* Noise status : 11 11 = High noise, 22 22 = Low noise
|
|
*/
|
|
if ((data[10] == AOP_BUTTON_HOVER) || (data[10] == AOP_DOUBLE_TAB) ||
|
|
(data[10] == WACOM_NOISE_HIGH) || (data[10] == WACOM_NOISE_LOW)) {
|
|
ret = true;
|
|
break;
|
|
}
|
|
|
|
/* READ IRQ status */
|
|
if (wacom_get_irq_state(wac_i2c) <= 0)
|
|
break;
|
|
|
|
msleep(10);
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"data[10] = %x data[11] = %x, retry(%d), %s\n", data[10],
|
|
data[11], 5 - retry, ret ? "success" : "failed");
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef LCD_FREQ_SYNC
|
|
void wacom_i2c_lcd_freq_check(struct wacom_i2c *wac_i2c, u8 *data)
|
|
{
|
|
u32 lcd_freq = 0;
|
|
|
|
if (wac_i2c->lcd_freq_wait == false) {
|
|
lcd_freq = ((u16) data[10] << 8) + (u16) data[11];
|
|
wac_i2c->lcd_freq = 2000000000 / (lcd_freq + 1);
|
|
wac_i2c->lcd_freq_wait = true;
|
|
schedule_work(&wac_i2c->lcd_freq_work);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void wacom_fullscan_check_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, fullscan_check_work.work);
|
|
int ret;
|
|
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"fullscan_check_work irq-cnt(%d)\n", coordc);
|
|
if (coordc <= 2) {
|
|
ret = wacom_get_scan_mode(wac_i2c);
|
|
if (ret < 0) {
|
|
input_info(true, &wac_i2c->client->dev, "work - stay scan mode\n");
|
|
} else if (ret == EPEN_GLOBAL_SCAN_MODE) {
|
|
input_info(true, &wac_i2c->client->dev, "work - full scan OUT\n");
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
wac_i2c->fullscan_mode = false;
|
|
} else if (ret == EPEN_HIGH_NOISE_MODE) {
|
|
input_info(true, &wac_i2c->client->dev, "work - wacom noise mode\n");
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_HIGH_NOISE_MODE); */
|
|
} else {
|
|
input_err(true, &wac_i2c->client->dev, "unexpected status\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
int wacom_i2c_coord(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
struct i2c_client *client = wac_i2c->client;
|
|
u8 data[COM_COORD_NUM + 1] = { 0, };
|
|
bool prox = false;
|
|
bool rdy = false;
|
|
int ret = 0, tsp = 0;
|
|
int stylus;
|
|
static int old_x = 0, old_y = 0;
|
|
s16 x, y, pressure;
|
|
s16 tmp;
|
|
u8 gain = 0;
|
|
s16 softkey, pressed, keycode;
|
|
s8 tilt_x = 0;
|
|
s8 tilt_y = 0;
|
|
s8 retry = 3;
|
|
|
|
while (retry--) {
|
|
ret = wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM + 1,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (ret >= 0)
|
|
break;
|
|
|
|
input_err(true, &client->dev,
|
|
"%s failed to read i2c.retry %d.L%d\n", __func__,
|
|
retry, __LINE__);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "i2c err, exit %s\n", __func__);
|
|
forced_release(wac_i2c);
|
|
wac_i2c->reset_flag = true;
|
|
if (work_busy(&wac_i2c->resume_work.work) == 0) {
|
|
input_err(true, &client->dev,
|
|
"%s schedule resume work\n", __func__);
|
|
cancel_delayed_work(&wac_i2c->resume_work);
|
|
schedule_delayed_work(&wac_i2c->resume_work,
|
|
msecs_to_jiffies(EPEN_RESUME_DELAY));
|
|
} else {
|
|
input_err(true, &client->dev,
|
|
"%s resume work is busy\n", __func__);
|
|
}
|
|
return 0;
|
|
}
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
#if 0
|
|
input_info(true, &client->dev,
|
|
"%x, %x, %x, %x, %x, %x, %x %x %x %x %x %x %x\n", data[0],
|
|
data[1], data[2], data[3], data[4], data[5], data[6],
|
|
data[7], data[8], data[9], data[10], data[11], data[12]);
|
|
#endif
|
|
#endif
|
|
|
|
rdy = data[0] & 0x80;
|
|
|
|
#ifdef LCD_FREQ_SYNC
|
|
if (!rdy && !data[1] && !data[2] && !data[3] && !data[4]) {
|
|
if (likely(wac_i2c->use_lcd_freq_sync)) {
|
|
if (unlikely(!wac_i2c->pen_prox))
|
|
wacom_i2c_lcd_freq_check(wac_i2c, data);
|
|
}
|
|
}
|
|
#endif
|
|
if (coordc < 100)
|
|
coordc++;
|
|
|
|
tsp = data[12] & 0x01;
|
|
|
|
if (wac_i2c->pdata->table_swap && data[0] == TABLE_SWAP_DATA &&
|
|
data[2] == 0 && data[3] == 0 && data[4] == 0) {
|
|
wac_i2c->dp_connect_state = data[1];
|
|
input_info(true, &client->dev,
|
|
"%s: usb typec DP %sconnected\n",
|
|
__func__, wac_i2c->dp_connect_state ? "" : "dis");
|
|
}
|
|
|
|
if (!rdy && tsp && wac_i2c->fullscan_mode == false) {
|
|
input_info(true, &client->dev,
|
|
"%x, %x, %x, %x, %x, %x, %x %x %x %x %x %x %x coordc(%d)\n",
|
|
data[0], data[1], data[2], data[3], data[4], data[5],
|
|
data[6], data[7], data[8], data[9], data[10],
|
|
data[11], data[12], coordc);
|
|
if (data[10] == HSYNC_COUNTER_UMAGIC && data[11] == HSYNC_COUNTER_LMAGIC) {
|
|
input_info(true, &client->dev,
|
|
"x-y scan IN, rdy(%d) tsp(%d)\n", rdy, tsp);
|
|
wac_i2c->fullscan_mode = true;
|
|
if (wac_i2c->wacom_noise_state != WACOM_NOISE_HIGH) {
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_LOCAL_SCAN_MODE); */
|
|
} else {
|
|
input_info(true, &client->dev, "high noise mode, skip tsp (F3 1)\n");
|
|
}
|
|
|
|
coordc = 0;
|
|
cancel_delayed_work_sync(&wac_i2c->fullscan_check_work);
|
|
schedule_delayed_work(&wac_i2c->fullscan_check_work,
|
|
msecs_to_jiffies(2000));
|
|
}
|
|
}
|
|
|
|
if (data[0] == 0x0F) {
|
|
input_info(true, &client->dev,
|
|
"%x %x %x %x %x, %x %x %x %x %x, %x %x %x\n",
|
|
data[0], data[1], data[2], data[3], data[4], data[5],
|
|
data[6], data[7], data[8], data[9], data[10],
|
|
data[11], data[12]);
|
|
if ((data[10] == WACOM_NOISE_HIGH) && (data[11] == WACOM_NOISE_HIGH)) {
|
|
input_err(true, &wac_i2c->client->dev, "11 11 high-noise mode\n");
|
|
wac_i2c->wacom_noise_state = WACOM_NOISE_HIGH;
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_HIGH_NOISE_MODE); */
|
|
} else if ((data[10] == WACOM_NOISE_LOW) && (data[11] == WACOM_NOISE_LOW)) {
|
|
input_err(true, &wac_i2c->client->dev, "22 22 low-noise mode\n");
|
|
wac_i2c->wacom_noise_state = WACOM_NOISE_LOW;
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
}
|
|
}
|
|
|
|
if (rdy) {
|
|
/* checking softkey */
|
|
softkey = !(!(data[12] & 0x80));
|
|
if (unlikely(softkey)) {
|
|
if (unlikely(wac_i2c->pen_prox))
|
|
forced_release(wac_i2c);
|
|
|
|
pressed = !(!(data[12] & 0x40));
|
|
keycode = (data[12] & 0x30) >> 4;
|
|
|
|
if (!wac_i2c->pdata->use_virtual_softkey)
|
|
wacom_i2c_softkey(wac_i2c, keycode, pressed);
|
|
else
|
|
input_info(true, &client->dev, "wacom use virtual softkey\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
prox = !(!(data[0] & 0x10));
|
|
stylus = !(!(data[0] & 0x20));
|
|
x = ((u16) data[1] << 8) + (u16) data[2];
|
|
y = ((u16) data[3] << 8) + (u16) data[4];
|
|
pressure = ((u16) data[5] << 8) + (u16) data[6];
|
|
gain = data[7];
|
|
tilt_x = (s8) data[9];
|
|
tilt_y = -(s8) data[8];
|
|
|
|
/* origin */
|
|
x = x - pdata->origin[0];
|
|
y = y - pdata->origin[1];
|
|
|
|
/* change axis from wacom to lcd */
|
|
if (pdata->x_invert)
|
|
x = pdata->max_x - x;
|
|
if (pdata->y_invert)
|
|
y = pdata->max_y - y;
|
|
|
|
if (pdata->xy_switch) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
if (wac_i2c->keyboard_cover_mode == true) {
|
|
if (y > KEYBOARD_COVER_BOUNDARY)
|
|
wac_i2c->keyboard_area = true;
|
|
else
|
|
wac_i2c->keyboard_area = false;
|
|
|
|
if (wac_i2c->keyboard_area == true &&
|
|
wac_i2c->virtual_tracking == EPEN_POS_NONE) {
|
|
/* input_info(true, &client->dev,
|
|
*"skip - approching on keyboard area prox(%d)\n", prox);
|
|
*/
|
|
return 0;
|
|
} else if (wac_i2c->keyboard_area == true &&
|
|
wac_i2c->virtual_tracking == EPEN_POS_COVER) {
|
|
/*input_info(true, &client->dev,
|
|
*"skip keyboard area prox(%d)\n", prox);
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/*input_info(true, &client->dev,
|
|
*"go through, prox(%d) y(%d) area(%d)\n", prox,y,wac_i2c->keyboard_area);
|
|
*/
|
|
}
|
|
|
|
/* validation check */
|
|
if (unlikely
|
|
(x < 0 || y < 0 || x > pdata->max_y || y > pdata->max_x)) {
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
input_info(true, &client->dev, "raw data x=%d, y=%d\n",
|
|
x, y);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (wac_i2c->dex_mode & DEX_MODE_EDGE_CROP) {
|
|
int max_x = wac_i2c->pdata->max_x;
|
|
if (pdata->xy_switch)
|
|
max_x = wac_i2c->pdata->max_y;
|
|
|
|
if (wac_i2c->dex_mode & DEX_MODE_MOUSE) {
|
|
if (x < max_x / 10)
|
|
x = max_x / 10;
|
|
else if (x > max_x * 9 / 10)
|
|
x = max_x * 9 / 10;
|
|
} else {
|
|
if (x < max_x / 10)
|
|
x = 0;
|
|
else if (x > max_x * 9 / 10)
|
|
x = max_x;
|
|
else
|
|
x = x * 5 / 4 - max_x / 8;
|
|
}
|
|
}
|
|
|
|
if (wac_i2c->dex_mode & DEX_MODE_IRIS) {
|
|
int max_y = wac_i2c->pdata->max_y;
|
|
if (pdata->xy_switch)
|
|
max_y = wac_i2c->pdata->max_x;
|
|
|
|
if (y < max_y / 2) {
|
|
x = old_x;
|
|
y = old_y;
|
|
}
|
|
}
|
|
|
|
if (!wac_i2c->pen_prox) {
|
|
if (!wac_i2c->pdata->use_garage) {
|
|
/* check pdct */
|
|
if (unlikely(wac_i2c->pen_pdct == PDCT_NOSIGNAL)) {
|
|
input_info(true, &client->dev,
|
|
"pdct is not active\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
wac_i2c->pen_prox = 1;
|
|
wac_i2c->virtual_tracking = EPEN_POS_VIEW;
|
|
|
|
if (wac_i2c->dex_mode & DEX_MODE_MOUSE) {
|
|
input_report_rel(wac_i2c->input_dev, REL_X, 0);
|
|
input_report_rel(wac_i2c->input_dev, REL_Y, 0);
|
|
old_x = x;
|
|
old_y = y;
|
|
input_sync(wac_i2c->input_dev);
|
|
}
|
|
|
|
if (data[0] & 0x40)
|
|
wac_i2c->tool = BTN_TOOL_RUBBER;
|
|
else
|
|
wac_i2c->tool = BTN_TOOL_PEN;
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
input_info(true, &client->dev, "hover in(%s)\n",
|
|
wac_i2c->tool ==
|
|
BTN_TOOL_PEN ? "pen" : "rubber");
|
|
#else
|
|
input_info(true, &client->dev, "hover in\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (wac_i2c->keyboard_cover_mode == true &&
|
|
wac_i2c->keyboard_area == true &&
|
|
wac_i2c->virtual_tracking == EPEN_POS_VIEW) {
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
input_report_key(wac_i2c->input_dev,
|
|
BTN_TOOL_RUBBER, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOOL_PEN, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
if (wac_i2c->pen_pressed) {
|
|
cancel_delayed_work_sync
|
|
(&wac_i2c->softkey_block_work);
|
|
wac_i2c->block_softkey = true;
|
|
schedule_delayed_work
|
|
(&wac_i2c->softkey_block_work,
|
|
SOFTKEY_BLOCK_DURATION);
|
|
}
|
|
#endif
|
|
input_info(true, &client->dev,
|
|
"virtual hover out by keyboard cover\n");
|
|
wac_i2c->pen_prox = 0;
|
|
wac_i2c->pen_pressed = 0;
|
|
wac_i2c->side_pressed = 0;
|
|
wac_i2c->virtual_tracking = EPEN_POS_COVER;
|
|
return 0;
|
|
}
|
|
|
|
/* report info */
|
|
if (wac_i2c->dex_mode & DEX_MODE_MOUSE) {
|
|
input_report_key(wac_i2c->input_dev, BTN_LEFT, prox);
|
|
input_report_rel(wac_i2c->input_dev, REL_X,
|
|
x / wac_i2c->pdata->dex_rate -
|
|
(old_x / wac_i2c->pdata->dex_rate));
|
|
input_report_rel(wac_i2c->input_dev, REL_Y,
|
|
y / wac_i2c->pdata->dex_rate -
|
|
(old_y / wac_i2c->pdata->dex_rate));
|
|
input_report_key(wac_i2c->input_dev, BTN_RIGHT, stylus);
|
|
|
|
} else {
|
|
input_report_abs(wac_i2c->input_dev, ABS_X, x);
|
|
input_report_abs(wac_i2c->input_dev, ABS_Y, y);
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, stylus);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, prox);
|
|
}
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, pressure);
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, gain);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_X, tilt_x);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_Y, tilt_y);
|
|
input_report_key(wac_i2c->input_dev, wac_i2c->tool, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
old_x = x;
|
|
old_y = y;
|
|
wac_i2c->mcount++;
|
|
|
|
/* log */
|
|
if (prox && !wac_i2c->pen_pressed) {
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
input_info(true, &client->dev,
|
|
"[P] x:%d, y:%d, mc:%d, pre:%d, tool:%x ps:%d dex:%d dp:%d\n",
|
|
x, y, wac_i2c->mcount, pressure, wac_i2c->tool,
|
|
wac_i2c->battery_saving_mode, wac_i2c->dex_mode, wac_i2c->dp_connect_state);
|
|
#else
|
|
input_info(true, &client->dev, "[P] mc:%d ps:%d dex:%d dp:%d\n",
|
|
wac_i2c->mcount, wac_i2c->battery_saving_mode,
|
|
wac_i2c->dex_mode, wac_i2c->dp_connect_state);
|
|
#endif
|
|
} else if (!prox && wac_i2c->pen_pressed) {
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
input_info(true, &client->dev,
|
|
"[R] x:%d, y:%d, mc:%d pre:%d, tool:%x dex:%d dp:%d [0x%x]\n",
|
|
x, y, wac_i2c->mcount, pressure, wac_i2c->tool,
|
|
wac_i2c->dex_mode, wac_i2c->dp_connect_state,
|
|
wac_i2c->wac_feature->fw_version);
|
|
#else
|
|
input_info(true, &client->dev, "[R] mc:%d dex:%d dp:%d [0x%x]\n",
|
|
wac_i2c->mcount, wac_i2c->dex_mode, wac_i2c->dp_connect_state,
|
|
wac_i2c->wac_feature->fw_version);
|
|
#endif
|
|
}
|
|
wac_i2c->pen_pressed = prox;
|
|
|
|
/* check side */
|
|
if (stylus && !wac_i2c->side_pressed)
|
|
input_info(true, &client->dev, "side on\n");
|
|
else if (!stylus && wac_i2c->side_pressed)
|
|
input_info(true, &client->dev, "side off\n");
|
|
|
|
wac_i2c->side_pressed = stylus;
|
|
} else {
|
|
if (wac_i2c->pen_prox) {
|
|
if (wac_i2c->dex_mode & DEX_MODE_MOUSE) {
|
|
input_report_key(wac_i2c->input_dev, BTN_LEFT, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_RIGHT, 0);
|
|
} else {
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
|
|
}
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
input_report_key(wac_i2c->input_dev,
|
|
BTN_TOOL_RUBBER, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOOL_PEN, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
if (wac_i2c->pen_pressed) {
|
|
cancel_delayed_work_sync
|
|
(&wac_i2c->softkey_block_work);
|
|
wac_i2c->block_softkey = true;
|
|
schedule_delayed_work
|
|
(&wac_i2c->softkey_block_work,
|
|
SOFTKEY_BLOCK_DURATION);
|
|
}
|
|
#endif
|
|
|
|
if (wac_i2c->pen_pressed) {
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
x = ((u16) data[1] << 8) + (u16) data[2];
|
|
y = ((u16) data[3] << 8) + (u16) data[4];
|
|
/* origin */
|
|
x = x - pdata->origin[0];
|
|
y = y - pdata->origin[1];
|
|
|
|
/* change axis from wacom to lcd */
|
|
if (pdata->x_invert)
|
|
x = pdata->max_x - x;
|
|
if (pdata->y_invert)
|
|
y = pdata->max_y - y;
|
|
|
|
if (pdata->xy_switch) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"[R] x:%d, y:%d, mc:%d dex:%d dp:%d [0x%x] & hover out\n",
|
|
x, y, wac_i2c->mcount, wac_i2c->dex_mode,
|
|
wac_i2c->dp_connect_state, wac_i2c->wac_feature->fw_version);
|
|
#else
|
|
input_info(true, &client->dev, "[R] mc:%d dex:%d dp:%d [0x%x] & hover out\n",
|
|
wac_i2c->mcount, wac_i2c->dex_mode,
|
|
wac_i2c->dp_connect_state, wac_i2c->wac_feature->fw_version);
|
|
#endif
|
|
} else {
|
|
input_info(true, &client->dev, "hover out mc:%d\n", wac_i2c->mcount);
|
|
}
|
|
|
|
}
|
|
|
|
if (!tsp) {
|
|
input_info(true, &client->dev, "full scan out (00 00)\n");
|
|
if (wac_i2c->wacom_noise_state != WACOM_NOISE_HIGH) {
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
} else {
|
|
input_info(true, &client->dev, "high noise mode, skip tsp (F3 0)\n");
|
|
}
|
|
|
|
cancel_delayed_work_sync(&wac_i2c->fullscan_check_work);
|
|
wac_i2c->fullscan_mode = false;
|
|
}
|
|
|
|
wac_i2c->pen_prox = 0;
|
|
wac_i2c->pen_pressed = 0;
|
|
wac_i2c->side_pressed = 0;
|
|
wac_i2c->mcount = 0;
|
|
wac_i2c->virtual_tracking = EPEN_POS_NONE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wacom_power(struct wacom_i2c *wac_i2c, bool on)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
struct i2c_client *client = wac_i2c->client;
|
|
static struct timeval off_time = { 0, 0 };
|
|
struct timeval cur_time = { 0, 0 };
|
|
int retval = 0;
|
|
static struct regulator *vddo;
|
|
|
|
input_info(true, &client->dev, "power %s\n",
|
|
on ? "enabled" : "disabled");
|
|
|
|
if (!pdata) {
|
|
input_err(true, &client->dev, "%s, pdata is null\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (wac_i2c->power_enable == on) {
|
|
input_info(true, &client->dev, "pwr already %s\n",
|
|
on ? "enabled" : "disabled");
|
|
return 0;
|
|
}
|
|
|
|
if (on) {
|
|
long sec, usec;
|
|
|
|
do_gettimeofday(&cur_time);
|
|
sec = cur_time.tv_sec - off_time.tv_sec;
|
|
usec = cur_time.tv_usec - off_time.tv_usec;
|
|
if (!sec) {
|
|
usec = EPEN_OFF_TIME_LIMIT - usec;
|
|
if (usec > 500) {
|
|
usleep_range(usec, usec);
|
|
input_info(true, &client->dev,
|
|
"%s, pwr on usleep %d\n", __func__,
|
|
(int)usec);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!vddo) {
|
|
vddo = devm_regulator_get(&client->dev, "vddo");
|
|
|
|
if (IS_ERR(vddo)) {
|
|
input_err(true, &client->dev,
|
|
"%s: could not get vddo, rc = %ld\n",
|
|
__func__, PTR_ERR(vddo));
|
|
vddo = NULL;
|
|
return -ENODEV;
|
|
}
|
|
retval = regulator_set_voltage(vddo, 3300000, 3300000);
|
|
if (retval)
|
|
input_err(true, &client->dev,
|
|
"%s: unable to set vddo voltage to 3.3V\n",
|
|
__func__);
|
|
input_err(true, &client->dev, "%s: 3.3V is enabled %s\n",
|
|
__func__,
|
|
regulator_is_enabled(vddo) ? "TRUE" : "FALSE");
|
|
|
|
}
|
|
|
|
if (on) {
|
|
retval = regulator_enable(vddo);
|
|
if (retval) {
|
|
input_err(true, &client->dev,
|
|
"%s: Fail to enable regulator vddo[%d]\n",
|
|
__func__, retval);
|
|
}
|
|
input_err(true, &client->dev, "%s: vddo is enabled[OK]\n",
|
|
__func__);
|
|
} else {
|
|
if (regulator_is_enabled(vddo)) {
|
|
retval = regulator_disable(vddo);
|
|
if (retval) {
|
|
input_err(true, &client->dev,
|
|
"%s: Fail to disable regulator vddo[%d]\n",
|
|
__func__, retval);
|
|
|
|
}
|
|
input_err(true, &client->dev,
|
|
"%s: vddo is disabled[OK]\n", __func__);
|
|
} else {
|
|
input_err(true, &client->dev,
|
|
"%s: vddo is already disabled\n", __func__);
|
|
}
|
|
}
|
|
|
|
wac_i2c->power_enable = on;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wacom_reset_hw(struct wacom_i2c *wac_i2c)
|
|
{
|
|
wacom_power(wac_i2c, false);
|
|
/* recommended delay in spec */
|
|
msleep(100);
|
|
wacom_power(wac_i2c, true);
|
|
|
|
msleep(200);
|
|
}
|
|
|
|
void wacom_compulsory_flash_mode(struct wacom_i2c *wac_i2c, bool enable)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
|
|
if (pdata)
|
|
gpio_direction_output(pdata->fwe_gpio, enable);
|
|
}
|
|
|
|
int wacom_get_irq_state(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
int level;
|
|
|
|
if (!pdata)
|
|
return -EINVAL;
|
|
|
|
level = gpio_get_value(pdata->irq_gpio);
|
|
|
|
if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
|
|
return !level;
|
|
|
|
return level;
|
|
}
|
|
|
|
void wacom_enable_irq(struct wacom_i2c *wac_i2c, bool enable)
|
|
{
|
|
static int depth;
|
|
|
|
mutex_lock(&wac_i2c->irq_lock);
|
|
if (enable) {
|
|
if (depth) {
|
|
--depth;
|
|
enable_irq(wac_i2c->irq);
|
|
}
|
|
} else {
|
|
if (!depth) {
|
|
++depth;
|
|
disable_irq(wac_i2c->irq);
|
|
}
|
|
}
|
|
mutex_unlock(&wac_i2c->irq_lock);
|
|
|
|
#ifdef WACOM_IRQ_DEBUG
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"%s: Enable %d, depth %d\n", __func__, (int)enable, depth);
|
|
#endif
|
|
}
|
|
|
|
void wacom_enable_pdct_irq(struct wacom_i2c *wac_i2c, bool enable)
|
|
{
|
|
static int depth;
|
|
|
|
mutex_lock(&wac_i2c->irq_lock);
|
|
if (enable) {
|
|
if (depth) {
|
|
--depth;
|
|
enable_irq(wac_i2c->irq_pdct);
|
|
}
|
|
} else {
|
|
if (!depth) {
|
|
++depth;
|
|
disable_irq(wac_i2c->irq_pdct);
|
|
}
|
|
}
|
|
mutex_unlock(&wac_i2c->irq_lock);
|
|
|
|
#ifdef WACOM_IRQ_DEBUG
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"%s: Enable %d, depth %d\n", __func__, (int)enable, depth);
|
|
#endif
|
|
}
|
|
|
|
static void wacom_enable_irq_wake(struct wacom_i2c *wac_i2c, bool enable)
|
|
{
|
|
static int depth;
|
|
|
|
if (enable) {
|
|
if (depth++ == 0) {
|
|
enable_irq_wake(wac_i2c->irq);
|
|
|
|
if (wac_i2c->pdata->use_garage)
|
|
enable_irq_wake(wac_i2c->irq_pdct);
|
|
}
|
|
} else {
|
|
if (depth == 0) {
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"Unbalanced IRQ wake disable\n");
|
|
} else if (--depth == 0) {
|
|
disable_irq_wake(wac_i2c->irq);
|
|
|
|
if (wac_i2c->pdata->use_garage)
|
|
disable_irq_wake(wac_i2c->irq_pdct);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wacom_wakeup_sequence(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
|
|
mutex_lock(&wac_i2c->lock);
|
|
|
|
input_info(true, &client->dev,
|
|
"%s: start, garage %s, ps %s, pen %s, epen %s, cover %s, count(%u,%u)[0x%x]\n",
|
|
__func__, wac_i2c->pdata->use_garage ? "used" : "unused",
|
|
wac_i2c->battery_saving_mode ? "on" : "off",
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT) ? "out" : "in",
|
|
wac_i2c->epen_blocked ? "blocked" : "unblocked",
|
|
wac_i2c->keyboard_cover_mode ? "on" : "off",
|
|
wac_i2c->i2c_fail_count, wac_i2c->abnormal_reset_count,
|
|
wac_i2c->wac_feature->fw_version);
|
|
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
if (wac_i2c->fac_garage_mode)
|
|
input_info(true, &client->dev, "%s: garage mode\n", __func__);
|
|
#endif
|
|
|
|
if (wake_lock_active(&wac_i2c->fw_wakelock)) {
|
|
input_info(true, &client->dev,
|
|
"fw wake lock active. pass %s\n", __func__);
|
|
goto out_power_on;
|
|
}
|
|
|
|
if (wac_i2c->screen_on) {
|
|
input_info(true, &client->dev,
|
|
"already enabled. pass %s\n", __func__);
|
|
goto out_power_on;
|
|
}
|
|
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
schedule_delayed_work(&wac_i2c->resume_work,
|
|
msecs_to_jiffies(EPEN_RESUME_DELAY));
|
|
|
|
if (device_may_wakeup(&client->dev))
|
|
wacom_enable_irq_wake(wac_i2c, false);
|
|
|
|
wac_i2c->screen_on = true;
|
|
|
|
out_power_on:
|
|
mutex_unlock(&wac_i2c->lock);
|
|
|
|
input_info(true, &client->dev, "%s: end\n", __func__);
|
|
}
|
|
|
|
void wacom_sleep_sequence(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int retry = 1;
|
|
|
|
mutex_lock(&wac_i2c->lock);
|
|
|
|
input_info(true, &client->dev,
|
|
"%s: start, garage %s, ps %s, pen %s, epen %s, cover %s, count(%u,%u), set(0x%x), ret(0x%x)[0x%x]\n",
|
|
__func__, wac_i2c->pdata->use_garage ? "used" : "unused",
|
|
wac_i2c->battery_saving_mode ? "on" : "off",
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT) ? "out" : "in",
|
|
wac_i2c->epen_blocked ? "blocked" : "unblocked",
|
|
wac_i2c->keyboard_cover_mode ? "on" : "off",
|
|
wac_i2c->i2c_fail_count, wac_i2c->abnormal_reset_count,
|
|
wac_i2c->function_set, wac_i2c->function_result,
|
|
wac_i2c->wac_feature->fw_version);
|
|
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
if (wac_i2c->fac_garage_mode)
|
|
input_info(true, &client->dev, "%s: garage mode\n", __func__);
|
|
|
|
#endif
|
|
|
|
if (wake_lock_active(&wac_i2c->fw_wakelock)) {
|
|
input_info(true, &client->dev,
|
|
"fw wake lock active. pass %s\n", __func__);
|
|
|
|
goto out_power_off;
|
|
}
|
|
|
|
if (!wac_i2c->screen_on) {
|
|
input_info(true, &client->dev,
|
|
"already disabled. pass %s\n", __func__);
|
|
goto out_power_off;
|
|
}
|
|
|
|
forced_release_fullscan(wac_i2c);
|
|
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
cancel_delayed_work_sync(&wac_i2c->fullscan_check_work);
|
|
|
|
#ifdef LCD_FREQ_SYNC
|
|
cancel_work_sync(&wac_i2c->lcd_freq_work);
|
|
cancel_delayed_work_sync(&wac_i2c->lcd_freq_done_work);
|
|
wac_i2c->lcd_freq_wait = false;
|
|
#endif
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
cancel_delayed_work_sync(&wac_i2c->softkey_block_work);
|
|
wac_i2c->block_softkey = false;
|
|
#endif
|
|
|
|
reset:
|
|
if (wac_i2c->reset_flag) {
|
|
input_info(true, &client->dev,
|
|
"%s: IC reset start\n", __func__);
|
|
|
|
wac_i2c->abnormal_reset_count++;
|
|
wac_i2c->reset_flag = false;
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
|
|
wacom_reset_hw(wac_i2c);
|
|
|
|
wac_i2c->pen_pdct =
|
|
gpio_get_value(wac_i2c->pdata->pdct_gpio);
|
|
|
|
input_info(true, &client->dev,
|
|
"%s : IC reset end, pdct(%d)\n", __func__,
|
|
wac_i2c->pen_pdct);
|
|
|
|
if (wac_i2c->pdata->use_garage) {
|
|
if (wac_i2c->pen_pdct)
|
|
wac_i2c->function_result &= ~EPEN_EVENT_PEN_OUT;
|
|
else
|
|
wac_i2c->function_result |= EPEN_EVENT_PEN_OUT;
|
|
}
|
|
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
}
|
|
|
|
wacom_select_survey_mode(wac_i2c, false);
|
|
|
|
/* release pen, if it is pressed */
|
|
if (wac_i2c->pen_pressed || wac_i2c->side_pressed || wac_i2c->pen_prox)
|
|
forced_release(wac_i2c);
|
|
|
|
if (!wac_i2c->pdata->use_virtual_softkey) {
|
|
if (wac_i2c->soft_key_pressed[0] || wac_i2c->soft_key_pressed[1])
|
|
forced_release_key(wac_i2c);
|
|
}
|
|
|
|
if (wac_i2c->reset_flag && retry--)
|
|
goto reset;
|
|
|
|
if (device_may_wakeup(&client->dev))
|
|
wacom_enable_irq_wake(wac_i2c, true);
|
|
|
|
wac_i2c->screen_on = false;
|
|
|
|
out_power_off:
|
|
mutex_unlock(&wac_i2c->lock);
|
|
|
|
input_info(true, &client->dev, "%s end\n", __func__);
|
|
}
|
|
|
|
static irqreturn_t wacom_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_id;
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
s16 x, y, pressure;
|
|
s16 tmp;
|
|
u8 gain = 0;
|
|
s8 tilt_x = 0;
|
|
s8 tilt_y = 0;
|
|
int ret = 0;
|
|
char data[COM_COORD_NUM + 1] = { 0, };
|
|
|
|
if (!wac_i2c->screen_on && wac_i2c->survey_mode) {
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"%s: lcd off & survey mode on\n", __func__);
|
|
|
|
/* in LPM, waiting blsp block resume */
|
|
if (wac_i2c->pm_suspend) {
|
|
wake_lock_timeout(&wac_i2c->wakelock,
|
|
msecs_to_jiffies(3 * MSEC_PER_SEC));
|
|
/* waiting for blsp block resuming, if not occurs
|
|
* i2c error
|
|
*/
|
|
ret =
|
|
wait_for_completion_interruptible_timeout(
|
|
&wac_i2c->resume_done,
|
|
msecs_to_jiffies(3 * MSEC_PER_SEC));
|
|
if (ret == 0) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: LPM: pm resume is not handled [timeout]\n",
|
|
__func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
|
|
ret = wacom_get_status_data(wac_i2c, data);
|
|
if (!ret)
|
|
return IRQ_HANDLED;
|
|
|
|
if (wac_i2c->function_set & EPEN_SETMODE_AOP) {
|
|
if (data[10] == AOP_BUTTON_HOVER) {
|
|
if (wac_i2c->function_set & EPEN_SETMODE_AOP_OPTION_SCREENOFFMEMO) {
|
|
input_info(true, &wac_i2c->client->dev, "Hover & Side Button detected\n");
|
|
|
|
input_report_key(wac_i2c->input_dev,
|
|
KEY_WAKEUP_UNLOCK, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_key(wac_i2c->input_dev,
|
|
KEY_WAKEUP_UNLOCK, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
x = ((u16) data[1] << 8) + (u16) data[2];
|
|
y = ((u16) data[3] << 8) + (u16) data[4];
|
|
|
|
/* origin */
|
|
x = x - pdata->origin[0];
|
|
y = y - pdata->origin[1];
|
|
/* change axis from wacom to lcd */
|
|
if (pdata->x_invert)
|
|
x = pdata->max_x - x;
|
|
|
|
if (pdata->y_invert)
|
|
y = pdata->max_y - y;
|
|
|
|
if (pdata->xy_switch) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
wac_i2c->survey_pos.id = EPEN_POS_ID_SCREEN_OF_MEMO;
|
|
wac_i2c->survey_pos.x = x;
|
|
wac_i2c->survey_pos.y = y;
|
|
} else {
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"AOP detected but skip report, screen_off_memo disabled\n");
|
|
}
|
|
} else if (data[10] == AOP_DOUBLE_TAB) {
|
|
if (wac_i2c->function_set & EPEN_SETMODE_AOP_OPTION_AOD) {
|
|
input_info(true, &wac_i2c->client->dev, "Double Tab detected in AOD\n");
|
|
|
|
x = ((u16) data[1] << 8) + (u16) data[2];
|
|
y = ((u16) data[3] << 8) + (u16) data[4];
|
|
pressure = ((u16) data[5] << 8) + (u16) data[6];
|
|
gain = data[7];
|
|
tilt_x = (s8) data[9];
|
|
tilt_y = -(s8) data[8];
|
|
|
|
/* origin */
|
|
x = x - pdata->origin[0];
|
|
y = y - pdata->origin[1];
|
|
/* change axis from wacom to lcd */
|
|
if (pdata->x_invert)
|
|
x = pdata->max_x - x;
|
|
|
|
if (pdata->y_invert)
|
|
y = pdata->max_y - y;
|
|
|
|
if (pdata->xy_switch) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
if (data[0] & 0x40)
|
|
wac_i2c->tool = BTN_TOOL_RUBBER;
|
|
else
|
|
wac_i2c->tool = BTN_TOOL_PEN;
|
|
|
|
/* make press / release event for AOP double tab gesture */
|
|
input_report_abs(wac_i2c->input_dev, ABS_X, x);
|
|
input_report_abs(wac_i2c->input_dev, ABS_Y, y);
|
|
input_report_key(wac_i2c->input_dev, wac_i2c->tool, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, pressure);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
input_report_key(wac_i2c->input_dev, wac_i2c->tool, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"x(%d), y(%d) P / R event\n", x, y);
|
|
#else
|
|
input_info(true, &wac_i2c->client->dev, "P / R event\n");
|
|
#endif
|
|
} else if (wac_i2c->function_set & EPEN_SETMODE_AOP_OPTION_AOT) {
|
|
input_info(true, &wac_i2c->client->dev, "Double Tab detected\n");
|
|
|
|
input_report_key(wac_i2c->input_dev, KEY_HOMEPAGE, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
input_report_key(wac_i2c->input_dev, KEY_HOMEPAGE, 0);
|
|
input_sync(wac_i2c->input_dev);
|
|
} else {
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"AOP Double Tab detected but skip report, aod & aot disabled\n");
|
|
}
|
|
} else {
|
|
input_info(true, &wac_i2c->client->dev, "unknown AOP status\n");
|
|
}
|
|
} else {
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"AOP Disabled \n");
|
|
}
|
|
|
|
if (data[0] == 0x0F) {
|
|
if (data[10] == WACOM_NOISE_HIGH) {
|
|
input_err(true, &wac_i2c->client->dev, "11 11 high-noise mode\n");
|
|
wac_i2c->wacom_noise_state = WACOM_NOISE_HIGH;
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_HIGH_NOISE_MODE); */
|
|
} else if (data[10] == WACOM_NOISE_LOW) {
|
|
input_err(true, &wac_i2c->client->dev, "22 22 low-noise mode\n");
|
|
wac_i2c->wacom_noise_state = WACOM_NOISE_LOW;
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
wacom_i2c_coord(wac_i2c);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t wacom_interrupt_pdct(int irq, void *dev_id)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_id;
|
|
struct i2c_client *client = wac_i2c->client;
|
|
int ret;
|
|
|
|
if (wac_i2c->query_status == false)
|
|
return IRQ_HANDLED;
|
|
|
|
wac_i2c->pen_pdct = gpio_get_value(wac_i2c->pdata->pdct_gpio);
|
|
|
|
if (wac_i2c->pdata->use_garage) {
|
|
#if defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
input_info(true, &client->dev, "%s: pen is %s garage\n",
|
|
__func__, wac_i2c->pen_pdct ? "IN " : "OUT of");
|
|
#else
|
|
input_info(true, &client->dev, "%s: pen is %s garage(%d)\n",
|
|
__func__, wac_i2c->pen_pdct ? "IN" : "OUT of",
|
|
(wacom_get_irq_state(wac_i2c) > 0));
|
|
#endif
|
|
if (wac_i2c->pen_pdct)
|
|
wac_i2c->function_result &= ~EPEN_EVENT_PEN_OUT;
|
|
else
|
|
wac_i2c->function_result |= EPEN_EVENT_PEN_OUT;
|
|
|
|
if (!wac_i2c->screen_on) {
|
|
/* in LPM, waiting blsp block resume */
|
|
if (wac_i2c->pm_suspend) {
|
|
wake_lock_timeout(&wac_i2c->wakelock,
|
|
msecs_to_jiffies(3 * MSEC_PER_SEC));
|
|
/* waiting for blsp block resuming, if not occurs
|
|
* i2c error
|
|
*/
|
|
ret =
|
|
wait_for_completion_interruptible_timeout(
|
|
&wac_i2c->resume_done,
|
|
msecs_to_jiffies(3 * MSEC_PER_SEC));
|
|
if (ret == 0) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: LPM: pm resume is not handled [timeout]\n",
|
|
__func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
input_report_switch(wac_i2c->input_dev_pen, SW_PEN_INSERT,
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT));
|
|
input_sync(wac_i2c->input_dev_pen);
|
|
|
|
if (wac_i2c->epen_blocked ||
|
|
(wac_i2c->battery_saving_mode && !(wac_i2c->function_result & EPEN_EVENT_PEN_OUT))) {
|
|
input_info(true, &client->dev,
|
|
"%s: %s & garage on. garage only mode\n", __func__,
|
|
wac_i2c->epen_blocked ? "epen blocked" : "ps on & pen in");
|
|
wacom_i2c_set_survey_mode(wac_i2c,
|
|
EPEN_SURVEY_MODE_GARAGE_ONLY);
|
|
} else if (wac_i2c->screen_on && wac_i2c->survey_mode) {
|
|
input_info(true, &client->dev,
|
|
"%s: ps %s & pen %s & lcd on. normal mode\n",
|
|
__func__,
|
|
wac_i2c->battery_saving_mode? "on" : "off",
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT) ? "out" : "in");
|
|
|
|
wacom_i2c_set_survey_mode(wac_i2c, EPEN_SURVEY_MODE_NONE);
|
|
} else {
|
|
input_info(true, &client->dev,
|
|
"%s: ps %s & pen %s & lcd %s. keep current mode(%s)\n",
|
|
__func__,
|
|
wac_i2c->battery_saving_mode? "on" : "off",
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT) ? "out" : "in",
|
|
wac_i2c->screen_on ? "on" : "off",
|
|
wac_i2c->function_result & EPEN_EVENT_SURVEY ? "survey" : "normal");
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void pen_insert_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, pen_insert_dwork.work);
|
|
|
|
if (wac_i2c->pdata->use_garage) {
|
|
wac_i2c->pen_pdct = gpio_get_value(wac_i2c->pdata->pdct_gpio);
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s: pdct(%d)\n",
|
|
__func__, wac_i2c->pen_pdct);
|
|
|
|
if (wac_i2c->pen_pdct)
|
|
wac_i2c->function_result &= ~EPEN_EVENT_PEN_OUT;
|
|
else
|
|
wac_i2c->function_result |= EPEN_EVENT_PEN_OUT;
|
|
}
|
|
|
|
input_report_switch(wac_i2c->input_dev_pen, SW_PEN_INSERT,
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT));
|
|
input_sync(wac_i2c->input_dev_pen);
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s : pen is %s\n", __func__,
|
|
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT) ? "OUT" : "IN");
|
|
}
|
|
|
|
static void init_pen_insert(struct wacom_i2c *wac_i2c)
|
|
{
|
|
INIT_DELAYED_WORK(&wac_i2c->pen_insert_dwork, pen_insert_work);
|
|
|
|
/* update the current status */
|
|
schedule_delayed_work(&wac_i2c->pen_insert_dwork, HZ * 5);
|
|
}
|
|
|
|
static int wacom_i2c_input_open(struct input_dev *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s(%s)\n", __func__,
|
|
dev->name);
|
|
|
|
wacom_wakeup_sequence(wac_i2c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void wacom_i2c_input_close(struct input_dev *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s(%s)\n", __func__,
|
|
dev->name);
|
|
|
|
wacom_sleep_sequence(wac_i2c);
|
|
}
|
|
|
|
static void wacom_i2c_set_input_values(struct wacom_i2c *wac_i2c,
|
|
struct input_dev *input_dev, u8 propbit)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
|
|
/* Set input values before registering input device */
|
|
|
|
input_dev->id.bustype = BUS_I2C;
|
|
input_dev->dev.parent = &client->dev;
|
|
|
|
input_dev->open = wacom_i2c_input_open;
|
|
input_dev->close = wacom_i2c_input_close;
|
|
|
|
input_set_capability(input_dev, EV_KEY, BTN_TOOL_PEN);
|
|
input_set_capability(input_dev, EV_KEY, BTN_TOOL_RUBBER);
|
|
|
|
input_set_abs_params(input_dev, ABS_PRESSURE, 0, pdata->max_pressure,
|
|
0, 0);
|
|
input_set_abs_params(input_dev, ABS_DISTANCE, 0, pdata->max_height,
|
|
0, 0);
|
|
input_set_abs_params(input_dev, ABS_TILT_X, -pdata->max_x_tilt,
|
|
pdata->max_x_tilt, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_TILT_Y, -pdata->max_y_tilt,
|
|
pdata->max_y_tilt, 0, 0);
|
|
|
|
/* AOP */
|
|
input_set_capability(input_dev, EV_KEY, KEY_WAKEUP_UNLOCK);
|
|
input_set_capability(input_dev, EV_KEY, KEY_HOMEPAGE);
|
|
|
|
if (propbit & DEX_MODE_MOUSE) {
|
|
input_set_capability(input_dev, EV_REL, REL_X);
|
|
input_set_capability(input_dev, EV_REL, REL_Y);
|
|
|
|
input_set_capability(input_dev, EV_KEY, BTN_LEFT);
|
|
input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
|
|
/* input_set_capability(input_dev, EV_KEY, BTN_MIDDLE); */
|
|
} else {
|
|
int max_x, max_y;
|
|
|
|
input_set_capability(input_dev, EV_SW, SW_PEN_INSERT);
|
|
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
|
input_set_capability(input_dev, EV_KEY, BTN_STYLUS);
|
|
|
|
/* input_set_capability(input_dev, EV_KEY, KEY_UNKNOWN); */
|
|
/* input_set_capability(input_dev, EV_KEY, BTN_TOOL_SPEN_SCAN); */
|
|
/* input_set_capability(input_dev, EV_KEY, KEY_PEN_PDCT); */
|
|
/* input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); */
|
|
/* input_set_capability(input_dev, EV_KEY, ABS_MISC); */
|
|
|
|
/* softkey */
|
|
if (!pdata->use_virtual_softkey) {
|
|
input_set_capability(input_dev, EV_KEY, KEY_RECENT);
|
|
input_set_capability(input_dev, EV_KEY, KEY_BACK);
|
|
}
|
|
|
|
max_x = pdata->max_x;
|
|
max_y = pdata->max_y;
|
|
|
|
if (pdata->xy_switch) {
|
|
input_set_abs_params(input_dev, ABS_X, 0, max_y, 4, 0);
|
|
input_set_abs_params(input_dev, ABS_Y, 0, max_x, 4, 0);
|
|
} else {
|
|
input_set_abs_params(input_dev, ABS_X, 0, max_x, 4, 0);
|
|
input_set_abs_params(input_dev, ABS_Y, 0, max_y, 4, 0);
|
|
}
|
|
}
|
|
|
|
input_set_drvdata(input_dev, wac_i2c);
|
|
}
|
|
|
|
static void wacom_i2c_resume_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, resume_work.work);
|
|
struct i2c_client *client = wac_i2c->client;
|
|
u8 irq_state = 0;
|
|
int retry = 1;
|
|
int ret = 0;
|
|
|
|
reset:
|
|
if (wac_i2c->reset_flag) {
|
|
input_info(true, &client->dev,
|
|
"%s: IC reset start\n", __func__);
|
|
|
|
wac_i2c->abnormal_reset_count++;
|
|
wac_i2c->reset_flag = false;
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
|
|
wacom_reset_hw(wac_i2c);
|
|
|
|
wac_i2c->pen_pdct =
|
|
gpio_get_value(wac_i2c->pdata->pdct_gpio);
|
|
|
|
input_info(true, &client->dev,
|
|
"%s: IC reset end, pdct(%d)\n", __func__,
|
|
wac_i2c->pen_pdct);
|
|
|
|
if (wac_i2c->pdata->use_garage) {
|
|
if (wac_i2c->pen_pdct)
|
|
wac_i2c->function_result &= ~EPEN_EVENT_PEN_OUT;
|
|
else
|
|
wac_i2c->function_result |= EPEN_EVENT_PEN_OUT;
|
|
}
|
|
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
}
|
|
|
|
wacom_select_survey_mode(wac_i2c, true);
|
|
|
|
if (wac_i2c->reset_flag && retry--)
|
|
goto reset;
|
|
|
|
if (wac_i2c->wcharging_mode)
|
|
wacom_i2c_set_sense_mode(wac_i2c);
|
|
|
|
if (wac_i2c->tsp_noise_mode < 0) {
|
|
/* wac_i2c->tsp_noise_mode = set_spen_mode(EPEN_GLOBAL_SCAN_MODE); */
|
|
}
|
|
|
|
irq_state = wacom_get_irq_state(wac_i2c);
|
|
if (unlikely(irq_state > 0)) {
|
|
u8 data[COM_COORD_NUM + 1] = { 0, };
|
|
|
|
input_info(true, &client->dev, "%s: irq was enabled\n",
|
|
__func__);
|
|
|
|
ret = wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM + 1,
|
|
WACOM_I2C_MODE_NORMAL);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev,
|
|
"%s: failed to receive\n", __func__,
|
|
__LINE__);
|
|
}
|
|
|
|
input_info(true, &client->dev,
|
|
"%x %x %x %x %x, %x %x %x %x %x, %x %x %x\n",
|
|
data[0], data[1], data[2], data[3], data[4], data[5],
|
|
data[6], data[7], data[8], data[9], data[10],
|
|
data[11], data[12]);
|
|
}
|
|
|
|
ret = gpio_get_value(wac_i2c->pdata->pdct_gpio);
|
|
|
|
input_info(true, &client->dev,
|
|
"%s: i(%d) p(%d) set(0x%x) ret(0x%x)\n",
|
|
__func__, irq_state, ret, wac_i2c->function_set,
|
|
wac_i2c->function_result);
|
|
}
|
|
|
|
#ifdef LCD_FREQ_SYNC
|
|
#define SYSFS_WRITE_LCD "/sys/class/lcd/panel/ldi_fps"
|
|
static void wacom_i2c_sync_lcd_freq(struct wacom_i2c *wac_i2c)
|
|
{
|
|
int ret = 0;
|
|
mm_segment_t old_fs;
|
|
struct file *write_node;
|
|
char freq[12] = { 0, };
|
|
int lcd_freq = wac_i2c->lcd_freq;
|
|
|
|
mutex_lock(&wac_i2c->freq_write_lock);
|
|
|
|
snprintf(freq, 12, "%d", lcd_freq);
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
/* write_node = filp_open(SYSFS_WRITE_LCD, O_RDONLY | O_SYNC, 0664); */
|
|
write_node = filp_open(SYSFS_WRITE_LCD, O_RDWR | O_SYNC, 0664);
|
|
if (IS_ERR(write_node)) {
|
|
ret = PTR_ERR(write_node);
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: node file open fail, %d\n", __func__, ret);
|
|
goto err_open_node;
|
|
}
|
|
|
|
ret = write_node->f_op->write(write_node, (char __user *)freq,
|
|
strlen(freq), &write_node->f_pos);
|
|
if (ret != strlen(freq)) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: Can't write node data\n", __func__);
|
|
}
|
|
input_info(true, &wac_i2c->client->dev, "%s write freq %s\n",
|
|
__func__, freq);
|
|
|
|
filp_close(write_node, current->files);
|
|
|
|
err_open_node:
|
|
set_fs(old_fs);
|
|
mutex_unlock(&wac_i2c->freq_write_lock);
|
|
|
|
schedule_delayed_work(&wac_i2c->lcd_freq_done_work, HZ * 5);
|
|
}
|
|
|
|
static void wacom_i2c_finish_lcd_freq_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, lcd_freq_done_work.work);
|
|
|
|
wac_i2c->lcd_freq_wait = false;
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s\n", __func__);
|
|
}
|
|
|
|
static void wacom_i2c_lcd_freq_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, lcd_freq_work);
|
|
|
|
wacom_i2c_sync_lcd_freq(wac_i2c);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
static void wacom_i2c_block_softkey_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, softkey_block_work.work);
|
|
|
|
wac_i2c->block_softkey = false;
|
|
}
|
|
#endif
|
|
|
|
int load_fw_built_in(struct wacom_i2c *wac_i2c, int fw_index)
|
|
{
|
|
int retry = 3;
|
|
int ret;
|
|
const char *fw_load_path = NULL;
|
|
|
|
input_info(true, &wac_i2c->client->dev, "load_fw_built_in (%d)\n",fw_index);
|
|
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
if (fw_index == FW_FACTORY_PROC)
|
|
fw_load_path = wac_i2c->pdata->fw_fac_path;
|
|
else
|
|
#endif
|
|
fw_load_path = wac_i2c->pdata->fw_path;
|
|
|
|
if (fw_load_path == NULL) {
|
|
input_err(true, wac_i2c->dev,
|
|
"Unable to open firmware. fw_path is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (retry--) {
|
|
ret =
|
|
request_firmware(&wac_i2c->firm_data,
|
|
fw_load_path,
|
|
&wac_i2c->client->dev);
|
|
if (ret < 0) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"Unable to open firmware. ret %d retry %d\n",
|
|
ret, retry);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
wac_i2c->fw_img = (struct fw_image *)wac_i2c->firm_data->data;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int load_fw_sdcard(struct wacom_i2c *wac_i2c)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
struct file *fp;
|
|
mm_segment_t old_fs;
|
|
long fsize, nread;
|
|
int ret = 0;
|
|
unsigned int nSize;
|
|
unsigned long nSize2;
|
|
u8 *ums_data;
|
|
|
|
nSize = WACOM_FW_SIZE;
|
|
nSize2 = nSize + sizeof(struct fw_image);
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
fp = filp_open(WACOM_FW_PATH_SDCARD, O_RDONLY, S_IRUSR);
|
|
|
|
if (IS_ERR(fp)) {
|
|
input_err(true, &client->dev, "failed to open %s.\n",
|
|
WACOM_FW_PATH_SDCARD);
|
|
ret = -ENOENT;
|
|
set_fs(old_fs);
|
|
return ret;
|
|
}
|
|
|
|
fsize = fp->f_path.dentry->d_inode->i_size;
|
|
input_info(true, &client->dev, "start, file path %s, size %ld Bytes\n",
|
|
WACOM_FW_PATH_SDCARD, fsize);
|
|
|
|
if ((fsize != nSize) && (fsize != nSize2)) {
|
|
input_err(true, &client->dev,
|
|
"UMS firmware size is different\n");
|
|
ret = -EFBIG;
|
|
goto out;
|
|
}
|
|
|
|
ums_data = kmalloc(fsize, GFP_KERNEL);
|
|
if (!ums_data) {
|
|
input_err(true, &client->dev, "%s, kmalloc failed\n", __func__);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
nread = vfs_read(fp, (char __user *)ums_data, fsize, &fp->f_pos);
|
|
input_info(true, &client->dev, "nread %ld Bytes\n", nread);
|
|
if (nread != fsize) {
|
|
input_err(true, &client->dev,
|
|
"failed to read firmware file, nread %ld Bytes\n",
|
|
nread);
|
|
ret = -EIO;
|
|
kfree(ums_data);
|
|
goto out;
|
|
}
|
|
|
|
filp_close(fp, current->files);
|
|
set_fs(old_fs);
|
|
|
|
wac_i2c->fw_img = (struct fw_image *)ums_data;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
filp_close(fp, current->files);
|
|
set_fs(old_fs);
|
|
return ret;
|
|
}
|
|
|
|
int wacom_i2c_load_fw(struct wacom_i2c *wac_i2c, u8 fw_path)
|
|
{
|
|
int ret = 0;
|
|
struct fw_image *fw_img;
|
|
struct i2c_client *client = wac_i2c->client;
|
|
|
|
switch (fw_path) {
|
|
case FW_BUILT_IN:
|
|
ret = load_fw_built_in(wac_i2c,FW_BUILT_IN);
|
|
break;
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
case FW_FACTORY_PROC:
|
|
ret = load_fw_built_in(wac_i2c,FW_FACTORY_PROC);
|
|
break;
|
|
#endif
|
|
case FW_IN_SDCARD:
|
|
ret = load_fw_sdcard(wac_i2c);
|
|
break;
|
|
default:
|
|
input_info(true, &client->dev, "unknown path(%d)\n", fw_path);
|
|
goto err_load_fw;
|
|
}
|
|
|
|
if (ret < 0)
|
|
goto err_load_fw;
|
|
|
|
fw_img = wac_i2c->fw_img;
|
|
|
|
/* header check */
|
|
if (fw_img->hdr_ver == 1 && fw_img->hdr_len == sizeof(struct fw_image)) {
|
|
wac_i2c->fw_data = (u8 *) fw_img->data;
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
if (fw_path == FW_BUILT_IN) {
|
|
#else
|
|
if ((fw_path == FW_BUILT_IN) || (fw_path == FW_FACTORY_PROC)) {
|
|
#endif
|
|
wac_i2c->fw_ver_file = fw_img->fw_ver1;
|
|
memcpy(wac_i2c->fw_chksum, fw_img->checksum, 5);
|
|
}
|
|
} else {
|
|
input_err(true, &client->dev, "no hdr\n");
|
|
wac_i2c->fw_data = (u8 *) fw_img;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_load_fw:
|
|
wac_i2c->fw_data = NULL;
|
|
return ret;
|
|
}
|
|
|
|
void wacom_i2c_unload_fw(struct wacom_i2c *wac_i2c)
|
|
{
|
|
switch (wac_i2c->fw_update_way) {
|
|
case FW_BUILT_IN:
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
case FW_FACTORY_PROC:
|
|
#endif
|
|
release_firmware(wac_i2c->firm_data);
|
|
break;
|
|
case FW_IN_SDCARD:
|
|
kfree(wac_i2c->fw_img);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
wac_i2c->fw_img = NULL;
|
|
wac_i2c->fw_update_way = FW_NONE;
|
|
wac_i2c->firm_data = NULL;
|
|
wac_i2c->fw_data = NULL;
|
|
}
|
|
|
|
int wacom_fw_update(struct wacom_i2c *wac_i2c, u8 fw_update_way, bool bforced)
|
|
{
|
|
struct i2c_client *client = wac_i2c->client;
|
|
u32 fw_ver_ic = wac_i2c->wac_feature->fw_version;
|
|
int ret;
|
|
|
|
input_info(true, &client->dev, "%s\n", __func__);
|
|
|
|
if (wake_lock_active(&wac_i2c->fw_wakelock)) {
|
|
input_info(true, &client->dev,
|
|
"update is already running. pass\n");
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&wac_i2c->update_lock);
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
|
|
/* release pen, if it is pressed */
|
|
if (wac_i2c->pen_pressed || wac_i2c->side_pressed || wac_i2c->pen_prox)
|
|
forced_release(wac_i2c);
|
|
|
|
ret = wacom_i2c_load_fw(wac_i2c, fw_update_way);
|
|
if (ret < 0) {
|
|
input_info(true, &client->dev, "failed to load fw data\n");
|
|
wac_i2c->wac_feature->update_status = FW_UPDATE_FAIL;
|
|
goto err_update_load_fw;
|
|
}
|
|
wac_i2c->fw_update_way = fw_update_way;
|
|
|
|
/* firmware info */
|
|
input_info(true, &client->dev,
|
|
"wacom fw ver : 0x%x, new fw ver : 0x%x\n",
|
|
wac_i2c->wac_feature->fw_version, wac_i2c->fw_ver_file);
|
|
|
|
if (!bforced) {
|
|
if (fw_ver_ic == wac_i2c->fw_ver_file) {
|
|
input_info(true, &client->dev, "pass fw update\n");
|
|
wac_i2c->do_crc_check = true;
|
|
/* need to check crc */
|
|
} else if (fw_ver_ic > wac_i2c->fw_ver_file) {
|
|
input_info(true, &client->dev,
|
|
"dont need to update fw\n");
|
|
goto out_update_fw;
|
|
}
|
|
|
|
/* ic < file then update */
|
|
}
|
|
|
|
cancel_work_sync(&wac_i2c->update_work);
|
|
schedule_work(&wac_i2c->update_work);
|
|
mutex_unlock(&wac_i2c->update_lock);
|
|
|
|
return 0;
|
|
|
|
out_update_fw:
|
|
wacom_i2c_unload_fw(wac_i2c);
|
|
err_update_load_fw:
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
mutex_unlock(&wac_i2c->update_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wacom_i2c_remove(struct i2c_client *client);
|
|
|
|
static void wacom_i2c_update_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, update_work);
|
|
struct i2c_client *client = wac_i2c->client;
|
|
struct wacom_features *feature = wac_i2c->wac_feature;
|
|
int ret = 0;
|
|
int retry = 3;
|
|
|
|
if (wac_i2c->fw_update_way == FW_NONE)
|
|
goto end_fw_update;
|
|
|
|
wake_lock(&wac_i2c->fw_wakelock);
|
|
|
|
/* CRC Check */
|
|
if (wac_i2c->do_crc_check) {
|
|
wac_i2c->do_crc_check = false;
|
|
|
|
ret = wacom_checksum(wac_i2c);
|
|
if (ret) {
|
|
input_info(true, &client->dev, "crc ok, do not update\n");
|
|
goto err_update_fw;
|
|
}
|
|
|
|
input_info(true, &client->dev, "crc err, do update\n");
|
|
}
|
|
|
|
feature->update_status = FW_UPDATE_RUNNING;
|
|
|
|
while (retry--) {
|
|
ret = wacom_i2c_flash(wac_i2c);
|
|
if (ret) {
|
|
input_info(true, &client->dev,
|
|
"failed to flash fw(%d)\n", ret);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (ret) {
|
|
feature->update_status = FW_UPDATE_FAIL;
|
|
feature->fw_version = 0;
|
|
goto err_update_fw;
|
|
}
|
|
|
|
ret = wacom_i2c_query(wac_i2c);
|
|
if (ret < 0) {
|
|
input_info(true, &client->dev, "failed to query to IC(%d)\n",
|
|
ret);
|
|
feature->update_status = FW_UPDATE_FAIL;
|
|
goto err_update_fw;
|
|
}
|
|
|
|
feature->update_status = FW_UPDATE_PASS;
|
|
|
|
err_update_fw:
|
|
wake_unlock(&wac_i2c->fw_wakelock);
|
|
end_fw_update:
|
|
wacom_i2c_unload_fw(wac_i2c);
|
|
|
|
ret = wacom_open_test(wac_i2c);
|
|
if (ret)
|
|
input_err(true, &client->dev, "open test check failed\n");
|
|
|
|
wacom_enable_irq(wac_i2c, true);
|
|
wacom_enable_pdct_irq(wac_i2c, true);
|
|
|
|
if (feature->update_status == FW_UPDATE_FAIL) {
|
|
input_err(true, &client->dev,
|
|
"%s : failed to download FW & unload wacom driver\n", __func__);
|
|
wacom_i2c_remove(wac_i2c->client);
|
|
}
|
|
}
|
|
|
|
static void wacom_usb_typec_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c, usb_typec_work.work);
|
|
char data[5] = { 0 };
|
|
int ret;
|
|
|
|
if (wac_i2c->dp_connect_state == wac_i2c->dp_connect_cmd)
|
|
return;
|
|
|
|
if (!wac_i2c->power_enable) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: powered off now\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (wake_lock_active(&wac_i2c->fw_wakelock)) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: fw update is running\n", __func__);
|
|
return;
|
|
}
|
|
|
|
data[0] = COM_SAMPLERATE_STOP;
|
|
ret = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (ret != 1) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: failed to send stop cmd %d\n",
|
|
__func__, ret);
|
|
return;
|
|
}
|
|
|
|
msleep(50);
|
|
|
|
if (wac_i2c->dp_connect_cmd)
|
|
data[0] = COM_SPECIAL_COMPENSATION;
|
|
else
|
|
data[0] = COM_NORMAL_COMPENSATION;
|
|
|
|
ret = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (ret != 1) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: failed to send table swap cmd %d\n",
|
|
__func__, ret);
|
|
return;
|
|
}
|
|
|
|
msleep(30);
|
|
|
|
data[0] = COM_SAMPLERATE_STOP;
|
|
ret = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (ret != 1) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: failed to send stop cmd %d\n",
|
|
__func__, ret);
|
|
return;
|
|
}
|
|
|
|
data[0] = COM_SAMPLERATE_133;
|
|
ret = wacom_i2c_send(wac_i2c, &data[0], 1, WACOM_I2C_MODE_NORMAL);
|
|
if (ret != 1) {
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: failed to send start cmd, %d\n",
|
|
__func__, ret);
|
|
return;
|
|
}
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s: %s\n",
|
|
__func__, wac_i2c->dp_connect_cmd ? "on" : "off");
|
|
}
|
|
|
|
static int wacom_usb_typec_notification_cb(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
struct wacom_i2c *wac_i2c = container_of(nb, struct wacom_i2c, typec_nb);
|
|
CC_NOTI_TYPEDEF usb_typec_info = *(CC_NOTI_TYPEDEF *)data;
|
|
|
|
if (usb_typec_info.src != CCIC_NOTIFY_DEV_CCIC ||
|
|
usb_typec_info.dest != CCIC_NOTIFY_DEV_DP ||
|
|
usb_typec_info.id != CCIC_NOTIFY_ID_DP_CONNECT)
|
|
goto out;
|
|
|
|
input_info(true, &wac_i2c->client->dev,
|
|
"%s: %sed (vid:0x%04X pid:0x%04X)\n",
|
|
__func__, usb_typec_info.sub1 ? "attach" : "detach",
|
|
usb_typec_info.sub2, usb_typec_info.sub3);
|
|
|
|
switch (usb_typec_info.sub1) {
|
|
case CCIC_NOTIFY_ATTACH:
|
|
if (usb_typec_info.sub2 != 0x04E8 ||
|
|
usb_typec_info.sub3 != 0xA020)
|
|
goto out;
|
|
break;
|
|
case CCIC_NOTIFY_DETACH:
|
|
break;
|
|
default:
|
|
input_err(true, &wac_i2c->client->dev,
|
|
"%s: invalid value %d\n", __func__, usb_typec_info.sub1);
|
|
goto out;
|
|
}
|
|
|
|
cancel_delayed_work(&wac_i2c->usb_typec_work);
|
|
wac_i2c->dp_connect_cmd = usb_typec_info.sub1;
|
|
schedule_delayed_work(&wac_i2c->usb_typec_work, msecs_to_jiffies(1));
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static void wacom_usb_typec_nb_register_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c,
|
|
typec_nb_reg_work.work);
|
|
int ret;
|
|
static int count;
|
|
|
|
if (!wac_i2c || count > 100)
|
|
return;
|
|
|
|
ret = manager_notifier_register(&wac_i2c->typec_nb,
|
|
wacom_usb_typec_notification_cb,
|
|
MANAGER_NOTIFY_CCIC_DP);
|
|
if (ret) {
|
|
count++;
|
|
schedule_delayed_work(&wac_i2c->typec_nb_reg_work, msecs_to_jiffies(10));
|
|
} else {
|
|
input_err(true, &wac_i2c->client->dev, "%s: success\n", __func__);
|
|
}
|
|
}
|
|
|
|
static int wacom_request_gpio(struct i2c_client *client,
|
|
struct wacom_g5_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
ret = devm_gpio_request(&client->dev, pdata->irq_gpio, "wacom_irq");
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"unable to request gpio for irq [%d]\n",
|
|
pdata->irq_gpio);
|
|
return ret;
|
|
}
|
|
|
|
ret = devm_gpio_request(&client->dev, pdata->pdct_gpio, "wacom_pdct");
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"unable to request gpio for pdct [%d]\n",
|
|
pdata->pdct_gpio);
|
|
return ret;
|
|
}
|
|
|
|
ret = devm_gpio_request(&client->dev, pdata->fwe_gpio, "wacom_fwe");
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"unable to request gpio for fwe [%d]\n",
|
|
pdata->fwe_gpio);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct wacom_g5_platform_data *wacom_parse_dt(struct i2c_client *client)
|
|
{
|
|
struct wacom_g5_platform_data *pdata;
|
|
struct device *dev = &client->dev;
|
|
struct device_node *np = dev->of_node;
|
|
u32 tmp[5] = { 0, };
|
|
bool flag;
|
|
int ret;
|
|
|
|
if (!np)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
pdata->irq_gpio = of_get_named_gpio(np, "wacom,irq-gpio", 0);
|
|
if (!gpio_is_valid(pdata->irq_gpio)) {
|
|
input_err(true, &client->dev, "failed to get irq-gpio\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
pdata->pdct_gpio = of_get_named_gpio(np, "wacom,pdct-gpio", 0);
|
|
if (!gpio_is_valid(pdata->pdct_gpio)) {
|
|
input_err(true, &client->dev, "failed to get pdct-gpio\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
pdata->fwe_gpio = of_get_named_gpio(np, "wacom,fwe-gpio", 0);
|
|
if (!gpio_is_valid(pdata->fwe_gpio)) {
|
|
input_err(true, &client->dev, "failed to get fwe-gpio\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
/* get features */
|
|
ret = of_property_read_u32(np, "wacom,irq_type", tmp);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read trigger type %d\n", ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
pdata->irq_type = tmp[0];
|
|
|
|
ret = of_property_read_u32(np, "wacom,boot_addr", tmp);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read boot address %d\n", ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
pdata->boot_addr = tmp[0];
|
|
|
|
ret = of_property_read_u32_array(np, "wacom,origin", pdata->origin, 2);
|
|
if (ret) {
|
|
input_err(true, dev, "failed to read origin %d\n", ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "wacom,max_coords", tmp, 2);
|
|
if (ret != -EINVAL) {
|
|
if (ret) {
|
|
input_err(true, dev,
|
|
"failed to read max coords %d\n", ret);
|
|
}
|
|
pdata->max_x = tmp[0];
|
|
pdata->max_y = tmp[1];
|
|
}
|
|
pdata->use_dt_coord = of_property_read_bool(np, "wacom,use_dt_coord");
|
|
|
|
ret = of_property_read_u32(np, "wacom,max_pressure", tmp);
|
|
if (ret != -EINVAL) {
|
|
if (ret) {
|
|
input_err(true, dev,
|
|
"failed to read max pressure %d\n", ret);
|
|
}
|
|
pdata->max_pressure = tmp[0];
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "wacom,max_x_tilt", tmp);
|
|
if (ret != -EINVAL) {
|
|
if (ret) {
|
|
input_err(true, dev,
|
|
"failed to read max x tilt %d\n", ret);
|
|
}
|
|
pdata->max_x_tilt = tmp[0];
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "wacom,max_y_tilt", tmp);
|
|
if (ret != -EINVAL) {
|
|
if (ret) {
|
|
input_err(true, dev,
|
|
"failed to read max y tilt %d\n", ret);
|
|
}
|
|
pdata->max_y_tilt = tmp[0];
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "wacom,max_height", tmp);
|
|
if (ret != -EINVAL) {
|
|
if (ret) {
|
|
input_err(true, dev,
|
|
"failed to read max height %d\n", ret);
|
|
}
|
|
pdata->max_height = tmp[0];
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "wacom,invert", tmp, 3);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read inverts %d\n", ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
pdata->x_invert = tmp[0];
|
|
pdata->y_invert = tmp[1];
|
|
pdata->xy_switch = tmp[2];
|
|
|
|
ret = of_property_read_u32(np, "wacom,ic_type", &pdata->ic_type);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read ic_type %d\n", ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ret = of_property_read_string(np, "wacom,fw_path", &pdata->fw_path);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read fw_path %d\n", ret);
|
|
}
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
ret = of_property_read_string(np, "wacom,fw_fac_path",
|
|
&pdata->fw_fac_path);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read fw_fac_path %d\n", ret);
|
|
}
|
|
#endif
|
|
ret = of_property_read_string_index(np, "wacom,project_name", 0,
|
|
&pdata->project_name);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read project name %d\n", ret);
|
|
}
|
|
|
|
ret = of_property_read_string_index(np, "wacom,project_name", 1,
|
|
&pdata->model_name);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read model name %d\n", ret);
|
|
}
|
|
|
|
flag = of_property_read_bool(np, "wacom,use_virtual_softkey");
|
|
pdata->use_virtual_softkey = flag;
|
|
|
|
flag = of_property_read_bool(np, "wacom,use_garage");
|
|
pdata->use_garage = flag;
|
|
|
|
flag = of_property_read_bool(np, "wacom,support_dex_mode");
|
|
pdata->support_dex = flag;
|
|
|
|
if (pdata->support_dex) {
|
|
ret = of_property_read_u32(np, "wacom,dex_rate",
|
|
&pdata->dex_rate);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to read dex_rate %d, default value is 10\n",
|
|
ret);
|
|
pdata->dex_rate = 10;
|
|
}
|
|
}
|
|
|
|
pdata->support_aot = of_property_read_bool(np, "wacom,support_aot_mode");
|
|
pdata->table_swap = of_property_read_bool(np, "wacom,table_swap_for_dex_station");
|
|
|
|
input_info(true, &client->dev,
|
|
"boot_addr: 0x%X, origin: (%d,%d), max_coords: (%d,%d), "
|
|
"max_pressure: %d, max_height: %d, max_tilt: (%d,%d) "
|
|
"project_name: (%s,%s), invert: (%d,%d,%d), fw_path: %s, "
|
|
"ic_type: %d, %s virtual softkey, %s garage, support_dex:%d, "
|
|
"dex_rate:%d, table_swap:%d, support_aot:%d\n",
|
|
pdata->boot_addr, pdata->origin[0], pdata->origin[1],
|
|
pdata->max_x, pdata->max_y, pdata->max_pressure,
|
|
pdata->max_height, pdata->max_x_tilt, pdata->max_y_tilt,
|
|
pdata->project_name, pdata->model_name, pdata->x_invert,
|
|
pdata->y_invert, pdata->xy_switch, pdata->fw_path,
|
|
pdata->ic_type,
|
|
pdata->use_virtual_softkey ? "enabled" : "disabled",
|
|
pdata->use_garage ? "enabled" : "disabled",
|
|
pdata->support_dex, pdata->dex_rate, pdata->table_swap, pdata->support_aot);
|
|
|
|
return pdata;
|
|
}
|
|
#else
|
|
static struct wacom_g5_platform_data *wacom_parse_dt(struct i2c_client *client)
|
|
{
|
|
input_err(true, &client->dev, "no platform data specified\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
#endif
|
|
|
|
static int wacom_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct wacom_g5_platform_data *pdata = dev_get_platdata(&client->dev);
|
|
struct wacom_i2c *wac_i2c;
|
|
struct input_dev *input;
|
|
int ret = 0;
|
|
|
|
ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
|
|
if (!ret) {
|
|
input_err(true, &client->dev,
|
|
"I2C functionality not supported\n");
|
|
return -EIO;
|
|
}
|
|
|
|
wac_i2c = devm_kzalloc(&client->dev, sizeof(*wac_i2c), GFP_KERNEL);
|
|
if (!wac_i2c)
|
|
return -ENOMEM;
|
|
|
|
if (!pdata) {
|
|
pdata = wacom_parse_dt(client);
|
|
if (IS_ERR(pdata)) {
|
|
input_err(true, &client->dev, "failed to parse dt\n");
|
|
return PTR_ERR(pdata);
|
|
}
|
|
}
|
|
|
|
ret = wacom_request_gpio(client, pdata);
|
|
if (ret) {
|
|
input_err(true, &client->dev, "failed to request gpio\n");
|
|
return ret;
|
|
}
|
|
|
|
/* using managed input device */
|
|
input = devm_input_allocate_device(&client->dev);
|
|
if (!input) {
|
|
input_err(true, &client->dev,
|
|
"failed to allocate input device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (pdata->support_dex) {
|
|
/* using new input device for relative coordinate for DeX */
|
|
wac_i2c->input_dev_pad
|
|
= devm_input_allocate_device(&client->dev);
|
|
if (!wac_i2c->input_dev_pad) {
|
|
input_err(true, &client->dev,
|
|
"failed to allocate input device pad\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
wac_i2c->input_dev_virtual
|
|
= devm_input_allocate_device(&client->dev);
|
|
if (!wac_i2c->input_dev_virtual) {
|
|
input_err(true, &client->dev,
|
|
"failed to allocate input device virtual\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
/* using 2 slave address. one is normal mode, another is boot mode for
|
|
* fw update.
|
|
*/
|
|
wac_i2c->client_boot = i2c_new_dummy(client->adapter, pdata->boot_addr);
|
|
if (!wac_i2c->client_boot) {
|
|
input_err(true, &client->dev,
|
|
"failed to register sub client[0x%x]\n",
|
|
pdata->boot_addr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
wac_i2c->client = client;
|
|
wac_i2c->pdata = pdata;
|
|
wac_i2c->input_dev = input;
|
|
wac_i2c->irq = gpio_to_irq(pdata->irq_gpio);
|
|
wac_i2c->irq_pdct = gpio_to_irq(pdata->pdct_gpio);
|
|
wac_i2c->pen_pdct = PDCT_NOSIGNAL;
|
|
wac_i2c->fw_img = NULL;
|
|
wac_i2c->fw_update_way = FW_NONE;
|
|
wac_i2c->fullscan_mode = false;
|
|
wac_i2c->wacom_noise_state = WACOM_NOISE_LOW;
|
|
wac_i2c->tsp_noise_mode = EPEN_GLOBAL_SCAN_MODE;
|
|
wac_i2c->wac_feature = &wacom_feature_EMR;
|
|
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
|
|
wac_i2c->function_result = EPEN_EVENT_PEN_OUT;
|
|
wac_i2c->battery_saving_mode = 0; /* it needs */
|
|
/* Consider about factory, it may be need to as default 1 */
|
|
wac_i2c->reset_flag = false;
|
|
wac_i2c->pm_suspend = false;
|
|
|
|
wacom_get_drv_data(wac_i2c);
|
|
|
|
/*Set client data */
|
|
i2c_set_clientdata(client, wac_i2c);
|
|
i2c_set_clientdata(wac_i2c->client_boot, wac_i2c);
|
|
|
|
/* compensation to protect from flash mode */
|
|
wacom_compulsory_flash_mode(wac_i2c, true);
|
|
wacom_compulsory_flash_mode(wac_i2c, false);
|
|
|
|
/* Power on */
|
|
wacom_power(wac_i2c, true);
|
|
wac_i2c->screen_on = true;
|
|
/* need to delay for query */
|
|
msleep(100);
|
|
|
|
wacom_i2c_query(wac_i2c);
|
|
|
|
input->name = "sec_e-pen";
|
|
wacom_i2c_set_input_values(wac_i2c, input, DEX_MODE_STYLUS);
|
|
|
|
if (pdata->support_dex) {
|
|
wac_i2c->input_dev_pad->name = "sec_e-pen-pad";
|
|
wacom_i2c_set_input_values(wac_i2c, wac_i2c->input_dev_pad,
|
|
DEX_MODE_MOUSE);
|
|
wac_i2c->input_dev_virtual->name = "sec_virtual-e-pen";
|
|
wacom_i2c_set_input_values(wac_i2c, wac_i2c->input_dev_virtual,
|
|
DEX_MODE_STYLUS);
|
|
}
|
|
wac_i2c->input_dev_pen = input;
|
|
|
|
/*Initializing for semaphor */
|
|
mutex_init(&wac_i2c->lock);
|
|
mutex_init(&wac_i2c->update_lock);
|
|
mutex_init(&wac_i2c->irq_lock);
|
|
|
|
wake_lock_init(&wac_i2c->fw_wakelock, WAKE_LOCK_SUSPEND, "wacom");
|
|
wake_lock_init(&wac_i2c->wakelock, WAKE_LOCK_SUSPEND, "wacom_wakelock");
|
|
|
|
INIT_DELAYED_WORK(&wac_i2c->resume_work, wacom_i2c_resume_work);
|
|
INIT_DELAYED_WORK(&wac_i2c->fullscan_check_work,
|
|
wacom_fullscan_check_work);
|
|
|
|
init_completion(&wac_i2c->resume_done);
|
|
|
|
#ifdef LCD_FREQ_SYNC
|
|
mutex_init(&wac_i2c->freq_write_lock);
|
|
INIT_WORK(&wac_i2c->lcd_freq_work, wacom_i2c_lcd_freq_work);
|
|
INIT_DELAYED_WORK(&wac_i2c->lcd_freq_done_work,
|
|
wacom_i2c_finish_lcd_freq_work);
|
|
wac_i2c->use_lcd_freq_sync = true;
|
|
#endif
|
|
#ifdef WACOM_USE_SOFTKEY_BLOCK
|
|
INIT_DELAYED_WORK(&wac_i2c->softkey_block_work,
|
|
wacom_i2c_block_softkey_work);
|
|
wac_i2c->block_softkey = false;
|
|
#endif
|
|
INIT_WORK(&wac_i2c->update_work, wacom_i2c_update_work);
|
|
|
|
ret = input_register_device(input);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to register input device\n");
|
|
/* managed input devices need not be explicitly unregistred or freed. */
|
|
goto err_register_input_dev;
|
|
|
|
}
|
|
|
|
if (pdata->support_dex) {
|
|
ret = input_register_device(wac_i2c->input_dev_pad);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to register input device pad\n");
|
|
goto err_register_input_dev;
|
|
}
|
|
|
|
ret = input_register_device(wac_i2c->input_dev_virtual);
|
|
if (ret) {
|
|
input_err(true, &client->dev,
|
|
"failed to register input device virtual\n");
|
|
goto err_register_input_dev;
|
|
}
|
|
}
|
|
|
|
/*Request IRQ */
|
|
ret = devm_request_threaded_irq(&client->dev, wac_i2c->irq, NULL,
|
|
wacom_interrupt, IRQF_ONESHOT |
|
|
pdata->irq_type,
|
|
"sec_epen_irq", wac_i2c);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev,
|
|
"failed to request irq(%d) - %d\n", wac_i2c->irq,
|
|
ret);
|
|
goto err_request_irq;
|
|
}
|
|
input_info(true, &client->dev, "init irq %d\n", wac_i2c->irq);
|
|
|
|
ret = devm_request_threaded_irq(&client->dev, wac_i2c->irq_pdct, NULL,
|
|
wacom_interrupt_pdct,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
|
|
IRQF_TRIGGER_RISING,
|
|
"sec_epen_pdct", wac_i2c);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev,
|
|
"failed to request pdct irq(%d) - %d\n",
|
|
wac_i2c->irq_pdct, ret);
|
|
goto err_request_pdct_irq;
|
|
}
|
|
input_info(true, &client->dev, "init pdct %d\n", wac_i2c->irq_pdct);
|
|
|
|
init_pen_insert(wac_i2c);
|
|
|
|
ret = wacom_sec_init(wac_i2c);
|
|
if (ret)
|
|
goto err_sec_init;
|
|
|
|
wacom_fw_update(wac_i2c, FW_BUILT_IN, false);
|
|
|
|
device_init_wakeup(&client->dev, true);
|
|
|
|
if (wac_i2c->pdata->table_swap) {
|
|
INIT_DELAYED_WORK(&wac_i2c->usb_typec_work, wacom_usb_typec_work);
|
|
INIT_DELAYED_WORK(&wac_i2c->typec_nb_reg_work,
|
|
wacom_usb_typec_nb_register_work);
|
|
schedule_delayed_work(&wac_i2c->typec_nb_reg_work, msecs_to_jiffies(10));
|
|
}
|
|
|
|
input_info(true, &client->dev, "probe done\n");
|
|
|
|
return 0;
|
|
|
|
err_sec_init:
|
|
err_request_pdct_irq:
|
|
err_request_irq:
|
|
err_register_input_dev:
|
|
wake_lock_destroy(&wac_i2c->fw_wakelock);
|
|
wake_lock_destroy(&wac_i2c->wakelock);
|
|
#ifdef LCD_FREQ_SYNC
|
|
mutex_destroy(&wac_i2c->freq_write_lock);
|
|
#endif
|
|
mutex_destroy(&wac_i2c->irq_lock);
|
|
mutex_destroy(&wac_i2c->update_lock);
|
|
mutex_destroy(&wac_i2c->lock);
|
|
|
|
i2c_unregister_device(wac_i2c->client_boot);
|
|
|
|
input_err(true, &client->dev, "failed to probe\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int wacom_i2c_suspend(struct device *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_get_drvdata(dev);
|
|
|
|
wac_i2c->pm_suspend = true;
|
|
reinit_completion(&wac_i2c->resume_done);
|
|
#ifndef USE_OPEN_CLOSE
|
|
if (wac_i2c->input_dev->users)
|
|
wacom_sleep_sequence(wac_i2c);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wacom_i2c_resume(struct device *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_get_drvdata(dev);
|
|
|
|
wac_i2c->pm_suspend = false;
|
|
complete_all(&wac_i2c->resume_done);
|
|
#ifndef USE_OPEN_CLOSE
|
|
if (wac_i2c->input_dev->users)
|
|
wacom_wakeup_sequence(wac_i2c);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(wacom_pm_ops, wacom_i2c_suspend, wacom_i2c_resume);
|
|
#endif
|
|
|
|
static void wacom_i2c_shutdown(struct i2c_client *client)
|
|
{
|
|
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
|
|
|
if (!wac_i2c)
|
|
return;
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
|
|
wacom_power(wac_i2c, false);
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s\n", __func__);
|
|
}
|
|
|
|
static int wacom_i2c_remove(struct i2c_client *client)
|
|
{
|
|
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
|
|
|
input_info(true, &wac_i2c->client->dev, "%s called!\n", __func__);
|
|
|
|
device_init_wakeup(&client->dev, false);
|
|
|
|
wacom_enable_irq(wac_i2c, false);
|
|
wacom_enable_pdct_irq(wac_i2c, false);
|
|
|
|
wacom_power(wac_i2c, false);
|
|
|
|
wake_lock_destroy(&wac_i2c->fw_wakelock);
|
|
wake_lock_destroy(&wac_i2c->wakelock);
|
|
mutex_destroy(&wac_i2c->irq_lock);
|
|
mutex_destroy(&wac_i2c->update_lock);
|
|
mutex_destroy(&wac_i2c->lock);
|
|
|
|
wacom_sec_remove(wac_i2c);
|
|
|
|
i2c_unregister_device(wac_i2c->client_boot);
|
|
|
|
if (wac_i2c->pdata->support_dex) {
|
|
input_unregister_device(wac_i2c->input_dev_pad);
|
|
input_unregister_device(wac_i2c->input_dev_virtual);
|
|
wac_i2c->input_dev = wac_i2c->input_dev_pen;
|
|
}
|
|
|
|
input_unregister_device(wac_i2c->input_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id wacom_i2c_id[] = {
|
|
{"wacom_w90xx", 0},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id wacom_dt_ids[] = {
|
|
{.compatible = "wacom,w90xx"},
|
|
{}
|
|
};
|
|
#endif
|
|
|
|
static struct i2c_driver wacom_i2c_driver = {
|
|
.driver = {
|
|
.owner = THIS_MODULE,
|
|
.name = "wacom_w90xx",
|
|
#ifdef CONFIG_PM
|
|
.pm = &wacom_pm_ops,
|
|
#endif
|
|
.of_match_table = of_match_ptr(wacom_dt_ids),
|
|
},
|
|
.probe = wacom_i2c_probe,
|
|
.remove = wacom_i2c_remove,
|
|
.shutdown = wacom_i2c_shutdown,
|
|
.id_table = wacom_i2c_id,
|
|
};
|
|
|
|
static int __init wacom_i2c_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
if (lpcharge) {
|
|
pr_info("%s: %s: Do not load driver due to : lpm %d\n",
|
|
SECLOG, __func__, lpcharge);
|
|
return ret;
|
|
}
|
|
#endif
|
|
ret = i2c_add_driver(&wacom_i2c_driver);
|
|
if (ret)
|
|
pr_err("%s: %s: failed to add i2c driver\n", SECLOG, __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit wacom_i2c_exit(void)
|
|
{
|
|
i2c_del_driver(&wacom_i2c_driver);
|
|
}
|
|
|
|
module_init(wacom_i2c_init);
|
|
module_exit(wacom_i2c_exit);
|
|
|
|
MODULE_AUTHOR("Samsung");
|
|
MODULE_DESCRIPTION("Driver for Wacom Digitizer Controller");
|
|
MODULE_LICENSE("GPL");
|