Commit 50a77c65 authored by Nick Dyer's avatar Nick Dyer Committed by Dmitry Torokhov

Input: atmel_mxt_ts - download device config using firmware loader

The existing implementation which encodes the configuration as a binary
blob in platform data is unsatisfactory since it requires a kernel
recompile for the configuration to be changed, and it doesn't deal well
with firmware changes that move values around on the chip.

Atmel define an ASCII format for the configuration which can be exported
from their tools. This patch implements a parser for that format which
loads the configuration via the firmware loader and sends it to the MXT
chip.
Signed-off-by: default avatarNick Dyer <nick.dyer@itdev.co.uk>
Acked-by: default avatarBenson Leung <bleung@chromium.org>
Acked-by: default avatarYufeng Shen <miletus@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 78188be3
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Atmel maXTouch Touchscreen driver * Atmel maXTouch Touchscreen driver
* *
* Copyright (C) 2010 Samsung Electronics Co.Ltd * Copyright (C) 2010 Samsung Electronics Co.Ltd
* Copyright (C) 2011-2014 Atmel Corporation
* Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Google, Inc.
* *
* Author: Joonyoung Shim <jy0922.shim@samsung.com> * Author: Joonyoung Shim <jy0922.shim@samsung.com>
...@@ -30,8 +31,10 @@ ...@@ -30,8 +31,10 @@
#define MXT_VER_21 21 #define MXT_VER_21 21
#define MXT_VER_22 22 #define MXT_VER_22 22
/* Firmware */ /* Firmware files */
#define MXT_FW_NAME "maxtouch.fw" #define MXT_FW_NAME "maxtouch.fw"
#define MXT_CFG_NAME "maxtouch.cfg"
#define MXT_CFG_MAGIC "OBP_RAW V1"
/* Registers */ /* Registers */
#define MXT_INFO 0x00 #define MXT_INFO 0x00
...@@ -298,37 +301,6 @@ static bool mxt_object_readable(unsigned int type) ...@@ -298,37 +301,6 @@ static bool mxt_object_readable(unsigned int type)
} }
} }
static bool mxt_object_writable(unsigned int type)
{
switch (type) {
case MXT_GEN_COMMAND_T6:
case MXT_GEN_POWER_T7:
case MXT_GEN_ACQUIRE_T8:
case MXT_TOUCH_MULTI_T9:
case MXT_TOUCH_KEYARRAY_T15:
case MXT_TOUCH_PROXIMITY_T23:
case MXT_TOUCH_PROXKEY_T52:
case MXT_PROCI_GRIPFACE_T20:
case MXT_PROCG_NOISE_T22:
case MXT_PROCI_ONETOUCH_T24:
case MXT_PROCI_TWOTOUCH_T27:
case MXT_PROCI_GRIP_T40:
case MXT_PROCI_PALM_T41:
case MXT_PROCI_TOUCHSUPPRESSION_T42:
case MXT_PROCI_STYLUS_T47:
case MXT_PROCG_NOISESUPPRESSION_T48:
case MXT_SPT_COMMSCONFIG_T18:
case MXT_SPT_GPIOPWM_T19:
case MXT_SPT_SELFTEST_T25:
case MXT_SPT_CTECONFIG_T28:
case MXT_SPT_DIGITIZER_T43:
case MXT_SPT_CTECONFIG_T46:
return true;
default:
return false;
}
}
static void mxt_dump_message(struct device *dev, static void mxt_dump_message(struct device *dev,
struct mxt_message *message) struct mxt_message *message)
{ {
...@@ -606,7 +578,7 @@ mxt_get_object(struct mxt_data *data, u8 type) ...@@ -606,7 +578,7 @@ mxt_get_object(struct mxt_data *data, u8 type)
return object; return object;
} }
dev_err(&data->client->dev, "Invalid object type T%u\n", type); dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
return NULL; return NULL;
} }
...@@ -877,58 +849,197 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) ...@@ -877,58 +849,197 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
} }
static int mxt_check_reg_init(struct mxt_data *data) /*
* mxt_update_cfg - download configuration to chip
*
* Atmel Raw Config File Format
*
* The first four lines of the raw config file contain:
* 1) Version
* 2) Chip ID Information (first 7 bytes of device memory)
* 3) Chip Information Block 24-bit CRC Checksum
* 4) Chip Configuration 24-bit CRC Checksum
*
* The rest of the file consists of one line per object instance:
* <TYPE> <INSTANCE> <SIZE> <CONTENTS>
*
* <TYPE> - 2-byte object type as hex
* <INSTANCE> - 2-byte object instance number as hex
* <SIZE> - 2-byte object size as hex
* <CONTENTS> - array of <SIZE> 1-byte hex values
*/
static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
{ {
const struct mxt_platform_data *pdata = data->pdata;
struct mxt_object *object;
struct device *dev = &data->client->dev; struct device *dev = &data->client->dev;
int index = 0; struct mxt_info cfg_info;
int i, size; struct mxt_object *object;
int ret; int ret;
int offset;
int pos;
int i;
u32 info_crc, config_crc;
unsigned int type, instance, size;
u8 val;
u16 reg;
if (!pdata->config) { mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
dev_dbg(dev, "No cfg data defined, skipping reg init\n");
return 0; if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
dev_err(dev, "Unrecognised config file\n");
ret = -EINVAL;
goto release;
} }
mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); pos = strlen(MXT_CFG_MAGIC);
/* Load information block and check */
for (i = 0; i < sizeof(struct mxt_info); i++) {
ret = sscanf(cfg->data + pos, "%hhx%n",
(unsigned char *)&cfg_info + i,
&offset);
if (ret != 1) {
dev_err(dev, "Bad format\n");
ret = -EINVAL;
goto release;
}
if (data->config_crc == pdata->config_crc) { pos += offset;
dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); }
return 0;
if (cfg_info.family_id != data->info.family_id) {
dev_err(dev, "Family ID mismatch!\n");
ret = -EINVAL;
goto release;
} }
dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", if (cfg_info.variant_id != data->info.variant_id) {
data->config_crc, pdata->config_crc); dev_err(dev, "Variant ID mismatch!\n");
ret = -EINVAL;
goto release;
}
for (i = 0; i < data->info.object_num; i++) { if (cfg_info.version != data->info.version)
object = data->object_table + i; dev_err(dev, "Warning: version mismatch!\n");
if (!mxt_object_writable(object->type)) if (cfg_info.build != data->info.build)
dev_err(dev, "Warning: build num mismatch!\n");
ret = sscanf(cfg->data + pos, "%x%n", &info_crc, &offset);
if (ret != 1) {
dev_err(dev, "Bad format: failed to parse Info CRC\n");
ret = -EINVAL;
goto release;
}
pos += offset;
/* Check config CRC */
ret = sscanf(cfg->data + pos, "%x%n", &config_crc, &offset);
if (ret != 1) {
dev_err(dev, "Bad format: failed to parse Config CRC\n");
ret = -EINVAL;
goto release;
}
pos += offset;
if (data->config_crc == config_crc) {
dev_dbg(dev, "Config CRC 0x%06X: OK\n", config_crc);
ret = 0;
goto release;
}
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
data->config_crc, config_crc);
while (pos < cfg->size) {
/* Read type, instance, length */
ret = sscanf(cfg->data + pos, "%x %x %x%n",
&type, &instance, &size, &offset);
if (ret == 0) {
/* EOF */
ret = 1;
goto release;
} else if (ret != 3) {
dev_err(dev, "Bad format: failed to parse object\n");
ret = -EINVAL;
goto release;
}
pos += offset;
object = mxt_get_object(data, type);
if (!object) {
/* Skip object */
for (i = 0; i < size; i++) {
ret = sscanf(cfg->data + pos, "%hhx%n",
&val,
&offset);
pos += offset;
}
continue; continue;
}
size = mxt_obj_size(object) * mxt_obj_instances(object); if (size > mxt_obj_size(object)) {
if (index + size > pdata->config_length) { dev_err(dev, "Discarding %zu byte(s) in T%u\n",
dev_err(dev, "Not enough config data!\n"); size - mxt_obj_size(object), type);
return -EINVAL;
} }
ret = __mxt_write_reg(data->client, object->start_address, if (instance >= mxt_obj_instances(object)) {
size, &pdata->config[index]); dev_err(dev, "Object instances exceeded!\n");
if (ret) ret = -EINVAL;
return ret; goto release;
index += size; }
reg = object->start_address + mxt_obj_size(object) * instance;
for (i = 0; i < size; i++) {
ret = sscanf(cfg->data + pos, "%hhx%n",
&val,
&offset);
if (ret != 1) {
dev_err(dev, "Bad format in T%d\n", type);
ret = -EINVAL;
goto release;
}
pos += offset;
if (i > mxt_obj_size(object))
continue;
ret = mxt_write_reg(data->client, reg + i, val);
if (ret)
goto release;
}
/*
* If firmware is upgraded, new bytes may be added to end of
* objects. It is generally forward compatible to zero these
* bytes - previous behaviour will be retained. However
* this does invalidate the CRC and will force a config
* download every time until the configuration is updated.
*/
if (size < mxt_obj_size(object)) {
dev_info(dev, "Zeroing %zu byte(s) in T%d\n",
mxt_obj_size(object) - size, type);
for (i = size + 1; i < mxt_obj_size(object); i++) {
ret = mxt_write_reg(data->client, reg + i, 0);
if (ret)
goto release;
}
}
} }
mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
ret = mxt_soft_reset(data); ret = mxt_soft_reset(data);
if (ret) if (ret)
return ret; goto release;
dev_info(dev, "Config successfully updated\n"); dev_info(dev, "Config successfully updated\n");
return 0; release:
release_firmware(cfg);
return ret;
} }
static int mxt_make_highchg(struct mxt_data *data) static int mxt_make_highchg(struct mxt_data *data)
...@@ -1204,10 +1315,17 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) ...@@ -1204,10 +1315,17 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
return error; return error;
} }
static int mxt_configure_objects(struct mxt_data *data,
const struct firmware *cfg);
static void mxt_config_cb(const struct firmware *cfg, void *ctx)
{
mxt_configure_objects(ctx, cfg);
}
static int mxt_initialize(struct mxt_data *data) static int mxt_initialize(struct mxt_data *data)
{ {
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
struct mxt_info *info = &data->info;
int error; int error;
error = mxt_get_info(data); error = mxt_get_info(data);
...@@ -1225,28 +1343,40 @@ static int mxt_initialize(struct mxt_data *data) ...@@ -1225,28 +1343,40 @@ static int mxt_initialize(struct mxt_data *data)
if (error) if (error)
goto err_free_object_table; goto err_free_object_table;
/* Check register init values */ request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
error = mxt_check_reg_init(data); &data->client->dev, GFP_KERNEL, data,
if (error) { mxt_config_cb);
dev_err(&client->dev, "Error %d initializing configuration\n",
error); return 0;
goto err_free_object_table;
err_free_object_table:
mxt_free_object_table(data);
return error;
}
static int mxt_configure_objects(struct mxt_data *data,
const struct firmware *cfg)
{
struct device *dev = &data->client->dev;
struct mxt_info *info = &data->info;
int error;
if (cfg) {
error = mxt_update_cfg(data, cfg);
if (error)
dev_warn(dev, "Error %d updating config\n", error);
} }
error = mxt_initialize_t9_input_device(data); error = mxt_initialize_t9_input_device(data);
if (error) if (error)
goto err_free_object_table; return error;
dev_info(&client->dev, dev_info(dev,
"Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
info->family_id, info->variant_id, info->version >> 4, info->family_id, info->variant_id, info->version >> 4,
info->version & 0xf, info->build, info->object_num); info->version & 0xf, info->build, info->object_num);
return 0; return 0;
err_free_object_table:
mxt_free_object_table(data);
return error;
} }
/* Firmware Version is returned as Major.Minor.Build */ /* Firmware Version is returned as Major.Minor.Build */
......
...@@ -97,8 +97,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = { ...@@ -97,8 +97,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
.irqflags = IRQF_TRIGGER_FALLING, .irqflags = IRQF_TRIGGER_FALLING,
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
.t19_keymap = mxt_t19_keys, .t19_keymap = mxt_t19_keys,
.config = NULL,
.config_length = 0,
}; };
static struct i2c_board_info atmel_224s_tp_device = { static struct i2c_board_info atmel_224s_tp_device = {
...@@ -109,8 +107,6 @@ static struct i2c_board_info atmel_224s_tp_device = { ...@@ -109,8 +107,6 @@ static struct i2c_board_info atmel_224s_tp_device = {
static struct mxt_platform_data atmel_1664s_platform_data = { static struct mxt_platform_data atmel_1664s_platform_data = {
.irqflags = IRQF_TRIGGER_FALLING, .irqflags = IRQF_TRIGGER_FALLING,
.config = NULL,
.config_length = 0,
}; };
static struct i2c_board_info atmel_1664s_device = { static struct i2c_board_info atmel_1664s_device = {
......
...@@ -17,9 +17,6 @@ ...@@ -17,9 +17,6 @@
/* The platform data for the Atmel maXTouch touchscreen driver */ /* The platform data for the Atmel maXTouch touchscreen driver */
struct mxt_platform_data { struct mxt_platform_data {
const u8 *config;
size_t config_length;
u32 config_crc;
unsigned long irqflags; unsigned long irqflags;
u8 t19_num_keys; u8 t19_num_keys;
const unsigned int *t19_keymap; const unsigned int *t19_keymap;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment