2363 lines
63 KiB
C
2363 lines
63 KiB
C
/*
|
|
* ELAN HID-I2C TouchScreen driver.
|
|
*
|
|
* Copyright (C) 2014 Elan Microelectronics Corporation.
|
|
* Chuming Zhang <chuming.zhang@elanic.com.cn>
|
|
*
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/input.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/regulator/consumer.h>
|
|
//#include <linux/wakelock.h>
|
|
#include <linux/pm_wakeup.h>
|
|
#include <linux/string.h>
|
|
#include <linux/delay.h>
|
|
#include "elan_ts.h"
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_FB)
|
|
static int fb_notifier_callback(struct notifier_block *self,unsigned long event, void *data);
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
static void elan_ts_early_suspend(struct early_suspend *h);
|
|
static void elan_ts_late_resume(struct early_suspend *h);
|
|
#endif
|
|
|
|
//define private data
|
|
struct elan_ts_data *private_ts;
|
|
unsigned long delay = HZ;
|
|
|
|
/************************key event define**************************/
|
|
static const int key_value[] = {KEY_MENU, KEY_HOMEPAGE, KEY_BACK};
|
|
|
|
void elan_ts_hw_reset(struct ts_chip_hw_info *hw_info)
|
|
{
|
|
gpio_set_value(hw_info->rst_gpio, 1);
|
|
msleep(10);
|
|
gpio_set_value(hw_info->rst_gpio, 0);
|
|
msleep(100);
|
|
gpio_set_value(hw_info->rst_gpio, 1);
|
|
printk("[elan] elan_ts_hw_reset()\n");
|
|
}
|
|
|
|
void elan_switch_irq(struct elan_ts_data *ts, int on)
|
|
{
|
|
// struct elan_ts_data *ts = private_ts;
|
|
|
|
dev_err(&ts->client->dev,
|
|
"[elan] %s enter, irq = %d, on = %d, irq_lock_flag=%d\n",
|
|
__func__, ts->hw_info.irq_num, on, ts->irq_lock_flag);
|
|
mutex_lock(&ts->irq_mutex);
|
|
if (on) {
|
|
if(ts->irq_lock_flag == 1) {
|
|
enable_irq(ts->hw_info.irq_num);
|
|
ts->irq_lock_flag = 0;
|
|
}
|
|
} else {
|
|
if(ts->irq_lock_flag == 0) {
|
|
disable_irq(ts->hw_info.irq_num);
|
|
ts->irq_lock_flag = 1;
|
|
}
|
|
}
|
|
mutex_unlock(&ts->irq_mutex);
|
|
}
|
|
|
|
static int elan_poll_int(void)
|
|
{
|
|
int status = 0, retry = 50;//20;
|
|
|
|
do {
|
|
status = gpio_get_value(private_ts->hw_info.intr_gpio);
|
|
if (status == 0)
|
|
break;
|
|
retry--;
|
|
msleep(10);//msleep(20);
|
|
} while (status == 1 && retry > 0);
|
|
|
|
if (status > 0)
|
|
dev_info(&private_ts->client->dev,
|
|
"%s: poll interrupt status %s\n",\
|
|
__func__, status == 1 ? "high" : "low");
|
|
|
|
return status == 0 ? 0 : -ETIMEDOUT;
|
|
}
|
|
|
|
static int elan_i2c_send(const uint8_t *buf, int count)
|
|
{
|
|
int ret = -1;
|
|
int retries = 0;
|
|
struct i2c_client *client = private_ts->client;
|
|
struct i2c_adapter *adap = client->adapter;
|
|
struct i2c_msg msg;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = client->flags & I2C_M_TEN;
|
|
msg.len = count;
|
|
msg.buf = (char *)buf;
|
|
|
|
while(retries < 5)
|
|
{
|
|
ret = i2c_transfer(adap, &msg, 1);
|
|
if (ret == 1)break;
|
|
retries++;
|
|
}
|
|
|
|
/*
|
|
* If everything went ok (i.e. 1 msg transmitted), return #bytes
|
|
* transmitted, else error code.
|
|
*/
|
|
return (ret == 1) ? count : ret;
|
|
}
|
|
|
|
static int elan_i2c_recv(uint8_t *buf, int count)
|
|
{
|
|
int ret = -1;
|
|
int retries = 0;
|
|
struct i2c_client *client = private_ts->client;
|
|
struct i2c_adapter *adap = client->adapter;
|
|
struct i2c_msg msg;
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = client->flags & I2C_M_TEN;
|
|
msg.flags |= I2C_M_RD;
|
|
msg.len = count;
|
|
msg.buf = buf;
|
|
|
|
while(retries < 5)
|
|
{
|
|
ret = i2c_transfer(adap, &msg, 1);
|
|
if(ret == 1)break;
|
|
retries++;
|
|
}
|
|
/*
|
|
* If everything went ok (i.e. 1 msg received), return #bytes received,
|
|
* else error code.
|
|
*/
|
|
|
|
return (ret == 1) ? count : ret;
|
|
}
|
|
|
|
struct elan_i2c_operation elan_ops = {
|
|
.send = elan_i2c_send,
|
|
.recv = elan_i2c_recv,
|
|
.poll = elan_poll_int,
|
|
};
|
|
|
|
int elan_ic_status(struct i2c_client *client)
|
|
{
|
|
uint8_t checkstatus[HID_CMD_LEN] = {0x04, 0x00, 0x23, 0x00, 0x03, 0x18};
|
|
uint8_t buf[HID_RECV_LEN] = {0x00};
|
|
int err = 0;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
int retry = 3;
|
|
|
|
/*HID protocol after reset need delay 300ms*/
|
|
msleep(100);
|
|
|
|
RETRY:
|
|
err = ts->ops->send(checkstatus,sizeof(checkstatus));
|
|
if (err != sizeof(checkstatus)) {
|
|
dev_err(&client->dev,
|
|
"[elan] ERROR: Send get hid hello cmd fail!len=%d\n", err);
|
|
if((retry--)> 0)
|
|
goto RETRY;
|
|
return -1;
|
|
}
|
|
|
|
err = ts->ops->poll();//elan_poll_int();
|
|
if (err) {
|
|
dev_err(&client->dev,
|
|
"[elan] ERROR: %s INT status high",__func__);
|
|
|
|
if((retry--) > 0)
|
|
goto RETRY;
|
|
return err;
|
|
}
|
|
|
|
err = ts->ops->recv(buf, sizeof(buf));
|
|
if (err != sizeof(buf)) {
|
|
dev_err(&client->dev,
|
|
"[elan] ERROR:%s Read Hello Data error\n", __func__);
|
|
if((retry--) > 0)
|
|
goto RETRY;
|
|
return -1;
|
|
}
|
|
|
|
dev_err(&client->dev, "[elan] FW Mode = 0x%2x\n",buf[4]);
|
|
if ( HID_FW_NORMAL_MODE == buf[4]) {
|
|
return COMPARE_UPGRADE;
|
|
} else if (HID_FW_RECOVERY_MODE == buf[4]) {
|
|
if (buf[6] != buf[7])
|
|
ts->fw_info.fw_bcl = buf[7];
|
|
else
|
|
ts->fw_info.fw_bcl = buf[4];
|
|
return FORCED_UPGRADE;
|
|
}else
|
|
return UNKNOW_TYPE;
|
|
}
|
|
|
|
static int get_normal_hello(struct i2c_client *client)
|
|
{
|
|
int err = 0;
|
|
uint8_t buf[8] = { 0 };
|
|
uint8_t normal_hello[4] = {NORMAL_FW_NORMAL_MODE,NORMAL_FW_NORMAL_MODE,NORMAL_FW_NORMAL_MODE,NORMAL_FW_NORMAL_MODE};
|
|
uint8_t recovery_hello[4] = {NORMAL_FW_NORMAL_MODE,NORMAL_FW_NORMAL_MODE,NORMAL_FW_RECOVERY_MODE,NORMAL_FW_RECOVERY_MODE};
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
|
|
err = ts->ops->poll();//elan_poll_int();
|
|
if (err) {
|
|
dev_err(&client->dev,
|
|
"[elan] ERROR: %s INT status high",__func__);
|
|
return -1;
|
|
}
|
|
|
|
err = ts->ops->recv(buf, sizeof(buf));
|
|
if (err != sizeof(buf)) {
|
|
dev_err(&client->dev,
|
|
"[elan] ERROR:%s Read Hello Data error\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if( memcmp(buf,normal_hello,sizeof(normal_hello)) == 0) {
|
|
dev_info(&client->dev, "[elan] hello packet check success!!\n");
|
|
return COMPARE_UPGRADE;
|
|
}
|
|
else if( memcmp(buf,recovery_hello,sizeof(recovery_hello)) == 0) {
|
|
dev_info(&client->dev, "[elan] hello packet check faile!!\n");
|
|
return FORCED_UPGRADE;
|
|
} else {
|
|
dev_info(&client->dev, "[elan] recive hello packet error!!\n");
|
|
return UNKNOW_TYPE;
|
|
}
|
|
}
|
|
|
|
int elan__hello_packet_handler(struct i2c_client *client, int chip_type)
|
|
{
|
|
int ret = 0;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
|
|
dev_err(&client->dev, "[elan] chip_type =%d\n", chip_type);
|
|
if (HID_TYPE_PROTOCOL == chip_type) {
|
|
ret = elan_ic_status(client);
|
|
} else if (chip_type == NORMAL_TYPE_PROTOCOL) {
|
|
ret = get_normal_hello(client);
|
|
}
|
|
|
|
ts->recover = ret;
|
|
dev_err(&client->dev, "[elan] ts->recover =%d\n", ts->recover);
|
|
return ret;
|
|
}
|
|
|
|
static int elan_ts_get_data(struct i2c_client *client, const uint8_t *wbuf,
|
|
size_t wsize, uint8_t *rbuf, size_t rsize)
|
|
{
|
|
int err = 0;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
|
|
if (!wbuf || !rbuf)
|
|
return -EINVAL;
|
|
|
|
err = ts->ops->send(wbuf, wsize);
|
|
if(err != wsize) {
|
|
dev_err(&client->dev, "[elan] %s send cmd faile\n",__func__);
|
|
err = -1;
|
|
return err;
|
|
}
|
|
|
|
err = ts->ops->poll();//elan_poll_int();
|
|
if (err != 0) {
|
|
dev_err(&client->dev, "[elan] %s Int status hight\n",__func__);
|
|
return err;
|
|
}
|
|
|
|
err = ts->ops->recv(rbuf,rsize);
|
|
if (err != rsize) {
|
|
dev_err(&client->dev, "[elan] %s cmd respone error\n",__func__);
|
|
err = -1;
|
|
return err;
|
|
} else {
|
|
if (ts->chip_type == HID_TYPE_PROTOCOL) {
|
|
if ((CMD_S_PKT == rbuf[4])|| (REG_S_PKT == rbuf[4]))
|
|
return 0;
|
|
else
|
|
return -EINVAL;
|
|
} else {
|
|
if ((CMD_S_PKT == rbuf[0]) || (REG_S_PKT == rbuf[0]))
|
|
return 0;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int hid_fw_packet_handler(struct i2c_client *client)
|
|
{
|
|
const int pen_osr = 260; /*Ntring=256, Warcon: 260*/
|
|
const uint8_t cmd_ver[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0x00,0x00,0x01};
|
|
const uint8_t cmd_id[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0xf0,0x00,0x01};
|
|
const uint8_t cmd_bc[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0x10,0x00,0x01};
|
|
const uint8_t cmd_osr[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0xD6,0x00,0x01};
|
|
const uint8_t cmd_test_ver[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0xe0,0x00,0x01};
|
|
const uint8_t cmd_whck_ver[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x53,0xd2,0x00,0x01};
|
|
const uint8_t cmd_res[HID_CMD_LEN] = {0x04,0x00,0x23,0x00,0x03,0x00,0x04,0x5B,0x00,0x00,0x00,0x00,0x00};
|
|
uint8_t rbuf[HID_RECV_LEN] = {0};
|
|
int err = 0;
|
|
int major, minor;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
struct elan_fw_info *fw_info = &ts->fw_info;
|
|
|
|
/*fw version*/
|
|
err = elan_ts_get_data(client, cmd_ver, sizeof(cmd_ver), rbuf, sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get fw version failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[5] & 0x0f) << 4) | ((rbuf[6] & 0xf0) >> 4);
|
|
minor = ((rbuf[6] & 0x0f) << 4) | ((rbuf[7] & 0xf0) >> 4);
|
|
fw_info->fw_ver = major << 8 | minor;
|
|
|
|
|
|
/*fw id*/
|
|
err = elan_ts_get_data(client, cmd_id, sizeof(cmd_id),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get fw id failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[5] & 0x0f) << 4) | ((rbuf[6] & 0xf0) >> 4);
|
|
minor = ((rbuf[6] & 0x0f) << 4) | ((rbuf[7] & 0xf0) >> 4);
|
|
fw_info->fw_id = major << 8 | minor;
|
|
|
|
/*get bootcode version*/
|
|
err = elan_ts_get_data(client, cmd_bc, sizeof(cmd_bc),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get bootcode version failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[5] & 0x0f) << 4) | ((rbuf[6] & 0xf0) >> 4);
|
|
minor = ((rbuf[6] & 0x0f) << 4) | ((rbuf[7] & 0xf0) >> 4);
|
|
fw_info->fw_bcv = major << 8 | minor;
|
|
fw_info->fw_bcl = minor;
|
|
|
|
/*get finger osr*/
|
|
err = elan_ts_get_data(client, cmd_osr, sizeof(cmd_osr),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get finger osr failed\n",__func__);
|
|
return err;
|
|
}
|
|
fw_info->finger_osr = rbuf[7];
|
|
|
|
/*get trace num*/
|
|
err = elan_ts_get_data(client, cmd_res, sizeof(cmd_res),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get finger osr failed\n",__func__);
|
|
return err;
|
|
}
|
|
|
|
// for ic rx = buf[6], tx = buf[7], rx > tx
|
|
fw_info->rx = rbuf[6];
|
|
fw_info->tx = rbuf[7];
|
|
/*finger resolution*/
|
|
fw_info->finger_xres = (rbuf[6] * 2 - 1) * fw_info->finger_osr;
|
|
fw_info->finger_yres = (rbuf[7] - 1) * fw_info->finger_osr;
|
|
|
|
/*pen resolution*/
|
|
fw_info->pen_xres = (rbuf[6] * 2 - 1) * pen_osr;
|
|
fw_info->pen_yres = (rbuf[7] - 1) * pen_osr;
|
|
|
|
/*get test ver*/
|
|
err = elan_ts_get_data(client, cmd_test_ver, sizeof(cmd_test_ver),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get test ver failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[5] & 0x0f) << 4) | ((rbuf[6] & 0xf0) >> 4);
|
|
minor = ((rbuf[6] & 0x0f) << 4) | ((rbuf[7] & 0xf0) >> 4);
|
|
fw_info->testsolversion = major << 8 | minor;
|
|
fw_info->testversion = major;
|
|
fw_info->solutionversion = minor;
|
|
|
|
/*get whck ver*/
|
|
err = elan_ts_get_data(client, cmd_whck_ver, sizeof(cmd_whck_ver),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get test ver failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[5] & 0x0f) << 4) | ((rbuf[6] & 0xf0) >> 4);
|
|
minor = ((rbuf[6] & 0x0f) << 4) | ((rbuf[7] & 0xf0) >> 4);
|
|
fw_info->whck_ver = major << 8 | minor;
|
|
|
|
dev_info(&client->dev,
|
|
"[elan] %s fw version:0x%4.4x\n",
|
|
__func__,fw_info->fw_ver);
|
|
dev_info(&client->dev,
|
|
"[elan] %s fw id:0x%4.4x\n",
|
|
__func__,fw_info->fw_id);
|
|
dev_info(&client->dev,
|
|
"[elan] %s bootcode version:0x%4.4x: low byte 0x%2.2x\n",
|
|
__func__,fw_info->fw_bcv,fw_info->fw_bcl);
|
|
dev_info(&client->dev,
|
|
"[elan] %s fw_info->rx, fw_info->tx: %d:%d\n",
|
|
__func__,fw_info->rx, fw_info->tx);
|
|
dev_info(&client->dev,
|
|
"[elan] %s finger x/y resolution:0x%4.4x/0x%4.4x\n",
|
|
__func__,fw_info->finger_xres,fw_info->finger_yres);
|
|
dev_info(&client->dev,
|
|
"[elan] %s pen x/y resolution:0x%4.4x/0x%4.4x\n",
|
|
__func__,fw_info->pen_xres,fw_info->pen_yres);
|
|
dev_info(&client->dev,
|
|
"[elan] %s testsolversion:testversion :0x%4.4x/0x%4.4x\n",
|
|
__func__,fw_info->testsolversion,fw_info->testversion);
|
|
dev_info(&client->dev,
|
|
"[elan] %s solutionversion:whck_ver : 0x%4.4x/0x%4.4x\n",
|
|
__func__,fw_info->solutionversion,fw_info->whck_ver);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int normal_fw_packet_handler(struct i2c_client *client)
|
|
{
|
|
int err = 0;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
int major, minor;
|
|
struct elan_fw_info *fw_info = &ts->fw_info;
|
|
|
|
const uint8_t cmd_ver[] = {0x53, 0x00, 0x00, 0x01};
|
|
const uint8_t cmd_id[] = {0x53, 0xf0, 0x00, 0x01};
|
|
const uint8_t cmd_bc[] = {0x53, 0x10, 0x00, 0x01};
|
|
|
|
#ifndef TWO_LAYER
|
|
const uint8_t cmd_x[] = {0x53, 0x60, 0x00, 0x00};
|
|
const uint8_t cmd_y[] = {0x53, 0x63, 0x00, 0x00};
|
|
uint8_t rbuf[4] = {0x00};
|
|
#else
|
|
const uint8_t cmd_info[] = {0x5B, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
uint8_t rbuf[17] = {0x00};
|
|
#endif
|
|
|
|
/*fw version*/
|
|
err = elan_ts_get_data(client, cmd_ver, sizeof(cmd_ver),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get fw version failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[1] & 0x0f) << 4) | ((rbuf[2] & 0xf0) >> 4);
|
|
minor = ((rbuf[2] & 0x0f) << 4) | ((rbuf[3] & 0xf0) >> 4);
|
|
fw_info->fw_ver = major << 8 | minor;
|
|
|
|
/*fw id*/
|
|
err = elan_ts_get_data(client, cmd_id, sizeof(cmd_id),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get fw id failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[1] & 0x0f) << 4) | ((rbuf[2] & 0xf0) >> 4);
|
|
minor = ((rbuf[2] & 0x0f) << 4) | ((rbuf[3] & 0xf0) >> 4);
|
|
fw_info->fw_id = major << 8 | minor;
|
|
|
|
/*get boocode version*/
|
|
err = elan_ts_get_data(client, cmd_bc, sizeof(cmd_bc),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get bootcode version failed\n",__func__);
|
|
return err;
|
|
}
|
|
major = ((rbuf[1] & 0x0f) << 4) | ((rbuf[2] & 0xf0) >> 4);
|
|
minor = ((rbuf[2] & 0x0f) << 4) | ((rbuf[3] & 0xf0) >> 4);
|
|
fw_info->fw_bcv = major << 8 | minor;
|
|
|
|
#ifndef TWO_LAYER
|
|
err = elan_ts_get_data(client, cmd_x, sizeof(cmd_x),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get finger xresolution failed\n",__func__);
|
|
return err;
|
|
}
|
|
minor = ((rbuf[2])) | ((rbuf[3] & 0xf0) << 4);
|
|
fw_info->finger_xres = minor;
|
|
|
|
err = elan_ts_get_data(client, cmd_y, sizeof(cmd_y),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get finger yresolution failed\n",__func__);
|
|
return err;
|
|
}
|
|
minor = ((rbuf[2])) | ((rbuf[3] & 0xf0) << 4);
|
|
fw_info->finger_yres = minor;
|
|
#else
|
|
err = elan_ts_get_data(client, cmd_info, sizeof(cmd_info),rbuf,sizeof(rbuf));
|
|
if ( err ) {
|
|
dev_err(&client->dev, "[elan] %s get two layer x/y resolution failed\n",__func__);
|
|
return err;
|
|
}
|
|
fw_info->finger_xres = (rbuf[2]+rbuf[6] - 1) * 64;
|
|
fw_info->finger_yres = (rbuf[3]+rbuf[7] - 1) * 64;
|
|
#endif
|
|
|
|
dev_info(&client->dev,
|
|
"[elan] %s fw version:0x%4.4x\n",
|
|
__func__,fw_info->fw_ver);
|
|
dev_info(&client->dev,
|
|
"[elan] %s fw id:0x%4.4x\n",
|
|
__func__,fw_info->fw_id);
|
|
dev_info(&client->dev,
|
|
"[elan] %s bootcode version:0x%4.4x\n",
|
|
__func__,fw_info->fw_bcv);
|
|
dev_info(&client->dev,
|
|
"[elan] %s finger x/y resolution:0x%4.4x/0x%4.4x\n",
|
|
__func__,fw_info->finger_xres,fw_info->finger_yres);
|
|
|
|
return err;
|
|
}
|
|
|
|
int elan__fw_packet_handler(struct i2c_client *client)
|
|
{
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
int ret = 0;
|
|
|
|
dev_err(&client->dev, "[elan] fw packet handler chip_type %d\n",ts->chip_type);
|
|
if (ts->chip_type == HID_TYPE_PROTOCOL) {
|
|
ret = hid_fw_packet_handler(client);
|
|
if ( ret ) {
|
|
dev_err(&client->dev,
|
|
"[elan] %s HID get fw msg failed\n",__func__);
|
|
}
|
|
} else if (ts->chip_type == NORMAL_TYPE_PROTOCOL) {
|
|
ret = normal_fw_packet_handler(client);
|
|
if ( ret ) {
|
|
dev_err(&client->dev,
|
|
"[elan] %s Normal get fw msg failed\n",__func__);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int elan_ts_recv_data(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
int rc = 0;
|
|
uint8_t rbuf[HID_RECV_LEN]={0};
|
|
struct elan_report_struct *report = &ts->report;
|
|
int i = 0;
|
|
|
|
rc = ts->ops->recv(rbuf, sizeof(rbuf));
|
|
if ( rc < 0) {
|
|
dev_err(&ts->client->dev, "[elan] recv report data error [%d] !!\n",rc);
|
|
return -1;
|
|
}
|
|
|
|
if (ts->chip_type == HID_TYPE_PROTOCOL) {
|
|
if (rbuf[2] == HID_FID) {
|
|
report->finger.fvalid_num = rbuf[62];
|
|
report->finger.fbutton_value = rbuf[63];
|
|
memcpy(buf,rbuf,67);
|
|
if (rbuf[62] > 5) {
|
|
rc = ts->ops->recv(rbuf, sizeof(rbuf));
|
|
if (rc != sizeof(rbuf)) {
|
|
dev_err(&ts->client->dev, "[elan] recv second report data error!!\n");
|
|
return -1;
|
|
}
|
|
report->finger.fbuf_valid_size = 67*2;
|
|
memcpy(buf+58,rbuf+3,67-3);
|
|
|
|
} else {
|
|
report->finger.fbuf_valid_size = 67;
|
|
}
|
|
report->finger.fid = HID_FID;
|
|
report->finger.fsupport_num = 10;
|
|
report->finger.freport_idx = 3;
|
|
report->finger.fshift_byte = 11;
|
|
report->tool_type = ELAN_FINGER;
|
|
} else if (rbuf[2] == HID_PID) {
|
|
report->stylus.pid = HID_PID;
|
|
report->stylus.pbuf_valid_size = rbuf[0];
|
|
report->stylus.pbutton_value = 0;//rbuf[13];
|
|
report->stylus.preport_idx = 3;
|
|
report->stylus.tip_status = (rbuf[3] & 0x33) >> 1;
|
|
report->stylus.inrange_status = rbuf[3] & 0x02;
|
|
report->stylus.key = rbuf[3] >> 1;
|
|
// report->stylus.eraser = rbuf[3] >> 1;
|
|
// report->stylus.inver = rbuf[3] >> 1;
|
|
// report->stylus.barrel = rbuf[3] >> 1;
|
|
report->stylus.barrel_tip = rbuf[3];
|
|
report->tool_type = ELAN_PEN;
|
|
memcpy(buf,rbuf,report->stylus.pbuf_valid_size);
|
|
}
|
|
|
|
} else if (ts->chip_type == NORMAL_TYPE_PROTOCOL) {
|
|
if (rbuf[0] == NOR2_FID) {
|
|
report->finger.fid = NOR2_FID;
|
|
report->finger.fbuf_valid_size = NOR2_SIZE;
|
|
report->finger.fsupport_num = 2;
|
|
report->finger.fvalid_num = rbuf[7] & 0x03;
|
|
report->finger.freport_idx = 1;
|
|
report->finger.fbits = rbuf[7] & 0x03;
|
|
} else if(rbuf[0] == NOR5_FID) {
|
|
report->finger.fid = NOR5_FID;
|
|
report->finger.fbuf_valid_size = NOR5_SIZE;
|
|
report->finger.fsupport_num = 5;
|
|
report->finger.fvalid_num = rbuf[1] & 0x07;
|
|
report->finger.freport_idx = 2;
|
|
report->finger.fbits = rbuf[1] >> 3;
|
|
} else if (rbuf[0] == NOR10_FID) {
|
|
report->finger.fid = NOR10_FID;
|
|
report->finger.fbuf_valid_size = NOR10_SIZE;
|
|
report->finger.fsupport_num = 10;
|
|
report->finger.fvalid_num = rbuf[2] & 0x0f;
|
|
report->finger.freport_idx = 3;
|
|
report->finger.fbits = ((rbuf[2] & 0x30)<<4) | (rbuf[1]);
|
|
}
|
|
report->finger.fbutton_value = rbuf[report->finger.fbuf_valid_size - 1];
|
|
report->finger.fshift_byte = 3;
|
|
report->tool_type = ELAN_FINGER;
|
|
memcpy(buf,rbuf, report->finger.fbuf_valid_size);
|
|
}
|
|
|
|
if ( report->tool_type == ELAN_PEN) {
|
|
for(i = 0; i < report->stylus.pbuf_valid_size/8 + 1; i++) {
|
|
print_log(ts->level,"%02x %02x %02x %02x %02x %02x %02x %02x\n",\
|
|
buf[i*8+0],buf[i*8+1],buf[i*8+2],buf[i*8+3],\
|
|
buf[i*8+4],buf[i*8+5],buf[i*8+6],buf[i*8+7]);
|
|
}
|
|
} else {
|
|
for(i = 0; i < report->finger.fbuf_valid_size/8 + 1; i++) {
|
|
print_log(ts->level,"%02x %02x %02x %02x %02x %02x %02x %02x\n",\
|
|
buf[i*8+0],buf[i*8+1],buf[i*8+2],buf[i*8+3],\
|
|
buf[i*8+4],buf[i*8+5],buf[i*8+6],buf[i*8+7]);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static inline int elan_ts_fparse_xy(uint8_t *data, uint16_t *x, uint16_t *y, const int type)
|
|
{
|
|
*x = *y = 0;
|
|
|
|
if (type == HID_FID) {
|
|
*x = (data[6]);
|
|
*x <<= 8;
|
|
*x |= data[5];
|
|
|
|
*y = (data[10]);
|
|
*y <<= 8;
|
|
*y |= data[9];
|
|
} else {
|
|
*x = (data[0] & 0xf0);
|
|
*x <<= 4;
|
|
*x |= data[1];
|
|
*y = (data[0] & 0x0f);
|
|
*y <<= 8;
|
|
*y |= data[2];
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static inline int elan_ts_pparse_xy(uint8_t *data, uint16_t *x, uint16_t *y, uint16_t *p)
|
|
{
|
|
*x = *y = *p = 0;
|
|
|
|
*x = data[5];
|
|
*x <<= 8;
|
|
*x |= data[4];
|
|
|
|
*y = data[7];
|
|
*y <<= 8;
|
|
*y |= data[6];
|
|
|
|
*p = data[9];
|
|
*p <<= 8;
|
|
*p |= data[8];
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void elants_a_report(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
struct elan_report_struct *report = &ts->report;
|
|
struct elan_finger_struct finger = report->finger;
|
|
struct elan_stylus_struct stylus = report->stylus;
|
|
int fbits = finger.fbits;
|
|
int reportid = 0;
|
|
int valid_num = finger.fvalid_num;
|
|
uint16_t x = 0, y = 0, p = 0;
|
|
int fbit = 0;
|
|
static int pkey = 0;
|
|
|
|
if (report->tool_type == ELAN_FINGER) {
|
|
for (reportid = 0; reportid < finger.fvalid_num; reportid++ ) {
|
|
if (finger.fid == HID_FID) { /*hid over i2c protocol*/
|
|
|
|
fbits = (buf[finger.freport_idx] & 0x03);
|
|
if (fbits) {
|
|
fbit = (((buf[finger.freport_idx] & 0xfc) >> 2) - 1);
|
|
elan_ts_fparse_xy(&buf[finger.freport_idx], &y, &x, finger.fid);
|
|
|
|
x = 800 * x / 2112;
|
|
y = 1200 * y / 3392;
|
|
|
|
input_report_key(ts->finger_idev, BTN_TOUCH, 1);
|
|
input_report_key(ts->finger_idev, BTN_TOOL_FINGER, true);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(ts->finger_idev, ABS_MT_TRACKING_ID, fbit);
|
|
input_mt_sync(ts->finger_idev);
|
|
} else
|
|
valid_num --;
|
|
finger.freport_idx += 11;
|
|
|
|
} else { /*normal i2c protocol*/
|
|
|
|
if (fbits & 0x01) {
|
|
|
|
elan_ts_fparse_xy(&buf[finger.freport_idx], &y, &x, finger.fid);
|
|
input_report_key(ts->finger_idev, BTN_TOUCH, 1);
|
|
input_report_key(ts->finger_idev, BTN_TOOL_FINGER, true);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(ts->finger_idev, ABS_MT_TRACKING_ID, reportid);
|
|
input_mt_sync(ts->finger_idev);
|
|
|
|
}else
|
|
valid_num--;
|
|
fbits = fbits >> 1;
|
|
finger.freport_idx += 3;
|
|
}
|
|
|
|
}
|
|
|
|
if (!valid_num) {
|
|
input_report_key(ts->finger_idev, BTN_TOUCH, 0);
|
|
input_report_key(ts->finger_idev, BTN_TOOL_FINGER, false);
|
|
input_mt_sync(ts->finger_idev);
|
|
}
|
|
|
|
input_sync(ts->finger_idev);
|
|
} else if (report->tool_type == ELAN_PEN) {
|
|
print_log(ts->level,"[elan] stylus.key %d, pkey %d\n",stylus.key,pkey);
|
|
if (stylus.key > 0 || !pkey) {
|
|
switch(stylus.key) {
|
|
case 2:
|
|
pkey = BTN_STYLUS;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 3:
|
|
pkey = BTN_STYLUS2;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 4:
|
|
pkey = BTN_STYLUS;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 8:
|
|
pkey = BTN_STYLUS2;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
default:
|
|
input_report_key(ts->pen_idev, pkey , 0);
|
|
pkey = 0;
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
print_log(ts->level, "[elan] stylus.tip_status %d, stylus.inrange_status %d\n",\
|
|
stylus.tip_status,stylus.inrange_status);
|
|
if (stylus.inrange_status) {
|
|
elan_ts_pparse_xy(&buf[0],&y,&x,&p);
|
|
|
|
x = 800 * x / 8580;
|
|
y = 1200 * y / 13780;
|
|
|
|
input_report_abs(ts->pen_idev, ABS_PRESSURE, p);
|
|
input_report_abs(ts->pen_idev, ABS_X, x);
|
|
input_report_abs(ts->pen_idev, ABS_Y, y);
|
|
dev_info(&ts->client->dev, "[elan] X:Y:P ====%d:%d:%d\n",x,y,p);
|
|
}
|
|
|
|
input_report_key(ts->pen_idev, BTN_TOUCH, stylus.tip_status);
|
|
input_report_key(ts->pen_idev, BTN_TOOL_PEN, stylus.tip_status);
|
|
input_sync(ts->pen_idev);
|
|
}
|
|
}
|
|
|
|
static void elants_slot_report(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
|
|
struct elan_report_struct *report = &ts->report;
|
|
struct elan_finger_struct finger = report->finger;
|
|
struct elan_stylus_struct stylus = report->stylus;
|
|
uint16_t x = 0, y = 0, p = 0;
|
|
int16_t x_tilt_raw = 0, y_tilt_raw = 0;
|
|
int8_t x_tilt = 0, y_tilt = 0;
|
|
int fbits = finger.fbits;
|
|
int fprebits = 0;
|
|
int fbits_tmp = 0;
|
|
int active = 0;
|
|
int id = 0, reportid = 0;
|
|
int num = finger.fvalid_num;
|
|
static int pkey = 0;
|
|
|
|
if (finger.fid > HID_PID)
|
|
fbits_tmp = fbits;
|
|
|
|
if (report->tool_type == ELAN_FINGER) {
|
|
|
|
|
|
if (finger.fid == HID_FID) { /*hid over i2c protocol*/
|
|
for (reportid = 0; reportid < finger.fvalid_num; reportid++ ) {
|
|
active = (buf[finger.freport_idx] & 0x03);
|
|
/*id = (((buf[finger.freport_idx] & 0xfc) >> 2) -1);*/
|
|
id = (((buf[finger.freport_idx] & 0xfc) >> 2));
|
|
elan_ts_fparse_xy(&buf[finger.freport_idx], &x, &y, finger.fid); //lcm x :y = 720 : 1280 tp x: y = 1296:720
|
|
x = ts->hw_info.screen_x * x / ts->fw_info.finger_xres;
|
|
y = ts->hw_info.screen_y * y / ts->fw_info.finger_yres;
|
|
//x = ts->hw_info.screen_x - x;
|
|
//y = ts->hw_info.screen_y - y;
|
|
|
|
if (active) { /*finger contact*/
|
|
input_mt_slot(ts->finger_idev, id);
|
|
//input_report_abs(ts->finger_idev, ABS_MT_PRESSURE, 100);
|
|
input_report_abs(ts->finger_idev, ABS_MT_TOUCH_MAJOR, 100);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_Y, y);
|
|
input_mt_report_slot_state(ts->finger_idev, MT_TOOL_FINGER, true);
|
|
input_report_key(ts->finger_idev, BTN_TOUCH, 1);
|
|
//dev_info(&ts->client->dev, "[elan] finger X:Y ====%d:%d\n",x,y);
|
|
} else { /*finger leave*/
|
|
input_mt_slot(ts->finger_idev, id);
|
|
input_mt_report_slot_state(ts->finger_idev, MT_TOOL_FINGER, false);
|
|
//input_report_key(ts->finger_idev, BTN_TOUCH, 0);
|
|
num--;
|
|
}
|
|
|
|
finger.freport_idx += 11;
|
|
}
|
|
//input_mt_sync(ts->finger_idev);
|
|
if(num == 0)
|
|
input_report_key(ts->finger_idev, BTN_TOUCH, 0);
|
|
|
|
input_sync(ts->finger_idev);
|
|
|
|
} else { /*notmal i2c protocol*/
|
|
|
|
|
|
if (fbits || fprebits) {
|
|
for (reportid = 0; reportid < finger.fvalid_num; reportid++ ) {
|
|
if(fbits&0x01){
|
|
elan_ts_fparse_xy(&buf[finger.freport_idx], &y, &x, finger.fid);
|
|
input_mt_slot(ts->finger_idev, reportid);
|
|
input_mt_report_slot_state(ts->finger_idev, MT_TOOL_FINGER, true);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->finger_idev, ABS_MT_POSITION_Y, y);
|
|
} else if(fprebits&0x01){
|
|
input_mt_slot(ts->finger_idev, id);
|
|
input_mt_report_slot_state(ts->finger_idev, MT_TOOL_FINGER, false);
|
|
}
|
|
finger.freport_idx += 3;
|
|
}
|
|
}
|
|
fprebits = fbits_tmp;
|
|
input_sync(ts->finger_idev);
|
|
}
|
|
} else if (report->tool_type == ELAN_PEN) {
|
|
if (stylus.key > 0 || !pkey) {
|
|
switch(stylus.key) {
|
|
case 2:
|
|
pkey = BTN_TOOL_RUBBER;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 3:
|
|
pkey = BTN_TOOL_RUBBER;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 4:
|
|
pkey = BTN_STYLUS;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
case 8:
|
|
pkey = BTN_STYLUS2;
|
|
input_report_key(ts->pen_idev, pkey , 1);
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
default:
|
|
input_report_key(ts->pen_idev, pkey , 0);
|
|
pkey = 0;
|
|
input_sync(ts->pen_idev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
print_log(ts->level, "[elan] stylus.inrange_status = %d, stylus.barrel_tip = %d", \
|
|
stylus.inrange_status,stylus.barrel_tip);
|
|
if(stylus.inrange_status) {
|
|
elan_ts_pparse_xy(&buf[0],&x,&y,&p);
|
|
x = ts->hw_info.screen_x * x / ts->fw_info.pen_xres;
|
|
y = ts->hw_info.screen_y * y / ts->fw_info.pen_yres;
|
|
|
|
//x = ts->hw_info.screen_x - x;
|
|
//y = ts->hw_info.screen_y - y;
|
|
|
|
x_tilt_raw = (int16_t)((buf[12] << 8) | buf[11]);
|
|
y_tilt_raw = (int16_t)((buf[14] << 8) | buf[13]);
|
|
x_tilt = (int8_t)(x_tilt_raw / 100);
|
|
y_tilt = (int8_t)(y_tilt_raw / 100);
|
|
|
|
|
|
input_mt_slot(ts->pen_idev, 0);
|
|
input_mt_report_slot_state(ts->pen_idev, MT_TOOL_PEN, true);
|
|
input_report_key(ts->pen_idev, BTN_TOUCH, 1);
|
|
input_report_abs(ts->pen_idev, ABS_MT_TOOL_TYPE, MT_TOOL_PEN);
|
|
input_report_abs(ts->pen_idev, ABS_MT_PRESSURE, p);
|
|
input_report_abs(ts->pen_idev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->pen_idev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(ts->pen_idev, ABS_TILT_X, x_tilt);
|
|
input_report_abs(ts->pen_idev, ABS_TILT_Y, y_tilt);
|
|
print_log(ts->level, "[elan] pen X:Y:P:TX:TY ====%d:%d:%d:%d:%d\n",
|
|
x, y, p, x_tilt, y_tilt);
|
|
} else {
|
|
input_mt_slot(ts->pen_idev, 0);
|
|
input_mt_report_slot_state(ts->pen_idev, MT_TOOL_PEN, false);
|
|
input_report_key(ts->pen_idev, BTN_TOUCH, 0);
|
|
dev_info(&ts->client->dev, "[elan] pen relese!!!!");
|
|
}
|
|
|
|
input_sync(ts->pen_idev);
|
|
}
|
|
|
|
}
|
|
|
|
static int report_mbutton(struct input_dev *idev, int button_value)
|
|
{
|
|
static int key;
|
|
|
|
switch(button_value) {
|
|
case 0x01:
|
|
key = KEY_BACK;
|
|
input_report_key(idev, key, 1);
|
|
break;
|
|
|
|
case 0x02:
|
|
key = KEY_HOMEPAGE;
|
|
input_report_key(idev, key, 1);
|
|
|
|
break;
|
|
case 0x03:
|
|
case 0x04:
|
|
key = KEY_BACK;
|
|
input_report_key(idev, key, 1);
|
|
break;
|
|
default:
|
|
if (key != 0) {
|
|
input_report_key(idev, key, 0);
|
|
key = 0;
|
|
}
|
|
break;
|
|
}
|
|
input_sync(idev);
|
|
return key;
|
|
}
|
|
|
|
|
|
|
|
static void elan_ts_hid_report(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
struct elan_report_struct *report = &ts->report;
|
|
int button = report->finger.fbutton_value;
|
|
int pbutton = report->stylus.pbutton_value;
|
|
static int prekey = 0;
|
|
|
|
/*for hid protocol finger contat mutual button or pen contact mutual button*/
|
|
/*button priority is higher than other*/
|
|
if ((button != 0 && button != 0xFF) || (pbutton != 0 && pbutton != 0xFF)|| (prekey != 0)) {
|
|
if (ts->report.tool_type == ELAN_FINGER) {
|
|
prekey = report_mbutton(ts->finger_idev,button);
|
|
return;
|
|
} else {
|
|
prekey = report_mbutton(ts->pen_idev,pbutton);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ts->report_type == PROTOCOL_TYPE_B) {
|
|
elants_slot_report(ts,buf);
|
|
} else {
|
|
elants_a_report(ts,buf);
|
|
}
|
|
}
|
|
|
|
static void elan_ts_normal_report(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
struct elan_report_struct *report = &ts->report;
|
|
int button = report->finger.fbutton_value;
|
|
static int prekey = 0;
|
|
|
|
if ((button != 0 && button != 0xFF) || (prekey != 0)) {
|
|
prekey = report_mbutton(ts->finger_idev,button);
|
|
return;
|
|
}
|
|
|
|
if (ts->report_type == PROTOCOL_TYPE_B) {
|
|
elants_slot_report(ts,buf);
|
|
return;
|
|
} else {
|
|
elants_a_report(ts,buf);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static void elan_ts_report_data(struct elan_ts_data *ts, uint8_t *buf)
|
|
{
|
|
switch (ts->chip_type) {
|
|
case HID_TYPE_PROTOCOL:
|
|
|
|
elan_ts_hid_report(ts, buf);
|
|
break;
|
|
case NORMAL_TYPE_PROTOCOL:
|
|
|
|
elan_ts_normal_report(ts,buf);
|
|
break;
|
|
default:
|
|
dev_err(&ts->client->dev,
|
|
"[elan] unknow type 0x%2x:0x%2x:0x%2x:0x%2x",\
|
|
buf[0],buf[1],buf[2],buf[3]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void elan_ts_work_func(struct work_struct *work)
|
|
{
|
|
struct elan_ts_data *ts =
|
|
container_of(work, struct elan_ts_data, ts_work);
|
|
uint8_t buf[HID_REPORT_MAX_LEN] = {0x00};
|
|
int rc = 0;
|
|
|
|
if(gpio_get_value(ts->hw_info.intr_gpio)) {
|
|
dev_err(&ts->client->dev,"[elan]interrupt jitter\n.");
|
|
return;
|
|
}
|
|
|
|
memset(&ts->report, 0, sizeof(ts->report));
|
|
|
|
rc = elan_ts_recv_data(ts,buf);
|
|
if (rc < 0) {
|
|
dev_err(&ts->client->dev,"[elan]recv data error\n.");
|
|
return;
|
|
}
|
|
|
|
elan_ts_report_data(ts,buf);
|
|
return;
|
|
}
|
|
|
|
|
|
static irqreturn_t elan_ts_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct elan_ts_data *ts = (struct elan_ts_data*)dev_id;
|
|
|
|
if (ts->user_handle_irq) {
|
|
wake_up_interruptible(&ts->elan_userqueue);
|
|
ts->int_val = 0;
|
|
return IRQ_HANDLED;
|
|
} else {
|
|
queue_work(ts->elan_wq,&ts->ts_work);
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
|
|
static int elan_request_pen_input_dev(struct elan_ts_data *ts)
|
|
{
|
|
int err = 0;
|
|
|
|
ts->pen_idev = input_allocate_device();
|
|
if (ts->pen_idev == NULL) {
|
|
err = -ENOMEM;
|
|
dev_err(&ts->client->dev,
|
|
"[elan error] Failed to allocate pen device\n");
|
|
return err;
|
|
}
|
|
|
|
if (ts->report_type == PROTOCOL_TYPE_B) {
|
|
//input_mt_init_slots(ts->pen_idev, 10);
|
|
input_mt_init_slots(ts->pen_idev, FINGERS_NUM,INPUT_MT_DIRECT);
|
|
input_set_abs_params(ts->pen_idev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_MT_POSITION_X, 0, ts->hw_info.screen_x, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_MT_POSITION_Y, 0, ts->hw_info.screen_y, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_MT_PRESSURE, 0, 4096, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_TILT_X, -90, 90, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_TILT_Y, -90, 90, 0, 0);
|
|
} else {
|
|
__set_bit(BTN_TOOL_PEN, ts->pen_idev->keybit);
|
|
__set_bit(BTN_TOUCH, ts->pen_idev->keybit);
|
|
ts->pen_idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
|
|
input_set_abs_params(ts->pen_idev, ABS_X, 0, ts->hw_info.screen_x, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_Y, 0, ts->hw_info.screen_y, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_PRESSURE, 0, 4096, 0, 0);
|
|
|
|
input_set_abs_params(ts->pen_idev, ABS_TILT_X, -90, 90, 0, 0);
|
|
input_set_abs_params(ts->pen_idev, ABS_TILT_Y, -90, 90, 0, 0);
|
|
}
|
|
|
|
ts->pen_idev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_SYN);
|
|
|
|
__set_bit(KEY_BACK, ts->pen_idev->keybit);
|
|
__set_bit(BTN_TOOL_RUBBER, ts->pen_idev->keybit);
|
|
__set_bit(BTN_STYLUS, ts->pen_idev->keybit);
|
|
__set_bit(BTN_STYLUS2, ts->pen_idev->keybit);
|
|
__set_bit(INPUT_PROP_DIRECT, ts->pen_idev->propbit);
|
|
__set_bit(BTN_TOUCH, ts->pen_idev->keybit);
|
|
input_set_abs_params(ts->pen_idev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
|
|
|
|
ts->pen_idev->name = "elan_pen";
|
|
ts->pen_idev->phys = "input/ts";
|
|
ts->pen_idev->id.bustype = BUS_I2C;
|
|
|
|
err = input_register_device(ts->pen_idev);
|
|
if (err) {
|
|
input_free_device(ts->pen_idev);
|
|
dev_err(&ts->client->dev,
|
|
"unable to register pen input device: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int elan_request_finger_input_dev(struct elan_ts_data *ts)
|
|
{
|
|
int err = 0;
|
|
int i = 0;
|
|
|
|
ts->finger_idev = input_allocate_device();
|
|
if (ts->finger_idev == NULL) {
|
|
err = -ENOMEM;
|
|
dev_err(&ts->client->dev,
|
|
"[elan] Failed to allocate input device\n");
|
|
return err;
|
|
}
|
|
|
|
ts->finger_idev->evbit[0] = BIT(EV_KEY)|BIT_MASK(EV_REP);
|
|
|
|
/*key setting*/
|
|
for (i = 0; i < ARRAY_SIZE(key_value); i++)
|
|
__set_bit(key_value[i], ts->finger_idev->keybit);
|
|
|
|
|
|
ts->finger_idev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
|
|
|
__set_bit(INPUT_PROP_DIRECT, ts->finger_idev->propbit);
|
|
|
|
if (ts->report_type == PROTOCOL_TYPE_B) {
|
|
//input_mt_init_slots(ts->finger_idev, 10);
|
|
input_mt_init_slots(ts->finger_idev, FINGERS_NUM,INPUT_MT_DIRECT);
|
|
input_set_abs_params(ts->finger_idev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
|
|
} else {
|
|
__set_bit(BTN_TOOL_FINGER, ts->finger_idev->keybit);
|
|
}
|
|
|
|
dev_info(&ts->client->dev,
|
|
"[elan] %s: x resolution: %d, y resolution: %d\n",
|
|
__func__, ts->fw_info.finger_xres, ts->fw_info.finger_yres);
|
|
|
|
ts->finger_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
|
input_set_abs_params(ts->finger_idev, ABS_MT_POSITION_X, 0, ts->hw_info.screen_x, 0, 0);
|
|
input_set_abs_params(ts->finger_idev, ABS_MT_POSITION_Y, 0, ts->hw_info.screen_y, 0, 0);
|
|
// input_set_abs_params(ts->finger_idev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
|
// input_set_abs_params(ts->finger_idev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
|
input_set_abs_params(ts->finger_idev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
|
|
|
|
ts->finger_idev->name = ELAN_TS_NAME;
|
|
ts->finger_idev->phys = "input/ts";
|
|
ts->finger_idev->id.bustype = BUS_I2C;
|
|
ts->finger_idev->id.vendor = 0x0001;
|
|
ts->finger_idev->id.product = 0x0002;
|
|
ts->finger_idev->id.version = 0x0003;
|
|
|
|
err = input_register_device(ts->finger_idev);
|
|
if (err) {
|
|
input_free_device(ts->finger_idev);
|
|
dev_err(&ts->client->dev,
|
|
"[elan]%s: unable to register %s input device\n",
|
|
__func__, ts->finger_idev->name);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int elan_ts_register_interrupt(struct elan_ts_data *ts)
|
|
{
|
|
int err = 0;
|
|
|
|
err = request_threaded_irq(ts->hw_info.irq_num,
|
|
NULL, elan_ts_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
ELAN_TS_NAME, ts);
|
|
|
|
if (err < 0)
|
|
dev_err(&ts->client->dev,
|
|
"[elan] %s: request_irq %d failed,err = %d\n",
|
|
__func__, ts->client->irq, err);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static void elan_ic_init_work(struct work_struct *work)
|
|
{
|
|
int rc = 0;
|
|
int retry_cnt = 0;
|
|
struct elan_ts_data *ts = private_ts;
|
|
struct i2c_client *client = ts->client;
|
|
|
|
/*Get FW MSG: ID,VERSION,X_RES,Y_RES,etc*/
|
|
if (ts->recover == COMPARE_UPGRADE) {
|
|
for (retry_cnt = 0; retry_cnt < 3; retry_cnt++) {
|
|
rc = elan__fw_packet_handler(client);
|
|
if (rc < 0)
|
|
dev_err(&client->dev,
|
|
"[elan]%s, fw_packet_handler fail, rc = %d\n",
|
|
__func__, rc);
|
|
else
|
|
break;
|
|
}
|
|
if (retry_cnt >= 3) {
|
|
dev_err(&client->dev,
|
|
"[elan]%s, fw_packet_handler failed,retry out, rc = %d\n",
|
|
__func__,rc);
|
|
return;
|
|
}
|
|
} else {
|
|
dev_err(&client->dev,
|
|
"[elan]%s, fw into recovery mode force update, rc = %d\n", __func__,rc);
|
|
}
|
|
|
|
#ifdef IAP_PORTION
|
|
|
|
dev_err(&ts->client->dev, "[elan]Start IAP Flow!!!\n");
|
|
ts->power_lock = 1; //skip resume / suspend flow
|
|
elan_check_update_flage(ts);
|
|
|
|
#endif
|
|
|
|
/*finget and pen input event register*/
|
|
rc = elan_request_pen_input_dev(ts);
|
|
if ( rc ) {
|
|
dev_err(&ts->client->dev,
|
|
"[elan]: %s pen input event request failed.\n",
|
|
__func__);
|
|
goto exit_pen_input_dev_failed;
|
|
|
|
}
|
|
|
|
rc = elan_request_finger_input_dev(ts);
|
|
if ( rc ) {
|
|
dev_err(&ts->client->dev,
|
|
"[elan]: %s finger input event request failed %d.\n",
|
|
__func__, rc);
|
|
goto exit_finger_input_dev_failed;
|
|
}
|
|
|
|
mutex_lock(&ts->irq_mutex);
|
|
ts->irq_lock_flag = 0;
|
|
mutex_unlock(&ts->irq_mutex);
|
|
|
|
/*elan irq resgister*/
|
|
rc = elan_ts_register_interrupt(private_ts);
|
|
if ( rc ) {
|
|
dev_err(&private_ts->client->dev,
|
|
"[elan]: %s elan_ts_register_interrupt failed %d\n",
|
|
__func__, rc);
|
|
goto exit_register_interrupt_failed;
|
|
}
|
|
|
|
#ifdef IAP_PORTION
|
|
ts->power_lock = 0;
|
|
#endif
|
|
|
|
return;
|
|
|
|
exit_register_interrupt_failed:
|
|
input_unregister_device(ts->finger_idev);
|
|
exit_finger_input_dev_failed:
|
|
input_unregister_device(ts->pen_idev);
|
|
exit_pen_input_dev_failed:
|
|
return;
|
|
}
|
|
|
|
|
|
static int elan_ts_setup(struct elan_ts_data *ts)
|
|
{
|
|
int err = 0;
|
|
|
|
dev_err(&ts->client->dev, "[elan] setup hw reset\n");
|
|
/*HW RESET TP and delay 200ms*/
|
|
elan_ts_hw_reset(&ts->hw_info);
|
|
msleep(500);
|
|
|
|
err = elan__hello_packet_handler(ts->client, ts->chip_type);
|
|
if ( err < 0 ) {
|
|
dev_err(&ts->client->dev,
|
|
"[elan error] %s, hello_packet_handler fail,err= %d\n",
|
|
__func__,err);
|
|
return err;
|
|
} else {
|
|
dev_err(&ts->client->dev,
|
|
"[elan] %s,ic status = %s",
|
|
__func__, err == FORCED_UPGRADE ? "recovery":"normal");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int elan_iap_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct elan_ts_data *ts = container_of(((struct miscdevice*)filp->private_data), struct elan_ts_data, firmware);
|
|
|
|
dev_dbg(&ts->client->dev,"%s enter\n", __func__);
|
|
|
|
filp->private_data = ts;
|
|
// ts->int_val = 1;
|
|
// ts->user_handle_irq = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int elan_iap_release(struct inode *inode, struct file *filp)
|
|
{
|
|
dev_info(&private_ts->client->dev,"%s enter", __func__);
|
|
|
|
filp->private_data = NULL;
|
|
//private_ts->user_handle_irq = 0;
|
|
//private_ts->int_val = 0;
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t elan_iap_write(struct file *filp, const char *buff, size_t count, loff_t *offp)
|
|
{
|
|
int ret;
|
|
char *tmp;
|
|
struct elan_ts_data *ts = (struct elan_ts_data *)filp->private_data;
|
|
struct i2c_client *client= ts->client;
|
|
|
|
dev_info(&client->dev,"%s enter", __func__);
|
|
if (count > 8192){
|
|
count = 8192;
|
|
}
|
|
|
|
tmp = kmalloc(count, GFP_KERNEL);
|
|
if (tmp == NULL){
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (copy_from_user(tmp, buff, count)) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = elan_i2c_send(tmp, count);
|
|
if (ret != count){
|
|
dev_err(&client->dev, "[elan]elan elan_i2c_send fail, ret=%d \n", ret);
|
|
}
|
|
|
|
kfree(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t elan_iap_read(struct file *filp, char *buff, size_t count, loff_t *offp)
|
|
{
|
|
char *tmp;
|
|
int ret;
|
|
long rc;
|
|
struct elan_ts_data *ts = (struct elan_ts_data *)filp->private_data;
|
|
struct i2c_client *client = ts->client;
|
|
|
|
dev_info(&client->dev, "%s enter", __func__);
|
|
|
|
if (count > 8192){
|
|
count = 8192;
|
|
}
|
|
|
|
tmp = kmalloc(count, GFP_KERNEL);
|
|
if (tmp == NULL){
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (ts->user_handle_irq == 1) {
|
|
wait_event_interruptible(ts->elan_userqueue, ts->int_val == 0);
|
|
}
|
|
|
|
ret = elan_i2c_recv(tmp, count);
|
|
if (ret != count){
|
|
dev_err(&client->dev, "[elan error]elan elan_i2c_recv fail, ret=%d \n", ret);
|
|
}
|
|
|
|
if (ret == count){
|
|
rc = copy_to_user(buff, tmp, count);
|
|
}
|
|
|
|
if (ts->user_handle_irq == 1) {
|
|
ts->int_val = 1;
|
|
}
|
|
|
|
kfree(tmp);
|
|
return ret;
|
|
}
|
|
|
|
static long elan_iap_ioctl( struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int __user *ip = (int __user *)arg;
|
|
struct elan_ts_data *ts = (struct elan_ts_data *)filp->private_data;
|
|
struct i2c_client *client = ts->client;
|
|
|
|
dev_info(&client->dev, "%s enter cmd value %x\n", __func__,cmd);
|
|
|
|
switch (cmd) {
|
|
case IOCTL_I2C_SLAVE:
|
|
dev_info(&client->dev, "pre addr is %X\n", client->addr);
|
|
client->addr = (int __user)arg;
|
|
dev_info(&client->dev, "new addr is %X\n", client->addr);
|
|
break;
|
|
case IOCTL_RESET:
|
|
elan_ts_hw_reset(&ts->hw_info);
|
|
break;
|
|
case IOCTL_IAP_MODE_LOCK:
|
|
if(private_ts->power_lock == 0){
|
|
private_ts->power_lock = 1;
|
|
elan_switch_irq(ts,0);
|
|
}
|
|
break;
|
|
case IOCTL_IAP_MODE_UNLOCK:
|
|
if(private_ts->power_lock == 1){
|
|
private_ts->power_lock = 0;
|
|
elan_switch_irq(ts,1);
|
|
}
|
|
break;
|
|
case IOCTL_CHECK_RECOVERY_MODE:
|
|
return private_ts->recover;
|
|
break;
|
|
case IOCTL_ROUGH_CALIBRATE:
|
|
return elan_ts_calibrate(ts->client);
|
|
case IOCTL_I2C_INT:
|
|
put_user(gpio_get_value(ts->hw_info.intr_gpio), ip);
|
|
break;
|
|
case IOCTL_USER_HANDLE_IRQ:
|
|
ts->user_handle_irq = 1;
|
|
break;
|
|
case IOCTL_KERN_HANDLE_IRQ:
|
|
ts->user_handle_irq = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int elan_iap_poll(struct file *filp, struct poll_table_struct *wait)
|
|
{
|
|
int mask = 0;
|
|
struct elan_ts_data *ts = (struct elan_ts_data *)filp->private_data;
|
|
dev_info(&ts->client->dev, "[elan] polling int_val = %d\n", ts->int_val);
|
|
|
|
poll_wait(filp,&ts->elan_userqueue, wait);
|
|
if (ts->int_val == 0)
|
|
mask |= POLLIN|POLLRDNORM;
|
|
else if(ts->int_val == 1)
|
|
mask |= POLLOUT|POLLWRNORM;
|
|
|
|
return mask;
|
|
}
|
|
|
|
struct file_operations elan_touch_fops = {
|
|
.open = elan_iap_open,
|
|
.write = elan_iap_write,
|
|
.read = elan_iap_read,
|
|
.release = elan_iap_release,
|
|
.unlocked_ioctl = elan_iap_ioctl,
|
|
.compat_ioctl = elan_iap_ioctl,
|
|
.poll = elan_iap_poll,
|
|
};
|
|
|
|
|
|
static void elan_touch_node_init(struct elan_ts_data *ts)
|
|
{
|
|
|
|
elan_sysfs_attri_file(ts);
|
|
|
|
/*creat dev/elan-iap node for fw operation*/
|
|
ts->firmware.minor = MISC_DYNAMIC_MINOR;
|
|
ts->firmware.name = "elan-iap";
|
|
ts->firmware.fops = &elan_touch_fops;
|
|
ts->firmware.mode = S_IFREG|S_IRWXUGO;
|
|
|
|
if (misc_register(&ts->firmware) < 0)
|
|
dev_err(&ts->client->dev, "misc_register failed!!\n");
|
|
|
|
ts->p = proc_create("elan-iap", 0664, NULL, (const struct proc_ops *)&elan_touch_fops);
|
|
if (ts->p == NULL)
|
|
dev_err(&ts->client->dev, "[elan error] proc_create failed!!\n");
|
|
else
|
|
dev_info(&ts->client->dev, "proc_create ok!!\n");
|
|
|
|
return;
|
|
}
|
|
|
|
static void elan_touch_node_deinit(struct elan_ts_data *ts)
|
|
{
|
|
elan_sysfs_attri_file_remove(ts);
|
|
misc_deregister(&ts->firmware);
|
|
remove_proc_entry("elan-iap", NULL);
|
|
}
|
|
/*******************************************************
|
|
Function:
|
|
Power on Funtion.
|
|
Input:
|
|
ts: elan_ts_data struct.
|
|
on: bool, true:on, flase:off
|
|
Output:
|
|
Executive outcomes.
|
|
0: succeed. otherwise: failed
|
|
*******************************************************/
|
|
#if 1
|
|
static int elan_ts_power_on(struct elan_ts_data *ts, bool on)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!on)
|
|
goto power_off;
|
|
|
|
ret = regulator_enable(ts->vdd);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator vdd enable failed ret = %d\n",ret);
|
|
return ret;
|
|
}
|
|
#if 0
|
|
ret = regulator_enable(ts->vcc_i2c);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator vcc_i2c enable failed ret = %d\n",ret);
|
|
regulator_disable(ts->vdd);
|
|
}
|
|
#endif
|
|
return ret;
|
|
|
|
power_off:
|
|
ret = regulator_disable(ts->vdd);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator vdd disable failed ret = %d\n",ret);
|
|
return ret;
|
|
}
|
|
#if 0
|
|
ret = regulator_disable(ts->vcc_i2c);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator vcc_i2c disable failed ret = %d\n", ret);
|
|
ret = regulator_enable(ts->vdd);
|
|
if (ret)
|
|
dev_err(&ts->client->dev,
|
|
"Regulator vdd enable failed ret = %d\n", ret);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int elan_power_initial(struct elan_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
|
|
ts->vdd = regulator_get(&ts->client->dev, "vdd");
|
|
if (IS_ERR(ts->vdd)) {
|
|
ret = PTR_ERR(ts->vdd);
|
|
dev_err(&ts->client->dev,
|
|
"Regulator get failed vdd rc=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
if (regulator_count_voltages(ts->vdd) > 0) {
|
|
ret = regulator_set_voltage(ts->vdd,ELAN_VTG_MIN_UV,
|
|
ELAN_VTG_MAX_UV);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator set_vtg failed vdd rc=%d\n", ret);
|
|
goto reg_vdd_put;
|
|
}
|
|
}
|
|
|
|
ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc_i2c");
|
|
if (IS_ERR(ts->vcc_i2c)) {
|
|
ret = PTR_ERR(ts->vcc_i2c);
|
|
dev_err(&ts->client->dev,
|
|
"Regulator get failed vcc_i2c rc=%d\n", ret);
|
|
goto reg_vdd_set_vtg;
|
|
}
|
|
|
|
if (regulator_count_voltages(ts->vcc_i2c) > 0) {
|
|
ret = regulator_set_voltage(ts->vcc_i2c, ELAN_I2C_VTG_MIN_UV,
|
|
ELAN_I2C_VTG_MAX_UV);
|
|
if (ret) {
|
|
dev_err(&ts->client->dev,
|
|
"Regulator set_vtg failed vcc_i2c rc=%d\n", ret);
|
|
goto reg_vcc_i2c_put;
|
|
}
|
|
}
|
|
#endif
|
|
return ret;
|
|
|
|
#if 0
|
|
reg_vcc_i2c_put:
|
|
regulator_put(ts->vcc_i2c);
|
|
reg_vdd_set_vtg:
|
|
if (regulator_count_voltages(ts->vdd) > 0)
|
|
regulator_set_voltage(ts->vdd, 0, ELAN_VTG_MAX_UV);
|
|
|
|
|
|
reg_vdd_put:
|
|
regulator_put(ts->vdd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int elan_ts_set_power(struct elan_ts_data *ts, bool on)
|
|
{
|
|
int ret = 0;
|
|
|
|
if(!on) {
|
|
ret = on;
|
|
goto pwr_deinit;
|
|
}
|
|
|
|
/*initial power*/
|
|
ret = elan_power_initial(ts);
|
|
if(ret)
|
|
goto elan_power_init_failed;
|
|
|
|
/*power on*/
|
|
ret = elan_ts_power_on(ts,on);
|
|
if(ret)
|
|
goto elan_power_on_failed;
|
|
|
|
return ret;
|
|
|
|
elan_power_on_failed:
|
|
regulator_put(ts->vdd);
|
|
regulator_put(ts->vcc_i2c);
|
|
elan_power_init_failed:
|
|
pwr_deinit:
|
|
return ret;
|
|
}
|
|
#endif
|
|
/*******************************************************
|
|
Function:
|
|
Initial gpio Funtion
|
|
Input:
|
|
hw_info: ts_chip_hw_info struct
|
|
Output:
|
|
Executive outcomes.
|
|
0: succeed. otherwise: failed
|
|
*******************************************************/
|
|
|
|
static int elan_ts_gpio_initial(struct ts_chip_hw_info *hw_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
printk("[elan] request reset gpio\n");
|
|
ret = gpio_request(hw_info->rst_gpio, "tp_reset");
|
|
if (ret < 0) {
|
|
pr_err("%s: request rst_gpio pin failed\n", __func__);
|
|
goto free_rst_gpio;
|
|
}
|
|
|
|
gpio_direction_output(hw_info->rst_gpio, 1);
|
|
|
|
printk("[elan] request interrupt gpio\n");
|
|
/*set int pin input*/
|
|
ret = gpio_request(hw_info->intr_gpio, "tp_irq");
|
|
if (ret < 0) {
|
|
pr_err("%s: request intr_gpio pin failed\n", __func__);
|
|
goto free_irq_gpio;
|
|
}
|
|
gpio_direction_input(hw_info->intr_gpio);
|
|
|
|
hw_info->irq_num = gpio_to_irq(hw_info->intr_gpio);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
free_irq_gpio:
|
|
if (gpio_is_valid(hw_info->intr_gpio))
|
|
gpio_free(hw_info->intr_gpio);
|
|
free_rst_gpio:
|
|
if (gpio_is_valid(hw_info->rst_gpio))
|
|
gpio_free(hw_info->rst_gpio);
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Function:
|
|
Get dts gpio num
|
|
Input:
|
|
dev: device struct.
|
|
hw_info: ts_chip_hw_info struct
|
|
Output:
|
|
Executive outcomes.
|
|
0: succeed. otherwise: failed
|
|
*******************************************************/
|
|
#ifdef CONFIG_OF
|
|
static int elan_parse_dt(struct device *dev,
|
|
struct ts_chip_hw_info *chip_hw_info)
|
|
{
|
|
int ret = 0;
|
|
u32 data = 0;
|
|
//struct device_node *node = NULL;
|
|
struct elan_ts_data *ts =
|
|
container_of(chip_hw_info, struct elan_ts_data, hw_info);
|
|
//u32 lcm_coordinate[2] = {0};
|
|
struct device_node *np = dev->of_node;
|
|
/*
|
|
node = of_find_compatible_node(NULL, NULL, "elan,ektf");
|
|
if(node){
|
|
dev_err(&ts->client->dev,"[elan]of_find_compatible_node of : %s\n", "elan,ektf");
|
|
return -ENODEV;
|
|
}
|
|
*/
|
|
/*get irq gpio from dts*/
|
|
chip_hw_info->intr_gpio = of_get_named_gpio_flags(np,
|
|
"elan,irq-gpio", 0, NULL);
|
|
if (!gpio_is_valid(chip_hw_info->intr_gpio)) {
|
|
dev_err(&ts->client->dev, "[elan] hw_info->intr_gpio invalid\n");
|
|
ret = -EINVAL;
|
|
goto request_intr_gpio_failed;
|
|
}
|
|
|
|
/*get reset gpio from dts*/
|
|
chip_hw_info->rst_gpio = of_get_named_gpio_flags(np,
|
|
"elan,rst-gpio", 0, NULL);
|
|
if (!gpio_is_valid(chip_hw_info->rst_gpio)) {
|
|
dev_err(&ts->client->dev, "[elan] hw_info->rst_gpio invalid\n");
|
|
ret = -EINVAL;
|
|
goto request_rst_gpio_failed;
|
|
}
|
|
|
|
/*get ic communicate protocol*/
|
|
ret = of_property_read_u32(np, "chip_type", &data);
|
|
if (ret == 0) {
|
|
ts->chip_type = data;
|
|
dev_info(&ts->client->dev,"[elan]:chip protocol_type=%s", ts->chip_type == 1 ? "HID IIC":"NORMAL IIC" );
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto read_chip_type_failed;
|
|
}
|
|
|
|
/*get report protocol */
|
|
ret = of_property_read_u32(np, "report_type", &data);
|
|
if (ret == 0) {
|
|
ts->report_type = data;
|
|
dev_info(&ts->client->dev,"[elan]:report protocol_type=%s", ts->report_type == 1?"B protocol":"A protocol");
|
|
} else {
|
|
ret = -EINVAL;//hw_info->rst_gpio;
|
|
goto read_report_type_failed;
|
|
}
|
|
ts->hw_info.screen_x = 2160; //1728; //2160;
|
|
ts->hw_info.screen_y = 1440; //2368; //1440
|
|
#if 0
|
|
/*get lcm coordinate*/
|
|
ret = of_property_read_u32_array(np, "lcm_resolution", lcm_coordinate,sizeof(lcm_coordinate));
|
|
if (ret == 0) {
|
|
ts->hw_info.screen_x = lcm_coordinate[0];
|
|
ts->hw_info.screen_y = lcm_coordinate[1];
|
|
dev_info(&ts->client->dev,"[elan]:LCM RESOLUTION X:Y=%d:%d,", ts->hw_info.screen_x, ts->hw_info.screen_y);
|
|
} else {
|
|
ret = -EINVAL;//hw_info->rst_gpio;
|
|
goto read_lcm_res_failed;
|
|
}
|
|
#endif
|
|
return ret;
|
|
|
|
//read_lcm_res_failed:
|
|
read_report_type_failed:
|
|
read_chip_type_failed:
|
|
if (gpio_is_valid(chip_hw_info->rst_gpio))
|
|
gpio_free(chip_hw_info->rst_gpio);
|
|
request_rst_gpio_failed:
|
|
if (gpio_is_valid(chip_hw_info->intr_gpio))
|
|
gpio_free(chip_hw_info->intr_gpio);
|
|
request_intr_gpio_failed:
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*******************************************************
|
|
Function:
|
|
Get platform data Funtion
|
|
Input:
|
|
ts: elan_ts_data struct.
|
|
Output:
|
|
Executive outcomes.
|
|
0: succeed. otherwise: failed
|
|
*******************************************************/
|
|
|
|
static int elan_ts_hw_initial(struct elan_ts_data *ts)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_client *client = ts->client;
|
|
struct ts_chip_hw_info *hw_info;
|
|
|
|
hw_info = &(ts->hw_info);
|
|
#if 0
|
|
hw_info = devm_kzalloc(&client->dev,sizeof(struct ts_chip_hw_info), GFP_KERNEL);
|
|
if (!hw_info) {
|
|
dev_err(&client->dev,
|
|
"ETP Failed to allocate memory for hw_info\n");
|
|
return -ENOMEM;
|
|
}
|
|
else {
|
|
hw_info = client->dev.platform_data;
|
|
ts->chip_type = 1; /*1:HID IIC, 0: NORMAL IIC*/
|
|
ts->report_type = 1; /*1:B protocol, 0:A protocol*/
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_OF
|
|
if (client->dev.of_node) {
|
|
ret = elan_parse_dt(&client->dev, hw_info);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
ts->fw_store_type = FROM_SYS_ETC_FIRMWARE; //define get fw solution
|
|
ts->user_handle_irq = 0;
|
|
//ts->irq_lock_flag = 0;
|
|
|
|
//ts->hw_info = *hw_info;
|
|
ret = elan_ts_gpio_initial(&ts->hw_info);
|
|
if (ret)
|
|
dev_err(&client->dev, "gpio initial failed ret = %d\n",ret);
|
|
|
|
dev_err(&client->dev, "[elan] rst = %d, int = %d, irq=%d\n",hw_info->rst_gpio, hw_info->intr_gpio,hw_info->irq_num);
|
|
dev_err(&client->dev, "[elan] lcm_x = %d, lcm_y = %d\n",hw_info->screen_x, hw_info->screen_y);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void elan_ts_hw_deinit(struct elan_ts_data *ts)
|
|
{
|
|
|
|
regulator_put(ts->vdd);
|
|
regulator_put(ts->vcc_i2c);
|
|
if (gpio_is_valid(ts->hw_info.intr_gpio))
|
|
gpio_free(ts->hw_info.intr_gpio);
|
|
|
|
if (gpio_is_valid(ts->hw_info.rst_gpio))
|
|
gpio_free(ts->hw_info.rst_gpio);
|
|
}
|
|
|
|
/*******************************************************
|
|
Function:
|
|
I2c probe.
|
|
Input:
|
|
client: i2c device struct.
|
|
id: device id.
|
|
Output:
|
|
Executive outcomes.
|
|
0: succeed.
|
|
*******************************************************/
|
|
static int elan_ts_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
//#define SM_BUS
|
|
int err;
|
|
#ifdef SM_BUS
|
|
union i2c_smbus_data dummy;
|
|
#endif
|
|
struct elan_ts_data *ts;
|
|
int retry = 0;
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*check i2c bus support fuction*/
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
dev_err(&client->dev,
|
|
"i2c check functionality error\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/*kzalloc struct elan_ts_data memory */
|
|
ts = devm_kzalloc(&client->dev, sizeof(struct elan_ts_data), GFP_KERNEL);
|
|
if (!ts) {
|
|
dev_err(&client->dev,
|
|
"%s: allocate elan_ts_data failed", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
|
|
ts->client = client;
|
|
i2c_set_clientdata(client, ts);
|
|
private_ts = ts;
|
|
|
|
|
|
/*get hw info and initial*/
|
|
err = elan_ts_hw_initial(ts);
|
|
if(err) {
|
|
dev_err(&client->dev, "%s hw initial failed\n",__func__);
|
|
goto free_client_data;
|
|
}
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*set power & power on*/
|
|
#if 1
|
|
err = elan_ts_set_power(ts,1);
|
|
if (err) {
|
|
dev_err(&client->dev, "%s power seting failed\n",__func__);
|
|
goto free_io_port;
|
|
}
|
|
msleep(100);
|
|
#endif
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*check elan ic in bus or not*/
|
|
#ifdef SM_BUS
|
|
if (i2c_smbus_xfer(client->adapter, client->addr, 0,
|
|
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
|
|
dev_err(&client->dev, "nothing at this address 0x%x\n", client->addr);
|
|
goto free_power_set;
|
|
}
|
|
#endif
|
|
|
|
/*elan ic transfer initial*/
|
|
ts->ops = &elan_ops;
|
|
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*check elan ic status*/
|
|
err = elan_ts_setup(ts);
|
|
if (err < 0) {
|
|
dev_err(&client->dev, "%s ic initial failed\n",__func__);
|
|
goto err_no_elan_chip;
|
|
}
|
|
|
|
/*check rek */
|
|
if(COMPARE_UPGRADE == ts->recover) {
|
|
for (retry = 0; retry < 3; retry++) {
|
|
err = elan_ts_check_calibrate(ts->client); /*ic reponse rek count,count != 0xff? "ok":"failed" */
|
|
if (err) {
|
|
dev_err(&ts->client->dev, "[elan] check rek failed, retry=%d\n",retry);
|
|
err = elan_ts_calibrate(ts->client);
|
|
if (err) {
|
|
dev_err(&ts->client->dev, "[elan]calibrate failed, retry=%d\n",retry);
|
|
} else
|
|
break;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*creat dev node & sysfs node for fw operatrion*/
|
|
elan_touch_node_init(ts);
|
|
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*get fw infomation, register input dev, register interrupt*/
|
|
INIT_DELAYED_WORK(&ts->init_work, elan_ic_init_work);
|
|
ts->init_elan_ic_wq = create_singlethread_workqueue("init_elan_ic_wq");
|
|
if (IS_ERR(ts->init_elan_ic_wq)) {
|
|
err = PTR_ERR(ts->init_elan_ic_wq);
|
|
goto err_ic_init_failed;
|
|
}
|
|
queue_delayed_work(ts->init_elan_ic_wq, &ts->init_work, delay);
|
|
|
|
|
|
printk("elan %s() %d\n", __func__, __LINE__);
|
|
/*report work thread*/
|
|
ts->elan_wq = create_singlethread_workqueue("elan_wq");
|
|
if (IS_ERR(ts->elan_wq)) {
|
|
err = PTR_ERR(ts->elan_wq);
|
|
dev_err(&client->dev,
|
|
"[elan error] failed to create kernel thread: %d\n",
|
|
err);
|
|
goto err_create_workqueue_failed;
|
|
}
|
|
INIT_WORK(&ts->ts_work, elan_ts_work_func);
|
|
|
|
/*set print log level*/
|
|
ts->level = TP_DEBUG;
|
|
|
|
/*initial wait queue for userspace*/
|
|
init_waitqueue_head(&ts->elan_userqueue);
|
|
/*lcm callback resume and suspend*/
|
|
#if defined(CONFIG_FB)
|
|
ts->fb_notif.notifier_call = fb_notifier_callback;
|
|
err = fb_register_client(&ts->fb_notif);
|
|
if (err)
|
|
dev_err(&client->dev,"[FB]Unable to register fb_notifier: %d", err);
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
ts->early_suspend.suspend = elan_ts_early_suspend;
|
|
ts->early_suspend.resume = elan_ts_late_resume;
|
|
register_early_suspend(&ts->early_suspend);
|
|
#endif
|
|
|
|
printk("elan %s() %d probe success!\n", __func__, __LINE__);
|
|
return err;
|
|
|
|
err_create_workqueue_failed:
|
|
destroy_workqueue(ts->elan_wq);
|
|
err_ic_init_failed:
|
|
destroy_workqueue(ts->init_elan_ic_wq);
|
|
err_no_elan_chip:
|
|
#ifdef SM_BUS
|
|
free_power_set:
|
|
#endif
|
|
regulator_put(ts->vdd);
|
|
regulator_put(ts->vcc_i2c);
|
|
#if 1
|
|
free_io_port:
|
|
if (gpio_is_valid(ts->hw_info.intr_gpio))
|
|
gpio_free(ts->hw_info.intr_gpio);
|
|
|
|
if (gpio_is_valid(ts->hw_info.rst_gpio))
|
|
gpio_free(ts->hw_info.rst_gpio);
|
|
#endif
|
|
free_client_data:
|
|
i2c_set_clientdata(client,NULL);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*******************************************************
|
|
Function:
|
|
Elan touchscreen driver release function.
|
|
Input:
|
|
client: i2c device struct.
|
|
Output:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
|
|
static void elan_ts_remove(struct i2c_client *client)
|
|
{
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
|
|
elan_ts_hw_deinit(ts);
|
|
elan_touch_node_deinit(ts);
|
|
|
|
input_unregister_device(ts->finger_idev);
|
|
input_unregister_device(ts->pen_idev);
|
|
free_irq(ts->hw_info.irq_num,(void *)elan_ts_irq_handler);
|
|
|
|
if (!IS_ERR(ts->init_elan_ic_wq)) {
|
|
destroy_workqueue(ts->init_elan_ic_wq);
|
|
}
|
|
|
|
if (!IS_ERR(ts->elan_wq)) {
|
|
destroy_workqueue(ts->elan_wq);
|
|
}
|
|
#if defined(CONFIG_FB)
|
|
fb_unregister_client(&ts->fb_notif);
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
unregister_early_suspend(&ts->early_suspend);
|
|
#endif
|
|
i2c_set_clientdata(client,NULL);
|
|
}
|
|
|
|
|
|
static void elan_release_point(void)
|
|
{
|
|
struct input_dev *fidev;
|
|
struct input_dev *pidev;
|
|
int i = 0;
|
|
|
|
if (private_ts->finger_idev && private_ts->pen_idev) {
|
|
fidev = private_ts->finger_idev;
|
|
pidev = private_ts->pen_idev;
|
|
|
|
if (private_ts->report_type == PROTOCOL_TYPE_B) {
|
|
for (i = 0; i < 10; i++) {
|
|
input_mt_slot(fidev, i);
|
|
input_mt_report_slot_state(fidev, MT_TOOL_FINGER, 0);
|
|
}
|
|
|
|
if (private_ts->report.tool_type == ELAN_PEN) {
|
|
input_mt_slot(pidev, 0);
|
|
input_mt_report_slot_state(pidev, MT_TOOL_PEN, false);
|
|
}
|
|
|
|
} else {
|
|
input_mt_sync(fidev);
|
|
input_report_key(fidev, BTN_TOUCH, 0);
|
|
|
|
if (private_ts->report.tool_type == ELAN_PEN) {
|
|
input_mt_sync(pidev);
|
|
input_report_key(pidev, BTN_TOUCH, 0);
|
|
}
|
|
}
|
|
input_sync(fidev);
|
|
|
|
if (private_ts->report.tool_type == ELAN_PEN) {
|
|
input_sync(pidev);
|
|
}
|
|
} else {
|
|
dev_err(&private_ts->client->dev, "Noting done\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
static int elan_ts_set_power_state(struct i2c_client *client, int state)
|
|
{
|
|
int err = 0;
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
/*send ic sleep/wake up command*/
|
|
uint8_t hid_cmd[HID_CMD_LEN] = {0x04, 0x00, 0x23, 0x00, 0x03, 0x00, 0x04, CMD_W_PKT, 0x50, 0x00, 0x01};
|
|
uint8_t cmd[4] = {CMD_W_PKT, 0x50, 0x00, 0x01};
|
|
|
|
if (ts->chip_type == HID_TYPE_PROTOCOL) {
|
|
hid_cmd[8] |= (state << 3);
|
|
err = ts->ops->send(hid_cmd, sizeof(hid_cmd));
|
|
if (err != sizeof(hid_cmd)) {
|
|
err = -EINVAL;
|
|
goto err_set_power_state;
|
|
}
|
|
} else {
|
|
cmd[1] |= (state << 3);
|
|
err = ts->ops->send(cmd,sizeof(cmd));
|
|
if (err != sizeof(cmd)) {
|
|
err = -EINVAL;
|
|
goto err_set_power_state;
|
|
}
|
|
}
|
|
|
|
print_log(ts->level, "[elan] set power stats success\n");
|
|
return 0;
|
|
|
|
err_set_power_state:
|
|
return err;
|
|
}
|
|
|
|
|
|
static int elan_ts_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
int err = 0;
|
|
int retry = RETRY_TIMES;
|
|
|
|
//if do fw upgrade, don't sleep
|
|
if (ts->power_lock == 0) {
|
|
dev_err(&client->dev, "[elan] %s suspend flow \n", __func__);
|
|
elan_switch_irq(ts, 0);
|
|
try_set_power: // if system would not power off, must do this and check
|
|
err = elan_ts_set_power_state(ts->client, PWR_STATE_DEEP_SLEEP);
|
|
if (err) {
|
|
dev_err(&client->dev, "[elan] set power stats failed err = %d\n", err);
|
|
if ( (retry --) > 0)
|
|
goto try_set_power;
|
|
}
|
|
|
|
/*release finger*/
|
|
elan_release_point();
|
|
|
|
/*power off*/
|
|
elan_ts_power_on(ts,false);
|
|
} else {
|
|
dev_err(&client->dev, "[elsn] %s Nothing Done!!\n",__func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int elan_ts_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct elan_ts_data *ts = i2c_get_clientdata(client);
|
|
int err = 0;
|
|
int retry = RETRY_TIMES;
|
|
|
|
|
|
|
|
|
|
/*
|
|
** enable irq, set ic status, reset ic
|
|
**/
|
|
if (ts->power_lock == 0) {
|
|
dev_err(&client->dev, "[elan] reset gpio to resum tp\n");
|
|
/*device power on*/
|
|
elan_ts_power_on(ts,true);
|
|
|
|
/*delay for ic initial*/
|
|
msleep(100);
|
|
|
|
reset_power_state:
|
|
err = elan_ts_set_power_state(ts->client, PWR_STATE_NORMAL);
|
|
if (err) {
|
|
dev_err(&client->dev, "[elan]%s set power stata failed!!\n",__func__);
|
|
if ((retry--) > 0)
|
|
goto reset_power_state;
|
|
else
|
|
elan_ts_hw_reset(&ts->hw_info);
|
|
}
|
|
/*release point*/
|
|
elan_release_point();
|
|
elan_switch_irq(ts, 1);
|
|
} else {
|
|
dev_err(&client->dev, "[elsn] %s Nothing Done!!\n",__func__);
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************
|
|
Function:
|
|
fb_notifier_callback function.
|
|
Input:
|
|
self: notifier_block struct.
|
|
event: unsigned long.
|
|
data: void
|
|
Output:
|
|
0.
|
|
*******************************************************/
|
|
#if defined(CONFIG_FB)
|
|
static int fb_notifier_callback(struct notifier_block *self,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct fb_event *evdata = data;
|
|
int *blank;
|
|
struct elan_ts_data *ts =
|
|
container_of(self, struct elan_ts_data, fb_notif);
|
|
|
|
if (evdata && evdata->data && event == FB_EVENT_BLANK &&
|
|
ts && ts->client) {
|
|
blank = evdata->data;
|
|
if (*blank == FB_BLANK_UNBLANK)
|
|
elan_ts_resume(&ts->client->dev);
|
|
else if (*blank == FB_BLANK_POWERDOWN)
|
|
elan_ts_suspend(&ts->client->dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
/*******************************************************
|
|
Function:
|
|
Early suspend function.
|
|
Input:
|
|
h: early_suspend struct.
|
|
Output:
|
|
None.
|
|
*******************************************************/
|
|
static void elan_ts_early_suspend(struct early_suspend *h)
|
|
{
|
|
struct elan_ts_data *ts;
|
|
ts = container_of(h, struct elan_ts_data, early_suspend);
|
|
elan_ts_suspend(&ts->client->dev);
|
|
}
|
|
|
|
/*******************************************************
|
|
Function:
|
|
Late resume function.
|
|
Input:
|
|
h: early_suspend struct.
|
|
Output:
|
|
None.
|
|
*******************************************************/
|
|
|
|
static void elan_ts_late_resume(struct early_suspend *h)
|
|
{
|
|
struct elan_ts_data *ts;
|
|
ts = container_of(h, struct elan_ts_data, early_suspend);
|
|
elan_ts_resume(&ts->client->dev);
|
|
}
|
|
#endif/* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
|
|
|
|
#ifdef CONFIG_PM
|
|
static const struct dev_pm_ops elan_ts_dev_pm_ops = {
|
|
#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
|
|
.suspend = elan_ts_suspend,
|
|
.resume = elan_ts_resume,
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
static const struct i2c_device_id elan_ts_id[] = {
|
|
{ ELAN_TS_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id elan_of_match[] = {
|
|
{.compatible = "elan,ektf"},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, elan_of_match);
|
|
#endif
|
|
|
|
static struct i2c_driver elan_ts_driver = {
|
|
.probe = elan_ts_probe,
|
|
.remove = elan_ts_remove,
|
|
.id_table = elan_ts_id,
|
|
.driver = {
|
|
.name = ELAN_TS_NAME,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = elan_of_match,
|
|
#endif
|
|
#ifdef CONFIG_PM
|
|
.pm = &elan_ts_dev_pm_ops,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int __init elan_ts_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = i2c_add_driver(&elan_ts_driver);
|
|
return ret;
|
|
}
|
|
|
|
static void __exit elan_ts_exit(void)
|
|
{
|
|
i2c_del_driver(&elan_ts_driver);
|
|
return;
|
|
}
|
|
|
|
module_init(elan_ts_init);
|
|
module_exit(elan_ts_exit);
|
|
MODULE_DESCRIPTION("ELAN HID-I2C and I2C Touchscreen Driver");
|
|
MODULE_AUTHOR("Minger Zhang <chuming.zhang@elanic.com.cn>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|