845 lines · 20.6 KB · C
RawDownload
/* * Copyright (C) 2012-2013 MundoReader S.L. * Author: Heiko Stuebner <heiko@sntech.de> * * based in parts on Nook zforce driver * * Copyright (C) 2010 Barnes & Noble, Inc. * Author: Pieter Truter<ptruter@intrinsyc.com> * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. */ #include <linux/module.h>#include <linux/hrtimer.h>#include <linux/slab.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/i2c.h>#include <linux/delay.h>#include <linux/gpio.h>#include <linux/device.h>#include <linux/sysfs.h>#include <linux/input/mt.h>#include <linux/platform_data/zforce_ts.h> #define WAIT_TIMEOUT		msecs_to_jiffies(1000) #define FRAME_START		0xee /* Offsets of the different parts of the payload the controller sends */#define PAYLOAD_HEADER		0#define PAYLOAD_LENGTH		1#define PAYLOAD_BODY		2 /* Response offsets */#define RESPONSE_ID		0#define RESPONSE_DATA		1 /* Commands */#define COMMAND_DEACTIVATE	0x00#define COMMAND_INITIALIZE	0x01#define COMMAND_RESOLUTION	0x02#define COMMAND_SETCONFIG	0x03#define COMMAND_DATAREQUEST	0x04#define COMMAND_SCANFREQ	0x08#define COMMAND_STATUS		0X1e /* * Responses the controller sends as a result of * command requests */#define RESPONSE_DEACTIVATE	0x00#define RESPONSE_INITIALIZE	0x01#define RESPONSE_RESOLUTION	0x02#define RESPONSE_SETCONFIG	0x03#define RESPONSE_SCANFREQ	0x08#define RESPONSE_STATUS		0X1e /* * Notifications are send by the touch controller without * being requested by the driver and include for example * touch indications */#define NOTIFICATION_TOUCH		0x04#define NOTIFICATION_BOOTCOMPLETE	0x07#define NOTIFICATION_OVERRUN		0x25#define NOTIFICATION_PROXIMITY		0x26#define NOTIFICATION_INVALID_COMMAND	0xfe #define ZFORCE_REPORT_POINTS		2#define ZFORCE_MAX_AREA			0xff #define STATE_DOWN			0#define STATE_MOVE			1#define STATE_UP			2 #define SETCONFIG_DUALTOUCH		(1 << 0) struct zforce_point {	int coord_x;	int coord_y;	int state;	int id;	int area_major;	int area_minor;	int orientation;	int pressure;	int prblty;}; /* * @client		the i2c_client * @input		the input device * @suspending		in the process of going to suspend (don't emit wakeup *			events for commands executed to suspend the device) * @suspended		device suspended * @access_mutex	serialize i2c-access, to keep multipart reads together * @command_done	completion to wait for the command result * @command_mutex	serialize commands send to the ic * @command_waiting	the id of the command that that is currently waiting *			for a result * @command_result	returned result of the command */struct zforce_ts {	struct i2c_client	*client;	struct input_dev	*input;	const struct zforce_ts_platdata *pdata;	char			phys[32]; 	bool			suspending;	bool			suspended;	bool			boot_complete; 	/* Firmware version information */	u16			version_major;	u16			version_minor;	u16			version_build;	u16			version_rev; 	struct mutex		access_mutex; 	struct completion	command_done;	struct mutex		command_mutex;	int			command_waiting;	int			command_result;}; static int zforce_command(struct zforce_ts *ts, u8 cmd){	struct i2c_client *client = ts->client;	char buf[3];	int ret; 	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); 	buf[0] = FRAME_START;	buf[1] = 1; /* data size, command only */	buf[2] = cmd; 	mutex_lock(&ts->access_mutex);	ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));	mutex_unlock(&ts->access_mutex);	if (ret < 0) {		dev_err(&client->dev, "i2c send data request error: %d\n", ret);		return ret;	} 	return 0;} static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len){	struct i2c_client *client = ts->client;	int ret; 	ret = mutex_trylock(&ts->command_mutex);	if (!ret) {		dev_err(&client->dev, "already waiting for a command\n");		return -EBUSY;	} 	dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",		buf[1], buf[2]); 	ts->command_waiting = buf[2]; 	mutex_lock(&ts->access_mutex);	ret = i2c_master_send(client, buf, len);	mutex_unlock(&ts->access_mutex);	if (ret < 0) {		dev_err(&client->dev, "i2c send data request error: %d\n", ret);		goto unlock;	} 	dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]); 	if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {		ret = -ETIME;		goto unlock;	} 	ret = ts->command_result; unlock:	mutex_unlock(&ts->command_mutex);	return ret;} static int zforce_command_wait(struct zforce_ts *ts, u8 cmd){	struct i2c_client *client = ts->client;	char buf[3];	int ret; 	dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd); 	buf[0] = FRAME_START;	buf[1] = 1; /* data size, command only */	buf[2] = cmd; 	ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));	if (ret < 0) {		dev_err(&client->dev, "i2c send data request error: %d\n", ret);		return ret;	} 	return 0;} static int zforce_resolution(struct zforce_ts *ts, u16 x, u16 y){	struct i2c_client *client = ts->client;	char buf[7] = { FRAME_START, 5, COMMAND_RESOLUTION,			(x & 0xff), ((x >> 8) & 0xff),			(y & 0xff), ((y >> 8) & 0xff) }; 	dev_dbg(&client->dev, "set resolution to (%d,%d)\n", x, y); 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));} static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger,				 u16 stylus){	struct i2c_client *client = ts->client;	char buf[9] = { FRAME_START, 7, COMMAND_SCANFREQ,			(idle & 0xff), ((idle >> 8) & 0xff),			(finger & 0xff), ((finger >> 8) & 0xff),			(stylus & 0xff), ((stylus >> 8) & 0xff) }; 	dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n",		idle, finger, stylus); 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));} static int zforce_setconfig(struct zforce_ts *ts, char b1){	struct i2c_client *client = ts->client;	char buf[7] = { FRAME_START, 5, COMMAND_SETCONFIG,			b1, 0, 0, 0 }; 	dev_dbg(&client->dev, "set config to (%d)\n", b1); 	return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));} static int zforce_start(struct zforce_ts *ts){	struct i2c_client *client = ts->client;	const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);	int ret; 	dev_dbg(&client->dev, "starting device\n"); 	ret = zforce_command_wait(ts, COMMAND_INITIALIZE);	if (ret) {		dev_err(&client->dev, "Unable to initialize, %d\n", ret);		return ret;	} 	ret = zforce_resolution(ts, pdata->x_max, pdata->y_max);	if (ret) {		dev_err(&client->dev, "Unable to set resolution, %d\n", ret);		goto error;	} 	ret = zforce_scan_frequency(ts, 10, 50, 50);	if (ret) {		dev_err(&client->dev, "Unable to set scan frequency, %d\n",			ret);		goto error;	} 	if (zforce_setconfig(ts, SETCONFIG_DUALTOUCH)) {		dev_err(&client->dev, "Unable to set config\n");		goto error;	} 	/* start sending touch events */	ret = zforce_command(ts, COMMAND_DATAREQUEST);	if (ret) {		dev_err(&client->dev, "Unable to request data\n");		goto error;	} 	/*	 * Per NN, initial cal. take max. of 200msec.	 * Allow time to complete this calibration	 */	msleep(200); 	return 0; error:	zforce_command_wait(ts, COMMAND_DEACTIVATE);	return ret;} static int zforce_stop(struct zforce_ts *ts){	struct i2c_client *client = ts->client;	int ret; 	dev_dbg(&client->dev, "stopping device\n"); 	/* Deactivates touch sensing and puts the device into sleep. */	ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);	if (ret != 0) {		dev_err(&client->dev, "could not deactivate device, %d\n",			ret);		return ret;	} 	return 0;} static int zforce_touch_event(struct zforce_ts *ts, u8 *payload){	struct i2c_client *client = ts->client;	const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);	struct zforce_point point;	int count, i, num = 0; 	count = payload[0];	if (count > ZFORCE_REPORT_POINTS) {		dev_warn(&client->dev, "to many coordinates %d, expected max %d\n",			 count, ZFORCE_REPORT_POINTS);		count = ZFORCE_REPORT_POINTS;	} 	for (i = 0; i < count; i++) {		point.coord_x =			payload[9 * i + 2] << 8 | payload[9 * i + 1];		point.coord_y =			payload[9 * i + 4] << 8 | payload[9 * i + 3]; 		if (point.coord_x > pdata->x_max ||		    point.coord_y > pdata->y_max) {			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",				point.coord_x, point.coord_y);			point.coord_x = point.coord_y = 0;		} 		point.state = payload[9 * i + 5] & 0x03;		point.id = (payload[9 * i + 5] & 0xfc) >> 2; 		/* determine touch major, minor and orientation */		point.area_major = max(payload[9 * i + 6],					  payload[9 * i + 7]);		point.area_minor = min(payload[9 * i + 6],					  payload[9 * i + 7]);		point.orientation = payload[9 * i + 6] > payload[9 * i + 7]; 		point.pressure = payload[9 * i + 8];		point.prblty = payload[9 * i + 9]; 		dev_dbg(&client->dev,			"point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",			i, count, point.state, point.id,			point.pressure, point.prblty,			point.coord_x, point.coord_y,			point.area_major, point.area_minor,			point.orientation); 		/* the zforce id starts with "1", so needs to be decreased */		input_mt_slot(ts->input, point.id - 1); 		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,						point.state != STATE_UP); 		if (point.state != STATE_UP) {			input_report_abs(ts->input, ABS_MT_POSITION_X,					 point.coord_x);			input_report_abs(ts->input, ABS_MT_POSITION_Y,					 point.coord_y);			input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,					 point.area_major);			input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,					 point.area_minor);			input_report_abs(ts->input, ABS_MT_ORIENTATION,					 point.orientation);			num++;		}	} 	input_mt_sync_frame(ts->input); 	input_mt_report_finger_count(ts->input, num); 	input_sync(ts->input); 	return 0;} static int zforce_read_packet(struct zforce_ts *ts, u8 *buf){	struct i2c_client *client = ts->client;	int ret; 	mutex_lock(&ts->access_mutex); 	/* read 2 byte message header */	ret = i2c_master_recv(client, buf, 2);	if (ret < 0) {		dev_err(&client->dev, "error reading header: %d\n", ret);		goto unlock;	} 	if (buf[PAYLOAD_HEADER] != FRAME_START) {		dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);		ret = -EIO;		goto unlock;	} 	if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) {		dev_err(&client->dev, "invalid payload length: %d\n",			buf[PAYLOAD_LENGTH]);		ret = -EIO;		goto unlock;	} 	/* read the message */	ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);	if (ret < 0) {		dev_err(&client->dev, "error reading payload: %d\n", ret);		goto unlock;	} 	dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",		buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]); unlock:	mutex_unlock(&ts->access_mutex);	return ret;} static void zforce_complete(struct zforce_ts *ts, int cmd, int result){	struct i2c_client *client = ts->client; 	if (ts->command_waiting == cmd) {		dev_dbg(&client->dev, "completing command 0x%x\n", cmd);		ts->command_result = result;		complete(&ts->command_done);	} else {		dev_dbg(&client->dev, "command %d not for us\n", cmd);	}} static irqreturn_t zforce_irq(int irq, void *dev_id){	struct zforce_ts *ts = dev_id;	struct i2c_client *client = ts->client; 	if (ts->suspended && device_may_wakeup(&client->dev))		pm_wakeup_event(&client->dev, 500); 	return IRQ_WAKE_THREAD;} static irqreturn_t zforce_irq_thread(int irq, void *dev_id){	struct zforce_ts *ts = dev_id;	struct i2c_client *client = ts->client;	const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);	int ret;	u8 payload_buffer[512];	u8 *payload; 	/*	 * When still suspended, return.	 * Due to the level-interrupt we will get re-triggered later.	 */	if (ts->suspended) {		msleep(20);		return IRQ_HANDLED;	} 	dev_dbg(&client->dev, "handling interrupt\n"); 	/* Don't emit wakeup events from commands run by zforce_suspend */	if (!ts->suspending && device_may_wakeup(&client->dev))		pm_stay_awake(&client->dev); 	while (!gpio_get_value(pdata->gpio_int)) {		ret = zforce_read_packet(ts, payload_buffer);		if (ret < 0) {			dev_err(&client->dev, "could not read packet, ret: %d\n",				ret);			break;		} 

Lines 1–500 of 845.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t