htcpen.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. * HTC Shift touchscreen driver
  3. *
  4. * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License version 2 as published
  8. * by the Free Software Foundation.
  9. */
  10. #include <linux/errno.h>
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/input.h>
  14. #include <linux/interrupt.h>
  15. #include <linux/io.h>
  16. #include <linux/init.h>
  17. #include <linux/irq.h>
  18. #include <linux/isa.h>
  19. #include <linux/ioport.h>
  20. #include <linux/dmi.h>
  21. MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
  22. MODULE_DESCRIPTION("HTC Shift touchscreen driver");
  23. MODULE_LICENSE("GPL");
  24. #define HTCPEN_PORT_IRQ_CLEAR 0x068
  25. #define HTCPEN_PORT_INIT 0x06c
  26. #define HTCPEN_PORT_INDEX 0x0250
  27. #define HTCPEN_PORT_DATA 0x0251
  28. #define HTCPEN_IRQ 3
  29. #define DEVICE_ENABLE 0xa2
  30. #define DEVICE_DISABLE 0xa3
  31. #define X_INDEX 3
  32. #define Y_INDEX 5
  33. #define TOUCH_INDEX 0xb
  34. #define LSB_XY_INDEX 0xc
  35. #define X_AXIS_MAX 2040
  36. #define Y_AXIS_MAX 2040
  37. static bool invert_x;
  38. module_param(invert_x, bool, 0644);
  39. MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
  40. static bool invert_y;
  41. module_param(invert_y, bool, 0644);
  42. MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
  43. static irqreturn_t htcpen_interrupt(int irq, void *handle)
  44. {
  45. struct input_dev *htcpen_dev = handle;
  46. unsigned short x, y, xy;
  47. /* 0 = press; 1 = release */
  48. outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
  49. if (inb_p(HTCPEN_PORT_DATA)) {
  50. input_report_key(htcpen_dev, BTN_TOUCH, 0);
  51. } else {
  52. outb_p(X_INDEX, HTCPEN_PORT_INDEX);
  53. x = inb_p(HTCPEN_PORT_DATA);
  54. outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
  55. y = inb_p(HTCPEN_PORT_DATA);
  56. outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
  57. xy = inb_p(HTCPEN_PORT_DATA);
  58. /* get high resolution value of X and Y using LSB */
  59. x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
  60. y = (y * 8) + (xy & 0xf);
  61. if (invert_x)
  62. x = X_AXIS_MAX - x;
  63. if (invert_y)
  64. y = Y_AXIS_MAX - y;
  65. if (x != X_AXIS_MAX && x != 0) {
  66. input_report_key(htcpen_dev, BTN_TOUCH, 1);
  67. input_report_abs(htcpen_dev, ABS_X, x);
  68. input_report_abs(htcpen_dev, ABS_Y, y);
  69. }
  70. }
  71. input_sync(htcpen_dev);
  72. inb_p(HTCPEN_PORT_IRQ_CLEAR);
  73. return IRQ_HANDLED;
  74. }
  75. static int htcpen_open(struct input_dev *dev)
  76. {
  77. outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
  78. return 0;
  79. }
  80. static void htcpen_close(struct input_dev *dev)
  81. {
  82. outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
  83. synchronize_irq(HTCPEN_IRQ);
  84. }
  85. static int htcpen_isa_probe(struct device *dev, unsigned int id)
  86. {
  87. struct input_dev *htcpen_dev;
  88. int err = -EBUSY;
  89. if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
  90. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  91. HTCPEN_PORT_IRQ_CLEAR);
  92. goto request_region1_failed;
  93. }
  94. if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
  95. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  96. HTCPEN_PORT_INIT);
  97. goto request_region2_failed;
  98. }
  99. if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
  100. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  101. HTCPEN_PORT_INDEX);
  102. goto request_region3_failed;
  103. }
  104. htcpen_dev = input_allocate_device();
  105. if (!htcpen_dev) {
  106. printk(KERN_ERR "htcpen: can't allocate device\n");
  107. err = -ENOMEM;
  108. goto input_alloc_failed;
  109. }
  110. htcpen_dev->name = "HTC Shift EC TouchScreen";
  111. htcpen_dev->id.bustype = BUS_ISA;
  112. htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
  113. htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  114. input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
  115. input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
  116. htcpen_dev->open = htcpen_open;
  117. htcpen_dev->close = htcpen_close;
  118. err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
  119. htcpen_dev);
  120. if (err) {
  121. printk(KERN_ERR "htcpen: irq busy\n");
  122. goto request_irq_failed;
  123. }
  124. inb_p(HTCPEN_PORT_IRQ_CLEAR);
  125. err = input_register_device(htcpen_dev);
  126. if (err)
  127. goto input_register_failed;
  128. dev_set_drvdata(dev, htcpen_dev);
  129. return 0;
  130. input_register_failed:
  131. free_irq(HTCPEN_IRQ, htcpen_dev);
  132. request_irq_failed:
  133. input_free_device(htcpen_dev);
  134. input_alloc_failed:
  135. release_region(HTCPEN_PORT_INDEX, 2);
  136. request_region3_failed:
  137. release_region(HTCPEN_PORT_INIT, 1);
  138. request_region2_failed:
  139. release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
  140. request_region1_failed:
  141. return err;
  142. }
  143. static int htcpen_isa_remove(struct device *dev, unsigned int id)
  144. {
  145. struct input_dev *htcpen_dev = dev_get_drvdata(dev);
  146. input_unregister_device(htcpen_dev);
  147. free_irq(HTCPEN_IRQ, htcpen_dev);
  148. release_region(HTCPEN_PORT_INDEX, 2);
  149. release_region(HTCPEN_PORT_INIT, 1);
  150. release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
  151. return 0;
  152. }
  153. #ifdef CONFIG_PM
  154. static int htcpen_isa_suspend(struct device *dev, unsigned int n,
  155. pm_message_t state)
  156. {
  157. outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
  158. return 0;
  159. }
  160. static int htcpen_isa_resume(struct device *dev, unsigned int n)
  161. {
  162. outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
  163. return 0;
  164. }
  165. #endif
  166. static struct isa_driver htcpen_isa_driver = {
  167. .probe = htcpen_isa_probe,
  168. .remove = htcpen_isa_remove,
  169. #ifdef CONFIG_PM
  170. .suspend = htcpen_isa_suspend,
  171. .resume = htcpen_isa_resume,
  172. #endif
  173. .driver = {
  174. .owner = THIS_MODULE,
  175. .name = "htcpen",
  176. }
  177. };
  178. static struct dmi_system_id htcshift_dmi_table[] __initdata = {
  179. {
  180. .ident = "Shift",
  181. .matches = {
  182. DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
  183. DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
  184. },
  185. },
  186. { }
  187. };
  188. MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
  189. static int __init htcpen_isa_init(void)
  190. {
  191. if (!dmi_check_system(htcshift_dmi_table))
  192. return -ENODEV;
  193. return isa_register_driver(&htcpen_isa_driver, 1);
  194. }
  195. static void __exit htcpen_isa_exit(void)
  196. {
  197. isa_unregister_driver(&htcpen_isa_driver);
  198. }
  199. module_init(htcpen_isa_init);
  200. module_exit(htcpen_isa_exit);