cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

resistive-adc-touch.c (7598B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ADC generic resistive touchscreen (GRTS)
      4 * This is a generic input driver that connects to an ADC
      5 * given the channels in device tree, and reports events to the input
      6 * subsystem.
      7 *
      8 * Copyright (C) 2017,2018 Microchip Technology,
      9 * Author: Eugen Hristev <eugen.hristev@microchip.com>
     10 *
     11 */
     12#include <linux/input.h>
     13#include <linux/input/touchscreen.h>
     14#include <linux/iio/consumer.h>
     15#include <linux/iio/iio.h>
     16#include <linux/mod_devicetable.h>
     17#include <linux/module.h>
     18#include <linux/platform_device.h>
     19#include <linux/property.h>
     20
     21#define DRIVER_NAME					"resistive-adc-touch"
     22#define GRTS_DEFAULT_PRESSURE_MIN			50000
     23#define GRTS_DEFAULT_PRESSURE_MAX			65535
     24#define GRTS_MAX_POS_MASK				GENMASK(11, 0)
     25#define GRTS_MAX_CHANNELS				4
     26
     27enum grts_ch_type {
     28	GRTS_CH_X,
     29	GRTS_CH_Y,
     30	GRTS_CH_PRESSURE,
     31	GRTS_CH_Z1,
     32	GRTS_CH_Z2,
     33	GRTS_CH_MAX = GRTS_CH_Z2 + 1
     34};
     35
     36/**
     37 * struct grts_state - generic resistive touch screen information struct
     38 * @x_plate_ohms:	resistance of the X plate
     39 * @pressure_min:	number representing the minimum for the pressure
     40 * @pressure:		are we getting pressure info or not
     41 * @iio_chans:		list of channels acquired
     42 * @iio_cb:		iio_callback buffer for the data
     43 * @input:		the input device structure that we register
     44 * @prop:		touchscreen properties struct
     45 * @ch_map:		map of channels that are defined for the touchscreen
     46 */
     47struct grts_state {
     48	u32				x_plate_ohms;
     49	u32				pressure_min;
     50	bool				pressure;
     51	struct iio_channel		*iio_chans;
     52	struct iio_cb_buffer		*iio_cb;
     53	struct input_dev		*input;
     54	struct touchscreen_properties	prop;
     55	u8				ch_map[GRTS_CH_MAX];
     56};
     57
     58static int grts_cb(const void *data, void *private)
     59{
     60	const u16 *touch_info = data;
     61	struct grts_state *st = private;
     62	unsigned int x, y, press = 0;
     63
     64	x = touch_info[st->ch_map[GRTS_CH_X]];
     65	y = touch_info[st->ch_map[GRTS_CH_Y]];
     66
     67	if (st->ch_map[GRTS_CH_PRESSURE] < GRTS_MAX_CHANNELS) {
     68		press = touch_info[st->ch_map[GRTS_CH_PRESSURE]];
     69	} else if (st->ch_map[GRTS_CH_Z1] < GRTS_MAX_CHANNELS) {
     70		unsigned int z1 = touch_info[st->ch_map[GRTS_CH_Z1]];
     71		unsigned int z2 = touch_info[st->ch_map[GRTS_CH_Z2]];
     72		unsigned int Rt;
     73
     74		if (likely(x && z1)) {
     75			Rt = z2;
     76			Rt -= z1;
     77			Rt *= st->x_plate_ohms;
     78			Rt = DIV_ROUND_CLOSEST(Rt, 16);
     79			Rt *= x;
     80			Rt /= z1;
     81			Rt = DIV_ROUND_CLOSEST(Rt, 256);
     82			/*
     83			 * On increased pressure the resistance (Rt) is
     84			 * decreasing so, convert values to make it looks as
     85			 * real pressure.
     86			 */
     87			if (Rt < GRTS_DEFAULT_PRESSURE_MAX)
     88				press = GRTS_DEFAULT_PRESSURE_MAX - Rt;
     89		}
     90	}
     91
     92	if ((!x && !y) || (st->pressure && (press < st->pressure_min))) {
     93		/* report end of touch */
     94		input_report_key(st->input, BTN_TOUCH, 0);
     95		input_sync(st->input);
     96		return 0;
     97	}
     98
     99	/* report proper touch to subsystem*/
    100	touchscreen_report_pos(st->input, &st->prop, x, y, false);
    101	if (st->pressure)
    102		input_report_abs(st->input, ABS_PRESSURE, press);
    103	input_report_key(st->input, BTN_TOUCH, 1);
    104	input_sync(st->input);
    105
    106	return 0;
    107}
    108
    109static int grts_open(struct input_dev *dev)
    110{
    111	int error;
    112	struct grts_state *st = input_get_drvdata(dev);
    113
    114	error = iio_channel_start_all_cb(st->iio_cb);
    115	if (error) {
    116		dev_err(dev->dev.parent, "failed to start callback buffer.\n");
    117		return error;
    118	}
    119	return 0;
    120}
    121
    122static void grts_close(struct input_dev *dev)
    123{
    124	struct grts_state *st = input_get_drvdata(dev);
    125
    126	iio_channel_stop_all_cb(st->iio_cb);
    127}
    128
    129static void grts_disable(void *data)
    130{
    131	iio_channel_release_all_cb(data);
    132}
    133
    134static int grts_map_channel(struct grts_state *st, struct device *dev,
    135			    enum grts_ch_type type, const char *name,
    136			    bool optional)
    137{
    138	int idx;
    139
    140	idx = device_property_match_string(dev, "io-channel-names", name);
    141	if (idx < 0) {
    142		if (!optional)
    143			return idx;
    144		idx = GRTS_MAX_CHANNELS;
    145	} else if (idx >= GRTS_MAX_CHANNELS) {
    146		return -EOVERFLOW;
    147	}
    148
    149	st->ch_map[type] = idx;
    150	return 0;
    151}
    152
    153static int grts_get_properties(struct grts_state *st, struct device *dev)
    154{
    155	int error;
    156
    157	error = grts_map_channel(st, dev, GRTS_CH_X, "x", false);
    158	if (error)
    159		return error;
    160
    161	error = grts_map_channel(st, dev, GRTS_CH_Y, "y", false);
    162	if (error)
    163		return error;
    164
    165	/* pressure is optional */
    166	error = grts_map_channel(st, dev, GRTS_CH_PRESSURE, "pressure", true);
    167	if (error)
    168		return error;
    169
    170	if (st->ch_map[GRTS_CH_PRESSURE] < GRTS_MAX_CHANNELS) {
    171		st->pressure = true;
    172		return 0;
    173	}
    174
    175	/* if no pressure is defined, try optional z1 + z2 */
    176	error = grts_map_channel(st, dev, GRTS_CH_Z1, "z1", true);
    177	if (error)
    178		return error;
    179
    180	if (st->ch_map[GRTS_CH_Z1] >= GRTS_MAX_CHANNELS)
    181		return 0;
    182
    183	/* if z1 is provided z2 is not optional */
    184	error = grts_map_channel(st, dev, GRTS_CH_Z2, "z2", true);
    185	if (error)
    186		return error;
    187
    188	error = device_property_read_u32(dev,
    189					 "touchscreen-x-plate-ohms",
    190					 &st->x_plate_ohms);
    191	if (error) {
    192		dev_err(dev, "can't get touchscreen-x-plate-ohms property\n");
    193		return error;
    194	}
    195
    196	st->pressure = true;
    197	return 0;
    198}
    199
    200static int grts_probe(struct platform_device *pdev)
    201{
    202	struct grts_state *st;
    203	struct input_dev *input;
    204	struct device *dev = &pdev->dev;
    205	int error;
    206
    207	st = devm_kzalloc(dev, sizeof(struct grts_state), GFP_KERNEL);
    208	if (!st)
    209		return -ENOMEM;
    210
    211	/* get the channels from IIO device */
    212	st->iio_chans = devm_iio_channel_get_all(dev);
    213	if (IS_ERR(st->iio_chans)) {
    214		error = PTR_ERR(st->iio_chans);
    215		if (error != -EPROBE_DEFER)
    216			dev_err(dev, "can't get iio channels.\n");
    217		return error;
    218	}
    219
    220	if (!device_property_present(dev, "io-channel-names"))
    221		return -ENODEV;
    222
    223	error = grts_get_properties(st, dev);
    224	if (error) {
    225		dev_err(dev, "Failed to parse properties\n");
    226		return error;
    227	}
    228
    229	if (st->pressure) {
    230		error = device_property_read_u32(dev,
    231						 "touchscreen-min-pressure",
    232						 &st->pressure_min);
    233		if (error) {
    234			dev_dbg(dev, "can't get touchscreen-min-pressure property.\n");
    235			st->pressure_min = GRTS_DEFAULT_PRESSURE_MIN;
    236		}
    237	}
    238
    239	input = devm_input_allocate_device(dev);
    240	if (!input) {
    241		dev_err(dev, "failed to allocate input device.\n");
    242		return -ENOMEM;
    243	}
    244
    245	input->name = DRIVER_NAME;
    246	input->id.bustype = BUS_HOST;
    247	input->open = grts_open;
    248	input->close = grts_close;
    249
    250	input_set_abs_params(input, ABS_X, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
    251	input_set_abs_params(input, ABS_Y, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
    252	if (st->pressure)
    253		input_set_abs_params(input, ABS_PRESSURE, st->pressure_min,
    254				     GRTS_DEFAULT_PRESSURE_MAX, 0, 0);
    255
    256	input_set_capability(input, EV_KEY, BTN_TOUCH);
    257
    258	/* parse optional device tree properties */
    259	touchscreen_parse_properties(input, false, &st->prop);
    260
    261	st->input = input;
    262	input_set_drvdata(input, st);
    263
    264	error = input_register_device(input);
    265	if (error) {
    266		dev_err(dev, "failed to register input device.");
    267		return error;
    268	}
    269
    270	st->iio_cb = iio_channel_get_all_cb(dev, grts_cb, st);
    271	if (IS_ERR(st->iio_cb)) {
    272		dev_err(dev, "failed to allocate callback buffer.\n");
    273		return PTR_ERR(st->iio_cb);
    274	}
    275
    276	error = devm_add_action_or_reset(dev, grts_disable, st->iio_cb);
    277	if (error) {
    278		dev_err(dev, "failed to add disable action.\n");
    279		return error;
    280	}
    281
    282	return 0;
    283}
    284
    285static const struct of_device_id grts_of_match[] = {
    286	{
    287		.compatible = "resistive-adc-touch",
    288	}, {
    289		/* sentinel */
    290	},
    291};
    292
    293MODULE_DEVICE_TABLE(of, grts_of_match);
    294
    295static struct platform_driver grts_driver = {
    296	.probe = grts_probe,
    297	.driver = {
    298		.name = DRIVER_NAME,
    299		.of_match_table = grts_of_match,
    300	},
    301};
    302
    303module_platform_driver(grts_driver);
    304
    305MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
    306MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver");
    307MODULE_LICENSE("GPL v2");