Skip to content

Commit 21c48db

Browse files
bentissdtor
authored andcommitted
Input: elantech - add support for SMBus devices
Many of the Elantech devices are connected through PS/2 and a different bus (SMBus or plain I2C). To not break any existing device, we only enable SMBus based on a module parameter. If some laptops require the quirk to be set, we will have to rely on a list of PNPIds or MDI matching to individually expose those hardware over SMBus. the parameter mentioned above is elantech_smbus from the psmouse module. Signed-off-by: Benjamin Tissoires <[email protected]> Acked-by: KT Liao <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]>
1 parent 80212ed commit 21c48db

File tree

6 files changed

+246
-11
lines changed

6 files changed

+246
-11
lines changed

drivers/input/mouse/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH
133133

134134
If unsure, say N.
135135

136+
config MOUSE_PS2_ELANTECH_SMBUS
137+
bool "Elantech PS/2 SMbus companion" if EXPERT
138+
default y
139+
depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
140+
depends on I2C=y || I2C=MOUSE_PS2
141+
select MOUSE_PS2_SMBUS
142+
help
143+
Say Y here if you have a Elantech touchpad connected to
144+
to an SMBus, but enumerated through PS/2.
145+
146+
If unsure, say Y.
147+
136148
config MOUSE_PS2_SENTELIC
137149
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
138150
depends on MOUSE_PS2

drivers/input/mouse/elantech.c

Lines changed: 184 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414
#include <linux/dmi.h>
1515
#include <linux/slab.h>
1616
#include <linux/module.h>
17+
#include <linux/i2c.h>
1718
#include <linux/input.h>
1819
#include <linux/input/mt.h>
20+
#include <linux/platform_device.h>
1921
#include <linux/serio.h>
2022
#include <linux/libps2.h>
2123
#include <asm/unaligned.h>
2224
#include "psmouse.h"
2325
#include "elantech.h"
26+
#include "elan_i2c.h"
2427

2528
#define elantech_debug(fmt, ...) \
2629
do { \
@@ -1084,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
10841087

10851088
static int elantech_get_resolution_v4(struct psmouse *psmouse,
10861089
unsigned int *x_res,
1087-
unsigned int *y_res)
1090+
unsigned int *y_res,
1091+
unsigned int *bus)
10881092
{
10891093
unsigned char param[3];
10901094

@@ -1093,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
10931097

10941098
*x_res = elantech_convert_res(param[1] & 0x0f);
10951099
*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
1100+
*bus = param[2];
10961101

10971102
return 0;
10981103
}
@@ -1474,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
14741479
{
14751480
struct elantech_data *etd = psmouse->private;
14761481

1482+
/*
1483+
* We might have left a breadcrumb when trying to
1484+
* set up SMbus companion.
1485+
*/
1486+
psmouse_smbus_cleanup(psmouse);
1487+
14771488
if (etd->tp_dev)
14781489
input_unregister_device(etd->tp_dev);
14791490
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1659,6 +1670,8 @@ static int elantech_query_info(struct psmouse *psmouse,
16591670
{
16601671
unsigned char param[3];
16611672

1673+
memset(info, 0, sizeof(*info));
1674+
16621675
/*
16631676
* Do the version query again so we can store the result
16641677
*/
@@ -1717,7 +1730,8 @@ static int elantech_query_info(struct psmouse *psmouse,
17171730
if (info->hw_version == 4) {
17181731
if (elantech_get_resolution_v4(psmouse,
17191732
&info->x_res,
1720-
&info->y_res)) {
1733+
&info->y_res,
1734+
&info->bus)) {
17211735
psmouse_warn(psmouse,
17221736
"failed to query resolution data.\n");
17231737
}
@@ -1726,6 +1740,129 @@ static int elantech_query_info(struct psmouse *psmouse,
17261740
return 0;
17271741
}
17281742

1743+
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
1744+
1745+
/*
1746+
* The newest Elantech device can use a secondary bus (over SMBus) which
1747+
* provides a better bandwidth and allow a better control of the touchpads.
1748+
* This is used to decide if we need to use this bus or not.
1749+
*/
1750+
enum {
1751+
ELANTECH_SMBUS_NOT_SET = -1,
1752+
ELANTECH_SMBUS_OFF,
1753+
ELANTECH_SMBUS_ON,
1754+
};
1755+
1756+
static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
1757+
ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
1758+
module_param_named(elantech_smbus, elantech_smbus, int, 0644);
1759+
MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
1760+
1761+
static int elantech_create_smbus(struct psmouse *psmouse,
1762+
struct elantech_device_info *info,
1763+
bool leave_breadcrumbs)
1764+
{
1765+
const struct property_entry i2c_properties[] = {
1766+
PROPERTY_ENTRY_BOOL("elan,trackpoint"),
1767+
{ },
1768+
};
1769+
struct i2c_board_info smbus_board = {
1770+
I2C_BOARD_INFO("elan_i2c", 0x15),
1771+
.flags = I2C_CLIENT_HOST_NOTIFY,
1772+
};
1773+
1774+
if (info->has_trackpoint)
1775+
smbus_board.properties = i2c_properties;
1776+
1777+
return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0,
1778+
leave_breadcrumbs);
1779+
}
1780+
1781+
/**
1782+
* elantech_setup_smbus - called once the PS/2 devices are enumerated
1783+
* and decides to instantiate a SMBus InterTouch device.
1784+
*/
1785+
static int elantech_setup_smbus(struct psmouse *psmouse,
1786+
struct elantech_device_info *info,
1787+
bool leave_breadcrumbs)
1788+
{
1789+
int error;
1790+
1791+
if (elantech_smbus == ELANTECH_SMBUS_OFF)
1792+
return -ENXIO;
1793+
1794+
if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
1795+
/*
1796+
* FIXME:
1797+
* constraint the I2C capable devices by using FW version,
1798+
* board version, or by using DMI matching
1799+
*/
1800+
return -ENXIO;
1801+
}
1802+
1803+
psmouse_info(psmouse, "Trying to set up SMBus access\n");
1804+
1805+
error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
1806+
if (error) {
1807+
if (error == -EAGAIN)
1808+
psmouse_info(psmouse, "SMbus companion is not ready yet\n");
1809+
else
1810+
psmouse_err(psmouse, "unable to create intertouch device\n");
1811+
1812+
return error;
1813+
}
1814+
1815+
return 0;
1816+
}
1817+
1818+
static bool elantech_use_host_notify(struct psmouse *psmouse,
1819+
struct elantech_device_info *info)
1820+
{
1821+
switch (info->bus) {
1822+
case ETP_BUS_PS2_ONLY:
1823+
/* expected case */
1824+
break;
1825+
case ETP_BUS_SMB_ALERT_ONLY:
1826+
/* fall-through */
1827+
case ETP_BUS_PS2_SMB_ALERT:
1828+
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
1829+
break;
1830+
case ETP_BUS_SMB_HST_NTFY_ONLY:
1831+
/* fall-through */
1832+
case ETP_BUS_PS2_SMB_HST_NTFY:
1833+
return true;
1834+
default:
1835+
psmouse_dbg(psmouse,
1836+
"Ignoring SMBus bus provider %d.\n",
1837+
info->bus);
1838+
}
1839+
1840+
return false;
1841+
}
1842+
1843+
int elantech_init_smbus(struct psmouse *psmouse)
1844+
{
1845+
struct elantech_device_info info;
1846+
int error = -EINVAL;
1847+
1848+
psmouse_reset(psmouse);
1849+
1850+
error = elantech_query_info(psmouse, &info);
1851+
if (error)
1852+
goto init_fail;
1853+
1854+
if (info.hw_version < 4) {
1855+
error = -ENXIO;
1856+
goto init_fail;
1857+
}
1858+
1859+
return elantech_create_smbus(psmouse, &info, false);
1860+
init_fail:
1861+
psmouse_reset(psmouse);
1862+
return error;
1863+
}
1864+
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
1865+
17291866
/*
17301867
* Initialize the touchpad and create sysfs entries
17311868
*/
@@ -1734,7 +1871,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
17341871
{
17351872
struct elantech_data *etd;
17361873
int i;
1737-
int error;
1874+
int error = -EINVAL;
17381875
struct input_dev *tp_dev;
17391876

17401877
psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
@@ -1821,7 +1958,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
18211958
return error;
18221959
}
18231960

1824-
int elantech_init(struct psmouse *psmouse)
1961+
int elantech_init_ps2(struct psmouse *psmouse)
18251962
{
18261963
struct elantech_device_info info;
18271964
int error = -EINVAL;
@@ -1841,3 +1978,46 @@ int elantech_init(struct psmouse *psmouse)
18411978
psmouse_reset(psmouse);
18421979
return error;
18431980
}
1981+
1982+
int elantech_init(struct psmouse *psmouse)
1983+
{
1984+
struct elantech_device_info info;
1985+
int error = -EINVAL;
1986+
1987+
psmouse_reset(psmouse);
1988+
1989+
error = elantech_query_info(psmouse, &info);
1990+
if (error)
1991+
goto init_fail;
1992+
1993+
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
1994+
1995+
if (elantech_use_host_notify(psmouse, &info)) {
1996+
if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
1997+
!IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
1998+
psmouse_warn(psmouse,
1999+
"The touchpad can support a better bus than the too old PS/2 protocol. "
2000+
"Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
2001+
}
2002+
error = elantech_setup_smbus(psmouse, &info, true);
2003+
if (!error)
2004+
return PSMOUSE_ELANTECH_SMBUS;
2005+
}
2006+
2007+
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
2008+
2009+
error = elantech_setup_ps2(psmouse, &info);
2010+
if (error < 0) {
2011+
/*
2012+
* Not using any flavor of Elantech support, so clean up
2013+
* SMbus breadcrumbs, if any.
2014+
*/
2015+
psmouse_smbus_cleanup(psmouse);
2016+
goto init_fail;
2017+
}
2018+
2019+
return PSMOUSE_ELANTECH;
2020+
init_fail:
2021+
psmouse_reset(psmouse);
2022+
return error;
2023+
}

drivers/input/mouse/elantech.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@
106106
*/
107107
#define ETP_WEIGHT_VALUE 5
108108

109+
/*
110+
* Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
111+
*/
112+
#define ETP_BUS_PS2_ONLY 0
113+
#define ETP_BUS_SMB_ALERT_ONLY 1
114+
#define ETP_BUS_SMB_HST_NTFY_ONLY 2
115+
#define ETP_BUS_PS2_SMB_ALERT 3
116+
#define ETP_BUS_PS2_SMB_HST_NTFY 4
117+
109118
/*
110119
* The base position for one finger, v4 hardware
111120
*/
@@ -122,6 +131,7 @@ struct elantech_device_info {
122131
unsigned int fw_version;
123132
unsigned int x_res;
124133
unsigned int y_res;
134+
unsigned int bus;
125135
bool paritycheck;
126136
bool jumpy_cursor;
127137
bool reports_pressure;
@@ -156,6 +166,7 @@ struct elantech_data {
156166

157167
#ifdef CONFIG_MOUSE_PS2_ELANTECH
158168
int elantech_detect(struct psmouse *psmouse, bool set_properties);
169+
int elantech_init_ps2(struct psmouse *psmouse);
159170
int elantech_init(struct psmouse *psmouse);
160171
#else
161172
static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
@@ -166,6 +177,19 @@ static inline int elantech_init(struct psmouse *psmouse)
166177
{
167178
return -ENOSYS;
168179
}
180+
static inline int elantech_init_ps2(struct psmouse *psmouse)
181+
{
182+
return -ENOSYS;
183+
}
169184
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
170185

186+
#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
187+
int elantech_init_smbus(struct psmouse *psmouse);
188+
#else
189+
static inline int elantech_init_smbus(struct psmouse *psmouse)
190+
{
191+
return -ENOSYS;
192+
}
193+
#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
194+
171195
#endif

drivers/input/mouse/psmouse-base.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
856856
.name = "ETPS/2",
857857
.alias = "elantech",
858858
.detect = elantech_detect,
859-
.init = elantech_init,
859+
.init = elantech_init_ps2,
860+
},
861+
#endif
862+
#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
863+
{
864+
.type = PSMOUSE_ELANTECH_SMBUS,
865+
.name = "ETSMBus",
866+
.alias = "elantech-smbus",
867+
.detect = elantech_detect,
868+
.init = elantech_init_smbus,
869+
.smbus_companion = true,
860870
},
861871
#endif
862872
#ifdef CONFIG_MOUSE_PS2_SENTELIC
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
11581168
/* Try Elantech touchpad */
11591169
if (max_proto > PSMOUSE_IMEX &&
11601170
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
1161-
&max_proto, set_properties, true)) {
1162-
return PSMOUSE_ELANTECH;
1171+
&max_proto, set_properties, false)) {
1172+
if (!set_properties)
1173+
return PSMOUSE_ELANTECH;
1174+
1175+
ret = elantech_init(psmouse);
1176+
if (ret >= 0)
1177+
return ret;
11631178
}
11641179

11651180
if (max_proto > PSMOUSE_IMEX) {

drivers/input/mouse/psmouse-smbus.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,13 @@ int psmouse_smbus_init(struct psmouse *psmouse,
237237
smbdev->psmouse = psmouse;
238238
smbdev->board = *board;
239239

240-
smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
241-
if (!smbdev->board.platform_data) {
242-
kfree(smbdev);
243-
return -ENOMEM;
240+
if (pdata) {
241+
smbdev->board.platform_data = kmemdup(pdata, pdata_size,
242+
GFP_KERNEL);
243+
if (!smbdev->board.platform_data) {
244+
kfree(smbdev);
245+
return -ENOMEM;
246+
}
244247
}
245248

246249
psmouse->private = smbdev;

drivers/input/mouse/psmouse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ enum psmouse_type {
6868
PSMOUSE_VMMOUSE,
6969
PSMOUSE_BYD,
7070
PSMOUSE_SYNAPTICS_SMBUS,
71+
PSMOUSE_ELANTECH_SMBUS,
7172
PSMOUSE_AUTO /* This one should always be last */
7273
};
7374

0 commit comments

Comments
 (0)