diff options
author | Josh Boyer <jwboyer@redhat.com> | 2014-04-10 09:50:17 -0400 |
---|---|---|
committer | Josh Boyer <jwboyer@redhat.com> | 2014-04-10 09:59:02 -0400 |
commit | 2ed67326d8900c168f4c199e2a4d4191563259f0 (patch) | |
tree | 51d2f3469eec3ca2b55f3225c84f6c6f5d913c12 /0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch | |
parent | 700baa35a69ec9d7b1bbd5551366a24560334451 (diff) | |
download | kernel-2ed67326d8900c168f4c199e2a4d4191563259f0.tar.gz kernel-2ed67326d8900c168f4c199e2a4d4191563259f0.tar.xz kernel-2ed67326d8900c168f4c199e2a4d4191563259f0.zip |
Backported HID RMI driver for Haswell Dell XPS machines from Benjamin Tissoires (rhbz 1048314)
Diffstat (limited to '0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch')
-rw-r--r-- | 0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch b/0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch new file mode 100644 index 000000000..632174608 --- /dev/null +++ b/0001-HID-rmi-introduce-RMI-driver-for-Synaptics-touchpads.patch @@ -0,0 +1,1001 @@ +Bugzilla: 1048314 +Upstream-status: Queued for 3.16 + +From 39141443c8ea2900af627d688a255e064e2b6e19 Mon Sep 17 00:00:00 2001 +From: Benjamin Tissoires <benjamin.tissoires@redhat.com> +Date: Wed, 9 Apr 2014 11:09:21 -0400 +Subject: [PATCH] HID: rmi: introduce RMI driver for Synaptics touchpads + +This driver add support for RMI4 over USB or I2C. +The current state is that it uses its own RMI4 implementation, but once +RMI4 is merged upstream, the driver will be a transport driver for the +RMI4 library. + +Part of this driver should be considered as temporary. Most of the RMI4 +processing and input handling will be deleted at some point. + +I based my work on Andrew's regarding its port of RMI4 over HID (see +https://github.com/mightybigcar/synaptics-rmi4/tree/rmihid ) +This repo presents how the driver may looks like at the end: +https://github.com/mightybigcar/synaptics-rmi4/blob/rmihid/drivers/input/rmi4/rmi_hid.c + +Without this temporary solution, the workaround we gave to users +is to disable i2c-hid, which leads to disabling the touchscreen on the +XPS 11 and 12 (Haswell generation). + +Related bugs: +https://bugzilla.redhat.com/show_bug.cgi?id=1048314 +https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1218973 + +Signed-off-by: Andrew Duggan <aduggan@synaptics.com> +Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> +Signed-off-by: Jiri Kosina <jkosina@suse.cz> + +- Removed obviously wrong hid_hw_stop() at the end of probe + +Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> +--- + drivers/hid/Kconfig | 8 + + drivers/hid/Makefile | 1 + + drivers/hid/hid-core.c | 2 + + drivers/hid/hid-rmi.c | 890 +++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/hid.h | 2 + + 6 files changed, 946 insertions(+) + create mode 100644 drivers/hid/hid-rmi.c + +diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig +index 34e2d39..b2d733b 100644 +--- a/drivers/hid/Kconfig ++++ b/drivers/hid/Kconfig +@@ -645,6 +645,14 @@ config HID_SUNPLUS + ---help--- + Support for Sunplus wireless desktop. + ++config HID_RMI ++ tristate "Synaptics RMI4 device support" ++ depends on HID ++ ---help--- ++ Support for Synaptics RMI4 touchpads. ++ Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid ++ and want support for its special functionalities. ++ + config HID_GREENASIA + tristate "GreenAsia (Product ID 0x12) game controller support" + depends on HID +diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile +index 30e4431..b326f79 100644 +--- a/drivers/hid/Makefile ++++ b/drivers/hid/Makefile +@@ -96,6 +96,7 @@ obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ + hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ + hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ + hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o ++obj-$(CONFIG_HID_RMI) += hid-rmi.o + obj-$(CONFIG_HID_SAITEK) += hid-saitek.o + obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o + obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index 253fe23..543dd1f 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1836,6 +1836,8 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, ++ { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, +diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c +new file mode 100644 +index 0000000..a4f04d4 +--- /dev/null ++++ b/drivers/hid/hid-rmi.c +@@ -0,0 +1,890 @@ ++/* ++ * Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com> ++ * Copyright (c) 2013 Synaptics Incorporated ++ * Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com> ++ * Copyright (c) 2014 Red Hat, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/hid.h> ++#include <linux/input.h> ++#include <linux/input/mt.h> ++#include <linux/module.h> ++#include <linux/pm.h> ++#include <linux/slab.h> ++#include <linux/wait.h> ++#include <linux/sched.h> ++#include "hid-ids.h" ++ ++/* removed backported compat.h include */ ++ ++#define RMI_MOUSE_REPORT_ID 0x01 /* Mouse emulation Report */ ++#define RMI_WRITE_REPORT_ID 0x09 /* Output Report */ ++#define RMI_READ_ADDR_REPORT_ID 0x0a /* Output Report */ ++#define RMI_READ_DATA_REPORT_ID 0x0b /* Input Report */ ++#define RMI_ATTN_REPORT_ID 0x0c /* Input Report */ ++#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */ ++ ++/* flags */ ++#define RMI_READ_REQUEST_PENDING BIT(0) ++#define RMI_READ_DATA_PENDING BIT(1) ++#define RMI_STARTED BIT(2) ++ ++enum rmi_mode_type { ++ RMI_MODE_OFF = 0, ++ RMI_MODE_ATTN_REPORTS = 1, ++ RMI_MODE_NO_PACKED_ATTN_REPORTS = 2, ++}; ++ ++struct rmi_function { ++ unsigned page; /* page of the function */ ++ u16 query_base_addr; /* base address for queries */ ++ u16 command_base_addr; /* base address for commands */ ++ u16 control_base_addr; /* base address for controls */ ++ u16 data_base_addr; /* base address for datas */ ++ unsigned int interrupt_base; /* cross-function interrupt number ++ * (uniq in the device)*/ ++ unsigned int interrupt_count; /* number of interrupts */ ++ unsigned int report_size; /* size of a report */ ++ unsigned long irq_mask; /* mask of the interrupts ++ * (to be applied against ATTN IRQ) */ ++}; ++ ++/** ++ * struct rmi_data - stores information for hid communication ++ * ++ * @page_mutex: Locks current page to avoid changing pages in unexpected ways. ++ * @page: Keeps track of the current virtual page ++ * ++ * @wait: Used for waiting for read data ++ * ++ * @writeReport: output buffer when writing RMI registers ++ * @readReport: input buffer when reading RMI registers ++ * ++ * @input_report_size: size of an input report (advertised by HID) ++ * @output_report_size: size of an output report (advertised by HID) ++ * ++ * @flags: flags for the current device (started, reading, etc...) ++ * ++ * @f11: placeholder of internal RMI function F11 description ++ * @f30: placeholder of internal RMI function F30 description ++ * ++ * @max_fingers: maximum finger count reported by the device ++ * @max_x: maximum x value reported by the device ++ * @max_y: maximum y value reported by the device ++ * ++ * @gpio_led_count: count of GPIOs + LEDs reported by F30 ++ * @button_count: actual physical buttons count ++ * @button_mask: button mask used to decode GPIO ATTN reports ++ * @button_state_mask: pull state of the buttons ++ * ++ * @input: pointer to the kernel input device ++ * ++ * @reset_work: worker which will be called in case of a mouse report ++ * @hdev: pointer to the struct hid_device ++ */ ++struct rmi_data { ++ struct mutex page_mutex; ++ int page; ++ ++ wait_queue_head_t wait; ++ ++ u8 *writeReport; ++ u8 *readReport; ++ ++ int input_report_size; ++ int output_report_size; ++ ++ unsigned long flags; ++ ++ struct rmi_function f11; ++ struct rmi_function f30; ++ ++ unsigned int max_fingers; ++ unsigned int max_x; ++ unsigned int max_y; ++ unsigned int x_size_mm; ++ unsigned int y_size_mm; ++ ++ unsigned int gpio_led_count; ++ unsigned int button_count; ++ unsigned long button_mask; ++ unsigned long button_state_mask; ++ ++ struct input_dev *input; ++ ++ struct work_struct reset_work; ++ struct hid_device *hdev; ++}; ++ ++#define RMI_PAGE(addr) (((addr) >> 8) & 0xff) ++ ++static int rmi_write_report(struct hid_device *hdev, u8 *report, int len); ++ ++/** ++ * rmi_set_page - Set RMI page ++ * @hdev: The pointer to the hid_device struct ++ * @page: The new page address. ++ * ++ * RMI devices have 16-bit addressing, but some of the physical ++ * implementations (like SMBus) only have 8-bit addressing. So RMI implements ++ * a page address at 0xff of every page so we can reliable page addresses ++ * every 256 registers. ++ * ++ * The page_mutex lock must be held when this function is entered. ++ * ++ * Returns zero on success, non-zero on failure. ++ */ ++static int rmi_set_page(struct hid_device *hdev, u8 page) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ int retval; ++ ++ data->writeReport[0] = RMI_WRITE_REPORT_ID; ++ data->writeReport[1] = 1; ++ data->writeReport[2] = 0xFF; ++ data->writeReport[4] = page; ++ ++ retval = rmi_write_report(hdev, data->writeReport, ++ data->output_report_size); ++ if (retval != data->output_report_size) { ++ dev_err(&hdev->dev, ++ "%s: set page failed: %d.", __func__, retval); ++ return retval; ++ } ++ ++ data->page = page; ++ return 0; ++} ++ ++static int rmi_set_mode(struct hid_device *hdev, u8 mode) ++{ ++ int ret; ++ u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode}; ++ ++ ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf, ++ sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT); ++ if (ret < 0) { ++ dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode, ++ ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rmi_write_report(struct hid_device *hdev, u8 *report, int len) ++{ ++ int ret; ++ ++ ret = hid_hw_output_report(hdev, (void *)report, len); ++ if (ret < 0) { ++ dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf, ++ const int len) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ int ret; ++ int bytes_read; ++ int bytes_needed; ++ int retries; ++ int read_input_count; ++ ++ mutex_lock(&data->page_mutex); ++ ++ if (RMI_PAGE(addr) != data->page) { ++ ret = rmi_set_page(hdev, RMI_PAGE(addr)); ++ if (ret < 0) ++ goto exit; ++ } ++ ++ for (retries = 5; retries > 0; retries--) { ++ data->writeReport[0] = RMI_READ_ADDR_REPORT_ID; ++ data->writeReport[1] = 0; /* old 1 byte read count */ ++ data->writeReport[2] = addr & 0xFF; ++ data->writeReport[3] = (addr >> 8) & 0xFF; ++ data->writeReport[4] = len & 0xFF; ++ data->writeReport[5] = (len >> 8) & 0xFF; ++ ++ set_bit(RMI_READ_REQUEST_PENDING, &data->flags); ++ ++ ret = rmi_write_report(hdev, data->writeReport, ++ data->output_report_size); ++ if (ret != data->output_report_size) { ++ clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); ++ dev_err(&hdev->dev, ++ "failed to write request output report (%d)\n", ++ ret); ++ goto exit; ++ } ++ ++ bytes_read = 0; ++ bytes_needed = len; ++ while (bytes_read < len) { ++ if (!wait_event_timeout(data->wait, ++ test_bit(RMI_READ_DATA_PENDING, &data->flags), ++ msecs_to_jiffies(1000))) { ++ hid_warn(hdev, "%s: timeout elapsed\n", ++ __func__); ++ ret = -EAGAIN; ++ break; ++ } ++ ++ read_input_count = data->readReport[1]; ++ memcpy(buf + bytes_read, &data->readReport[2], ++ read_input_count < bytes_needed ? ++ read_input_count : bytes_needed); ++ ++ bytes_read += read_input_count; ++ bytes_needed -= read_input_count; ++ clear_bit(RMI_READ_DATA_PENDING, &data->flags); ++ } ++ ++ if (ret >= 0) { ++ ret = 0; ++ break; ++ } ++ } ++ ++exit: ++ clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); ++ mutex_unlock(&data->page_mutex); ++ return ret; ++} ++ ++static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf) ++{ ++ return rmi_read_block(hdev, addr, buf, 1); ++} ++ ++static void rmi_f11_process_touch(struct rmi_data *hdata, int slot, ++ u8 finger_state, u8 *touch_data) ++{ ++ int x, y, wx, wy; ++ int wide, major, minor; ++ int z; ++ ++ input_mt_slot(hdata->input, slot); ++ input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, ++ finger_state == 0x01); ++ if (finger_state == 0x01) { ++ x = (touch_data[0] << 4) | (touch_data[2] & 0x07); ++ y = (touch_data[1] << 4) | (touch_data[2] >> 4); ++ wx = touch_data[3] & 0x07; ++ wy = touch_data[3] >> 4; ++ wide = (wx > wy); ++ major = max(wx, wy); ++ minor = min(wx, wy); ++ z = touch_data[4]; ++ ++ /* y is inverted */ ++ y = hdata->max_y - y; ++ ++ input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x); ++ input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y); ++ input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide); ++ input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z); ++ input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); ++ input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); ++ } ++} ++ ++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); ++} ++ ++static inline int rmi_schedule_reset(struct hid_device *hdev) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ return schedule_work(&hdata->reset_work); ++} ++ ++static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, ++ int size) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ int offset; ++ int i; ++ ++ if (size < hdata->f11.report_size) ++ return 0; ++ ++ if (!(irq & hdata->f11.irq_mask)) ++ return 0; ++ ++ offset = (hdata->max_fingers >> 2) + 1; ++ for (i = 0; i < hdata->max_fingers; i++) { ++ int fs_byte_position = i >> 2; ++ int fs_bit_position = (i & 0x3) << 1; ++ int finger_state = (data[fs_byte_position] >> fs_bit_position) & ++ 0x03; ++ ++ rmi_f11_process_touch(hdata, i, finger_state, ++ &data[offset + 5 * i]); ++ } ++ input_mt_sync_frame(hdata->input); ++ input_sync(hdata->input); ++ return hdata->f11.report_size; ++} ++ ++static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data, ++ int size) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ int i; ++ int button = 0; ++ bool value; ++ ++ if (!(irq & hdata->f30.irq_mask)) ++ return 0; ++ ++ for (i = 0; i < hdata->gpio_led_count; i++) { ++ if (test_bit(i, &hdata->button_mask)) { ++ value = (data[i / 8] >> (i & 0x07)) & BIT(0); ++ if (test_bit(i, &hdata->button_state_mask)) ++ value = !value; ++ input_event(hdata->input, EV_KEY, BTN_LEFT + button++, ++ value); ++ } ++ } ++ return hdata->f30.report_size; ++} ++ ++static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ unsigned long irq_mask = 0; ++ unsigned index = 2; ++ ++ if (!(test_bit(RMI_STARTED, &hdata->flags))) ++ return 0; ++ ++ irq_mask |= hdata->f11.irq_mask; ++ irq_mask |= hdata->f30.irq_mask; ++ ++ if (data[1] & ~irq_mask) ++ hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", ++ data[1] & ~irq_mask, __FILE__, __LINE__); ++ ++ if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { ++ index += rmi_f11_input_event(hdev, data[1], &data[index], ++ size - index); ++ index += rmi_f30_input_event(hdev, data[1], &data[index], ++ size - index); ++ } else { ++ index += rmi_f30_input_event(hdev, data[1], &data[index], ++ size - index); ++ index += rmi_f11_input_event(hdev, data[1], &data[index], ++ size - index); ++ } ++ ++ return 1; ++} ++ ++static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ ++ if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { ++ hid_err(hdev, "no read request pending\n"); ++ return 0; ++ } ++ ++ memcpy(hdata->readReport, data, size < hdata->input_report_size ? ++ size : hdata->input_report_size); ++ set_bit(RMI_READ_DATA_PENDING, &hdata->flags); ++ wake_up(&hdata->wait); ++ ++ return 1; ++} ++ ++static int rmi_raw_event(struct hid_device *hdev, ++ struct hid_report *report, u8 *data, int size) ++{ ++ switch (data[0]) { ++ case RMI_READ_DATA_REPORT_ID: ++ return rmi_read_data_event(hdev, data, size); ++ case RMI_ATTN_REPORT_ID: ++ return rmi_input_event(hdev, data, size); ++ case RMI_MOUSE_REPORT_ID: ++ rmi_schedule_reset(hdev); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int rmi_post_reset(struct hid_device *hdev) ++{ ++ return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); ++} ++ ++static int rmi_post_resume(struct hid_device *hdev) ++{ ++ return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); ++} ++ ++#define RMI4_MAX_PAGE 0xff ++#define RMI4_PAGE_SIZE 0x0100 ++ ++#define PDT_START_SCAN_LOCATION 0x00e9 ++#define PDT_END_SCAN_LOCATION 0x0005 ++#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) ++ ++struct pdt_entry { ++ u8 query_base_addr:8; ++ u8 command_base_addr:8; ++ u8 control_base_addr:8; ++ u8 data_base_addr:8; ++ u8 interrupt_source_count:3; ++ u8 bits3and4:2; ++ u8 function_version:2; ++ u8 bit7:1; ++ u8 function_number:8; ++} __attribute__((__packed__)); ++ ++static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count) ++{ ++ return GENMASK(irq_count + irq_base - 1, irq_base); ++} ++ ++static void rmi_register_function(struct rmi_data *data, ++ struct pdt_entry *pdt_entry, int page, unsigned interrupt_count) ++{ ++ struct rmi_function *f = NULL; ++ u16 page_base = page << 8; ++ ++ switch (pdt_entry->function_number) { ++ case 0x11: ++ f = &data->f11; ++ break; ++ case 0x30: ++ f = &data->f30; ++ break; ++ } ++ ++ if (f) { ++ f->page = page; ++ f->query_base_addr = page_base | pdt_entry->query_base_addr; ++ f->command_base_addr = page_base | pdt_entry->command_base_addr; ++ f->control_base_addr = page_base | pdt_entry->control_base_addr; ++ f->data_base_addr = page_base | pdt_entry->data_base_addr; ++ f->interrupt_base = interrupt_count; ++ f->interrupt_count = pdt_entry->interrupt_source_count; ++ f->irq_mask = rmi_gen_mask(f->interrupt_base, ++ f->interrupt_count); ++ } ++} ++ ++static int rmi_scan_pdt(struct hid_device *hdev) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ struct pdt_entry entry; ++ int page; ++ bool page_has_function; ++ int i; ++ int retval; ++ int interrupt = 0; ++ u16 page_start, pdt_start , pdt_end; ++ ++ hid_info(hdev, "Scanning PDT...\n"); ++ ++ for (page = 0; (page <= RMI4_MAX_PAGE); page++) { ++ page_start = RMI4_PAGE_SIZE * page; ++ pdt_start = page_start + PDT_START_SCAN_LOCATION; ++ pdt_end = page_start + PDT_END_SCAN_LOCATION; ++ ++ page_has_function = false; ++ for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) { ++ retval = rmi_read_block(hdev, i, &entry, sizeof(entry)); ++ if (retval) { ++ hid_err(hdev, ++ "Read of PDT entry at %#06x failed.\n", ++ i); ++ goto error_exit; ++ } ++ ++ if (RMI4_END_OF_PDT(entry.function_number)) ++ break; ++ ++ page_has_function = true; ++ ++ hid_info(hdev, "Found F%02X on page %#04x\n", ++ entry.function_number, page); ++ ++ rmi_register_function(data, &entry, page, interrupt); ++ interrupt += entry.interrupt_source_count; ++ } ++ ++ if (!page_has_function) ++ break; ++ } ++ ++ hid_info(hdev, "%s: Done with PDT scan.\n", __func__); ++ retval = 0; ++ ++error_exit: ++ return retval; ++} ++ ++static int rmi_populate_f11(struct hid_device *hdev) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ u8 buf[20]; ++ int ret; ++ bool has_query12; ++ bool has_physical_props; ++ unsigned x_size, y_size; ++ ++ if (!data->f11.query_base_addr) { ++ hid_err(hdev, "No 2D sensor found, giving up.\n"); ++ return -ENODEV; ++ } ++ ++ /* query 0 contains some useful information */ ++ ret = rmi_read(hdev, data->f11.query_base_addr, buf); ++ if (ret) { ++ hid_err(hdev, "can not get query 0: %d.\n", ret); ++ return ret; ++ } ++ has_query12 = !!(buf[0] & BIT(5)); ++ ++ /* query 1 to get the max number of fingers */ ++ ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf); ++ if (ret) { ++ hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret); ++ return ret; ++ } ++ data->max_fingers = (buf[0] & 0x07) + 1; ++ if (data->max_fingers > 5) ++ data->max_fingers = 10; ++ ++ data->f11.report_size = data->max_fingers * 5 + ++ DIV_ROUND_UP(data->max_fingers, 4); ++ ++ if (!(buf[0] & BIT(4))) { ++ hid_err(hdev, "No absolute events, giving up.\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * query 12 to know if the physical properties are reported ++ * (query 12 is at offset 10 for HID devices) ++ */ ++ if (has_query12) { ++ ret = rmi_read(hdev, data->f11.query_base_addr + 10, buf); ++ if (ret) { ++ hid_err(hdev, "can not get query 12: %d.\n", ret); ++ return ret; ++ } ++ has_physical_props = !!(buf[0] & BIT(5)); ++ ++ if (has_physical_props) { ++ ret = rmi_read_block(hdev, ++ data->f11.query_base_addr + 11, buf, 4); ++ if (ret) { ++ hid_err(hdev, "can not read query 15-18: %d.\n", ++ ret); ++ return ret; ++ } ++ ++ x_size = buf[0] | (buf[1] << 8); ++ y_size = buf[2] | (buf[3] << 8); ++ ++ data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10); ++ data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10); ++ ++ hid_info(hdev, "%s: size in mm: %d x %d\n", ++ __func__, data->x_size_mm, data->y_size_mm); ++ } ++ } ++ ++ /* retrieve the ctrl registers */ ++ ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 20); ++ if (ret) { ++ hid_err(hdev, "can not read ctrl block of size 20: %d.\n", ret); ++ return ret; ++ } ++ ++ data->max_x = buf[6] | (buf[7] << 8); ++ data->max_y = buf[8] | (buf[9] << 8); ++ ++ return 0; ++} ++ ++static int rmi_populate_f30(struct hid_device *hdev) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ u8 buf[20]; ++ int ret; ++ bool has_gpio, has_led; ++ unsigned bytes_per_ctrl; ++ u8 ctrl2_addr; ++ int ctrl2_3_length; ++ int i; ++ ++ /* function F30 is for physical buttons */ ++ if (!data->f30.query_base_addr) { ++ hid_err(hdev, "No GPIO/LEDs found, giving up.\n"); ++ return -ENODEV; ++ } ++ ++ ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2); ++ if (ret) { ++ hid_err(hdev, "can not get F30 query registers: %d.\n", ret); ++ return ret; ++ } ++ ++ has_gpio = !!(buf[0] & BIT(3)); ++ has_led = !!(buf[0] & BIT(2)); ++ data->gpio_led_count = buf[1] & 0x1f; ++ ++ /* retrieve ctrl 2 & 3 registers */ ++ bytes_per_ctrl = (data->gpio_led_count + 7) / 8; ++ /* Ctrl0 is present only if both has_gpio and has_led are set*/ ++ ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0; ++ /* Ctrl1 is always be present */ ++ ctrl2_addr += bytes_per_ctrl; ++ ctrl2_3_length = 2 * bytes_per_ctrl; ++ ++ data->f30.report_size = bytes_per_ctrl; ++ ++ ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr, ++ buf, ctrl2_3_length); ++ if (ret) { ++ hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n", ++ ctrl2_3_length, ret); ++ return ret; ++ } ++ ++ for (i = 0; i < data->gpio_led_count; i++) { ++ int byte_position = i >> 3; ++ int bit_position = i & 0x07; ++ u8 dir_byte = buf[byte_position]; ++ u8 data_byte = buf[byte_position + bytes_per_ctrl]; ++ bool dir = (dir_byte >> bit_position) & BIT(0); ++ bool dat = (data_byte >> bit_position) & BIT(0); ++ ++ if (dir == 0) { ++ /* input mode */ ++ if (dat) { ++ /* actual buttons have pull up resistor */ ++ data->button_count++; ++ set_bit(i, &data->button_mask); ++ set_bit(i, &data->button_state_mask); ++ } ++ } ++ ++ } ++ ++ return 0; ++} ++ ++static int rmi_populate(struct hid_device *hdev) ++{ ++ int ret; ++ ++ ret = rmi_scan_pdt(hdev); ++ if (ret) { ++ hid_err(hdev, "PDT scan failed with code %d.\n", ret); ++ return ret; ++ } ++ ++ ret = rmi_populate_f11(hdev); ++ if (ret) { ++ hid_err(hdev, "Error while initializing F11 (%d).\n", ret); ++ return ret; ++ } ++ ++ ret = rmi_populate_f30(hdev); ++ if (ret) ++ hid_warn(hdev, "Error while initializing F30 (%d).\n", ret); ++ ++ return 0; ++} ++ ++static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) ++{ ++ struct rmi_data *data = hid_get_drvdata(hdev); ++ struct input_dev *input = hi->input; ++ int ret; ++ int res_x, res_y, i; ++ ++ data->input = input; ++ ++ hid_dbg(hdev, "Opening low level driver\n"); ++ ret = hid_hw_open(hdev); ++ if (ret) ++ return; ++ ++ /* Allow incoming hid reports */ ++ hid_device_io_start(hdev); ++ ++ ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); ++ if (ret < 0) { ++ dev_err(&hdev->dev, "failed to set rmi mode\n"); ++ goto exit; ++ } ++ ++ ret = rmi_set_page(hdev, 0); ++ if (ret < 0) { ++ dev_err(&hdev->dev, "failed to set page select to 0.\n"); ++ goto exit; ++ } ++ ++ ret = rmi_populate(hdev); ++ if (ret) ++ goto exit; ++ ++ __set_bit(EV_ABS, input->evbit); ++ input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0); ++ input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0); ++ ++ if (data->x_size_mm && data->x_size_mm) { ++ res_x = (data->max_x - 1) / data->x_size_mm; ++ res_y = (data->max_y - 1) / data->x_size_mm; ++ ++ input_abs_set_res(input, ABS_MT_POSITION_X, res_x); ++ input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); ++ } ++ ++ input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); ++ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); ++ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); ++ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); ++ ++ input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); ++ ++ if (data->button_count) { ++ __set_bit(EV_KEY, input->evbit); ++ for (i = 0; i < data->button_count; i++) ++ __set_bit(BTN_LEFT + i, input->keybit); ++ ++ if (data->button_count == 1) ++ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); ++ } ++ ++ set_bit(RMI_STARTED, &data->flags); ++ ++exit: ++ hid_device_io_stop(hdev); ++ hid_hw_close(hdev); ++} ++ ++static int rmi_input_mapping(struct hid_device *hdev, ++ struct hid_input *hi, struct hid_field *field, ++ struct hid_usage *usage, unsigned long **bit, int *max) ++{ ++ /* we want to make HID ignore the advertised HID collection */ ++ return -1; ++} ++ ++static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) ++{ ++ struct rmi_data *data = NULL; ++ int ret; ++ size_t alloc_size; ++ ++ data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ INIT_WORK(&data->reset_work, rmi_reset_work); ++ data->hdev = hdev; ++ ++ hid_set_drvdata(hdev, data); ++ ++ hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; ++ ++ ret = hid_parse(hdev); ++ if (ret) { ++ hid_err(hdev, "parse failed\n"); ++ return ret; ++ } ++ ++ data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] ++ .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) ++ + 1 /* report id */; ++ data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] ++ .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) ++ + 1 /* report id */; ++ ++ alloc_size = data->output_report_size + data->input_report_size; ++ ++ data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); ++ if (!data->writeReport) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ data->readReport = data->writeReport + data->output_report_size; ++ ++ init_waitqueue_head(&data->wait); ++ ++ mutex_init(&data->page_mutex); ++ ++ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); ++ if (ret) { ++ hid_err(hdev, "hw start failed\n"); ++ return ret; ++ } ++ ++ if (!test_bit(RMI_STARTED, &data->flags)) { ++ hid_hw_stop(hdev); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void rmi_remove(struct hid_device *hdev) ++{ ++ struct rmi_data *hdata = hid_get_drvdata(hdev); ++ ++ clear_bit(RMI_STARTED, &hdata->flags); ++ ++ hid_hw_stop(hdev); ++} ++ ++static const struct hid_device_id rmi_id[] = { ++ { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, rmi_id); ++ ++static struct hid_driver rmi_driver = { ++ .name = "hid-rmi", ++ .id_table = rmi_id, ++ .probe = rmi_probe, ++ .remove = rmi_remove, ++ .raw_event = rmi_raw_event, ++ .input_mapping = rmi_input_mapping, ++ .input_configured = rmi_input_configured, ++#ifdef CONFIG_PM ++ .resume = rmi_post_resume, ++ .reset_resume = rmi_post_reset, ++#endif ++}; ++ ++module_hid_driver(rmi_driver); ++ ++MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); ++MODULE_DESCRIPTION("RMI HID driver"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 31b9d29..1b5f1e9 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -571,6 +571,8 @@ struct hid_descriptor { + .bus = BUS_USB, .vendor = (ven), .product = (prod) + #define HID_BLUETOOTH_DEVICE(ven, prod) \ + .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod) ++#define HID_I2C_DEVICE(ven, prod) \ ++ .bus = BUS_I2C, .vendor = (ven), .product = (prod) + + #define HID_REPORT_ID(rep) \ + .report_type = (rep) +-- +1.8.3.1 + |