// SPDX-License-Identifier: GPL-2.0// STMicroelectronics FTS Touchscreen device driver//// Copyright (c) 2017 Samsung Electronics Co., Ltd.// Copyright (c) 2017 Andi Shyti <andi@etezian.org> #include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/i2c.h>#include <linux/input/mt.h>#include <linux/input/touchscreen.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/leds.h>#include <linux/module.h>#include <linux/pm_runtime.h>#include <linux/regulator/consumer.h> /* I2C commands */#define STMFTS_READ_INFO 0x80#define STMFTS_READ_STATUS 0x84#define STMFTS_READ_ONE_EVENT 0x85#define STMFTS_READ_ALL_EVENT 0x86#define STMFTS_LATEST_EVENT 0x87#define STMFTS_SLEEP_IN 0x90#define STMFTS_SLEEP_OUT 0x91#define STMFTS_MS_MT_SENSE_OFF 0x92#define STMFTS_MS_MT_SENSE_ON 0x93#define STMFTS_SS_HOVER_SENSE_OFF 0x94#define STMFTS_SS_HOVER_SENSE_ON 0x95#define STMFTS_MS_KEY_SENSE_OFF 0x9a#define STMFTS_MS_KEY_SENSE_ON 0x9b#define STMFTS_SYSTEM_RESET 0xa0#define STMFTS_CLEAR_EVENT_STACK 0xa1#define STMFTS_FULL_FORCE_CALIBRATION 0xa2#define STMFTS_MS_CX_TUNING 0xa3#define STMFTS_SS_CX_TUNING 0xa4 /* events */#define STMFTS_EV_NO_EVENT 0x00#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05#define STMFTS_EV_HOVER_ENTER 0x07#define STMFTS_EV_HOVER_LEAVE 0x08#define STMFTS_EV_HOVER_MOTION 0x09#define STMFTS_EV_KEY_STATUS 0x0e#define STMFTS_EV_ERROR 0x0f#define STMFTS_EV_CONTROLLER_READY 0x10#define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11#define STMFTS_EV_STATUS 0x16#define STMFTS_EV_DEBUG 0xdb /* multi touch related event masks */#define STMFTS_MASK_EVENT_ID 0x0f#define STMFTS_MASK_TOUCH_ID 0xf0#define STMFTS_MASK_LEFT_EVENT 0x0f#define STMFTS_MASK_X_MSB 0x0f#define STMFTS_MASK_Y_LSB 0xf0 /* key related event masks */#define STMFTS_MASK_KEY_NO_TOUCH 0x00#define STMFTS_MASK_KEY_MENU 0x01#define STMFTS_MASK_KEY_BACK 0x02 #define STMFTS_EVENT_SIZE 8#define STMFTS_STACK_DEPTH 32#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH)#define STMFTS_MAX_FINGERS 10#define STMFTS_DEV_NAME "stmfts" static const struct regulator_bulk_data stmfts_supplies[] = { { .supply = "vdd" }, { .supply = "avdd" },}; struct stmfts_data { struct i2c_client *client; struct input_dev *input; struct gpio_desc *reset_gpio; struct led_classdev led_cdev; struct mutex mutex; struct touchscreen_properties prop; struct regulator_bulk_data *supplies; /* * Presence of ledvdd will be used also to check * whether the LED is supported. */ struct regulator *ledvdd; u16 chip_id; u8 chip_ver; u16 fw_ver; u8 config_id; u8 config_ver; u8 data[STMFTS_DATA_MAX_SIZE]; struct completion cmd_done; bool use_key; bool led_status; bool hover_enabled; bool running;}; static int stmfts_brightness_set(struct led_classdev *led_cdev, enum led_brightness value){ struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); int err; if (value != sdata->led_status && sdata->ledvdd) { if (!value) { regulator_disable(sdata->ledvdd); } else { err = regulator_enable(sdata->ledvdd); if (err) { dev_warn(&sdata->client->dev, "failed to enable ledvdd regulator: %d\n", err); return err; } } sdata->led_status = value; } return 0;} static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev){ struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); return !!regulator_is_enabled(sdata->ledvdd);} /* * We can't simply use i2c_smbus_read_i2c_block_data because we * need to read 256 bytes, which exceeds the 255-byte SMBus block limit. */static int stmfts_read_events(struct stmfts_data *sdata){ u8 cmd = STMFTS_READ_ALL_EVENT; struct i2c_msg msgs[2] = { { .addr = sdata->client->addr, .len = 1, .buf = &cmd, }, { .addr = sdata->client->addr, .flags = I2C_M_RD, .len = STMFTS_DATA_MAX_SIZE, .buf = sdata->data, }, }; int ret; ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;} static void stmfts_report_contact_event(struct stmfts_data *sdata, const u8 event[]){ u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); u16 y = (event[2] >> 4) | (event[3] << 4); u8 maj = event[4]; u8 min = event[5]; u8 orientation = event[6]; u8 area = event[7]; input_mt_slot(sdata->input, slot_id); input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); input_report_abs(sdata->input, ABS_MT_POSITION_X, x); input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj); input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min); input_report_abs(sdata->input, ABS_MT_PRESSURE, area); input_report_abs(sdata->input, ABS_MT_ORIENTATION, orientation); input_sync(sdata->input);} static void stmfts_report_contact_release(struct stmfts_data *sdata, const u8 event[]){ u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; input_mt_slot(sdata->input, slot_id); input_mt_report_slot_inactive(sdata->input); input_sync(sdata->input);} static void stmfts_report_hover_event(struct stmfts_data *sdata, const u8 event[]){ u16 x = (event[2] << 4) | (event[4] >> 4); u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); u8 z = event[5]; input_report_abs(sdata->input, ABS_X, x); input_report_abs(sdata->input, ABS_Y, y); input_report_abs(sdata->input, ABS_DISTANCE, z); input_sync(sdata->input);} static void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[]){ switch (event[2]) { case 0: input_report_key(sdata->input, KEY_BACK, 0); input_report_key(sdata->input, KEY_MENU, 0); break; case STMFTS_MASK_KEY_BACK: input_report_key(sdata->input, KEY_BACK, 1); break; case STMFTS_MASK_KEY_MENU: input_report_key(sdata->input, KEY_MENU, 1); break; default: dev_warn(&sdata->client->dev, "unknown key event: %#02x\n", event[2]); break; } input_sync(sdata->input);} static void stmfts_parse_events(struct stmfts_data *sdata){ int i; for (i = 0; i < STMFTS_STACK_DEPTH; i++) { u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; switch (event[0]) { case STMFTS_EV_CONTROLLER_READY: case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: case STMFTS_EV_STATUS: complete(&sdata->cmd_done); fallthrough; case STMFTS_EV_NO_EVENT: case STMFTS_EV_DEBUG: return; } switch (event[0] & STMFTS_MASK_EVENT_ID) { case STMFTS_EV_MULTI_TOUCH_ENTER: case STMFTS_EV_MULTI_TOUCH_MOTION: stmfts_report_contact_event(sdata, event); break; case STMFTS_EV_MULTI_TOUCH_LEAVE: stmfts_report_contact_release(sdata, event); break; case STMFTS_EV_HOVER_ENTER: case STMFTS_EV_HOVER_LEAVE: case STMFTS_EV_HOVER_MOTION: stmfts_report_hover_event(sdata, event); break; case STMFTS_EV_KEY_STATUS: stmfts_report_key_event(sdata, event); break; case STMFTS_EV_ERROR: dev_warn(&sdata->client->dev, "error code: 0x%x%x%x%x%x%x", event[6], event[5], event[4], event[3], event[2], event[1]); break; default: dev_err(&sdata->client->dev, "unknown event %#02x\n", event[0]); } }} static irqreturn_t stmfts_irq_handler(int irq, void *dev){ struct stmfts_data *sdata = dev; int err; guard(mutex)(&sdata->mutex); err = stmfts_read_events(sdata); if (unlikely(err)) dev_err(&sdata->client->dev, "failed to read events: %d\n", err); else stmfts_parse_events(sdata); return IRQ_HANDLED;} static int stmfts_command(struct stmfts_data *sdata, const u8 cmd){ int err; reinit_completion(&sdata->cmd_done); err = i2c_smbus_write_byte(sdata->client, cmd); if (err) return err; if (!wait_for_completion_timeout(&sdata->cmd_done, msecs_to_jiffies(1000))) return -ETIMEDOUT; return 0;} static int stmfts_input_open(struct input_dev *dev){ struct stmfts_data *sdata = input_get_drvdata(dev); int err; err = pm_runtime_resume_and_get(&sdata->client->dev); if (err) return err; err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON); if (err) { pm_runtime_put_sync(&sdata->client->dev); return err; } scoped_guard(mutex, &sdata->mutex) { sdata->running = true; if (sdata->hover_enabled) { err = i2c_smbus_write_byte(sdata->client, STMFTS_SS_HOVER_SENSE_ON); if (err) dev_warn(&sdata->client->dev, "failed to enable hover\n"); } } if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_KEY_SENSE_ON); if (err) /* I can still use only the touch screen */ dev_warn(&sdata->client->dev, "failed to enable touchkey\n"); } return 0;} static void stmfts_input_close(struct input_dev *dev){ struct stmfts_data *sdata = input_get_drvdata(dev); int err; err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable touchscreen: %d\n", err); scoped_guard(mutex, &sdata->mutex) { sdata->running = false; if (sdata->hover_enabled) { err = i2c_smbus_write_byte(sdata->client, STMFTS_SS_HOVER_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable hover: %d\n", err); } } if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_KEY_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable touchkey: %d\n", err); } pm_runtime_put_sync(&sdata->client->dev);} static ssize_t stmfts_sysfs_chip_id(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%#x\n", sdata->chip_id);} static ssize_t stmfts_sysfs_chip_version(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->chip_ver);} static ssize_t stmfts_sysfs_fw_ver(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->fw_ver);} static ssize_t stmfts_sysfs_config_id(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%#x\n", sdata->config_id);} static ssize_t stmfts_sysfs_config_version(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->config_ver);} static ssize_t stmfts_sysfs_read_status(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); u8 status[4]; int err; err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_STATUS, sizeof(status), status); if (err) return err; return sysfs_emit(buf, "%#02x\n", status[0]);} static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, struct device_attribute *attr, char *buf){ struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->hover_enabled);} static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct stmfts_data *sdata = dev_get_drvdata(dev); unsigned long value; bool hover; int err; if (kstrtoul(buf, 0, &value)) return -EINVAL; hover = !!value; guard(mutex)(&sdata->mutex); if (hover != sdata->hover_enabled) { if (sdata->running) { err = i2c_smbus_write_byte(sdata->client, value ? STMFTS_SS_HOVER_SENSE_ON : STMFTS_SS_HOVER_SENSE_OFF); if (err) return err; } sdata->hover_enabled = hover; } Lines 1–500 of 862. Load more
// SPDX-License-Identifier: GPL-2.0 // STMicroelectronics FTS Touchscreen device driver // // Copyright (c) 2017 Samsung Electronics Co., Ltd. // Copyright (c) 2017 Andi Shyti <andi@etezian.org> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> /* I2C commands */ #define STMFTS_READ_INFO 0x80 #define STMFTS_READ_STATUS 0x84 #define STMFTS_READ_ONE_EVENT 0x85 #define STMFTS_READ_ALL_EVENT 0x86 #define STMFTS_LATEST_EVENT 0x87 #define STMFTS_SLEEP_IN 0x90 #define STMFTS_SLEEP_OUT 0x91 #define STMFTS_MS_MT_SENSE_OFF 0x92 #define STMFTS_MS_MT_SENSE_ON 0x93 #define STMFTS_SS_HOVER_SENSE_OFF 0x94 #define STMFTS_SS_HOVER_SENSE_ON 0x95 #define STMFTS_MS_KEY_SENSE_OFF 0x9a #define STMFTS_MS_KEY_SENSE_ON 0x9b #define STMFTS_SYSTEM_RESET 0xa0 #define STMFTS_CLEAR_EVENT_STACK 0xa1 #define STMFTS_FULL_FORCE_CALIBRATION 0xa2 #define STMFTS_MS_CX_TUNING 0xa3 #define STMFTS_SS_CX_TUNING 0xa4 /* events */ #define STMFTS_EV_NO_EVENT 0x00 #define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02 #define STMFTS_EV_MULTI_TOUCH_ENTER 0x03 #define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04 #define STMFTS_EV_MULTI_TOUCH_MOTION 0x05 #define STMFTS_EV_HOVER_ENTER 0x07 #define STMFTS_EV_HOVER_LEAVE 0x08 #define STMFTS_EV_HOVER_MOTION 0x09 #define STMFTS_EV_KEY_STATUS 0x0e #define STMFTS_EV_ERROR 0x0f #define STMFTS_EV_CONTROLLER_READY 0x10 #define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 #define STMFTS_EV_STATUS 0x16 #define STMFTS_EV_DEBUG 0xdb /* multi touch related event masks */ #define STMFTS_MASK_EVENT_ID 0x0f #define STMFTS_MASK_TOUCH_ID 0xf0 #define STMFTS_MASK_LEFT_EVENT 0x0f #define STMFTS_MASK_X_MSB 0x0f #define STMFTS_MASK_Y_LSB 0xf0 /* key related event masks */ #define STMFTS_MASK_KEY_NO_TOUCH 0x00 #define STMFTS_MASK_KEY_MENU 0x01 #define STMFTS_MASK_KEY_BACK 0x02 #define STMFTS_EVENT_SIZE 8 #define STMFTS_STACK_DEPTH 32 #define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH) #define STMFTS_MAX_FINGERS 10 #define STMFTS_DEV_NAME "stmfts" static const struct regulator_bulk_data stmfts_supplies[] = { { .supply = "vdd" }, { .supply = "avdd" }, }; struct stmfts_data { struct i2c_client *client; struct input_dev *input; struct gpio_desc *reset_gpio; struct led_classdev led_cdev; struct mutex mutex; struct touchscreen_properties prop; struct regulator_bulk_data *supplies; /* * Presence of ledvdd will be used also to check * whether the LED is supported. */ struct regulator *ledvdd; u16 chip_id; u8 chip_ver; u16 fw_ver; u8 config_id; u8 config_ver; u8 data[STMFTS_DATA_MAX_SIZE]; struct completion cmd_done; bool use_key; bool led_status; bool hover_enabled; bool running; }; static int stmfts_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); int err; if (value != sdata->led_status && sdata->ledvdd) { if (!value) { regulator_disable(sdata->ledvdd); } else { err = regulator_enable(sdata->ledvdd); if (err) { dev_warn(&sdata->client->dev, "failed to enable ledvdd regulator: %d\n", err); return err; } } sdata->led_status = value; } return 0; } static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev) { struct stmfts_data *sdata = container_of(led_cdev, struct stmfts_data, led_cdev); return !!regulator_is_enabled(sdata->ledvdd); } /* * We can't simply use i2c_smbus_read_i2c_block_data because we * need to read 256 bytes, which exceeds the 255-byte SMBus block limit. */ static int stmfts_read_events(struct stmfts_data *sdata) { u8 cmd = STMFTS_READ_ALL_EVENT; struct i2c_msg msgs[2] = { { .addr = sdata->client->addr, .len = 1, .buf = &cmd, }, { .addr = sdata->client->addr, .flags = I2C_M_RD, .len = STMFTS_DATA_MAX_SIZE, .buf = sdata->data, }, }; int ret; ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) return ret; return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; } static void stmfts_report_contact_event(struct stmfts_data *sdata, const u8 event[]) { u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); u16 y = (event[2] >> 4) | (event[3] << 4); u8 maj = event[4]; u8 min = event[5]; u8 orientation = event[6]; u8 area = event[7]; input_mt_slot(sdata->input, slot_id); input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); input_report_abs(sdata->input, ABS_MT_POSITION_X, x); input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj); input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min); input_report_abs(sdata->input, ABS_MT_PRESSURE, area); input_report_abs(sdata->input, ABS_MT_ORIENTATION, orientation); input_sync(sdata->input); } static void stmfts_report_contact_release(struct stmfts_data *sdata, const u8 event[]) { u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; input_mt_slot(sdata->input, slot_id); input_mt_report_slot_inactive(sdata->input); input_sync(sdata->input); } static void stmfts_report_hover_event(struct stmfts_data *sdata, const u8 event[]) { u16 x = (event[2] << 4) | (event[4] >> 4); u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); u8 z = event[5]; input_report_abs(sdata->input, ABS_X, x); input_report_abs(sdata->input, ABS_Y, y); input_report_abs(sdata->input, ABS_DISTANCE, z); input_sync(sdata->input); } static void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[]) { switch (event[2]) { case 0: input_report_key(sdata->input, KEY_BACK, 0); input_report_key(sdata->input, KEY_MENU, 0); break; case STMFTS_MASK_KEY_BACK: input_report_key(sdata->input, KEY_BACK, 1); break; case STMFTS_MASK_KEY_MENU: input_report_key(sdata->input, KEY_MENU, 1); break; default: dev_warn(&sdata->client->dev, "unknown key event: %#02x\n", event[2]); break; } input_sync(sdata->input); } static void stmfts_parse_events(struct stmfts_data *sdata) { int i; for (i = 0; i < STMFTS_STACK_DEPTH; i++) { u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; switch (event[0]) { case STMFTS_EV_CONTROLLER_READY: case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: case STMFTS_EV_STATUS: complete(&sdata->cmd_done); fallthrough; case STMFTS_EV_NO_EVENT: case STMFTS_EV_DEBUG: return; } switch (event[0] & STMFTS_MASK_EVENT_ID) { case STMFTS_EV_MULTI_TOUCH_ENTER: case STMFTS_EV_MULTI_TOUCH_MOTION: stmfts_report_contact_event(sdata, event); break; case STMFTS_EV_MULTI_TOUCH_LEAVE: stmfts_report_contact_release(sdata, event); break; case STMFTS_EV_HOVER_ENTER: case STMFTS_EV_HOVER_LEAVE: case STMFTS_EV_HOVER_MOTION: stmfts_report_hover_event(sdata, event); break; case STMFTS_EV_KEY_STATUS: stmfts_report_key_event(sdata, event); break; case STMFTS_EV_ERROR: dev_warn(&sdata->client->dev, "error code: 0x%x%x%x%x%x%x", event[6], event[5], event[4], event[3], event[2], event[1]); break; default: dev_err(&sdata->client->dev, "unknown event %#02x\n", event[0]); } } } static irqreturn_t stmfts_irq_handler(int irq, void *dev) { struct stmfts_data *sdata = dev; int err; guard(mutex)(&sdata->mutex); err = stmfts_read_events(sdata); if (unlikely(err)) dev_err(&sdata->client->dev, "failed to read events: %d\n", err); else stmfts_parse_events(sdata); return IRQ_HANDLED; } static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) { int err; reinit_completion(&sdata->cmd_done); err = i2c_smbus_write_byte(sdata->client, cmd); if (err) return err; if (!wait_for_completion_timeout(&sdata->cmd_done, msecs_to_jiffies(1000))) return -ETIMEDOUT; return 0; } static int stmfts_input_open(struct input_dev *dev) { struct stmfts_data *sdata = input_get_drvdata(dev); int err; err = pm_runtime_resume_and_get(&sdata->client->dev); if (err) return err; err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON); if (err) { pm_runtime_put_sync(&sdata->client->dev); return err; } scoped_guard(mutex, &sdata->mutex) { sdata->running = true; if (sdata->hover_enabled) { err = i2c_smbus_write_byte(sdata->client, STMFTS_SS_HOVER_SENSE_ON); if (err) dev_warn(&sdata->client->dev, "failed to enable hover\n"); } } if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_KEY_SENSE_ON); if (err) /* I can still use only the touch screen */ dev_warn(&sdata->client->dev, "failed to enable touchkey\n"); } return 0; } static void stmfts_input_close(struct input_dev *dev) { struct stmfts_data *sdata = input_get_drvdata(dev); int err; err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable touchscreen: %d\n", err); scoped_guard(mutex, &sdata->mutex) { sdata->running = false; if (sdata->hover_enabled) { err = i2c_smbus_write_byte(sdata->client, STMFTS_SS_HOVER_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable hover: %d\n", err); } } if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_KEY_SENSE_OFF); if (err) dev_warn(&sdata->client->dev, "failed to disable touchkey: %d\n", err); } pm_runtime_put_sync(&sdata->client->dev); } static ssize_t stmfts_sysfs_chip_id(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%#x\n", sdata->chip_id); } static ssize_t stmfts_sysfs_chip_version(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->chip_ver); } static ssize_t stmfts_sysfs_fw_ver(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->fw_ver); } static ssize_t stmfts_sysfs_config_id(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%#x\n", sdata->config_id); } static ssize_t stmfts_sysfs_config_version(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->config_ver); } static ssize_t stmfts_sysfs_read_status(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); u8 status[4]; int err; err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_STATUS, sizeof(status), status); if (err) return err; return sysfs_emit(buf, "%#02x\n", status[0]); } static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, struct device_attribute *attr, char *buf) { struct stmfts_data *sdata = dev_get_drvdata(dev); return sysfs_emit(buf, "%u\n", sdata->hover_enabled); } static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct stmfts_data *sdata = dev_get_drvdata(dev); unsigned long value; bool hover; int err; if (kstrtoul(buf, 0, &value)) return -EINVAL; hover = !!value; guard(mutex)(&sdata->mutex); if (hover != sdata->hover_enabled) { if (sdata->running) { err = i2c_smbus_write_byte(sdata->client, value ? STMFTS_SS_HOVER_SENSE_ON : STMFTS_SS_HOVER_SENSE_OFF); if (err) return err; } sdata->hover_enabled = hover; }