内核版本:2.6.36
源码路径:drivers/input/touchscreen/s3c2410_ts.c
在Linux-2.6.36中,S3C2410对应的触摸屏驱动程序是drivers/input/touchscreen/s3c2410_ts.c,本文对这个文件进行分析,详细介绍相关知识点。S3C2410的触摸屏驱动和ADC驱动紧密联系在一起,在读本文之前,请大家先看我的上一篇博客《S3C2410驱动分析之ADC通用驱动》。
首先我们看模块初始化函数:
- 423static struct platform_driver s3c_ts_driver = {
- 424 .driver = {
- 425 .name = "samsung-ts",
- 426 .owner = THIS_MODULE,
- 427#ifdef CONFIG_PM
- 428 .pm = &s3c_ts_pmops,
- 429#endif
- 430 },
- 431 .id_table = s3cts_driver_ids,
- 432 .probe = s3c2410ts_probe,
- 433 .remove = __devexit_p(s3c2410ts_remove),
- 434};
423static struct platform_driver s3c_ts_driver = { 424 .driver = { 425 .name = "samsung-ts", 426 .owner = THIS_MODULE, 427#ifdef CONFIG_PM 428 .pm = &s3c_ts_pmops, 429#endif 430 }, 431 .id_table = s3cts_driver_ids, 432 .probe = s3c2410ts_probe, 433 .remove = __devexit_p(s3c2410ts_remove), 434};
这里需要关注的是platform_driver结构的id_table和probe两个成员。s3cts_driver_ids定义如下:
- 415static struct platform_device_id s3cts_driver_ids[] = {
- 416 { "s3c2410-ts", 0 },
- 417 { "s3c2440-ts", 0 },
- 418 { "s3c64xx-ts", FEAT_PEN_IRQ },
- 419 { }
- 420};
415static struct platform_device_id s3cts_driver_ids[] = { 416 { "s3c2410-ts", 0 }, 417 { "s3c2440-ts", 0 }, 418 { "s3c64xx-ts", FEAT_PEN_IRQ }, 419 { } 420};
421MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
probe成员函数s3c2410ts_probe当模块被加载时就会执行,其代码如下:
- 234/**
- 235 * s3c2410ts_probe - device core probe entry point
- 236 * @pdev: The device we are being bound to.
- 237 *
- 238 * Initialise, find and allocate any resources we need to run and then
- 239 * register with the ADC and input systems.
- 240 */
- 241static int __devinit s3c2410ts_probe(struct platform_device *pdev)
- 242{
- 243 struct s3c2410_ts_mach_info *info;
- 244 struct device *dev = &pdev->dev;
- 245 struct input_dev *input_dev;
- 246 struct resource *res;
- 247 int ret = -EINVAL;
- 248
- 249 /* Initialise input stuff */
- 250 memset(&ts, 0, sizeof(struct s3c2410ts));
- 251
- 252 ts.dev = dev;
- 253
- 254 info = pdev->dev.platform_data;
- 255 if (!info) {
- 256 dev_err(dev, "no platform data, cannot attach\n");
- 257 return -EINVAL;
- 258 }
- 259
- 260 dev_dbg(dev, "initialising touchscreen\n");
- 261
- 262 ts.clock = clk_get(dev, "adc");
- 263 if (IS_ERR(ts.clock)) {
- 264 dev_err(dev, "cannot get adc clock source\n");
- 265 return -ENOENT;
- 266 }
- 267
- 268 clk_enable(ts.clock);
- 269 dev_dbg(dev, "got and enabled clocks\n");
- 270
- 271 ts.irq_tc = ret = platform_get_irq(pdev, 0);
- 272 if (ret < 0) {
- 273 dev_err(dev, "no resource for interrupt\n");
- 274 goto err_clk;
- 275 }
- 276
- 277 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- 278 if (!res) {
- 279 dev_err(dev, "no resource for registers\n");
- 280 ret = -ENOENT;
- 281 goto err_clk;
- 282 }
- 283
- 284 ts.io = ioremap(res->start, resource_size(res));
- 285 if (ts.io == NULL) {
- 286 dev_err(dev, "cannot map registers\n");
- 287 ret = -ENOMEM;
- 288 goto err_clk;
- 289 }
- 290
- 291 /* inititalise the gpio */
- 292 if (info->cfg_gpio)
- 293 info->cfg_gpio(to_platform_device(ts.dev));
- 294
- 295 ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
- 296 s3c24xx_ts_conversion, 1);
- 297 if (IS_ERR(ts.client)) {
- 298 dev_err(dev, "failed to register adc client\n");
- 299 ret = PTR_ERR(ts.client);
- 300 goto err_iomap;
- 301 }
- 302
- 303 /* Initialise registers */
- 304 if ((info->delay & 0xffff) > 0)
- 305 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
- 306
- 307 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
- 308
- 309 input_dev = input_allocate_device();
- 310 if (!input_dev) {
- 311 dev_err(dev, "Unable to allocate the input device !!\n");
- 312 ret = -ENOMEM;
- 313 goto err_iomap;
- 314 }
- 315
- 316 ts.input = input_dev;
- 317 ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- 318 ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- 319 input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
- 320 input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
- 321
- 322 ts.input->name = "S3C24XX TouchScreen";
- 323 ts.input->id.bustype = BUS_HOST;
- 324 ts.input->id.vendor = 0xDEAD;
- 325 ts.input->id.product = 0xBEEF;
- 326 ts.input->id.version = 0x0102;
- 327
- 328 ts.shift = info->oversampling_shift;
- 329 ts.features = platform_get_device_id(pdev)->driver_data;
- 330
- 331 ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,
- 332 "s3c2410_ts_pen", ts.input);
- 333 if (ret) {
- 334 dev_err(dev, "cannot get TC interrupt\n");
- 335 goto err_inputdev;
- 336 }
- 337
- 338 dev_info(dev, "driver attached, registering input device\n");
- 339
- 340 /* All went ok, so register to the input system */
- 341 ret = input_register_device(ts.input);
- 342 if (ret < 0) {
- 343 dev_err(dev, "failed to register input device\n");
- 344 ret = -EIO;
- 345 goto err_tcirq;
- 346 }
- 347
- 348 return 0;
- 349
- 350 err_tcirq:
- 351 free_irq(ts.irq_tc, ts.input);
- 352 err_inputdev:
- 353 input_unregister_device(ts.input);
- 354 err_iomap:
- 355 iounmap(ts.io);
- 356 err_clk:
- 357 del_timer_sync(&touch_timer);
- 358 clk_put(ts.clock);
- 359 return ret;
- 360}
234/** 235 * s3c2410ts_probe - device core probe entry point 236 * @pdev: The device we are being bound to. 237 * 238 * Initialise, find and allocate any resources we need to run and then 239 * register with the ADC and input systems. 240 */ 241static int __devinit s3c2410ts_probe(struct platform_device *pdev) 242{ 243 struct s3c2410_ts_mach_info *info; 244 struct device *dev = &pdev->dev; 245 struct input_dev *input_dev; 246 struct resource *res; 247 int ret = -EINVAL; 248 249 /* Initialise input stuff */ 250 memset(&ts, 0, sizeof(struct s3c2410ts)); 251 252 ts.dev = dev; 253 254 info = pdev->dev.platform_data; 255 if (!info) { 256 dev_err(dev, "no platform data, cannot attach\n"); 257 return -EINVAL; 258 } 259 260 dev_dbg(dev, "initialising touchscreen\n"); 261 262 ts.clock = clk_get(dev, "adc"); 263 if (IS_ERR(ts.clock)) { 264 dev_err(dev, "cannot get adc clock source\n"); 265 return -ENOENT; 266 } 267 268 clk_enable(ts.clock); 269 dev_dbg(dev, "got and enabled clocks\n"); 270 271 ts.irq_tc = ret = platform_get_irq(pdev, 0); 272 if (ret < 0) { 273 dev_err(dev, "no resource for interrupt\n"); 274 goto err_clk; 275 } 276 277 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 278 if (!res) { 279 dev_err(dev, "no resource for registers\n"); 280 ret = -ENOENT; 281 goto err_clk; 282 } 283 284 ts.io = ioremap(res->start, resource_size(res)); 285 if (ts.io == NULL) { 286 dev_err(dev, "cannot map registers\n"); 287 ret = -ENOMEM; 288 goto err_clk; 289 } 290 291 /* inititalise the gpio */ 292 if (info->cfg_gpio) 293 info->cfg_gpio(to_platform_device(ts.dev)); 294 295 ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, 296 s3c24xx_ts_conversion, 1); 297 if (IS_ERR(ts.client)) { 298 dev_err(dev, "failed to register adc client\n"); 299 ret = PTR_ERR(ts.client); 300 goto err_iomap; 301 } 302 303 /* Initialise registers */ 304 if ((info->delay & 0xffff) > 0) 305 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); 306 307 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); 308 309 input_dev = input_allocate_device(); 310 if (!input_dev) { 311 dev_err(dev, "Unable to allocate the input device !!\n"); 312 ret = -ENOMEM; 313 goto err_iomap; 314 } 315 316 ts.input = input_dev; 317 ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 318 ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 319 input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); 320 input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); 321 322 ts.input->name = "S3C24XX TouchScreen"; 323 ts.input->id.bustype = BUS_HOST; 324 ts.input->id.vendor = 0xDEAD; 325 ts.input->id.product = 0xBEEF; 326 ts.input->id.version = 0x0102; 327 328 ts.shift = info->oversampling_shift; 329 ts.features = platform_get_device_id(pdev)->driver_data; 330 331 ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED, 332 "s3c2410_ts_pen", ts.input); 333 if (ret) { 334 dev_err(dev, "cannot get TC interrupt\n"); 335 goto err_inputdev; 336 } 337 338 dev_info(dev, "driver attached, registering input device\n"); 339 340 /* All went ok, so register to the input system */ 341 ret = input_register_device(ts.input); 342 if (ret < 0) { 343 dev_err(dev, "failed to register input device\n"); 344 ret = -EIO; 345 goto err_tcirq; 346 } 347 348 return 0; 349 350 err_tcirq: 351 free_irq(ts.irq_tc, ts.input); 352 err_inputdev: 353 input_unregister_device(ts.input); 354 err_iomap: 355 iounmap(ts.io); 356 err_clk: 357 del_timer_sync(&touch_timer); 358 clk_put(ts.clock); 359 return ret; 360}
243行,定义s3c2410_ts_mach_info结构体变量info,该结构体定义在arch/arm/plat-samsung/include/plat/ts.h文件中:
- 13struct s3c2410_ts_mach_info {
- 14 int delay;
- 15 int presc;
- 16 int oversampling_shift;
- 17 void (*cfg_gpio)(struct platform_device *dev);
- 18};
13struct s3c2410_ts_mach_info { 14 int delay; 15 int presc; 16 int oversampling_shift; 17 void (*cfg_gpio)(struct platform_device *dev); 18};
delay保存ADC的延迟时间。
presc保存ADC的预分频系数。
oversampling_shift保存采样次数log2值。
cfg_gpio函数用于设置gpio。
250行,将ts清0,ts是s3c2410ts结构体类型变量,其定义如下:
- 62/**
- 63 * struct s3c2410ts - driver touchscreen state.
- 64 * @client: The ADC client we registered with the core driver.
- 65 * @dev: The device we are bound to.
- 66 * @input: The input device we registered with the input subsystem.
- 67 * @clock: The clock for the adc.
- 68 * @io: Pointer to the IO base.
- 69 * @xp: The accumulated X position data.
- 70 * @yp: The accumulated Y position data.
- 71 * @irq_tc: The interrupt number for pen up/down interrupt
- 72 * @count: The number of samples collected.
- 73 * @shift: The log2 of the maximum count to read in one go.
- 74 * @features: The features supported by the TSADC MOdule.
- 75 */
- 76struct s3c2410ts {
- 77 struct s3c_adc_client *client;
- 78 struct device *dev;
- 79 struct input_dev *input;
- 80 struct clk *clock;
- 81 void __iomem *io;
- 82 unsigned long xp;
- 83 unsigned long yp;
- 84 int irq_tc;
- 85 int count;
- 86 int shift;
- 87 int features;
- 88};
- 89
- 90static struct s3c2410ts ts;
62/** 63 * struct s3c2410ts - driver touchscreen state. 64 * @client: The ADC client we registered with the core driver. 65 * @dev: The device we are bound to. 66 * @input: The input device we registered with the input subsystem. 67 * @clock: The clock for the adc. 68 * @io: Pointer to the IO base. 69 * @xp: The accumulated X position data. 70 * @yp: The accumulated Y position data. 71 * @irq_tc: The interrupt number for pen up/down interrupt 72 * @count: The number of samples collected. 73 * @shift: The log2 of the maximum count to read in one go. 74 * @features: The features supported by the TSADC MOdule. 75 */ 76struct s3c2410ts { 77 struct s3c_adc_client *client; 78 struct device *dev; 79 struct input_dev *input; 80 struct clk *clock; 81 void __iomem *io; 82 unsigned long xp; 83 unsigned long yp; 84 int irq_tc; 85 int count; 86 int shift; 87 int features; 88}; 89 90static struct s3c2410ts ts;
s3c2410ts结构体的各个成员的含义在注册中已经说的很清楚,这里不再解释。
回到s3c2410ts_probe函数,接着往下看
262 - 268行,获取并使能触摸屏adc时钟,因为AD转换频率与时钟有关。
271行,取得中断号保存在ts.irq_tc中。
277行,取得触摸屏设备的I/O内存保存在res中。
284行,调用ioremap得到触摸屏的I/O内存对应的虚拟地址,保存在ts.io中。
292 - 293行,如果设备定义了cfg_gpio函数,则调用cfg_gpio函数初始化GPIO。
295 - 296行,调用s3c_adc_register函数注册ADC客户(client)。
s3c_adc_register函数定义在arch/arm/plat-samsung/adc.c文件中:
- 207struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
- 208 void (*select)(struct s3c_adc_client *client,
- 209 unsigned int selected),
- 210 void (*conv)(struct s3c_adc_client *client,
- 211 unsigned d0, unsigned d1,
- 212 unsigned *samples_left),
- 213 unsigned int is_ts)
- 214{
- 215 struct s3c_adc_client *client;
- 216
- 217 WARN_ON(!pdev);
- 218
- 219 if (!select)
- 220 select = s3c_adc_default_select;
- 221
- 222 if (!pdev)
- 223 return ERR_PTR(-EINVAL);
- 224
- 225 client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
- 226 if (!client) {
- 227 dev_err(&pdev->dev, "no memory for adc client\n");
- 228 return ERR_PTR(-ENOMEM);
- 229 }
- 230
- 231 client->pdev = pdev;
- 232 client->is_ts = is_ts;
- 233 client->select_cb = select;
- 234 client->convert_cb = conv;
- 235
- 236 return client;
- 237}
207struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, 208 void (*select)(struct s3c_adc_client *client, 209 unsigned int selected), 210 void (*conv)(struct s3c_adc_client *client, 211 unsigned d0, unsigned d1, 212 unsigned *samples_left), 213 unsigned int is_ts) 214{ 215 struct s3c_adc_client *client; 216 217 WARN_ON(!pdev); 218 219 if (!select) 220 select = s3c_adc_default_select; 221 222 if (!pdev) 223 return ERR_PTR(-EINVAL); 224 225 client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL); 226 if (!client) { 227 dev_err(&pdev->dev, "no memory for adc client\n"); 228 return ERR_PTR(-ENOMEM); 229 } 230 231 client->pdev = pdev; 232 client->is_ts = is_ts; 233 client->select_cb = select; 234 client->convert_cb = conv; 235 236 return client; 237}
s3c_adc_client结构体定义在arch/arm/plat-samsung/adc.c中:
- 46struct s3c_adc_client {
- 47 struct platform_device *pdev;
- 48 struct list_head pend;
- 49 wait_queue_head_t *wait;
- 50
- 51 unsigned int nr_samples;
- 52 int result;
- 53 unsigned char is_ts;
- 54 unsigned char channel;
- 55
- 56 void (*select_cb)(struct s3c_adc_client *c, unsigned selected);
- 57 void (*convert_cb)(struct s3c_adc_client *c,
- 58 unsigned val1, unsigned val2,
- 59 unsigned *samples_left);
- 60};
46struct s3c_adc_client { 47 struct platform_device *pdev; 48 struct list_head pend; 49 wait_queue_head_t *wait; 50 51 unsigned int nr_samples; 52 int result; 53 unsigned char is_ts; 54 unsigned char channel; 55 56 void (*select_cb)(struct s3c_adc_client *c, unsigned selected); 57 void (*convert_cb)(struct s3c_adc_client *c, 58 unsigned val1, unsigned val2, 59 unsigned *samples_left); 60};
再回到s3c2410ts_probe函数:
304 - 305行,设定ADC延迟时间,
- 304 if ((info->delay & 0xffff) > 0)
- 305 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
304 if ((info->delay & 0xffff) > 0) 305 writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
info->delay & 0xffff表示取延迟时间的低32位值。
在arch/arm/plat-samsung/include/plat/regs-adc.h文件中,S3C2410_ADCDLY定义如下:
- #define S3C2410_ADCREG(x) (x)
- #define S3C2410_ADCDLY S3C2410_ADCREG(0x08)
#define S3C2410_ADCREG(x) (x) #define S3C2410_ADCDLY S3C2410_ADCREG(0x08)
所以S3C2410_ADCDLY的值就是0x08。
查S3C2410 Datasheet,可以发现ADC相关寄存器的起始地址是0x58000000,而ADC start delay(ADCDLY)寄存器的地址是0x58000008。ADC相关寄存器的虚拟地址保存在ts.io中,所以ADCDLY寄存器的虚拟地址是ts.io + 0x08。
再回到s3c2410ts_probe函数,307行,设置ADC touch screen control(ADCTSC)寄存器,进入等待中断模式。
- 307 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
307 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
S3C2410_ADCTSC宏定义如下:
- #define S3C2410_ADCREG(x) (x)
- #define S3C2410_ADCTSC S3C2410_ADCREG(0x04)
#define S3C2410_ADCREG(x) (x) #define S3C2410_ADCTSC S3C2410_ADCREG(0x04)
INT_DOWN宏定义如下:
- 44#define INT_DOWN (0)
44#define INT_DOWN (0)
WAIT4INT宏定义如下:
- 47#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
- 48 S3C2410_ADCTSC_YP_SEN | \
- 49 S3C2410_ADCTSC_XP_SEN | \
- 50 S3C2410_ADCTSC_XY_PST(3))
47#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \ 48 S3C2410_ADCTSC_YP_SEN | \ 49 S3C2410_ADCTSC_XP_SEN | \ 50 S3C2410_ADCTSC_XY_PST(3))
在arch/arm/plat-samsung/include/plat/regs-adc.h文件中,有如下宏定义:
- 41/* ADCTSC Register Bits */
- 42#define S3C2410_ADCTSC_YM_SEN (1<<7)
- 43#define S3C2410_ADCTSC_YP_SEN (1<<6)
- 44#define S3C2410_ADCTSC_XM_SEN (1<<5)
- 45#define S3C2410_ADCTSC_XP_SEN (1<<4)
- 46#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
- 47#define S3C2410_ADCTSC_AUTO_PST (1<<2)
- 48#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
41/* ADCTSC Register Bits */ 42#define S3C2410_ADCTSC_YM_SEN (1<<7) 43#define S3C2410_ADCTSC_YP_SEN (1<<6) 44#define S3C2410_ADCTSC_XM_SEN (1<<5) 45#define S3C2410_ADCTSC_XP_SEN (1<<4) 46#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3) 47#define S3C2410_ADCTSC_AUTO_PST (1<<2) 48#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
所以用WAIT4INT宏设置ADCTSC寄存器就是把ADCTSC寄存器的第0,1,4,6,7位设置为1。查看S3C2410数据手册可知,把ADCTSC寄存器设置为0xD3表示进入“Waiting for interrupt Mode”。
再回到s3c2410ts_probe函数,309 - 326行分配input_dev并进行初始化:
- 309 input_dev = input_allocate_device();
- 310 if (!input_dev) {
- 311 dev_err(dev, "Unable to allocate the input device !!\n");
- 312 ret = -ENOMEM;
- 313 goto err_iomap;
- 314 }
- 315
- 316 ts.input = input_dev;
- 317 ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- 318 ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- 319 input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
- 320 input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
- 321
- 322 ts.input->name = "S3C24XX TouchScreen";
- 323 ts.input->id.bustype = BUS_HOST;
- 324 ts.input->id.vendor = 0xDEAD;
- 325 ts.input->id.product = 0xBEEF;
- 326 ts.input->id.version = 0x0102;
309 input_dev = input_allocate_device(); 310 if (!input_dev) { 311 dev_err(dev, "Unable to allocate the input device !!\n"); 312 ret = -ENOMEM; 313 goto err_iomap; 314 } 315 316 ts.input = input_dev; 317 ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 318 ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 319 input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); 320 input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); 321 322 ts.input->name = "S3C24XX TouchScreen"; 323 ts.input->id.bustype = BUS_HOST; 324 ts.input->id.vendor = 0xDEAD; 325 ts.input->id.product = 0xBEEF; 326 ts.input->id.version = 0x0102;
309行,调用input_allocate_device分配input_dev设备,并保存在input_dev变量中。
317行,设置input_dev的evbit成员,evbit代表一个input_dev设备支持的事件类型,这里支持按键事件和绝对坐标事件。
318行,设置input_dev的keybit成员,keybit代表按键的类型,这里设置为触摸屏。
319行,设置X坐标,最小值为0,最大值为0x3FF(1023)。
320行,设置Y坐标,最小值为0,最大值为1023。
322 - 326行,设置触摸屏信息,这些信息以后可以在/proc/bus/input/devices中查到。注意323行,设置总线类型为BUS_HOST,根据触摸屏不同,总线可以是I2C,SPI,RS232等各种总线类型。
328行,设置采样次数log2值。
331 - 332行,申请中断,设置中断处理函数为stylus_irq。
341行,注册input_dev设备。
至此,模块初始化及probe过程就完成了。
当触摸屏被按下时,将触发触摸屏中断IRQ_TC,下面看中断处理函数stylus_irq:
- 151/**
- 152 * stylus_irq - touchscreen stylus event interrupt
- 153 * @irq: The interrupt number
- 154 * @dev_id: The device ID.
- 155 *
- 156 * Called when the IRQ_TC is fired for a pen up or down event.
- 157 */
- 158static irqreturn_t stylus_irq(int irq, void *dev_id)
- 159{
- 160 unsigned long data0;
- 161 unsigned long data1;
- 162 bool down;
- 163
- 164 data0 = readl(ts.io + S3C2410_ADCDAT0);
- 165 data1 = readl(ts.io + S3C2410_ADCDAT1);
- 166
- 167 down = get_down(data0, data1);
- 168
- 169 /* TODO we should never get an interrupt with down set while
- 170 * the timer is running, but maybe we ought to verify that the
- 171 * timer isn't running anyways. */
- 172
- 173 if (down)
- 174 s3c_adc_start(ts.client, 0, 1 << ts.shift);
- 175 else
- 176 dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);
- 177
- 178 if (ts.features & FEAT_PEN_IRQ) {
- 179 /* Clear pen down/up interrupt */
- 180 writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP);
- 181 }
- 182
- 183 return IRQ_HANDLED;
- 184}
151/** 152 * stylus_irq - touchscreen stylus event interrupt 153 * @irq: The interrupt number 154 * @dev_id: The device ID. 155 * 156 * Called when the IRQ_TC is fired for a pen up or down event. 157 */ 158static irqreturn_t stylus_irq(int irq, void *dev_id) 159{ 160 unsigned long data0; 161 unsigned long data1; 162 bool down; 163 164 data0 = readl(ts.io + S3C2410_ADCDAT0); 165 data1 = readl(ts.io + S3C2410_ADCDAT1); 166 167 down = get_down(data0, data1); 168 169 /* TODO we should never get an interrupt with down set while 170 * the timer is running, but maybe we ought to verify that the 171 * timer isn't running anyways. */ 172 173 if (down) 174 s3c_adc_start(ts.client, 0, 1 << ts.shift); 175 else 176 dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count); 177 178 if (ts.features & FEAT_PEN_IRQ) { 179 /* Clear pen down/up interrupt */ 180 writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP); 181 } 182 183 return IRQ_HANDLED; 184}
164 - 165行,读取ADCDAT0和ADCDAT1两个寄存器。注意,如果S3C2410处理器的ADCCON寄存器的READ_START位被置为1,则在读ADCDATA0和ADCDATA1时即会启动下一次AD转换。
167行,调用get_down函数获取触摸屏是否被按下,如果是被按下的,则返回1,否则返回0。get_down函数定义如下:
- 92/**
- 93 * get_down - return the down state of the pen
- 94 * @data0: The data read from ADCDAT0 register.
- 95 * @data1: The data read from ADCDAT1 register.
- 96 *
- 97 * Return non-zero if both readings show that the pen is down.
- 98 */
- 99static inline bool get_down(unsigned long data0, unsigned long data1)
- 100{
- 101 /* returns true if both data values show stylus down */
- 102 return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
- 103 !(data1 & S3C2410_ADCDAT0_UPDOWN));
- 104}
92/** 93 * get_down - return the down state of the pen 94 * @data0: The data read from ADCDAT0 register. 95 * @data1: The data read from ADCDAT1 register. 96 * 97 * Return non-zero if both readings show that the pen is down. 98 */ 99static inline bool get_down(unsigned long data0, unsigned long data1) 100{ 101 /* returns true if both data values show stylus down */ 102 return (!(data0 & S3C2410_ADCDAT0_UPDOWN) && 103 !(data1 & S3C2410_ADCDAT0_UPDOWN)); 104}
- #define S3C2410_ADCDAT0_UPDOWN (1<<15)
- #define S3C2410_ADCDAT1_UPDOWN (1<<15)
#define S3C2410_ADCDAT0_UPDOWN (1<<15) #define S3C2410_ADCDAT1_UPDOWN (1<<15)
ADCDAT0和ADCDAT1寄存器的第15位为0,表示触摸屏被按下,如果为1,表示触摸屏抬起。
回到触摸屏中断处理函数stylus_irq,
173 - 174行,如果触摸屏是被按下的,则调用s3c_adc_start函数,该函数在arch/arm/plat-samsung/adc.c文件中定义,我在《S3C2410驱动分析之ADC通用驱动》中对s3c_adc_start函数已经进行过详细分析,具体函数内容,这里不再重复,这里列出其函数调用逻辑:
s3c_adc_start ->
s3c_adc_try ->
s3c_adc_select ->
client->select_cb(client, 1)
s3c_adc_convert ->
s3c_adc_irq ->
(client->convert_cb)(client, data0, data1, &client->nr_samples)
(client->select_cb)(client, 0)
首先s3c_adc_start完成一些初始化工作后,调用s3c_adc_try,该函数检测等待处理的客户(client),然后调用s3c_adc_select,该函数会调用客户定义的select回调函数选择客户(client)。s3c_adc_try执行完s3c_adc_select后,又会调用s3c_adc_convert,该函数启动AD转换,转换结束后,触发中断,ADC中断处理函数s3c_adc_irq执行,在s3c_adc_irq函数中,会调用客户定义的convert回调函数对转换结果进行处理,然后调用select回调函数取消对客户的选择。
根据上面的调用逻辑,下面我们要分析的就是触摸屏的select和convert回调函数。先看convert回调函数,因为它比较简单,其定义如下:
- 186/**
- 187 * s3c24xx_ts_conversion - ADC conversion callback
- 188 * @client: The client that was registered with the ADC core.
- 189 * @data0: The reading from ADCDAT0.
- 190 * @data1: The reading from ADCDAT1.
- 191 * @left: The number of samples left.
- 192 *
- 193 * Called when a conversion has finished.
- 194 */
- 195static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
- 196 unsigned data0, unsigned data1,
- 197 unsigned *left)
- 198{
- 199 dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
- 200
- 201 ts.xp += data0;
- 202 ts.yp += data1;
- 203
- 204 ts.count++;
- 205
- 206 /* From tests, it seems that it is unlikely to get a pen-up
- 207 * event during the conversion process which means we can
- 208 * ignore any pen-up events with less than the requisite
- 209 * count done.
- 210 *
- 211 * In several thousand conversions, no pen-ups where detected
- 212 * before count completed.
- 213 */
- 214}
186/** 187 * s3c24xx_ts_conversion - ADC conversion callback 188 * @client: The client that was registered with the ADC core. 189 * @data0: The reading from ADCDAT0. 190 * @data1: The reading from ADCDAT1. 191 * @left: The number of samples left. 192 * 193 * Called when a conversion has finished. 194 */ 195static void s3c24xx_ts_conversion(struct s3c_adc_client *client, 196 unsigned data0, unsigned data1, 197 unsigned *left) 198{ 199 dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1); 200 201 ts.xp += data0; 202 ts.yp += data1; 203 204 ts.count++; 205 206 /* From tests, it seems that it is unlikely to get a pen-up 207 * event during the conversion process which means we can 208 * ignore any pen-up events with less than the requisite 209 * count done. 210 * 211 * In several thousand conversions, no pen-ups where detected 212 * before count completed. 213 */ 214}
201行,将从ADCDAT0寄存器中读得的x坐标值累加到ts.xp上。
202行,将从ADCDAT1寄存器中读得的y坐标值累加到ts.yp上。
204行,ts.count累加1。
所以,convert回调函数s3c24xx_ts_conversion的作用就是累加每次采样后得到的x,y坐标值,并记录采样次数,在其它函数中,可以取ts.xp和ts.yp的平均值,得到传递给用户空间的x,y坐标值。
下面看触摸屏的select回调函数,其定义如下:
- 216/**
- 217 * s3c24xx_ts_select - ADC selection callback.
- 218 * @client: The client that was registered with the ADC core.
- 219 * @select: The reason for select.
- 220 *
- 221 * Called when the ADC core selects (or deslects) us as a client.
- 222 */
- 223static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
- 224{
- 225 if (select) {
- 226 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
- 227 ts.io + S3C2410_ADCTSC);
- 228 } else {
- 229 mod_timer(&touch_timer, jiffies+1);
- 230 writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
- 231 }
- 232}
216/** 217 * s3c24xx_ts_select - ADC selection callback. 218 * @client: The client that was registered with the ADC core. 219 * @select: The reason for select. 220 * 221 * Called when the ADC core selects (or deslects) us as a client. 222 */ 223static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select) 224{ 225 if (select) { 226 writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, 227 ts.io + S3C2410_ADCTSC); 228 } else { 229 mod_timer(&touch_timer, jiffies+1); 230 writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); 231 } 232}
S3C2410_ADCTSC_PULL_UP_DISABLE宏表示禁止XP上拉,其定义如下:
- #define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
AUTOPST宏表示启用自动(连续)x/y轴坐标转换模式,其定义如下:
- 52#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
- 53 S3C2410_ADCTSC_YP_SEN | \
- 54 S3C2410_ADCTSC_XP_SEN | \
- 55 S3C2410_ADCTSC_AUTO_PST | \
- 56 S3C2410_ADCTSC_XY_PST(0))
52#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \ 53 S3C2410_ADCTSC_YP_SEN | \ 54 S3C2410_ADCTSC_XP_SEN | \ 55 S3C2410_ADCTSC_AUTO_PST | \ 56 S3C2410_ADCTSC_XY_PST(0))
我们来看select回调函数s3c24xx_ts_select,
225 - 227行,如果select参数为1,则启用自动(连续)x/y轴坐标转换模式。
228 - 231行,如果select参数为0,将touch_timer定时器的到时时间设置为此后一个时钟滴答,并进入中断等待模式。
再次回忆一下前面介绍的s3c_adc_start执行流程:
s3c_adc_start ->
s3c_adc_try ->
s3c_adc_select ->
client->select_cb(client, 1)
s3c_adc_convert ->
s3c_adc_irq ->
(client->convert_cb)(client, data0, data1, &client->nr_samples)
(client->select_cb)(client, 0)
重点看select和convert回调函数的调用。s3c_adc_try首先是调用client->select_cb(client, 1),即启用自动(连续)x/y轴坐标转换模式,然后调用s3c_adc_convert进行AD转换,AD转换结束后,触发AD中断,中断处理函数s3c_adc_irq会首先调用s3c24xx_ts_conversion,累加x,y坐标值及采样次数,然后再调用client->select_cb(client, 0)设置定时器和重新进入等待中断模式。
下面要分析的是定时器touch_timer,其定义如下:
- 149static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
149static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
定时器函数touch_timer_fire定义如下:
- 106static void touch_timer_fire(unsigned long data)
- 107{
- 108 unsigned long data0;
- 109 unsigned long data1;
- 110 bool down;
- 111
- 112 data0 = readl(ts.io + S3C2410_ADCDAT0);
- 113 data1 = readl(ts.io + S3C2410_ADCDAT1);
- 114
- 115 down = get_down(data0, data1);
- 116
- 117 if (down) {
- 118 if (ts.count == (1 << ts.shift)) {
- 119 ts.xp >>= ts.shift;
- 120 ts.yp >>= ts.shift;
- 121
- 122 dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
- 123 __func__, ts.xp, ts.yp, ts.count);
- 124
- 125 input_report_abs(ts.input, ABS_X, ts.xp);
- 126 input_report_abs(ts.input, ABS_Y, ts.yp);
- 127
- 128 input_report_key(ts.input, BTN_TOUCH, 1);
- 129 input_sync(ts.input);
- 130
- 131 ts.xp = 0;
- 132 ts.yp = 0;
- 133 ts.count = 0;
- 134 }
- 135
- 136 s3c_adc_start(ts.client, 0, 1 << ts.shift);
- 137 } else {
- 138 ts.xp = 0;
- 139 ts.yp = 0;
- 140 ts.count = 0;
- 141
- 142 input_report_key(ts.input, BTN_TOUCH, 0);
- 143 input_sync(ts.input);
- 144
- 145 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
- 146 }
- 147}
106static void touch_timer_fire(unsigned long data) 107{ 108 unsigned long data0; 109 unsigned long data1; 110 bool down; 111 112 data0 = readl(ts.io + S3C2410_ADCDAT0); 113 data1 = readl(ts.io + S3C2410_ADCDAT1); 114 115 down = get_down(data0, data1); 116 117 if (down) { 118 if (ts.count == (1 << ts.shift)) { 119 ts.xp >>= ts.shift; 120 ts.yp >>= ts.shift; 121 122 dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n", 123 __func__, ts.xp, ts.yp, ts.count); 124 125 input_report_abs(ts.input, ABS_X, ts.xp); 126 input_report_abs(ts.input, ABS_Y, ts.yp); 127 128 input_report_key(ts.input, BTN_TOUCH, 1); 129 input_sync(ts.input); 130 131 ts.xp = 0; 132 ts.yp = 0; 133 ts.count = 0; 134 } 135 136 s3c_adc_start(ts.client, 0, 1 << ts.shift); 137 } else { 138 ts.xp = 0; 139 ts.yp = 0; 140 ts.count = 0; 141 142 input_report_key(ts.input, BTN_TOUCH, 0); 143 input_sync(ts.input); 144 145 writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); 146 } 147}
212 - 215行,取得ADCDAT0和ADCDAT1的值保存在data0和data1中,并由get_down函数判断触摸屏是按下还是松开,如果是按下的,down为1,否则down为0。
117 - 118行,如果触摸屏是按下的,再判断采样次数是否是用户指定的次数。
119行,取ts.xp的平均值,即x坐标平均值。
120行,取ts.yp的平均值,即y坐标平均值。
125行,使用input子系统函数input_report_abs提交x绝对坐标值。
126行,使用input子系统函数input_report_abs提交y绝对坐标值。
128行, 使用input子系统函数input_report_key提交按键事件为触摸屏按下。
129行,使用input子系统函数input_sync(ts.input)同步所有事件,将所有事件组成一个evdev包,并通过/dev/input/eventX发送出去。input子系统要求提交各种事件后必须执行同步。
131 - 133行,将ts.xp,ts.yp和ts.count清0,准备一下次采样。
136行,如果采样次数与用户指定的采样次数不同,则重新执行s3c_adc_start进行采样。但是这里我有个疑问,再次采样,ts.count并没有清0,那么采样计数可以会超过用户指定的次数,这是怎么回事呢?
137 - 146行,如果触摸屏是抬起的,则将ts.xp,ts.yp和ts.count清0,准备一下次采样。然后提交触摸屏抬起事件,并将触摸屏设置为等待中断模式。
至此,整个S3C2410的触摸屏驱动我们就分析完了。