1777 lines
47 KiB
C
1777 lines
47 KiB
C
/*
|
|
* wacom_i2c.c - Wacom G5 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 "wacom_i2c.h"
|
|
#include "wacom_i2c_flash.h"
|
|
|
|
/*
|
|
&i2c_3 {
|
|
status = "okay";
|
|
wacom_i2c@56 {
|
|
compatible = "wacom,wacom_i2c-ts";
|
|
reg = <0x56>;
|
|
interrupt-parent = <&msmgpio>;
|
|
interrupts = <24 0>;
|
|
// vdd_en-gpio = <&msmgpio 136 0x1>;
|
|
vcc_en-supply = <&pma8084_l18>;
|
|
wacom,scl-gpio = <&msmgpio 50 0x00>;
|
|
wacom,sda-gpio = <&msmgpio 49 0x00>;
|
|
// wacom,reset_n-gpio = <&msmgpio 72 0x00>;
|
|
wacom,pen_fwe1-gpio = <&msmgpio 16 0x00>;
|
|
wacom,pen_pdct-gpio = <&msmgpio 143 0x00>;
|
|
// wacom,sense-gpio = <&pma8084_mpps 3 0x0>;
|
|
wacom,irq-gpio = <&msmgpio 9 0x00>;
|
|
wacom,panel-coords = <1 0 0 12544 0 7056 1 0 2047>;
|
|
wacom,i2c-pull-up = <1>;
|
|
wacom,basic_model = "TR_LTE";
|
|
wacom,irq_flags = <0x3>;
|
|
wacom,ic_mpu_ver = <0x2D>;
|
|
wacom,firmup_flag = <1>;
|
|
};
|
|
};
|
|
*/
|
|
|
|
int wacom_i2c_send(struct wacom_i2c *wac_i2c,
|
|
const char *buf, int count, bool mode)
|
|
{
|
|
struct i2c_client *client;
|
|
|
|
if (!wac_i2c->enabled) {
|
|
dev_info(&wac_i2c->client->dev, "%s: is not enabled\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
client = mode ? wac_i2c->client_boot : wac_i2c->client;
|
|
if (wac_i2c->boot_mode && !mode) {
|
|
dev_info(&client->dev,
|
|
"%s: failed to send\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
return i2c_master_send(client, buf, count);
|
|
}
|
|
|
|
int wacom_i2c_recv(struct wacom_i2c *wac_i2c,
|
|
char *buf, int count, bool mode)
|
|
{
|
|
struct i2c_client *client;
|
|
|
|
if (!wac_i2c->enabled) {
|
|
dev_info(&wac_i2c->client->dev, "%s: is not enabled\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
client = mode ? wac_i2c->client_boot : wac_i2c->client;
|
|
if (wac_i2c->boot_mode && !mode) {
|
|
dev_info(&client->dev,
|
|
"%s: failed to received\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
return i2c_master_recv(client, buf, count);
|
|
}
|
|
|
|
static int wacom_i2c_set_query_data(struct wacom_i2c *wac_i2c, unsigned char *query_data, unsigned char length)
|
|
{
|
|
unsigned char *data;
|
|
|
|
data = kzalloc(length, GFP_KERNEL);
|
|
if (!data) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to allocate query data memory\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(data, query_data, length);
|
|
|
|
wac_i2c->wac_query_data->x_max = (u16)(data[2] + (data[1] << 8));
|
|
wac_i2c->wac_query_data->y_max = (u16)(data[4] + (data[3] << 8));
|
|
|
|
wac_i2c->wac_query_data->pressure_max = (u16)(data[6] + (data[5] << 8));
|
|
wac_i2c->wac_query_data->mpu_type = data[9];
|
|
wac_i2c->wac_query_data->bootloader_ver = data[10];
|
|
wac_i2c->wac_query_data->tiltx_max = data[11];
|
|
wac_i2c->wac_query_data->tilty_max = data[12];
|
|
wac_i2c->wac_query_data->height_max = data[13];
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: maxX:%d, maxY:%d, maxP:%d, fw_ver: 0x%X, mpu:0x%X, bootloader:0x%x, tiltX:%d, tiltY:%d, maxH:%d\n",
|
|
__func__,wac_i2c->wac_query_data->x_max, wac_i2c->wac_query_data->y_max,
|
|
wac_i2c->wac_query_data->pressure_max, wac_i2c->wac_query_data->fw_version_ic,
|
|
wac_i2c->wac_query_data->mpu_type, wac_i2c->wac_query_data->bootloader_ver,
|
|
wac_i2c->wac_query_data->tiltx_max, wac_i2c->wac_query_data->tilty_max,
|
|
wac_i2c->wac_query_data->height_max);
|
|
wac_i2c->query_status = true;
|
|
|
|
/*
|
|
* This code will be removed!!!!!
|
|
* Over Wacom Firmware version 0025, Y axis is not inverted
|
|
*/
|
|
#ifdef CONFIG_SEC_TRLTE_PROJECT
|
|
if (wac_i2c->wac_query_data->fw_version_ic >= 0x25)
|
|
wac_i2c->wac_dt_data->y_invert = 0;
|
|
else
|
|
wac_i2c->wac_dt_data->y_invert = 1;
|
|
#endif
|
|
|
|
kfree(data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int wacom_i2c_query_no_wait(struct wacom_i2c *wac_i2c)
|
|
{
|
|
int ret;
|
|
u8 data[COM_READ_ALL_DATA_LENGTH] = {0, };
|
|
int i = 0, j = 0;
|
|
const int query_limit = 3;
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: start\n", __func__);
|
|
|
|
for (i = 0; i < query_limit; i++) {
|
|
ret = wac_i2c->wacom_i2c_recv(wac_i2c, data, COM_READ_ALL_DATA_LENGTH, false);
|
|
if ((ret < 0) || (COM_READ_ALL_DATA_LENGTH != ret)) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: I2C recv failed(%d, %d)\n",
|
|
__func__, i, ret);
|
|
msleep(100);
|
|
continue;
|
|
}
|
|
|
|
if (0x0f == data[NUM_OF_UNNECESSARY_DATA]) {
|
|
wac_i2c->wac_query_data->fw_version_ic =
|
|
((u16) data[NUM_OF_UNNECESSARY_DATA + 7] << 8) + (u16) data[NUM_OF_UNNECESSARY_DATA + 8];
|
|
|
|
for (j = 0; j < 4; j++)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s:[%d] %X, %X, %X, %X, %X, %X, %X\n",
|
|
__func__, j,
|
|
data[(j * 7)], data[(j * 7) + 1], data[(j * 7) + 2], data[(j * 7) + 3],
|
|
data[(j * 7) + 4], data[(j * 7) + 5], data[(j * 7) + 6]);
|
|
|
|
break;
|
|
} else {
|
|
for (j = 0; j < 4; j++)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s:[%d] %X, %X, %X, %X, %X, %X, %X\n",
|
|
__func__, j,
|
|
data[(j * 7)], data[(j * 7) + 1], data[(j * 7) + 2], data[(j * 7) + 3],
|
|
data[(j * 7) + 4], data[(j * 7) + 5], data[(j * 7) + 6]);
|
|
|
|
}
|
|
}
|
|
|
|
if ((i == query_limit)) {
|
|
dev_info(&wac_i2c->client->dev, "%s: failed\n", __func__);
|
|
wac_i2c->query_status = false;
|
|
|
|
wac_i2c->wac_query_data->x_max = (u16) wac_i2c->wac_dt_data->max_x;
|
|
wac_i2c->wac_query_data->y_max = (u16) wac_i2c->wac_dt_data->max_y;
|
|
wac_i2c->wac_query_data->pressure_max = (u16) wac_i2c->wac_dt_data->max_pressure;
|
|
wac_i2c->wac_query_data->fw_version_ic = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
wacom_i2c_set_query_data(wac_i2c, &data[NUM_OF_UNNECESSARY_DATA], COM_QUERY_NUM);
|
|
|
|
return wac_i2c->wac_query_data->fw_version_ic;
|
|
}
|
|
|
|
int wacom_i2c_query(struct wacom_i2c *wac_i2c)
|
|
{
|
|
int ret;
|
|
u8 buf;
|
|
u8 data[COM_QUERY_NUM] = {0, };
|
|
int i = 0, j = 0;
|
|
const int query_limit = 3;
|
|
|
|
buf = COM_QUERY;
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: start\n", __func__);
|
|
|
|
ret = wacom_i2c_query_no_wait(wac_i2c);
|
|
if (ret > 0) {
|
|
dev_info(&wac_i2c->client->dev, "%s: read query(no_wait):%X\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < query_limit; i++) {
|
|
ret = wac_i2c->wacom_i2c_send(wac_i2c, &buf, 1, false);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: I2C send failed(%d)\n",
|
|
__func__, ret);
|
|
continue;
|
|
}
|
|
msleep(100);
|
|
ret = wac_i2c->wacom_i2c_recv(wac_i2c, data, COM_QUERY_NUM, false);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: I2C recv failed(%d)\n",
|
|
__func__, ret);
|
|
continue;
|
|
}
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: %dth ret of wacom query=%d\n",
|
|
__func__, i, ret);
|
|
if (COM_QUERY_NUM != ret) {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: epen:failed to read i2c(%d)\n",
|
|
__func__, ret);
|
|
continue;
|
|
}
|
|
if (0x0f == data[0]) {
|
|
wac_i2c->wac_query_data->fw_version_ic =
|
|
((u16) data[7] << 8) + (u16) data[8];
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < 2; j++)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s:[%d] %X, %X, %X, %X, %X, %X, %X\n",
|
|
__func__, j,
|
|
data[(j * 7)], data[(j * 7) + 1], data[(j * 7) + 2], data[(j * 7) + 3],
|
|
data[(j * 7) + 4], data[(j * 7) + 5], data[(j * 7) + 6]);
|
|
|
|
if (i == query_limit) {
|
|
dev_info(&wac_i2c->client->dev, "%s: failed\n", __func__);
|
|
wac_i2c->query_status = false;
|
|
|
|
wac_i2c->wac_query_data->x_max = (u16) wac_i2c->wac_dt_data->max_x;
|
|
wac_i2c->wac_query_data->y_max = (u16) wac_i2c->wac_dt_data->max_y;
|
|
wac_i2c->wac_query_data->pressure_max = (u16) wac_i2c->wac_dt_data->max_pressure;
|
|
wac_i2c->wac_query_data->fw_version_ic = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
wacom_i2c_set_query_data(wac_i2c, data, COM_QUERY_NUM);
|
|
|
|
return wac_i2c->wac_query_data->fw_version_ic;
|
|
}
|
|
|
|
int wacom_i2c_modecheck(struct wacom_i2c *wac_i2c)
|
|
{
|
|
u8 buf = COM_QUERY;
|
|
int ret;
|
|
int mode = WACOM_I2C_MODE_NORMAL;
|
|
|
|
ret = wacom_i2c_send(wac_i2c, &buf, 1, false);
|
|
if (ret < 0) {
|
|
mode = WACOM_I2C_MODE_BOOT;
|
|
}
|
|
else{
|
|
mode = WACOM_I2C_MODE_NORMAL;
|
|
}
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s :I2C send at usermode(%d)\n", __func__, ret);
|
|
return mode;
|
|
}
|
|
|
|
static 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);
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
enable_irq(wac_i2c->irq_pdct);
|
|
#endif
|
|
}
|
|
} else {
|
|
if (!depth) {
|
|
++depth;
|
|
disable_irq(wac_i2c->irq);
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
disable_irq(wac_i2c->irq_pdct);
|
|
#endif
|
|
}
|
|
}
|
|
mutex_unlock(&wac_i2c->irq_lock);
|
|
}
|
|
|
|
#define WACOM_USE_PMLDO
|
|
#ifdef WACOM_USE_PMLDO
|
|
static struct regulator *reg_l18;
|
|
#endif
|
|
static int wacom_start(struct wacom_i2c *wac_i2c)
|
|
{
|
|
dev_info(&wac_i2c->client->dev, "%s\n", __func__);
|
|
|
|
if (wac_i2c->wac_dt_data->gpio_pen_reset_n > 0)
|
|
gpio_direction_output(wac_i2c->wac_dt_data->gpio_pen_reset_n, 1);
|
|
|
|
if(wac_i2c->wac_dt_data->vdd_en > 0)
|
|
gpio_direction_output(wac_i2c->wac_dt_data->vdd_en, 1);
|
|
|
|
#ifdef WACOM_USE_PMLDO
|
|
{
|
|
int ret;
|
|
if (!regulator_is_enabled(reg_l18)) {
|
|
ret = regulator_enable(reg_l18);
|
|
if (ret) {
|
|
dev_err(&wac_i2c->client->dev, "enable L18 failed, rc=%d\n", ret);
|
|
return ret;
|
|
}
|
|
dev_info(&wac_i2c->client->dev, "wacom 3.3V on is finished.\n");
|
|
} else{
|
|
dev_err(&wac_i2c->client->dev, "wacom 3.3V is already on.\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
wac_i2c->power_enable = true;
|
|
return 0;
|
|
}
|
|
|
|
static int wacom_stop(struct wacom_i2c *wac_i2c)
|
|
{
|
|
|
|
dev_info(&wac_i2c->client->dev, "%s\n", __func__);
|
|
|
|
if (wac_i2c->wac_dt_data->gpio_pen_reset_n > 0)
|
|
gpio_direction_output(wac_i2c->wac_dt_data->gpio_pen_reset_n, 0);
|
|
|
|
if(wac_i2c->wac_dt_data->vdd_en > 0)
|
|
gpio_direction_output(wac_i2c->wac_dt_data->vdd_en, 0);
|
|
|
|
#ifdef WACOM_USE_PMLDO
|
|
{
|
|
int ret;
|
|
if (regulator_is_enabled(reg_l18)) {
|
|
ret = regulator_disable(reg_l18);
|
|
if (ret) {
|
|
dev_err(&wac_i2c->client->dev, "disable L18 failed, rc=%d\n", ret);
|
|
return ret;
|
|
}
|
|
dev_info(&wac_i2c->client->dev, "wacom 3.3V off is finished.\n");
|
|
} else {
|
|
dev_err(&wac_i2c->client->dev, "wacom 3.3V is already off.\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WACOM_BOOSTER
|
|
if(wac_i2c->wacom_booster->dvfs_set)
|
|
wac_i2c->wacom_booster->dvfs_set(wac_i2c->wacom_booster, -1);
|
|
|
|
#endif
|
|
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
wac_i2c->touch_pressed = false;
|
|
wac_i2c->touchkey_skipped = false;
|
|
#endif
|
|
|
|
wac_i2c->power_enable = false;
|
|
return 0;
|
|
}
|
|
|
|
static int wacom_reset_hw(struct wacom_i2c *wac_i2c)
|
|
{
|
|
wac_i2c->wacom_stop(wac_i2c);
|
|
msleep(30);
|
|
wac_i2c->wacom_start(wac_i2c);
|
|
msleep(200);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void forced_release(struct wacom_i2c *wac_i2c)
|
|
{
|
|
dev_dbg(&wac_i2c->client->dev,"%s\n", __func__);
|
|
input_report_abs(wac_i2c->input_dev, ABS_X, wac_i2c->last_x);
|
|
input_report_abs(wac_i2c->input_dev, ABS_Y, wac_i2c->last_y);
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_X, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_Y, 0);
|
|
#endif
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 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);
|
|
|
|
wac_i2c->last_x = 0;
|
|
wac_i2c->last_y = 0;
|
|
wac_i2c->pen_prox = 0;
|
|
wac_i2c->pen_pressed = 0;
|
|
wac_i2c->side_pressed = 0;
|
|
wac_i2c->pen_pdct = PDCT_NOSIGNAL;
|
|
|
|
}
|
|
|
|
static void wacom_i2c_enable(struct wacom_i2c *wac_i2c)
|
|
{
|
|
bool en = true;
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
|
|
#ifdef BATTERY_SAVING_MODE
|
|
if (wac_i2c->battery_saving_mode)
|
|
#ifdef WACOM_PEN_DETECT
|
|
if (wac_i2c->pen_insert)
|
|
#endif
|
|
en = false;
|
|
#endif
|
|
|
|
if (en) {
|
|
if (!wac_i2c->power_enable)
|
|
wac_i2c->wacom_start(wac_i2c);
|
|
|
|
wac_i2c->compulsory_flash_mode(wac_i2c, false); /* compensation to protect from flash mode */
|
|
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
schedule_delayed_work(&wac_i2c->resume_work, HZ / 5);
|
|
}
|
|
}
|
|
|
|
static void wacom_i2c_disable(struct wacom_i2c *wac_i2c)
|
|
{
|
|
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
|
|
if (wac_i2c->power_enable) {
|
|
wac_i2c->wacom_enable_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);
|
|
|
|
wac_i2c->wacom_stop(wac_i2c);
|
|
wac_i2c->compulsory_flash_mode(wac_i2c, false); /* compensation to protect from flash mode */
|
|
}
|
|
}
|
|
|
|
#ifdef WACOM_USE_SOFTKEY
|
|
static int keycode[] = {
|
|
KEY_RECENT, KEY_BACK,
|
|
};
|
|
|
|
void wacom_i2c_softkey(struct wacom_i2c *wac_i2c, s16 key, s16 pressed)
|
|
{
|
|
if (wac_i2c->pen_prox) {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: prox:%d, run release_hover\n",
|
|
__func__, wac_i2c->pen_prox);
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_X, 0);
|
|
input_report_abs(wac_i2c->input_dev, ABS_TILT_Y, 0);
|
|
#endif
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 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);
|
|
|
|
wac_i2c->pen_prox = 0;
|
|
}
|
|
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
wac_i2c->touchkey_skipped = false;
|
|
#endif
|
|
input_report_key(wac_i2c->input_dev,
|
|
keycode[key], pressed);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
#ifdef WACOM_BOOSTER
|
|
if(wac_i2c->wacom_booster->dvfs_set)
|
|
wac_i2c->wacom_booster->dvfs_set(wac_i2c->wacom_booster, pressed);
|
|
#endif
|
|
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: keycode:%d pressed:%d. pen_prox=%d\n",
|
|
__func__, keycode[key], pressed, wac_i2c->pen_prox);
|
|
#else
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: pressed:%d\n",
|
|
__func__, pressed);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static bool wacom_i2c_coord_range(struct wacom_i2c *wac_i2c, s16 *x, s16 *y)
|
|
{
|
|
if (wac_i2c->wac_dt_data->xy_switch) {
|
|
if ((*x <= wac_i2c->wac_query_data->y_max) && (*y <= wac_i2c->wac_query_data->x_max))
|
|
return true;
|
|
} else {
|
|
if ((*x <= wac_i2c->wac_query_data->x_max) && (*y <= wac_i2c->wac_query_data->y_max))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
void wacom_i2c_write_vsync(struct wacom_i2c *wac_i2c)
|
|
{
|
|
int retval = 1;
|
|
|
|
if (wac_i2c->wait_done) {
|
|
dev_info(&wac_i2c->client->dev, "%s write %d\n", __func__, wac_i2c->vsync);
|
|
retval = ldi_fps(wac_i2c->vsync);
|
|
if (!retval)
|
|
dev_info(&wac_i2c->client->dev, "%s failed\n", __func__);
|
|
wac_i2c->wait_done = false;
|
|
schedule_delayed_work(&wac_i2c->read_vsync_work,
|
|
msecs_to_jiffies(wac_i2c->delay_time * 1000));
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int wacom_i2c_coord(struct wacom_i2c *wac_i2c)
|
|
{
|
|
bool prox = false;
|
|
int ret = 0;
|
|
u8 *data;
|
|
int rubber, stylus;
|
|
static s16 x, y, pressure;
|
|
static s16 tmp;
|
|
int rdy = 0;
|
|
u8 gain = 0;
|
|
s8 tilt_x = 0, tilt_y = 0;
|
|
|
|
#ifdef WACOM_USE_SOFTKEY
|
|
static s16 softkey, pressed, keycode;
|
|
#endif
|
|
|
|
data = wac_i2c->wac_query_data->data;
|
|
|
|
ret = wac_i2c->wacom_i2c_recv(wac_i2c, data, COM_COORD_NUM, false);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to read i2c.L%d\n",
|
|
__func__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
if (!data[1] && !data[2] && !data[3] && !data[4]) {
|
|
if ((data[0] >> 7 == 0) && wac_i2c->boot_done && (data[10] != 0) && (data[11] != 0)) {
|
|
wac_i2c->vsync = 2000000 * 1000 / (((data[10] << 8) | data[11]) + 1);
|
|
wacom_i2c_write_vsync(wac_i2c);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (data[0] & 0x80) {
|
|
/* enable emr device */
|
|
#ifdef WACOM_USE_SOFTKEY
|
|
softkey = !!(data[5] & 0x80);
|
|
if (softkey) {
|
|
pressed = !!(data[5] & 0x40);
|
|
keycode = (data[5] & 0x30) >> 4;
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
if (wac_i2c->touch_pressed) {
|
|
if (pressed) {
|
|
wac_i2c->touchkey_skipped = true;
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s : skip key press\n", __func__);
|
|
} else {
|
|
wac_i2c->touchkey_skipped = false;
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s : skip key release\n", __func__);
|
|
}
|
|
} else {
|
|
if (wac_i2c->touchkey_skipped) {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: skipped touchkey event[%d]\n",
|
|
__func__, pressed);
|
|
if (!pressed)
|
|
wac_i2c->touchkey_skipped = false;
|
|
} else {
|
|
wacom_i2c_softkey(wac_i2c, keycode, pressed);
|
|
}
|
|
}
|
|
#else
|
|
wacom_i2c_softkey(wac_i2c, keycode, pressed);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (!wac_i2c->pen_prox) {
|
|
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
if (gpio_get_value(wac_i2c->wac_dt_data->gpio_pen_pdct)) {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: IC interrupt ocurrs, but PDCT HIGH, return.\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WACOM_BOOSTER
|
|
if(wac_i2c->wacom_booster->dvfs_set)
|
|
wac_i2c->wacom_booster->dvfs_set(wac_i2c->wacom_booster, 1);
|
|
#endif
|
|
wac_i2c->pen_prox = 1;
|
|
|
|
if (data[0] & 0x40)
|
|
wac_i2c->tool = BTN_TOOL_RUBBER;
|
|
else
|
|
wac_i2c->tool = BTN_TOOL_PEN;
|
|
}
|
|
prox = !!(data[0] & 0x10);
|
|
stylus = !!(data[0] & 0x20);
|
|
rubber = !!(data[0] & 0x40);
|
|
rdy = !!(data[0] & 0x80);
|
|
|
|
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];
|
|
|
|
if (wac_i2c->wac_dt_data->x_invert)
|
|
x = wac_i2c->wac_query_data->x_max - x;
|
|
if (wac_i2c->wac_dt_data->y_invert)
|
|
y = wac_i2c->wac_query_data->y_max - y;
|
|
|
|
if (wac_i2c->wac_dt_data->xy_switch) {
|
|
tmp = x;
|
|
x = y;
|
|
y = tmp;
|
|
}
|
|
|
|
if (wacom_i2c_coord_range(wac_i2c, &x, &y)) {
|
|
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, pressure);
|
|
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
input_report_abs(wac_i2c->input_dev,
|
|
ABS_DISTANCE, gain);
|
|
|
|
tilt_x = (s8)data[9];
|
|
tilt_y = -(s8)data[8];
|
|
input_report_abs(wac_i2c->input_dev,
|
|
ABS_TILT_X, tilt_x);
|
|
input_report_abs(wac_i2c->input_dev,
|
|
ABS_TILT_Y, tilt_y);
|
|
#endif
|
|
|
|
input_report_key(wac_i2c->input_dev,
|
|
BTN_STYLUS, stylus);
|
|
input_report_key(wac_i2c->input_dev, BTN_TOUCH, prox);
|
|
input_report_key(wac_i2c->input_dev, wac_i2c->tool, 1);
|
|
input_sync(wac_i2c->input_dev);
|
|
wac_i2c->last_x = x;
|
|
wac_i2c->last_y = y;
|
|
|
|
if (prox && !wac_i2c->pen_pressed) {
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
wac_i2c->touch_pressed = true;
|
|
#endif
|
|
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: pressed. x:%d, y:%d, tx:%d, ty:%d, h:%d, p:%d, data[0]:%X\n",
|
|
__func__, x, y, tilt_x, tilt_y, gain, pressure, data[0]);
|
|
#else
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: pressed\n",
|
|
__func__);
|
|
#endif
|
|
} else if (!prox && wac_i2c->pen_pressed) {
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
schedule_delayed_work(&wac_i2c->touch_pressed_work,
|
|
msecs_to_jiffies(wac_i2c->key_delay_time));
|
|
#endif
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: released\n",
|
|
__func__);
|
|
}
|
|
|
|
wac_i2c->pen_pressed = prox;
|
|
|
|
if (stylus && !wac_i2c->side_pressed)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: side on\n",
|
|
__func__);
|
|
else if (!stylus && wac_i2c->side_pressed)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: side off\n",
|
|
__func__);
|
|
|
|
wac_i2c->side_pressed = stylus;
|
|
}
|
|
} else {
|
|
|
|
if (wac_i2c->pen_prox) {
|
|
/* input_report_abs(wac->input_dev,
|
|
ABS_X, x); */
|
|
/* input_report_abs(wac->input_dev,
|
|
ABS_Y, y); */
|
|
|
|
input_report_abs(wac_i2c->input_dev, ABS_PRESSURE, 0);
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
input_report_abs(wac_i2c->input_dev, ABS_DISTANCE, 0);
|
|
#endif
|
|
input_report_key(wac_i2c->input_dev, BTN_STYLUS, 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);
|
|
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
schedule_delayed_work(&wac_i2c->touch_pressed_work,
|
|
msecs_to_jiffies(wac_i2c->key_delay_time));
|
|
#endif
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: is out\n",
|
|
__func__);
|
|
}
|
|
wac_i2c->pen_prox = 0;
|
|
wac_i2c->pen_pressed = 0;
|
|
wac_i2c->side_pressed = 0;
|
|
wac_i2c->last_x = 0;
|
|
wac_i2c->last_y = 0;
|
|
|
|
#ifdef WACOM_BOOSTER
|
|
if(wac_i2c->wacom_booster->dvfs_set)
|
|
wac_i2c->wacom_booster->dvfs_set(wac_i2c->wacom_booster, 0);
|
|
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef WACOM_HAVE_FWE_PIN
|
|
/* bool en is TRUE ? boot flash mode : boot normal mode */
|
|
static void wacom_compulsory_flash_mode(struct wacom_i2c *wac_i2c, bool en)
|
|
{
|
|
int retry = 100;
|
|
int status = 0;
|
|
while (retry--) {
|
|
gpio_direction_output(wac_i2c->wac_dt_data->gpio_pen_fwe1, en ? 1 : 0);
|
|
|
|
status = gpio_get_value(wac_i2c->wac_dt_data->gpio_pen_fwe1);
|
|
if (status == en) {
|
|
dev_info(&wac_i2c->client->dev, "%s: FWE1 is %s, status:%d\n",
|
|
__func__, en ? "HIGH" : "LOW", status);
|
|
break;
|
|
} else {
|
|
dev_err(&wac_i2c->client->dev, "%s: FWE1 is not set [%s]/[%d]\n",
|
|
__func__, en ? "HIGH" : "LOW", status);
|
|
usleep_range(100, 110);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static irqreturn_t wacom_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_id;
|
|
|
|
wacom_i2c_coord(wac_i2c);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#if defined(WACOM_PDCT_WORK_AROUND)
|
|
static irqreturn_t wacom_interrupt_pdct(int irq, void *dev_id)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_id;
|
|
|
|
if (wac_i2c->query_status == false) {
|
|
dev_info(&wac_i2c->client->dev, "%s: query_read_failed\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
wac_i2c->pen_pdct = gpio_get_value(wac_i2c->wac_dt_data->gpio_pen_pdct);
|
|
|
|
dev_info(&wac_i2c->client->dev, "%s: pdct %d(%d) [%s]\n",
|
|
__func__, wac_i2c->pen_pdct, wac_i2c->pen_prox,
|
|
wac_i2c->pen_pdct ? "Released" : "Pressed");
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WACOM_PEN_DETECT
|
|
static void pen_insert_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, pen_insert_dwork.work);
|
|
|
|
dev_info(&wac_i2c->client->dev, "%s: %d\n", __func__, __LINE__);
|
|
|
|
if (wac_i2c->init_fail)
|
|
return;
|
|
wac_i2c->pen_insert = !gpio_get_value(wac_i2c->gpio_pen_insert);
|
|
|
|
dev_info(&wac_i2c->client->dev, "%s: pen %s\n",
|
|
__func__, wac_i2c->pen_insert ? "instert" : "remove");
|
|
|
|
input_report_switch(wac_i2c->input_dev,
|
|
SW_PEN_INSERT, !wac_i2c->pen_insert);
|
|
input_sync(wac_i2c->input_dev);
|
|
|
|
#ifdef BATTERY_SAVING_MODE
|
|
if (wac_i2c->pen_insert) {
|
|
if (wac_i2c->battery_saving_mode)
|
|
wac_i2c->wacom_i2c_disable(wac_i2c);
|
|
} else {
|
|
wac_i2c->wacom_i2c_enable(wac_i2c);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static irqreturn_t wacom_pen_detect(int irq, void *dev_id)
|
|
{
|
|
struct wacom_i2c *wac_i2c = dev_id;
|
|
bool temp_gpio1,temp_gpio2;
|
|
|
|
dev_info(&wac_i2c->client->dev, "%s: %d\n", __func__, __LINE__);
|
|
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
|
|
|
|
temp_gpio1 = !gpio_get_value(wac_i2c->gpio_pen_insert);
|
|
usleep_range(10 * 1000, 10 * 1000); /* RF call noise is 6~7ms */
|
|
temp_gpio2 = !gpio_get_value(wac_i2c->gpio_pen_insert);
|
|
|
|
if (temp_gpio1 != temp_gpio2)
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: happened noise. old:%d t1:%d, t2:%d\n",
|
|
__func__, wac_i2c->pen_insert, temp_gpio1, temp_gpio2);
|
|
|
|
if (wac_i2c->pen_insert != temp_gpio2) {
|
|
schedule_delayed_work(&wac_i2c->pen_insert_dwork, HZ / 20);
|
|
|
|
wake_lock_timeout(&wac_i2c->wakelock_pen_insert, 3 * HZ);
|
|
} else {
|
|
dev_info(&wac_i2c->client->dev, "%s: ignored. state same.\n", __func__);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
static int wacom_i2c_input_open(struct input_dev *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
|
|
wac_i2c->wacom_i2c_enable(wac_i2c);
|
|
wac_i2c->enabled = true;
|
|
return 0;
|
|
}
|
|
|
|
static void wacom_i2c_input_close(struct input_dev *dev)
|
|
{
|
|
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
|
|
|
if (wake_lock_active(&wac_i2c->wakelock)) {
|
|
dev_err(&wac_i2c->client->dev, "%s: wakelock active\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
|
|
wac_i2c->wacom_i2c_disable(wac_i2c);
|
|
wac_i2c->enabled = false;
|
|
}
|
|
|
|
|
|
static void wacom_i2c_set_input_values(struct i2c_client *client,
|
|
struct wacom_i2c *wac_i2c,
|
|
struct input_dev *input_dev)
|
|
{
|
|
/*Set input values before registering input device */
|
|
|
|
input_dev->name = "sec_e-pen";
|
|
input_dev->id.bustype = BUS_I2C;
|
|
input_dev->dev.parent = &client->dev;
|
|
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
|
|
|
input_dev->evbit[0] |= BIT_MASK(EV_SW);
|
|
input_set_capability(input_dev, EV_SW, SW_PEN_INSERT);
|
|
|
|
input_dev->open = wacom_i2c_input_open;
|
|
input_dev->close = wacom_i2c_input_close;
|
|
|
|
__set_bit(ABS_X, input_dev->absbit);
|
|
__set_bit(ABS_Y, input_dev->absbit);
|
|
__set_bit(ABS_PRESSURE, input_dev->absbit);
|
|
__set_bit(BTN_TOUCH, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
|
__set_bit(BTN_STYLUS, input_dev->keybit);
|
|
__set_bit(KEY_UNKNOWN, input_dev->keybit);
|
|
/* __set_bit(KEY_PEN_PDCT, input_dev->keybit);*/
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
__set_bit(ABS_DISTANCE, input_dev->absbit);
|
|
__set_bit(ABS_TILT_X, input_dev->absbit);
|
|
__set_bit(ABS_TILT_Y, input_dev->absbit);
|
|
#endif
|
|
/* __set_bit(BTN_STYLUS2, input_dev->keybit); */
|
|
/* __set_bit(ABS_MISC, input_dev->absbit); */
|
|
|
|
/*softkey*/
|
|
#ifdef WACOM_USE_SOFTKEY
|
|
__set_bit(KEY_RECENT, input_dev->keybit);
|
|
__set_bit(KEY_BACK, input_dev->keybit);
|
|
#endif
|
|
}
|
|
|
|
static void wacom_i2c_resume_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, resume_work.work);
|
|
|
|
if (wac_i2c->init_fail) {
|
|
dev_info(&wac_i2c->client->dev, "%s: is init failed\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (!wac_i2c->enabled) {
|
|
dev_info(&wac_i2c->client->dev, "%s: is not enabled, turn off IC\n", __func__);
|
|
|
|
return;
|
|
}
|
|
|
|
wac_i2c->wacom_enable_irq(wac_i2c, true);
|
|
|
|
if (wacom_i2c_modecheck(wac_i2c))
|
|
wacom_i2c_usermode(wac_i2c);
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
}
|
|
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
static void wacom_i2c_touch_pressed_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, touch_pressed_work.work);
|
|
|
|
cancel_delayed_work(&wac_i2c->touch_pressed_work);
|
|
wac_i2c->touch_pressed = false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
static void wacom_i2c_read_vsync_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, read_vsync_work.work);
|
|
|
|
wac_i2c->wait_done = true;
|
|
}
|
|
|
|
static void wacom_i2c_boot_done_work(struct work_struct *work)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(work, struct wacom_i2c, boot_done_work.work);
|
|
|
|
wac_i2c->boot_done = true;
|
|
}
|
|
#endif
|
|
|
|
static int wacom_firmware_update(struct wacom_i2c *wac_i2c)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = wacom_load_fw_from_req_fw(wac_i2c);
|
|
if (ret)
|
|
goto failure;
|
|
|
|
if(wac_i2c->wac_dt_data->wacom_firmup_flag != 1){
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: firmup pass (~hw rev03), firmup_flag=%d\n",
|
|
__func__, wac_i2c->wac_dt_data->wacom_firmup_flag);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (wac_i2c->wac_query_data->fw_version_ic < wac_i2c->wac_query_data->fw_version_bin) {
|
|
/*start firm update*/
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: Start firmware flashing (kernel image).\n",
|
|
__func__);
|
|
wake_lock(&wac_i2c->wakelock);
|
|
mutex_lock(&wac_i2c->lock);
|
|
wac_i2c->wacom_enable_irq(wac_i2c, false);
|
|
wac_i2c->wac_query_data->firm_update_status = 1;
|
|
ret = wacom_i2c_firm_update(wac_i2c);
|
|
if (ret)
|
|
goto update_err;
|
|
wac_i2c->fw_data= NULL;
|
|
wac_i2c->wacom_i2c_query(wac_i2c);
|
|
wac_i2c->wac_query_data->firm_update_status = 2;
|
|
wac_i2c->wacom_enable_irq(wac_i2c, true);
|
|
mutex_unlock(&wac_i2c->lock);
|
|
wake_unlock(&wac_i2c->wakelock);
|
|
} else {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: firmware update does not need.\n",
|
|
__func__);
|
|
}
|
|
return ret;
|
|
|
|
update_err:
|
|
wac_i2c->fw_data= NULL;
|
|
wac_i2c->wac_query_data->firm_update_status = -1;
|
|
wac_i2c->wacom_enable_irq(wac_i2c, true);
|
|
mutex_unlock(&wac_i2c->lock);
|
|
wake_unlock(&wac_i2c->wakelock);
|
|
failure:
|
|
return ret;
|
|
}
|
|
static void wacom_init_abs_params(struct wacom_i2c *wac_i2c)
|
|
{
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
int temp, temp1;
|
|
#endif
|
|
if (wac_i2c->wac_dt_data->xy_switch) {
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_X, 0,
|
|
wac_i2c->wac_query_data->y_max, 4, 0);
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_Y, 0,
|
|
wac_i2c->wac_query_data->x_max, 4, 0);
|
|
} else {
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_X, 0,
|
|
wac_i2c->wac_query_data->x_max, 4, 0);
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_Y, 0,
|
|
wac_i2c->wac_query_data->y_max, 4, 0);
|
|
}
|
|
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_PRESSURE, 0,
|
|
wac_i2c->wac_query_data->pressure_max, 0, 0);
|
|
|
|
#ifdef USE_WACOM_TILT_HEIGH
|
|
temp = wac_i2c->wac_query_data->tiltx_max;
|
|
temp1 = temp * -1;
|
|
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_TILT_X, temp1,
|
|
temp, 0, 0);
|
|
|
|
temp = wac_i2c->wac_query_data->tilty_max;
|
|
temp1 = temp * -1;
|
|
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_TILT_Y, temp1,
|
|
temp, 0, 0);
|
|
|
|
input_set_abs_params(wac_i2c->input_dev, ABS_DISTANCE, 0,
|
|
wac_i2c->wac_query_data->height_max, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
static void wacom_request_gpio(struct wacom_devicetree_data *wac_dt_data)
|
|
{
|
|
int ret;
|
|
pr_info("%s: request gpio\n", __func__);
|
|
|
|
ret = gpio_request(wac_dt_data->gpio_int, "wacom_irq");
|
|
if (ret)
|
|
pr_err("%s: unable to request wacom_irq [%d]\n",
|
|
__func__, wac_dt_data->gpio_int);
|
|
|
|
if(wac_dt_data->vdd_en > 0){
|
|
ret = gpio_request(wac_dt_data->vdd_en, "wacom_vdd_en");
|
|
if (ret)
|
|
pr_err("%s: unable to request wacom_vdd_en [%d]\n",
|
|
__func__, wac_dt_data->vdd_en);
|
|
}
|
|
|
|
if (wac_dt_data->gpio_pen_reset_n > 0) {
|
|
ret = gpio_request(wac_dt_data->gpio_pen_reset_n, "wacom_pen_reset_n");
|
|
if (ret)
|
|
pr_err("%s: unable to request wacom_pen_reset_n [%d]\n",
|
|
__func__, wac_dt_data->gpio_pen_reset_n);
|
|
|
|
// gpio_tlmm_config(GPIO_CFG(wac_dt_data->gpio_pen_reset_n, 0,
|
|
// GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1);
|
|
// will be fixed to pinctrl
|
|
}
|
|
|
|
ret = gpio_request(wac_dt_data->gpio_pen_pdct, "pen_pdct-gpio");
|
|
if (ret)
|
|
pr_err("%s: unable to request pen_pdct-gpio [%d]\n",
|
|
__func__, wac_dt_data->gpio_pen_pdct);
|
|
|
|
ret = gpio_request(wac_dt_data->gpio_pen_fwe1, "wacom_pen_fwe1");
|
|
if (ret)
|
|
pr_err("%s: unable to request wacom_pen_fwe1 [%d]\n",
|
|
__func__, wac_dt_data->gpio_pen_fwe1);
|
|
|
|
// gpio_tlmm_config(GPIO_CFG(wac_dt_data->gpio_pen_fwe1, 0,
|
|
// GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1);
|
|
// will be fixed to pinctrl
|
|
|
|
gpio_direction_output(wac_dt_data->gpio_pen_fwe1, 0);
|
|
|
|
if (wac_dt_data->gpio_pen_insert > 0) {
|
|
ret = gpio_request(wac_dt_data->gpio_pen_insert, "wacom_pen_insert");
|
|
if (ret)
|
|
pr_err("[WACOM]%s: unable to request wacom_pen_insert [%d]\n",
|
|
__func__, wac_dt_data->gpio_pen_insert);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static int wacom_get_dt_coords(struct device *dev, char *name,
|
|
struct wacom_devicetree_data *wac_dt_data)
|
|
{
|
|
u32 coords[WACOM_COORDS_ARR_SIZE];
|
|
struct property *prop;
|
|
struct device_node *np = dev->of_node;
|
|
int coords_size, rc;
|
|
|
|
prop = of_find_property(np, name, NULL);
|
|
if (!prop)
|
|
return -EINVAL;
|
|
if (!prop->value)
|
|
return -ENODATA;
|
|
|
|
coords_size = prop->length / sizeof(u32);
|
|
if (coords_size != WACOM_COORDS_ARR_SIZE) {
|
|
dev_err(dev, "invalid %s\n", name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = of_property_read_u32_array(np, name, coords, coords_size);
|
|
if (rc && (rc != -EINVAL)) {
|
|
dev_err(dev, "%s: Unable to read %s\n", __func__, name);
|
|
return rc;
|
|
}
|
|
|
|
if (strncmp(name, "wacom,panel-coords",
|
|
sizeof("wacom,panel-coords")) == 0) {
|
|
wac_dt_data->x_invert = coords[0];
|
|
wac_dt_data->y_invert = coords[1];
|
|
/*
|
|
* below max_x, max_y, min_x, min_y, min_pressure, max_pressure values will be removed.
|
|
* using received value from wacom query data.
|
|
*/
|
|
wac_dt_data->min_x = coords[2];
|
|
wac_dt_data->max_x = coords[3];
|
|
wac_dt_data->min_y = coords[4];
|
|
wac_dt_data->max_y = coords[5];
|
|
wac_dt_data->xy_switch = coords[6];
|
|
wac_dt_data->min_pressure = coords[7];
|
|
wac_dt_data->max_pressure = coords[8];
|
|
|
|
pr_err("%s: x_invert = %d, y_invert = %d, xy_switch = %d\n",
|
|
__func__, wac_dt_data->x_invert,
|
|
wac_dt_data->y_invert, wac_dt_data->xy_switch);
|
|
} else {
|
|
dev_err(dev, "%s: nsupported property %s\n", __func__, name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wacom_parse_dt(struct device *dev,
|
|
struct wacom_devicetree_data *wac_dt_data)
|
|
{
|
|
int rc;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
rc = wacom_get_dt_coords(dev, "wacom,panel-coords", wac_dt_data);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* regulator info */
|
|
wac_dt_data->i2c_pull_up = of_property_read_bool(np, "wacom,i2c-pull-up");
|
|
wac_dt_data->vdd_en = of_get_named_gpio(np, "vdd_en-gpio", 0);
|
|
|
|
/* reset, irq gpio info */
|
|
wac_dt_data->gpio_int = of_get_named_gpio_flags(np, "wacom,irq-gpio",
|
|
0, &wac_dt_data->irq_gpio_flags);
|
|
wac_dt_data->gpio_pen_fwe1 = of_get_named_gpio_flags(np,
|
|
"wacom,pen_fwe1-gpio", 0, &wac_dt_data->pen_fwe1_gpio_flags);
|
|
wac_dt_data->gpio_pen_reset_n = of_get_named_gpio_flags(np,
|
|
"wacom,reset_n-gpio", 0, &wac_dt_data->pen_reset_n_gpio_flags);
|
|
wac_dt_data->gpio_pen_pdct = of_get_named_gpio_flags(np,
|
|
"wacom,pen_pdct-gpio", 0, &wac_dt_data->pen_pdct_gpio_flags);
|
|
wac_dt_data->gpio_pen_insert = of_get_named_gpio(np, "wacom,sense-gpio", 0);
|
|
|
|
rc = of_property_read_string(np, "wacom,basic_model", &wac_dt_data->basic_model);
|
|
if (rc < 0) {
|
|
dev_info(dev, "%s: Unable to read wacom,basic_model\n", __func__);
|
|
wac_dt_data->basic_model = "NULL";
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "wacom,ic_mpu_ver", &wac_dt_data->ic_mpu_ver);
|
|
if (rc < 0)
|
|
dev_info(dev, "%s: Unable to read wacom,ic_mpu_ver\n", __func__);
|
|
|
|
/*Change below if irq is needed */
|
|
rc = of_property_read_u32(np, "wacom,irq_flags", &wac_dt_data->irq_flags);
|
|
if (rc < 0)
|
|
dev_info(dev, "%s: Unable to read wacom,irq_flags\n", __func__);
|
|
|
|
//wac_dt_data->wacom_firmup_flag = of_property_read_bool(np, "wacom,firmup_flag");
|
|
rc = of_property_read_u32(np, "wacom,firmup_flag", &wac_dt_data->wacom_firmup_flag);
|
|
if (rc < 0)
|
|
dev_info(dev, "%s: Unable to read wacom,firmup_flag\n", __func__);
|
|
|
|
|
|
pr_err("%s: en:%d, fwe1: %d, reset_n: %d, pdct: %d, insert: %d, model: %s, mpu: %x, irq_f=%x, fu_f=%d\n",
|
|
__func__, wac_dt_data->vdd_en, wac_dt_data->gpio_pen_fwe1, wac_dt_data->gpio_pen_reset_n,
|
|
wac_dt_data->gpio_pen_pdct, wac_dt_data->gpio_pen_insert,
|
|
wac_dt_data->basic_model, wac_dt_data->ic_mpu_ver,
|
|
wac_dt_data->irq_flags, wac_dt_data->wacom_firmup_flag);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int wacom_parse_dt(struct device *dev,
|
|
struct wacom_devicetree_data *wac_dt_data)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
|
|
static int wacom_i2c_remove(struct i2c_client *client)
|
|
{
|
|
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
|
|
|
free_irq(client->irq, wac_i2c);
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
free_irq(wac_i2c->irq_pdct, wac_i2c);
|
|
#endif
|
|
#ifdef WACOM_PEN_DETECT
|
|
if (wac_i2c->wac_dt_data->gpio_pen_insert > 0)
|
|
free_irq(wac_i2c->irq_pen_insert, wac_i2c);
|
|
#endif
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
cancel_delayed_work_sync(&wac_i2c->touch_pressed_work);
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
cancel_delayed_work_sync(&wac_i2c->read_vsync_work);
|
|
cancel_delayed_work_sync(&wac_i2c->boot_done_work);
|
|
#endif
|
|
#ifdef WACOM_PEN_DETECT
|
|
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
|
|
#endif
|
|
#ifdef WACOM_BOOSTER
|
|
kfree(wac_i2c->wacom_booster);
|
|
#endif
|
|
mutex_destroy(&wac_i2c->lock);
|
|
mutex_destroy(&wac_i2c->irq_lock);
|
|
|
|
wacom_factory_release(wac_i2c->dev);
|
|
input_unregister_device(wac_i2c->input_dev);
|
|
input_free_device(wac_i2c->input_dev);
|
|
|
|
wake_lock_destroy(&wac_i2c->wakelock);
|
|
wake_lock_destroy(&wac_i2c->wakelock_pen_insert);
|
|
|
|
kfree(wac_i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
#define wacom_i2c_suspend NULL
|
|
#define wacom_i2c_resume NULL
|
|
|
|
static void wacom_i2c_early_suspend(struct early_suspend *h)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(h, struct wacom_i2c, early_suspend);
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
|
|
wac_i2c->wacom_i2c_disable(wac_i2c);
|
|
}
|
|
|
|
static void wacom_i2c_late_resume(struct early_suspend *h)
|
|
{
|
|
struct wacom_i2c *wac_i2c =
|
|
container_of(h, struct wacom_i2c, early_suspend);
|
|
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s\n", __func__);
|
|
|
|
wac_i2c->wacom_i2c_enable(wac_i2c);
|
|
}
|
|
#endif
|
|
|
|
static int wacom_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct wacom_devicetree_data *wac_dt_data;
|
|
struct wacom_i2c *wac_i2c;
|
|
struct input_dev *input;
|
|
int ret = 0;
|
|
int error;
|
|
int fw_ver;
|
|
unsigned char buffer_clear[COM_READ_ALL_DATA_LENGTH] = {0, };
|
|
|
|
pr_err("%s\n", __func__);
|
|
/*
|
|
if (boot_mode_recovery == 1) {
|
|
dev_err(&client->dev, "%s: recovery mode\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!get_lcd_attached()) {
|
|
dev_err(&client->dev, "%s: LCD is not attached\n",__func__);
|
|
return -EIO;
|
|
}
|
|
*/
|
|
/*Check I2C functionality */
|
|
ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
|
|
if (!ret) {
|
|
pr_err("%s: No I2C functionality found\n", __func__);
|
|
ret = -ENODEV;
|
|
goto err_i2c_fail;
|
|
}
|
|
|
|
/*Obtain kernel memory space for wacom i2c */
|
|
if (client->dev.of_node) {
|
|
wac_dt_data = devm_kzalloc(&client->dev,
|
|
sizeof(struct wacom_devicetree_data), GFP_KERNEL);
|
|
if (!wac_dt_data) {
|
|
dev_err(&client->dev,
|
|
"%s: Failed to allocate memory\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
error = wacom_parse_dt(&client->dev, wac_dt_data);
|
|
if (error)
|
|
return error;
|
|
} else {
|
|
wac_dt_data = client->dev.platform_data;
|
|
if (!wac_dt_data) {
|
|
dev_err(&client->dev, "%s: no wac_dt_data\n", __func__);
|
|
ret = -ENODEV;
|
|
goto err_i2c_fail;
|
|
}
|
|
}
|
|
|
|
wacom_request_gpio(wac_dt_data);
|
|
|
|
wac_i2c = kzalloc(sizeof(struct wacom_i2c), GFP_KERNEL);
|
|
if (!wac_i2c) {
|
|
dev_err(&client->dev,
|
|
"%s: failed to allocate wac_i2c.\n",
|
|
__func__);
|
|
ret = -ENOMEM;
|
|
goto err_i2c_fail;
|
|
}
|
|
|
|
wac_i2c->client_boot = i2c_new_dummy(client->adapter,
|
|
WACOM_I2C_BOOT);
|
|
if (!wac_i2c->client_boot) {
|
|
dev_err(&client->dev, "Fail to register sub client[0x%x]\n",
|
|
WACOM_I2C_BOOT);
|
|
}
|
|
|
|
input = input_allocate_device();
|
|
if (!input) {
|
|
dev_err(&client->dev,
|
|
"%s: failed to allocate input device.\n",
|
|
__func__);
|
|
ret = -ENOMEM;
|
|
goto err_freemem;
|
|
}
|
|
|
|
wacom_i2c_set_input_values(client, wac_i2c, input);
|
|
|
|
wac_i2c->wac_dt_data = wac_dt_data;
|
|
wac_i2c->input_dev = input;
|
|
wac_i2c->client = client;
|
|
|
|
#ifdef WACOM_HAVE_FWE_PIN
|
|
wac_i2c->compulsory_flash_mode = wacom_compulsory_flash_mode;
|
|
#endif
|
|
wac_i2c->reset_platform_hw = wacom_reset_hw;
|
|
wac_i2c->wacom_start = wacom_start;
|
|
wac_i2c->wacom_stop = wacom_stop;
|
|
wac_i2c->wacom_i2c_send = wacom_i2c_send;
|
|
wac_i2c->wacom_i2c_recv = wacom_i2c_recv;
|
|
wac_i2c->wacom_i2c_query = wacom_i2c_query;
|
|
wac_i2c->wacom_i2c_enable = wacom_i2c_enable;
|
|
wac_i2c->wacom_i2c_disable = wacom_i2c_disable;
|
|
wac_i2c->wacom_enable_irq = wacom_enable_irq;
|
|
|
|
wac_i2c->wac_query_data = kzalloc(sizeof(struct wacom_query_data), GFP_KERNEL);
|
|
if (!wac_i2c->wac_query_data) {
|
|
dev_err(&client->dev,
|
|
"%s: failed to allocate wac_i2c.\n",
|
|
__func__);
|
|
ret = -ENOMEM;
|
|
goto err_freemem;
|
|
}
|
|
/* Set default command state to QUERY */
|
|
wac_i2c->wac_query_data->comstat = COM_QUERY;
|
|
|
|
client->irq = gpio_to_irq(wac_i2c->wac_dt_data->gpio_int);
|
|
dev_info(&wac_i2c->client->dev, "%s: gpio_to_irq : %d\n",
|
|
__func__, client->irq);
|
|
wac_i2c->irq = client->irq;
|
|
|
|
/*Set client data */
|
|
i2c_set_clientdata(client, wac_i2c);
|
|
i2c_set_clientdata(wac_i2c->client_boot, wac_i2c);
|
|
|
|
wake_lock_init(&wac_i2c->wakelock, WAKE_LOCK_SUSPEND, "wacom_wakelock");
|
|
wake_lock_init(&wac_i2c->wakelock_pen_insert, WAKE_LOCK_SUSPEND, "wacom_pen_wakelock");
|
|
|
|
#ifdef WACOM_USE_PMLDO
|
|
reg_l18 = regulator_get(&client->dev, "vcc_en");
|
|
if (IS_ERR(reg_l18)) {
|
|
dev_err(&client->dev, "%s, could not get 8084_l18, rc = %ld=n",__func__,PTR_ERR(reg_l18));
|
|
}else{
|
|
ret = regulator_set_voltage(reg_l18, 3300000, 3300000);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s: unable to set ldo18 voltage to 3.3V\n", __func__);
|
|
}
|
|
}
|
|
dev_info(&wac_i2c->client->dev, "%s: vcc_en ldo18 is done %d\n", __func__, __LINE__);
|
|
#endif
|
|
|
|
#ifdef WACOM_BOOSTER
|
|
wac_i2c->wacom_booster = kzalloc(sizeof(struct input_booster), GFP_KERNEL);
|
|
if (!wac_i2c->wacom_booster) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: Failed to alloc mem for wacom_booster\n", __func__);
|
|
goto err_get_wacom_booster;
|
|
} else {
|
|
input_booster_init_dvfs(wac_i2c->wacom_booster, INPUT_BOOSTER_ID_WACOM);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
wac_i2c->irq_pdct = gpio_to_irq(wac_dt_data->gpio_pen_pdct);
|
|
wac_i2c->pen_pdct = PDCT_NOSIGNAL;
|
|
#endif
|
|
#ifdef WACOM_PEN_DETECT
|
|
if (wac_i2c->wac_dt_data->gpio_pen_insert > 0) {
|
|
wac_i2c->gpio_pen_insert = wac_i2c->wac_dt_data->gpio_pen_insert;
|
|
wac_i2c->irq_pen_insert = gpio_to_irq(wac_i2c->gpio_pen_insert);
|
|
}
|
|
#endif
|
|
if (wac_i2c->wac_dt_data->ic_mpu_ver > 0) {
|
|
wac_i2c->ic_mpu_ver = wac_i2c->wac_dt_data->ic_mpu_ver;
|
|
|
|
wac_i2c->compulsory_flash_mode(wac_i2c, false);
|
|
wac_i2c->wacom_start(wac_i2c);
|
|
msleep(150);
|
|
wac_i2c->enabled = true;
|
|
} else {
|
|
wac_i2c->compulsory_flash_mode(wac_i2c, true);
|
|
/*Reset */
|
|
wac_i2c->wacom_start(wac_i2c);
|
|
msleep(200);
|
|
wac_i2c->enabled = true;
|
|
wac_i2c->ic_mpu_ver = wacom_check_flash_mode(wac_i2c, BOOT_MPU);
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: mpu version: %x\n", __func__, ret);
|
|
|
|
if (wac_i2c->ic_mpu_ver == MPU_W9001)
|
|
wac_i2c->client_boot = i2c_new_dummy(client->adapter,
|
|
WACOM_I2C_9001_BOOT);
|
|
else if ((wac_i2c->ic_mpu_ver == MPU_W9007)||(wac_i2c->ic_mpu_ver == MPU_W9012)) {
|
|
ret = wacom_enter_bootloader(wac_i2c);
|
|
if (ret < 0) {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: failed to get BootLoader version, %d\n", __func__, ret);
|
|
goto err_wacom_i2c_bootloader_ver;
|
|
} else {
|
|
dev_info(&wac_i2c->client->dev,
|
|
"%s: BootLoader version: %x\n", __func__, ret);
|
|
}
|
|
}
|
|
|
|
wac_i2c->compulsory_flash_mode(wac_i2c, false);
|
|
wac_i2c->reset_platform_hw(wac_i2c);
|
|
wac_i2c->power_enable = true;
|
|
}
|
|
|
|
/* Firmware Feature */
|
|
fw_ver = wac_i2c->wacom_i2c_query(wac_i2c);
|
|
pr_err("%s: wacom fw_ver = %d\n", __func__, fw_ver);
|
|
|
|
wacom_init_abs_params(wac_i2c);
|
|
input_set_drvdata(input, wac_i2c);
|
|
|
|
/*Initializing for semaphor */
|
|
mutex_init(&wac_i2c->lock);
|
|
mutex_init(&wac_i2c->irq_lock);
|
|
|
|
INIT_DELAYED_WORK(&wac_i2c->resume_work, wacom_i2c_resume_work);
|
|
|
|
#ifdef USE_WACOM_BLOCK_KEYEVENT
|
|
INIT_DELAYED_WORK(&wac_i2c->touch_pressed_work, wacom_i2c_touch_pressed_work);
|
|
wac_i2c->key_delay_time = 100;
|
|
#endif
|
|
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
wac_i2c->wait_done = true;
|
|
wac_i2c->delay_time = 5;
|
|
INIT_DELAYED_WORK(&wac_i2c->read_vsync_work, wacom_i2c_read_vsync_work);
|
|
|
|
wac_i2c->boot_done = false;
|
|
|
|
INIT_DELAYED_WORK(&wac_i2c->boot_done_work, wacom_i2c_boot_done_work);
|
|
#endif
|
|
#ifdef WACOM_PEN_DETECT
|
|
INIT_DELAYED_WORK(&wac_i2c->pen_insert_dwork, pen_insert_work);
|
|
#endif
|
|
/*Before registering input device, data in each input_dev must be set */
|
|
ret = input_register_device(input);
|
|
if (ret) {
|
|
pr_err("[E-PEN] failed to register input device.\n");
|
|
goto err_input_allocate_device;
|
|
}
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
wac_i2c->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
wac_i2c->early_suspend.suspend = wacom_i2c_early_suspend;
|
|
wac_i2c->early_suspend.resume = wacom_i2c_late_resume;
|
|
register_early_suspend(&wac_i2c->early_suspend);
|
|
#endif
|
|
|
|
wac_i2c->dev = device_create(sec_class, NULL, 0, NULL, "sec_epen");
|
|
if (IS_ERR(wac_i2c->dev)) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: Failed to create device(wac_i2c->dev)!\n",
|
|
__func__);
|
|
goto err_sysfs_create_group;
|
|
}
|
|
|
|
dev_set_drvdata(wac_i2c->dev, wac_i2c);
|
|
|
|
ret = wacom_factory_probe(wac_i2c->dev);
|
|
if (ret) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to create sysfs group\n",
|
|
__func__);
|
|
goto err_sysfs_create_group;
|
|
}
|
|
|
|
ret = sysfs_create_link(&wac_i2c->dev->kobj, &wac_i2c->input_dev->dev.kobj, "input");
|
|
if (ret < 0)
|
|
dev_err(&wac_i2c->client->dev, "%s: Failed to create input symbolic link[%d]\n",
|
|
__func__, ret);
|
|
ret = wacom_firmware_update(wac_i2c);
|
|
if (ret) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: firmware update failed.\n",
|
|
__func__);
|
|
if (fw_ver > 0 && wac_i2c->ic_mpu_ver < 0)
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: read query but not enter boot mode[%x,%x]\n",
|
|
__func__, fw_ver, wac_i2c->ic_mpu_ver);
|
|
else
|
|
goto err_fw_update;
|
|
}
|
|
/*Request IRQ */
|
|
if (wac_dt_data->irq_flags) {
|
|
ret =
|
|
request_threaded_irq(wac_i2c->irq, NULL, wacom_interrupt,
|
|
IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_ONESHOT
|
|
, WACOM_INTERRUPT_NAME, wac_i2c);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to request irq(%d) - %d\n",
|
|
__func__, wac_i2c->irq, ret);
|
|
goto err_fw_update;
|
|
}
|
|
|
|
#if defined(WACOM_PDCT_WORK_AROUND)
|
|
ret = request_threaded_irq(wac_i2c->irq_pdct, NULL,
|
|
wacom_interrupt_pdct,
|
|
IRQF_DISABLED | IRQF_TRIGGER_RISING |
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
WACOM_PDCT_NAME, wac_i2c);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to request irq(%d) - %d\n",
|
|
__func__, wac_i2c->irq_pdct, ret);
|
|
goto err_request_irq_pdct;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WACOM_PEN_DETECT
|
|
if (wac_i2c->wac_dt_data->gpio_pen_insert > 0) {
|
|
ret = request_threaded_irq(
|
|
wac_i2c->irq_pen_insert, NULL,
|
|
wacom_pen_detect,
|
|
IRQF_DISABLED | IRQF_TRIGGER_RISING |
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
WACOM_PEN_INSERT_NAME, wac_i2c);
|
|
if (ret < 0) {
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to request irq(%d) - %d\n",
|
|
__func__, wac_i2c->irq_pen_insert, ret);
|
|
goto err_request_irq_pen_instert;
|
|
}
|
|
enable_irq_wake(wac_i2c->irq_pen_insert);
|
|
|
|
/* update the current status */
|
|
schedule_delayed_work(&wac_i2c->pen_insert_dwork, HZ / 2);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
schedule_delayed_work(&wac_i2c->boot_done_work,
|
|
msecs_to_jiffies(20 * 1000));
|
|
#endif
|
|
|
|
if (!gpio_get_value(wac_i2c->wac_dt_data->gpio_int)) {
|
|
ret = wac_i2c->wacom_i2c_recv(wac_i2c, buffer_clear, COM_READ_ALL_DATA_LENGTH, false);
|
|
if (ret < 0)
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: failed to read register %d\n",
|
|
__func__, ret);
|
|
else
|
|
dev_err(&wac_i2c->client->dev,
|
|
"%s: clear ic i2c buffer\n", __func__);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#ifdef WACOM_PEN_DETECT
|
|
err_request_irq_pen_instert:
|
|
#endif
|
|
#ifdef WACOM_PDCT_WORK_AROUND
|
|
free_irq(wac_i2c->irq_pdct, wac_i2c);
|
|
err_request_irq_pdct:
|
|
#endif
|
|
free_irq(wac_i2c->irq, wac_i2c);
|
|
err_fw_update:
|
|
wacom_factory_release(wac_i2c->dev);
|
|
err_sysfs_create_group:
|
|
wac_i2c->init_fail = true;
|
|
input_unregister_device(input);
|
|
err_input_allocate_device:
|
|
cancel_delayed_work_sync(&wac_i2c->resume_work);
|
|
cancel_delayed_work_sync(&wac_i2c->touch_pressed_work);
|
|
#ifdef USE_WACOM_LCD_WORKAROUND
|
|
cancel_delayed_work_sync(&wac_i2c->read_vsync_work);
|
|
cancel_delayed_work_sync(&wac_i2c->boot_done_work);
|
|
#endif
|
|
#ifdef WACOM_PEN_DETECT
|
|
cancel_delayed_work_sync(&wac_i2c->pen_insert_dwork);
|
|
#endif
|
|
wac_i2c->wacom_stop(wac_i2c);
|
|
mutex_destroy(&wac_i2c->irq_lock);
|
|
mutex_destroy(&wac_i2c->lock);
|
|
err_wacom_i2c_bootloader_ver:
|
|
#ifdef WACOM_BOOSTER
|
|
kfree(wac_i2c->wacom_booster);
|
|
err_get_wacom_booster:
|
|
#endif
|
|
kfree(wac_i2c->wac_query_data);
|
|
wake_lock_destroy(&wac_i2c->wakelock);
|
|
wake_lock_destroy(&wac_i2c->wakelock_pen_insert);
|
|
// input_free_device(input);
|
|
err_freemem:
|
|
kfree(wac_i2c);
|
|
err_i2c_fail:
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id wacom_i2c_id[] = {
|
|
{WACOM_DEVICE_NAME, 0},
|
|
{},
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct of_device_id wacom_match_table[] = {
|
|
{ .compatible = "wacom,wacom_i2c-ts",},
|
|
{ },
|
|
};
|
|
#else
|
|
#define wacom_match_table NULL
|
|
#endif
|
|
/*Create handler for wacom_i2c_driver*/
|
|
static struct i2c_driver wacom_i2c_driver = {
|
|
.driver = {
|
|
.name = WACOM_DEVICE_NAME,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = wacom_match_table,
|
|
#endif
|
|
},
|
|
.probe = wacom_i2c_probe,
|
|
.remove = wacom_i2c_remove,
|
|
.id_table = wacom_i2c_id,
|
|
};
|
|
|
|
static int __init wacom_i2c_init(void)
|
|
{
|
|
int ret = 0;
|
|
pr_info("%s\n", __func__);
|
|
|
|
#ifdef CONFIG_SAMSUNG_LPM_MODE
|
|
if (poweroff_charging) {
|
|
pr_notice("%s : LPM Charging Mode!!\n", __func__);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
ret = i2c_add_driver(&wacom_i2c_driver);
|
|
if (ret)
|
|
pr_err("%s: fail to i2c_add_driver\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit wacom_i2c_exit(void)
|
|
{
|
|
i2c_del_driver(&wacom_i2c_driver);
|
|
}
|
|
/*
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
module_init(wacom_i2c_init);
|
|
#else
|
|
deferred_initcall(wacom_i2c_init);
|
|
#endif
|
|
*/
|
|
module_init(wacom_i2c_init);
|
|
module_exit(wacom_i2c_exit);
|
|
|
|
MODULE_AUTHOR("Samsung");
|
|
MODULE_DESCRIPTION("Driver for Wacom G5SP Digitizer Controller");
|
|
|
|
MODULE_LICENSE("GPL");
|