Skip to content

Commit cbdd16b

Browse files
ppetter1025dianders
authored andcommitted
HID: i2c-hid: Make elan touch controllers power on after panel is enabled
Introduce a new HID quirk to indicate that this device has to be enabled after the panel's backlight is enabled, and update the driver data for the elan devices to enable this quirk. This cannot be a I2C HID quirk because the kernel needs to acknowledge this before powering up the device and read the VID/PID. When this quirk is enabled, register .panel_enabled()/.panel_disabling() instead for the panel follower. Also rename the *panel_prepare* functions into *panel_follower* because they could be called in other situations now. Fixes: bd3cba0 ("HID: i2c-hid: elan: Add support for Elan eKTH6915 i2c-hid touchscreens") Fixes: d06651b ("HID: i2c-hid: elan: Add elan-ekth6a12nay timing") Reviewed-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Pin-yen Lin <treapking@chromium.org> Acked-by: Jiri Kosina <jkosina@suse.com> Signed-off-by: Douglas Anderson <dianders@chromium.org> Link: https://lore.kernel.org/r/20250818115015.2909525-2-treapking@chromium.org
1 parent 2eb2221 commit cbdd16b

3 files changed

Lines changed: 40 additions & 19 deletions

File tree

drivers/hid/i2c-hid/i2c-hid-core.c

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ struct i2c_hid {
112112

113113
struct i2chid_ops *ops;
114114
struct drm_panel_follower panel_follower;
115-
struct work_struct panel_follower_prepare_work;
115+
struct work_struct panel_follower_work;
116116
bool is_panel_follower;
117-
bool prepare_work_finished;
117+
bool panel_follower_work_finished;
118118
};
119119

120120
static const struct i2c_hid_quirks {
@@ -1110,10 +1110,10 @@ static int i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid)
11101110
return ret;
11111111
}
11121112

1113-
static void ihid_core_panel_prepare_work(struct work_struct *work)
1113+
static void ihid_core_panel_follower_work(struct work_struct *work)
11141114
{
11151115
struct i2c_hid *ihid = container_of(work, struct i2c_hid,
1116-
panel_follower_prepare_work);
1116+
panel_follower_work);
11171117
struct hid_device *hid = ihid->hid;
11181118
int ret;
11191119

@@ -1130,7 +1130,7 @@ static void ihid_core_panel_prepare_work(struct work_struct *work)
11301130
if (ret)
11311131
dev_warn(&ihid->client->dev, "Power on failed: %d\n", ret);
11321132
else
1133-
WRITE_ONCE(ihid->prepare_work_finished, true);
1133+
WRITE_ONCE(ihid->panel_follower_work_finished, true);
11341134

11351135
/*
11361136
* The work APIs provide a number of memory ordering guarantees
@@ -1139,50 +1139,60 @@ static void ihid_core_panel_prepare_work(struct work_struct *work)
11391139
* guarantee that a write that happened in the work is visible after
11401140
* cancel_work_sync(). We'll add a write memory barrier here to match
11411141
* with i2c_hid_core_panel_unpreparing() to ensure that our write to
1142-
* prepare_work_finished is visible there.
1142+
* panel_follower_work_finished is visible there.
11431143
*/
11441144
smp_wmb();
11451145
}
11461146

1147-
static int i2c_hid_core_panel_prepared(struct drm_panel_follower *follower)
1147+
static int i2c_hid_core_panel_follower_resume(struct drm_panel_follower *follower)
11481148
{
11491149
struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower);
11501150

11511151
/*
11521152
* Powering on a touchscreen can be a slow process. Queue the work to
11531153
* the system workqueue so we don't block the panel's power up.
11541154
*/
1155-
WRITE_ONCE(ihid->prepare_work_finished, false);
1156-
schedule_work(&ihid->panel_follower_prepare_work);
1155+
WRITE_ONCE(ihid->panel_follower_work_finished, false);
1156+
schedule_work(&ihid->panel_follower_work);
11571157

11581158
return 0;
11591159
}
11601160

1161-
static int i2c_hid_core_panel_unpreparing(struct drm_panel_follower *follower)
1161+
static int i2c_hid_core_panel_follower_suspend(struct drm_panel_follower *follower)
11621162
{
11631163
struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower);
11641164

1165-
cancel_work_sync(&ihid->panel_follower_prepare_work);
1165+
cancel_work_sync(&ihid->panel_follower_work);
11661166

1167-
/* Match with ihid_core_panel_prepare_work() */
1167+
/* Match with ihid_core_panel_follower_work() */
11681168
smp_rmb();
1169-
if (!READ_ONCE(ihid->prepare_work_finished))
1169+
if (!READ_ONCE(ihid->panel_follower_work_finished))
11701170
return 0;
11711171

11721172
return i2c_hid_core_suspend(ihid, true);
11731173
}
11741174

1175-
static const struct drm_panel_follower_funcs i2c_hid_core_panel_follower_funcs = {
1176-
.panel_prepared = i2c_hid_core_panel_prepared,
1177-
.panel_unpreparing = i2c_hid_core_panel_unpreparing,
1175+
static const struct drm_panel_follower_funcs
1176+
i2c_hid_core_panel_follower_prepare_funcs = {
1177+
.panel_prepared = i2c_hid_core_panel_follower_resume,
1178+
.panel_unpreparing = i2c_hid_core_panel_follower_suspend,
1179+
};
1180+
1181+
static const struct drm_panel_follower_funcs
1182+
i2c_hid_core_panel_follower_enable_funcs = {
1183+
.panel_enabled = i2c_hid_core_panel_follower_resume,
1184+
.panel_disabling = i2c_hid_core_panel_follower_suspend,
11781185
};
11791186

11801187
static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
11811188
{
11821189
struct device *dev = &ihid->client->dev;
11831190
int ret;
11841191

1185-
ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs;
1192+
if (ihid->hid->initial_quirks | HID_QUIRK_POWER_ON_AFTER_BACKLIGHT)
1193+
ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_enable_funcs;
1194+
else
1195+
ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_prepare_funcs;
11861196

11871197
/*
11881198
* If we're not in control of our own power up/power down then we can't
@@ -1237,7 +1247,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
12371247
init_waitqueue_head(&ihid->wait);
12381248
mutex_init(&ihid->cmd_lock);
12391249
mutex_init(&ihid->reset_lock);
1240-
INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work);
1250+
INIT_WORK(&ihid->panel_follower_work, ihid_core_panel_follower_work);
12411251

12421252
/* we need to allocate the command buffer without knowing the maximum
12431253
* size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the

drivers/hid/i2c-hid/i2c-hid-of-elan.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/delay.h>
99
#include <linux/device.h>
1010
#include <linux/gpio/consumer.h>
11+
#include <linux/hid.h>
1112
#include <linux/i2c.h>
1213
#include <linux/kernel.h>
1314
#include <linux/module.h>
@@ -23,6 +24,7 @@ struct elan_i2c_hid_chip_data {
2324
unsigned int post_power_delay_ms;
2425
u16 hid_descriptor_address;
2526
const char *main_supply_name;
27+
bool power_after_backlight;
2628
};
2729

2830
struct i2c_hid_of_elan {
@@ -97,6 +99,7 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
9799
{
98100
struct i2c_hid_of_elan *ihid_elan;
99101
int ret;
102+
u32 quirks = 0;
100103

101104
ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL);
102105
if (!ihid_elan)
@@ -131,8 +134,12 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
131134
}
132135
}
133136

137+
if (ihid_elan->chip_data->power_after_backlight)
138+
quirks = HID_QUIRK_POWER_ON_AFTER_BACKLIGHT;
139+
134140
ret = i2c_hid_core_probe(client, &ihid_elan->ops,
135-
ihid_elan->chip_data->hid_descriptor_address, 0);
141+
ihid_elan->chip_data->hid_descriptor_address,
142+
quirks);
136143
if (ret)
137144
goto err_deassert_reset;
138145

@@ -150,13 +157,15 @@ static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = {
150157
.post_gpio_reset_on_delay_ms = 300,
151158
.hid_descriptor_address = 0x0001,
152159
.main_supply_name = "vcc33",
160+
.power_after_backlight = true,
153161
};
154162

155163
static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = {
156164
.post_power_delay_ms = 10,
157165
.post_gpio_reset_on_delay_ms = 300,
158166
.hid_descriptor_address = 0x0001,
159167
.main_supply_name = "vcc33",
168+
.power_after_backlight = true,
160169
};
161170

162171
static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {

include/linux/hid.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ struct hid_item {
364364
* | @HID_QUIRK_HAVE_SPECIAL_DRIVER:
365365
* | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE:
366366
* | @HID_QUIRK_IGNORE_SPECIAL_DRIVER
367+
* | @HID_QUIRK_POWER_ON_AFTER_BACKLIGHT
367368
* | @HID_QUIRK_FULLSPEED_INTERVAL:
368369
* | @HID_QUIRK_NO_INIT_REPORTS:
369370
* | @HID_QUIRK_NO_IGNORE:
@@ -391,6 +392,7 @@ struct hid_item {
391392
#define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20)
392393
#define HID_QUIRK_NOINVERT BIT(21)
393394
#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22)
395+
#define HID_QUIRK_POWER_ON_AFTER_BACKLIGHT BIT(23)
394396
#define HID_QUIRK_FULLSPEED_INTERVAL BIT(28)
395397
#define HID_QUIRK_NO_INIT_REPORTS BIT(29)
396398
#define HID_QUIRK_NO_IGNORE BIT(30)

0 commit comments

Comments
 (0)