Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca4c8252 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: touchscreen: synaptics_dsx: add new touch driver"

parents 94a6d3e4 506d4711
Loading
Loading
Loading
Loading
+142 −0
Original line number Diff line number Diff line
#
# Synaptics DSX touchscreen driver configuration
#
menuconfig TOUCHSCREEN_SYNAPTICS_DSX
	bool "Synaptics DSX touchscreen"
	help
	  Say Y here if you have a Synaptics DSX touchscreen connected
	  to your system.

	  If unsure, say N.

if TOUCHSCREEN_SYNAPTICS_DSX

choice
	default TOUCHSCREEN_SYNAPTICS_DSX_I2C
	prompt "Synaptics DSX bus interface"
config TOUCHSCREEN_SYNAPTICS_DSX_I2C
	bool "RMI over I2C"
	depends on I2C
config TOUCHSCREEN_SYNAPTICS_DSX_SPI
	bool "RMI over SPI"
	depends on SPI_MASTER
config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C
	bool "HID over I2C"
	depends on I2C
endchoice

config TOUCHSCREEN_SYNAPTICS_DSX_CORE
	tristate "Synaptics DSX core driver module"
	depends on I2C || SPI_MASTER
	default y
	help
	  Say Y here to enable basic touch reporting functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_core.

config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV
	tristate "Synaptics DSX RMI device module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	default y
	help
	  Say Y here to enable support for direct RMI register access.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_rmi_dev.

config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
	tristate "Synaptics DSX firmware update module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	default y
	help
	  Say Y here to enable support for doing firmware update.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_fw_update.

config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
	bool "Synaptics DSX firmware update sysfs attributes"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
	default y
	help
	  Say Y here to enable support for sysfs attributes for
	  performing firmware update in a development environment.
	  This does not affect the core or other subsystem attributes.

	  If unsure, say N.

config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING
	tristate "Synaptics DSX test reporting module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	default y
	help
	  Say Y here to enable support for retrieving production test reports.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_test_reporting.

config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY
	tristate "Synaptics DSX proximity module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	help
	  Say Y here to enable support for proximity functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_proximity.

config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN
	tristate "Synaptics DSX active pen module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	help
	  Say Y here to enable support for active pen functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_active_pen.

config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE
	tristate "Synaptics DSX user defined gesture module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	help
	  Say Y here to enable support for user defined gesture functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_gesture.

config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO
	tristate "Synaptics DSX video module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	help
	  Say Y here to enable support for video communication functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_video.

config TOUCHSCREEN_SYNAPTICS_DSX_DEBUG
	tristate "Synaptics DSX debug module"
	depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
	help
	  Say Y here to enable support for firmware debug functionality.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called synaptics_dsx_debug.

endif
+18 −0
Original line number Diff line number Diff line
#
# Makefile for the Synaptics DSX touchscreen driver.
#

# Each configuration option enables a list of files.

obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI) += synaptics_dsx_spi.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C) += synaptics_dsx_rmi_hid_i2c.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE) += synaptics_dsx_core.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV) += synaptics_dsx_rmi_dev.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_dsx_fw_update.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING) += synaptics_dsx_test_reporting.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY) += synaptics_dsx_proximity.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN) += synaptics_dsx_active_pen.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE) += synaptics_dsx_gesture.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO) += synaptics_dsx_video.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_DEBUG) += synaptics_dsx_debug.o
+608 −0
Original line number Diff line number Diff line
/*
 * Synaptics DSX touchscreen driver
 *
 * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
 *
 * Copyright (c) 2018 The Linux Foundation. All rights reserved.
 * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
 * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
 * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
 * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
 * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
 * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
 * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
 * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
 * DOLLARS.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/input/synaptics_dsx.h>
#include "synaptics_dsx_core.h"

#define APEN_PHYS_NAME "synaptics_dsx/active_pen"

#define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535
#define ACTIVE_PEN_MAX_PRESSURE_8BIT 255

struct synaptics_rmi4_f12_query_8 {
	union {
		struct {
			unsigned char size_of_query9;
			struct {
				unsigned char data0_is_present:1;
				unsigned char data1_is_present:1;
				unsigned char data2_is_present:1;
				unsigned char data3_is_present:1;
				unsigned char data4_is_present:1;
				unsigned char data5_is_present:1;
				unsigned char data6_is_present:1;
				unsigned char data7_is_present:1;
			} __packed;
		};
		unsigned char data[2];
	};
};

struct apen_data_8b_pressure {
	union {
		struct {
			unsigned char status_pen:1;
			unsigned char status_invert:1;
			unsigned char status_barrel:1;
			unsigned char status_reserved:5;
			unsigned char x_lsb;
			unsigned char x_msb;
			unsigned char y_lsb;
			unsigned char y_msb;
			unsigned char pressure_msb;
			unsigned char battery_state;
			unsigned char pen_id_0_7;
			unsigned char pen_id_8_15;
			unsigned char pen_id_16_23;
			unsigned char pen_id_24_31;
		} __packed;
		unsigned char data[11];
	};
};

struct apen_data {
	union {
		struct {
			unsigned char status_pen:1;
			unsigned char status_invert:1;
			unsigned char status_barrel:1;
			unsigned char status_reserved:5;
			unsigned char x_lsb;
			unsigned char x_msb;
			unsigned char y_lsb;
			unsigned char y_msb;
			unsigned char pressure_lsb;
			unsigned char pressure_msb;
			unsigned char battery_state;
			unsigned char pen_id_0_7;
			unsigned char pen_id_8_15;
			unsigned char pen_id_16_23;
			unsigned char pen_id_24_31;
		} __packed;
		unsigned char data[12];
	};
};

struct synaptics_rmi4_apen_handle {
	bool apen_present;
	unsigned char intr_mask;
	unsigned char battery_state;
	unsigned short query_base_addr;
	unsigned short control_base_addr;
	unsigned short data_base_addr;
	unsigned short command_base_addr;
	unsigned short apen_data_addr;
	unsigned short max_pressure;
	unsigned int pen_id;
	struct input_dev *apen_dev;
	struct apen_data *apen_data;
	struct synaptics_rmi4_data *rmi4_data;
};

static struct synaptics_rmi4_apen_handle *apen;

DECLARE_COMPLETION(apen_remove_complete);

static void apen_lift(void)
{
	input_report_key(apen->apen_dev, BTN_TOUCH, 0);
	input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0);
	input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0);
	input_sync(apen->apen_dev);
	apen->apen_present = false;
}

static void apen_report(void)
{
	int retval;
	int x;
	int y;
	int pressure;
	static int invert = -1;
	struct apen_data_8b_pressure *apen_data_8b;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->apen_data_addr,
			apen->apen_data->data,
			sizeof(apen->apen_data->data));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to read active pen data\n",
				__func__);
		return;
	}

	if (apen->apen_data->status_pen == 0) {
		if (apen->apen_present)
			apen_lift();

		dev_dbg(rmi4_data->pdev->dev.parent,
				"%s: No active pen data\n",
				__func__);

		return;
	}

	x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb);
	y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb);

	if ((x == -1) && (y == -1)) {
		if (apen->apen_present)
			apen_lift();

		dev_dbg(rmi4_data->pdev->dev.parent,
				"%s: Active pen in range but no valid x & y\n",
				__func__);

		return;
	}

	if (!apen->apen_present)
		invert = -1;

	if (invert != -1 && invert != apen->apen_data->status_invert)
		apen_lift();

	invert = apen->apen_data->status_invert;

	if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) {
		pressure = (apen->apen_data->pressure_msb << 8) |
				apen->apen_data->pressure_lsb;
		apen->battery_state = apen->apen_data->battery_state;
		apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) |
				(apen->apen_data->pen_id_16_23 << 16) |
				(apen->apen_data->pen_id_8_15 << 8) |
				apen->apen_data->pen_id_0_7;
	} else {
		apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data;
		pressure = apen_data_8b->pressure_msb;
		apen->battery_state = apen_data_8b->battery_state;
		apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) |
				(apen_data_8b->pen_id_16_23 << 16) |
				(apen_data_8b->pen_id_8_15 << 8) |
				apen_data_8b->pen_id_0_7;
	}

	input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0);
	input_report_key(apen->apen_dev,
			apen->apen_data->status_invert > 0 ?
			BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1);
	input_report_key(apen->apen_dev,
			BTN_STYLUS, apen->apen_data->status_barrel > 0 ?
			1 : 0);
	input_report_abs(apen->apen_dev, ABS_X, x);
	input_report_abs(apen->apen_dev, ABS_Y, y);
	input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure);

	input_sync(apen->apen_dev);

	dev_dbg(rmi4_data->pdev->dev.parent,
			"%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n",
			__func__,
			apen->apen_data->status_pen,
			apen->apen_data->status_invert,
			apen->apen_data->status_barrel,
			x, y, pressure);

	apen->apen_present = true;
}

static void apen_set_params(void)
{
	input_set_abs_params(apen->apen_dev, ABS_X, 0,
			apen->rmi4_data->sensor_max_x, 0, 0);
	input_set_abs_params(apen->apen_dev, ABS_Y, 0,
			apen->rmi4_data->sensor_max_y, 0, 0);
	input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0,
			apen->max_pressure, 0, 0);

	return;
}

static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8)
{
	int retval;
	unsigned char ii;
	unsigned char data_reg_presence;
	unsigned char size_of_query_9;
	unsigned char *query_9;
	unsigned char *data_desc;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	data_reg_presence = query_8->data[1];

	size_of_query_9 = query_8->size_of_query9;
	query_9 = kmalloc(size_of_query_9, GFP_KERNEL);

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 9,
			query_9,
			size_of_query_9);
	if (retval < 0)
		goto exit;

	data_desc = query_9;

	for (ii = 0; ii < 6; ii++) {
		if (!(data_reg_presence & (1 << ii)))
			continue; /* The data register is not present */
		data_desc++; /* Jump over the size entry */
		while (*data_desc & (1 << 7))
			data_desc++;
		data_desc++; /* Go to the next descriptor */
	}

	data_desc++; /* Jump over the size entry */
	/* Check for the presence of subpackets 1 and 2 */
	if ((*data_desc & (3 << 1)) == (3 << 1))
		apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT;
	else
		apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT;

exit:
	kfree(query_9);

	return retval;
}

static int apen_reg_init(void)
{
	int retval;
	unsigned char data_offset;
	unsigned char size_of_query8;
	struct synaptics_rmi4_f12_query_8 query_8;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 7,
			&size_of_query8,
			sizeof(size_of_query8));
	if (retval < 0)
		return retval;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 8,
			query_8.data,
			sizeof(query_8.data));
	if (retval < 0)
		return retval;

	if ((size_of_query8 >= 2) && (query_8.data6_is_present)) {
		data_offset = query_8.data0_is_present +
				query_8.data1_is_present +
				query_8.data2_is_present +
				query_8.data3_is_present +
				query_8.data4_is_present +
				query_8.data5_is_present;
		apen->apen_data_addr = apen->data_base_addr + data_offset;
		retval = apen_pressure(&query_8);
		if (retval < 0)
			return retval;
	} else {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Active pen support unavailable\n",
				__func__);
		retval = -ENODEV;
	}

	return retval;
}

static int apen_scan_pdt(void)
{
	int retval;
	unsigned char ii;
	unsigned char page;
	unsigned char intr_count = 0;
	unsigned char intr_off;
	unsigned char intr_src;
	unsigned short addr;
	struct synaptics_rmi4_fn_desc fd;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	for (page = 0; page < PAGES_TO_SERVICE; page++) {
		for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
			addr |= (page << 8);

			retval = synaptics_rmi4_reg_read(rmi4_data,
					addr,
					(unsigned char *)&fd,
					sizeof(fd));
			if (retval < 0)
				return retval;

			addr &= ~(MASK_8BIT << 8);

			if (fd.fn_number) {
				dev_dbg(rmi4_data->pdev->dev.parent,
						"%s: Found F%02x\n",
						__func__, fd.fn_number);
				switch (fd.fn_number) {
				case SYNAPTICS_RMI4_F12:
					goto f12_found;
				}
			} else {
				break;
			}

			intr_count += fd.intr_src_count;
		}
	}

	dev_err(rmi4_data->pdev->dev.parent,
			"%s: Failed to find F12\n",
			__func__);
	return -EINVAL;

f12_found:
	apen->query_base_addr = fd.query_base_addr | (page << 8);
	apen->control_base_addr = fd.ctrl_base_addr | (page << 8);
	apen->data_base_addr = fd.data_base_addr | (page << 8);
	apen->command_base_addr = fd.cmd_base_addr | (page << 8);

	retval = apen_reg_init();
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to initialize active pen registers\n",
				__func__);
		return retval;
	}

	apen->intr_mask = 0;
	intr_src = fd.intr_src_count;
	intr_off = intr_count % 8;
	for (ii = intr_off;
			ii < (intr_src + intr_off);
			ii++) {
		apen->intr_mask |= 1 << ii;
	}

	rmi4_data->intr_mask[0] |= apen->intr_mask;

	addr = rmi4_data->f01_ctrl_base_addr + 1;

	retval = synaptics_rmi4_reg_write(rmi4_data,
			addr,
			&(rmi4_data->intr_mask[0]),
			sizeof(rmi4_data->intr_mask[0]));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to set interrupt enable bit\n",
				__func__);
		return retval;
	}

	return 0;
}

static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data,
		unsigned char intr_mask)
{
	if (!apen)
		return;

	if (apen->intr_mask & intr_mask)
		apen_report();

	return;
}

static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data)
{
	int retval;

	if (apen) {
		dev_dbg(rmi4_data->pdev->dev.parent,
				"%s: Handle already exists\n",
				__func__);
		return 0;
	}

	apen = kzalloc(sizeof(*apen), GFP_KERNEL);
	if (!apen) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to alloc mem for apen\n",
				__func__);
		retval = -ENOMEM;
		goto exit;
	}

	apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL);
	if (!apen->apen_data) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to alloc mem for apen_data\n",
				__func__);
		retval = -ENOMEM;
		goto exit_free_apen;
	}

	apen->rmi4_data = rmi4_data;

	retval = apen_scan_pdt();
	if (retval < 0)
		goto exit_free_apen_data;

	apen->apen_dev = input_allocate_device();
	if (apen->apen_dev == NULL) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to allocate active pen device\n",
				__func__);
		retval = -ENOMEM;
		goto exit_free_apen_data;
	}

	apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME;
	apen->apen_dev->phys = APEN_PHYS_NAME;
	apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
	apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
	apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent;
	input_set_drvdata(apen->apen_dev, rmi4_data);

	set_bit(EV_KEY, apen->apen_dev->evbit);
	set_bit(EV_ABS, apen->apen_dev->evbit);
	set_bit(BTN_TOUCH, apen->apen_dev->keybit);
	set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit);
	set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit);
	set_bit(BTN_STYLUS, apen->apen_dev->keybit);
#ifdef INPUT_PROP_DIRECT
	set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit);
#endif

	apen_set_params();

	retval = input_register_device(apen->apen_dev);
	if (retval) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to register active pen device\n",
				__func__);
		goto exit_free_input_device;
	}

	return 0;

exit_free_input_device:
	input_free_device(apen->apen_dev);

exit_free_apen_data:
	kfree(apen->apen_data);

exit_free_apen:
	kfree(apen);
	apen = NULL;

exit:
	return retval;
}

static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data)
{
	if (!apen)
		goto exit;

	input_unregister_device(apen->apen_dev);
	kfree(apen->apen_data);
	kfree(apen);
	apen = NULL;

exit:
	complete(&apen_remove_complete);
}

static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data)
{
	if (!apen) {
		synaptics_rmi4_apen_init(rmi4_data);
		return;
	}

	apen_lift();

	apen_scan_pdt();
}

static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data)
{
	if (!apen)
		return;

	apen_lift();
}

static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data)
{
	if (!apen)
		return;

	apen_lift();
}

static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data)
{
	if (!apen)
		return;

	apen_lift();
}

static struct synaptics_rmi4_exp_fn active_pen_module = {
	.fn_type = RMI_ACTIVE_PEN,
	.init = synaptics_rmi4_apen_init,
	.remove = synaptics_rmi4_apen_remove,
	.reset = synaptics_rmi4_apen_reset,
	.reinit = synaptics_rmi4_apen_reinit,
	.early_suspend = synaptics_rmi4_apen_e_suspend,
	.suspend = synaptics_rmi4_apen_suspend,
	.resume = NULL,
	.late_resume = NULL,
	.attn = synaptics_rmi4_apen_attn,
};

static int __init rmi4_active_pen_module_init(void)
{
	synaptics_rmi4_new_function(&active_pen_module, true);

	return 0;
}

static void __exit rmi4_active_pen_module_exit(void)
{
	synaptics_rmi4_new_function(&active_pen_module, false);

	wait_for_completion(&apen_remove_complete);
}

module_init(rmi4_active_pen_module_init);
module_exit(rmi4_active_pen_module_exit);

MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX Active Pen Module");
MODULE_LICENSE("GPL v2");
+4943 −0

File added.

Preview size limit exceeded, changes collapsed.

+538 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading