Android 2.1 farsight version for s5pc100

File Name: s3c-ts.c

1           简介

1.1          本例基于s5pc100开发板,触摸屏与CPU直接使用ADC连接。下次再找个I2C的驱动分析一下(比如:tsc2007.c)。

接口如下:

1.2          相关寄存器设置请看《S5PC100_UM_REV104.PDF》第1669页,REGISTER DESCRIPTION.

1.3          总体上触摸屏驱动程序的工作流程如下:

Step1:在Probe里注册一个Input设备,并注册TSADC Pen Down Interrupt和TSADC EOC (End of conversion) Interrupt。

Step2:触摸笔按下,响应中断,打开AD转换

Step3:响应AD转换结束中断,记录转换结果。

Step4:如果没超过最大连续AD转换次数,再次转换。

Step5:如果超过最大连续AD转换次数,则计算坐标上报事件。打开Timer。

Step6:Timer到期,检查按下状态。

Step7:如果按下打开AD转换,转换结束进入Step3

Step8:如果抬起状态,上报抬起事件。

1.4          源代码如下:.

2           初始化

static struct platform_driver s3c_ts_driver = {

.probe          = s3c_ts_probe,

.remove         = s3c_ts_remove,

.suspend        = s3c_ts_suspend,

.resume         = s3c_ts_resume,

.driver                  = {

.owner     = THIS_MODULE,

.name       = "s3c-ts",

},

};

static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2008 Samsung Electronics\n";//一个常量信息,放在init.data文件中,内核启动结束后释放。

static int __init s3c_ts_init(void)

{

printk(banner);

return platform_driver_register(&s3c_ts_driver);//注册一个名为s3c-ts的驱动程序

}

static void __exit s3c_ts_exit(void)

{

platform_driver_unregister(&s3c_ts_driver); //注销一个名为s3c-ts的驱动程序

}

module_init(s3c_ts_init);//入口宏

module_exit(s3c_ts_exit);//出口宏

MODULE_AUTHOR("Samsung AP");

MODULE_DESCRIPTION("S3C touchscreen driver");

MODULE_LICENSE("GPL");

3           探测函数s3c_ts_probe

3.1          res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

获得资源并保存在res变量中。获得了flags= IORESOURCE_MEM,索引为0的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

注意除了ts外很多资源定义在arch\arm\plat-s5pc1xx\devs.c中

/* Touch srcreen */

static struct resource s3c_ts_resource[] = {

[0] = {

.start = S3C_PA_ADC,

.end   = S3C_PA_ADC + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_PENDN,

.end   = IRQ_PENDN,

.flags = IORESOURCE_IRQ,

},

[2] = {

.start = IRQ_ADC,

.end   = IRQ_ADC,

.flags = IORESOURCE_IRQ,

}

};

3.2           ts_mem = request_mem_region(res->start, size, pdev->name);

申请内存区域。申请之后,才能开始ioremap()或者ioremap_nocache()来映射,映射后的变量才能使用。

3.3          ts_base = ioremap(res->start, size);

将io映射到变量ts_base,以后访问这个变量相当于访问ts对应的io地址了。

3.4          ts_clock = clk_get(&pdev->dev, "adc");

得到adc的时钟。adc时钟定义在arch/arm/plat-s5pc1xx/clock.c中

static struct clk init_clocks[] = {

}, {

.name                = "adc",

.id              = -1,

.parent              = &clk_p,

.enable              = s5pc1xx_clk_d15_ctrl,

.ctrlbit      = S5P_CLKGATE_D15_TSADC,

}, {

3.5          s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); //得到dev的platdata,如果为空则返回s3c_ts_default_cfg的数据。

3.6          寄存器初始化

if ((s3c_ts_cfg->presc&0xff) > 0)//cfg是否设置了proscale,

writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),\

ts_base+S3C_ADCCON); // 如果设置了,则设置到寄存器

else

writel(0, ts_base+S3C_ADCCON);

/* Initialise registers */

if ((s3c_ts_cfg->delay&0xffff) > 0)//是否设置了延时

writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);

if (s3c_ts_cfg->resol_bit==12) {//设置AD模式,10bit 或12bit

switch(s3c_ts_cfg->s3c_adc_con) {

case ADC_TYPE_2://6410 或s5pc100

writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);

break;

case ADC_TYPE_1://2410

writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);

break;

default:

dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");

break;

}

}

3.7          writel(WAIT4INT(0), ts_base+S3C_ADCTSC); // S3C_ADCTSC 寄存器清零,此时应该不会发生TouchScreen引起的AD转换。

3.8          input_dev = input_allocate_device();

使用input子系统的一般流程为:input_allocate_device()申请一个input_dev设备——>初始化该input_dev——>input_register_device()向子系统注册该设备——>中断时input_event()向子系统报告事件

3.9          初始化input_dev(即:ts->dev)

初始化的内容各个TS差不多。

注意:BTN_TOUCH 这个事件的相应是必须的。这是Andorid特有的要求。

ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

3.10       register_early_suspend(&ts->early_suspend);

注册一个early_suspend函数,这个函数会在power management的时候用到。

3.11       ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

获得资源并保存在ts_irq变量中。获得了flags= IORESOURCE_IRQ,索引为0的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

(详见本节开头)

3.12       ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);

注册一个中断响应函数。其中:ts_irq->start 等于IRQ_PENDN

此函数在触摸屏按下或者抬起的时候发生。

详见arch/arm/plat-s5pc1xx/ include/plat/irqs.h

#define IRQ_PENDN               S5PC1XX_IRQ_VIC2(24)

#define IRQ_TC                       IRQ_PENDN

3.13       ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);

获得资源并保存在ts_irq变量中。获得了flags= IORESOURCE_IRQ,索引为1的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

(详见本节开头)

3.14       ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);

注册一个中断响应函数。其中:ts_irq->start 等于IRQ_ADC

当触摸屏按下后发生滑动的时候发生。

3.15       ret = input_register_device(ts->dev);

注册一个input 设备

4           s3c_ts_remove

s3c_ts_probe的反过程。注销设备,释放资源。

5           static irqreturn_t stylus_updown(int irqno, void *param)

5.1          读取按下的状态

data0 = readl(ts_base+S3C_ADCDAT0);

data1 = readl(ts_base+S3C_ADCDAT1);

//判断data0,data1最高位是否仍为"0",为“0”表示触摸笔状态保持为down

updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));

#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG

printk(KERN_INFO "   %c\n", updown ? 'D' : 'U');

#endif

如果updown是1表示按下。

5.2          判断按下的状态

if (updown)

touch_timer_fire(0);

5.3          如果是6410或者s5pc100则,关闭中断(ADCCLRINT 和ADCCLRINTPNDNUP)

if(ts->s3c_adc_con==ADC_TYPE_2) {

__raw_writel(0x0, ts_base+S3C_ADCCLRWK);

__raw_writel(0x0, ts_base+S3C_ADCCLRINT);

}

6           static irqreturn_t stylus_action(int irqno, void *param)

unsigned long data0;

unsigned long data1;

data0 = readl(ts_base+S3C_ADCDAT0);//读取数据

data1 = readl(ts_base+S3C_ADCDAT1);

if(ts->resol_bit==12) {//根据类型设置不同的掩码,将数据保存在yp,xp变量中

#if defined(CONFIG_SMDK6410_REV10) || defined(CONFIG_TOUCHSCREEN_NEW)

ts->yp += S3C_ADCDAT0_XPDATA_MASK_12BIT - (data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT);

ts->xp += S3C_ADCDAT1_YPDATA_MASK_12BIT - (data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT);

#else

ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT;

ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;

#endif

}

else {//这里是10bit

#if defined(CONFIG_SMDK6410_REV10) || defined(CONFIG_TOUCHSCREEN_NEW)

ts->yp += S3C_ADCDAT0_XPDATA_MASK - (data0 & S3C_ADCDAT0_XPDATA_MASK);

ts->xp += S3C_ADCDAT1_YPDATA_MASK - (data1 & S3C_ADCDAT1_YPDATA_MASK);

#else

ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK;

ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK;

#endif

}

ts->count++;//累计AD转化次数

if (ts->count < (1<shift)) {//如果未到达次数限制,通过设置寄存器开始AD转化

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 {

#ifdef CONFIG_FB_S3C_INNOLUX430

ts->yp = S3C_ADCDAT1_YPDATA_MASK_12BIT * ts->count - ts->yp;

#endif

mod_timer(&touch_timer, jiffies+1);//设置一个Timer

writel(WAIT4INT(1), ts_base+S3C_ADCTSC);//TS 的AD转化进入等待状态

}

if(ts->s3c_adc_con==ADC_TYPE_2) {//6410,S5pc100清理中断

__raw_writel(0x0, ts_base+S3C_ADCCLRWK);

__raw_writel(0x0, ts_base+S3C_ADCCLRINT);

}

return IRQ_HANDLED;

7           static void touch_timer_fire(unsigned long data)

7.1          校准变量

a0=205,a1=-4999,a2=64552724,a3=6326,a4=-12,a5=-37526976,a6=65536;

这是本驱动所用的变量。

Android要求对获得的xy数据进行校准,代码如下:(相关解释见下面备注)

ts->xp=(long) ((a2+(a0*x)+(a1*y))/a6);

ts->yp=(long) ((a5+(a3*x)+(a4*y))/a6);

备注:

Android的校准主要有如下两种方案:

方案一:移植TSLIB,通过TSLIB产生 pointercal 校准参数文件。

方案二:在驱动程序中校准。

校准公式解释如下:

(XL, YL是显示屏坐标,XT, YT是触摸屏坐标,)

XL = XT*A+YT*B+C

YL = XT*D+YT*E+F

由于具体计算是希望是整数运算,所以实际中保存的ABCDEF为整数,而增加一个参数Div

XL = (XT*A+YT*B+C) / Div

YL = (YT*D+YT*E+F) / Div

TSLIB把以上的7个参数 ABCDEF Div 保存在 pointercal 文件中。

不校准的数据: A=1, B=0, C=0, D=0, E=1, F=0, Div=1

A    B    C    D    E    F    Div

-411    37818    -3636780    -51325    39    47065584    65536

7.2          判断是按下或者抬起

data0 = readl(ts_base+S3C_ADCDAT0);

data1 = readl(ts_base+S3C_ADCDAT1);

updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));

updown==1表示按下

7.3          如果累计AD转换次数大于零,计算坐标并上报事件

x=(int) ts->xp;

y=(int) ts->yp;

ts->xp=(long) ((a2+(a0*x)+(a1*y))/a6);//计算校准

ts->yp=(long) ((a5+(a3*x)+(a4*y))/a6);

if(ts->xp!=ts->xp_old || ts->yp!=ts->yp_old)//如果和上一次的坐标不同

{

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;

7.4          如果累计AD转换次数为零,则开启AD转换

//设置ADC开启转换

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);

7.5          如果不是按下,上报抬起事件

ts->count = 0;//计数器清空

#ifdef ANDROID_TS

input_report_abs(ts->dev, ABS_X, ts->xp_old);//上报位置

input_report_abs(ts->dev, ABS_Y, ts->yp_old);

input_report_abs(ts->dev, ABS_Z, 0);

#endif

input_report_key(ts->dev, BTN_TOUCH, 0);//上报抬起事件

#ifndef ANDROID_TS

input_report_abs(ts->dev, ABS_PRESSURE, 0);

#endif

input_sync(ts->dev);

writel(WAIT4INT(0), ts_base+S3C_ADCTSC);//清除ADC中断

备注:

1.        oversampling_shift 防止采样过度。

2.       关于中断类型的解释如下:

/*

* These flags used only by the kernel as part of the

* irq handling routines.

*

* IRQF_DISABLED - keep irqs disabled when calling the action handler

* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator

* IRQF_SHARED - allow sharing the irq among several devices

* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur

* IRQF_TIMER - Flag to mark this interrupt as timer interrupt

* IRQF_PERCPU - Interrupt is per cpu

* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing

* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is

*                registered first in an shared interrupt is considered for

*                performance reasons)

*/

#define IRQF_DISABLED            0x00000020

#define IRQF_SAMPLE_RANDOM    0x00000040

#define IRQF_SHARED               0x00000080

#define IRQF_PROBE_SHARED         0x00000100

#define IRQF_TIMER                  0x00000200

#define IRQF_PERCPU                0x00000400

#define IRQF_NOBALANCING  0x00000800

#define IRQF_IRQPOLL               0x00001000

3.       adc type defination

File name: arch\arm\plat-s3c\include\plat\ts.h

enum s3c_adc_type {

ADC_TYPE_0,

ADC_TYPE_1, //S3C2416, S3C2450

ADC_TYPE_2, //S3C64XX, S5PC1XX

};

4.       s3c_ts_mach_info type defination

//用于设置触摸屏信息

struct s3c_ts_mach_info {

int delay; //AD转换延时

int presc; //分频

int oversampling_shift; // 采样的数据

int resol_bit; //转换精度

enum s3c_adc_type s3c_adc_con;

};

struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {

.delay =            10000,

.presc =            49,

.oversampling_shift =     2,

.resol_bit =               10

};

//采集触摸屏信息

struct s3c_ts_info {

struct input_dev *dev;

long xp; //x方向位置

long yp; //y方向位置

int count; //累加xp或yp数据的次数

int shift;

char phys[32];

int resol_bit; //转换精度

enum s3c_adc_type s3c_adc_con;

};

android 触摸屏驱动分析,Android 触摸屏驱动代码分析(ADC 类型触摸屏 CPU:s3c相关推荐

  1. 【高通SDM660平台 Android 10.0】(12) --- Camera Chromatix 代码分析

    [高通SDM660平台 Android 10.0]Qcom Camera Daemon 代码分析 一.chromatix_imx258_lc898217xc 目录 1.1 例:imx258_lc898 ...

  2. OOM分析(1) Android 源,如何分析android的OOM,与java静态代码分析工具

    用MAT分析OOM 很多OOM看似发生在bitmap 分配得时候,但它一般不是rootcause.根本原因都在于本应该自动释放的资源,因为代码的错误,而导致某些对象一直被引用(Reference),例 ...

  3. 如何分析android的OOM,与java静态代码分析工具

    2019独角兽企业重金招聘Python工程师标准>>> 用MAT分析OOM 很多OOM看似发生在bitmap 分配得时候,但它一般不是rootcause.根本原因都在于本应该自动释放 ...

  4. Android 下简单的 MP3 播放(代码分析)

    本代码为教学材料,仅作学习使用,分析在注释里. 采用读取 SD 或者其他路径下的方式播放音乐文件. AndoirdManifest.xml 配置文件 <?xml version="1. ...

  5. u-boot分析之两阶段代码分析(三)

    目录 u-boot(三)启动文件 1,概述 2,uboot第一阶段代码分析: 汇编 2,uboot第二阶段代码分析 C:_start_armboot C:main_loop u-boot(三)启动文件 ...

  6. linux 锁屏 代码,Android锁屏与解屏相关代码分析

    我觉得对于普通人来说,最常见的是Android解屏的界面,然后应该是Home界面.今天就来分析一下解屏界面的相关代码(以索爱的解锁界面为例). 首先看解屏的界面,我把解屏的界面分为两个部分,最上部是s ...

  7. 【CSRMesh2.1蓝牙开发】-Android demo介绍【二】之代码分析

    怎么介绍呢,一直考虑,还是从业务流程开始,1.Meshservice初始化-->2.扫描设备--->3.连接设备-->4.扫描设备-->5.设备组网 这样分析多初学者,或者刚接 ...

  8. linux驱动开发: wm8960 codec代码分析

    关于alsa架构已经啃了好久好久,但是也卡了好久好久.难说皮毛到底有看懂多少,不管,我们先来啃wm8960 codec的驱动代码: 必要相关函数说明: 1.#define SOC_ENUM_SINGL ...

  9. Linux Alsa声卡驱动(2):代码分析

    一:初始化/注册声卡设备 (1)注册ALSA kernel\sound\core:sound.cint __init alsa_sound_init(void) {... ...if (registe ...

最新文章

  1. 源码 linux下编译_Linux云服务器软硬链接及源码编译安装python3.8的一些备注
  2. Linux内核中影响tcp三次握手的一些协议配置
  3. linux+dhcp服务的安装包,服务器_Linux教程:配置DHCP服务器方法介绍,  1.安装dhcp软件包 #rpm nd - phpStudy...
  4. JavaScript技巧
  5. git fock的子项目从上游仓库(源项目)同步更新
  6. python统计段落单词词频_使用Python统计文件中词频,并且生成词云
  7. 链路状态路由协议与OSPF
  8. Python案例:获取天气信息并绘制气温折线图
  9. 天津大学计算机专硕_「20考研」计算机考研专业课变动汇总
  10. PyTorch中“CUDA out of memory”的调试笔记
  11. squid android 工具包,xUtils Android工具包 v3.9.0
  12. PMF镜像中文件提取/diskgenius做的镜像文件
  13. 【深度学习--图像分类】imageAI自定义模型训练
  14. vue 实现 word、ppt、excel、图片、PDF、视频、音频等格式的文件下载
  15. 计算机主板上的纽扣电池型号是,主板上的纽扣电池是什么
  16. flac转mp3的方法
  17. Oracle 外部表
  18. Scala中Either两个子类Left/Right
  19. vue+mysql实现前端对接数据库
  20. 高频电容 低频电容

热门文章

  1. UCF Local Programming Contest Round 1A记录
  2. 【学习笔记】软件设计模式(一)基本概念与归纳总结
  3. Altium Designer,导入CAD文件
  4. 邓应海:下周黄金高开高走已注定?最新黄金走势分析
  5. 远程抄表系统如何实现自动抄表?
  6. 监控易:超大规模IT设备监控性能运维挑战如何破?
  7. html中div图片大小,如何用DIV+CSS控制图片大小范围?
  8. 2017云栖社区之星评选暨年度颁奖盛典_投票即可参与抽奖
  9. HPLC分类及其原理
  10. Dynamic Wallpaper for Mac(Mac动态壁纸桌面)