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

Commit 7a810268 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: atmel_maxtouch_ts: add support for force cfg update"

parents 52016a56 b3ec3adb
Loading
Loading
Loading
Loading
+378 −308
Original line number Diff line number Diff line
@@ -318,6 +318,8 @@ struct mxt_data {
	struct regulator *reg_xvdd;
	char fw_name[MXT_NAME_MAX_LEN];
	char cfg_name[MXT_NAME_MAX_LEN];
	u8 cfg_version[3];
	bool fw_w_no_cfg_update;

#if defined(CONFIG_FB)
	struct notifier_block fb_notif;
@@ -1074,6 +1076,8 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
		if (data->t100_aux_ampl)
			input_report_abs(input_dev, ABS_MT_PRESSURE,
					 message[data->t100_aux_ampl]);
		else
			input_report_abs(input_dev, ABS_MT_PRESSURE, 255);

		if (data->t100_aux_area) {
			if (tool == MT_TOOL_PEN)
@@ -1586,8 +1590,230 @@ static int mxt_check_retrigen(struct mxt_data *data)

static int mxt_init_t7_power_cfg(struct mxt_data *data);

static int mxt_update_cfg_version(struct mxt_data *data)
{
	struct mxt_object *object;
	int error;

	object = mxt_get_object(data, MXT_SPT_USERDATA_T38);
	if (!object)
		return -EINVAL;

	error = __mxt_read_reg(data->client, object->start_address,
			       sizeof(data->cfg_version), &data->cfg_version);
	if (error)
		return error;

	return 0;
}

static int mxt_update_t100_resolution(struct mxt_data *data)
{
	struct i2c_client *client = data->client;
	int error;
	struct mxt_object *object;
	u16 range_x, range_y, temp;
	u8 cfg, tchaux;
	u8 aux;
	bool update = false;

	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
	if (!object)
		return -EINVAL;

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T100_XRANGE,
			       sizeof(range_x), &range_x);
	if (error)
		return error;

	le16_to_cpus(range_x);

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T100_YRANGE,
			       sizeof(range_y), &range_y);
	if (error)
		return error;

	le16_to_cpus(range_y);

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T100_CFG1,
				1, &cfg);
	if (error)
		return error;

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T100_TCHAUX,
				1, &tchaux);
	if (error)
		return error;

	/* Handle default values */
	if (range_x == 0)
		range_x = 1023;

	/* Handle default values */
	if (range_x == 0)
		range_x = 1023;

	if (range_y == 0)
		range_y = 1023;

	dev_dbg(&client->dev, "initial x=%d y=%d\n", range_x, range_y);

	if (cfg & MXT_T100_CFG_SWITCHXY) {
		dev_dbg(&client->dev, "flip x and y\n");
		temp = range_y;
		range_y = range_x;
		range_x = temp;
	}

	if (data->pdata->panel_maxx != range_x) {
		if (cfg & MXT_T100_CFG_SWITCHXY)
			range_x = data->pdata->panel_maxy;
		else
			range_x = data->pdata->panel_maxx;
		cpu_to_le16s(range_x);
		error =  __mxt_write_reg(client,
				object->start_address + MXT_T100_XRANGE,
				sizeof(range_x), &range_x);
		if (error)
			return error;
		dev_dbg(&client->dev, "panel maxx mismatch. update\n");
		update = true;
	}

	if (data->pdata->panel_maxy != range_y) {
		if (cfg & MXT_T100_CFG_SWITCHXY)
			range_y = data->pdata->panel_maxx;
		else
			range_y = data->pdata->panel_maxy;

		cpu_to_le16s(range_y);
		error =  __mxt_write_reg(client,
				object->start_address + MXT_T100_YRANGE,
				sizeof(range_y), &range_y);
		if (error)
			return error;
		dev_dbg(&client->dev, "panel maxy mismatch. update\n");
		update = true;
	}

	if (update) {
		mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);

		mxt_soft_reset(data);
	}

	/* allocate aux bytes */
	aux = 6;

	if (tchaux & MXT_T100_TCHAUX_VECT)
		data->t100_aux_vect = aux++;

	if (tchaux & MXT_T100_TCHAUX_AMPL)
		data->t100_aux_ampl = aux++;

	if (tchaux & MXT_T100_TCHAUX_AREA)
		data->t100_aux_area = aux++;

	dev_info(&client->dev,
		 "T100 Touchscreen size X%uY%u\n", range_x, range_y);

	return 0;
}

static int mxt_update_t9_resolution(struct mxt_data *data)
{
	struct i2c_client *client = data->client;
	int error;
	struct t9_range range;
	unsigned char orient;
	struct mxt_object *object;
	u16 temp;
	bool update = false;

	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
	if (!object)
		return -EINVAL;

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T9_RANGE,
			       sizeof(range), &range);
	if (error)
		return error;

	le16_to_cpus(range.x);
	le16_to_cpus(range.y);

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T9_ORIENT,
				1, &orient);
	if (error)
		return error;

	/* Handle default values */
	if (range.x == 0)
		range.x = 1023;

	if (range.y == 0)
		range.y = 1023;

	dev_dbg(&client->dev, "initial x=%d y=%d\n", range.x, range.y);

	if (orient & MXT_T9_ORIENT_SWITCH) {
		dev_dbg(&client->dev, "flip x and y\n");
		temp = range.y;
		range.y = range.x;
		range.x = temp;
	}

	if (data->pdata->panel_maxx != range.x) {
		if (orient & MXT_T9_ORIENT_SWITCH)
			range.x = data->pdata->panel_maxy;
		else
			range.x = data->pdata->panel_maxx;
		cpu_to_le16s(range.x);
		error =  __mxt_write_reg(client,
				object->start_address + MXT_T100_XRANGE,
				sizeof(range.x), &range.x);
		if (error)
			return error;
		dev_dbg(&client->dev, "panel maxx mismatch. update\n");
		update = true;
	}

	if (data->pdata->panel_maxy != range.y) {
		if (orient & MXT_T9_ORIENT_SWITCH)
			range.y = data->pdata->panel_maxx;
		else
			range.y = data->pdata->panel_maxy;

		cpu_to_le16s(range.y);
		error =  __mxt_write_reg(client,
				object->start_address + MXT_T100_YRANGE,
				sizeof(range.y), &range.y);
		if (error)
			return error;
		dev_dbg(&client->dev, "panel maxy mismatch. update\n");
		update = true;
	}

	if (update) {
		mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);

		mxt_soft_reset(data);
	}

	dev_info(&client->dev,
		 "Touchscreen size X%uY%u\n", range.x, range.y);

	return 0;
}

/*
 * mxt_check_reg_init - download configuration to chip
 * mxt_load_cfg - download configuration to chip
 *
 * Atmel Raw Config File Format
 *
@@ -1605,13 +1831,13 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data);
 *   <SIZE> - 2-byte object size as hex
 *   <CONTENTS> - array of <SIZE> 1-byte hex values
 */
static int mxt_check_reg_init(struct mxt_data *data)
static int mxt_load_cfg(struct mxt_data *data, bool force)
{
	struct device *dev = &data->client->dev;
	struct mxt_info cfg_info;
	struct mxt_object *object;
	const struct firmware *cfg = NULL;
	int ret;
	int ret = 0;
	int offset;
	int data_pos;
	int byte_offset;
@@ -1622,18 +1848,19 @@ static int mxt_check_reg_init(struct mxt_data *data)
	unsigned int config_mem_size;
	unsigned int type, instance, size;
	u8 val;
	int ver[3];
	u16 reg;

	if (!data->cfg_name) {
		dev_dbg(dev, "Skipping cfg download\n");
		return 0;
		goto report_enable;
	}

	ret = request_firmware(&cfg, data->cfg_name, dev);
	if (ret < 0) {
		dev_err(dev, "Failure to request config file %s\n",
			data->cfg_name);
		return 0;
		goto report_enable;
	}

	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
@@ -1689,28 +1916,6 @@ static int mxt_check_reg_init(struct mxt_data *data)
	}
	data_pos += offset;

	/* The Info Block CRC is calculated over mxt_info and the object table
	 * If it does not match then we are trying to load the configuration
	 * from a different chip or firmware version, so the configuration CRC
	 * is invalid anyway. */
	if (info_crc == data->info_crc) {
		if (config_crc == 0 || data->config_crc == 0) {
			dev_info(dev, "CRC zero, attempting to apply config\n");
		} else if (config_crc == data->config_crc) {
			dev_info(dev, "Config CRC 0x%06X: OK\n",
				 data->config_crc);
			ret = 0;
			goto release;
		} else {
			dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
				 data->config_crc, config_crc);
		}
	} else {
		dev_warn(dev,
			 "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
			data->info_crc, info_crc);
	}

	/* Malloc memory to store configuration */
	cfg_start_ofs = MXT_OBJECT_START
		+ data->info->object_num * sizeof(struct mxt_object)
@@ -1737,6 +1942,34 @@ static int mxt_check_reg_init(struct mxt_data *data)
		}
		data_pos += offset;

		if (type == MXT_SPT_USERDATA_T38) {
			ret = sscanf(cfg->data + data_pos, "%x %x %x",
			     &ver[0], &ver[1], &ver[2]);
			dev_info(dev, "controller version:%d.%d.%d file version:%d.%d.%d",
				data->cfg_version[0], data->cfg_version[1],
				data->cfg_version[2], ver[0], ver[1], ver[2]);

			if (force || data->fw_w_no_cfg_update) {
				dev_info(dev, "starting force cfg update\n");
			} else if (data->cfg_version[0] != ver[0]) {
				dev_info(dev, "cfg major versions do not match\n");
				ret = -EINVAL;
				goto release_mem;
			} else if (data->cfg_version[1] > ver[1]) {
				dev_info(dev, "configuration is up-to-date\n");
				ret = -EINVAL;
				goto release_mem;
			} else if (data->cfg_version[1] == ver[1]) {
				if (data->cfg_version[2] >= ver[2]) {
					dev_info(dev, "configuration is up-to-date\n");
					ret = -EINVAL;
					goto release_mem;
				}
			} else {
				dev_info(dev, "starting cfg update\n");
			}
		}

		object = mxt_get_object(data, type);
		if (!object) {
			/* Skip object */
@@ -1853,10 +2086,28 @@ static int mxt_check_reg_init(struct mxt_data *data)
	/* T7 config may have changed */
	mxt_init_t7_power_cfg(data);

	mxt_update_cfg_version(data);

	/* update resolution if needed */
	if (data->T9_reportid_min) {
		ret = mxt_update_t9_resolution(data);
		if (ret)
			goto release_mem;
	} else if (data->T100_reportid_min) {
		ret = mxt_update_t100_resolution(data);
		if (ret)
			goto release_mem;
	} else {
		dev_warn(dev, "No touch object detected\n");
	}

release_mem:
	kfree(config_mem);
release:
	release_firmware(cfg);
report_enable:
	data->enable_reporting = true;

	return ret;
}

@@ -2149,7 +2400,7 @@ static int mxt_read_info_block(struct mxt_data *data)
			"Info Block CRC error calculated=0x%06X read=0x%06X\n",
			data->info_crc, calculated_crc);
		if (!data->pdata->ignore_crc)
			return -EIO;
			goto err_free_mem;
	}

	/* Save pointers in device data structure */
@@ -2157,73 +2408,33 @@ static int mxt_read_info_block(struct mxt_data *data)
	data->info = (struct mxt_info *)buf;
	data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);

	dev_info(&client->dev,
		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
		 data->info->family_id, data->info->variant_id,
		 data->info->version >> 4, data->info->version & 0xf,
		 data->info->build, data->info->object_num);

	/* Parse object table information */
	error = mxt_parse_object_table(data);
	if (error) {
		dev_err(&client->dev, "Error %d reading object table\n", error);
		mxt_free_object_table(data);
		return error;
	}

	return 0;

err_free_mem:
	kfree(buf);
	return error;
		goto err_free_obj_table;
	}

static int mxt_read_t9_resolution(struct mxt_data *data)
{
	struct i2c_client *client = data->client;
	int error;
	struct t9_range range;
	unsigned char orient;
	struct mxt_object *object;

	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
	if (!object)
		return -EINVAL;

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T9_RANGE,
			       sizeof(range), &range);
	error = mxt_update_cfg_version(data);
	if (error)
		return error;

	le16_to_cpus(range.x);
	le16_to_cpus(range.y);

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T9_ORIENT,
				1, &orient);
	if (error)
		return error;

	/* Handle default values */
	if (range.x == 0)
		range.x = 1023;

	if (range.y == 0)
		range.y = 1023;

	if (orient & MXT_T9_ORIENT_SWITCH) {
		data->max_x = range.y;
		data->max_y = range.x;
	} else {
		data->max_x = range.x;
		data->max_y = range.y;
	}
		goto err_free_obj_table;

	dev_info(&client->dev,
		 "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u cfg version: %d.%d.%d\n",
		 data->info->family_id, data->info->variant_id,
		 data->info->version >> 4, data->info->version & 0xf,
		 data->info->build, data->info->object_num,
		 data->cfg_version[0], data->cfg_version[1],
		data->cfg_version[2]);


	return 0;

err_free_obj_table:
	mxt_free_object_table(data);
err_free_mem:
	kfree(buf);
	return error;
}

static int mxt_pinctrl_init(struct mxt_data *data)
@@ -2398,7 +2609,6 @@ static int mxt_regulator_enable(struct mxt_data *data)
{
	int error;


	if (!data->use_regulator)
		return 0;

@@ -2526,83 +2736,6 @@ fail_put_vdd:
	return error;
}

static int mxt_read_t100_config(struct mxt_data *data)
{
	struct i2c_client *client = data->client;
	int error;
	struct mxt_object *object;
	u16 range_x, range_y;
	u8 cfg, tchaux;
	u8 aux;

	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
	if (!object)
		return -EINVAL;

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T100_XRANGE,
			       sizeof(range_x), &range_x);
	if (error)
		return error;

	le16_to_cpus(range_x);

	error = __mxt_read_reg(client,
			       object->start_address + MXT_T100_YRANGE,
			       sizeof(range_y), &range_y);
	if (error)
		return error;

	le16_to_cpus(range_y);

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T100_CFG1,
				1, &cfg);
	if (error)
		return error;

	error =  __mxt_read_reg(client,
				object->start_address + MXT_T100_TCHAUX,
				1, &tchaux);
	if (error)
		return error;

	/* Handle default values */
	if (range_x == 0)
		range_x = 1023;

	/* Handle default values */
	if (range_x == 0)
		range_x = 1023;

	if (range_y == 0)
		range_y = 1023;

	if (cfg & MXT_T100_CFG_SWITCHXY) {
		data->max_x = range_y;
		data->max_y = range_x;
	} else {
		data->max_x = range_x;
		data->max_y = range_y;
	}

	/* allocate aux bytes */
	aux = 6;

	if (tchaux & MXT_T100_TCHAUX_VECT)
		data->t100_aux_vect = aux++;

	if (tchaux & MXT_T100_TCHAUX_AMPL)
		data->t100_aux_ampl = aux++;

	if (tchaux & MXT_T100_TCHAUX_AREA)
		data->t100_aux_area = aux++;

	dev_info(&client->dev,
		 "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);

	return 0;
}

#if defined(CONFIG_SECURE_TOUCH)
static void mxt_secure_touch_stop(struct mxt_data *data, int blocking)
@@ -2703,99 +2836,29 @@ static void mxt_input_close(struct input_dev *dev)
	mxt_stop(data);
}

static int mxt_initialize_t100_input_device(struct mxt_data *data)
static int mxt_create_input_dev(struct mxt_data *data)
{
	struct device *dev = &data->client->dev;
	struct input_dev *input_dev;
	int error;
	unsigned int num_mt_slots;
	int i;

	error = mxt_read_t100_config(data);
	if (error)
		dev_warn(dev, "Failed to initialize T100 resolution\n");

	input_dev = input_allocate_device();
	if (!data || !input_dev) {
		dev_err(dev, "Failed to allocate memory\n");
		return -ENOMEM;
	}

	input_dev->name = "atmel_mxt_ts T100 touchscreen";

	input_dev->phys = data->phys;
	input_dev->id.bustype = BUS_I2C;
	input_dev->dev.parent = &data->client->dev;
	input_dev->open = mxt_input_open;
	input_dev->close = mxt_input_close;

	__set_bit(EV_ABS, input_dev->evbit);
	__set_bit(EV_KEY, input_dev->evbit);
	__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,
			     0, data->max_x, 0, 0);
	input_set_abs_params(input_dev, ABS_Y,
			     0, data->max_y, 0, 0);

	if (data->t100_aux_ampl)
		input_set_abs_params(input_dev, ABS_PRESSURE,
				     0, 255, 0, 0);

	/* For multi touch */
	error = input_mt_init_slots(input_dev, data->num_touchids, 0);
	if (data->T9_reportid_min) {
		error = mxt_update_t9_resolution(data);
		if (error) {
		dev_err(dev, "Error %d initialising slots\n", error);
		goto err_free_mem;
			dev_err(dev, "update resolution failed\n");
			return error;
		}

	input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
			     0, data->max_x, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
			     0, data->max_y, 0, 0);

	if (data->t100_aux_area)
		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
				     0, MXT_MAX_AREA, 0, 0);

	if (data->t100_aux_ampl)
		input_set_abs_params(input_dev, ABS_MT_PRESSURE,
				     0, 255, 0, 0);

	if (data->t100_aux_vect)
		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
				     0, 255, 0, 0);

	input_set_drvdata(input_dev, data);

	error = input_register_device(input_dev);
	} else if (data->T100_reportid_min) {
		error = mxt_update_t100_resolution(data);
		if (error) {
		dev_err(dev, "Error %d registering input device\n", error);
		goto err_free_mem;
	}

	data->input_dev = input_dev;

	return 0;

err_free_mem:
	input_free_device(input_dev);
			dev_err(dev, "update resolution failed\n");
			return error;
		}

static int mxt_initialize_t9_input_device(struct mxt_data *data)
{
	struct device *dev = &data->client->dev;
	const struct mxt_platform_data *pdata = data->pdata;
	struct input_dev *input_dev;
	int error;
	unsigned int num_mt_slots;
	int i;

	error = mxt_read_t9_resolution(data);
	if (error)
		dev_warn(dev, "Failed to initialize T9 resolution\n");
	} else {
		dev_warn(dev, "No touch object detected\n");
	}

	input_dev = input_allocate_device();
	if (!input_dev) {
@@ -2815,40 +2878,6 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
	__set_bit(BTN_TOUCH, input_dev->keybit);
	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);

	if (pdata->t19_num_keys) {
		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);

		for (i = 0; i < pdata->t19_num_keys; i++)
			if (pdata->t19_keymap[i] != KEY_RESERVED)
				input_set_capability(input_dev, EV_KEY,
						     pdata->t19_keymap[i]);

		__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
		__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
		__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
		__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);

		input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
		input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
		input_abs_set_res(input_dev, ABS_MT_POSITION_X,
				  MXT_PIXELS_PER_MM);
		input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
				  MXT_PIXELS_PER_MM);

		input_dev->name = "Atmel maXTouch Touchpad";
	}

	/* 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 */
	num_mt_slots = data->num_touchids + data->num_stylusids;
	error = input_mt_init_slots(input_dev, num_mt_slots, 0);
	if (error) {
@@ -2913,24 +2942,9 @@ static int mxt_configure_objects(struct mxt_data *data)
		return error;

	error = mxt_init_t7_power_cfg(data);
	if (error) {
		dev_err(&client->dev, "Failed to initialize power cfg\n");
		return error;
	}

	if (data->T9_reportid_min) {
		error = mxt_initialize_t9_input_device(data);
	if (error)
			return error;
	} else if (data->T100_reportid_min) {
		error = mxt_initialize_t100_input_device(data);
		if (error)
			return error;
	} else {
		dev_warn(&client->dev, "No touch object detected\n");
	}
		dev_dbg(&client->dev, "Failed to initialize power cfg\n");

	data->enable_reporting = true;
	return 0;
}

@@ -2991,7 +3005,7 @@ static int mxt_search_fw_name(struct mxt_data *data)

	for_each_child_of_node(np, temp) {
		rc = of_property_read_u32(temp, "atmel,version", &temp_val);
		if (rc && (rc != -EINVAL)) {
		if (rc) {
			dev_err(dev, "Unable to read controller version\n");
			return rc;
		}
@@ -3000,7 +3014,7 @@ static int mxt_search_fw_name(struct mxt_data *data)
			continue;

		rc = of_property_read_u32(temp, "atmel,build", &temp_val);
		if (rc && (rc != -EINVAL)) {
		if (rc) {
			dev_err(dev, "Unable to read build id\n");
			return rc;
		}
@@ -3157,6 +3171,14 @@ retry_bootloader:
	if (error)
		return error;

	error = mxt_create_input_dev(data);
	if (error) {
		dev_err(&client->dev, "Failed to create input dev\n");
		return error;
	}

	data->enable_reporting = true;

	error = mxt_search_fw_name(data);
	if (error)
		dev_dbg(&client->dev, "firmware name search fail\n");
@@ -3183,6 +3205,16 @@ static ssize_t mxt_hw_version_show(struct device *dev,
			data->info->family_id, data->info->variant_id);
}

/* Hardware Version is returned as FamilyID.VariantID */
static ssize_t mxt_cfg_version_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	return scnprintf(buf, PAGE_SIZE, "%u.%u.%u\n",
			data->cfg_version[0],  data->cfg_version[1],
			data->cfg_version[2]);
}

static ssize_t mxt_show_instance(char *buf, int count,
				 struct mxt_object *object, int instance,
				 const u8 *val)
@@ -3399,6 +3431,11 @@ static ssize_t mxt_update_fw_store(struct device *dev,
	struct mxt_data *data = dev_get_drvdata(dev);
	int error;

	if (data->fw_name[0] == '\0') {
		dev_info(dev, "firmware is up-to-date\n");
		return 0;
	}

	error = mxt_load_fw(dev);
	if (error) {
		dev_err(dev, "The firmware update failed(%d)\n", error);
@@ -3412,20 +3449,19 @@ static ssize_t mxt_update_fw_store(struct device *dev,
		error = mxt_initialize(data);
		if (error)
			return error;

		data->fw_w_no_cfg_update = true;
	}

	return count;
}

static ssize_t mxt_update_cfg_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
static int mxt_update_cfg(struct mxt_data *data, bool force)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	int ret;

	if (data->in_bootloader) {
		dev_err(dev, "Not in appmode\n");
		dev_err(&data->client->dev, "Not in appmode\n");
		return -EINVAL;
	}

@@ -3442,8 +3478,38 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
		data->suspended = false;
	}

	/* Check register init values */
	ret = mxt_check_reg_init(data);
	/* load config */
	ret = mxt_load_cfg(data, force);
	if (ret)
		return ret;

	return 0;
}

static ssize_t mxt_update_cfg_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	int ret;

	/* update config */
	ret = mxt_update_cfg(data, false);
	if (ret)
		return ret;

	return count;
}

static ssize_t mxt_force_update_cfg_store(struct device *dev,
		struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	int ret;

	/* update force config */
	ret = mxt_update_cfg(data, true);
	if (ret)
		return ret;

@@ -3738,9 +3804,11 @@ static DEVICE_ATTR(cfg_name, S_IWUSR | S_IRUSR,
			mxt_cfg_name_show, mxt_cfg_name_store);
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
static DEVICE_ATTR(cfg_version, S_IRUGO, mxt_cfg_version_show, NULL);
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store);
static DEVICE_ATTR(force_update_cfg, S_IWUSR, NULL, mxt_force_update_cfg_store);
static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store);
static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);
static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show,
@@ -3751,9 +3819,11 @@ static struct attribute *mxt_attrs[] = {
	&dev_attr_cfg_name.attr,
	&dev_attr_fw_version.attr,
	&dev_attr_hw_version.attr,
	&dev_attr_cfg_version.attr,
	&dev_attr_object.attr,
	&dev_attr_update_fw.attr,
	&dev_attr_update_cfg.attr,
	&dev_attr_force_update_cfg.attr,
	&dev_attr_debug_enable.attr,
	&dev_attr_debug_v2_enable.attr,
	&dev_attr_debug_notify.attr,