summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-rmi.c57
1 files changed, 52 insertions, 5 deletions
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 9a792e7..2c14812 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -141,6 +141,8 @@ struct rmi_data {
unsigned long firmware_id;
u8 f01_ctrl0;
+ u8 interrupt_enable_mask;
+ bool restore_interrupt_mask;
};
#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
@@ -361,13 +363,34 @@ static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
}
}
+static int rmi_reset_attn_mode(struct hid_device *hdev)
+{
+ struct rmi_data *data = hid_get_drvdata(hdev);
+ int ret;
+
+ ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ if (ret)
+ return ret;
+
+ if (data->restore_interrupt_mask) {
+ ret = rmi_write(hdev, data->f01.control_base_addr + 1,
+ &data->interrupt_enable_mask);
+ if (ret) {
+ hid_err(hdev, "can not write F01 control register\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void rmi_reset_work(struct work_struct *work)
{
struct rmi_data *hdata = container_of(work, struct rmi_data,
reset_work);
/* switch the device to RMI if we receive a generic mouse report */
- rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
+ rmi_reset_attn_mode(hdata->hdev);
}
static inline int rmi_schedule_reset(struct hid_device *hdev)
@@ -590,7 +613,7 @@ static int rmi_post_reset(struct hid_device *hdev)
struct rmi_data *data = hid_get_drvdata(hdev);
int ret;
- ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ ret = rmi_reset_attn_mode(hdev);
if (ret) {
hid_err(hdev, "can not set rmi mode\n");
return ret;
@@ -617,7 +640,7 @@ static int rmi_post_reset(struct hid_device *hdev)
static int rmi_post_resume(struct hid_device *hdev)
{
- return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+ return rmi_reset_attn_mode(hdev);
}
#endif /* CONFIG_PM */
@@ -673,6 +696,7 @@ static void rmi_register_function(struct rmi_data *data,
f->interrupt_count = pdt_entry->interrupt_source_count;
f->irq_mask = rmi_gen_mask(f->interrupt_base,
f->interrupt_count);
+ data->interrupt_enable_mask |= f->irq_mask;
}
}
@@ -810,12 +834,35 @@ static int rmi_populate_f01(struct hid_device *hdev)
data->firmware_id += info[2] * 65536;
}
- ret = rmi_read(hdev, data->f01.control_base_addr, &data->f01_ctrl0);
+ ret = rmi_read_block(hdev, data->f01.control_base_addr, info,
+ 2);
if (ret) {
- hid_err(hdev, "can not read f01 ctrl0\n");
+ hid_err(hdev, "can not read f01 ctrl registers\n");
return ret;
}
+
+ data->f01_ctrl0 = info[0];
+
+ if (!info[1]) {
+ /*
+ * Do to a firmware bug in some touchpads the F01 interrupt
+ * enable control register will be cleared on reset.
+ * This will stop the touchpad from reporting data, so
+ * if F01 CTRL1 is 0 then we need to explicitly enable
+ * interrupts for the functions we want data for.
+ */
+ data->restore_interrupt_mask = true;
+
+ ret = rmi_write(hdev, data->f01.control_base_addr + 1,
+ &data->interrupt_enable_mask);
+ if (ret) {
+ hid_err(hdev, "can not write to control reg 1: %d.\n",
+ ret);
+ return ret;
+ }
+ }
+
return 0;
}
OpenPOWER on IntegriCloud