18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HID Driver for ELAN Touchpad 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Currently only supports touchpad found on HP Pavilion X2 10 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2016 Alexandrov Stanislav <neko@nya.ai> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/hid.h> 118c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 128c2ecf20Sopenharmony_ci#include <linux/leds.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "hid-ids.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define ELAN_MT_I2C 0x5d 198c2ecf20Sopenharmony_ci#define ELAN_SINGLE_FINGER 0x81 208c2ecf20Sopenharmony_ci#define ELAN_MT_FIRST_FINGER 0x82 218c2ecf20Sopenharmony_ci#define ELAN_MT_SECOND_FINGER 0x83 228c2ecf20Sopenharmony_ci#define ELAN_INPUT_REPORT_SIZE 8 238c2ecf20Sopenharmony_ci#define ELAN_I2C_REPORT_SIZE 32 248c2ecf20Sopenharmony_ci#define ELAN_FINGER_DATA_LEN 5 258c2ecf20Sopenharmony_ci#define ELAN_MAX_FINGERS 5 268c2ecf20Sopenharmony_ci#define ELAN_MAX_PRESSURE 255 278c2ecf20Sopenharmony_ci#define ELAN_TP_USB_INTF 1 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define ELAN_FEATURE_REPORT 0x0d 308c2ecf20Sopenharmony_ci#define ELAN_FEATURE_SIZE 5 318c2ecf20Sopenharmony_ci#define ELAN_PARAM_MAX_X 6 328c2ecf20Sopenharmony_ci#define ELAN_PARAM_MAX_Y 7 338c2ecf20Sopenharmony_ci#define ELAN_PARAM_RES 8 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ELAN_MUTE_LED_REPORT 0xBC 368c2ecf20Sopenharmony_ci#define ELAN_LED_REPORT_SIZE 8 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ELAN_HAS_LED BIT(0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct elan_drvdata { 418c2ecf20Sopenharmony_ci struct input_dev *input; 428c2ecf20Sopenharmony_ci u8 prev_report[ELAN_INPUT_REPORT_SIZE]; 438c2ecf20Sopenharmony_ci struct led_classdev mute_led; 448c2ecf20Sopenharmony_ci u8 mute_led_state; 458c2ecf20Sopenharmony_ci u16 max_x; 468c2ecf20Sopenharmony_ci u16 max_y; 478c2ecf20Sopenharmony_ci u16 res_x; 488c2ecf20Sopenharmony_ci u16 res_y; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int is_not_elan_touchpad(struct hid_device *hdev) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (hid_is_usb(hdev)) { 548c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return (intf->altsetting->desc.bInterfaceNumber != 578c2ecf20Sopenharmony_ci ELAN_TP_USB_INTF); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, 648c2ecf20Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 658c2ecf20Sopenharmony_ci unsigned long **bit, int *max) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci if (is_not_elan_touchpad(hdev)) 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (field->report->id == ELAN_SINGLE_FINGER || 718c2ecf20Sopenharmony_ci field->report->id == ELAN_MT_FIRST_FINGER || 728c2ecf20Sopenharmony_ci field->report->id == ELAN_MT_SECOND_FINGER || 738c2ecf20Sopenharmony_ci field->report->id == ELAN_MT_I2C) 748c2ecf20Sopenharmony_ci return -1; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int elan_get_device_param(struct hid_device *hdev, 808c2ecf20Sopenharmony_ci unsigned char *dmabuf, unsigned char param) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dmabuf[0] = ELAN_FEATURE_REPORT; 858c2ecf20Sopenharmony_ci dmabuf[1] = 0x05; 868c2ecf20Sopenharmony_ci dmabuf[2] = 0x03; 878c2ecf20Sopenharmony_ci dmabuf[3] = param; 888c2ecf20Sopenharmony_ci dmabuf[4] = 0x01; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, 918c2ecf20Sopenharmony_ci ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, 928c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 938c2ecf20Sopenharmony_ci if (ret != ELAN_FEATURE_SIZE) { 948c2ecf20Sopenharmony_ci hid_err(hdev, "Set report error for parm %d: %d\n", param, ret); 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, 998c2ecf20Sopenharmony_ci ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, 1008c2ecf20Sopenharmony_ci HID_REQ_GET_REPORT); 1018c2ecf20Sopenharmony_ci if (ret != ELAN_FEATURE_SIZE) { 1028c2ecf20Sopenharmony_ci hid_err(hdev, "Get report error for parm %d: %d\n", param, ret); 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic unsigned int elan_convert_res(char val) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * (value from firmware) * 10 + 790 = dpi 1138c2ecf20Sopenharmony_ci * dpi * 10 / 254 = dots/mm 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci return (val * 10 + 790) * 10 / 254; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int elan_get_device_params(struct hid_device *hdev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 1218c2ecf20Sopenharmony_ci unsigned char *dmabuf; 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci dmabuf = kmalloc(ELAN_FEATURE_SIZE, GFP_KERNEL); 1258c2ecf20Sopenharmony_ci if (!dmabuf) 1268c2ecf20Sopenharmony_ci return -ENOMEM; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_X); 1298c2ecf20Sopenharmony_ci if (ret) 1308c2ecf20Sopenharmony_ci goto err; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci drvdata->max_x = (dmabuf[4] << 8) | dmabuf[3]; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_Y); 1358c2ecf20Sopenharmony_ci if (ret) 1368c2ecf20Sopenharmony_ci goto err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3]; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_RES); 1418c2ecf20Sopenharmony_ci if (ret) 1428c2ecf20Sopenharmony_ci goto err; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci drvdata->res_x = elan_convert_res(dmabuf[3]); 1458c2ecf20Sopenharmony_ci drvdata->res_y = elan_convert_res(dmabuf[4]); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cierr: 1488c2ecf20Sopenharmony_ci kfree(dmabuf); 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci struct input_dev *input; 1568c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (is_not_elan_touchpad(hdev)) 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = elan_get_device_params(hdev); 1628c2ecf20Sopenharmony_ci if (ret) 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci input = devm_input_allocate_device(&hdev->dev); 1668c2ecf20Sopenharmony_ci if (!input) 1678c2ecf20Sopenharmony_ci return -ENOMEM; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci input->name = "Elan Touchpad"; 1708c2ecf20Sopenharmony_ci input->phys = hdev->phys; 1718c2ecf20Sopenharmony_ci input->uniq = hdev->uniq; 1728c2ecf20Sopenharmony_ci input->id.bustype = hdev->bus; 1738c2ecf20Sopenharmony_ci input->id.vendor = hdev->vendor; 1748c2ecf20Sopenharmony_ci input->id.product = hdev->product; 1758c2ecf20Sopenharmony_ci input->id.version = hdev->version; 1768c2ecf20Sopenharmony_ci input->dev.parent = &hdev->dev; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x, 1798c2ecf20Sopenharmony_ci 0, 0); 1808c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y, 1818c2ecf20Sopenharmony_ci 0, 0); 1828c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE, 1838c2ecf20Sopenharmony_ci 0, 0); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci __set_bit(BTN_LEFT, input->keybit); 1868c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER); 1898c2ecf20Sopenharmony_ci if (ret) { 1908c2ecf20Sopenharmony_ci hid_err(hdev, "Failed to init elan MT slots: %d\n", ret); 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci input_abs_set_res(input, ABS_X, drvdata->res_x); 1958c2ecf20Sopenharmony_ci input_abs_set_res(input, ABS_Y, drvdata->res_y); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = input_register_device(input); 1988c2ecf20Sopenharmony_ci if (ret) { 1998c2ecf20Sopenharmony_ci hid_err(hdev, "Failed to register elan input device: %d\n", 2008c2ecf20Sopenharmony_ci ret); 2018c2ecf20Sopenharmony_ci input_mt_destroy_slots(input); 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci drvdata->input = input; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, 2118c2ecf20Sopenharmony_ci unsigned int slot_num) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct input_dev *input = drvdata->input; 2148c2ecf20Sopenharmony_ci int x, y, p; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci bool active = !!data; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci input_mt_slot(input, slot_num); 2198c2ecf20Sopenharmony_ci input_mt_report_slot_state(input, MT_TOOL_FINGER, active); 2208c2ecf20Sopenharmony_ci if (active) { 2218c2ecf20Sopenharmony_ci x = ((data[0] & 0xF0) << 4) | data[1]; 2228c2ecf20Sopenharmony_ci y = drvdata->max_y - 2238c2ecf20Sopenharmony_ci (((data[0] & 0x07) << 8) | data[2]); 2248c2ecf20Sopenharmony_ci p = data[4]; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_X, x); 2278c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_Y, y); 2288c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_PRESSURE, p); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void elan_usb_report_input(struct elan_drvdata *drvdata, u8 *data) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci int i; 2358c2ecf20Sopenharmony_ci struct input_dev *input = drvdata->input; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * There is 3 types of reports: for single touch, 2398c2ecf20Sopenharmony_ci * for multitouch - first finger and for multitouch - second finger 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * packet structure for ELAN_SINGLE_FINGER and ELAN_MT_FIRST_FINGER: 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * byte 1: 1 0 0 0 0 0 0 1 // 0x81 or 0x82 2448c2ecf20Sopenharmony_ci * byte 2: 0 0 0 0 0 0 0 0 // looks like unused 2458c2ecf20Sopenharmony_ci * byte 3: f5 f4 f3 f2 f1 0 0 L 2468c2ecf20Sopenharmony_ci * byte 4: x12 x11 x10 x9 0? y11 y10 y9 2478c2ecf20Sopenharmony_ci * byte 5: x8 x7 x6 x5 x4 x3 x2 x1 2488c2ecf20Sopenharmony_ci * byte 6: y8 y7 y6 y5 y4 y3 y2 y1 2498c2ecf20Sopenharmony_ci * byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 2508c2ecf20Sopenharmony_ci * byte 8: p8 p7 p6 p5 p4 p3 p2 p1 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * packet structure for ELAN_MT_SECOND_FINGER: 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * byte 1: 1 0 0 0 0 0 1 1 // 0x83 2558c2ecf20Sopenharmony_ci * byte 2: x12 x11 x10 x9 0 y11 y10 y9 2568c2ecf20Sopenharmony_ci * byte 3: x8 x7 x6 x5 x4 x3 x2 x1 2578c2ecf20Sopenharmony_ci * byte 4: y8 y7 y6 y5 y4 y3 y2 y1 2588c2ecf20Sopenharmony_ci * byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 2598c2ecf20Sopenharmony_ci * byte 6: p8 p7 p6 p5 p4 p3 p2 p1 2608c2ecf20Sopenharmony_ci * byte 7: 0 0 0 0 0 0 0 0 2618c2ecf20Sopenharmony_ci * byte 8: 0 0 0 0 0 0 0 0 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * f5-f1: finger touch bits 2648c2ecf20Sopenharmony_ci * L: clickpad button 2658c2ecf20Sopenharmony_ci * sy / sx: finger width / height expressed in traces, the total number 2668c2ecf20Sopenharmony_ci * of traces can be queried by doing a HID_REQ_SET_REPORT 2678c2ecf20Sopenharmony_ci * { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the 2688c2ecf20Sopenharmony_ci * returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces. 2698c2ecf20Sopenharmony_ci * p: pressure 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (data[0] == ELAN_SINGLE_FINGER) { 2738c2ecf20Sopenharmony_ci for (i = 0; i < ELAN_MAX_FINGERS; i++) { 2748c2ecf20Sopenharmony_ci if (data[2] & BIT(i + 3)) 2758c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, data + 3, i); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, NULL, i); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci input_report_key(input, BTN_LEFT, data[2] & 0x01); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * When touched with two fingers Elan touchpad will emit two HID reports 2838c2ecf20Sopenharmony_ci * first is ELAN_MT_FIRST_FINGER and second is ELAN_MT_SECOND_FINGER 2848c2ecf20Sopenharmony_ci * we will save ELAN_MT_FIRST_FINGER report and wait for 2858c2ecf20Sopenharmony_ci * ELAN_MT_SECOND_FINGER to finish multitouch 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci if (data[0] == ELAN_MT_FIRST_FINGER) { 2888c2ecf20Sopenharmony_ci memcpy(drvdata->prev_report, data, 2898c2ecf20Sopenharmony_ci sizeof(drvdata->prev_report)); 2908c2ecf20Sopenharmony_ci return; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (data[0] == ELAN_MT_SECOND_FINGER) { 2948c2ecf20Sopenharmony_ci int first = 0; 2958c2ecf20Sopenharmony_ci u8 *prev_report = drvdata->prev_report; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (prev_report[0] != ELAN_MT_FIRST_FINGER) 2988c2ecf20Sopenharmony_ci return; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci for (i = 0; i < ELAN_MAX_FINGERS; i++) { 3018c2ecf20Sopenharmony_ci if (prev_report[2] & BIT(i + 3)) { 3028c2ecf20Sopenharmony_ci if (!first) { 3038c2ecf20Sopenharmony_ci first = 1; 3048c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, prev_report + 3, i); 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, data + 1, i); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, NULL, i); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci input_report_key(input, BTN_LEFT, prev_report[2] & 0x01); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci input_mt_sync_frame(input); 3168c2ecf20Sopenharmony_ci input_sync(input); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void elan_i2c_report_input(struct elan_drvdata *drvdata, u8 *data) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct input_dev *input = drvdata->input; 3228c2ecf20Sopenharmony_ci u8 *finger_data; 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Elan MT touchpads in i2c mode send finger data in the same format 3278c2ecf20Sopenharmony_ci * as in USB mode, but then with all fingers in a single packet. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * packet structure for ELAN_MT_I2C: 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * byte 1: 1 0 0 1 1 1 0 1 // 0x5d 3328c2ecf20Sopenharmony_ci * byte 2: f5 f4 f3 f2 f1 0 0 L 3338c2ecf20Sopenharmony_ci * byte 3: x12 x11 x10 x9 0? y11 y10 y9 3348c2ecf20Sopenharmony_ci * byte 4: x8 x7 x6 x5 x4 x3 x2 x1 3358c2ecf20Sopenharmony_ci * byte 5: y8 y7 y6 y5 y4 y3 y2 y1 3368c2ecf20Sopenharmony_ci * byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 3378c2ecf20Sopenharmony_ci * byte 7: p8 p7 p6 p5 p4 p3 p2 p1 3388c2ecf20Sopenharmony_ci * byte 8-12: Same as byte 3-7 for second finger down 3398c2ecf20Sopenharmony_ci * byte 13-17: Same as byte 3-7 for third finger down 3408c2ecf20Sopenharmony_ci * byte 18-22: Same as byte 3-7 for fourth finger down 3418c2ecf20Sopenharmony_ci * byte 23-27: Same as byte 3-7 for fifth finger down 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci finger_data = data + 2; 3458c2ecf20Sopenharmony_ci for (i = 0; i < ELAN_MAX_FINGERS; i++) { 3468c2ecf20Sopenharmony_ci if (data[1] & BIT(i + 3)) { 3478c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, finger_data, i); 3488c2ecf20Sopenharmony_ci finger_data += ELAN_FINGER_DATA_LEN; 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci elan_report_mt_slot(drvdata, NULL, i); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci input_report_key(input, BTN_LEFT, data[1] & 0x01); 3558c2ecf20Sopenharmony_ci input_mt_sync_frame(input); 3568c2ecf20Sopenharmony_ci input_sync(input); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int elan_raw_event(struct hid_device *hdev, 3608c2ecf20Sopenharmony_ci struct hid_report *report, u8 *data, int size) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (is_not_elan_touchpad(hdev)) 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (data[0] == ELAN_SINGLE_FINGER || 3688c2ecf20Sopenharmony_ci data[0] == ELAN_MT_FIRST_FINGER || 3698c2ecf20Sopenharmony_ci data[0] == ELAN_MT_SECOND_FINGER) { 3708c2ecf20Sopenharmony_ci if (size == ELAN_INPUT_REPORT_SIZE) { 3718c2ecf20Sopenharmony_ci elan_usb_report_input(drvdata, data); 3728c2ecf20Sopenharmony_ci return 1; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) { 3778c2ecf20Sopenharmony_ci elan_i2c_report_input(drvdata, data); 3788c2ecf20Sopenharmony_ci return 1; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int elan_start_multitouch(struct hid_device *hdev) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * This byte sequence will enable multitouch mode and disable 3908c2ecf20Sopenharmony_ci * mouse emulation 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci static const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 }; 3938c2ecf20Sopenharmony_ci unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!dmabuf) 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf), 3998c2ecf20Sopenharmony_ci HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci kfree(dmabuf); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (ret != sizeof(buf)) { 4048c2ecf20Sopenharmony_ci hid_err(hdev, "Failed to start multitouch: %d\n", ret); 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic enum led_brightness elan_mute_led_get_brigtness(struct led_classdev *led_cdev) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct device *dev = led_cdev->dev->parent; 4148c2ecf20Sopenharmony_ci struct hid_device *hdev = to_hid_device(dev); 4158c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return drvdata->mute_led_state; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int elan_mute_led_set_brigtness(struct led_classdev *led_cdev, 4218c2ecf20Sopenharmony_ci enum led_brightness value) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci int ret; 4248c2ecf20Sopenharmony_ci u8 led_state; 4258c2ecf20Sopenharmony_ci struct device *dev = led_cdev->dev->parent; 4268c2ecf20Sopenharmony_ci struct hid_device *hdev = to_hid_device(dev); 4278c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci unsigned char *dmabuf = kzalloc(ELAN_LED_REPORT_SIZE, GFP_KERNEL); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!dmabuf) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci led_state = !!value; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci dmabuf[0] = ELAN_MUTE_LED_REPORT; 4378c2ecf20Sopenharmony_ci dmabuf[1] = 0x02; 4388c2ecf20Sopenharmony_ci dmabuf[2] = led_state; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, ELAN_LED_REPORT_SIZE, 4418c2ecf20Sopenharmony_ci HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci kfree(dmabuf); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (ret != ELAN_LED_REPORT_SIZE) { 4468c2ecf20Sopenharmony_ci hid_err(hdev, "Failed to set mute led brightness: %d\n", ret); 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci drvdata->mute_led_state = led_state; 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int elan_init_mute_led(struct hid_device *hdev) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata = hid_get_drvdata(hdev); 4578c2ecf20Sopenharmony_ci struct led_classdev *mute_led = &drvdata->mute_led; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mute_led->name = "elan:red:mute"; 4608c2ecf20Sopenharmony_ci mute_led->brightness_get = elan_mute_led_get_brigtness; 4618c2ecf20Sopenharmony_ci mute_led->brightness_set_blocking = elan_mute_led_set_brigtness; 4628c2ecf20Sopenharmony_ci mute_led->max_brightness = LED_ON; 4638c2ecf20Sopenharmony_ci mute_led->dev = &hdev->dev; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return devm_led_classdev_register(&hdev->dev, mute_led); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int ret; 4718c2ecf20Sopenharmony_ci struct elan_drvdata *drvdata; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!drvdata) 4768c2ecf20Sopenharmony_ci return -ENOMEM; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, drvdata); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = hid_parse(hdev); 4818c2ecf20Sopenharmony_ci if (ret) { 4828c2ecf20Sopenharmony_ci hid_err(hdev, "Hid Parse failed\n"); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 4878c2ecf20Sopenharmony_ci if (ret) { 4888c2ecf20Sopenharmony_ci hid_err(hdev, "Hid hw start failed\n"); 4898c2ecf20Sopenharmony_ci return ret; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (is_not_elan_touchpad(hdev)) 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!drvdata->input) { 4968c2ecf20Sopenharmony_ci hid_err(hdev, "Input device is not registered\n"); 4978c2ecf20Sopenharmony_ci ret = -ENAVAIL; 4988c2ecf20Sopenharmony_ci goto err; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ret = elan_start_multitouch(hdev); 5028c2ecf20Sopenharmony_ci if (ret) 5038c2ecf20Sopenharmony_ci goto err; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (id->driver_data & ELAN_HAS_LED) { 5068c2ecf20Sopenharmony_ci ret = elan_init_mute_led(hdev); 5078c2ecf20Sopenharmony_ci if (ret) 5088c2ecf20Sopenharmony_ci goto err; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_cierr: 5138c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic void elan_remove(struct hid_device *hdev) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct hid_device_id elan_devices[] = { 5238c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2), 5248c2ecf20Sopenharmony_ci .driver_data = ELAN_HAS_LED }, 5258c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), 5268c2ecf20Sopenharmony_ci .driver_data = ELAN_HAS_LED }, 5278c2ecf20Sopenharmony_ci { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) }, 5288c2ecf20Sopenharmony_ci { } 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, elan_devices); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic struct hid_driver elan_driver = { 5338c2ecf20Sopenharmony_ci .name = "elan", 5348c2ecf20Sopenharmony_ci .id_table = elan_devices, 5358c2ecf20Sopenharmony_ci .input_mapping = elan_input_mapping, 5368c2ecf20Sopenharmony_ci .input_configured = elan_input_configured, 5378c2ecf20Sopenharmony_ci .raw_event = elan_raw_event, 5388c2ecf20Sopenharmony_ci .probe = elan_probe, 5398c2ecf20Sopenharmony_ci .remove = elan_remove, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cimodule_hid_driver(elan_driver); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexandrov Stanislav"); 5468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for HID ELAN Touchpads"); 547