2342 lines
59 KiB
C
2342 lines
59 KiB
C
|
/*
|
||
|
* Atmel maXTouch Touchscreen Controller Driver
|
||
|
*
|
||
|
*
|
||
|
* Copyright (C) 2010 Atmel Corporation
|
||
|
* Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
|
||
|
* Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com>
|
||
|
*
|
||
|
* 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; version 2 of the License
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
*
|
||
|
* Driver for Atmel maXTouch family of touch controllers.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/input.h>
|
||
|
#include <linux/debugfs.h>
|
||
|
#include <linux/cdev.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/module.h>
|
||
|
|
||
|
#include <asm/uaccess.h>
|
||
|
|
||
|
#include <linux/atmel_maxtouch.h>
|
||
|
|
||
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
#include <linux/earlysuspend.h>
|
||
|
|
||
|
/* Early-suspend level */
|
||
|
#define MXT_SUSPEND_LEVEL 1
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define DRIVER_VERSION "0.91a_mod"
|
||
|
|
||
|
static int debug = DEBUG_INFO;
|
||
|
static int comms = 0;
|
||
|
module_param(debug, int, 0644);
|
||
|
module_param(comms, int, 0644);
|
||
|
|
||
|
MODULE_PARM_DESC(debug, "Activate debugging output");
|
||
|
MODULE_PARM_DESC(comms, "Select communications mode");
|
||
|
|
||
|
#define T7_DATA_SIZE 3
|
||
|
|
||
|
/* Device Info descriptor */
|
||
|
/* Parsed from maXTouch "Id information" inside device */
|
||
|
struct mxt_device_info {
|
||
|
u8 family_id;
|
||
|
u8 variant_id;
|
||
|
u8 major;
|
||
|
u8 minor;
|
||
|
u8 build;
|
||
|
u8 num_objs;
|
||
|
u8 x_size;
|
||
|
u8 y_size;
|
||
|
char family_name[16]; /* Family name */
|
||
|
char variant_name[16]; /* Variant name */
|
||
|
u16 num_nodes; /* Number of sensor nodes */
|
||
|
};
|
||
|
|
||
|
/* object descriptor table, parsed from maXTouch "object table" */
|
||
|
struct mxt_object {
|
||
|
u16 chip_addr;
|
||
|
u8 type;
|
||
|
u8 size;
|
||
|
u8 instances;
|
||
|
u8 num_report_ids;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Mapping from report id to object type and instance */
|
||
|
struct report_id_map {
|
||
|
u8 object;
|
||
|
u8 instance;
|
||
|
/*
|
||
|
* This is the first report ID belonging to object. It enables us to
|
||
|
* find out easily the touch number: each touch has different report
|
||
|
* ID (which are assigned to touches in increasing order). By
|
||
|
* subtracting the first report ID from current, we get the touch
|
||
|
* number.
|
||
|
*/
|
||
|
u8 first_rid;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Driver datastructure */
|
||
|
struct mxt_data {
|
||
|
struct i2c_client *client;
|
||
|
struct input_dev *input;
|
||
|
char phys_name[32];
|
||
|
int irq;
|
||
|
|
||
|
u16 last_read_addr;
|
||
|
bool new_msgs;
|
||
|
u8 *last_message;
|
||
|
|
||
|
int valid_irq_counter;
|
||
|
int invalid_irq_counter;
|
||
|
int irq_counter;
|
||
|
int message_counter;
|
||
|
int read_fail_counter;
|
||
|
|
||
|
|
||
|
int bytes_to_read;
|
||
|
|
||
|
struct delayed_work dwork;
|
||
|
u8 xpos_format;
|
||
|
u8 ypos_format;
|
||
|
|
||
|
u8 numtouch;
|
||
|
|
||
|
struct mxt_device_info device_info;
|
||
|
|
||
|
u32 info_block_crc;
|
||
|
u32 configuration_crc;
|
||
|
u16 report_id_count;
|
||
|
struct report_id_map *rid_map;
|
||
|
struct mxt_object *object_table;
|
||
|
|
||
|
u16 msg_proc_addr;
|
||
|
u8 message_size;
|
||
|
|
||
|
u16 min_x_val;
|
||
|
u16 min_y_val;
|
||
|
u16 max_x_val;
|
||
|
u16 max_y_val;
|
||
|
|
||
|
int (*init_hw)(struct i2c_client *client);
|
||
|
int (*exit_hw)(struct i2c_client *client);
|
||
|
int (*power_on)(bool on);
|
||
|
u8 (*valid_interrupt)(void);
|
||
|
u8 (*read_chg)(void);
|
||
|
|
||
|
/* debugfs variables */
|
||
|
struct dentry *debug_dir;
|
||
|
int current_debug_datap;
|
||
|
|
||
|
struct mutex debug_mutex;
|
||
|
u16 *debug_data;
|
||
|
|
||
|
/* Character device variables */
|
||
|
struct cdev cdev;
|
||
|
struct cdev cdev_messages; /* 2nd Char dev for messages */
|
||
|
dev_t dev_num;
|
||
|
struct class *mxt_class;
|
||
|
|
||
|
|
||
|
u16 address_pointer;
|
||
|
bool valid_ap;
|
||
|
|
||
|
/* Message buffer & pointers */
|
||
|
char *messages;
|
||
|
int msg_buffer_startp, msg_buffer_endp;
|
||
|
/* Put only non-touch messages to buffer if this is set */
|
||
|
char nontouch_msg_only;
|
||
|
struct mutex msg_mutex;
|
||
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
struct early_suspend early_suspend;
|
||
|
#endif
|
||
|
u8 t7_data[T7_DATA_SIZE];
|
||
|
bool is_suspended;
|
||
|
};
|
||
|
/*default value, enough to read versioning*/
|
||
|
#define CONFIG_DATA_SIZE 6
|
||
|
static u16 t38_size = CONFIG_DATA_SIZE;
|
||
|
static int mxt_read_block(struct i2c_client *client, u16 addr, u16 length,
|
||
|
u8 *value);
|
||
|
static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value);
|
||
|
static int mxt_write_block(struct i2c_client *client, u16 addr, u16 length,
|
||
|
u8 *value);
|
||
|
static u8 mxt_valid_interrupt_dummy(void)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#define I2C_RETRY_COUNT 5
|
||
|
#define I2C_PAYLOAD_SIZE 254
|
||
|
|
||
|
/* Returns the start address of object in mXT memory. */
|
||
|
#define MXT_BASE_ADDR(object_type, mxt) \
|
||
|
get_object_address(object_type, 0, mxt->object_table, \
|
||
|
mxt->device_info.num_objs)
|
||
|
|
||
|
/* Maps a report ID to an object type (object type number). */
|
||
|
#define REPORT_ID_TO_OBJECT(rid, mxt) \
|
||
|
(((rid) == 0xff) ? 0 : mxt->rid_map[rid].object)
|
||
|
|
||
|
/* Maps a report ID to an object type (string). */
|
||
|
#define REPORT_ID_TO_OBJECT_NAME(rid, mxt) \
|
||
|
object_type_name[REPORT_ID_TO_OBJECT(rid, mxt)]
|
||
|
|
||
|
/* Returns non-zero if given object is a touch object */
|
||
|
#define IS_TOUCH_OBJECT(object) \
|
||
|
((object == MXT_TOUCH_MULTITOUCHSCREEN_T9) || \
|
||
|
(object == MXT_TOUCH_KEYARRAY_T15) || \
|
||
|
(object == MXT_TOUCH_PROXIMITY_T23) || \
|
||
|
(object == MXT_TOUCH_SINGLETOUCHSCREEN_T10) || \
|
||
|
(object == MXT_TOUCH_XSLIDER_T11) || \
|
||
|
(object == MXT_TOUCH_YSLIDER_T12) || \
|
||
|
(object == MXT_TOUCH_XWHEEL_T13) || \
|
||
|
(object == MXT_TOUCH_YWHEEL_T14) || \
|
||
|
(object == MXT_TOUCH_KEYSET_T31) || \
|
||
|
(object == MXT_TOUCH_XSLIDERSET_T32) ? 1 : 0)
|
||
|
|
||
|
#define mxt_debug(level, ...) \
|
||
|
do { \
|
||
|
if (debug >= (level)) \
|
||
|
pr_debug(__VA_ARGS__); \
|
||
|
} while (0)
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Check whether we have multi-touch enabled kernel; if not, report just the
|
||
|
* first touch (on mXT224, the maximum is 10 simultaneous touches).
|
||
|
* Because just the 1st one is reported, it might seem that the screen is not
|
||
|
* responding to touch if the first touch is removed while the screen is being
|
||
|
* touched by another finger, so beware.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifdef ABS_MT_TRACKING_ID
|
||
|
static inline void report_mt(int touch_number, int size, int x, int y, struct
|
||
|
mxt_data *mxt) {
|
||
|
input_report_abs(mxt->input, ABS_MT_TRACKING_ID, touch_number);
|
||
|
input_report_abs(mxt->input, ABS_MT_TOUCH_MAJOR, size);
|
||
|
input_report_abs(mxt->input, ABS_MT_POSITION_X, x);
|
||
|
input_report_abs(mxt->input, ABS_MT_POSITION_Y, y);
|
||
|
input_mt_sync(mxt->input);
|
||
|
}
|
||
|
#else
|
||
|
static inline void report_mt(int touch_number, int size, int x, int y, struct
|
||
|
mxt_data *mxt) {
|
||
|
if (touch_number == 0) {
|
||
|
input_report_abs(mxt->input, ABS_TOOL_WIDTH, size);
|
||
|
input_report_abs(mxt->input, ABS_X, x);
|
||
|
input_report_abs(mxt->input, ABS_Y, y);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static inline void report_gesture(int data, struct mxt_data *mxt)
|
||
|
{
|
||
|
input_event(mxt->input, EV_MSC, MSC_GESTURE, data);
|
||
|
}
|
||
|
|
||
|
|
||
|
static const u8 *object_type_name[] = {
|
||
|
[0] = "Reserved",
|
||
|
[5] = "GEN_MESSAGEPROCESSOR_T5",
|
||
|
[6] = "GEN_COMMANDPROCESSOR_T6",
|
||
|
[7] = "GEN_POWERCONFIG_T7",
|
||
|
[8] = "GEN_ACQUIRECONFIG_T8",
|
||
|
[9] = "TOUCH_MULTITOUCHSCREEN_T9",
|
||
|
[15] = "TOUCH_KEYARRAY_T15",
|
||
|
[17] = "SPT_COMMSCONFIG_T18",
|
||
|
[19] = "SPT_GPIOPWM_T19",
|
||
|
[20] = "PROCI_GRIPFACESUPPRESSION_T20",
|
||
|
[22] = "PROCG_NOISESUPPRESSION_T22",
|
||
|
[23] = "TOUCH_PROXIMITY_T23",
|
||
|
[24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24",
|
||
|
[25] = "SPT_SELFTEST_T25",
|
||
|
[27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27",
|
||
|
[28] = "SPT_CTECONFIG_T28",
|
||
|
[37] = "DEBUG_DIAGNOSTICS_T37",
|
||
|
[38] = "SPT_USER_DATA_T38",
|
||
|
[40] = "PROCI_GRIPSUPPRESSION_T40",
|
||
|
[41] = "PROCI_PALMSUPPRESSION_T41",
|
||
|
[42] = "PROCI_FACESUPPRESSION_T42",
|
||
|
[43] = "SPT_DIGITIZER_T43",
|
||
|
[44] = "SPT_MESSAGECOUNT_T44",
|
||
|
};
|
||
|
|
||
|
|
||
|
static u16 get_object_address(uint8_t object_type,
|
||
|
uint8_t instance,
|
||
|
struct mxt_object *object_table,
|
||
|
int max_objs);
|
||
|
|
||
|
int mxt_write_ap(struct mxt_data *mxt, u16 ap);
|
||
|
|
||
|
static int mxt_read_block_wo_addr(struct i2c_client *client,
|
||
|
u16 length,
|
||
|
u8 *value);
|
||
|
|
||
|
ssize_t debug_data_read(struct mxt_data *mxt, char *buf, size_t count,
|
||
|
loff_t *ppos, u8 debug_command){
|
||
|
int i;
|
||
|
u16 *data;
|
||
|
u16 diagnostics_reg;
|
||
|
int offset = 0;
|
||
|
int size;
|
||
|
int read_size;
|
||
|
int error;
|
||
|
char *buf_start;
|
||
|
u16 debug_data_addr;
|
||
|
u16 page_address;
|
||
|
u8 page;
|
||
|
u8 debug_command_reg;
|
||
|
|
||
|
data = mxt->debug_data;
|
||
|
if (data == NULL)
|
||
|
return -EIO;
|
||
|
|
||
|
/* If first read after open, read all data to buffer. */
|
||
|
if (mxt->current_debug_datap == 0){
|
||
|
|
||
|
diagnostics_reg = MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6,
|
||
|
mxt) +
|
||
|
MXT_ADR_T6_DIAGNOSTIC;
|
||
|
if (count > (mxt->device_info.num_nodes * 2))
|
||
|
count = mxt->device_info.num_nodes;
|
||
|
|
||
|
debug_data_addr = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt)+
|
||
|
MXT_ADR_T37_DATA;
|
||
|
page_address = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
|
||
|
MXT_ADR_T37_PAGE;
|
||
|
error = mxt_read_block(mxt->client, page_address, 1, &page);
|
||
|
if (error < 0)
|
||
|
return error;
|
||
|
mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
|
||
|
while (page != 0) {
|
||
|
error = mxt_write_byte(mxt->client,
|
||
|
diagnostics_reg,
|
||
|
MXT_CMD_T6_PAGE_DOWN);
|
||
|
if (error < 0)
|
||
|
return error;
|
||
|
/* Wait for command to be handled; when it has, the
|
||
|
register will be cleared. */
|
||
|
debug_command_reg = 1;
|
||
|
while (debug_command_reg != 0) {
|
||
|
error = mxt_read_block(mxt->client,
|
||
|
diagnostics_reg, 1,
|
||
|
&debug_command_reg);
|
||
|
if (error < 0)
|
||
|
return error;
|
||
|
mxt_debug(DEBUG_TRACE,
|
||
|
"Waiting for debug diag command "
|
||
|
"to propagate...\n");
|
||
|
|
||
|
}
|
||
|
error = mxt_read_block(mxt->client, page_address, 1,
|
||
|
&page);
|
||
|
if (error < 0)
|
||
|
return error;
|
||
|
mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Lock mutex to prevent writing some unwanted data to debug
|
||
|
* command register. User can still write through the char
|
||
|
* device interface though. TODO: fix?
|
||
|
*/
|
||
|
|
||
|
mutex_lock(&mxt->debug_mutex);
|
||
|
/* Configure Debug Diagnostics object to show deltas/refs */
|
||
|
error = mxt_write_byte(mxt->client, diagnostics_reg,
|
||
|
debug_command);
|
||
|
|
||
|
/* Wait for command to be handled; when it has, the
|
||
|
* register will be cleared. */
|
||
|
debug_command_reg = 1;
|
||
|
while (debug_command_reg != 0) {
|
||
|
error = mxt_read_block(mxt->client,
|
||
|
diagnostics_reg, 1,
|
||
|
&debug_command_reg);
|
||
|
if (error < 0)
|
||
|
return error;
|
||
|
mxt_debug(DEBUG_TRACE, "Waiting for debug diag command "
|
||
|
"to propagate...\n");
|
||
|
|
||
|
}
|
||
|
|
||
|
if (error < 0) {
|
||
|
printk (KERN_WARNING
|
||
|
"Error writing to maXTouch device!\n");
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
size = mxt->device_info.num_nodes * sizeof(u16);
|
||
|
|
||
|
while (size > 0) {
|
||
|
read_size = size > 128 ? 128 : size;
|
||
|
mxt_debug(DEBUG_TRACE,
|
||
|
"Debug data read loop, reading %d bytes...\n",
|
||
|
read_size);
|
||
|
error = mxt_read_block(mxt->client,
|
||
|
debug_data_addr,
|
||
|
read_size,
|
||
|
(u8 *) &data[offset]);
|
||
|
if (error < 0) {
|
||
|
printk(KERN_WARNING
|
||
|
"Error reading debug data\n");
|
||
|
goto error;
|
||
|
}
|
||
|
offset += read_size/2;
|
||
|
size -= read_size;
|
||
|
|
||
|
/* Select next page */
|
||
|
error = mxt_write_byte(mxt->client, diagnostics_reg,
|
||
|
MXT_CMD_T6_PAGE_UP);
|
||
|
if (error < 0) {
|
||
|
printk(KERN_WARNING
|
||
|
"Error writing to maXTouch device!\n");
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&mxt->debug_mutex);
|
||
|
}
|
||
|
|
||
|
buf_start = buf;
|
||
|
i = mxt->current_debug_datap;
|
||
|
|
||
|
while (((buf- buf_start) < (count - 6)) &&
|
||
|
(i < mxt->device_info.num_nodes)){
|
||
|
|
||
|
mxt->current_debug_datap++;
|
||
|
if (debug_command == MXT_CMD_T6_REFERENCES_MODE)
|
||
|
buf += sprintf(buf, "%d: %5d\n", i,
|
||
|
(u16) le16_to_cpu(data[i]));
|
||
|
else if (debug_command == MXT_CMD_T6_DELTAS_MODE)
|
||
|
buf += sprintf(buf, "%d: %5d\n", i,
|
||
|
(s16) le16_to_cpu(data[i]));
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
return (buf - buf_start);
|
||
|
error:
|
||
|
mutex_unlock(&mxt->debug_mutex);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
ssize_t deltas_read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
||
|
{
|
||
|
return debug_data_read(file->private_data, buf, count, ppos,
|
||
|
MXT_CMD_T6_DELTAS_MODE);
|
||
|
}
|
||
|
|
||
|
ssize_t refs_read(struct file *file, char *buf, size_t count,
|
||
|
loff_t *ppos)
|
||
|
{
|
||
|
return debug_data_read(file->private_data, buf, count, ppos,
|
||
|
MXT_CMD_T6_REFERENCES_MODE);
|
||
|
}
|
||
|
|
||
|
int debug_data_open(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
int i;
|
||
|
mxt = inode->i_private;
|
||
|
if (mxt == NULL)
|
||
|
return -EIO;
|
||
|
mxt->current_debug_datap = 0;
|
||
|
mxt->debug_data = kmalloc(mxt->device_info.num_nodes * sizeof(u16),
|
||
|
GFP_KERNEL);
|
||
|
if (mxt->debug_data == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
|
||
|
for (i = 0; i < mxt->device_info.num_nodes; i++)
|
||
|
mxt->debug_data[i] = 7777;
|
||
|
|
||
|
|
||
|
file->private_data = mxt;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int debug_data_release(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
mxt = file->private_data;
|
||
|
kfree(mxt->debug_data);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct file_operations delta_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = debug_data_open,
|
||
|
.release = debug_data_release,
|
||
|
.read = deltas_read,
|
||
|
};
|
||
|
|
||
|
static struct file_operations refs_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = debug_data_open,
|
||
|
.release = debug_data_release,
|
||
|
.read = refs_read,
|
||
|
};
|
||
|
|
||
|
|
||
|
int mxt_memory_open(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
mxt = container_of(inode->i_cdev, struct mxt_data, cdev);
|
||
|
if (mxt == NULL)
|
||
|
return -EIO;
|
||
|
file->private_data = mxt;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mxt_message_open(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
mxt = container_of(inode->i_cdev, struct mxt_data, cdev_messages);
|
||
|
if (mxt == NULL)
|
||
|
return -EIO;
|
||
|
file->private_data = mxt;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
ssize_t mxt_memory_read(struct file *file, char *buf, size_t count,
|
||
|
loff_t *ppos)
|
||
|
{
|
||
|
int i;
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
mxt = file->private_data;
|
||
|
if (mxt->valid_ap){
|
||
|
mxt_debug(DEBUG_TRACE, "Reading %d bytes from current ap\n",
|
||
|
(int) count);
|
||
|
i = mxt_read_block_wo_addr(mxt->client, count, (u8 *) buf);
|
||
|
} else {
|
||
|
mxt_debug(DEBUG_TRACE, "Address pointer changed since set;"
|
||
|
"writing AP (%d) before reading %d bytes",
|
||
|
mxt->address_pointer, (int) count);
|
||
|
i = mxt_read_block(mxt->client, mxt->address_pointer, count,
|
||
|
buf);
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
ssize_t mxt_memory_write(struct file *file, const char *buf, size_t count,
|
||
|
loff_t *ppos)
|
||
|
{
|
||
|
int i;
|
||
|
int whole_blocks;
|
||
|
int last_block_size;
|
||
|
struct mxt_data *mxt;
|
||
|
u16 address;
|
||
|
|
||
|
mxt = file->private_data;
|
||
|
address = mxt->address_pointer;
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "mxt_memory_write entered\n");
|
||
|
whole_blocks = count / I2C_PAYLOAD_SIZE;
|
||
|
last_block_size = count % I2C_PAYLOAD_SIZE;
|
||
|
|
||
|
for (i = 0; i < whole_blocks; i++) {
|
||
|
mxt_debug(DEBUG_TRACE, "About to write to %d...",
|
||
|
address);
|
||
|
mxt_write_block(mxt->client, address, I2C_PAYLOAD_SIZE,
|
||
|
(u8 *) buf);
|
||
|
address += I2C_PAYLOAD_SIZE;
|
||
|
buf += I2C_PAYLOAD_SIZE;
|
||
|
}
|
||
|
|
||
|
mxt_write_block(mxt->client, address, last_block_size, (u8 *) buf);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static long mxt_ioctl(struct file *file,
|
||
|
unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
int retval;
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
retval = 0;
|
||
|
mxt = file->private_data;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case MXT_SET_ADDRESS_IOCTL:
|
||
|
retval = mxt_write_ap(mxt, (u16) arg);
|
||
|
if (retval >= 0) {
|
||
|
mxt->address_pointer = (u16) arg;
|
||
|
mxt->valid_ap = 1;
|
||
|
}
|
||
|
break;
|
||
|
case MXT_RESET_IOCTL:
|
||
|
retval = mxt_write_byte(mxt->client,
|
||
|
MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
|
||
|
MXT_ADR_T6_RESET,
|
||
|
1);
|
||
|
break;
|
||
|
case MXT_CALIBRATE_IOCTL:
|
||
|
retval = mxt_write_byte(mxt->client,
|
||
|
MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
|
||
|
MXT_ADR_T6_CALIBRATE,
|
||
|
1);
|
||
|
|
||
|
break;
|
||
|
case MXT_BACKUP_IOCTL:
|
||
|
retval = mxt_write_byte(mxt->client,
|
||
|
MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
|
||
|
MXT_ADR_T6_BACKUPNV,
|
||
|
MXT_CMD_T6_BACKUP);
|
||
|
break;
|
||
|
case MXT_NONTOUCH_MSG_IOCTL:
|
||
|
mxt->nontouch_msg_only = 1;
|
||
|
break;
|
||
|
case MXT_ALL_MSG_IOCTL:
|
||
|
mxt->nontouch_msg_only = 0;
|
||
|
break;
|
||
|
default:
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Copies messages from buffer to user space.
|
||
|
*
|
||
|
* NOTE: if less than (mxt->message_size * 5 + 1) bytes requested,
|
||
|
* this will return 0!
|
||
|
*
|
||
|
*/
|
||
|
ssize_t mxt_message_read(struct file *file, char *buf, size_t count,
|
||
|
loff_t *ppos)
|
||
|
{
|
||
|
int i;
|
||
|
struct mxt_data *mxt;
|
||
|
char *buf_start;
|
||
|
|
||
|
mxt = file->private_data;
|
||
|
if (mxt == NULL)
|
||
|
return -EIO;
|
||
|
buf_start = buf;
|
||
|
|
||
|
mutex_lock(&mxt->msg_mutex);
|
||
|
/* Copy messages until buffer empty, or 'count' bytes written */
|
||
|
while ((mxt->msg_buffer_startp != mxt->msg_buffer_endp) &&
|
||
|
((buf - buf_start) < (count - (5 * mxt->message_size) - 1))){
|
||
|
|
||
|
for (i = 0; i < mxt->message_size; i++){
|
||
|
buf += sprintf(buf, "[%2X] ",
|
||
|
*(mxt->messages + mxt->msg_buffer_endp *
|
||
|
mxt->message_size + i));
|
||
|
}
|
||
|
buf += sprintf(buf, "\n");
|
||
|
if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
|
||
|
mxt->msg_buffer_endp++;
|
||
|
else
|
||
|
mxt->msg_buffer_endp = 0;
|
||
|
}
|
||
|
mutex_unlock(&mxt->msg_mutex);
|
||
|
return (buf - buf_start);
|
||
|
}
|
||
|
|
||
|
static struct file_operations mxt_message_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = mxt_message_open,
|
||
|
.read = mxt_message_read,
|
||
|
};
|
||
|
|
||
|
static struct file_operations mxt_memory_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = mxt_memory_open,
|
||
|
.read = mxt_memory_read,
|
||
|
.write = mxt_memory_write,
|
||
|
.unlocked_ioctl = mxt_ioctl,
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Writes the address pointer (to set up following reads). */
|
||
|
|
||
|
int mxt_write_ap(struct mxt_data *mxt, u16 ap)
|
||
|
{
|
||
|
struct i2c_client *client;
|
||
|
__le16 le_ap = cpu_to_le16(ap);
|
||
|
client = mxt->client;
|
||
|
if (mxt != NULL)
|
||
|
mxt->last_read_addr = -1;
|
||
|
if (i2c_master_send(client, (u8 *) &le_ap, 2) == 2) {
|
||
|
mxt_debug(DEBUG_TRACE, "Address pointer set to %d\n", ap);
|
||
|
return 0;
|
||
|
} else {
|
||
|
mxt_debug(DEBUG_INFO, "Error writing address pointer!\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Calculates the 24-bit CRC sum. */
|
||
|
static u32 CRC_24(u32 crc, u8 byte1, u8 byte2)
|
||
|
{
|
||
|
static const u32 crcpoly = 0x80001B;
|
||
|
u32 result;
|
||
|
u32 data_word;
|
||
|
|
||
|
data_word = ((((u16) byte2) << 8u) | byte1);
|
||
|
result = ((crc << 1u) ^ data_word);
|
||
|
if (result & 0x1000000)
|
||
|
result ^= crcpoly;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/* Returns object address in mXT chip, or zero if object is not found */
|
||
|
static u16 get_object_address(uint8_t object_type,
|
||
|
uint8_t instance,
|
||
|
struct mxt_object *object_table,
|
||
|
int max_objs)
|
||
|
{
|
||
|
uint8_t object_table_index = 0;
|
||
|
uint8_t address_found = 0;
|
||
|
uint16_t address = 0;
|
||
|
struct mxt_object *obj;
|
||
|
|
||
|
while ((object_table_index < max_objs) && !address_found) {
|
||
|
obj = &object_table[object_table_index];
|
||
|
if (obj->type == object_type) {
|
||
|
address_found = 1;
|
||
|
/* Are there enough instances defined in the FW? */
|
||
|
if (obj->instances >= instance) {
|
||
|
address = obj->chip_addr +
|
||
|
(obj->size + 1) * instance;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
object_table_index++;
|
||
|
}
|
||
|
return address;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Reads a block of bytes from given address from mXT chip. If we are
|
||
|
* reading from message window, and previous read was from message window,
|
||
|
* there's no need to write the address pointer: the mXT chip will
|
||
|
* automatically set the address pointer back to message window start.
|
||
|
*/
|
||
|
|
||
|
static int mxt_read_block(struct i2c_client *client,
|
||
|
u16 addr,
|
||
|
u16 length,
|
||
|
u8 *value)
|
||
|
{
|
||
|
struct i2c_adapter *adapter = client->adapter;
|
||
|
struct i2c_msg msg[2];
|
||
|
__le16 le_addr;
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
mxt = i2c_get_clientdata(client);
|
||
|
|
||
|
if (mxt != NULL) {
|
||
|
if ((mxt->last_read_addr == addr) &&
|
||
|
(addr == mxt->msg_proc_addr)) {
|
||
|
if (i2c_master_recv(client, value, length) == length)
|
||
|
return length;
|
||
|
else
|
||
|
return -EIO;
|
||
|
} else {
|
||
|
mxt->last_read_addr = addr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "Writing address pointer & reading %d bytes "
|
||
|
"in on i2c transaction...\n", length);
|
||
|
|
||
|
le_addr = cpu_to_le16(addr);
|
||
|
msg[0].addr = client->addr;
|
||
|
msg[0].flags = 0x00;
|
||
|
msg[0].len = 2;
|
||
|
msg[0].buf = (u8 *) &le_addr;
|
||
|
|
||
|
msg[1].addr = client->addr;
|
||
|
msg[1].flags = I2C_M_RD;
|
||
|
msg[1].len = length;
|
||
|
msg[1].buf = (u8 *) value;
|
||
|
if (i2c_transfer(adapter, msg, 2) == 2)
|
||
|
return length;
|
||
|
else
|
||
|
return -EIO;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Reads a block of bytes from current address from mXT chip. */
|
||
|
|
||
|
static int mxt_read_block_wo_addr(struct i2c_client *client,
|
||
|
u16 length,
|
||
|
u8 *value)
|
||
|
{
|
||
|
|
||
|
|
||
|
if (i2c_master_recv(client, value, length) == length) {
|
||
|
mxt_debug(DEBUG_TRACE, "I2C block read ok\n");
|
||
|
return length;
|
||
|
} else {
|
||
|
mxt_debug(DEBUG_INFO, "I2C block read failed\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Writes one byte to given address in mXT chip. */
|
||
|
|
||
|
static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value)
|
||
|
{
|
||
|
struct {
|
||
|
__le16 le_addr;
|
||
|
u8 data;
|
||
|
|
||
|
} i2c_byte_transfer;
|
||
|
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
mxt = i2c_get_clientdata(client);
|
||
|
if (mxt != NULL)
|
||
|
mxt->last_read_addr = -1;
|
||
|
i2c_byte_transfer.le_addr = cpu_to_le16(addr);
|
||
|
i2c_byte_transfer.data = value;
|
||
|
if (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3)
|
||
|
return 0;
|
||
|
else
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Writes a block of bytes (max 256) to given address in mXT chip. */
|
||
|
static int mxt_write_block(struct i2c_client *client,
|
||
|
u16 addr,
|
||
|
u16 length,
|
||
|
u8 *value)
|
||
|
{
|
||
|
int i;
|
||
|
struct {
|
||
|
__le16 le_addr;
|
||
|
u8 data[256];
|
||
|
|
||
|
} i2c_block_transfer;
|
||
|
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "Writing %d bytes to %d...", length, addr);
|
||
|
if (length > 256)
|
||
|
return -EINVAL;
|
||
|
mxt = i2c_get_clientdata(client);
|
||
|
if (mxt != NULL)
|
||
|
mxt->last_read_addr = -1;
|
||
|
for (i = 0; i < length; i++)
|
||
|
i2c_block_transfer.data[i] = *value++;
|
||
|
i2c_block_transfer.le_addr = cpu_to_le16(addr);
|
||
|
i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2);
|
||
|
if (i == (length + 2))
|
||
|
return length;
|
||
|
else
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
/* Calculates the CRC value for mXT infoblock. */
|
||
|
int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size)
|
||
|
{
|
||
|
u32 crc = 0;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < (crc_area_size - 1); i = i + 2)
|
||
|
crc = CRC_24(crc, *(data + i), *(data + i + 1));
|
||
|
/* If uneven size, pad with zero */
|
||
|
if (crc_area_size & 0x0001)
|
||
|
crc = CRC_24(crc, *(data + i), 0);
|
||
|
/* Return only 24 bits of CRC. */
|
||
|
*crc_result = (crc & 0x00FFFFFF);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Processes a touchscreen message. */
|
||
|
void process_T9_message(u8 *message, struct mxt_data *mxt, int last_touch)
|
||
|
{
|
||
|
|
||
|
struct input_dev *input;
|
||
|
u8 status;
|
||
|
u16 xpos = 0xFFFF;
|
||
|
u16 ypos = 0xFFFF;
|
||
|
u8 touch_size = 255;
|
||
|
u8 touch_number;
|
||
|
u8 amplitude;
|
||
|
u8 report_id;
|
||
|
|
||
|
static int stored_size[10];
|
||
|
static int stored_x[10];
|
||
|
static int stored_y[10];
|
||
|
int i;
|
||
|
int active_touches = 0;
|
||
|
/*
|
||
|
* If the 'last_touch' flag is set, we have received all the touch
|
||
|
* messages
|
||
|
* there are available in this cycle, so send the events for touches
|
||
|
* that are
|
||
|
* active.
|
||
|
*/
|
||
|
if (last_touch){
|
||
|
/* TODO: For compatibility with single-touch systems, send ABS_X &
|
||
|
* ABS_Y */
|
||
|
/*
|
||
|
if (stored_size[0]){
|
||
|
input_report_abs(mxt->input, ABS_X, stored_x[0]);
|
||
|
input_report_abs(mxt->input, ABS_Y, stored_y[0]);
|
||
|
}*/
|
||
|
|
||
|
|
||
|
for (i = 0; i < 10; i++){
|
||
|
if (stored_size[i]){
|
||
|
active_touches++;
|
||
|
input_report_abs(mxt->input,
|
||
|
ABS_MT_TRACKING_ID,
|
||
|
i);
|
||
|
input_report_abs(mxt->input,
|
||
|
ABS_MT_TOUCH_MAJOR,
|
||
|
stored_size[i]);
|
||
|
input_report_abs(mxt->input,
|
||
|
ABS_MT_POSITION_X,
|
||
|
stored_x[i]);
|
||
|
input_report_abs(mxt->input,
|
||
|
ABS_MT_POSITION_Y,
|
||
|
stored_y[i]);
|
||
|
input_mt_sync(mxt->input);
|
||
|
}
|
||
|
}
|
||
|
input_report_key(mxt->input, BTN_TOUCH, !!active_touches);
|
||
|
if (active_touches == 0)
|
||
|
input_mt_sync(mxt->input);
|
||
|
input_sync(mxt->input);
|
||
|
|
||
|
}else{
|
||
|
|
||
|
input = mxt->input;
|
||
|
status = message[MXT_MSG_T9_STATUS];
|
||
|
report_id = message[0];
|
||
|
|
||
|
if (status & MXT_MSGB_T9_SUPPRESS) {
|
||
|
/* Touch has been suppressed by grip/face */
|
||
|
/* detection */
|
||
|
mxt_debug(DEBUG_TRACE, "SUPRESS");
|
||
|
} else {
|
||
|
/* Put together the 10-/12-bit coordinate values. */
|
||
|
xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
|
||
|
ypos = message[MXT_MSG_T9_YPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF);
|
||
|
|
||
|
if (mxt->max_x_val < 1024)
|
||
|
xpos >>= 2;
|
||
|
if (mxt->max_y_val < 1024)
|
||
|
ypos >>= 2;
|
||
|
|
||
|
touch_number = message[MXT_MSG_REPORTID] -
|
||
|
mxt->rid_map[report_id].first_rid;
|
||
|
|
||
|
stored_x[touch_number] = xpos;
|
||
|
stored_y[touch_number] = ypos;
|
||
|
|
||
|
if (status & MXT_MSGB_T9_DETECT) {
|
||
|
/*
|
||
|
* mXT224 reports the number of touched nodes,
|
||
|
* so the exact value for touch ellipse major
|
||
|
* axis length in nodes would be 2*sqrt(touch_size/pi)
|
||
|
* (assuming round touch shape), which would then need
|
||
|
* to be scaled using information about how many sensor
|
||
|
* lines we do have. So this is very much simplified,
|
||
|
* but sufficient for most if not all apps?
|
||
|
*/
|
||
|
touch_size = message[MXT_MSG_T9_TCHAREA];
|
||
|
touch_size = touch_size >> 2;
|
||
|
if (!touch_size)
|
||
|
touch_size = 1;
|
||
|
/*
|
||
|
* report_mt(touch_number, touch_size, xpos, ypos, mxt);
|
||
|
*/
|
||
|
|
||
|
stored_size[touch_number] = touch_size;
|
||
|
|
||
|
if (status & MXT_MSGB_T9_AMP)
|
||
|
/* Amplitude of touch has changed */
|
||
|
amplitude = message[MXT_MSG_T9_TCHAMPLITUDE];
|
||
|
}
|
||
|
|
||
|
if (status & MXT_MSGB_T9_RELEASE) {
|
||
|
/* The previously reported touch has been removed.*/
|
||
|
/* report_mt(touch_number, 0, xpos, ypos, mxt); */
|
||
|
stored_size[touch_number] = 0;
|
||
|
}
|
||
|
|
||
|
/* input_sync(input); */
|
||
|
}
|
||
|
|
||
|
if (status & MXT_MSGB_T9_SUPPRESS) {
|
||
|
mxt_debug(DEBUG_TRACE, "SUPRESS");
|
||
|
} else {
|
||
|
if (status & MXT_MSGB_T9_DETECT) {
|
||
|
mxt_debug(DEBUG_TRACE, "DETECT:%s%s%s%s",
|
||
|
((status & MXT_MSGB_T9_PRESS) ? " PRESS" : ""),
|
||
|
((status & MXT_MSGB_T9_MOVE) ? " MOVE" : ""),
|
||
|
((status & MXT_MSGB_T9_AMP) ? " AMP" : ""),
|
||
|
((status & MXT_MSGB_T9_VECTOR) ? " VECT" : ""));
|
||
|
|
||
|
} else if (status & MXT_MSGB_T9_RELEASE) {
|
||
|
mxt_debug(DEBUG_TRACE, "RELEASE");
|
||
|
}
|
||
|
}
|
||
|
mxt_debug(DEBUG_TRACE, "X=%d, Y=%d, TOUCHSIZE=%d",
|
||
|
xpos, ypos, touch_size);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
int process_message(u8 *message, u8 object, struct mxt_data *mxt)
|
||
|
{
|
||
|
struct i2c_client *client;
|
||
|
u8 status;
|
||
|
u16 xpos = 0xFFFF;
|
||
|
u16 ypos = 0xFFFF;
|
||
|
u8 event;
|
||
|
u8 direction;
|
||
|
u16 distance;
|
||
|
u8 length;
|
||
|
u8 report_id;
|
||
|
static u8 error_cond = 0;
|
||
|
|
||
|
client = mxt->client;
|
||
|
length = mxt->message_size;
|
||
|
report_id = message[0];
|
||
|
|
||
|
if ((mxt->nontouch_msg_only == 0) ||
|
||
|
(!IS_TOUCH_OBJECT(object))){
|
||
|
mutex_lock(&mxt->msg_mutex);
|
||
|
/* Copy the message to buffer */
|
||
|
if (mxt->msg_buffer_startp < MXT_MESSAGE_BUFFER_SIZE) {
|
||
|
mxt->msg_buffer_startp++;
|
||
|
} else {
|
||
|
mxt->msg_buffer_startp = 0;
|
||
|
}
|
||
|
|
||
|
if (mxt->msg_buffer_startp == mxt->msg_buffer_endp) {
|
||
|
mxt_debug(DEBUG_TRACE,
|
||
|
"Message buf full, discarding last entry.\n");
|
||
|
if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE) {
|
||
|
mxt->msg_buffer_endp++;
|
||
|
} else {
|
||
|
mxt->msg_buffer_endp = 0;
|
||
|
}
|
||
|
}
|
||
|
memcpy((mxt->messages + mxt->msg_buffer_startp * length),
|
||
|
message,
|
||
|
length);
|
||
|
mutex_unlock(&mxt->msg_mutex);
|
||
|
}
|
||
|
|
||
|
switch (object) {
|
||
|
case MXT_GEN_COMMANDPROCESSOR_T6:
|
||
|
status = message[1];
|
||
|
|
||
|
if (status & MXT_MSGB_T6_COMSERR) {
|
||
|
if ((!error_cond) & MXT_MSGB_T6_COMSERR){
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch checksum error\n");
|
||
|
error_cond |= MXT_MSGB_T6_COMSERR;
|
||
|
}
|
||
|
}
|
||
|
if (status & MXT_MSGB_T6_CFGERR) {
|
||
|
/*
|
||
|
* Configuration error. A proper configuration
|
||
|
* needs to be written to chip and backed up.
|
||
|
*/
|
||
|
if ((!error_cond) & MXT_MSGB_T6_CFGERR){
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch configuration error\n");
|
||
|
error_cond |= MXT_MSGB_T6_CFGERR;
|
||
|
}
|
||
|
}
|
||
|
if (status & MXT_MSGB_T6_CAL) {
|
||
|
/* Calibration in action, no need to react */
|
||
|
dev_dbg(&client->dev,
|
||
|
"maXTouch calibration in progress\n");
|
||
|
}
|
||
|
if (status & MXT_MSGB_T6_SIGERR) {
|
||
|
/*
|
||
|
* Signal acquisition error, something is seriously
|
||
|
* wrong, not much we can in the driver to correct
|
||
|
* this
|
||
|
*/
|
||
|
if ((!error_cond) & MXT_MSGB_T6_SIGERR){
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch acquisition error\n");
|
||
|
error_cond |= MXT_MSGB_T6_SIGERR;
|
||
|
}
|
||
|
}
|
||
|
if (status & MXT_MSGB_T6_OFL) {
|
||
|
/*
|
||
|
* Cycle overflow, the acquisition interval is too
|
||
|
* short.
|
||
|
*/
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch cycle overflow\n");
|
||
|
}
|
||
|
if (status & MXT_MSGB_T6_RESET) {
|
||
|
/* Chip has reseted, no need to react. */
|
||
|
dev_dbg(&client->dev,
|
||
|
"maXTouch chip reset\n");
|
||
|
}
|
||
|
if (status == 0) {
|
||
|
/* Chip status back to normal. */
|
||
|
dev_dbg(&client->dev,
|
||
|
"maXTouch status normal\n");
|
||
|
error_cond = 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MXT_TOUCH_MULTITOUCHSCREEN_T9:
|
||
|
process_T9_message(message, mxt, 0);
|
||
|
break;
|
||
|
|
||
|
case MXT_SPT_GPIOPWM_T19:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving GPIO message\n");
|
||
|
break;
|
||
|
|
||
|
case MXT_PROCI_GRIPFACESUPPRESSION_T20:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving face suppression msg\n");
|
||
|
break;
|
||
|
|
||
|
case MXT_PROCG_NOISESUPPRESSION_T22:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving noise suppression msg\n");
|
||
|
status = message[MXT_MSG_T22_STATUS];
|
||
|
if (status & MXT_MSGB_T22_FHCHG) {
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"maXTouch: Freq changed\n");
|
||
|
}
|
||
|
if (status & MXT_MSGB_T22_GCAFERR) {
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"maXTouch: High noise "
|
||
|
"level\n");
|
||
|
}
|
||
|
if (status & MXT_MSGB_T22_FHERR) {
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"maXTouch: Freq changed - "
|
||
|
"Noise level too high\n");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving one-touch gesture msg\n");
|
||
|
|
||
|
event = message[MXT_MSG_T24_STATUS] & 0x0F;
|
||
|
xpos = message[MXT_MSG_T24_XPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F);
|
||
|
ypos = message[MXT_MSG_T24_YPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F);
|
||
|
if (mxt->max_x_val < 1024)
|
||
|
xpos >>= 2;
|
||
|
if (mxt->max_y_val < 1024)
|
||
|
ypos >>= 2;
|
||
|
direction = message[MXT_MSG_T24_DIR];
|
||
|
distance = message[MXT_MSG_T24_DIST] +
|
||
|
(message[MXT_MSG_T24_DIST + 1] << 16);
|
||
|
|
||
|
report_gesture((event << 24) | (direction << 16) | distance,
|
||
|
mxt);
|
||
|
report_gesture((xpos << 16) | ypos, mxt);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case MXT_SPT_SELFTEST_T25:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving Self-Test msg\n");
|
||
|
|
||
|
if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_OK) {
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"maXTouch: Self-Test OK\n");
|
||
|
|
||
|
} else {
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch: Self-Test Failed [%02x]:"
|
||
|
"{%02x,%02x,%02x,%02x,%02x}\n",
|
||
|
message[MXT_MSG_T25_STATUS],
|
||
|
message[MXT_MSG_T25_STATUS + 0],
|
||
|
message[MXT_MSG_T25_STATUS + 1],
|
||
|
message[MXT_MSG_T25_STATUS + 2],
|
||
|
message[MXT_MSG_T25_STATUS + 3],
|
||
|
message[MXT_MSG_T25_STATUS + 4]
|
||
|
);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving 2-touch gesture message\n");
|
||
|
|
||
|
event = message[MXT_MSG_T27_STATUS] & 0xF0;
|
||
|
xpos = message[MXT_MSG_T27_XPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T27_XYPOSLSB] >> 4) & 0x0F);
|
||
|
ypos = message[MXT_MSG_T27_YPOSMSB] * 16 +
|
||
|
((message[MXT_MSG_T27_XYPOSLSB] >> 0) & 0x0F);
|
||
|
if (mxt->max_x_val < 1024)
|
||
|
xpos >>= 2;
|
||
|
if (mxt->max_y_val < 1024)
|
||
|
ypos >>= 2;
|
||
|
|
||
|
direction = message[MXT_MSG_T27_ANGLE];
|
||
|
distance = message[MXT_MSG_T27_SEPARATION] +
|
||
|
(message[MXT_MSG_T27_SEPARATION + 1] << 16);
|
||
|
|
||
|
report_gesture((event << 24) | (direction << 16) | distance,
|
||
|
mxt);
|
||
|
report_gesture((xpos << 16) | ypos, mxt);
|
||
|
|
||
|
|
||
|
break;
|
||
|
|
||
|
case MXT_SPT_CTECONFIG_T28:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"Receiving CTE message...\n");
|
||
|
status = message[MXT_MSG_T28_STATUS];
|
||
|
if (status & MXT_MSGB_T28_CHKERR)
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch: Power-Up CRC failure\n");
|
||
|
|
||
|
break;
|
||
|
default:
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev,
|
||
|
"maXTouch: Unknown message!\n");
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Processes messages when the interrupt line (CHG) is asserted. Keeps
|
||
|
* reading messages until a message with report ID 0xFF is received,
|
||
|
* which indicates that there is no more new messages.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
static void mxt_worker(struct work_struct *work)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
struct i2c_client *client;
|
||
|
|
||
|
u8 *message;
|
||
|
u16 message_length;
|
||
|
u16 message_addr;
|
||
|
u8 report_id;
|
||
|
u8 object;
|
||
|
int error;
|
||
|
int i;
|
||
|
char *message_string;
|
||
|
char *message_start;
|
||
|
|
||
|
message = NULL;
|
||
|
mxt = container_of(work, struct mxt_data, dwork.work);
|
||
|
client = mxt->client;
|
||
|
message_addr = mxt->msg_proc_addr;
|
||
|
message_length = mxt->message_size;
|
||
|
|
||
|
if (message_length < 256) {
|
||
|
message = kmalloc(message_length, GFP_KERNEL);
|
||
|
if (message == NULL) {
|
||
|
dev_err(&client->dev, "Error allocating memory\n");
|
||
|
goto fail_worker;
|
||
|
}
|
||
|
} else {
|
||
|
dev_err(&client->dev,
|
||
|
"Message length larger than 256 bytes not supported\n");
|
||
|
goto fail_worker;
|
||
|
}
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch worker active:\n");
|
||
|
|
||
|
do {
|
||
|
/* Read next message, reread on failure. */
|
||
|
/* TODO: message length, CRC included? */
|
||
|
mxt->message_counter++;
|
||
|
for (i = 1; i < I2C_RETRY_COUNT; i++) {
|
||
|
error = mxt_read_block(client,
|
||
|
message_addr,
|
||
|
message_length - 1,
|
||
|
message);
|
||
|
if (error >= 0)
|
||
|
break;
|
||
|
mxt->read_fail_counter++;
|
||
|
dev_err(&client->dev,
|
||
|
"Failure reading maxTouch device\n");
|
||
|
}
|
||
|
if (error < 0) {
|
||
|
kfree(message);
|
||
|
goto fail_worker;
|
||
|
}
|
||
|
|
||
|
if (mxt->address_pointer != message_addr)
|
||
|
mxt->valid_ap = 0;
|
||
|
report_id = message[0];
|
||
|
|
||
|
if (debug >= DEBUG_RAW) {
|
||
|
mxt_debug(DEBUG_RAW, "%s message [msg count: %08x]:",
|
||
|
REPORT_ID_TO_OBJECT_NAME(report_id, mxt),
|
||
|
mxt->message_counter
|
||
|
);
|
||
|
/* 5 characters per one byte */
|
||
|
message_string = kmalloc(message_length * 5,
|
||
|
GFP_KERNEL);
|
||
|
if (message_string == NULL) {
|
||
|
dev_err(&client->dev,
|
||
|
"Error allocating memory\n");
|
||
|
kfree(message);
|
||
|
goto fail_worker;
|
||
|
}
|
||
|
message_start = message_string;
|
||
|
for (i = 0; i < message_length; i++) {
|
||
|
message_string +=
|
||
|
sprintf(message_string,
|
||
|
"0x%02X ", message[i]);
|
||
|
}
|
||
|
mxt_debug(DEBUG_RAW, "%s", message_start);
|
||
|
kfree(message_start);
|
||
|
}
|
||
|
|
||
|
if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) {
|
||
|
memcpy(mxt->last_message, message, message_length);
|
||
|
mxt->new_msgs = 1;
|
||
|
smp_wmb();
|
||
|
/* Get type of object and process the message */
|
||
|
object = mxt->rid_map[report_id].object;
|
||
|
process_message(message, object, mxt);
|
||
|
}
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "chgline: %d\n", mxt->read_chg());
|
||
|
} while (comms ? (mxt->read_chg() == 0) :
|
||
|
((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)));
|
||
|
|
||
|
/* All messages processed, send the events) */
|
||
|
process_T9_message(NULL, mxt, 1);
|
||
|
|
||
|
kfree(message);
|
||
|
|
||
|
fail_worker:
|
||
|
/* Make sure we just didn't miss a interrupt. */
|
||
|
if (mxt->read_chg() == 0){
|
||
|
schedule_delayed_work(&mxt->dwork, 0);
|
||
|
} else
|
||
|
enable_irq(mxt->irq);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* The maXTouch device will signal the host about a new message by asserting
|
||
|
* the CHG line. This ISR schedules a worker routine to read the message when
|
||
|
* that happens.
|
||
|
*/
|
||
|
|
||
|
static irqreturn_t mxt_irq_handler(int irq, void *_mxt)
|
||
|
{
|
||
|
struct mxt_data *mxt = _mxt;
|
||
|
|
||
|
mxt->irq_counter++;
|
||
|
if (mxt->valid_interrupt()) {
|
||
|
/* Send the signal only if falling edge generated the irq. */
|
||
|
disable_irq_nosync(mxt->irq);
|
||
|
schedule_delayed_work(&mxt->dwork, 0);
|
||
|
mxt->valid_irq_counter++;
|
||
|
} else {
|
||
|
mxt->invalid_irq_counter++;
|
||
|
return IRQ_NONE;
|
||
|
}
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************************/
|
||
|
/* Initialization of driver */
|
||
|
/******************************************************************************/
|
||
|
|
||
|
static int __devinit mxt_identify(struct i2c_client *client,
|
||
|
struct mxt_data *mxt,
|
||
|
u8 *id_block_data)
|
||
|
{
|
||
|
u8 buf[MXT_ID_BLOCK_SIZE];
|
||
|
int error;
|
||
|
int identified;
|
||
|
|
||
|
identified = 0;
|
||
|
|
||
|
/* Read Device info to check if chip is valid */
|
||
|
error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE,
|
||
|
(u8 *) buf);
|
||
|
|
||
|
if (error < 0) {
|
||
|
mxt->read_fail_counter++;
|
||
|
dev_err(&client->dev, "Failure accessing maXTouch device\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE);
|
||
|
|
||
|
mxt->device_info.family_id = buf[0];
|
||
|
mxt->device_info.variant_id = buf[1];
|
||
|
mxt->device_info.major = ((buf[2] >> 4) & 0x0F);
|
||
|
mxt->device_info.minor = (buf[2] & 0x0F);
|
||
|
mxt->device_info.build = buf[3];
|
||
|
mxt->device_info.x_size = buf[4];
|
||
|
mxt->device_info.y_size = buf[5];
|
||
|
mxt->device_info.num_objs = buf[6];
|
||
|
mxt->device_info.num_nodes = mxt->device_info.x_size *
|
||
|
mxt->device_info.y_size;
|
||
|
|
||
|
/*
|
||
|
* Check Family & Variant Info; warn if not recognized but
|
||
|
* still continue.
|
||
|
*/
|
||
|
|
||
|
/* MXT224 */
|
||
|
if (mxt->device_info.family_id == MXT224_FAMILYID) {
|
||
|
strcpy(mxt->device_info.family_name, "mXT224");
|
||
|
|
||
|
if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) {
|
||
|
strcpy(mxt->device_info.variant_name, "Calibrated");
|
||
|
} else if (mxt->device_info.variant_id ==
|
||
|
MXT224_UNCAL_VARIANTID) {
|
||
|
strcpy(mxt->device_info.variant_name, "Uncalibrated");
|
||
|
} else {
|
||
|
dev_err(&client->dev,
|
||
|
"Warning: maXTouch Variant ID [%d] not "
|
||
|
"supported\n",
|
||
|
mxt->device_info.variant_id);
|
||
|
strcpy(mxt->device_info.variant_name, "UNKNOWN");
|
||
|
/* identified = -ENXIO; */
|
||
|
}
|
||
|
|
||
|
/* MXT1386 */
|
||
|
} else if (mxt->device_info.family_id == MXT1386_FAMILYID) {
|
||
|
strcpy(mxt->device_info.family_name, "mXT1386");
|
||
|
|
||
|
if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) {
|
||
|
strcpy(mxt->device_info.variant_name, "Calibrated");
|
||
|
} else {
|
||
|
dev_err(&client->dev,
|
||
|
"Warning: maXTouch Variant ID [%d] not "
|
||
|
"supported\n",
|
||
|
mxt->device_info.variant_id);
|
||
|
strcpy(mxt->device_info.variant_name, "UNKNOWN");
|
||
|
/* identified = -ENXIO; */
|
||
|
}
|
||
|
/* Unknown family ID! */
|
||
|
} else {
|
||
|
dev_err(&client->dev,
|
||
|
"Warning: maXTouch Family ID [%d] not supported\n",
|
||
|
mxt->device_info.family_id);
|
||
|
strcpy(mxt->device_info.family_name, "UNKNOWN");
|
||
|
strcpy(mxt->device_info.variant_name, "UNKNOWN");
|
||
|
/* identified = -ENXIO; */
|
||
|
}
|
||
|
|
||
|
dev_info(
|
||
|
&client->dev,
|
||
|
"Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware "
|
||
|
"version [%d.%d] Build %d\n",
|
||
|
mxt->device_info.family_name,
|
||
|
mxt->device_info.family_id,
|
||
|
mxt->device_info.variant_name,
|
||
|
mxt->device_info.variant_id,
|
||
|
mxt->device_info.major,
|
||
|
mxt->device_info.minor,
|
||
|
mxt->device_info.build
|
||
|
);
|
||
|
dev_dbg(
|
||
|
&client->dev,
|
||
|
"Atmel maXTouch Configuration "
|
||
|
"[X: %d] x [Y: %d]\n",
|
||
|
mxt->device_info.x_size,
|
||
|
mxt->device_info.y_size
|
||
|
);
|
||
|
return identified;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reads the object table from maXTouch chip to get object data like
|
||
|
* address, size, report id. For Info Block CRC calculation, already read
|
||
|
* id data is passed to this function too (Info Block consists of the ID
|
||
|
* block and object table).
|
||
|
*
|
||
|
*/
|
||
|
static int __devinit mxt_read_object_table(struct i2c_client *client,
|
||
|
struct mxt_data *mxt,
|
||
|
u8 *raw_id_data)
|
||
|
{
|
||
|
u16 report_id_count;
|
||
|
u8 buf[MXT_OBJECT_TABLE_ELEMENT_SIZE];
|
||
|
u8 *raw_ib_data;
|
||
|
u8 object_type;
|
||
|
u16 object_address;
|
||
|
u16 object_size;
|
||
|
u8 object_instances;
|
||
|
u8 object_report_ids;
|
||
|
u16 object_info_address;
|
||
|
u32 crc;
|
||
|
u32 calculated_crc;
|
||
|
int i;
|
||
|
int error;
|
||
|
|
||
|
u8 object_instance;
|
||
|
u8 object_report_id;
|
||
|
u8 report_id;
|
||
|
int first_report_id;
|
||
|
int ib_pointer;
|
||
|
struct mxt_object *object_table;
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver reading configuration\n");
|
||
|
|
||
|
object_table = kzalloc(sizeof(struct mxt_object) *
|
||
|
mxt->device_info.num_objs,
|
||
|
GFP_KERNEL);
|
||
|
if (object_table == NULL) {
|
||
|
printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_object_table_alloc;
|
||
|
}
|
||
|
|
||
|
raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE *
|
||
|
mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE,
|
||
|
GFP_KERNEL);
|
||
|
if (raw_ib_data == NULL) {
|
||
|
printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_ib_alloc;
|
||
|
}
|
||
|
|
||
|
/* Copy the ID data for CRC calculation. */
|
||
|
memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE);
|
||
|
ib_pointer = MXT_ID_BLOCK_SIZE;
|
||
|
|
||
|
mxt->object_table = object_table;
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver Memory allocated\n");
|
||
|
|
||
|
object_info_address = MXT_ADDR_OBJECT_TABLE;
|
||
|
|
||
|
report_id_count = 0;
|
||
|
for (i = 0; i < mxt->device_info.num_objs; i++) {
|
||
|
mxt_debug(DEBUG_TRACE, "Reading maXTouch at [0x%04x]: ",
|
||
|
object_info_address);
|
||
|
|
||
|
error = mxt_read_block(client, object_info_address,
|
||
|
MXT_OBJECT_TABLE_ELEMENT_SIZE, buf);
|
||
|
|
||
|
if (error < 0) {
|
||
|
mxt->read_fail_counter++;
|
||
|
dev_err(&client->dev,
|
||
|
"maXTouch Object %d could not be read\n", i);
|
||
|
error = -EIO;
|
||
|
goto err_object_read;
|
||
|
}
|
||
|
|
||
|
memcpy(raw_ib_data + ib_pointer, buf,
|
||
|
MXT_OBJECT_TABLE_ELEMENT_SIZE);
|
||
|
ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE;
|
||
|
|
||
|
object_type = buf[0];
|
||
|
object_address = (buf[2] << 8) + buf[1];
|
||
|
object_size = buf[3] + 1;
|
||
|
object_instances = buf[4] + 1;
|
||
|
object_report_ids = buf[5];
|
||
|
mxt_debug(DEBUG_TRACE, "Type=%03d, Address=0x%04x, "
|
||
|
"Size=0x%02x, %d instances, %d report id's\n",
|
||
|
object_type,
|
||
|
object_address,
|
||
|
object_size,
|
||
|
object_instances,
|
||
|
object_report_ids
|
||
|
);
|
||
|
|
||
|
if (object_type == 38)
|
||
|
t38_size = object_size;
|
||
|
/* TODO: check whether object is known and supported? */
|
||
|
|
||
|
/* Save frequently needed info. */
|
||
|
if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) {
|
||
|
mxt->msg_proc_addr = object_address;
|
||
|
mxt->message_size = object_size;
|
||
|
}
|
||
|
|
||
|
object_table[i].type = object_type;
|
||
|
object_table[i].chip_addr = object_address;
|
||
|
object_table[i].size = object_size;
|
||
|
object_table[i].instances = object_instances;
|
||
|
object_table[i].num_report_ids = object_report_ids;
|
||
|
report_id_count += object_instances * object_report_ids;
|
||
|
|
||
|
object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE;
|
||
|
}
|
||
|
|
||
|
mxt->rid_map =
|
||
|
kzalloc(sizeof(struct report_id_map) * (report_id_count + 1),
|
||
|
/* allocate for report_id 0, even if not used */
|
||
|
GFP_KERNEL);
|
||
|
if (mxt->rid_map == NULL) {
|
||
|
printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_rid_map_alloc;
|
||
|
}
|
||
|
|
||
|
mxt->messages = kzalloc(mxt->message_size * MXT_MESSAGE_BUFFER_SIZE,
|
||
|
GFP_KERNEL);
|
||
|
if (mxt->messages == NULL) {
|
||
|
printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_msg_alloc;
|
||
|
}
|
||
|
|
||
|
mxt->last_message = kzalloc(mxt->message_size, GFP_KERNEL);
|
||
|
if (mxt->last_message == NULL) {
|
||
|
printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_msg_alloc;
|
||
|
}
|
||
|
|
||
|
mxt->report_id_count = report_id_count;
|
||
|
if (report_id_count > 254) { /* 0 & 255 are reserved */
|
||
|
dev_err(&client->dev,
|
||
|
"Too many maXTouch report id's [%d]\n",
|
||
|
report_id_count);
|
||
|
error = -ENXIO;
|
||
|
goto err_max_rid;
|
||
|
}
|
||
|
|
||
|
/* Create a mapping from report id to object type */
|
||
|
report_id = 1; /* Start from 1, 0 is reserved. */
|
||
|
|
||
|
/* Create table associating report id's with objects & instances */
|
||
|
for (i = 0; i < mxt->device_info.num_objs; i++) {
|
||
|
for (object_instance = 0;
|
||
|
object_instance < object_table[i].instances;
|
||
|
object_instance++){
|
||
|
first_report_id = report_id;
|
||
|
for (object_report_id = 0;
|
||
|
object_report_id < object_table[i].num_report_ids;
|
||
|
object_report_id++) {
|
||
|
mxt->rid_map[report_id].object =
|
||
|
object_table[i].type;
|
||
|
mxt->rid_map[report_id].instance =
|
||
|
object_instance;
|
||
|
mxt->rid_map[report_id].first_rid =
|
||
|
first_report_id;
|
||
|
report_id++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Read 3 byte CRC */
|
||
|
error = mxt_read_block(client, object_info_address, 3, buf);
|
||
|
if (error < 0) {
|
||
|
mxt->read_fail_counter++;
|
||
|
dev_err(&client->dev, "Error reading CRC\n");
|
||
|
}
|
||
|
|
||
|
crc = (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||
|
|
||
|
if (calculate_infoblock_crc(&calculated_crc, raw_ib_data,
|
||
|
ib_pointer)) {
|
||
|
printk(KERN_WARNING "Error while calculating CRC!\n");
|
||
|
calculated_crc = 0;
|
||
|
}
|
||
|
kfree(raw_ib_data);
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "\nReported info block CRC = 0x%6X\n", crc);
|
||
|
mxt_debug(DEBUG_TRACE, "Calculated info block CRC = 0x%6X\n\n",
|
||
|
calculated_crc);
|
||
|
|
||
|
if (crc == calculated_crc) {
|
||
|
mxt->info_block_crc = crc;
|
||
|
} else {
|
||
|
mxt->info_block_crc = 0;
|
||
|
printk(KERN_ALERT "maXTouch: Info block CRC invalid!\n");
|
||
|
}
|
||
|
|
||
|
if (debug >= DEBUG_VERBOSE) {
|
||
|
|
||
|
dev_info(&client->dev, "maXTouch: %d Objects\n",
|
||
|
mxt->device_info.num_objs);
|
||
|
|
||
|
for (i = 0; i < mxt->device_info.num_objs; i++) {
|
||
|
dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n",
|
||
|
object_table[i].type,
|
||
|
object_type_name[object_table[i].type]);
|
||
|
dev_info(&client->dev, "\tAddress:\t0x%04X\n",
|
||
|
object_table[i].chip_addr);
|
||
|
dev_info(&client->dev, "\tSize:\t\t%d Bytes\n",
|
||
|
object_table[i].size);
|
||
|
dev_info(&client->dev, "\tInstances:\t%d\n",
|
||
|
object_table[i].instances);
|
||
|
dev_info(&client->dev, "\tReport Id's:\t%d\n",
|
||
|
object_table[i].num_report_ids);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
err_max_rid:
|
||
|
kfree(mxt->last_message);
|
||
|
err_msg_alloc:
|
||
|
kfree(mxt->rid_map);
|
||
|
err_rid_map_alloc:
|
||
|
err_object_read:
|
||
|
kfree(raw_ib_data);
|
||
|
err_ib_alloc:
|
||
|
kfree(object_table);
|
||
|
err_object_table_alloc:
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_PM)
|
||
|
static int mxt_suspend(struct device *dev)
|
||
|
{
|
||
|
struct mxt_data *mxt = dev_get_drvdata(dev);
|
||
|
int error, i;
|
||
|
u8 t7_deepsl_data[T7_DATA_SIZE];
|
||
|
u16 t7_addr;
|
||
|
|
||
|
if (device_may_wakeup(dev)) {
|
||
|
enable_irq_wake(mxt->irq);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
disable_irq(mxt->irq);
|
||
|
|
||
|
flush_delayed_work_sync(&mxt->dwork);
|
||
|
|
||
|
for (i = 0; i < T7_DATA_SIZE; i++)
|
||
|
t7_deepsl_data[i] = 0;
|
||
|
|
||
|
t7_addr = MXT_BASE_ADDR(MXT_GEN_POWERCONFIG_T7, mxt);
|
||
|
/* save current power state values */
|
||
|
error = mxt_read_block(mxt->client, t7_addr,
|
||
|
ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
|
||
|
if (error < 0)
|
||
|
goto err_enable_irq;
|
||
|
|
||
|
/* configure deep sleep mode */
|
||
|
error = mxt_write_block(mxt->client, t7_addr,
|
||
|
ARRAY_SIZE(t7_deepsl_data), t7_deepsl_data);
|
||
|
if (error < 0)
|
||
|
goto err_enable_irq;
|
||
|
|
||
|
/* power off the device */
|
||
|
if (mxt->power_on) {
|
||
|
error = mxt->power_on(false);
|
||
|
if (error) {
|
||
|
dev_err(dev, "power off failed");
|
||
|
goto err_write_block;
|
||
|
}
|
||
|
}
|
||
|
mxt->is_suspended = true;
|
||
|
return 0;
|
||
|
|
||
|
err_write_block:
|
||
|
mxt_write_block(mxt->client, t7_addr,
|
||
|
ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
|
||
|
err_enable_irq:
|
||
|
enable_irq(mxt->irq);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
static int mxt_resume(struct device *dev)
|
||
|
{
|
||
|
struct mxt_data *mxt = dev_get_drvdata(dev);
|
||
|
int error;
|
||
|
u16 t7_addr;
|
||
|
|
||
|
if (device_may_wakeup(dev)) {
|
||
|
disable_irq_wake(mxt->irq);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!mxt->is_suspended)
|
||
|
return 0;
|
||
|
|
||
|
/* power on the device */
|
||
|
if (mxt->power_on) {
|
||
|
error = mxt->power_on(true);
|
||
|
if (error) {
|
||
|
dev_err(dev, "power on failed");
|
||
|
return error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t7_addr = MXT_BASE_ADDR(MXT_GEN_POWERCONFIG_T7, mxt);
|
||
|
/* restore the old power state values */
|
||
|
error = mxt_write_block(mxt->client, t7_addr,
|
||
|
ARRAY_SIZE(mxt->t7_data), mxt->t7_data);
|
||
|
if (error < 0)
|
||
|
goto err_write_block;
|
||
|
|
||
|
/* Make sure we just didn't miss a interrupt. */
|
||
|
if (mxt->read_chg() == 0)
|
||
|
schedule_delayed_work(&mxt->dwork, 0);
|
||
|
else
|
||
|
enable_irq(mxt->irq);
|
||
|
|
||
|
mxt->is_suspended = false;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_write_block:
|
||
|
if (mxt->power_on)
|
||
|
mxt->power_on(false);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
static void mxt_early_suspend(struct early_suspend *h)
|
||
|
{
|
||
|
struct mxt_data *mxt = container_of(h, struct mxt_data, early_suspend);
|
||
|
|
||
|
mxt_suspend(&mxt->client->dev);
|
||
|
}
|
||
|
|
||
|
static void mxt_late_resume(struct early_suspend *h)
|
||
|
{
|
||
|
struct mxt_data *mxt = container_of(h, struct mxt_data, early_suspend);
|
||
|
|
||
|
mxt_resume(&mxt->client->dev);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static const struct dev_pm_ops mxt_pm_ops = {
|
||
|
#ifndef CONFIG_HAS_EARLYSUSPEND
|
||
|
.suspend = mxt_suspend,
|
||
|
.resume = mxt_resume,
|
||
|
#endif
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static int __devinit mxt_probe(struct i2c_client *client,
|
||
|
const struct i2c_device_id *id)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
struct maxtouch_platform_data *pdata;
|
||
|
struct input_dev *input;
|
||
|
u8 *id_data;
|
||
|
u8 *t38_data;
|
||
|
u16 t38_addr;
|
||
|
int error;
|
||
|
|
||
|
mxt_debug(DEBUG_INFO, "mXT224: mxt_probe\n");
|
||
|
|
||
|
if (client == NULL) {
|
||
|
pr_debug("maXTouch: client == NULL\n");
|
||
|
return -EINVAL;
|
||
|
} else if (client->adapter == NULL) {
|
||
|
pr_debug("maXTouch: client->adapter == NULL\n");
|
||
|
return -EINVAL;
|
||
|
} else if (&client->dev == NULL) {
|
||
|
pr_debug("maXTouch: client->dev == NULL\n");
|
||
|
return -EINVAL;
|
||
|
} else if (&client->adapter->dev == NULL) {
|
||
|
pr_debug("maXTouch: client->adapter->dev == NULL\n");
|
||
|
return -EINVAL;
|
||
|
} else if (id == NULL) {
|
||
|
pr_debug("maXTouch: id == NULL\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
/* Enable runtime PM ops, start in ACTIVE mode */
|
||
|
error = pm_runtime_set_active(&client->dev);
|
||
|
if (error < 0)
|
||
|
dev_dbg(&client->dev, "unable to set runtime pm state\n");
|
||
|
pm_runtime_enable(&client->dev);
|
||
|
|
||
|
mxt_debug(DEBUG_INFO, "maXTouch driver v. %s\n", DRIVER_VERSION);
|
||
|
mxt_debug(DEBUG_INFO, "\t \"%s\"\n", client->name);
|
||
|
mxt_debug(DEBUG_INFO, "\taddr:\t0x%04x\n", client->addr);
|
||
|
mxt_debug(DEBUG_INFO, "\tirq:\t%d\n", client->irq);
|
||
|
mxt_debug(DEBUG_INFO, "\tflags:\t0x%04x\n", client->flags);
|
||
|
mxt_debug(DEBUG_INFO, "\tadapter:\"%s\"\n", client->adapter->name);
|
||
|
mxt_debug(DEBUG_INFO, "\tdevice:\t\"%s\"\n", client->dev.init_name);
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver functionality OK\n");
|
||
|
|
||
|
/* Allocate structure - we need it to identify device */
|
||
|
mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
|
||
|
if (mxt == NULL) {
|
||
|
dev_err(&client->dev, "insufficient memory\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_mxt_alloc;
|
||
|
}
|
||
|
|
||
|
id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL);
|
||
|
if (id_data == NULL) {
|
||
|
dev_err(&client->dev, "insufficient memory\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_id_alloc;
|
||
|
}
|
||
|
|
||
|
input = input_allocate_device();
|
||
|
if (!input) {
|
||
|
dev_err(&client->dev, "error allocating input device\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_input_dev_alloc;
|
||
|
}
|
||
|
|
||
|
/* Initialize Platform data */
|
||
|
|
||
|
pdata = client->dev.platform_data;
|
||
|
if (pdata == NULL) {
|
||
|
dev_err(&client->dev, "platform data is required!\n");
|
||
|
error = -EINVAL;
|
||
|
goto err_pdata;
|
||
|
}
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
printk(KERN_INFO "Platform OK: pdata = 0x%08x\n",
|
||
|
(unsigned int) pdata);
|
||
|
|
||
|
mxt->is_suspended = false;
|
||
|
mxt->read_fail_counter = 0;
|
||
|
mxt->message_counter = 0;
|
||
|
|
||
|
if (pdata->min_x)
|
||
|
mxt->min_x_val = pdata->min_x;
|
||
|
else
|
||
|
mxt->min_x_val = 0;
|
||
|
|
||
|
if (pdata->min_y)
|
||
|
mxt->min_y_val = pdata->min_y;
|
||
|
else
|
||
|
mxt->min_y_val = 0;
|
||
|
|
||
|
mxt->max_x_val = pdata->max_x;
|
||
|
mxt->max_y_val = pdata->max_y;
|
||
|
|
||
|
/* Get data that is defined in board specific code. */
|
||
|
mxt->init_hw = pdata->init_platform_hw;
|
||
|
mxt->exit_hw = pdata->exit_platform_hw;
|
||
|
mxt->power_on = pdata->power_on;
|
||
|
mxt->read_chg = pdata->read_chg;
|
||
|
|
||
|
if (pdata->valid_interrupt != NULL)
|
||
|
mxt->valid_interrupt = pdata->valid_interrupt;
|
||
|
else
|
||
|
mxt->valid_interrupt = mxt_valid_interrupt_dummy;
|
||
|
|
||
|
if (mxt->init_hw) {
|
||
|
error = mxt->init_hw(client);
|
||
|
if (error) {
|
||
|
dev_err(&client->dev, "hw init failed");
|
||
|
goto err_init_hw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* power on the device */
|
||
|
if (mxt->power_on) {
|
||
|
error = mxt->power_on(true);
|
||
|
if (error) {
|
||
|
dev_err(&client->dev, "power on failed");
|
||
|
goto err_pwr_on;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
printk(KERN_INFO "maXTouch driver identifying chip\n");
|
||
|
|
||
|
if (mxt_identify(client, mxt, id_data) < 0) {
|
||
|
dev_err(&client->dev, "Chip could not be identified\n");
|
||
|
error = -ENODEV;
|
||
|
goto err_identify;
|
||
|
}
|
||
|
/* Chip is valid and active. */
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
printk(KERN_INFO "maXTouch driver allocating input device\n");
|
||
|
|
||
|
mxt->client = client;
|
||
|
mxt->input = input;
|
||
|
|
||
|
INIT_DELAYED_WORK(&mxt->dwork, mxt_worker);
|
||
|
mutex_init(&mxt->debug_mutex);
|
||
|
mutex_init(&mxt->msg_mutex);
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver creating device name\n");
|
||
|
|
||
|
snprintf(
|
||
|
mxt->phys_name,
|
||
|
sizeof(mxt->phys_name),
|
||
|
"%s/input0",
|
||
|
dev_name(&client->dev)
|
||
|
);
|
||
|
input->name = "Atmel maXTouch Touchscreen controller";
|
||
|
input->phys = mxt->phys_name;
|
||
|
input->id.bustype = BUS_I2C;
|
||
|
input->dev.parent = &client->dev;
|
||
|
|
||
|
mxt_debug(DEBUG_INFO, "maXTouch name: \"%s\"\n", input->name);
|
||
|
mxt_debug(DEBUG_INFO, "maXTouch phys: \"%s\"\n", input->phys);
|
||
|
mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n");
|
||
|
|
||
|
set_bit(BTN_TOUCH, input->keybit);
|
||
|
set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||
|
|
||
|
/* Single touch */
|
||
|
input_set_abs_params(input, ABS_X, mxt->min_x_val,
|
||
|
mxt->max_x_val, 0, 0);
|
||
|
input_set_abs_params(input, ABS_Y, mxt->min_y_val,
|
||
|
mxt->max_y_val, 0, 0);
|
||
|
input_set_abs_params(input, ABS_PRESSURE, 0, MXT_MAX_REPORTED_PRESSURE,
|
||
|
0, 0);
|
||
|
input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MXT_MAX_REPORTED_WIDTH,
|
||
|
0, 0);
|
||
|
|
||
|
/* Multitouch */
|
||
|
input_set_abs_params(input, ABS_MT_POSITION_X, mxt->min_x_val,
|
||
|
mxt->max_x_val, 0, 0);
|
||
|
input_set_abs_params(input, ABS_MT_POSITION_Y, mxt->min_y_val,
|
||
|
mxt->max_y_val, 0, 0);
|
||
|
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE,
|
||
|
0, 0);
|
||
|
input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, MXT_MAX_NUM_TOUCHES,
|
||
|
0, 0);
|
||
|
|
||
|
__set_bit(EV_ABS, input->evbit);
|
||
|
__set_bit(EV_SYN, input->evbit);
|
||
|
__set_bit(EV_KEY, input->evbit);
|
||
|
__set_bit(EV_MSC, input->evbit);
|
||
|
input->mscbit[0] = BIT_MASK(MSC_GESTURE);
|
||
|
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver setting client data\n");
|
||
|
i2c_set_clientdata(client, mxt);
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver setting drv data\n");
|
||
|
input_set_drvdata(input, mxt);
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver input register device\n");
|
||
|
error = input_register_device(mxt->input);
|
||
|
if (error < 0) {
|
||
|
dev_err(&client->dev,
|
||
|
"Failed to register input device\n");
|
||
|
goto err_register_device;
|
||
|
}
|
||
|
|
||
|
error = mxt_read_object_table(client, mxt, id_data);
|
||
|
if (error < 0)
|
||
|
goto err_read_ot;
|
||
|
|
||
|
|
||
|
/* Create debugfs entries. */
|
||
|
mxt->debug_dir = debugfs_create_dir("maXTouch", NULL);
|
||
|
if (mxt->debug_dir == ERR_PTR(-ENODEV)) {
|
||
|
/* debugfs is not enabled. */
|
||
|
printk(KERN_WARNING "debugfs not enabled in kernel\n");
|
||
|
} else if (mxt->debug_dir == NULL) {
|
||
|
printk(KERN_WARNING "error creating debugfs dir\n");
|
||
|
} else {
|
||
|
mxt_debug(DEBUG_TRACE, "created \"maXTouch\" debugfs dir\n");
|
||
|
|
||
|
debugfs_create_file("deltas", S_IRUSR, mxt->debug_dir, mxt,
|
||
|
&delta_fops);
|
||
|
debugfs_create_file("refs", S_IRUSR, mxt->debug_dir, mxt,
|
||
|
&refs_fops);
|
||
|
}
|
||
|
|
||
|
/* Create character device nodes for reading & writing registers */
|
||
|
mxt->mxt_class = class_create(THIS_MODULE, "maXTouch_memory");
|
||
|
if (IS_ERR(mxt->mxt_class)){
|
||
|
printk(KERN_WARNING "class create failed! exiting...");
|
||
|
goto err_class_create;
|
||
|
|
||
|
}
|
||
|
/* 2 numbers; one for memory and one for messages */
|
||
|
error = alloc_chrdev_region(&mxt->dev_num, 0, 2,
|
||
|
"maXTouch_memory");
|
||
|
mxt_debug(DEBUG_VERBOSE,
|
||
|
"device number %d allocated!\n", MAJOR(mxt->dev_num));
|
||
|
if (error){
|
||
|
printk(KERN_WARNING "Error registering device\n");
|
||
|
}
|
||
|
cdev_init(&mxt->cdev, &mxt_memory_fops);
|
||
|
cdev_init(&mxt->cdev_messages, &mxt_message_fops);
|
||
|
|
||
|
mxt_debug(DEBUG_VERBOSE, "cdev initialized\n");
|
||
|
mxt->cdev.owner = THIS_MODULE;
|
||
|
mxt->cdev_messages.owner = THIS_MODULE;
|
||
|
|
||
|
error = cdev_add(&mxt->cdev, mxt->dev_num, 1);
|
||
|
if (error){
|
||
|
printk(KERN_WARNING "Bad cdev\n");
|
||
|
}
|
||
|
|
||
|
error = cdev_add(&mxt->cdev_messages, mxt->dev_num + 1, 1);
|
||
|
if (error){
|
||
|
printk(KERN_WARNING "Bad cdev\n");
|
||
|
}
|
||
|
|
||
|
mxt_debug(DEBUG_VERBOSE, "cdev added\n");
|
||
|
|
||
|
device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 0), NULL,
|
||
|
"maXTouch");
|
||
|
|
||
|
device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 1), NULL,
|
||
|
"maXTouch_messages");
|
||
|
|
||
|
mxt->msg_buffer_startp = 0;
|
||
|
mxt->msg_buffer_endp = 0;
|
||
|
|
||
|
/* Allocate the interrupt */
|
||
|
mxt_debug(DEBUG_TRACE, "maXTouch driver allocating interrupt...\n");
|
||
|
mxt->irq = client->irq;
|
||
|
mxt->valid_irq_counter = 0;
|
||
|
mxt->invalid_irq_counter = 0;
|
||
|
mxt->irq_counter = 0;
|
||
|
if (mxt->irq) {
|
||
|
/* Try to request IRQ with falling edge first. This is
|
||
|
* not always supported. If it fails, try with any edge. */
|
||
|
error = request_irq(mxt->irq,
|
||
|
mxt_irq_handler,
|
||
|
IRQF_TRIGGER_FALLING,
|
||
|
client->dev.driver->name,
|
||
|
mxt);
|
||
|
if (error < 0) {
|
||
|
/* TODO: why only 0 works on STK1000? */
|
||
|
error = request_irq(mxt->irq,
|
||
|
mxt_irq_handler,
|
||
|
0,
|
||
|
client->dev.driver->name,
|
||
|
mxt);
|
||
|
}
|
||
|
|
||
|
if (error < 0) {
|
||
|
dev_err(&client->dev,
|
||
|
"failed to allocate irq %d\n", mxt->irq);
|
||
|
goto err_irq;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (debug > DEBUG_INFO)
|
||
|
dev_info(&client->dev, "touchscreen, irq %d\n", mxt->irq);
|
||
|
|
||
|
t38_data = kmalloc(t38_size*sizeof(u8), GFP_KERNEL);
|
||
|
|
||
|
if (t38_data == NULL) {
|
||
|
dev_err(&client->dev, "insufficient memory\n");
|
||
|
error = -ENOMEM;
|
||
|
goto err_t38;
|
||
|
}
|
||
|
|
||
|
t38_addr = MXT_BASE_ADDR(MXT_USER_INFO_T38, mxt);
|
||
|
mxt_read_block(client, t38_addr, t38_size, t38_data);
|
||
|
dev_info(&client->dev, "VERSION:%02x.%02x.%02x, DATE: %d/%d/%d\n",
|
||
|
t38_data[0], t38_data[1], t38_data[2],
|
||
|
t38_data[3], t38_data[4], t38_data[5]);
|
||
|
|
||
|
/* Schedule a worker routine to read any messages that might have
|
||
|
* been sent before interrupts were enabled. */
|
||
|
cancel_delayed_work(&mxt->dwork);
|
||
|
disable_irq(mxt->irq);
|
||
|
schedule_delayed_work(&mxt->dwork, 0);
|
||
|
kfree(t38_data);
|
||
|
kfree(id_data);
|
||
|
|
||
|
device_init_wakeup(&client->dev, pdata->wakeup);
|
||
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
mxt->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
|
||
|
MXT_SUSPEND_LEVEL;
|
||
|
mxt->early_suspend.suspend = mxt_early_suspend;
|
||
|
mxt->early_suspend.resume = mxt_late_resume;
|
||
|
register_early_suspend(&mxt->early_suspend);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_t38:
|
||
|
free_irq(mxt->irq, mxt);
|
||
|
err_irq:
|
||
|
kfree(mxt->rid_map);
|
||
|
kfree(mxt->object_table);
|
||
|
kfree(mxt->last_message);
|
||
|
err_class_create:
|
||
|
if (mxt->debug_dir)
|
||
|
debugfs_remove(mxt->debug_dir);
|
||
|
kfree(mxt->last_message);
|
||
|
kfree(mxt->rid_map);
|
||
|
kfree(mxt->object_table);
|
||
|
err_read_ot:
|
||
|
input_unregister_device(mxt->input);
|
||
|
mxt->input = NULL;
|
||
|
err_register_device:
|
||
|
mutex_destroy(&mxt->debug_mutex);
|
||
|
mutex_destroy(&mxt->msg_mutex);
|
||
|
err_identify:
|
||
|
if (mxt->power_on)
|
||
|
mxt->power_on(false);
|
||
|
err_pwr_on:
|
||
|
if (mxt->exit_hw != NULL)
|
||
|
mxt->exit_hw(client);
|
||
|
err_init_hw:
|
||
|
err_pdata:
|
||
|
input_free_device(input);
|
||
|
err_input_dev_alloc:
|
||
|
kfree(id_data);
|
||
|
err_id_alloc:
|
||
|
kfree(mxt);
|
||
|
err_mxt_alloc:
|
||
|
pm_runtime_set_suspended(&client->dev);
|
||
|
pm_runtime_disable(&client->dev);
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
static int __devexit mxt_remove(struct i2c_client *client)
|
||
|
{
|
||
|
struct mxt_data *mxt;
|
||
|
|
||
|
pm_runtime_set_suspended(&client->dev);
|
||
|
pm_runtime_disable(&client->dev);
|
||
|
|
||
|
mxt = i2c_get_clientdata(client);
|
||
|
|
||
|
/* Remove debug dir entries */
|
||
|
debugfs_remove_recursive(mxt->debug_dir);
|
||
|
|
||
|
device_init_wakeup(&client->dev, 0);
|
||
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
||
|
unregister_early_suspend(&mxt->early_suspend);
|
||
|
#endif
|
||
|
|
||
|
if (mxt != NULL) {
|
||
|
if (mxt->power_on)
|
||
|
mxt->power_on(false);
|
||
|
|
||
|
if (mxt->exit_hw != NULL)
|
||
|
mxt->exit_hw(client);
|
||
|
|
||
|
if (mxt->irq) {
|
||
|
free_irq(mxt->irq, mxt);
|
||
|
}
|
||
|
|
||
|
unregister_chrdev_region(mxt->dev_num, 2);
|
||
|
device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 0));
|
||
|
device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 1));
|
||
|
cdev_del(&mxt->cdev);
|
||
|
cdev_del(&mxt->cdev_messages);
|
||
|
cancel_delayed_work_sync(&mxt->dwork);
|
||
|
input_unregister_device(mxt->input);
|
||
|
class_destroy(mxt->mxt_class);
|
||
|
debugfs_remove(mxt->debug_dir);
|
||
|
|
||
|
kfree(mxt->rid_map);
|
||
|
kfree(mxt->object_table);
|
||
|
kfree(mxt->last_message);
|
||
|
}
|
||
|
kfree(mxt);
|
||
|
|
||
|
i2c_set_clientdata(client, NULL);
|
||
|
if (debug >= DEBUG_TRACE)
|
||
|
dev_info(&client->dev, "Touchscreen unregistered\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct i2c_device_id mxt_idtable[] = {
|
||
|
{"maXTouch", 0,},
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
MODULE_DEVICE_TABLE(i2c, mxt_idtable);
|
||
|
|
||
|
static struct i2c_driver mxt_driver = {
|
||
|
.driver = {
|
||
|
.name = "maXTouch",
|
||
|
.owner = THIS_MODULE,
|
||
|
#if defined(CONFIG_PM)
|
||
|
.pm = &mxt_pm_ops,
|
||
|
#endif
|
||
|
},
|
||
|
|
||
|
.id_table = mxt_idtable,
|
||
|
.probe = mxt_probe,
|
||
|
.remove = __devexit_p(mxt_remove),
|
||
|
};
|
||
|
|
||
|
static int __init mxt_init(void)
|
||
|
{
|
||
|
int err;
|
||
|
err = i2c_add_driver(&mxt_driver);
|
||
|
if (err) {
|
||
|
printk(KERN_WARNING "Adding maXTouch driver failed "
|
||
|
"(errno = %d)\n", err);
|
||
|
} else {
|
||
|
mxt_debug(DEBUG_TRACE, "Successfully added driver %s\n",
|
||
|
mxt_driver.driver.name);
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static void __exit mxt_cleanup(void)
|
||
|
{
|
||
|
i2c_del_driver(&mxt_driver);
|
||
|
}
|
||
|
|
||
|
|
||
|
module_init(mxt_init);
|
||
|
module_exit(mxt_cleanup);
|
||
|
|
||
|
MODULE_AUTHOR("Iiro Valkonen");
|
||
|
MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller");
|
||
|
MODULE_LICENSE("GPL");
|