Merge branch 'synaptics-rmi4' into next
Bring in support for devices using Synaptics RMI4 protocol, including RMI4 bus, 2D sensor and button handlers, and SPI and I2C interface drivers.
This commit is contained in:
commit
62d5bdf972
@ -0,0 +1,56 @@
|
|||||||
|
Synaptics RMI4 2D Sensor Device Binding
|
||||||
|
|
||||||
|
The Synaptics RMI4 core is able to support RMI4 devices using different
|
||||||
|
transports and different functions. This file describes the device tree
|
||||||
|
bindings for devices which contain 2D sensors using Function 11 or
|
||||||
|
Function 12. Complete documentation for transports and other functions
|
||||||
|
can be found in:
|
||||||
|
Documentation/devicetree/bindings/input/rmi4.
|
||||||
|
|
||||||
|
RMI4 Function 11 and Function 12 are for 2D touch position sensing.
|
||||||
|
Additional documentation for F11 can be found at:
|
||||||
|
http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
|
||||||
|
|
||||||
|
Optional Touch Properties:
|
||||||
|
Description in Documentation/devicetree/bindings/input/touch
|
||||||
|
- touchscreen-inverted-x
|
||||||
|
- touchscreen-inverted-y
|
||||||
|
- touchscreen-swapped-x-y
|
||||||
|
- touchscreen-x-mm
|
||||||
|
- touchscreen-y-mm
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- syna,clip-x-low: Sets a minimum value for X.
|
||||||
|
- syna,clip-y-low: Sets a minimum value for Y.
|
||||||
|
- syna,clip-x-high: Sets a maximum value for X.
|
||||||
|
- syna,clip-y-high: Sets a maximum value for Y.
|
||||||
|
- syna,offset-x: Add an offset to X.
|
||||||
|
- syna,offset-y: Add an offset to Y.
|
||||||
|
- syna,delta-x-threshold: Set the minimum distance on the X axis required
|
||||||
|
to generate an interrupt in reduced reporting
|
||||||
|
mode.
|
||||||
|
- syna,delta-y-threshold: Set the minimum distance on the Y axis required
|
||||||
|
to generate an interrupt in reduced reporting
|
||||||
|
mode.
|
||||||
|
- syna,sensor-type: Set the sensor type. 1 for touchscreen 2 for touchpad.
|
||||||
|
- syna,disable-report-mask: Mask for disabling posiiton reporting. Used to
|
||||||
|
disable reporing absolute position data.
|
||||||
|
- syna,rezero-wait-ms: Time in miliseconds to wait after issuing a rezero
|
||||||
|
command.
|
||||||
|
|
||||||
|
|
||||||
|
Example of a RMI4 I2C device with F11:
|
||||||
|
Example:
|
||||||
|
&i2c1 {
|
||||||
|
rmi4-i2c-dev@2c {
|
||||||
|
compatible = "syna,rmi4-i2c";
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
rmi4-f11@11 {
|
||||||
|
reg = <0x11>;
|
||||||
|
touchscreen-inverted-y;
|
||||||
|
syna,sensor-type = <2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
39
Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
Normal file
39
Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Synaptics RMI4 F01 Device Binding
|
||||||
|
|
||||||
|
The Synaptics RMI4 core is able to support RMI4 devices using different
|
||||||
|
transports and different functions. This file describes the device tree
|
||||||
|
bindings for devices which contain Function 1. Complete documentation
|
||||||
|
for transports and other functions can be found in:
|
||||||
|
Documentation/devicetree/bindings/input/rmi4.
|
||||||
|
|
||||||
|
Additional documentation for F01 can be found at:
|
||||||
|
http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- syna,nosleep-mode: If set the device will run at full power without sleeping.
|
||||||
|
nosleep has 3 modes, 0 will not change the default
|
||||||
|
setting, 1 will disable nosleep (allow sleeping),
|
||||||
|
and 2 will enable nosleep (disabling sleep).
|
||||||
|
- syna,wakeup-threshold: Defines the amplitude of the disturbance to the
|
||||||
|
background capacitance that will cause the
|
||||||
|
device to wake from dozing.
|
||||||
|
- syna,doze-holdoff-ms: The delay to wait after the last finger lift and the
|
||||||
|
first doze cycle.
|
||||||
|
- syna,doze-interval-ms: The time period that the device sleeps between finger
|
||||||
|
activity.
|
||||||
|
|
||||||
|
|
||||||
|
Example of a RMI4 I2C device with F01:
|
||||||
|
Example:
|
||||||
|
&i2c1 {
|
||||||
|
rmi4-i2c-dev@2c {
|
||||||
|
compatible = "syna,rmi4-i2c";
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
rmi4-f01@1 {
|
||||||
|
reg = <0x1>;
|
||||||
|
syna,nosleep-mode = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
53
Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
Normal file
53
Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
Synaptics RMI4 I2C Device Binding
|
||||||
|
|
||||||
|
The Synaptics RMI4 core is able to support RMI4 devices using different
|
||||||
|
transports and different functions. This file describes the device tree
|
||||||
|
bindings for devices using the I2C transport driver. Complete documentation
|
||||||
|
for other transports and functions can be found in
|
||||||
|
Documentation/devicetree/bindings/input/rmi4.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
- compatible: syna,rmi4-i2c
|
||||||
|
- reg: I2C address
|
||||||
|
- #address-cells: Set to 1 to indicate that the function child nodes
|
||||||
|
consist of only on uint32 value.
|
||||||
|
- #size-cells: Set to 0 to indicate that the function child nodes do not
|
||||||
|
have a size property.
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- interrupts: interrupt which the rmi device is connected to.
|
||||||
|
- interrupt-parent: The interrupt controller.
|
||||||
|
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
|
|
||||||
|
- syna,reset-delay-ms: The number of milliseconds to wait after resetting the
|
||||||
|
device.
|
||||||
|
|
||||||
|
Function Parameters:
|
||||||
|
Parameters specific to RMI functions are contained in child nodes of the rmi device
|
||||||
|
node. Documentation for the parameters of each function can be found in:
|
||||||
|
Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
&i2c1 {
|
||||||
|
rmi4-i2c-dev@2c {
|
||||||
|
compatible = "syna,rmi4-i2c";
|
||||||
|
reg = <0x2c>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupt-parent = <&gpio>;
|
||||||
|
interrupts = <4 2>;
|
||||||
|
|
||||||
|
rmi4-f01@1 {
|
||||||
|
reg = <0x1>;
|
||||||
|
syna,nosleep-mode = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
rmi4-f11@11 {
|
||||||
|
reg = <0x11>;
|
||||||
|
touchscreen-inverted-y;
|
||||||
|
syna,sensor-type = <2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
57
Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt
Normal file
57
Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
Synaptics RMI4 SPI Device Binding
|
||||||
|
|
||||||
|
The Synaptics RMI4 core is able to support RMI4 devices using different
|
||||||
|
transports and different functions. This file describes the device tree
|
||||||
|
bindings for devices using the SPI transport driver. Complete documentation
|
||||||
|
for other transports and functions can be found in
|
||||||
|
Documentation/devicetree/bindings/input/rmi4.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
- compatible: syna,rmi4-spi
|
||||||
|
- reg: Chip select address for the device
|
||||||
|
- #address-cells: Set to 1 to indicate that the function child nodes
|
||||||
|
consist of only on uint32 value.
|
||||||
|
- #size-cells: Set to 0 to indicate that the function child nodes do not
|
||||||
|
have a size property.
|
||||||
|
|
||||||
|
Optional Properties:
|
||||||
|
- interrupts: interrupt which the rmi device is connected to.
|
||||||
|
- interrupt-parent: The interrupt controller.
|
||||||
|
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
|
|
||||||
|
- spi-rx-delay-us: microsecond delay after a read transfer.
|
||||||
|
- spi-tx-delay-us: microsecond delay after a write transfer.
|
||||||
|
|
||||||
|
Function Parameters:
|
||||||
|
Parameters specific to RMI functions are contained in child nodes of the rmi device
|
||||||
|
node. Documentation for the parameters of each function can be found in:
|
||||||
|
Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
spi@7000d800 {
|
||||||
|
rmi4-spi-dev@0 {
|
||||||
|
compatible = "syna,rmi4-spi";
|
||||||
|
reg = <0x0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
spi-max-frequency = <4000000>;
|
||||||
|
spi-cpha;
|
||||||
|
spi-cpol;
|
||||||
|
interrupt-parent = <&gpio>;
|
||||||
|
interrupts = <TEGRA_GPIO(K, 2) 0x2>;
|
||||||
|
spi-rx-delay-us = <30>;
|
||||||
|
|
||||||
|
rmi4-f01@1 {
|
||||||
|
reg = <0x1>;
|
||||||
|
syna,nosleep-mode = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
rmi4-f11@11 {
|
||||||
|
reg = <0x11>;
|
||||||
|
touchscreen-inverted-y;
|
||||||
|
syna,sensor-type = <2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -18,6 +18,8 @@ Optional properties for Touchscreens:
|
|||||||
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
||||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||||
Swapping is done after inverting the axis
|
Swapping is done after inverting the axis
|
||||||
|
- touchscreen-x-mm : horizontal length in mm of the touchscreen
|
||||||
|
- touchscreen-y-mm : vertical length in mm of the touchscreen
|
||||||
|
|
||||||
Deprecated properties for Touchscreens:
|
Deprecated properties for Touchscreens:
|
||||||
- x-size : deprecated name for touchscreen-size-x
|
- x-size : deprecated name for touchscreen-size-x
|
||||||
|
@ -61,6 +61,8 @@ contain the following properties.
|
|||||||
used for MOSI. Defaults to 1 if not present.
|
used for MOSI. Defaults to 1 if not present.
|
||||||
- spi-rx-bus-width - (optional) The bus width(number of data wires) that
|
- spi-rx-bus-width - (optional) The bus width(number of data wires) that
|
||||||
used for MISO. Defaults to 1 if not present.
|
used for MISO. Defaults to 1 if not present.
|
||||||
|
- spi-rx-delay-us - (optional) Microsecond delay after a read transfer.
|
||||||
|
- spi-tx-delay-us - (optional) Microsecond delay after a write transfer.
|
||||||
|
|
||||||
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
|
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
|
||||||
It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD).
|
It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD).
|
||||||
|
@ -220,6 +220,7 @@ sprd Spreadtrum Communications Inc.
|
|||||||
st STMicroelectronics
|
st STMicroelectronics
|
||||||
ste ST-Ericsson
|
ste ST-Ericsson
|
||||||
stericsson ST-Ericsson
|
stericsson ST-Ericsson
|
||||||
|
syna Synaptics Inc.
|
||||||
synology Synology, Inc.
|
synology Synology, Inc.
|
||||||
tbs TBS Technologies
|
tbs TBS Technologies
|
||||||
tcl Toby Churchill Ltd.
|
tcl Toby Churchill Ltd.
|
||||||
|
@ -201,6 +201,8 @@ source "drivers/input/touchscreen/Kconfig"
|
|||||||
|
|
||||||
source "drivers/input/misc/Kconfig"
|
source "drivers/input/misc/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/input/rmi4/Kconfig"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
menu "Hardware I/O ports"
|
menu "Hardware I/O ports"
|
||||||
|
@ -26,3 +26,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
|
|||||||
obj-$(CONFIG_INPUT_MISC) += misc/
|
obj-$(CONFIG_INPUT_MISC) += misc/
|
||||||
|
|
||||||
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
|
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_RMI4_CORE) += rmi4/
|
||||||
|
63
drivers/input/rmi4/Kconfig
Normal file
63
drivers/input/rmi4/Kconfig
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#
|
||||||
|
# RMI4 configuration
|
||||||
|
#
|
||||||
|
config RMI4_CORE
|
||||||
|
tristate "Synaptics RMI4 bus support"
|
||||||
|
help
|
||||||
|
Say Y here if you want to support the Synaptics RMI4 bus. This is
|
||||||
|
required for all RMI4 device support.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config RMI4_I2C
|
||||||
|
tristate "RMI4 I2C Support"
|
||||||
|
depends on RMI4_CORE && I2C
|
||||||
|
help
|
||||||
|
Say Y here if you want to support RMI4 devices connected to an I2C
|
||||||
|
bus.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config RMI4_SPI
|
||||||
|
tristate "RMI4 SPI Support"
|
||||||
|
depends on RMI4_CORE && SPI
|
||||||
|
help
|
||||||
|
Say Y here if you want to support RMI4 devices connected to a SPI
|
||||||
|
bus.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config RMI4_2D_SENSOR
|
||||||
|
bool
|
||||||
|
depends on RMI4_CORE
|
||||||
|
|
||||||
|
config RMI4_F11
|
||||||
|
bool "RMI4 Function 11 (2D pointing)"
|
||||||
|
select RMI4_2D_SENSOR
|
||||||
|
depends on RMI4_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you want to add support for RMI4 function 11.
|
||||||
|
|
||||||
|
Function 11 provides 2D multifinger pointing for touchscreens and
|
||||||
|
touchpads. For sensors that support relative pointing, F11 also
|
||||||
|
provides mouse input.
|
||||||
|
|
||||||
|
config RMI4_F12
|
||||||
|
bool "RMI4 Function 12 (2D pointing)"
|
||||||
|
select RMI4_2D_SENSOR
|
||||||
|
depends on RMI4_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you want to add support for RMI4 function 12.
|
||||||
|
|
||||||
|
Function 12 provides 2D multifinger pointing for touchscreens and
|
||||||
|
touchpads. For sensors that support relative pointing, F12 also
|
||||||
|
provides mouse input.
|
||||||
|
|
||||||
|
config RMI4_F30
|
||||||
|
bool "RMI4 Function 30 (GPIO LED)"
|
||||||
|
depends on RMI4_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you want to add support for RMI4 function 30.
|
||||||
|
|
||||||
|
Function 30 provides GPIO and LED support for RMI4 devices. This
|
||||||
|
includes support for buttons on TouchPads and ClickPads.
|
13
drivers/input/rmi4/Makefile
Normal file
13
drivers/input/rmi4/Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
obj-$(CONFIG_RMI4_CORE) += rmi_core.o
|
||||||
|
rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
|
||||||
|
|
||||||
|
rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
|
||||||
|
|
||||||
|
# Function drivers
|
||||||
|
rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
|
||||||
|
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
|
||||||
|
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
|
||||||
|
|
||||||
|
# Transports
|
||||||
|
obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
|
||||||
|
obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
|
329
drivers/input/rmi4/rmi_2d_sensor.c
Normal file
329
drivers/input/rmi4/rmi_2d_sensor.c
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/mt.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
#include "rmi_2d_sensor.h"
|
||||||
|
|
||||||
|
#define RMI_2D_REL_POS_MIN -128
|
||||||
|
#define RMI_2D_REL_POS_MAX 127
|
||||||
|
|
||||||
|
/* maximum ABS_MT_POSITION displacement (in mm) */
|
||||||
|
#define DMAX 10
|
||||||
|
|
||||||
|
void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor,
|
||||||
|
struct rmi_2d_sensor_abs_object *obj,
|
||||||
|
int slot)
|
||||||
|
{
|
||||||
|
struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
|
||||||
|
|
||||||
|
/* we keep the previous values if the finger is released */
|
||||||
|
if (obj->type == RMI_2D_OBJECT_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (axis_align->swap_axes)
|
||||||
|
swap(obj->x, obj->y);
|
||||||
|
|
||||||
|
if (axis_align->flip_x)
|
||||||
|
obj->x = sensor->max_x - obj->x;
|
||||||
|
|
||||||
|
if (axis_align->flip_y)
|
||||||
|
obj->y = sensor->max_y - obj->y;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here checking if X offset or y offset are specified is
|
||||||
|
* redundant. We just add the offsets or clip the values.
|
||||||
|
*
|
||||||
|
* Note: offsets need to be applied before clipping occurs,
|
||||||
|
* or we could get funny values that are outside of
|
||||||
|
* clipping boundaries.
|
||||||
|
*/
|
||||||
|
obj->x += axis_align->offset_x;
|
||||||
|
obj->y += axis_align->offset_y;
|
||||||
|
|
||||||
|
obj->x = max(axis_align->clip_x_low, obj->x);
|
||||||
|
obj->y = max(axis_align->clip_y_low, obj->y);
|
||||||
|
|
||||||
|
if (axis_align->clip_x_high)
|
||||||
|
obj->x = min(sensor->max_x, obj->x);
|
||||||
|
|
||||||
|
if (axis_align->clip_y_high)
|
||||||
|
obj->y = min(sensor->max_y, obj->y);
|
||||||
|
|
||||||
|
sensor->tracking_pos[slot].x = obj->x;
|
||||||
|
sensor->tracking_pos[slot].y = obj->y;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_process);
|
||||||
|
|
||||||
|
void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor,
|
||||||
|
struct rmi_2d_sensor_abs_object *obj,
|
||||||
|
int slot)
|
||||||
|
{
|
||||||
|
struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
|
||||||
|
struct input_dev *input = sensor->input;
|
||||||
|
int wide, major, minor;
|
||||||
|
|
||||||
|
if (sensor->kernel_tracking)
|
||||||
|
input_mt_slot(input, sensor->tracking_slots[slot]);
|
||||||
|
else
|
||||||
|
input_mt_slot(input, slot);
|
||||||
|
|
||||||
|
input_mt_report_slot_state(input, obj->mt_tool,
|
||||||
|
obj->type != RMI_2D_OBJECT_NONE);
|
||||||
|
|
||||||
|
if (obj->type != RMI_2D_OBJECT_NONE) {
|
||||||
|
obj->x = sensor->tracking_pos[slot].x;
|
||||||
|
obj->y = sensor->tracking_pos[slot].y;
|
||||||
|
|
||||||
|
if (axis_align->swap_axes)
|
||||||
|
swap(obj->wx, obj->wy);
|
||||||
|
|
||||||
|
wide = (obj->wx > obj->wy);
|
||||||
|
major = max(obj->wx, obj->wy);
|
||||||
|
minor = min(obj->wx, obj->wy);
|
||||||
|
|
||||||
|
if (obj->type == RMI_2D_OBJECT_STYLUS) {
|
||||||
|
major = max(1, major);
|
||||||
|
minor = max(1, minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x);
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y);
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_ORIENTATION, wide);
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_PRESSURE, obj->z);
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
|
||||||
|
input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_2D_SENSOR, &sensor->input->dev,
|
||||||
|
"%s: obj[%d]: type: 0x%02x X: %d Y: %d Z: %d WX: %d WY: %d\n",
|
||||||
|
__func__, slot, obj->type, obj->x, obj->y, obj->z,
|
||||||
|
obj->wx, obj->wy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_report);
|
||||||
|
|
||||||
|
void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y)
|
||||||
|
{
|
||||||
|
struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
|
||||||
|
|
||||||
|
x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x));
|
||||||
|
y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y));
|
||||||
|
|
||||||
|
if (axis_align->swap_axes)
|
||||||
|
swap(x, y);
|
||||||
|
|
||||||
|
if (axis_align->flip_x)
|
||||||
|
x = min(RMI_2D_REL_POS_MAX, -x);
|
||||||
|
|
||||||
|
if (axis_align->flip_y)
|
||||||
|
y = min(RMI_2D_REL_POS_MAX, -y);
|
||||||
|
|
||||||
|
if (x || y) {
|
||||||
|
input_report_rel(sensor->input, REL_X, x);
|
||||||
|
input_report_rel(sensor->input, REL_Y, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_rel_report);
|
||||||
|
|
||||||
|
static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor)
|
||||||
|
{
|
||||||
|
struct input_dev *input = sensor->input;
|
||||||
|
int res_x;
|
||||||
|
int res_y;
|
||||||
|
int input_flags = 0;
|
||||||
|
|
||||||
|
if (sensor->report_abs) {
|
||||||
|
if (sensor->axis_align.swap_axes)
|
||||||
|
swap(sensor->max_x, sensor->max_y);
|
||||||
|
|
||||||
|
sensor->min_x = sensor->axis_align.clip_x_low;
|
||||||
|
if (sensor->axis_align.clip_x_high)
|
||||||
|
sensor->max_x = min(sensor->max_x,
|
||||||
|
sensor->axis_align.clip_x_high);
|
||||||
|
|
||||||
|
sensor->min_y = sensor->axis_align.clip_y_low;
|
||||||
|
if (sensor->axis_align.clip_y_high)
|
||||||
|
sensor->max_y = min(sensor->max_y,
|
||||||
|
sensor->axis_align.clip_y_high);
|
||||||
|
|
||||||
|
set_bit(EV_ABS, input->evbit);
|
||||||
|
input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x,
|
||||||
|
0, 0);
|
||||||
|
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
if (sensor->x_mm && sensor->y_mm) {
|
||||||
|
res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm;
|
||||||
|
res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm;
|
||||||
|
|
||||||
|
input_abs_set_res(input, ABS_X, res_x);
|
||||||
|
input_abs_set_res(input, ABS_Y, res_y);
|
||||||
|
|
||||||
|
input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
|
||||||
|
input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
|
||||||
|
|
||||||
|
if (!sensor->dmax)
|
||||||
|
sensor->dmax = DMAX * res_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
|
||||||
|
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
|
||||||
|
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
|
||||||
|
input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
if (sensor->sensor_type == rmi_sensor_touchpad)
|
||||||
|
input_flags = INPUT_MT_POINTER;
|
||||||
|
else
|
||||||
|
input_flags = INPUT_MT_DIRECT;
|
||||||
|
|
||||||
|
if (sensor->kernel_tracking)
|
||||||
|
input_flags |= INPUT_MT_TRACK;
|
||||||
|
|
||||||
|
input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensor->report_rel) {
|
||||||
|
set_bit(EV_REL, input->evbit);
|
||||||
|
set_bit(REL_X, input->relbit);
|
||||||
|
set_bit(REL_Y, input->relbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensor->topbuttonpad)
|
||||||
|
set_bit(INPUT_PROP_TOPBUTTONPAD, input->propbit);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_set_input_params);
|
||||||
|
|
||||||
|
int rmi_2d_sensor_configure_input(struct rmi_function *fn,
|
||||||
|
struct rmi_2d_sensor *sensor)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
|
||||||
|
|
||||||
|
if (!drv_data->input)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
sensor->input = drv_data->input;
|
||||||
|
rmi_2d_sensor_set_input_params(sensor);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_configure_input);
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
int rmi_2d_sensor_of_probe(struct device *dev,
|
||||||
|
struct rmi_2d_sensor_platform_data *pdata)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
pdata->axis_align.swap_axes = of_property_read_bool(dev->of_node,
|
||||||
|
"touchscreen-swapped-x-y");
|
||||||
|
|
||||||
|
pdata->axis_align.flip_x = of_property_read_bool(dev->of_node,
|
||||||
|
"touchscreen-inverted-x");
|
||||||
|
|
||||||
|
pdata->axis_align.flip_y = of_property_read_bool(dev->of_node,
|
||||||
|
"touchscreen-inverted-y");
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-low", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.clip_x_low = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-low", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.clip_y_low = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-high", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.clip_x_high = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-high", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.clip_y_high = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,offset-x", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.offset_x = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,offset-y", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.offset_y = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,delta-x-threshold",
|
||||||
|
1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.delta_x_threshold = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,delta-y-threshold",
|
||||||
|
1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->axis_align.delta_y_threshold = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, (u32 *)&pdata->sensor_type,
|
||||||
|
"syna,sensor-type", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "touchscreen-x-mm", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->x_mm = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "touchscreen-y-mm", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->y_mm = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val,
|
||||||
|
"syna,disable-report-mask", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->disable_report_mask = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val, "syna,rezero-wait-ms",
|
||||||
|
1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->rezero_wait = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline int rmi_2d_sensor_of_probe(struct device *dev,
|
||||||
|
struct rmi_2d_sensor_platform_data *pdata)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_2d_sensor_of_probe);
|
87
drivers/input/rmi4/rmi_2d_sensor.h
Normal file
87
drivers/input/rmi4/rmi_2d_sensor.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RMI_2D_SENSOR_H
|
||||||
|
#define _RMI_2D_SENSOR_H
|
||||||
|
|
||||||
|
enum rmi_2d_sensor_object_type {
|
||||||
|
RMI_2D_OBJECT_NONE,
|
||||||
|
RMI_2D_OBJECT_FINGER,
|
||||||
|
RMI_2D_OBJECT_STYLUS,
|
||||||
|
RMI_2D_OBJECT_PALM,
|
||||||
|
RMI_2D_OBJECT_UNCLASSIFIED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmi_2d_sensor_abs_object {
|
||||||
|
enum rmi_2d_sensor_object_type type;
|
||||||
|
int mt_tool;
|
||||||
|
u16 x;
|
||||||
|
u16 y;
|
||||||
|
u8 z;
|
||||||
|
u8 wx;
|
||||||
|
u8 wy;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @axis_align - controls parameters that are useful in system prototyping
|
||||||
|
* and bring up.
|
||||||
|
* @max_x - The maximum X coordinate that will be reported by this sensor.
|
||||||
|
* @max_y - The maximum Y coordinate that will be reported by this sensor.
|
||||||
|
* @nbr_fingers - How many fingers can this sensor report?
|
||||||
|
* @data_pkt - buffer for data reported by this sensor.
|
||||||
|
* @pkt_size - number of bytes in that buffer.
|
||||||
|
* @attn_size - Size of the HID attention report (only contains abs data).
|
||||||
|
* position when two fingers are on the device. When this is true, we
|
||||||
|
* assume we have one of those sensors and report events appropriately.
|
||||||
|
* @sensor_type - indicates whether we're touchscreen or touchpad.
|
||||||
|
* @input - input device for absolute pointing stream
|
||||||
|
* @input_phys - buffer for the absolute phys name for this sensor.
|
||||||
|
*/
|
||||||
|
struct rmi_2d_sensor {
|
||||||
|
struct rmi_2d_axis_alignment axis_align;
|
||||||
|
struct input_mt_pos *tracking_pos;
|
||||||
|
int *tracking_slots;
|
||||||
|
bool kernel_tracking;
|
||||||
|
struct rmi_2d_sensor_abs_object *objs;
|
||||||
|
int dmax;
|
||||||
|
u16 min_x;
|
||||||
|
u16 max_x;
|
||||||
|
u16 min_y;
|
||||||
|
u16 max_y;
|
||||||
|
u8 nbr_fingers;
|
||||||
|
u8 *data_pkt;
|
||||||
|
int pkt_size;
|
||||||
|
int attn_size;
|
||||||
|
bool topbuttonpad;
|
||||||
|
enum rmi_sensor_type sensor_type;
|
||||||
|
struct input_dev *input;
|
||||||
|
struct rmi_function *fn;
|
||||||
|
char input_phys[32];
|
||||||
|
u8 report_abs;
|
||||||
|
u8 report_rel;
|
||||||
|
u8 x_mm;
|
||||||
|
u8 y_mm;
|
||||||
|
};
|
||||||
|
|
||||||
|
int rmi_2d_sensor_of_probe(struct device *dev,
|
||||||
|
struct rmi_2d_sensor_platform_data *pdata);
|
||||||
|
|
||||||
|
void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor,
|
||||||
|
struct rmi_2d_sensor_abs_object *obj,
|
||||||
|
int slot);
|
||||||
|
|
||||||
|
void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor,
|
||||||
|
struct rmi_2d_sensor_abs_object *obj,
|
||||||
|
int slot);
|
||||||
|
|
||||||
|
void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y);
|
||||||
|
|
||||||
|
int rmi_2d_sensor_configure_input(struct rmi_function *fn,
|
||||||
|
struct rmi_2d_sensor *sensor);
|
||||||
|
#endif /* _RMI_2D_SENSOR_H */
|
419
drivers/input/rmi4/rmi_bus.c
Normal file
419
drivers/input/rmi4/rmi_bus.c
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include "rmi_bus.h"
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
|
||||||
|
static int debug_flags;
|
||||||
|
module_param(debug_flags, int, 0644);
|
||||||
|
MODULE_PARM_DESC(debug_flags, "control debugging information");
|
||||||
|
|
||||||
|
void rmi_dbg(int flags, struct device *dev, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
struct va_format vaf;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (flags & debug_flags) {
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
vaf.fmt = fmt;
|
||||||
|
vaf.va = &args;
|
||||||
|
|
||||||
|
dev_printk(KERN_DEBUG, dev, "%pV", &vaf);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_dbg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RMI Physical devices
|
||||||
|
*
|
||||||
|
* Physical RMI device consists of several functions serving particular
|
||||||
|
* purpose. For example F11 is a 2D touch sensor while F01 is a generic
|
||||||
|
* function present in every RMI device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void rmi_release_device(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = to_rmi_device(dev);
|
||||||
|
|
||||||
|
kfree(rmi_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_type rmi_device_type = {
|
||||||
|
.name = "rmi4_sensor",
|
||||||
|
.release = rmi_release_device,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool rmi_is_physical_device(struct device *dev)
|
||||||
|
{
|
||||||
|
return dev->type == &rmi_device_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_register_transport_device - register a transport device connection
|
||||||
|
* on the RMI bus. Transport drivers provide communication from the devices
|
||||||
|
* on a bus (such as SPI, I2C, and so on) to the RMI4 sensor.
|
||||||
|
*
|
||||||
|
* @xport: the transport device to register
|
||||||
|
*/
|
||||||
|
int rmi_register_transport_device(struct rmi_transport_dev *xport)
|
||||||
|
{
|
||||||
|
static atomic_t transport_device_count = ATOMIC_INIT(0);
|
||||||
|
struct rmi_device *rmi_dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
|
||||||
|
if (!rmi_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
device_initialize(&rmi_dev->dev);
|
||||||
|
|
||||||
|
rmi_dev->xport = xport;
|
||||||
|
rmi_dev->number = atomic_inc_return(&transport_device_count) - 1;
|
||||||
|
|
||||||
|
dev_set_name(&rmi_dev->dev, "rmi4-%02d", rmi_dev->number);
|
||||||
|
|
||||||
|
rmi_dev->dev.bus = &rmi_bus_type;
|
||||||
|
rmi_dev->dev.type = &rmi_device_type;
|
||||||
|
|
||||||
|
xport->rmi_dev = rmi_dev;
|
||||||
|
|
||||||
|
error = device_add(&rmi_dev->dev);
|
||||||
|
if (error)
|
||||||
|
goto err_put_device;
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_CORE, xport->dev,
|
||||||
|
"%s: Registered %s as %s.\n", __func__,
|
||||||
|
dev_name(rmi_dev->xport->dev), dev_name(&rmi_dev->dev));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_put_device:
|
||||||
|
put_device(&rmi_dev->dev);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_register_transport_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_unregister_transport_device - unregister a transport device connection
|
||||||
|
* @xport: the transport driver to unregister
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = xport->rmi_dev;
|
||||||
|
|
||||||
|
device_del(&rmi_dev->dev);
|
||||||
|
put_device(&rmi_dev->dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(rmi_unregister_transport_device);
|
||||||
|
|
||||||
|
|
||||||
|
/* Function specific stuff */
|
||||||
|
|
||||||
|
static void rmi_release_function(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rmi_function *fn = to_rmi_function(dev);
|
||||||
|
|
||||||
|
kfree(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_type rmi_function_type = {
|
||||||
|
.name = "rmi4_function",
|
||||||
|
.release = rmi_release_function,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool rmi_is_function_device(struct device *dev)
|
||||||
|
{
|
||||||
|
return dev->type == &rmi_function_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_function_match(struct device *dev, struct device_driver *drv)
|
||||||
|
{
|
||||||
|
struct rmi_function_handler *handler = to_rmi_function_handler(drv);
|
||||||
|
struct rmi_function *fn = to_rmi_function(dev);
|
||||||
|
|
||||||
|
return fn->fd.function_number == handler->func;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static void rmi_function_of_probe(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
char of_name[9];
|
||||||
|
|
||||||
|
snprintf(of_name, sizeof(of_name), "rmi4-f%02x",
|
||||||
|
fn->fd.function_number);
|
||||||
|
fn->dev.of_node = of_find_node_by_name(
|
||||||
|
fn->rmi_dev->xport->dev->of_node, of_name);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void rmi_function_of_probe(struct rmi_function *fn)
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int rmi_function_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rmi_function *fn = to_rmi_function(dev);
|
||||||
|
struct rmi_function_handler *handler =
|
||||||
|
to_rmi_function_handler(dev->driver);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
rmi_function_of_probe(fn);
|
||||||
|
|
||||||
|
if (handler->probe) {
|
||||||
|
error = handler->probe(fn);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_function_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rmi_function *fn = to_rmi_function(dev);
|
||||||
|
struct rmi_function_handler *handler =
|
||||||
|
to_rmi_function_handler(dev->driver);
|
||||||
|
|
||||||
|
if (handler->remove)
|
||||||
|
handler->remove(fn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rmi_register_function(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
device_initialize(&fn->dev);
|
||||||
|
|
||||||
|
dev_set_name(&fn->dev, "%s.fn%02x",
|
||||||
|
dev_name(&rmi_dev->dev), fn->fd.function_number);
|
||||||
|
|
||||||
|
fn->dev.parent = &rmi_dev->dev;
|
||||||
|
fn->dev.type = &rmi_function_type;
|
||||||
|
fn->dev.bus = &rmi_bus_type;
|
||||||
|
|
||||||
|
error = device_add(&fn->dev);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed device_register function device %s\n",
|
||||||
|
dev_name(&fn->dev));
|
||||||
|
goto err_put_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Registered F%02X.\n",
|
||||||
|
fn->fd.function_number);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_put_device:
|
||||||
|
put_device(&fn->dev);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rmi_unregister_function(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
device_del(&fn->dev);
|
||||||
|
|
||||||
|
if (fn->dev.of_node)
|
||||||
|
of_node_put(fn->dev.of_node);
|
||||||
|
|
||||||
|
put_device(&fn->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_register_function_handler - register a handler for an RMI function
|
||||||
|
* @handler: RMI handler that should be registered.
|
||||||
|
* @module: pointer to module that implements the handler
|
||||||
|
* @mod_name: name of the module implementing the handler
|
||||||
|
*
|
||||||
|
* This function performs additional setup of RMI function handler and
|
||||||
|
* registers it with the RMI core so that it can be bound to
|
||||||
|
* RMI function devices.
|
||||||
|
*/
|
||||||
|
int __rmi_register_function_handler(struct rmi_function_handler *handler,
|
||||||
|
struct module *owner,
|
||||||
|
const char *mod_name)
|
||||||
|
{
|
||||||
|
struct device_driver *driver = &handler->driver;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
driver->bus = &rmi_bus_type;
|
||||||
|
driver->owner = owner;
|
||||||
|
driver->mod_name = mod_name;
|
||||||
|
driver->probe = rmi_function_probe;
|
||||||
|
driver->remove = rmi_function_remove;
|
||||||
|
|
||||||
|
error = driver_register(&handler->driver);
|
||||||
|
if (error) {
|
||||||
|
pr_err("driver_register() failed for %s, error: %d\n",
|
||||||
|
handler->driver.name, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(__rmi_register_function_handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_unregister_function_handler - unregister given RMI function handler
|
||||||
|
* @handler: RMI handler that should be unregistered.
|
||||||
|
*
|
||||||
|
* This function unregisters given function handler from RMI core which
|
||||||
|
* causes it to be unbound from the function devices.
|
||||||
|
*/
|
||||||
|
void rmi_unregister_function_handler(struct rmi_function_handler *handler)
|
||||||
|
{
|
||||||
|
driver_unregister(&handler->driver);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_unregister_function_handler);
|
||||||
|
|
||||||
|
/* Bus specific stuff */
|
||||||
|
|
||||||
|
static int rmi_bus_match(struct device *dev, struct device_driver *drv)
|
||||||
|
{
|
||||||
|
bool physical = rmi_is_physical_device(dev);
|
||||||
|
|
||||||
|
/* First see if types are not compatible */
|
||||||
|
if (physical != rmi_is_physical_driver(drv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return physical || rmi_function_match(dev, drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bus_type rmi_bus_type = {
|
||||||
|
.match = rmi_bus_match,
|
||||||
|
.name = "rmi4",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct rmi_function_handler *fn_handlers[] = {
|
||||||
|
&rmi_f01_handler,
|
||||||
|
#ifdef CONFIG_RMI4_F11
|
||||||
|
&rmi_f11_handler,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_RMI4_F12
|
||||||
|
&rmi_f12_handler,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_RMI4_F30
|
||||||
|
&rmi_f30_handler,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __rmi_unregister_function_handlers(int start_idx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = start_idx; i >= 0; i--)
|
||||||
|
rmi_unregister_function_handler(fn_handlers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rmi_unregister_function_handlers(void)
|
||||||
|
{
|
||||||
|
__rmi_unregister_function_handlers(ARRAY_SIZE(fn_handlers) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_register_function_handlers(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(fn_handlers); i++) {
|
||||||
|
ret = rmi_register_function_handler(fn_handlers[i]);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: error registering the RMI F%02x handler: %d\n",
|
||||||
|
__func__, fn_handlers[i]->func, ret);
|
||||||
|
goto err_unregister_function_handlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unregister_function_handlers:
|
||||||
|
__rmi_unregister_function_handlers(i - 1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rmi_of_property_read_u32(struct device *dev, u32 *result,
|
||||||
|
const char *prop, bool optional)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
u32 val = 0;
|
||||||
|
|
||||||
|
retval = of_property_read_u32(dev->of_node, prop, &val);
|
||||||
|
if (retval && (!optional && retval == -EINVAL)) {
|
||||||
|
dev_err(dev, "Failed to get %s value: %d\n",
|
||||||
|
prop, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
*result = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rmi_of_property_read_u32);
|
||||||
|
|
||||||
|
static int __init rmi_bus_init(void)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = bus_register(&rmi_bus_type);
|
||||||
|
if (error) {
|
||||||
|
pr_err("%s: error registering the RMI bus: %d\n",
|
||||||
|
__func__, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = rmi_register_function_handlers();
|
||||||
|
if (error)
|
||||||
|
goto err_unregister_bus;
|
||||||
|
|
||||||
|
error = rmi_register_physical_driver();
|
||||||
|
if (error) {
|
||||||
|
pr_err("%s: error registering the RMI physical driver: %d\n",
|
||||||
|
__func__, error);
|
||||||
|
goto err_unregister_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unregister_bus:
|
||||||
|
bus_unregister(&rmi_bus_type);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
module_init(rmi_bus_init);
|
||||||
|
|
||||||
|
static void __exit rmi_bus_exit(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We should only ever get here if all drivers are unloaded, so
|
||||||
|
* all we have to do at this point is unregister ourselves.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rmi_unregister_physical_driver();
|
||||||
|
rmi_unregister_function_handlers();
|
||||||
|
bus_unregister(&rmi_bus_type);
|
||||||
|
}
|
||||||
|
module_exit(rmi_bus_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
|
||||||
|
MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com");
|
||||||
|
MODULE_DESCRIPTION("RMI bus");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_VERSION(RMI_DRIVER_VERSION);
|
182
drivers/input/rmi4/rmi_bus.h
Normal file
182
drivers/input/rmi4/rmi_bus.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RMI_BUS_H
|
||||||
|
#define _RMI_BUS_H
|
||||||
|
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
|
||||||
|
struct rmi_device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_function - represents the implementation of an RMI4
|
||||||
|
* function for a particular device (basically, a driver for that RMI4 function)
|
||||||
|
*
|
||||||
|
* @fd: The function descriptor of the RMI function
|
||||||
|
* @rmi_dev: Pointer to the RMI device associated with this function container
|
||||||
|
* @dev: The device associated with this particular function.
|
||||||
|
*
|
||||||
|
* @num_of_irqs: The number of irqs needed by this function
|
||||||
|
* @irq_pos: The position in the irq bitfield this function holds
|
||||||
|
* @irq_mask: For convenience, can be used to mask IRQ bits off during ATTN
|
||||||
|
* interrupt handling.
|
||||||
|
*
|
||||||
|
* @node: entry in device's list of functions
|
||||||
|
*/
|
||||||
|
struct rmi_function {
|
||||||
|
struct rmi_function_descriptor fd;
|
||||||
|
struct rmi_device *rmi_dev;
|
||||||
|
struct device dev;
|
||||||
|
struct list_head node;
|
||||||
|
|
||||||
|
unsigned int num_of_irqs;
|
||||||
|
unsigned int irq_pos;
|
||||||
|
unsigned long irq_mask[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_rmi_function(d) container_of(d, struct rmi_function, dev)
|
||||||
|
|
||||||
|
bool rmi_is_function_device(struct device *dev);
|
||||||
|
|
||||||
|
int __must_check rmi_register_function(struct rmi_function *);
|
||||||
|
void rmi_unregister_function(struct rmi_function *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_function_handler - driver routines for a particular RMI function.
|
||||||
|
*
|
||||||
|
* @func: The RMI function number
|
||||||
|
* @reset: Called when a reset of the touch sensor is detected. The routine
|
||||||
|
* should perform any out-of-the-ordinary reset handling that might be
|
||||||
|
* necessary. Restoring of touch sensor configuration registers should be
|
||||||
|
* handled in the config() callback, below.
|
||||||
|
* @config: Called when the function container is first initialized, and
|
||||||
|
* after a reset is detected. This routine should write any necessary
|
||||||
|
* configuration settings to the device.
|
||||||
|
* @attention: Called when the IRQ(s) for the function are set by the touch
|
||||||
|
* sensor.
|
||||||
|
* @suspend: Should perform any required operations to suspend the particular
|
||||||
|
* function.
|
||||||
|
* @resume: Should perform any required operations to resume the particular
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* All callbacks are expected to return 0 on success, error code on failure.
|
||||||
|
*/
|
||||||
|
struct rmi_function_handler {
|
||||||
|
struct device_driver driver;
|
||||||
|
|
||||||
|
u8 func;
|
||||||
|
|
||||||
|
int (*probe)(struct rmi_function *fn);
|
||||||
|
void (*remove)(struct rmi_function *fn);
|
||||||
|
int (*config)(struct rmi_function *fn);
|
||||||
|
int (*reset)(struct rmi_function *fn);
|
||||||
|
int (*attention)(struct rmi_function *fn, unsigned long *irq_bits);
|
||||||
|
int (*suspend)(struct rmi_function *fn);
|
||||||
|
int (*resume)(struct rmi_function *fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_rmi_function_handler(d) \
|
||||||
|
container_of(d, struct rmi_function_handler, driver)
|
||||||
|
|
||||||
|
int __must_check __rmi_register_function_handler(struct rmi_function_handler *,
|
||||||
|
struct module *, const char *);
|
||||||
|
#define rmi_register_function_handler(handler) \
|
||||||
|
__rmi_register_function_handler(handler, THIS_MODULE, KBUILD_MODNAME)
|
||||||
|
|
||||||
|
void rmi_unregister_function_handler(struct rmi_function_handler *);
|
||||||
|
|
||||||
|
#define to_rmi_driver(d) \
|
||||||
|
container_of(d, struct rmi_driver, driver)
|
||||||
|
|
||||||
|
#define to_rmi_device(d) container_of(d, struct rmi_device, dev)
|
||||||
|
|
||||||
|
static inline struct rmi_device_platform_data *
|
||||||
|
rmi_get_platform_data(struct rmi_device *d)
|
||||||
|
{
|
||||||
|
return &d->xport->pdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmi_is_physical_device(struct device *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_read - read a single byte
|
||||||
|
* @d: Pointer to an RMI device
|
||||||
|
* @addr: The address to read from
|
||||||
|
* @buf: The read buffer
|
||||||
|
*
|
||||||
|
* Reads a single byte of data using the underlying transport protocol
|
||||||
|
* into memory pointed by @buf. It returns 0 on success or a negative
|
||||||
|
* error code.
|
||||||
|
*/
|
||||||
|
static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
|
||||||
|
{
|
||||||
|
return d->xport->ops->read_block(d->xport, addr, buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_read_block - read a block of bytes
|
||||||
|
* @d: Pointer to an RMI device
|
||||||
|
* @addr: The start address to read from
|
||||||
|
* @buf: The read buffer
|
||||||
|
* @len: Length of the read buffer
|
||||||
|
*
|
||||||
|
* Reads a block of byte data using the underlying transport protocol
|
||||||
|
* into memory pointed by @buf. It returns 0 on success or a negative
|
||||||
|
* error code.
|
||||||
|
*/
|
||||||
|
static inline int rmi_read_block(struct rmi_device *d, u16 addr,
|
||||||
|
void *buf, size_t len)
|
||||||
|
{
|
||||||
|
return d->xport->ops->read_block(d->xport, addr, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_write - write a single byte
|
||||||
|
* @d: Pointer to an RMI device
|
||||||
|
* @addr: The address to write to
|
||||||
|
* @data: The data to write
|
||||||
|
*
|
||||||
|
* Writes a single byte using the underlying transport protocol. It
|
||||||
|
* returns zero on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
|
||||||
|
{
|
||||||
|
return d->xport->ops->write_block(d->xport, addr, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rmi_write_block - write a block of bytes
|
||||||
|
* @d: Pointer to an RMI device
|
||||||
|
* @addr: The start address to write to
|
||||||
|
* @buf: The write buffer
|
||||||
|
* @len: Length of the write buffer
|
||||||
|
*
|
||||||
|
* Writes a block of byte data from buf using the underlaying transport
|
||||||
|
* protocol. It returns the amount of bytes written or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int rmi_write_block(struct rmi_device *d, u16 addr,
|
||||||
|
const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
return d->xport->ops->write_block(d->xport, addr, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
|
||||||
|
|
||||||
|
extern struct bus_type rmi_bus_type;
|
||||||
|
|
||||||
|
int rmi_of_property_read_u32(struct device *dev, u32 *result,
|
||||||
|
const char *prop, bool optional);
|
||||||
|
|
||||||
|
#define RMI_DEBUG_CORE BIT(0)
|
||||||
|
#define RMI_DEBUG_XPORT BIT(1)
|
||||||
|
#define RMI_DEBUG_FN BIT(2)
|
||||||
|
#define RMI_DEBUG_2D_SENSOR BIT(3)
|
||||||
|
|
||||||
|
void rmi_dbg(int flags, struct device *dev, const char *fmt, ...);
|
||||||
|
#endif
|
1055
drivers/input/rmi4/rmi_driver.c
Normal file
1055
drivers/input/rmi4/rmi_driver.c
Normal file
File diff suppressed because it is too large
Load Diff
105
drivers/input/rmi4/rmi_driver.h
Normal file
105
drivers/input/rmi4/rmi_driver.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RMI_DRIVER_H
|
||||||
|
#define _RMI_DRIVER_H
|
||||||
|
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/hrtimer.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include "rmi_bus.h"
|
||||||
|
|
||||||
|
#define RMI_DRIVER_VERSION "2.0"
|
||||||
|
|
||||||
|
#define SYNAPTICS_INPUT_DEVICE_NAME "Synaptics RMI4 Touch Sensor"
|
||||||
|
#define SYNAPTICS_VENDOR_ID 0x06cb
|
||||||
|
|
||||||
|
#define GROUP(_attrs) { \
|
||||||
|
.attrs = _attrs, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PDT_PROPERTIES_LOCATION 0x00EF
|
||||||
|
#define BSR_LOCATION 0x00FE
|
||||||
|
|
||||||
|
#define RMI_PDT_PROPS_HAS_BSR 0x02
|
||||||
|
|
||||||
|
#define NAME_BUFFER_SIZE 256
|
||||||
|
|
||||||
|
#define RMI_PDT_ENTRY_SIZE 6
|
||||||
|
#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
|
||||||
|
#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
|
||||||
|
|
||||||
|
#define PDT_START_SCAN_LOCATION 0x00e9
|
||||||
|
#define PDT_END_SCAN_LOCATION 0x0005
|
||||||
|
#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
|
||||||
|
|
||||||
|
struct pdt_entry {
|
||||||
|
u16 page_start;
|
||||||
|
u8 query_base_addr;
|
||||||
|
u8 command_base_addr;
|
||||||
|
u8 control_base_addr;
|
||||||
|
u8 data_base_addr;
|
||||||
|
u8 interrupt_source_count;
|
||||||
|
u8 function_version;
|
||||||
|
u8 function_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
|
||||||
|
u16 pdt_address);
|
||||||
|
|
||||||
|
#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE)
|
||||||
|
#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE)
|
||||||
|
|
||||||
|
/* describes a single packet register */
|
||||||
|
struct rmi_register_desc_item {
|
||||||
|
u16 reg;
|
||||||
|
unsigned long reg_size;
|
||||||
|
u8 num_subpackets;
|
||||||
|
unsigned long subpacket_map[BITS_TO_LONGS(
|
||||||
|
RMI_REG_DESC_SUBPACKET_BITS)];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* describes the packet registers for a particular type
|
||||||
|
* (ie query, control, data)
|
||||||
|
*/
|
||||||
|
struct rmi_register_descriptor {
|
||||||
|
unsigned long struct_size;
|
||||||
|
unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)];
|
||||||
|
u8 num_registers;
|
||||||
|
struct rmi_register_desc_item *registers;
|
||||||
|
};
|
||||||
|
|
||||||
|
int rmi_read_register_desc(struct rmi_device *d, u16 addr,
|
||||||
|
struct rmi_register_descriptor *rdesc);
|
||||||
|
const struct rmi_register_desc_item *rmi_get_register_desc_item(
|
||||||
|
struct rmi_register_descriptor *rdesc, u16 reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the total size of all of the registers described in the
|
||||||
|
* descriptor.
|
||||||
|
*/
|
||||||
|
size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc);
|
||||||
|
int rmi_register_desc_calc_reg_offset(
|
||||||
|
struct rmi_register_descriptor *rdesc, u16 reg);
|
||||||
|
bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
|
||||||
|
u8 subpacket);
|
||||||
|
|
||||||
|
bool rmi_is_physical_driver(struct device_driver *);
|
||||||
|
int rmi_register_physical_driver(void);
|
||||||
|
void rmi_unregister_physical_driver(void);
|
||||||
|
|
||||||
|
char *rmi_f01_get_product_ID(struct rmi_function *fn);
|
||||||
|
|
||||||
|
extern struct rmi_function_handler rmi_f01_handler;
|
||||||
|
extern struct rmi_function_handler rmi_f11_handler;
|
||||||
|
extern struct rmi_function_handler rmi_f12_handler;
|
||||||
|
extern struct rmi_function_handler rmi_f30_handler;
|
||||||
|
#endif
|
624
drivers/input/rmi4/rmi_f01.c
Normal file
624
drivers/input/rmi4/rmi_f01.c
Normal file
@ -0,0 +1,624 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kconfig.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
|
||||||
|
#define RMI_PRODUCT_ID_LENGTH 10
|
||||||
|
#define RMI_PRODUCT_INFO_LENGTH 2
|
||||||
|
|
||||||
|
#define RMI_DATE_CODE_LENGTH 3
|
||||||
|
|
||||||
|
#define PRODUCT_ID_OFFSET 0x10
|
||||||
|
#define PRODUCT_INFO_OFFSET 0x1E
|
||||||
|
|
||||||
|
|
||||||
|
/* Force a firmware reset of the sensor */
|
||||||
|
#define RMI_F01_CMD_DEVICE_RESET 1
|
||||||
|
|
||||||
|
/* Various F01_RMI_QueryX bits */
|
||||||
|
|
||||||
|
#define RMI_F01_QRY1_CUSTOM_MAP BIT(0)
|
||||||
|
#define RMI_F01_QRY1_NON_COMPLIANT BIT(1)
|
||||||
|
#define RMI_F01_QRY1_HAS_LTS BIT(2)
|
||||||
|
#define RMI_F01_QRY1_HAS_SENSOR_ID BIT(3)
|
||||||
|
#define RMI_F01_QRY1_HAS_CHARGER_INP BIT(4)
|
||||||
|
#define RMI_F01_QRY1_HAS_ADJ_DOZE BIT(5)
|
||||||
|
#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF BIT(6)
|
||||||
|
#define RMI_F01_QRY1_HAS_QUERY42 BIT(7)
|
||||||
|
|
||||||
|
#define RMI_F01_QRY5_YEAR_MASK 0x1f
|
||||||
|
#define RMI_F01_QRY6_MONTH_MASK 0x0f
|
||||||
|
#define RMI_F01_QRY7_DAY_MASK 0x1f
|
||||||
|
|
||||||
|
#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
|
||||||
|
|
||||||
|
#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
|
||||||
|
|
||||||
|
struct f01_basic_properties {
|
||||||
|
u8 manufacturer_id;
|
||||||
|
bool has_lts;
|
||||||
|
bool has_adjustable_doze;
|
||||||
|
bool has_adjustable_doze_holdoff;
|
||||||
|
char dom[11]; /* YYYY/MM/DD + '\0' */
|
||||||
|
u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
|
||||||
|
u16 productinfo;
|
||||||
|
u32 firmware_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* F01 device status bits */
|
||||||
|
|
||||||
|
/* Most recent device status event */
|
||||||
|
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
|
||||||
|
/* The device has lost its configuration for some reason. */
|
||||||
|
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
|
||||||
|
|
||||||
|
/* Control register bits */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sleep mode controls power management on the device and affects all
|
||||||
|
* functions of the device.
|
||||||
|
*/
|
||||||
|
#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
|
||||||
|
|
||||||
|
#define RMI_SLEEP_MODE_NORMAL 0x00
|
||||||
|
#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
|
||||||
|
#define RMI_SLEEP_MODE_RESERVED0 0x02
|
||||||
|
#define RMI_SLEEP_MODE_RESERVED1 0x03
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This bit disables whatever sleep mode may be selected by the sleep_mode
|
||||||
|
* field and forces the device to run at full power without sleeping.
|
||||||
|
*/
|
||||||
|
#define RMI_F01_CRTL0_NOSLEEP_BIT BIT(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When this bit is set, the touch controller employs a noise-filtering
|
||||||
|
* algorithm designed for use with a connected battery charger.
|
||||||
|
*/
|
||||||
|
#define RMI_F01_CRTL0_CHARGER_BIT BIT(5)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the report rate for the device. The effect of this setting is
|
||||||
|
* highly product dependent. Check the spec sheet for your particular
|
||||||
|
* touch sensor.
|
||||||
|
*/
|
||||||
|
#define RMI_F01_CRTL0_REPORTRATE_BIT BIT(6)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Written by the host as an indicator that the device has been
|
||||||
|
* successfully configured.
|
||||||
|
*/
|
||||||
|
#define RMI_F01_CRTL0_CONFIGURED_BIT BIT(7)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ctrl0 - see the bit definitions above.
|
||||||
|
* @doze_interval - controls the interval between checks for finger presence
|
||||||
|
* when the touch sensor is in doze mode, in units of 10ms.
|
||||||
|
* @wakeup_threshold - controls the capacitance threshold at which the touch
|
||||||
|
* sensor will decide to wake up from that low power state.
|
||||||
|
* @doze_holdoff - controls how long the touch sensor waits after the last
|
||||||
|
* finger lifts before entering the doze state, in units of 100ms.
|
||||||
|
*/
|
||||||
|
struct f01_device_control {
|
||||||
|
u8 ctrl0;
|
||||||
|
u8 doze_interval;
|
||||||
|
u8 wakeup_threshold;
|
||||||
|
u8 doze_holdoff;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct f01_data {
|
||||||
|
struct f01_basic_properties properties;
|
||||||
|
struct f01_device_control device_control;
|
||||||
|
|
||||||
|
u16 doze_interval_addr;
|
||||||
|
u16 wakeup_threshold_addr;
|
||||||
|
u16 doze_holdoff_addr;
|
||||||
|
|
||||||
|
bool suspended;
|
||||||
|
bool old_nosleep;
|
||||||
|
|
||||||
|
unsigned int num_of_irq_regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
|
||||||
|
u16 query_base_addr,
|
||||||
|
struct f01_basic_properties *props)
|
||||||
|
{
|
||||||
|
u8 queries[RMI_F01_BASIC_QUERY_LEN];
|
||||||
|
int ret;
|
||||||
|
int query_offset = query_base_addr;
|
||||||
|
bool has_ds4_queries = false;
|
||||||
|
bool has_query42 = false;
|
||||||
|
bool has_sensor_id = false;
|
||||||
|
bool has_package_id_query = false;
|
||||||
|
bool has_build_id_query = false;
|
||||||
|
u16 prod_info_addr;
|
||||||
|
u8 ds4_query_len;
|
||||||
|
|
||||||
|
ret = rmi_read_block(rmi_dev, query_offset,
|
||||||
|
queries, RMI_F01_BASIC_QUERY_LEN);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed to read device query registers: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
prod_info_addr = query_offset + 17;
|
||||||
|
query_offset += RMI_F01_BASIC_QUERY_LEN;
|
||||||
|
|
||||||
|
/* Now parse what we got */
|
||||||
|
props->manufacturer_id = queries[0];
|
||||||
|
|
||||||
|
props->has_lts = queries[1] & RMI_F01_QRY1_HAS_LTS;
|
||||||
|
props->has_adjustable_doze =
|
||||||
|
queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE;
|
||||||
|
props->has_adjustable_doze_holdoff =
|
||||||
|
queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF;
|
||||||
|
has_query42 = queries[1] & RMI_F01_QRY1_HAS_QUERY42;
|
||||||
|
has_sensor_id = queries[1] & RMI_F01_QRY1_HAS_SENSOR_ID;
|
||||||
|
|
||||||
|
snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d",
|
||||||
|
queries[5] & RMI_F01_QRY5_YEAR_MASK,
|
||||||
|
queries[6] & RMI_F01_QRY6_MONTH_MASK,
|
||||||
|
queries[7] & RMI_F01_QRY7_DAY_MASK);
|
||||||
|
|
||||||
|
memcpy(props->product_id, &queries[11],
|
||||||
|
RMI_PRODUCT_ID_LENGTH);
|
||||||
|
props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
|
||||||
|
|
||||||
|
props->productinfo =
|
||||||
|
((queries[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
|
||||||
|
(queries[3] & RMI_F01_QRY2_PRODINFO_MASK);
|
||||||
|
|
||||||
|
if (has_sensor_id)
|
||||||
|
query_offset++;
|
||||||
|
|
||||||
|
if (has_query42) {
|
||||||
|
ret = rmi_read(rmi_dev, query_offset, queries);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed to read query 42 register: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_ds4_queries = !!(queries[0] & BIT(0));
|
||||||
|
query_offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_ds4_queries) {
|
||||||
|
ret = rmi_read(rmi_dev, query_offset, &ds4_query_len);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed to read DS4 queries length: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
query_offset++;
|
||||||
|
|
||||||
|
if (ds4_query_len > 0) {
|
||||||
|
ret = rmi_read(rmi_dev, query_offset, queries);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed to read DS4 queries: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_package_id_query = !!(queries[0] & BIT(0));
|
||||||
|
has_build_id_query = !!(queries[0] & BIT(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_package_id_query)
|
||||||
|
prod_info_addr++;
|
||||||
|
|
||||||
|
if (has_build_id_query) {
|
||||||
|
ret = rmi_read_block(rmi_dev, prod_info_addr, queries,
|
||||||
|
3);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&rmi_dev->dev,
|
||||||
|
"Failed to read product info: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
props->firmware_id = queries[1] << 8 | queries[0];
|
||||||
|
props->firmware_id += queries[2] * 65536;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *rmi_f01_get_product_ID(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f01_data *f01 = dev_get_drvdata(&fn->dev);
|
||||||
|
|
||||||
|
return f01->properties.product_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static int rmi_f01_of_probe(struct device *dev,
|
||||||
|
struct rmi_device_platform_data *pdata)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev,
|
||||||
|
(u32 *)&pdata->power_management.nosleep,
|
||||||
|
"syna,nosleep-mode", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val,
|
||||||
|
"syna,wakeup-threshold", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->power_management.wakeup_threshold = val;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val,
|
||||||
|
"syna,doze-holdoff-ms", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->power_management.doze_holdoff = val * 100;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev, &val,
|
||||||
|
"syna,doze-interval-ms", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
pdata->power_management.doze_interval = val / 10;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int rmi_f01_of_probe(struct device *dev,
|
||||||
|
struct rmi_device_platform_data *pdata)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int rmi_f01_probe(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
|
||||||
|
struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
|
||||||
|
struct f01_data *f01;
|
||||||
|
int error;
|
||||||
|
u16 ctrl_base_addr = fn->fd.control_base_addr;
|
||||||
|
u8 device_status;
|
||||||
|
u8 temp;
|
||||||
|
|
||||||
|
if (fn->dev.of_node) {
|
||||||
|
error = rmi_f01_of_probe(&fn->dev, pdata);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL);
|
||||||
|
if (!f01)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
f01->num_of_irq_regs = driver_data->num_of_irq_regs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the configured bit and (optionally) other important stuff
|
||||||
|
* in the device control register.
|
||||||
|
*/
|
||||||
|
|
||||||
|
error = rmi_read(rmi_dev, fn->fd.control_base_addr,
|
||||||
|
&f01->device_control.ctrl0);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev, "Failed to read F01 control: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pdata->power_management.nosleep) {
|
||||||
|
case RMI_F01_NOSLEEP_DEFAULT:
|
||||||
|
break;
|
||||||
|
case RMI_F01_NOSLEEP_OFF:
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
break;
|
||||||
|
case RMI_F01_NOSLEEP_ON:
|
||||||
|
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sleep mode might be set as a hangover from a system crash or
|
||||||
|
* reboot without power cycle. If so, clear it so the sensor
|
||||||
|
* is certain to function.
|
||||||
|
*/
|
||||||
|
if ((f01->device_control.ctrl0 & RMI_F01_CTRL0_SLEEP_MODE_MASK) !=
|
||||||
|
RMI_SLEEP_MODE_NORMAL) {
|
||||||
|
dev_warn(&fn->dev,
|
||||||
|
"WARNING: Non-zero sleep mode found. Clearing...\n");
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT;
|
||||||
|
|
||||||
|
error = rmi_write(rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f01->device_control.ctrl0);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev, "Failed to write F01 control: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dummy read in order to clear irqs */
|
||||||
|
error = rmi_read(rmi_dev, fn->fd.data_base_addr + 1, &temp);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&fn->dev, "Failed to read Interrupt Status.\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = rmi_f01_read_properties(rmi_dev, fn->fd.query_base_addr,
|
||||||
|
&f01->properties);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&fn->dev, "Failed to read F01 properties.\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s, fw id: %d\n",
|
||||||
|
f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown",
|
||||||
|
f01->properties.product_id, f01->properties.firmware_id);
|
||||||
|
|
||||||
|
/* Advance to interrupt control registers, then skip over them. */
|
||||||
|
ctrl_base_addr++;
|
||||||
|
ctrl_base_addr += f01->num_of_irq_regs;
|
||||||
|
|
||||||
|
/* read control register */
|
||||||
|
if (f01->properties.has_adjustable_doze) {
|
||||||
|
f01->doze_interval_addr = ctrl_base_addr;
|
||||||
|
ctrl_base_addr++;
|
||||||
|
|
||||||
|
if (pdata->power_management.doze_interval) {
|
||||||
|
f01->device_control.doze_interval =
|
||||||
|
pdata->power_management.doze_interval;
|
||||||
|
error = rmi_write(rmi_dev, f01->doze_interval_addr,
|
||||||
|
f01->device_control.doze_interval);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to configure F01 doze interval register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = rmi_read(rmi_dev, f01->doze_interval_addr,
|
||||||
|
&f01->device_control.doze_interval);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read F01 doze interval register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f01->wakeup_threshold_addr = ctrl_base_addr;
|
||||||
|
ctrl_base_addr++;
|
||||||
|
|
||||||
|
if (pdata->power_management.wakeup_threshold) {
|
||||||
|
f01->device_control.wakeup_threshold =
|
||||||
|
pdata->power_management.wakeup_threshold;
|
||||||
|
error = rmi_write(rmi_dev, f01->wakeup_threshold_addr,
|
||||||
|
f01->device_control.wakeup_threshold);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to configure F01 wakeup threshold register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = rmi_read(rmi_dev, f01->wakeup_threshold_addr,
|
||||||
|
&f01->device_control.wakeup_threshold);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read F01 wakeup threshold register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f01->properties.has_lts)
|
||||||
|
ctrl_base_addr++;
|
||||||
|
|
||||||
|
if (f01->properties.has_adjustable_doze_holdoff) {
|
||||||
|
f01->doze_holdoff_addr = ctrl_base_addr;
|
||||||
|
ctrl_base_addr++;
|
||||||
|
|
||||||
|
if (pdata->power_management.doze_holdoff) {
|
||||||
|
f01->device_control.doze_holdoff =
|
||||||
|
pdata->power_management.doze_holdoff;
|
||||||
|
error = rmi_write(rmi_dev, f01->doze_holdoff_addr,
|
||||||
|
f01->device_control.doze_holdoff);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to configure F01 doze holdoff register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = rmi_read(rmi_dev, f01->doze_holdoff_addr,
|
||||||
|
&f01->device_control.doze_holdoff);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read F01 doze holdoff register: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read device status: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Device was reset during configuration process, status: %#02x!\n",
|
||||||
|
RMI_F01_STATUS_CODE(device_status));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(&fn->dev, f01);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f01_config(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f01_data *f01 = dev_get_drvdata(&fn->dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f01->device_control.ctrl0);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to write device_control register: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f01->properties.has_adjustable_doze) {
|
||||||
|
error = rmi_write(fn->rmi_dev, f01->doze_interval_addr,
|
||||||
|
f01->device_control.doze_interval);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to write doze interval: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = rmi_write_block(fn->rmi_dev,
|
||||||
|
f01->wakeup_threshold_addr,
|
||||||
|
&f01->device_control.wakeup_threshold,
|
||||||
|
sizeof(u8));
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to write wakeup threshold: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f01->properties.has_adjustable_doze_holdoff) {
|
||||||
|
error = rmi_write(fn->rmi_dev, f01->doze_holdoff_addr,
|
||||||
|
f01->device_control.doze_holdoff);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to write doze holdoff: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f01_suspend(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f01_data *f01 = dev_get_drvdata(&fn->dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
f01->old_nosleep =
|
||||||
|
f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||||
|
if (device_may_wakeup(fn->rmi_dev->xport->dev))
|
||||||
|
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_RESERVED1;
|
||||||
|
else
|
||||||
|
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_SENSOR_SLEEP;
|
||||||
|
|
||||||
|
error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f01->device_control.ctrl0);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error);
|
||||||
|
if (f01->old_nosleep)
|
||||||
|
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||||
|
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f01_resume(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f01_data *f01 = dev_get_drvdata(&fn->dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (f01->old_nosleep)
|
||||||
|
f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
||||||
|
|
||||||
|
f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
|
||||||
|
f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
|
||||||
|
|
||||||
|
error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f01->device_control.ctrl0);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to restore normal operation: %d.\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f01_attention(struct rmi_function *fn,
|
||||||
|
unsigned long *irq_bits)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
int error;
|
||||||
|
u8 device_status;
|
||||||
|
|
||||||
|
error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read device status: %d.\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
|
||||||
|
dev_warn(&fn->dev, "Device reset detected.\n");
|
||||||
|
error = rmi_dev->driver->reset_handler(rmi_dev);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->dev, "Device reset failed: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rmi_function_handler rmi_f01_handler = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rmi4_f01",
|
||||||
|
/*
|
||||||
|
* Do not allow user unbinding F01 as it is critical
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
.suppress_bind_attrs = true,
|
||||||
|
},
|
||||||
|
.func = 0x01,
|
||||||
|
.probe = rmi_f01_probe,
|
||||||
|
.config = rmi_f01_config,
|
||||||
|
.attention = rmi_f01_attention,
|
||||||
|
.suspend = rmi_f01_suspend,
|
||||||
|
.resume = rmi_f01_resume,
|
||||||
|
};
|
1317
drivers/input/rmi4/rmi_f11.c
Normal file
1317
drivers/input/rmi4/rmi_f11.c
Normal file
File diff suppressed because it is too large
Load Diff
457
drivers/input/rmi4/rmi_f12.c
Normal file
457
drivers/input/rmi4/rmi_f12.c
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2016 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/mt.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
#include "rmi_2d_sensor.h"
|
||||||
|
|
||||||
|
enum rmi_f12_object_type {
|
||||||
|
RMI_F12_OBJECT_NONE = 0x00,
|
||||||
|
RMI_F12_OBJECT_FINGER = 0x01,
|
||||||
|
RMI_F12_OBJECT_STYLUS = 0x02,
|
||||||
|
RMI_F12_OBJECT_PALM = 0x03,
|
||||||
|
RMI_F12_OBJECT_UNCLASSIFIED = 0x04,
|
||||||
|
RMI_F12_OBJECT_GLOVED_FINGER = 0x06,
|
||||||
|
RMI_F12_OBJECT_NARROW_OBJECT = 0x07,
|
||||||
|
RMI_F12_OBJECT_HAND_EDGE = 0x08,
|
||||||
|
RMI_F12_OBJECT_COVER = 0x0A,
|
||||||
|
RMI_F12_OBJECT_STYLUS_2 = 0x0B,
|
||||||
|
RMI_F12_OBJECT_ERASER = 0x0C,
|
||||||
|
RMI_F12_OBJECT_SMALL_OBJECT = 0x0D,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct f12_data {
|
||||||
|
struct rmi_function *fn;
|
||||||
|
struct rmi_2d_sensor sensor;
|
||||||
|
struct rmi_2d_sensor_platform_data sensor_pdata;
|
||||||
|
|
||||||
|
u16 data_addr;
|
||||||
|
|
||||||
|
struct rmi_register_descriptor query_reg_desc;
|
||||||
|
struct rmi_register_descriptor control_reg_desc;
|
||||||
|
struct rmi_register_descriptor data_reg_desc;
|
||||||
|
|
||||||
|
/* F12 Data1 describes sensed objects */
|
||||||
|
const struct rmi_register_desc_item *data1;
|
||||||
|
u16 data1_offset;
|
||||||
|
|
||||||
|
/* F12 Data5 describes finger ACM */
|
||||||
|
const struct rmi_register_desc_item *data5;
|
||||||
|
u16 data5_offset;
|
||||||
|
|
||||||
|
/* F12 Data5 describes Pen */
|
||||||
|
const struct rmi_register_desc_item *data6;
|
||||||
|
u16 data6_offset;
|
||||||
|
|
||||||
|
|
||||||
|
/* F12 Data9 reports relative data */
|
||||||
|
const struct rmi_register_desc_item *data9;
|
||||||
|
u16 data9_offset;
|
||||||
|
|
||||||
|
const struct rmi_register_desc_item *data15;
|
||||||
|
u16 data15_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
|
||||||
|
{
|
||||||
|
const struct rmi_register_desc_item *item;
|
||||||
|
struct rmi_2d_sensor *sensor = &f12->sensor;
|
||||||
|
struct rmi_function *fn = sensor->fn;
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
int ret;
|
||||||
|
int offset;
|
||||||
|
u8 buf[14];
|
||||||
|
int pitch_x = 0;
|
||||||
|
int pitch_y = 0;
|
||||||
|
int clip_x_low = 0;
|
||||||
|
int clip_x_high = 0;
|
||||||
|
int clip_y_low = 0;
|
||||||
|
int clip_y_high = 0;
|
||||||
|
int rx_receivers = 0;
|
||||||
|
int tx_receivers = 0;
|
||||||
|
int sensor_flags = 0;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->control_reg_desc, 8);
|
||||||
|
if (!item) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"F12 does not have the sensor tuning control register\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 8);
|
||||||
|
|
||||||
|
if (item->reg_size > 14) {
|
||||||
|
dev_err(&fn->dev, "F12 control8 should be 14 bytes, not: %ld\n",
|
||||||
|
item->reg_size);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, buf,
|
||||||
|
item->reg_size);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
if (rmi_register_desc_has_subpacket(item, 0)) {
|
||||||
|
sensor->max_x = (buf[offset + 1] << 8) | buf[offset];
|
||||||
|
sensor->max_y = (buf[offset + 3] << 8) | buf[offset + 2];
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: max_x: %d max_y: %d\n", __func__,
|
||||||
|
sensor->max_x, sensor->max_y);
|
||||||
|
|
||||||
|
if (rmi_register_desc_has_subpacket(item, 1)) {
|
||||||
|
pitch_x = (buf[offset + 1] << 8) | buf[offset];
|
||||||
|
pitch_y = (buf[offset + 3] << 8) | buf[offset + 2];
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmi_register_desc_has_subpacket(item, 2)) {
|
||||||
|
sensor->axis_align.clip_x_low = buf[offset];
|
||||||
|
sensor->axis_align.clip_x_high = sensor->max_x
|
||||||
|
- buf[offset + 1];
|
||||||
|
sensor->axis_align.clip_y_low = buf[offset + 2];
|
||||||
|
sensor->axis_align.clip_y_high = sensor->max_y
|
||||||
|
- buf[offset + 3];
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n",
|
||||||
|
__func__, clip_x_low, clip_x_high, clip_y_low, clip_y_high);
|
||||||
|
|
||||||
|
if (rmi_register_desc_has_subpacket(item, 3)) {
|
||||||
|
rx_receivers = buf[offset];
|
||||||
|
tx_receivers = buf[offset + 1];
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmi_register_desc_has_subpacket(item, 4)) {
|
||||||
|
sensor_flags = buf[offset];
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor->x_mm = (pitch_x * rx_receivers) >> 12;
|
||||||
|
sensor->y_mm = (pitch_y * tx_receivers) >> 12;
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__,
|
||||||
|
sensor->x_mm, sensor->y_mm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct rmi_2d_sensor *sensor = &f12->sensor;
|
||||||
|
|
||||||
|
for (i = 0; i < f12->data1->num_subpackets; i++) {
|
||||||
|
struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i];
|
||||||
|
|
||||||
|
obj->type = RMI_2D_OBJECT_NONE;
|
||||||
|
obj->mt_tool = MT_TOOL_FINGER;
|
||||||
|
|
||||||
|
switch (data1[0]) {
|
||||||
|
case RMI_F12_OBJECT_FINGER:
|
||||||
|
obj->type = RMI_2D_OBJECT_FINGER;
|
||||||
|
break;
|
||||||
|
case RMI_F12_OBJECT_STYLUS:
|
||||||
|
obj->type = RMI_2D_OBJECT_STYLUS;
|
||||||
|
obj->mt_tool = MT_TOOL_PEN;
|
||||||
|
break;
|
||||||
|
case RMI_F12_OBJECT_PALM:
|
||||||
|
obj->type = RMI_2D_OBJECT_PALM;
|
||||||
|
obj->mt_tool = MT_TOOL_PALM;
|
||||||
|
break;
|
||||||
|
case RMI_F12_OBJECT_UNCLASSIFIED:
|
||||||
|
obj->type = RMI_2D_OBJECT_UNCLASSIFIED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->x = (data1[2] << 8) | data1[1];
|
||||||
|
obj->y = (data1[4] << 8) | data1[3];
|
||||||
|
obj->z = data1[5];
|
||||||
|
obj->wx = data1[6];
|
||||||
|
obj->wy = data1[7];
|
||||||
|
|
||||||
|
rmi_2d_sensor_abs_process(sensor, obj, i);
|
||||||
|
|
||||||
|
data1 += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensor->kernel_tracking)
|
||||||
|
input_mt_assign_slots(sensor->input,
|
||||||
|
sensor->tracking_slots,
|
||||||
|
sensor->tracking_pos,
|
||||||
|
sensor->nbr_fingers,
|
||||||
|
sensor->dmax);
|
||||||
|
|
||||||
|
for (i = 0; i < sensor->nbr_fingers; i++)
|
||||||
|
rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f12_attention(struct rmi_function *fn,
|
||||||
|
unsigned long *irq_nr_regs)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
struct f12_data *f12 = dev_get_drvdata(&fn->dev);
|
||||||
|
struct rmi_2d_sensor *sensor = &f12->sensor;
|
||||||
|
|
||||||
|
if (rmi_dev->xport->attn_data) {
|
||||||
|
memcpy(sensor->data_pkt, rmi_dev->xport->attn_data,
|
||||||
|
sensor->attn_size);
|
||||||
|
rmi_dev->xport->attn_data += sensor->attn_size;
|
||||||
|
rmi_dev->xport->attn_size -= sensor->attn_size;
|
||||||
|
} else {
|
||||||
|
retval = rmi_read_block(rmi_dev, f12->data_addr,
|
||||||
|
sensor->data_pkt, sensor->pkt_size);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(&fn->dev, "Failed to read object data. Code: %d.\n",
|
||||||
|
retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f12->data1)
|
||||||
|
rmi_f12_process_objects(f12,
|
||||||
|
&sensor->data_pkt[f12->data1_offset]);
|
||||||
|
|
||||||
|
input_mt_sync_frame(sensor->input);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f12_config(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct rmi_driver *drv = fn->rmi_dev->driver;
|
||||||
|
|
||||||
|
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f12_probe(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f12_data *f12;
|
||||||
|
int ret;
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
char buf;
|
||||||
|
u16 query_addr = fn->fd.query_base_addr;
|
||||||
|
const struct rmi_register_desc_item *item;
|
||||||
|
struct rmi_2d_sensor *sensor;
|
||||||
|
struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
|
||||||
|
struct rmi_transport_dev *xport = rmi_dev->xport;
|
||||||
|
u16 data_offset = 0;
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
ret = rmi_read(fn->rmi_dev, query_addr, &buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&fn->dev, "Failed to read general info register: %d\n",
|
||||||
|
ret);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
++query_addr;
|
||||||
|
|
||||||
|
if (!(buf & 0x1)) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Behavior of F12 without register descriptors is undefined.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data), GFP_KERNEL);
|
||||||
|
if (!f12)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (fn->dev.of_node) {
|
||||||
|
ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else if (pdata->sensor_pdata) {
|
||||||
|
f12->sensor_pdata = *pdata->sensor_pdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rmi_read_register_desc(rmi_dev, query_addr,
|
||||||
|
&f12->query_reg_desc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read the Query Register Descriptor: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
query_addr += 3;
|
||||||
|
|
||||||
|
ret = rmi_read_register_desc(rmi_dev, query_addr,
|
||||||
|
&f12->control_reg_desc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read the Control Register Descriptor: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
query_addr += 3;
|
||||||
|
|
||||||
|
ret = rmi_read_register_desc(rmi_dev, query_addr,
|
||||||
|
&f12->data_reg_desc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to read the Data Register Descriptor: %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
query_addr += 3;
|
||||||
|
|
||||||
|
sensor = &f12->sensor;
|
||||||
|
sensor->fn = fn;
|
||||||
|
f12->data_addr = fn->fd.data_base_addr;
|
||||||
|
sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc);
|
||||||
|
|
||||||
|
sensor->axis_align =
|
||||||
|
f12->sensor_pdata.axis_align;
|
||||||
|
|
||||||
|
sensor->x_mm = f12->sensor_pdata.x_mm;
|
||||||
|
sensor->y_mm = f12->sensor_pdata.y_mm;
|
||||||
|
|
||||||
|
if (sensor->sensor_type == rmi_sensor_default)
|
||||||
|
sensor->sensor_type =
|
||||||
|
f12->sensor_pdata.sensor_type;
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__,
|
||||||
|
sensor->pkt_size);
|
||||||
|
sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL);
|
||||||
|
if (!sensor->data_pkt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev_set_drvdata(&fn->dev, f12);
|
||||||
|
|
||||||
|
ret = rmi_f12_read_sensor_tuning(f12);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out what data is contained in the data registers. HID devices
|
||||||
|
* may have registers defined, but their data is not reported in the
|
||||||
|
* HID attention report. Registers which are not reported in the HID
|
||||||
|
* attention report check to see if the device is receiving data from
|
||||||
|
* HID attention reports.
|
||||||
|
*/
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 0);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 1);
|
||||||
|
if (item) {
|
||||||
|
f12->data1 = item;
|
||||||
|
f12->data1_offset = data_offset;
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
sensor->nbr_fingers = item->num_subpackets;
|
||||||
|
sensor->report_abs = 1;
|
||||||
|
sensor->attn_size += item->reg_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 2);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 3);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 4);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 5);
|
||||||
|
if (item) {
|
||||||
|
f12->data5 = item;
|
||||||
|
f12->data5_offset = data_offset;
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
sensor->attn_size += item->reg_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 6);
|
||||||
|
if (item && !xport->attn_data) {
|
||||||
|
f12->data6 = item;
|
||||||
|
f12->data6_offset = data_offset;
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 7);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 8);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 9);
|
||||||
|
if (item && !xport->attn_data) {
|
||||||
|
f12->data9 = item;
|
||||||
|
f12->data9_offset = data_offset;
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
if (!sensor->report_abs)
|
||||||
|
sensor->report_rel = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 10);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 11);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 12);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 13);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 14);
|
||||||
|
if (item && !xport->attn_data)
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
|
||||||
|
item = rmi_get_register_desc_item(&f12->data_reg_desc, 15);
|
||||||
|
if (item && !xport->attn_data) {
|
||||||
|
f12->data15 = item;
|
||||||
|
f12->data15_offset = data_offset;
|
||||||
|
data_offset += item->reg_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate the in-kernel tracking buffers */
|
||||||
|
sensor->tracking_pos = devm_kzalloc(&fn->dev,
|
||||||
|
sizeof(struct input_mt_pos) * sensor->nbr_fingers,
|
||||||
|
GFP_KERNEL);
|
||||||
|
sensor->tracking_slots = devm_kzalloc(&fn->dev,
|
||||||
|
sizeof(int) * sensor->nbr_fingers, GFP_KERNEL);
|
||||||
|
sensor->objs = devm_kzalloc(&fn->dev,
|
||||||
|
sizeof(struct rmi_2d_sensor_abs_object)
|
||||||
|
* sensor->nbr_fingers, GFP_KERNEL);
|
||||||
|
if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = rmi_2d_sensor_configure_input(fn, sensor);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rmi_function_handler rmi_f12_handler = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rmi4_f12",
|
||||||
|
},
|
||||||
|
.func = 0x12,
|
||||||
|
.probe = rmi_f12_probe,
|
||||||
|
.config = rmi_f12_config,
|
||||||
|
.attention = rmi_f12_attention,
|
||||||
|
};
|
407
drivers/input/rmi4/rmi_f30.c
Normal file
407
drivers/input/rmi4/rmi_f30.c
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012-2016 Synaptics Incorporated
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
|
||||||
|
#define RMI_F30_QUERY_SIZE 2
|
||||||
|
|
||||||
|
/* Defs for Query 0 */
|
||||||
|
#define RMI_F30_EXTENDED_PATTERNS 0x01
|
||||||
|
#define RMI_F30_HAS_MAPPABLE_BUTTONS (1 << 1)
|
||||||
|
#define RMI_F30_HAS_LED (1 << 2)
|
||||||
|
#define RMI_F30_HAS_GPIO (1 << 3)
|
||||||
|
#define RMI_F30_HAS_HAPTIC (1 << 4)
|
||||||
|
#define RMI_F30_HAS_GPIO_DRV_CTL (1 << 5)
|
||||||
|
#define RMI_F30_HAS_MECH_MOUSE_BTNS (1 << 6)
|
||||||
|
|
||||||
|
/* Defs for Query 1 */
|
||||||
|
#define RMI_F30_GPIO_LED_COUNT 0x1F
|
||||||
|
|
||||||
|
/* Defs for Control Registers */
|
||||||
|
#define RMI_F30_CTRL_1_GPIO_DEBOUNCE 0x01
|
||||||
|
#define RMI_F30_CTRL_1_HALT (1 << 4)
|
||||||
|
#define RMI_F30_CTRL_1_HALTED (1 << 5)
|
||||||
|
#define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS 0x03
|
||||||
|
|
||||||
|
struct rmi_f30_ctrl_data {
|
||||||
|
int address;
|
||||||
|
int length;
|
||||||
|
u8 *regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RMI_F30_CTRL_MAX_REGS 32
|
||||||
|
#define RMI_F30_CTRL_MAX_BYTES ((RMI_F30_CTRL_MAX_REGS + 7) >> 3)
|
||||||
|
#define RMI_F30_CTRL_MAX_REG_BLOCKS 11
|
||||||
|
|
||||||
|
#define RMI_F30_CTRL_REGS_MAX_SIZE (RMI_F30_CTRL_MAX_BYTES \
|
||||||
|
+ 1 \
|
||||||
|
+ RMI_F30_CTRL_MAX_BYTES \
|
||||||
|
+ RMI_F30_CTRL_MAX_BYTES \
|
||||||
|
+ RMI_F30_CTRL_MAX_BYTES \
|
||||||
|
+ 6 \
|
||||||
|
+ RMI_F30_CTRL_MAX_REGS \
|
||||||
|
+ RMI_F30_CTRL_MAX_REGS \
|
||||||
|
+ RMI_F30_CTRL_MAX_BYTES \
|
||||||
|
+ 1 \
|
||||||
|
+ 1)
|
||||||
|
|
||||||
|
struct f30_data {
|
||||||
|
/* Query Data */
|
||||||
|
bool has_extended_pattern;
|
||||||
|
bool has_mappable_buttons;
|
||||||
|
bool has_led;
|
||||||
|
bool has_gpio;
|
||||||
|
bool has_haptic;
|
||||||
|
bool has_gpio_driver_control;
|
||||||
|
bool has_mech_mouse_btns;
|
||||||
|
u8 gpioled_count;
|
||||||
|
|
||||||
|
u8 register_count;
|
||||||
|
|
||||||
|
/* Control Register Data */
|
||||||
|
struct rmi_f30_ctrl_data ctrl[RMI_F30_CTRL_MAX_REG_BLOCKS];
|
||||||
|
u8 ctrl_regs[RMI_F30_CTRL_REGS_MAX_SIZE];
|
||||||
|
u32 ctrl_regs_size;
|
||||||
|
|
||||||
|
u8 data_regs[RMI_F30_CTRL_MAX_BYTES];
|
||||||
|
u16 *gpioled_key_map;
|
||||||
|
|
||||||
|
struct input_dev *input;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rmi_f30_read_control_parameters(struct rmi_function *fn,
|
||||||
|
struct f30_data *f30)
|
||||||
|
{
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
error = rmi_read_block(rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f30->ctrl_regs, f30->ctrl_regs_size);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&rmi_dev->dev, "%s : Could not read control registers at 0x%x error (%d)\n",
|
||||||
|
__func__, fn->fd.control_base_addr, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
|
||||||
|
{
|
||||||
|
struct f30_data *f30 = dev_get_drvdata(&fn->dev);
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
int retval;
|
||||||
|
int gpiled = 0;
|
||||||
|
int value = 0;
|
||||||
|
int i;
|
||||||
|
int reg_num;
|
||||||
|
|
||||||
|
if (!f30->input)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Read the gpi led data. */
|
||||||
|
if (rmi_dev->xport->attn_data) {
|
||||||
|
memcpy(f30->data_regs, rmi_dev->xport->attn_data,
|
||||||
|
f30->register_count);
|
||||||
|
rmi_dev->xport->attn_data += f30->register_count;
|
||||||
|
rmi_dev->xport->attn_size -= f30->register_count;
|
||||||
|
} else {
|
||||||
|
retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr,
|
||||||
|
f30->data_regs, f30->register_count);
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&fn->dev, "%s: Failed to read F30 data registers.\n",
|
||||||
|
__func__);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (reg_num = 0; reg_num < f30->register_count; ++reg_num) {
|
||||||
|
for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i,
|
||||||
|
++gpiled) {
|
||||||
|
if (f30->gpioled_key_map[gpiled] != 0) {
|
||||||
|
/* buttons have pull up resistors */
|
||||||
|
value = (((f30->data_regs[reg_num] >> i) & 0x01)
|
||||||
|
== 0);
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev,
|
||||||
|
"%s: call input report key (0x%04x) value (0x%02x)",
|
||||||
|
__func__,
|
||||||
|
f30->gpioled_key_map[gpiled], value);
|
||||||
|
input_report_key(f30->input,
|
||||||
|
f30->gpioled_key_map[gpiled],
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f30_register_device(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
|
||||||
|
struct f30_data *f30 = dev_get_drvdata(&fn->dev);
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int button_count = 0;
|
||||||
|
|
||||||
|
input_dev = drv_data->input;
|
||||||
|
if (!input_dev) {
|
||||||
|
dev_info(&fn->dev, "F30: no input device found, ignoring.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
f30->input = input_dev;
|
||||||
|
|
||||||
|
set_bit(EV_KEY, input_dev->evbit);
|
||||||
|
|
||||||
|
input_dev->keycode = f30->gpioled_key_map;
|
||||||
|
input_dev->keycodesize = sizeof(u16);
|
||||||
|
input_dev->keycodemax = f30->gpioled_count;
|
||||||
|
|
||||||
|
for (i = 0; i < f30->gpioled_count; i++) {
|
||||||
|
if (f30->gpioled_key_map[i] != 0) {
|
||||||
|
input_set_capability(input_dev, EV_KEY,
|
||||||
|
f30->gpioled_key_map[i]);
|
||||||
|
button_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button_count == 1)
|
||||||
|
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f30_config(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f30_data *f30 = dev_get_drvdata(&fn->dev);
|
||||||
|
struct rmi_driver *drv = fn->rmi_dev->driver;
|
||||||
|
const struct rmi_device_platform_data *pdata =
|
||||||
|
rmi_get_platform_data(fn->rmi_dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (pdata->f30_data && pdata->f30_data->disable) {
|
||||||
|
drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask);
|
||||||
|
} else {
|
||||||
|
/* Write Control Register values back to device */
|
||||||
|
error = rmi_write_block(fn->rmi_dev, fn->fd.control_base_addr,
|
||||||
|
f30->ctrl_regs, f30->ctrl_regs_size);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&fn->rmi_dev->dev,
|
||||||
|
"%s : Could not write control registers at 0x%x error (%d)\n",
|
||||||
|
__func__, fn->fd.control_base_addr, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl,
|
||||||
|
int *ctrl_addr, int len, u8 **reg)
|
||||||
|
{
|
||||||
|
ctrl->address = *ctrl_addr;
|
||||||
|
ctrl->length = len;
|
||||||
|
ctrl->regs = *reg;
|
||||||
|
*ctrl_addr += len;
|
||||||
|
*reg += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool rmi_f30_is_valid_button(int button,
|
||||||
|
struct rmi_f30_ctrl_data *ctrl)
|
||||||
|
{
|
||||||
|
int byte_position = button >> 3;
|
||||||
|
int bit_position = button & 0x07;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ctrl2 -> dir == 0 -> input mode
|
||||||
|
* ctrl3 -> data == 1 -> actual button
|
||||||
|
*/
|
||||||
|
return !(ctrl[2].regs[byte_position] & BIT(bit_position)) &&
|
||||||
|
(ctrl[3].regs[byte_position] & BIT(bit_position));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int rmi_f30_initialize(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
struct f30_data *f30;
|
||||||
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
||||||
|
const struct rmi_device_platform_data *pdata;
|
||||||
|
int retval = 0;
|
||||||
|
int control_address;
|
||||||
|
int i;
|
||||||
|
int button;
|
||||||
|
u8 buf[RMI_F30_QUERY_SIZE];
|
||||||
|
u8 *ctrl_reg;
|
||||||
|
u8 *map_memory;
|
||||||
|
|
||||||
|
f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!f30)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev_set_drvdata(&fn->dev, f30);
|
||||||
|
|
||||||
|
retval = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, buf,
|
||||||
|
RMI_F30_QUERY_SIZE);
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&fn->dev, "Failed to read query register.\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS;
|
||||||
|
f30->has_mappable_buttons = buf[0] & RMI_F30_HAS_MAPPABLE_BUTTONS;
|
||||||
|
f30->has_led = buf[0] & RMI_F30_HAS_LED;
|
||||||
|
f30->has_gpio = buf[0] & RMI_F30_HAS_GPIO;
|
||||||
|
f30->has_haptic = buf[0] & RMI_F30_HAS_HAPTIC;
|
||||||
|
f30->has_gpio_driver_control = buf[0] & RMI_F30_HAS_GPIO_DRV_CTL;
|
||||||
|
f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS;
|
||||||
|
f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT;
|
||||||
|
|
||||||
|
f30->register_count = (f30->gpioled_count + 7) >> 3;
|
||||||
|
|
||||||
|
control_address = fn->fd.control_base_addr;
|
||||||
|
ctrl_reg = f30->ctrl_regs;
|
||||||
|
|
||||||
|
if (f30->has_gpio && f30->has_led)
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[0], &control_address,
|
||||||
|
f30->register_count, &ctrl_reg);
|
||||||
|
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address, sizeof(u8),
|
||||||
|
&ctrl_reg);
|
||||||
|
|
||||||
|
if (f30->has_gpio) {
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[2], &control_address,
|
||||||
|
f30->register_count, &ctrl_reg);
|
||||||
|
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[3], &control_address,
|
||||||
|
f30->register_count, &ctrl_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f30->has_led) {
|
||||||
|
int ctrl5_len;
|
||||||
|
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[4], &control_address,
|
||||||
|
f30->register_count, &ctrl_reg);
|
||||||
|
|
||||||
|
if (f30->has_extended_pattern)
|
||||||
|
ctrl5_len = 6;
|
||||||
|
else
|
||||||
|
ctrl5_len = 2;
|
||||||
|
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[5], &control_address,
|
||||||
|
ctrl5_len, &ctrl_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f30->has_led || f30->has_gpio_driver_control) {
|
||||||
|
/* control 6 uses a byte per gpio/led */
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[6], &control_address,
|
||||||
|
f30->gpioled_count, &ctrl_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f30->has_mappable_buttons) {
|
||||||
|
/* control 7 uses a byte per gpio/led */
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[7], &control_address,
|
||||||
|
f30->gpioled_count, &ctrl_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f30->has_haptic) {
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[8], &control_address,
|
||||||
|
f30->register_count, &ctrl_reg);
|
||||||
|
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[9], &control_address,
|
||||||
|
sizeof(u8), &ctrl_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f30->has_mech_mouse_btns)
|
||||||
|
rmi_f30_set_ctrl_data(&f30->ctrl[10], &control_address,
|
||||||
|
sizeof(u8), &ctrl_reg);
|
||||||
|
|
||||||
|
f30->ctrl_regs_size = ctrl_reg - f30->ctrl_regs
|
||||||
|
?: RMI_F30_CTRL_REGS_MAX_SIZE;
|
||||||
|
|
||||||
|
retval = rmi_f30_read_control_parameters(fn, f30);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(&fn->dev,
|
||||||
|
"Failed to initialize F19 control params.\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_memory = devm_kzalloc(&fn->dev,
|
||||||
|
(f30->gpioled_count * (sizeof(u16))),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!map_memory) {
|
||||||
|
dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
f30->gpioled_key_map = (u16 *)map_memory;
|
||||||
|
|
||||||
|
pdata = rmi_get_platform_data(rmi_dev);
|
||||||
|
if (pdata && f30->has_gpio) {
|
||||||
|
button = BTN_LEFT;
|
||||||
|
for (i = 0; i < f30->gpioled_count; i++) {
|
||||||
|
if (rmi_f30_is_valid_button(i, f30->ctrl)) {
|
||||||
|
f30->gpioled_key_map[i] = button++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* buttonpad might be given by
|
||||||
|
* f30->has_mech_mouse_btns, but I am
|
||||||
|
* not sure, so use only the pdata info
|
||||||
|
*/
|
||||||
|
if (pdata->f30_data &&
|
||||||
|
pdata->f30_data->buttonpad)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_f30_probe(struct rmi_function *fn)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
const struct rmi_device_platform_data *pdata =
|
||||||
|
rmi_get_platform_data(fn->rmi_dev);
|
||||||
|
|
||||||
|
if (pdata->f30_data && pdata->f30_data->disable)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rc = rmi_f30_initialize(fn);
|
||||||
|
if (rc < 0)
|
||||||
|
goto error_exit;
|
||||||
|
|
||||||
|
rc = rmi_f30_register_device(fn);
|
||||||
|
if (rc < 0)
|
||||||
|
goto error_exit;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error_exit:
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rmi_function_handler rmi_f30_handler = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rmi4_f30",
|
||||||
|
},
|
||||||
|
.func = 0x30,
|
||||||
|
.probe = rmi_f30_probe,
|
||||||
|
.config = rmi_f30_config,
|
||||||
|
.attention = rmi_f30_attention,
|
||||||
|
};
|
397
drivers/input/rmi4/rmi_i2c.c
Normal file
397
drivers/input/rmi4/rmi_i2c.c
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE_INCREMENT 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_i2c_xport - stores information for i2c communication
|
||||||
|
*
|
||||||
|
* @xport: The transport interface structure
|
||||||
|
*
|
||||||
|
* @page_mutex: Locks current page to avoid changing pages in unexpected ways.
|
||||||
|
* @page: Keeps track of the current virtual page
|
||||||
|
*
|
||||||
|
* @tx_buf: Buffer used for transmitting data to the sensor over i2c.
|
||||||
|
* @tx_buf_size: Size of the buffer
|
||||||
|
*/
|
||||||
|
struct rmi_i2c_xport {
|
||||||
|
struct rmi_transport_dev xport;
|
||||||
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
struct mutex page_mutex;
|
||||||
|
int page;
|
||||||
|
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
u8 *tx_buf;
|
||||||
|
size_t tx_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RMI_PAGE_SELECT_REGISTER 0xff
|
||||||
|
#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmi_set_page - Set RMI page
|
||||||
|
* @xport: The pointer to the rmi_transport_dev struct
|
||||||
|
* @page: The new page address.
|
||||||
|
*
|
||||||
|
* RMI devices have 16-bit addressing, but some of the transport
|
||||||
|
* implementations (like SMBus) only have 8-bit addressing. So RMI implements
|
||||||
|
* a page address at 0xff of every page so we can reliable page addresses
|
||||||
|
* every 256 registers.
|
||||||
|
*
|
||||||
|
* The page_mutex lock must be held when this function is entered.
|
||||||
|
*
|
||||||
|
* Returns zero on success, non-zero on failure.
|
||||||
|
*/
|
||||||
|
static int rmi_set_page(struct rmi_i2c_xport *rmi_i2c, u8 page)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = rmi_i2c->client;
|
||||||
|
u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = i2c_master_send(client, txbuf, sizeof(txbuf));
|
||||||
|
if (retval != sizeof(txbuf)) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"%s: set page failed: %d.", __func__, retval);
|
||||||
|
return (retval < 0) ? retval : -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_i2c->page = page;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_write_block(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct rmi_i2c_xport *rmi_i2c =
|
||||||
|
container_of(xport, struct rmi_i2c_xport, xport);
|
||||||
|
struct i2c_client *client = rmi_i2c->client;
|
||||||
|
size_t tx_size = len + 1;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
mutex_lock(&rmi_i2c->page_mutex);
|
||||||
|
|
||||||
|
if (!rmi_i2c->tx_buf || rmi_i2c->tx_buf_size < tx_size) {
|
||||||
|
if (rmi_i2c->tx_buf)
|
||||||
|
devm_kfree(&client->dev, rmi_i2c->tx_buf);
|
||||||
|
rmi_i2c->tx_buf_size = tx_size + BUFFER_SIZE_INCREMENT;
|
||||||
|
rmi_i2c->tx_buf = devm_kzalloc(&client->dev,
|
||||||
|
rmi_i2c->tx_buf_size,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rmi_i2c->tx_buf) {
|
||||||
|
rmi_i2c->tx_buf_size = 0;
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_i2c->tx_buf[0] = addr & 0xff;
|
||||||
|
memcpy(rmi_i2c->tx_buf + 1, buf, len);
|
||||||
|
|
||||||
|
if (RMI_I2C_PAGE(addr) != rmi_i2c->page) {
|
||||||
|
retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr));
|
||||||
|
if (retval)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = i2c_master_send(client, rmi_i2c->tx_buf, tx_size);
|
||||||
|
if (retval == tx_size)
|
||||||
|
retval = 0;
|
||||||
|
else if (retval >= 0)
|
||||||
|
retval = -EIO;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &client->dev,
|
||||||
|
"write %zd bytes at %#06x: %d (%*ph)\n",
|
||||||
|
len, addr, retval, (int)len, buf);
|
||||||
|
|
||||||
|
mutex_unlock(&rmi_i2c->page_mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_read_block(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
void *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct rmi_i2c_xport *rmi_i2c =
|
||||||
|
container_of(xport, struct rmi_i2c_xport, xport);
|
||||||
|
struct i2c_client *client = rmi_i2c->client;
|
||||||
|
u8 addr_offset = addr & 0xff;
|
||||||
|
int retval;
|
||||||
|
struct i2c_msg msgs[] = {
|
||||||
|
{
|
||||||
|
.addr = client->addr,
|
||||||
|
.len = sizeof(addr_offset),
|
||||||
|
.buf = &addr_offset,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.addr = client->addr,
|
||||||
|
.flags = I2C_M_RD,
|
||||||
|
.len = len,
|
||||||
|
.buf = buf,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mutex_lock(&rmi_i2c->page_mutex);
|
||||||
|
|
||||||
|
if (RMI_I2C_PAGE(addr) != rmi_i2c->page) {
|
||||||
|
retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr));
|
||||||
|
if (retval)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
|
if (retval == ARRAY_SIZE(msgs))
|
||||||
|
retval = 0; /* success */
|
||||||
|
else if (retval >= 0)
|
||||||
|
retval = -EIO;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &client->dev,
|
||||||
|
"read %zd bytes at %#06x: %d (%*ph)\n",
|
||||||
|
len, addr, retval, (int)len, buf);
|
||||||
|
|
||||||
|
mutex_unlock(&rmi_i2c->page_mutex);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rmi_transport_ops rmi_i2c_ops = {
|
||||||
|
.write_block = rmi_i2c_write_block,
|
||||||
|
.read_block = rmi_i2c_read_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t rmi_i2c_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = dev_id;
|
||||||
|
struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_process_interrupt_requests(rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev,
|
||||||
|
"Failed to process interrupt request: %d\n", ret);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_init_irq(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq));
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!irq_flags)
|
||||||
|
irq_flags = IRQF_TRIGGER_LOW;
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL,
|
||||||
|
rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name,
|
||||||
|
rmi_i2c);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_warn(&client->dev, "Failed to register interrupt %d\n",
|
||||||
|
rmi_i2c->irq);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id rmi_i2c_of_match[] = {
|
||||||
|
{ .compatible = "syna,rmi4-i2c" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rmi_i2c_of_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int rmi_i2c_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct rmi_device_platform_data *pdata;
|
||||||
|
struct rmi_device_platform_data *client_pdata =
|
||||||
|
dev_get_platdata(&client->dev);
|
||||||
|
struct rmi_i2c_xport *rmi_i2c;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
rmi_i2c = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_xport),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rmi_i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdata = &rmi_i2c->xport.pdata;
|
||||||
|
|
||||||
|
if (!client->dev.of_node && client_pdata)
|
||||||
|
*pdata = *client_pdata;
|
||||||
|
|
||||||
|
if (client->irq > 0)
|
||||||
|
rmi_i2c->irq = client->irq;
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n",
|
||||||
|
dev_name(&client->dev));
|
||||||
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"adapter does not support required functionality.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_i2c->client = client;
|
||||||
|
mutex_init(&rmi_i2c->page_mutex);
|
||||||
|
|
||||||
|
rmi_i2c->xport.dev = &client->dev;
|
||||||
|
rmi_i2c->xport.proto_name = "i2c";
|
||||||
|
rmi_i2c->xport.ops = &rmi_i2c_ops;
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, rmi_i2c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting the page to zero will (a) make sure the PSR is in a
|
||||||
|
* known state, and (b) make sure we can talk to the device.
|
||||||
|
*/
|
||||||
|
retval = rmi_set_page(rmi_i2c, 0);
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&client->dev, "Failed to set page select to 0.\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = rmi_register_transport_device(&rmi_i2c->xport);
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
|
||||||
|
client->addr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = rmi_i2c_init_irq(client);
|
||||||
|
if (retval < 0)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
|
||||||
|
client->addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
rmi_unregister_transport_device(&rmi_i2c->xport);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int rmi_i2c_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
disable_irq(rmi_i2c->irq);
|
||||||
|
if (device_may_wakeup(&client->dev)) {
|
||||||
|
ret = enable_irq_wake(rmi_i2c->irq);
|
||||||
|
if (!ret)
|
||||||
|
dev_warn(dev, "Failed to enable irq for wake: %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
enable_irq(rmi_i2c->irq);
|
||||||
|
if (device_may_wakeup(&client->dev)) {
|
||||||
|
ret = disable_irq_wake(rmi_i2c->irq);
|
||||||
|
if (!ret)
|
||||||
|
dev_warn(dev, "Failed to disable irq for wake: %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int rmi_i2c_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
disable_irq(rmi_i2c->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_i2c_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
enable_irq(rmi_i2c->irq);
|
||||||
|
|
||||||
|
ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops rmi_i2c_pm = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(rmi_i2c_suspend, rmi_i2c_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(rmi_i2c_runtime_suspend, rmi_i2c_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_device_id rmi_id[] = {
|
||||||
|
{ "rmi4_i2c", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, rmi_id);
|
||||||
|
|
||||||
|
static struct i2c_driver rmi_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rmi4_i2c",
|
||||||
|
.pm = &rmi_i2c_pm,
|
||||||
|
.of_match_table = of_match_ptr(rmi_i2c_of_match),
|
||||||
|
},
|
||||||
|
.id_table = rmi_id,
|
||||||
|
.probe = rmi_i2c_probe,
|
||||||
|
.remove = rmi_i2c_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(rmi_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||||
|
MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
|
||||||
|
MODULE_DESCRIPTION("RMI I2C driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_VERSION(RMI_DRIVER_VERSION);
|
589
drivers/input/rmi4/rmi_spi.c
Normal file
589
drivers/input/rmi4/rmi_spi.c
Normal file
@ -0,0 +1,589 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/rmi.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include "rmi_driver.h"
|
||||||
|
|
||||||
|
#define RMI_SPI_DEFAULT_XFER_BUF_SIZE 64
|
||||||
|
|
||||||
|
#define RMI_PAGE_SELECT_REGISTER 0x00FF
|
||||||
|
#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80)
|
||||||
|
#define RMI_SPI_XFER_SIZE_LIMIT 255
|
||||||
|
|
||||||
|
#define BUFFER_SIZE_INCREMENT 32
|
||||||
|
|
||||||
|
enum rmi_spi_op {
|
||||||
|
RMI_SPI_WRITE = 0,
|
||||||
|
RMI_SPI_READ,
|
||||||
|
RMI_SPI_V2_READ_UNIFIED,
|
||||||
|
RMI_SPI_V2_READ_SPLIT,
|
||||||
|
RMI_SPI_V2_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmi_spi_cmd {
|
||||||
|
enum rmi_spi_op op;
|
||||||
|
u16 addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmi_spi_xport {
|
||||||
|
struct rmi_transport_dev xport;
|
||||||
|
struct spi_device *spi;
|
||||||
|
|
||||||
|
struct mutex page_mutex;
|
||||||
|
int page;
|
||||||
|
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
u8 *rx_buf;
|
||||||
|
u8 *tx_buf;
|
||||||
|
int xfer_buf_size;
|
||||||
|
|
||||||
|
struct spi_transfer *rx_xfers;
|
||||||
|
struct spi_transfer *tx_xfers;
|
||||||
|
int rx_xfer_count;
|
||||||
|
int tx_xfer_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rmi_spi_manage_pools(struct rmi_spi_xport *rmi_spi, int len)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = rmi_spi->spi;
|
||||||
|
int buf_size = rmi_spi->xfer_buf_size
|
||||||
|
? rmi_spi->xfer_buf_size : RMI_SPI_DEFAULT_XFER_BUF_SIZE;
|
||||||
|
struct spi_transfer *xfer_buf;
|
||||||
|
void *buf;
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
while (buf_size < len)
|
||||||
|
buf_size *= 2;
|
||||||
|
|
||||||
|
if (buf_size > RMI_SPI_XFER_SIZE_LIMIT)
|
||||||
|
buf_size = RMI_SPI_XFER_SIZE_LIMIT;
|
||||||
|
|
||||||
|
tmp = rmi_spi->rx_buf;
|
||||||
|
buf = devm_kzalloc(&spi->dev, buf_size * 2,
|
||||||
|
GFP_KERNEL | GFP_DMA);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rmi_spi->rx_buf = buf;
|
||||||
|
rmi_spi->tx_buf = &rmi_spi->rx_buf[buf_size];
|
||||||
|
rmi_spi->xfer_buf_size = buf_size;
|
||||||
|
|
||||||
|
if (tmp)
|
||||||
|
devm_kfree(&spi->dev, tmp);
|
||||||
|
|
||||||
|
if (rmi_spi->xport.pdata.spi_data.read_delay_us)
|
||||||
|
rmi_spi->rx_xfer_count = buf_size;
|
||||||
|
else
|
||||||
|
rmi_spi->rx_xfer_count = 1;
|
||||||
|
|
||||||
|
if (rmi_spi->xport.pdata.spi_data.write_delay_us)
|
||||||
|
rmi_spi->tx_xfer_count = buf_size;
|
||||||
|
else
|
||||||
|
rmi_spi->tx_xfer_count = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a pool of spi_transfer buffers for devices which need
|
||||||
|
* per byte delays.
|
||||||
|
*/
|
||||||
|
tmp = rmi_spi->rx_xfers;
|
||||||
|
xfer_buf = devm_kzalloc(&spi->dev,
|
||||||
|
(rmi_spi->rx_xfer_count + rmi_spi->tx_xfer_count)
|
||||||
|
* sizeof(struct spi_transfer), GFP_KERNEL);
|
||||||
|
if (!xfer_buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rmi_spi->rx_xfers = xfer_buf;
|
||||||
|
rmi_spi->tx_xfers = &xfer_buf[rmi_spi->rx_xfer_count];
|
||||||
|
|
||||||
|
if (tmp)
|
||||||
|
devm_kfree(&spi->dev, tmp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_xfer(struct rmi_spi_xport *rmi_spi,
|
||||||
|
const struct rmi_spi_cmd *cmd, const u8 *tx_buf,
|
||||||
|
int tx_len, u8 *rx_buf, int rx_len)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = rmi_spi->spi;
|
||||||
|
struct rmi_device_platform_data_spi *spi_data =
|
||||||
|
&rmi_spi->xport.pdata.spi_data;
|
||||||
|
struct spi_message msg;
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
int ret = 0;
|
||||||
|
int len;
|
||||||
|
int cmd_len = 0;
|
||||||
|
int total_tx_len;
|
||||||
|
int i;
|
||||||
|
u16 addr = cmd->addr;
|
||||||
|
|
||||||
|
spi_message_init(&msg);
|
||||||
|
|
||||||
|
switch (cmd->op) {
|
||||||
|
case RMI_SPI_WRITE:
|
||||||
|
case RMI_SPI_READ:
|
||||||
|
cmd_len += 2;
|
||||||
|
break;
|
||||||
|
case RMI_SPI_V2_READ_UNIFIED:
|
||||||
|
case RMI_SPI_V2_READ_SPLIT:
|
||||||
|
case RMI_SPI_V2_WRITE:
|
||||||
|
cmd_len += 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_tx_len = cmd_len + tx_len;
|
||||||
|
len = max(total_tx_len, rx_len);
|
||||||
|
|
||||||
|
if (len > RMI_SPI_XFER_SIZE_LIMIT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (rmi_spi->xfer_buf_size < len)
|
||||||
|
rmi_spi_manage_pools(rmi_spi, len);
|
||||||
|
|
||||||
|
if (addr == 0)
|
||||||
|
/*
|
||||||
|
* SPI needs an address. Use 0x7FF if we want to keep
|
||||||
|
* reading from the last position of the register pointer.
|
||||||
|
*/
|
||||||
|
addr = 0x7FF;
|
||||||
|
|
||||||
|
switch (cmd->op) {
|
||||||
|
case RMI_SPI_WRITE:
|
||||||
|
rmi_spi->tx_buf[0] = (addr >> 8);
|
||||||
|
rmi_spi->tx_buf[1] = addr & 0xFF;
|
||||||
|
break;
|
||||||
|
case RMI_SPI_READ:
|
||||||
|
rmi_spi->tx_buf[0] = (addr >> 8) | 0x80;
|
||||||
|
rmi_spi->tx_buf[1] = addr & 0xFF;
|
||||||
|
break;
|
||||||
|
case RMI_SPI_V2_READ_UNIFIED:
|
||||||
|
break;
|
||||||
|
case RMI_SPI_V2_READ_SPLIT:
|
||||||
|
break;
|
||||||
|
case RMI_SPI_V2_WRITE:
|
||||||
|
rmi_spi->tx_buf[0] = 0x40;
|
||||||
|
rmi_spi->tx_buf[1] = (addr >> 8) & 0xFF;
|
||||||
|
rmi_spi->tx_buf[2] = addr & 0xFF;
|
||||||
|
rmi_spi->tx_buf[3] = tx_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_buf)
|
||||||
|
memcpy(&rmi_spi->tx_buf[cmd_len], tx_buf, tx_len);
|
||||||
|
|
||||||
|
if (rmi_spi->tx_xfer_count > 1) {
|
||||||
|
for (i = 0; i < total_tx_len; i++) {
|
||||||
|
xfer = &rmi_spi->tx_xfers[i];
|
||||||
|
memset(xfer, 0, sizeof(struct spi_transfer));
|
||||||
|
xfer->tx_buf = &rmi_spi->tx_buf[i];
|
||||||
|
xfer->len = 1;
|
||||||
|
xfer->delay_usecs = spi_data->write_delay_us;
|
||||||
|
spi_message_add_tail(xfer, &msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xfer = rmi_spi->tx_xfers;
|
||||||
|
memset(xfer, 0, sizeof(struct spi_transfer));
|
||||||
|
xfer->tx_buf = rmi_spi->tx_buf;
|
||||||
|
xfer->len = total_tx_len;
|
||||||
|
spi_message_add_tail(xfer, &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: cmd: %s tx_buf len: %d tx_buf: %*ph\n",
|
||||||
|
__func__, cmd->op == RMI_SPI_WRITE ? "WRITE" : "READ",
|
||||||
|
total_tx_len, total_tx_len, rmi_spi->tx_buf);
|
||||||
|
|
||||||
|
if (rx_buf) {
|
||||||
|
if (rmi_spi->rx_xfer_count > 1) {
|
||||||
|
for (i = 0; i < rx_len; i++) {
|
||||||
|
xfer = &rmi_spi->rx_xfers[i];
|
||||||
|
memset(xfer, 0, sizeof(struct spi_transfer));
|
||||||
|
xfer->rx_buf = &rmi_spi->rx_buf[i];
|
||||||
|
xfer->len = 1;
|
||||||
|
xfer->delay_usecs = spi_data->read_delay_us;
|
||||||
|
spi_message_add_tail(xfer, &msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xfer = rmi_spi->rx_xfers;
|
||||||
|
memset(xfer, 0, sizeof(struct spi_transfer));
|
||||||
|
xfer->rx_buf = rmi_spi->rx_buf;
|
||||||
|
xfer->len = rx_len;
|
||||||
|
spi_message_add_tail(xfer, &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = spi_sync(spi, &msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&spi->dev, "spi xfer failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_buf) {
|
||||||
|
memcpy(rx_buf, rmi_spi->rx_buf, rx_len);
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: (%d) %*ph\n",
|
||||||
|
__func__, rx_len, rx_len, rx_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rmi_set_page - Set RMI page
|
||||||
|
* @xport: The pointer to the rmi_transport_dev struct
|
||||||
|
* @page: The new page address.
|
||||||
|
*
|
||||||
|
* RMI devices have 16-bit addressing, but some of the transport
|
||||||
|
* implementations (like SMBus) only have 8-bit addressing. So RMI implements
|
||||||
|
* a page address at 0xff of every page so we can reliable page addresses
|
||||||
|
* every 256 registers.
|
||||||
|
*
|
||||||
|
* The page_mutex lock must be held when this function is entered.
|
||||||
|
*
|
||||||
|
* Returns zero on success, non-zero on failure.
|
||||||
|
*/
|
||||||
|
static int rmi_set_page(struct rmi_spi_xport *rmi_spi, u8 page)
|
||||||
|
{
|
||||||
|
struct rmi_spi_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cmd.op = RMI_SPI_WRITE;
|
||||||
|
cmd.addr = RMI_PAGE_SELECT_REGISTER;
|
||||||
|
|
||||||
|
ret = rmi_spi_xfer(rmi_spi, &cmd, &page, 1, NULL, 0);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
rmi_spi->page = page;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_write_block(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi =
|
||||||
|
container_of(xport, struct rmi_spi_xport, xport);
|
||||||
|
struct rmi_spi_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&rmi_spi->page_mutex);
|
||||||
|
|
||||||
|
if (RMI_SPI_PAGE(addr) != rmi_spi->page) {
|
||||||
|
ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr));
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.op = RMI_SPI_WRITE;
|
||||||
|
cmd.addr = addr;
|
||||||
|
|
||||||
|
ret = rmi_spi_xfer(rmi_spi, &cmd, buf, len, NULL, 0);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mutex_unlock(&rmi_spi->page_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_read_block(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
void *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi =
|
||||||
|
container_of(xport, struct rmi_spi_xport, xport);
|
||||||
|
struct rmi_spi_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&rmi_spi->page_mutex);
|
||||||
|
|
||||||
|
if (RMI_SPI_PAGE(addr) != rmi_spi->page) {
|
||||||
|
ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr));
|
||||||
|
if (ret)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.op = RMI_SPI_READ;
|
||||||
|
cmd.addr = addr;
|
||||||
|
|
||||||
|
ret = rmi_spi_xfer(rmi_spi, &cmd, NULL, 0, buf, len);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mutex_unlock(&rmi_spi->page_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rmi_transport_ops rmi_spi_ops = {
|
||||||
|
.write_block = rmi_spi_write_block,
|
||||||
|
.read_block = rmi_spi_read_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t rmi_spi_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi = dev_id;
|
||||||
|
struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_process_interrupt_requests(rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev,
|
||||||
|
"Failed to process interrupt request: %d\n", ret);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_init_irq(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq));
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!irq_flags)
|
||||||
|
irq_flags = IRQF_TRIGGER_LOW;
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL,
|
||||||
|
rmi_spi_irq, irq_flags | IRQF_ONESHOT,
|
||||||
|
dev_name(&spi->dev), rmi_spi);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_warn(&spi->dev, "Failed to register interrupt %d\n",
|
||||||
|
rmi_spi->irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static int rmi_spi_of_probe(struct spi_device *spi,
|
||||||
|
struct rmi_device_platform_data *pdata)
|
||||||
|
{
|
||||||
|
struct device *dev = &spi->dev;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev,
|
||||||
|
&pdata->spi_data.read_delay_us,
|
||||||
|
"spi-rx-delay-us", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = rmi_of_property_read_u32(dev,
|
||||||
|
&pdata->spi_data.write_delay_us,
|
||||||
|
"spi-tx-delay-us", 1);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id rmi_spi_of_match[] = {
|
||||||
|
{ .compatible = "syna,rmi4-spi" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rmi_spi_of_match);
|
||||||
|
#else
|
||||||
|
static inline int rmi_spi_of_probe(struct spi_device *spi,
|
||||||
|
struct rmi_device_platform_data *pdata)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int rmi_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi;
|
||||||
|
struct rmi_device_platform_data *pdata;
|
||||||
|
struct rmi_device_platform_data *spi_pdata = spi->dev.platform_data;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rmi_spi = devm_kzalloc(&spi->dev, sizeof(struct rmi_spi_xport),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rmi_spi)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdata = &rmi_spi->xport.pdata;
|
||||||
|
|
||||||
|
if (spi->dev.of_node) {
|
||||||
|
retval = rmi_spi_of_probe(spi, pdata);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
} else if (spi_pdata) {
|
||||||
|
*pdata = *spi_pdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->spi_data.bits_per_word)
|
||||||
|
spi->bits_per_word = pdata->spi_data.bits_per_word;
|
||||||
|
|
||||||
|
if (pdata->spi_data.mode)
|
||||||
|
spi->mode = pdata->spi_data.mode;
|
||||||
|
|
||||||
|
retval = spi_setup(spi);
|
||||||
|
if (retval < 0) {
|
||||||
|
dev_err(&spi->dev, "spi_setup failed!\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi->irq > 0)
|
||||||
|
rmi_spi->irq = spi->irq;
|
||||||
|
|
||||||
|
rmi_spi->spi = spi;
|
||||||
|
mutex_init(&rmi_spi->page_mutex);
|
||||||
|
|
||||||
|
rmi_spi->xport.dev = &spi->dev;
|
||||||
|
rmi_spi->xport.proto_name = "spi";
|
||||||
|
rmi_spi->xport.ops = &rmi_spi_ops;
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, rmi_spi);
|
||||||
|
|
||||||
|
retval = rmi_spi_manage_pools(rmi_spi, RMI_SPI_DEFAULT_XFER_BUF_SIZE);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting the page to zero will (a) make sure the PSR is in a
|
||||||
|
* known state, and (b) make sure we can talk to the device.
|
||||||
|
*/
|
||||||
|
retval = rmi_set_page(rmi_spi, 0);
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&spi->dev, "Failed to set page select to 0.\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = rmi_register_transport_device(&rmi_spi->xport);
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&spi->dev, "failed to register transport.\n");
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = rmi_spi_init_irq(spi);
|
||||||
|
if (retval < 0)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
dev_info(&spi->dev, "registered RMI SPI driver\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
rmi_unregister_transport_device(&rmi_spi->xport);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int rmi_spi_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
disable_irq(rmi_spi->irq);
|
||||||
|
if (device_may_wakeup(&spi->dev)) {
|
||||||
|
ret = enable_irq_wake(rmi_spi->irq);
|
||||||
|
if (!ret)
|
||||||
|
dev_warn(dev, "Failed to enable irq for wake: %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
enable_irq(rmi_spi->irq);
|
||||||
|
if (device_may_wakeup(&spi->dev)) {
|
||||||
|
ret = disable_irq_wake(rmi_spi->irq);
|
||||||
|
if (!ret)
|
||||||
|
dev_warn(dev, "Failed to disable irq for wake: %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rmi_driver_resume(rmi_spi->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int rmi_spi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
disable_irq(rmi_spi->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmi_spi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(dev);
|
||||||
|
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
enable_irq(rmi_spi->irq);
|
||||||
|
|
||||||
|
ret = rmi_driver_resume(rmi_spi->xport.rmi_dev);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "Failed to resume device: %d\n", ret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops rmi_spi_pm = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(rmi_spi_suspend, rmi_spi_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(rmi_spi_runtime_suspend, rmi_spi_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct spi_device_id rmi_id[] = {
|
||||||
|
{ "rmi4_spi", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, rmi_id);
|
||||||
|
|
||||||
|
static struct spi_driver rmi_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rmi4_spi",
|
||||||
|
.pm = &rmi_spi_pm,
|
||||||
|
.of_match_table = of_match_ptr(rmi_spi_of_match),
|
||||||
|
},
|
||||||
|
.id_table = rmi_id,
|
||||||
|
.probe = rmi_spi_probe,
|
||||||
|
.remove = rmi_spi_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_spi_driver(rmi_spi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||||
|
MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
|
||||||
|
MODULE_DESCRIPTION("RMI SPI driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_VERSION(RMI_DRIVER_VERSION);
|
359
include/linux/rmi.h
Normal file
359
include/linux/rmi.h
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2016 Synaptics Incorporated
|
||||||
|
* Copyright (c) 2011 Unixphere
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RMI_H
|
||||||
|
#define _RMI_H
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define NAME_BUFFER_SIZE 256
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_2d_axis_alignment - target axis alignment
|
||||||
|
* @swap_axes: set to TRUE if desired to swap x- and y-axis
|
||||||
|
* @flip_x: set to TRUE if desired to flip direction on x-axis
|
||||||
|
* @flip_y: set to TRUE if desired to flip direction on y-axis
|
||||||
|
* @clip_x_low - reported X coordinates below this setting will be clipped to
|
||||||
|
* the specified value
|
||||||
|
* @clip_x_high - reported X coordinates above this setting will be clipped to
|
||||||
|
* the specified value
|
||||||
|
* @clip_y_low - reported Y coordinates below this setting will be clipped to
|
||||||
|
* the specified value
|
||||||
|
* @clip_y_high - reported Y coordinates above this setting will be clipped to
|
||||||
|
* the specified value
|
||||||
|
* @offset_x - this value will be added to all reported X coordinates
|
||||||
|
* @offset_y - this value will be added to all reported Y coordinates
|
||||||
|
* @rel_report_enabled - if set to true, the relative reporting will be
|
||||||
|
* automatically enabled for this sensor.
|
||||||
|
*/
|
||||||
|
struct rmi_2d_axis_alignment {
|
||||||
|
bool swap_axes;
|
||||||
|
bool flip_x;
|
||||||
|
bool flip_y;
|
||||||
|
u16 clip_x_low;
|
||||||
|
u16 clip_y_low;
|
||||||
|
u16 clip_x_high;
|
||||||
|
u16 clip_y_high;
|
||||||
|
u16 offset_x;
|
||||||
|
u16 offset_y;
|
||||||
|
u8 delta_x_threshold;
|
||||||
|
u8 delta_y_threshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This is used to override any hints an F11 2D sensor might have provided
|
||||||
|
* as to what type of sensor it is.
|
||||||
|
*
|
||||||
|
* @rmi_f11_sensor_default - do not override, determine from F11_2D_QUERY14 if
|
||||||
|
* available.
|
||||||
|
* @rmi_f11_sensor_touchscreen - treat the sensor as a touchscreen (direct
|
||||||
|
* pointing).
|
||||||
|
* @rmi_f11_sensor_touchpad - thread the sensor as a touchpad (indirect
|
||||||
|
* pointing).
|
||||||
|
*/
|
||||||
|
enum rmi_sensor_type {
|
||||||
|
rmi_sensor_default = 0,
|
||||||
|
rmi_sensor_touchscreen,
|
||||||
|
rmi_sensor_touchpad
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RMI_F11_DISABLE_ABS_REPORT BIT(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_2d_sensor_data - overrides defaults for a 2D sensor.
|
||||||
|
* @axis_align - provides axis alignment overrides (see above).
|
||||||
|
* @sensor_type - Forces the driver to treat the sensor as an indirect
|
||||||
|
* pointing device (touchpad) rather than a direct pointing device
|
||||||
|
* (touchscreen). This is useful when F11_2D_QUERY14 register is not
|
||||||
|
* available.
|
||||||
|
* @disable_report_mask - Force data to not be reported even if it is supported
|
||||||
|
* by the firware.
|
||||||
|
* @topbuttonpad - Used with the "5 buttons touchpads" found on the Lenovo 40
|
||||||
|
* series
|
||||||
|
* @kernel_tracking - most moderns RMI f11 firmwares implement Multifinger
|
||||||
|
* Type B protocol. However, there are some corner cases where the user
|
||||||
|
* triggers some jumps by tapping with two fingers on the touchpad.
|
||||||
|
* Use this setting and dmax to filter out these jumps.
|
||||||
|
* Also, when using an old sensor using MF Type A behavior, set to true to
|
||||||
|
* report an actual MT protocol B.
|
||||||
|
* @dmax - the maximum distance (in sensor units) the kernel tracking allows two
|
||||||
|
* distincts fingers to be considered the same.
|
||||||
|
*/
|
||||||
|
struct rmi_2d_sensor_platform_data {
|
||||||
|
struct rmi_2d_axis_alignment axis_align;
|
||||||
|
enum rmi_sensor_type sensor_type;
|
||||||
|
int x_mm;
|
||||||
|
int y_mm;
|
||||||
|
int disable_report_mask;
|
||||||
|
u16 rezero_wait;
|
||||||
|
bool topbuttonpad;
|
||||||
|
bool kernel_tracking;
|
||||||
|
int dmax;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_f30_data - overrides defaults for a single F30 GPIOs/LED chip.
|
||||||
|
* @buttonpad - the touchpad is a buttonpad, so enable only the first actual
|
||||||
|
* button that is found.
|
||||||
|
* @trackstick_buttons - Set when the function 30 is handling the physical
|
||||||
|
* buttons of the trackstick (as a PD/2 passthrough device.
|
||||||
|
* @disable - the touchpad incorrectly reports F30 and it should be ignored.
|
||||||
|
* This is a special case which is due to misconfigured firmware.
|
||||||
|
*/
|
||||||
|
struct rmi_f30_data {
|
||||||
|
bool buttonpad;
|
||||||
|
bool trackstick_buttons;
|
||||||
|
bool disable;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_f01_power - override default power management settings.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum rmi_f01_nosleep {
|
||||||
|
RMI_F01_NOSLEEP_DEFAULT = 0,
|
||||||
|
RMI_F01_NOSLEEP_OFF = 1,
|
||||||
|
RMI_F01_NOSLEEP_ON = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_f01_power_management -When non-zero, these values will be written
|
||||||
|
* to the touch sensor to override the default firmware settigns. For a
|
||||||
|
* detailed explanation of what each field does, see the corresponding
|
||||||
|
* documention in the RMI4 specification.
|
||||||
|
*
|
||||||
|
* @nosleep - specifies whether the device is permitted to sleep or doze (that
|
||||||
|
* is, enter a temporary low power state) when no fingers are touching the
|
||||||
|
* sensor.
|
||||||
|
* @wakeup_threshold - controls the capacitance threshold at which the touch
|
||||||
|
* sensor will decide to wake up from that low power state.
|
||||||
|
* @doze_holdoff - controls how long the touch sensor waits after the last
|
||||||
|
* finger lifts before entering the doze state, in units of 100ms.
|
||||||
|
* @doze_interval - controls the interval between checks for finger presence
|
||||||
|
* when the touch sensor is in doze mode, in units of 10ms.
|
||||||
|
*/
|
||||||
|
struct rmi_f01_power_management {
|
||||||
|
enum rmi_f01_nosleep nosleep;
|
||||||
|
u8 wakeup_threshold;
|
||||||
|
u8 doze_holdoff;
|
||||||
|
u8 doze_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_device_platform_data_spi - provides parameters used in SPI
|
||||||
|
* communications. All Synaptics SPI products support a standard SPI
|
||||||
|
* interface; some also support what is called SPI V2 mode, depending on
|
||||||
|
* firmware and/or ASIC limitations. In V2 mode, the touch sensor can
|
||||||
|
* support shorter delays during certain operations, and these are specified
|
||||||
|
* separately from the standard mode delays.
|
||||||
|
*
|
||||||
|
* @block_delay - for standard SPI transactions consisting of both a read and
|
||||||
|
* write operation, the delay (in microseconds) between the read and write
|
||||||
|
* operations.
|
||||||
|
* @split_read_block_delay_us - for V2 SPI transactions consisting of both a
|
||||||
|
* read and write operation, the delay (in microseconds) between the read and
|
||||||
|
* write operations.
|
||||||
|
* @read_delay_us - the delay between each byte of a read operation in normal
|
||||||
|
* SPI mode.
|
||||||
|
* @write_delay_us - the delay between each byte of a write operation in normal
|
||||||
|
* SPI mode.
|
||||||
|
* @split_read_byte_delay_us - the delay between each byte of a read operation
|
||||||
|
* in V2 mode.
|
||||||
|
* @pre_delay_us - the delay before the start of a SPI transaction. This is
|
||||||
|
* typically useful in conjunction with custom chip select assertions (see
|
||||||
|
* below).
|
||||||
|
* @post_delay_us - the delay after the completion of an SPI transaction. This
|
||||||
|
* is typically useful in conjunction with custom chip select assertions (see
|
||||||
|
* below).
|
||||||
|
* @cs_assert - For systems where the SPI subsystem does not control the CS/SSB
|
||||||
|
* line, or where such control is broken, you can provide a custom routine to
|
||||||
|
* handle a GPIO as CS/SSB. This routine will be called at the beginning and
|
||||||
|
* end of each SPI transaction. The RMI SPI implementation will wait
|
||||||
|
* pre_delay_us after this routine returns before starting the SPI transfer;
|
||||||
|
* and post_delay_us after completion of the SPI transfer(s) before calling it
|
||||||
|
* with assert==FALSE.
|
||||||
|
*/
|
||||||
|
struct rmi_device_platform_data_spi {
|
||||||
|
u32 block_delay_us;
|
||||||
|
u32 split_read_block_delay_us;
|
||||||
|
u32 read_delay_us;
|
||||||
|
u32 write_delay_us;
|
||||||
|
u32 split_read_byte_delay_us;
|
||||||
|
u32 pre_delay_us;
|
||||||
|
u32 post_delay_us;
|
||||||
|
u8 bits_per_word;
|
||||||
|
u16 mode;
|
||||||
|
|
||||||
|
void *cs_assert_data;
|
||||||
|
int (*cs_assert)(const void *cs_assert_data, const bool assert);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_device_platform_data - system specific configuration info.
|
||||||
|
*
|
||||||
|
* @reset_delay_ms - after issuing a reset command to the touch sensor, the
|
||||||
|
* driver waits a few milliseconds to give the firmware a chance to
|
||||||
|
* to re-initialize. You can override the default wait period here.
|
||||||
|
*/
|
||||||
|
struct rmi_device_platform_data {
|
||||||
|
int reset_delay_ms;
|
||||||
|
|
||||||
|
struct rmi_device_platform_data_spi spi_data;
|
||||||
|
|
||||||
|
/* function handler pdata */
|
||||||
|
struct rmi_2d_sensor_platform_data *sensor_pdata;
|
||||||
|
struct rmi_f01_power_management power_management;
|
||||||
|
struct rmi_f30_data *f30_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_function_descriptor - RMI function base addresses
|
||||||
|
*
|
||||||
|
* @query_base_addr: The RMI Query base address
|
||||||
|
* @command_base_addr: The RMI Command base address
|
||||||
|
* @control_base_addr: The RMI Control base address
|
||||||
|
* @data_base_addr: The RMI Data base address
|
||||||
|
* @interrupt_source_count: The number of irqs this RMI function needs
|
||||||
|
* @function_number: The RMI function number
|
||||||
|
*
|
||||||
|
* This struct is used when iterating the Page Description Table. The addresses
|
||||||
|
* are 16-bit values to include the current page address.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct rmi_function_descriptor {
|
||||||
|
u16 query_base_addr;
|
||||||
|
u16 command_base_addr;
|
||||||
|
u16 control_base_addr;
|
||||||
|
u16 data_base_addr;
|
||||||
|
u8 interrupt_source_count;
|
||||||
|
u8 function_number;
|
||||||
|
u8 function_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmi_device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_transport_dev - represent an RMI transport device
|
||||||
|
*
|
||||||
|
* @dev: Pointer to the communication device, e.g. i2c or spi
|
||||||
|
* @rmi_dev: Pointer to the RMI device
|
||||||
|
* @proto_name: name of the transport protocol (SPI, i2c, etc)
|
||||||
|
* @ops: pointer to transport operations implementation
|
||||||
|
*
|
||||||
|
* The RMI transport device implements the glue between different communication
|
||||||
|
* buses such as I2C and SPI.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct rmi_transport_dev {
|
||||||
|
struct device *dev;
|
||||||
|
struct rmi_device *rmi_dev;
|
||||||
|
|
||||||
|
const char *proto_name;
|
||||||
|
const struct rmi_transport_ops *ops;
|
||||||
|
|
||||||
|
struct rmi_device_platform_data pdata;
|
||||||
|
|
||||||
|
struct input_dev *input;
|
||||||
|
|
||||||
|
void *attn_data;
|
||||||
|
int attn_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_transport_ops - defines transport protocol operations.
|
||||||
|
*
|
||||||
|
* @write_block: Writing a block of data to the specified address
|
||||||
|
* @read_block: Read a block of data from the specified address.
|
||||||
|
*/
|
||||||
|
struct rmi_transport_ops {
|
||||||
|
int (*write_block)(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
const void *buf, size_t len);
|
||||||
|
int (*read_block)(struct rmi_transport_dev *xport, u16 addr,
|
||||||
|
void *buf, size_t len);
|
||||||
|
int (*reset)(struct rmi_transport_dev *xport, u16 reset_addr);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_driver - driver for an RMI4 sensor on the RMI bus.
|
||||||
|
*
|
||||||
|
* @driver: Device driver model driver
|
||||||
|
* @reset_handler: Called when a reset is detected.
|
||||||
|
* @clear_irq_bits: Clear the specified bits in the current interrupt mask.
|
||||||
|
* @set_irq_bist: Set the specified bits in the current interrupt mask.
|
||||||
|
* @store_productid: Callback for cache product id from function 01
|
||||||
|
* @data: Private data pointer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct rmi_driver {
|
||||||
|
struct device_driver driver;
|
||||||
|
|
||||||
|
int (*reset_handler)(struct rmi_device *rmi_dev);
|
||||||
|
int (*clear_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
|
||||||
|
int (*set_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
|
||||||
|
int (*store_productid)(struct rmi_device *rmi_dev);
|
||||||
|
int (*set_input_params)(struct rmi_device *rmi_dev,
|
||||||
|
struct input_dev *input);
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rmi_device - represents an RMI4 sensor device on the RMI bus.
|
||||||
|
*
|
||||||
|
* @dev: The device created for the RMI bus
|
||||||
|
* @number: Unique number for the device on the bus.
|
||||||
|
* @driver: Pointer to associated driver
|
||||||
|
* @xport: Pointer to the transport interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct rmi_device {
|
||||||
|
struct device dev;
|
||||||
|
int number;
|
||||||
|
|
||||||
|
struct rmi_driver *driver;
|
||||||
|
struct rmi_transport_dev *xport;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rmi_driver_data {
|
||||||
|
struct list_head function_list;
|
||||||
|
|
||||||
|
struct rmi_device *rmi_dev;
|
||||||
|
|
||||||
|
struct rmi_function *f01_container;
|
||||||
|
bool f01_bootloader_mode;
|
||||||
|
|
||||||
|
u32 attn_count;
|
||||||
|
int num_of_irq_regs;
|
||||||
|
int irq_count;
|
||||||
|
unsigned long *irq_status;
|
||||||
|
unsigned long *fn_irq_bits;
|
||||||
|
unsigned long *current_irq_mask;
|
||||||
|
unsigned long *new_irq_mask;
|
||||||
|
struct mutex irq_mutex;
|
||||||
|
struct input_dev *input;
|
||||||
|
|
||||||
|
u8 pdt_props;
|
||||||
|
u8 bsr;
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
int rmi_register_transport_device(struct rmi_transport_dev *xport);
|
||||||
|
void rmi_unregister_transport_device(struct rmi_transport_dev *xport);
|
||||||
|
int rmi_process_interrupt_requests(struct rmi_device *rmi_dev);
|
||||||
|
|
||||||
|
int rmi_driver_suspend(struct rmi_device *rmi_dev);
|
||||||
|
int rmi_driver_resume(struct rmi_device *rmi_dev);
|
||||||
|
#endif
|
@ -246,6 +246,7 @@ struct input_mask {
|
|||||||
#define BUS_GSC 0x1A
|
#define BUS_GSC 0x1A
|
||||||
#define BUS_ATARI 0x1B
|
#define BUS_ATARI 0x1B
|
||||||
#define BUS_SPI 0x1C
|
#define BUS_SPI 0x1C
|
||||||
|
#define BUS_RMI 0x1D
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MT_TOOL types
|
* MT_TOOL types
|
||||||
|
Loading…
Reference in New Issue
Block a user