From: Nick Dyer <hidden> Date: 2013-06-27 12:50:36
Some minor updates to these patches to address review comments. I've rebased
them on the latest dtor/next tree.
Input: atmel_mxt_ts - Verify Information Block checksum on probe
- Fix bug which might cause erroneous debug output due to use of krealloc (thanks to
Yufeng Shen for spotting this)
Input: atmel_mxt_ts - Implement T63 Active Stylus support
- Add T63 to #defines since there will be other active stylus objects
Input: atmel_mxt_ts - Implement debug output for messages
Input: atmel_mxt_ts - Add memory access interface via sysfs
- remove from series (move mem_size calculation)
--
Nick Dyer Senior Software Engineer
ITDev Hardware and Software Development Consultancy http://www.itdev.co.uk
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:40
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: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 234 ++++++++++++++++++++++--------
drivers/platform/x86/chromeos_laptop.c | 4 -
include/linux/i2c/atmel_mxt_ts.h | 4 -
3 files changed, 175 insertions(+), 67 deletions(-)
@@ -326,37 +328,6 @@ static bool mxt_object_readable(unsigned int type)}}-staticboolmxt_object_writable(unsignedinttype)-{-switch(type){-caseMXT_GEN_COMMAND_T6:-caseMXT_GEN_POWER_T7:-caseMXT_GEN_ACQUIRE_T8:-caseMXT_TOUCH_MULTI_T9:-caseMXT_TOUCH_KEYARRAY_T15:-caseMXT_TOUCH_PROXIMITY_T23:-caseMXT_TOUCH_PROXKEY_T52:-caseMXT_PROCI_GRIPFACE_T20:-caseMXT_PROCG_NOISE_T22:-caseMXT_PROCI_ONETOUCH_T24:-caseMXT_PROCI_TWOTOUCH_T27:-caseMXT_PROCI_GRIP_T40:-caseMXT_PROCI_PALM_T41:-caseMXT_PROCI_TOUCHSUPPRESSION_T42:-caseMXT_PROCI_STYLUS_T47:-caseMXT_PROCG_NOISESUPPRESSION_T48:-caseMXT_SPT_COMMSCONFIG_T18:-caseMXT_SPT_GPIOPWM_T19:-caseMXT_SPT_SELFTEST_T25:-caseMXT_SPT_CTECONFIG_T28:-caseMXT_SPT_DIGITIZER_T43:-caseMXT_SPT_CTECONFIG_T46:-returntrue;-default:-returnfalse;-}-}-staticvoidmxt_dump_message(structdevice*dev,structmxt_message*message){
@@ -550,7 +521,7 @@ mxt_get_object(struct mxt_data *data, u8 type)returnobject;}-dev_err(&data->client->dev,"Invalid object type T%u\n",type);+dev_warn(&data->client->dev,"Invalid object type T%u\n",type);returnNULL;}
@@ -797,58 +768,203 @@ 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_check_reg_init-downloadconfigurationtochip+*+*AtmelRawConfigFileFormat+*+*Thefirstfourlinesoftherawconfigfilecontain:+*1)Version+*2)ChipIDInformation(first7bytesofdevicememory)+*3)ChipInformationBlock24-bitCRCChecksum+*4)ChipConfiguration24-bitCRCChecksum+*+*Therestofthefileconsistsofonelineperobjectinstance:+*<TYPE><INSTANCE><SIZE><CONTENTS>+*+*<TYPE>-2-byteobjecttypeashex+*<INSTANCE>-2-byteobjectinstancenumberashex+*<SIZE>-2-byteobjectsizeashex+*<CONTENTS>-arrayof<SIZE>1-bytehexvalues+*/staticintmxt_check_reg_init(structmxt_data*data){-conststructmxt_platform_data*pdata=data->pdata;-structmxt_object*object;structdevice*dev=&data->client->dev;-intindex=0;-inti,size;+structmxt_infocfg_info;+structmxt_object*object;+conststructfirmware*cfg=NULL;intret;+intoffset;+intpos;+inti;+u32info_crc,config_crc;+unsignedinttype,instance,size;+u8val;+u16reg;-if(!pdata->config){-dev_dbg(dev,"No cfg data defined, skipping reg init\n");+ret=request_firmware(&cfg,MXT_CFG_NAME,dev);+if(ret<0){+dev_err(dev,"Failure to request config file %s\n",+MXT_CFG_NAME);return0;}mxt_update_crc(data,MXT_COMMAND_REPORTALL,1);-if(data->config_crc==pdata->config_crc){-dev_info(dev,"Config CRC 0x%06X: OK\n",data->config_crc);-return0;-}else{-dev_info(dev,"Config CRC 0x%06X: does not match 0x%06X\n",-data->config_crc,pdata->config_crc);+if(strncmp(cfg->data,MXT_CFG_MAGIC,strlen(MXT_CFG_MAGIC))){+dev_err(dev,"Unrecognised config file\n");+ret=-EINVAL;+gotorelease;}-for(i=0;i<data->info.object_num;i++){-object=data->object_table+i;+pos=strlen(MXT_CFG_MAGIC);++/* Load information block and check */+for(i=0;i<sizeof(structmxt_info);i++){+ret=sscanf(cfg->data+pos,"%hhx%n",+(unsignedchar*)&cfg_info+i,+&offset);+if(ret!=1){+dev_err(dev,"Bad format\n");+ret=-EINVAL;+gotorelease;+}++pos+=offset;+}++if(cfg_info.family_id!=data->info.family_id){+dev_err(dev,"Family ID mismatch!\n");+ret=-EINVAL;+gotorelease;+}-if(!mxt_object_writable(object->type))+if(cfg_info.variant_id!=data->info.variant_id){+dev_err(dev,"Variant ID mismatch!\n");+ret=-EINVAL;+gotorelease;+}++if(cfg_info.version!=data->info.version)+dev_err(dev,"Warning: version mismatch!\n");++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;+gotorelease;+}+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;+gotorelease;+}+pos+=offset;++if(data->config_crc==config_crc){+dev_info(dev,"Config CRC 0x%06X: OK\n",config_crc);+ret=0;+gotorelease;+}else{+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;+gotorelease;+}elseif(ret!=3){+dev_err(dev,"Bad format: failed to parse object\n");+ret=-EINVAL;+gotorelease;+}+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;+}-size=mxt_obj_size(object)*mxt_obj_instances(object);-if(index+size>pdata->config_length){-dev_err(dev,"Not enough config data!\n");-return-EINVAL;+if(size>mxt_obj_size(object)){+dev_err(dev,"Discarding %u byte(s) in T%u\n",+size-mxt_obj_size(object),type);}-ret=__mxt_write_reg(data->client,object->start_address,-size,&pdata->config[index]);-if(ret)-returnret;-index+=size;+if(instance>=mxt_obj_instances(object)){+dev_err(dev,"Object instances exceeded!\n");+ret=-EINVAL;+gotorelease;+}++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;+gotorelease;+}+pos+=offset;++if(i>mxt_obj_size(object))+continue;++ret=mxt_write_reg(data->client,reg+i,val);+if(ret)+gotorelease;++}++/* If firmware is upgraded, new bytes may be added to end of+*objects.Itisgenerallyforwardcompatibletozerothese+*bytes-previousbehaviourwillberetained.However+*thisdoesinvalidatetheCRCandwillforceaconfig+*downloadeverytimeuntiltheconfigurationisupdated*/+if(size<mxt_obj_size(object)){+dev_info(dev,"Zeroing %u 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)+gotorelease;+}+}}mxt_update_crc(data,MXT_COMMAND_BACKUPNV,MXT_BACKUP_VALUE);ret=mxt_soft_reset(data);if(ret)-returnret;+gotorelease;dev_info(dev,"Config written\n");-return0;+release:+release_firmware(cfg);+returnret;}staticintmxt_make_highchg(structmxt_data*data)
@@ -27,10 +27,6 @@/* The platform data for the Atmel maXTouch touchscreen driver */structmxt_platform_data{-constu8*config;-size_tconfig_length;-u32config_crc;-unsignedintx_size;unsignedinty_size;unsignedcharorient;
From: Martin Fuzzey <hidden> Date: 2013-09-18 17:08:26
On 27/06/13 14:48, Nick Dyer wrote:
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.
I am using the mxt-app tool (v1.13) from
git://github.com/atmel-maxtouch/obp-utils.git
However when an existing config is dumped using that tool's --save
option the
config CRC is always zero.
That means that with this patch the config is loaded every time unless the
dumped file is manually tweaked to set the correct CRC.
---
- dev_dbg(dev, "No cfg data defined, skipping reg init\n");
+ ret = request_firmware(&cfg, MXT_CFG_NAME, dev);
When building the driver into the kernel this just hangs for 60 seconds
then fails.
Wouldn't it be better to use request_firmware_nowait() ?
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:42
By validating the checksum, we can identify if the configuration is corrupt.
In addition, this patch writes the configuration in a short series of block
writes rather than as many individual values.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 224 +++++++++++++++++++++++-------
1 file changed, 171 insertions(+), 53 deletions(-)
@@ -768,6 +773,45 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)mxt_wait_for_completion(data,&data->crc_completion,MXT_CRC_TIMEOUT);}+staticvoidmxt_calc_crc24(u32*crc,u8firstbyte,u8secondbyte)+{+staticconstunsignedintcrcpoly=0x80001B;+u32result;+u32data_word;++data_word=(secondbyte<<8)|firstbyte;+result=((*crc<<1)^data_word);++if(result&0x1000000)+result^=crcpoly;++*crc=result;+}++staticu32mxt_calculate_crc(u8*base,off_tstart_off,off_tend_off)+{+u32crc=0;+u8*ptr=base+start_off;+u8*last_val=base+end_off-1;++if(end_off<start_off)+return-EINVAL;++while(ptr<last_val){+mxt_calc_crc24(&crc,*ptr,*(ptr+1));+ptr+=2;+}++/* if len is odd, fill the last byte with 0 */+if(ptr==last_val)+mxt_calc_crc24(&crc,*ptr,0);++/* Mask to 24-bit */+crc&=0x00FFFFFF;++returncrc;+}+/**mxt_check_reg_init-downloadconfigurationtochip*
@@ -795,9 +839,13 @@ static int mxt_check_reg_init(struct mxt_data *data)conststructfirmware*cfg=NULL;intret;intoffset;-intpos;+intdata_pos;+intbyte_offset;inti;-u32info_crc,config_crc;+intcfg_start_ofs;+u32info_crc,config_crc,calculated_crc;+u8*config_mem;+size_tconfig_mem_size;unsignedinttype,instance,size;u8val;u16reg;
@@ -817,11 +865,11 @@ static int mxt_check_reg_init(struct mxt_data *data)gotorelease;}-pos=strlen(MXT_CFG_MAGIC);+data_pos=strlen(MXT_CFG_MAGIC);/* Load information block and check */for(i=0;i<sizeof(structmxt_info);i++){-ret=sscanf(cfg->data+pos,"%hhx%n",+ret=sscanf(cfg->data+data_pos,"%hhx%n",(unsignedchar*)&cfg_info+i,&offset);if(ret!=1){
@@ -830,7 +878,7 @@ static int mxt_check_reg_init(struct mxt_data *data)gotorelease;}-pos+=offset;+data_pos+=offset;}if(cfg_info.family_id!=data->info.family_id){
@@ -845,123 +893,182 @@ static int mxt_check_reg_init(struct mxt_data *data)gotorelease;}-if(cfg_info.version!=data->info.version)-dev_err(dev,"Warning: version mismatch!\n");--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);+/* Read CRCs */+ret=sscanf(cfg->data+data_pos,"%x%n",&info_crc,&offset);if(ret!=1){dev_err(dev,"Bad format: failed to parse Info CRC\n");ret=-EINVAL;gotorelease;}-pos+=offset;+data_pos+=offset;-/* Check config CRC */-ret=sscanf(cfg->data+pos,"%x%n",&config_crc,&offset);+ret=sscanf(cfg->data+data_pos,"%x%n",&config_crc,&offset);if(ret!=1){dev_err(dev,"Bad format: failed to parse Config CRC\n");ret=-EINVAL;gotorelease;}-pos+=offset;+data_pos+=offset;++/* The Info Block CRC is calculated over mxt_info and the object table+*Ifitdoesnotmatchthenwearetryingtoloadtheconfiguration+*fromadifferentchiporfirmwareversion,sotheconfigurationCRC+*isinvalidanyway.*/+if(info_crc==data->info_crc){+if(config_crc==0||data->config_crc==0){+dev_info(dev,"CRC zero, attempting to apply config\n");+}elseif(config_crc==data->config_crc){+dev_info(dev,"Config CRC 0x%06X: OK\n",+data->config_crc);+ret=0;+gotorelease;+}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);+}-if(data->config_crc==config_crc){-dev_info(dev,"Config CRC 0x%06X: OK\n",config_crc);-ret=0;+/* Malloc memory to store configuration */+cfg_start_ofs=MXT_OBJECT_START++data->info.object_num*sizeof(structmxt_object)++MXT_INFO_CHECKSUM_SIZE;+config_mem_size=data->mem_size-cfg_start_ofs;+config_mem=kzalloc(config_mem_size,GFP_KERNEL);+if(!config_mem){+dev_err(dev,"Failed to allocate memory\n");+ret=-ENOMEM;gotorelease;-}else{-dev_info(dev,"Config CRC 0x%06X: does not match file 0x%06X\n",-data->config_crc,config_crc);}-while(pos<cfg->size){+while(data_pos<cfg->size){/* Read type, instance, length */-ret=sscanf(cfg->data+pos,"%x %x %x%n",+ret=sscanf(cfg->data+data_pos,"%x %x %x%n",&type,&instance,&size,&offset);if(ret==0){/* EOF */-ret=1;-gotorelease;+break;}elseif(ret!=3){dev_err(dev,"Bad format: failed to parse object\n");ret=-EINVAL;-gotorelease;+gotorelease_mem;}-pos+=offset;+data_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",+ret=sscanf(cfg->data+data_pos,"%hhx%n",&val,&offset);-pos+=offset;+data_pos+=offset;}continue;}if(size>mxt_obj_size(object)){-dev_err(dev,"Discarding %u byte(s) in T%u\n",-size-mxt_obj_size(object),type);+/* Either we are in fallback mode due to wrong+*configorconfigfromalaterfwversion,+*orthefileiscorruptorhand-edited*/+dev_warn(dev,"Discarding %u byte(s) in T%u\n",+size-mxt_obj_size(object),type);+}elseif(mxt_obj_size(object)>size){+/* If firmware is upgraded, new bytes may be added to+*endofobjects.Itisgenerallyforwardcompatible+*tozerothesebytes-previousbehaviourwillbe+*retained.HoweverthisdoesinvalidatetheCRCand+*willforcefallbackmodeuntiltheconfigurationis+*updated.Wewarnherebutdonothingelse-the+*mallochaszeroedtheentireconfiguration.*/+dev_warn(dev,"Zeroing %u byte(s) in T%d\n",+mxt_obj_size(object)-size,type);}if(instance>=mxt_obj_instances(object)){dev_err(dev,"Object instances exceeded!\n");ret=-EINVAL;-gotorelease;+gotorelease_mem;}reg=object->start_address+mxt_obj_size(object)*instance;for(i=0;i<size;i++){-ret=sscanf(cfg->data+pos,"%hhx%n",+ret=sscanf(cfg->data+data_pos,"%hhx%n",&val,&offset);if(ret!=1){dev_err(dev,"Bad format in T%d\n",type);ret=-EINVAL;-gotorelease;+gotorelease_mem;}-pos+=offset;+data_pos+=offset;if(i>mxt_obj_size(object))continue;-ret=mxt_write_reg(data->client,reg+i,val);-if(ret)-gotorelease;+byte_offset=reg+i-cfg_start_ofs;+if((byte_offset>=0)+&&(byte_offset<=config_mem_size)){+*(config_mem+byte_offset)=val;+}else{+dev_err(dev,"Bad object: reg:%d, T%d, ofs=%d\n",+reg,object->type,byte_offset);+ret=-EINVAL;+gotorelease_mem;+}}+}-/* If firmware is upgraded, new bytes may be added to end of-*objects.Itisgenerallyforwardcompatibletozerothese-*bytes-previousbehaviourwillberetained.However-*thisdoesinvalidatetheCRCandwillforceaconfig-*downloadeverytimeuntiltheconfigurationisupdated*/-if(size<mxt_obj_size(object)){-dev_info(dev,"Zeroing %u byte(s) in T%d\n",-mxt_obj_size(object)-size,type);+/* calculate crc of the received configs (not the raw config file) */+if(data->T7_address<cfg_start_ofs){+dev_err(dev,"Bad T7 address, T7addr = %x, config offset %x\n",+data->T7_address,cfg_start_ofs);+ret=0;+gotorelease_mem;+}-for(i=size+1;i<mxt_obj_size(object);i++){-ret=mxt_write_reg(data->client,reg+i,0);-if(ret)-gotorelease;-}+calculated_crc=mxt_calculate_crc(config_mem,+data->T7_address-cfg_start_ofs,+config_mem_size);++if(config_crc>0&&(config_crc!=calculated_crc))+dev_warn(dev,"Config CRC error, calculated=%06X, file=%06X\n",+calculated_crc,config_crc);++/* Write configuration as blocks */+byte_offset=0;+while(byte_offset<config_mem_size){+size=config_mem_size-byte_offset;++if(size>MXT_MAX_BLOCK_WRITE)+size=MXT_MAX_BLOCK_WRITE;++ret=__mxt_write_reg(data->client,+cfg_start_ofs+byte_offset,+size,config_mem+byte_offset);+if(ret!=0){+dev_err(dev,"Config write error, ret=%d\n",ret);+gotorelease_mem;}++byte_offset+=size;}mxt_update_crc(data,MXT_COMMAND_BACKUPNV,MXT_BACKUP_VALUE);ret=mxt_soft_reset(data);if(ret)-gotorelease;+gotorelease_mem;dev_info(dev,"Config written\n");+release_mem:+kfree(config_mem);release:release_firmware(cfg);returnret;
@@ -1023,6 +1130,7 @@ static int mxt_get_object_table(struct mxt_data *data)interror;inti;u8reportid;+u16end_address;table_size=data->info.object_num*sizeof(structmxt_object);error=__mxt_read_reg(client,MXT_OBJECT_START,table_size,
@@ -1032,6 +1140,7 @@ static int mxt_get_object_table(struct mxt_data *data)/* Valid Report IDs start counting from 1 */reportid=1;+data->mem_size=0;for(i=0;i<data->info.object_num;i++){structmxt_object*object=data->object_table+i;u8min_id,max_id;
@@ -1059,6 +1168,9 @@ static int mxt_get_object_table(struct mxt_data *data)data->T6_reportid=min_id;data->T6_address=object->start_address;break;+caseMXT_GEN_POWER_T7:+data->T7_address=object->start_address;+break;caseMXT_TOUCH_MULTI_T9:data->T9_reportid_min=min_id;data->T9_reportid_max=max_id;
@@ -1067,6 +1179,12 @@ static int mxt_get_object_table(struct mxt_data *data)data->T19_reportid=min_id;break;}++end_address=object->start_address++mxt_obj_size(object)*mxt_obj_instances(object)-1;++if(end_address>=data->mem_size)+data->mem_size=end_address+1;}return0;
From: Nick Dyer <hidden> Date: 2013-07-08 09:56:10
Dmitry Torokhov wrote:
On Thu, Jun 27, 2013 at 01:48:57PM +0100, Nick Dyer wrote:
quoted
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Why is this needed?
The patch disables the interrupt handler on shutdown.
One of our customers reported a bug caused by input events being generated
during shutdown (for example if the user was touching the device whilst it
was turning off), which was solved by putting in this change.
However, now you've drawn my attention to it again, it seems to me that
probably a better thing for us to be doing would be to power off the
touchscreen controller here, and let the interrupt disable be handled by
core code - do you agree?
On Mon, Jul 08, 2013 at 10:56:06AM +0100, Nick Dyer wrote:
Dmitry Torokhov wrote:
quoted
On Thu, Jun 27, 2013 at 01:48:57PM +0100, Nick Dyer wrote:
quoted
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
Why is this needed?
The patch disables the interrupt handler on shutdown.
One of our customers reported a bug caused by input events being generated
during shutdown (for example if the user was touching the device whilst it
was turning off), which was solved by putting in this change.
What kind of bug? Could you please be more precise?
However, now you've drawn my attention to it again, it seems to me that
probably a better thing for us to be doing would be to power off the
touchscreen controller here, and let the interrupt disable be handled by
core code - do you agree?
We'd be powering off everything in a moment anyway, no? Or is there a
concern that the device will stay powered up even if the system is in off
state?
Thanks.
--
Dmitry
From: Nick Dyer <hidden> Date: 2013-07-10 18:33:00
Dmitry Torokhov wrote:
quoted
The patch disables the interrupt handler on shutdown.
One of our customers reported a bug caused by input events being generated
during shutdown (for example if the user was touching the device whilst it
was turning off), which was solved by putting in this change.
What kind of bug? Could you please be more precise?
I did some more digging. Unfortunately the original problem report (some
time around March 2012) was in a customer ticket system that I no longer
have access to. It could well be trying to paper over a problem in their
app layer, to be honest.
quoted
However, now you've drawn my attention to it again, it seems to me that
probably a better thing for us to be doing would be to power off the
touchscreen controller here, and let the interrupt disable be handled by
core code - do you agree?
We'd be powering off everything in a moment anyway, no? Or is there a
concern that the device will stay powered up even if the system is in off
state?
I agree. I think it's best to not apply this patch for the moment, until we
can prove it's actually required.
Let me know if you want me to generate a new pull request.
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:46
By writing zero to both the active and idle cycle times the maXTouch device is
put into a deep sleep mode when it consumes minimal power. It is unnecessary
to change the configuration of any other objects (for example to disable T9
touchscreen).
It is counterproductive to reset the chip on resume, it will result in a long
delay. However it is necessary to issue a calibrate command after the chip
has spent any time in deep sleep.
This patch also deals with the situation where the power configuration is zero
on probe, which would mean that the device never wakes up to execute commands.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 100 ++++++++++++++++++++++--------
1 file changed, 74 insertions(+), 26 deletions(-)
@@ -604,20 +608,6 @@ static int mxt_read_message(struct mxt_data *data,sizeof(structmxt_message),message);}-staticintmxt_write_object(structmxt_data*data,-u8type,u8offset,u8val)-{-structmxt_object*object;-u16reg;--object=mxt_get_object(data,type);-if(!object||offset>=mxt_obj_size(object))-return-EINVAL;--reg=object->start_address;-returnmxt_write_reg(data->client,reg+offset,val);-}-staticvoidmxt_input_button(structmxt_data*data,structmxt_message*message){structinput_dev*input=data->input_dev;
@@ -1133,6 +1123,61 @@ release:returnret;}+staticintmxt_set_t7_power_cfg(structmxt_data*data,u8sleep)+{+structdevice*dev=&data->client->dev;+interror;+structt7_config*new_config;+structt7_configdeepsleep={.active=0,.idle=0};++if(sleep==MXT_POWER_CFG_DEEPSLEEP)+new_config=&deepsleep;+else+new_config=&data->t7_cfg;++error=__mxt_write_reg(data->client,data->T7_address,+sizeof(data->t7_cfg),+new_config);+if(error)+returnerror;++dev_dbg(dev,"Set T7 ACTV:%d IDLE:%d\n",+new_config->active,new_config->idle);++return0;+}++staticintmxt_init_t7_power_cfg(structmxt_data*data)+{+structdevice*dev=&data->client->dev;+interror;+boolretry=false;++recheck:+error=__mxt_read_reg(data->client,data->T7_address,+sizeof(data->t7_cfg),&data->t7_cfg);+if(error)+returnerror;++if(data->t7_cfg.active==0||data->t7_cfg.idle==0){+if(!retry){+dev_info(dev,"T7 cfg zero, resetting\n");+mxt_soft_reset(data);+retry=true;+gotorecheck;+}else{+dev_dbg(dev,"T7 cfg zero after reset, overriding\n");+data->t7_cfg.active=20;+data->t7_cfg.idle=100;+returnmxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);+}+}else{+dev_info(dev,"Initialised power cfg: ACTV %d, IDLE %d\n",+data->t7_cfg.active,data->t7_cfg.idle);+return0;+}+}+staticintmxt_make_highchg(structmxt_data*data){structdevice*dev=&data->client->dev;
@@ -1345,6 +1390,12 @@ static int mxt_initialize(struct mxt_data *data)gotoerr_free_object_table;}+error=mxt_init_t7_power_cfg(data);+if(error){+dev_err(&client->dev,"Failed to initialize power cfg\n");+gotoerr_free_object_table;+}+error=mxt_read_t9_resolution(data);if(error){dev_err(&client->dev,"Failed to initialize T9 resolution\n");
@@ -1612,16 +1663,15 @@ static const struct attribute_group mxt_attr_group = {staticvoidmxt_start(structmxt_data*data){-/* Touch enable */-mxt_write_object(data,-MXT_TOUCH_MULTI_T9,MXT_TOUCH_CTRL,0x83);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);++/* Recalibrate since chip has been in deep sleep */+mxt_t6_command(data,MXT_COMMAND_CALIBRATE,1,false);}staticvoidmxt_stop(structmxt_data*data){-/* Touch disable */-mxt_write_object(data,-MXT_TOUCH_MULTI_T9,MXT_TOUCH_CTRL,0);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_DEEPSLEEP);}staticintmxt_input_open(structinput_dev*dev)
@@ -1834,8 +1884,6 @@ static int mxt_resume(struct device *dev)structmxt_data*data=i2c_get_clientdata(client);structinput_dev*input_dev=data->input_dev;-mxt_soft_reset(data);-mutex_lock(&input_dev->mutex);if(input_dev->users)
@@ -637,7 +637,7 @@ static void mxt_input_touchevent(struct mxt_data *data,intx;inty;intarea;-intpressure;+intamplitude;/* do not report events if input device not yet registered */if(!data->enable_reporting)
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:50
This is in preparation for support of the T44 message count object.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 111 ++++++++++++++++--------------
1 file changed, 58 insertions(+), 53 deletions(-)
@@ -772,44 +760,61 @@ static void mxt_input_touchevent(struct mxt_data *data, u8 *message)/* Touch no longer active, close out slot */input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,0);}++data->update_input=true;}-staticboolmxt_is_T9_message(structmxt_data*data,u8*msg)+staticintmxt_proc_message(structmxt_data*data,u8*message){-u8id=msg[0];-return(id>=data->T9_reportid_min&&id<=data->T9_reportid_max);+u8report_id=message[0];++if(report_id==MXT_RPTID_NOMSG)+return0;++if(report_id==data->T6_reportid){+mxt_proc_t6_messages(data,message);+}elseif(report_id>=data->T9_reportid_min+&&report_id<=data->T9_reportid_max){+mxt_proc_t9_message(data,message);+}elseif(report_id==data->T19_reportid){+mxt_input_button(data,message);+data->update_input=true;+}else{+mxt_dump_message(data,message);+}++return1;}-staticirqreturn_tmxt_process_messages_until_invalid(structmxt_data*data)+staticintmxt_read_and_process_message(structmxt_data*data){-u8*message=&data->msg_buf[0];structdevice*dev=&data->client->dev;-u8reportid;-boolupdate_input=false;+intret;-do{-if(mxt_read_message(data,message)){-dev_err(dev,"Failed to read message\n");-returnIRQ_NONE;-}+ret=__mxt_read_reg(data->client,data->T5_address,+data->T5_msg_size,data->msg_buf);+if(ret){+dev_err(dev,"Error %d reading message\n",ret);+returnret;+}-reportid=message[0];+returnmxt_proc_message(data,data->msg_buf);+}-if(reportid==data->T6_reportid){-mxt_proc_t6_messages(data,message);-}elseif(mxt_is_T9_message(data,message)){-mxt_input_touchevent(data,message);-update_input=true;-}elseif(reportid==data->T19_reportid){-mxt_input_button(data,message);-update_input=true;-}else{-mxt_dump_message(data,message);-}-}while(reportid!=MXT_RPTID_NOMSG);+staticirqreturn_tmxt_process_messages_until_invalid(structmxt_data*data)+{+intret;++do{+ret=mxt_read_and_process_message(data);+if(ret<0)+returnIRQ_NONE;+}while(ret>0);-if(data->enable_reporting&&update_input)+if(data->enable_reporting&&data->update_input){mxt_input_sync(data->input_dev);+data->update_input=false;+}returnIRQ_HANDLED;}
@@ -1256,21 +1261,19 @@ static int mxt_make_highchg(struct mxt_data *data){structdevice*dev=&data->client->dev;intcount=10;-interror;+intret;-/* Read dummy message to make high CHG pin */+/* Read messages until we force an invalid */do{-error=mxt_read_message(data,data->msg_buf);-if(error)-returnerror;-}while(data->msg_buf[0]!=MXT_RPTID_NOMSG&&--count);--if(!count){-dev_err(dev,"CHG pin isn't cleared\n");-return-EBUSY;-}+ret=mxt_read_and_process_message(data);+if(ret==0)+return0;+elseif(ret<0)+returnret;+}while(--count);-return0;+dev_err(dev,"CHG pin isn't cleared\n");+return-EBUSY;}staticintmxt_acquire_irq(structmxt_data*data)
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:52
Some maXTouch chips (eg mXT1386) will not respond on the first I2C request
when they are in a sleep state. It must be retried after a delay for the chip
to wake up.
Signed-off-by: Nick Dyer <redacted>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 45 ++++++++++++++++++++----------
1 file changed, 30 insertions(+), 15 deletions(-)
@@ -274,6 +289,8 @@ struct mxt_data {u8T42_reportid_max;u16T44_address;u8T48_reportid;+u8T63_reportid_min;+u8T63_reportid_max;/* for fw update in bootloader */structcompletionbl_completion;
@@ -823,6 +840,63 @@ static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg)return0;}+staticvoidmxt_proc_t63_messages(structmxt_data*data,u8*msg)+{+structdevice*dev=&data->client->dev;+structinput_dev*input_dev=data->input_dev;+u8id;+u16x,y;+u8pressure;++/* do not report events if input device not yet registered */+if(!data->enable_reporting)+return;++/* stylus slots come after touch slots */+id=data->num_touchids+(msg[0]-data->T63_reportid_min);++if(id<0||id>(data->num_touchids+data->num_stylusids)){+dev_err(dev,"invalid stylus id %d, max slot is %d\n",+id,data->num_stylusids);+return;+}++x=msg[3]|(msg[4]<<8);+y=msg[5]|(msg[6]<<8);+pressure=msg[7]&MXT_T63_STYLUS_PRESSURE_MASK;++dev_dbg(dev,+"[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n",+id,+(msg[1]&MXT_T63_STYLUS_SUPPRESS)?'S':'.',+(msg[1]&MXT_T63_STYLUS_MOVE)?'M':'.',+(msg[1]&MXT_T63_STYLUS_RELEASE)?'R':'.',+(msg[1]&MXT_T63_STYLUS_PRESS)?'P':'.',+x,y,pressure,+(msg[2]&MXT_T63_STYLUS_BARREL)?'B':'.',+(msg[2]&MXT_T63_STYLUS_ERASER)?'E':'.',+(msg[2]&MXT_T63_STYLUS_TIP)?'T':'.',+(msg[2]&MXT_T63_STYLUS_DETECT)?'D':'.');++input_mt_slot(input_dev,id);++if(msg[2]&MXT_T63_STYLUS_DETECT){+input_mt_report_slot_state(input_dev,MT_TOOL_PEN,1);+input_report_abs(input_dev,ABS_MT_POSITION_X,x);+input_report_abs(input_dev,ABS_MT_POSITION_Y,y);+input_report_abs(input_dev,ABS_MT_PRESSURE,pressure);+}else{+input_mt_report_slot_state(input_dev,MT_TOOL_PEN,0);+}++input_report_key(input_dev,BTN_STYLUS,+(msg[2]&MXT_T63_STYLUS_ERASER));+input_report_key(input_dev,BTN_STYLUS2,+(msg[2]&MXT_T63_STYLUS_BARREL));++mxt_input_sync(input_dev);+}+staticintmxt_proc_message(structmxt_data*data,u8*message){u8report_id=message[0];
@@ -1563,6 +1642,12 @@ static int mxt_get_object_table(struct mxt_data *data)caseMXT_PROCG_NOISESUPPRESSION_T48:data->T48_reportid=min_id;break;+caseMXT_PROCI_ACTIVE_STYLUS_T63:+data->T63_reportid_min=min_id;+data->T63_reportid_max=max_id;+data->num_stylusids=object->num_report_ids+*mxt_obj_instances(object);+break;}end_address=object->start_address
@@ -2103,7 +2188,7 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)0,255,0,0);/* For multi touch */-num_mt_slots=data->T9_reportid_max-data->T9_reportid_min+1;+num_mt_slots=data->num_touchids+data->num_stylusids;error=input_mt_init_slots(input_dev,num_mt_slots,mt_flags);if(error){dev_err(dev,"Error %d initialising slots\n",error);
@@ -2121,6 +2206,14 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)input_set_abs_params(input_dev,ABS_MT_ORIENTATION,0,255,0,0);+/* For T63 active stylus */+if(data->T63_reportid_min){+input_set_capability(input_dev,EV_KEY,BTN_STYLUS);+input_set_capability(input_dev,EV_KEY,BTN_STYLUS2);+input_set_abs_params(input_dev,ABS_MT_TOOL_TYPE,+0,MT_TOOL_MAX,0,0);+}+input_set_drvdata(input_dev,data);error=input_register_device(input_dev);
@@ -274,6 +289,8 @@ struct mxt_data {u8T42_reportid_max;u16T44_address;u8T48_reportid;+u8T63_reportid_min;+u8T63_reportid_max;/* for fw update in bootloader */structcompletionbl_completion;
@@ -823,6 +840,63 @@ static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg)return0;}+staticvoidmxt_proc_t63_messages(structmxt_data*data,u8*msg)+{+structdevice*dev=&data->client->dev;+structinput_dev*input_dev=data->input_dev;+u8id;+u16x,y;+u8pressure;++/* do not report events if input device not yet registered */+if(!data->enable_reporting)+return;++/* stylus slots come after touch slots */+id=data->num_touchids+(msg[0]-data->T63_reportid_min);++if(id<0||id>(data->num_touchids+data->num_stylusids)){+dev_err(dev,"invalid stylus id %d, max slot is %d\n",+id,data->num_stylusids);+return;+}++x=msg[3]|(msg[4]<<8);+y=msg[5]|(msg[6]<<8);+pressure=msg[7]&MXT_T63_STYLUS_PRESSURE_MASK;++dev_dbg(dev,+"[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n",+id,+(msg[1]&MXT_T63_STYLUS_SUPPRESS)?'S':'.',+(msg[1]&MXT_T63_STYLUS_MOVE)?'M':'.',+(msg[1]&MXT_T63_STYLUS_RELEASE)?'R':'.',+(msg[1]&MXT_T63_STYLUS_PRESS)?'P':'.',+x,y,pressure,+(msg[2]&MXT_T63_STYLUS_BARREL)?'B':'.',+(msg[2]&MXT_T63_STYLUS_ERASER)?'E':'.',+(msg[2]&MXT_T63_STYLUS_TIP)?'T':'.',+(msg[2]&MXT_T63_STYLUS_DETECT)?'D':'.');++input_mt_slot(input_dev,id);++if(msg[2]&MXT_T63_STYLUS_DETECT){+input_mt_report_slot_state(input_dev,MT_TOOL_PEN,1);+input_report_abs(input_dev,ABS_MT_POSITION_X,x);+input_report_abs(input_dev,ABS_MT_POSITION_Y,y);+input_report_abs(input_dev,ABS_MT_PRESSURE,pressure);+}else{+input_mt_report_slot_state(input_dev,MT_TOOL_PEN,0);+}++input_report_key(input_dev,BTN_STYLUS,+(msg[2]&MXT_T63_STYLUS_ERASER));+input_report_key(input_dev,BTN_STYLUS2,+(msg[2]&MXT_T63_STYLUS_BARREL));++mxt_input_sync(input_dev);+}+staticintmxt_proc_message(structmxt_data*data,u8*message){u8report_id=message[0];
@@ -1563,6 +1642,12 @@ static int mxt_get_object_table(struct mxt_data *data)caseMXT_PROCG_NOISESUPPRESSION_T48:data->T48_reportid=min_id;break;+caseMXT_PROCI_ACTIVE_STYLUS_T63:+data->T63_reportid_min=min_id;+data->T63_reportid_max=max_id;+data->num_stylusids=object->num_report_ids+*mxt_obj_instances(object);+break;}end_address=object->start_address
@@ -2103,7 +2188,7 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)0,255,0,0);/* For multi touch */-num_mt_slots=data->T9_reportid_max-data->T9_reportid_min+1;+num_mt_slots=data->num_touchids+data->num_stylusids;error=input_mt_init_slots(input_dev,num_mt_slots,mt_flags);if(error){dev_err(dev,"Error %d initialising slots\n",error);
@@ -2121,6 +2206,14 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)input_set_abs_params(input_dev,ABS_MT_ORIENTATION,0,255,0,0);+/* For T63 active stylus */+if(data->T63_reportid_min){+input_set_capability(input_dev,EV_KEY,BTN_STYLUS);+input_set_capability(input_dev,EV_KEY,BTN_STYLUS2);+input_set_abs_params(input_dev,ABS_MT_TOOL_TYPE,+0,MT_TOOL_MAX,0,0);+}+input_set_drvdata(input_dev,data);error=input_register_device(input_dev);
--
1.7.10.4
Reviewed-by: Henrik Rydberg [off-list ref]
Thanks,
Henrik
@@ -741,8 +746,17 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)mxt_input_sync(input_dev);}+/* A reported size of zero indicates that the reported touch+*isastylusfromalinkedStylusT47object.*/+if(area==0){+area=MXT_TOUCH_MAJOR_T47_STYLUS;+tool=MT_TOOL_PEN;+}else{+tool=MT_TOOL_FINGER;+}+/* Touch active */-input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,1);+input_mt_report_slot_state(input_dev,tool,1);input_report_abs(input_dev,ABS_MT_POSITION_X,x);input_report_abs(input_dev,ABS_MT_POSITION_Y,y);input_report_abs(input_dev,ABS_MT_PRESSURE,amplitude);
@@ -696,6 +699,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) int area; int amplitude; u8 vector;+ int tool; /* do not report events if input device not yet registered */ if (!data->enable_reporting)
From: Nick Dyer <hidden> Date: 2013-06-27 12:50:58
On platforms which have multiple device instances using this driver, the
firmware may be different on each device. This patch makes the user give the
name of the firmware file when flashing.
This also prevents accidental triggering of the firmware load process.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 45 ++++++++++++++++++++++++++----
1 file changed, 39 insertions(+), 6 deletions(-)
@@ -2366,7 +2366,7 @@ static int mxt_check_firmware_format(struct device *dev,return-1;}-staticintmxt_load_fw(structdevice*dev,constchar*fn)+staticintmxt_load_fw(structdevice*dev){structmxt_data*data=dev_get_drvdata(dev);conststructfirmware*fw=NULL;
@@ -2376,9 +2376,9 @@ static int mxt_load_fw(struct device *dev, const char *fn)unsignedintframe=0;intret;-ret=request_firmware(&fw,fn,dev);+ret=request_firmware(&fw,data->fw_name,dev);if(ret){-dev_err(dev,"Unable to open firmware %s\n",fn);+dev_err(dev,"Unable to open firmware %s\n",data->fw_name);returnret;}
@@ -2493,6 +2493,35 @@ release_firmware:returnret;}+staticintmxt_update_file_name(structdevice*dev,char**file_name,+constchar*buf,size_tcount)+{+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';++return0;+}+staticssize_tmxt_update_fw_store(structdevice*dev,structdevice_attribute*attr,constchar*buf,size_tcount)
From: Nick Dyer <hidden> Date: 2013-06-27 12:51:00
The driver only registers one input device, which uses the screen parameters
from the first T9 instance. The first T63 instance also uses those parameters.
It is incorrect to send input reports from the second instances of these
objects if they are enabled: the input scaling will be wrong and the positions
will be mashed together.
This also causes problems on Android if the number of slots exceeds 32.
In the future, this could be handled by looking for enabled touch object
instances and creating an input device for each one.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
@@ -1770,10 +1770,11 @@ static int mxt_parse_object_table(struct mxt_data *data)data->T7_address=object->start_address;break;caseMXT_TOUCH_MULTI_T9:+/* Only handle messages from first T9 instance */data->T9_reportid_min=min_id;-data->T9_reportid_max=max_id;-data->num_touchids=object->num_report_ids-*mxt_obj_instances(object);+data->T9_reportid_max=min_id++object->num_report_ids-1;+data->num_touchids=object->num_report_ids;break;caseMXT_TOUCH_KEYARRAY_T15:data->T15_reportid_min=min_id;
@@ -1796,10 +1797,10 @@ static int mxt_parse_object_table(struct mxt_data *data)data->T48_reportid=min_id;break;caseMXT_PROCI_ACTIVE_STYLUS_T63:+/* Only handle messages from first T63 instance */data->T63_reportid_min=min_id;-data->T63_reportid_max=max_id;-data->num_stylusids=object->num_report_ids-*mxt_obj_instances(object);+data->T63_reportid_max=min_id;+data->num_stylusids=1;break;caseMXT_TOUCH_MULTITOUCHSCREEN_T100:data->T100_reportid_min=min_id;
From: Nick Dyer <hidden> Date: 2013-06-27 12:51:02
There may be multiple maXTouch chips on a single device which will require
different configuration files. Add a platform data value for the configuration
filename.
Add sysfs entry to write configuration file if the platform data is not set.
Split out the object initialisation code from mxt_initialize() into
mxt_configure_objects() to allow this.
Signed-off-by: Nick Dyer <redacted>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 104 ++++++++++++++++++++++++------
include/linux/i2c/atmel_mxt_ts.h | 1 +
2 files changed, 86 insertions(+), 19 deletions(-)
@@ -2224,16 +2235,28 @@ retry_bootloader:error=mxt_check_retrigen(data);if(error)-gotoerr_free_object_table;+returnerror;error=mxt_acquire_irq(data);if(error)-gotoerr_free_object_table;+returnerror;++error=mxt_configure_objects(data);+if(error)+returnerror;++return0;+}++staticintmxt_configure_objects(structmxt_data*data)+{+structi2c_client*client=data->client;+interror;error=mxt_init_t7_power_cfg(data);if(error){dev_err(&client->dev,"Failed to initialize power cfg\n");-gotoerr_free_object_table;+returnerror;}/* Check register init values */
@@ -2241,28 +2264,23 @@ retry_bootloader:if(error){dev_err(&client->dev,"Error %d initialising configuration\n",error);-gotoerr_free_object_table;+returnerror;}if(data->T9_reportid_min){error=mxt_initialize_t9_input_device(data);if(error)-gotoerr_free_object_table;+returnerror;}elseif(data->T100_reportid_min){error=mxt_initialize_t100_input_device(data);if(error)-gotoerr_free_object_table;+returnerror;}else{dev_warn(&client->dev,"No touch object detected\n");}data->enable_reporting=true;-return0;--err_free_object_table:-mxt_free_object_table(data);-returnerror;}/* Firmware Version is returned as Major.Minor.Build */
@@ -2644,8 +2703,15 @@ static int mxt_handle_pdata(struct mxt_data *data)data->pdata=dev_get_platdata(&data->client->dev);/* Use provided platform data if present */-if(data->pdata)+if(data->pdata){+if(data->pdata->cfg_name)+mxt_update_file_name(&data->client->dev,+&data->cfg_name,+data->pdata->cfg_name,+strlen(data->pdata->cfg_name));+return0;+}data->pdata=kzalloc(sizeof(*data->pdata),GFP_KERNEL);if(!data->pdata){
From: Nick Dyer <hidden> Date: 2013-06-27 12:51:48
The T100 object replaces the old T9 multitouch touchscreen object in new
chips.
Signed-off-by: Nick Dyer <redacted>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 299 ++++++++++++++++++++++++++++--
1 file changed, 288 insertions(+), 11 deletions(-)
@@ -244,6 +265,8 @@ struct mxt_data {u8T48_reportid;u8T63_reportid_min;u8T63_reportid_max;+u8T100_reportid_min;+u8T100_reportid_max;/* for fw update in bootloader */structcompletionbl_completion;
@@ -780,6 +803,79 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)data->update_input=true;}+staticvoidmxt_proc_t100_message(structmxt_data*data,u8*message)+{+structdevice*dev=&data->client->dev;+structinput_dev*input_dev=data->input_dev;+intid;+u8status;+intx;+inty;+inttool;++/* do not report events if input device not yet registered */+if(!data->enable_reporting)+return;++id=message[0]-data->T100_reportid_min-2;++/* ignore SCRSTATUS events */+if(id<0)+return;++status=message[1];+x=(message[3]<<8)|message[2];+y=(message[5]<<8)|message[4];++dev_dbg(dev,+"[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n",+id,+status,+x,y,+(data->t100_aux_area)?message[data->t100_aux_area]:0,+(data->t100_aux_ampl)?message[data->t100_aux_ampl]:0,+(data->t100_aux_vect)?message[data->t100_aux_vect]:0);++input_mt_slot(input_dev,id);++if(status&MXT_T100_DETECT){+/* A reported size of zero indicates that the reported touch+*isastylusfromalinkedStylusT47object.*/+if((status&MXT_T100_TYPE_MASK)==MXT_T100_TYPE_STYLUS)+tool=MT_TOOL_PEN;+else+tool=MT_TOOL_FINGER;++/* Touch active */+input_mt_report_slot_state(input_dev,tool,1);+input_report_abs(input_dev,ABS_MT_POSITION_X,x);+input_report_abs(input_dev,ABS_MT_POSITION_Y,y);++if(data->t100_aux_ampl)+input_report_abs(input_dev,ABS_MT_PRESSURE,+message[data->t100_aux_ampl]);++if(data->t100_aux_area){+if(tool==MT_TOOL_PEN)+input_report_abs(input_dev,ABS_MT_TOUCH_MAJOR,+MXT_TOUCH_MAJOR_T47_STYLUS);+else+input_report_abs(input_dev,ABS_MT_TOUCH_MAJOR,+message[data->t100_aux_area]);+}++if(data->t100_aux_vect)+input_report_abs(input_dev,ABS_MT_ORIENTATION,+message[data->t100_aux_vect]);+}else{+/* Touch no longer active, close out slot */+input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,0);+}++data->update_input=true;+}++staticvoidmxt_proc_t15_messages(structmxt_data*data,u8*msg){structinput_dev*input_dev=data->input_dev;
@@ -1684,6 +1791,12 @@ static int mxt_parse_object_table(struct mxt_data *data)data->num_stylusids=object->num_report_ids*mxt_obj_instances(object);break;+caseMXT_TOUCH_MULTITOUCHSCREEN_T100:+data->T100_reportid_min=min_id;+data->T100_reportid_max=max_id;+/* first two report IDs reserved */+data->num_touchids=object->num_report_ids-2;+break;}end_address=object->start_address
@@ -1907,6 +2020,168 @@ fail:data->use_regulator=false;}+staticintmxt_read_t100_config(structmxt_data*data)+{+structi2c_client*client=data->client;+interror;+structmxt_object*object;+u16range_x,range_y;+u8cfg,tchaux;+u8aux;++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)+returnerror;++le16_to_cpus(range_x);++error=__mxt_read_reg(client,+object->start_address+MXT_T100_YRANGE,+sizeof(range_y),&range_y);+if(error)+returnerror;++le16_to_cpus(range_y);++error=__mxt_read_reg(client,+object->start_address+MXT_T100_CFG1,+1,&cfg);+if(error)+returnerror;++error=__mxt_read_reg(client,+object->start_address+MXT_T100_TCHAUX,+1,&tchaux);+if(error)+returnerror;++/* 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);++return0;+}++staticintmxt_input_open(structinput_dev*dev);+staticvoidmxt_input_close(structinput_dev*dev);++staticintmxt_initialize_t100_input_device(structmxt_data*data)+{+structdevice*dev=&data->client->dev;+structinput_dev*input_dev;+interror;++error=mxt_read_t100_config(data);+if(error)+dev_warn(dev,"Failed to initialize T9 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);+input_set_capability(input_dev,EV_KEY,BTN_TOUCH);++/* 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(error){+dev_err(dev,"Error %d initialising slots\n",error);+gotoerr_free_mem;+}++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);+if(error){+dev_err(dev,"Error %d registering input device\n",error);+gotoerr_free_mem;+}++data->input_dev=input_dev;++return0;++err_free_mem:+input_free_device(input_dev);+returnerror;+}++staticintmxt_initialize_t9_input_device(structmxt_data*data);+staticintmxt_initialize(structmxt_data*data){structi2c_client*client=data->client;
From: Nick Dyer <hidden> Date: 2013-06-27 12:52:27
Allow the driver to optionally manage enabling/disable power to the touch
controller itself. If the regulators are not present then use the deep sleep
power mode instead.
For a correct power on sequence, it is required that we have control over the
RESET line.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 95 +++++++++++++++++++++++++++---
include/linux/i2c/atmel_mxt_ts.h | 1 +
2 files changed, 89 insertions(+), 7 deletions(-)
@@ -1840,6 +1847,66 @@ static int mxt_read_t9_resolution(struct mxt_data *data)return0;}+staticvoidmxt_regulator_enable(structmxt_data*data)+{+gpio_set_value(data->pdata->gpio_reset,0);++regulator_enable(data->reg_vdd);+regulator_enable(data->reg_avdd);+msleep(MXT_REGULATOR_DELAY);++INIT_COMPLETION(data->bl_completion);+gpio_set_value(data->pdata->gpio_reset,1);+mxt_wait_for_completion(data,&data->bl_completion,MXT_POWERON_DELAY);+}++staticvoidmxt_regulator_disable(structmxt_data*data)+{+regulator_disable(data->reg_vdd);+regulator_disable(data->reg_avdd);+}++staticvoidmxt_probe_regulators(structmxt_data*data)+{+structdevice*dev=&data->client->dev;+interror;++/* According to maXTouch power sequencing specification, RESET line+*mustbekeptlowuntilsometimeafterregulatorscomeupto+*voltage*/+if(!data->pdata->gpio_reset){+dev_warn(dev,"Must have reset GPIO to use regulator support\n");+gotofail;+}++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);+gotofail;+}++data->reg_avdd=regulator_get(dev,"avdd");+if(IS_ERR(data->reg_vdd)){+error=PTR_ERR(data->reg_vdd);+dev_err(dev,"Error %d getting avdd regulator\n",error);+gotofail_release;+}++data->use_regulator=true;+mxt_regulator_enable(data);++dev_dbg(dev,"Initialised regulators\n");+return;++fail_release:+regulator_put(data->reg_vdd);+fail:+data->reg_vdd=NULL;+data->reg_avdd=NULL;+data->use_regulator=false;+}+staticintmxt_initialize(structmxt_data*data){structi2c_client*client=data->client;
@@ -2198,14 +2268,18 @@ static void mxt_start(struct mxt_data *data)if(!data->suspended||data->in_bootloader)return;-/* Discard any touch messages still in message buffer from before chip-*wenttosleep*/-mxt_process_messages_until_invalid(data);+if(data->use_regulator){+mxt_regulator_enable(data);+}else{+/* Discard any messages still in message buffer from before+*chipwenttosleep*/+mxt_process_messages_until_invalid(data);-mxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);-/* Recalibrate since chip has been in deep sleep */-mxt_t6_command(data,MXT_COMMAND_CALIBRATE,1,false);+/* Recalibrate since chip has been in deep sleep */+mxt_t6_command(data,MXT_COMMAND_CALIBRATE,1,false);+}mxt_acquire_irq(data);data->enable_reporting=true;
From: Nick Dyer <hidden> Date: 2013-06-27 12:53:00
If the power configuration is zero then the configuration download may fail to
work properly, so initialize T7 before config download. The downloaded
configuration may reset the T7 power configuration so it must be
re-initialized afterwards.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
From: Nick Dyer <hidden> Date: 2013-06-27 12:53:39
If fingers are down as the MXT chip goes into suspend it does not send a lift
message. In addition, it may not complete its final measurement cycle
immediately, which means touch messages may be received by the interrupt
handler after mxt_stop() has completed.
So:
- disable irq during suspend
- flush any messages created after suspend
- tell app layer that slots were released at suspend
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
@@ -2162,17 +2172,53 @@ static const struct attribute_group mxt_attr_group = {.attrs=mxt_attrs,};+staticvoidmxt_reset_slots(structmxt_data*data)+{+structinput_dev*input_dev=data->input_dev;+unsignedintnum_mt_slots;+intid;++num_mt_slots=data->num_touchids+data->num_stylusids;++for(id=0;id<num_mt_slots;id++){+input_mt_slot(input_dev,id);+input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,0);+}++mxt_input_sync(input_dev);+}+staticvoidmxt_start(structmxt_data*data){+if(!data->suspended||data->in_bootloader)+return;++/* Discard any touch messages still in message buffer from before chip+*wenttosleep*/+mxt_process_messages_until_invalid(data);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);/* Recalibrate since chip has been in deep sleep */mxt_t6_command(data,MXT_COMMAND_CALIBRATE,1,false);++mxt_acquire_irq(data);+data->enable_reporting=true;+data->suspended=false;}staticvoidmxt_stop(structmxt_data*data){+if(data->suspended||data->in_bootloader)+return;++data->enable_reporting=false;+disable_irq(data->irq);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_DEEPSLEEP);++mxt_reset_slots(data);+data->suspended=true;}staticintmxt_input_open(structinput_dev*dev)
On Thu, Jun 27, 2013 at 01:49:20PM +0100, Nick Dyer wrote:
If fingers are down as the MXT chip goes into suspend it does not send a lift
message. In addition, it may not complete its final measurement cycle
immediately, which means touch messages may be received by the interrupt
handler after mxt_stop() has completed.
How long is the window of possible stray interrupts? Could this be
done with a small delay instead of keeping track of the suspend state?
quoted hunk
So:
- disable irq during suspend
- flush any messages created after suspend
- tell app layer that slots were released at suspend
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
@@ -2162,17 +2172,53 @@ static const struct attribute_group mxt_attr_group = {.attrs=mxt_attrs,};+staticvoidmxt_reset_slots(structmxt_data*data)+{+structinput_dev*input_dev=data->input_dev;+unsignedintnum_mt_slots;+intid;++num_mt_slots=data->num_touchids+data->num_stylusids;++for(id=0;id<num_mt_slots;id++){+input_mt_slot(input_dev,id);+input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,0);+}++mxt_input_sync(input_dev);+}+staticvoidmxt_start(structmxt_data*data){+if(!data->suspended||data->in_bootloader)+return;++/* Discard any touch messages still in message buffer from before chip+*wenttosleep*/+mxt_process_messages_until_invalid(data);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_RUN);/* Recalibrate since chip has been in deep sleep */mxt_t6_command(data,MXT_COMMAND_CALIBRATE,1,false);++mxt_acquire_irq(data);+data->enable_reporting=true;+data->suspended=false;}staticvoidmxt_stop(structmxt_data*data){+if(data->suspended||data->in_bootloader)+return;++data->enable_reporting=false;+disable_irq(data->irq);+mxt_set_t7_power_cfg(data,MXT_POWER_CFG_DEEPSLEEP);++mxt_reset_slots(data);+data->suspended=true;}staticintmxt_input_open(structinput_dev*dev)
--
1.7.10.4
Reviewed-by: Henrik Rydberg [off-list ref]
Thanks,
Henrik
From: Nick Dyer <hidden> Date: 2013-08-15 15:52:48
rydberg@euromail.se wrote:
On Thu, Jun 27, 2013 at 01:49:20PM +0100, Nick Dyer wrote:
quoted
If fingers are down as the MXT chip goes into suspend it does not send a lift
message. In addition, it may not complete its final measurement cycle
immediately, which means touch messages may be received by the interrupt
handler after mxt_stop() has completed.
How long is the window of possible stray interrupts? Could this be
done with a small delay instead of keeping track of the suspend state?
The touch controller has its own acquisition scheduling which switches
between active/idle modes. Which mode it is in isn't explicitly
communicated to the driver.
If we could tell it was in active mode (which would involve some hairier
code than this patch involves), we might have to wait ~20ms on a modern
device. But the worst case is that we would have to wait for the idle scan
interval plus some margin, so a couple of hundred ms. I think that is too
long, right?
From: Nick Dyer <hidden> Date: 2013-06-27 12:53:58
The workaround of reading all messages until an invalid is received is a way
of forcing the CHG line high, which means that when using edge-triggered
interrupts the interrupt can be acquired.
With level-triggered interrupts this is unnecessary.
Also, most recent maXTouch chips have a feature called RETRIGEN which, when
enabled, reasserts the interrupt line every cycle if there are messages
waiting. This also makes the workaround unnecessary.
Note: the RETRIGEN feature is only in some firmware versions/chips, it's not
valid simply to enable the bit.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 48 ++++++++++++++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)
From: Nick Dyer <hidden> Date: 2013-06-27 12:54:18
By reading the information block and the object table into a contiguous region
of memory, we can verify the checksum at probe time. This means we verify that
we are indeed talking to a chip that supports object protocol correctly. We
also detect I2C comms problems much earlier, resulting in easier diagnosis.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 182 ++++++++++++++++++------------
1 file changed, 112 insertions(+), 70 deletions(-)
@@ -363,12 +364,16 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry){u8appmode=data->client->addr;u8bootloader;+u8family_id=0;++if(data->info)+family_id=data->info->family_id;switch(appmode){case0x4a:case0x4b:/* Chips after 1664S use different scheme */-if(retry||data->info.family_id>=0xa2){+if(retry||family_id>=0xa2){bootloader=appmode-0x24;break;}
@@ -1245,13 +1250,13 @@ static int mxt_check_reg_init(struct mxt_data *data)data_pos+=offset;}-if(cfg_info.family_id!=data->info.family_id){+if(cfg_info.family_id!=data->info->family_id){dev_err(dev,"Family ID mismatch!\n");ret=-EINVAL;gotorelease;}-if(cfg_info.variant_id!=data->info.variant_id){+if(cfg_info.variant_id!=data->info->variant_id){dev_err(dev,"Variant ID mismatch!\n");ret=-EINVAL;gotorelease;
@@ -1298,7 +1303,7 @@ static int mxt_check_reg_init(struct mxt_data *data)/* Malloc memory to store configuration */cfg_start_ofs=MXT_OBJECT_START-+data->info.object_num*sizeof(structmxt_object)++data->info->object_num*sizeof(structmxt_object)+MXT_INFO_CHECKSUM_SIZE;config_mem_size=data->mem_size-cfg_start_ofs;config_mem=kzalloc(config_mem_size,GFP_KERNEL);
@@ -1506,24 +1511,12 @@ static int mxt_acquire_irq(struct mxt_data *data)return0;}-staticintmxt_get_info(structmxt_data*data)-{-structi2c_client*client=data->client;-structmxt_info*info=&data->info;-interror;--/* Read 7-byte info block starting at address 0 */-error=__mxt_read_reg(client,0,sizeof(*info),info);-if(error)-returnerror;--return0;-}-staticvoidmxt_free_object_table(structmxt_data*data){-kfree(data->object_table);+kfree(data->raw_info_block);data->object_table=NULL;+data->info=NULL;+data->raw_info_block=NULL;kfree(data->msg_buf);data->msg_buf=NULL;data->enable_reporting=false;
@@ -1587,7 +1572,7 @@ static int mxt_get_object_table(struct mxt_data *data)switch(object->type){caseMXT_GEN_MESSAGE_T5:-if(data->info.family_id==0x80){+if(data->info->family_id==0x80){/* On mXT224 read and discard unused CRC byte*otherwiseDMAreadsaremisaligned*/data->T5_msg_size=mxt_obj_size(object);
@@ -1647,22 +1632,103 @@ static int mxt_get_object_table(struct mxt_data *data)/* If T44 exists, T5 position has to be directly after */if(data->T44_address&&(data->T5_address!=data->T44_address+1)){dev_err(&client->dev,"Invalid T44 position\n");-error=-EINVAL;-gotofree_object_table;+return-EINVAL;}data->msg_buf=kcalloc(data->max_reportid,data->T5_msg_size,GFP_KERNEL);if(!data->msg_buf){dev_err(&client->dev,"Failed to allocate message buffer\n");+return-ENOMEM;+}++return0;+}++staticintmxt_read_info_block(structmxt_data*data)+{+structi2c_client*client=data->client;+interror;+size_tsize;+void*buf;+uint8_tnum_objects;+u32calculated_crc;+u8*crc_ptr;++/* If info block already allocated, free it */+if(data->raw_info_block!=NULL)+mxt_free_object_table(data);++/* Read 7-byte ID information block starting at address 0 */+size=sizeof(structmxt_info);+buf=kzalloc(size,GFP_KERNEL);+if(!buf){+dev_err(&client->dev,"Failed to allocate memory\n");+return-ENOMEM;+}++error=__mxt_read_reg(client,0,size,buf);+if(error)+gotoerr_free_mem;++/* Resize buffer to give space for rest of info block */+num_objects=((structmxt_info*)buf)->object_num;+size+=(num_objects*sizeof(structmxt_object))++MXT_INFO_CHECKSUM_SIZE;++buf=krealloc(buf,size,GFP_KERNEL);+if(!buf){+dev_err(&client->dev,"Failed to allocate memory\n");error=-ENOMEM;-gotofree_object_table;+gotoerr_free_mem;+}++/* Read rest of info block */+error=__mxt_read_reg(client,MXT_OBJECT_START,+size-MXT_OBJECT_START,+buf+MXT_OBJECT_START);+if(error)+gotoerr_free_mem;++/* Extract & calculate checksum */+crc_ptr=buf+size-MXT_INFO_CHECKSUM_SIZE;+data->info_crc=crc_ptr[0]|(crc_ptr[1]<<8)|(crc_ptr[2]<<16);++calculated_crc=mxt_calculate_crc(buf,0,+size-MXT_INFO_CHECKSUM_SIZE);++/* CRC mismatch can be caused by data corruption due to I2C comms+*issueorelsedeviceisnotusingObjectBasedProtocol*/+if((data->info_crc==0)||(data->info_crc!=calculated_crc)){+dev_err(&client->dev,+"Info Block CRC error calculated=0x%06X read=0x%06X\n",+data->info_crc,calculated_crc);+return-EIO;+}++/* Save pointers in device data structure */+data->raw_info_block=buf;+data->info=(structmxt_info*)buf;+data->object_table=(structmxt_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);+returnerror;}return0;-free_object_table:-mxt_free_object_table(data);+err_free_mem:+kfree(buf);returnerror;}
@@ -1717,13 +1783,12 @@ static int mxt_read_t9_resolution(struct mxt_data *data)staticintmxt_initialize(structmxt_data*data){structi2c_client*client=data->client;-structmxt_info*info=&data->info;interror;boolalt_bootloader_addr=false;boolretry=false;retry_info:-error=mxt_get_info(data);+error=mxt_read_info_block(data);if(error){retry_bootloader:error=mxt_probe_bootloader(data,alt_bootloader_addr);
@@ -1755,21 +1820,6 @@ retry_bootloader:}}-data->object_table=kcalloc(info->object_num,-sizeof(structmxt_object),-GFP_KERNEL);-if(!data->object_table){-dev_err(&client->dev,"Failed to allocate memory\n");-return-ENOMEM;-}--/* Get object table information */-error=mxt_get_object_table(data);-if(error){-dev_err(&client->dev,"Error %d reading object table\n",error);-gotoerr_free_object_table;-}-error=mxt_acquire_irq(data);if(error)gotoerr_free_object_table;
@@ -1813,9 +1852,9 @@ static ssize_t mxt_fw_version_show(struct device *dev,structdevice_attribute*attr,char*buf){structmxt_data*data=dev_get_drvdata(dev);-structmxt_info*info=&data->info;returnscnprintf(buf,PAGE_SIZE,"%u.%u.%02X\n",-info->version>>4,info->version&0xf,info->build);+data->info->version>>4,data->info->version&0xf,+data->info->build);}/* Hardware Version is returned as FamilyID.VariantID */
From: Nick Dyer <hidden> Date: 2013-06-27 12:54:40
Many of these values are out of date and they aren't used in the driver - all
they do is increase the size of the kernel source.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 66 +-----------------------------
1 file changed, 1 insertion(+), 65 deletions(-)
From: Nick Dyer <hidden> Date: 2013-06-27 12:55:29
There is a key array object in many maXTouch chips which allows some X/Y lines
to be used as a key array. This patch maps them to a series of keys which may
be configured in a platform data array.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 57 ++++++++++++++++++++++++++++++
include/linux/i2c/atmel_mxt_ts.h | 2 ++
2 files changed, 59 insertions(+)
From: Nick Dyer <hidden> Date: 2013-06-27 12:55:47
The atmel touch messages contain orientation information as a byte in a packed
format which can be passed straight on to Android if the input device
configuration is correct, see
http://source.android.com/tech/input/touch-devices.html#touchorientationcalibration
This requires vector reports to be enabled in maXTouch config (zero DISVECT
bit in T9 CTRL field)
Android converts the format in frameworks/base/services/input/Input.cpp,
search for ORIENTATION_CALIBRATION_VECTOR.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
On Thu, Jun 27, 2013 at 01:49:12PM +0100, Nick Dyer wrote:
The atmel touch messages contain orientation information as a byte in a packed
format which can be passed straight on to Android if the input device
configuration is correct, see
http://source.android.com/tech/input/touch-devices.html#touchorientationcalibration
This requires vector reports to be enabled in maXTouch config (zero DISVECT
bit in T9 CTRL field)
Android converts the format in frameworks/base/services/input/Input.cpp,
search for ORIENTATION_CALIBRATION_VECTOR.
How does this compare to the input mt documentation?
This requires vector reports to be enabled in maXTouch config (zero DISVECT
bit in T9 CTRL field)
Android converts the format in frameworks/base/services/input/Input.cpp,
search for ORIENTATION_CALIBRATION_VECTOR.
How does this compare to the input mt documentation?
http://lxr.free-electrons.com/source/Documentation/input/multi-touch-protocol.txt#L263
So yes, we don't meet the documented format. Options:
1. Leave out this patch entirely and support out of tree
2. Update multi-touch-protocol.txt to include the Atmel format
3. Convert in driver to match documented protocol. Presumably via a LUT of
the 256 possible values. Although this loses the confidence level that is
implied.
4. Getting the firmware changed is not a valid option I'm afraid (too many
devices already out there).
Which do you prefer (I suspect 3) ?
From: Nick Dyer <hidden> Date: 2013-06-27 12:56:55
maXTouch chips allow the reading of multiple messages in a single I2C
transaction. The number of messages available to be read is given by the value
in the T44 object which is located directly before the T5 object.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 190 +++++++++++++++++++++++++-----
1 file changed, 158 insertions(+), 32 deletions(-)
@@ -263,6 +266,7 @@ struct mxt_data {u8T9_reportid_min;u8T9_reportid_max;u8T19_reportid;+u16T44_address;/* for fw update in bootloader */structcompletionbl_completion;
@@ -786,30 +790,143 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)return1;}-staticintmxt_read_and_process_message(structmxt_data*data)+staticintmxt_read_and_process_messages(structmxt_data*data,u8count){structdevice*dev=&data->client->dev;intret;+inti;+u8num_valid=0;++/* Safety check for msg_buf */+if(count>data->max_reportid)+return-EINVAL;+/* Process remaining messages if necessary */ret=__mxt_read_reg(data->client,data->T5_address,-data->T5_msg_size,data->msg_buf);+data->T5_msg_size*count,data->msg_buf);if(ret){-dev_err(dev,"Error %d reading message\n",ret);+dev_err(dev,"Failed to read %u messages (%d)\n",count,ret);returnret;}-returnmxt_proc_message(data,data->msg_buf);+for(i=0;i<count;i++){+ret=mxt_proc_message(data,+data->msg_buf+data->T5_msg_size*i);++if(ret==1)+num_valid++;+}++/* return number of messages read */+returnnum_valid;}-staticirqreturn_tmxt_process_messages_until_invalid(structmxt_data*data)+staticirqreturn_tmxt_process_messages_t44(structmxt_data*data){+structdevice*dev=&data->client->dev;intret;+u8count,num_left;-do{-ret=mxt_read_and_process_message(data);+/* Read T44 and T5 together */+ret=__mxt_read_reg(data->client,data->T44_address,+data->T5_msg_size+1,data->msg_buf);+if(ret){+dev_err(dev,"Failed to read T44 and T5 (%d)\n",ret);+returnIRQ_NONE;+}++count=data->msg_buf[0];++if(count==0){+dev_warn(dev,"Interrupt triggered but zero messages\n");+returnIRQ_NONE;+}elseif(count>data->max_reportid){+dev_err(dev,"T44 count %d exceeded max report id\n",count);+count=data->max_reportid;+}++/* Process first message */+ret=mxt_proc_message(data,data->msg_buf+1);+if(ret<0){+dev_warn(dev,"Unexpected invalid message\n");+returnIRQ_NONE;+}++num_left=count-1;++/* Process remaining messages if necessary */+if(num_left){+ret=mxt_read_and_process_messages(data,num_left);if(ret<0)+gotoend;+elseif(ret!=num_left)+dev_warn(dev,"Unexpected invalid message\n");+}++end:+if(data->update_input){+mxt_input_sync(data->input_dev);+data->update_input=false;+}++returnIRQ_HANDLED;+}++staticintmxt_process_messages_until_invalid(structmxt_data*data)+{+structdevice*dev=&data->client->dev;+intcount,read;+u8tries=2;++count=data->max_reportid;++/* Read messages until we force an invalid */+do{+read=mxt_read_and_process_messages(data,count);+if(read<count)+return0;+}while(--tries);++if(data->update_input){+mxt_input_sync(data->input_dev);+data->update_input=false;+}++dev_err(dev,"CHG pin isn't cleared\n");+return-EBUSY;+}++staticirqreturn_tmxt_process_messages(structmxt_data*data)+{+inttotal_handled,num_handled;+u8count=data->last_message_count;++if(count<1||count>data->max_reportid)+count=1;++/* include final invalid message */+total_handled=mxt_read_and_process_messages(data,count+1);+if(total_handled<0)+returnIRQ_NONE;+/* if there were invalid messages, then we are done */+elseif(total_handled<=count)+gotoupdate_count;++/* read two at a time until an invalid message or else we reach+*reportidlimit*/+do{+num_handled=mxt_read_and_process_messages(data,2);+if(num_handled<0)returnIRQ_NONE;-}while(ret>0);++total_handled+=num_handled;++if(num_handled<2)+break;+}while(total_handled<data->num_touchids);++update_count:+data->last_message_count=total_handled;if(data->enable_reporting&&data->update_input){mxt_input_sync(data->input_dev);
@@ -1257,32 +1378,13 @@ recheck:}}-staticintmxt_make_highchg(structmxt_data*data)-{-structdevice*dev=&data->client->dev;-intcount=10;-intret;--/* Read messages until we force an invalid */-do{-ret=mxt_read_and_process_message(data);-if(ret==0)-return0;-elseif(ret<0)-returnret;-}while(--count);--dev_err(dev,"CHG pin isn't cleared\n");-return-EBUSY;-}-staticintmxt_acquire_irq(structmxt_data*data){interror;enable_irq(data->irq);-error=mxt_make_highchg(data);+error=mxt_process_messages_until_invalid(data);if(error)returnerror;
@@ -1361,8 +1465,14 @@ static int mxt_get_object_table(struct mxt_data *data)switch(object->type){caseMXT_GEN_MESSAGE_T5:-/* CRC not enabled, therefore don't read last byte */-data->T5_msg_size=mxt_obj_size(object)-1;+if(data->info.family_id==0x80){+/* On mXT224 read and discard unused CRC byte+*otherwiseDMAreadsaremisaligned*/+data->T5_msg_size=mxt_obj_size(object);+}else{+/* CRC not enabled, so skip last byte */+data->T5_msg_size=mxt_obj_size(object)-1;+}data->T5_address=object->start_address;caseMXT_GEN_COMMAND_T6:data->T6_reportid=min_id;
@@ -1374,6 +1484,11 @@ static int mxt_get_object_table(struct mxt_data *data)caseMXT_TOUCH_MULTI_T9:data->T9_reportid_min=min_id;data->T9_reportid_max=max_id;+data->num_touchids=object->num_report_ids+*mxt_obj_instances(object);+break;+caseMXT_SPT_MESSAGECOUNT_T44:+data->T44_address=object->start_address;break;caseMXT_SPT_GPIOPWM_T19:data->T19_reportid=min_id;
@@ -1387,7 +1502,18 @@ static int mxt_get_object_table(struct mxt_data *data)data->mem_size=end_address+1;}-data->msg_buf=kzalloc(data->T5_msg_size,GFP_KERNEL);+/* Store maximum reportid */+data->max_reportid=reportid;++/* If T44 exists, T5 position has to be directly after */+if(data->T44_address&&(data->T5_address!=data->T44_address+1)){+dev_err(&client->dev,"Invalid T44 position\n");+error=-EINVAL;+gotofree_object_table;+}++data->msg_buf=kcalloc(data->max_reportid,+data->T5_msg_size,GFP_KERNEL);if(!data->msg_buf){dev_err(&client->dev,"Failed to allocate message buffer\n");error=-ENOMEM;
From: Nick Dyer <hidden> Date: 2013-06-27 12:56:58
By storing the previous T6 status byte we can detect reset completion more
correctly, and multiple debug output of the same status can be suppressed (for
example CFGERR).
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 60 ++++++++++++++++++++----------
1 file changed, 40 insertions(+), 20 deletions(-)
@@ -629,6 +635,39 @@ mxt_get_object(struct mxt_data *data, u8 type)returnNULL;}+staticvoidmxt_proc_t6_messages(structmxt_data*data,u8*msg)+{+structdevice*dev=&data->client->dev;+u8status=msg[1];+u32crc=msg[2]|(msg[3]<<8)|(msg[4]<<16);++if(crc!=data->config_crc){+data->config_crc=crc;+dev_dbg(dev,"T6 Config Checksum: 0x%06X\n",crc);+complete(&data->crc_completion);+}++/* Detect transition out of reset */+if((data->t6_status&MXT_T6_STATUS_RESET)&&+!(status&MXT_T6_STATUS_RESET))+complete(&data->reset_completion);++/* Output debug if status has changed */+if(status!=data->t6_status)+dev_dbg(dev,"T6 Status 0x%02X%s%s%s%s%s%s%s\n",+status,+(status==0)?" OK":"",+(status&MXT_T6_STATUS_RESET)?" RESET":"",+(status&MXT_T6_STATUS_OFL)?" OFL":"",+(status&MXT_T6_STATUS_SIGERR)?" SIGERR":"",+(status&MXT_T6_STATUS_CAL)?" CAL":"",+(status&MXT_T6_STATUS_CFGERR)?" CFGERR":"",+(status&MXT_T6_STATUS_COMSERR)?" COMSERR":"");++/* Save current status */+data->t6_status=status;+}+staticintmxt_read_message(structmxt_data*data,u8*message){structmxt_object*object;
From: Nick Dyer <hidden> Date: 2013-06-27 12:57:35
The T5 object may have various sizes depending on the objects used on the
particular maXTouch chip and firmware version, therefore it can't be hardcoded
in the driver. Allocate a buffer on probe instead.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 114 +++++++++++++++++-------------
1 file changed, 65 insertions(+), 49 deletions(-)
@@ -225,11 +228,6 @@ struct mxt_object {u8num_report_ids;}__packed;-structmxt_message{-u8reportid;-u8message[7];-};-/* Each client has this additional data */structmxt_data{structi2c_client*client;
From: Nick Dyer <hidden> Date: 2013-06-27 12:57:53
The MXT device may be in bootloader mode on probe, due to:
1) APP CRC failure, either:
a) flash corruption
b) bad power or other intermittent problem while checking CRC
2) If the device has been reset 10 or more times without accessing comms
3) Warm probe, device was in bootloader mode already
This code attempts to recover from 1(b) and 3.
Signed-off-by: Nick Dyer <redacted>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 68 ++++++++++++++++++++++--------
1 file changed, 51 insertions(+), 17 deletions(-)
@@ -385,7 +385,7 @@ static int mxt_bootloader_write(struct mxt_data *data,returnret;}-staticintmxt_lookup_bootloader_address(structmxt_data*data)+staticintmxt_lookup_bootloader_address(structmxt_data*data,boolretry){u8appmode=data->client->addr;u8bootloader;
@@ -394,7 +394,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data)case0x4a:case0x4b:/* Chips after 1664S use different scheme */-if(data->info.family_id>=0xa2){+if(retry||data->info.family_id>=0xa2){bootloader=appmode-0x24;break;}
@@ -416,14 +416,14 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data)return0;}-staticintmxt_probe_bootloader(structmxt_data*data)+staticintmxt_probe_bootloader(structmxt_data*data,boolretry){structdevice*dev=&data->client->dev;intret;u8val;boolcrc_failure;-ret=mxt_lookup_bootloader_address(data);+ret=mxt_lookup_bootloader_address(data,retry);if(ret)returnret;
@@ -1408,15 +1413,40 @@ static int mxt_initialize(struct mxt_data *data)structi2c_client*client=data->client;structmxt_info*info=&data->info;interror;+boolalt_bootloader_addr=false;+boolretry=false;+retry_info:error=mxt_get_info(data);if(error){-error=mxt_probe_bootloader(data);-if(error)-returnerror;+retry_bootloader:+error=mxt_probe_bootloader(data,alt_bootloader_addr);+if(error){+if(alt_bootloader_addr){+/* Chip is not in appmode or bootloader mode */+returnerror;+}-data->in_bootloader=true;-return0;+dev_info(&client->dev,"Trying alternate bootloader address\n");+alt_bootloader_addr=true;+gotoretry_bootloader;+}else{+if(retry){+dev_err(&client->dev,+"Could not recover device from "+"bootloader mode\n");+/* this is not an error state, we can reflash+*fromhere*/+data->in_bootloader=true;+return0;+}++/* Attempt to exit bootloader into app mode */+mxt_send_bootloader_cmd(data,false);+msleep(MXT_FW_RESET_TIME);+retry=true;+gotoretry_info;+}}data->object_table=kcalloc(info->object_num,
@@ -1595,10 +1625,6 @@ static int mxt_load_fw(struct device *dev, const char *fn)if(ret)gotorelease_firmware;-ret=mxt_lookup_bootloader_address(data);-if(ret)-gotorelease_firmware;-if(!data->in_bootloader){/* Change to the bootloader mode */data->in_bootloader=true;
@@ -1609,6 +1635,14 @@ static int mxt_load_fw(struct device *dev, const char *fn)gotorelease_firmware;msleep(MXT_RESET_TIME);++/* At this stage, do not need to scan since we know+*familyID*/+ret=mxt_lookup_bootloader_address(data,0);+if(ret)+gotorelease_firmware;+}else{+enable_irq(data->irq);}mxt_free_object_table(data);
From: Nick Dyer <hidden> Date: 2013-06-27 12:59:03
Later chips (for example mXT1664S) different mappings for bootloader addresses.
This means that we must look at the family ID to determine which address to
use. There is an additional complication: when we probe and we don't know the
family ID yet, we need to try both possible addresses to find the bootloader.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 6 ++++++
1 file changed, 6 insertions(+)
@@ -393,6 +393,12 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data)switch(appmode){case0x4a:case0x4b:+/* Chips after 1664S use different scheme */+if(data->info.family_id>=0xa2){+bootloader=appmode-0x24;+break;+}+/* Fall through for normal case */case0x4c:case0x4d:case0x5a:
From: Nick Dyer <hidden> Date: 2013-06-27 12:59:48
If the bootloader fails to start the appmode image on the touch controller, it
stays in bootloader mode. It is possible to reflash a working firmware image
from this state.
Signed-off-by: Nick Dyer <redacted>
Acked-by: Benson Leung <bleung@chromium.org>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 61 +++++++++++++++++++++++-------
1 file changed, 48 insertions(+), 13 deletions(-)
@@ -1378,8 +1403,14 @@ static int mxt_initialize(struct mxt_data *data)interror;error=mxt_get_info(data);-if(error)-returnerror;+if(error){+error=mxt_probe_bootloader(data);+if(error)+returnerror;++data->in_bootloader=true;+return0;+}data->object_table=kcalloc(info->object_num,sizeof(structmxt_object),
@@ -1561,15 +1592,19 @@ static int mxt_load_fw(struct device *dev, const char *fn)if(ret)gotorelease_firmware;-/* Change to the bootloader mode */-data->in_bootloader=true;+if(!data->in_bootloader){+/* Change to the bootloader mode */+data->in_bootloader=true;-ret=mxt_t6_command(data,MXT_COMMAND_RESET,MXT_BOOT_VALUE,false);-if(ret)-gotorelease_firmware;+ret=mxt_t6_command(data,MXT_COMMAND_RESET,+MXT_BOOT_VALUE,false);+if(ret)+gotorelease_firmware;-msleep(MXT_RESET_TIME);+msleep(MXT_RESET_TIME);+}+mxt_free_object_table(data);INIT_COMPLETION(data->bl_completion);ret=mxt_check_bootloader(data,MXT_WAITING_BOOTLOAD_CMD);
@@ -1728,73 +1728,39 @@ static int mxt_handle_pdata(struct mxt_data *data)return0;}-staticintmxt_probe(structi2c_client*client,-conststructi2c_device_id*id)+staticintmxt_initialize_t9_input_device(structmxt_data*data){-structmxt_data*data;+structdevice*dev=&data->client->dev;+conststructmxt_platform_data*pdata=data->pdata;structinput_dev*input_dev;interror;unsignedintnum_mt_slots;unsignedintmt_flags=0;inti;-data=kzalloc(sizeof(structmxt_data),GFP_KERNEL);input_dev=input_allocate_device();-if(!data||!input_dev){-dev_err(&client->dev,"Failed to allocate memory\n");-error=-ENOMEM;-gotoerr_free_mem;+if(!input_dev){+dev_err(dev,"Failed to allocate memory\n");+return-ENOMEM;}input_dev->name="Atmel maXTouch Touchscreen";-snprintf(data->phys,sizeof(data->phys),"i2c-%u-%04x/input0",-client->adapter->nr,client->addr);-input_dev->phys=data->phys;-input_dev->id.bustype=BUS_I2C;-input_dev->dev.parent=&client->dev;+input_dev->dev.parent=dev;input_dev->open=mxt_input_open;input_dev->close=mxt_input_close;-data->client=client;-data->input_dev=input_dev;-data->irq=client->irq;-i2c_set_clientdata(client,data);--error=mxt_handle_pdata(data);-if(error)-gotoerr_free_mem;--init_completion(&data->bl_completion);-init_completion(&data->reset_completion);-init_completion(&data->crc_completion);--error=request_threaded_irq(data->irq,NULL,mxt_interrupt,-data->pdata->irqflags|IRQF_ONESHOT,-client->name,data);-if(error){-dev_err(&client->dev,"Failed to register interrupt\n");-gotoerr_free_pdata;-}--disable_irq(client->irq);--error=mxt_initialize(data);-if(error)-gotoerr_free_irq;-__set_bit(EV_ABS,input_dev->evbit);-__set_bit(EV_KEY,input_dev->evbit);-__set_bit(BTN_TOUCH,input_dev->keybit);+input_set_capability(input_dev,EV_KEY,BTN_TOUCH);-if(data->pdata->t19_num_keys){+if(pdata->t19_num_keys){__set_bit(INPUT_PROP_BUTTONPAD,input_dev->propbit);-for(i=0;i<data->pdata->t19_num_keys;i++)-if(data->pdata->t19_keymap[i]!=KEY_RESERVED)+for(i=0;i<pdata->t19_num_keys;i++)+if(pdata->t19_keymap[i]!=KEY_RESERVED)input_set_capability(input_dev,EV_KEY,-data->pdata->t19_keymap[i]);+pdata->t19_keymap[i]);mt_flags|=INPUT_MT_POINTER;
@@ -1819,8 +1785,11 @@ static int mxt_probe(struct i2c_client *client,/* For multi touch */num_mt_slots=data->T9_reportid_max-data->T9_reportid_min+1;error=input_mt_init_slots(input_dev,num_mt_slots,mt_flags);-if(error)-gotoerr_free_object;+if(error){+dev_err(dev,"Error %d initialising slots\n",error);+gotoerr_free_mem;+}+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,
@@ -1834,11 +1803,64 @@ static int mxt_probe(struct i2c_client *client,error=input_register_device(input_dev);if(error){-dev_err(&client->dev,"Error %d registering input device\n",-error);-gotoerr_free_object;+dev_err(dev,"Error %d registering input device\n",error);+gotoerr_free_mem;}+data->input_dev=input_dev;++return0;++err_free_mem:+input_free_device(input_dev);+returnerror;+}++staticintmxt_probe(structi2c_client*client,+conststructi2c_device_id*id)+{+structmxt_data*data;+interror;++data=kzalloc(sizeof(structmxt_data),GFP_KERNEL);+if(!data){+dev_err(&client->dev,"Failed to allocate memory\n");+return-ENOMEM;+}++snprintf(data->phys,sizeof(data->phys),"i2c-%u-%04x/input0",+client->adapter->nr,client->addr);++data->client=client;+data->irq=client->irq;+i2c_set_clientdata(client,data);++error=mxt_handle_pdata(data);+if(error)+gotoerr_free_mem;++init_completion(&data->bl_completion);+init_completion(&data->reset_completion);+init_completion(&data->crc_completion);++error=request_threaded_irq(data->irq,NULL,mxt_interrupt,+data->pdata->irqflags|IRQF_ONESHOT,+client->name,data);+if(error){+dev_err(&client->dev,"Failed to register interrupt\n");+gotoerr_free_pdata;+}++disable_irq(data->irq);++error=mxt_initialize(data);+if(error)+gotoerr_free_irq;++error=mxt_initialize_t9_input_device(data);+if(error)+gotoerr_free_object;+error=sysfs_create_group(&client->dev.kobj,&mxt_attr_group);if(error){dev_err(&client->dev,"Failure %d creating sysfs group\n",
@@ -1849,8 +1871,8 @@ static int mxt_probe(struct i2c_client *client,return0;err_unregister_device:-input_unregister_device(input_dev);-input_dev=NULL;+input_unregister_device(data->input_dev);+data->input_dev=NULL;err_free_object:kfree(data->object_table);err_free_irq:
On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
Signed-off-by: Nick Dyer <redacted>
So before we allocated input device before requesting IRQ, now we fo it
afterwards so there is moment where the interrupt is requested and not
disabled and input device is not allocated yet. Is it possible for
interrupt to happen at that moment?
Thanks.
@@ -1728,73 +1728,39 @@ static int mxt_handle_pdata(struct mxt_data *data)return0;}-staticintmxt_probe(structi2c_client*client,-conststructi2c_device_id*id)+staticintmxt_initialize_t9_input_device(structmxt_data*data){-structmxt_data*data;+structdevice*dev=&data->client->dev;+conststructmxt_platform_data*pdata=data->pdata;structinput_dev*input_dev;interror;unsignedintnum_mt_slots;unsignedintmt_flags=0;inti;-data=kzalloc(sizeof(structmxt_data),GFP_KERNEL);input_dev=input_allocate_device();-if(!data||!input_dev){-dev_err(&client->dev,"Failed to allocate memory\n");-error=-ENOMEM;-gotoerr_free_mem;+if(!input_dev){+dev_err(dev,"Failed to allocate memory\n");+return-ENOMEM;}input_dev->name="Atmel maXTouch Touchscreen";-snprintf(data->phys,sizeof(data->phys),"i2c-%u-%04x/input0",-client->adapter->nr,client->addr);-input_dev->phys=data->phys;-input_dev->id.bustype=BUS_I2C;-input_dev->dev.parent=&client->dev;+input_dev->dev.parent=dev;input_dev->open=mxt_input_open;input_dev->close=mxt_input_close;-data->client=client;-data->input_dev=input_dev;-data->irq=client->irq;-i2c_set_clientdata(client,data);--error=mxt_handle_pdata(data);-if(error)-gotoerr_free_mem;--init_completion(&data->bl_completion);-init_completion(&data->reset_completion);-init_completion(&data->crc_completion);--error=request_threaded_irq(data->irq,NULL,mxt_interrupt,-data->pdata->irqflags|IRQF_ONESHOT,-client->name,data);-if(error){-dev_err(&client->dev,"Failed to register interrupt\n");-gotoerr_free_pdata;-}--disable_irq(client->irq);--error=mxt_initialize(data);-if(error)-gotoerr_free_irq;-__set_bit(EV_ABS,input_dev->evbit);-__set_bit(EV_KEY,input_dev->evbit);-__set_bit(BTN_TOUCH,input_dev->keybit);+input_set_capability(input_dev,EV_KEY,BTN_TOUCH);-if(data->pdata->t19_num_keys){+if(pdata->t19_num_keys){__set_bit(INPUT_PROP_BUTTONPAD,input_dev->propbit);-for(i=0;i<data->pdata->t19_num_keys;i++)-if(data->pdata->t19_keymap[i]!=KEY_RESERVED)+for(i=0;i<pdata->t19_num_keys;i++)+if(pdata->t19_keymap[i]!=KEY_RESERVED)input_set_capability(input_dev,EV_KEY,-data->pdata->t19_keymap[i]);+pdata->t19_keymap[i]);mt_flags|=INPUT_MT_POINTER;
@@ -1819,8 +1785,11 @@ static int mxt_probe(struct i2c_client *client,/* For multi touch */num_mt_slots=data->T9_reportid_max-data->T9_reportid_min+1;error=input_mt_init_slots(input_dev,num_mt_slots,mt_flags);-if(error)-gotoerr_free_object;+if(error){+dev_err(dev,"Error %d initialising slots\n",error);+gotoerr_free_mem;+}+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,
@@ -1834,11 +1803,64 @@ static int mxt_probe(struct i2c_client *client,error=input_register_device(input_dev);if(error){-dev_err(&client->dev,"Error %d registering input device\n",-error);-gotoerr_free_object;+dev_err(dev,"Error %d registering input device\n",error);+gotoerr_free_mem;}+data->input_dev=input_dev;++return0;++err_free_mem:+input_free_device(input_dev);+returnerror;+}++staticintmxt_probe(structi2c_client*client,+conststructi2c_device_id*id)+{+structmxt_data*data;+interror;++data=kzalloc(sizeof(structmxt_data),GFP_KERNEL);+if(!data){+dev_err(&client->dev,"Failed to allocate memory\n");+return-ENOMEM;+}++snprintf(data->phys,sizeof(data->phys),"i2c-%u-%04x/input0",+client->adapter->nr,client->addr);++data->client=client;+data->irq=client->irq;+i2c_set_clientdata(client,data);++error=mxt_handle_pdata(data);+if(error)+gotoerr_free_mem;++init_completion(&data->bl_completion);+init_completion(&data->reset_completion);+init_completion(&data->crc_completion);++error=request_threaded_irq(data->irq,NULL,mxt_interrupt,+data->pdata->irqflags|IRQF_ONESHOT,+client->name,data);+if(error){+dev_err(&client->dev,"Failed to register interrupt\n");+gotoerr_free_pdata;+}++disable_irq(data->irq);++error=mxt_initialize(data);+if(error)+gotoerr_free_irq;++error=mxt_initialize_t9_input_device(data);+if(error)+gotoerr_free_object;+error=sysfs_create_group(&client->dev.kobj,&mxt_attr_group);if(error){dev_err(&client->dev,"Failure %d creating sysfs group\n",
@@ -1849,8 +1871,8 @@ static int mxt_probe(struct i2c_client *client,return0;err_unregister_device:-input_unregister_device(input_dev);-input_dev=NULL;+input_unregister_device(data->input_dev);+data->input_dev=NULL;err_free_object:kfree(data->object_table);err_free_irq:
From: Nick Dyer <hidden> Date: 2013-07-08 09:41:38
Dmitry Torokhov wrote:
On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
quoted
Signed-off-by: Nick Dyer <redacted>
So before we allocated input device before requesting IRQ, now we fo it
afterwards so there is moment where the interrupt is requested and not
disabled and input device is not allocated yet. Is it possible for
interrupt to happen at that moment?
Yes, and it will be handled correctly, there are guards in the correct
places to ensure the input device will not be used before being registered.
It is registered at this point since there are several paths that might
need the interrupt handler (for example, to handle flash if device is in
failed state, or to upload configuration if necessary).
On Mon, Jul 08, 2013 at 10:41:33AM +0100, Nick Dyer wrote:
Dmitry Torokhov wrote:
quoted
On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
quoted
Signed-off-by: Nick Dyer <redacted>
So before we allocated input device before requesting IRQ, now we fo it
afterwards so there is moment where the interrupt is requested and not
disabled and input device is not allocated yet. Is it possible for
interrupt to happen at that moment?
Yes, and it will be handled correctly, there are guards in the correct
places to ensure the input device will not be used before being registered.
It is registered at this point since there are several paths that might
need the interrupt handler (for example, to handle flash if device is in
failed state, or to upload configuration if necessary).
@@ -667,14 +675,26 @@ static void mxt_input_touchevent(struct mxt_data *data,x,y,area,amplitude);input_mt_slot(input_dev,id);-input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,-status&MXT_T9_DETECT);if(status&MXT_T9_DETECT){+/* Multiple bits may be set if the host is slow to read the+*statusmessages,indicatingalltheeventsthathave+*happened*/+if(status&MXT_T9_RELEASE){+input_mt_report_slot_state(input_dev,+MT_TOOL_FINGER,0);+mxt_input_sync(input_dev);+}++/* Touch active */+input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,1);input_report_abs(input_dev,ABS_MT_POSITION_X,x);input_report_abs(input_dev,ABS_MT_POSITION_Y,y);input_report_abs(input_dev,ABS_MT_PRESSURE,amplitude);input_report_abs(input_dev,ABS_MT_TOUCH_MAJOR,area);+}else{+/* Touch no longer active, close out slot */+input_mt_report_slot_state(input_dev,MT_TOOL_FINGER,0);}}
@@ -645,10 +651,12 @@ static void mxt_input_touchevent(struct mxt_data *data, x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); y = (message->message[2] << 4) | ((message->message[3] & 0xf));++ /* Handle 10/12 bit switching */ if (data->max_x < 1024)- x = x >> 2;+ x >>= 2; if (data->max_y < 1024)- y = y >> 2;+ y >>= 2;
Unrelated changes.
quoted hunk
area = message->message[4];
amplitude = message->message[5];
@@ -667,14 +675,26 @@ static void mxt_input_touchevent(struct mxt_data *data, x, y, area, amplitude); input_mt_slot(input_dev, id);- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,- status & MXT_T9_DETECT); if (status & MXT_T9_DETECT) {+ /* Multiple bits may be set if the host is slow to read the+ * status messages, indicating all the events that have+ * happened */+ if (status & MXT_T9_RELEASE) {+ input_mt_report_slot_state(input_dev,+ MT_TOOL_FINGER, 0);+ mxt_input_sync(input_dev);
What are the guarantees that nobody else expects the frame to not be
cut off here? What is the update_input state after this operation?