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 #include <linux/slab.h> 18 19 #define MEAS_LEN (8) 20 #define ACCURATE_BIT (12) 21 22 /* touch register */ 23 #define MEAS_EN3 (0x52) 24 25 #define MEAS_TSIX_1 (0x8D) 26 #define MEAS_TSIX_2 (0x8E) 27 #define MEAS_TSIY_1 (0x8F) 28 #define MEAS_TSIY_2 (0x90) 29 #define MEAS_TSIZ1_1 (0x91) 30 #define MEAS_TSIZ1_2 (0x92) 31 #define MEAS_TSIZ2_1 (0x93) 32 #define MEAS_TSIZ2_2 (0x94) 33 34 /* bit definitions of touch */ 35 #define MEAS_PD_EN (1 << 3) 36 #define MEAS_TSIX_EN (1 << 4) 37 #define MEAS_TSIY_EN (1 << 5) 38 #define MEAS_TSIZ1_EN (1 << 6) 39 #define MEAS_TSIZ2_EN (1 << 7) 40 41 struct pm860x_touch { 42 struct input_dev *idev; 43 struct i2c_client *i2c; 44 struct pm860x_chip *chip; 45 int irq; 46 int res_x; /* resistor of Xplate */ 47 }; 48 49 static irqreturn_t pm860x_touch_handler(int irq, void *data) 50 { 51 struct pm860x_touch *touch = data; 52 struct pm860x_chip *chip = touch->chip; 53 unsigned char buf[MEAS_LEN]; 54 int x, y, pen_down; 55 int z1, z2, rt = 0; 56 int ret; 57 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 87 out: 88 return IRQ_HANDLED; 89 } 90 91 static int pm860x_touch_open(struct input_dev *dev) 92 { 93 struct pm860x_touch *touch = input_get_drvdata(dev); 94 int data, ret; 95 96 data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN 97 | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; 98 ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data); 99 if (ret < 0) 100 goto out; 101 return 0; 102 out: 103 return ret; 104 } 105 106 static void pm860x_touch_close(struct input_dev *dev) 107 { 108 struct pm860x_touch *touch = input_get_drvdata(dev); 109 int data; 110 111 data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN 112 | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; 113 pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0); 114 } 115 116 static int __devinit pm860x_touch_probe(struct platform_device *pdev) 117 { 118 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 119 struct pm860x_platform_data *pm860x_pdata = \ 120 pdev->dev.parent->platform_data; 121 struct pm860x_touch_pdata *pdata = NULL; 122 struct pm860x_touch *touch; 123 int irq, ret; 124 125 irq = platform_get_irq(pdev, 0); 126 if (irq < 0) { 127 dev_err(&pdev->dev, "No IRQ resource!\n"); 128 return -EINVAL; 129 } 130 131 if (!pm860x_pdata) { 132 dev_err(&pdev->dev, "platform data is missing\n"); 133 return -EINVAL; 134 } 135 136 pdata = pm860x_pdata->touch; 137 if (!pdata) { 138 dev_err(&pdev->dev, "touchscreen data is missing\n"); 139 return -EINVAL; 140 } 141 142 touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); 143 if (touch == NULL) 144 return -ENOMEM; 145 dev_set_drvdata(&pdev->dev, touch); 146 147 touch->idev = input_allocate_device(); 148 if (touch->idev == NULL) { 149 dev_err(&pdev->dev, "Failed to allocate input device!\n"); 150 ret = -ENOMEM; 151 goto out; 152 } 153 154 touch->idev->name = "88pm860x-touch"; 155 touch->idev->phys = "88pm860x/input0"; 156 touch->idev->id.bustype = BUS_I2C; 157 touch->idev->dev.parent = &pdev->dev; 158 touch->idev->open = pm860x_touch_open; 159 touch->idev->close = pm860x_touch_close; 160 touch->chip = chip; 161 touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; 162 touch->irq = irq + chip->irq_base; 163 touch->res_x = pdata->res_x; 164 input_set_drvdata(touch->idev, touch); 165 166 ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler, 167 IRQF_ONESHOT, "touch", touch); 168 if (ret < 0) 169 goto out_irq; 170 171 __set_bit(EV_ABS, touch->idev->evbit); 172 __set_bit(ABS_X, touch->idev->absbit); 173 __set_bit(ABS_Y, touch->idev->absbit); 174 __set_bit(ABS_PRESSURE, touch->idev->absbit); 175 __set_bit(EV_SYN, touch->idev->evbit); 176 __set_bit(EV_KEY, touch->idev->evbit); 177 __set_bit(BTN_TOUCH, touch->idev->keybit); 178 179 input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0); 180 input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0); 181 input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT, 182 0, 0); 183 184 ret = input_register_device(touch->idev); 185 if (ret < 0) { 186 dev_err(chip->dev, "Failed to register touch!\n"); 187 goto out_rg; 188 } 189 190 platform_set_drvdata(pdev, touch); 191 return 0; 192 out_rg: 193 free_irq(touch->irq, touch); 194 out_irq: 195 input_free_device(touch->idev); 196 out: 197 kfree(touch); 198 return ret; 199 } 200 201 static int __devexit pm860x_touch_remove(struct platform_device *pdev) 202 { 203 struct pm860x_touch *touch = platform_get_drvdata(pdev); 204 205 input_unregister_device(touch->idev); 206 free_irq(touch->irq, touch); 207 platform_set_drvdata(pdev, NULL); 208 kfree(touch); 209 return 0; 210 } 211 212 static struct platform_driver pm860x_touch_driver = { 213 .driver = { 214 .name = "88pm860x-touch", 215 .owner = THIS_MODULE, 216 }, 217 .probe = pm860x_touch_probe, 218 .remove = __devexit_p(pm860x_touch_remove), 219 }; 220 module_platform_driver(pm860x_touch_driver); 221 222 MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x"); 223 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 224 MODULE_LICENSE("GPL"); 225 MODULE_ALIAS("platform:88pm860x-touch"); 226 227