tsc2007驱动

tsc2007是个多功能电阻触摸屏, 具有12bit精度的电阻屏A/D装换器, 压力传感器, 温度传感器等功能.

根据不同的触摸屏尺寸,可以支持8bit和12bit精度的转换.

支持I2C接口, 一个中断输出引脚, 中断输出低电平.

内部结构框图如下:

1.  tsc2007 结构体定义:


   
   
  1. struct tsc2007 {
  2. struct input_dev *input;
  3. char phys[ 32];
  4. struct delayed_work work;
  5. struct i2c_client *client;
  6. u16 model;
  7. u16 x_plate_ohms;
  8. bool pendown;
  9. int irq;
  10. int (*get_pendown_state)( void);
  11. void (*clear_penirq)( void);
  12. };
因为是input设备,所以肯定会有 input_dev 指针。

这里由于中断使用workqueue 下半部机制,所以才会有 delayed_work。

因为是一个i2c设备,所以肯定会有代表i2c设备的i2c_client.

irq 是该设备的中断输出用的中断号。

不知道 model, x_plate_ohms 和pendown是干啥用的。

2. module init:


   
   
  1. static const struct i2c_device_id tsc2007_idtable[] = {
  2. { "tsc2007", 0 },
  3. { }
  4. };
  5. MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
  6. static struct i2c_driver tsc2007_driver = {
  7. .driver = {
  8. .owner = THIS_MODULE,
  9. .name = "tsc2007"
  10. },
  11. .id_table = tsc2007_idtable,
  12. .probe = tsc2007_probe,
  13. .remove = __devexit_p(tsc2007_remove),
  14. };
  15. static int __init tsc2007_init(void)
  16. {
  17. return i2c_add_driver(&tsc2007_driver);
  18. }
  19. static void __exit tsc2007_exit(void)
  20. {
  21. i2c_del_driver(&tsc2007_driver);
  22. }
  23. module_init(tsc2007_init);
  24. module_exit(tsc2007_exit);
这里用 i2c_add_driver() 来添加tsc2007_driver,就相当于把i2c_driver注册进I2C bus,

还会把tsc2007_idtable 添加进 i2c_device id_table 中。

3. tsc2007_probe:


   
   
  1. static int __devinit tsc2007_probe(struct i2c_client *client,
  2. const struct i2c_device_id *id)
  3. {
  4. struct tsc2007 *ts;
  5. struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
  6. struct input_dev *input_dev;
  7. int err;
  8. void __iomem *cpld_base;
  9. unsigned short reg;
  10. cpld_base = ioremap(CPLD_BASE_ADDR, 64);
  11. if(!cpld_base) {
  12. dev_err(&client->dev, "map cpld base failed!\n");
  13. return -ENOMEM;
  14. }
  15. reg = __raw_readw(cpld_base + CNTL_REG1);
  16. reg &= ~TP_POWER;
  17. __raw_writew(reg, cpld_base + CNTL_REG1);
  18. mdelay( 500);
  19. reg |= TP_POWER;
  20. __raw_writew(reg, cpld_base + CNTL_REG1);
  21. if (!pdata) {
  22. dev_err(&client->dev, "platform data is required!\n");
  23. return -EINVAL;
  24. }
  25. if (! i2c_check_functionality(client->adapter,
  26. I2C_FUNC_SMBUS_READ_WORD_DATA))
  27. return -EIO;
  28. ts = kzalloc( sizeof( struct tsc2007), GFP_KERNEL);
  29. input_dev = input_allocate_device();
  30. if (!ts || !input_dev) {
  31. err = -ENOMEM;
  32. goto err_free_mem;
  33. }
  34. ts->client = client;
  35. ts->irq = client->irq;
  36. ts->input = input_dev;
  37. INIT_DELAYED_WORK(&ts->work, tsc2007_work);
  38. ts->model = pdata->model;
  39. ts->x_plate_ohms = pdata->x_plate_ohms;
  40. ts->get_pendown_state = pdata->get_pendown_state;
  41. ts->clear_penirq = pdata->clear_penirq;
  42. i2c_set_clientdata(client, ts);
  43. set_irq_type(ts->irq, IRQF_TRIGGER_FALLING);
  44. if (pdata->init_platform_hw) {
  45. pdata-> init_platform_hw();
  46. }
  47. if ( tsc2007_xfer(ts, PWRDOWN) < 0) {
  48. err = -ENODEV;
  49. goto err_no_dev;
  50. }
  51. snprintf(ts->phys, sizeof(ts->phys),
  52. "%s/input0", dev_name(&client->dev));
  53. input_dev->name = "TSC2007 Touchscreen";
  54. input_dev->phys = ts->phys;
  55. input_dev->id.bustype = BUS_I2C;
  56. input_dev->evbit[ 0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
  57. input_dev->keybit[ BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  58. input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
  59. input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
  60. input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
  61. if (pdata->init_platform_hw)
  62. pdata-> init_platform_hw();
  63. err = request_irq(ts->irq, tsc2007_irq, 0,
  64. client->dev.driver->name, ts);
  65. if (err < 0) {
  66. dev_err(&client->dev, "irq %d busy?\n", ts->irq);
  67. goto err_free_mem;
  68. }
  69. err = input_register_device(input_dev);
  70. if (err)
  71. goto err_free_irq;
  72. /* Prepare for touch readings - power down ADC and enable PENIRQ */
  73. err = tsc2007_xfer(ts, PWRDOWN);
  74. if (err < 0)
  75. goto err_free_irq;
  76. return 0;
  77. err_free_irq:
  78. tsc2007_free_irq(ts);
  79. if (pdata->exit_platform_hw)
  80. pdata-> exit_platform_hw();
  81. err_free_mem:
  82. input_free_device(input_dev);
  83. err_no_dev:
  84. printk( "Can't find TSC2007, I2C time out!\n");
  85. if (pdata->exit_platform_hw) pdata-> exit_platform_hw();
  86. kfree(ts);
  87. return err;
  88. }
这里probe算是最长的函数了,内容也算是标准的input设备初始化过程。

首先因为TSC2007 的上电需要一个300ms的低电平,用来reset 该芯片,

所以这里用cpld的一个引脚来连接VDD,所以就有了一段CPLD拉低TP_POWER引脚的控制代码:


   
   
  1. cpld_base = ioremap(CPLD_BASE_ADDR, 64);
  2. if(!cpld_base) {
  3. dev_err(&client->dev, "map cpld base failed!\n");
  4. return -ENOMEM;
  5. }
  6. reg = __raw_readw(cpld_base + CNTL_REG1);
  7. reg &= ~TP_POWER;
  8. __raw_writew(reg, cpld_base + CNTL_REG1);
  9. mdelay( 500);
  10. reg |= TP_POWER;
  11. __raw_writew(reg, cpld_base + CNTL_REG1);
  12. iounmap(cpld_base);
接着是检查i2c 的功能, 这个功能会在i2c 通讯(i2c_trasfer)的时候使用,所以要先检查是不是有这个功能:


   
   
  1. if (! i2c_check_functionality(client->adapter,
  2. I2C_FUNC_SMBUS_READ_WORD_DATA))
  3. return -EIO;
然后是初始化 tsc2007 设备,给里面的成员赋值,如中断号,input_dev 等:


   
   
  1. ts = kzalloc( sizeof( struct tsc2007), GFP_KERNEL);
  2. input_dev = input_allocate_device();
  3. if (!ts || !input_dev) {
  4. err = -ENOMEM;
  5. goto err_free_mem;
  6. }
  7. ts->client = client;
  8. ts->irq = client->irq;
  9. ts->input = input_dev;
  10. INIT_DELAYED_WORK(&ts->work, tsc2007_work);
  11. ts->model = pdata->model;
  12. ts->x_plate_ohms = pdata->x_plate_ohms;
  13. ts->get_pendown_state = pdata->get_pendown_state;
  14. ts->clear_penirq = pdata->clear_penirq;
  15. i2c_set_clientdata(client, ts);
初始化工作队列:

        INIT_DELAYED_WORK(&ts->work, tsc2007_work);
   
   
设置中断触发方式,这段其实可以在初始化的时候完成,跟gpio初始化一起最好:


   
   
  1. set_irq_type(ts->irq, IRQF_TRIGGER_FALLING);
  2. if (pdata->init_platform_hw) {
  3. pdata-> init_platform_hw();
  4. }

接着是使能芯片的中断功能:


   
   
  1. if ( tsc2007_xfer(ts, PWRDOWN) < 0) {
  2. err = -ENODEV;
  3. goto err_no_dev;
  4. }
接着是input_dev , 标准的触摸屏input设备的初始化:


   
   
  1. input_dev->name = "TSC2007 Touchscreen";
  2. input_dev->phys = ts->phys;
  3. input_dev->id.bustype = BUS_I2C;
  4. input_dev->evbit[ 0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
  5. input_dev->keybit[ BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  6. input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
  7. input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
  8. input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
注册input设备:


   
   
  1. err = input_register_device(input_dev);
  2. if (err)
  3. goto err_free_irq;
注册中断:


   
   
  1. err = request_irq(ts->irq, tsc2007_irq, 0,
  2. client->dev.driver->name, ts);
  3. if (err < 0) {
  4. dev_err(&client->dev, "irq %d busy?\n", ts->irq);
  5. goto err_free_mem;
  6. }
probe之后就等着中断就可以了。

4. 中断服务程序tsc2007_irq() 和下半部工作队列tsc2007_work:


   
   
  1. static irqreturn_t tsc2007_irq(int irq, void *handle)
  2. {
  3. struct tsc2007 *ts = handle;
  4. if (!ts->get_pendown_state || likely(ts-> get_pendown_state())) {
  5. disable_irq_nosync(ts->irq);
  6. schedule_delayed_work(&ts->work,
  7. msecs_to_jiffies(TS_POLL_DELAY));
  8. }
  9. if (ts->clear_penirq)
  10. ts-> clear_penirq();
  11. return IRQ_HANDLED;
  12. }
这个中断服务程序会调用ts-〉work 工作队列,也就是probe中注册的tsc2007_work():

   
   
  1. static void tsc2007_work(struct work_struct *work)
  2. {
  3. struct tsc2007 *ts =
  4. container_of( to_delayed_work(work), struct tsc2007, work);
  5. struct ts_event tc;
  6. u32 rt;
  7. /*
  8. * NOTE: We can't rely on the pressure to determine the pen down
  9. * state, even though this controller has a pressure sensor.
  10. * The pressure value can fluctuate for quite a while after
  11. * lifting the pen and in some cases may not even settle at the
  12. * expected value.
  13. *
  14. * The only safe way to check for the pen up condition is in the
  15. * work function by reading the pen signal state (it's a GPIO
  16. * and IRQ). Unfortunately such callback is not always available,
  17. * in that case we have rely on the pressure anyway.
  18. */
  19. if (ts->get_pendown_state) {
  20. if ( unlikely(!ts-> get_pendown_state())) {
  21. tsc2007_send_up_event(ts);
  22. ts->pendown = false;
  23. goto out;
  24. }
  25. dev_dbg(&ts->client->dev, "pen is still down\n");
  26. }
  27. tsc2007_read_values(ts, &tc);
  28. rt = tsc2007_calculate_pressure(ts, &tc);
  29. if (rt > MAX_12BIT) {
  30. /*
  31. * Sample found inconsistent by debouncing or pressure is
  32. * beyond the maximum. Don't report it to user space,
  33. * repeat at least once more the measurement.
  34. */
  35. dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
  36. goto out;
  37. }
  38. if (rt) {
  39. struct input_dev *input = ts->input;
  40. if (!ts->pendown) {
  41. dev_dbg(&ts->client->dev, "DOWN\n");
  42. input_report_key(input, BTN_TOUCH, 1);
  43. ts->pendown = true;
  44. }
  45. input_report_abs(input, ABS_X, tc.x);
  46. input_report_abs(input, ABS_Y, tc.y);
  47. input_report_abs(input, ABS_PRESSURE, rt);
  48. input_sync(input);
  49. dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
  50. tc.x, tc.y, rt);
  51. } else if (!ts->get_pendown_state && ts->pendown) {
  52. /*
  53. * We don't have callback to check pendown state, so we
  54. * have to assume that since pressure reported is 0 the
  55. * pen was lifted up.
  56. */
  57. tsc2007_send_up_event(ts);
  58. ts->pendown = false;
  59. }
  60. out:
  61. if (ts->pendown)
  62. schedule_delayed_work(&ts->work,
  63. msecs_to_jiffies(TS_POLL_PERIOD));
  64. else
  65. enable_irq(ts->irq);
  66. }
这个工作队列的内容就是该芯片核心内容了。

tsc2007_read_values(), 用来读取X,Y,Z1, Z2等坐标值和压力值。


   
   
  1. static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
  2. {
  3. /* y- still on; turn on only y+ (and ADC) */
  4. tc->y = tsc2007_xfer(tsc, READ_Y);
  5. /* turn y- off, x+ on, then leave in lowpower */
  6. tc->x = tsc2007_xfer(tsc, READ_X);
  7. /* turn y+ off, x- on; we'll use formula #1 */
  8. tc->z1 = tsc2007_xfer(tsc, READ_Z1);
  9. tc->z2 = tsc2007_xfer(tsc, READ_Z2);
  10. /* Prepare for next touch reading - power down ADC, enable PENIRQ */
  11. tsc2007_xfer(tsc, PWRDOWN);
  12. }
tsc2007_calculate_pressure() 用来计算压力值:


   
   
  1. static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
  2. {
  3. u32 rt = 0;
  4. /* range filtering */
  5. if (tc->x == MAX_12BIT)
  6. tc->x = 0;
  7. if ( likely(tc->x && tc->z1)) {
  8. /* compute touch pressure resistance using equation #1 */
  9. rt = tc->z2 - tc->z1;
  10. rt *= tc->x;
  11. rt *= tsc->x_plate_ohms;
  12. rt /= tc->z1;
  13. rt = (rt + 2047) >> 12;
  14. }
  15. return rt;
  16. }
向事件层报告事件:


   
   
  1. if (rt) {
  2. struct input_dev *input = ts->input;
  3. if (!ts->pendown) {
  4. dev_dbg(&ts->client->dev, "DOWN\n");
  5. input_report_key(input, BTN_TOUCH, 1);
  6. ts->pendown = true;
  7. }
  8. input_report_abs(input, ABS_X, tc.x);
  9. input_report_abs(input, ABS_Y, tc.y);
  10. input_report_abs(input, ABS_PRESSURE, rt);
  11. input_sync(input);
  12. dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
  13. tc.x, tc.y, rt);
  14. } else if (!ts->get_pendown_state && ts->pendown) {
  15. /*
  16. * We don't have callback to check pendown state, so we
  17. * have to assume that since pressure reported is 0 the
  18. * pen was lifted up.
  19. */
  20. tsc2007_send_up_event(ts);
  21. ts->pendown = false;
  22. }

tsc2007_send_up_event():


   
   
  1. static void tsc2007_send_up_event(struct tsc2007 *tsc)
  2. {
  3. struct input_dev *input = tsc->input;
  4. dev_dbg(&tsc->client->dev, "UP\n");
  5. input_report_key(input, BTN_TOUCH, 0);
  6. input_report_abs(input, ABS_PRESSURE, 0);
  7. input_sync(input);
  8. }
触摸屏事件结构体的定义,主要就是2个坐标值,2个压力值:


   
   
  1. struct ts_event {
  2. u16 x;
  3. u16 y;
  4. u16 z1, z2;
  5. };

5. i2c 传输:


   
   
  1. static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
  2. {
  3. s32 data;
  4. u16 val;
  5. data = i2c_smbus_read_word_data(tsc->client, cmd);
  6. if (data < 0) {
  7. dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
  8. return data;
  9. }
  10. /* The protocol and raw data format from i2c interface:
  11. * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
  12. * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
  13. */
  14. val = swab16(data) >> 4;
  15. dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
  16. return val;
  17. }



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值