Uwe Kleine-König (The Capable Hub)Input: Use named initializers for arrays of i2c_device_data47ceab26 months agoHistory
862 lines · 20.6 KB · C
RawDownload
// 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.