mfd: Fix WM8994 error handling
[linux-2.6-block.git] / drivers / input / touchscreen / 88pm860x-ts.c
CommitLineData
866a98ae
HZ
1/*
2 * Touchscreen driver for Marvell 88PM860x
3 *
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/i2c.h>
15#include <linux/input.h>
16#include <linux/mfd/88pm860x.h>
17
18#define MEAS_LEN (8)
19#define ACCURATE_BIT (12)
20
21/* touch register */
22#define MEAS_EN3 (0x52)
23
24#define MEAS_TSIX_1 (0x8D)
25#define MEAS_TSIX_2 (0x8E)
26#define MEAS_TSIY_1 (0x8F)
27#define MEAS_TSIY_2 (0x90)
28#define MEAS_TSIZ1_1 (0x91)
29#define MEAS_TSIZ1_2 (0x92)
30#define MEAS_TSIZ2_1 (0x93)
31#define MEAS_TSIZ2_2 (0x94)
32
33/* bit definitions of touch */
34#define MEAS_PD_EN (1 << 3)
35#define MEAS_TSIX_EN (1 << 4)
36#define MEAS_TSIY_EN (1 << 5)
37#define MEAS_TSIZ1_EN (1 << 6)
38#define MEAS_TSIZ2_EN (1 << 7)
39
40struct pm860x_touch {
41 struct input_dev *idev;
42 struct i2c_client *i2c;
43 struct pm860x_chip *chip;
44 int irq;
45 int res_x; /* resistor of Xplate */
46};
47
48static irqreturn_t pm860x_touch_handler(int irq, void *data)
49{
50 struct pm860x_touch *touch = data;
51 struct pm860x_chip *chip = touch->chip;
52 unsigned char buf[MEAS_LEN];
53 int x, y, pen_down;
54 int z1, z2, rt = 0;
55 int ret;
56
57 pm860x_mask_irq(chip, irq);
58 ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
59 if (ret < 0)
60 goto out;
61
62 pen_down = buf[1] & (1 << 6);
63 x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
64 y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
65 z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
66 z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
67
68 if (pen_down) {
69 if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
70 rt = z2 / z1 - 1;
71 rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
72 dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
73 z1, z2, rt);
74 }
75 input_report_abs(touch->idev, ABS_X, x);
76 input_report_abs(touch->idev, ABS_Y, y);
77 input_report_abs(touch->idev, ABS_PRESSURE, rt);
78 input_report_key(touch->idev, BTN_TOUCH, 1);
79 dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
80 } else {
81 input_report_abs(touch->idev, ABS_PRESSURE, 0);
82 input_report_key(touch->idev, BTN_TOUCH, 0);
83 dev_dbg(chip->dev, "pen release\n");
84 }
85 input_sync(touch->idev);
86 pm860x_unmask_irq(chip, irq);
87
88out:
89 return IRQ_HANDLED;
90}
91
92static int pm860x_touch_open(struct input_dev *dev)
93{
94 struct pm860x_touch *touch = input_get_drvdata(dev);
95 struct pm860x_chip *chip = touch->chip;
96 int data, ret;
97
98 data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
99 | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
100 ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
101 if (ret < 0)
102 goto out;
103 pm860x_unmask_irq(chip, touch->irq);
104 return 0;
105out:
106 return ret;
107}
108
109static void pm860x_touch_close(struct input_dev *dev)
110{
111 struct pm860x_touch *touch = input_get_drvdata(dev);
112 struct pm860x_chip *chip = touch->chip;
113 int data;
114
115 data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
116 | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
117 pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
118 pm860x_mask_irq(chip, touch->irq);
119}
120
121static int __devinit pm860x_touch_probe(struct platform_device *pdev)
122{
123 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
124 struct pm860x_platform_data *pm860x_pdata = \
125 pdev->dev.parent->platform_data;
126 struct pm860x_touch_pdata *pdata = NULL;
127 struct pm860x_touch *touch;
128 int irq, ret;
129
130 irq = platform_get_irq(pdev, 0);
131 if (irq < 0) {
132 dev_err(&pdev->dev, "No IRQ resource!\n");
133 return -EINVAL;
134 }
135
136 if (!pm860x_pdata) {
137 dev_err(&pdev->dev, "platform data is missing\n");
138 return -EINVAL;
139 }
140
141 pdata = pm860x_pdata->touch;
142 if (!pdata) {
143 dev_err(&pdev->dev, "touchscreen data is missing\n");
144 return -EINVAL;
145 }
146
147 touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL);
148 if (touch == NULL)
149 return -ENOMEM;
150 dev_set_drvdata(&pdev->dev, touch);
151
152 touch->idev = input_allocate_device();
153 if (touch->idev == NULL) {
154 dev_err(&pdev->dev, "Failed to allocate input device!\n");
155 ret = -ENOMEM;
156 goto out;
157 }
158
159 touch->idev->name = "88pm860x-touch";
160 touch->idev->phys = "88pm860x/input0";
161 touch->idev->id.bustype = BUS_I2C;
162 touch->idev->dev.parent = &pdev->dev;
163 touch->idev->open = pm860x_touch_open;
164 touch->idev->close = pm860x_touch_close;
165 touch->chip = chip;
166 touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
167 touch->irq = irq;
168 touch->res_x = pdata->res_x;
169 input_set_drvdata(touch->idev, touch);
170
171 ret = pm860x_request_irq(chip, irq, pm860x_touch_handler, touch);
172 if (ret < 0)
173 goto out_irq;
174
175 __set_bit(EV_ABS, touch->idev->evbit);
176 __set_bit(ABS_X, touch->idev->absbit);
177 __set_bit(ABS_Y, touch->idev->absbit);
178 __set_bit(ABS_PRESSURE, touch->idev->absbit);
179 __set_bit(EV_SYN, touch->idev->evbit);
180 __set_bit(EV_KEY, touch->idev->evbit);
181 __set_bit(BTN_TOUCH, touch->idev->keybit);
182
183 input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
184 input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
185 input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
186 0, 0);
187
188 ret = input_register_device(touch->idev);
189 if (ret < 0) {
190 dev_err(chip->dev, "Failed to register touch!\n");
191 goto out_rg;
192 }
193
194 platform_set_drvdata(pdev, touch);
195 return 0;
196out_rg:
197 pm860x_free_irq(chip, irq);
198out_irq:
199 input_free_device(touch->idev);
200out:
201 kfree(touch);
202 return ret;
203}
204
205static int __devexit pm860x_touch_remove(struct platform_device *pdev)
206{
207 struct pm860x_touch *touch = platform_get_drvdata(pdev);
208
209 input_unregister_device(touch->idev);
210 pm860x_free_irq(touch->chip, touch->irq);
211 platform_set_drvdata(pdev, NULL);
212 kfree(touch);
213 return 0;
214}
215
216static struct platform_driver pm860x_touch_driver = {
217 .driver = {
218 .name = "88pm860x-touch",
219 .owner = THIS_MODULE,
220 },
221 .probe = pm860x_touch_probe,
222 .remove = __devexit_p(pm860x_touch_remove),
223};
224
225static int __init pm860x_touch_init(void)
226{
227 return platform_driver_register(&pm860x_touch_driver);
228}
229module_init(pm860x_touch_init);
230
231static void __exit pm860x_touch_exit(void)
232{
233 platform_driver_unregister(&pm860x_touch_driver);
234}
235module_exit(pm860x_touch_exit);
236
237MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
238MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
239MODULE_LICENSE("GPL");
240MODULE_ALIAS("platform:88pm860x-touch");
241