Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36ff7af2 authored by Shantanu Jain's avatar Shantanu Jain Committed by Gerrit - the friendly Code Review server
Browse files

input: atmel_mxt_ts: add support for configuring soft keys



The touchscreen controllers are field-swappable on both customer
devices and internal reference devices. It is possible to have
multiple Atmel touch controllers where each controller would
support softkeys in a different way. While some Atmel touch
controllers support sending a series of keys (using T15 object),
some others report native X-Y touch coordinates of the touch
sensor in the soft key region. While in the earlier case, the
touch sensor directly reports the key codes and hence there is
no need for software to do any translation, In the latter case
software needs to translate these coordinates to keycodes.

The current architecture of Atmel touchscreen driver does not
have support to disambiguate between more than two Atmel touch
controllers that send coordinates in lieu of key codes. To
support this functionality in driver, a device-tree property
is defined that describes the key codes for the soft-key area.
The driver calculates the total soft key area based on the
display resolution and touch sensor dimensions. Based on the
total soft-key area and number of keys to be supported, it
calculates the area for each of the soft-keys. It exports this
information to userspace by populating a sysfs file. User-space
programs can then translate the touch coordinates received to
key codes.

Change-Id: I69c411f65f439dddf39f00b31fb409bb3e5c54bf
Signed-off-by: default avatarHimanshu Aggarwal <haggarwa@codeaurora.org>
Signed-off-by: default avatarShantanu Jain <shjain@codeaurora.org>
parent 46ff9a21
Loading
Loading
Loading
Loading
+32 −6
Original line number Diff line number Diff line
@@ -7,10 +7,6 @@ Required properties:
 - interrupt-parent	: parent of interrupt
 - interrupts		: touch sample interrupt to indicate presense or release
				of fingers on the panel.
 - atmel,panel-coords	: touch panel minimum x, minimum y, maximum x and
				maximum y resolution
 - atmel,display-coords : LCD display minimum x, minimum y, maximum x and
				maximum y resolution
 - vdd_ana-supply	: Analog power supply needed to power device
 - atmel,irq-gpio	: irq gpio
 - atmel,reset-gpio	: reset gpio
@@ -50,6 +46,34 @@ Optional property:
 - atmel,i2cmode-gpio		: specify gpio number for i2cmode, which provides
					internal pull-ups to i2c bus in case there
					is no hardware pull-up support
 - atmel,panel-coords	: touch panel minimum x, minimum y, maximum x and
				maximum y resolution. Used to configure the input
				device.
 - atmel,display-coords : LCD display minimum x, minimum y, maximum x and
				maximum y resolution. Used to configure the input
				device.
 - atmel,vkey-codes		: virtual key code mappings to be used when touch controller
				 does not support sending keycodes and sends coordinates
				 instead. In this case user space programs need to translate
				 coordinates to keycodes instead. Virtual key region sits
				 between display's lower bound and touchscreen's lower bound.
				 Horizontally these keys are evenly distributed between touch
				 boundaries. These codes map to buttons from left to right
				 according to icons on the touch panel. The value of these
				 codes is taken from include/uapi/linux/input.h. If
				 atmel,vkey-codes are specified, it is mandatory to specify
				 atmel,panel-coords and atmel,display-coords as well because
				 rectangular area of each of the virtual key is derived using
				 these two properties.
 - atmel,create-vkeys		: Define this property to create vkey entries for Atmel
				touchscreen driver. This is used when there are multiple Atmel
				touchscreen controllers, and everyone has to support virtual
				keys. Following are the mandatory properties that must be
				defined inside the cfg nodes:
				- atmel,family-id
				- atmel,panel-coords
				- atmel,display-coords
				- atmel,vkey-codes

Example:
	i2c@f9966000 {
@@ -71,8 +95,6 @@ Example:
			interrupts = <48 0x0>;
			vdd_ana-supply = <&pm8941_l18>;
			vcc_i2c-supply = <&pm8941_lvs1>;
			atmel,panel-coords = <0 0 479 799>;
			atmel,display-coords = <0 0 479 799>;
			/* pins used by touchscreen */
			pinctrl-names = "pmx_ts_active","pmx_ts_suspend", "pmx_ts_release";
			pinctrl-0 = <&ts_int_active &ts_reset_active>;
@@ -88,11 +110,15 @@ Example:
				0 0 0 0 0 0 0 0 >;
			atmel,irq-gpio = <&msmgpio 48 0>;
			atmel,reset-gpio = <&msmgpio 26 0>;
			atmel,create-vkeys;
			atmel,cfg_1 {
				atmel,family-id = <0x81>;
				atmel,variant-id = <0x01>;
				atmel,version = <0x10>;
				atmel,build = <0xaa>;
				atmel,panel-coords = <0 0 479 799>;
				atmel,display-coords = <0 0 479 799>;
				atmel,vkey-codes = <139 172 158 217>;
				atmel,config = [
					/* Object 6, Instance = 0 */
					00 00 00 00 00 00
+233 −28
Original line number Diff line number Diff line
@@ -330,6 +330,18 @@ enum mxt_device_state { INIT, APPMODE, BOOTLOADER };
#define MXT_DEBUGFS_DIR	"ts_debug"
#define MXT_DEBUGFS_FILE_OBJ	"object"
#define MXT_DEBUGFS_FILE_SUSPEND	"suspend"
#define DISP_PROP "atmel,display-coords"
#define PANEL_PROP "atmel,panel-coords"

#define MAX_BUF_SIZE	256
#define VKEY_VER_CODE	"0x01"

#define HEIGHT_SCALE_NUM 8
#define HEIGHT_SCALE_DENOM 10

/* numerator and denomenator for border equations */
#define BORDER_ADJUST_NUM 3
#define BORDER_ADJUST_DENOM 4

struct mxt_info {
	u8 family_id;
@@ -397,6 +409,8 @@ struct mxt_data {
	u32 keyarray_new;
	u8 t9_max_reportid;
	u8 t9_min_reportid;
	u16 t9_ymax_reso;
	u16 t9_xmax_reso;
	u8 t15_max_reportid;
	u8 t15_min_reportid;
	u8 t42_max_reportid;
@@ -418,6 +432,81 @@ struct mxt_data {
};

static struct dentry *debug_base;
static struct kobject *vkey_kobj;
static char *vkey_buf;

static ssize_t vkey_show(struct kobject  *obj,
	struct kobj_attribute *attr, char *buf)
{
	strlcpy(buf, vkey_buf, MAX_BUF_SIZE);
	return strnlen(buf, MAX_BUF_SIZE);
}

static struct kobj_attribute vkey_obj_attr = {
	.attr = {
		.mode = S_IRUGO,
		.name = "virtualkeys.atmel_mxt_ts",
	},
	.show = vkey_show,
};

static struct attribute *vkey_attr[] = {
	&vkey_obj_attr.attr,
	NULL,
};

static struct attribute_group vkey_grp = {
	.attrs = vkey_attr,
};

static int mxt_virtual_keys_init(struct device *dev,
			struct mxt_data *data)
{
	int width, height, center_x, center_y;
	int x1 = 0, x2 = 0, i, c = 0, rc = 0, border;

	vkey_buf = devm_kzalloc(dev, MAX_BUF_SIZE, GFP_KERNEL);
	if (!vkey_buf) {
		dev_err(dev, "Failed to allocate memory\n");
		return -ENOMEM;
	}

	border = (data->pdata->panel_maxx - data->pdata->disp_maxx) * 2;
	width = ((data->pdata->disp_maxx -
			(border * (data->pdata->nvkeys - 1)))
			/ data->pdata->nvkeys);
	height = (data->pdata->panel_maxy - data->pdata->disp_maxy);
	center_y = data->pdata->disp_maxy + (height / 2);
	height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;

	x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;

	for (i = 0; i < data->pdata->nvkeys; i++) {
		x1 = x2 + border;
		x2 = x2 + border + width;
		center_x = x1 + (x2 - x1) / 2;
		c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c,
				"%s:%d:%d:%d:%d:%d\n", VKEY_VER_CODE,
				data->pdata->vkey_codes[i],
				center_x, center_y, width, height);
	}

	vkey_buf[c] = '\0';

	vkey_kobj = kobject_create_and_add("board_properties", NULL);
	if (!vkey_kobj) {
		dev_err(dev, "unable to create kobject\n");
		return -ENOMEM;
	}

	rc = sysfs_create_group(vkey_kobj, &vkey_grp);
	if (rc) {
		dev_err(dev, "failed to create attributes\n");
		kobject_put(vkey_kobj);
	}

	return rc;
}

static bool mxt_object_readable(unsigned int type)
{
@@ -1497,6 +1586,7 @@ static int mxt_save_objects(struct mxt_data *data)
	struct mxt_object *t15_object;
	struct mxt_object *t42_object;
	int error;
	u8 val[4];

	/* Store T7 and T9 locally, used in suspend/resume operations */
	t7_object = mxt_get_object(data, MXT_GEN_POWER_T7);
@@ -1525,6 +1615,18 @@ static int mxt_save_objects(struct mxt_data *data)
					(t9_object->num_report_ids *
					(t9_object->instances + 1)) + 1;

	error = __mxt_read_reg(client,
		t9_object->start_address + MXT_TOUCH_XRANGE_LSB, 4, val);
	if (error) {
		dev_err(&client->dev, "Failed to get X-Y reso\n");
		return -EINVAL;
	}

	/* Y resolution */
	data->t9_ymax_reso = (val[1]<<8) + val[0];
	/* X resolution */
	data->t9_xmax_reso = (val[3]<<8) + val[2];

	if (data->pdata->key_codes) {
		t15_object = mxt_get_object(data, MXT_TOUCH_KEYARRAY_T15);
		if (!t15_object)
@@ -1662,8 +1764,9 @@ static int mxt_initialize(struct mxt_data *data)
	info->matrix_ysize = val;

	dev_info(&client->dev,
			"Matrix X Size: %d Matrix Y Size: %d\n",
			info->matrix_xsize, info->matrix_ysize);
		"Matrix X Size: %d Matrix Y Size: %d Panel X Resolution: %d Panel Y Resolution: %d\n",
		info->matrix_xsize, info->matrix_ysize,
		data->t9_xmax_reso, data->t9_ymax_reso);

	return 0;

@@ -2966,11 +3069,11 @@ static void mxt_debugfs_init(struct mxt_data *data)

#ifdef CONFIG_OF
static int mxt_get_dt_coords(struct device *dev, char *name,
				struct mxt_platform_data *pdata)
		struct device_node *node, struct mxt_platform_data *pdata)
{
	u32 coords[MXT_COORDS_ARR_SIZE];
	struct property *prop;
	struct device_node *np = dev->of_node;
	struct device_node *np = (node == NULL) ? (dev->of_node) : (node);
	int coords_size, rc;

	prop = of_find_property(np, name, NULL);
@@ -3048,12 +3151,12 @@ static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
	struct property *prop;
	u32 temp_val;

	rc = mxt_get_dt_coords(dev, "atmel,panel-coords", pdata);
	if (rc)
	rc = mxt_get_dt_coords(dev, "atmel,panel-coords", NULL, pdata);
	if (rc && (rc != -EINVAL))
		return rc;

	rc = mxt_get_dt_coords(dev, "atmel,display-coords", pdata);
	if (rc)
	rc = mxt_get_dt_coords(dev, "atmel,display-coords", NULL, pdata);
	if (rc && (rc != -EINVAL))
		return rc;

	/* regulator info */
@@ -3074,6 +3177,8 @@ static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
	pdata->irq_gpio = of_get_named_gpio_flags(np, "atmel,irq-gpio",
				0, &pdata->irq_gpio_flags);

	pdata->create_vkeys = of_property_read_bool(np, "atmel,create-vkeys");

	/* keycodes for keyarray object*/
	prop = of_find_property(np, "atmel,key-codes", NULL);
	if (prop) {
@@ -3285,6 +3390,85 @@ err_pinctrl_get:
	return retval;
}

static int mxt_check_child_node_and_create_vkeys(struct mxt_data *data,
		struct mxt_platform_data *pdata, struct device_node *np)
{
	u8 family_id;
	u16 x_reso, y_reso;
	u32 temp_val, d[MXT_COORDS_ARR_SIZE], p[MXT_COORDS_ARR_SIZE];
	int error, proplen;
	struct i2c_client *client = data->client;

	if (!of_property_read_u32_array(np, DISP_PROP, d, ARRAY_SIZE(d)) &&
		!of_property_read_u32_array(np, PANEL_PROP, p, ARRAY_SIZE(p)) &&
		!of_property_read_u32(np, "atmel,family-id", &temp_val) &&
		of_find_property(np, "atmel,vkey-codes", &proplen)) {

		/* Read family id from dt */
		family_id = (u8) temp_val;

		/* Read X resolution of touch panel from dt */
		x_reso = (u16) p[2];

		/* Read Y resolution of touch panel from dt */
		y_reso = (u16) p[3];

		/*
		 * Check if family id, x-resolution and y-resolution read from
		 * the child DT node match with those read from the touch
		 * controller. In case there is a match:
		 * 1. Populate platform data
		 * 2. Create /sys/board_properties/virtual_keys.<devicename>
		 *  sysfs
		 * Otherwise return -EINVAL to indicate that current child
		 * node's characteristics don't match that of the touch
		 * controller's.
		 */
		if (family_id == data->info.family_id &&
				x_reso == data->t9_xmax_reso &&
				y_reso == data->t9_ymax_reso) {

			/* Populate platform data's display coordinates */
			pdata->disp_minx = d[0];
			pdata->disp_miny = d[1];
			pdata->disp_maxx = d[2];
			pdata->disp_maxy = d[3];

			/* Populate platform data's panel coordinates */
			pdata->panel_minx = p[0];
			pdata->panel_miny = p[1];
			pdata->panel_maxx = p[2];
			pdata->panel_maxy = p[3];

			/* Parse vkey-codes property */
			pdata->nvkeys = proplen / sizeof(u32);
			pdata->vkey_codes = devm_kzalloc(&client->dev,
					sizeof(int) * pdata->nvkeys,
					GFP_KERNEL);
			if (!pdata->vkey_codes)
				return -ENOMEM;

			error = of_property_read_u32_array(np,
					"atmel,vkey-codes",
					pdata->vkey_codes, pdata->nvkeys);
			if (error) {
				dev_err(&client->dev, "Unable to read virtual key codes\n");
				return error;
			}

			error = mxt_virtual_keys_init(&client->dev, data);
			if (error) {
				dev_err(&client->dev, "Unable to create virtual keys\n");
				return error;
			}
		} else
			return -EINVAL;
	} else
		return -EINVAL;

	return 0;
}

static int mxt_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
@@ -3292,6 +3476,7 @@ static int mxt_probe(struct i2c_client *client,
	struct mxt_data *data;
	struct input_dev *input_dev;
	int error, i;
	struct device_node *child, *np;

	if (client->dev.of_node) {
		pdata = devm_kzalloc(&client->dev,
@@ -3337,25 +3522,6 @@ static int mxt_probe(struct i2c_client *client,
	__set_bit(BTN_TOUCH, input_dev->keybit);
	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);

	/* For single touch */
	input_set_abs_params(input_dev, ABS_X,
			pdata->disp_minx, pdata->disp_maxx, 0, 0);
	input_set_abs_params(input_dev, ABS_Y,
			pdata->disp_miny, pdata->disp_maxy, 0, 0);
	input_set_abs_params(input_dev, ABS_PRESSURE,
			     0, 255, 0, 0);

	/* For multi touch */
	input_mt_init_slots(input_dev, MXT_MAX_FINGER, 0);
	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
			     0, MXT_MAX_AREA, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
			pdata->disp_minx, pdata->disp_maxx, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
			pdata->disp_miny, pdata->disp_maxy, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_PRESSURE,
			     0, 255, 0, 0);

	/* set key array supported keys */
	if (pdata->key_codes) {
		for (i = 0; i < MXT_KEYARRAY_MAX_KEYS; i++) {
@@ -3421,9 +3587,39 @@ static int mxt_probe(struct i2c_client *client,
	if (error)
		goto err_configure_gpio;

	if (pdata->create_vkeys) {
		np = client->dev.of_node;
		for_each_child_of_node(np, child) {
			if (!mxt_check_child_node_and_create_vkeys(data,
						 pdata, child)) {
				dev_info(&client->dev, "vkeys created successfully\n");
				break;
			}
		}
		if (child == NULL)
			dev_err(&client->dev, "Failed to create vkeys\n");
	}

	/* For single touch */
	input_set_abs_params(input_dev, ABS_X,
			data->pdata->disp_minx, data->pdata->disp_maxx, 0, 0);
	input_set_abs_params(input_dev, ABS_Y,
			data->pdata->disp_miny, data->pdata->disp_maxy, 0, 0);
	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);

	/* For multi touch */
	input_mt_init_slots(input_dev, MXT_MAX_FINGER, 0);
	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
			     0, MXT_MAX_AREA, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
			data->pdata->disp_minx, data->pdata->disp_maxx, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
			data->pdata->disp_miny, data->pdata->disp_maxy, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);

	error = irq_of_parse_and_map(client->dev.of_node, 0);
	if (!error)
		goto err_configure_gpio;
		goto err_parse_irq;

	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
			pdata->irqflags | IRQF_ONESHOT,
@@ -3478,6 +3674,11 @@ err_free_irq:
	free_irq(client->irq, data);
err_free_object:
	kfree(data->object_table);
err_parse_irq:
	if (data->pdata->create_vkeys) {
		sysfs_remove_group(vkey_kobj, &vkey_grp);
		kobject_put(vkey_kobj);
	}
err_configure_gpio:
	if (gpio_is_valid(pdata->irq_gpio))
		gpio_free(pdata->irq_gpio);
@@ -3516,6 +3717,10 @@ static int mxt_remove(struct i2c_client *client)
	struct mxt_data *data = i2c_get_clientdata(client);

	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
	if (data->pdata->create_vkeys) {
		sysfs_remove_group(vkey_kobj, &vkey_grp);
		kobject_put(vkey_kobj);
	}
	free_irq(data->irq, data);
	input_unregister_device(data->input_dev);
#if defined(CONFIG_FB)
+4 −0
Original line number Diff line number Diff line
@@ -88,7 +88,11 @@ struct mxt_platform_data {
	bool need_calibration;
	bool no_force_update;
	bool no_lpm_support;
	bool create_vkeys;
	u8 bl_addr;
	/* Points to the virtual key array */
	unsigned int *vkey_codes;
	unsigned char nvkeys;

	u8(*read_chg) (void);
	int (*init_hw) (bool);