162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// STMicroelectronics FTS Touchscreen device driver 362306a36Sopenharmony_ci// 462306a36Sopenharmony_ci// Copyright (c) 2017 Samsung Electronics Co., Ltd. 562306a36Sopenharmony_ci// Copyright (c) 2017 Andi Shyti <andi@etezian.org> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/i2c.h> 962306a36Sopenharmony_ci#include <linux/input/mt.h> 1062306a36Sopenharmony_ci#include <linux/input/touchscreen.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/leds.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* I2C commands */ 1962306a36Sopenharmony_ci#define STMFTS_READ_INFO 0x80 2062306a36Sopenharmony_ci#define STMFTS_READ_STATUS 0x84 2162306a36Sopenharmony_ci#define STMFTS_READ_ONE_EVENT 0x85 2262306a36Sopenharmony_ci#define STMFTS_READ_ALL_EVENT 0x86 2362306a36Sopenharmony_ci#define STMFTS_LATEST_EVENT 0x87 2462306a36Sopenharmony_ci#define STMFTS_SLEEP_IN 0x90 2562306a36Sopenharmony_ci#define STMFTS_SLEEP_OUT 0x91 2662306a36Sopenharmony_ci#define STMFTS_MS_MT_SENSE_OFF 0x92 2762306a36Sopenharmony_ci#define STMFTS_MS_MT_SENSE_ON 0x93 2862306a36Sopenharmony_ci#define STMFTS_SS_HOVER_SENSE_OFF 0x94 2962306a36Sopenharmony_ci#define STMFTS_SS_HOVER_SENSE_ON 0x95 3062306a36Sopenharmony_ci#define STMFTS_MS_KEY_SENSE_OFF 0x9a 3162306a36Sopenharmony_ci#define STMFTS_MS_KEY_SENSE_ON 0x9b 3262306a36Sopenharmony_ci#define STMFTS_SYSTEM_RESET 0xa0 3362306a36Sopenharmony_ci#define STMFTS_CLEAR_EVENT_STACK 0xa1 3462306a36Sopenharmony_ci#define STMFTS_FULL_FORCE_CALIBRATION 0xa2 3562306a36Sopenharmony_ci#define STMFTS_MS_CX_TUNING 0xa3 3662306a36Sopenharmony_ci#define STMFTS_SS_CX_TUNING 0xa4 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* events */ 3962306a36Sopenharmony_ci#define STMFTS_EV_NO_EVENT 0x00 4062306a36Sopenharmony_ci#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02 4162306a36Sopenharmony_ci#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03 4262306a36Sopenharmony_ci#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04 4362306a36Sopenharmony_ci#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05 4462306a36Sopenharmony_ci#define STMFTS_EV_HOVER_ENTER 0x07 4562306a36Sopenharmony_ci#define STMFTS_EV_HOVER_LEAVE 0x08 4662306a36Sopenharmony_ci#define STMFTS_EV_HOVER_MOTION 0x09 4762306a36Sopenharmony_ci#define STMFTS_EV_KEY_STATUS 0x0e 4862306a36Sopenharmony_ci#define STMFTS_EV_ERROR 0x0f 4962306a36Sopenharmony_ci#define STMFTS_EV_CONTROLLER_READY 0x10 5062306a36Sopenharmony_ci#define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 5162306a36Sopenharmony_ci#define STMFTS_EV_STATUS 0x16 5262306a36Sopenharmony_ci#define STMFTS_EV_DEBUG 0xdb 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* multi touch related event masks */ 5562306a36Sopenharmony_ci#define STMFTS_MASK_EVENT_ID 0x0f 5662306a36Sopenharmony_ci#define STMFTS_MASK_TOUCH_ID 0xf0 5762306a36Sopenharmony_ci#define STMFTS_MASK_LEFT_EVENT 0x0f 5862306a36Sopenharmony_ci#define STMFTS_MASK_X_MSB 0x0f 5962306a36Sopenharmony_ci#define STMFTS_MASK_Y_LSB 0xf0 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* key related event masks */ 6262306a36Sopenharmony_ci#define STMFTS_MASK_KEY_NO_TOUCH 0x00 6362306a36Sopenharmony_ci#define STMFTS_MASK_KEY_MENU 0x01 6462306a36Sopenharmony_ci#define STMFTS_MASK_KEY_BACK 0x02 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define STMFTS_EVENT_SIZE 8 6762306a36Sopenharmony_ci#define STMFTS_STACK_DEPTH 32 6862306a36Sopenharmony_ci#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH) 6962306a36Sopenharmony_ci#define STMFTS_MAX_FINGERS 10 7062306a36Sopenharmony_ci#define STMFTS_DEV_NAME "stmfts" 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cienum stmfts_regulators { 7362306a36Sopenharmony_ci STMFTS_REGULATOR_VDD, 7462306a36Sopenharmony_ci STMFTS_REGULATOR_AVDD, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct stmfts_data { 7862306a36Sopenharmony_ci struct i2c_client *client; 7962306a36Sopenharmony_ci struct input_dev *input; 8062306a36Sopenharmony_ci struct led_classdev led_cdev; 8162306a36Sopenharmony_ci struct mutex mutex; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci struct touchscreen_properties prop; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci struct regulator_bulk_data regulators[2]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * Presence of ledvdd will be used also to check 8962306a36Sopenharmony_ci * whether the LED is supported. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci struct regulator *ledvdd; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci u16 chip_id; 9462306a36Sopenharmony_ci u8 chip_ver; 9562306a36Sopenharmony_ci u16 fw_ver; 9662306a36Sopenharmony_ci u8 config_id; 9762306a36Sopenharmony_ci u8 config_ver; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci u8 data[STMFTS_DATA_MAX_SIZE]; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci struct completion cmd_done; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci bool use_key; 10462306a36Sopenharmony_ci bool led_status; 10562306a36Sopenharmony_ci bool hover_enabled; 10662306a36Sopenharmony_ci bool running; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int stmfts_brightness_set(struct led_classdev *led_cdev, 11062306a36Sopenharmony_ci enum led_brightness value) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct stmfts_data *sdata = container_of(led_cdev, 11362306a36Sopenharmony_ci struct stmfts_data, led_cdev); 11462306a36Sopenharmony_ci int err; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (value != sdata->led_status && sdata->ledvdd) { 11762306a36Sopenharmony_ci if (!value) { 11862306a36Sopenharmony_ci regulator_disable(sdata->ledvdd); 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci err = regulator_enable(sdata->ledvdd); 12162306a36Sopenharmony_ci if (err) { 12262306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 12362306a36Sopenharmony_ci "failed to disable ledvdd regulator: %d\n", 12462306a36Sopenharmony_ci err); 12562306a36Sopenharmony_ci return err; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci sdata->led_status = value; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct stmfts_data *sdata = container_of(led_cdev, 13762306a36Sopenharmony_ci struct stmfts_data, led_cdev); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return !!regulator_is_enabled(sdata->ledvdd); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * We can't simply use i2c_smbus_read_i2c_block_data because we 14462306a36Sopenharmony_ci * need to read more than 255 bytes ( 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic int stmfts_read_events(struct stmfts_data *sdata) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci u8 cmd = STMFTS_READ_ALL_EVENT; 14962306a36Sopenharmony_ci struct i2c_msg msgs[2] = { 15062306a36Sopenharmony_ci { 15162306a36Sopenharmony_ci .addr = sdata->client->addr, 15262306a36Sopenharmony_ci .len = 1, 15362306a36Sopenharmony_ci .buf = &cmd, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci { 15662306a36Sopenharmony_ci .addr = sdata->client->addr, 15762306a36Sopenharmony_ci .flags = I2C_M_RD, 15862306a36Sopenharmony_ci .len = STMFTS_DATA_MAX_SIZE, 15962306a36Sopenharmony_ci .buf = sdata->data, 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci }; 16262306a36Sopenharmony_ci int ret; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); 16562306a36Sopenharmony_ci if (ret < 0) 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void stmfts_report_contact_event(struct stmfts_data *sdata, 17262306a36Sopenharmony_ci const u8 event[]) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; 17562306a36Sopenharmony_ci u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); 17662306a36Sopenharmony_ci u16 y = (event[2] >> 4) | (event[3] << 4); 17762306a36Sopenharmony_ci u8 maj = event[4]; 17862306a36Sopenharmony_ci u8 min = event[5]; 17962306a36Sopenharmony_ci u8 orientation = event[6]; 18062306a36Sopenharmony_ci u8 area = event[7]; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci input_mt_slot(sdata->input, slot_id); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); 18562306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_POSITION_X, x); 18662306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); 18762306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj); 18862306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min); 18962306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_PRESSURE, area); 19062306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_MT_ORIENTATION, orientation); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci input_sync(sdata->input); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void stmfts_report_contact_release(struct stmfts_data *sdata, 19662306a36Sopenharmony_ci const u8 event[]) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci input_mt_slot(sdata->input, slot_id); 20162306a36Sopenharmony_ci input_mt_report_slot_inactive(sdata->input); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci input_sync(sdata->input); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void stmfts_report_hover_event(struct stmfts_data *sdata, 20762306a36Sopenharmony_ci const u8 event[]) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u16 x = (event[2] << 4) | (event[4] >> 4); 21062306a36Sopenharmony_ci u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); 21162306a36Sopenharmony_ci u8 z = event[5]; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_X, x); 21462306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_Y, y); 21562306a36Sopenharmony_ci input_report_abs(sdata->input, ABS_DISTANCE, z); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci input_sync(sdata->input); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[]) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci switch (event[2]) { 22362306a36Sopenharmony_ci case 0: 22462306a36Sopenharmony_ci input_report_key(sdata->input, KEY_BACK, 0); 22562306a36Sopenharmony_ci input_report_key(sdata->input, KEY_MENU, 0); 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci case STMFTS_MASK_KEY_BACK: 22962306a36Sopenharmony_ci input_report_key(sdata->input, KEY_BACK, 1); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci case STMFTS_MASK_KEY_MENU: 23362306a36Sopenharmony_ci input_report_key(sdata->input, KEY_MENU, 1); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci default: 23762306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 23862306a36Sopenharmony_ci "unknown key event: %#02x\n", event[2]); 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci input_sync(sdata->input); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void stmfts_parse_events(struct stmfts_data *sdata) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci int i; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci for (i = 0; i < STMFTS_STACK_DEPTH; i++) { 25062306a36Sopenharmony_ci u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci switch (event[0]) { 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci case STMFTS_EV_CONTROLLER_READY: 25562306a36Sopenharmony_ci case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: 25662306a36Sopenharmony_ci case STMFTS_EV_STATUS: 25762306a36Sopenharmony_ci complete(&sdata->cmd_done); 25862306a36Sopenharmony_ci fallthrough; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci case STMFTS_EV_NO_EVENT: 26162306a36Sopenharmony_ci case STMFTS_EV_DEBUG: 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci switch (event[0] & STMFTS_MASK_EVENT_ID) { 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci case STMFTS_EV_MULTI_TOUCH_ENTER: 26862306a36Sopenharmony_ci case STMFTS_EV_MULTI_TOUCH_MOTION: 26962306a36Sopenharmony_ci stmfts_report_contact_event(sdata, event); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci case STMFTS_EV_MULTI_TOUCH_LEAVE: 27362306a36Sopenharmony_ci stmfts_report_contact_release(sdata, event); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci case STMFTS_EV_HOVER_ENTER: 27762306a36Sopenharmony_ci case STMFTS_EV_HOVER_LEAVE: 27862306a36Sopenharmony_ci case STMFTS_EV_HOVER_MOTION: 27962306a36Sopenharmony_ci stmfts_report_hover_event(sdata, event); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci case STMFTS_EV_KEY_STATUS: 28362306a36Sopenharmony_ci stmfts_report_key_event(sdata, event); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci case STMFTS_EV_ERROR: 28762306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 28862306a36Sopenharmony_ci "error code: 0x%x%x%x%x%x%x", 28962306a36Sopenharmony_ci event[6], event[5], event[4], 29062306a36Sopenharmony_ci event[3], event[2], event[1]); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci default: 29462306a36Sopenharmony_ci dev_err(&sdata->client->dev, 29562306a36Sopenharmony_ci "unknown event %#02x\n", event[0]); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic irqreturn_t stmfts_irq_handler(int irq, void *dev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct stmfts_data *sdata = dev; 30362306a36Sopenharmony_ci int err; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci mutex_lock(&sdata->mutex); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci err = stmfts_read_events(sdata); 30862306a36Sopenharmony_ci if (unlikely(err)) 30962306a36Sopenharmony_ci dev_err(&sdata->client->dev, 31062306a36Sopenharmony_ci "failed to read events: %d\n", err); 31162306a36Sopenharmony_ci else 31262306a36Sopenharmony_ci stmfts_parse_events(sdata); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mutex_unlock(&sdata->mutex); 31562306a36Sopenharmony_ci return IRQ_HANDLED; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int stmfts_command(struct stmfts_data *sdata, const u8 cmd) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int err; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci reinit_completion(&sdata->cmd_done); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, cmd); 32562306a36Sopenharmony_ci if (err) 32662306a36Sopenharmony_ci return err; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&sdata->cmd_done, 32962306a36Sopenharmony_ci msecs_to_jiffies(1000))) 33062306a36Sopenharmony_ci return -ETIMEDOUT; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int stmfts_input_open(struct input_dev *dev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct stmfts_data *sdata = input_get_drvdata(dev); 33862306a36Sopenharmony_ci int err; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci err = pm_runtime_resume_and_get(&sdata->client->dev); 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci return err; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON); 34562306a36Sopenharmony_ci if (err) { 34662306a36Sopenharmony_ci pm_runtime_put_sync(&sdata->client->dev); 34762306a36Sopenharmony_ci return err; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci mutex_lock(&sdata->mutex); 35162306a36Sopenharmony_ci sdata->running = true; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (sdata->hover_enabled) { 35462306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, 35562306a36Sopenharmony_ci STMFTS_SS_HOVER_SENSE_ON); 35662306a36Sopenharmony_ci if (err) 35762306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 35862306a36Sopenharmony_ci "failed to enable hover\n"); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci mutex_unlock(&sdata->mutex); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (sdata->use_key) { 36362306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, 36462306a36Sopenharmony_ci STMFTS_MS_KEY_SENSE_ON); 36562306a36Sopenharmony_ci if (err) 36662306a36Sopenharmony_ci /* I can still use only the touch screen */ 36762306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 36862306a36Sopenharmony_ci "failed to enable touchkey\n"); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void stmfts_input_close(struct input_dev *dev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct stmfts_data *sdata = input_get_drvdata(dev); 37762306a36Sopenharmony_ci int err; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); 38062306a36Sopenharmony_ci if (err) 38162306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 38262306a36Sopenharmony_ci "failed to disable touchscreen: %d\n", err); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci mutex_lock(&sdata->mutex); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci sdata->running = false; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (sdata->hover_enabled) { 38962306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, 39062306a36Sopenharmony_ci STMFTS_SS_HOVER_SENSE_OFF); 39162306a36Sopenharmony_ci if (err) 39262306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 39362306a36Sopenharmony_ci "failed to disable hover: %d\n", err); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci mutex_unlock(&sdata->mutex); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (sdata->use_key) { 39862306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, 39962306a36Sopenharmony_ci STMFTS_MS_KEY_SENSE_OFF); 40062306a36Sopenharmony_ci if (err) 40162306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 40262306a36Sopenharmony_ci "failed to disable touchkey: %d\n", err); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci pm_runtime_put_sync(&sdata->client->dev); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_chip_id(struct device *dev, 40962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return sprintf(buf, "%#x\n", sdata->chip_id); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_chip_version(struct device *dev, 41762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return sprintf(buf, "%u\n", sdata->chip_ver); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_fw_ver(struct device *dev, 42562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return sprintf(buf, "%u\n", sdata->fw_ver); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_config_id(struct device *dev, 43362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return sprintf(buf, "%#x\n", sdata->config_id); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_config_version(struct device *dev, 44162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return sprintf(buf, "%u\n", sdata->config_ver); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_read_status(struct device *dev, 44962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 45262306a36Sopenharmony_ci u8 status[4]; 45362306a36Sopenharmony_ci int err; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_STATUS, 45662306a36Sopenharmony_ci sizeof(status), status); 45762306a36Sopenharmony_ci if (err) 45862306a36Sopenharmony_ci return err; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return sprintf(buf, "%#02x\n", status[0]); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, 46462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return sprintf(buf, "%u\n", sdata->hover_enabled); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, 47262306a36Sopenharmony_ci struct device_attribute *attr, 47362306a36Sopenharmony_ci const char *buf, size_t len) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 47662306a36Sopenharmony_ci unsigned long value; 47762306a36Sopenharmony_ci int err = 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (kstrtoul(buf, 0, &value)) 48062306a36Sopenharmony_ci return -EINVAL; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_lock(&sdata->mutex); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (value && sdata->hover_enabled) 48562306a36Sopenharmony_ci goto out; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (sdata->running) 48862306a36Sopenharmony_ci err = i2c_smbus_write_byte(sdata->client, 48962306a36Sopenharmony_ci value ? STMFTS_SS_HOVER_SENSE_ON : 49062306a36Sopenharmony_ci STMFTS_SS_HOVER_SENSE_OFF); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!err) 49362306a36Sopenharmony_ci sdata->hover_enabled = !!value; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciout: 49662306a36Sopenharmony_ci mutex_unlock(&sdata->mutex); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return len; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic DEVICE_ATTR(chip_id, 0444, stmfts_sysfs_chip_id, NULL); 50262306a36Sopenharmony_cistatic DEVICE_ATTR(chip_version, 0444, stmfts_sysfs_chip_version, NULL); 50362306a36Sopenharmony_cistatic DEVICE_ATTR(fw_ver, 0444, stmfts_sysfs_fw_ver, NULL); 50462306a36Sopenharmony_cistatic DEVICE_ATTR(config_id, 0444, stmfts_sysfs_config_id, NULL); 50562306a36Sopenharmony_cistatic DEVICE_ATTR(config_version, 0444, stmfts_sysfs_config_version, NULL); 50662306a36Sopenharmony_cistatic DEVICE_ATTR(status, 0444, stmfts_sysfs_read_status, NULL); 50762306a36Sopenharmony_cistatic DEVICE_ATTR(hover_enable, 0644, stmfts_sysfs_hover_enable_read, 50862306a36Sopenharmony_ci stmfts_sysfs_hover_enable_write); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct attribute *stmfts_sysfs_attrs[] = { 51162306a36Sopenharmony_ci &dev_attr_chip_id.attr, 51262306a36Sopenharmony_ci &dev_attr_chip_version.attr, 51362306a36Sopenharmony_ci &dev_attr_fw_ver.attr, 51462306a36Sopenharmony_ci &dev_attr_config_id.attr, 51562306a36Sopenharmony_ci &dev_attr_config_version.attr, 51662306a36Sopenharmony_ci &dev_attr_status.attr, 51762306a36Sopenharmony_ci &dev_attr_hover_enable.attr, 51862306a36Sopenharmony_ci NULL 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic struct attribute_group stmfts_attribute_group = { 52262306a36Sopenharmony_ci .attrs = stmfts_sysfs_attrs 52362306a36Sopenharmony_ci}; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int stmfts_power_on(struct stmfts_data *sdata) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci int err; 52862306a36Sopenharmony_ci u8 reg[8]; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators), 53162306a36Sopenharmony_ci sdata->regulators); 53262306a36Sopenharmony_ci if (err) 53362306a36Sopenharmony_ci return err; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * The datasheet does not specify the power on time, but considering 53762306a36Sopenharmony_ci * that the reset time is < 10ms, I sleep 20ms to be sure 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci msleep(20); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_INFO, 54262306a36Sopenharmony_ci sizeof(reg), reg); 54362306a36Sopenharmony_ci if (err < 0) 54462306a36Sopenharmony_ci return err; 54562306a36Sopenharmony_ci if (err != sizeof(reg)) 54662306a36Sopenharmony_ci return -EIO; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci sdata->chip_id = be16_to_cpup((__be16 *)®[6]); 54962306a36Sopenharmony_ci sdata->chip_ver = reg[0]; 55062306a36Sopenharmony_ci sdata->fw_ver = be16_to_cpup((__be16 *)®[2]); 55162306a36Sopenharmony_ci sdata->config_id = reg[4]; 55262306a36Sopenharmony_ci sdata->config_ver = reg[5]; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci enable_irq(sdata->client->irq); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci msleep(50); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); 55962306a36Sopenharmony_ci if (err) 56062306a36Sopenharmony_ci return err; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci err = stmfts_command(sdata, STMFTS_SLEEP_OUT); 56362306a36Sopenharmony_ci if (err) 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* optional tuning */ 56762306a36Sopenharmony_ci err = stmfts_command(sdata, STMFTS_MS_CX_TUNING); 56862306a36Sopenharmony_ci if (err) 56962306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 57062306a36Sopenharmony_ci "failed to perform mutual auto tune: %d\n", err); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* optional tuning */ 57362306a36Sopenharmony_ci err = stmfts_command(sdata, STMFTS_SS_CX_TUNING); 57462306a36Sopenharmony_ci if (err) 57562306a36Sopenharmony_ci dev_warn(&sdata->client->dev, 57662306a36Sopenharmony_ci "failed to perform self auto tune: %d\n", err); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION); 57962306a36Sopenharmony_ci if (err) 58062306a36Sopenharmony_ci return err; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* 58362306a36Sopenharmony_ci * At this point no one is using the touchscreen 58462306a36Sopenharmony_ci * and I don't really care about the return value 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci (void) i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void stmfts_power_off(void *data) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct stmfts_data *sdata = data; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci disable_irq(sdata->client->irq); 59662306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(sdata->regulators), 59762306a36Sopenharmony_ci sdata->regulators); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* This function is void because I don't want to prevent using the touch key 60162306a36Sopenharmony_ci * only because the LEDs don't get registered 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_cistatic int stmfts_enable_led(struct stmfts_data *sdata) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci int err; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* get the regulator for powering the leds on */ 60862306a36Sopenharmony_ci sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd"); 60962306a36Sopenharmony_ci if (IS_ERR(sdata->ledvdd)) 61062306a36Sopenharmony_ci return PTR_ERR(sdata->ledvdd); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci sdata->led_cdev.name = STMFTS_DEV_NAME; 61362306a36Sopenharmony_ci sdata->led_cdev.max_brightness = LED_ON; 61462306a36Sopenharmony_ci sdata->led_cdev.brightness = LED_OFF; 61562306a36Sopenharmony_ci sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set; 61662306a36Sopenharmony_ci sdata->led_cdev.brightness_get = stmfts_brightness_get; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev); 61962306a36Sopenharmony_ci if (err) { 62062306a36Sopenharmony_ci devm_regulator_put(sdata->ledvdd); 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int stmfts_probe(struct i2c_client *client) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int err; 63062306a36Sopenharmony_ci struct stmfts_data *sdata; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | 63362306a36Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | 63462306a36Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK)) 63562306a36Sopenharmony_ci return -ENODEV; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL); 63862306a36Sopenharmony_ci if (!sdata) 63962306a36Sopenharmony_ci return -ENOMEM; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci i2c_set_clientdata(client, sdata); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci sdata->client = client; 64462306a36Sopenharmony_ci mutex_init(&sdata->mutex); 64562306a36Sopenharmony_ci init_completion(&sdata->cmd_done); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd"; 64862306a36Sopenharmony_ci sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd"; 64962306a36Sopenharmony_ci err = devm_regulator_bulk_get(&client->dev, 65062306a36Sopenharmony_ci ARRAY_SIZE(sdata->regulators), 65162306a36Sopenharmony_ci sdata->regulators); 65262306a36Sopenharmony_ci if (err) 65362306a36Sopenharmony_ci return err; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci sdata->input = devm_input_allocate_device(&client->dev); 65662306a36Sopenharmony_ci if (!sdata->input) 65762306a36Sopenharmony_ci return -ENOMEM; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci sdata->input->name = STMFTS_DEV_NAME; 66062306a36Sopenharmony_ci sdata->input->id.bustype = BUS_I2C; 66162306a36Sopenharmony_ci sdata->input->open = stmfts_input_open; 66262306a36Sopenharmony_ci sdata->input->close = stmfts_input_close; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_X); 66562306a36Sopenharmony_ci input_set_capability(sdata->input, EV_ABS, ABS_MT_POSITION_Y); 66662306a36Sopenharmony_ci touchscreen_parse_properties(sdata->input, true, &sdata->prop); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 66962306a36Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); 67062306a36Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0); 67162306a36Sopenharmony_ci input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0); 67262306a36Sopenharmony_ci input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci sdata->use_key = device_property_read_bool(&client->dev, 67562306a36Sopenharmony_ci "touch-key-connected"); 67662306a36Sopenharmony_ci if (sdata->use_key) { 67762306a36Sopenharmony_ci input_set_capability(sdata->input, EV_KEY, KEY_MENU); 67862306a36Sopenharmony_ci input_set_capability(sdata->input, EV_KEY, KEY_BACK); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci err = input_mt_init_slots(sdata->input, 68262306a36Sopenharmony_ci STMFTS_MAX_FINGERS, INPUT_MT_DIRECT); 68362306a36Sopenharmony_ci if (err) 68462306a36Sopenharmony_ci return err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci input_set_drvdata(sdata->input, sdata); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * stmfts_power_on expects interrupt to be disabled, but 69062306a36Sopenharmony_ci * at this point the device is still off and I do not trust 69162306a36Sopenharmony_ci * the status of the irq line that can generate some spurious 69262306a36Sopenharmony_ci * interrupts. To be on the safe side it's better to not enable 69362306a36Sopenharmony_ci * the interrupts during their request. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci err = devm_request_threaded_irq(&client->dev, client->irq, 69662306a36Sopenharmony_ci NULL, stmfts_irq_handler, 69762306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_NO_AUTOEN, 69862306a36Sopenharmony_ci "stmfts_irq", sdata); 69962306a36Sopenharmony_ci if (err) 70062306a36Sopenharmony_ci return err; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n"); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci err = stmfts_power_on(sdata); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci return err; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata); 70962306a36Sopenharmony_ci if (err) 71062306a36Sopenharmony_ci return err; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci err = input_register_device(sdata->input); 71362306a36Sopenharmony_ci if (err) 71462306a36Sopenharmony_ci return err; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (sdata->use_key) { 71762306a36Sopenharmony_ci err = stmfts_enable_led(sdata); 71862306a36Sopenharmony_ci if (err) { 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * Even if the LEDs have failed to be initialized and 72162306a36Sopenharmony_ci * used in the driver, I can still use the device even 72262306a36Sopenharmony_ci * without LEDs. The ledvdd regulator pointer will be 72362306a36Sopenharmony_ci * used as a flag. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci dev_warn(&client->dev, "unable to use touchkey leds\n"); 72662306a36Sopenharmony_ci sdata->ledvdd = NULL; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci err = devm_device_add_group(&client->dev, &stmfts_attribute_group); 73162306a36Sopenharmony_ci if (err) 73262306a36Sopenharmony_ci return err; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci pm_runtime_enable(&client->dev); 73562306a36Sopenharmony_ci device_enable_async_suspend(&client->dev); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void stmfts_remove(struct i2c_client *client) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci pm_runtime_disable(&client->dev); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int stmfts_runtime_suspend(struct device *dev) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 74862306a36Sopenharmony_ci int ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci dev_warn(dev, "failed to suspend device: %d\n", ret); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int stmfts_runtime_resume(struct device *dev) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 76062306a36Sopenharmony_ci int ret; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT); 76362306a36Sopenharmony_ci if (ret) 76462306a36Sopenharmony_ci dev_err(dev, "failed to resume device: %d\n", ret); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return ret; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int stmfts_suspend(struct device *dev) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci stmfts_power_off(sdata); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic int stmfts_resume(struct device *dev) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct stmfts_data *sdata = dev_get_drvdata(dev); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return stmfts_power_on(sdata); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic const struct dev_pm_ops stmfts_pm_ops = { 78662306a36Sopenharmony_ci SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume) 78762306a36Sopenharmony_ci RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL) 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci#ifdef CONFIG_OF 79162306a36Sopenharmony_cistatic const struct of_device_id stmfts_of_match[] = { 79262306a36Sopenharmony_ci { .compatible = "st,stmfts", }, 79362306a36Sopenharmony_ci { }, 79462306a36Sopenharmony_ci}; 79562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stmfts_of_match); 79662306a36Sopenharmony_ci#endif 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic const struct i2c_device_id stmfts_id[] = { 79962306a36Sopenharmony_ci { "stmfts", 0 }, 80062306a36Sopenharmony_ci { }, 80162306a36Sopenharmony_ci}; 80262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, stmfts_id); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic struct i2c_driver stmfts_driver = { 80562306a36Sopenharmony_ci .driver = { 80662306a36Sopenharmony_ci .name = STMFTS_DEV_NAME, 80762306a36Sopenharmony_ci .of_match_table = of_match_ptr(stmfts_of_match), 80862306a36Sopenharmony_ci .pm = pm_ptr(&stmfts_pm_ops), 80962306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 81062306a36Sopenharmony_ci }, 81162306a36Sopenharmony_ci .probe = stmfts_probe, 81262306a36Sopenharmony_ci .remove = stmfts_remove, 81362306a36Sopenharmony_ci .id_table = stmfts_id, 81462306a36Sopenharmony_ci}; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cimodule_i2c_driver(stmfts_driver); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ciMODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>"); 81962306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen"); 82062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 821