672 lines
16 KiB
C
Executable File
672 lines
16 KiB
C
Executable File
/*
|
|
* Synaptics DSX touchscreen driver
|
|
*
|
|
* Copyright (C) 2012 Synaptics Incorporated
|
|
*
|
|
* 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.
|
|
*/
|
|
#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_v2.h>
|
|
#include "synaptics_dsx_core.h"
|
|
|
|
#define PROX_PHYS_NAME "synaptics_dsx/input1"
|
|
|
|
#define HOVER_Z_MAX (255)
|
|
|
|
#define HOVERING_FINGER_EN (1 << 4)
|
|
|
|
static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
|
|
static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count);
|
|
|
|
static struct device_attribute attrs[] = {
|
|
__ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
|
|
synaptics_rmi4_hover_finger_en_show,
|
|
synaptics_rmi4_hover_finger_en_store),
|
|
};
|
|
|
|
struct synaptics_rmi4_f12_query_5 {
|
|
union {
|
|
struct {
|
|
unsigned char size_of_query6;
|
|
struct {
|
|
unsigned char ctrl0_is_present:1;
|
|
unsigned char ctrl1_is_present:1;
|
|
unsigned char ctrl2_is_present:1;
|
|
unsigned char ctrl3_is_present:1;
|
|
unsigned char ctrl4_is_present:1;
|
|
unsigned char ctrl5_is_present:1;
|
|
unsigned char ctrl6_is_present:1;
|
|
unsigned char ctrl7_is_present:1;
|
|
} __packed;
|
|
struct {
|
|
unsigned char ctrl8_is_present:1;
|
|
unsigned char ctrl9_is_present:1;
|
|
unsigned char ctrl10_is_present:1;
|
|
unsigned char ctrl11_is_present:1;
|
|
unsigned char ctrl12_is_present:1;
|
|
unsigned char ctrl13_is_present:1;
|
|
unsigned char ctrl14_is_present:1;
|
|
unsigned char ctrl15_is_present:1;
|
|
} __packed;
|
|
struct {
|
|
unsigned char ctrl16_is_present:1;
|
|
unsigned char ctrl17_is_present:1;
|
|
unsigned char ctrl18_is_present:1;
|
|
unsigned char ctrl19_is_present:1;
|
|
unsigned char ctrl20_is_present:1;
|
|
unsigned char ctrl21_is_present:1;
|
|
unsigned char ctrl22_is_present:1;
|
|
unsigned char ctrl23_is_present:1;
|
|
} __packed;
|
|
};
|
|
unsigned char data[4];
|
|
};
|
|
};
|
|
|
|
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 prox_finger_data {
|
|
union {
|
|
struct {
|
|
unsigned char object_type_and_status;
|
|
unsigned char x_lsb;
|
|
unsigned char x_msb;
|
|
unsigned char y_lsb;
|
|
unsigned char y_msb;
|
|
unsigned char z;
|
|
} __packed;
|
|
unsigned char proximity_data[6];
|
|
};
|
|
};
|
|
|
|
struct synaptics_rmi4_prox_handle {
|
|
bool hover_finger_present;
|
|
bool hover_finger_en;
|
|
unsigned char intr_mask;
|
|
unsigned short query_base_addr;
|
|
unsigned short control_base_addr;
|
|
unsigned short data_base_addr;
|
|
unsigned short command_base_addr;
|
|
unsigned short hover_finger_en_addr;
|
|
unsigned short hover_finger_data_addr;
|
|
struct input_dev *prox_dev;
|
|
struct prox_finger_data *finger_data;
|
|
struct synaptics_rmi4_data *rmi4_data;
|
|
};
|
|
|
|
static struct synaptics_rmi4_prox_handle *prox;
|
|
|
|
DECLARE_COMPLETION(prox_remove_complete);
|
|
|
|
static void prox_hover_finger_lift(void)
|
|
{
|
|
input_report_key(prox->prox_dev, BTN_TOUCH, 0);
|
|
input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0);
|
|
input_sync(prox->prox_dev);
|
|
prox->hover_finger_present = false;
|
|
|
|
return;
|
|
}
|
|
|
|
static void prox_hover_finger_report(void)
|
|
{
|
|
int retval;
|
|
int x;
|
|
int y;
|
|
int z;
|
|
struct prox_finger_data *data;
|
|
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
|
|
|
|
data = prox->finger_data;
|
|
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
prox->hover_finger_data_addr,
|
|
data->proximity_data,
|
|
sizeof(data->proximity_data));
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read hovering finger data\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
|
|
if (prox->hover_finger_present)
|
|
prox_hover_finger_lift();
|
|
|
|
return;
|
|
}
|
|
|
|
x = (data->x_msb << 8) | (data->x_lsb);
|
|
y = (data->y_msb << 8) | (data->y_lsb);
|
|
z = HOVER_Z_MAX - data->z;
|
|
|
|
input_report_key(prox->prox_dev, BTN_TOUCH, 0);
|
|
input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
|
|
input_report_abs(prox->prox_dev, ABS_X, x);
|
|
input_report_abs(prox->prox_dev, ABS_Y, y);
|
|
input_report_abs(prox->prox_dev, ABS_DISTANCE, z);
|
|
|
|
input_sync(prox->prox_dev);
|
|
|
|
dev_dbg(rmi4_data->pdev->dev.parent,
|
|
"%s: x = %d y = %d z = %d\n",
|
|
__func__, x, y, z);
|
|
|
|
prox->hover_finger_present = true;
|
|
|
|
return;
|
|
}
|
|
|
|
static int prox_set_hover_finger_en(void)
|
|
{
|
|
int retval;
|
|
unsigned char object_report_enable;
|
|
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
|
|
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
prox->hover_finger_en_addr,
|
|
&object_report_enable,
|
|
sizeof(object_report_enable));
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to read from object report enable register\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
|
|
if (prox->hover_finger_en)
|
|
object_report_enable |= HOVERING_FINGER_EN;
|
|
else
|
|
object_report_enable &= ~HOVERING_FINGER_EN;
|
|
|
|
retval = synaptics_rmi4_reg_write(rmi4_data,
|
|
prox->hover_finger_en_addr,
|
|
&object_report_enable,
|
|
sizeof(object_report_enable));
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to write to object report enable register\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void prox_set_params(void)
|
|
{
|
|
input_set_abs_params(prox->prox_dev, ABS_X, 0,
|
|
prox->rmi4_data->sensor_max_x, 0, 0);
|
|
input_set_abs_params(prox->prox_dev, ABS_Y, 0,
|
|
prox->rmi4_data->sensor_max_y, 0, 0);
|
|
input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0,
|
|
HOVER_Z_MAX, 0, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
static int prox_reg_init(void)
|
|
{
|
|
int retval;
|
|
unsigned char ctrl_23_offset;
|
|
unsigned char data_1_offset;
|
|
struct synaptics_rmi4_f12_query_5 query_5;
|
|
struct synaptics_rmi4_f12_query_8 query_8;
|
|
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
|
|
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
prox->query_base_addr + 5,
|
|
query_5.data,
|
|
sizeof(query_5.data));
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
ctrl_23_offset = query_5.ctrl0_is_present +
|
|
query_5.ctrl1_is_present +
|
|
query_5.ctrl2_is_present +
|
|
query_5.ctrl3_is_present +
|
|
query_5.ctrl4_is_present +
|
|
query_5.ctrl5_is_present +
|
|
query_5.ctrl6_is_present +
|
|
query_5.ctrl7_is_present +
|
|
query_5.ctrl8_is_present +
|
|
query_5.ctrl9_is_present +
|
|
query_5.ctrl10_is_present +
|
|
query_5.ctrl11_is_present +
|
|
query_5.ctrl12_is_present +
|
|
query_5.ctrl13_is_present +
|
|
query_5.ctrl14_is_present +
|
|
query_5.ctrl15_is_present +
|
|
query_5.ctrl16_is_present +
|
|
query_5.ctrl17_is_present +
|
|
query_5.ctrl18_is_present +
|
|
query_5.ctrl19_is_present +
|
|
query_5.ctrl20_is_present +
|
|
query_5.ctrl21_is_present +
|
|
query_5.ctrl22_is_present;
|
|
|
|
prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;
|
|
|
|
retval = synaptics_rmi4_reg_read(rmi4_data,
|
|
prox->query_base_addr + 8,
|
|
query_8.data,
|
|
sizeof(query_8.data));
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
data_1_offset = query_8.data0_is_present;
|
|
prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int prox_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 = prox->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;
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
intr_count += (fd.intr_src_count & MASK_3BIT);
|
|
}
|
|
}
|
|
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to find F12\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
|
|
f12_found:
|
|
prox->query_base_addr = fd.query_base_addr | (page << 8);
|
|
prox->control_base_addr = fd.ctrl_base_addr | (page << 8);
|
|
prox->data_base_addr = fd.data_base_addr | (page << 8);
|
|
prox->command_base_addr = fd.cmd_base_addr | (page << 8);
|
|
|
|
retval = prox_reg_init();
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to initialize proximity registers\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
|
|
prox->intr_mask = 0;
|
|
intr_src = fd.intr_src_count;
|
|
intr_off = intr_count % 8;
|
|
for (ii = intr_off;
|
|
ii < ((intr_src & MASK_3BIT) +
|
|
intr_off);
|
|
ii++) {
|
|
prox->intr_mask |= 1 << ii;
|
|
}
|
|
|
|
rmi4_data->intr_mask[0] |= prox->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 ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
if (!prox)
|
|
return -ENODEV;
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
|
prox->hover_finger_en);
|
|
}
|
|
|
|
static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
int retval;
|
|
unsigned int input;
|
|
struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
|
|
|
|
if (!prox)
|
|
return -ENODEV;
|
|
|
|
if (sscanf(buf, "%x", &input) != 1)
|
|
return -EINVAL;
|
|
|
|
if (input == 1)
|
|
prox->hover_finger_en = true;
|
|
else if (input == 0)
|
|
prox->hover_finger_en = false;
|
|
else
|
|
return -EINVAL;
|
|
|
|
retval = prox_set_hover_finger_en();
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to change hovering finger enable setting\n",
|
|
__func__);
|
|
return retval;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
int synaptics_rmi4_prox_hover_finger_en(bool enable)
|
|
{
|
|
int retval;
|
|
|
|
if (!prox)
|
|
return -ENODEV;
|
|
|
|
prox->hover_finger_en = enable;
|
|
|
|
retval = prox_set_hover_finger_en();
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en);
|
|
|
|
static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data,
|
|
unsigned char intr_mask)
|
|
{
|
|
if (!prox)
|
|
return;
|
|
|
|
if (prox->intr_mask & intr_mask)
|
|
prox_hover_finger_report();
|
|
|
|
return;
|
|
}
|
|
|
|
static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
int retval;
|
|
unsigned char attr_count;
|
|
|
|
prox = kzalloc(sizeof(*prox), GFP_KERNEL);
|
|
if (!prox) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to alloc mem for prox\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL);
|
|
if (!prox->finger_data) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to alloc mem for finger_data\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
goto exit_free_prox;
|
|
}
|
|
|
|
prox->rmi4_data = rmi4_data;
|
|
|
|
retval = prox_scan_pdt();
|
|
if (retval < 0)
|
|
goto exit_free_finger_data;
|
|
|
|
prox->hover_finger_en = true;
|
|
|
|
retval = prox_set_hover_finger_en();
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
prox->prox_dev = input_allocate_device();
|
|
if (prox->prox_dev == NULL) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to allocate proximity device\n",
|
|
__func__);
|
|
retval = -ENOMEM;
|
|
goto exit_free_finger_data;
|
|
}
|
|
|
|
prox->prox_dev->name = PLATFORM_DRIVER_NAME;
|
|
prox->prox_dev->phys = PROX_PHYS_NAME;
|
|
prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
|
|
prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
|
|
prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent;
|
|
input_set_drvdata(prox->prox_dev, rmi4_data);
|
|
|
|
set_bit(EV_KEY, prox->prox_dev->evbit);
|
|
set_bit(EV_ABS, prox->prox_dev->evbit);
|
|
set_bit(BTN_TOUCH, prox->prox_dev->keybit);
|
|
set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit);
|
|
#ifdef INPUT_PROP_DIRECT
|
|
set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit);
|
|
#endif
|
|
|
|
prox_set_params();
|
|
|
|
retval = input_register_device(prox->prox_dev);
|
|
if (retval) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to register proximity device\n",
|
|
__func__);
|
|
goto exit_free_input_device;
|
|
}
|
|
|
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
|
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
|
|
&attrs[attr_count].attr);
|
|
if (retval < 0) {
|
|
dev_err(rmi4_data->pdev->dev.parent,
|
|
"%s: Failed to create sysfs attributes\n",
|
|
__func__);
|
|
goto exit_free_sysfs;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit_free_sysfs:
|
|
for (attr_count--; attr_count >= 0; attr_count--) {
|
|
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
|
|
&attrs[attr_count].attr);
|
|
}
|
|
|
|
input_unregister_device(prox->prox_dev);
|
|
prox->prox_dev = NULL;
|
|
|
|
exit_free_input_device:
|
|
if (prox->prox_dev)
|
|
input_free_device(prox->prox_dev);
|
|
|
|
exit_free_finger_data:
|
|
kfree(prox->finger_data);
|
|
|
|
exit_free_prox:
|
|
kfree(prox);
|
|
prox = NULL;
|
|
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
unsigned char attr_count;
|
|
|
|
if (!prox)
|
|
goto exit;
|
|
|
|
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
|
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
|
|
&attrs[attr_count].attr);
|
|
}
|
|
|
|
input_unregister_device(prox->prox_dev);
|
|
kfree(prox->finger_data);
|
|
kfree(prox);
|
|
prox = NULL;
|
|
|
|
exit:
|
|
complete(&prox_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
if (!prox)
|
|
return;
|
|
|
|
prox_hover_finger_lift();
|
|
|
|
prox_scan_pdt();
|
|
|
|
prox_set_hover_finger_en();
|
|
|
|
prox_set_params();
|
|
|
|
return;
|
|
}
|
|
|
|
static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
if (!prox)
|
|
return;
|
|
|
|
prox_hover_finger_lift();
|
|
|
|
prox_set_hover_finger_en();
|
|
|
|
return;
|
|
}
|
|
|
|
static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
if (!prox)
|
|
return;
|
|
|
|
prox_hover_finger_lift();
|
|
|
|
return;
|
|
}
|
|
|
|
static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data)
|
|
{
|
|
if (!prox)
|
|
return;
|
|
|
|
prox_hover_finger_lift();
|
|
|
|
return;
|
|
}
|
|
|
|
static struct synaptics_rmi4_exp_fn proximity_module = {
|
|
.fn_type = RMI_PROXIMITY,
|
|
.init = synaptics_rmi4_prox_init,
|
|
.remove = synaptics_rmi4_prox_remove,
|
|
.reset = synaptics_rmi4_prox_reset,
|
|
.reinit = synaptics_rmi4_prox_reinit,
|
|
.early_suspend = synaptics_rmi4_prox_e_suspend,
|
|
.suspend = synaptics_rmi4_prox_suspend,
|
|
.resume = NULL,
|
|
.late_resume = NULL,
|
|
.attn = synaptics_rmi4_prox_attn,
|
|
};
|
|
|
|
static int __init rmi4_proximity_module_init(void)
|
|
{
|
|
synaptics_rmi4_dsx_new_function(&proximity_module, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit rmi4_proximity_module_exit(void)
|
|
{
|
|
synaptics_rmi4_dsx_new_function(&proximity_module, false);
|
|
|
|
wait_for_completion(&prox_remove_complete);
|
|
|
|
return;
|
|
}
|
|
|
|
module_init(rmi4_proximity_module_init);
|
|
module_exit(rmi4_proximity_module_exit);
|
|
|
|
MODULE_AUTHOR("Synaptics, Inc.");
|
|
MODULE_DESCRIPTION("Synaptics DSX Proximity Module");
|
|
MODULE_LICENSE("GPL v2");
|