blob: f3325043693e44fe012682a775c5741d2a2e61e0 [file] [log] [blame]
/*
* 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/spu-verify.h>
#include <linux/usb/manager/usb_typec_manager_notifier.h>
#include "wacom.h"
#ifdef CONFIG_DISPLAY_SAMSUNG
int get_lcd_attached(char *mode);
#endif
#ifdef CONFIG_EXYNOS_DPU20
int get_lcd_info(char *arg);
#endif
#define WACOM_I2C_RETRY 3
#define WACOM_PATH_EXTERNAL_FW "/sdcard/FIRMWARE/WACOM/wacom.bin"
#define WACOM_PATH_EXTERNAL_FW_SIGNED "/sdcard/FIRMWARE/WACOM/wacom_signed.bin"
#define WACOM_PATH_SPU_FW_SIGNED "/spu/WACOM/ffu_wacom.bin"
#define WACOM_INVALID_IRQ_COUNT 2
enum WAKEUP_ID {
HOVER_WAKEUP = 2,
SINGLETAP_WAKEUP,
DOUBLETAP_WAKEUP,
};
enum SCAN_MODE {
GLOBAL_Y_MODE = 1,
GLOBAL_X_MODE,
FULL_MODE,
LOCAL_MODE,
};
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__);
input_report_abs(wac_i2c->input_dev, ABS_X, 0);
input_report_abs(wac_i2c->input_dev, ABS_Y, 0);
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 0);
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
/* prevent invalid operation of input booster */
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;
}
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;
u8 *buff;
buff = kzalloc(count, GFP_KERNEL);
if (!buff)
return -ENOMEM;
memcpy(buff, buf, count);
do {
if (!wac_i2c->power_enable) {
input_err(true, &wac_i2c->client->dev, "%s: Power status off\n",
__func__);
ret = -EIO;
goto out;
}
ret = i2c_master_send(client, buff, 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);
out:
kfree(buff);
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;
u8 *buff;
buff = kzalloc(count, GFP_KERNEL);
if (!buff)
return -ENOMEM;
do {
if (!wac_i2c->power_enable) {
input_err(true, &wac_i2c->client->dev, "%s: Power status off\n",
__func__);
ret = -EIO;
goto out;
}
ret = i2c_master_recv(client, buff, 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);
memcpy(buf, buff, count);
out:
kfree(buff);
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, };
while (retry--) {
buf[0] = COM_CHECKSUM;
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 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 < RETRY_COUNT; 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_i2c->fw_ver_ic =
((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_i2c->fw_ver_ic = 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_ver_ic=0x%X\n", wac_i2c->fw_ver_ic);
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;
}
wac_i2c->samplerate_state = false;
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;
}
wac_i2c->samplerate_state = true;
msleep(60);
return data[3];
}
void wacom_select_survey_mode(struct wacom_i2c *wac_i2c, bool enable)
{
struct i2c_client *client = wac_i2c->client;
mutex_lock(&wac_i2c->mode_lock);
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);
wac_i2c->reset_flag = true;
msleep(200);
input_info(true, &client->dev, "%s: reset_flag %d\n",
__func__, wac_i2c->reset_flag);
} 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);
}
}
mutex_unlock(&wac_i2c->mode_lock);
}
int 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 -EPERM;
}
data[0] = COM_SURVEY_TARGET_GARAGEONLY;
break;
case EPEN_SURVEY_MODE_GARAGE_AOP:
if ((wac_i2c->function_set & EPEN_SETMODE_AOP_OPTION_AOD_LCD_ON)
== EPEN_SETMODE_AOP_OPTION_AOD_LCD_ON)
data[0] = COM_SURVEYSYNCSCAN;
else
data[0] = COM_SURVEYSCAN;
break;
default:
input_err(true, &client->dev,
"%s: wrong param %d\n", __func__, mode);
return -EINVAL;
}
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 send data(%02x %d)\n",
__func__, data[0], retval);
wac_i2c->reset_flag = true;
return retval;
}
wac_i2c->check_survey_mode = mode;
if (mode)
msleep(300);
wac_i2c->reset_flag = false;
wac_i2c->function_result &= ~EPEN_EVENT_SURVEY;
wac_i2c->function_result |= mode;
return 0;
}
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_scan_mode = set_scan_mode(DISABLE_TSP_SCAN_BLCOK);
wac_i2c->is_tsp_block = false;
}
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 *vdd, *vddio;
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 (pdata->use_vddio && !vddio) {
vddio = devm_regulator_get(&client->dev, "vddio");
if (IS_ERR(vddio)) {
input_err(true, &client->dev,
"%s: could not get vddio, rc = %ld\n",
__func__, PTR_ERR(vddio));
vddio = NULL;
return -ENODEV;
}
retval = regulator_set_voltage(vddio, 1800000, 1800000);
if (retval)
input_err(true, &client->dev,
"%s: unable to set vddio voltage to 1.8V\n",
__func__);
input_err(true, &client->dev, "%s: 1.8V is enabled %s\n",
__func__,
regulator_is_enabled(vddio) ? "TRUE" : "FALSE");
}
if (!vdd) {
vdd = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(vdd)) {
input_err(true, &client->dev,
"%s: could not get vdd, rc = %ld\n",
__func__, PTR_ERR(vdd));
vdd = NULL;
return -ENODEV;
}
retval = regulator_set_voltage(vdd, 3300000, 3300000);
if (retval)
input_err(true, &client->dev,
"%s: unable to set vdd voltage to 3.3V\n",
__func__);
input_err(true, &client->dev, "%s: 3.3V is enabled %s\n",
__func__,
regulator_is_enabled(vdd) ? "TRUE" : "FALSE");
}
if (on) {
if (pdata->use_vddio) {
retval = regulator_enable(vddio);
if (retval) {
input_err(true, &client->dev,
"%s: Fail to enable regulator vddio[%d]\n",
__func__, retval);
} else {
input_err(true, &client->dev, "%s: vddio is enabled[OK]\n",
__func__);
}
}
retval = regulator_enable(vdd);
if (retval) {
input_err(true, &client->dev,
"%s: Fail to enable regulator vdd[%d]\n",
__func__, retval);
} else {
input_err(true, &client->dev, "%s: vdd is enabled[OK]\n",
__func__);
}
} else {
if (pdata->use_vddio) {
if (regulator_is_enabled(vddio)) {
retval = regulator_disable(vddio);
if (retval) {
input_err(true, &client->dev,
"%s: Fail to disable regulator vddio[%d]\n",
__func__, retval);
} else {
input_err(true, &client->dev,
"%s: vddio is disabled[OK]\n",
__func__);
}
} else {
input_err(true, &client->dev,
"%s: vddio is already disabled\n",
__func__);
}
}
if (regulator_is_enabled(vdd)) {
retval = regulator_disable(vdd);
if (retval) {
input_err(true, &client->dev,
"%s: Fail to disable regulator vdd[%d]\n",
__func__, retval);
} else {
input_err(true, &client->dev,
"%s: vdd is disabled[OK]\n", __func__);
}
} else {
input_err(true, &client->dev,
"%s: vdd 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);
#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));
cancel_delayed_work(&wac_i2c->work_print_info);
wac_i2c->print_info_cnt_open = 0;
wac_i2c->tsp_block_cnt = 0;
schedule_work(&wac_i2c->work_print_info.work);
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);
#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;
}
cancel_delayed_work_sync(&wac_i2c->resume_work);
cancel_delayed_work_sync(&wac_i2c->work_print_info);
wacom_print_info(wac_i2c);
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);
forced_release_fullscan(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 void wac_i2c_table_swap_reply(struct wacom_i2c *wac_i2c, char *data)
{
u8 table_id;
table_id = data[3];
if (table_id == 1) {
if (wac_i2c->pdata->table_swap == TABLE_SWAP_DEX_STATION)
wac_i2c->dp_connect_state = true;
if (wac_i2c->pdata->table_swap == TABLE_SWAP_KBD_COVER)
wac_i2c->kbd_cur_conn_state = true;
} else if (!table_id) {
if (wac_i2c->pdata->table_swap == TABLE_SWAP_DEX_STATION)
wac_i2c->dp_connect_state = false;
if (wac_i2c->pdata->table_swap == TABLE_SWAP_KBD_COVER)
wac_i2c->kbd_cur_conn_state = false;
}
}
static void wacom_i2c_reply_handler(struct wacom_i2c *wac_i2c, char *data)
{
char pack_sub_id;
#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
char buff;
int ret;
#endif
pack_sub_id = data[1];
switch (pack_sub_id) {
case SWAP_PACKET:
wac_i2c_table_swap_reply(wac_i2c, data);
break;
case ELEC_TEST_PACKET:
wac_i2c->check_elec++;
#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
buff = COM_SAMPLERATE_STOP;
ret = wacom_i2c_send(wac_i2c, &buff, 1, WACOM_I2C_MODE_NORMAL);
if (ret != 1) {
input_err(true, &wac_i2c->client->dev,
"%s: failed to send data(%02x %02x)\n",
__func__, buff, ret);
} else {
wac_i2c->samplerate_state = false;
}
msleep(50);
buff = COM_SAMPLERATE_133;
ret = wacom_i2c_send(wac_i2c, &buff, 1, WACOM_I2C_MODE_NORMAL);
if (ret != 1) {
input_err(true, &wac_i2c->client->dev,
"%s: failed to send data(%02x %02x)\n",
__func__, buff, ret);
return;
} else {
wac_i2c->samplerate_state = true;
}
#endif
break;
default:
input_info(true, &wac_i2c->client->dev, "%s: unexpected packet %d\n",
__func__, pack_sub_id);
break;
};
}
static void wac_i2c_cmd_noti_handler(struct wacom_i2c *wac_i2c, char *data)
{
wac_i2c->reset_flag = false;
input_info(true, &wac_i2c->client->dev, "%s: reset_flag %d\n", __func__,
wac_i2c->reset_flag);
}
static void wac_i2c_block_tsp_scan(struct wacom_i2c *wac_i2c, char *data)
{
bool tsp;
u8 wacom_mode, scan_mode;
wacom_mode = (data[2] & 0xF0) >> 4;
scan_mode = data[2] & 0x0F;
tsp = !!(data[5] & 0x80);
if (scan_mode == 2)
wac_i2c->is_mode_change = true;
if (tsp && !wac_i2c->is_tsp_block) {
input_info(true, &wac_i2c->client->dev, "%s: tsp:%d wacom mode:%d scan mode:%d\n",
__func__, tsp, wacom_mode, scan_mode);
wac_i2c->is_tsp_block = true;
wac_i2c->tsp_scan_mode = set_scan_mode(ENABLE_TSP_SCAN_BLCOK);
wac_i2c->tsp_block_cnt++;
} else if (!tsp && wac_i2c->is_tsp_block) {
input_info(true, &wac_i2c->client->dev, "%s: tsp:%d wacom mode:%d scan mode:%d\n",
__func__, tsp, wacom_mode, scan_mode);
wac_i2c->tsp_scan_mode = set_scan_mode(DISABLE_TSP_SCAN_BLCOK);
wac_i2c->is_tsp_block = false;
}
}
static void wacom_i2c_noti_handler(struct wacom_i2c *wac_i2c, char *data)
{
char pack_sub_id;
pack_sub_id = data[1];
switch (pack_sub_id) {
case ERROR_PACKET:
if (data[4] == 0x10 && data[5] == 0x11) {
wac_i2c->ble_disable_flag = true;
input_err(true, &wac_i2c->client->dev, "%s: ble mode is disabled!\n", __func__);
}
case TABLE_SWAP_PACKET:
case EM_NOISE_PACKET:
break;
case TSP_STOP_PACKET:
wac_i2c_block_tsp_scan(wac_i2c, data);
break;
case OOK_PACKET:
break;
case CMD_PACKET:
wac_i2c_cmd_noti_handler(wac_i2c, data);
break;
default:
input_info(true, &wac_i2c->client->dev, "%s: unexpected packet %d\n",
__func__, pack_sub_id);
break;
};
}
static void wacom_i2c_aop_handler(struct wacom_i2c *wac_i2c, char *data)
{
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
bool stylus, prox = false;
s16 x, y, tmp;
u8 wacom_mode, wakeup_id;
if (wac_i2c->screen_on || !wac_i2c->survey_mode ||
!(wac_i2c->function_set & EPEN_SETMODE_AOP)) {
input_info(true, &wac_i2c->client->dev, "%s: unexpected status(%d %d %d)\n",
__func__, wac_i2c->screen_on, wac_i2c->survey_mode,
wac_i2c->function_set & EPEN_SETMODE_AOP);
return;
}
prox = !!(data[0] & 0x10);
stylus = !!(data[0] & 0x20);
x = ((u16)data[1] << 8) + (u16)data[2];
y = ((u16)data[3] << 8) + (u16)data[4];
wacom_mode = (data[6] & 0xF0) >> 4;
wakeup_id = data[6] & 0x0F;
/* 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;
/* [for checking */
input_info(true, &wac_i2c->client->dev, "wacom mode %d, wakeup id %d\n", wacom_mode, wakeup_id);
/* for checking] */
if (stylus && (wakeup_id == HOVER_WAKEUP) &&
(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);
wac_i2c->survey_pos.id = EPEN_POS_ID_SCREEN_OF_MEMO;
wac_i2c->survey_pos.x = x;
wac_i2c->survey_pos.y = y;
} else if (wakeup_id == DOUBLETAP_WAKEUP) {
if ((wac_i2c->function_set & EPEN_SETMODE_AOP_OPTION_AOD_LCD_ON) == EPEN_SETMODE_AOP_OPTION_AOD_LCD_ON) {
input_info(true, &wac_i2c->client->dev, "Double Tab detected in AOD\n");
/* 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_abs(wac_i2c->input_dev, ABS_PRESSURE, 1);
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 1);
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, 0);
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
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,
"[P/R] x:%d y:%d tool:%x\n", x, y, wac_i2c->tool);
#else
input_info(true, &wac_i2c->client->dev, "[P/R] tool:%x\n",
wac_i2c->tool);
#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,
"unexpected AOP data : %x %x %x %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], data[13], data[14], data[15]);
}
}
#define EPEN_LOCATION_DETECT_SIZE 6
void epen_location_detect(struct wacom_i2c *wac_i2c, char *loc, int x, int y)
{
int i;
int max_x = wac_i2c->pdata->max_x;
int max_y = wac_i2c->pdata->max_y;
if (wac_i2c->pdata->xy_switch) {
max_x = wac_i2c->pdata->max_y;
max_y = wac_i2c->pdata->max_x;
}
if (wac_i2c->pdata->area_indicator == 0){
/* approximately value */
wac_i2c->pdata->area_indicator = max_y * 4 / 100;
wac_i2c->pdata->area_navigation = max_y * 6 / 100;
wac_i2c->pdata->area_edge = max_y * 3 / 100;
input_raw_info(true, &wac_i2c->client->dev,
"MAX XY(%d/%d), area_edge %d, area_indicator %d, area_navigation %d\n",
max_x, max_y, wac_i2c->pdata->area_edge,
wac_i2c->pdata->area_indicator, wac_i2c->pdata->area_navigation);
}
for (i = 0; i < EPEN_LOCATION_DETECT_SIZE; ++i)
loc[i] = 0;
if (x < wac_i2c->pdata->area_edge)
strcat(loc, "E.");
else if (x < (max_x - wac_i2c->pdata->area_edge))
strcat(loc, "C.");
else
strcat(loc, "e.");
if (y < wac_i2c->pdata->area_indicator)
strcat(loc, "S");
else if (y < (max_y - wac_i2c->pdata->area_navigation))
strcat(loc, "C");
else
strcat(loc, "N");
}
static void wacom_i2c_coord_handler(struct wacom_i2c *wac_i2c, char *data)
{
struct wacom_g5_platform_data *pdata = wac_i2c->pdata;
struct i2c_client *client = wac_i2c->client;
bool prox = false;
bool rdy = false;
bool stylus;
static int old_x = 0, old_y = 0;
s16 x, y, pressure;
s16 tmp;
u8 height = 0;
s8 tilt_x = 0;
s8 tilt_y = 0;
char location[EPEN_LOCATION_DETECT_SIZE] = { 0, };
rdy = !!(data[0] & 0x80);
x = ((u16)data[1] << 8) + (u16)data[2];
y = ((u16)data[3] << 8) + (u16)data[4];
pressure = ((u16)(data[5] & 0x0F) << 8) + (u16)data[6];
height = (u8)data[7];
tilt_x = (s8)data[8];
tilt_y = (s8)data[9];
/* 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;
tilt_x = -tilt_x;
}
if (pdata->y_invert) {
y = pdata->max_y - y;
tilt_y = -tilt_y;
}
if (pdata->xy_switch) {
tmp = x;
x = y;
y = tmp;
tmp = tilt_x;
tilt_x = tilt_y;
tilt_y = tmp;
}
if (rdy) {
prox = !!(data[0] & 0x10);
stylus = !!(data[0] & 0x20);
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;
} 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;
}
/*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;
}
if (!wac_i2c->pen_prox) {
wac_i2c->pen_prox = true;
wac_i2c->virtual_tracking = EPEN_POS_VIEW;
if (data[0] & 0x40)
wac_i2c->tool = BTN_TOOL_RUBBER;
else
wac_i2c->tool = BTN_TOOL_PEN;
epen_location_detect(wac_i2c, location, x, y);
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev, "[HI] x:%d y:%d loc:%s (%s) \n",
x, y, location, wac_i2c->tool == BTN_TOOL_PEN ? "pen" : "rubber");
#else
input_info(true, &client->dev, "[HI] loc:%s (%s)\n",
location, wac_i2c->tool == BTN_TOOL_PEN ? "p" : "r");
#endif
return;
}
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);
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;
}
/* report info */
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, height);
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) {
epen_location_detect(wac_i2c, location, x, y);
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev,
"[P] x:%d y:%d p:%d loc:%s tool:%x mc:%d\n",
x, y, pressure, location, wac_i2c->tool, wac_i2c->mcount);
#else
input_info(true, &client->dev, "[P] p:%d loc:%s tool:%x mc:%d\n",
pressure, location, wac_i2c->tool, wac_i2c->mcount);
#endif
} else if (!prox && wac_i2c->pen_pressed) {
epen_location_detect(wac_i2c, location, x, y);
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev,
"[R] lx:%d ly:%d loc:%s tool:%x mc:%d\n",
x, y, location, wac_i2c->tool, wac_i2c->mcount);
#else
input_info(true, &client->dev, "[R] loc:%s tool:%x mc:%d\n",
location, wac_i2c->tool, wac_i2c->mcount);
#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) {
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 0);
input_report_key(wac_i2c->input_dev, BTN_TOUCH, 0);
/* prevent invalid operation of input booster */
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);
epen_location_detect(wac_i2c, location, x, y);
if (wac_i2c->pen_pressed) {
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev,
"[R] lx:%d ly:%d loc:%s tool:%x mc:%d & [HO]\n",
x, y, location, wac_i2c->tool, wac_i2c->mcount);
#else
input_info(true, &client->dev,
"[R] loc:%s tool:%x mc:%d & [HO]\n",
location, wac_i2c->tool, wac_i2c->mcount);
#endif
} else {
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev, "[HO] lx:%d ly:%d loc:%s mc:%d\n",
x, y, location, wac_i2c->mcount);
#else
input_info(true, &client->dev, "[HO] loc:%s mc:%d\n",
location, wac_i2c->mcount);
#endif
}
} else {
input_info(true, &client->dev,
"unexpected data : %x %x %x %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], data[13], data[14], data[15]);
}
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;
}
static void wacom_event_handler(struct wacom_i2c *wac_i2c, char *buff)
{
char pack_id;
bool debug = true;
pack_id = *buff & 0x0F;
switch (pack_id) {
case COORD_PACKET:
wacom_i2c_coord_handler(wac_i2c, buff);
debug = false;
break;
case AOP_PACKET:
wacom_i2c_aop_handler(wac_i2c, buff);
break;
case NOTI_PACKET:
wacom_i2c_noti_handler(wac_i2c, buff);
break;
case REPLY_PACKET:
wacom_i2c_reply_handler(wac_i2c, buff);
break;
case SEPC_PACKET:
break;
default:
input_info(true, &wac_i2c->client->dev, "%s: unexpected packet %d\n",
__func__, pack_id);
break;
};
if (debug) {
input_info(true, &wac_i2c->client->dev,
"%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
buff[0], buff[1], buff[2], buff[3], buff[4], buff[5],
buff[6], buff[7], buff[8], buff[9], buff[10], buff[11],
buff[12], buff[13], buff[14], buff[15]);
}
}
static irqreturn_t wacom_interrupt(int irq, void *dev_id)
{
struct wacom_i2c *wac_i2c = dev_id;
char data[COM_COORD_NUM + 1] = { 0, };
int ret = 0;
/* in LPM, waiting blsp block resume */
if (wac_i2c->pm_suspend) {
wake_lock_timeout(&wac_i2c->wakelock, msecs_to_jiffies(500));
/* waiting for blsp block resuming, if not occurs i2c error */
ret = wait_for_completion_interruptible_timeout(
&wac_i2c->resume_done, msecs_to_jiffies(500));
if (ret <= 0) {
input_err(true, &wac_i2c->client->dev,
"LPM: pm resume is not handled [%d]\n",
ret);
goto out;
}
input_info(true, &wac_i2c->client->dev, "run LPM interrupt handler [%d]\n",
jiffies_to_msecs(ret));
}
ret = wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM + 1,
WACOM_I2C_MODE_NORMAL);
if (ret > 0) {
wacom_event_handler(wac_i2c, data);
goto out;
}
forced_release(wac_i2c);
forced_release_fullscan(wac_i2c);
wac_i2c->reset_flag = true;
if (!work_busy(&wac_i2c->resume_work.work)) {
input_info(true, &wac_i2c->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, &wac_i2c->client->dev,
"%s resume work is busy\n", __func__);
}
out:
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;
struct timeval current_time;
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) {
do_gettimeofday(&current_time);
wac_i2c->chg_time_stamp = (int)(current_time.tv_sec);
input_err(true, &client->dev, "%s: chg_time_stamp(%d)\n",
__func__, wac_i2c->chg_time_stamp);
}
if (wac_i2c->pen_pdct) {
wac_i2c->function_result &= ~EPEN_EVENT_PEN_OUT;
if (wac_i2c->charging)
start_epen_ble_charging(wac_i2c);
} 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(1 * 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, SW_PEN_INSERTED,
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT));
input_sync(wac_i2c->input_dev);
if (wac_i2c->function_result & EPEN_EVENT_PEN_OUT)
wac_i2c->pen_out_count++;
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");
mutex_lock(&wac_i2c->mode_lock);
wacom_i2c_set_survey_mode(wac_i2c,
EPEN_SURVEY_MODE_GARAGE_ONLY);
mutex_unlock(&wac_i2c->mode_lock);
} 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");
mutex_lock(&wac_i2c->mode_lock);
wacom_i2c_set_survey_mode(wac_i2c, EPEN_SURVEY_MODE_NONE);
mutex_unlock(&wac_i2c->mode_lock);
} 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, SW_PEN_INSERTED,
(wac_i2c->function_result & EPEN_EVENT_PEN_OUT));
input_sync(wac_i2c->input_dev);
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);
#ifdef CONFIG_SEC_FACTORY
if (wac_i2c->epen_blocked){
input_err(true, &wac_i2c->client->dev, "%s : FAC epen_blocked SKIP!!\n", __func__);
return ret;
}
#endif
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);
#ifdef CONFIG_SEC_FACTORY
if (wac_i2c->epen_blocked){
input_err(true, &wac_i2c->client->dev, "%s : FAC epen_blocked SKIP!!\n", __func__);
return;
}
#endif
wacom_sleep_sequence(wac_i2c);
}
static void wacom_i2c_set_input_values(struct wacom_i2c *wac_i2c,
struct input_dev *input_dev)
{
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_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);
if (pdata->xy_switch) {
input_set_abs_params(input_dev, ABS_X, 0, pdata->max_y, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, pdata->max_x, 4, 0);
} else {
input_set_abs_params(input_dev, ABS_X, 0, pdata->max_x, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, pdata->max_y, 4, 0);
}
input_set_capability(input_dev, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input_dev, EV_KEY, BTN_TOOL_RUBBER);
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
input_set_capability(input_dev, EV_KEY, BTN_STYLUS);
input_set_capability(input_dev, EV_SW, SW_PEN_INSERTED);
/* AOP */
input_set_capability(input_dev, EV_KEY, KEY_WAKEUP_UNLOCK);
input_set_capability(input_dev, EV_KEY, KEY_HOMEPAGE);
input_set_drvdata(input_dev, wac_i2c);
}
void wacom_print_info(struct wacom_i2c *wac_i2c)
{
if (!wac_i2c)
return;
if (!wac_i2c->client)
return;
wac_i2c->print_info_cnt_open++;
if (wac_i2c->print_info_cnt_open > 0xfff0)
wac_i2c->print_info_cnt_open = 0;
if (wac_i2c->scan_info_fail_cnt > 1000)
wac_i2c->scan_info_fail_cnt = 1000;
input_info(true, &wac_i2c->client->dev,
"%s: garage %s, ps %s, pen %s, chg %s, ble_mode(0x%x%s), epen %s, cover %s, count(%u,%u,%u), mode(%d), block_cnt(%d), check(%d), ver[0x%x] #%d\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->is_mode_change ? "true" : "false", wac_i2c->ble_mode,
wac_i2c->ble_disable_flag ? ":disabled" : "",
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->scan_info_fail_cnt,
wac_i2c->check_survey_mode, wac_i2c->tsp_block_cnt, wac_i2c->check_elec,
wac_i2c->fw_ver_ic, wac_i2c->print_info_cnt_open);
}
static void wacom_print_info_work(struct work_struct *work)
{
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c, work_print_info.work);
wacom_print_info(wac_i2c);
schedule_delayed_work(&wac_i2c->work_print_info, msecs_to_jiffies(30000)); // 30s
}
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;
if (!wac_i2c->reset_flag && wac_i2c->ble_disable_flag) {
input_info(true, &client->dev,
"%s: ble is diabled & send enable cmd!\n", __func__);
mutex_lock(&wac_i2c->ble_charge_mode_lock);
wacom_ble_charge_mode(wac_i2c, EPEN_BLE_C_ENABLE);
wac_i2c->ble_disable_flag = false;
mutex_unlock(&wac_i2c->ble_charge_mode_lock);
}
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_scan_mode < 0) {
wac_i2c->tsp_scan_mode = set_scan_mode(DISABLE_TSP_SCAN_BLCOK);
wac_i2c->is_tsp_block = false;
}
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__);
}
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 (!wac_i2c->samplerate_state) {
char cmd = COM_SAMPLERATE_START;
input_info(true, &client->dev, "%s: samplerate state is %d, need to recovery\n",
__func__, wac_i2c->samplerate_state);
ret = wacom_i2c_send(wac_i2c, &cmd, 1, WACOM_I2C_MODE_NORMAL);
if (ret < 0) {
input_err(true, &client->dev,
"failed to sned start cmd %d\n",
ret);
} else {
wac_i2c->samplerate_state = true;
}
}
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) samplerate(%d)\n",
__func__, irq_state, ret, wac_i2c->function_set,
wac_i2c->function_result, wac_i2c->samplerate_state);
}
int load_fw_built_in(struct wacom_i2c *wac_i2c, int fw_index)
{
int retry = 3;
int ret;
const char *fw_load_path = NULL;
#ifdef CONFIG_SEC_FACTORY
int index = 0;
#endif
input_info(true, &wac_i2c->client->dev, "%s: load_fw_built_in (%d)\n",
__func__, fw_index);
fw_load_path = wac_i2c->pdata->fw_path;
#ifdef CONFIG_SEC_FACTORY
if (fw_index != FW_BUILT_IN) {
if (fw_index == FW_FACTORY_GARAGE)
index = 0;
else if (fw_index == FW_FACTORY_UNIT)
index = 1;
ret = of_property_read_string_index(wac_i2c->client->dev.of_node,
"wacom,fw_fac_path", index,
&wac_i2c->pdata->fw_fac_path);
if (ret) {
input_err(true, &wac_i2c->client->dev,
"%s: failed to read fw_fac_path %d\n",
__func__, ret);
wac_i2c->pdata->fw_fac_path = NULL;
}
fw_load_path = wac_i2c->pdata->fw_fac_path;
input_info(true, &wac_i2c->client->dev, "%s: load %s firmware\n",
__func__, fw_load_path);
}
#endif
if (fw_load_path == NULL) {
input_err(true, &wac_i2c->client->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;
}
static int wacom_i2c_get_fw_size(struct wacom_i2c *wac_i2c)
{
u32 fw_size = 0;
if (wac_i2c->pdata->ic_type == 9020)
fw_size = 144 * 1024;
else
fw_size = 128 * 1024;
return fw_size;
}
static int load_fw_sdcard(struct wacom_i2c *wac_i2c, const char *file_path)
{
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;
long spu_fsize, spu_ret;
u8 *spu_ums_data;
nSize = wacom_i2c_get_fw_size(wac_i2c);
nSize2 = nSize + sizeof(struct fw_image);
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(file_path, O_RDONLY, S_IRUSR);
if (IS_ERR(fp)) {
input_err(true, &client->dev, "failed to open %s.\n",
file_path);
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",
file_path, fsize);
if (strncmp(file_path, WACOM_PATH_EXTERNAL_FW_SIGNED, strlen(WACOM_PATH_EXTERNAL_FW_SIGNED)) == 0
|| strncmp(file_path, WACOM_PATH_SPU_FW_SIGNED, strlen(WACOM_PATH_SPU_FW_SIGNED)) == 0) {
/* name 5, digest 32, signature 512 */
spu_fsize = fsize;
fsize -= SPU_METADATA_SIZE(WACOM);
}
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;
}
if (strncmp(file_path, WACOM_PATH_EXTERNAL_FW_SIGNED, strlen(WACOM_PATH_EXTERNAL_FW_SIGNED)) == 0
|| strncmp(file_path, WACOM_PATH_SPU_FW_SIGNED, strlen(WACOM_PATH_SPU_FW_SIGNED)) == 0) {
spu_ums_data = kmalloc(spu_fsize, GFP_KERNEL);
if (!spu_ums_data) {
input_err(true, &client->dev, "%s, kmalloc failed\n", __func__);
ret = -EFAULT;
kfree(ums_data);
goto out;
}
nread = vfs_read(fp, (char __user *)spu_ums_data, spu_fsize, &fp->f_pos);
input_info(true, &client->dev, "nread %ld Bytes\n", nread);
if (nread != spu_fsize) {
input_err(true, &client->dev,
"failed to read spu firmware file, nread %ld Bytes\n",
nread);
ret = -EIO;
kfree(spu_ums_data);
kfree(ums_data);
goto out;
}
spu_ret = spu_firmware_signature_verify("WACOM", spu_ums_data, spu_fsize);
input_info(true, &client->dev, "%s: spu_ret : %ld, spu_fsize : %ld // fsize:%ld\n", __func__, spu_ret, spu_fsize, fsize);
if (spu_ret != fsize) {
input_err(true, &client->dev, "%s: signature verify failed, %ld\n", __func__, spu_ret);
ret = -ENOENT;
kfree(spu_ums_data);
kfree(ums_data);
goto out;
}
memcpy(ums_data, spu_ums_data, fsize);
kfree(spu_ums_data);
} else {
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;
}
#if 0
static int load_fw_ffu(struct wacom_i2c *wac_i2c)
{
int ret;
const char *fw_load_path = WACOM_FW_PATH_FFU;
input_info(true, &wac_i2c->client->dev, "%s: (%s)\n", __func__, fw_load_path);
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\n",ret);
return ret;
}
wac_i2c->fw_img = (struct fw_image *)wac_i2c->firm_data->data;
return 0;
}
#endif
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:
#ifdef CONFIG_SEC_FACTORY
case FW_FACTORY_GARAGE:
case FW_FACTORY_UNIT:
#endif
ret = load_fw_built_in(wac_i2c, fw_path);
break;
case FW_IN_SDCARD:
ret = load_fw_sdcard(wac_i2c, WACOM_PATH_EXTERNAL_FW);
break;
case FW_IN_SDCARD_SIGNED:
ret = load_fw_sdcard(wac_i2c, WACOM_PATH_EXTERNAL_FW_SIGNED);
break;
case FW_SPU:
ret = load_fw_sdcard(wac_i2c, WACOM_PATH_SPU_FW_SIGNED);
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_UNIT) || (fw_path == FW_FACTORY_GARAGE)) {
#endif
wac_i2c->fw_ver_bin = 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_GARAGE:
case FW_FACTORY_UNIT:
#endif
release_firmware(wac_i2c->firm_data);
break;
case FW_IN_SDCARD:
case FW_IN_SDCARD_SIGNED:
case FW_SPU:
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->fw_ver_ic;
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);
if (wac_i2c->is_tsp_block)
forced_release_fullscan(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->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->fw_ver_ic, wac_i2c->fw_ver_bin);
if (wac_i2c->pdata->bringup == 1) {
input_info(true, &client->dev, "bringup. do not update\n");
wac_i2c->update_status = FW_UPDATE_FAIL;
goto out_update_fw;
}
if (wac_i2c->pdata->bringup == 3) {
input_info(true, &client->dev, "bringup. force update\n");
bforced = true;
}
/* If FFU firmware version is lower than IC's version, do not run update routine */
if (fw_update_way == FW_SPU && fw_ver_ic >= wac_i2c->fw_ver_bin) {
input_info(true, &client->dev, "FFU. update is skipped\n");
wac_i2c->update_status = FW_UPDATE_PASS;
wacom_i2c_unload_fw(wac_i2c);
wacom_enable_irq(wac_i2c, true);
wacom_enable_pdct_irq(wac_i2c, true);
mutex_unlock(&wac_i2c->update_lock);
wac_i2c->probe_done = true;
return 0;
}
if (!bforced) {
if (fw_ver_ic == wac_i2c->fw_ver_bin) {
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_bin) {
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);
wac_i2c->probe_done = true;
#ifdef CONFIG_SEC_FACTORY
ret = wacom_check_ub(wac_i2c);
if (!ret) {
wacom_i2c_set_survey_mode(wac_i2c, EPEN_SURVEY_MODE_GARAGE_AOP);
input_info(true, &client->dev, "Change mode for garage scan\n");
}
#endif
return -1;
}
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;
int ret = 0;
int retry = 3;
wake_lock(&wac_i2c->fw_wakelock);
if (wac_i2c->fw_update_way == FW_NONE)
goto end_fw_update;
/* 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 end_fw_update;
}
input_info(true, &client->dev, "crc err, do update\n");
}
wac_i2c->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) {
wac_i2c->update_status = FW_UPDATE_FAIL;
wac_i2c->fw_ver_ic = 0;
goto end_fw_update;
}
ret = wacom_i2c_query(wac_i2c);
if (ret < 0) {
input_info(true, &client->dev, "failed to query to IC(%d)\n",
ret);
wac_i2c->update_status = FW_UPDATE_FAIL;
goto end_fw_update;
}
wac_i2c->update_status = FW_UPDATE_PASS;
end_fw_update:
wacom_i2c_unload_fw(wac_i2c);
#ifndef CONFIG_SEC_FACTORY
ret = wacom_open_test(wac_i2c);
if (ret) {
input_err(true, &client->dev, "open test check failed %d\n", ret);
wacom_reset_hw(wac_i2c);
}
#endif
wake_unlock(&wac_i2c->fw_wakelock);
wacom_enable_irq(wac_i2c, true);
wacom_enable_pdct_irq(wac_i2c, true);
if (wac_i2c->update_status == FW_UPDATE_FAIL) {
input_err(true, &client->dev,
"%s : failed to download FW & unload wacom driver\n", __func__);
wacom_sec_remove(wac_i2c);
devm_free_irq(&client->dev, wac_i2c->irq, wac_i2c);
devm_free_irq(&client->dev, wac_i2c->irq_pdct, wac_i2c);
input_unregister_device(wac_i2c->input_dev);
}
wac_i2c->probe_done = true;
#ifdef CONFIG_SEC_FACTORY
ret = wacom_check_ub(wac_i2c);
if (!ret) {
wacom_i2c_set_survey_mode(wac_i2c, EPEN_SURVEY_MODE_GARAGE_AOP);
input_info(true, &client->dev, "Change mode for garage scan\n");
}
#endif
}
#if defined(CONFIG_MUIC_SUPPORT_KEYBOARDDOCK)
static void wac_i2c_kbd_work(struct work_struct *work)
{
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c, kbd_work);
char data;
int ret;
if (wac_i2c->kbd_conn_state == wac_i2c->kbd_cur_conn_state) {
input_info(true, &wac_i2c->client->dev, "%s: already %sconnected\n",
__func__, wac_i2c->kbd_conn_state ? "" : "dis");
return;
}
input_info(true, &wac_i2c->client->dev, "%s: %d\n", __func__,
wac_i2c->kbd_conn_state);
if (!wac_i2c->power_enable) {
input_err(true, &wac_i2c->client->dev,
"%s: powered off\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 = COM_SAMPLERATE_STOP;
ret = wacom_i2c_send(wac_i2c, &data, 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;
}
wac_i2c->samplerate_state = false;
msleep(50);
if (wac_i2c->kbd_conn_state)
data = COM_SPECIAL_COMPENSATION;
else
data = COM_NORMAL_COMPENSATION;
ret = wacom_i2c_send(wac_i2c, &data, 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);
}
data = COM_SAMPLERATE_STOP;
ret = wacom_i2c_send(wac_i2c, &data, 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);
data = COM_SAMPLERATE_133;
ret = wacom_i2c_send(wac_i2c, &data, 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;
}
wac_i2c->samplerate_state = true;
input_info(true, &wac_i2c->client->dev, "%s: %s\n",
__func__, wac_i2c->kbd_conn_state ? "on" : "off");
}
static int wacom_i2c_keyboard_notification_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
struct wacom_i2c *wac_i2c = container_of(nb, struct wacom_i2c, kbd_nb);
int state = !!action;
if (wac_i2c->kbd_conn_state == state)
goto out;
cancel_work_sync(&wac_i2c->kbd_work);
wac_i2c->kbd_conn_state = state;
input_info(true, &wac_i2c->client->dev, "%s: current %d change to %d\n",
__func__, wac_i2c->kbd_cur_conn_state, state);
schedule_work(&wac_i2c->kbd_work);
out:
return NOTIFY_DONE;
}
#endif
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
static void wacom_i2c_usb_typec_work(struct work_struct *work)
{
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c, typec_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;
}
wac_i2c->samplerate_state = false;
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;
}
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);
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;
}
wac_i2c->samplerate_state = true;
input_info(true, &wac_i2c->client->dev, "%s: %s\n",
__func__, wac_i2c->dp_connect_cmd ? "on" : "off");
}
static int wacom_i2c_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_work(&wac_i2c->typec_work);
wac_i2c->dp_connect_cmd = usb_typec_info.sub1;
schedule_work(&wac_i2c->typec_work);
out:
return 0;
}
#endif
static void wacom_i2c_nb_register_work(struct work_struct *work)
{
struct wacom_i2c *wac_i2c = container_of(work, struct wacom_i2c,
nb_reg_work.work);
u32 table_swap = wac_i2c->pdata->table_swap;
u32 count = 0;
int ret;
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if (table_swap == TABLE_SWAP_DEX_STATION)
INIT_WORK(&wac_i2c->typec_work, wacom_i2c_usb_typec_work);
#endif
#ifdef CONFIG_MUIC_SUPPORT_KEYBOARDDOCK
if (table_swap == TABLE_SWAP_KBD_COVER)
INIT_WORK(&wac_i2c->kbd_work, wac_i2c_kbd_work);
#endif
do {
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if (table_swap == TABLE_SWAP_DEX_STATION) {
static bool manager_flag = false;
if (!manager_flag) {
ret = manager_notifier_register(&wac_i2c->typec_nb,
wacom_i2c_usb_typec_notification_cb,
MANAGER_NOTIFY_CCIC_WACOM);
if (!ret) {
manager_flag = true;
input_info(true, &wac_i2c->client->dev,
"%s: typec notifier register success\n",
__func__);
break;
}
}
}
#endif
#ifdef CONFIG_MUIC_SUPPORT_KEYBOARDDOCK
if (table_swap == TABLE_SWAP_KBD_COVER) {
static bool kbd_flag = false;
if (!kbd_flag) {
ret = keyboard_notifier_register(&wac_i2c->kbd_nb,
wacom_i2c_keyboard_notification_cb,
KEYBOARD_NOTIFY_DEV_WACOM);
if (!ret) {
kbd_flag = true;
input_info(true, &wac_i2c->client->dev,
"%s: kbd notifier register success\n",
__func__);
break;
}
}
}
#endif
count++;
msleep(30);
} while (count < 100);
}
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_SEC_FACTORY
bool wacom_check_ub(struct wacom_i2c *wac_i2c)
{
int lcdtype = 0;
#ifdef CONFIG_EXYNOS_DPU20
int connected;
#endif
#ifdef CONFIG_DISPLAY_SAMSUNG
lcdtype = get_lcd_attached("GET");
if (lcdtype == 0xFFFFFF) {
input_info(true, &wac_i2c->client->dev, "lcd is not attached\n");
return false;
}
#endif
#ifdef CONFIG_EXYNOS_DPU20
connected = get_lcd_info("connected");
if (connected < 0) {
input_info(true, &wac_i2c->client->dev,
"Failed to get connection info\n");
return false;
}
if (!connected) {
input_info(true, &wac_i2c->client->dev, "lcd is not attached\n");
return false;
}
lcdtype = get_lcd_info("id");
if (lcdtype < 0) {
input_info(true, &wac_i2c->client->dev, "Failed to get id info\n");
return false;
}
#endif
input_info(true, &wac_i2c->client->dev, "%s: lcdtype 0x%08x\n",
__func__, lcdtype);
return true;
}
#endif
#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, };
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 of irq %d\n", ret);
return ERR_PTR(-EINVAL);
}
pdata->irq_type = tmp[0];
ret = of_property_read_u32(np, "wacom,pdct_type", tmp);
if (ret) {
input_err(true, &client->dev,
"failed to read trigger type of pdct %d\n", ret);
return ERR_PTR(-EINVAL);
}
pdata->pdct_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_array(np, "wacom,max_tilt", tmp, 2);
if (ret != -EINVAL) {
if (ret) {
input_err(true, dev,
"failed to read max x tilt %d\n", ret);
}
pdata->max_x_tilt = tmp[0];
pdata->max_y_tilt = tmp[1];
}
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);
}
ret = of_property_read_u32(np, "wacom,module_ver", &pdata->module_ver);
if (ret) {
input_err(true, &client->dev,
"failed to read module_ver %d\n", ret);
/* default setting to open test */
pdata->module_ver = 1;
}
pdata->use_garage = of_property_read_bool(np, "wacom,use_garage");
ret = of_property_read_u32(np, "wacom,table_swap", &pdata->table_swap);
if (ret) {
input_err(true, &client->dev,
"failed to read module_ver %d\n", ret);
pdata->table_swap = 0;
}
pdata->use_vddio = of_property_read_bool(np, "vddio-supply");
ret = of_property_read_u32(np, "wacom,bringup", &pdata->bringup);
if (ret) {
input_err(true, &client->dev,
"failed to read bringup %d\n", ret);
/* default setting to open test */
pdata->bringup = 0;
}
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) "
"invert: (%d,%d,%d), fw_path: %s, ic_type: %d, "
"table_swap:%d%s%s\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->x_invert, pdata->y_invert, pdata->xy_switch,
pdata->fw_path, pdata->ic_type, pdata->table_swap,
pdata->use_garage ? ", use garage" : "",
pdata->use_vddio ? ", control vddio" : "");
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;
}
/* 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->tsp_scan_mode = DISABLE_TSP_SCAN_BLCOK;
wac_i2c->survey_mode = EPEN_SURVEY_MODE_NONE;
wac_i2c->function_result = EPEN_EVENT_PEN_OUT;
/* Consider about factory, it may be need to as default 1 */
#ifdef CONFIG_SEC_FACTORY
wac_i2c->battery_saving_mode = true;
#else
wac_i2c->battery_saving_mode = false;
#endif
wac_i2c->reset_flag = false;
wac_i2c->pm_suspend = false;
wac_i2c->samplerate_state = true;
wac_i2c->update_status = FW_UPDATE_RUNNING;
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);
/*Initializing for semaphor */
mutex_init(&wac_i2c->lock);
mutex_init(&wac_i2c->update_lock);
mutex_init(&wac_i2c->irq_lock);
mutex_init(&wac_i2c->mode_lock);
mutex_init(&wac_i2c->ble_charge_mode_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_completion(&wac_i2c->resume_done);
INIT_WORK(&wac_i2c->update_work, wacom_i2c_update_work);
INIT_DELAYED_WORK(&wac_i2c->work_print_info, wacom_print_info_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;
}
/*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_ONESHOT |
pdata->pdct_type,
"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->nb_reg_work, wacom_i2c_nb_register_work);
schedule_delayed_work(&wac_i2c->nb_reg_work, msecs_to_jiffies(500));
}
input_info(true, &client->dev, "probe done\n");
input_log_fix();
return 0;
err_sec_init:
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
err_request_pdct_irq:
err_request_irq:
err_register_input_dev:
cancel_delayed_work_sync(&wac_i2c->resume_work);
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);
mutex_destroy(&wac_i2c->mode_lock);
mutex_destroy(&wac_i2c->ble_charge_mode_lock);
i2c_unregister_device(wac_i2c->client_boot);
input_err(true, &client->dev, "failed to probe\n");
input_log_fix();
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;
wac_i2c->probe_done = false;
input_info(true, &wac_i2c->client->dev, "%s called!\n", __func__);
if (wac_i2c->pdata->table_swap) {
#ifdef CONFIG_MUIC_SUPPORT_KEYBOARDDOCK
if (wac_i2c->pdata->table_swap == TABLE_SWAP_KBD_COVER)
keyboard_notifier_unregister(&wac_i2c->kbd_nb);
#endif
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if (wac_i2c->pdata->table_swap == TABLE_SWAP_DEX_STATION)
manager_notifier_unregister(&wac_i2c->typec_nb);
#endif
}
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
cancel_work_sync(&wac_i2c->update_work);
cancel_delayed_work_sync(&wac_i2c->work_print_info);
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 Done\n", __func__);
}
static int wacom_i2c_remove(struct i2c_client *client)
{
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
wac_i2c->probe_done = false;
input_info(true, &wac_i2c->client->dev, "%s called!\n", __func__);
if (wac_i2c->pdata->table_swap) {
#ifdef CONFIG_MUIC_SUPPORT_KEYBOARDDOCK
if (wac_i2c->pdata->table_swap == TABLE_SWAP_KBD_COVER)
keyboard_notifier_unregister(&wac_i2c->kbd_nb);
#endif
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
if (wac_i2c->pdata->table_swap == TABLE_SWAP_DEX_STATION)
manager_notifier_unregister(&wac_i2c->typec_nb);
#endif
}
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
cancel_work_sync(&wac_i2c->update_work);
cancel_delayed_work_sync(&wac_i2c->work_print_info);
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);
mutex_destroy(&wac_i2c->mode_lock);
mutex_destroy(&wac_i2c->ble_charge_mode_lock);
wacom_sec_remove(wac_i2c);
i2c_unregister_device(wac_i2c->client_boot);
input_info(true, &wac_i2c->client->dev, "%s Done\n", __func__);
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");