Input: edt-ft5x06 - fix regmap leak when probe fails

The driver neglects to free the instance of I2C regmap constructed at
the beginning of the edt_ft5x06_ts_probe() method when probe fails.
Additionally edt_ft5x06_ts_remove() is freeing the regmap too early,
before the rest of the device resources that are managed by devm are
released.

Fix this by installing a custom devm action that will ensure that the
regmap is released at the right time during normal teardown as well as
in case of probe failure.

Note that devm_regmap_init_i2c() could not be used because the driver
may replace the original regmap with a regmap specific for M06 devices
in the middle of the probe, and using devm_regmap_init_i2c() would
result in releasing the M06 regmap too early.

Reported-by: Li Zetao <lizetao1@huawei.com>
Fixes: 9dfd9708ffba ("Input: edt-ft5x06 - convert to use regmap API")
Cc: stable@vger.kernel.org
Reviewed-by: Oliver Graute <oliver.graute@kococonnector.com>
Link: https://lore.kernel.org/r/ZxL6rIlVlgsAu-Jv@google.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

+18 -1
+18 -1
drivers/input/touchscreen/edt-ft5x06.c
··· 1121 1121 } 1122 1122 } 1123 1123 1124 + static void edt_ft5x06_exit_regmap(void *arg) 1125 + { 1126 + struct edt_ft5x06_ts_data *data = arg; 1127 + 1128 + if (!IS_ERR_OR_NULL(data->regmap)) 1129 + regmap_exit(data->regmap); 1130 + } 1131 + 1124 1132 static void edt_ft5x06_disable_regulators(void *arg) 1125 1133 { 1126 1134 struct edt_ft5x06_ts_data *data = arg; ··· 1161 1153 dev_err(&client->dev, "regmap allocation failed\n"); 1162 1154 return PTR_ERR(tsdata->regmap); 1163 1155 } 1156 + 1157 + /* 1158 + * We are not using devm_regmap_init_i2c() and instead install a 1159 + * custom action because we may replace regmap with M06-specific one 1160 + * and we need to make sure that it will not be released too early. 1161 + */ 1162 + error = devm_add_action_or_reset(&client->dev, edt_ft5x06_exit_regmap, 1163 + tsdata); 1164 + if (error) 1165 + return error; 1164 1166 1165 1167 chip_data = device_get_match_data(&client->dev); 1166 1168 if (!chip_data) ··· 1365 1347 struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); 1366 1348 1367 1349 edt_ft5x06_ts_teardown_debugfs(tsdata); 1368 - regmap_exit(tsdata->regmap); 1369 1350 } 1370 1351 1371 1352 static int edt_ft5x06_ts_suspend(struct device *dev)