1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23ea7e551SDmitry Artamonow /* 33ea7e551SDmitry Artamonow * 43ea7e551SDmitry Artamonow * h3600 atmel micro companion support, touchscreen subdevice 53ea7e551SDmitry Artamonow * Author : Alessandro Gardich <gremlin@gremlin.it> 63ea7e551SDmitry Artamonow * Author : Dmitry Artamonow <mad_soft@inbox.ru> 73ea7e551SDmitry Artamonow * Author : Linus Walleij <linus.walleij@linaro.org> 83ea7e551SDmitry Artamonow */ 93ea7e551SDmitry Artamonow 103ea7e551SDmitry Artamonow #include <asm/byteorder.h> 113ea7e551SDmitry Artamonow #include <linux/module.h> 123ea7e551SDmitry Artamonow #include <linux/init.h> 133ea7e551SDmitry Artamonow #include <linux/interrupt.h> 143ea7e551SDmitry Artamonow #include <linux/pm.h> 153ea7e551SDmitry Artamonow #include <linux/delay.h> 163ea7e551SDmitry Artamonow #include <linux/device.h> 173ea7e551SDmitry Artamonow #include <linux/input.h> 183ea7e551SDmitry Artamonow #include <linux/platform_device.h> 193ea7e551SDmitry Artamonow #include <linux/slab.h> 203ea7e551SDmitry Artamonow #include <linux/mfd/ipaq-micro.h> 213ea7e551SDmitry Artamonow 223ea7e551SDmitry Artamonow struct touchscreen_data { 233ea7e551SDmitry Artamonow struct input_dev *input; 243ea7e551SDmitry Artamonow struct ipaq_micro *micro; 253ea7e551SDmitry Artamonow }; 263ea7e551SDmitry Artamonow 273ea7e551SDmitry Artamonow static void micro_ts_receive(void *data, int len, unsigned char *msg) 283ea7e551SDmitry Artamonow { 293ea7e551SDmitry Artamonow struct touchscreen_data *ts = data; 303ea7e551SDmitry Artamonow 313ea7e551SDmitry Artamonow if (len == 4) { 323ea7e551SDmitry Artamonow input_report_abs(ts->input, ABS_X, 333ea7e551SDmitry Artamonow be16_to_cpup((__be16 *) &msg[2])); 343ea7e551SDmitry Artamonow input_report_abs(ts->input, ABS_Y, 353ea7e551SDmitry Artamonow be16_to_cpup((__be16 *) &msg[0])); 363ea7e551SDmitry Artamonow input_report_key(ts->input, BTN_TOUCH, 1); 373ea7e551SDmitry Artamonow input_sync(ts->input); 383ea7e551SDmitry Artamonow } else if (len == 0) { 393ea7e551SDmitry Artamonow input_report_abs(ts->input, ABS_X, 0); 403ea7e551SDmitry Artamonow input_report_abs(ts->input, ABS_Y, 0); 413ea7e551SDmitry Artamonow input_report_key(ts->input, BTN_TOUCH, 0); 423ea7e551SDmitry Artamonow input_sync(ts->input); 433ea7e551SDmitry Artamonow } 443ea7e551SDmitry Artamonow } 453ea7e551SDmitry Artamonow 46f348f329SDmitry Torokhov static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) 47f348f329SDmitry Torokhov { 48f348f329SDmitry Torokhov struct ipaq_micro *micro = ts->micro; 49f348f329SDmitry Torokhov 50f348f329SDmitry Torokhov spin_lock_irq(µ->lock); 51f348f329SDmitry Torokhov 52f348f329SDmitry Torokhov if (enable) { 53f348f329SDmitry Torokhov micro->ts = micro_ts_receive; 54f348f329SDmitry Torokhov micro->ts_data = ts; 55f348f329SDmitry Torokhov } else { 56f348f329SDmitry Torokhov micro->ts = NULL; 57f348f329SDmitry Torokhov micro->ts_data = NULL; 58f348f329SDmitry Torokhov } 59f348f329SDmitry Torokhov 60f348f329SDmitry Torokhov spin_unlock_irq(&ts->micro->lock); 61f348f329SDmitry Torokhov } 62f348f329SDmitry Torokhov 63f348f329SDmitry Torokhov static int micro_ts_open(struct input_dev *input) 64f348f329SDmitry Torokhov { 65f348f329SDmitry Torokhov struct touchscreen_data *ts = input_get_drvdata(input); 66f348f329SDmitry Torokhov 67f348f329SDmitry Torokhov micro_ts_toggle_receive(ts, true); 68f348f329SDmitry Torokhov 69f348f329SDmitry Torokhov return 0; 70f348f329SDmitry Torokhov } 71f348f329SDmitry Torokhov 72f348f329SDmitry Torokhov static void micro_ts_close(struct input_dev *input) 73f348f329SDmitry Torokhov { 74f348f329SDmitry Torokhov struct touchscreen_data *ts = input_get_drvdata(input); 75f348f329SDmitry Torokhov 76f348f329SDmitry Torokhov micro_ts_toggle_receive(ts, false); 77f348f329SDmitry Torokhov } 78f348f329SDmitry Torokhov 793ea7e551SDmitry Artamonow static int micro_ts_probe(struct platform_device *pdev) 803ea7e551SDmitry Artamonow { 81f348f329SDmitry Torokhov struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent); 823ea7e551SDmitry Artamonow struct touchscreen_data *ts; 83f348f329SDmitry Torokhov int error; 843ea7e551SDmitry Artamonow 853ea7e551SDmitry Artamonow ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); 863ea7e551SDmitry Artamonow if (!ts) 873ea7e551SDmitry Artamonow return -ENOMEM; 883ea7e551SDmitry Artamonow 89f348f329SDmitry Torokhov ts->micro = micro; 903ea7e551SDmitry Artamonow 913ea7e551SDmitry Artamonow ts->input = devm_input_allocate_device(&pdev->dev); 923ea7e551SDmitry Artamonow if (!ts->input) { 933ea7e551SDmitry Artamonow dev_err(&pdev->dev, "failed to allocate input device\n"); 943ea7e551SDmitry Artamonow return -ENOMEM; 953ea7e551SDmitry Artamonow } 963ea7e551SDmitry Artamonow 97f348f329SDmitry Torokhov ts->input->name = "ipaq micro ts"; 98f348f329SDmitry Torokhov ts->input->open = micro_ts_open; 99f348f329SDmitry Torokhov ts->input->close = micro_ts_close; 100f348f329SDmitry Torokhov 101f348f329SDmitry Torokhov input_set_drvdata(ts->input, ts); 102f348f329SDmitry Torokhov 1033ea7e551SDmitry Artamonow input_set_capability(ts->input, EV_KEY, BTN_TOUCH); 1043ea7e551SDmitry Artamonow input_set_capability(ts->input, EV_ABS, ABS_X); 1053ea7e551SDmitry Artamonow input_set_capability(ts->input, EV_ABS, ABS_Y); 1063ea7e551SDmitry Artamonow input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0); 1073ea7e551SDmitry Artamonow input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0); 1083ea7e551SDmitry Artamonow 109f348f329SDmitry Torokhov error = input_register_device(ts->input); 110f348f329SDmitry Torokhov if (error) { 1113ea7e551SDmitry Artamonow dev_err(&pdev->dev, "error registering touch input\n"); 112f348f329SDmitry Torokhov return error; 1133ea7e551SDmitry Artamonow } 1143ea7e551SDmitry Artamonow 115f348f329SDmitry Torokhov platform_set_drvdata(pdev, ts); 1163ea7e551SDmitry Artamonow 1173ea7e551SDmitry Artamonow dev_info(&pdev->dev, "iPAQ micro touchscreen\n"); 1183ea7e551SDmitry Artamonow 1193ea7e551SDmitry Artamonow return 0; 1203ea7e551SDmitry Artamonow } 1213ea7e551SDmitry Artamonow 122*144ff5e0SJonathan Cameron static int micro_ts_suspend(struct device *dev) 1233ea7e551SDmitry Artamonow { 1243ea7e551SDmitry Artamonow struct touchscreen_data *ts = dev_get_drvdata(dev); 1253ea7e551SDmitry Artamonow 126f348f329SDmitry Torokhov micro_ts_toggle_receive(ts, false); 127f348f329SDmitry Torokhov 1283ea7e551SDmitry Artamonow return 0; 1293ea7e551SDmitry Artamonow } 1303ea7e551SDmitry Artamonow 131*144ff5e0SJonathan Cameron static int micro_ts_resume(struct device *dev) 1323ea7e551SDmitry Artamonow { 1333ea7e551SDmitry Artamonow struct touchscreen_data *ts = dev_get_drvdata(dev); 134f348f329SDmitry Torokhov struct input_dev *input = ts->input; 1353ea7e551SDmitry Artamonow 136f348f329SDmitry Torokhov mutex_lock(&input->mutex); 137f348f329SDmitry Torokhov 138d69f0a43SAndrzej Pietrasiewicz if (input_device_enabled(input)) 139f348f329SDmitry Torokhov micro_ts_toggle_receive(ts, true); 140f348f329SDmitry Torokhov 141f348f329SDmitry Torokhov mutex_unlock(&input->mutex); 142f348f329SDmitry Torokhov 1433ea7e551SDmitry Artamonow return 0; 1443ea7e551SDmitry Artamonow } 1453ea7e551SDmitry Artamonow 146*144ff5e0SJonathan Cameron static DEFINE_SIMPLE_DEV_PM(micro_ts_dev_pm_ops, 147*144ff5e0SJonathan Cameron micro_ts_suspend, micro_ts_resume); 1483ea7e551SDmitry Artamonow 1493ea7e551SDmitry Artamonow static struct platform_driver micro_ts_device_driver = { 1503ea7e551SDmitry Artamonow .driver = { 1513ea7e551SDmitry Artamonow .name = "ipaq-micro-ts", 152*144ff5e0SJonathan Cameron .pm = pm_sleep_ptr(µ_ts_dev_pm_ops), 1533ea7e551SDmitry Artamonow }, 1543ea7e551SDmitry Artamonow .probe = micro_ts_probe, 1553ea7e551SDmitry Artamonow }; 1563ea7e551SDmitry Artamonow module_platform_driver(micro_ts_device_driver); 1573ea7e551SDmitry Artamonow 1583ea7e551SDmitry Artamonow MODULE_LICENSE("GPL"); 1593ea7e551SDmitry Artamonow MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen"); 1603ea7e551SDmitry Artamonow MODULE_ALIAS("platform:ipaq-micro-ts"); 161