From: Hans de Goede <hidden> Date: 2020-03-07 12:15:16
Suspending Goodix touchscreens requires changing the interrupt pin to
output before sending them a power-down command. Followed by wiggling
the interrupt pin to wake the device up, after which it is put back
in input mode.
So far we have only effectively supported this on devices which use
devicetree. On X86 ACPI platforms both looking up the pins; and using a
pin as both IRQ and GPIO is a bit more complicated. E.g. on some devices
we cannot directly access the IRQ pin as GPIO and we need to call ACPI
methods to control it instead.
This commit adds a new irq_pin_access_method field to the goodix_chip_data
struct and adds goodix_irq_direction_output and goodix_irq_direction_input
helpers which together abstract the GPIO accesses to the IRQ pin.
This is a preparation patch for adding support for properly suspending the
touchscreen on X86 ACPI platforms.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Signed-off-by: Hans de Goede <redacted>
---
Changes in v2:
- Make enum member names upper-case
---
drivers/input/touchscreen/goodix.c | 62 ++++++++++++++++++++++++------
1 file changed, 51 insertions(+), 11 deletions(-)
@@ -502,17 +508,48 @@ static int goodix_send_cfg(struct goodix_ts_data *ts,return0;}+staticintgoodix_irq_direction_output(structgoodix_ts_data*ts,+intvalue)+{+switch(ts->irq_pin_access_method){+caseIRQ_PIN_ACCESS_NONE:+dev_err(&ts->client->dev,+"%s called without an irq_pin_access_method set\n",+__func__);+return-EINVAL;+caseIRQ_PIN_ACCESS_GPIO:+returngpiod_direction_output(ts->gpiod_int,value);+}++return-EINVAL;/* Never reached */+}++staticintgoodix_irq_direction_input(structgoodix_ts_data*ts)+{+switch(ts->irq_pin_access_method){+caseIRQ_PIN_ACCESS_NONE:+dev_err(&ts->client->dev,+"%s called without an irq_pin_access_method set\n",+__func__);+return-EINVAL;+caseIRQ_PIN_ACCESS_GPIO:+returngpiod_direction_input(ts->gpiod_int);+}++return-EINVAL;/* Never reached */+}+staticintgoodix_int_sync(structgoodix_ts_data*ts){interror;-error=gpiod_direction_output(ts->gpiod_int,0);+error=goodix_irq_direction_output(ts,0);if(error)returnerror;msleep(50);/* T5: 50ms */-error=gpiod_direction_input(ts->gpiod_int);+error=goodix_irq_direction_input(ts);if(error)returnerror;
@@ -943,7 +983,7 @@ static int goodix_ts_remove(struct i2c_client *client){structgoodix_ts_data*ts=i2c_get_clientdata(client);-if(ts->gpiod_int&&ts->gpiod_rst)+if(ts->irq_pin_access_method==IRQ_PIN_ACCESS_GPIO)wait_for_completion(&ts->firmware_loading_complete);return0;
@@ -956,7 +996,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)interror;/* We need gpio pins to suspend/resume */-if(!ts->gpiod_int||!ts->gpiod_rst){+if(ts->irq_pin_access_method==IRQ_PIN_ACCESS_NONE){disable_irq(client->irq);return0;}
@@ -967,7 +1007,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)goodix_free_irq(ts);/* Output LOW on the INT pin for 5 ms */-error=gpiod_direction_output(ts->gpiod_int,0);+error=goodix_irq_direction_output(ts,0);if(error){goodix_request_irq(ts);returnerror;
@@ -979,7 +1019,7 @@ static int __maybe_unused goodix_suspend(struct device *dev)GOODIX_CMD_SCREEN_OFF);if(error){dev_err(&ts->client->dev,"Screen off command failed\n");-gpiod_direction_input(ts->gpiod_int);+goodix_irq_direction_input(ts);goodix_request_irq(ts);return-EAGAIN;}
@@ -999,7 +1039,7 @@ static int __maybe_unused goodix_resume(struct device *dev)structgoodix_ts_data*ts=i2c_get_clientdata(client);interror;-if(!ts->gpiod_int||!ts->gpiod_rst){+if(ts->irq_pin_access_method==IRQ_PIN_ACCESS_NONE){enable_irq(client->irq);return0;}
@@ -1008,7 +1048,7 @@ static int __maybe_unused goodix_resume(struct device *dev)*ExitsleepmodebyoutputtingHIGHleveltoINTpin*for2ms~5ms.*/-error=gpiod_direction_output(ts->gpiod_int,1);+error=goodix_irq_direction_output(ts,1);if(error)returnerror;
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:19
Before this commit we would always reset the controller at probe when we
have access to the GPIOs which are necessary to do a reset.
Doing the reset requires access to the GPIOs, but just because we have
access to the GPIOs does not mean that we should always reset the
controller at probe. On X86 ACPI platforms the BIOS / UEFI firmware will
already have reset the controller and it will have loaded the device
specific config into the controller. Doing the reset sometimes causes the
controller to lose its configuration, so on X86 ACPI platforms this is not
a good idea.
This commit adds a new reset_controller_at_probe boolean to control the
reset at probe behavior.
This commits sets the new bool to true when we set irq_pin_access_method
to IRQ_PIN_ACCESS_GPIO, so there are no functional changes.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
drivers/input/touchscreen/goodix.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:19
At least on X86 ACPI platforms it is not necessary to load the touchscreen
controller config from disk, if it needs to be loaded this has already been
done by the BIOS / UEFI firmware.
Even on other (e.g. devicetree) platforms the config-loading as currently
done has the issue that the loaded cfg file is based on the controller
model, but the actual cfg is device specific, so the cfg files are not
part of linux-firmware and this can only work with a device specific OS
image which includes the cfg file.
And we do not need access to the GPIOs at all to load the config, if we
do not have access we can still load the config.
So all in all tying the decision to try to load the config from disk to
being able to access the GPIOs is not desirable. This commit adds a new
load_cfg_from_disk boolean to control the firmware loading instead.
This commits sets the new bool to true when we set irq_pin_access_method
to IRQ_PIN_ACCESS_GPIO, so there are no functional changes.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
Changes in v3:
- Move the wait_for_completion(&ts->firmware_loading_complete);
in goodix_suspend() to above the early exit case for
ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE
---
drivers/input/touchscreen/goodix.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
@@ -983,7 +986,7 @@ static int goodix_ts_remove(struct i2c_client *client){structgoodix_ts_data*ts=i2c_get_clientdata(client);-if(ts->irq_pin_access_method==IRQ_PIN_ACCESS_GPIO)+if(ts->load_cfg_from_disk)wait_for_completion(&ts->firmware_loading_complete);return0;
@@ -995,14 +998,15 @@ static int __maybe_unused goodix_suspend(struct device *dev)structgoodix_ts_data*ts=i2c_get_clientdata(client);interror;+if(ts->load_cfg_from_disk)+wait_for_completion(&ts->firmware_loading_complete);+/* We need gpio pins to suspend/resume */if(ts->irq_pin_access_method==IRQ_PIN_ACCESS_NONE){disable_irq(client->irq);return0;}-wait_for_completion(&ts->firmware_loading_complete);-/* Free IRQ as IRQ pin is used as output in the suspend sequence */goodix_free_irq(ts);
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:19
On most Bay Trail (x86, UEFI + ACPI) devices the ACPI tables do not have
a _DSD with a "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" UUID, adding
"irq-gpios" and "reset-gpios" mappings, so we cannot get the GPIOS by name
without first manually adding mappings ourselves.
These devices contain 2 GpioIo resource in their _CRS table, on all 4 such
devices which I have access to, the order of the 2 GPIOs is reset, int.
Note that the GPIO to which the touchscreen controller irq pin is connected
is configured in direct-irq mode on these Bay Trail devices, the
pinctrl-baytrail.c driver still allows controlling the pin as a GPIO in
this case, but this is not necessarily the case on other X86 ACPI
platforms, nor do we have a guarantee that the GPIO order is the same
elsewhere, so we limit the use of a _CRS table with 2 GpioIo resources
to Bay Trail devices only.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
drivers/input/touchscreen/goodix.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
@@ -610,6 +610,21 @@ static int goodix_reset(struct goodix_ts_data *ts)}#if defined CONFIG_X86 && defined CONFIG_ACPI+#include<asm/cpu_device_id.h>+#include<asm/intel-family.h>++staticconststructx86_cpu_idbaytrail_cpu_ids[]={+{X86_VENDOR_INTEL,6,INTEL_FAM6_ATOM_SILVERMONT,X86_FEATURE_ANY,},+{}+};++staticinlineboolis_byt(void)+{+conststructx86_cpu_id*id=x86_match_cpu(baytrail_cpu_ids);++return!!id;+}+staticconststructacpi_gpio_paramsfirst_gpio={0,0,false};staticconststructacpi_gpio_paramssecond_gpio={1,0,false};
@@ -682,6 +697,10 @@ static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)}elseif(ts->gpio_count==2&&ts->gpio_int_idx==1){ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;gpio_mapping=acpi_goodix_int_last_gpios;+}elseif(is_byt()&&ts->gpio_count==2&&ts->gpio_int_idx==-1){+dev_info(dev,"No ACPI GpioInt resource, assuming that the GPIO order is reset, int\n");+ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;+gpio_mapping=acpi_goodix_int_last_gpios;}else{dev_warn(dev,"Unexpected ACPI resources: gpio_count %d, gpio_int_idx %d\n",ts->gpio_count,ts->gpio_int_idx);
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:20
On most Cherry Trail (x86, UEFI + ACPI) devices the ACPI tables do not have
a _DSD with a "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" UUID, adding
"irq-gpios" and "reset-gpios" mappings, so we cannot get the GPIOS by name
without first manually adding mappings ourselves.
These devices contain 1 GpioInt and 1 GpioIo resource in their _CRS table:
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
{
Name (RBUF, ResourceTemplate ()
{
I2cSerialBusV2 (0x0014, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C2",
0x00, ResourceConsumer, , Exclusive,
)
GpioInt (Edge, ActiveLow, Shared, PullDefault, 0x0000,
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0013
}
GpioIo (Shared, PullDefault, 0x0000, 0x0000,
IoRestrictionOutputOnly,
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0019
}
})
Return (RBUF) /* \_SB_.PCI0.I2C2.TCS1._CRS.RBUF */
}
There is no fixed order for these 2. This commit adds code to check that
there is 1 of each as expected and then registers a mapping matching their
order using devm_acpi_dev_add_driver_gpios().
This gives us access to both GPIOs allowing us to properly suspend the
controller during suspend, and making it possible to reset the controller
if necessary.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Signed-off-by: Hans de Goede <redacted>
---
Changes in v2:
- Add example _CRS method to commit message
- Add some extra comments to clarify the code
---
drivers/input/touchscreen/goodix.c | 128 ++++++++++++++++++++++++++++-
1 file changed, 124 insertions(+), 4 deletions(-)
@@ -521,6 +524,12 @@ static int goodix_irq_direction_output(struct goodix_ts_data *ts,return-EINVAL;caseIRQ_PIN_ACCESS_GPIO:returngpiod_direction_output(ts->gpiod_int,value);+caseIRQ_PIN_ACCESS_ACPI_GPIO:+/*+*TheIRQpintriggersonafallingedge,soitsgetsmarked+*asactive-low,useoutput_rawtoavoidthevalueinversion.+*/+returngpiod_direction_output_raw(ts->gpiod_int,value);}return-EINVAL;/* Never reached */
@@ -535,6 +544,7 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts)__func__);return-EINVAL;caseIRQ_PIN_ACCESS_GPIO:+caseIRQ_PIN_ACCESS_ACPI_GPIO:returngpiod_direction_input(ts->gpiod_int);}
@@ -599,6 +609,94 @@ static int goodix_reset(struct goodix_ts_data *ts)return0;}+#if defined CONFIG_X86 && defined CONFIG_ACPI+staticconststructacpi_gpio_paramsfirst_gpio={0,0,false};+staticconststructacpi_gpio_paramssecond_gpio={1,0,false};++staticconststructacpi_gpio_mappingacpi_goodix_int_first_gpios[]={+{GOODIX_GPIO_INT_NAME"-gpios",&first_gpio,1},+{GOODIX_GPIO_RST_NAME"-gpios",&second_gpio,1},+{},+};++staticconststructacpi_gpio_mappingacpi_goodix_int_last_gpios[]={+{GOODIX_GPIO_RST_NAME"-gpios",&first_gpio,1},+{GOODIX_GPIO_INT_NAME"-gpios",&second_gpio,1},+{},+};++staticintgoodix_resource(structacpi_resource*ares,void*data)+{+structgoodix_ts_data*ts=data;+structdevice*dev=&ts->client->dev;+structacpi_resource_gpio*gpio;++switch(ares->type){+caseACPI_RESOURCE_TYPE_GPIO:+gpio=&ares->data.gpio;+if(gpio->connection_type==ACPI_RESOURCE_GPIO_TYPE_INT){+if(ts->gpio_int_idx==-1){+ts->gpio_int_idx=ts->gpio_count;+}else{+dev_err(dev,"More then one GpioInt resource, ignoring ACPI GPIO resources\n");+ts->gpio_int_idx=-2;+}+}+ts->gpio_count++;+break;+default:+break;+}++return0;+}++/*+*ThisfunctiongetscalledincasewefailtogettheirqGPIOdirectly+*becausetheACPItableslackGPIO-nametoAPCI_CRSindexmappings+*(no_DSDUUIDdaffd814-6eba-4d8c-8a91-bc9bbf4aa301data).+*Inthatcaseweaddourownmappingandthengoodix_get_gpio_config()+*retriestogettheGPIOsbasedontheaddedmapping.+*/+staticintgoodix_add_acpi_gpio_mappings(structgoodix_ts_data*ts)+{+conststructacpi_gpio_mapping*gpio_mapping=NULL;+structdevice*dev=&ts->client->dev;+LIST_HEAD(resources);+intret;++ts->gpio_count=0;+ts->gpio_int_idx=-1;+ret=acpi_dev_get_resources(ACPI_COMPANION(dev),&resources,+goodix_resource,ts);+if(ret<0){+dev_err(dev,"Error getting ACPI resources: %d\n",ret);+returnret;+}++acpi_dev_free_resource_list(&resources);++if(ts->gpio_count==2&&ts->gpio_int_idx==0){+ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;+gpio_mapping=acpi_goodix_int_first_gpios;+}elseif(ts->gpio_count==2&&ts->gpio_int_idx==1){+ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;+gpio_mapping=acpi_goodix_int_last_gpios;+}else{+dev_warn(dev,"Unexpected ACPI resources: gpio_count %d, gpio_int_idx %d\n",+ts->gpio_count,ts->gpio_int_idx);+return-EINVAL;+}++returndevm_acpi_dev_add_driver_gpios(dev,gpio_mapping);+}+#else+staticintgoodix_add_acpi_gpio_mappings(structgoodix_ts_data*ts)+{+return-EINVAL;+}+#endif /* CONFIG_X86 && CONFIG_ACPI */+/***goodix_get_gpio_config-GetGPIOconfigfromACPI/DT*
@@ -609,6 +707,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)interror;structdevice*dev;structgpio_desc*gpiod;+booladded_acpi_mappings=false;if(!ts->client)return-EINVAL;
@@ -632,6 +731,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)returnerror;}+retry_get_irq_gpio:/* Get the interrupt GPIO pin number */gpiod=devm_gpiod_get_optional(dev,GOODIX_GPIO_INT_NAME,GPIOD_IN);if(IS_ERR(gpiod)){
@@ -641,6 +741,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)GOODIX_GPIO_INT_NAME,error);returnerror;}+if(!gpiod&&has_acpi_companion(dev)&&!added_acpi_mappings){+added_acpi_mappings=true;+if(goodix_add_acpi_gpio_mappings(ts)==0)+gotoretry_get_irq_gpio;+}ts->gpiod_int=gpiod;
@@ -656,10 +761,25 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)ts->gpiod_rst=gpiod;-if(ts->gpiod_int&&ts->gpiod_rst){-ts->reset_controller_at_probe=true;-ts->load_cfg_from_disk=true;-ts->irq_pin_access_method=IRQ_PIN_ACCESS_GPIO;+switch(ts->irq_pin_access_method){+caseIRQ_PIN_ACCESS_ACPI_GPIO:+/*+*Weenduphereifgoodix_add_acpi_gpio_mappings()has+*calleddevm_acpi_dev_add_driver_gpios()becausetheACPI+*tablesdidnotcontainnametoindexmappings.+*CheckthatwesuccessfullygotbothGPIOsafterwe've+*addedourownacpi_gpio_mappingandifwedidnotgetboth+*GPIOsresetirq_pin_access_methodtoIRQ_PIN_ACCESS_NONE.+*/+if(!ts->gpiod_int||!ts->gpiod_rst)+ts->irq_pin_access_method=IRQ_PIN_ACCESS_NONE;+break;+default:+if(ts->gpiod_int&&ts->gpiod_rst){+ts->reset_controller_at_probe=true;+ts->load_cfg_from_disk=true;+ts->irq_pin_access_method=IRQ_PIN_ACCESS_GPIO;+}}return0;
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:21
Some Apollo Lake (x86, UEFI + ACPI) devices only list the reset GPIO
in their _CRS table and the bit-banging of the IRQ line necessary to
wake-up the controller from suspend can be done by calling 2 Goodix
custom / specific ACPI methods.
This commit adds support for controlling the IRQ line in this matter,
allowing us to properly suspend the touchscreen controller on such
devices.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
drivers/input/touchscreen/goodix.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
@@ -697,6 +717,12 @@ static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)}elseif(ts->gpio_count==2&&ts->gpio_int_idx==1){ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;gpio_mapping=acpi_goodix_int_last_gpios;+}elseif(ts->gpio_count==1&&ts->gpio_int_idx==-1&&+acpi_has_method(ACPI_HANDLE(dev),"INTI")&&+acpi_has_method(ACPI_HANDLE(dev),"INTO")){+dev_info(dev,"Using ACPI INTI and INTO methods for IRQ pin access\n");+ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_METHOD;+gpio_mapping=acpi_goodix_reset_only_gpios;}elseif(is_byt()&&ts->gpio_count==2&&ts->gpio_int_idx==-1){dev_info(dev,"No ACPI GpioInt resource, assuming that the GPIO order is reset, int\n");ts->irq_pin_access_method=IRQ_PIN_ACCESS_ACPI_GPIO;
@@ -793,6 +819,10 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)if(!ts->gpiod_int||!ts->gpiod_rst)ts->irq_pin_access_method=IRQ_PIN_ACCESS_NONE;break;+caseIRQ_PIN_ACCESS_ACPI_METHOD:+if(!ts->gpiod_rst)+ts->irq_pin_access_method=IRQ_PIN_ACCESS_NONE;+break;default:if(ts->gpiod_int&&ts->gpiod_rst){ts->reset_controller_at_probe=true;
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:23
Save a copy of the config in goodix_read_config(), this is a preparation
patch for restoring the config if it was lost after a supend/resume cycle.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
Changes in v2:
- s/fix_config/calc_config_checksum/
- Add a comment explaining that the "ts->config[raw_cfg_len + 1] = 1" lines
are setting the "config_fresh" bit
---
drivers/input/touchscreen/goodix.c | 47 ++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 6 deletions(-)
@@ -442,6 +450,19 @@ static int goodix_check_cfg_8(struct goodix_ts_data *ts,return0;}+staticvoidgoodix_calc_cfg_checksum_8(structgoodix_ts_data*ts)+{+inti,raw_cfg_len=ts->chip->config_len-2;+u8check_sum=0;++for(i=0;i<raw_cfg_len;i++)+check_sum+=ts->config[i];+check_sum=(~check_sum)+1;++ts->config[raw_cfg_len]=check_sum;+ts->config[raw_cfg_len+1]=1;/* Set "config_fresh" bit */+}+staticintgoodix_check_cfg_16(structgoodix_ts_data*ts,conststructfirmware*cfg){
@@ -466,6 +487,19 @@ static int goodix_check_cfg_16(struct goodix_ts_data *ts,return0;}+staticvoidgoodix_calc_cfg_checksum_16(structgoodix_ts_data*ts)+{+inti,raw_cfg_len=ts->chip->config_len-3;+u16check_sum=0;++for(i=0;i<raw_cfg_len;i+=2)+check_sum+=get_unaligned_be16(&ts->config[i]);+check_sum=(~check_sum)+1;++put_unaligned_be16(check_sum,&ts->config[raw_cfg_len]);+ts->config[raw_cfg_len+2]=1;/* Set "config_fresh" bit */+}+/***goodix_check_cfg-Checksifconfigfwisvalid*
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:25
Our goodix_check_cfg_* helpers do things like:
int i, raw_cfg_len = cfg->size - 2;
...
if (check_sum != cfg->data[raw_cfg_len]) {
When cfg->size < 2, this will end up indexing the cfg->data array with
a negative value, which will not end well.
To fix this this commit adds a new GOODIX_CONFIG_MIN_LENGTH define and
adds a minimum size check for firmware-config files using this new define.
For consistency this commit also adds a new GOODIX_CONFIG_GT9X_LENGTH for
the length used for recent gt9xx and gt1xxx chips, instead of using
GOODIX_CONFIG_MAX_LENGTH for this, so that if other length defines get
added in the future it will be clear that the MIN and MAX defines should
contain the min and max values of all the other defines.
Signed-off-by: Hans de Goede <redacted>
---
Changes in v2:
- New patch in v2 of this patch series
---
drivers/input/touchscreen/goodix.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
@@ -509,7 +511,8 @@ static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts)staticintgoodix_check_cfg(structgoodix_ts_data*ts,conststructfirmware*cfg){-if(cfg->size>GOODIX_CONFIG_MAX_LENGTH){+if(cfg->size<GOODIX_CONFIG_MIN_LENGTH||+cfg->size>GOODIX_CONFIG_MAX_LENGTH){dev_err(&ts->client->dev,"The length of the config fw is not correct");return-EINVAL;
@@ -428,22 +428,21 @@ static int goodix_request_irq(struct goodix_ts_data *ts)ts->irq_flags,ts->client->name,ts);}-staticintgoodix_check_cfg_8(structgoodix_ts_data*ts,-conststructfirmware*cfg)+staticintgoodix_check_cfg_8(structgoodix_ts_data*ts,constu8*cfg,intlen){-inti,raw_cfg_len=cfg->size-2;+inti,raw_cfg_len=len-2;u8check_sum=0;for(i=0;i<raw_cfg_len;i++)-check_sum+=cfg->data[i];+check_sum+=cfg[i];check_sum=(~check_sum)+1;-if(check_sum!=cfg->data[raw_cfg_len]){+if(check_sum!=cfg[raw_cfg_len]){dev_err(&ts->client->dev,"The checksum of the config fw is not correct");return-EINVAL;}-if(cfg->data[raw_cfg_len+1]!=1){+if(cfg[raw_cfg_len+1]!=1){dev_err(&ts->client->dev,"Config fw must have Config_Fresh register set");return-EINVAL;
@@ -465,22 +464,22 @@ static void goodix_calc_cfg_checksum_8(struct goodix_ts_data *ts)ts->config[raw_cfg_len+1]=1;/* Set "config_fresh" bit */}-staticintgoodix_check_cfg_16(structgoodix_ts_data*ts,-conststructfirmware*cfg)+staticintgoodix_check_cfg_16(structgoodix_ts_data*ts,constu8*cfg,+intlen){-inti,raw_cfg_len=cfg->size-3;+inti,raw_cfg_len=len-3;u16check_sum=0;for(i=0;i<raw_cfg_len;i+=2)-check_sum+=get_unaligned_be16(&cfg->data[i]);+check_sum+=get_unaligned_be16(&cfg[i]);check_sum=(~check_sum)+1;-if(check_sum!=get_unaligned_be16(&cfg->data[raw_cfg_len])){+if(check_sum!=get_unaligned_be16(&cfg[raw_cfg_len])){dev_err(&ts->client->dev,"The checksum of the config fw is not correct");return-EINVAL;}-if(cfg->data[raw_cfg_len+2]!=1){+if(cfg[raw_cfg_len+2]!=1){dev_err(&ts->client->dev,"Config fw must have Config_Fresh register set");return-EINVAL;
@@ -508,17 +507,16 @@ static void goodix_calc_cfg_checksum_16(struct goodix_ts_data *ts)*@ts:goodix_ts_datapointer*@cfg:firmwareconfigdata*/-staticintgoodix_check_cfg(structgoodix_ts_data*ts,-conststructfirmware*cfg)+staticintgoodix_check_cfg(structgoodix_ts_data*ts,constu8*cfg,intlen){-if(cfg->size<GOODIX_CONFIG_MIN_LENGTH||-cfg->size>GOODIX_CONFIG_MAX_LENGTH){+if(len<GOODIX_CONFIG_MIN_LENGTH||+len>GOODIX_CONFIG_MAX_LENGTH){dev_err(&ts->client->dev,"The length of the config fw is not correct");return-EINVAL;}-returnts->chip->check_config(ts,cfg);+returnts->chip->check_config(ts,cfg,len);}/**
@@ -527,17 +525,15 @@ static int goodix_check_cfg(struct goodix_ts_data *ts,*@ts:goodix_ts_datapointer*@cfg:configfirmwaretowritetodevice*/-staticintgoodix_send_cfg(structgoodix_ts_data*ts,-conststructfirmware*cfg)+staticintgoodix_send_cfg(structgoodix_ts_data*ts,constu8*cfg,intlen){interror;-error=goodix_check_cfg(ts,cfg);+error=goodix_check_cfg(ts,cfg,len);if(error)returnerror;-error=goodix_i2c_write(ts->client,ts->chip->config_addr,cfg->data,-cfg->size);+error=goodix_i2c_write(ts->client,ts->chip->config_addr,cfg,len);if(error){dev_err(&ts->client->dev,"Failed to write config data: %d",error);
@@ -1072,7 +1068,7 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)if(cfg){/* send device configuration to the firmware */-error=goodix_send_cfg(ts,cfg);+error=goodix_send_cfg(ts,cfg->data,cfg->size);if(error)gotoerr_release_cfg;}
From: Hans de Goede <hidden> Date: 2020-03-07 12:15:27
Some devices, e.g the Trekstor Primetab S11B, lose there config over
a suspend/resume cycle (likely the controller loses power during suspend).
This commit reads back the config version on resume and if matches the
expected config version it resets the controller and resends the config
we read back and saved at probe time.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Reviewed-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Hans de Goede <redacted>
---
Changes in v2:
- Replace dev_warn on version mismatch on resume with dev_info
---
drivers/input/touchscreen/goodix.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
On Sat, 2020-03-07 at 13:14 +0100, Hans de Goede wrote:
Suspending Goodix touchscreens requires changing the interrupt pin to
output before sending them a power-down command. Followed by wiggling
the interrupt pin to wake the device up, after which it is put back
in input mode.
So far we have only effectively supported this on devices which use
devicetree. On X86 ACPI platforms both looking up the pins; and using
a
pin as both IRQ and GPIO is a bit more complicated. E.g. on some
devices
we cannot directly access the IRQ pin as GPIO and we need to call
ACPI
methods to control it instead.
This commit adds a new irq_pin_access_method field to the
goodix_chip_data
struct and adds goodix_irq_direction_output and
goodix_irq_direction_input
helpers which together abstract the GPIO accesses to the IRQ pin.
This is a preparation patch for adding support for properly
suspending the
touchscreen on X86 ACPI platforms.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Signed-off-by: Hans de Goede <redacted>
On Sat, 2020-03-07 at 13:14 +0100, Hans de Goede wrote:
On most Cherry Trail (x86, UEFI + ACPI) devices the ACPI tables do
not have
a _DSD with a "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" UUID, adding
"irq-gpios" and "reset-gpios" mappings, so we cannot get the GPIOS by
name
without first manually adding mappings ourselves.
These devices contain 1 GpioInt and 1 GpioIo resource in their _CRS
table:
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
{
Name (RBUF, ResourceTemplate ()
{
I2cSerialBusV2 (0x0014, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C2",
0x00, ResourceConsumer, , Exclusive,
)
GpioInt (Edge, ActiveLow, Shared, PullDefault, 0x0000,
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0013
}
GpioIo (Shared, PullDefault, 0x0000, 0x0000,
IoRestrictionOutputOnly,
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0019
}
})
Return (RBUF) /* \_SB_.PCI0.I2C2.TCS1._CRS.RBUF */
}
There is no fixed order for these 2. This commit adds code to check
that
there is 1 of each as expected and then registers a mapping matching
their
order using devm_acpi_dev_add_driver_gpios().
This gives us access to both GPIOs allowing us to properly suspend
the
controller during suspend, and making it possible to reset the
controller
if necessary.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Signed-off-by: Hans de Goede <redacted>
On Sat, 2020-03-07 at 13:15 +0100, Hans de Goede wrote:
Our goodix_check_cfg_* helpers do things like:
int i, raw_cfg_len = cfg->size - 2;
...
if (check_sum != cfg->data[raw_cfg_len]) {
When cfg->size < 2, this will end up indexing the cfg->data array
with
a negative value, which will not end well.
To fix this this commit adds a new GOODIX_CONFIG_MIN_LENGTH define
and
adds a minimum size check for firmware-config files using this new
define.
For consistency this commit also adds a new GOODIX_CONFIG_GT9X_LENGTH
for
the length used for recent gt9xx and gt1xxx chips, instead of using
GOODIX_CONFIG_MAX_LENGTH for this, so that if other length defines
get
added in the future it will be clear that the MIN and MAX defines
should
contain the min and max values of all the other defines.
Signed-off-by: Hans de Goede <redacted>
On Sat, Mar 07, 2020 at 01:14:55PM +0100, Hans de Goede wrote:
Suspending Goodix touchscreens requires changing the interrupt pin to
output before sending them a power-down command. Followed by wiggling
the interrupt pin to wake the device up, after which it is put back
in input mode.
So far we have only effectively supported this on devices which use
devicetree. On X86 ACPI platforms both looking up the pins; and using a
pin as both IRQ and GPIO is a bit more complicated. E.g. on some devices
we cannot directly access the IRQ pin as GPIO and we need to call ACPI
methods to control it instead.
This commit adds a new irq_pin_access_method field to the goodix_chip_data
struct and adds goodix_irq_direction_output and goodix_irq_direction_input
helpers which together abstract the GPIO accesses to the IRQ pin.
This is a preparation patch for adding support for properly suspending the
touchscreen on X86 ACPI platforms.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207
Cc: Dmitry Mastykin <redacted>
Signed-off-by: Hans de Goede <redacted>