blob: 8e8d9ec6b15cc3c9947d092ee069473371c3afb6 [file] [log] [blame]
/*
* MStar MSG21XX touchscreen driver
*
* Copyright (c) 2006-2012 MStar Semiconductor, Inc.
*
* Copyright (C) 2012 Bruce Ding <bruce.ding@mstarsemi.com>
*
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
#include <linux/regulator/consumer.h>
#if defined(CONFIG_FB)
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
#include <linux/input/vir_ps.h>
#endif
/* Constant Value & Variable Definition*/
#define MSTAR_VTG_MIN_UV 2800000
#define MSTAR_VTG_MAX_UV 3300000
#define MSTAR_I2C_VTG_MIN_UV 1800000
#define MSTAR_I2C_VTG_MAX_UV 1800000
#define MAX_BUTTONS 4
#define FT_COORDS_ARR_SIZE 4
#define MSTAR_FW_NAME_MAX_LEN 50
#define MSTAR_CHIPTOP_REGISTER_BANK 0x1E
#define MSTAR_CHIPTOP_REGISTER_ICTYPE 0xCC
#define MSTAR_INIT_SW_ID 0x7FF
#define MSTAR_DEBUG_DIR_NAME "ts_debug"
#define MSG_FW_FILE_MAJOR_VERSION(x) \
(((x)->data[0x7f4f] << 8) + ((x)->data[0x7f4e]))
#define MSG_FW_FILE_MINOR_VERSION(x) \
(((x)->data[0x7f51] << 8) + ((x)->data[0x7f50]))
/*
* Note.
* Please do not change the below setting.
*/
#define TPD_WIDTH (2048)
#define TPD_HEIGHT (2048)
#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
#define PINCTRL_STATE_RELEASE "pmx_ts_release"
#define SLAVE_I2C_ID_DBBUS (0xC4>>1)
#define DEMO_MODE_PACKET_LENGTH (8)
#define TP_PRINT
/*store the frimware binary data*/
static unsigned char fw_bin_data[94][1024];
static unsigned int crc32_table[256];
static unsigned short fw_file_major, fw_file_minor;
static unsigned short main_sw_id = MSTAR_INIT_SW_ID;
static unsigned short info_sw_id = MSTAR_INIT_SW_ID;
static unsigned int bin_conf_crc32;
struct msg21xx_ts_platform_data {
const char *name;
char fw_name[MSTAR_FW_NAME_MAX_LEN];
u8 fw_version_major;
u8 fw_version_minor;
u32 irq_gpio;
u32 irq_gpio_flags;
u32 reset_gpio;
u32 reset_gpio_flags;
u32 x_max;
u32 y_max;
u32 x_min;
u32 y_min;
u32 panel_minx;
u32 panel_miny;
u32 panel_maxx;
u32 panel_maxy;
u32 num_max_touches;
u8 ic_type;
u32 button_map[MAX_BUTTONS];
u32 num_buttons;
u32 hard_reset_delay_ms;
u32 post_hard_reset_delay_ms;
bool updating_fw;
};
/* Touch Data Type Definition */
struct touchPoint_t {
unsigned short x;
unsigned short y;
};
struct touchInfo_t {
struct touchPoint_t *point;
unsigned char count;
unsigned char keycode;
};
struct msg21xx_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct msg21xx_ts_platform_data *pdata;
struct regulator *vdd;
struct regulator *vcc_i2c;
bool suspended;
#if defined(CONFIG_FB)
struct notifier_block fb_notif;
#endif
struct pinctrl *ts_pinctrl;
struct pinctrl_state *pinctrl_state_active;
struct pinctrl_state *pinctrl_state_suspend;
struct pinctrl_state *pinctrl_state_release;
struct mutex ts_mutex;
struct touchInfo_t info;
};
#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
#endif
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
static unsigned char bEnableTpProximity;
static unsigned char bFaceClosingTp;
#endif
#ifdef TP_PRINT
static int tp_print_proc_read(struct msg21xx_ts_data *ts_data);
static void tp_print_create_entry(struct msg21xx_ts_data *ts_data);
#endif
static void _ReadBinConfig(struct msg21xx_ts_data *ts_data);
static unsigned int _CalMainCRC32(struct msg21xx_ts_data *ts_data);
static struct mutex msg21xx_mutex;
enum EMEM_TYPE_t {
EMEM_ALL = 0,
EMEM_MAIN,
EMEM_INFO,
};
/* Function Definition */
static unsigned int _CRC_doReflect(unsigned int ref, signed char ch)
{
unsigned int value = 0;
unsigned int i = 0;
for (i = 1; i < (ch + 1); i++) {
if (ref & 1)
value |= 1 << (ch - i);
ref >>= 1;
}
return value;
}
static unsigned int _CRC_getValue(unsigned int text, unsigned int prevCRC)
{
unsigned int ulCRC = prevCRC;
ulCRC = (ulCRC >> 8) ^ crc32_table[(ulCRC & 0xFF) ^ text];
return ulCRC;
}
static void _CRC_initTable(void)
{
unsigned int magic_number = 0x04c11db7;
unsigned int i, j;
for (i = 0; i <= 0xFF; i++) {
crc32_table[i] = _CRC_doReflect(i, 8) << 24;
for (j = 0; j < 8; j++)
crc32_table[i] = (crc32_table[i] << 1) ^
(crc32_table[i] & (0x80000000L) ?
magic_number : 0);
crc32_table[i] = _CRC_doReflect(crc32_table[i], 32);
}
}
static void msg21xx_reset_hw(struct msg21xx_ts_platform_data *pdata)
{
unsigned int delay;
gpio_direction_output(pdata->reset_gpio, 1);
gpio_set_value_cansleep(pdata->reset_gpio, 0);
/* Note that the RST must be in LOW 10ms at least */
delay = pdata->hard_reset_delay_ms * 1000;
usleep_range(delay, delay + 1);
gpio_set_value_cansleep(pdata->reset_gpio, 1);
/* Enable the interrupt service thread/routine for INT after 50ms */
delay = pdata->post_hard_reset_delay_ms * 1000;
usleep_range(delay, delay + 1);
}
static int read_i2c_seq(struct msg21xx_ts_data *ts_data, unsigned char addr,
unsigned char *buf, unsigned short size)
{
int rc = 0;
struct i2c_msg msgs[] = {
{
.addr = addr,
.flags = I2C_M_RD, /* read flag */
.len = size,
.buf = buf,
},
};
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
if (ts_data->client != NULL) {
rc = i2c_transfer(ts_data->client->adapter, msgs, 1);
if (rc < 0)
dev_err(&ts_data->client->dev,
"%s error %d\n", __func__, rc);
} else {
dev_err(&ts_data->client->dev, "ts_data->client is NULL\n");
}
return rc;
}
static int write_i2c_seq(struct msg21xx_ts_data *ts_data, unsigned char addr,
unsigned char *buf, unsigned short size)
{
int rc = 0;
struct i2c_msg msgs[] = {
{
.addr = addr,
/*
* if read flag is undefined,
* then it means write flag.
*/
.flags = 0,
.len = size,
.buf = buf,
},
};
/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
if (ts_data->client != NULL) {
rc = i2c_transfer(ts_data->client->adapter, msgs, 1);
if (rc < 0)
dev_err(&ts_data->client->dev,
"%s error %d\n", __func__, rc);
} else {
dev_err(&ts_data->client->dev, "ts_data->client is NULL\n");
}
return rc;
}
static unsigned short read_reg(struct msg21xx_ts_data *ts_data,
unsigned char bank, unsigned char addr)
{
unsigned char tx_data[3] = {0x10, bank, addr};
unsigned char rx_data[2] = {0};
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data));
read_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, rx_data, sizeof(rx_data));
return rx_data[1] << 8 | rx_data[0];
}
static void write_reg(struct msg21xx_ts_data *ts_data, unsigned char bank,
unsigned char addr,
unsigned short data)
{
unsigned char tx_data[5] = {0x10, bank, addr, data & 0xFF, data >> 8};
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data));
}
static void write_reg_8bit(struct msg21xx_ts_data *ts_data, unsigned char bank,
unsigned char addr,
unsigned char data)
{
unsigned char tx_data[4] = {0x10, bank, addr, data};
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data));
}
static void dbbusDWIICEnterSerialDebugMode(struct msg21xx_ts_data *ts_data)
{
unsigned char data[5];
/* Enter the Serial Debug Mode */
data[0] = 0x53;
data[1] = 0x45;
data[2] = 0x52;
data[3] = 0x44;
data[4] = 0x42;
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data));
}
static void dbbusDWIICStopMCU(struct msg21xx_ts_data *ts_data)
{
unsigned char data[1];
/* Stop the MCU */
data[0] = 0x37;
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data));
}
static void dbbusDWIICIICUseBus(struct msg21xx_ts_data *ts_data)
{
unsigned char data[1];
/* IIC Use Bus */
data[0] = 0x35;
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data));
}
static void dbbusDWIICIICReshape(struct msg21xx_ts_data *ts_data)
{
unsigned char data[1];
/* IIC Re-shape */
data[0] = 0x71;
write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data));
}
static unsigned char msg21xx_get_ic_type(struct msg21xx_ts_data *ts_data)
{
unsigned char ic_type = 0;
unsigned char bank;
unsigned char addr;
msg21xx_reset_hw(ts_data->pdata);
dbbusDWIICEnterSerialDebugMode(ts_data);
dbbusDWIICStopMCU(ts_data);
dbbusDWIICIICUseBus(ts_data);
dbbusDWIICIICReshape(ts_data);
msleep(300);
/* stop mcu */
write_reg_8bit(ts_data, 0x0F, 0xE6, 0x01);
/* disable watch dog */
write_reg(ts_data, 0x3C, 0x60, 0xAA55);
/* get ic type */
bank = MSTAR_CHIPTOP_REGISTER_BANK;
addr = MSTAR_CHIPTOP_REGISTER_ICTYPE;
ic_type = (0xff)&(read_reg(ts_data, bank, addr));
if (ic_type != ts_data->pdata->ic_type)
ic_type = 0;
msg21xx_reset_hw(ts_data->pdata);
return ic_type;
}
static int msg21xx_read_firmware_id(struct msg21xx_ts_data *ts_data)
{
unsigned char command[3] = { 0x53, 0x00, 0x2A};
unsigned char response[4] = { 0 };
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data, ts_data->client->addr, command, sizeof(command));
read_i2c_seq(ts_data, ts_data->client->addr, response,
sizeof(response));
mutex_unlock(&msg21xx_mutex);
ts_data->pdata->fw_version_major = (response[1]<<8) + response[0];
ts_data->pdata->fw_version_minor = (response[3]<<8) + response[2];
dev_info(&ts_data->client->dev, "major num = %d, minor num = %d\n",
ts_data->pdata->fw_version_major,
ts_data->pdata->fw_version_minor);
return 0;
}
static int firmware_erase_c33(struct msg21xx_ts_data *ts_data,
enum EMEM_TYPE_t emem_type)
{
/* stop mcu */
write_reg(ts_data, 0x0F, 0xE6, 0x0001);
/* disable watch dog */
write_reg_8bit(ts_data, 0x3C, 0x60, 0x55);
write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA);
/* set PROGRAM password */
write_reg_8bit(ts_data, 0x16, 0x1A, 0xBA);
write_reg_8bit(ts_data, 0x16, 0x1B, 0xAB);
write_reg_8bit(ts_data, 0x16, 0x18, 0x80);
if (emem_type == EMEM_ALL)
write_reg_8bit(ts_data, 0x16, 0x08, 0x10);
write_reg_8bit(ts_data, 0x16, 0x18, 0x40);
msleep(20);
/* clear pce */
write_reg_8bit(ts_data, 0x16, 0x18, 0x80);
/* erase trigger */
if (emem_type == EMEM_MAIN)
write_reg_8bit(ts_data, 0x16, 0x0E, 0x04); /* erase main */
else
write_reg_8bit(ts_data, 0x16, 0x0E, 0x08); /* erase all block */
return 0;
}
static ssize_t firmware_update_c33(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size,
enum EMEM_TYPE_t emem_type,
bool isForce) {
unsigned int i, j;
unsigned int crc_main, crc_main_tp;
unsigned int crc_info, crc_info_tp;
unsigned short reg_data = 0;
int update_pass = 1;
bool fw_upgrade = false;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
crc_main = 0xffffffff;
crc_info = 0xffffffff;
msg21xx_reset_hw(ts_data->pdata);
msg21xx_read_firmware_id(ts_data);
_ReadBinConfig(ts_data);
if ((main_sw_id == info_sw_id) &&
(_CalMainCRC32(ts_data) == bin_conf_crc32) &&
(fw_file_major == ts_data->pdata->fw_version_major) &&
(fw_file_minor > ts_data->pdata->fw_version_minor)) {
fw_upgrade = true;
}
if (!fw_upgrade && !isForce) {
dev_dbg(dev, "no need to update\n");
msg21xx_reset_hw(ts_data->pdata);
return size;
}
msg21xx_reset_hw(ts_data->pdata);
msleep(300);
dbbusDWIICEnterSerialDebugMode(ts_data);
dbbusDWIICStopMCU(ts_data);
dbbusDWIICIICUseBus(ts_data);
dbbusDWIICIICReshape(ts_data);
msleep(300);
/* erase main */
firmware_erase_c33(ts_data, EMEM_MAIN);
msleep(1000);
msg21xx_reset_hw(ts_data->pdata);
dbbusDWIICEnterSerialDebugMode(ts_data);
dbbusDWIICStopMCU(ts_data);
dbbusDWIICIICUseBus(ts_data);
dbbusDWIICIICReshape(ts_data);
msleep(300);
/*
* Program
*/
/* polling 0x3CE4 is 0x1C70 */
if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) {
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0x1C70);
}
switch (emem_type) {
case EMEM_ALL:
write_reg(ts_data, 0x3C, 0xE4, 0xE38F); /* for all-blocks */
break;
case EMEM_MAIN:
write_reg(ts_data, 0x3C, 0xE4, 0x7731); /* for main block */
break;
case EMEM_INFO:
write_reg(ts_data, 0x3C, 0xE4, 0x7731); /* for info block */
write_reg_8bit(ts_data, 0x0F, 0xE6, 0x01);
write_reg_8bit(ts_data, 0x3C, 0xE4, 0xC5);
write_reg_8bit(ts_data, 0x3C, 0xE5, 0x78);
write_reg_8bit(ts_data, MSTAR_CHIPTOP_REGISTER_BANK,
0x04, 0x9F);
write_reg_8bit(ts_data, MSTAR_CHIPTOP_REGISTER_BANK,
0x05, 0x82);
write_reg_8bit(ts_data, 0x0F, 0xE6, 0x00);
msleep(100);
break;
}
/* polling 0x3CE4 is 0x2F43 */
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0x2F43);
/* calculate CRC 32 */
_CRC_initTable();
/* total 32 KB : 2 byte per R/W */
for (i = 0; i < 32; i++) {
if (i == 31) {
fw_bin_data[i][1014] = 0x5A;
fw_bin_data[i][1015] = 0xA5;
for (j = 0; j < 1016; j++)
crc_main = _CRC_getValue(fw_bin_data[i][j],
crc_main);
} else {
for (j = 0; j < 1024; j++)
crc_main = _CRC_getValue(fw_bin_data[i][j],
crc_main);
}
for (j = 0; j < 8; j++)
write_i2c_seq(ts_data, ts_data->client->addr,
&fw_bin_data[i][j * 128], 128);
msleep(100);
/* polling 0x3CE4 is 0xD0BC */
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0xD0BC);
write_reg(ts_data, 0x3C, 0xE4, 0x2F43);
}
if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) {
/* write file done and check crc */
write_reg(ts_data, 0x3C, 0xE4, 0x1380);
}
msleep(20);
if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) {
/* polling 0x3CE4 is 0x9432 */
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0x9432);
}
crc_main = crc_main ^ 0xffffffff;
crc_info = crc_info ^ 0xffffffff;
if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) {
/* CRC Main from TP */
crc_main_tp = read_reg(ts_data, 0x3C, 0x80);
crc_main_tp = (crc_main_tp << 16) |
read_reg(ts_data, 0x3C, 0x82);
/* CRC Info from TP */
crc_info_tp = read_reg(ts_data, 0x3C, 0xA0);
crc_info_tp = (crc_info_tp << 16) |
read_reg(ts_data, 0x3C, 0xA2);
}
update_pass = 1;
if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) {
if (crc_main_tp != crc_main)
update_pass = 0;
}
if (!update_pass) {
dev_err(dev, "update_C33 failed\n");
msg21xx_reset_hw(ts_data->pdata);
return 0;
}
dev_dbg(dev, "update_C33 OK\n");
msg21xx_reset_hw(ts_data->pdata);
return size;
}
static unsigned int _CalMainCRC32(struct msg21xx_ts_data *ts_data)
{
unsigned int ret = 0;
unsigned short reg_data = 0;
msg21xx_reset_hw(ts_data->pdata);
dbbusDWIICEnterSerialDebugMode(ts_data);
dbbusDWIICStopMCU(ts_data);
dbbusDWIICIICUseBus(ts_data);
dbbusDWIICIICReshape(ts_data);
msleep(100);
/* Stop MCU */
write_reg(ts_data, 0x0F, 0xE6, 0x0001);
/* Stop Watchdog */
write_reg_8bit(ts_data, 0x3C, 0x60, 0x55);
write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA);
/* cmd */
write_reg(ts_data, 0x3C, 0xE4, 0xDF4C);
write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x7d60);
/* TP SW reset */
write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x829F);
/* MCU run */
write_reg(ts_data, 0x0F, 0xE6, 0x0000);
/* polling 0x3CE4 */
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0x9432);
/* Cal CRC Main from TP */
ret = read_reg(ts_data, 0x3C, 0x80);
ret = (ret << 16) | read_reg(ts_data, 0x3C, 0x82);
dev_dbg(&ts_data->client->dev,
"[21xxA]:Current main crc32=0x%x\n", ret);
return ret;
}
static void _ReadBinConfig(struct msg21xx_ts_data *ts_data)
{
unsigned char dbbus_tx_data[5] = {0};
unsigned char dbbus_rx_data[4] = {0};
unsigned short reg_data = 0;
msg21xx_reset_hw(ts_data->pdata);
dbbusDWIICEnterSerialDebugMode(ts_data);
dbbusDWIICStopMCU(ts_data);
dbbusDWIICIICUseBus(ts_data);
dbbusDWIICIICReshape(ts_data);
msleep(100);
/* Stop MCU */
write_reg(ts_data, 0x0F, 0xE6, 0x0001);
/* Stop Watchdog */
write_reg_8bit(ts_data, 0x3C, 0x60, 0x55);
write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA);
/* cmd */
write_reg(ts_data, 0x3C, 0xE4, 0xA4AB);
write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x7d60);
/* TP SW reset */
write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x829F);
/* MCU run */
write_reg(ts_data, 0x0F, 0xE6, 0x0000);
/* polling 0x3CE4 */
do {
reg_data = read_reg(ts_data, 0x3C, 0xE4);
} while (reg_data != 0x5B58);
dbbus_tx_data[0] = 0x72;
dbbus_tx_data[1] = 0x7F;
dbbus_tx_data[2] = 0x55;
dbbus_tx_data[3] = 0x00;
dbbus_tx_data[4] = 0x04;
write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5);
read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4);
if ((dbbus_rx_data[0] >= 0x30 && dbbus_rx_data[0] <= 0x39)
&& (dbbus_rx_data[1] >= 0x30 && dbbus_rx_data[1] <= 0x39)
&& (dbbus_rx_data[2] >= 0x31 && dbbus_rx_data[2] <= 0x39)) {
main_sw_id = (dbbus_rx_data[0] - 0x30) * 100 +
(dbbus_rx_data[1] - 0x30) * 10 +
(dbbus_rx_data[2] - 0x30);
}
dbbus_tx_data[0] = 0x72;
dbbus_tx_data[1] = 0x7F;
dbbus_tx_data[2] = 0xFC;
dbbus_tx_data[3] = 0x00;
dbbus_tx_data[4] = 0x04;
write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5);
read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4);
bin_conf_crc32 = (dbbus_rx_data[0] << 24) |
(dbbus_rx_data[1] << 16) |
(dbbus_rx_data[2] << 8) |
(dbbus_rx_data[3]);
dbbus_tx_data[0] = 0x72;
dbbus_tx_data[1] = 0x83;
dbbus_tx_data[2] = 0x00;
dbbus_tx_data[3] = 0x00;
dbbus_tx_data[4] = 0x04;
write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5);
read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4);
if ((dbbus_rx_data[0] >= 0x30 && dbbus_rx_data[0] <= 0x39)
&& (dbbus_rx_data[1] >= 0x30 && dbbus_rx_data[1] <= 0x39)
&& (dbbus_rx_data[2] >= 0x31 && dbbus_rx_data[2] <= 0x39)) {
info_sw_id = (dbbus_rx_data[0] - 0x30) * 100 +
(dbbus_rx_data[1] - 0x30) * 10 +
(dbbus_rx_data[2] - 0x30);
}
dev_dbg(&ts_data->client->dev,
"[21xxA]:main_sw_id = %d, info_sw_id = %d, bin_conf_crc32 = 0x%x\n",
main_sw_id, info_sw_id, bin_conf_crc32);
}
static ssize_t firmware_update_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
return snprintf(buf, 3, "%d\n", ts_data->pdata->updating_fw);
}
static ssize_t firmware_update_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
ts_data->pdata->updating_fw = true;
disable_irq(ts_data->client->irq);
size = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, false);
enable_irq(ts_data->client->irq);
ts_data->pdata->updating_fw = false;
return size;
}
static DEVICE_ATTR(update, (S_IRUGO | S_IWUSR),
firmware_update_show,
firmware_update_store);
static int prepare_fw_data(struct device *dev)
{
int count;
int i;
int ret;
const struct firmware *fw = NULL;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
ret = request_firmware(&fw, ts_data->pdata->fw_name, dev);
if (ret < 0) {
dev_err(dev, "Request firmware failed - %s (%d)\n",
ts_data->pdata->fw_name, ret);
return ret;
}
count = fw->size / 1024;
for (i = 0; i < count; i++)
memcpy(fw_bin_data[i], fw->data + (i * 1024), 1024);
fw_file_major = MSG_FW_FILE_MAJOR_VERSION(fw);
fw_file_minor = MSG_FW_FILE_MINOR_VERSION(fw);
dev_dbg(dev, "New firmware: %d.%d",
fw_file_major, fw_file_minor);
return fw->size;
}
static ssize_t firmware_update_smart_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
int ret;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
ret = prepare_fw_data(dev);
if (ret < 0) {
dev_err(dev, "Request firmware failed -(%d)\n", ret);
return ret;
}
ts_data->pdata->updating_fw = true;
disable_irq(ts_data->client->irq);
ret = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, false);
if (ret == 0)
dev_err(dev, "firmware_update_c33 ret = %d\n", ret);
enable_irq(ts_data->client->irq);
ts_data->pdata->updating_fw = false;
return ret;
}
static ssize_t firmware_force_update_smart_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
int ret;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
ret = prepare_fw_data(dev);
if (ret < 0) {
dev_err(dev, "Request firmware failed -(%d)\n", ret);
return ret;
}
ts_data->pdata->updating_fw = true;
disable_irq(ts_data->client->irq);
ret = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, true);
if (ret == 0)
dev_err(dev, "firmware_update_c33 et = %d\n", ret);
enable_irq(ts_data->client->irq);
ts_data->pdata->updating_fw = false;
return ret;
}
static DEVICE_ATTR(update_fw, (S_IRUGO | S_IWUSR),
firmware_update_show,
firmware_update_smart_store);
static DEVICE_ATTR(force_update_fw, (S_IRUGO | S_IWUSR),
firmware_update_show,
firmware_force_update_smart_store);
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
msg21xx_read_firmware_id(ts_data);
return snprintf(buf, sizeof(char) * 8, "%03d%03d\n",
ts_data->pdata->fw_version_major,
ts_data->pdata->fw_version_minor);
}
static DEVICE_ATTR(version, S_IRUGO,
firmware_version_show,
NULL);
static ssize_t msg21xx_fw_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
return snprintf(buf, MSTAR_FW_NAME_MAX_LEN - 1,
"%s\n", ts_data->pdata->fw_name);
}
static ssize_t msg21xx_fw_name_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
if (size > MSTAR_FW_NAME_MAX_LEN - 1)
return -EINVAL;
strlcpy(ts_data->pdata->fw_name, buf, size);
if (ts_data->pdata->fw_name[size - 1] == '\n')
ts_data->pdata->fw_name[size - 1] = 0;
return size;
}
static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR),
msg21xx_fw_name_show, msg21xx_fw_name_store);
static ssize_t firmware_data_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
int count = size / 1024;
int i;
for (i = 0; i < count; i++)
memcpy(fw_bin_data[i], buf + (i * 1024), 1024);
if (buf != NULL)
dev_dbg(dev, "buf[0] = %c\n", buf[0]);
return size;
}
static DEVICE_ATTR(data, S_IWUSR, NULL, firmware_data_store);
static ssize_t tp_print_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
tp_print_proc_read(ts_data);
return snprintf(buf, 3, "%d\n", ts_data->suspended);
}
static ssize_t tp_print_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
return size;
}
static DEVICE_ATTR(tpp, (S_IRUGO | S_IWUSR),
tp_print_show, tp_print_store);
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
static void _msg_enable_proximity(void)
{
unsigned char tx_data[4] = {0};
tx_data[0] = 0x52;
tx_data[1] = 0x00;
tx_data[2] = 0x47;
tx_data[3] = 0xa0;
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data->client->addr, &tx_data[0], 4);
mutex_unlock(&msg21xx_mutex);
bEnableTpProximity = 1;
}
static void _msg_disable_proximity(void)
{
unsigned char tx_data[4] = {0};
tx_data[0] = 0x52;
tx_data[1] = 0x00;
tx_data[2] = 0x47;
tx_data[3] = 0xa1;
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data->client->addr, &tx_data[0], 4);
mutex_unlock(&msg21xx_mutex);
bEnableTpProximity = 0;
bFaceClosingTp = 0;
}
static void tsps_msg21xx_enable(int en)
{
if (en)
_msg_enable_proximity();
else
_msg_disable_proximity();
}
static int tsps_msg21xx_data(void)
{
return bFaceClosingTp;
}
#endif
static int msg21xx_pinctrl_init(struct msg21xx_ts_data *ts_data)
{
int retval;
/* Get pinctrl if target uses pinctrl */
ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev));
if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) {
retval = PTR_ERR(ts_data->ts_pinctrl);
dev_dbg(&ts_data->client->dev,
"Target does not use pinctrl %d\n", retval);
goto err_pinctrl_get;
}
ts_data->pinctrl_state_active = pinctrl_lookup_state(
ts_data->ts_pinctrl, PINCTRL_STATE_ACTIVE);
if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) {
retval = PTR_ERR(ts_data->pinctrl_state_active);
dev_dbg(&ts_data->client->dev,
"Can't lookup %s pinstate %d\n",
PINCTRL_STATE_ACTIVE, retval);
goto err_pinctrl_lookup;
}
ts_data->pinctrl_state_suspend = pinctrl_lookup_state(
ts_data->ts_pinctrl, PINCTRL_STATE_SUSPEND);
if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) {
retval = PTR_ERR(ts_data->pinctrl_state_suspend);
dev_dbg(&ts_data->client->dev,
"Can't lookup %s pinstate %d\n",
PINCTRL_STATE_SUSPEND, retval);
goto err_pinctrl_lookup;
}
ts_data->pinctrl_state_release = pinctrl_lookup_state(
ts_data->ts_pinctrl, PINCTRL_STATE_RELEASE);
if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) {
retval = PTR_ERR(ts_data->pinctrl_state_release);
dev_dbg(&ts_data->client->dev,
"Can't lookup %s pinstate %d\n",
PINCTRL_STATE_RELEASE, retval);
}
return 0;
err_pinctrl_lookup:
devm_pinctrl_put(ts_data->ts_pinctrl);
err_pinctrl_get:
ts_data->ts_pinctrl = NULL;
return retval;
}
static unsigned char calculate_checksum(unsigned char *msg, int length)
{
int checksum = 0, i;
for (i = 0; i < length; i++)
checksum += msg[i];
return (unsigned char)((-checksum) & 0xFF);
}
static int parse_info(struct msg21xx_ts_data *ts_data)
{
unsigned char data[DEMO_MODE_PACKET_LENGTH] = {0};
unsigned char checksum = 0;
unsigned int x = 0, y = 0;
unsigned int x2 = 0, y2 = 0;
unsigned int delta_x = 0, delta_y = 0;
mutex_lock(&msg21xx_mutex);
read_i2c_seq(ts_data, ts_data->client->addr, &data[0],
DEMO_MODE_PACKET_LENGTH);
mutex_unlock(&msg21xx_mutex);
checksum = calculate_checksum(&data[0], (DEMO_MODE_PACKET_LENGTH-1));
dev_dbg(&ts_data->client->dev, "check sum: [%x] == [%x]?\n",
data[DEMO_MODE_PACKET_LENGTH-1], checksum);
if (data[DEMO_MODE_PACKET_LENGTH-1] != checksum) {
dev_err(&ts_data->client->dev, "WRONG CHECKSUM\n");
return -EINVAL;
}
if (data[0] != 0x52) {
dev_err(&ts_data->client->dev, "WRONG HEADER\n");
return -EINVAL;
}
ts_data->info.keycode = 0xFF;
if ((data[1] == 0xFF) && (data[2] == 0xFF) &&
(data[3] == 0xFF) && (data[4] == 0xFF) &&
(data[6] == 0xFF)) {
if ((data[5] == 0xFF) || (data[5] == 0)) {
ts_data->info.keycode = 0xFF;
} else if ((data[5] == 1) || (data[5] == 2) ||
(data[5] == 4) || (data[5] == 8)) {
ts_data->info.keycode = data[5] >> 1;
dev_dbg(&ts_data->client->dev,
"ts_data->info.keycode index %d\n",
ts_data->info.keycode);
}
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
else if (bEnableTpProximity && ((data[5] == 0x80) ||
(data[5] == 0x40))) {
if (data[5] == 0x80)
bFaceClosingTp = 1;
else if (data[5] == 0x40)
bFaceClosingTp = 0;
return -EINVAL;
}
#endif
else {
dev_err(&ts_data->client->dev, "WRONG KEY\n");
return -EINVAL;
}
} else {
x = (((data[1] & 0xF0) << 4) | data[2]);
y = (((data[1] & 0x0F) << 8) | data[3]);
delta_x = (((data[4] & 0xF0) << 4) | data[5]);
delta_y = (((data[4] & 0x0F) << 8) | data[6]);
if ((delta_x == 0) && (delta_y == 0)) {
ts_data->info.point[0].x =
x * ts_data->pdata->x_max / TPD_WIDTH;
ts_data->info.point[0].y =
y * ts_data->pdata->y_max / TPD_HEIGHT;
ts_data->info.count = 1;
} else {
if (delta_x > 2048)
delta_x -= 4096;
if (delta_y > 2048)
delta_y -= 4096;
x2 = (unsigned int)((signed short)x +
(signed short)delta_x);
y2 = (unsigned int)((signed short)y +
(signed short)delta_y);
ts_data->info.point[0].x =
x * ts_data->pdata->x_max / TPD_WIDTH;
ts_data->info.point[0].y =
y * ts_data->pdata->y_max / TPD_HEIGHT;
ts_data->info.point[1].x =
x2 * ts_data->pdata->x_max / TPD_WIDTH;
ts_data->info.point[1].y =
y2 * ts_data->pdata->y_max / TPD_HEIGHT;
ts_data->info.count = ts_data->pdata->num_max_touches;
}
}
return 0;
}
static void touch_driver_touch_released(struct msg21xx_ts_data *ts_data)
{
int i;
for (i = 0; i < ts_data->pdata->num_max_touches; i++) {
input_mt_slot(ts_data->input_dev, i);
input_mt_report_slot_state(ts_data->input_dev,
MT_TOOL_FINGER, 0);
}
input_report_key(ts_data->input_dev, BTN_TOUCH, 0);
input_report_key(ts_data->input_dev, BTN_TOOL_FINGER, 0);
input_sync(ts_data->input_dev);
}
/* read data through I2C then report data to input
sub-system when interrupt occurred */
static irqreturn_t msg21xx_ts_interrupt(int irq, void *dev_id)
{
int i = 0;
static int last_keycode = 0xFF;
static int last_count;
struct msg21xx_ts_data *ts_data = dev_id;
ts_data->info.count = 0;
if (0 == parse_info(ts_data)) {
if (ts_data->info.keycode != 0xFF) { /* key touch pressed */
if (ts_data->info.keycode <
ts_data->pdata->num_buttons) {
if (ts_data->info.keycode != last_keycode) {
dev_dbg(&ts_data->client->dev,
"key touch pressed");
input_report_key(ts_data->input_dev,
BTN_TOUCH, 1);
input_report_key(ts_data->input_dev,
ts_data->pdata->button_map[
ts_data->info.keycode], 1);
last_keycode = ts_data->info.keycode;
} else {
/* pass duplicate key-pressing */
dev_dbg(&ts_data->client->dev,
"REPEATED KEY\n");
}
} else {
dev_dbg(&ts_data->client->dev, "WRONG KEY\n");
}
} else { /* key touch released */
if (last_keycode != 0xFF) {
dev_dbg(&ts_data->client->dev, "key touch released");
input_report_key(ts_data->input_dev,
BTN_TOUCH, 0);
input_report_key(ts_data->input_dev,
ts_data->pdata->button_map[last_keycode],
0);
last_keycode = 0xFF;
}
}
if (ts_data->info.count > 0) { /* point touch pressed */
for (i = 0; i < ts_data->info.count; i++) {
input_mt_slot(ts_data->input_dev, i);
input_mt_report_slot_state(ts_data->input_dev,
MT_TOOL_FINGER, 1);
input_report_abs(ts_data->input_dev,
ABS_MT_TOUCH_MAJOR, 1);
input_report_abs(ts_data->input_dev,
ABS_MT_POSITION_X,
ts_data->info.point[i].x);
input_report_abs(ts_data->input_dev,
ABS_MT_POSITION_Y,
ts_data->info.point[i].y);
}
}
if (last_count > ts_data->info.count) {
for (i = ts_data->info.count;
i < ts_data->pdata->num_max_touches;
i++) {
input_mt_slot(ts_data->input_dev, i);
input_mt_report_slot_state(ts_data->input_dev,
MT_TOOL_FINGER, 0);
}
}
last_count = ts_data->info.count;
input_report_key(ts_data->input_dev, BTN_TOUCH,
ts_data->info.count > 0);
input_report_key(ts_data->input_dev, BTN_TOOL_FINGER,
ts_data->info.count > 0);
input_sync(ts_data->input_dev);
}
return IRQ_HANDLED;
}
static int msg21xx_ts_power_init(struct msg21xx_ts_data *ts_data, bool init)
{
int rc;
if (init) {
ts_data->vdd = regulator_get(&ts_data->client->dev,
"vdd");
if (IS_ERR(ts_data->vdd)) {
rc = PTR_ERR(ts_data->vdd);
dev_err(&ts_data->client->dev,
"Regulator get failed vdd rc=%d\n", rc);
return rc;
}
if (regulator_count_voltages(ts_data->vdd) > 0) {
rc = regulator_set_voltage(ts_data->vdd,
MSTAR_VTG_MIN_UV,
MSTAR_VTG_MAX_UV);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator set_vtg failed vdd rc=%d\n",
rc);
goto reg_vdd_put;
}
}
ts_data->vcc_i2c = regulator_get(&ts_data->client->dev,
"vcc_i2c");
if (IS_ERR(ts_data->vcc_i2c)) {
rc = PTR_ERR(ts_data->vcc_i2c);
dev_err(&ts_data->client->dev,
"Regulator get failed vcc_i2c rc=%d\n", rc);
goto reg_vdd_set_vtg;
}
if (regulator_count_voltages(ts_data->vcc_i2c) > 0) {
rc = regulator_set_voltage(ts_data->vcc_i2c,
MSTAR_I2C_VTG_MIN_UV,
MSTAR_I2C_VTG_MAX_UV);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
goto reg_vcc_i2c_put;
}
}
} else {
if (regulator_count_voltages(ts_data->vdd) > 0)
regulator_set_voltage(ts_data->vdd, 0,
MSTAR_VTG_MAX_UV);
regulator_put(ts_data->vdd);
if (regulator_count_voltages(ts_data->vcc_i2c) > 0)
regulator_set_voltage(ts_data->vcc_i2c, 0,
MSTAR_I2C_VTG_MAX_UV);
regulator_put(ts_data->vcc_i2c);
}
return 0;
reg_vcc_i2c_put:
regulator_put(ts_data->vcc_i2c);
reg_vdd_set_vtg:
if (regulator_count_voltages(ts_data->vdd) > 0)
regulator_set_voltage(ts_data->vdd, 0, MSTAR_VTG_MAX_UV);
reg_vdd_put:
regulator_put(ts_data->vdd);
return rc;
}
static int msg21xx_ts_power_on(struct msg21xx_ts_data *ts_data, bool on)
{
int rc;
if (!on)
goto power_off;
rc = regulator_enable(ts_data->vdd);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator vdd enable failed rc=%d\n", rc);
return rc;
}
rc = regulator_enable(ts_data->vcc_i2c);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator vcc_i2c enable failed rc=%d\n", rc);
regulator_disable(ts_data->vdd);
}
return rc;
power_off:
rc = regulator_disable(ts_data->vdd);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator vdd disable failed rc=%d\n", rc);
return rc;
}
rc = regulator_disable(ts_data->vcc_i2c);
if (rc) {
dev_err(&ts_data->client->dev,
"Regulator vcc_i2c disable failed rc=%d\n", rc);
rc = regulator_enable(ts_data->vdd);
}
return rc;
}
static int msg21xx_ts_gpio_configure(struct msg21xx_ts_data *ts_data, bool on)
{
int ret = 0;
if (!on)
goto pwr_deinit;
if (gpio_is_valid(ts_data->pdata->irq_gpio)) {
ret = gpio_request(ts_data->pdata->irq_gpio,
"msg21xx_irq_gpio");
if (ret) {
dev_err(&ts_data->client->dev,
"Failed to request GPIO[%d], %d\n",
ts_data->pdata->irq_gpio, ret);
goto err_irq_gpio_req;
}
ret = gpio_direction_input(ts_data->pdata->irq_gpio);
if (ret) {
dev_err(&ts_data->client->dev,
"Failed to set direction for gpio[%d], %d\n",
ts_data->pdata->irq_gpio, ret);
goto err_irq_gpio_dir;
}
gpio_set_value_cansleep(ts_data->pdata->irq_gpio, 1);
} else {
dev_err(&ts_data->client->dev, "irq gpio not provided\n");
goto err_irq_gpio_req;
}
if (gpio_is_valid(ts_data->pdata->reset_gpio)) {
ret = gpio_request(ts_data->pdata->reset_gpio,
"msg21xx_reset_gpio");
if (ret) {
dev_err(&ts_data->client->dev,
"Failed to request GPIO[%d], %d\n",
ts_data->pdata->reset_gpio, ret);
goto err_reset_gpio_req;
}
/* power on TP */
ret = gpio_direction_output(
ts_data->pdata->reset_gpio, 1);
if (ret) {
dev_err(&ts_data->client->dev,
"Failed to set direction for GPIO[%d], %d\n",
ts_data->pdata->reset_gpio, ret);
goto err_reset_gpio_dir;
}
msleep(100);
gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 0);
msleep(20);
gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 1);
msleep(200);
} else {
dev_err(&ts_data->client->dev, "reset gpio not provided\n");
goto err_reset_gpio_req;
}
return 0;
err_reset_gpio_dir:
if (gpio_is_valid(ts_data->pdata->reset_gpio))
gpio_free(ts_data->pdata->irq_gpio);
err_reset_gpio_req:
err_irq_gpio_dir:
if (gpio_is_valid(ts_data->pdata->irq_gpio))
gpio_free(ts_data->pdata->irq_gpio);
err_irq_gpio_req:
return ret;
pwr_deinit:
if (gpio_is_valid(ts_data->pdata->irq_gpio))
gpio_free(ts_data->pdata->irq_gpio);
if (gpio_is_valid(ts_data->pdata->reset_gpio)) {
gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 0);
ret = gpio_direction_input(ts_data->pdata->reset_gpio);
if (ret)
dev_err(&ts_data->client->dev,
"Unable to set direction for gpio [%d]\n",
ts_data->pdata->reset_gpio);
gpio_free(ts_data->pdata->reset_gpio);
}
return 0;
}
#ifdef CONFIG_PM
static int msg21xx_ts_resume(struct device *dev)
{
int retval;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
if (!ts_data->suspended) {
dev_info(dev, "msg21xx_ts already in resume\n");
return 0;
}
mutex_lock(&ts_data->ts_mutex);
retval = msg21xx_ts_power_on(ts_data, true);
if (retval) {
dev_err(dev, "msg21xx_ts power on failed");
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
if (ts_data->ts_pinctrl) {
retval = pinctrl_select_state(ts_data->ts_pinctrl,
ts_data->pinctrl_state_active);
if (retval < 0) {
dev_err(dev, "Cannot get active pinctrl state\n");
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
}
retval = msg21xx_ts_gpio_configure(ts_data, true);
if (retval) {
dev_err(dev, "Failed to put gpios in active state %d",
retval);
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
enable_irq(ts_data->client->irq);
ts_data->suspended = false;
mutex_unlock(&ts_data->ts_mutex);
return 0;
}
static int msg21xx_ts_suspend(struct device *dev)
{
int retval;
struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev);
if (ts_data->pdata->updating_fw) {
dev_info(dev, "Firmware loading in progress\n");
return 0;
}
if (ts_data->suspended) {
dev_info(dev, "msg21xx_ts already in suspend\n");
return 0;
}
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
if (bEnableTpProximity) {
dev_dbg(dev, "suspend bEnableTpProximity=%d\n",
bEnableTpProximity);
return 0;
}
#endif
mutex_lock(&ts_data->ts_mutex);
disable_irq(ts_data->client->irq);
touch_driver_touch_released(ts_data);
if (ts_data->ts_pinctrl) {
retval = pinctrl_select_state(ts_data->ts_pinctrl,
ts_data->pinctrl_state_suspend);
if (retval < 0) {
dev_err(dev, "Cannot get idle pinctrl state %d\n",
retval);
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
}
retval = msg21xx_ts_gpio_configure(ts_data, false);
if (retval) {
dev_err(dev, "Failed to put gpios in idle state %d",
retval);
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
retval = msg21xx_ts_power_on(ts_data, false);
if (retval) {
dev_err(dev, "msg21xx_ts power off failed");
mutex_unlock(&ts_data->ts_mutex);
return retval;
}
ts_data->suspended = true;
mutex_unlock(&ts_data->ts_mutex);
return 0;
}
#else
static int msg21xx_ts_resume(struct device *dev)
{
return 0;
}
static int msg21xx_ts_suspend(struct device *dev)
{
return 0;
}
#endif
static int msg21xx_debug_suspend_set(void *_data, u64 val)
{
struct msg21xx_ts_data *data = _data;
mutex_lock(&data->input_dev->mutex);
if (val)
msg21xx_ts_suspend(&data->client->dev);
else
msg21xx_ts_resume(&data->client->dev);
mutex_unlock(&data->input_dev->mutex);
return 0;
}
static int msg21xx_debug_suspend_get(void *_data, u64 *val)
{
struct msg21xx_ts_data *data = _data;
mutex_lock(&data->input_dev->mutex);
*val = data->suspended;
mutex_unlock(&data->input_dev->mutex);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, msg21xx_debug_suspend_get,
msg21xx_debug_suspend_set, "%lld\n");
#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 msg21xx_ts_data *ts_data =
container_of(self, struct msg21xx_ts_data, fb_notif);
if (evdata && evdata->data && event == FB_EVENT_BLANK) {
blank = evdata->data;
if (*blank == FB_BLANK_UNBLANK)
msg21xx_ts_resume(&ts_data->client->dev);
else if (*blank == FB_BLANK_POWERDOWN)
msg21xx_ts_suspend(&ts_data->client->dev);
}
return 0;
}
#endif
static int msg21xx_get_dt_coords(struct device *dev, char *name,
struct msg21xx_ts_platform_data *pdata)
{
u32 coords[FT_COORDS_ARR_SIZE];
struct property *prop;
struct device_node *np = dev->of_node;
int coords_size, rc;
prop = of_find_property(np, name, NULL);
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
coords_size = prop->length / sizeof(u32);
if (coords_size != FT_COORDS_ARR_SIZE) {
dev_err(dev, "invalid %s\n", name);
return -EINVAL;
}
rc = of_property_read_u32_array(np, name, coords, coords_size);
if (rc && (rc != -EINVAL)) {
dev_err(dev, "Unable to read %s\n", name);
return rc;
}
if (!strcmp(name, "mstar,panel-coords")) {
pdata->panel_minx = coords[0];
pdata->panel_miny = coords[1];
pdata->panel_maxx = coords[2];
pdata->panel_maxy = coords[3];
} else if (!strcmp(name, "mstar,display-coords")) {
pdata->x_min = coords[0];
pdata->y_min = coords[1];
pdata->x_max = coords[2];
pdata->y_max = coords[3];
} else {
dev_err(dev, "unsupported property %s\n", name);
return -EINVAL;
}
return 0;
}
static int msg21xx_parse_dt(struct device *dev,
struct msg21xx_ts_platform_data *pdata)
{
int rc;
struct device_node *np = dev->of_node;
struct property *prop;
u32 temp_val;
rc = msg21xx_get_dt_coords(dev, "mstar,panel-coords", pdata);
if (rc && (rc != -EINVAL))
return rc;
rc = msg21xx_get_dt_coords(dev, "mstar,display-coords", pdata);
if (rc)
return rc;
rc = of_property_read_u32(np, "mstar,hard-reset-delay-ms",
&temp_val);
if (!rc)
pdata->hard_reset_delay_ms = temp_val;
else
return rc;
rc = of_property_read_u32(np, "mstar,post-hard-reset-delay-ms",
&temp_val);
if (!rc)
pdata->post_hard_reset_delay_ms = temp_val;
else
return rc;
/* reset, irq gpio info */
pdata->reset_gpio = of_get_named_gpio_flags(np, "mstar,reset-gpio",
0, &pdata->reset_gpio_flags);
if (pdata->reset_gpio < 0)
return pdata->reset_gpio;
pdata->irq_gpio = of_get_named_gpio_flags(np, "mstar,irq-gpio",
0, &pdata->irq_gpio_flags);
if (pdata->irq_gpio < 0)
return pdata->irq_gpio;
rc = of_property_read_u32(np, "mstar,ic-type", &temp_val);
if (rc && (rc != -EINVAL))
return rc;
pdata->ic_type = temp_val;
rc = of_property_read_u32(np, "mstar,num-max-touches", &temp_val);
if (!rc)
pdata->num_max_touches = temp_val;
else
return rc;
prop = of_find_property(np, "mstar,button-map", NULL);
if (prop) {
pdata->num_buttons = prop->length / sizeof(temp_val);
if (pdata->num_buttons > MAX_BUTTONS)
return -EINVAL;
rc = of_property_read_u32_array(np,
"mstar,button-map", pdata->button_map,
pdata->num_buttons);
if (rc) {
dev_err(dev, "Unable to read key codes\n");
return rc;
}
}
return 0;
}
/* probe function is used for matching and initializing input device */
static int msg21xx_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id) {
int ret = 0, i;
struct dentry *temp, *dir;
struct input_dev *input_dev;
struct msg21xx_ts_data *ts_data;
struct msg21xx_ts_platform_data *pdata;
if (client->dev.of_node) {
pdata = devm_kzalloc(&client->dev,
sizeof(struct msg21xx_ts_platform_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
ret = msg21xx_parse_dt(&client->dev, pdata);
if (ret) {
dev_err(&client->dev, "DT parsing failed\n");
return ret;
}
} else
pdata = client->dev.platform_data;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C not supported\n");
return -ENODEV;
}
ts_data = devm_kzalloc(&client->dev,
sizeof(struct msg21xx_ts_data), GFP_KERNEL);
if (!ts_data)
return -ENOMEM;
ts_data->client = client;
ts_data->info.point = devm_kzalloc(&client->dev,
sizeof(struct touchPoint_t) * pdata->num_max_touches,
GFP_KERNEL);
if (!ts_data->info.point) {
dev_err(&client->dev, "Not enough memory\n");
return -ENOMEM;
}
/* allocate an input device */
input_dev = input_allocate_device();
if (!input_dev) {
ret = -ENOMEM;
dev_err(&client->dev, "input device allocation failed\n");
goto err_input_allocate_dev;
}
input_dev->name = client->name;
input_dev->phys = "I2C";
input_dev->dev.parent = &client->dev;
input_dev->id.bustype = BUS_I2C;
ts_data->input_dev = input_dev;
ts_data->client = client;
ts_data->pdata = pdata;
input_set_drvdata(input_dev, ts_data);
i2c_set_clientdata(client, ts_data);
ret = msg21xx_ts_power_init(ts_data, true);
if (ret) {
dev_err(&client->dev, "Mstar power init failed\n");
return ret;
}
ret = msg21xx_ts_power_on(ts_data, true);
if (ret) {
dev_err(&client->dev, "Mstar power on failed\n");
goto exit_deinit_power;
}
ret = msg21xx_pinctrl_init(ts_data);
if (!ret && ts_data->ts_pinctrl) {
/*
* Pinctrl handle is optional. If pinctrl handle is found
* let pins to be configured in active state. If not
* found continue further without error.
*/
ret = pinctrl_select_state(ts_data->ts_pinctrl,
ts_data->pinctrl_state_active);
if (ret < 0)
dev_err(&client->dev,
"Failed to select %s pinatate %d\n",
PINCTRL_STATE_ACTIVE, ret);
}
ret = msg21xx_ts_gpio_configure(ts_data, true);
if (ret) {
dev_err(&client->dev, "Failed to configure gpio %d\n", ret);
goto exit_gpio_config;
}
if (msg21xx_get_ic_type(ts_data) == 0) {
dev_err(&client->dev, "The current IC is not Mstar\n");
ret = -1;
goto err_wrong_ic_type;
}
mutex_init(&msg21xx_mutex);
mutex_init(&ts_data->ts_mutex);
/* set the supported event type for input device */
set_bit(EV_ABS, input_dev->evbit);
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
for (i = 0; i < pdata->num_buttons; i++)
input_set_capability(input_dev, EV_KEY, pdata->button_map[i]);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 2, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, pdata->y_max, 0, 0);
ret = input_mt_init_slots(input_dev, pdata->num_max_touches, 0);
if (ret) {
dev_err(&client->dev,
"Error %d initialising slots\n", ret);
goto err_free_mem;
}
/* register the input device to input sub-system */
ret = input_register_device(input_dev);
if (ret < 0) {
dev_err(&client->dev,
"Unable to register ms-touchscreen input device\n");
goto err_input_reg_dev;
}
/* version */
if (device_create_file(&client->dev, &dev_attr_version) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_version.attr.name);
goto err_create_fw_ver_file;
}
/* update */
if (device_create_file(&client->dev, &dev_attr_update) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_update.attr.name);
goto err_create_fw_update_file;
}
/* data */
if (device_create_file(&client->dev, &dev_attr_data) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_data.attr.name);
goto err_create_fw_data_file;
}
/* fw name */
if (device_create_file(&client->dev, &dev_attr_fw_name) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_fw_name.attr.name);
goto err_create_fw_name_file;
}
/* smart fw update */
if (device_create_file(&client->dev, &dev_attr_update_fw) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_update_fw.attr.name);
goto err_create_update_fw_file;
}
/* smart fw force update */
if (device_create_file(&client->dev,
&dev_attr_force_update_fw) < 0) {
dev_err(&client->dev,
"Failed to create device file(%s)!\n",
dev_attr_force_update_fw.attr.name);
goto err_create_force_update_fw_file;
}
dir = debugfs_create_dir(MSTAR_DEBUG_DIR_NAME, NULL);
temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, dir,
ts_data, &debug_suspend_fops);
if (temp == NULL || IS_ERR(temp)) {
dev_err(&client->dev,
"debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
goto free_debug_dir;
}
#ifdef TP_PRINT
tp_print_create_entry(ts_data);
#endif
ret = request_threaded_irq(client->irq, NULL,
msg21xx_ts_interrupt,
pdata->irq_gpio_flags | IRQF_ONESHOT,
"msg21xx", ts_data);
if (ret)
goto err_req_irq;
disable_irq(client->irq);
#if defined(CONFIG_FB)
ts_data->fb_notif.notifier_call = fb_notifier_callback;
ret = fb_register_client(&ts_data->fb_notif);
#endif
#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR
tsps_assist_register_callback("msg21xx", &tsps_msg21xx_enable,
&tsps_msg21xx_data);
#endif
dev_dbg(&client->dev, "mstar touch screen registered\n");
enable_irq(client->irq);
return 0;
err_req_irq:
free_irq(client->irq, ts_data);
device_remove_file(&client->dev, &dev_attr_data);
free_debug_dir:
debugfs_remove_recursive(dir);
err_create_fw_data_file:
device_remove_file(&client->dev, &dev_attr_update);
err_create_fw_update_file:
device_remove_file(&client->dev, &dev_attr_version);
err_create_fw_name_file:
device_remove_file(&client->dev, &dev_attr_fw_name);
err_create_update_fw_file:
device_remove_file(&client->dev, &dev_attr_update_fw);
err_create_force_update_fw_file:
device_remove_file(&client->dev, &dev_attr_force_update_fw);
err_create_fw_ver_file:
input_unregister_device(input_dev);
err_input_reg_dev:
input_free_device(input_dev);
input_dev = NULL;
err_input_allocate_dev:
mutex_destroy(&msg21xx_mutex);
mutex_destroy(&ts_data->ts_mutex);
err_wrong_ic_type:
msg21xx_ts_gpio_configure(ts_data, false);
exit_gpio_config:
if (ts_data->ts_pinctrl) {
if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) {
devm_pinctrl_put(ts_data->ts_pinctrl);
ts_data->ts_pinctrl = NULL;
} else {
ret = pinctrl_select_state(ts_data->ts_pinctrl,
ts_data->pinctrl_state_release);
if (ret < 0)
dev_err(&ts_data->client->dev,
"Cannot get release pinctrl state\n");
}
}
msg21xx_ts_power_on(ts_data, false);
exit_deinit_power:
msg21xx_ts_power_init(ts_data, false);
err_free_mem:
input_free_device(input_dev);
return ret;
}
/* remove function is triggered when the input device is removed
from input sub-system */
static int touch_driver_remove(struct i2c_client *client)
{
int retval = 0;
struct msg21xx_ts_data *ts_data = i2c_get_clientdata(client);
free_irq(ts_data->client->irq, ts_data);
gpio_free(ts_data->pdata->irq_gpio);
gpio_free(ts_data->pdata->reset_gpio);
if (ts_data->ts_pinctrl) {
if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) {
devm_pinctrl_put(ts_data->ts_pinctrl);
ts_data->ts_pinctrl = NULL;
} else {
retval = pinctrl_select_state(ts_data->ts_pinctrl,
ts_data->pinctrl_state_release);
if (retval < 0)
dev_err(&ts_data->client->dev,
"Cannot get release pinctrl state\n");
}
}
input_unregister_device(ts_data->input_dev);
mutex_destroy(&msg21xx_mutex);
mutex_destroy(&ts_data->ts_mutex);
return retval;
}
/* The I2C device list is used for matching I2C device
and I2C device driver. */
static const struct i2c_device_id touch_device_id[] = {
{"msg21xx", 0},
{}, /* should not omitted */
};
static struct of_device_id msg21xx_match_table[] = {
{ .compatible = "mstar,msg21xx", },
{ },
};
MODULE_DEVICE_TABLE(i2c, touch_device_id);
static struct i2c_driver touch_device_driver = {
.driver = {
.name = "ms-msg21xx",
.owner = THIS_MODULE,
.of_match_table = msg21xx_match_table,
},
.probe = msg21xx_ts_probe,
.remove = touch_driver_remove,
.id_table = touch_device_id,
};
module_i2c_driver(touch_device_driver);
#ifdef TP_PRINT
#include <linux/proc_fs.h>
static unsigned short InfoAddr = 0x0F, PoolAddr = 0x10, TransLen = 256;
static unsigned char row, units, cnt;
static int tp_print_proc_read(struct msg21xx_ts_data *ts_data)
{
unsigned short i, j;
unsigned short left, offset = 0;
unsigned char dbbus_tx_data[3] = {0};
unsigned char u8Data;
signed short s16Data;
int s32Data;
char *buf = NULL;
left = cnt*row*units;
if ((ts_data->suspended == 0) &&
(InfoAddr != 0x0F) &&
(PoolAddr != 0x10) &&
(left > 0)) {
buf = kmalloc(left, GFP_KERNEL);
if (buf != NULL) {
while (left > 0) {
dbbus_tx_data[0] = 0x53;
dbbus_tx_data[1] = ((PoolAddr + offset) >> 8)
& 0xFF;
dbbus_tx_data[2] = (PoolAddr + offset) & 0xFF;
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data, ts_data->client->addr,
&dbbus_tx_data[0], 3);
read_i2c_seq(ts_data, ts_data->client->addr,
&buf[offset],
left > TransLen ? TransLen : left);
mutex_unlock(&msg21xx_mutex);
if (left > TransLen) {
left -= TransLen;
offset += TransLen;
} else {
left = 0;
}
}
for (i = 0; i < cnt; i++) {
for (j = 0; j < row; j++) {
if (units == 1) {
u8Data = buf[i * row * units +
j * units];
} else if (units == 2) {
s16Data = buf[i * row * units +
j * units] +
(buf[i * row * units +
j * units + 1] << 8);
} else if (units == 4) {
s32Data = buf[i * row * units +
j * units] +
(buf[i * row * units +
j * units + 1] << 8) +
(buf[i * row * units +
j * units + 2] << 16) +
(buf[i * row * units +
j * units + 3] << 24);
}
}
}
kfree(buf);
}
}
return 0;
}
static void tp_print_create_entry(struct msg21xx_ts_data *ts_data)
{
unsigned char dbbus_tx_data[3] = {0};
unsigned char dbbus_rx_data[8] = {0};
dbbus_tx_data[0] = 0x53;
dbbus_tx_data[1] = 0x00;
dbbus_tx_data[2] = 0x58;
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 3);
read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4);
mutex_unlock(&msg21xx_mutex);
InfoAddr = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0];
PoolAddr = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2];
if ((InfoAddr != 0x0F) && (PoolAddr != 0x10)) {
msleep(20);
dbbus_tx_data[0] = 0x53;
dbbus_tx_data[1] = (InfoAddr >> 8) & 0xFF;
dbbus_tx_data[2] = InfoAddr & 0xFF;
mutex_lock(&msg21xx_mutex);
write_i2c_seq(ts_data, ts_data->client->addr,
&dbbus_tx_data[0], 3);
read_i2c_seq(ts_data, ts_data->client->addr,
&dbbus_rx_data[0], 8);
mutex_unlock(&msg21xx_mutex);
units = dbbus_rx_data[0];
row = dbbus_rx_data[1];
cnt = dbbus_rx_data[2];
TransLen = (dbbus_rx_data[7]<<8) + dbbus_rx_data[6];
if (device_create_file(&ts_data->client->dev,
&dev_attr_tpp) < 0)
dev_err(&ts_data->client->dev, "Failed to create device file(%s)!\n",
dev_attr_tpp.attr.name);
}
}
#endif
MODULE_AUTHOR("MStar Semiconductor, Inc.");
MODULE_LICENSE("GPL v2");