@@ -110,7 +110,9 @@ struct i2c_hid {
110
110
111
111
struct i2chid_ops * ops ;
112
112
struct drm_panel_follower panel_follower ;
113
+ struct work_struct panel_follower_prepare_work ;
113
114
bool is_panel_follower ;
115
+ bool prepare_work_finished ;
114
116
};
115
117
116
118
static const struct i2c_hid_quirks {
@@ -1062,26 +1064,65 @@ static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid)
1062
1064
return ret ;
1063
1065
}
1064
1066
1065
- static int i2c_hid_core_panel_prepared (struct drm_panel_follower * follower )
1067
+ static void ihid_core_panel_prepare_work (struct work_struct * work )
1066
1068
{
1067
- struct i2c_hid * ihid = container_of (follower , struct i2c_hid , panel_follower );
1069
+ struct i2c_hid * ihid = container_of (work , struct i2c_hid ,
1070
+ panel_follower_prepare_work );
1068
1071
struct hid_device * hid = ihid -> hid ;
1072
+ int ret ;
1069
1073
1070
1074
/*
1071
1075
* hid->version is set on the first power up. If it's still zero then
1072
1076
* this is the first power on so we should perform initial power up
1073
1077
* steps.
1074
1078
*/
1075
1079
if (!hid -> version )
1076
- return __do_i2c_hid_core_initial_power_up (ihid );
1080
+ ret = __do_i2c_hid_core_initial_power_up (ihid );
1081
+ else
1082
+ ret = i2c_hid_core_resume (ihid );
1077
1083
1078
- return i2c_hid_core_resume (ihid );
1084
+ if (ret )
1085
+ dev_warn (& ihid -> client -> dev , "Power on failed: %d\n" , ret );
1086
+ else
1087
+ WRITE_ONCE (ihid -> prepare_work_finished , true);
1088
+
1089
+ /*
1090
+ * The work APIs provide a number of memory ordering guarantees
1091
+ * including one that says that memory writes before schedule_work()
1092
+ * are always visible to the work function, but they don't appear to
1093
+ * guarantee that a write that happened in the work is visible after
1094
+ * cancel_work_sync(). We'll add a write memory barrier here to match
1095
+ * with i2c_hid_core_panel_unpreparing() to ensure that our write to
1096
+ * prepare_work_finished is visible there.
1097
+ */
1098
+ smp_wmb ();
1099
+ }
1100
+
1101
+ static int i2c_hid_core_panel_prepared (struct drm_panel_follower * follower )
1102
+ {
1103
+ struct i2c_hid * ihid = container_of (follower , struct i2c_hid , panel_follower );
1104
+
1105
+ /*
1106
+ * Powering on a touchscreen can be a slow process. Queue the work to
1107
+ * the system workqueue so we don't block the panel's power up.
1108
+ */
1109
+ WRITE_ONCE (ihid -> prepare_work_finished , false);
1110
+ schedule_work (& ihid -> panel_follower_prepare_work );
1111
+
1112
+ return 0 ;
1079
1113
}
1080
1114
1081
1115
static int i2c_hid_core_panel_unpreparing (struct drm_panel_follower * follower )
1082
1116
{
1083
1117
struct i2c_hid * ihid = container_of (follower , struct i2c_hid , panel_follower );
1084
1118
1119
+ cancel_work_sync (& ihid -> panel_follower_prepare_work );
1120
+
1121
+ /* Match with ihid_core_panel_prepare_work() */
1122
+ smp_rmb ();
1123
+ if (!READ_ONCE (ihid -> prepare_work_finished ))
1124
+ return 0 ;
1125
+
1085
1126
return i2c_hid_core_suspend (ihid , true);
1086
1127
}
1087
1128
@@ -1173,6 +1214,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
1173
1214
1174
1215
init_waitqueue_head (& ihid -> wait );
1175
1216
mutex_init (& ihid -> reset_lock );
1217
+ INIT_WORK (& ihid -> panel_follower_prepare_work , ihid_core_panel_prepare_work );
1176
1218
1177
1219
/* we need to allocate the command buffer without knowing the maximum
1178
1220
* size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
0 commit comments