414 lines · 9.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  Driver for Ntrig/Microsoft Touchscreens over SPI * *  Copyright (c) 2016 Red Hat Inc. */  #include <linux/kernel.h> #include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/spi/spi.h>#include <linux/acpi.h> #include <linux/unaligned.h> #define SURFACE3_PACKET_SIZE	264 #define SURFACE3_REPORT_TOUCH	0xd2#define SURFACE3_REPORT_PEN	0x16 struct surface3_ts_data {	struct spi_device *spi;	struct gpio_desc *gpiod_rst[2];	struct input_dev *input_dev;	struct input_dev *pen_input_dev;	int pen_tool; 	u8 rd_buf[SURFACE3_PACKET_SIZE]		____cacheline_aligned;}; struct surface3_ts_data_finger {	u8 status;	__le16 tracking_id;	__le16 x;	__le16 cx;	__le16 y;	__le16 cy;	__le16 width;	__le16 height;	u32 padding;} __packed; struct surface3_ts_data_pen {	u8 status;	__le16 x;	__le16 y;	__le16 pressure;	u8 padding;} __packed; static int surface3_spi_read(struct surface3_ts_data *ts_data){	struct spi_device *spi = ts_data->spi; 	memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));	return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));} static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,				   struct surface3_ts_data_finger *finger){	int st = finger->status & 0x01;	int slot; 	slot = input_mt_get_slot_by_key(ts_data->input_dev,				get_unaligned_le16(&finger->tracking_id));	if (slot < 0)		return; 	input_mt_slot(ts_data->input_dev, slot);	input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);	if (st) {		input_report_abs(ts_data->input_dev,				 ABS_MT_POSITION_X,				 get_unaligned_le16(&finger->x));		input_report_abs(ts_data->input_dev,				 ABS_MT_POSITION_Y,				 get_unaligned_le16(&finger->y));		input_report_abs(ts_data->input_dev,				 ABS_MT_WIDTH_MAJOR,				 get_unaligned_le16(&finger->width));		input_report_abs(ts_data->input_dev,				 ABS_MT_WIDTH_MINOR,				 get_unaligned_le16(&finger->height));	}} static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data){	unsigned int i; 	for (i = 0; i < 13; i++) {		struct surface3_ts_data_finger *finger; 		finger = (struct surface3_ts_data_finger *)&data[17 +				i * sizeof(struct surface3_ts_data_finger)]; 		/*		 * When bit 5 of status is 1, it marks the end of the report:		 * - touch present: 0xe7		 * - touch released: 0xe4		 * - nothing valuable: 0xff		 */		if (finger->status & 0x10)			break; 		surface3_spi_report_touch(ts_data, finger);	} 	input_mt_sync_frame(ts_data->input_dev);	input_sync(ts_data->input_dev);} static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,				    struct surface3_ts_data_pen *pen){	struct input_dev *dev = ts_data->pen_input_dev;	int st = pen->status;	int prox = st & 0x01;	int rubber = st & 0x18;	int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; 	/* fake proximity out to switch tools */	if (ts_data->pen_tool != tool) {		input_report_key(dev, ts_data->pen_tool, 0);		input_sync(dev);		ts_data->pen_tool = tool;	} 	input_report_key(dev, BTN_TOUCH, st & 0x12); 	input_report_key(dev, ts_data->pen_tool, prox); 	if (st) {		input_report_key(dev,				 BTN_STYLUS,				 st & 0x04); 		input_report_abs(dev,				 ABS_X,				 get_unaligned_le16(&pen->x));		input_report_abs(dev,				 ABS_Y,				 get_unaligned_le16(&pen->y));		input_report_abs(dev,				 ABS_PRESSURE,				 get_unaligned_le16(&pen->pressure));	}} static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data){	struct surface3_ts_data_pen *pen; 	pen = (struct surface3_ts_data_pen *)&data[15]; 	surface3_spi_report_pen(ts_data, pen);	input_sync(ts_data->pen_input_dev);} static void surface3_spi_process(struct surface3_ts_data *ts_data){	static const char header[] = {		0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01	};	u8 *data = ts_data->rd_buf; 	if (memcmp(header, data, sizeof(header)))		dev_err(&ts_data->spi->dev,			"%s header error: %*ph, ignoring...\n",			__func__, (int)sizeof(header), data); 	switch (data[9]) {	case SURFACE3_REPORT_TOUCH:		surface3_spi_process_touch(ts_data, data);		break;	case SURFACE3_REPORT_PEN:		surface3_spi_process_pen(ts_data, data);		break;	default:		dev_err(&ts_data->spi->dev,			"%s unknown packet type: %x, ignoring...\n",			__func__, data[9]);		break;	}} static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id){	struct surface3_ts_data *data = dev_id; 	if (surface3_spi_read(data))		return IRQ_HANDLED; 	dev_dbg(&data->spi->dev, "%s received -> %*ph\n",		__func__, SURFACE3_PACKET_SIZE, data->rd_buf);	surface3_spi_process(data); 	return IRQ_HANDLED;} static void surface3_spi_power(struct surface3_ts_data *data, bool on){	gpiod_set_value(data->gpiod_rst[0], on);	gpiod_set_value(data->gpiod_rst[1], on);	/* let the device settle a little */	msleep(20);} /** * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT * * @data: surface3_spi_ts_data pointer */static int surface3_spi_get_gpio_config(struct surface3_ts_data *data){	struct device *dev;	struct gpio_desc *gpiod;	int i; 	dev = &data->spi->dev; 	/* Get the reset lines GPIO pin number */	for (i = 0; i < 2; i++) {		gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);		if (IS_ERR(gpiod))			return dev_err_probe(dev, PTR_ERR(gpiod),					     "Failed to get power GPIO %d\n", i); 		data->gpiod_rst[i] = gpiod;	} 	return 0;} static int surface3_spi_create_touch_input(struct surface3_ts_data *data){	struct input_dev *input;	int error; 	input = devm_input_allocate_device(&data->spi->dev);	if (!input)		return -ENOMEM; 	data->input_dev = input; 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);	input_abs_set_res(input, ABS_MT_POSITION_X, 40);	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);	input_abs_set_res(input, ABS_MT_POSITION_Y, 48);	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);	input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);	input_mt_init_slots(input, 10, INPUT_MT_DIRECT); 	input->name = "Surface3 SPI Capacitive TouchScreen";	input->phys = "input/ts";	input->id.bustype = BUS_SPI;	input->id.vendor = 0x045e;	/* Microsoft */	input->id.product = 0x0001;	input->id.version = 0x0000; 	error = input_register_device(input);	if (error) {		dev_err(&data->spi->dev,			"Failed to register input device: %d", error);		return error;	} 	return 0;} static int surface3_spi_create_pen_input(struct surface3_ts_data *data){	struct input_dev *input;	int error; 	input = devm_input_allocate_device(&data->spi->dev);	if (!input)		return -ENOMEM; 	data->pen_input_dev = input;	data->pen_tool = BTN_TOOL_PEN; 	__set_bit(INPUT_PROP_DIRECT, input->propbit);	__set_bit(INPUT_PROP_POINTER, input->propbit);	input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);	input_abs_set_res(input, ABS_X, 40);	input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);	input_abs_set_res(input, ABS_Y, 48);	input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);	input_set_capability(input, EV_KEY, BTN_TOUCH);	input_set_capability(input, EV_KEY, BTN_STYLUS);	input_set_capability(input, EV_KEY, BTN_TOOL_PEN);	input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER); 	input->name = "Surface3 SPI Pen Input";	input->phys = "input/ts";	input->id.bustype = BUS_SPI;	input->id.vendor = 0x045e;     /* Microsoft */	input->id.product = 0x0002;	input->id.version = 0x0000; 	error = input_register_device(input);	if (error) {		dev_err(&data->spi->dev,			"Failed to register input device: %d", error);		return error;	} 	return 0;} static int surface3_spi_probe(struct spi_device *spi){	struct surface3_ts_data *data;	int error; 	/* Set up SPI*/	spi->bits_per_word = 8;	spi->mode = SPI_MODE_0;	error = spi_setup(spi);	if (error)		return error; 	data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);	if (!data)		return -ENOMEM; 	data->spi = spi;	spi_set_drvdata(spi, data); 	error = surface3_spi_get_gpio_config(data);	if (error)		return error; 	surface3_spi_power(data, true);	surface3_spi_power(data, false);	surface3_spi_power(data, true); 	error = surface3_spi_create_touch_input(data);	if (error)		return error; 	error = surface3_spi_create_pen_input(data);	if (error)		return error; 	error = devm_request_threaded_irq(&spi->dev, spi->irq,					  NULL, surface3_spi_irq_handler,					  IRQF_ONESHOT,					  "Surface3-irq", data);	if (error)		return error; 	return 0;} static int surface3_spi_suspend(struct device *dev){	struct spi_device *spi = to_spi_device(dev);	struct surface3_ts_data *data = spi_get_drvdata(spi); 	disable_irq(data->spi->irq); 	surface3_spi_power(data, false); 	return 0;} static int surface3_spi_resume(struct device *dev){	struct spi_device *spi = to_spi_device(dev);	struct surface3_ts_data *data = spi_get_drvdata(spi); 	surface3_spi_power(data, true); 	enable_irq(data->spi->irq); 	return 0;} static DEFINE_SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,				surface3_spi_suspend,				surface3_spi_resume); #ifdef CONFIG_ACPIstatic const struct acpi_device_id surface3_spi_acpi_match[] = {	{ "MSHW0037", 0 },	{ }};MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);#endif static struct spi_driver surface3_spi_driver = {	.driver = {		.name	= "Surface3-spi",		.acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),		.pm = pm_sleep_ptr(&surface3_spi_pm_ops),	},	.probe = surface3_spi_probe,}; module_spi_driver(surface3_spi_driver); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");MODULE_LICENSE("GPL v2");