基于设备树的TQ2440触摸屏驱动移植
平台
开发板:tq2440
内核:Linux-4.9
u-boot:u-boot-2015.04
概述
之前移植了LCD驱动,下面继续移植触摸屏驱动,然后将tslib也移植上去。
正文
一、移植触摸屏驱动
为了简单起见我们对TQ2440自带的触摸屏驱动进行改写,改成设备树的形式。
1、设备树
触摸屏使用了两个中断,如下:
这两个中断是子中断,隶属于主中断INT_ADC:
关于寄存器,参考芯片手册的第16章,知道了上面的信息,我们就可以得到如下的设备树节点(可以参考博文基于设备树的TQ2440的中断(1)):
tq2440ts@5800000 { compatible = "tq2440,ts"; reg = <0x58000000 0x100>; reg-names = "adc_ts_physical"; interrupts = <1 31 9 3>, <1 31 10 3>; interrupt-names = "int_ts", "int_adc_s"; clocks = <&clocks PCLK_ADC>; clock-names = "adc"; };
2、驱动
对应的触摸屏驱动是drivers/input/touchscreen/tq2440_ts.c
这部分我已经上传到github上面了,可以使用下面的命令下载:
git clone git@github.com:pengdonglin137/linux-4.9.git -b tq2440_dt
代码如下:
1 /************************************* 2 3 NAME:tq2440_ts.c 4 COPYRIGHT:www.embedsky.net 5 6 *************************************/ 7 #include <linux/errno.h> 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/slab.h> 11 #include <linux/input.h> 12 #include <linux/init.h> 13 #include <linux/serio.h> 14 #include <linux/delay.h> 15 #include <linux/platform_device.h> 16 #include <linux/clk.h> 17 #include <linux/of_device.h> 18 #include <linux/of.h> 19 #include <linux/of_gpio.h> 20 #include <asm/io.h> 21 #include <asm/irq.h> 22 23 #include <plat/regs-adc.h> 24 #include <mach/regs-gpio.h> 25 26 /* For ts.dev.id.version */ 27 #define S3C2410TSVERSION 0x0101 28 29 #define WAIT4INT(x) (((x)<<8) | \ 30 S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \ 31 S3C2410_ADCTSC_XY_PST(3)) 32 33 #define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \ 34 S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0)) 35 36 static char *tq2440ts_name = "TQ2440 TouchScreen"; 37 38 static struct input_dev *idev; 39 static long xp; 40 static long yp; 41 static int count; 42 43 static void __iomem *base_addr; 44 45 static void touch_timer_fire(unsigned long data) 46 { 47 u32 data0; 48 u32 data1; 49 int updown; 50 51 data0 = readl(base_addr+S3C2410_ADCDAT0); 52 data1 = readl(base_addr+S3C2410_ADCDAT1); 53 54 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN)); 55 56 if (updown) { 57 if (count != 0) { 58 long tmp; 59 60 tmp = xp; 61 xp = yp; 62 yp = tmp; 63 64 xp >>= 2; 65 yp >>= 2; 66 67 input_report_abs(idev, ABS_X, xp); 68 input_report_abs(idev, ABS_Y, yp); 69 70 input_report_key(idev, BTN_TOUCH, 1); 71 input_report_abs(idev, ABS_PRESSURE, 1); 72 input_sync(idev); 73 } 74 75 xp = 0; 76 yp = 0; 77 count = 0; 78 79 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); 80 writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON); 81 } else { 82 count = 0; 83 84 input_report_key(idev, BTN_TOUCH, 0); 85 input_report_abs(idev, ABS_PRESSURE, 0); 86 input_sync(idev); 87 88 writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); 89 } 90 } 91 92 static struct timer_list touch_timer = 93 TIMER_INITIALIZER(touch_timer_fire, 0, 0); 94 95 static irqreturn_t stylus_updown(int irq, void *dev_id) 96 { 97 u32 data0; 98 u32 data1; 99 int updown; 100 101 data0 = readl(base_addr+S3C2410_ADCDAT0); 102 data1 = readl(base_addr+S3C2410_ADCDAT1); 103 104 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN)); 105 106 if (updown) 107 touch_timer_fire(0); 108 109 return IRQ_HANDLED; 110 } 111 112 static irqreturn_t stylus_action(int irq, void *dev_id) 113 { 114 u32 data0; 115 u32 data1; 116 117 data0 = readl(base_addr+S3C2410_ADCDAT0); 118 data1 = readl(base_addr+S3C2410_ADCDAT1); 119 120 xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; 121 yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; 122 count++; 123 124 if (count < (1<<2)) { 125 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); 126 writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON); 127 } else { 128 mod_timer(&touch_timer, jiffies+1); 129 writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC); 130 } 131 132 return IRQ_HANDLED; 133 } 134 135 static int tq2440ts_probe(struct platform_device *pdev) 136 { 137 struct device *dev = &pdev->dev; 138 struct device_node *node = dev->of_node; 139 struct clk *adc_clock; 140 struct resource *tsmem, *irq; 141 struct input_dev *input_dev; 142 int ret; 143 144 if (!node) { 145 dev_dbg(dev, "of_node is NULL\n"); 146 return -EINVAL; 147 } 148 149 adc_clock = devm_clk_get(dev, "adc"); 150 dev_dbg(dev, "adc_clock: %p\n", adc_clock); 151 if (IS_ERR(adc_clock)) { 152 dev_err(dev, "cannot get clock\n"); 153 return -ENOENT; 154 } 155 clk_prepare(adc_clock); 156 clk_enable(adc_clock); 157 158 dev_dbg(dev, "get mem\n"); 159 tsmem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "adc_ts_physical"); 160 if (!tsmem) { 161 dev_dbg(dev, "get mem resource failed.\n"); 162 ret = -EINVAL; 163 goto err; 164 } 165 166 base_addr = devm_ioremap_resource(dev, tsmem); 167 if (IS_ERR(base_addr)) { 168 dev_dbg(dev, "ioremap failed.\n"); 169 ret = PTR_ERR(base_addr); 170 goto err; 171 } 172 173 writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON); 174 writel(0xffff, base_addr+S3C2410_ADCDLY); 175 writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); 176 177 input_dev = devm_input_allocate_device(dev); 178 if (!input_dev) { 179 dev_dbg(dev, "ioremap failed.\n"); 180 ret = -ENOMEM; 181 goto err; 182 } 183 184 idev = input_dev; 185 idev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); 186 187 188 __set_bit(EV_SYN, idev->evbit); 189 __set_bit(EV_KEY, idev->evbit); 190 __set_bit(EV_ABS, idev->evbit); 191 __set_bit(BTN_TOUCH, idev->keybit); 192 193 input_set_abs_params(idev, ABS_X, 0, 0x3FF, 0, 0); 194 input_set_abs_params(idev, ABS_Y, 0, 0x3FF, 0, 0); 195 input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0); 196 197 idev->name = tq2440ts_name; 198 idev->id.bustype = BUS_RS232; 199 idev->id.vendor = 0xDEAD; 200 idev->id.product = 0xBEEF; 201 idev->id.version = S3C2410TSVERSION; 202 203 irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "int_ts"); 204 if (!irq) { 205 dev_err(dev, "get irq resource int_ts failed.\n"); 206 ret = -EINVAL; 207 goto err; 208 } 209 ret = devm_request_irq(dev, irq->start, stylus_updown, IRQF_ONESHOT, "int_ts", NULL); 210 if (ret < 0){ 211 dev_err(dev, "request irq tsirq %d failed.\n", irq->start); 212 goto err; 213 } 214 215 irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "int_adc_s"); 216 if (!irq) { 217 dev_err(dev, "get irq resource int_adc_s failed.\n"); 218 ret = -EINVAL; 219 goto err; 220 } 221 ret = devm_request_irq(dev, irq->start, stylus_action, IRQF_ONESHOT, "int_adc_s", NULL); 222 if (ret < 0) { 223 dev_err(dev, "request irq adcirq %d failed.\n", irq->start); 224 goto err; 225 } 226 227 dev_info(dev, "%s successfully loaded\n", tq2440ts_name); 228 input_register_device(idev); 229 230 return 0; 231 err: 232 clk_disable(adc_clock); 233 return ret; 234 } 235 236 static const struct of_device_id tq2440ts_match[] = { 237 { .compatible = "tq2440,ts", .data = (void *)0 }, 238 {}, 239 }; 240 241 static struct platform_driver tq2440ts_driver = { 242 .probe = tq2440ts_probe, 243 .driver = { 244 .name = "tq2440ts", 245 .of_match_table = of_match_ptr(tq2440ts_match), 246 }, 247 }; 248 249 static int __init tq2440ts_init(void) 250 { 251 return platform_driver_register(&tq2440ts_driver); 252 } 253 254 static void __exit tq2440ts_exit(void) 255 { 256 platform_driver_unregister(&tq2440ts_driver); 257 } 258 259 module_init(tq2440ts_init); 260 module_exit(tq2440ts_exit); 261 262 MODULE_LICENSE("GPL");
3、测试
查看中断信息:
[root@tq2440 ]# cat /proc/interrupts CPU0 7: 973 s3c-eint 7 Edge eth0 8: 0 s3c 8 Edge s3c2410-rtc tick 13: 559459 s3c 13 Edge samsung_time_irq 16: 0 s3c 16 Edge 4d000000.fb 26: 0 s3c 26 Edge ohci_hcd:usb1 27: 4 s3c 27 Edge 54000000.i2c 30: 0 s3c 30 Edge s3c2410-rtc alarm 32: 218 s3c-level 32 Level 50000000.serial 33: 11203 s3c-level 33 Level 50000000.serial 41: 758 s3c-level 41 Edge int_ts 42: 16712 s3c-level 42 Edge int_adc_s 59: 0 s3c-level 59 Edge 53000000.watchdog Err: 0
使用hexdump /dev/input/event0:
[root@tq2440 ]# hexdump /dev/input/event0 0000000 cb74 386e bc0b 000b 0003 0000 0201 0000 0000010 cb74 386e bc0b 000b 0003 0001 01e4 0000 0000020 cb74 386e bc0b 000b 0001 014a 0001 0000 0000030 cb74 386e bc0b 000b 0003 0018 0001 0000 0000040 cb74 386e bc0b 000b 0000 0000 0000 0000 0000050 cb74 386e 0a4e 000c 0003 0000 01dc 0000 0000060 cb74 386e 0a4e 000c 0003 0001 01fa 0000 0000070 cb74 386e 0a4e 000c 0000 0000 0000 0000 0000080 cb74 386e 585a 000c 0001 014a 0000 0000 0000090 cb74 386e 585a 000c 0003 0018 0000 0000
二、移植tslib
参考:
登陆http://www.tslib.org/下载最新的版本:
https://github.com/kergoth/tslib/releases/download/1.10/tslib-1.10.tar.xz
编译安装:
#!/bin/bash ./autogen.sh mkdir install ./configure \ --prefix="`pwd`/install" \ --host=arm-linux \ ac_cv_func_malloc_0_nonnull=yes make make install
安装完成后,可以看到:
$ls install bin/ etc/ include/ lib/ share/
将这些文件拷贝到开发板上面,然后修改板子上面的/etc/profile文件,添加如下内容:
echo "Set Env for Tslib" export TSLIB_ROOT=/ export TSLIB_TSDEVICE=/dev/input/event0 export TSLIB_CONFFILE=$TSLIB_ROOT/etc/ts.conf export TSLIB_PLUGINDIR=$TSLIB_ROOT/lib/ts export TSLIB_CALIBFILE=/etc/pointercal export TSLIB_CONSOLEDEVICE=none export TSLIB_FBDEVICE=/dev/fb0 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TSLIB_ROOT/lib
修改完成后,重新启动开发板。
三、测试
运行ts_calibrate生成校准数据
[root@tq2440 ]# ts_calibrate xres = 480, yres = 272 Took 4 samples... Top left : X = 870 Y = 279 Took 8 samples... Top right : X = 162 Y = 277 Took 5 samples... Bot right : X = 156 Y = 757 Took 2 samples... Bot left : X = 688 Y = 660 Took 3 samples... Center : X = 517 Y = 522 582.617065 -0.602301 -0.108929 -72.806641 0.023253 0.396149 Calibration constants: 38182392 -39472 -7138 -4771456 1523 25962 65536
运行ts_test测试:
[root@tq2440 ]# ts_test 946785862.424089: 321 141 1 946785862.444036: 321 141 0 946785865.264038: 82 196 1 946785865.284058: 82 196 0 946785865.519036: 26 219 1 946785865.539107: 29 219 1 946785865.559054: 30 220 0 946785865.829038: 229 206 1
完。
本文来自博客园,作者:摩斯电码,未经同意,禁止转载