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

Commit 828ec0ce authored by Mohan Pallaka's avatar Mohan Pallaka
Browse files

input: atmel_maxtouch_ts: fix firmware update routines



Fix firmware and configuration update routines to follow
device specification. Parse the firmware and configuration
file names from DT and provide interfaces to read them
from user space.

Change-Id: I9fa2a13a17702cd26d36987a6584adf398e962a8
Signed-off-by: default avatarMohan Pallaka <mpallaka@codeaurora.org>
parent c6297e1e
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ Required properties:
 - atmel,reset-gpio	: reset gpio

Optional property:
 - atmel,cfg-name	: Configuration file name with file extension as "raw".
			Default value maxtouch_generic_cfg.raw.
 - atmel,ignore-crc	: Boolean flag to ignore cyclic redundancy check (CRC) on
			configuration
 - xvdd-supply		: x-line drive voltage supply. It is used for charging
@@ -43,6 +45,13 @@ Optional property:
 - clock-names:		: clock names used for secure touch. The names are:
			"iface_clk", "core_clk".

Optional sub-child properties:
The sub-child properties are used to identify different firmware versions supported.
- atmel,version		: Firmware version of the controller
- atmel,build		: Firmware build of the controller
- atmel,fw-name		: Firmware file name for the next upgrade. Do not specify this
			when the firmware version is latest.

Example:
	i2c@f9966000 {
		atmel_maxtouch_ts@4a {
@@ -66,5 +75,11 @@ Example:
			clock-names = "iface_clk", "core_clk";
			clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
				 <&clock_gcc clk_gcc_blsp1_qup2_i2c_apps_clk>;
			atmel,cfg-name = "maxtouch_8994_liquid_cfg.raw";
			atmel,cfg_1 {
				atmel,version = <0x10>;
				atmel,build = <0xaa>;
				atmel,fw-name = "maxtouch_8994_liquid_v1_1_AB.fw";
			};
		};
	};
+279 −195
Original line number Diff line number Diff line
@@ -251,6 +251,9 @@ struct t9_range {
#define MXT_XVDD_VTG_MIN_UV	2700000
#define MXT_XVDD_VTG_MAX_UV	10000000

#define MXT_GEN_CFG	"maxtouch_generic_cfg.raw"
#define MXT_NAME_MAX_LEN 100

struct mxt_info {
	u8 family_id;
	u8 variant_id;
@@ -313,8 +316,8 @@ struct mxt_data {
	struct regulator *reg_vdd;
	struct regulator *reg_avdd;
	struct regulator *reg_xvdd;
	char *fw_name;
	char *cfg_name;
	char fw_name[MXT_NAME_MAX_LEN];
	char cfg_name[MXT_NAME_MAX_LEN];

#if defined(CONFIG_FB)
	struct notifier_block fb_notif;
@@ -532,11 +535,8 @@ static int mxt_debug_msg_init(struct mxt_data *data)
	data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX;

	if (sysfs_create_bin_file(&data->client->dev.kobj,
				  &data->debug_msg_attr) < 0) {
		dev_err(&data->client->dev, "Failed to create %s\n",
			data->debug_msg_attr.attr.name);
		return -EINVAL;
	}
				  &data->debug_msg_attr) < 0)
		dev_info(&data->client->dev, "Debugfs already exists\n");

	return 0;
}
@@ -2397,6 +2397,11 @@ err_free_irq:
static int mxt_regulator_enable(struct mxt_data *data)
{
	int error;


	if (!data->use_regulator)
		return 0;

	gpio_set_value(data->pdata->gpio_reset, 0);

	error = regulator_enable(data->reg_vdd);
@@ -2443,24 +2448,27 @@ static void mxt_regulator_disable(struct mxt_data *data)
		regulator_disable(data->reg_xvdd);
}

static int mxt_probe_regulators(struct mxt_data *data)
static int mxt_regulator_configure(struct mxt_data *data, bool state)
{
	struct device *dev = &data->client->dev;
	int error;
	int error = 0;

	/* According to maXTouch power sequencing specification, RESET line
	 * must be kept low until some time after regulators come up to
	 * voltage */
	if (!data->pdata->gpio_reset) {
		dev_warn(dev, "Must have reset GPIO to use regulator support\n");
		goto fail;
		return 0;
	}

	if (!state)
		goto deconfig;

	data->reg_vdd = regulator_get(dev, "vdd");
	if (IS_ERR(data->reg_vdd)) {
		error = PTR_ERR(data->reg_vdd);
		dev_err(dev, "Error %d getting vdd regulator\n", error);
		goto fail;
		return error;
	}

	if (regulator_count_voltages(data->reg_vdd) > 0) {
@@ -2469,7 +2477,7 @@ static int mxt_probe_regulators(struct mxt_data *data)
		if (error) {
			dev_err(&data->client->dev,
				"vdd set_vtg failed err=%d\n", error);
			goto fail;
			goto fail_put_vdd;
		}
	}

@@ -2477,7 +2485,7 @@ static int mxt_probe_regulators(struct mxt_data *data)
	if (IS_ERR(data->reg_avdd)) {
		error = PTR_ERR(data->reg_avdd);
		dev_err(dev, "Error %d getting avdd regulator\n", error);
		goto fail_release;
		goto fail_put_vdd;
	}

	if (regulator_count_voltages(data->reg_avdd) > 0) {
@@ -2486,7 +2494,7 @@ static int mxt_probe_regulators(struct mxt_data *data)
		if (error) {
			dev_err(&data->client->dev,
				"avdd set_vtg failed err=%d\n", error);
			goto fail_release;
			goto fail_put_avdd;
		}
	}

@@ -2504,26 +2512,17 @@ static int mxt_probe_regulators(struct mxt_data *data)
	}

	data->use_regulator = true;
	error = mxt_regulator_enable(data);
	if (error) {
		dev_err(dev, "Error %d enabling regulators\n", error);
		goto fail_release_all;
	}

	dev_dbg(dev, "Initialised regulators\n");
	return 0;

fail_release_all:
deconfig:
	if (!IS_ERR(data->reg_xvdd))
		regulator_put(data->reg_xvdd);
fail_put_avdd:
	regulator_put(data->reg_avdd);
fail_release:
fail_put_vdd:
	regulator_put(data->reg_vdd);
fail:
	data->reg_vdd = NULL;
	data->reg_avdd = NULL;
	data->reg_xvdd = NULL;
	data->use_regulator = false;
	return error;
}

@@ -2919,14 +2918,6 @@ static int mxt_configure_objects(struct mxt_data *data)
		return error;
	}

	/* Check register init values */
	error = mxt_check_reg_init(data);
	if (error) {
		dev_err(&client->dev, "Error %d initialising configuration\n",
			error);
		return error;
	}

	if (data->T9_reportid_min) {
		error = mxt_initialize_t9_input_device(data);
		if (error)
@@ -2943,6 +2934,176 @@ static int mxt_configure_objects(struct mxt_data *data)
	return 0;
}

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

	prop = of_find_property(np, name, NULL);
	if (!prop)
		return -EINVAL;
	if (!prop->value)
		return -ENODATA;

	coords_size = prop->length / sizeof(u32);
	if (coords_size != MXT_COORDS_ARR_SIZE) {
		dev_err(dev, "invalid %s\n", name);
		return -EINVAL;
	}

	error = of_property_read_u32_array(np, name, coords, coords_size);
	if (error && (error != -EINVAL)) {
		dev_err(dev, "Unable to read %s\n", name);
		return error;
	}

	if (strcmp(name, "atmel,panel-coords") == 0) {
		pdata->panel_minx = coords[0];
		pdata->panel_miny = coords[1];
		pdata->panel_maxx = coords[2];
		pdata->panel_maxy = coords[3];
	} else if (strcmp(name, "atmel,display-coords") == 0) {
		pdata->disp_minx = coords[0];
		pdata->disp_miny = coords[1];
		pdata->disp_maxx = coords[2];
		pdata->disp_maxy = coords[3];
	} else {
		dev_err(dev, "unsupported property %s\n", name);
		return -EINVAL;
	}

	return 0;
}

static int mxt_search_fw_name(struct mxt_data *data)
{
	struct device *dev = &data->client->dev;
	struct device_node *np = dev->of_node, *temp;
	u32 temp_val; size_t len;
	int rc;

	data->fw_name[0] = '\0';

	for_each_child_of_node(np, temp) {
		rc = of_property_read_u32(temp, "atmel,version", &temp_val);
		if (rc && (rc != -EINVAL)) {
			dev_err(dev, "Unable to read controller version\n");
			return rc;
		}

		if (temp_val != data->info->version)
			continue;

		rc = of_property_read_u32(temp, "atmel,build", &temp_val);
		if (rc && (rc != -EINVAL)) {
			dev_err(dev, "Unable to read build id\n");
			return rc;
		}

		if (temp_val != data->info->build)
			continue;

		rc = of_property_read_string(temp, "atmel,fw-name",
					&data->pdata->fw_name);
		if (rc && (rc != -EINVAL)) {
			dev_err(dev, "Unable to read fw name\n");
			return rc;
		}

		dev_dbg(dev, "fw name found(%s)\n", data->pdata->fw_name);

		if (data->pdata->fw_name) {
			len = strlen(data->pdata->fw_name);
			if (len > MXT_NAME_MAX_LEN - 1) {
				dev_err(dev, "Invalid firmware name\n");
				return -EINVAL;
			}
			strlcpy(data->fw_name, data->pdata->fw_name, len + 1);
		}
	}

	return 0;
}

static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
{
	int error;
	u32 temp_val;
	struct device_node *np = dev->of_node;
	struct property *prop;

	error = mxt_get_dt_coords(dev, "atmel,panel-coords", pdata);
	if (error)
		return error;

	error = mxt_get_dt_coords(dev, "atmel,display-coords", pdata);
	if (error)
		return error;

	pdata->cfg_name = MXT_GEN_CFG;
	error = of_property_read_string(np, "atmel,cfg-name", &pdata->cfg_name);
	if (error && (error != -EINVAL)) {
		dev_err(dev, "Unable to read cfg name\n");
		return error;
	}

	/* reset, irq gpio info */
	pdata->gpio_reset = of_get_named_gpio_flags(np, "atmel,reset-gpio",
				0, &temp_val);
	pdata->resetflags = temp_val;
	pdata->gpio_irq = of_get_named_gpio_flags(np, "atmel,irq-gpio",
				0, &temp_val);
	pdata->irqflags = temp_val;
	pdata->gpio_i2cmode = of_get_named_gpio_flags(np, "atmel,i2cmode-gpio",
				0, &temp_val);

	pdata->ignore_crc = of_property_read_bool(np, "atmel,ignore-crc");

	error = of_property_read_u32(np, "atmel,bl-addr", &temp_val);
	if (error && (error != -EINVAL))
		dev_err(dev, "Unable to read bootloader address\n");
	else if (error != -EINVAL)
		pdata->bl_addr = (u8) temp_val;

	/* keycodes for keyarray object */
	prop = of_find_property(np, "atmel,key-codes", NULL);
	if (prop) {
		pdata->key_codes = devm_kzalloc(dev,
				sizeof(int) * MXT_KEYARRAY_MAX_KEYS,
				GFP_KERNEL);
		if (!pdata->key_codes)
			return -ENOMEM;
		if ((prop->length/sizeof(u32)) == MXT_KEYARRAY_MAX_KEYS) {
			error = of_property_read_u32_array(np,
				"atmel,key-codes", pdata->key_codes,
				MXT_KEYARRAY_MAX_KEYS);
			if (error) {
				dev_err(dev, "Unable to read key codes\n");
				return error;
			}
		} else
			return -EINVAL;
	}

	return 0;
}
#else
static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
{
	return -ENODEV;
}

static int mxt_search_fw_name(struct mxt_data *data)
{

	return -ENODEV;
}
#endif

static int mxt_initialize(struct mxt_data *data)
{
@@ -2996,6 +3157,10 @@ retry_bootloader:
	if (error)
		return error;

	error = mxt_search_fw_name(data);
	if (error)
		dev_dbg(&client->dev, "firmware name search fail\n");

	return 0;
}

@@ -3227,35 +3392,6 @@ release_firmware:
	return ret;
}

static int mxt_update_file_name(struct device *dev, char **file_name,
				const char *buf, size_t count)
{
	char *file_name_tmp;

	/* Simple sanity check */
	if (count > 64) {
		dev_warn(dev, "File name too long\n");
		return -EINVAL;
	}

	file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL);
	if (!file_name_tmp) {
		dev_warn(dev, "no memory\n");
		return -ENOMEM;
	}

	*file_name = file_name_tmp;
	memcpy(*file_name, buf, count);

	/* Echo into the sysfs entry may append newline at the end of buf */
	if (buf[count - 1] == '\n')
		(*file_name)[count - 1] = '\0';
	else
		(*file_name)[count] = '\0';

	return 0;
}

static ssize_t mxt_update_fw_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
@@ -3263,10 +3399,6 @@ static ssize_t mxt_update_fw_store(struct device *dev,
	struct mxt_data *data = dev_get_drvdata(dev);
	int error;

	error = mxt_update_file_name(dev, &data->fw_name, buf, count);
	if (error)
		return error;

	error = mxt_load_fw(dev);
	if (error) {
		dev_err(dev, "The firmware update failed(%d)\n", error);
@@ -3274,6 +3406,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
	} else {
		dev_info(dev, "The firmware update succeeded\n");

		msleep(MXT_FW_RESET_TIME);
		data->suspended = false;

		error = mxt_initialize(data);
@@ -3296,17 +3429,12 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
		return -EINVAL;
	}

	ret = mxt_update_file_name(dev, &data->cfg_name, buf, count);
	if (ret)
		return ret;

	data->enable_reporting = false;
	mxt_free_input_device(data);

	if (data->suspended) {
		if (data->use_regulator)
			mxt_regulator_enable(data);
		else

		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);

		mxt_acquire_irq(data);
@@ -3314,13 +3442,12 @@ static ssize_t mxt_update_cfg_store(struct device *dev,
		data->suspended = false;
	}

	ret = mxt_configure_objects(data);
	/* Check register init values */
	ret = mxt_check_reg_init(data);
	if (ret)
		goto out;

	ret = count;
out:
		return ret;

	return count;
}

static ssize_t mxt_debug_enable_show(struct device *dev,
@@ -3559,6 +3686,56 @@ static DEVICE_ATTR(secure_touch_enable, S_IRUGO | S_IWUSR | S_IWGRP ,
static DEVICE_ATTR(secure_touch, S_IRUGO, mxt_secure_touch_show, NULL);
#endif

static ssize_t mxt_fw_name_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	return snprintf(buf, MXT_NAME_MAX_LEN - 1, "%s\n", data->fw_name);
}

static ssize_t mxt_fw_name_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t size)
{
	struct mxt_data *data = dev_get_drvdata(dev);

	if (size > MXT_NAME_MAX_LEN - 1)
		return -EINVAL;

	strlcpy(data->fw_name, buf, size);
	if (data->fw_name[size-1] == '\n')
		data->fw_name[size-1] = 0;

	return size;
}

static ssize_t mxt_cfg_name_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mxt_data *data = dev_get_drvdata(dev);
	return snprintf(buf, MXT_NAME_MAX_LEN - 1, "%s\n", data->cfg_name);
}

static ssize_t mxt_cfg_name_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t size)
{
	struct mxt_data *data = dev_get_drvdata(dev);

	if (size > MXT_NAME_MAX_LEN - 1)
		return -EINVAL;

	strlcpy(data->cfg_name, buf, size);
	if (data->cfg_name[size-1] == '\n')
		data->cfg_name[size-1] = 0;

	return size;
}

static DEVICE_ATTR(fw_name, S_IWUSR | S_IRUSR,
			mxt_fw_name_show, mxt_fw_name_store);
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(object, S_IRUGO, mxt_object_show, NULL);
@@ -3570,6 +3747,8 @@ static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show,
		   mxt_debug_enable_store);

static struct attribute *mxt_attrs[] = {
	&dev_attr_fw_name.attr,
	&dev_attr_cfg_name.attr,
	&dev_attr_fw_version.attr,
	&dev_attr_hw_version.attr,
	&dev_attr_object.attr,
@@ -3589,52 +3768,6 @@ static const struct attribute_group mxt_attr_group = {
	.attrs = mxt_attrs,
};

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

	prop = of_find_property(np, name, NULL);
	if (!prop)
		return -EINVAL;
	if (!prop->value)
		return -ENODATA;

	coords_size = prop->length / sizeof(u32);
	if (coords_size != MXT_COORDS_ARR_SIZE) {
		dev_err(dev, "invalid %s\n", name);
		return -EINVAL;
	}

	error = of_property_read_u32_array(np, name, coords, coords_size);
	if (error && (error != -EINVAL)) {
		dev_err(dev, "Unable to read %s\n", name);
		return error;
	}

	if (strcmp(name, "atmel,panel-coords") == 0) {
		pdata->panel_minx = coords[0];
		pdata->panel_miny = coords[1];
		pdata->panel_maxx = coords[2];
		pdata->panel_maxy = coords[3];
	} else if (strcmp(name, "atmel,display-coords") == 0) {
		pdata->disp_minx = coords[0];
		pdata->disp_miny = coords[1];
		pdata->disp_maxx = coords[2];
		pdata->disp_maxy = coords[3];
	} else {
		dev_err(dev, "unsupported property %s\n", name);
		return -EINVAL;
	}

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int mxt_suspend(struct device *dev)
{
@@ -3715,68 +3848,6 @@ static const struct dev_pm_ops mxt_pm_ops = {
};
#endif

static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
{
	int error;
	u32 temp_val;
	struct device_node *np = dev->of_node;
	struct property *prop;

	error = mxt_get_dt_coords(dev, "atmel,panel-coords", pdata);
	if (error)
		return error;

	error = mxt_get_dt_coords(dev, "atmel,display-coords", pdata);
	if (error)
		return error;

	/* reset, irq gpio info */
	pdata->gpio_reset = of_get_named_gpio_flags(np, "atmel,reset-gpio",
				0, &temp_val);
	pdata->resetflags = temp_val;
	pdata->gpio_irq = of_get_named_gpio_flags(np, "atmel,irq-gpio",
				0, &temp_val);
	pdata->irqflags = temp_val;
	pdata->gpio_i2cmode = of_get_named_gpio_flags(np, "atmel,i2cmode-gpio",
				0, &temp_val);

	pdata->ignore_crc = of_property_read_bool(np, "atmel,ignore-crc");

	error = of_property_read_u32(np, "atmel,bl-addr", &temp_val);
	if (error && (error != -EINVAL))
		dev_err(dev, "Unable to read bootloader address\n");
	else if (error != -EINVAL)
		pdata->bl_addr = (u8) temp_val;

	/* keycodes for keyarray object*/
	prop = of_find_property(np, "atmel,key-codes", NULL);
	if (prop) {
		pdata->key_codes = devm_kzalloc(dev,
				sizeof(int) * MXT_KEYARRAY_MAX_KEYS,
				GFP_KERNEL);
		if (!pdata->key_codes)
			return -ENOMEM;
		if ((prop->length/sizeof(u32)) == MXT_KEYARRAY_MAX_KEYS) {
			error = of_property_read_u32_array(np,
				"atmel,key-codes", pdata->key_codes,
				MXT_KEYARRAY_MAX_KEYS);
			if (error) {
				dev_err(dev, "Unable to read key codes\n");
				return error;
			}
		} else
			return -EINVAL;
	}

	return 0;
}
#else
static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
{
	return -ENODEV;
}
#endif

#if defined(CONFIG_SECURE_TOUCH)
static void mxt_secure_touch_init(struct mxt_data *data)
{
@@ -3818,7 +3889,7 @@ static int mxt_probe(struct i2c_client *client,
{
	struct mxt_data *data;
	struct mxt_platform_data *pdata;
	int error;
	int error, len;

	if (client->dev.of_node) {
		pdata = devm_kzalloc(&client->dev,
@@ -3851,17 +3922,21 @@ static int mxt_probe(struct i2c_client *client,
	data->pdata = pdata;
	i2c_set_clientdata(client, data);

	if (data->pdata->cfg_name)
		mxt_update_file_name(&data->client->dev,
				     &data->cfg_name,
				     data->pdata->cfg_name,
				     strlen(data->pdata->cfg_name));

	init_completion(&data->bl_completion);
	init_completion(&data->reset_completion);
	init_completion(&data->crc_completion);
	mutex_init(&data->debug_msg_lock);

	if (data->pdata->cfg_name) {
		len = strlen(data->pdata->cfg_name);
		if (len > MXT_NAME_MAX_LEN - 1) {
			dev_err(&client->dev, "Invalid config name\n");
			goto err_destroy_mutex;
		}

		strlcpy(data->cfg_name, data->pdata->cfg_name, len + 1);
	}

	error = mxt_pinctrl_init(data);
	if (error)
		dev_info(&client->dev, "No pinctrl support\n");
@@ -3874,12 +3949,18 @@ static int mxt_probe(struct i2c_client *client,
	data->irq = data->client->irq =
				gpio_to_irq(data->pdata->gpio_irq);

	error = mxt_probe_regulators(data);
	error = mxt_regulator_configure(data, true);
	if (error) {
		dev_err(&client->dev, "Failed to probe regulators\n");
		goto err_free_gpios;
	}

	error = mxt_regulator_enable(data);
	if (error) {
		dev_err(&client->dev, "Error %d enabling regulators\n", error);
		goto err_put_regs;
	}

	error = mxt_initialize(data);
	if (error)
		goto err_free_regs;
@@ -3941,6 +4022,8 @@ err_remove_sysfs_group:
	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
err_free_object:
	mxt_free_object_table(data);
err_put_regs:
	mxt_regulator_configure(data, false);
err_free_regs:
	mxt_regulator_disable(data);
err_free_gpios:
@@ -3965,6 +4048,7 @@ static int mxt_remove(struct i2c_client *client)
#endif
	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
	free_irq(data->irq, data);
	mxt_regulator_configure(data, false);
	mxt_regulator_disable(data);
	if (!IS_ERR(data->reg_xvdd))
		regulator_put(data->reg_xvdd);
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ struct mxt_platform_data {
	int t15_num_keys;
	const unsigned int *t15_keymap;
	const char *cfg_name;
	const char *fw_name;
	bool ignore_crc;

	const struct mxt_config_info *config_array;