htcpen.c 5.5 KB

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