Chinaunix首页 | 论坛 | 博客
  • 博客访问: 451584
  • 博文数量: 40
  • 博客积分: 1410
  • 博客等级: 军士长
  • 技术积分: 1396
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-22 19:26
个人简介

嵌入式系统工程师,从事视频、图像、网络、虚拟化等方面的底层软件开发与优化。

文章存档

2014年(4)

2013年(10)

2012年(14)

2011年(12)

分类: LINUX

2012-04-28 19:21:40

注:我的硬件平台是Tiny6410,触摸屏部分把默认的一线触摸改成了标准的四线触摸,硬件上就是把屏幕背面四个零欧电阻R34/R35/R36/R37取了,把取了的零欧电阻焊接到R28/R29/R30/R31。这样,软件上就可以用内核自带的触摸屏驱动了。

一、触摸屏驱动移植与tslib移植部分

(一). 触摸屏驱动移植

  1. Kconfig修改
arch/arm/mach-s3c64xx/Kconfig里配置用到arch/arm/plat-samsung/devs.c里的平台设备,如下配置:

点击(此处)折叠或打开

  1. @arch/arm/mach-s3c64xx/Kconfig
  2. about line 98 add:
  3.     select SAMSUNG_DEV_ADC
  4.     select SAMSUNG_DEV_TS

  5. then, it like this:
  6. config MACH_JASON6410
  7.     bool "JASON6410"
  8.     select CPU_S3C6410
  9.     select S3C_DEV_FB
  10.     select S3C64XX_SETUP_FB_24BPP
  11.     select SAMSUNG_DEV_ADC
  12.     select SAMSUNG_DEV_TS
  13.     help
  14.      Machine support for the JASON6410
下面是截取自devs.c里的用到的平台设备:

点击(此处)折叠或打开

  1. #if defined(CONFIG_SAMSUNG_DEV_ADC)
  2. static struct resource s3c_adc_resource[] = {
  3. [0] = DEFINE_RES_MEM(SAMSUNG_PA_ADC, SZ_256),
  4. [1] = DEFINE_RES_IRQ(IRQ_TC),
  5. [2] = DEFINE_RES_IRQ(IRQ_ADC),
  6. };

  7. struct platform_device s3c_device_adc = {
  8. .name = "samsung-adc",
  9. .id = -1,
  10. .num_resources = ARRAY_SIZE(s3c_adc_resource),
  11. .resource = s3c_adc_resource,
  12. };
  13. #endif /* CONFIG_SAMSUNG_DEV_ADC */

  14.  
  15. #ifdef CONFIG_SAMSUNG_DEV_TS
  16. static struct resource s3c_ts_resource[] = {
  17.     [0] = DEFINE_RES_MEM(SAMSUNG_PA_ADC, SZ_256),
  18.     [1] = DEFINE_RES_IRQ(IRQ_TC),
  19. };

  20. static struct s3c2410_ts_mach_info default_ts_data __initdata = {
  21.     .delay            = 10000,
  22.     .presc            = 49,
  23.     .oversampling_shift    = 2,
  24. };

  25. struct platform_device s3c_device_ts = {
  26.     .name        = "s3c64xx-ts",
  27.     .id        = -1,
  28.     .num_resources    = ARRAY_SIZE(s3c_ts_resource),
  29.     .resource    = s3c_ts_resource,
  30. };

  31. void __init s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *pd)
  32. {
  33.     if (!pd)
  34.         pd = &default_ts_data;

  35.     s3c_set_platdata(pd, sizeof(struct s3c2410_ts_mach_info),
  36.              &s3c_device_ts);
  37. }
  38. #endif /* CONFIG_SAMSUNG_DEV_TS */
  
  2. 内核配置
配置Event interface是因为tslib的TSLIB_TSDEVICE环境变量需要用到/dev/input/event0设备节点。

点击(此处)折叠或打开

  1. Device Drivers --->
  2.         Input device support --->         
  3.             [*] Touchscreens --->
  4.                 <*> Samsung S3C2410/generic touchscreen input driver

  5. Device Drivers --->
  6.         Input device support --->
  7.             <*> Event interface

  3. BSP部分修改
如下绿色部分,是针对触摸屏驱动,在BSP上添加的代码。

点击(此处)折叠或打开

  1. /* linux/arch/arm/mach-s3c64xx/mach-jason6410.c
  2.  *
  3.  * Copyright 2012 Jason Lu <gfvvz@yahoo.com.cn>
  4.  *     http://jason2012.blog.chinaunix.net
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10. */

  11. #include <linux/init.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/fb.h>
  14. #include <linux/gpio.h>
  15. #include <linux/kernel.h>
  16. #include <linux/list.h>
  17. #include <linux/dm9000.h>
  18. #include <linux/mtd/mtd.h>
  19. #include <linux/mtd/partitions.h>
  20. #include <linux/serial_core.h>
  21. #include <linux/types.h>

  22. #include <asm/mach-types.h>
  23. #include <asm/mach/arch.h>
  24. #include <asm/mach/map.h>

  25. #include <mach/map.h>
  26. #include <mach/regs-gpio.h>
  27. #include <mach/regs-modem.h>
  28. #include <mach/regs-srom.h>

  29. #include <plat/s3c6410.h>
  30. #include <plat/adc.h>
  31. #include <plat/cpu.h>
  32. #include <plat/devs.h>
  33. #include <plat/fb.h>
  34. #include <plat/nand.h>
  35. #include <plat/regs-serial.h>
  36. #include <plat/ts.h>
  37. #include <plat/regs-fb-v4.h>
  38. #include <plat/iic.h>

  39. #include <video/platform_lcd.h>

  40. #define UCON S3C2410_UCON_DEFAULT
  41. #define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
  42. #define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)

  43. static struct s3c2410_uartcfg jason6410_uartcfgs[] __initdata = {
  44.     [0] = {
  45.         .hwport    = 0,
  46.         .flags    = 0,
  47.         .ucon    = UCON,
  48.         .ulcon    = ULCON,
  49.         .ufcon    = UFCON,
  50.     },
  51.     [1] = {
  52.         .hwport    = 1,
  53.         .flags    = 0,
  54.         .ucon    = UCON,
  55.         .ulcon    = ULCON,
  56.         .ufcon    = UFCON,
  57.     },
  58.     [2] = {
  59.         .hwport    = 2,
  60.         .flags    = 0,
  61.         .ucon    = UCON,
  62.         .ulcon    = ULCON,
  63.         .ufcon    = UFCON,
  64.     },
  65.     [3] = {
  66.         .hwport    = 3,
  67.         .flags    = 0,
  68.         .ucon    = UCON,
  69.         .ulcon    = ULCON,
  70.         .ufcon    = UFCON,
  71.     },
  72. };

  73. /* Framebuffer. */

  74. static struct s3c_fb_pd_win jason6410_fb_win0 = {
  75.     /* this is to ensure we use win0 */
  76.     .win_mode    = {
  77. #if 0
  78.         .pixclock    = 115440,
  79. #endif
  80.         .left_margin    = 0x03,
  81.         .right_margin    = 0x02,
  82.         .upper_margin    = 0x01,
  83.         .lower_margin    = 0x01,
  84.         .hsync_len    = 0x28,
  85.         .vsync_len    = 0x01,
  86.         .xres        = 480,
  87.         .yres        = 272,
  88.     },
  89.     .max_bpp    = 32,
  90.     .default_bpp    = 16,
  91. };

  92. /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
  93. static struct s3c_fb_platdata jason6410_lcd_pdata __initdata = {
  94.     .setup_gpio    = s3c64xx_fb_gpio_setup_24bpp,
  95.     .win[0]        = &jason6410_fb_win0,
  96.     .vidcon0    = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
  97.     .vidcon1    = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
  98. };

  99. /* Nand flash */
  100. static struct mtd_partition jason6410_nand_part[] = {
  101.     {
  102.         .name        = "u-boot-2011.06",
  103.         .offset        = 0,
  104.         .size        = (4 * 128 *SZ_1K),
  105.         .mask_flags    = MTD_CAP_NANDFLASH,
  106.     },
  107.     {
  108.         .name        = "Linux Kernel 3.2.8",
  109.         .offset        = MTDPART_OFS_APPEND,
  110.         .size        = (5*SZ_1M) ,
  111.         .mask_flags    = MTD_CAP_NANDFLASH,
  112.     },
  113.     {
  114.         .name        = "UBI File System",
  115.         .offset        = MTDPART_OFS_APPEND,
  116.         .size        = MTDPART_SIZ_FULL,
  117.     }
  118. };

  119. static struct s3c2410_nand_set jason6410_nand_sets[] = {
  120.     [0] = {
  121.         .name        = "nand",
  122.         .nr_chips    = 1,
  123.         .nr_partitions    = ARRAY_SIZE(jason6410_nand_part),
  124.         .partitions    = jason6410_nand_part,
  125.     },
  126. };

  127. static struct s3c2410_platform_nand jason6410_nand_info = {
  128.     .tacls        = 25,
  129.     .twrph0        = 55,
  130.     .twrph1        = 40,
  131.     .nr_sets    = ARRAY_SIZE(jason6410_nand_sets),
  132.     .sets        = jason6410_nand_sets,
  133. };

  134. static struct map_desc jason6410_iodesc[] = {
  135. };

  136. static struct platform_device *jason6410_devices[] __initdata = {
  137.     &s3c_device_nand,
  138.     &s3c_device_i2c0,
  139.     &s3c_device_fb,
  140.     &s3c_device_ts,
  141.     &s3c_device_adc,
  142. };

  143. static struct i2c_board_info i2c_devs0[] __initdata = {
  144.     { I2C_BOARD_INFO("24c08", 0x50), },
  145. };

  146. static struct i2c_board_info i2c_devs1[] __initdata = {
  147.     /* Add your i2c device here */
  148. };

  149. static struct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
  150.     .delay            = 0xFFFF,
  151.     .presc            = 0xFF,
  152.     .oversampling_shift    = 2,
  153. };

  154. static void __init jason6410_map_io(void)
  155. {
  156.     u32 tmp;

  157.     s3c64xx_init_io(jason6410_iodesc, ARRAY_SIZE(jason6410_iodesc));
  158.     s3c24xx_init_clocks(12000000);
  159.     s3c24xx_init_uarts(jason6410_uartcfgs, ARRAY_SIZE(jason6410_uartcfgs));

  160.     /* set the LCD type */
  161.     tmp = __raw_readl(S3C64XX_SPCON);
  162.     tmp &= ~S3C64XX_SPCON_LCD_SEL_MASK;
  163.     tmp |= S3C64XX_SPCON_LCD_SEL_RGB;
  164.     __raw_writel(tmp, S3C64XX_SPCON);

  165.     /* remove the lcd bypass */
  166.     tmp = __raw_readl(S3C64XX_MODEM_MIFPCON);
  167.     tmp &= ~MIFPCON_LCD_BYPASS;
  168.     __raw_writel(tmp, S3C64XX_MODEM_MIFPCON);
  169. }

  170. static void __init jason6410_machine_init(void)
  171. {
  172.     s3c_device_nand.name = "s3c6410-nand";
  173.     s3c_nand_set_platdata(&jason6410_nand_info);

  174.     s3c_i2c0_set_platdata(NULL);
  175.     s3c_fb_set_platdata(&jason6410_lcd_pdata);
  176.     s3c24xx_ts_set_platdata(&s3c_ts_platform);

  177.     if (ARRAY_SIZE(i2c_devs0)) {
  178.         i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
  179.     }
  180.     if (ARRAY_SIZE(i2c_devs1)) {
  181.         i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
  182.     }

  183.     platform_add_devices(jason6410_devices, ARRAY_SIZE(jason6410_devices));
  184. }

  185. MACHINE_START(JASON6410, "JASON6410")
  186.     /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  187.     .atag_offset    = 0x100,
  188.     .init_irq    = s3c6410_init_irq,
  189.     .map_io        = jason6410_map_io,
  190.     .init_machine    = jason6410_machine_init,
  191.     .timer        = &s3c24xx_timer,
  192. MACHINE_END

(二). tslib移植及测试
我的内核版本是3.2.8,使用tslib-1.4,在执行ts_calibrate会报段错误。在下载了最新的tslib,重新编译,就能使用了。 kergoth-tslib-1.0-106-gf6c499a.zip   
    1. 为虚拟机里的Linux系统安装工具:

点击(此处)折叠或打开

  1. sudo apt-get install autoconf
  2. sudo apt-get install automake
  3. sudo apt-get install libtool
    2. 解压后编译

点击(此处)折叠或打开

  1. mv xxx(解压后名字) tslib  //名字改为tslib 

  2. cd tslib

  3. ./autogen.sh 

  4. mkdir tmp

  5. echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache

  6. ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp

  7. make

  8. make install
    3. 打开tmp目录,里面有四个文件夹,分别是bin、etc、include、lib。将etc目录下的ts.conf里的第2行去掉注释。即:

点击(此处)折叠或打开

  1. # module_raw input
  2. 改为:
  3. module_raw input
    4. 将bin/etc/lib目录下的文件及连接拷贝到文件系统下对应同名目录。include目录估计是编译应用程序时用的,在此可以不使用。
    5. 修改etc/init.d/rcS,注释掉启动QT界面的代码:
    6. 重新生成文件系统镜像,并且重新烧写新的内核与文件系统。
    7. 在串口终端,顺序输入下列环境变量。

点击(此处)折叠或打开

  1. export TSLIB_TSDEVICE=/dev/input/event0
  2. export TSLIB_CALIBFILE=/etc/pointercal
  3. export TSLIB_CONFFILE=/etc/ts.conf
  4. export TSLIB_PLUGINDIR=/lib/ts
  5. export TSLIB_CONSOLEDEVICE=none
  6. export TSLIB_FBDEVICE=/dev/fb0
    8. 终端输入命令ts_calibrate,LCD如下显示类似如下界面,触摸校准触摸屏。
    9. 终端输入命令ts_test,LCD显示类似如下界面,可以触摸屏幕,十字光标跟随触摸点移动,按Draw按钮,可以画出触摸轨迹。




二、触摸屏及ADC驱动分析
(一)代码详细分析
  1. 触摸屏驱动中的probe函数分析

点击(此处)折叠或打开

  1. /**
  2.  * s3c2410ts_probe - device core probe entry point
  3.  * @pdev: The device we are being bound to.
  4.  *
  5.  * Initialise, find and allocate any resources we need to run and then
  6.  * register with the ADC and input systems.
  7.  */
  8. static int __devinit s3c2410ts_probe(struct platform_device *pdev)
  9. {
  10.     struct s3c2410_ts_mach_info *info;
  11.     struct device *dev = &pdev->dev;
  12.     struct input_dev *input_dev;
  13.     struct resource *res;
  14.     int ret = -EINVAL;

  15.     /* Initialise input stuff */
  16.     memset(&ts, 0, sizeof(struct s3c2410ts)); //清零ts结构体

  17.     ts.dev = dev; //ts的设备成员指向匹配的平台设备

  18.     info = pdev->dev.platform_data; //info指向平台设备的平台数据
  19.     if (!info) {
  20.         dev_err(dev, "no platform data, cannot attach\n");
  21.         return -EINVAL;
  22.     }

  23.     dev_dbg(dev, "initialising touchscreen\n");

  24.     ts.clock = clk_get(dev, "adc"); //获取ADC时钟
  25.     if (IS_ERR(ts.clock)) {
  26.         dev_err(dev, "cannot get adc clock source\n");
  27.         return -ENOENT;
  28.     }

  29.     clk_enable(ts.clock); //使能ADC时钟
  30.     dev_dbg(dev, "got and enabled clocks\n");

  31.     ts.irq_tc = ret = platform_get_irq(pdev, 0); //获取中断号并赋给irq_tc成员
  32.     if (ret < 0) {
  33.         dev_err(dev, "no resource for interrupt\n");
  34.         goto err_clk;
  35.     }

  36.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  37.     if (!res) {
  38.         dev_err(dev, "no resource for registers\n");
  39.         ret = -ENOENT;
  40.         goto err_clk;
  41.     }

  42.     ts.io = ioremap(res->start, resource_size(res)); //映射寄存器组所在地址范围
  43.     if (ts.io == NULL) {
  44.         dev_err(dev, "cannot map registers\n");
  45.         ret = -ENOMEM;
  46.         goto err_clk;
  47.     }

  48.     /* inititalise the gpio */
  49.     if (info->cfg_gpio) //如果定义了GPIO,就在这里初始化GPIO
  50.         info->cfg_gpio(to_platform_device(ts.dev));

  51.     ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, //客户端函数指定
  52.                  s3c24xx_ts_conversion, 1);
  53.     if (IS_ERR(ts.client)) {
  54.         dev_err(dev, "failed to register adc client\n");
  55.         ret = PTR_ERR(ts.client);
  56.         goto err_iomap;
  57.     }

  58.     /* Initialise registers */ //初始化寄存器组
  59.     if ((info->delay & 0xffff) > 0)
  60.         writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); //看下图

  61.     writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); //写入0xd3,看下图

  62.     input_dev = input_allocate_device(); //分配一个input_dev结构体
  63.     if (!input_dev) {
  64.         dev_err(dev, "Unable to allocate the input device !!\n");
  65.         ret = -ENOMEM;
  66.         goto err_iomap;
  67.     }

  68.     ts.input = input_dev; //ts的input成员指向新分配的input_dev结构体
  69.     ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  //设备支持的事件类型
  70.     ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //设备支持的按键
  71.     input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);  //X轴
  72.     input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);  //Y轴

  73.     ts.input->name = "S3C24XX TouchScreen"; //输入设备名、总线类型等信息
  74.     ts.input->id.bustype = BUS_HOST;
  75.     ts.input->id.vendor = 0xDEAD;
  76.     ts.input->id.product = 0xBEEF;
  77.     ts.input->id.version = 0x0102;

  78.     ts.shift = info->oversampling_shift; //多次采样次数
  79.     ts.features = platform_get_device_id(pdev)->driver_data; //驱动数据
  80. 点击(此处)折叠或打开

    1. static struct platform_device_id s3cts_driver_ids[] = {
    2.     { "s3c2410-ts", 0 },
    3.     { "s3c2440-ts", 0 },
    4.     { "s3c64xx-ts", FEAT_PEN_IRQ }, //程序中获取的驱动数据
    5.     { }
    6. };

  81.     ret = request_irq(ts.irq_tc, stylus_irq, 0, //注册触摸屏中断
  82.              "s3c2410_ts_pen", ts.input);
  83.     if (ret) {
  84.         dev_err(dev, "cannot get TC interrupt\n");
  85.         goto err_inputdev;
  86.     }

  87.     dev_info(dev, "driver attached, registering input device\n");

  88.     /* All went ok, so register to the input system */
  89.     ret = input_register_device(ts.input); //注册输入设备
  90.     if (ret < 0) {
  91.         dev_err(dev, "failed to register input device\n");
  92.         ret = -EIO;
  93.         goto err_tcirq;
  94.     }

  95.     return 0;

  96.  err_tcirq:
  97.     free_irq(ts.irq_tc, ts.input);
  98.  err_inputdev:
  99.     input_free_device(ts.input);
  100.  err_iomap:
  101.     iounmap(ts.io);
  102.  err_clk:
  103.     del_timer_sync(&touch_timer);
  104.     clk_put(ts.clock);
  105.     return ret;
  106. }
  2. 触摸屏驱动中的stylus_irq函数分析

点击(此处)折叠或打开

  1. /**
  2.  * stylus_irq - touchscreen stylus event interrupt
  3.  * @irq: The interrupt number
  4.  * @dev_id: The device ID.
  5.  *
  6.  * Called when the IRQ_TC is fired for a pen up or down event.
  7.  */
  8. static irqreturn_t stylus_irq(int irq, void *dev_id)
  9. {
  10.     unsigned long data0;
  11.     unsigned long data1;
  12.     bool down;
  13.  
  14.     //读取ADCDAT0寄存器中数据,最高位UPDOWN是按下/释放标志
  15.     data0 = readl(ts.io + S3C2410_ADCDAT0);
  16.     data1 = readl(ts.io + S3C2410_ADCDAT1);

  17.     down = get_down(data0, data1); //根据ADCDAT0/ADCDAT1最高位值判断按下还是释放触摸屏

  18.     /* TODO we should never get an interrupt with down set while
  19.      * the timer is running, but maybe we ought to verify that the
  20.      * timer isn't running anyways. */

  21.     if (down)
  22.         s3c_adc_start(ts.client, 0, 1 << ts.shift); //如果是按下,启动ADC采样
  23.     else
  24.         dev_dbg(ts.dev, "%s: count=%d\n", __func__, ts.count);

  25.     if (ts.features & FEAT_PEN_IRQ) {
  26.         /* Clear pen down/up interrupt */
  27.         writel(0x0, ts.io + S3C64XX_ADCCLRINTPNDNUP); //清零触摸中断
  28.     }

  29.     return IRQ_HANDLED;
  30. }
  3. ADC驱动中的probe函数分析
stylus_irq中的s3c_adc_start函数位于ADC驱动中,在分析它之前,还是先分析ADC驱动的probe函数。有一点要说明的是,在devs.c中定义的ADC平台设备,名称是"samsung-adc",然而在ADC平台驱动结构体的id_table里没有这个名称,只有"s3c64xx-adc",启动信息里可以看到,匹配名就是这个,那么这个名称是从哪来的呢?通过搜索内核代码,发现在s3c6410.c里,有如下代码:

点击(此处)折叠或打开

  1. void __init s3c6410_map_io(void)
  2. {
  3.     /* initialise device information early */
  4.     s3c6410_default_sdhci0();
  5.     s3c6410_default_sdhci1();
  6.     s3c6410_default_sdhci2();

  7.     /* the i2c devices are directly compatible with s3c2440 */
  8.     s3c_i2c0_setname("s3c2440-i2c");
  9.     s3c_i2c1_setname("s3c2440-i2c");

  10.     s3c_adc_setname("s3c64xx-adc");
  11.     s3c_device_nand.name = "s3c6400-nand";
  12.     s3c_onenand_setname("s3c6410-onenand");
  13.     s3c64xx_onenand1_setname("s3c6410-onenand");
  14.     s3c_cfcon_setname("s3c64xx-pata");
  15. }
如上s3c_adc_setname函数的源码如下,给函数就是重命名结构体名字用的!

点击(此处)折叠或打开

  1. /* re-define device name depending on support. */
  2. static inline void s3c_adc_setname(char *name)
  3. {
  4. #if defined(CONFIG_SAMSUNG_DEV_ADC) || defined(CONFIG_PLAT_S3C24XX)
  5.     s3c_device_adc.name = name;
  6. #endif
  7. }
接下来还有一个问题,就是调用s3c_adc_setname函数的s3c6410_map_io函数在内核启动的什么时候被调用?被哪个函数调用?通过代码追踪,发下如下关系:

点击(此处)折叠或打开

  1. MACHINE_START(JASON6410, "JASON6410")
  2.     /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  3.     .atag_offset = 0x100,
  4.     .init_irq = s3c6410_init_irq,
  5.     .map_io = jason6410_map_io,
  6.     .init_machine = jason6410_machine_init,
  7.     .timer = &s3c24xx_timer,
  8. MACHINE_END
  9.  
  10. jason6410_map_io
  11. s3c64xx_init_io
  12. s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
  13. static struct cpu_table cpu_ids[] __initdata = {
  14. {
  15. .idcode = S3C6400_CPU_ID,
  16. .idmask = S3C64XX_CPU_MASK,
  17. .map_io = s3c6400_map_io,
  18. .init_clocks = s3c6400_init_clocks,
  19. .init_uarts = s3c6400_init_uarts,
  20. .init = s3c6400_init,
  21. .name = name_s3c6400,
  22. }, {
  23. .idcode = S3C6410_CPU_ID,
  24. .idmask = S3C64XX_CPU_MASK,
  25. .map_io = s3c6410_map_io,
  26. .init_clocks = s3c6410_init_clocks,
  27. .init_uarts = s3c6410_init_uarts,
  28. .init = s3c6410_init,
  29. .name = name_s3c6410,
  30. },
  31. };
好了,圆规正传,开始分析probe函数。

点击(此处)折叠或打开

  1. static int s3c_adc_probe(struct platform_device *pdev)
  2. {
  3.     struct device *dev = &pdev->dev;
  4.     struct adc_device *adc;
  5.     struct resource *regs;
  6.     enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
  7.     int ret;
  8.     unsigned tmp;

  9.     adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);  //分配一个adc_device结构体
  10.     if (adc == NULL) {
  11.         dev_err(dev, "failed to allocate adc_device\n");
  12.         return -ENOMEM;
  13.     }

  14.     spin_lock_init(&adc->lock); //初始化自旋锁

  15.     adc->pdev = pdev;
  16.     adc->prescale = S3C2410_ADCCON_PRSCVL(49); //预分频

  17.     adc->vdd = regulator_get(dev, "vdd");
  18.     if (IS_ERR(adc->vdd)) {
  19.         dev_err(dev, "operating without regulator \"vdd\" .\n");
  20.         ret = PTR_ERR(adc->vdd);
  21.         goto err_alloc;
  22.     }

  23.     adc->irq = platform_get_irq(pdev, 1); //获取ADC中断
  24.     if (adc->irq <= 0) {
  25.         dev_err(dev, "failed to get adc irq\n");
  26.         ret = -ENOENT;
  27.         goto err_reg;
  28.     }

  29.     ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); //注册ADC中断
  30.     if (ret < 0) {
  31.         dev_err(dev, "failed to attach adc irq\n");
  32.         goto err_reg;
  33.     }

  34.     adc->clk = clk_get(dev, "adc"); //获取ADC时钟
  35.     if (IS_ERR(adc->clk)) {
  36.         dev_err(dev, "failed to get adc clock\n");
  37.         ret = PTR_ERR(adc->clk);
  38.         goto err_irq;
  39.     }

  40.     regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取ADC寄存器地址范围
  41.     if (!regs) {
  42.         dev_err(dev, "failed to find registers\n");
  43.         ret = -ENXIO;
  44.         goto err_clk;
  45.     }

  46.     adc->regs = ioremap(regs->start, resource_size(regs)); //寄存器地址映射到内存
  47.     if (!adc->regs) {
  48.         dev_err(dev, "failed to map registers\n");
  49.         ret = -ENXIO;
  50.         goto err_clk;
  51.     }

  52.     ret = regulator_enable(adc->vdd);
  53.     if (ret)
  54.         goto err_ioremap;

  55.     clk_enable(adc->clk); //使能ADC时钟

  56.     tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; //预分频使能

  57.     /* Enable 12-bit ADC resolution */
  58.     if (cpu == TYPE_ADCV12)
  59.         tmp |= S3C2416_ADCCON_RESSEL;
  60.     if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
  61.         tmp |= S3C64XX_ADCCON_RESSEL;
  62.  
  63.     //ADCCON寄存器配置:12位、预分频使能、预分频值49
  64.     writel(tmp, adc->regs + S3C2410_ADCCON);

  65.     dev_info(dev, "attached adc driver\n");

  66.     platform_set_drvdata(pdev, adc);
  67.     adc_dev = adc; //adc_dev指向这里分配和设置的adc_device结构体,下面的函数要用到

  68.     return 0;

  69.  err_ioremap:
  70.     iounmap(adc->regs);
  71.  err_clk:
  72.     clk_put(adc->clk);

  73.  err_irq:
  74.     free_irq(adc->irq, adc);
  75.  err_reg:
  76.     regulator_put(adc->vdd);
  77.  err_alloc:
  78.     kfree(adc);
  79.     return ret;
  80. }
  4. ADC驱动中的s3c_adc_start函数分析

点击(此处)折叠或打开

  1. int s3c_adc_start(struct s3c_adc_client *client,
  2.          unsigned int channel, unsigned int nr_samples)
  3. {
  4.     struct adc_device *adc = adc_dev;
  5.     unsigned long flags;
  6.   
  7.     /* 先判断adc所指向的adc_device是否有效,可以作为设备驱动是否匹配的判断依据;
  8.      * 因为adc_dev是在上面的probe函数里被复制的
  9.      */
  10.     if (!adc) {
  11.         printk(KERN_ERR "%s: failed to find adc\n", __func__);
  12.         return -EINVAL;
  13.     }

  14.     if (client->is_ts && adc->ts_pend) //如果是触摸屏并且是当前有客户,则返回
  15.         return -EAGAIN;

  16.     spin_lock_irqsave(&adc->lock, flags); //获取自旋锁,并把中断状态存入flags

  17.     client->channel = channel; //采样通道
  18.     client->nr_samples = nr_samples; //采样次数

  19.     if (client->is_ts) //如果是触摸屏,将client保存在adc->ts_pend中
  20.         adc->ts_pend = client;
  21.     else //如果不是触摸屏,将client->pend插入adc_pending链表中
  22.         list_add_tail(&client->pend, &adc_pending);

  23.     if (!adc->cur) //如果当前没有处理其他用户请求,则执行新的用户请求
  24.         s3c_adc_try(adc);

  25.     spin_unlock_irqrestore(&adc->lock, flags); //释放自旋锁,恢复中断状态

  26.     return 0;
  27. }
  28. EXPORT_SYMBOL_GPL(s3c_adc_start); //将函数导出,让所有遵循GPL协议的模块都可以使用
  5. ADC驱动中的s3c_adc_try函数分析

点击(此处)折叠或打开

  1. static void s3c_adc_try(struct adc_device *adc)
  2. {
  3.     struct s3c_adc_client *next = adc->ts_pend;

  4.     if (!next && !list_empty(&adc_pending)) {
  5.         next = list_first_entry(&adc_pending,
  6.                     struct s3c_adc_client, pend);
  7.         list_del(&next->pend);
  8.     } else
  9.         adc->ts_pend = NULL; //去除客户

  10.     if (next) { //如果触摸屏有客户请求,则执行下列函数
  11.         adc_dbg(adc, "new client is %p\n", next);
  12.         adc->cur = next;
  13.         s3c_adc_select(adc, next);
  14.         s3c_adc_convert(adc);
  15.         s3c_adc_dbgshow(adc); //打印ADCCON,ADCTSC,ADCDLY三个寄存器值
  16.     }
  17. }
  6. ADC驱动中的s3c_adc_select函数分析

点击(此处)折叠或打开

  1. static inline void s3c_adc_select(struct adc_device *adc,
  2.                  struct s3c_adc_client *client)
  3. {
  4.     unsigned con = readl(adc->regs + S3C2410_ADCCON); //读取ADCCON寄存器当前值
  5.     enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data; //CPU类型
  6.  
  7.     //执行客户定义的select回调函数,也就是触摸屏驱动中的s3c24xx_ts_select函数
  8.     client->select_cb(client, 1)
  9. 点击(此处)折叠或打开

    1. /**
    2.  * s3c24xx_ts_select - ADC selection callback.
    3.  * @client: The client that was registered with the ADC core.
    4.  * @select: The reason for select.
    5.  *
    6.  * Called when the ADC core selects (or deslects) us as a client.
    7.  */
    8. static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
    9. {
    10.     if (select) {//启用自动(连续)x/y轴坐标转换模式
    11.         writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
    12.          ts.io + S3C2410_ADCTSC);
    13.     } else {//将touch_timer定时器定时时间设置为此后一个时钟滴答,并进入中断等待模式
    14.         mod_timer(&touch_timer, jiffies+1);  //1 jiffies = 1/Hz s Hz可取100
    15.         writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
    16.     }
    17. }

  10.     if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV2)
  11.         con &= ~S3C2410_ADCCON_MUXMASK;
  12.     con &= ~S3C2410_ADCCON_STDBM;
  13.     con &= ~S3C2410_ADCCON_STARTMASK;

  14.     if (!client->is_ts) {
  15.         if (cpu == TYPE_ADCV3)
  16.             writel(client->channel & 0xf, adc->regs + S5P_ADCMUX);
  17.         else if (cpu == TYPE_ADCV11 || cpu == TYPE_ADCV12)
  18.             writel(client->channel & 0xf,
  19.                         adc->regs + S3C2443_ADCMUX);
  20.         else
  21.             con |= S3C2410_ADCCON_SELMUX(client->channel);
  22.     }

  23.     writel(con, adc->regs + S3C2410_ADCCON);
  24. }
  7. ADC驱动中的s3c_adc_convert函数分析

点击(此处)折叠或打开

  1. static inline void s3c_adc_convert(struct adc_device *adc)
  2. {
  3.     unsigned con = readl(adc->regs + S3C2410_ADCCON);

  4.     con |= S3C2410_ADCCON_ENABLE_START; //启动ADC转换
  5.     writel(con, adc->regs + S3C2410_ADCCON);
  6. }
  8. ADC驱动中的s3c_adc_irq函数分析

点击(此处)折叠或打开

  1. static irqreturn_t s3c_adc_irq(int irq, void *pw)
  2. {
  3.     struct adc_device *adc = pw;
  4.     struct s3c_adc_client *client = adc->cur;
  5.     enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
  6.     unsigned data0, data1;

  7.     if (!client) {
  8.         dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
  9.         goto exit;
  10.     }

  11.     data0 = readl(adc->regs + S3C2410_ADCDAT0);
  12.     data1 = readl(adc->regs + S3C2410_ADCDAT1);
  13.     adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);

  14.     client->nr_samples--; //s3c_adc_start函数中被首次复制

  15.     if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV11) {
  16.         data0 &= 0x3ff;
  17.         data1 &= 0x3ff;
  18.     } else {
  19.         /* S3C2416/S3C64XX/S5P ADC resolution is 12-bit */
  20.         data0 &= 0xfff;
  21.         data1 &= 0xfff;
  22.     }
  23.  
  24.     //回调函数,在触摸屏驱动中定义
  25.     if (client->convert_cb)
  26.         (client->convert_cb)(client, data0, data1, &client->nr_samples);
  27. 点击(此处)折叠或打开

    1. /**
    2.  * s3c24xx_ts_conversion - ADC conversion callback
    3.  * @client: The client that was registered with the ADC core.
    4.  * @data0: The reading from ADCDAT0.
    5.  * @data1: The reading from ADCDAT1.
    6.  * @left: The number of samples left.
    7.  *
    8.  * Called when a conversion has finished.
    9.  */
    10. static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
    11.                  unsigned data0, unsigned data1,
    12.                  unsigned *left)
    13. {
    14.     dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);

    15.     ts.xp += data0;
    16.     ts.yp += data1;

    17.     ts.count++;

    18.     /* From tests, it seems that it is unlikely to get a pen-up
    19.      * event during the conversion process which means we can
    20.      * ignore any pen-up events with less than the requisite
    21.      * count done.
    22.      *
    23.      * In several thousand conversions, no pen-ups where detected
    24.      * before count completed.
    25.      */
    26. }

  28.     if (client->nr_samples > 0) { //如果大于零,表示还未达到多次采样的次数,继续启动
  29.         /* fire another conversion for this */

  30.         client->select_cb(client, 1);
  31.         s3c_adc_convert(adc);
  32.     } else { //如果等于零,表示已到达多次采样次数,启动最后一次采样
  33.         spin_lock(&adc->lock);
  34.         //将touch_timer定时器定时时间设置为此后一个时钟滴答,并进入中断等待模式
  35.         (client->select_cb)(client, 0);
  36.         adc->cur = NULL;

  37.         s3c_adc_try(adc); //启动最后一次采样
  38.         spin_unlock(&adc->lock);
  39.     }

  40. exit:
  41.     if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) {
  42.         /* Clear ADC interrupt */
  43.         writel(0, adc->regs + S3C64XX_ADCCLRINT);
  44.     }
  45.     return IRQ_HANDLED;
  46. }
  9. 触摸屏驱动中的touch_timer_fire函数分析
当上面select_cb回调函数中设置的定时器定时时间到到达时,会调用如下函数。

点击(此处)折叠或打开

  1. static void touch_timer_fire(unsigned long data)
  2. {
  3.     unsigned long data0;
  4.     unsigned long data1;
  5.     bool down;

  6.     data0 = readl(ts.io + S3C2410_ADCDAT0);
  7.     data1 = readl(ts.io + S3C2410_ADCDAT1);

  8.     down = get_down(data0, data1);

  9.     if (down) { //按下触摸屏
  10.         if (ts.count == (1 << ts.shift)) { //如果到达多次采样数
  11.             ts.xp >>= ts.shift; //除以多次采样数
  12.             ts.yp >>= ts.shift;

  13.             dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
  14.                 __func__, ts.xp, ts.yp, ts.count);

  15.             input_report_abs(ts.input, ABS_X, ts.xp); //向上报告X坐标
  16.             input_report_abs(ts.input, ABS_Y, ts.yp); //向上报告Y坐标

  17.             input_report_key(ts.input, BTN_TOUCH, 1); //向上报告按下
  18.             input_sync(ts.input); //同步

  19.             ts.xp = 0; 清零
  20.             ts.yp = 0;
  21.             ts.count = 0;
  22.         }

  23.         s3c_adc_start(ts.client, 0, 1 << ts.shift); //启动采样
  24.     } else { //释放触摸屏
  25.         ts.xp = 0;
  26.         ts.yp = 0;
  27.         ts.count = 0;

  28.         input_report_key(ts.input, BTN_TOUCH, 0); //向上报告释放
  29.         input_sync(ts.input); //同步

  30.         writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); //等待中断模式
  31.     }
  32. }
(二)总结过程
首先probe函数执行完后,触摸屏进入等待中断模式。当有触摸屏中断发生时,即检测到被触摸时,进入触摸屏中断处理函数。中断函数判断是否被按下,如果是,调用s3c_adc_start,s3c_adc_start完成一些初始化工作后,调用s3c_adc_try,该函数检测等待处理的客户(client),然后调用s3c_adc_select,该函数会调用客户定义的select回调函数选择客户(client)。s3c_adc_try执行完s3c_adc_select后,又会调用s3c_adc_convert,该函数启动AD转换,转换结束后,触发中断,中断处理函数s3c_adc_irq会首先调用s3c24xx_ts_conversion,累加x,y坐标值及采样次数,如果采样次数未到达多次采样次数,继续采样,否者调用client->select_cb(client, 0)设置定时器和重新进入等待中断模式。

点击(此处)折叠或打开

  1. stylus_irq ->
  2. s3c_adc_start ->
  3. s3c_adc_try ->
  4. s3c_adc_select -> 
  5. client->select_cb(client, 1)
  6. s3c_adc_convert -> 
  7. s3c_adc_irq ->
  8. (client->convert_cb)(client, data0, data1, &client->nr_samples)
  9. (client->select_cb)(client, 0)



阅读(6384) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~