1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2866a98aeSHaojian Zhuang /* 3866a98aeSHaojian Zhuang * Touchscreen driver for Marvell 88PM860x 4866a98aeSHaojian Zhuang * 5866a98aeSHaojian Zhuang * Copyright (C) 2009 Marvell International Ltd. 6866a98aeSHaojian Zhuang * Haojian Zhuang <haojian.zhuang@marvell.com> 7866a98aeSHaojian Zhuang */ 8866a98aeSHaojian Zhuang #include <linux/kernel.h> 9866a98aeSHaojian Zhuang #include <linux/module.h> 102e57d567SHaojian Zhuang #include <linux/of.h> 11866a98aeSHaojian Zhuang #include <linux/platform_device.h> 12866a98aeSHaojian Zhuang #include <linux/i2c.h> 13866a98aeSHaojian Zhuang #include <linux/input.h> 14866a98aeSHaojian Zhuang #include <linux/mfd/88pm860x.h> 155a0e3ad6STejun Heo #include <linux/slab.h> 165ac66de5SHimangi Saraogi #include <linux/device.h> 17866a98aeSHaojian Zhuang 18866a98aeSHaojian Zhuang #define MEAS_LEN (8) 19866a98aeSHaojian Zhuang #define ACCURATE_BIT (12) 20866a98aeSHaojian Zhuang 21866a98aeSHaojian Zhuang /* touch register */ 22866a98aeSHaojian Zhuang #define MEAS_EN3 (0x52) 23866a98aeSHaojian Zhuang 24866a98aeSHaojian Zhuang #define MEAS_TSIX_1 (0x8D) 25866a98aeSHaojian Zhuang #define MEAS_TSIX_2 (0x8E) 26866a98aeSHaojian Zhuang #define MEAS_TSIY_1 (0x8F) 27866a98aeSHaojian Zhuang #define MEAS_TSIY_2 (0x90) 28866a98aeSHaojian Zhuang #define MEAS_TSIZ1_1 (0x91) 29866a98aeSHaojian Zhuang #define MEAS_TSIZ1_2 (0x92) 30866a98aeSHaojian Zhuang #define MEAS_TSIZ2_1 (0x93) 31866a98aeSHaojian Zhuang #define MEAS_TSIZ2_2 (0x94) 32866a98aeSHaojian Zhuang 33866a98aeSHaojian Zhuang /* bit definitions of touch */ 34866a98aeSHaojian Zhuang #define MEAS_PD_EN (1 << 3) 35866a98aeSHaojian Zhuang #define MEAS_TSIX_EN (1 << 4) 36866a98aeSHaojian Zhuang #define MEAS_TSIY_EN (1 << 5) 37866a98aeSHaojian Zhuang #define MEAS_TSIZ1_EN (1 << 6) 38866a98aeSHaojian Zhuang #define MEAS_TSIZ2_EN (1 << 7) 39866a98aeSHaojian Zhuang 40866a98aeSHaojian Zhuang struct pm860x_touch { 41866a98aeSHaojian Zhuang struct input_dev *idev; 42866a98aeSHaojian Zhuang struct i2c_client *i2c; 43866a98aeSHaojian Zhuang struct pm860x_chip *chip; 44866a98aeSHaojian Zhuang int irq; 45866a98aeSHaojian Zhuang int res_x; /* resistor of Xplate */ 46866a98aeSHaojian Zhuang }; 47866a98aeSHaojian Zhuang 48866a98aeSHaojian Zhuang static irqreturn_t pm860x_touch_handler(int irq, void *data) 49866a98aeSHaojian Zhuang { 50866a98aeSHaojian Zhuang struct pm860x_touch *touch = data; 51866a98aeSHaojian Zhuang struct pm860x_chip *chip = touch->chip; 52866a98aeSHaojian Zhuang unsigned char buf[MEAS_LEN]; 53866a98aeSHaojian Zhuang int x, y, pen_down; 54866a98aeSHaojian Zhuang int z1, z2, rt = 0; 55866a98aeSHaojian Zhuang int ret; 56866a98aeSHaojian Zhuang 57866a98aeSHaojian Zhuang ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf); 58866a98aeSHaojian Zhuang if (ret < 0) 59866a98aeSHaojian Zhuang goto out; 60866a98aeSHaojian Zhuang 61866a98aeSHaojian Zhuang pen_down = buf[1] & (1 << 6); 62866a98aeSHaojian Zhuang x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F); 63866a98aeSHaojian Zhuang y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F); 64866a98aeSHaojian Zhuang z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F); 65866a98aeSHaojian Zhuang z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F); 66866a98aeSHaojian Zhuang 67866a98aeSHaojian Zhuang if (pen_down) { 68866a98aeSHaojian Zhuang if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) { 69866a98aeSHaojian Zhuang rt = z2 / z1 - 1; 70866a98aeSHaojian Zhuang rt = (rt * touch->res_x * x) >> ACCURATE_BIT; 71866a98aeSHaojian Zhuang dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n", 72866a98aeSHaojian Zhuang z1, z2, rt); 73866a98aeSHaojian Zhuang } 74866a98aeSHaojian Zhuang input_report_abs(touch->idev, ABS_X, x); 75866a98aeSHaojian Zhuang input_report_abs(touch->idev, ABS_Y, y); 76866a98aeSHaojian Zhuang input_report_abs(touch->idev, ABS_PRESSURE, rt); 77866a98aeSHaojian Zhuang input_report_key(touch->idev, BTN_TOUCH, 1); 78866a98aeSHaojian Zhuang dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y); 79866a98aeSHaojian Zhuang } else { 80866a98aeSHaojian Zhuang input_report_abs(touch->idev, ABS_PRESSURE, 0); 81866a98aeSHaojian Zhuang input_report_key(touch->idev, BTN_TOUCH, 0); 82866a98aeSHaojian Zhuang dev_dbg(chip->dev, "pen release\n"); 83866a98aeSHaojian Zhuang } 84866a98aeSHaojian Zhuang input_sync(touch->idev); 85866a98aeSHaojian Zhuang 86866a98aeSHaojian Zhuang out: 87866a98aeSHaojian Zhuang return IRQ_HANDLED; 88866a98aeSHaojian Zhuang } 89866a98aeSHaojian Zhuang 90866a98aeSHaojian Zhuang static int pm860x_touch_open(struct input_dev *dev) 91866a98aeSHaojian Zhuang { 92866a98aeSHaojian Zhuang struct pm860x_touch *touch = input_get_drvdata(dev); 93866a98aeSHaojian Zhuang int data, ret; 94866a98aeSHaojian Zhuang 95866a98aeSHaojian Zhuang data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN 96866a98aeSHaojian Zhuang | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; 97866a98aeSHaojian Zhuang ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data); 98866a98aeSHaojian Zhuang if (ret < 0) 99866a98aeSHaojian Zhuang goto out; 100866a98aeSHaojian Zhuang return 0; 101866a98aeSHaojian Zhuang out: 102866a98aeSHaojian Zhuang return ret; 103866a98aeSHaojian Zhuang } 104866a98aeSHaojian Zhuang 105866a98aeSHaojian Zhuang static void pm860x_touch_close(struct input_dev *dev) 106866a98aeSHaojian Zhuang { 107866a98aeSHaojian Zhuang struct pm860x_touch *touch = input_get_drvdata(dev); 108866a98aeSHaojian Zhuang int data; 109866a98aeSHaojian Zhuang 110866a98aeSHaojian Zhuang data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN 111866a98aeSHaojian Zhuang | MEAS_TSIZ1_EN | MEAS_TSIZ2_EN; 112866a98aeSHaojian Zhuang pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0); 113866a98aeSHaojian Zhuang } 114866a98aeSHaojian Zhuang 1152e57d567SHaojian Zhuang #ifdef CONFIG_OF 1165298cc4cSBill Pemberton static int pm860x_touch_dt_init(struct platform_device *pdev, 117fe1d38e8SHaojian Zhuang struct pm860x_chip *chip, 1182e57d567SHaojian Zhuang int *res_x) 1192e57d567SHaojian Zhuang { 1202e57d567SHaojian Zhuang struct device_node *np = pdev->dev.parent->of_node; 121fe1d38e8SHaojian Zhuang struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 122fe1d38e8SHaojian Zhuang : chip->companion; 123fe1d38e8SHaojian Zhuang int data, n, ret; 1242e57d567SHaojian Zhuang if (!np) 1252e57d567SHaojian Zhuang return -ENODEV; 126906bf7daSJohan Hovold np = of_get_child_by_name(np, "touch"); 1272e57d567SHaojian Zhuang if (!np) { 1282e57d567SHaojian Zhuang dev_err(&pdev->dev, "Can't find touch node\n"); 1292e57d567SHaojian Zhuang return -EINVAL; 1302e57d567SHaojian Zhuang } 131fe1d38e8SHaojian Zhuang /* set GPADC MISC1 register */ 132fe1d38e8SHaojian Zhuang data = 0; 133fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n)) 134fe1d38e8SHaojian Zhuang data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK; 135fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n)) 136fe1d38e8SHaojian Zhuang data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK; 137fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n)) 138fe1d38e8SHaojian Zhuang data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK; 139fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n)) 140fe1d38e8SHaojian Zhuang data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK; 141fe1d38e8SHaojian Zhuang if (data) { 142fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); 143fe1d38e8SHaojian Zhuang if (ret < 0) 144906bf7daSJohan Hovold goto err_put_node; 145fe1d38e8SHaojian Zhuang } 146fe1d38e8SHaojian Zhuang /* set tsi prebias time */ 147fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) { 148fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); 149fe1d38e8SHaojian Zhuang if (ret < 0) 150906bf7daSJohan Hovold goto err_put_node; 151fe1d38e8SHaojian Zhuang } 152fe1d38e8SHaojian Zhuang /* set prebias & prechg time of pen detect */ 153fe1d38e8SHaojian Zhuang data = 0; 154fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n)) 155fe1d38e8SHaojian Zhuang data |= n & PM8607_PD_PREBIAS_MASK; 156fe1d38e8SHaojian Zhuang if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n)) 157fe1d38e8SHaojian Zhuang data |= n & PM8607_PD_PRECHG_MASK; 158fe1d38e8SHaojian Zhuang if (data) { 159fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); 160fe1d38e8SHaojian Zhuang if (ret < 0) 161906bf7daSJohan Hovold goto err_put_node; 162fe1d38e8SHaojian Zhuang } 1632e57d567SHaojian Zhuang of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); 164906bf7daSJohan Hovold 165906bf7daSJohan Hovold of_node_put(np); 166906bf7daSJohan Hovold 1672e57d567SHaojian Zhuang return 0; 168906bf7daSJohan Hovold 169906bf7daSJohan Hovold err_put_node: 170906bf7daSJohan Hovold of_node_put(np); 171906bf7daSJohan Hovold 172906bf7daSJohan Hovold return -EINVAL; 1732e57d567SHaojian Zhuang } 1742e57d567SHaojian Zhuang #else 175fe1d38e8SHaojian Zhuang #define pm860x_touch_dt_init(x, y, z) (-1) 1762e57d567SHaojian Zhuang #endif 1772e57d567SHaojian Zhuang 1785298cc4cSBill Pemberton static int pm860x_touch_probe(struct platform_device *pdev) 179866a98aeSHaojian Zhuang { 180866a98aeSHaojian Zhuang struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 181c838cb3dSJingoo Han struct pm860x_touch_pdata *pdata = dev_get_platdata(&pdev->dev); 182866a98aeSHaojian Zhuang struct pm860x_touch *touch; 183fe1d38e8SHaojian Zhuang struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 184fe1d38e8SHaojian Zhuang : chip->companion; 185fe1d38e8SHaojian Zhuang int irq, ret, res_x = 0, data = 0; 186866a98aeSHaojian Zhuang 187866a98aeSHaojian Zhuang irq = platform_get_irq(pdev, 0); 188*0bec8b7eSStephen Boyd if (irq < 0) 189866a98aeSHaojian Zhuang return -EINVAL; 190866a98aeSHaojian Zhuang 191fe1d38e8SHaojian Zhuang if (pm860x_touch_dt_init(pdev, chip, &res_x)) { 192fe1d38e8SHaojian Zhuang if (pdata) { 193fe1d38e8SHaojian Zhuang /* set GPADC MISC1 register */ 194fe1d38e8SHaojian Zhuang data = 0; 195fe1d38e8SHaojian Zhuang data |= (pdata->gpadc_prebias << 1) 196fe1d38e8SHaojian Zhuang & PM8607_GPADC_PREBIAS_MASK; 197fe1d38e8SHaojian Zhuang data |= (pdata->slot_cycle << 3) 198fe1d38e8SHaojian Zhuang & PM8607_GPADC_SLOT_CYCLE_MASK; 199fe1d38e8SHaojian Zhuang data |= (pdata->off_scale << 5) 200fe1d38e8SHaojian Zhuang & PM8607_GPADC_OFF_SCALE_MASK; 201fe1d38e8SHaojian Zhuang data |= (pdata->sw_cal << 7) 202fe1d38e8SHaojian Zhuang & PM8607_GPADC_SW_CAL_MASK; 203fe1d38e8SHaojian Zhuang if (data) { 204fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, 205fe1d38e8SHaojian Zhuang PM8607_GPADC_MISC1, data); 206fe1d38e8SHaojian Zhuang if (ret < 0) 207fe1d38e8SHaojian Zhuang return -EINVAL; 208fe1d38e8SHaojian Zhuang } 209fe1d38e8SHaojian Zhuang /* set tsi prebias time */ 210fe1d38e8SHaojian Zhuang if (pdata->tsi_prebias) { 211fe1d38e8SHaojian Zhuang data = pdata->tsi_prebias; 212fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, 213fe1d38e8SHaojian Zhuang PM8607_TSI_PREBIAS, data); 214fe1d38e8SHaojian Zhuang if (ret < 0) 215fe1d38e8SHaojian Zhuang return -EINVAL; 216fe1d38e8SHaojian Zhuang } 217fe1d38e8SHaojian Zhuang /* set prebias & prechg time of pen detect */ 218fe1d38e8SHaojian Zhuang data = 0; 219fe1d38e8SHaojian Zhuang data |= pdata->pen_prebias 220fe1d38e8SHaojian Zhuang & PM8607_PD_PREBIAS_MASK; 221fe1d38e8SHaojian Zhuang data |= (pdata->pen_prechg << 5) 222fe1d38e8SHaojian Zhuang & PM8607_PD_PRECHG_MASK; 223fe1d38e8SHaojian Zhuang if (data) { 224fe1d38e8SHaojian Zhuang ret = pm860x_reg_write(i2c, 225fe1d38e8SHaojian Zhuang PM8607_PD_PREBIAS, data); 226fe1d38e8SHaojian Zhuang if (ret < 0) 227fe1d38e8SHaojian Zhuang return -EINVAL; 228fe1d38e8SHaojian Zhuang } 2292e57d567SHaojian Zhuang res_x = pdata->res_x; 230fe1d38e8SHaojian Zhuang } else { 2312e57d567SHaojian Zhuang dev_err(&pdev->dev, "failed to get platform data\n"); 232866a98aeSHaojian Zhuang return -EINVAL; 233866a98aeSHaojian Zhuang } 234866a98aeSHaojian Zhuang } 235fe1d38e8SHaojian Zhuang /* enable GPADC */ 236fe1d38e8SHaojian Zhuang ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN, 237fe1d38e8SHaojian Zhuang PM8607_GPADC_EN); 238fe1d38e8SHaojian Zhuang if (ret) 239fe1d38e8SHaojian Zhuang return ret; 240866a98aeSHaojian Zhuang 2415ac66de5SHimangi Saraogi touch = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_touch), 2425ac66de5SHimangi Saraogi GFP_KERNEL); 2435ac66de5SHimangi Saraogi if (!touch) 244866a98aeSHaojian Zhuang return -ENOMEM; 2455ac66de5SHimangi Saraogi 2465ac66de5SHimangi Saraogi touch->idev = devm_input_allocate_device(&pdev->dev); 2475ac66de5SHimangi Saraogi if (!touch->idev) { 248866a98aeSHaojian Zhuang dev_err(&pdev->dev, "Failed to allocate input device!\n"); 2495ac66de5SHimangi Saraogi return -ENOMEM; 250866a98aeSHaojian Zhuang } 251866a98aeSHaojian Zhuang 252866a98aeSHaojian Zhuang touch->idev->name = "88pm860x-touch"; 253866a98aeSHaojian Zhuang touch->idev->phys = "88pm860x/input0"; 254866a98aeSHaojian Zhuang touch->idev->id.bustype = BUS_I2C; 255866a98aeSHaojian Zhuang touch->idev->dev.parent = &pdev->dev; 256866a98aeSHaojian Zhuang touch->idev->open = pm860x_touch_open; 257866a98aeSHaojian Zhuang touch->idev->close = pm860x_touch_close; 258866a98aeSHaojian Zhuang touch->chip = chip; 259fe1d38e8SHaojian Zhuang touch->i2c = i2c; 2602e57d567SHaojian Zhuang touch->irq = irq; 2612e57d567SHaojian Zhuang touch->res_x = res_x; 262866a98aeSHaojian Zhuang input_set_drvdata(touch->idev, touch); 263866a98aeSHaojian Zhuang 2645ac66de5SHimangi Saraogi ret = devm_request_threaded_irq(&pdev->dev, touch->irq, NULL, 2655ac66de5SHimangi Saraogi pm860x_touch_handler, IRQF_ONESHOT, 2665ac66de5SHimangi Saraogi "touch", touch); 267866a98aeSHaojian Zhuang if (ret < 0) 2685ac66de5SHimangi Saraogi return ret; 269866a98aeSHaojian Zhuang 270866a98aeSHaojian Zhuang __set_bit(EV_ABS, touch->idev->evbit); 271866a98aeSHaojian Zhuang __set_bit(ABS_X, touch->idev->absbit); 272866a98aeSHaojian Zhuang __set_bit(ABS_Y, touch->idev->absbit); 273866a98aeSHaojian Zhuang __set_bit(ABS_PRESSURE, touch->idev->absbit); 274866a98aeSHaojian Zhuang __set_bit(EV_SYN, touch->idev->evbit); 275866a98aeSHaojian Zhuang __set_bit(EV_KEY, touch->idev->evbit); 276866a98aeSHaojian Zhuang __set_bit(BTN_TOUCH, touch->idev->keybit); 277866a98aeSHaojian Zhuang 278866a98aeSHaojian Zhuang input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0); 279866a98aeSHaojian Zhuang input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0); 280866a98aeSHaojian Zhuang input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT, 281866a98aeSHaojian Zhuang 0, 0); 282866a98aeSHaojian Zhuang 283866a98aeSHaojian Zhuang ret = input_register_device(touch->idev); 284866a98aeSHaojian Zhuang if (ret < 0) { 285866a98aeSHaojian Zhuang dev_err(chip->dev, "Failed to register touch!\n"); 286866a98aeSHaojian Zhuang return ret; 287866a98aeSHaojian Zhuang } 288866a98aeSHaojian Zhuang 289866a98aeSHaojian Zhuang return 0; 290866a98aeSHaojian Zhuang } 291866a98aeSHaojian Zhuang 292866a98aeSHaojian Zhuang static struct platform_driver pm860x_touch_driver = { 293866a98aeSHaojian Zhuang .driver = { 294866a98aeSHaojian Zhuang .name = "88pm860x-touch", 295866a98aeSHaojian Zhuang }, 296866a98aeSHaojian Zhuang .probe = pm860x_touch_probe, 297866a98aeSHaojian Zhuang }; 298cdcc96e2SJJ Ding module_platform_driver(pm860x_touch_driver); 299866a98aeSHaojian Zhuang 300866a98aeSHaojian Zhuang MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x"); 301866a98aeSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 302866a98aeSHaojian Zhuang MODULE_LICENSE("GPL"); 303866a98aeSHaojian Zhuang MODULE_ALIAS("platform:88pm860x-touch"); 304866a98aeSHaojian Zhuang 305