From 8e4bff4cab0ddac6060645b0715210484d02ff40 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 30 Aug 2017 08:25:59 +0000 Subject: Initial file dump from open source release --- drivers/input/touchscreen/mms100s_ts.c | 1593 ++++++++++++++++++++++++++++++++ 1 file changed, 1593 insertions(+) create mode 100755 drivers/input/touchscreen/mms100s_ts.c (limited to 'drivers/input/touchscreen/mms100s_ts.c') diff --git a/drivers/input/touchscreen/mms100s_ts.c b/drivers/input/touchscreen/mms100s_ts.c new file mode 100755 index 00000000..cdf8ec25 --- /dev/null +++ b/drivers/input/touchscreen/mms100s_ts.c @@ -0,0 +1,1593 @@ +/* + * Touchscreen driver for Melfas MMS-100s series + * + * Copyright (C) 2013 Melfas Inc. + * Author: DVK team + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Flag to enable touch key */ +#define MMS_HAS_TOUCH_KEY 1 + +#define ESD_DETECT_COUNT 10 + +/* + * ISC_XFER_LEN - ISC unit transfer length. + * Give number of 2 power n, where n is between 2 and 10 + * i.e. 4, 8, 16 ,,, 1024 + */ +#define ISC_XFER_LEN 1024 + +#define MMS_FLASH_PAGE_SZ 1024 +#define ISC_BLOCK_NUM (MMS_FLASH_PAGE_SZ / ISC_XFER_LEN) + +#define FLASH_VERBOSE_DEBUG 1 +#define MAX_SECTION_NUM 3 + +#define MAX_FINGER_NUM 5 +#define FINGER_EVENT_SZ 6 +#define MAX_WIDTH 30 +#define MAX_PRESSURE 255 +#define MAX_LOG_LENGTH 128 + +/* Registers */ +#define MMS_MODE_CONTROL 0x01 +#define MMS_TX_NUM 0x0B +#define MMS_RX_NUM 0x0C +#define MMS_KEY_NUM 0x0D +#define MMS_EVENT_PKT_SZ 0x0F +#define MMS_INPUT_EVENT 0x10 +#define MMS_UNIVERSAL_CMD 0xA0 +#define MMS_UNIVERSAL_RESULT 0xAF + +#define MMS_CMD_ENTER_ISC 0x5F +#define MMS_FW_VERSION 0xE1 +#define MMS_POWER_CONTROL 0xB0 + +/* Universal commands */ +#define MMS_CMD_SET_LOG_MODE 0x20 + +/* Event types */ +#define MMS_LOG_EVENT 0xD +#define MMS_NOTIFY_EVENT 0xE +#define MMS_ERROR_EVENT 0xF +#define MMS_TOUCH_KEY_EVENT 0x40 + +/* Firmware file name */ +#define FW_NAME "mms_ts.fw" + +enum{ + SYS_TXNUM = 3, + SYS_RXNUM, + SYS_CLEAR, + SYS_ENABLE, + SYS_DISABLE, + SYS_INTERRUPT, + SYS_RESET, +}; + +enum { + GET_RX_NUM = 1, + GET_TX_NUM, + GET_EVENT_DATA, +}; + +enum { + LOG_TYPE_U08 = 2, + LOG_TYPE_S08, + LOG_TYPE_U16, + LOG_TYPE_S16, + LOG_TYPE_U32 = 8, + LOG_TYPE_S32, +}; + +enum { + ISC_ADDR = 0xD5, + + ISC_CMD_READ_STATUS = 0xD9, + ISC_CMD_READ = 0x4000, + ISC_CMD_EXIT = 0x8200, + ISC_CMD_PAGE_ERASE = 0xC000, + ISC_CMD_ERASE = 0xC100, + ISC_PAGE_ERASE_DONE = 0x10000, + ISC_PAGE_ERASE_ENTER = 0x20000, +}; + +struct mms_ts_info { + struct i2c_client *client; + struct input_dev *input_dev; + char phys[32]; + + u8 tx_num; + u8 rx_num; + u8 key_num; + int irq; + + int data_cmd; + + struct mms_ts_platform_data *pdata; + + char *fw_name; + struct completion init_done; + struct early_suspend early_suspend; + + struct mutex lock; + bool enabled; + + struct cdev cdev; + dev_t mms_dev; + struct class *class; + + struct mms_log_data { + u8 *data; + int cmd; + } log; +}; + +struct mms_bin_hdr { + char tag[8]; + u16 core_version; + u16 section_num; + u16 contains_full_binary; + u16 reserved0; + + u32 binary_offset; + u32 binary_length; + + u32 extention_offset; + u32 reserved1; + +} __attribute__ ((packed)); + +struct mms_fw_img { + u16 type; + u16 version; + + u16 start_page; + u16 end_page; + + u32 offset; + u32 length; + +} __attribute__ ((packed)); + +struct isc_packet { + u8 cmd; + u32 addr; + u8 data[0]; +} __attribute__ ((packed)); + + +static int esd_cnt; +static void mms_report_input_data(struct mms_ts_info *info, u8 sz, u8 *buf); +static void mms_ts_early_suspend(struct early_suspend *h); +static void mms_ts_late_resume(struct early_suspend *h); +static int mms_ts_config(struct mms_ts_info *info); + +/* mms_ts_enable - wake-up func (VDD on) */ +static void mms_ts_enable(struct mms_ts_info *info) +{ +pr_debug("%s enable tsp %d\n",__func__,__LINE__); + if (info->enabled) + return; + pr_debug("%s %d\n",__func__,__LINE__); + mutex_lock(&info->lock); + + /* + gpio_direction_output(info->pdata->gpio_resetb, 0); + udelay(5); + gpio_direction_input(info->pdata->gpio_resetb); + */ + + /* use vdd control instead */ + info->pdata->vdd_on(1); + msleep(50); + pr_debug("%s %d \n",__func__,__LINE__); + info->enabled = true; + pr_debug("%s %d \n",__func__,__LINE__); + enable_irq(info->irq); +pr_debug("%s %d \n",__func__,__LINE__); + mutex_unlock(&info->lock); + +} + +/* mms_ts_disable - sleep func (VDD off) */ +static void mms_ts_disable(struct mms_ts_info *info) +{ +pr_debug("%s disable tsp %d \n",__func__,__LINE__); + if (!info->enabled) + return; + + i2c_smbus_write_byte_data(info->client, MMS_POWER_CONTROL, 1); + + mutex_lock(&info->lock); + + disable_irq(info->irq); + + /* + i2c_smbus_write_byte_data(info->client, MMS_MODE_CONTROL, 0); + usleep_range(10000, 12000); + */ + + /* use vdd control instead */ + msleep(50); + info->pdata->vdd_on(0); + msleep(50); + + info->enabled = false; + + mutex_unlock(&info->lock); +} + +/* mms_reboot - IC reset */ +static void mms_reboot(struct mms_ts_info *info) +{ + struct i2c_adapter *adapter = to_i2c_adapter(info->client->dev.parent); +pr_debug("%s reboot 1 %d",__func__,__LINE__); + i2c_smbus_write_byte_data(info->client, MMS_POWER_CONTROL, 1); + i2c_lock_adapter(adapter); + msleep(50); + info->pdata->vdd_on(0); + msleep(150); + + info->pdata->vdd_on(1); + msleep(50); + + i2c_unlock_adapter(adapter); +} + +/* + * mms_clear_input_data - all finger point release + */ +static void mms_clear_input_data(struct mms_ts_info *info) +{ + int i; + + for (i = 0; i < MAX_FINGER_NUM; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, false); + } + input_sync(info->input_dev); + + return; +} + +/* + * mms_fs_open, mms_fs_release, mms_event_handler, mms_fs_read, mms_fs_write + * melfas debugging function + */ +static int mms_fs_open(struct inode *node, struct file *fp) +{ + struct mms_ts_info *info; + struct i2c_client *client; + struct i2c_msg msg; + u8 buf[3] = { + MMS_UNIVERSAL_CMD, + MMS_CMD_SET_LOG_MODE, + true, + }; + + info = container_of(node->i_cdev, struct mms_ts_info, cdev); + client = info->client; + + disable_irq(info->irq); + fp->private_data = info; + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + i2c_transfer(client->adapter, &msg, 1); + + info->log.data = kzalloc(MAX_LOG_LENGTH * 20 + 5, GFP_KERNEL); + + mms_clear_input_data(info); + + return 0; +} + +static int mms_fs_release(struct inode *node, struct file *fp) +{ + struct mms_ts_info *info = fp->private_data; + + mms_clear_input_data(info); + mms_reboot(info); + + kfree(info->log.data); + enable_irq(info->irq); + + return 0; +} + +static void mms_event_handler(struct mms_ts_info *info) +{ + struct i2c_client *client = info->client; + int sz; + int ret; + int row_num; + u8 reg = MMS_INPUT_EVENT; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®, + .len = 1, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = info->log.data, + }, + + }; + struct mms_log_pkt { + u8 marker; + u8 log_info; + u8 code; + u8 element_sz; + u8 row_sz; + } __attribute__ ((packed)) *pkt = (struct mms_log_pkt *)info->log.data; + + memset(pkt, 0, sizeof(*pkt)); + + if (gpio_get_value(info->pdata->gpio_resetb)) + return; + + sz = i2c_smbus_read_byte_data(client, MMS_EVENT_PKT_SZ); + msg[1].len = sz; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(&client->dev, + "failed to read %d bytes of data\n", + sz); + return; + } + + if ((pkt->marker & 0xf) == MMS_LOG_EVENT) { + if ((pkt->log_info & 0x7) == 0x1) { + pkt->element_sz = 0; + pkt->row_sz = 0; + + return; + } + + switch (pkt->log_info >> 4) { + case LOG_TYPE_U08: + case LOG_TYPE_S08: + msg[1].len = pkt->element_sz; + break; + case LOG_TYPE_U16: + case LOG_TYPE_S16: + msg[1].len = pkt->element_sz * 2; + break; + case LOG_TYPE_U32: + case LOG_TYPE_S32: + msg[1].len = pkt->element_sz * 4; + break; + default: + dev_err(&client->dev, "invalied log type\n"); + return; + } + + msg[1].buf = info->log.data + sizeof(struct mms_log_pkt); + reg = MMS_UNIVERSAL_RESULT; + row_num = pkt->row_sz ? pkt->row_sz : 1; + + while (row_num--) { + while (gpio_get_value(info->pdata->gpio_resetb)) + ; + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + msg[1].buf += msg[1].len; + }; + } else { + mms_report_input_data(info, sz, info->log.data); + memset(pkt, 0, sizeof(*pkt)); + } + + return; +} + +/* mms_report_input_data - The position of a touch send to platfrom */ +static void mms_report_input_data(struct mms_ts_info *info, u8 sz, u8 *buf) +{ + int i; + struct i2c_client *client = info->client; + int id; + int x; + int y; + int touch_major; + int pressure; + int key_code; + int key_state; + u8 *tmp; + + if (buf[0] == MMS_NOTIFY_EVENT) { + dev_info(&client->dev, "TSP mode changed (%d)\n", buf[1]); + goto out; + } else if (buf[0] == MMS_ERROR_EVENT) { + dev_info(&client->dev, "Error detected, restarting TSP\n"); + mms_clear_input_data(info); + mms_reboot(info); + esd_cnt++; + if (esd_cnt>= ESD_DETECT_COUNT) + { + i2c_smbus_write_byte_data(info->client, MMS_MODE_CONTROL, 0x04); + esd_cnt =0; + } + goto out; + } + + for (i = 0; i < sz; i += FINGER_EVENT_SZ) { + tmp = buf + i; + esd_cnt =0; + if (tmp[0] & MMS_TOUCH_KEY_EVENT) { + switch (tmp[0] & 0xf) { + case 1: + key_code = KEY_MENU; + break; + case 2: + key_code = KEY_BACK; + break; + default: + dev_err(&client->dev, "unknown key type\n"); + goto out; + break; + } + + key_state = (tmp[0] & 0x80) ? 1 : 0; + input_report_key(info->input_dev, key_code, key_state); + + } else { + id = (tmp[0] & 0xf) -1; + + x = tmp[2] | ((tmp[1] & 0xf) << 8); + y = tmp[3] | (((tmp[1] >> 4 ) & 0xf) << 8); + touch_major = tmp[4]; + pressure = tmp[5]; + + input_mt_slot(info->input_dev, id); + + if (!(tmp[0] & 0x80)) { + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, false); + continue; + } + + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, true); + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(info->input_dev, ABS_MT_PRESSURE, pressure); +// dev_notice(&client->dev,"P [%2d],([%3d],[%4d]) , major=%d, pressure=%d",id, x, y, tmp[4], tmp[5]); + } + } + + input_sync(info->input_dev); + +out: + return; + +} + +static ssize_t mms_fs_read(struct file *fp, char *rbuf, size_t cnt, loff_t *fpos) +{ + struct mms_ts_info *info = fp->private_data; + struct i2c_client *client = info->client; + int ret = 0; + + switch (info->log.cmd) { + case GET_RX_NUM: + ret = copy_to_user(rbuf, &info->rx_num, 1); + break; + case GET_TX_NUM: + ret = copy_to_user(rbuf, &info->tx_num, 1); + break; + case GET_EVENT_DATA: + mms_event_handler(info); + /* copy data without log marker */ + ret = copy_to_user(rbuf, info->log.data + 1, cnt); + break; + default: + dev_err(&client->dev, "unknown command\n"); + ret = -EFAULT; + break; + } + + return ret; +} + +static ssize_t mms_fs_write(struct file *fp, const char *wbuf, size_t cnt, loff_t *fpos) +{ + struct mms_ts_info *info = fp->private_data; + struct i2c_client *client = info->client; + u8 *buf; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = cnt, + }; + int ret = 0; + + mutex_lock(&info->lock); + + if (!info->enabled) + goto tsp_disabled; + + msg.buf = buf = kzalloc(cnt + 1, GFP_KERNEL); + + if ((buf == NULL) || copy_from_user(buf, wbuf, cnt)) { + dev_err(&client->dev, "failed to read data from user\n"); + ret = -EIO; + goto out; + } + + if (cnt == 1) { + info->log.cmd = *buf; + } else { + if (i2c_transfer(client->adapter, &msg, 1) != 1) { + dev_err(&client->dev, "failed to transfer data\n"); + ret = -EIO; + goto out; + } + } + + ret = 0; + +out: + kfree(buf); +tsp_disabled: + mutex_unlock(&info->lock); + + return ret; +} +/* + * mms_isc_read_status - Check erase state function + */ +static int mms_isc_read_status(struct mms_ts_info *info, u32 val) +{ + struct i2c_client *client = info->client; + u8 cmd = ISC_CMD_READ_STATUS; + u32 result = 0; + int cnt = 100; + int ret = 0; + + do { + i2c_smbus_read_i2c_block_data(client, cmd, 4, (u8 *)&result); + if (result == val) + break; + msleep(1); + } while (--cnt); + + if (!cnt) { + dev_err(&client->dev, + "status read fail. cnt : %d, val : 0x%x != 0x%x\n", + cnt, result, val); + } + return ret; +} + +static int mms_isc_transfer_cmd(struct mms_ts_info *info, int cmd) +{ + struct i2c_client *client = info->client; + struct isc_packet pkt = { ISC_ADDR, cmd }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(struct isc_packet), + .buf = (u8 *)&pkt, + }; + + return (i2c_transfer(client->adapter, &msg, 1) != 1); +} + +/* + * mms_isc_erase_page - ic page erase(1 kbyte) + */ +static int mms_isc_erase_page(struct mms_ts_info *info, int page) +{ + return mms_isc_transfer_cmd(info, ISC_CMD_PAGE_ERASE | page) || + mms_isc_read_status(info, ISC_PAGE_ERASE_DONE | ISC_PAGE_ERASE_ENTER | page); +} + +/* + * mms_isc_enter - isc enter command + */ +static int mms_isc_enter(struct mms_ts_info *info) +{ + return i2c_smbus_write_byte_data(info->client, MMS_CMD_ENTER_ISC, true); +} + +static int mms_isc_exit(struct mms_ts_info *info) +{ + return mms_isc_transfer_cmd(info, ISC_CMD_EXIT); +} + +/* + * mms_flash_section - f/w section(boot,core,config) download func + */ +static int mms_flash_section(struct mms_ts_info *info, struct mms_fw_img *img, const u8 *data) +{ + struct i2c_client *client = info->client; + struct isc_packet *isc_packet; + int ret; + int page, i; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = ISC_XFER_LEN, + }, + }; + int ptr = img->offset; + + isc_packet = kzalloc(sizeof(*isc_packet) + ISC_XFER_LEN, GFP_KERNEL); + isc_packet->cmd = ISC_ADDR; + + msg[0].buf = (u8 *)isc_packet; + msg[1].buf = kzalloc(ISC_XFER_LEN, GFP_KERNEL); + + for (page = img->start_page; page <= img->end_page; page++) { + mms_isc_erase_page(info, page); + + for (i = 0; i < ISC_BLOCK_NUM; i++, ptr += ISC_XFER_LEN) { + /* flash firmware */ + u32 tmp = page * 256 + i * (ISC_XFER_LEN / 4); + put_unaligned_le32(tmp, &isc_packet->addr); + msg[0].len = sizeof(struct isc_packet) + ISC_XFER_LEN; + + memcpy(isc_packet->data, data + ptr, ISC_XFER_LEN); + if (i2c_transfer(client->adapter, msg, 1) != 1) + goto i2c_err; + + /* verify firmware */ + tmp |= ISC_CMD_READ; + put_unaligned_le32(tmp, &isc_packet->addr); + msg[0].len = sizeof(struct isc_packet); + + if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) != ARRAY_SIZE(msg)) + goto i2c_err; + + if (memcmp(isc_packet->data, msg[1].buf, ISC_XFER_LEN)) { +#if FLASH_VERBOSE_DEBUG + print_hex_dump(KERN_ERR, "mms fw wr : ", + DUMP_PREFIX_OFFSET, 16, 1, + isc_packet->data, ISC_XFER_LEN, false); + + print_hex_dump(KERN_ERR, "mms fw rd : ", + DUMP_PREFIX_OFFSET, 16, 1, + msg[1].buf, ISC_XFER_LEN, false); +#endif + dev_err(&client->dev, "flash verify failed\n"); + ret = -1; + goto out; + } + + } + } + + dev_info(&client->dev, "section [%d] update succeeded\n", img->type); + + ret = 0; + goto out; + +i2c_err: + dev_err(&client->dev, "i2c failed @ %s\n", __func__); + ret = -1; + +out: + kfree(isc_packet); + kfree(msg[1].buf); + + return ret; +} + +/* + * get_fw_version - f/w version read + */ +static int get_fw_version(struct i2c_client *client, u8 *buf) +{ + u8 cmd = MMS_FW_VERSION; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .buf = &cmd, + .len = 1, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = MAX_SECTION_NUM, + }, + }; + + return (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) != ARRAY_SIZE(msg)); +} + +/* + * mms_flash_fw -flash_section of parent function + * this function is IC version binary version compare and Download + */ +static int mms_flash_fw(const struct firmware *fw, struct mms_ts_info *info) +{ + int ret; + int i; + struct mms_bin_hdr *fw_hdr; + struct mms_fw_img **img; + struct i2c_client *client = info->client; + u8 ver[MAX_SECTION_NUM]; + u8 target[MAX_SECTION_NUM]; + int offset = sizeof(struct mms_bin_hdr); + int retires = 3; + bool update_flag = false; + bool isc_flag = true; + + fw_hdr = (struct mms_bin_hdr *)fw->data; + img = kzalloc(sizeof(*img) * fw_hdr->section_num, GFP_KERNEL); + + while (retires--) { + if (!get_fw_version(client, ver)) + break; + else + mms_reboot(info); + } + + if (retires < 0) { + dev_warn(&client->dev, "failed to obtain ver. info\n"); + isc_flag = false; + memset(ver, 0xff, sizeof(ver)); + } else { + print_hex_dump(KERN_INFO, "mms_ts fw ver : ", DUMP_PREFIX_NONE, 16, 1, + ver, MAX_SECTION_NUM, false); + } + + for (i = 0; i < fw_hdr->section_num; i++, offset += sizeof(struct mms_fw_img)) { + img[i] = (struct mms_fw_img *)(fw->data + offset); + target[i] = img[i]->version; + + if (ver[img[i]->type] != target[i]) { + if(isc_flag){ + mms_isc_enter(info); + isc_flag = false; + } + update_flag = true; + dev_info(&client->dev, + "section [%d] is need to be updated. ver : 0x%x, bin : 0x%x\n", + img[i]->type, ver[img[i]->type], target[i]); + + if ((ret = mms_flash_section(info, img[i], fw->data + fw_hdr->binary_offset))) { + mms_reboot(info); + goto out; + } + //memset(ver, 0xff, sizeof(ver)); + } else { + dev_info(&client->dev, "section [%d] is up to date\n", i); + } + } + + if (update_flag){ + mms_isc_exit(info); + msleep(5); + mms_reboot(info); + } + + + + if (get_fw_version(client, ver)) { + dev_err(&client->dev, "failed to obtain version after flash\n"); + ret = -1; + goto out; + } else { + for (i = 0; i < fw_hdr->section_num; i++) { + if (ver[img[i]->type] != target[i]) { + dev_info(&client->dev, + "version mismatch after flash. [%d] 0x%x != 0x%x\n", + i, ver[img[i]->type], target[i]); + + ret = -1; + goto out; + } + } + } + + ret = 0; + +out: + kfree(img); + + return ret; +} + +static void mms_fw_update_controller(const struct firmware *fw, void * context) +{ + struct mms_ts_info *info = context; + int retires = 3; + int ret; + + if (!fw) { + dev_err(&info->client->dev, "failed to read firmware\n"); + complete_all(&info->init_done); + return; + } + + do { + ret = mms_flash_fw(fw, info); + } while (ret && --retires); + + if (!retires) { + dev_err(&info->client->dev, "failed to flash firmware after retires\n"); + } + mms_ts_config(info); + release_firmware(fw); +} + +/* mms_ts_interrupt - interrupt thread */ +static irqreturn_t mms_ts_interrupt(int irq, void *dev_id) +{ +pr_debug("%s interrupt call %d \n",__func__,__LINE__); + struct mms_ts_info *info = dev_id; + struct i2c_client *client = info->client; + u8 buf[MAX_FINGER_NUM * FINGER_EVENT_SZ] = { 0, }; + int ret; + int sz; + u8 reg = MMS_INPUT_EVENT; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®, + .len = 1, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = buf, + }, + }; + + sz = i2c_smbus_read_byte_data(client, MMS_EVENT_PKT_SZ); + msg[1].len = sz; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + if (ret != ARRAY_SIZE(msg)) { + dev_err(&client->dev, + "failed to read %d bytes of touch data (%d)\n", + sz, ret); + } else { + pr_debug("%s report event %d \n",__func__,__LINE__); + mms_report_input_data(info, sz, buf); + } + + return IRQ_HANDLED; +} + +/* + * mms_ts_input_open - Register input device after call this function + * this function is wait firmware flash wait + */ +static int mms_ts_input_open(struct input_dev *dev) +{ +pr_debug("%s enter1 %d",__func__,__LINE__); + struct mms_ts_info *info = input_get_drvdata(dev); + int ret; +pr_debug("%s enterw %d",__func__,__LINE__); + ret = wait_for_completion_interruptible_timeout(&info->init_done, + msecs_to_jiffies(90 * MSEC_PER_SEC)); + + if (ret > 0) { + if (info->irq != -1) { + pr_debug("%s %d",__func__,__LINE__); + mms_ts_enable(info); + ret = 0; + } else { + ret = -ENXIO; + } + } else { + dev_err(&dev->dev, "error while waiting for device to init\n"); + ret = -ENXIO; + } + + return ret; +} + +/* + * mms_ts_input_close -If device power off state call this function + */ +static void mms_ts_input_close(struct input_dev *dev) +{ + struct mms_ts_info *info = input_get_drvdata(dev); + + mms_ts_disable(info); +} + +/* + * mms_ts_config - f/w check download & irq thread register + */ +static int mms_ts_config(struct mms_ts_info *info) +{ +pr_debug("%s config 1 %d \n",__func__,__LINE__); + struct i2c_client *client = info->client; + int ret; + pr_debug("%s config 2 %d \n",__func__,__LINE__); + ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "mms_ts", info); +pr_debug("%s irq registered %d\n",__func__,__LINE__); + if (ret) { + dev_err(&client->dev, "failed to register irq\n"); + goto out; + } + disable_irq(client->irq); + info->irq = client->irq; + barrier(); + + info->tx_num = i2c_smbus_read_byte_data(client, MMS_TX_NUM); + info->rx_num = i2c_smbus_read_byte_data(client, MMS_RX_NUM); + info->key_num = i2c_smbus_read_byte_data(client, MMS_KEY_NUM); + + dev_info(&client->dev, "Melfas touch controller initialized\n"); + pr_debug("%s reboot called %d\n",__func__,__LINE__); + mms_reboot(info); + complete_all(&info->init_done); + pr_debug("%s init done %d\n",__func__,__LINE__); + +out: + return ret; +} + +/* + * bin_report_read, bin_report_write, bin_sysfs_read, bin_sysfs_write + * melfas debugging function + */ +static ssize_t bin_report_read(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + count = 0; + switch(info->data_cmd){ + case SYS_TXNUM: + dev_info(&info->client->dev, "tx send %d \n",info->tx_num); + buf[0]=info->tx_num; + count =1; + info->data_cmd = 0; + break; + case SYS_RXNUM: + dev_info(&info->client->dev, "rx send%d\n", info->rx_num); + buf[0]=info->rx_num; + count =1; + info->data_cmd = 0; + break; + case SYS_CLEAR: + dev_info(&info->client->dev, "Input clear\n"); + mms_clear_input_data(info); + count = 1; + info->data_cmd = 0; + break; + case SYS_ENABLE: + dev_info(&info->client->dev, "enable_irq \n"); + enable_irq(info->irq); + count = 1; + info->data_cmd = 0; + break; + case SYS_DISABLE: + dev_info(&info->client->dev, "disable_irq \n"); + disable_irq(info->irq); + count = 1; + info->data_cmd = 0; + break; + case SYS_INTERRUPT: + count = gpio_get_value(info->pdata->gpio_resetb); + info->data_cmd = 0; + break; + case SYS_RESET: + mms_reboot(info); + dev_info(&info->client->dev, "read mms_reboot\n"); + count = 1; + info->data_cmd = 0; + break; + } + return count; +} + +static ssize_t bin_report_write(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + if(buf[0]==100){ + mms_report_input_data(info, buf[1], &buf[2]); + }else{ + info->data_cmd=(int)buf[0]; + } + return count; +} +static struct bin_attribute bin_attr_data = { + .attr = { + .name = "report_data", + .mode = S_IRWXUGO, +}, + .size = PAGE_SIZE, + .read = bin_report_read, + .write = bin_report_write, +}; + +static ssize_t bin_sysfs_read(struct file *fp, struct kobject *kobj , struct bin_attribute *attr, + char *buf, loff_t off,size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + struct i2c_msg msg; + info->client = client; + msg.addr = client->addr; + msg.flags = I2C_M_RD ; + msg.buf = (u8 *)buf; + msg.len = count; + + switch (count) + { + case 65535: + mms_reboot(info); + dev_info(&client->dev, "read mms_reboot\n"); + return 0; + + default : + if(i2c_transfer(client->adapter, &msg, 1) != 1){ + dev_err(&client->dev, "failed to transfer data\n"); + mms_reboot(info); + return 0; + } + break; + + } + + return count; +} + +static ssize_t bin_sysfs_write(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + struct i2c_msg msg; + + msg.addr =client->addr; + msg.flags = 0; + msg.buf = (u8 *)buf; + msg.len = count; + + + if(i2c_transfer(client->adapter, &msg, 1) != 1){ + dev_err(&client->dev, "failed to transfer data\n"); + mms_reboot(info); + return 0; + } + + return count; +} + +static struct bin_attribute bin_attr = { + .attr = { + .name = "mms_bin", + .mode = S_IRWXUGO, + }, + .size = MMS_FLASH_PAGE_SZ, + .read = bin_sysfs_read, + .write = bin_sysfs_write, +}; + +static struct file_operations mms_fops = { + .owner = THIS_MODULE, + .open = mms_fs_open, + .release = mms_fs_release, + .read = mms_fs_read, + .write = mms_fs_write, +}; + +//melfas test mode +#ifdef __MMS_TEST_MODE__ +struct mms_ts_test test; +static ssize_t mms_intensity(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mms_ts_info *info = dev_get_drvdata(dev); + int ret; + dev_info(&info->client->dev, "Intensity Test\n"); + if(get_intensity(&test)!=0){ + dev_err(&info->client->dev, "Intensity Test failed\n"); + return -1; + } + ret = snprintf(buf,PAGE_SIZE,"%s\n",test.get_data); + return ret; +} + +static DEVICE_ATTR(intensity, 0666, mms_intensity, NULL); + +static ssize_t mms_cmdelta(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mms_ts_info *info = dev_get_drvdata(dev); + int ret; + dev_info(&info->client->dev, "cm delta Test\n"); + get_cm_test_init(&test); + get_cm_delta(&test); + get_cm_test_exit(&test); + + ret = snprintf(buf,PAGE_SIZE,"%s\n",test.get_data); + memset(test.get_data,0,4096); + return ret; +} +static DEVICE_ATTR(cmdelta, 0666, mms_cmdelta, NULL); + +static ssize_t mms_cmabs(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mms_ts_info *info = dev_get_drvdata(dev); + int ret; + dev_info(&info->client->dev, "cm delta Test\n"); + get_cm_test_init(&test); + get_cm_abs(&test); + get_cm_test_exit(&test); + + + ret = snprintf(buf,PAGE_SIZE,"%s\n",test.get_data); + memset(test.get_data,0,4096); + return ret; +} +static DEVICE_ATTR(cmabs, 0666, mms_cmabs, NULL); + +static ssize_t mms_cmjitter(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mms_ts_info *info = dev_get_drvdata(dev); + int ret; + dev_info(&info->client->dev, "cm delta Test\n"); + get_cm_test_init(&test); + get_cm_jitter(&test); + get_cm_test_exit(&test); + + ret = snprintf(buf,PAGE_SIZE,"%s\n",test.get_data); + memset(test.get_data,0,4096); + return ret; +} +static DEVICE_ATTR(cmjitter, 0666, mms_cmjitter, NULL); + +static struct attribute *mms_attrs[] = { + &dev_attr_intensity.attr, + &dev_attr_cmdelta.attr, + &dev_attr_cmabs.attr, + &dev_attr_cmjitter.attr, + NULL, +}; + +static const struct attribute_group mms_attr_group = { + .attrs = mms_attrs, +}; +#endif + +#ifdef CONFIG_OF +static const u8 mms_ts_keycode[] = {KEY_MENU, KEY_BACK}; +static unsigned int gpio_touchkey_len_en; +static const char * vdd_name; + +static void mms_ts_vdd_enable(bool on) +{ + static struct regulator *ts_vdd = NULL; + + if (ts_vdd == NULL) { + ts_vdd = regulator_get(NULL, vdd_name); + + if (IS_ERR(ts_vdd)) { + pr_err("Get regulator of TSP error!\n"); + return; + } + } + if (on) { + regulator_set_voltage(ts_vdd, 3000000, 3000000); + regulator_enable(ts_vdd); + } + else if (regulator_is_enabled(ts_vdd)) { + regulator_disable(ts_vdd); + } +} + +static void touchkey_led_vdd_enable(bool on) +{ + static int ret = 0; + + if (ret == 0) { + ret = gpio_request(gpio_touchkey_len_en, "touchkey_led_en"); + if (ret) { + printk("%s: request gpio error\n", __func__); + return -EIO; + } + gpio_direction_output(gpio_touchkey_len_en, 0); + ret = 1; + } + gpio_set_value(gpio_touchkey_len_en,on); +} + +static struct mms_ts_platform_data * mms_ts_parse_dt(struct device *dev) +{ + struct mms_ts_platform_data *pdata; + struct device_node *np = dev->of_node; + int ret; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "Could not allocate struct mms_ts_platform_data"); + return NULL; + } + pdata->gpio_sda = of_get_gpio(np, 0); + if(pdata->gpio_sda < 0){ + dev_err(dev, "fail to get gpio_sda\n"); + goto fail; + } + pdata->gpio_scl = of_get_gpio(np, 1); + if(pdata->gpio_scl < 0){ + dev_err(dev, "fail to get gpio_scl\n"); + goto fail; + } + pdata->gpio_int = of_get_gpio(np, 2); + if(pdata->gpio_int < 0){ + dev_err(dev, "fail to get gpio_int\n"); + goto fail; + } + gpio_touchkey_len_en = of_get_gpio(np, 3); + if(gpio_touchkey_len_en < 0){ + dev_err(dev, "fail to gpio_touchkey_len_en\n"); + goto fail; + } + ret = of_property_read_string(np, "vdd_name", &vdd_name); + if(ret){ + dev_err(dev, "fail to get vdd_name\n"); + goto fail; + } + ret = of_property_read_u32(np, "max_x", &pdata->max_x); + if(ret){ + dev_err(dev, "fail to get max_x\n"); + goto fail; + } + ret = of_property_read_u32(np, "max_y", &pdata->max_y); + if(ret){ + dev_err(dev, "fail to get max_y\n"); + goto fail; + } + ret = of_property_read_u32(np, "use_touchkey", &pdata->use_touchkey); + if(ret){ + dev_err(dev, "fail to get use_touchkey\n"); + goto fail; + } + + pdata->vdd_on = mms_ts_vdd_enable; + pdata->tkey_led_vdd_on = touchkey_led_vdd_enable; + pdata->touchkey_keycode = mms_ts_keycode; + return pdata; +fail: + kfree(pdata); + return NULL; +} +#endif + + +static int __init mms_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct mms_ts_info *info; + struct input_dev *input_dev; + int ret = 0; + const char *fw_name = FW_NAME; + struct mms_ts_platform_data * pdata; + + printk("probe enter \n"); + +#ifdef CONFIG_OF + struct device_node *np = client->dev.of_node; + if (np && !client->dev.platform_data) { + pdata = mms_ts_parse_dt(&client->dev); + if(pdata) { + client->dev.platform_data = pdata; + } else { + return -ENODEV; + } + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + info = kzalloc(sizeof(struct mms_ts_info), GFP_KERNEL); + + + input_dev = input_allocate_device(); + + if (!input_dev) { + dev_err(&client->dev, "Failed to allocated memory\n"); + return -ENOMEM; + } + + info->client = client; + info->input_dev = input_dev; + info->pdata = client->dev.platform_data; + init_completion(&info->init_done); + info->irq = -1; +pr_debug("probe mutex init \n"); + mutex_init(&info->lock); + + client->irq = gpio_to_irq(info->pdata->gpio_int); + input_mt_init_slots(input_dev, MAX_FINGER_NUM,0); + + snprintf(info->phys, sizeof(info->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = "Melfas MMS100s Touch Screen"; + input_dev->phys = info->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + +pr_debug("probe dev init \n"); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_WIDTH, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, info->pdata->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, info->pdata->max_y, 0, 0); + + input_set_drvdata(input_dev, info); + +#if MMS_HAS_TOUCH_KEY + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_MENU, input_dev->keybit); + __set_bit(KEY_BACK, input_dev->keybit); +#endif + + ret = input_register_device(input_dev); + if (ret) { + dev_err(&client->dev, "failed to register input dev\n"); + return -EIO; + } + + i2c_set_clientdata(client, info); + + //mms_reboot(info); + mms_ts_enable(info); + info->enabled = true; + ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT," mms_ts", info); + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + } + info->irq = client->irq; + +pr_debug("%s enable FW %d\n",__func__,__LINE__); + info->fw_name = kstrdup(fw_name, GFP_KERNEL); +pr_debug("%s enable tsp %d\n",__func__,__LINE__); + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, &client->dev, + GFP_KERNEL, info, mms_fw_update_controller); + if (ret) { + dev_err(&client->dev, "failed to schedule firmware update\n"); + return -EIO; + } + + esd_cnt = 0; +#ifdef CONFIG_HAS_EARLYSUSPEND + info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + info->early_suspend.suspend = mms_ts_early_suspend; + info->early_suspend.resume = mms_ts_late_resume; + register_early_suspend(&info->early_suspend); +#endif +pr_debug("%s ALLOCH %d\n",__func__,__LINE__); + if (alloc_chrdev_region(&info->mms_dev, 0, 1, "mms_ts")) { + dev_err(&client->dev, "failed to allocate device region\n"); + return -ENOMEM; + } +pr_debug("%s FOPS %d\n",__func__,__LINE__); + cdev_init(&info->cdev, &mms_fops); + info->cdev.owner = THIS_MODULE; + + if (cdev_add(&info->cdev, info->mms_dev, 1)) { + dev_err(&client->dev, "failed to add ch dev\n"); + return -EIO; + } +pr_debug("%s CLASS CREATE %d\n",__func__,__LINE__); + info->class = class_create(THIS_MODULE, "mms_ts"); + device_create(info->class, NULL, info->mms_dev, NULL, "mms_ts"); + + if(sysfs_create_bin_file(&client->dev.kobj ,&bin_attr)){ + dev_err(&client->dev, "failed to create sysfs symlink\n"); + return -EAGAIN; + } + + if(sysfs_create_bin_file(&client->dev.kobj ,&bin_attr_data)){ + dev_err(&client->dev, "failed to create sysfs symlink\n"); + return -EAGAIN; + } + + if (sysfs_create_link(NULL, &client->dev.kobj, "mms_ts")) { + dev_err(&client->dev, "failed to create sysfs symlink\n"); + return -EAGAIN; + } +#ifdef __MMS_TEST_MODE__ + if (sysfs_create_group(&client->dev.kobj, &mms_attr_group)) { + dev_err(&client->dev, "failed to create sysfs group\n"); + return -EAGAIN; + } + test.get_data = kzalloc(4096, GFP_KERNEL); + test.client = client; + test.pdata = info->pdata; + test.tx_num = info->tx_num; + test.rx_num = info->rx_num; + test.key_num = info->key_num; + test.irq = info->irq; +#endif + dev_notice(&client->dev, "mms dev initialized\n"); + + return 0; +} + +static int __exit mms_ts_remove(struct i2c_client *client) +{ + struct mms_ts_info *info = i2c_get_clientdata(client); + + if (info->irq >= 0) + free_irq(info->irq, info); + +#ifdef __MMS_TEST_MODE__ + sysfs_remove_group(&info->client->dev.kobj, &mms_attr_group); + kfree(test.get_data); +#endif + sysfs_remove_link(NULL, "mms_ts"); + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr); + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_data); + input_unregister_device(info->input_dev); + unregister_early_suspend(&info->early_suspend); + + device_destroy(info->class, info->mms_dev); + class_destroy(info->class); +#ifdef CONFIG_OF + kfree(info->pdata); +#endif + kfree(info->fw_name); + kfree(info); + + return 0; +} + +#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND) +static int mms_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) { + mms_ts_disable(info); + mms_clear_input_data(info); + } + + mutex_unlock(&info->input_dev->mutex); + return 0; + +} + +static int mms_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mms_ts_info *info = i2c_get_clientdata(client); + + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) + mms_ts_enable(info); + + mutex_unlock(&info->input_dev->mutex); + + return 0; +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mms_ts_early_suspend(struct early_suspend *h) +{ + struct mms_ts_info *info; + info = container_of(h, struct mms_ts_info, early_suspend); + mms_ts_suspend(&info->client->dev); +} + +static void mms_ts_late_resume(struct early_suspend *h) +{ + struct mms_ts_info *info; + info = container_of(h, struct mms_ts_info, early_suspend); + mms_ts_resume(&info->client->dev); +} +#endif + +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) +static const struct dev_pm_ops mms_ts_pm_ops = { + .suspend = mms_ts_suspend, + .resume = mms_ts_resume, +} +#endif + +static const struct i2c_device_id mms_ts_id[] = { + {"mms_ts", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, mms_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id mms_ts_of_match[] = { + { .compatible = "Melfas,mms_ts", }, + { } +}; +#endif + +static struct i2c_driver mms_ts_driver = { + .probe = mms_ts_probe, + .remove = mms_ts_remove, + .driver = { + .name = "mms_ts", +#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) + .pm = &mms_ts_pm_ops, +#endif +#ifdef CONFIG_OF + .of_match_table = mms_ts_of_match, +#endif + }, + .id_table = mms_ts_id, +}; + +static int __init mms_ts_init(void) +{ + return i2c_add_driver(&mms_ts_driver); +} + +static void __exit mms_ts_exit(void) +{ + return i2c_del_driver(&mms_ts_driver); +} + +module_init(mms_ts_init); +module_exit(mms_ts_exit); + +MODULE_VERSION("1.6.1"); +MODULE_DESCRIPTION("Touchscreen driver for MELFAS MMS-100s series"); +MODULE_LICENSE("GPL"); + -- cgit v1.3.1