18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Samsung S6SY761 Touchscreen device driver 38c2ecf20Sopenharmony_ci// 48c2ecf20Sopenharmony_ci// Copyright (c) 2017 Samsung Electronics Co., Ltd. 58c2ecf20Sopenharmony_ci// Copyright (c) 2017 Andi Shyti <andi@etezian.org> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 118c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* commands */ 198c2ecf20Sopenharmony_ci#define S6SY761_SENSE_ON 0x10 208c2ecf20Sopenharmony_ci#define S6SY761_SENSE_OFF 0x11 218c2ecf20Sopenharmony_ci#define S6SY761_TOUCH_FUNCTION 0x30 /* R/W for get/set */ 228c2ecf20Sopenharmony_ci#define S6SY761_FIRMWARE_INTEGRITY 0x21 238c2ecf20Sopenharmony_ci#define S6SY761_PANEL_INFO 0x23 248c2ecf20Sopenharmony_ci#define S6SY761_DEVICE_ID 0x52 258c2ecf20Sopenharmony_ci#define S6SY761_BOOT_STATUS 0x55 268c2ecf20Sopenharmony_ci#define S6SY761_READ_ONE_EVENT 0x60 278c2ecf20Sopenharmony_ci#define S6SY761_READ_ALL_EVENT 0x61 288c2ecf20Sopenharmony_ci#define S6SY761_CLEAR_EVENT_STACK 0x62 298c2ecf20Sopenharmony_ci#define S6SY761_APPLICATION_MODE 0xe4 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* events */ 328c2ecf20Sopenharmony_ci#define S6SY761_EVENT_INFO 0x02 338c2ecf20Sopenharmony_ci#define S6SY761_EVENT_VENDOR_INFO 0x07 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* info */ 368c2ecf20Sopenharmony_ci#define S6SY761_INFO_BOOT_COMPLETE 0x00 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* firmware status */ 398c2ecf20Sopenharmony_ci#define S6SY761_FW_OK 0x80 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * the functionalities are put as a reference 438c2ecf20Sopenharmony_ci * as in the device I am using none of them 448c2ecf20Sopenharmony_ci * works therefore not used in this driver yet. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci/* touchscreen functionalities */ 478c2ecf20Sopenharmony_ci#define S6SY761_MASK_TOUCH BIT(0) 488c2ecf20Sopenharmony_ci#define S6SY761_MASK_HOVER BIT(1) 498c2ecf20Sopenharmony_ci#define S6SY761_MASK_COVER BIT(2) 508c2ecf20Sopenharmony_ci#define S6SY761_MASK_GLOVE BIT(3) 518c2ecf20Sopenharmony_ci#define S6SY761_MASK_STYLUS BIT(4) 528c2ecf20Sopenharmony_ci#define S6SY761_MASK_PALM BIT(5) 538c2ecf20Sopenharmony_ci#define S6SY761_MASK_WET BIT(6) 548c2ecf20Sopenharmony_ci#define S6SY761_MASK_PROXIMITY BIT(7) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* boot status (BS) */ 578c2ecf20Sopenharmony_ci#define S6SY761_BS_BOOT_LOADER 0x10 588c2ecf20Sopenharmony_ci#define S6SY761_BS_APPLICATION 0x20 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* event id */ 618c2ecf20Sopenharmony_ci#define S6SY761_EVENT_ID_COORDINATE 0x00 628c2ecf20Sopenharmony_ci#define S6SY761_EVENT_ID_STATUS 0x01 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* event register masks */ 658c2ecf20Sopenharmony_ci#define S6SY761_MASK_TOUCH_STATE 0xc0 /* byte 0 */ 668c2ecf20Sopenharmony_ci#define S6SY761_MASK_TID 0x3c 678c2ecf20Sopenharmony_ci#define S6SY761_MASK_EID 0x03 688c2ecf20Sopenharmony_ci#define S6SY761_MASK_X 0xf0 /* byte 3 */ 698c2ecf20Sopenharmony_ci#define S6SY761_MASK_Y 0x0f 708c2ecf20Sopenharmony_ci#define S6SY761_MASK_Z 0x3f /* byte 6 */ 718c2ecf20Sopenharmony_ci#define S6SY761_MASK_LEFT_EVENTS 0x3f /* byte 7 */ 728c2ecf20Sopenharmony_ci#define S6SY761_MASK_TOUCH_TYPE 0xc0 /* MSB in byte 6, LSB in byte 7 */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* event touch state values */ 758c2ecf20Sopenharmony_ci#define S6SY761_TS_NONE 0x00 768c2ecf20Sopenharmony_ci#define S6SY761_TS_PRESS 0x01 778c2ecf20Sopenharmony_ci#define S6SY761_TS_MOVE 0x02 788c2ecf20Sopenharmony_ci#define S6SY761_TS_RELEASE 0x03 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* application modes */ 818c2ecf20Sopenharmony_ci#define S6SY761_APP_NORMAL 0x0 828c2ecf20Sopenharmony_ci#define S6SY761_APP_LOW_POWER 0x1 838c2ecf20Sopenharmony_ci#define S6SY761_APP_TEST 0x2 848c2ecf20Sopenharmony_ci#define S6SY761_APP_FLASH 0x3 858c2ecf20Sopenharmony_ci#define S6SY761_APP_SLEEP 0x4 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define S6SY761_EVENT_SIZE 8 888c2ecf20Sopenharmony_ci#define S6SY761_EVENT_COUNT 32 898c2ecf20Sopenharmony_ci#define S6SY761_DEVID_SIZE 3 908c2ecf20Sopenharmony_ci#define S6SY761_PANEL_ID_SIZE 11 918c2ecf20Sopenharmony_ci#define S6SY761_TS_STATUS_SIZE 5 928c2ecf20Sopenharmony_ci#define S6SY761_MAX_FINGERS 10 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define S6SY761_DEV_NAME "s6sy761" 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cienum s6sy761_regulators { 978c2ecf20Sopenharmony_ci S6SY761_REGULATOR_VDD, 988c2ecf20Sopenharmony_ci S6SY761_REGULATOR_AVDD, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct s6sy761_data { 1028c2ecf20Sopenharmony_ci struct i2c_client *client; 1038c2ecf20Sopenharmony_ci struct regulator_bulk_data regulators[2]; 1048c2ecf20Sopenharmony_ci struct input_dev *input; 1058c2ecf20Sopenharmony_ci struct touchscreen_properties prop; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci u8 data[S6SY761_EVENT_SIZE * S6SY761_EVENT_COUNT]; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci u16 devid; 1108c2ecf20Sopenharmony_ci u8 tx_channel; 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * We can't simply use i2c_smbus_read_i2c_block_data because we 1158c2ecf20Sopenharmony_ci * need to read more than 255 bytes 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic int s6sy761_read_events(struct s6sy761_data *sdata, u16 n_events) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u8 cmd = S6SY761_READ_ALL_EVENT; 1208c2ecf20Sopenharmony_ci struct i2c_msg msgs[2] = { 1218c2ecf20Sopenharmony_ci { 1228c2ecf20Sopenharmony_ci .addr = sdata->client->addr, 1238c2ecf20Sopenharmony_ci .len = 1, 1248c2ecf20Sopenharmony_ci .buf = &cmd, 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci { 1278c2ecf20Sopenharmony_ci .addr = sdata->client->addr, 1288c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 1298c2ecf20Sopenharmony_ci .len = (n_events * S6SY761_EVENT_SIZE), 1308c2ecf20Sopenharmony_ci .buf = sdata->data + S6SY761_EVENT_SIZE, 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci }; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); 1368c2ecf20Sopenharmony_ci if (ret < 0) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void s6sy761_report_coordinates(struct s6sy761_data *sdata, 1438c2ecf20Sopenharmony_ci u8 *event, u8 tid) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u8 major = event[4]; 1468c2ecf20Sopenharmony_ci u8 minor = event[5]; 1478c2ecf20Sopenharmony_ci u8 z = event[6] & S6SY761_MASK_Z; 1488c2ecf20Sopenharmony_ci u16 x = (event[1] << 4) | ((event[3] & S6SY761_MASK_X) >> 4); 1498c2ecf20Sopenharmony_ci u16 y = (event[2] << 4) | (event[3] & S6SY761_MASK_Y); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci input_mt_slot(sdata->input, tid); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); 1548c2ecf20Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_POSITION_X, x); 1558c2ecf20Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); 1568c2ecf20Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, major); 1578c2ecf20Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, minor); 1588c2ecf20Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_PRESSURE, z); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci input_sync(sdata->input); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void s6sy761_report_release(struct s6sy761_data *sdata, 1648c2ecf20Sopenharmony_ci u8 *event, u8 tid) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci input_mt_slot(sdata->input, tid); 1678c2ecf20Sopenharmony_ci input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, false); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci input_sync(sdata->input); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void s6sy761_handle_coordinates(struct s6sy761_data *sdata, u8 *event) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u8 tid; 1758c2ecf20Sopenharmony_ci u8 touch_state; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (unlikely(!(event[0] & S6SY761_MASK_TID))) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci tid = ((event[0] & S6SY761_MASK_TID) >> 2) - 1; 1818c2ecf20Sopenharmony_ci touch_state = (event[0] & S6SY761_MASK_TOUCH_STATE) >> 6; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci switch (touch_state) { 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci case S6SY761_TS_NONE: 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case S6SY761_TS_RELEASE: 1888c2ecf20Sopenharmony_ci s6sy761_report_release(sdata, event, tid); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case S6SY761_TS_PRESS: 1918c2ecf20Sopenharmony_ci case S6SY761_TS_MOVE: 1928c2ecf20Sopenharmony_ci s6sy761_report_coordinates(sdata, event, tid); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void s6sy761_handle_events(struct s6sy761_data *sdata, u8 n_events) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int i; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (i = 0; i < n_events; i++) { 2028c2ecf20Sopenharmony_ci u8 *event = &sdata->data[i * S6SY761_EVENT_SIZE]; 2038c2ecf20Sopenharmony_ci u8 event_id = event[0] & S6SY761_MASK_EID; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!event[0]) 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci switch (event_id) { 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci case S6SY761_EVENT_ID_COORDINATE: 2118c2ecf20Sopenharmony_ci s6sy761_handle_coordinates(sdata, event); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci case S6SY761_EVENT_ID_STATUS: 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci default: 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic irqreturn_t s6sy761_irq_handler(int irq, void *dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev; 2268c2ecf20Sopenharmony_ci int ret; 2278c2ecf20Sopenharmony_ci u8 n_events; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(sdata->client, 2308c2ecf20Sopenharmony_ci S6SY761_READ_ONE_EVENT, 2318c2ecf20Sopenharmony_ci S6SY761_EVENT_SIZE, 2328c2ecf20Sopenharmony_ci sdata->data); 2338c2ecf20Sopenharmony_ci if (ret < 0) { 2348c2ecf20Sopenharmony_ci dev_err(&sdata->client->dev, "failed to read events\n"); 2358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!sdata->data[0]) 2398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci n_events = sdata->data[7] & S6SY761_MASK_LEFT_EVENTS; 2428c2ecf20Sopenharmony_ci if (unlikely(n_events > S6SY761_EVENT_COUNT - 1)) 2438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (n_events) { 2468c2ecf20Sopenharmony_ci ret = s6sy761_read_events(sdata, n_events); 2478c2ecf20Sopenharmony_ci if (ret < 0) { 2488c2ecf20Sopenharmony_ci dev_err(&sdata->client->dev, "failed to read events\n"); 2498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci s6sy761_handle_events(sdata, n_events + 1); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int s6sy761_input_open(struct input_dev *dev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = input_get_drvdata(dev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_ON); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void s6sy761_input_close(struct input_dev *dev) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = input_get_drvdata(dev); 2688c2ecf20Sopenharmony_ci int ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_OFF); 2718c2ecf20Sopenharmony_ci if (ret) 2728c2ecf20Sopenharmony_ci dev_err(&sdata->client->dev, "failed to turn off sensing\n"); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic ssize_t s6sy761_sysfs_devid(struct device *dev, 2768c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev_get_drvdata(dev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return sprintf(buf, "%#x\n", sdata->devid); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic DEVICE_ATTR(devid, 0444, s6sy761_sysfs_devid, NULL); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic struct attribute *s6sy761_sysfs_attrs[] = { 2868c2ecf20Sopenharmony_ci &dev_attr_devid.attr, 2878c2ecf20Sopenharmony_ci NULL 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic struct attribute_group s6sy761_attribute_group = { 2918c2ecf20Sopenharmony_ci .attrs = s6sy761_sysfs_attrs 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int s6sy761_power_on(struct s6sy761_data *sdata) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci u8 buffer[S6SY761_EVENT_SIZE]; 2978c2ecf20Sopenharmony_ci u8 event; 2988c2ecf20Sopenharmony_ci int ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators), 3018c2ecf20Sopenharmony_ci sdata->regulators); 3028c2ecf20Sopenharmony_ci if (ret) 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci msleep(140); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* double check whether the touch is functional */ 3088c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(sdata->client, 3098c2ecf20Sopenharmony_ci S6SY761_READ_ONE_EVENT, 3108c2ecf20Sopenharmony_ci S6SY761_EVENT_SIZE, 3118c2ecf20Sopenharmony_ci buffer); 3128c2ecf20Sopenharmony_ci if (ret < 0) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci event = (buffer[0] >> 2) & 0xf; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if ((event != S6SY761_EVENT_INFO && 3188c2ecf20Sopenharmony_ci event != S6SY761_EVENT_VENDOR_INFO) || 3198c2ecf20Sopenharmony_ci buffer[1] != S6SY761_INFO_BOOT_COMPLETE) { 3208c2ecf20Sopenharmony_ci return -ENODEV; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(sdata->client, S6SY761_BOOT_STATUS); 3248c2ecf20Sopenharmony_ci if (ret < 0) 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* for some reasons the device might be stuck in the bootloader */ 3288c2ecf20Sopenharmony_ci if (ret != S6SY761_BS_APPLICATION) 3298c2ecf20Sopenharmony_ci return -ENODEV; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* enable touch functionality */ 3328c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_data(sdata->client, 3338c2ecf20Sopenharmony_ci S6SY761_TOUCH_FUNCTION, 3348c2ecf20Sopenharmony_ci S6SY761_MASK_TOUCH); 3358c2ecf20Sopenharmony_ci if (ret) 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int s6sy761_hw_init(struct s6sy761_data *sdata, 3428c2ecf20Sopenharmony_ci unsigned int *max_x, unsigned int *max_y) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci u8 buffer[S6SY761_PANEL_ID_SIZE]; /* larger read size */ 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = s6sy761_power_on(sdata); 3488c2ecf20Sopenharmony_ci if (ret) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(sdata->client, 3528c2ecf20Sopenharmony_ci S6SY761_DEVICE_ID, 3538c2ecf20Sopenharmony_ci S6SY761_DEVID_SIZE, 3548c2ecf20Sopenharmony_ci buffer); 3558c2ecf20Sopenharmony_ci if (ret < 0) 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci sdata->devid = get_unaligned_be16(buffer + 1); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(sdata->client, 3618c2ecf20Sopenharmony_ci S6SY761_PANEL_INFO, 3628c2ecf20Sopenharmony_ci S6SY761_PANEL_ID_SIZE, 3638c2ecf20Sopenharmony_ci buffer); 3648c2ecf20Sopenharmony_ci if (ret < 0) 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci *max_x = get_unaligned_be16(buffer); 3688c2ecf20Sopenharmony_ci *max_y = get_unaligned_be16(buffer + 2); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* if no tx channels defined, at least keep one */ 3718c2ecf20Sopenharmony_ci sdata->tx_channel = max_t(u8, buffer[8], 1); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(sdata->client, 3748c2ecf20Sopenharmony_ci S6SY761_FIRMWARE_INTEGRITY); 3758c2ecf20Sopenharmony_ci if (ret < 0) 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci else if (ret != S6SY761_FW_OK) 3788c2ecf20Sopenharmony_ci return -ENODEV; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void s6sy761_power_off(void *data) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = data; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci disable_irq(sdata->client->irq); 3888c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(sdata->regulators), 3898c2ecf20Sopenharmony_ci sdata->regulators); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int s6sy761_probe(struct i2c_client *client, 3938c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct s6sy761_data *sdata; 3968c2ecf20Sopenharmony_ci unsigned int max_x, max_y; 3978c2ecf20Sopenharmony_ci int err; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | 4008c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | 4018c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK)) 4028c2ecf20Sopenharmony_ci return -ENODEV; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL); 4058c2ecf20Sopenharmony_ci if (!sdata) 4068c2ecf20Sopenharmony_ci return -ENOMEM; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci i2c_set_clientdata(client, sdata); 4098c2ecf20Sopenharmony_ci sdata->client = client; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci sdata->regulators[S6SY761_REGULATOR_VDD].supply = "vdd"; 4128c2ecf20Sopenharmony_ci sdata->regulators[S6SY761_REGULATOR_AVDD].supply = "avdd"; 4138c2ecf20Sopenharmony_ci err = devm_regulator_bulk_get(&client->dev, 4148c2ecf20Sopenharmony_ci ARRAY_SIZE(sdata->regulators), 4158c2ecf20Sopenharmony_ci sdata->regulators); 4168c2ecf20Sopenharmony_ci if (err) 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci err = devm_add_action_or_reset(&client->dev, s6sy761_power_off, sdata); 4208c2ecf20Sopenharmony_ci if (err) 4218c2ecf20Sopenharmony_ci return err; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci err = s6sy761_hw_init(sdata, &max_x, &max_y); 4248c2ecf20Sopenharmony_ci if (err) 4258c2ecf20Sopenharmony_ci return err; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci sdata->input = devm_input_allocate_device(&client->dev); 4288c2ecf20Sopenharmony_ci if (!sdata->input) 4298c2ecf20Sopenharmony_ci return -ENOMEM; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci sdata->input->name = S6SY761_DEV_NAME; 4328c2ecf20Sopenharmony_ci sdata->input->id.bustype = BUS_I2C; 4338c2ecf20Sopenharmony_ci sdata->input->open = s6sy761_input_open; 4348c2ecf20Sopenharmony_ci sdata->input->close = s6sy761_input_close; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_POSITION_X, 0, max_x, 0, 0); 4378c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); 4388c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 4398c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); 4408c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 4418c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); 4428c2ecf20Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci touchscreen_parse_properties(sdata->input, true, &sdata->prop); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (!input_abs_get_max(sdata->input, ABS_X) || 4478c2ecf20Sopenharmony_ci !input_abs_get_max(sdata->input, ABS_Y)) { 4488c2ecf20Sopenharmony_ci dev_warn(&client->dev, "the axis have not been set\n"); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci err = input_mt_init_slots(sdata->input, sdata->tx_channel, 4528c2ecf20Sopenharmony_ci INPUT_MT_DIRECT); 4538c2ecf20Sopenharmony_ci if (err) 4548c2ecf20Sopenharmony_ci return err; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci input_set_drvdata(sdata->input, sdata); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = input_register_device(sdata->input); 4598c2ecf20Sopenharmony_ci if (err) 4608c2ecf20Sopenharmony_ci return err; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(&client->dev, client->irq, NULL, 4638c2ecf20Sopenharmony_ci s6sy761_irq_handler, 4648c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 4658c2ecf20Sopenharmony_ci "s6sy761_irq", sdata); 4668c2ecf20Sopenharmony_ci if (err) 4678c2ecf20Sopenharmony_ci return err; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci err = devm_device_add_group(&client->dev, &s6sy761_attribute_group); 4708c2ecf20Sopenharmony_ci if (err) 4718c2ecf20Sopenharmony_ci return err; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic int s6sy761_remove(struct i2c_client *client) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int __maybe_unused s6sy761_runtime_suspend(struct device *dev) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev_get_drvdata(dev); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(sdata->client, 4908c2ecf20Sopenharmony_ci S6SY761_APPLICATION_MODE, S6SY761_APP_SLEEP); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int __maybe_unused s6sy761_runtime_resume(struct device *dev) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev_get_drvdata(dev); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(sdata->client, 4988c2ecf20Sopenharmony_ci S6SY761_APPLICATION_MODE, S6SY761_APP_NORMAL); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int __maybe_unused s6sy761_suspend(struct device *dev) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev_get_drvdata(dev); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci s6sy761_power_off(sdata); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int __maybe_unused s6sy761_resume(struct device *dev) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct s6sy761_data *sdata = dev_get_drvdata(dev); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci enable_irq(sdata->client->irq); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return s6sy761_power_on(sdata); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct dev_pm_ops s6sy761_pm_ops = { 5208c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(s6sy761_suspend, s6sy761_resume) 5218c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(s6sy761_runtime_suspend, 5228c2ecf20Sopenharmony_ci s6sy761_runtime_resume, NULL) 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5268c2ecf20Sopenharmony_cistatic const struct of_device_id s6sy761_of_match[] = { 5278c2ecf20Sopenharmony_ci { .compatible = "samsung,s6sy761", }, 5288c2ecf20Sopenharmony_ci { }, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s6sy761_of_match); 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic const struct i2c_device_id s6sy761_id[] = { 5348c2ecf20Sopenharmony_ci { "s6sy761", 0 }, 5358c2ecf20Sopenharmony_ci { }, 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s6sy761_id); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic struct i2c_driver s6sy761_driver = { 5408c2ecf20Sopenharmony_ci .driver = { 5418c2ecf20Sopenharmony_ci .name = S6SY761_DEV_NAME, 5428c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(s6sy761_of_match), 5438c2ecf20Sopenharmony_ci .pm = &s6sy761_pm_ops, 5448c2ecf20Sopenharmony_ci }, 5458c2ecf20Sopenharmony_ci .probe = s6sy761_probe, 5468c2ecf20Sopenharmony_ci .remove = s6sy761_remove, 5478c2ecf20Sopenharmony_ci .id_table = s6sy761_id, 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cimodule_i2c_driver(s6sy761_driver); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>"); 5538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S6SY761 Touch Screen"); 5548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 555