2169 lines
54 KiB
C
2169 lines
54 KiB
C
|
/*
|
||
|
* 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");
|