This repository has been archived on 2025-06-06. You can view files and clone it, but cannot push or open issues or pull requests.
Files
android-g900/kernel-2.6.33/arch/arm/mach-pxa/g900/g900_ts/tssc_manager.c
2011-04-04 23:17:39 +06:00

633 lines
16 KiB
C
Executable File

/* drivers/input/touchscreen/tssc_manager.c * * Copyright (C) 2008 HTC, Inc.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
/*#include <mach/iomap.h>*/
#include <mach/g900-gpio.h>
#include "tssc.h"
#include <linux/earlysuspend.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
static void tssc_manager_early_suspend(struct early_suspend *h);
static void tssc_manager_late_resume(struct early_suspend *h);
#endif
typedef int ts_handler_t(int, int, int);
ts_handler_t *ts_handler_pad; // blackstone handler
/// For calibration, display the reference points.
static ssize_t tssc_calibration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return calibration_show(buf);
}
/// For calibration, store the reference points.
static ssize_t tssc_calibration_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
calibration_store(buf);
return count;
}
static ssize_t tssc_calibration_points_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return calibration_points_show(buf);
}
static ssize_t tssc_calibration_points_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
calibration_points_store(buf);
return count;
}
static ssize_t tssc_calibration_screen_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return calibration_screen_show(buf);
}
static ssize_t tssc_calibration_screen_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
calibration_screen_store(buf);
return count;
}
/*
static ssize_t tssc_calibration_mfg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return calibration_mfg_show(buf);
}
static ssize_t tssc_calibration_mfg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
calibration_mfg_store(buf);
return count;
}
*/
/// sys/class/input/input1/calibration
static DEVICE_ATTR(calibration, 0666, tssc_calibration_show, tssc_calibration_store);
/// sys/class/input/input1/calibration_points
static DEVICE_ATTR(calibration_points, 0666, tssc_calibration_points_show, tssc_calibration_points_store);
/// sys/class/input/input1/calibration_screen
static DEVICE_ATTR(calibration_screen, 0666, tssc_calibration_screen_show, tssc_calibration_screen_store);
/// sys/class/input/input1/calibration_mfg
//static DEVICE_ATTR(calibration_mfg, 0666, tssc_calibration_mfg_show, tssc_calibration_mfg_store);
#define TSSC_MANAGER_NAME "tssc-manager"
static unsigned int ts_irq_down = -1;
struct tssc_manager_data {
struct input_dev *input_dev;
int use_irq;
struct hrtimer timer;
struct work_struct work;
struct work_struct polling_work;
int x;
int x16;
int y;
int y16;
int z1;
int z2;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
};
//----------------------------------------------
#define ENABLE_TSSC_AVERAGE
#define TOUCH_POLLING_NSEC 8400000//9000000//
#define TOUCH_QUEUE_NUMBER 2//3
static long touch_sample_count = 0;
static long touch_report_count = 0;
static int touch_queue_index = 0;
static bool first_touch = true;
int touch_queue_x[TOUCH_QUEUE_NUMBER];
int touch_queue_y[TOUCH_QUEUE_NUMBER];
int touch_queue_p[TOUCH_QUEUE_NUMBER];
int touch_deviation_x[TOUCH_QUEUE_NUMBER];
int touch_deviation_y[TOUCH_QUEUE_NUMBER];
int touch_average_x;
int touch_average_y;
int touch_average_p;
long total_x = 0;
long total_y = 0;
#define TOUCH_MOVE_VALUE 30
#define XY_temp 7//10//5//
int touch_noise_index = TOUCH_QUEUE_NUMBER;
enum {
DEBUG_TP_OFF = 0,
DEBUG_TP_ON = 1,
};
static int debug_tp;
module_param_named(debug, debug_tp, int, S_IRUGO | S_IWUSR | S_IWGRP);
//----------------------------------------------
static int touch_check_noise(int x, int x16, int y, int y16, int p)
{
struct timespec ts;
struct rtc_time tm;
int X_temp=abs(x-x16);
int Y_temp=abs(y-y16);
if (x16>0 && y16>0) {
if ((X_temp>=XY_temp)||(Y_temp>=XY_temp)) {
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "touch_check_noise(): This is a noise point.\t");
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "x=%d y=%d x16=%d y16=%d\t", x, y, x16, y16);
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
if(debug_tp & DEBUG_TP_ON) pr_info("(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
/*
* Add touch point data into the queue.
*/
static int touch_add_queue(int x, int x16, int y, int y16,int p)
{
if(touch_check_noise(x, x16, y, y16, p))
{
return 0;
}
touch_queue_x[touch_queue_index] = x;
touch_queue_y[touch_queue_index] = y;
touch_queue_p[touch_queue_index] = p;
touch_queue_index++;
if (touch_queue_index >= TOUCH_QUEUE_NUMBER)
touch_queue_index = 0;
//touch_sample_count++;
return ++touch_sample_count;
}
/*
* Report touch up input event.
*/
static void touch_input_up(struct input_dev *dev)
{
if (ts_handler_pad)
(*ts_handler_pad)(0, 0, 0);
if (touch_report_count > 0) {
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_abs(dev, ABS_TOOL_WIDTH, 0);
input_report_key(dev, BTN_TOUCH, 0);
input_report_key(dev, BTN_2, 0);
input_sync(dev);
touch_report_count = 0; /* reset report count */
}
}
static void touch_get_average(void)
{
int i;
long total_x = 0;
long total_y = 0;
long total_p = 0;
for (i = 0; i < TOUCH_QUEUE_NUMBER; i++) {
total_x += touch_queue_x[i];
total_y += touch_queue_y[i];
total_p += touch_queue_p[i];
}
touch_average_x = total_x / TOUCH_QUEUE_NUMBER;
touch_average_y = total_y / TOUCH_QUEUE_NUMBER;
touch_average_p = total_p / TOUCH_QUEUE_NUMBER;
}
/*
* Process the current touch point in the queue.
*/
static void touch_process_queue(struct input_dev *dev)
{
int x = 0;
int y = 0;
int bspad = 0;
struct timespec ts;
struct rtc_time tm;
if (touch_sample_count <= 1) {
if (touch_sample_count == 1) {
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "touch_process_queue(): This is point #%ld.\t", touch_sample_count);
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
if(debug_tp & DEBUG_TP_ON) pr_info("(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
return;
}
if (touch_sample_count >= TOUCH_QUEUE_NUMBER)
touch_get_average();
calibration_translate(touch_average_x, touch_average_y, &x, &y);
if (ts_handler_pad)
bspad = (*ts_handler_pad)(x, y, 1);
if (!bspad && x >= 0 && y >= 0) {
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "touch_process_queue(): x=%d\t", x);
input_report_abs(dev, ABS_X, x);
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "y=%d\t", y);
input_report_abs(dev, ABS_Y, y);
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "p=%d\n", touch_average_p);
input_report_abs(dev, ABS_PRESSURE, touch_average_p);
input_report_abs(dev, ABS_TOOL_WIDTH, 1);
input_report_key(dev, BTN_TOUCH, 1);
input_report_key(dev, BTN_2, 0);
input_sync(dev);
touch_report_count++;
}
}
/*
* Process the final touch points in the queue.
*/
static void touch_finish_queue(struct input_dev *dev)
{
touch_sample_count = 0;
touch_queue_index = 0;
touch_input_up(dev);
}
static void tssc_manager_work_func(struct work_struct *work)
{
struct tssc_manager_data *ts;
ts = container_of(work, struct tssc_manager_data, work);
hrtimer_start(&ts->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
}
static int tssc_manager_polling_func(struct tssc_manager_data *ts)
{
//struct tssc_manager_data *ts;
int pressure;
//ts = container_of(polling_work, struct tssc_manager_data, polling_work);
/***************************************************************************/
if(debug_tp & DEBUG_TP_ON) printk(KERN_DEBUG "tssc_manager_polling_func(): ts->z1=%d ts->z2=%d\n", ts->z1, ts->z2);
if (ts->z1) {
pressure = (ts->x * ((ts->z2 * 100 / ts->z1) - 100)) / 900;
//pressure = 350 - pressure;
pressure = 270 - pressure;
if (pressure <= 5)
pressure = 5; /* Report all touch points */
else if (pressure > 255)
pressure = 255;
} else {
pressure = 0;
//printk(KERN_DEBUG "tssc_manager_polling_func(): ts->z1==0\n");
}
if (ts->x!=0 && ts->y!=0 && ts->z1!=0 && ts->z2!=0 && pressure) {
if (touch_add_queue(ts->x, ts->x16, ts->y, ts->y16, pressure)) {
touch_process_queue(ts->input_dev);
}
} else {
//printk(KERN_DEBUG "tssc_manager_polling_func(): !(ts->x!=0 && ts->y!=0 && ts->z1!=0 && ts->z2!=0)\n");
//printk(KERN_DEBUG "tssc_manager_polling_func(): pressure==0)\n");
}
//if ((tssc_reg->tssc_status.penirq_status == 1) && (tssc_reg->tssc_status.busy == 0)) {
touch_finish_queue(ts->input_dev);
//hrtimer_cancel(&ts->timer);
enable_irq(ts_irq_down);
first_touch=true;
return 1;
//}
return 0;
}
static enum hrtimer_restart tssc_polling_timer_func (struct hrtimer *timer)
{
struct tssc_manager_data *ts = container_of(timer, struct tssc_manager_data, timer);
//schedule_work(&ts->polling_work);
if(!tssc_manager_polling_func(ts))
{
hrtimer_start(&ts->timer, ktime_set(0, TOUCH_POLLING_NSEC), HRTIMER_MODE_REL);
}
return HRTIMER_NORESTART;
}
static irqreturn_t tssc_manager_irq_down_handler(int irq, void *dev_id)
{
struct tssc_manager_data *ts = dev_id;
disable_irq(ts_irq_down);
schedule_work(&ts->work);
return IRQ_HANDLED;
}
static void tssc_power_on(void)
{
touch_sample_count = 0;
touch_report_count = 0;
touch_queue_index = 0;
}
static void tssc_power_off(void)
{
}
static int tssc_manager_input_init(void)
{
struct tssc_manager_data *ts;
int ret = 0;
int ret_down = 0;
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL) {
ret = -ENOMEM;
goto err_alloc_data_failed;
}
INIT_WORK(&ts->work, tssc_manager_work_func);
//INIT_WORK(&ts->polling_work, tssc_manager_polling_work_func);
tssc_power_on();
// Allocate input device.
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL) {
ret = -ENOMEM;
printk(KERN_ERR "tssc_manager_input_init: Failed to allocate input device\n");
goto err_input_dev_alloc_failed;
}
ts->input_dev->name = TSSC_MANAGER_NAME;
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(BTN_2, ts->input_dev->keybit);
set_bit(EV_ABS, ts->input_dev->evbit);
// Set input parameters boundary.
input_set_abs_params(ts->input_dev, ABS_X, TP_X_MIN, TP_X_MAX, 0, 0);
input_set_abs_params(ts->input_dev, ABS_Y, TP_Y_MIN, TP_Y_MAX, 0, 0);
input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
input_set_abs_params(ts->input_dev, ABS_HAT0X, TP_X_MIN, TP_X_MAX, 0, 0);
input_set_abs_params(ts->input_dev, ABS_HAT0Y, TP_Y_MIN, TP_Y_MAX, 0, 0);
ret = input_register_device(ts->input_dev);
if (ret) {
printk(KERN_ERR "tssc_manager_input_init: Unable to register %s input device\n", ts->input_dev->name);
goto err_input_register_device_failed;
}
// Create device files.
ret = device_create_file(&ts->input_dev->dev, &dev_attr_calibration);
if (ret) {
printk(KERN_ERR "tssc_manager_input_init: Error to create calibration attribute\n");
goto err_input_register_device_failed;
}
ret = device_create_file(&ts->input_dev->dev, &dev_attr_calibration_points);
if (ret) {
printk(KERN_ERR "tssc_manager_input_init: Error to create calibration_points attribute\n");
goto err_input_register_device_failed;
}
ret = device_create_file(&ts->input_dev->dev, &dev_attr_calibration_screen);
if (ret) {
printk(KERN_ERR "tssc_manager_input_init: Error to create calibration_screen attribute\n");
goto err_input_register_device_failed;
}
/* ret = device_create_file(&ts->input_dev->dev, &dev_attr_calibration_mfg);
if (ret) {
printk(KERN_ERR "tssc_manager_input_init: Error to create calibration_mfg attribute\n");
goto err_input_register_device_failed;
}*/
if (ts_irq_down) {
ret_down = request_irq(ts_irq_down, tssc_manager_irq_down_handler,
IRQF_TRIGGER_RISING, TSSC_MANAGER_NAME, ts);
}
if (ret_down == 0) {
ts->use_irq = 1;
} else {
ts->use_irq = 0;
}
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = tssc_polling_timer_func ;
//hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
#ifdef CONFIG_HAS_EARLYSUSPEND
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->early_suspend.suspend = tssc_manager_early_suspend;
ts->early_suspend.resume = tssc_manager_late_resume;
register_early_suspend(&ts->early_suspend);
#endif
printk(KERN_INFO "tssc_manager_input_init: Start touchscreen %s in %s mode\n",
ts->input_dev->name, ts->use_irq ? "interrupt" : "polling");
calibration_init();
return 0;
err_input_register_device_failed:
input_free_device(ts->input_dev);
err_input_dev_alloc_failed:
kfree(ts);
err_alloc_data_failed:
return ret;
}
static int tssc_manager_probe(struct platform_device *platform_dev)
{
printk(KERN_INFO "tssc_manager_probe\n");
return tssc_manager_input_init();
}
static int tssc_manager_remove(struct platform_device *platform_dev)
{
struct tssc_manager_data *ts = dev_get_drvdata(&platform_dev->dev);
printk(KERN_INFO "tssc_manager_remove\n");
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&ts->early_suspend);
#endif
if (ts->use_irq) {
free_irq(ts_irq_down, ts);
}
hrtimer_cancel(&ts->timer);
input_unregister_device(ts->input_dev);
// Remove device files.
device_remove_file(&ts->input_dev->dev, &dev_attr_calibration);
device_remove_file(&ts->input_dev->dev, &dev_attr_calibration_points);
device_remove_file(&ts->input_dev->dev, &dev_attr_calibration_screen);
//device_remove_file(&ts->input_dev->dev, &dev_attr_calibration_mfg);
kfree(ts);
return 0;
}
static int tssc_manager_suspend(struct tssc_manager_data *ts, pm_message_t mesg)
{
if (ts->use_irq) {
disable_irq(ts_irq_down);
}
//hrtimer_cancel(&ts->timer);
//cancel_work_sync(&ts->work);
//cancel_work_sync(&ts->polling_work);
tssc_power_off();
return 0;
}
static int tssc_manager_resume(struct tssc_manager_data *ts)
{
printk(KERN_DEBUG "tssc_manager_resume(): ts->use_irq=%d\n", ts->use_irq);
tssc_power_on();
if (ts->use_irq) {
enable_irq(ts_irq_down);
}
//hrtimer_start(&ts->timer, ktime_set(0, TOUCH_POLLING_NSEC), HRTIMER_MODE_REL);
return 0;
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void tssc_manager_early_suspend(struct early_suspend *h)
{
struct tssc_manager_data *ts;
ts = container_of(h, struct tssc_manager_data, early_suspend);
tssc_manager_suspend(ts, PMSG_SUSPEND);
}
static void tssc_manager_late_resume(struct early_suspend *h)
{
struct tssc_manager_data *ts;
ts = container_of(h, struct tssc_manager_data, early_suspend);
tssc_manager_resume(ts);
}
#endif
static struct platform_driver tssc_manager_driver = {
.probe = tssc_manager_probe,
.remove = tssc_manager_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = tssc_manager_suspend,
.resume = tssc_manager_resume,
#endif
.driver = {
.name = TSSC_MANAGER_NAME,
},
};
static int __devinit tssc_manager_init(void)
{
int result = 0;
debug_tp = 1;
printk(KERN_INFO "tssc_manager_init\n");
result = platform_driver_register(&tssc_manager_driver);
if (result < 0)
printk(KERN_ERR "tssc_manager_init: platform_driver_register failed\n");
return result;
}
static void __exit tssc_manager_exit(void)
{
printk(KERN_ERR "tssc_manager_exit\n");
}
module_init(tssc_manager_init);
module_exit(tssc_manager_exit);
MODULE_DESCRIPTION("TSSC Manager Driver");
MODULE_LICENSE("GPL");