From 2e21c93ee97977d84590a484ff5570599e13b6bb Mon Sep 17 00:00:00 2001 From: "Alfred.Deng" Date: Thu, 29 Jan 2015 19:11:43 +0800 Subject: [PATCH] input: touchscreen: Add Touch screen driver for IC it7258 This is the reference driver source code of it7258 touchscreen driver. Signed-off-by: Alfred Deng Git-commit: 3852a7ccce89c17ec3c4165acf9c81753c940104 Git-repo: git://github.com/ite-touch/touchscreen-driver Change-Id: Ic1eab3ba79b8e8e5c259bb92f2692fd0db5fc8d3 Signed-off-by: Himanshu Aggarwal (cherry picked from commit c25c3f1a0f29d7989b7d7058da74e465665bf279) Signed-off-by: Abinaya P --- drivers/input/touchscreen/it7258_ts_i2c.c | 1140 +++++++++++++++++++++ 1 file changed, 1140 insertions(+) create mode 100644 drivers/input/touchscreen/it7258_ts_i2c.c diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c new file mode 100644 index 000000000000..812605ed1270 --- /dev/null +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -0,0 +1,1140 @@ +/* drivers/input/touchscreen/it7258_ts_i2c.c + * + * Copyright (C) 2014 ITE Tech. Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BUFFER_SIZE 144 +#define DEVICE_NAME "IT7260" +#define SCREEN_X_RESOLUTION 320 +#define SCREEN_Y_RESOLUTION 320 + +#define IOC_MAGIC 'd' +#define IOCTL_SET _IOW(IOC_MAGIC, 1, struct ioctl_cmd168) +#define IOCTL_GET _IOR(IOC_MAGIC, 2, struct ioctl_cmd168) + +#define BUF_COMMAND 0x20 /* all commands writes go to this idx */ +#define BUF_SYS_COMMAND 0x40 +#define BUF_QUERY 0x80 /* "revice ready?" and "wake up please" and "read touch data" reads go to this idx */ +#define BUF_RESPONSE 0xA0 /* most command responce reads go to this idx */ +#define BUF_SYS_RESPONSE 0xC0 +#define BUF_POINT_INFO 0xE0 /* reads of "point" go through here and produce 14 bytes of data */ + +/* commands and their subcommands. when no subcommands exist, a zero is send as the second byte */ +#define CMD_IDENT_CHIP 0x00 +#define CMD_READ_VERSIONS 0x01 /* VERSION_LENGTH bytes of data in response */ +# define VER_FIRMWARE 0x00 +# define VER_CONFIG 0x06 +# define VERSION_LENGTH 10 +#define CMD_PWR_CTL 0x04 /* subcommand is zero, next byte is power mode */ +# define PWR_CTL_LOW_POWER_MODE 0x01 /* idle mode */ +# define PWR_CTL_SLEEP_MODE 0x02 /* sleep mode */ +#define CMD_UNKNOWN_7 0x07 /* command is not documented in the datasheet v1.0.0.7 */ +#define CMD_FIRMWARE_REINIT_C 0x0C +#define CMD_CALIBRATE 0x13 /* needs to be followed by 4 bytes of zeroes */ +#define CMD_FIRMWARE_UPGRADE 0x60 +# define FIRMWARE_MODE_ENTER 0x00 +# define FIRMWARE_MODE_EXIT 0x80 +#define CMD_SET_START_OFFSET 0x61 /* address for FW read/write */ +#define CMD_FW_WRITE 0x62 /* subcommand is number of bytes to write */ +#define CMD_FW_READ 0x63 /* subcommand is number of bytes to read */ +#define CMD_FIRMWARE_REINIT_6F 0x6F + +#define FW_WRITE_CHUNK_SIZE 128 +#define FW_WRITE_RETRY_COUNT 4 +#define CHIP_FLASH_SIZE 0x8000 +#define SYSFS_FW_UPLOAD_MODE_MANUAL 2 +#define SYSFS_RESULT_FAIL (-1) +#define SYSFS_RESULT_NOT_DONE 0 +#define SYSFS_RESULT_SUCCESS 1 +#define DEVICE_READY_MAX_WAIT 500 + + +//result of reading with BUF_QUERY bits +#define CMD_STATUS_BITS 0x07 +#define CMD_STATUS_DONE 0x00 +#define CMD_STATUS_BUSY 0x01 +#define CMD_STATUS_ERROR 0x02 +#define PT_INFO_BITS 0xF8 +#define BT_INFO_NONE 0x00 +#define PT_INFO_YES 0x80 +#define BT_INFO_NONE_BUT_DOWN 0x08 /* no new data but finder(s) still down */ + +/* use this to include integers in commands */ +#define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8)) + + +struct FingerData { + uint8_t xLo; + uint8_t hi; + uint8_t yLo; + uint8_t pressure; +} __attribute__((packed)); + +struct PointData { + uint8_t flags; + uint8_t palm; + struct FingerData fd[3]; +} __attribute__((packed)); + +#define PD_FLAGS_DATA_TYPE_BITS 0xF0 +# define PD_FLAGS_DATA_TYPE_TOUCH 0x00 /* other types (like chip-detected gestures) exist but we do not care */ +#define PD_FLAGS_NOT_PEN 0x08 /* set if pen touched, clear if finger(s) */ +#define PD_FLAGS_HAVE_FINGERS 0x07 /* a bit for each finger data that is valid (from lsb to msb) */ +#define PD_PALM_FLAG_BIT 0x01 +#define FD_PRESSURE_BITS 0x0F +# define FD_PRESSURE_NONE 0x00 +# define FD_PRESSURE_HOVER 0x01 +# define FD_PRESSURE_LIGHT 0x02 +# define FD_PRESSURE_NORMAL 0x04 +# define FD_PRESSURE_HIGH 0x08 +# define FD_PRESSURE_HEAVY 0x0F + +struct ioctl_cmd168 { + uint16_t bufferIndex; + uint16_t length; + uint16_t buffer[MAX_BUFFER_SIZE]; +}; + +struct IT7260_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct delayed_work palmdown_work; + struct delayed_work palmup_work; +}; + +struct ite7260_perfile_data { + rwlock_t lock; + uint16_t bufferIndex; + uint16_t length; + uint16_t buffer[MAX_BUFFER_SIZE]; +}; + +static int8_t fwUploadResult = SYSFS_RESULT_NOT_DONE; +static int8_t calibrationWasSuccessful = SYSFS_RESULT_NOT_DONE; +static bool devicePresent = false; +static DEFINE_MUTEX(sleepModeMutex); +static bool chipAwake = true; +static bool hadFingerDown = false; +static bool isDeviceSleeping = false; +static bool isDeviceSuspend = false; +static int ite7260_major = 0; +static int ite7260_minor = 0; +static struct cdev ite7260_cdev; +static struct class *ite7260_class = NULL; +static dev_t ite7260_dev; +static struct input_dev *input_dev; +static struct device *class_dev = NULL; +static int suspend_touch_down = 0; +static int suspend_touch_up = 0; +static struct IT7260_ts_data *gl_ts; +static struct wake_lock touch_lock; + +#define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__) +#define LOGI(...) printk(DEVICE_NAME ": " __VA_ARGS__) + +/* add workqueue for delay_work */ +static struct workqueue_struct *IT7260_wq; + +/* internal use func - does not make sure chip is ready before read */ +static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength) +{ + struct i2c_msg msgs[2] = { + { + .addr = gl_ts->client->addr, + .flags = I2C_M_NOSTART, + .len = 1, + .buf = &bufferIndex + }, + { + .addr = gl_ts->client->addr, + .flags = I2C_M_RD, + .len = dataLength, + .buf = dataBuffer + } + }; + + memset(dataBuffer, 0xFF, dataLength); + + return i2c_transfer(gl_ts->client->adapter, msgs, 2); +} + +static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength) +{ + uint8_t txbuf[257]; + struct i2c_msg msg = { + .addr = gl_ts->client->addr, + .flags = 0, + .len = dataLength + 1, + .buf = txbuf + }; + + /* just to be careful */ + BUG_ON(dataLength > sizeof(txbuf) - 1); + + txbuf[0] = bufferIndex; + memcpy(txbuf + 1, dataBuffer, dataLength); + + return i2c_transfer(gl_ts->client->adapter, &msg, 1); +} + +/* + * Device is apparently always ready for i2c but not for actual register reads/writes. + * This function ascertains it is ready for that too. the results of this call often + * were ignored. + */ +static bool waitDeviceReady(bool forever, bool slowly) +{ + uint8_t ucQuery; + uint32_t count = DEVICE_READY_MAX_WAIT; + + do{ + if (!i2cReadNoReadyCheck(BUF_QUERY, &ucQuery, sizeof(ucQuery))) + ucQuery = CMD_STATUS_BUSY; + + if (slowly) + mdelay(1000); + if (!forever) + count--; + + }while((ucQuery & CMD_STATUS_BUSY) && count); + + return !ucQuery; +} + +static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength) +{ + waitDeviceReady(false, false); + return i2cReadNoReadyCheck(bufferIndex, dataBuffer, dataLength); +} + +static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength) +{ + waitDeviceReady(false, false); + return i2cWriteNoReadyCheck(bufferIndex, dataBuffer, dataLength); +} + +static bool chipFirmwareReinitialize(uint8_t cmdOfChoice) +{ + uint8_t cmd[] = {cmdOfChoice}; + uint8_t rsp[2]; + + if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!i2cRead(BUF_RESPONSE, rsp, sizeof(rsp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !rsp[0] && !rsp[1]; +} + +static bool chipFirmwareUpgradeModeEnterExit(bool enter) +{ + uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', '6', '0', 0x55, 0xAA}; + uint8_t resp[2]; + + cmd[1] = enter ? FIRMWARE_MODE_ENTER : FIRMWARE_MODE_EXIT; + if (!i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + +static bool chipSetStartOffset(uint16_t offset) +{ + uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, CMD_UINT16(offset)}; + uint8_t resp[2]; + + if (!i2cWrite(BUF_COMMAND, cmd, 4)) + return false; + + + if (!i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + + +//write fwLength bytes from fwData at chip offset writeStartOffset +static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData, uint16_t writeStartOffset) +{ + uint32_t curDataOfst; + + for (curDataOfst = 0; curDataOfst < fwLength; curDataOfst += FW_WRITE_CHUNK_SIZE) { + + uint8_t cmdWrite[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE}; + uint8_t bufRead[FW_WRITE_CHUNK_SIZE]; + uint8_t cmdRead[2] = {CMD_FW_READ}; + unsigned i, nRetries; + uint32_t curWriteSz; + + /* figure out how much to write */ + curWriteSz = fwLength - curDataOfst; + if (curWriteSz > FW_WRITE_CHUNK_SIZE) + curWriteSz = FW_WRITE_CHUNK_SIZE; + + /* prepare the write command */ + cmdWrite[1] = curWriteSz; + for (i = 0; i < curWriteSz; i++) + cmdWrite[i + 2] = fwData[curDataOfst + i]; + + /* prepare the read command */ + cmdRead[1] = curWriteSz; + + for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT; nRetries++) { + + /* set write offset and write the data*/ + chipSetStartOffset(writeStartOffset + curDataOfst); + i2cWrite(BUF_COMMAND, cmdWrite, 2 + curWriteSz); + + /* set offset and read the data back */ + chipSetStartOffset(writeStartOffset + curDataOfst); + i2cWrite(BUF_COMMAND, cmdRead, sizeof(cmdRead)); + i2cRead(BUF_RESPONSE, bufRead, curWriteSz); + + /* verify. If success break out of retry loop */ + for (i = 0; i < curWriteSz && bufRead[i] == cmdWrite[i + 2]; i++); + if (i == curWriteSz) + break; + LOGE("write of data offset %u failed on try %u at byte %u/%u\n", curDataOfst, nRetries, i, curWriteSz); + } + /* if we've failed after all the retries, tell the caller */ + if (nRetries == FW_WRITE_RETRY_COUNT) + return false; + } + + return true; +} + +static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t cfgLen, const uint8_t *cfgData) +{ + bool success = false; + + /* enter fw upload mode */ + if (!chipFirmwareUpgradeModeEnterExit(true)) + return false; + + /* flash the firmware if requested */ + if (fwLen && fwData && !chipFlashWriteAndVerify(fwLen, fwData, 0)) { + LOGE("failed to upload touch firmware\n"); + goto out; + } + + /* flash config data if requested */ + if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData, CHIP_FLASH_SIZE - cfgLen)) { + LOGE("failed to upload touch cfg data\n"); + goto out; + } + + success = true; + +out: + return chipFirmwareUpgradeModeEnterExit(false) && chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success; +} + +//both buffers should be VERSION_LENGTH in size, but only a part of them is significant */ +static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt) +{ + /* this code to get versions is reproduced as was written, but it does not make sense. Something here *PROBABLY IS* wrong */ + static const uint8_t cmdReadFwVer[] = {CMD_READ_VERSIONS, VER_FIRMWARE}; + static const uint8_t cmdReadCfgVer[] = {CMD_READ_VERSIONS, VER_CONFIG}; + bool ret = true; + + /* this structure is so that we definitely do all the calls, but still return a status in case anyone cares */ + ret = i2cWrite(BUF_COMMAND, cmdReadFwVer, sizeof(cmdReadFwVer)) && ret; + ret = i2cRead(BUF_RESPONSE, verFw, VERSION_LENGTH) && ret; + ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer, sizeof(cmdReadCfgVer)) && ret; + ret = i2cRead(BUF_RESPONSE, verCfg, VERSION_LENGTH) && ret; + + if (logIt) + LOGI("current versions: fw@{%X,%X,%X,%X}, cfg@{%X,%X,%X,%X}\n", + verFw[5], verFw[6], verFw[7], verFw[8], + verCfg[1], verCfg[2], verCfg[3], verCfg[4]); + + return ret; +} + +/* fix touch will not wake up system in suspend mode */ +static void chipLowPowerMode(bool low) +{ + int allow_irq_wake = !(isDeviceSleeping); + static const uint8_t cmdLowPower[] = { CMD_PWR_CTL, 0x00, PWR_CTL_LOW_POWER_MODE}; + uint8_t dummy; + + if (devicePresent) { + LOGI("low power %s\n", low ? "enter" : "exit"); + + if (low) { + if (allow_irq_wake){ + smp_wmb(); + enable_irq_wake(gl_ts->client->irq); + } + isDeviceSleeping = true; + wake_unlock(&touch_lock); + i2cWriteNoReadyCheck(BUF_COMMAND, cmdLowPower, sizeof(cmdLowPower)); + } else { + if (!allow_irq_wake){ + smp_wmb(); + disable_irq_wake(gl_ts->client->irq); + } + isDeviceSleeping = false; + isDeviceSuspend = false; + wake_unlock(&touch_lock); + i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); + } + } +} + +static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + const struct firmware *fw, *cfg; + uint8_t verFw[10], verCfg[10]; + unsigned fwLen = 0, cfgLen = 0; + bool manualUpgrade, success; + int mode = 0; + + if (request_firmware(&fw, "it7260.fw", dev)) + LOGE("failed to get firmware for it7260\n"); + else + fwLen = fw->size; + + + if (request_firmware(&cfg, "it7260.cfg", dev)) + LOGE("failed to get config data for it7260\n"); + else + cfgLen = cfg->size; + + sscanf(buf, "%d", &mode); + manualUpgrade = mode == SYSFS_FW_UPLOAD_MODE_MANUAL; + LOGI("firmware found %ub of fw and %ub of config in %s mode\n", + fwLen, cfgLen, manualUpgrade ? "manual" : "normal"); + + chipGetVersions(verFw, verCfg, true); + + fwUploadResult = SYSFS_RESULT_NOT_DONE; + if (fwLen && cfgLen) { + if (manualUpgrade || (verFw[5] < fw->data[8] || verFw[6] < fw->data[9] || verFw[7] < fw->data[10] || verFw[8] < fw->data[11]) || + (verCfg[1] < cfg->data[cfgLen - 8] || verCfg[2] < cfg->data[cfgLen - 7] || verCfg[3] < cfg->data[cfgLen - 6] || verCfg[4] < cfg->data[cfgLen - 5])){ + LOGI("firmware/config will be upgraded\n"); + disable_irq(gl_ts->client->irq); + success = chipFirmwareUpload(fwLen, fw->data, cfgLen, cfg->data); + enable_irq(gl_ts->client->irq); + + fwUploadResult = success ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; + LOGI("upload %s\n", success ? "success" : "failed"); + } + else { + LOGI("firmware/config upgrade not needed\n"); + } + } + + if (fwLen) + release_firmware(fw); + + if (cfgLen) + release_firmware(cfg); + + return count; +} + +static ssize_t sysfsUpgradeShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", fwUploadResult); +} + +static ssize_t sysfsCalibrationShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", calibrationWasSuccessful); +} + +static bool chipSendCalibrationCmd(bool autoTuneOn) +{ + uint8_t cmdCalibrate[] = {CMD_CALIBRATE, 0, autoTuneOn ? 1 : 0, 0, 0}; + return i2cWrite(BUF_COMMAND, cmdCalibrate, sizeof(cmdCalibrate)); +} + +static ssize_t sysfsCalibrationStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + uint8_t resp; + + if (!chipSendCalibrationCmd(false)) + LOGE("failed to send calibration command\n"); + else { + calibrationWasSuccessful = i2cRead(BUF_RESPONSE, &resp, sizeof(resp)) ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; + + /* previous logic that was here never called chipFirmwareReinitialize() due to checking a guaranteed-not-null value against null. We now call it. Hopefully this is OK */ + if (!resp) + LOGI("chipFirmwareReinitialize -> %s\n", chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) ? "success" : "fail"); + } + + return count; +} + +static ssize_t sysfsPointShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t pointData[sizeof(struct PointData)]; + bool readSuccess; + ssize_t ret; + + readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData, sizeof(pointData)); + ret = sprintf(buf, "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n", + readSuccess, pointData[0],pointData[1],pointData[2],pointData[3], + pointData[4],pointData[5],pointData[6],pointData[7],pointData[8], + pointData[9],pointData[10],pointData[11],pointData[12],pointData[13]); + + + LOGI("%s", buf); + + return ret; +} + +static ssize_t sysfsPointStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t sysfsStatusShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", devicePresent ? 1 : 0); +} + +static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + uint8_t verFw[10], verCfg[10]; + + chipGetVersions(verFw, verCfg, true); + + return count; +} + +static ssize_t sysfsVersionShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t verFw[10], verCfg[10]; + + chipGetVersions(verFw, verCfg, false); + return sprintf(buf, "%x,%x,%x,%x # %x,%x,%x,%x\n",verFw[5], verFw[6], verFw[7], verFw[8], verCfg[1], verCfg[2], verCfg[3], verCfg[4]); +} + +static ssize_t sysfsVersionStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t sysfsSleepShow(struct device *dev, struct device_attribute *attr, char *buf) +{ + /* + * The usefulness of this was questionable at best - we were at least leaking a byte of + * kernel data (by claiming to return a byte but not writing to buf. To fix this now + * we actually return the sleep status + */ + if (!mutex_lock_interruptible(&sleepModeMutex)) { + *buf = chipAwake ? '1' : '0'; + mutex_unlock(&sleepModeMutex); + return 1; + } + else + return -EINTR; +} + +static ssize_t sysfsSleepStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, 0x00, PWR_CTL_SLEEP_MODE}; + int goToSleepVal; + bool goToWake; + uint8_t dummy; + + sscanf(buf, "%d", &goToSleepVal); + goToWake = !goToSleepVal; /* convert to bool of proper polarity */ + + if (!mutex_lock_interruptible(&sleepModeMutex)) { + if ((chipAwake && goToWake) || (!chipAwake && !goToWake)) + LOGE("duplicate request to %s chip\n", goToWake ? "wake" : "sleep"); + else if (goToWake) { + i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); + enable_irq(gl_ts->client->irq); + LOGI("touch is going to wake!\n\n"); + } else { + disable_irq(gl_ts->client->irq); + i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, sizeof(cmdGoSleep)); + LOGI("touch is going to sleep...\n\n"); + } + chipAwake = goToWake; + mutex_unlock(&sleepModeMutex); + return count; + } else + return -EINTR; +} + + +static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, sysfsStatusShow, sysfsStatusStore); +static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP, sysfsVersionShow, sysfsVersionStore); +static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP, sysfsSleepShow, sysfsSleepStore); + +static struct attribute *it7260_attrstatus[] = { + &dev_attr_status.attr, + &dev_attr_version.attr, + &dev_attr_sleep.attr, + NULL +}; + +static const struct attribute_group it7260_attrstatus_group = { + .attrs = it7260_attrstatus, +}; + +static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP, sysfsCalibrationShow, sysfsCalibrationStore); +static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP, sysfsUpgradeShow, sysfsUpgradeStore); +static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP, sysfsPointShow, sysfsPointStore); + + +static struct attribute *it7260_attributes[] = { + &dev_attr_calibration.attr, + &dev_attr_upgrade.attr, + &dev_attr_point.attr, + NULL +}; + +static const struct attribute_group it7260_attr_group = { + .attrs = it7260_attributes, +}; + +static void chipExternalCalibration(bool autoTuneEnabled) +{ + uint8_t resp[2]; + + LOGI("sent calibration command -> %d\n", chipSendCalibrationCmd(autoTuneEnabled)); + waitDeviceReady(true, true); + i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp)); + chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_C); +} + +void sendCalibrationCmd(void) +{ + chipExternalCalibration(false); +} +EXPORT_SYMBOL(sendCalibrationCmd); + + +static int mode_notify_sys(struct notifier_block *notif, unsigned long code, void *data) +{ + switch (code) { + case 0: //FB_BLANK_ENTER_NON_INTERACTIVE + chipLowPowerMode(1); + break; + case 1: //FB_BLANK_ENTER_INTERACTIVE + chipLowPowerMode(0); + break; + default: + break; + } + + return 0; +} + +static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, const struct FingerData *fd) +{ + uint16_t x = fd->xLo; + uint16_t y = fd->yLo; + + x += ((uint16_t)(fd->hi & 0x0F)) << 8; + y += ((uint16_t)(fd->hi & 0xF0)) << 4; + + if (xP) + *xP = x; + if (yP) + *yP = y; + if (pressureP) + *pressureP = fd->pressure & FD_PRESSURE_BITS; +} + +static uint64_t getMsTime(void) +{ + struct timespec ts; + uint64_t ret; + + getnstimeofday(&ts); + + /* convert nsec to msec */ + ret = ts.tv_nsec; + do_div(ret, 1000000UL); + + /* add in sec */ + return ret + ts.tv_sec * 1000ULL; +} + +static void readTouchDataPoint(void) +{ + struct PointData pointData; + uint8_t devStatus; + uint8_t pressure = FD_PRESSURE_NONE; + uint16_t x, y; + + /* verify there is point data to read & it is readable and valid */ + i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus)); + if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) { + LOGE("readTouchDataPoint() called when no data available (0x%02X)\n", devStatus); + return; + } + if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void*)&pointData, sizeof(pointData))) { + LOGE("readTouchDataPoint() failed to read point data buffer\n"); + return; + } + if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != PD_FLAGS_DATA_TYPE_TOUCH) { + LOGE("readTouchDataPoint() dropping non-point data of type 0x%02X\n", pointData.flags); + return; + } + + if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1) + readFingerData(&x, &y, &pressure, pointData.fd); + + if (pressure >= FD_PRESSURE_LIGHT) { + + if (!hadFingerDown) + hadFingerDown = true; + + readFingerData(&x, &y, &pressure, pointData.fd); + + input_report_abs(gl_ts->input_dev, ABS_X, x); + input_report_abs(gl_ts->input_dev, ABS_Y, y); + input_report_key(gl_ts->input_dev, BTN_TOUCH, 1); + input_sync(gl_ts->input_dev); + + } else if (hadFingerDown) { + hadFingerDown = false; + + input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); + input_sync(gl_ts->input_dev); + } + +} + +static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) +{ + smp_rmb(); + if (isDeviceSleeping) { + smp_wmb(); + } else { + readTouchDataPoint(); + } + + return IRQ_HANDLED; +} + +static bool chipIdentifyIT7260(void) +{ + static const uint8_t cmdIdent[] = {CMD_IDENT_CHIP}; + static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7', '2', '6', '0'}; + uint8_t chipID[10] = {0,}; + + waitDeviceReady(true, false); + + if (!i2cWriteNoReadyCheck(BUF_COMMAND, cmdIdent, sizeof(cmdIdent))) { + LOGE("i2cWrite() failed\n"); + return false; + } + + waitDeviceReady(true, false); + + if (!i2cReadNoReadyCheck(BUF_RESPONSE, chipID, sizeof(chipID))) { + LOGE("i2cRead() failed\n"); + return false; + } + + LOGI("chipIdentifyIT7260 read id: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + chipID[0], chipID[1], chipID[2], chipID[3], chipID[4], + chipID[5], chipID[6], chipID[7], chipID[8], chipID[9]); + + if (memcmp(chipID, expectedID, sizeof(expectedID))) + return false; + + if (chipID[8] == '5' && chipID[9] == '6') + LOGI("rev BX3 found\n"); + else if (chipID[8] == '6' && chipID[9] == '6') + LOGI("rev BX4 found\n"); + else + LOGI("unknown revision (0x%02X 0x%02X) found\n", chipID[8], chipID[9]); + + return true; +} + +static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + static const uint8_t cmdStart[] = {CMD_UNKNOWN_7}; + struct IT7260_i2c_platform_data *pdata; + uint8_t rsp[2]; + int ret = -1; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + LOGE("need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_out; + } + + if (!client->irq) { + LOGE("need IRQ\n"); + ret = -ENODEV; + goto err_out; + } + + gl_ts = kzalloc(sizeof(*gl_ts), GFP_KERNEL); + if (!gl_ts) { + ret = -ENOMEM; + goto err_out; + } + + gl_ts->client = client; + i2c_set_clientdata(client, gl_ts); + pdata = client->dev.platform_data; + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attrstatus_group)) { + dev_err(&client->dev, "failed to register sysfs #1\n"); + goto err_sysfs_grp_create_1; + } + + if (!chipIdentifyIT7260()) { + LOGI ("chipIdentifyIT7260 FAIL"); + goto err_ident_fail_or_input_alloc; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + LOGE("failed to allocate input device\n"); + ret = -ENOMEM; + goto err_ident_fail_or_input_alloc; + } + gl_ts->input_dev = input_dev; + + input_dev->name = DEVICE_NAME; + input_dev->phys = "I2C"; + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x7260; + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); + set_bit(INPUT_PROP_DIRECT,input_dev->propbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(KEY_SLEEP,input_dev->keybit); + set_bit(KEY_WAKEUP,input_dev->keybit); + set_bit(KEY_POWER,input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0); + + IT7260_wq = create_workqueue("IT7260_wq"); + if (!IT7260_wq) + goto err_check_functionality_failed; + + if (input_register_device(input_dev)) { + LOGE("failed to register input device\n"); + goto err_input_register; + } + + if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_irq_reg; + } + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attr_group)) { + dev_err(&client->dev, "failed to register sysfs #2\n"); + goto err_sysfs_grp_create_2; + } + wake_lock_init(&touch_lock, WAKE_LOCK_SUSPEND, "touch-lock"); + + devicePresent = true; + + i2cWriteNoReadyCheck(BUF_COMMAND, cmdStart, sizeof(cmdStart)); + mdelay(10); + i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); + mdelay(10); + + return 0; + +err_sysfs_grp_create_2: + free_irq(client->irq, gl_ts); + +err_irq_reg: + input_unregister_device(input_dev); + input_dev = NULL; + +err_input_register: + if (input_dev) + input_free_device(input_dev); + +err_ident_fail_or_input_alloc: + sysfs_remove_group(&(client->dev.kobj), &it7260_attrstatus_group); + +err_sysfs_grp_create_1: + kfree(gl_ts); + +err_check_functionality_failed: + if (IT7260_wq) + destroy_workqueue(IT7260_wq); + +err_out: + return ret; +} + +static int IT7260_ts_remove(struct i2c_client *client) +{ + destroy_workqueue(IT7260_wq); + devicePresent = false; + return 0; +} + +static long ite7260_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + static const uint8_t fakeCmdIrqOff[] = {0x00, 0x49, 0x54, 0x37, 0x32}; + static const uint8_t fakeCmdIrqOn[] = {0x80, 0x49, 0x54, 0x37, 0x32}; + uint8_t buffer[MAX_BUFFER_SIZE] = {0,}; + struct ioctl_cmd168 data = {0, }; + unsigned i; + + switch (cmd) { + case IOCTL_SET: + LOGE("direct command TX from userspace is undoubtedly bad!\n"); + if (copy_from_user(&data, (void __user *)arg, sizeof(struct ioctl_cmd168))) + return -EFAULT; + if (data.length >= sizeof(buffer)) + return -EINVAL; /* stop trying to overflow kernel stack! */ + + for (i = 0; i < data.length; i++) + buffer[i] = data.buffer[i]; + + if (data.bufferIndex == 0x60) { + if (!memcmp(buffer, fakeCmdIrqOff, sizeof(fakeCmdIrqOff))) { + pr_info("Disabling IRQ.\n"); + disable_irq(gl_ts->client->irq); + } else if (!memcmp(buffer, fakeCmdIrqOn, sizeof(fakeCmdIrqOff))) { + pr_info("Enabling IRQ.\n"); + enable_irq(gl_ts->client->irq); + } + LOGE("reserved command being sent to chip, this is probably bad!\n"); + } + i2cWriteNoReadyCheck(data.bufferIndex, buffer, data.length); + return 0; + + case IOCTL_GET: + LOGE("direct command RX from userspace is undoubtedly bad!\n"); + if (copy_from_user(&data, (void __user *)arg, sizeof(struct ioctl_cmd168))) + return -EFAULT; + if (data.length >= sizeof(buffer)) + return -EINVAL; /* stop trying to overflow kernel stack! */ + + if (!data.length) + data.buffer[0] = 128; + else { + i2cReadNoReadyCheck(data.bufferIndex, buffer, data.length); + for (i = 0; i < data.length; i++) + data.buffer[i] = buffer[i]; + } + + if (copy_to_user((int __user *)arg, &data, sizeof(struct ioctl_cmd168))) + return -EFAULT; + return 0; + + default: + return -ENOTTY; + } +} + +static int ite7260_open(struct inode *inode, struct file *filp) +{ + struct ite7260_perfile_data *dev; + int i; + + + dev = kmalloc(sizeof(struct ite7260_perfile_data), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + rwlock_init(&dev->lock); + for (i = 0; i < MAX_BUFFER_SIZE; i++) + dev->buffer[i] = 0xFF; + + filp->private_data = dev; + + return 0; +} + +static int ite7260_close(struct inode *inode, struct file *filp) +{ + struct ite7260_perfile_data *dev = filp->private_data; + + if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations ite7260_fops = { + .owner = THIS_MODULE, + .open = ite7260_open, + .release = ite7260_close, + .unlocked_ioctl = ite7260_ioctl, +}; + +static const struct i2c_device_id IT7260_ts_id[] = { + { DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, IT7260_ts_id); + +static const struct of_device_id IT7260_match_table[] = { + { .compatible = "ITE,IT7260_ts",}, + {}, +}; + +static struct notifier_block display_mode_notifier = { + .notifier_call = mode_notify_sys, +}; + +static int IT7260_ts_resume(struct i2c_client *i2cdev) +{ + isDeviceSuspend = false; + return 0; +} + +static int IT7260_ts_suspend(struct i2c_client *i2cdev, pm_message_t pmesg) +{ + isDeviceSuspend = true; + return 0; +} + +static struct i2c_driver IT7260_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_NAME, + .of_match_table = IT7260_match_table, + }, + .probe = IT7260_ts_probe, + .remove = IT7260_ts_remove, + .id_table = IT7260_ts_id, + .resume = IT7260_ts_resume, + .suspend = IT7260_ts_suspend, +}; + +static int __init IT7260_ts_init(void) +{ + dev_t dev; + + + if (alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME)) { + LOGE("cdev can't get major number\n"); + goto err_cdev_alloc; + } + ite7260_major = MAJOR(dev); + + cdev_init(&ite7260_cdev, &ite7260_fops); + ite7260_cdev.owner = THIS_MODULE; + + if (cdev_add(&ite7260_cdev, MKDEV(ite7260_major, ite7260_minor), 1)) { + LOGE("cdev can't get minor number\n"); + goto err_cdev_add; + } + + ite7260_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(ite7260_class)) { + LOGE("failed in creating class.\n"); + goto err_class_create; + } + + class_dev = device_create(ite7260_class, NULL, MKDEV(ite7260_major, ite7260_minor), NULL, DEVICE_NAME); + if (!class_dev) { + + LOGE("failed in creating device.\n"); + goto err_dev_create; + } + + if (device_create_file(class_dev, &device_attr) < 0) { + LOGE("failed in creating file.\n"); + goto err_file_create; + } + + register_mode_notifier(&display_mode_notifier); + + LOGI("=========================================\n"); + LOGI("register IT7260 cdev, major: %d, minor: %d \n", ite7260_major, ite7260_minor); + LOGI("=========================================\n"); + + if (!i2c_add_driver(&IT7260_ts_driver)) + return 0; + + unregister_mode_notifier(&display_mode_notifier); + device_remove_file(class_dev, &device_attr); + +err_file_create: + device_destroy(ite7260_class, ite7260_dev); + +err_dev_create: + class_destroy(ite7260_class); + +err_class_create: + cdev_del(&ite7260_cdev); + +err_cdev_add: + unregister_chrdev_region(dev, 1); + +err_cdev_alloc: + return -1; +} + +static void __exit IT7260_ts_exit(void) +{ + dev_t dev = MKDEV(ite7260_major, ite7260_minor); + + i2c_del_driver(&IT7260_ts_driver); + unregister_mode_notifier(&display_mode_notifier); + device_remove_file(class_dev, &device_attr); + device_destroy(ite7260_class, ite7260_dev); + class_destroy(ite7260_class); + cdev_del(&ite7260_cdev); + unregister_chrdev_region(dev, 1); + wake_lock_destroy(&touch_lock); + if (IT7260_wq) + destroy_workqueue(IT7260_wq); +} + +module_init(IT7260_ts_init); +module_exit(IT7260_ts_exit); + +MODULE_DESCRIPTION("IT7260 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +