18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ADC generic resistive touchscreen (GRTS) 48c2ecf20Sopenharmony_ci * This is a generic input driver that connects to an ADC 58c2ecf20Sopenharmony_ci * given the channels in device tree, and reports events to the input 68c2ecf20Sopenharmony_ci * subsystem. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2017,2018 Microchip Technology, 98c2ecf20Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DRIVER_NAME "resistive-adc-touch" 228c2ecf20Sopenharmony_ci#define GRTS_DEFAULT_PRESSURE_MIN 50000 238c2ecf20Sopenharmony_ci#define GRTS_MAX_POS_MASK GENMASK(11, 0) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/** 268c2ecf20Sopenharmony_ci * grts_state - generic resistive touch screen information struct 278c2ecf20Sopenharmony_ci * @pressure_min: number representing the minimum for the pressure 288c2ecf20Sopenharmony_ci * @pressure: are we getting pressure info or not 298c2ecf20Sopenharmony_ci * @iio_chans: list of channels acquired 308c2ecf20Sopenharmony_ci * @iio_cb: iio_callback buffer for the data 318c2ecf20Sopenharmony_ci * @input: the input device structure that we register 328c2ecf20Sopenharmony_ci * @prop: touchscreen properties struct 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistruct grts_state { 358c2ecf20Sopenharmony_ci u32 pressure_min; 368c2ecf20Sopenharmony_ci bool pressure; 378c2ecf20Sopenharmony_ci struct iio_channel *iio_chans; 388c2ecf20Sopenharmony_ci struct iio_cb_buffer *iio_cb; 398c2ecf20Sopenharmony_ci struct input_dev *input; 408c2ecf20Sopenharmony_ci struct touchscreen_properties prop; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int grts_cb(const void *data, void *private) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci const u16 *touch_info = data; 468c2ecf20Sopenharmony_ci struct grts_state *st = private; 478c2ecf20Sopenharmony_ci unsigned int x, y, press = 0x0; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* channel data coming in buffer in the order below */ 508c2ecf20Sopenharmony_ci x = touch_info[0]; 518c2ecf20Sopenharmony_ci y = touch_info[1]; 528c2ecf20Sopenharmony_ci if (st->pressure) 538c2ecf20Sopenharmony_ci press = touch_info[2]; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if ((!x && !y) || (st->pressure && (press < st->pressure_min))) { 568c2ecf20Sopenharmony_ci /* report end of touch */ 578c2ecf20Sopenharmony_ci input_report_key(st->input, BTN_TOUCH, 0); 588c2ecf20Sopenharmony_ci input_sync(st->input); 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* report proper touch to subsystem*/ 638c2ecf20Sopenharmony_ci touchscreen_report_pos(st->input, &st->prop, x, y, false); 648c2ecf20Sopenharmony_ci if (st->pressure) 658c2ecf20Sopenharmony_ci input_report_abs(st->input, ABS_PRESSURE, press); 668c2ecf20Sopenharmony_ci input_report_key(st->input, BTN_TOUCH, 1); 678c2ecf20Sopenharmony_ci input_sync(st->input); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int grts_open(struct input_dev *dev) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci int error; 758c2ecf20Sopenharmony_ci struct grts_state *st = input_get_drvdata(dev); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci error = iio_channel_start_all_cb(st->iio_cb); 788c2ecf20Sopenharmony_ci if (error) { 798c2ecf20Sopenharmony_ci dev_err(dev->dev.parent, "failed to start callback buffer.\n"); 808c2ecf20Sopenharmony_ci return error; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void grts_close(struct input_dev *dev) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct grts_state *st = input_get_drvdata(dev); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci iio_channel_stop_all_cb(st->iio_cb); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void grts_disable(void *data) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci iio_channel_release_all_cb(data); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int grts_probe(struct platform_device *pdev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct grts_state *st; 1008c2ecf20Sopenharmony_ci struct input_dev *input; 1018c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1028c2ecf20Sopenharmony_ci struct iio_channel *chan; 1038c2ecf20Sopenharmony_ci int error; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci st = devm_kzalloc(dev, sizeof(struct grts_state), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (!st) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* get the channels from IIO device */ 1108c2ecf20Sopenharmony_ci st->iio_chans = devm_iio_channel_get_all(dev); 1118c2ecf20Sopenharmony_ci if (IS_ERR(st->iio_chans)) { 1128c2ecf20Sopenharmony_ci error = PTR_ERR(st->iio_chans); 1138c2ecf20Sopenharmony_ci if (error != -EPROBE_DEFER) 1148c2ecf20Sopenharmony_ci dev_err(dev, "can't get iio channels.\n"); 1158c2ecf20Sopenharmony_ci return error; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci chan = &st->iio_chans[0]; 1198c2ecf20Sopenharmony_ci st->pressure = false; 1208c2ecf20Sopenharmony_ci while (chan && chan->indio_dev) { 1218c2ecf20Sopenharmony_ci if (!strcmp(chan->channel->datasheet_name, "pressure")) 1228c2ecf20Sopenharmony_ci st->pressure = true; 1238c2ecf20Sopenharmony_ci chan++; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (st->pressure) { 1278c2ecf20Sopenharmony_ci error = device_property_read_u32(dev, 1288c2ecf20Sopenharmony_ci "touchscreen-min-pressure", 1298c2ecf20Sopenharmony_ci &st->pressure_min); 1308c2ecf20Sopenharmony_ci if (error) { 1318c2ecf20Sopenharmony_ci dev_dbg(dev, "can't get touchscreen-min-pressure property.\n"); 1328c2ecf20Sopenharmony_ci st->pressure_min = GRTS_DEFAULT_PRESSURE_MIN; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 1378c2ecf20Sopenharmony_ci if (!input) { 1388c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate input device.\n"); 1398c2ecf20Sopenharmony_ci return -ENOMEM; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci input->name = DRIVER_NAME; 1438c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 1448c2ecf20Sopenharmony_ci input->open = grts_open; 1458c2ecf20Sopenharmony_ci input->close = grts_close; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_X, 0, GRTS_MAX_POS_MASK - 1, 0, 0); 1488c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Y, 0, GRTS_MAX_POS_MASK - 1, 0, 0); 1498c2ecf20Sopenharmony_ci if (st->pressure) 1508c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_PRESSURE, st->pressure_min, 1518c2ecf20Sopenharmony_ci 0xffff, 0, 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOUCH); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* parse optional device tree properties */ 1568c2ecf20Sopenharmony_ci touchscreen_parse_properties(input, false, &st->prop); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci st->input = input; 1598c2ecf20Sopenharmony_ci input_set_drvdata(input, st); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci error = input_register_device(input); 1628c2ecf20Sopenharmony_ci if (error) { 1638c2ecf20Sopenharmony_ci dev_err(dev, "failed to register input device."); 1648c2ecf20Sopenharmony_ci return error; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci st->iio_cb = iio_channel_get_all_cb(dev, grts_cb, st); 1688c2ecf20Sopenharmony_ci if (IS_ERR(st->iio_cb)) { 1698c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate callback buffer.\n"); 1708c2ecf20Sopenharmony_ci return PTR_ERR(st->iio_cb); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci error = devm_add_action_or_reset(dev, grts_disable, st->iio_cb); 1748c2ecf20Sopenharmony_ci if (error) { 1758c2ecf20Sopenharmony_ci dev_err(dev, "failed to add disable action.\n"); 1768c2ecf20Sopenharmony_ci return error; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic const struct of_device_id grts_of_match[] = { 1838c2ecf20Sopenharmony_ci { 1848c2ecf20Sopenharmony_ci .compatible = "resistive-adc-touch", 1858c2ecf20Sopenharmony_ci }, { 1868c2ecf20Sopenharmony_ci /* sentinel */ 1878c2ecf20Sopenharmony_ci }, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, grts_of_match); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct platform_driver grts_driver = { 1938c2ecf20Sopenharmony_ci .probe = grts_probe, 1948c2ecf20Sopenharmony_ci .driver = { 1958c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1968c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(grts_of_match), 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cimodule_platform_driver(grts_driver); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>"); 2038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic ADC Resistive Touch Driver"); 2048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 205