/* linux/drivers/input/touchscreen/s3c-ts.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
* iPAQ H1940 touchscreen support
*
* ChangeLog
*
* 2004-09-05: Herbert Potzl <herbert@13thfloor.at>
* - added clock (de-)allocation code
*
* 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
* - h1940_ -> s3c24xx (this driver is now also used on the n30
* machines :P)
* - Debug messages are now enabled with the config option
* TOUCHSCREEN_S3C_DEBUG
* - Changed the way the value are read
* - Input subsystem should now work
* - Use ioremap and readl/writel
*
* 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
* - Make use of some undocumented features of the touchscreen
* controller
*
* 2006-09-05: Ryu Euiyoul <ryu.real@gmail.com>
* - added power management suspend and resume code
*
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif /* CONFIG_HAS_EARLYSUSPEND */
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/regs-adc.h>
#include <mach/ts-s3c.h>
#include <mach/irqs.h>
#define CONFIG_TOUCHSCREEN_S3C_DEBUG
#undef CONFIG_TOUCHSCREEN_S3C_DEBUG
#define X_COOR_MIN 180
#define X_COOR_MAX 4000
#define X_COOR_FUZZ 32
#define Y_COOR_MIN 300
#define Y_COOR_MAX 3900
#define Y_COOR_FUZZ 30
/* For ts->dev.id.version */
#define S3C_TSVERSION 0x0101
#define WAIT4INT(x) (((x)<<8) | \
S3C_ADCTSC_YM_SEN | S3C_ADCTSC_YP_SEN | S3C_ADCTSC_XP_SEN | \
S3C_ADCTSC_XY_PST(3))
#define AUTOPST (S3C_ADCTSC_YM_SEN | S3C_ADCTSC_YP_SEN | S3C_ADCTSC_XP_SEN | \
S3C_ADCTSC_AUTO_PST | S3C_ADCTSC_XY_PST(0))
#define DEBUG_LVL KERN_DEBUG
#ifdef CONFIG_HAS_EARLYSUSPEND
void ts_early_suspend(struct early_suspend *h);
void ts_late_resume(struct early_suspend *h);
#endif /* CONFIG_HAS_EARLYSUSPEND */
#define LCD070 1
#define LCD043 2
#define LCD101 3
//#define LCD_MODE LCD043
#define LCD_MODE LCD070
//#define LCD_MODE LCD101
#define CALIBRATE_FILE "/data/pointercal" //lxg added.
/* Touchscreen default configuration */
struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
.resol_bit = 10
};
/*
* Definitions & global arrays.
*/
static char *s3c_ts_name = "S5P TouchScreen";
static void __iomem *ts_base;
static struct resource *ts_mem;
static struct resource *ts_irq;
static struct clk *ts_clock;
static struct s3c_ts_info *ts;
int calibrate_para[7] = {0, 1, 0, 0, 0, 1, 1}; //lxg added.
static int ts_filter_fixed(unsigned int * buf)
{
//printk("**********buf[0]=%d,buf[1]=%d***********\n",buf[0],buf[1]);
#if(LCD_MODE==LCD043)
#define XMIN 500
#define XMAX 15500
#define YMIN 2000
#define YMAX 14700
buf[0] = (buf[0] - XMIN) * 480 / (XMAX - XMIN);
buf[1] = (buf[1] - YMIN) * 272 / (YMAX - YMIN);
#elif(LCD_MODE==LCD070)
#define XMIN 400
#define XMAX 15800
#define YMIN 1350
#define YMAX 14800
buf[0] = (buf[0] - XMIN) * 800 / (XMAX - XMIN);
buf[1] = (buf[1] - YMIN) * 480 / (YMAX - YMIN);
#else//LCD101
#define XMIN 360
#define XMAX 15300
#define YMIN 1110
#define YMAX 14900
buf[0] = (buf[0] - XMIN) * 1024 / (XMAX - XMIN);
buf[1] = (buf[1] - YMIN) * 600 / (YMAX - YMIN);
#endif
return 1;
}
static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
int updown;
bool skip = false;
int x,y;
//int a0,a1,a2,a3,a4,a5,a6;
int buf[3];
//printk(KERN_ERR"calibrate para: %d %d %d %d %d %d %d \n", calibrate_para[0], calibrate_para[1], calibrate_para[2], calibrate_para[3], calibrate_para[4], calibrate_para[5], calibrate_para[6]);
data0 = readl(ts_base+S3C_ADCDAT0);
data1 = readl(ts_base+S3C_ADCDAT1);
updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));
if (updown)
{
if (ts->count)
{
#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
struct timeval tv;
do_gettimeofday(&tv);
//printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts->xp, ts->yp);
#endif
#ifdef CONFIG_ANDROID
#if 1
buf[1] = ts->xp;
buf[2] = ts->yp;
ts_filter_fixed(&buf[1]);
ts->xp = buf[1];
ts->yp = buf[2];
//printk("Convert before: Cordinates x=%d, y=%d\n",(int)ts->xp,(int)ts->yp);
x=(int) ts->xp;
y=(int) ts->yp;
ts->xp=(long) ((calibrate_para[0]+(calibrate_para[1]*x)+(calibrate_para[2]*y))/calibrate_para[6]);
ts->yp=(long) ((calibrate_para[3]+(calibrate_para[4]*x)+(calibrate_para[5]*y))/calibrate_para[6]);
#else
buf[1] = ts->xp;
buf[2] = ts->yp;
ts_filter_fixed(&buf[1]);
ts->xp = buf[1];
ts->yp = buf[2];
#endif
//printk("Cordinates x=%d, y=%d\n",(int)ts->xp,(int)ts->yp);
skip = false;
//ts->yp = 4000 - ts->yp;
if ((ts->xp_old != 0) && (ts->yp_old != 0) &&
((abs(ts->xp - ts->xp_old) > 100) || (abs(ts->yp - ts->yp_old) > 50)))
{
skip = true;
}
if ((ts->xp != ts->xp_old || ts->yp != ts->yp_old) && !skip)
{
input_report_abs(ts->dev, ABS_X, ts->xp);
input_report_abs(ts->dev, ABS_Y, ts->yp);
input_report_abs(ts->dev, ABS_Z, 0);
input_report_key(ts->dev, BTN_TOUCH, 1);
input_sync(ts->dev);
ts->xp_old = ts->xp;
ts->yp_old = ts->yp;
//printk("report x=%d, y=%d\n",(int)ts->xp,(int)ts->yp);
}
#else /* CONFIG_ANDROID */
input_report_abs(ts->dev, ABS_X, ts->xp);
input_report_abs(ts->dev, ABS_Y, ts->yp);
input_report_key(ts->dev, BTN_TOUCH, 1);
input_report_abs(ts->dev, ABS_PRESSURE, 1);
input_sync(ts->dev);
#endif /* CONFIG_ANDROID */
}
ts->xp_old = ts->xp;
ts->yp_old = ts->yp;
ts->xp = 0;
ts->yp = 0;
ts->count = 0;
writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);
} else {
ts->count = 0;
#ifdef CONFIG_ANDROID
input_report_abs(ts->dev, ABS_X, ts->xp);
input_report_abs(ts->dev, ABS_Y, ts->yp);
input_report_abs(ts->dev, ABS_Z, 0);
#endif /* CONFIG_ANDROID */
input_report_key(ts->dev, BTN_TOUCH, 0);
#ifndef CONFIG_ANDROID
input_report_abs(ts->dev, ABS_PRESSURE, 0);
#endif /* !CONFIG_ANDROID */
input_sync(ts->dev);
writel(WAIT4INT(0), ts_base+S3C_ADCTSC);
}
}
static struct timer_list touch_timer =
TIMER_INITIALIZER(touch_timer_fire, 0, 0);
static irqreturn_t stylus_updown(int irqno, void *param)
{
unsigned long data0;
unsigned long data1;
int updown;
data0 = readl(ts_base+S3C_ADCDAT0);
data1 = readl(ts_base+S3C_ADCDAT1);
updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));
#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
printk(KERN_INFO " %c\n", updown ? 'D' : 'U');
#endif
/* TODO we should never get an interrupt with updown set while
* the timer is running, but maybe we ought to verify that the
* tim