htcpen.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 int invert_x;
  38. module_param(invert_x, bool, 0644);
  39. MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
  40. static int invert_y;
  41. module_param(invert_y, bool, 0644);
  42. MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
  43. static struct pnp_device_id pnp_ids[] = {
  44. { .id = "PNP0cc0" },
  45. { .id = "" }
  46. };
  47. MODULE_DEVICE_TABLE(pnp, pnp_ids);
  48. static irqreturn_t htcpen_interrupt(int irq, void *handle)
  49. {
  50. struct input_dev *htcpen_dev = handle;
  51. unsigned short x, y, xy;
  52. /* 0 = press; 1 = release */
  53. outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
  54. if (inb_p(HTCPEN_PORT_DATA)) {
  55. input_report_key(htcpen_dev, BTN_TOUCH, 0);
  56. } else {
  57. outb_p(X_INDEX, HTCPEN_PORT_INDEX);
  58. x = inb_p(HTCPEN_PORT_DATA);
  59. outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
  60. y = inb_p(HTCPEN_PORT_DATA);
  61. outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
  62. xy = inb_p(HTCPEN_PORT_DATA);
  63. /* get high resolution value of X and Y using LSB */
  64. x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
  65. y = (y * 8) + (xy & 0xf);
  66. if (invert_x)
  67. x = X_AXIS_MAX - x;
  68. if (invert_y)
  69. y = Y_AXIS_MAX - y;
  70. if (x != X_AXIS_MAX && x != 0) {
  71. input_report_key(htcpen_dev, BTN_TOUCH, 1);
  72. input_report_abs(htcpen_dev, ABS_X, x);
  73. input_report_abs(htcpen_dev, ABS_Y, y);
  74. }
  75. }
  76. input_sync(htcpen_dev);
  77. inb_p(HTCPEN_PORT_IRQ_CLEAR);
  78. return IRQ_HANDLED;
  79. }
  80. static int htcpen_open(struct input_dev *dev)
  81. {
  82. outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
  83. return 0;
  84. }
  85. static void htcpen_close(struct input_dev *dev)
  86. {
  87. outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
  88. synchronize_irq(HTCPEN_IRQ);
  89. }
  90. static int __devinit htcpen_isa_probe(struct device *dev, unsigned int id)
  91. {
  92. struct input_dev *htcpen_dev;
  93. int err = -EBUSY;
  94. if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
  95. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  96. HTCPEN_PORT_IRQ_CLEAR);
  97. goto request_region1_failed;
  98. }
  99. if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
  100. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  101. HTCPEN_PORT_INIT);
  102. goto request_region2_failed;
  103. }
  104. if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
  105. printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
  106. HTCPEN_PORT_INDEX);
  107. goto request_region3_failed;
  108. }
  109. htcpen_dev = input_allocate_device();
  110. if (!htcpen_dev) {
  111. printk(KERN_ERR "htcpen: can't allocate device\n");
  112. err = -ENOMEM;
  113. goto input_alloc_failed;
  114. }
  115. htcpen_dev->name = "HTC Shift EC TouchScreen";
  116. htcpen_dev->id.bustype = BUS_ISA;
  117. htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
  118. htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  119. input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
  120. input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
  121. htcpen_dev->open = htcpen_open;
  122. htcpen_dev->close = htcpen_close;
  123. err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
  124. htcpen_dev);
  125. if (err) {
  126. printk(KERN_ERR "htcpen: irq busy\n");
  127. goto request_irq_failed;
  128. }
  129. inb_p(HTCPEN_PORT_IRQ_CLEAR);
  130. err = input_register_device(htcpen_dev);
  131. if (err)
  132. goto input_register_failed;
  133. dev_set_drvdata(dev, htcpen_dev);
  134. return 0;
  135. input_register_failed:
  136. free_irq(HTCPEN_IRQ, htcpen_dev);
  137. request_irq_failed:
  138. input_free_device(htcpen_dev);
  139. input_alloc_failed:
  140. release_region(HTCPEN_PORT_INDEX, 2);
  141. request_region3_failed:
  142. release_region(HTCPEN_PORT_INIT, 1);
  143. request_region2_failed:
  144. release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
  145. request_region1_failed:
  146. return err;
  147. }
  148. static int __devexit htcpen_isa_remove(struct device *dev, unsigned int id)
  149. {
  150. struct input_dev *htcpen_dev = dev_get_drvdata(dev);
  151. input_unregister_device(htcpen_dev);
  152. free_irq(HTCPEN_IRQ, htcpen_dev);
  153. release_region(HTCPEN_PORT_INDEX, 2);
  154. release_region(HTCPEN_PORT_INIT, 1);
  155. release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
  156. dev_set_drvdata(dev, NULL);
  157. return 0;
  158. }
  159. #ifdef CONFIG_PM
  160. static int htcpen_isa_suspend(struct device *dev, unsigned int n,
  161. pm_message_t state)
  162. {
  163. outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
  164. return 0;
  165. }
  166. static int htcpen_isa_resume(struct device *dev, unsigned int n)
  167. {
  168. outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
  169. return 0;
  170. }
  171. #endif
  172. static struct isa_driver htcpen_isa_driver = {
  173. .probe = htcpen_isa_probe,
  174. .remove = __devexit_p(htcpen_isa_remove),
  175. #ifdef CONFIG_PM
  176. .suspend = htcpen_isa_suspend,
  177. .resume = htcpen_isa_resume,
  178. #endif
  179. .driver = {
  180. .owner = THIS_MODULE,
  181. .name = "htcpen",
  182. }
  183. };
  184. static struct dmi_system_id __initdata htcshift_dmi_table[] = {
  185. {
  186. .ident = "Shift",
  187. .matches = {
  188. DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
  189. DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
  190. },
  191. },
  192. { }
  193. };
  194. static int __init htcpen_isa_init(void)
  195. {
  196. if (!dmi_check_system(htcshift_dmi_table))
  197. return -ENODEV;
  198. return isa_register_driver(&htcpen_isa_driver, 1);
  199. }
  200. static void __exit htcpen_isa_exit(void)
  201. {
  202. isa_unregister_driver(&htcpen_isa_driver);
  203. }
  204. module_init(htcpen_isa_init);
  205. module_exit(htcpen_isa_exit);