/*
* TI Touch Screen driver
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/sort.h>
#include <linux/pm_wakeirq.h>
#include <linux/mfd/ti_am335x_tscadc.h>
#define ADCFSM_STEPID 0x10
#define SEQ_SETTLE 275
#define MAX_12BIT ((1 << 12) - 1)
static const int config_pins[] = {
STEPCONFIG_XPP,
STEPCONFIG_XNN,
STEPCONFIG_YPP,
STEPCONFIG_YNN,
};
struct titsc {
struct input_dev *input;
struct ti_tscadc_dev *mfd_tscadc;
unsigned int irq;
unsigned int wires;
unsigned int x_plate_resistance;
bool pen_down;
int coordinate_readouts;
u32 config_inp[4];
u32 bit_xp, bit_xn, bit_yp, bit_yn;
u32 inp_xp, inp_xn, inp_yp, inp_yn;
u32 step_mask;
u32 charge_delay;
/*add by lc 2018-10-16
* 触摸点值偶然随机偏离。解决办法,每检测到3次上报1次,只上报第2次,1、3次的数据抛弃。
* cnt:检测次数计数器;
* x/y/z:记录第2次数据,上报的就是这组数据。
*/
u32 cnt;
u32 x;
u32 y;
u32 z;
};
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
{
return readl(ts->mfd_tscadc->tscadc_base + reg);
}
static void titsc_writel(struct titsc *tsc, unsigned int reg,
unsigned int val)
{
writel(val, tsc->mfd_tscadc->tscadc_base + reg);
}
static int titsc_config_wires(struct titsc *ts_dev)
{
u32 analog_line[4];
u32 wire_order[4];
int i, bit_cfg;
for (i = 0; i < 4; i++) {
/*
* Get the order in which TSC wires are attached
* w.r.t. each of the analog input lines on the EVM.
*/
analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4;
wire_order[i] = ts_dev->config_inp[i] & 0x0F;
if (WARN_ON(analog_line[i] > 7))
return -EINVAL;
if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins)))
return -EINVAL;
}
for (i = 0; i < 4; i++) {
int an_line;
int wi_order;
an_line = analog_line[i];
wi_order = wire_order[i];
bit_cfg = config_pins[wi_order];
if (bit_cfg == 0)
return -EINVAL;
switch (wi_order) {
case 0:
ts_dev->bit_xp = bit_cfg;
ts_dev->inp_xp = an_line;
break;
case 1:
ts_dev->bit_xn = bit_cfg;
ts_dev->inp_xn = an_line;
break;
case 2:
ts_dev->bit_yp = bit_cfg;
ts_dev->inp_yp = an_line;
break;
case 3:
ts_dev->bit_yn = bit_cfg;
ts_dev->inp_yn = an_line;
break;
}
}
return 0;
}
static void titsc_step_config(struct titsc *ts_dev)
{
unsigned int config;
int i;
int end_step, first_step, tsc_steps;
u32 stepenable;
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | ts_dev->bit_xp;
switch (ts_dev->wires) {
case 4:
config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
break;
case 5:
config |= ts_dev->bit_yn |
STEPCONFIG_INP_AN4 | ts_dev->bit_xn |
ts_dev->bit_yp;
break;
case 8:
config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn;
break;
}
tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
first_step = TOTAL_STEPS - tsc_steps;
/* Steps 16 to 16-coordinate_readouts is for X */
end_step = first_step + tsc_steps;
for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
config = 0;
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | ts_dev->bit_yn |
STEPCONFIG_INM_ADCREFM;
switch (ts_dev->wires) {
case 4:
config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
break;
case 5:
config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 |
STEPCONFIG_XNP | STEPCONFIG_YPN;
break;
case 8:
config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp);
break;
}
/* 1 ... coordinate_readouts is for Y */
end_step = first_step + ts_dev->coordinate_readouts;
for (i = first_step; i < end_step; i++) {
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
}
/* Make CHARGECONFIG same as IDLECONFIG */
config = titsc_readl(ts_dev, REG_IDLECONFIG);
titsc_writel(ts_dev, REG_CHARGECONFIG, config);
titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | ts_dev->bit_yp |
ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
STEPCONFIG_INP(ts_dev->inp_xp);
titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
titsc_writel(ts_dev, REG_STEPDELAY(end_step),
STEPCONFIG_OPENDLY);
end_step++;
config |= STEPCONFIG_INP(ts_dev->inp_yn);
titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config);
titsc_writel(ts_dev, REG_STEPDELAY(end_step),
STEPCONFIG_OPENDLY);
/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
stepenable = 1;
for (i = 0; i < tsc_steps; i++)
stepenable |= 1 << (first_step + i + 1);
ts_dev->step_mask = stepenable;
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
}
static int titsc_cmp_coord(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
static void titsc_clean_fifo(struct titsc *ts_dev)
{
int i;
unsigned int creads = (ts_dev->coordinate_readouts<<1) + 2;
for (i = 0; i < creads; i++) {
titsc_readl(ts_dev, REG_FIFO0);
}
}
static void titsc_read_coordinates(struct titsc *ts_dev,
u32 *x, u32 *y, u32 *z1, u32 *z2)
{
unsigned int yvals[7], xvals[7];
unsigned int i, xsum = 0, ysum = 0;
unsigned int creads = ts_dev->coordinate_readouts;
for (i = 0; i < creads; i++) {
yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
yvals[i] &= 0xfff;
}
*z1 = titsc_readl(ts_dev, REG_FIFO0);
*z1 &= 0xfff;
*z2 = titsc_readl(ts_dev, REG_FIFO0);
*z2 &= 0xfff;
for (i = 0; i < creads; i++) {
xvals[i] = titsc_readl(ts_dev, REG_FIFO0);
xvals[i] &= 0xfff;
}
/*
* If co-ordinates readouts is less than 4 then
* report the average. In case of 4 or more
* readouts, sort the co-ordinate samples, drop
* min and max values and report the average of
* remaining values.
*/
if (creads <= 3) {
for (i = 0; i < creads; i++) {
ysum += yvals[i];
xsum += xvals[i];
}
ysum /= creads;
xsum /= creads;
} else {
sort(yvals, creads, sizeof(unsigned int),
titsc_cmp_coord, NULL);
sort(xvals, creads, sizeof(unsigned int),
titsc_cmp_coord, NULL);
for (i = 1; i < creads - 1; i++) {
ysum += yvals[i];
xsum += xvals[i];
}
ysum /= creads - 2;
xsum /= creads - 2;
}
*y = ysum;
*x = xsum;
}
static irqreturn_t titsc_irq(int irq, void *dev)
{
struct titsc *ts_dev = dev;
struct input_dev *input_dev = ts_dev->input;
unsigned int fsm, status, irqclr = 0;
unsigned int x = 0, y = 0;
unsigned int z1, z2, z;
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
#if 1
/*相当于延时*/
{
int i;
for(i=0; i<20; i++) titsc_readl(ts_dev, REG_ADCFSM);
}
#endif
if (status & IRQENB_HW_PEN) {
ts_dev->pen_down = true;
irqclr |= IRQENB_HW_PEN;
pm_stay_awake(ts_dev->mfd_tscadc->dev);
}
if (status & IRQENB_PENUP) {
fsm = titsc_readl(ts_dev, REG_ADCFSM);
if (fsm == ADCFSM_STEPID || status==0x600) {
ts_dev->pen_down = false;
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_sync(input_dev);
pm_relax(ts_dev->mfd_tscadc->dev);
} else {
ts_dev->pen_down = true;

LUCAN1213
- 粉丝: 0
最新资源
- 数据库原理及应用模拟试题7.doc
- 基于社会学习理论的网络共读机制研究.docx
- 数据中心网络的链路故障检测分析.docx
- 大数据下鱼饲料中淀粉含量的研究.docx
- 置入式广告在网络游戏中的应用分析.docx
- 网络销售合作协议.doc
- 2017年下半年-网络工程施工师-答案详解.docx
- 面向基于功能性的机器人控制研讨会论文集
- SQL数据库课程教学讲义第2章(1)DataBase.ppt
- 网络经济下互联网行业的垄断与规制研究.docx
- 自动化-检测实验指导.doc
- PLC彩灯控制-课程设计[1].doc
- 电气自动化模块生产实习教学大纲(电子电工专业部实习项目).doc
- 利用多媒体是计算机发展的必然趋势.docx
- 面向云计算的下一代数据中心安全方案.pptx
- 人工智能的数学解题学习工具-微软数学.docx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈


