lkml.org 
[lkml]   [2013]   [Oct]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
SubjectFwd: Driver for touchscreen BF6931A1 instead S2100A
From
Hi there :)

I replace digitizer in my phone (Huawei u8815), but its based on
BF6931A1 instead S2100A.

BF6931A1 is not proper initialized by driver. It start fail on
synaptics_rmi4_read_pdt where
ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); is called
(set rmi page to zero).

Its just the first symptom where i see that the i2c communication fail.

I try on many way to reset this chip by software but without success:

Dmesg part when the BF6931A1 chip is connected:
<6>[1, swapper] [ 1.083416] rmi_bus_init: RMI Bus Driver Init
<7>[1, swapper] [ 1.083533] rmi_bus_init: registered bus.
<7>[1, swapper] [ 1.083559] rmi_sensor_init: RMI Sensor Init
<7>[1, swapper] [ 1.083578] rmi_function_init: RMI Function Init
<4>[1, swapper] [ 1.083688] i2c-core: driver [rmi4_ts] using legacy
suspend method
<4>[1, swapper] [ 1.083718] i2c-core: driver [rmi4_ts] using legacy
resume method
<4>[1, swapper] [ 1.083804] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 1.083833] Client adress is: 70
<6>[1, swapper] [ 1.083856] the i2c_check_functionality is ok
<3>[1, swapper] [ 1.083888] synaptics_rmi4_probe: it's the first
touch driver!
<3>[1, swapper] [ 1.150668] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[1, swapper] [ 1.151383] failed to set rmi page
<3>[1, swapper] [ 1.151428] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[1, swapper] [ 1.152136] I2C read failed querying RMI4 $70 capabilities
<3>[1, swapper] [ 1.152164] Error identifying device (-5)
<4>[60, synaptics_wq] [ 1.152218] do_exit: exit code=4294967292
<4>[60, synaptics_wq] [ 1.152271] synaptics_wq used greatest stack
depth: 6552 bytes left
<4>[1, swapper] [ 1.152424] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 1.152456] Client adress is: 24
<6>[1, swapper] [ 1.152478] the i2c_check_functionality is ok
<3>[1, swapper] [ 1.152509] synaptics_rmi4_probe: it's the first
touch driver!
<7>[1, swapper] [ 1.152539] gpio_request: gpio-96 (TOUCH_RESET) status -16
<3>[1, swapper] [ 1.218838] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[1, swapper] [ 1.219549] failed to set rmi page
<3>[1, swapper] [ 1.219591] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[1, swapper] [ 1.220299] I2C read failed querying RMI4 $24 capabilities
<3>[1, swapper] [ 1.220326] Error identifying device (-5)

And now how its look with original S2100A:

<6>[1, swapper] [ 1.039414] rmi_bus_init: RMI Bus Driver Init
<7>[1, swapper] [ 1.039529] rmi_bus_init: registered bus.
<7>[1, swapper] [ 1.039556] rmi_sensor_init: RMI Sensor Init
<7>[1, swapper] [ 1.039576] rmi_function_init: RMI Function Init
<4>[1, swapper] [ 1.039683] i2c-core: driver [rmi4_ts] using legacy
suspend method
<4>[1, swapper] [ 1.039713] i2c-core: driver [rmi4_ts] using legacy
resume method
<4>[1, swapper] [ 1.039798] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 1.039828] Client adress is: 70
<6>[1, swapper] [ 1.039851] the i2c_check_functionality is ok
<3>[1, swapper] [ 1.039883] synaptics_rmi4_probe: it's the first
touch driver!
<4>[1, swapper] [ 1.106931] set rmi page to zero successfull
<6>[1, swapper] [ 1.111396] 2 fingers
<6>[1, swapper] [ 1.111419] EGR features:
<6>[1, swapper] [ 1.111441] pinch: 0
<6>[1, swapper] [ 1.111463] press: 0
<6>[1, swapper] [ 1.111483] flick: 0
<6>[1, swapper] [ 1.111503] early tap: 0
<6>[1, swapper] [ 1.111524] double tap: 0
<6>[1, swapper] [ 1.111544] tap and hold: 0
<6>[1, swapper] [ 1.111566] single tap: 1
<6>[1, swapper] [ 1.111588] palm detect: 0
<6>[1, swapper] [ 1.113298] max X: 1068; max Y: 1868
<6>[1, swapper] [ 1.114231] Read 0 functions from PDT
<3>[1, swapper] [ 1.114256] RMI4 $70 data read: $13 + 15
<6>[1, swapper] [ 1.116009] the ReportingMode is changged ok!
<6>[1, swapper] [ 1.116376] input: synaptics as /devices/virtual/input/input0
<6>[1, swapper] [ 1.116594] synaptics input device registered
<7>[1, swapper] [ 1.116696] gpio_request: gpio-146 (Synaptics_rmi) status -22
<7>[1, swapper] [ 1.116721] gpio_direction_input: gpio-146 status -22
<6>[1, swapper] [ 1.116738] Requesting IRQ...
<6>[1, swapper] [ 1.116828] Received IRQ!
<3>[1, swapper] [ 1.116868] probingX for Synaptics RMI4 device
Synaptics_rmi at $70...
<4>[1, swapper] [ 1.116928] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 1.116958] Client adress is: 24
<6>[1, swapper] [ 1.116979] the i2c_check_functionality is ok
<3>[1, swapper] [ 1.117011] synaptics_rmi4_probe: the touch driver
has detected :)
<4>[1, swapper] [ 1.117054] Synaptics_rmi: probe of 1-0024 failed
with error -1

How we can see set rmi page to zero is successfull and also reading
capabilities are fine: RMI4 $70 data read: $13 + 15

Now when I switch(without rebooting) again to BF6931A1 I`m able to use
my touchscreen without any issues!

Now its time to reboot phone and see what dmesg show:

<6>[1, swapper] [ 1.039468] rmi_bus_init: RMI Bus Driver Init
<7>[1, swapper] [ 1.039583] rmi_bus_init: registered bus.
<7>[1, swapper] [ 1.039609] rmi_sensor_init: RMI Sensor Init
<7>[1, swapper] [ 1.039628] rmi_function_init: RMI Function Init
<4>[1, swapper] [ 1.039736] i2c-core: driver [rmi4_ts] using legacy
suspend method
<4>[1, swapper] [ 1.039764] i2c-core: driver [rmi4_ts] using legacy
resume method
<4>[1, swapper] [ 1.039851] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 1.039879] Client adress is: 70
<6>[1, swapper] [ 1.039903] the i2c_check_functionality is ok
<3>[1, swapper] [ 1.039934] synaptics_rmi4_probe: it's the first
touch driver!
<4>[1, swapper] [ 1.106981] set rmi page to zero successfull
<6>[1, swapper] [ 1.111361] 2 fingers
<6>[1, swapper] [ 1.111384] EGR features:
<6>[1, swapper] [ 1.111406] pinch: 0
<6>[1, swapper] [ 1.111426] press: 0
<6>[1, swapper] [ 1.111448] flick: 0
<6>[1, swapper] [ 1.111468] early tap: 0
<6>[1, swapper] [ 1.111489] double tap: 0
<6>[1, swapper] [ 1.111509] tap and hold: 0
<6>[1, swapper] [ 1.111531] single tap: 1
<6>[1, swapper] [ 1.111553] palm detect: 0
<6>[1, swapper] [ 1.113243] max X: 1036; max Y: 1904
<6>[1, swapper] [ 1.114176] Read 0 functions from PDT
<3>[1, swapper] [ 1.114203] RMI4 $70 data read: $13 + 15
<7>[18, kworker/0:1] [ 1.254419] gpio_request: gpio-141
(i2c_host_vbus_en) status -22
<3>[18, kworker/0:1] [ 1.254443] failed to request 141 GPIO
<6>[18, kworker/0:1] [ 1.354574] msm_hsusb_host msm_hsusb_host.0:
Qualcomm On-Chip EHCI Host Controller
<6>[18, kworker/0:1] [ 1.354644] msm_hsusb_host msm_hsusb_host.0:
new USB bus registered, assigned bus number 1
<6>[18, kworker/0:1] [ 1.354824] msm_hsusb_host msm_hsusb_host.0:
irq 47, io base 0xa0800000
<6>[18, kworker/0:1] [ 1.354996] usb usb1: New USB device found,
idVendor=1d6b, idProduct=0002
<6>[18, kworker/0:1] [ 1.355034] usb usb1: New USB device strings:
Mfr=3, Product=2, SerialNumber=1
<6>[18, kworker/0:1] [ 1.355069] usb usb1: Product: Qualcomm
On-Chip EHCI Host Controller
<6>[18, kworker/0:1] [ 1.355099] usb usb1: Manufacturer: Linux
3.0.8-perf ehci_hcd
<6>[18, kworker/0:1] [ 1.355129] usb usb1: SerialNumber: msm_hsusb_host.0
<6>[18, kworker/0:1] [ 1.355879] hub 1-0:1.0: USB hub found
<6>[18, kworker/0:1] [ 1.355934] hub 1-0:1.0: 1 port detected
<6>[18, kworker/0:1] [ 1.356509] msm_hsusb_host msm_hsusb_host.0:
remove, state 1
<6>[18, kworker/0:1] [ 1.356561] usb usb1: USB disconnect, device number 1
<6>[18, kworker/0:1] [ 1.374768] msm_hsusb_host msm_hsusb_host.0:
USB bus 1 deregistered
<7>[18, kworker/0:1] [ 1.604413] gpio_request: gpio-141
(i2c_host_vbus_en) status -22
<3>[18, kworker/0:1] [ 1.604451] failed to request 141 GPIO
<3>[1, swapper] [ 2.144336] qup_i2c qup_i2c.1: Transaction timed
out, SL-AD = 0x70
<3>[1, swapper] [ 2.144371] qup_i2c qup_i2c.1: I2C Status: 349b00
<3>[1, swapper] [ 2.144398] qup_i2c qup_i2c.1: QUP Status: 0
<3>[1, swapper] [ 2.144424] qup_i2c qup_i2c.1: OP Flags: 0
<4>[1, swapper] [ 2.144458] Failed to read IC name.
<3>[1, swapper] [ 2.144506] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[1, swapper] [ 2.145221] tp_read_input_name: i2c_transfer failed
at address $70
<6>[1, swapper] [ 2.145286] the ReportingMode is changged ok!
<6>[1, swapper] [ 2.145656] input: synaptics as /devices/virtual/input/input0
<6>[1, swapper] [ 2.145863] synaptics input device registered
<7>[1, swapper] [ 2.145963] gpio_request: gpio-146 (Synaptics_rmi) status -22
<7>[1, swapper] [ 2.145986] gpio_direction_input: gpio-146 status -22
<6>[1, swapper] [ 2.146004] Requesting IRQ...
<6>[1, swapper] [ 2.146093] Received IRQ!
<3>[1, swapper] [ 2.146129] probingX for Synaptics RMI4 device
Synaptics_rmi at $70...
<4>[1, swapper] [ 2.146193] *************** synaptics_rmi4_probe
******************
<4>[1, swapper] [ 2.146223] Client adress is: 24
<6>[1, swapper] [ 2.146244] the i2c_check_functionality is ok
<3>[1, swapper] [ 2.146276] synaptics_rmi4_probe: the touch driver
has detected :)
<4>[1, swapper] [ 2.146316] Synaptics_rmi: probe of 1-0024 failed
with error -1

As we can see after reboot driver was able to set rmi page to zero abd
read capabilities.
Unfortunate In this moment my touchscreen is not responding and in
dmesg i see a lot of:

<3>[19, kworker/u:1] [ 290.841264] synaptics_rmi4_work_func:
i2c_transfer failed
<3>[19, kworker/u:1] [ 290.841343] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43
<3>[19, kworker/u:1] [ 290.841869] synaptics_rmi4_work_func:
i2c_transfer failed
<3>[19, kworker/u:1] [ 290.844888] qup_i2c qup_i2c.1: QUP: I2C status
flags :0x1121c4, irq:43

This state is until i reconnect BF6931A1, (still the same without
connecting the old S2100A).
Now again works fine.

Then when re-flash new kernel (and sometimes when I disconnect all
power) its a big chance to back to state from first dmesg.


In attachment i put my latest code.

Maybe somebody here have idea what i can do else (maybe some hardware
reset of i2c or other magic).

General i`m opened on any tip.

Best Regards,
Grzegorz Hetman.

/* drivers/input/keyboard/synaptics_i2c_rmi.c
*
* Copyright (C) 2007 Google, Inc.
* Copyright (C) 2008 Texas Instrument Inc.
* Copyright (C) 2009 Synaptics, Inc.
*
* provides device files /dev/input/event#
* for named device files, use udev
* 2D sensors report ABS_X_FINGER(0), ABS_Y_FINGER(0) through ABS_X_FINGER(7), ABS_Y_FINGER(7)
* NOTE: requires updated input.h, which should be included with this driver
* 1D/Buttons report BTN_0 through BTN_0 + button_count
* TODO: report REL_X, REL_Y for flick, BTN_TOUCH for tap (on 1D/0D; done for 2D)
* TODO: check ioctl (EVIOCGABS) to query 2D max X & Y, 1D button count
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/

/* modify for 4125 baseline */
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/synaptics_i2c_rmi_1564.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/earlysuspend.h>
#include <linux/device.h>
#include <mach/vreg.h>
#include <mach/gpio.h>

/*include the .h file*/
#include <linux/proc_fs.h>
#include <linux/touch_platform_config.h>
#ifdef CONFIG_HUAWEI_HW_DEV_DCT
#include <linux/hw_dev_dec.h>
#endif
#define CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE
#ifdef CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/namei.h>
#include <linux/vmalloc.h>
#endif
#define DEV_ATTR(_pre, _name, _mode) \
DEVICE_ATTR(_pre##_##_name,_mode,_pre##_##_name##_show,_pre##_##_name##_store)

#include <linux/kernel.h>
#include <asm/mach-types.h>
#include "linux/hardware_self_adapt.h"
#define BTN_F19 BTN_0
#define BTN_F30 BTN_0
#define SCROLL_ORIENTATION REL_Y

/*
//#define TS_RMI_DEBUG
#undef TS_RMI_DEBUG
#ifdef TS_RMI_DEBUG
#define TS_DEBUG_RMI(fmt, args...) printk(KERN_INFO fmt, ##args)
#else
#define TS_DEBUG_RMI(fmt, args...)
#endif
*/

#define TS_DEBUG_RMI(fmt, args...) printk(KERN_INFO fmt, ##args)

// protoypes,else the structure initialization that follows fail
static int dev_open(struct inode *, struct file *);
static int dev_rls(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
struct i2c_client *global_client;

#ifdef CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE
#define SYNAPITICS_DEBUG(fmt, args...) printk(KERN_DEBUG fmt, ##args)
static struct i2c_client *g_client = NULL;
static ssize_t update_firmware_show(struct kobject *kobj, struct kobj_attribute *attr,char *buf);
static ssize_t update_firmware_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);

static int ts_firmware_file(void);
static int i2c_update_firmware(struct i2c_client *client);

static struct kobj_attribute update_firmware_attribute = {
.attr = {.name = "update_firmware", .mode = 0664},
.show = update_firmware_show,
.store = update_firmware_store,
};
#endif /* CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE */
static int synaptics_debug_mask;
module_param_named(synaptics_debug, synaptics_debug_mask, int,
S_IRUGO | S_IWUSR | S_IWGRP);

#define DBG_MASK(x...) do {\
if (synaptics_debug_mask) \
printk(KERN_DEBUG x);\
} while (0)


static struct workqueue_struct *synaptics_wq;

static struct synaptics_rmi4 *ts = NULL;


#define EGR_PINCH_REG 0
#define EGR_PINCH (1 << 6)
#define EGR_PRESS_REG 0
#define EGR_PRESS (1 << 5)
#define EGR_FLICK_REG 0
#define EGR_FLICK (1 << 4)
#define EGR_EARLY_TAP_REG 0
#define EGR_EARLY_TAP (1 << 3)
#define EGR_DOUBLE_TAP_REG 0
#define EGR_DOUBLE_TAP (1 << 2)
#define EGR_TAP_AND_HOLD_REG 0
#define EGR_TAP_AND_HOLD (1 << 1)
#define EGR_SINGLE_TAP_REG 0
#define EGR_SINGLE_TAP (1 << 0)
/* Register: EGR_1 */
#define EGR_PALM_DETECT_REG 1
#define EGR_PALM_DETECT (1 << 0)



#define FINGER_MAX 9
#define FINGER_CNT (FINGER_MAX+1)
#define SYNAPTICS_I2C_RMI_NAME "Synaptics_RMI4"





#define ABS_XF 0
#define ABS_YF 1
#define ABS_ZF 2
#define EVENTS_PER_FINGER 3
#define ABS_FINGER(f) (0x29 + EVENTS_PER_FINGER * f)

#define ABS_X_FINGER(f) (ABS_FINGER(f) + ABS_XF)
#define ABS_Y_FINGER(f) (ABS_FINGER(f) + ABS_YF)
#define ABS_Z_FINGER(f) (ABS_FINGER(f) + ABS_ZF)
#define ABS_CNT (ABS_MAX+1)


struct synaptics_function_descriptor {
__u8 queryBase;
__u8 commandBase;
__u8 controlBase;
__u8 dataBase;
__u8 intSrc;
#define FUNCTION_VERSION(x) ((x >> 5) & 3)
#define INTERRUPT_SOURCE_COUNT(x) (x & 7)

__u8 functionNumber;
};
#define FD_ADDR_MAX 0xE9
#define FD_ADDR_MIN 0x05
#define FD_BYTE_COUNT 6


#define BYD 1
#define CMI 2
#define TRULY 3
#define TPK 4
#define LENSONE 5
#define OFILM 6
#define EELY 7
#define SUCCESS 8
#define ALPS 9

static u16 touch_ic_name = 0;
static char touch_info[50] = {0};

static int RMI4_enable_program(struct i2c_client *client);
int RMI4_disable_program(struct i2c_client *client);
static struct synaptics_function_descriptor fd_34;

static struct synaptics_function_descriptor fd_01;
static struct i2c_msg query_i2c_msg_name[2];
static __u8 query_name[8];

static int ts_x_max = 0;
static int ts_y_max = 0;
static int lcd_x = 0;
static int lcd_y = 0;
static int lcd_all = 0;
static __u8 point_supported_huawei = 0;

extern struct i2c_device_id synaptics_rmi4_id[];

#ifdef CONFIG_HAS_EARLYSUSPEND
static void synaptics_rmi4_early_suspend(struct early_suspend *h);
static void synaptics_rmi4_late_resume(struct early_suspend *h);
#endif

/*Prototypes */
static int synaptics_rmi4_suspend(struct i2c_client *client, pm_message_t mesg);
static int synaptics_rmi4_resume(struct i2c_client *client);

u12 check_scope_X(u12 x)
{
u12 temp = x;
if (x >= lcd_x -1)
{
temp = lcd_x -2;
}
if (x <= 1)
{
temp = 1;
}

return temp;
}

static int synaptics_rmi4_read_pdt(struct synaptics_rmi4 *ts)
{
int ret = 0;
int nFd = 0;
int interruptCount = 0;
struct i2c_msg fd_i2c_msg[2];
struct i2c_msg query_i2c_msg[2];
struct synaptics_function_descriptor fd;
__u8 data_length = 0;
__u8 fd_reg;
__u8 query[14];
__u8 *egr;

/* check whether rmi page is 0 */
printk("\033[22;31m OK sprawdzamy rmi page... \033[01;0m\n");

ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00);

if(ret < 0) {
printk(KERN_ERR "failed to set rmi page\n");
} else {
printk("set rmi page to zero successfull\n");
}

fd_i2c_msg[0].addr = ts->client->addr;
fd_i2c_msg[0].flags = 0;
fd_i2c_msg[0].buf = &fd_reg;
fd_i2c_msg[0].len = 1;

fd_i2c_msg[1].addr = ts->client->addr;
fd_i2c_msg[1].flags = I2C_M_RD;
fd_i2c_msg[1].buf = (__u8 *)(&fd);
fd_i2c_msg[1].len = FD_BYTE_COUNT;

query_i2c_msg[0].addr = ts->client->addr;
query_i2c_msg[0].flags = 0;
query_i2c_msg[0].buf = &fd.queryBase;
query_i2c_msg[0].len = 1;

query_i2c_msg[1].addr = ts->client->addr;
query_i2c_msg[1].flags = I2C_M_RD;
query_i2c_msg[1].buf = query;
query_i2c_msg[1].len = sizeof(query);

ts->hasF11 = false;
ts->hasF19 = false;
ts->hasF30 = false;
ts->data_reg = 0xff;
ts->data_length = 0;

for (fd_reg = FD_ADDR_MAX; fd_reg >= FD_ADDR_MIN; fd_reg -= FD_BYTE_COUNT){
ret = i2c_transfer(ts->client->adapter, fd_i2c_msg, 2);
if (ret < 0){
printk(KERN_ERR "I2C read failed querying RMI4 $%02X capabilities\n", ts->client->addr);
return ret;
}

if (!fd.functionNumber){
/* End of PDT */
ret = nFd;
TS_DEBUG_RMI("Read %d functions from PDT\n", fd.functionNumber);
break;
}

++nFd;

switch (fd.functionNumber) {
case 0x34:
fd_34.queryBase = fd.queryBase;
fd_34.dataBase = fd.dataBase;
fd_34.controlBase = fd.controlBase;
break;
case 0x01: /* Interrupt */
ts->f01.data_offset = fd.dataBase;
fd_01.queryBase = fd.queryBase;
fd_01.dataBase = fd.dataBase;
fd_01.commandBase = fd.commandBase;
fd_01.controlBase = fd.controlBase;
/*
* Can't determine data_length
* until whole PDT has been read to count interrupt sources
* and calculate number of interrupt status registers.
* Setting to 0 safely "ignores" for now.
*/
data_length = 0;
break;
case 0x11: /* 2D */
ts->hasF11 = true;
ts->f11.data_offset = fd.dataBase;
ts->f11.interrupt_offset = interruptCount / 8;
ts->f11.interrupt_mask = ((1 << INTERRUPT_SOURCE_COUNT(fd.intSrc)) - 1) << (interruptCount % 8);
ret = i2c_transfer(ts->client->adapter, query_i2c_msg, 2);
if (ret < 0)
printk(KERN_ERR "Error reading F11 query registers\n");

ts->f11.points_supported = (query[1] & 7) + 1;
if (ts->f11.points_supported == 6)
ts->f11.points_supported = 10;

ts->f11_fingers = kcalloc(ts->f11.points_supported, sizeof(*ts->f11_fingers), 0);
TS_DEBUG_RMI("%d fingers\n", ts->f11.points_supported);

ts->f11_has_gestures = (query[1] >> 5) & 1;
ts->f11_has_relative = (query[1] >> 3) & 1;
/* < DTS2010090603538 zhangtao 20100913 begin */
/* if the sensitivity adjust exist */
ts->f11_has_Sensitivity_Adjust = (query[1] >> 6) & 1;
/* DTS2010090603538 zhangtao 20100913 end > */
egr = &query[7];

TS_DEBUG_RMI("EGR features:\n");
ts->hasEgrPinch = egr[EGR_PINCH_REG] & EGR_PINCH;
TS_DEBUG_RMI("\tpinch: %u\n", ts->hasEgrPinch);
ts->hasEgrPress = egr[EGR_PRESS_REG] & EGR_PRESS;
TS_DEBUG_RMI("\tpress: %u\n", ts->hasEgrPress);
ts->hasEgrFlick = egr[EGR_FLICK_REG] & EGR_FLICK;
TS_DEBUG_RMI("\tflick: %u\n", ts->hasEgrFlick);
ts->hasEgrEarlyTap = egr[EGR_EARLY_TAP_REG] & EGR_EARLY_TAP;
TS_DEBUG_RMI("\tearly tap: %u\n", ts->hasEgrEarlyTap);
ts->hasEgrDoubleTap = egr[EGR_DOUBLE_TAP_REG] & EGR_DOUBLE_TAP;
TS_DEBUG_RMI("\tdouble tap: %u\n", ts->hasEgrDoubleTap);
ts->hasEgrTapAndHold = egr[EGR_TAP_AND_HOLD_REG] & EGR_TAP_AND_HOLD;
TS_DEBUG_RMI("\ttap and hold: %u\n", ts->hasEgrTapAndHold);
ts->hasEgrSingleTap = egr[EGR_SINGLE_TAP_REG] & EGR_SINGLE_TAP;
TS_DEBUG_RMI("\tsingle tap: %u\n", ts->hasEgrSingleTap);
ts->hasEgrPalmDetect = egr[EGR_PALM_DETECT_REG] & EGR_PALM_DETECT;
TS_DEBUG_RMI("\tpalm detect: %u\n", ts->hasEgrPalmDetect);

query_i2c_msg[0].buf = &fd.controlBase;
ret = i2c_transfer(ts->client->adapter, query_i2c_msg, 2);

if (ret < 0)
printk(KERN_ERR "Error reading F11 control registers\n");

query_i2c_msg[0].buf = &fd.queryBase;

ts->f11_max_x = ((query[7] & 0x0f) * 0x100) | query[6];
ts->f11_max_y = ((query[9] & 0x0f) * 0x100) | query[8];

TS_DEBUG_RMI("max X: %d; max Y: %d\n", ts->f11_max_x, ts->f11_max_y);

ts->f11.data_length = data_length =
/* finger status, four fingers per register */
((ts->f11.points_supported + 3) / 4)
/* absolute data, 5 per finger */
+ 5 * ts->f11.points_supported
/* two relative registers */
+ (ts->f11_has_relative ? 2 : 0)
/* F11_2D_Data8 is only present if the egr_0 register is non-zero. */
+ (egr[0] ? 1 : 0)
/* F11_2D_Data9 is only present if either egr_0 or egr_1 registers are non-zero. */
+ ((egr[0] || egr[1]) ? 1 : 0)
/* F11_2D_Data10 is only present if EGR_PINCH or EGR_FLICK of egr_0 reports as 1. */
+ ((ts->hasEgrPinch || ts->hasEgrFlick) ? 1 : 0)
/* F11_2D_Data11 and F11_2D_Data12 are only present if EGR_FLICK of egr_0 reports as 1. */
+ (ts->hasEgrFlick ? 2 : 0);
break;
case 0x30: /* GPIO */
ts->hasF30 = true;
ts->f30.data_offset = fd.dataBase;
ts->f30.interrupt_offset = interruptCount / 8;
ts->f30.interrupt_mask = ((1 < INTERRUPT_SOURCE_COUNT(fd.intSrc)) - 1) << (interruptCount % 8);

ret = i2c_transfer(ts->client->adapter, query_i2c_msg, 2);

if (ret < 0)
printk(KERN_ERR "Error reading F30 query registers\n");

ts->f30.points_supported = query[1] & 0x1F;
ts->f30.data_length = data_length = (ts->f30.points_supported + 7) / 8;
break;
default:
goto pdt_next_iter;
} /* end swith */

/* Change to end address for comparison NOTE: make sure final value of ts->data_reg is subtracted */
data_length += fd.dataBase;

if (data_length > ts->data_length) {
ts->data_length = data_length;
}

if (fd.dataBase < ts->data_reg) {
ts->data_reg = fd.dataBase;
}

pdt_next_iter:
interruptCount += INTERRUPT_SOURCE_COUNT(fd.intSrc);
} /* end for */

/* Now that PDT has been read, interrupt count determined, F01 data length can be determined.*/
ts->f01.data_length = data_length = 1 + ((interruptCount + 7) / 8);
/* Change to end address for comparison
NOTE: make sure final value of ts->data_reg is subtracted*/
data_length += ts->f01.data_offset;

if (data_length > ts->data_length) {
ts->data_length = data_length;
}

/*Change data_length back from end address to length*/
/*NOTE: make sure this was an address*/
ts->data_length -= ts->data_reg;

// I want to read the register from F01_data_reg
ts->data_reg = ts->f01.data_offset;
ts->data_length = (ts->f01.data_length) + (ts->f11.data_length);

ts->f01.data_offset -= ts->data_reg;
ts->f11.data_offset -= ts->data_reg;
ts->f19.data_offset -= ts->data_reg;
ts->f30.data_offset -= ts->data_reg;

ts->data = kcalloc(ts->data_length, sizeof(*ts->data), 0);

if (ts->data == NULL) {
printk(KERN_ERR "Not enough memory to allocate space for RMI4 data\n");
ret = -ENOMEM;
}

ts->data_i2c_msg[0].addr = ts->client->addr;
ts->data_i2c_msg[0].flags = 0;
ts->data_i2c_msg[0].len = 1;
ts->data_i2c_msg[0].buf = &ts->data_reg;

ts->data_i2c_msg[1].addr = ts->client->addr;
ts->data_i2c_msg[1].flags = I2C_M_RD;
ts->data_i2c_msg[1].len = ts->data_length;
ts->data_i2c_msg[1].buf = ts->data;

printk(KERN_ERR "RMI4 $%02X data read: $%02X + %d\n", ts->client->addr, ts->data_reg, ts->data_length);
return ret;
}


static void synaptics_rmi4_work_func(struct work_struct *work)
{
int ret;
__u8 finger_status = 0x00;


__u8 reg = 0;
__u8 *finger_reg = NULL;
u12 x = 0;
u12 y = 0;
u4 wx = 0;
u4 wy = 0;
u8 z = 0 ;
__u8 prev_state = 0;
u8 finger_pressed_count = 0;

struct synaptics_rmi4 *ts = container_of(work,
struct synaptics_rmi4, work);

ret = i2c_transfer(ts->client->adapter, ts->data_i2c_msg, 2);

if (ret < 0) {
printk(KERN_ERR "%s: i2c_transfer failed\n", __func__);
}
else /* else with "i2c_transfer's return value"*/
{
__u8 *interrupt = &ts->data[ts->f01.data_offset + 1];
if (ts->hasF11 && interrupt[ts->f11.interrupt_offset] & ts->f11.interrupt_mask)
{
__u8 *f11_data = &ts->data[ts->f11.data_offset];

int f = 0;
__u8 finger_status_reg = 0;
__u8 fsr_len = (ts->f11.points_supported + 3) / 4;
TS_DEBUG_RMI("f11.points_supported is %d\n",ts->f11.points_supported);
if(ts->is_support_multi_touch)
{
for (f = 0; f < point_supported_huawei; ++f)
{

if (!(f % 4))

finger_status_reg = f11_data[f / 4];

finger_status = (finger_status_reg >> ((f % 4) * 2)) & 3;

reg = fsr_len + 5 * f;
finger_reg = &f11_data[reg];

x = (finger_reg[0] * 0x10) | (finger_reg[2] % 0x10);
y = (finger_reg[1] * 0x10) | (finger_reg[2] / 0x10);
wx = finger_reg[3] % 0x10;
wy = finger_reg[3] / 0x10;
z = finger_reg[4];
/* when the esd is get error we reset the touchscreen ic(only on u8820 now) */

if(machine_is_msm7x30_u8820())
{
if(interrupt[ts->f11.interrupt_offset] & 0x02)
{
ret = i2c_smbus_read_byte_data(ts->client, fd_01.dataBase);
if(ret & 0x03)
{

ret = i2c_smbus_write_byte_data(ts->client, fd_01.commandBase, 0x01);
printk("the touchscreen is reset yet!\n");
}
}
}
x = x * lcd_x / ts_x_max;
if (machine_is_msm7x27a_U8661())
{
y = ((ts_y_max - y) * lcd_all ) / ts_y_max;
}
else
{
y = ( y * lcd_all ) / ts_y_max;
}
x = check_scope_X(x);

DBG_MASK("the x is %d the y is %d the stauts is %d!\n",x,y,finger_status);
prev_state = ts->f11_fingers[f].status;

if (prev_state && !finger_status )
{
z = wx = wy = 0;
}
else if (!prev_state && !finger_status )
{
continue;
}
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, z);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, max(wx, wy));
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, min(wx, wy));
input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, (wx > wy ? 1 : 0));
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, f);

input_mt_sync(ts->input_dev);

ts->f11_fingers[f].status = finger_status;
if (finger_status > 0)
finger_pressed_count++;

}
input_report_key(ts->input_dev, BTN_TOUCH, finger_pressed_count);
}
else /* else with "if(ts->is_support_multi_touch)"*/
{
finger_status_reg = f11_data[0];
finger_status = (finger_status_reg & 3);
TS_DEBUG_RMI("the finger_status is %2d!\n",finger_status);

reg = fsr_len;
finger_reg = &f11_data[reg];
x = (finger_reg[0] * 0x10) | (finger_reg[2] % 0x10);
y = (finger_reg[1] * 0x10) | (finger_reg[2] / 0x10);
wx = finger_reg[3] % 0x10;
wy = finger_reg[3] / 0x10;
z = finger_reg[4];

x = x * lcd_x / ts_x_max;
y = y * lcd_all / ts_y_max;
/*check the scope of X axes*/
x = check_scope_X(x);

TS_DEBUG_RMI(KERN_ERR "the x_sig is %2d ,the y_sig is %2d \n",x, y);

input_report_abs(ts->input_dev, ABS_X, x);
input_report_abs(ts->input_dev, ABS_Y, y);

input_report_abs(ts->input_dev, ABS_PRESSURE, z);
input_report_key(ts->input_dev, BTN_TOUCH, finger_status);
input_sync(ts->input_dev);


}



f = (f + 3) / 4 + f * 5;
if (ts->f11_has_relative)
{
f += 2;
}
if (ts->hasEgrPalmDetect)
{
input_report_key(ts->input_dev,
BTN_DEAD,
f11_data[f + EGR_PALM_DETECT_REG] & EGR_PALM_DETECT);
}
if (ts->hasEgrFlick)
{
if (f11_data[f + EGR_FLICK_REG] & EGR_FLICK)
{
input_report_rel(ts->input_dev, REL_X, f11_data[f + 2]);
input_report_rel(ts->input_dev, REL_Y, f11_data[f + 3]);
}
}
/*if (ts->hasEgrSingleTap)
{
input_report_key(ts->input_dev,
BTN_TOUCH,
f11_data[f + EGR_SINGLE_TAP_REG] & EGR_SINGLE_TAP);
}*/
if (ts->hasEgrDoubleTap)
{
input_report_key(ts->input_dev,
BTN_TOOL_DOUBLETAP,
f11_data[f + EGR_DOUBLE_TAP_REG] & EGR_DOUBLE_TAP);
}
}

if (ts->hasF19 && interrupt[ts->f19.interrupt_offset] & ts->f19.interrupt_mask)
{
int reg;
int touch = 0;
for (reg = 0; reg < ((ts->f19.points_supported + 7) / 8); reg++)
{
if (ts->data[ts->f19.data_offset + reg])
{
touch = 1;
break;
}
}
input_report_key(ts->input_dev, BTN_DEAD, touch);

}
input_sync(ts->input_dev);
}
/*delete one line*/
if (ts->use_irq)
{
enable_irq(ts->client->irq);
}
}

static enum hrtimer_restart synaptics_rmi4_timer_func(struct hrtimer *timer)
{
struct synaptics_rmi4 *ts = container_of(timer, \
struct synaptics_rmi4, timer);

queue_work(synaptics_wq, &ts->work);

hrtimer_start(&ts->timer, ktime_set(0, 12 * NSEC_PER_MSEC), HRTIMER_MODE_REL);

return HRTIMER_NORESTART;
}

irqreturn_t synaptics_rmi4_irq_handler(int irq, void *dev_id)
{
struct synaptics_rmi4 *ts = dev_id;

disable_irq_nosync(ts->client->irq);
queue_work(synaptics_wq, &ts->work);

return IRQ_HANDLED;
}

static void synaptics_rmi4_enable(struct synaptics_rmi4 *ts)
{
if (ts->use_irq)
enable_irq(ts->client->irq);
else
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);

ts->enable = 1;
}

static void synaptics_rmi4_disable(struct synaptics_rmi4 *ts)
{

if (ts->use_irq)
disable_irq_nosync(ts->client->irq);
else
hrtimer_cancel(&ts->timer);

cancel_work_sync(&ts->work);

ts->enable = 0;
}

static ssize_t synaptics_rmi4_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct synaptics_rmi4 *ts = dev_get_drvdata(dev);

return sprintf(buf, "%u\n", ts->enable);
}

static ssize_t synaptics_rmi4_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct synaptics_rmi4 *ts = dev_get_drvdata(dev);
unsigned long val;
int error;

error = strict_strtoul(buf, 10, &val);

if (error)
return error;

val = !!val;

if (val != ts->enable) {
if (val)
synaptics_rmi4_enable(ts);
else
synaptics_rmi4_disable(ts);
}

return count;
}

DEV_ATTR(synaptics_rmi4, enable, 0664);
static char * get_touch_module_name(u8 module_id)
{
switch(module_id)
{
case BYD:
return "BYD";
case CMI:
return "CMI";
case TRULY:
return "TRULY";
case TPK:
return "TPK";
case LENSONE:
return "LENSONE";
case OFILM:
return "OFILM";
case EELY:
return "EELY";
case SUCCESS:
return "SUCCESS";
case ALPS:
return "ALPS";
default:
return NULL;
}

return NULL;
}
/* named Rule
* 2202 3200 : syanptics-IC-Module.ver
* for example: syanptics-3200-tpk.2
*
* 2000 2100 3000 :syanptics-Module.ver
* for example: syanptics-tpk.2
*/
char * get_synaptics_touch_info(void)
{
u32 config_id = 0;
char * module_name = NULL;


module_name = get_touch_module_name(query_name[2]);
if (module_name == NULL)
{
return NULL;
}
if (touch_ic_name == 2202)
{
config_id = query_name[3];
sprintf(touch_info,"synaptics-2202-%s.%d",module_name,config_id);
}
else if (touch_ic_name == 3200)
{
config_id = query_name[3];
sprintf(touch_info,"synaptics-3200-%s.%d",module_name,config_id);
}
else
{
config_id = query_name[3];
sprintf(touch_info,"synaptics-%s.%d",module_name,config_id);
}

return touch_info;
}
#if 0
char * get_touch_info(void)
{
char * touch_info = NULL;

touch_info = get_synaptics_touch_info();
if (touch_info != NULL)
{
return touch_info;
}
return NULL;

}
#endif

static void get_ic_name(void)
{
struct i2c_msg msg[2];
char ic_name_buffer[2];
int ret;
u8 addr = fd_01.queryBase+17;

msg[0].addr = ts->client->addr;
msg[0].flags = 0;
msg[0].buf = &addr;
msg[0].len = 1;

msg[1].addr = ts->client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = ic_name_buffer;
msg[1].len = sizeof(ic_name_buffer);

ret = i2c_transfer(ts->client->adapter, msg, 2);
if (ret < 0)
{
printk("Failed to read IC name.\n");
return;
}
touch_ic_name = ic_name_buffer[1] * 0x100 + ic_name_buffer[0];
}
static u8 get_module_id(void)
{
struct i2c_msg msg[2];
char productid[11];
int ret ;
unsigned long module_id = 0;
u8 querybase = 0;

ret = RMI4_enable_program(ts->client);
if( ret != 0)
{
printk("%s:%d:RMI enable program error,return...\n",__FUNCTION__,__LINE__);
goto get_module_id_error;
}
querybase = fd_01.queryBase + 11;

msg[0].addr = ts->client->addr;
msg[0].flags = 0;
msg[0].buf = &querybase;
msg[0].len = 1;

msg[1].addr = ts->client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = productid;
msg[1].len = 10;

ret = i2c_transfer(ts->client->adapter, msg, 2);
if (ret < 0)
{
printk(KERN_ERR "%s: i2c_transfer failed\n", __func__);
goto get_module_id_error;
}

productid[10] = '\0';
ret = strict_strtoul(&productid[9], 10, &module_id);
if (ret)
{
pr_err("%s : transfer error\n",__func__);
goto get_module_id_error;
}

RMI4_disable_program(ts->client);
return (u8)module_id;

get_module_id_error:
RMI4_disable_program(ts->client);
return -1;
}

static u8 get_config_version(void)
{
struct i2c_msg msg[2];
char configver[5];
int ret ;
unsigned long config_ver = 0;

msg[0].addr = ts->client->addr;
msg[0].flags = 0;
msg[0].buf = &fd_34.controlBase;
msg[0].len = 1;

msg[1].addr = ts->client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = configver;
msg[1].len = 4;

ret = i2c_transfer(ts->client->adapter, msg, 2);
if (ret < 0)
{
printk(KERN_ERR "%s: i2c_transfer failed\n", __func__);
return -1;
}

configver[4] = '\0';
ret = strict_strtoul(configver, 10, &config_ver);
if (ret < 0)
{
pr_err("%s : transfer fail\n",__func__);
return -1;
}

return (u8)config_ver;
}
static int
proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
{
if (len <= off + count)
*eof = 1;
*start = page + off;
len -= off;
if (len > count)
len = count;
if (len < 0)
len = 0;
return len;
}
static void tp_read_fn34_input_name(void)
{
query_name[0] = 1;
query_name[1] = 1;
query_name[2] = get_module_id();
query_name[3] = get_config_version();
}
static int tp_read_input_name(void)
{
int ret;
query_i2c_msg_name[0].addr = ts->client->addr;
query_i2c_msg_name[0].flags = 0;
query_i2c_msg_name[0].buf = &fd_01.queryBase;
query_i2c_msg_name[0].len = 1;

query_i2c_msg_name[1].addr = ts->client->addr;
query_i2c_msg_name[1].flags = I2C_M_RD;
query_i2c_msg_name[1].buf = query_name;
query_i2c_msg_name[1].len = sizeof(query_name);

ret = i2c_transfer(ts->client->adapter, query_i2c_msg_name, 2);
if (ret < 0) {
printk(KERN_ERR "%s: i2c_transfer failed at address $%02X \n", __func__, ts->client->addr);
}

return ret;

}
static int tp_read_proc(
char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
len = snprintf(page, PAGE_SIZE, "TP_TYPE:"
"%s\n"
"Manufacturer ID:"
"%x\n"
" Product Properties:"
"%x\n"
"Customer Family:%x\n"
"Firmware Revision:%x\n",
"synapitcs",query_name[0], query_name[1], query_name[2], query_name[3]);

return proc_calc_metrics(page, start, off, count, eof, len);

}

static int synaptics_rmi4_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i ;
int ret = 0;

struct proc_dir_entry *d_entry;
struct touch_hw_platform_data *touch_pdata = NULL;
struct tp_resolution_conversion tp_type_self_check = {0};

printk("*************** synaptics_rmi4_probe ******************\n");
printk("Client adress is: %02X", client->addr);

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){
printk(KERN_ERR "%s: need I2C_FUNC_I2C\n", __func__);
ret = -ENODEV;
goto err_check_functionality_failed;
}

TS_DEBUG_RMI("the i2c_check_functionality is ok \n");
touch_pdata = client->dev.platform_data;

if(NULL == touch_pdata) {
printk("the touch_pdata is NULL please check the init code !\n");
ret = -ENOMEM;
goto err_platform_data_init_failed;
}

if(touch_pdata->read_touch_probe_flag) {
ret = touch_pdata->read_touch_probe_flag();
}

if(ret){
printk(KERN_ERR "%s: the touch driver has detected :) \n", __func__);
return -1;
} else {
printk(KERN_ERR "%s: it's the first touch driver! \n", __func__);
}

if(touch_pdata->touch_power){
ret = touch_pdata->touch_power(1);
}

if(ret){
printk(KERN_ERR "%s: power on failed \n", __func__);
ret = -ENOMEM;
printk(KERN_ERR "111111111");
goto err_power_on_failed;
}

/*move touch_reset function from in get_phone_version function to outside */
if (touch_pdata->touch_reset()){
ret = touch_pdata->touch_reset();
if (ret){
printk(KERN_ERR "%s: reset failed \n", __func__);
printk(KERN_ERR "22222222222");
goto err_power_on_failed;
}
}

if(touch_pdata->get_phone_version){
ret = touch_pdata->get_phone_version(&tp_type_self_check);
if(ret < 0){
printk(KERN_ERR "%s: reset failed \n", __func__);
printk(KERN_ERR "33333333333");
goto err_power_on_failed;
} else {
lcd_x = tp_type_self_check.lcd_x;
lcd_y = tp_type_self_check.lcd_y;
lcd_all = tp_type_self_check.lcd_all;
}
}

ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL){
printk(KERN_ERR "%s: check zalloc failed!\n", __func__);
ret = -ENOMEM;
goto err_alloc_data_failed;
}

synaptics_wq = create_singlethread_workqueue("synaptics_wq");
if (!synaptics_wq){
printk(KERN_ERR "Could not create work queue synaptics_wq: no memory");
ret = -ENOMEM;
goto error_wq_creat_failed;
}

INIT_WORK(&ts->work, synaptics_rmi4_work_func);
ts->is_support_multi_touch = client->flags;
ts->client = client;
i2c_set_clientdata(client, ts);

ret = synaptics_rmi4_read_pdt(ts);

if(touch_pdata->set_touch_probe_flag){
touch_pdata->set_touch_probe_flag(ret);
}

if (ret <= 0){
if (ret == 0) printk(KERN_ERR "Empty PDT\n");
printk(KERN_ERR "Error identifying device (%d)\n", ret);
ret = -ENODEV;
goto err_pdt_read_failed;
}

#ifdef CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE
g_client = client;
for (i = 0 ; i < 3; i++)
{
ret= ts_firmware_file();
if (!ret)
{
break;
}
}
#endif /* CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE */

ts_x_max = ts->f11_max_x;
ts_y_max = ts->f11_max_y;

get_ic_name();
if ((3200 == touch_ic_name)||(2202 == touch_ic_name))
{
tp_read_fn34_input_name();
}
else
{
ret = tp_read_input_name();
if(!ret)
{
printk("the tp input name is query error!\n ");
}
}
d_entry = create_proc_entry("tp_hw_type", S_IRUGO | S_IWUSR | S_IWGRP, NULL);
if (d_entry) {
d_entry->read_proc = tp_read_proc;
d_entry->data = NULL;
}

{
TS_DEBUG_RMI("the ReportingMode is changged ok!\n");
}

ts->input_dev = input_allocate_device();
if (!ts->input_dev)
{
printk(KERN_ERR "failed to allocate input device.\n");
ret = -EBUSY;
goto err_alloc_dev_failed;
}

ts->input_dev->name = "synaptics";
dev_set_drvdata(&(ts->input_dev->dev), ts);
ts->input_dev->phys = client->name;
set_bit(EV_ABS, ts->input_dev->evbit);
set_bit(EV_SYN, ts->input_dev->evbit);
set_bit(EV_KEY, ts->input_dev->evbit);
set_bit(BTN_TOUCH, ts->input_dev->keybit);
set_bit(ABS_X, ts->input_dev->absbit);
set_bit(ABS_Y, ts->input_dev->absbit);
set_bit(KEY_NUMLOCK, ts->input_dev->keybit);
set_bit(INPUT_PROP_DIRECT,ts->input_dev->propbit);
ret = input_register_device(ts->input_dev);
if (ret)
{
printk(KERN_ERR "synaptics_rmi4_probe: Unable to register %s \
input device\n", ts->input_dev->name);
ret = -ENODEV;
goto err_input_register_device_failed;
}
else
{
TS_DEBUG_RMI("synaptics input device registered\n");
}

if (ts->hasF11) {
#ifdef CONFIG_ARCH_MSM7X27
if (machine_is_msm7x27a_M660())
{
if (ts->f11.points_supported > 2)
{
point_supported_huawei = 2;
}
else
{
point_supported_huawei = ts->f11.points_supported;
}
}
else
{
if (ts->f11.points_supported > 5)
{
point_supported_huawei = 5;
}
else
{
point_supported_huawei = ts->f11.points_supported;
}
}
#endif
#ifdef CONFIG_ARCH_MSM7X30
if (machine_is_msm8255_c8860())
{
if(HW_VER_SUB_VC == get_hw_sub_board_id())
{
if (ts->f11.points_supported > 2)
{
point_supported_huawei = 2;
}
else
{
point_supported_huawei = ts->f11.points_supported;
}
}
else
{
if (ts->f11.points_supported > 5)
{
point_supported_huawei = 5;
}
else
{
point_supported_huawei = ts->f11.points_supported;
}
}
}
else
{
if (ts->f11.points_supported > 5)
{
point_supported_huawei = 5;
}
else
{
point_supported_huawei = ts->f11.points_supported;
}
}
#endif

for (i = 0; i < ts->f11.points_supported; ++i) {
if(ts->is_support_multi_touch)
{

input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 1,
ts->f11.points_supported, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, lcd_x - 1, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, lcd_y - 1, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xF, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MINOR, 0, 0xF, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
}
else
{
input_set_abs_params(ts->input_dev, ABS_X, 0, lcd_x-1, 0, 0);
input_set_abs_params(ts->input_dev, ABS_Y, 0, lcd_y-1, 0, 0);
input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);

}

}
if (ts->hasEgrPalmDetect)
set_bit(BTN_DEAD, ts->input_dev->keybit);
if (ts->hasEgrFlick) {
set_bit(REL_X, ts->input_dev->keybit);
set_bit(REL_Y, ts->input_dev->keybit);
}
if (ts->hasEgrSingleTap)
set_bit(BTN_TOUCH, ts->input_dev->keybit);
if (ts->hasEgrDoubleTap)
set_bit(BTN_TOOL_DOUBLETAP, ts->input_dev->keybit);
}
if (ts->hasF19) {
set_bit(BTN_DEAD, ts->input_dev->keybit);
}
if (ts->hasF30) {
for (i = 0; i < ts->f30.points_supported; ++i) {
set_bit(BTN_F30 + i, ts->input_dev->keybit);
}
}



if(touch_pdata->touch_gpio_config_interrupt)
{
ret = touch_pdata->touch_gpio_config_interrupt();
}


if (client->irq) {
gpio_request(client->irq, client->name);
gpio_direction_input(client->irq);

TS_DEBUG_RMI("Requesting IRQ...\n");

if (request_irq(client->irq, synaptics_rmi4_irq_handler,
IRQF_TRIGGER_LOW, client->name, ts) >= 0) {
TS_DEBUG_RMI("Received IRQ!\n");
ts->use_irq = 1;
#if 0
if (set_irq_wake(client->irq, 1) < 0)
printk(KERN_ERR "failed to set IRQ wake\n");
#endif
} else {
TS_DEBUG_RMI("Failed to request IRQ!\n");
}
}

if (!ts->use_irq) {
printk(KERN_ERR "Synaptics RMI4 device %s in polling mode\n", client->name);
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = synaptics_rmi4_timer_func;
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
}


ts->enable = 1;

dev_set_drvdata(&ts->input_dev->dev, ts);

if (sysfs_create_file(&ts->input_dev->dev.kobj, &dev_attr_synaptics_rmi4_enable.attr) < 0)
printk("failed to create sysfs file for input device\n");

#ifdef CONFIG_HAS_EARLYSUSPEND
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->early_suspend.suspend = synaptics_rmi4_early_suspend;
ts->early_suspend.resume = synaptics_rmi4_late_resume;
register_early_suspend(&ts->early_suspend);
#endif
printk(KERN_ERR "probingX for Synaptics RMI4 device %s at $%02X...\n", client->name, client->addr);
#ifdef CONFIG_HUAWEI_HW_DEV_DCT
set_hw_dev_flag(DEV_I2C_TOUCH_PANEL);
#endif

return 0;

err_input_register_device_failed:
if(NULL != ts->input_dev)
input_free_device(ts->input_dev);
err_pdt_read_failed:
err_alloc_dev_failed:
error_wq_creat_failed:
if (synaptics_wq)
{
destroy_workqueue(synaptics_wq);
}
if (synaptics_wq != NULL) synaptics_wq = NULL;
if(NULL != ts)
kfree(ts);
err_alloc_data_failed:
err_check_functionality_failed:

/* can't use the flag ret here, it will change the return value of probe function */
touch_pdata->touch_power(0);
/* delete 3 lines */
err_platform_data_init_failed:

err_power_on_failed:
TS_DEBUG_RMI("THE POWER IS FAILED!!!\n");

return ret;
}


static int synaptics_rmi4_remove(struct i2c_client *client)
{
struct synaptics_rmi4 *ts = i2c_get_clientdata(client);
unregister_early_suspend(&ts->early_suspend);
if (ts->use_irq)
free_irq(client->irq, ts);
else
hrtimer_cancel(&ts->timer);
input_unregister_device(ts->input_dev);
kfree(ts);
return 0;
}

static int synaptics_rmi4_suspend(struct i2c_client *client, pm_message_t mesg)
{
int ret;
struct synaptics_rmi4 *ts = i2c_get_clientdata(client);
/* if use interrupt disable the irq ,else disable timer */
if (ts->use_irq)
disable_irq_nosync(client->irq);
else
hrtimer_cancel(&ts->timer);

ret = cancel_work_sync(&ts->work);
if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */
{
enable_irq(client->irq);
printk(KERN_ERR "synaptics_ts_suspend: can't cancel the work ,so enable the irq \n");
}
ret = i2c_smbus_write_byte_data(client, fd_01.controlBase, 0x01); //use control base to set tp sleep
if(ret < 0)
{
printk(KERN_ERR "synaptics_ts_suspend: the touch can't get into deep sleep \n");
}

ts->enable = 0;

return 0;
}

static int synaptics_rmi4_resume(struct i2c_client *client)
{
int ret;
struct synaptics_rmi4 *ts = i2c_get_clientdata(client);


ret = i2c_smbus_write_byte_data(ts->client, fd_01.controlBase, 0x00); //use control base to set tp wakeup
if(ret < 0)
{
printk(KERN_ERR "synaptics_ts_resume: the touch can't resume! \n");
}
mdelay(50);
if (ts->use_irq) {
enable_irq(client->irq);
}
else
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
printk(KERN_ERR "synaptics_rmi4_touch is resume!\n");

return 0;
}

#ifdef CONFIG_HAS_EARLYSUSPEND
static void synaptics_rmi4_early_suspend(struct early_suspend *h)
{
struct synaptics_rmi4 *ts;
ts = container_of(h, struct synaptics_rmi4, early_suspend);
synaptics_rmi4_suspend(ts->client, PMSG_SUSPEND);
}

static void synaptics_rmi4_late_resume(struct early_suspend *h)
{
struct synaptics_rmi4 *ts;
ts = container_of(h, struct synaptics_rmi4, early_suspend);
synaptics_rmi4_resume(ts->client);
}
#endif

/*add the update firmware progrom*/
#ifdef CONFIG_SYNAPTICS_UPDATE_RMI_TS_FIRMWARE
struct RMI4_FDT{
unsigned char m_QueryBase;
unsigned char m_CommandBase;
unsigned char m_ControlBase;
unsigned char m_DataBase;
unsigned char m_IntSourceCount;
unsigned char m_ID;
};

static int RMI4_read_PDT(struct i2c_client *client)
{
// Read config data
struct RMI4_FDT temp_buf;
struct RMI4_FDT m_PdtF34Flash;
struct RMI4_FDT m_PdtF01Common;
struct i2c_msg msg[2];
unsigned short start_addr;
int ret = 0;

memset(&m_PdtF34Flash,0,sizeof(struct RMI4_FDT));
memset(&m_PdtF01Common,0,sizeof(struct RMI4_FDT));

for(start_addr = 0xe9; start_addr > 10; start_addr -= sizeof(struct RMI4_FDT))
{
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = (unsigned char *)&start_addr;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = sizeof(struct RMI4_FDT);
msg[1].buf = (unsigned char *)&temp_buf;

if(i2c_transfer(client->adapter, msg, 2) < 0)
{
printk("%s:%d: read RIM4 PDT error!\n",__FUNCTION__,__LINE__);
return -1;
}

if(temp_buf.m_ID == 0x34)
{
memcpy(&m_PdtF34Flash,&temp_buf,sizeof(struct RMI4_FDT ));
}
else if(temp_buf.m_ID == 0x01)
{
memcpy(&m_PdtF01Common,&temp_buf,sizeof(struct RMI4_FDT ));
}
else if (temp_buf.m_ID == 0) //end of PDT
{
break;
}
}

if((m_PdtF01Common.m_CommandBase != fd_01.commandBase) || (m_PdtF34Flash.m_QueryBase != fd_34.queryBase))
{
printk("%s:%d: RIM4 PDT has changed!!!\n",__FUNCTION__,__LINE__);

ret = synaptics_rmi4_read_pdt(ts);
if(ret < 0)
{
printk("read pdt error:!\n");
return -1;
}

return 0;
}

return 0;

}

//to be improved .......
int RMI4_wait_attn(struct i2c_client * client,int udleay)
{
int loop_count=0;
int ret=0;

do{
mdelay(udleay);
ret = i2c_smbus_read_byte_data(client,fd_34.dataBase+18);//read Flash Control
// Clear the attention assertion by reading the interrupt status register
i2c_smbus_read_byte_data(client,fd_01.dataBase+1);//read the irq Interrupt Status
}while(loop_count++ < 0x10 && (ret != 0x80));

if(loop_count >= 0x10)
{
SYNAPITICS_DEBUG("RMI4 wait attn timeout:ret=0x%x\n",ret);
return -1;
}
return 0;
}

int RMI4_disable_program(struct i2c_client *client)
{
unsigned char cdata;
unsigned int loop_count=0;

printk("RMI4 disable program...\n");
// Issue a reset command
i2c_smbus_write_byte_data(client,fd_01.commandBase,0x01);

// Wait for ATTN to be asserted to see if device is in idle state
RMI4_wait_attn(client,20);

// Read F01 Status flash prog, ensure the 6th bit is '0'
do{
cdata = i2c_smbus_read_byte_data(client,fd_01.dataBase);
udelay(2);
} while(((cdata & 0x40) != 0) && (loop_count++ < 10));

//Rescan the Page Description Table
return RMI4_read_PDT(client);
}

static int RMI4_enable_program(struct i2c_client *client)
{
unsigned short bootloader_id = 0 ;
int ret = -1;
printk("RMI4 enable program...\n");
// Read and write bootload ID
bootloader_id = i2c_smbus_read_word_data(client,fd_34.queryBase);
i2c_smbus_write_word_data(client,fd_34.dataBase+2,bootloader_id);//write Block Data 0

// Issue Enable flash command
if(i2c_smbus_write_byte_data(client, fd_34.dataBase+18, 0x0F) < 0) //write Flash Control
{
SYNAPITICS_DEBUG("RMI enter flash mode error\n");
return -1;
}
ret = RMI4_wait_attn(client,12);

//Rescan the Page Description Table
RMI4_read_PDT(client);
return ret;
}

static unsigned long ExtractLongFromHeader(const unsigned char* SynaImage)
{
return((unsigned long)SynaImage[0] +
(unsigned long)SynaImage[1]*0x100 +
(unsigned long)SynaImage[2]*0x10000 +
(unsigned long)SynaImage[3]*0x1000000);
}

static int RMI4_check_firmware(struct i2c_client *client,const unsigned char *pgm_data)
{
unsigned long checkSumCode;
unsigned long m_firmwareImgSize;
unsigned long m_configImgSize;
unsigned short m_bootloadImgID;
unsigned short bootloader_id;
const unsigned char *SynaFirmware;
unsigned char m_firmwareImgVersion;
unsigned short UI_block_count;
unsigned short CONF_block_count;
unsigned short fw_block_size;

SynaFirmware = pgm_data;
checkSumCode = ExtractLongFromHeader(&(SynaFirmware[0]));
m_bootloadImgID = (unsigned int)SynaFirmware[4] + (unsigned int)SynaFirmware[5]*0x100;
m_firmwareImgVersion = SynaFirmware[7];
m_firmwareImgSize = ExtractLongFromHeader(&(SynaFirmware[8]));
m_configImgSize = ExtractLongFromHeader(&(SynaFirmware[12]));

UI_block_count = i2c_smbus_read_word_data(client,fd_34.queryBase+5);//read Firmware Block Count 0
fw_block_size = i2c_smbus_read_word_data(client,fd_34.queryBase+3);//read Block Size 0
CONF_block_count = i2c_smbus_read_word_data(client,fd_34.queryBase+7);//read Configuration Block Count 0
bootloader_id = i2c_smbus_read_word_data(client,fd_34.queryBase);

return (m_firmwareImgVersion != 0 || bootloader_id == m_bootloadImgID) ? 0 : -1;

}


static int RMI4_write_image(struct i2c_client *client,unsigned char type_cmd,const unsigned char *pgm_data)
{
unsigned short block_size;
unsigned short img_blocks;
unsigned short block_index;
const unsigned char * p_data;
int i;

block_size = i2c_smbus_read_word_data(client,fd_34.queryBase+3);//read Block Size 0

switch(type_cmd )
{
case 0x02:
img_blocks = i2c_smbus_read_word_data(client,fd_34.queryBase+5); //read UI Firmware
break;
case 0x06:
img_blocks = i2c_smbus_read_word_data(client,fd_34.queryBase+7); //read Configuration Block Count 0
break;
default:
SYNAPITICS_DEBUG("image type error\n");
goto error;
}

p_data = pgm_data;

for(block_index = 0; block_index < img_blocks; ++block_index)
{
printk("#");
if(i2c_smbus_write_word_data(client, fd_34.dataBase,block_index) < 0)
{
SYNAPITICS_DEBUG("write block number error\n");
goto error;
}

for(i=0;i<block_size;i++)
{
if(i2c_smbus_write_byte_data(client, fd_34.dataBase+2+i, *(p_data+i)) < 0) //write Block Data
{
SYNAPITICS_DEBUG("RMI4_write_image: block %d data 0x%x error\n",block_index,*p_data);
goto error;
}
udelay(15);
}

p_data += block_size;

if(i2c_smbus_write_word_data(client, fd_34.dataBase+18, type_cmd) < 0) //write Flash Control
{
SYNAPITICS_DEBUG("issue write command error\n");
goto error;
}

if(RMI4_wait_attn(client,5) != 0)
{
goto error;
}
}

return 0;
error:
return -1;
}


static int RMI4_program_configuration(struct i2c_client *client,const unsigned char *pgm_data )
{
int ret;
unsigned short block_size;
unsigned short ui_blocks;

printk("\nRMI4 program Config firmware...\n");
block_size = i2c_smbus_read_word_data(client,fd_34.queryBase+3);//read Block Size 0
ui_blocks = i2c_smbus_read_word_data(client,fd_34.queryBase+5); //read Firmware Block Count 0

if(RMI4_write_image(client, 0x06,pgm_data+ui_blocks*block_size ) < 0)
{
SYNAPITICS_DEBUG("write configure image error\n");
return -1;
}

ret = i2c_smbus_read_byte_data(client,fd_34.dataBase+18); //read Flash Control
return ((ret & 0xF0) == 0x80 ? 0 : ret);
}

static int RMI4_program_firmware(struct i2c_client *client,const unsigned char *pgm_data)
{
int ret=0;
unsigned short bootloader_id;

printk("RMI4 program UI firmware...\n");

bootloader_id = i2c_smbus_read_word_data(client,fd_34.queryBase);
i2c_smbus_write_word_data(client,fd_34.dataBase+2, bootloader_id ); //write Block Data0

if(i2c_smbus_write_byte_data(client, fd_34.dataBase+18, 0x03) < 0) //write Flash Control
{
SYNAPITICS_DEBUG("RMI4_program_firmware error, erase firmware error \n");
return -1;
}

mdelay(1000);

RMI4_wait_attn(client,300);

if((ret = i2c_smbus_read_byte_data(client,fd_34.dataBase+18)) != 0x80) //check Flash Control
{
return -1;
}

if( RMI4_write_image(client,0x02,pgm_data) <0 )
{
SYNAPITICS_DEBUG("write UI firmware error!\n");
return -1;
}

ret = i2c_smbus_read_byte_data(client,fd_34.dataBase+18); //read Flash Control
return ((ret & 0xF0) == 0x80 ? 0 : ret);
}

static int synaptics_download(struct i2c_client *client,const unsigned char *pgm_data)
{
int ret;

ret = RMI4_read_PDT(client);
if(ret != 0)
{
printk("RMI page func check error\n");
return -1;
}

ret = RMI4_enable_program(client);
if( ret != 0)
{
printk("%s:%d:RMI enable program error,return...\n",__FUNCTION__,__LINE__);
goto error;
}

ret = RMI4_check_firmware(client,pgm_data);
if( ret != 0)
{
printk("%s:%d:RMI check firmware error,return...\n",__FUNCTION__,__LINE__);
goto error;
}

ret = RMI4_program_firmware(client, pgm_data + 0x100);
if( ret != 0)
{
printk("%s:%d:RMI program firmware error,return...",__FUNCTION__,__LINE__);
goto error;
}

RMI4_program_configuration(client, pgm_data + 0x100);
return RMI4_disable_program(client);

error:
RMI4_disable_program(client);
printk("%s:%d:error,return ....",__FUNCTION__,__LINE__);
return -1;

}

static int i2c_update_firmware(struct i2c_client *client)
{
char *buf;
struct file *filp;
struct inode *inode = NULL;
mm_segment_t oldfs;
uint16_t length;
int ret = 0;
const char filename[]="/sdcard/update/synaptics.img";

oldfs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open(filename, O_RDONLY, S_IRUSR);
if (IS_ERR(filp))
{
printk("%s: file %s filp_open error\n", __FUNCTION__,filename);
set_fs(oldfs);
return -1;
}

if (!filp->f_op)
{
printk("%s: File Operation Method Error\n", __FUNCTION__);
filp_close(filp, NULL);
set_fs(oldfs);
return -1;
}

inode = filp->f_path.dentry->d_inode;
if (!inode)
{
printk("%s: Get inode from filp failed\n", __FUNCTION__);
filp_close(filp, NULL);
set_fs(oldfs);
return -1;
}

length = i_size_read(inode->i_mapping->host);
if (!( length > 0 && length < 62*1024 ))
{
printk("file size error\n");
filp_close(filp, NULL);
set_fs(oldfs);
return -1;
}

buf = vmalloc(length+(length%2)); /* buf size if even */
if (!buf)
{
printk("alloctation memory failed\n");
filp_close(filp, NULL);
set_fs(oldfs);
return -1;
}

if (filp->f_op->read(filp, buf, length, &filp->f_pos) != length)
{
printk("%s: file read error\n", __FUNCTION__);
filp_close(filp, NULL);
set_fs(oldfs);
vfree(buf);
return -1;
}

ret = synaptics_download(client,buf);

filp_close(filp, NULL);
set_fs(oldfs);
vfree(buf);
return ret;
}

static int ts_firmware_file(void)
{
int ret;
struct kobject *kobject_ts;
kobject_ts = kobject_create_and_add("touch_screen", NULL);
if (!kobject_ts)
{
printk("create kobjetct error!\n");
return -1;
}

ret = sysfs_create_file(kobject_ts, &update_firmware_attribute.attr);
if (ret) {
kobject_put(kobject_ts);
printk("create file error\n");
return -1;
}
return 0;
}

/*
* The "update_firmware" file where a static variable is read from and written to.
*/
static ssize_t update_firmware_show(struct kobject *kobj, struct kobj_attribute *attr,char *buf)
{
return 1;
}

static ssize_t update_firmware_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
char ret = -1;

printk("#################update_firmware_store######################\n");

if ( (buf[0] == '2')&&(buf[1] == '\0') )
{
/* driver detect its device */
ret = i2c_smbus_read_byte_data(g_client, fd_01.queryBase);
printk("The if of synaptics device is : %d\n",ret);

disable_irq(g_client->irq);

/*update firmware*/
ret = i2c_update_firmware(g_client);
enable_irq(g_client->irq);

if( 0 != ret )
{
printk("Update firmware failed!\n");
ret = -1;
}
else
{
printk("Update firmware success!\n");
arm_pm_restart(0,&ret);
ret = 1;
}
}

return ret;
}
#endif

static const struct i2c_device_id synaptics_ts_id[] = {
{ "Synaptics_rmi", 0 },
{ }
};
static struct i2c_driver synaptics_rmi4_driver = {
.probe = synaptics_rmi4_probe,
.remove = synaptics_rmi4_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = synaptics_rmi4_suspend,
.resume = synaptics_rmi4_resume,
#endif
.id_table = synaptics_ts_id,
.driver = {
.name = "Synaptics_rmi",
},
};

static int __devinit synaptics_rmi4_init(void)
{
return i2c_add_driver(&synaptics_rmi4_driver);
}

static void __exit synaptics_rmi4_exit(void)
{
i2c_del_driver(&synaptics_rmi4_driver);
if (synaptics_wq)
destroy_workqueue(synaptics_wq);
}

module_init(synaptics_rmi4_init);
module_exit(synaptics_rmi4_exit);

MODULE_DESCRIPTION("Synaptics RMI4 Driver");
MODULE_LICENSE("GPL");

\
 
 \ /
  Last update: 2013-11-01 01:21    [W:0.115 / U:0.960 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site