diff mbox

[v2,5/8] input: goodix: write configuration data to device

Message ID 1433774273-23103-6-git-send-email-irina.tirdea@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

tip-bot for Irina Tirdea June 8, 2015, 2:37 p.m. UTC
Goodix devices can be configured by writing custom data to the device at
init. The configuration data is read with request_firmware from
"goodix_<id>_cfg.bin", where <id> is the product id read from the device
(e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
GT9271).

The configuration information has a specific format described in the Goodix
datasheet. It includes X/Y resolution, maximum supported touch points,
interrupt flags, various sesitivity factors and settings for advanced
features (like gesture recognition).

This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
driver gt9xx.c for Android (publicly available in Android kernel
trees for various devices).

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
---
 drivers/input/touchscreen/goodix.c | 128 +++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)

Comments

Dmitry Torokhov June 9, 2015, 6:14 p.m. UTC | #1
Hi Irina,

On Mon, Jun 08, 2015 at 05:37:50PM +0300, Irina Tirdea wrote:
> Goodix devices can be configured by writing custom data to the device at
> init. The configuration data is read with request_firmware from
> "goodix_<id>_cfg.bin", where <id> is the product id read from the device
> (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
> GT9271).
> 
> The configuration information has a specific format described in the Goodix
> datasheet. It includes X/Y resolution, maximum supported touch points,
> interrupt flags, various sesitivity factors and settings for advanced
> features (like gesture recognition).
> 
> This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> driver gt9xx.c for Android (publicly available in Android kernel
> trees for various devices).

I am afraid that just using request_firmware() in probe() is not the
best option as it may cause either errors or delays in
initialization of the driver is compiled into the kernel and tries to
initialize before filesystem with configuration file is mounted (and
firmware is not built into the kernel). We can either try switch to
request_firmware_nowait() or at least document that we need firmware at
boot.

> 
> Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> ---
>  drivers/input/touchscreen/goodix.c | 128 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 128 insertions(+)
> 
> diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> index c345eb7..1ce9278 100644
> --- a/drivers/input/touchscreen/goodix.c
> +++ b/drivers/input/touchscreen/goodix.c
> @@ -24,6 +24,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/slab.h>
>  #include <linux/acpi.h>
> +#include <linux/firmware.h>
>  #include <linux/gpio.h>
>  #include <linux/of.h>
>  #include <asm/unaligned.h>
> @@ -95,6 +96,39 @@ static int goodix_i2c_read(struct i2c_client *client,
>  	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
>  }
>  
> +/**
> + * goodix_i2c_write - write data to a register of the i2c slave device.
> + *
> + * @client: i2c device.
> + * @reg: the register to write to.
> + * @buf: raw data buffer to write.
> + * @len: length of the buffer to write
> + */
> +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
> +			    unsigned len)
> +{
> +	u8 *addr_buf;
> +	struct i2c_msg msg;
> +	int ret;
> +
> +	addr_buf = kmalloc(len + 2, GFP_KERNEL);
> +	if (!addr_buf)
> +		return -ENOMEM;
> +
> +	addr_buf[0] = reg >> 8;
> +	addr_buf[1] = reg & 0xFF;
> +	memcpy(&addr_buf[2], buf, len);
> +
> +	msg.flags = 0;
> +	msg.addr = client->addr;
> +	msg.buf = addr_buf;
> +	msg.len = len + 2;
> +
> +	ret = i2c_transfer(client->adapter, &msg, 1);
> +	kfree(addr_buf);
> +	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
> +}
> +
>  static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
>  {
>  	int touch_num;
> @@ -192,6 +226,95 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> +/**
> + * goodix_check_cfg - Checks if config buffer is valid
> + *
> + * @ts: goodix_ts_data pointer
> + * @fw: firmware config data
> + */
> +static int goodix_check_cfg(struct goodix_ts_data *ts,
> +			    const struct firmware *fw)
> +{
> +	int i, raw_cfg_len;
> +	u8 check_sum = 0;
> +
> +	if (fw->size > GOODIX_CONFIG_MAX_LENGTH) {
> +		dev_err(&ts->client->dev,
> +			"The length of the config buffer array is not correct");
> +		return -EINVAL;
> +	}
> +
> +	raw_cfg_len = fw->size - 2;
> +	for (i = 0; i < raw_cfg_len; i++)
> +		check_sum += fw->data[i];
> +	check_sum = (~check_sum) + 1;
> +	if (check_sum != fw->data[raw_cfg_len]) {
> +		dev_err(&ts->client->dev,
> +			"The checksum of the config buffer array is not correct");
> +		return -EINVAL;
> +	}
> +
> +	if (fw->data[raw_cfg_len + 1] != 1) {
> +		dev_err(&ts->client->dev,
> +			"The Config_Fresh register needs to be set");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * goodix_send_cfg - Write device config
> + *
> + * @ts: goodix_ts_data pointer
> + * @id: product id read from device
> + */
> +static int goodix_send_cfg(struct goodix_ts_data *ts, u16 id)
> +{
> +	const struct firmware *fw = NULL;
> +	char *fw_name;
> +	int ret;
> +
> +	fw_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin", id);
> +	if (!fw_name)
> +		return -ENOMEM;
> +
> +	ret = request_firmware(&fw, fw_name, &ts->client->dev);
> +	if (ret) {
> +		dev_err(&ts->client->dev, "Unable to open firmware %s\n",
> +			fw_name);
> +		goto err_free_fw_name;

That I think will cause driver to abort binding if config is not there.
Do we always need to load the config? Is configuration stored in NVRAM?
Maybe configuration should be only loaded when userspace requests it?

Thanks.
tip-bot for Irina Tirdea June 23, 2015, 1:27 p.m. UTC | #2
> -----Original Message-----
> From: linux-input-owner@vger.kernel.org [mailto:linux-input-owner@vger.kernel.org] On Behalf Of Dmitry Torokhov
> Sent: 09 June, 2015 21:14
> To: Tirdea, Irina
> Cc: Bastien Nocera; Mark Rutland; linux-input@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Rob
> Herring; Pawel Moll; Ian Campbell; Kumar Gala; Purdila, Octavian
> Subject: Re: [PATCH v2 5/8] input: goodix: write configuration data to device
> 
> Hi Irina,
> 

Hi Dmitry,

> On Mon, Jun 08, 2015 at 05:37:50PM +0300, Irina Tirdea wrote:
> > Goodix devices can be configured by writing custom data to the device at
> > init. The configuration data is read with request_firmware from
> > "goodix_<id>_cfg.bin", where <id> is the product id read from the device
> > (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
> > GT9271).
> >
> > The configuration information has a specific format described in the Goodix
> > datasheet. It includes X/Y resolution, maximum supported touch points,
> > interrupt flags, various sesitivity factors and settings for advanced
> > features (like gesture recognition).
> >
> > This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> > driver gt9xx.c for Android (publicly available in Android kernel
> > trees for various devices).
> 
> I am afraid that just using request_firmware() in probe() is not the
> best option as it may cause either errors or delays in
> initialization of the driver is compiled into the kernel and tries to
> initialize before filesystem with configuration file is mounted (and
> firmware is not built into the kernel). We can either try switch to
> request_firmware_nowait() or at least document that we need firmware at
> boot.
> 

The reason I did not use request_firmware_nowait() is that this configuration data
includes information needed by probe (x/y resolution, interrupt trigger type, max
touch num), used in goodix_read_config, goodix_ts_report_touch  and for irq flags
when requesting the interrupt. I will document that we need this configuration
firmware at boot in the commit message and add a comment in the code.
Is there any other documentation I need to update?

> >
> > Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > ---
> >  drivers/input/touchscreen/goodix.c | 128 +++++++++++++++++++++++++++++++++++++
> >  1 file changed, 128 insertions(+)
> >
> > diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> > index c345eb7..1ce9278 100644
> > --- a/drivers/input/touchscreen/goodix.c
> > +++ b/drivers/input/touchscreen/goodix.c
> > @@ -24,6 +24,7 @@
> >  #include <linux/interrupt.h>
> >  #include <linux/slab.h>
> >  #include <linux/acpi.h>
> > +#include <linux/firmware.h>
> >  #include <linux/gpio.h>
> >  #include <linux/of.h>
> >  #include <asm/unaligned.h>
> > @@ -95,6 +96,39 @@ static int goodix_i2c_read(struct i2c_client *client,
> >  	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
> >  }
> >
> > +/**
> > + * goodix_i2c_write - write data to a register of the i2c slave device.
> > + *
> > + * @client: i2c device.
> > + * @reg: the register to write to.
> > + * @buf: raw data buffer to write.
> > + * @len: length of the buffer to write
> > + */
> > +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
> > +			    unsigned len)
> > +{
> > +	u8 *addr_buf;
> > +	struct i2c_msg msg;
> > +	int ret;
> > +
> > +	addr_buf = kmalloc(len + 2, GFP_KERNEL);
> > +	if (!addr_buf)
> > +		return -ENOMEM;
> > +
> > +	addr_buf[0] = reg >> 8;
> > +	addr_buf[1] = reg & 0xFF;
> > +	memcpy(&addr_buf[2], buf, len);
> > +
> > +	msg.flags = 0;
> > +	msg.addr = client->addr;
> > +	msg.buf = addr_buf;
> > +	msg.len = len + 2;
> > +
> > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > +	kfree(addr_buf);
> > +	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
> > +}
> > +
> >  static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
> >  {
> >  	int touch_num;
> > @@ -192,6 +226,95 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
> >  	return IRQ_HANDLED;
> >  }
> >
> > +/**
> > + * goodix_check_cfg - Checks if config buffer is valid
> > + *
> > + * @ts: goodix_ts_data pointer
> > + * @fw: firmware config data
> > + */
> > +static int goodix_check_cfg(struct goodix_ts_data *ts,
> > +			    const struct firmware *fw)
> > +{
> > +	int i, raw_cfg_len;
> > +	u8 check_sum = 0;
> > +
> > +	if (fw->size > GOODIX_CONFIG_MAX_LENGTH) {
> > +		dev_err(&ts->client->dev,
> > +			"The length of the config buffer array is not correct");
> > +		return -EINVAL;
> > +	}
> > +
> > +	raw_cfg_len = fw->size - 2;
> > +	for (i = 0; i < raw_cfg_len; i++)
> > +		check_sum += fw->data[i];
> > +	check_sum = (~check_sum) + 1;
> > +	if (check_sum != fw->data[raw_cfg_len]) {
> > +		dev_err(&ts->client->dev,
> > +			"The checksum of the config buffer array is not correct");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (fw->data[raw_cfg_len + 1] != 1) {
> > +		dev_err(&ts->client->dev,
> > +			"The Config_Fresh register needs to be set");
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * goodix_send_cfg - Write device config
> > + *
> > + * @ts: goodix_ts_data pointer
> > + * @id: product id read from device
> > + */
> > +static int goodix_send_cfg(struct goodix_ts_data *ts, u16 id)
> > +{
> > +	const struct firmware *fw = NULL;
> > +	char *fw_name;
> > +	int ret;
> > +
> > +	fw_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin", id);
> > +	if (!fw_name)
> > +		return -ENOMEM;
> > +
> > +	ret = request_firmware(&fw, fw_name, &ts->client->dev);
> > +	if (ret) {
> > +		dev_err(&ts->client->dev, "Unable to open firmware %s\n",
> > +			fw_name);
> > +		goto err_free_fw_name;
> 
> That I think will cause driver to abort binding if config is not there.
> Do we always need to load the config? Is configuration stored in NVRAM?
> Maybe configuration should be only loaded when userspace requests it?
> 

The device has a default configuration, but usually this does not match the platform
needs. It is stored in volatile RAM, so we need to rewrite it after power off.
From my tests, the default values are invalid if I do not write any config data
(goodix_read_config will print "Invalid config, using defaults").

However, we should still be able to use the device with these defaults even
if the config firmware is not there. I will just return 0 if firmware is not found.
Since configuration needs to be written before configuring the input device and
the interrupts, I don't think we can allow userspace to control it.

Thanks,
Irina

> Thanks.
> 
> --
> Dmitry
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitry Torokhov June 23, 2015, 6:59 p.m. UTC | #3
On Tue, Jun 23, 2015 at 01:27:01PM +0000, Tirdea, Irina wrote:
> 
> 
> > -----Original Message-----
> > From: linux-input-owner@vger.kernel.org [mailto:linux-input-owner@vger.kernel.org] On Behalf Of Dmitry Torokhov
> > Sent: 09 June, 2015 21:14
> > To: Tirdea, Irina
> > Cc: Bastien Nocera; Mark Rutland; linux-input@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Rob
> > Herring; Pawel Moll; Ian Campbell; Kumar Gala; Purdila, Octavian
> > Subject: Re: [PATCH v2 5/8] input: goodix: write configuration data to device
> > 
> > Hi Irina,
> > 
> 
> Hi Dmitry,
> 
> > On Mon, Jun 08, 2015 at 05:37:50PM +0300, Irina Tirdea wrote:
> > > Goodix devices can be configured by writing custom data to the device at
> > > init. The configuration data is read with request_firmware from
> > > "goodix_<id>_cfg.bin", where <id> is the product id read from the device
> > > (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
> > > GT9271).
> > >
> > > The configuration information has a specific format described in the Goodix
> > > datasheet. It includes X/Y resolution, maximum supported touch points,
> > > interrupt flags, various sesitivity factors and settings for advanced
> > > features (like gesture recognition).
> > >
> > > This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> > > driver gt9xx.c for Android (publicly available in Android kernel
> > > trees for various devices).
> > 
> > I am afraid that just using request_firmware() in probe() is not the
> > best option as it may cause either errors or delays in
> > initialization of the driver is compiled into the kernel and tries to
> > initialize before filesystem with configuration file is mounted (and
> > firmware is not built into the kernel). We can either try switch to
> > request_firmware_nowait() or at least document that we need firmware at
> > boot.
> > 
> 
> The reason I did not use request_firmware_nowait() is that this configuration data
> includes information needed by probe (x/y resolution, interrupt trigger type, max
> touch num), used in goodix_read_config, goodix_ts_report_touch  and for irq flags
> when requesting the interrupt. I will document that we need this configuration
> firmware at boot in the commit message and add a comment in the code.
> Is there any other documentation I need to update?
> 
> > >
> > > Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> > > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > > ---
> > >  drivers/input/touchscreen/goodix.c | 128 +++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 128 insertions(+)
> > >
> > > diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> > > index c345eb7..1ce9278 100644
> > > --- a/drivers/input/touchscreen/goodix.c
> > > +++ b/drivers/input/touchscreen/goodix.c
> > > @@ -24,6 +24,7 @@
> > >  #include <linux/interrupt.h>
> > >  #include <linux/slab.h>
> > >  #include <linux/acpi.h>
> > > +#include <linux/firmware.h>
> > >  #include <linux/gpio.h>
> > >  #include <linux/of.h>
> > >  #include <asm/unaligned.h>
> > > @@ -95,6 +96,39 @@ static int goodix_i2c_read(struct i2c_client *client,
> > >  	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
> > >  }
> > >
> > > +/**
> > > + * goodix_i2c_write - write data to a register of the i2c slave device.
> > > + *
> > > + * @client: i2c device.
> > > + * @reg: the register to write to.
> > > + * @buf: raw data buffer to write.
> > > + * @len: length of the buffer to write
> > > + */
> > > +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
> > > +			    unsigned len)
> > > +{
> > > +	u8 *addr_buf;
> > > +	struct i2c_msg msg;
> > > +	int ret;
> > > +
> > > +	addr_buf = kmalloc(len + 2, GFP_KERNEL);
> > > +	if (!addr_buf)
> > > +		return -ENOMEM;
> > > +
> > > +	addr_buf[0] = reg >> 8;
> > > +	addr_buf[1] = reg & 0xFF;
> > > +	memcpy(&addr_buf[2], buf, len);
> > > +
> > > +	msg.flags = 0;
> > > +	msg.addr = client->addr;
> > > +	msg.buf = addr_buf;
> > > +	msg.len = len + 2;
> > > +
> > > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > > +	kfree(addr_buf);
> > > +	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
> > > +}
> > > +
> > >  static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
> > >  {
> > >  	int touch_num;
> > > @@ -192,6 +226,95 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
> > >  	return IRQ_HANDLED;
> > >  }
> > >
> > > +/**
> > > + * goodix_check_cfg - Checks if config buffer is valid
> > > + *
> > > + * @ts: goodix_ts_data pointer
> > > + * @fw: firmware config data
> > > + */
> > > +static int goodix_check_cfg(struct goodix_ts_data *ts,
> > > +			    const struct firmware *fw)
> > > +{
> > > +	int i, raw_cfg_len;
> > > +	u8 check_sum = 0;
> > > +
> > > +	if (fw->size > GOODIX_CONFIG_MAX_LENGTH) {
> > > +		dev_err(&ts->client->dev,
> > > +			"The length of the config buffer array is not correct");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	raw_cfg_len = fw->size - 2;
> > > +	for (i = 0; i < raw_cfg_len; i++)
> > > +		check_sum += fw->data[i];
> > > +	check_sum = (~check_sum) + 1;
> > > +	if (check_sum != fw->data[raw_cfg_len]) {
> > > +		dev_err(&ts->client->dev,
> > > +			"The checksum of the config buffer array is not correct");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	if (fw->data[raw_cfg_len + 1] != 1) {
> > > +		dev_err(&ts->client->dev,
> > > +			"The Config_Fresh register needs to be set");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/**
> > > + * goodix_send_cfg - Write device config
> > > + *
> > > + * @ts: goodix_ts_data pointer
> > > + * @id: product id read from device
> > > + */
> > > +static int goodix_send_cfg(struct goodix_ts_data *ts, u16 id)
> > > +{
> > > +	const struct firmware *fw = NULL;
> > > +	char *fw_name;
> > > +	int ret;
> > > +
> > > +	fw_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin", id);
> > > +	if (!fw_name)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = request_firmware(&fw, fw_name, &ts->client->dev);
> > > +	if (ret) {
> > > +		dev_err(&ts->client->dev, "Unable to open firmware %s\n",
> > > +			fw_name);
> > > +		goto err_free_fw_name;
> > 
> > That I think will cause driver to abort binding if config is not there.
> > Do we always need to load the config? Is configuration stored in NVRAM?
> > Maybe configuration should be only loaded when userspace requests it?
> > 
> 
> The device has a default configuration, but usually this does not match the platform
> needs. It is stored in volatile RAM, so we need to rewrite it after power off.
> From my tests, the default values are invalid if I do not write any config data
> (goodix_read_config will print "Invalid config, using defaults").
> 
> However, we should still be able to use the device with these defaults even
> if the config firmware is not there. I will just return 0 if firmware is not found.
> Since configuration needs to be written before configuring the input device and
> the interrupts, I don't think we can allow userspace to control it.

Well, my concern is that we currently have devices and DTSes that work
just fine without special config. How do they manage to do that? We need
to make sure that new changes do not break existing users.

Thanks.
tip-bot for Irina Tirdea June 29, 2015, 4:01 p.m. UTC | #4
> -----Original Message-----
> From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com]
> Sent: 23 June, 2015 21:59
> To: Tirdea, Irina
> Cc: Bastien Nocera; Mark Rutland; linux-input@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Rob
> Herring; Pawel Moll; Ian Campbell; Kumar Gala; Purdila, Octavian
> Subject: Re: [PATCH v2 5/8] input: goodix: write configuration data to device
> 
> On Tue, Jun 23, 2015 at 01:27:01PM +0000, Tirdea, Irina wrote:
> >
> >
> > > -----Original Message-----
> > > From: linux-input-owner@vger.kernel.org [mailto:linux-input-owner@vger.kernel.org] On Behalf Of Dmitry Torokhov
> > > Sent: 09 June, 2015 21:14
> > > To: Tirdea, Irina
> > > Cc: Bastien Nocera; Mark Rutland; linux-input@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Rob
> > > Herring; Pawel Moll; Ian Campbell; Kumar Gala; Purdila, Octavian
> > > Subject: Re: [PATCH v2 5/8] input: goodix: write configuration data to device
> > >
> > > Hi Irina,
> > >
> >
> > Hi Dmitry,
> >
> > > On Mon, Jun 08, 2015 at 05:37:50PM +0300, Irina Tirdea wrote:
> > > > Goodix devices can be configured by writing custom data to the device at
> > > > init. The configuration data is read with request_firmware from
> > > > "goodix_<id>_cfg.bin", where <id> is the product id read from the device
> > > > (e.g.: goodix_911_cfg.bin for Goodix GT911, goodix_9271_cfg.bin for
> > > > GT9271).
> > > >
> > > > The configuration information has a specific format described in the Goodix
> > > > datasheet. It includes X/Y resolution, maximum supported touch points,
> > > > interrupt flags, various sesitivity factors and settings for advanced
> > > > features (like gesture recognition).
> > > >
> > > > This is based on Goodix datasheets for GT911 and GT9271 and on Goodix
> > > > driver gt9xx.c for Android (publicly available in Android kernel
> > > > trees for various devices).
> > >
> > > I am afraid that just using request_firmware() in probe() is not the
> > > best option as it may cause either errors or delays in
> > > initialization of the driver is compiled into the kernel and tries to
> > > initialize before filesystem with configuration file is mounted (and
> > > firmware is not built into the kernel). We can either try switch to
> > > request_firmware_nowait() or at least document that we need firmware at
> > > boot.
> > >
> >
> > The reason I did not use request_firmware_nowait() is that this configuration data
> > includes information needed by probe (x/y resolution, interrupt trigger type, max
> > touch num), used in goodix_read_config, goodix_ts_report_touch  and for irq flags
> > when requesting the interrupt. I will document that we need this configuration
> > firmware at boot in the commit message and add a comment in the code.
> > Is there any other documentation I need to update?
> >
> > > >
> > > > Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
> > > > Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
> > > > ---
> > > >  drivers/input/touchscreen/goodix.c | 128 +++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 128 insertions(+)
> > > >
> > > > diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
> > > > index c345eb7..1ce9278 100644
> > > > --- a/drivers/input/touchscreen/goodix.c
> > > > +++ b/drivers/input/touchscreen/goodix.c
> > > > @@ -24,6 +24,7 @@
> > > >  #include <linux/interrupt.h>
> > > >  #include <linux/slab.h>
> > > >  #include <linux/acpi.h>
> > > > +#include <linux/firmware.h>
> > > >  #include <linux/gpio.h>
> > > >  #include <linux/of.h>
> > > >  #include <asm/unaligned.h>
> > > > @@ -95,6 +96,39 @@ static int goodix_i2c_read(struct i2c_client *client,
> > > >  	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
> > > >  }
> > > >
> > > > +/**
> > > > + * goodix_i2c_write - write data to a register of the i2c slave device.
> > > > + *
> > > > + * @client: i2c device.
> > > > + * @reg: the register to write to.
> > > > + * @buf: raw data buffer to write.
> > > > + * @len: length of the buffer to write
> > > > + */
> > > > +static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
> > > > +			    unsigned len)
> > > > +{
> > > > +	u8 *addr_buf;
> > > > +	struct i2c_msg msg;
> > > > +	int ret;
> > > > +
> > > > +	addr_buf = kmalloc(len + 2, GFP_KERNEL);
> > > > +	if (!addr_buf)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	addr_buf[0] = reg >> 8;
> > > > +	addr_buf[1] = reg & 0xFF;
> > > > +	memcpy(&addr_buf[2], buf, len);
> > > > +
> > > > +	msg.flags = 0;
> > > > +	msg.addr = client->addr;
> > > > +	msg.buf = addr_buf;
> > > > +	msg.len = len + 2;
> > > > +
> > > > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > > > +	kfree(addr_buf);
> > > > +	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
> > > > +}
> > > > +
> > > >  static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
> > > >  {
> > > >  	int touch_num;
> > > > @@ -192,6 +226,95 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
> > > >  	return IRQ_HANDLED;
> > > >  }
> > > >
> > > > +/**
> > > > + * goodix_check_cfg - Checks if config buffer is valid
> > > > + *
> > > > + * @ts: goodix_ts_data pointer
> > > > + * @fw: firmware config data
> > > > + */
> > > > +static int goodix_check_cfg(struct goodix_ts_data *ts,
> > > > +			    const struct firmware *fw)
> > > > +{
> > > > +	int i, raw_cfg_len;
> > > > +	u8 check_sum = 0;
> > > > +
> > > > +	if (fw->size > GOODIX_CONFIG_MAX_LENGTH) {
> > > > +		dev_err(&ts->client->dev,
> > > > +			"The length of the config buffer array is not correct");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	raw_cfg_len = fw->size - 2;
> > > > +	for (i = 0; i < raw_cfg_len; i++)
> > > > +		check_sum += fw->data[i];
> > > > +	check_sum = (~check_sum) + 1;
> > > > +	if (check_sum != fw->data[raw_cfg_len]) {
> > > > +		dev_err(&ts->client->dev,
> > > > +			"The checksum of the config buffer array is not correct");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	if (fw->data[raw_cfg_len + 1] != 1) {
> > > > +		dev_err(&ts->client->dev,
> > > > +			"The Config_Fresh register needs to be set");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * goodix_send_cfg - Write device config
> > > > + *
> > > > + * @ts: goodix_ts_data pointer
> > > > + * @id: product id read from device
> > > > + */
> > > > +static int goodix_send_cfg(struct goodix_ts_data *ts, u16 id)
> > > > +{
> > > > +	const struct firmware *fw = NULL;
> > > > +	char *fw_name;
> > > > +	int ret;
> > > > +
> > > > +	fw_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin", id);
> > > > +	if (!fw_name)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	ret = request_firmware(&fw, fw_name, &ts->client->dev);
> > > > +	if (ret) {
> > > > +		dev_err(&ts->client->dev, "Unable to open firmware %s\n",
> > > > +			fw_name);
> > > > +		goto err_free_fw_name;
> > >
> > > That I think will cause driver to abort binding if config is not there.
> > > Do we always need to load the config? Is configuration stored in NVRAM?
> > > Maybe configuration should be only loaded when userspace requests it?
> > >
> >
> > The device has a default configuration, but usually this does not match the platform
> > needs. It is stored in volatile RAM, so we need to rewrite it after power off.
> > From my tests, the default values are invalid if I do not write any config data
> > (goodix_read_config will print "Invalid config, using defaults").
> >
> > However, we should still be able to use the device with these defaults even
> > if the config firmware is not there. I will just return 0 if firmware is not found.
> > Since configuration needs to be written before configuring the input device and
> > the interrupts, I don't think we can allow userspace to control it.
> 
> Well, my concern is that we currently have devices and DTSes that work
> just fine without special config. How do they manage to do that? We need
> to make sure that new changes do not break existing users.
> 

I took a better look at the config issue and it seems to depend on the device/platform.
I have some devices that work just fine with the driver defaults while others
won't work until the config is written (due to custom configuration values for parameters
like trigger type, axis switch-over).

I initially misunderstood the request_firmware_nowait interface, but after doing some
additional tests this seems to be the best option.  I will update the rest of the patches
as well so that devices that do not have gpio pins declared in ACPI/DT or the config fw
present will work just as they do now.

Thanks,
Irina

> Thanks.
> 
> --
> Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index c345eb7..1ce9278 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -24,6 +24,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <linux/of.h>
 #include <asm/unaligned.h>
@@ -95,6 +96,39 @@  static int goodix_i2c_read(struct i2c_client *client,
 	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+			    unsigned len)
+{
+	u8 *addr_buf;
+	struct i2c_msg msg;
+	int ret;
+
+	addr_buf = kmalloc(len + 2, GFP_KERNEL);
+	if (!addr_buf)
+		return -ENOMEM;
+
+	addr_buf[0] = reg >> 8;
+	addr_buf[1] = reg & 0xFF;
+	memcpy(&addr_buf[2], buf, len);
+
+	msg.flags = 0;
+	msg.addr = client->addr;
+	msg.buf = addr_buf;
+	msg.len = len + 2;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	kfree(addr_buf);
+	return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
 static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
 {
 	int touch_num;
@@ -192,6 +226,95 @@  static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/**
+ * goodix_check_cfg - Checks if config buffer is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @fw: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+			    const struct firmware *fw)
+{
+	int i, raw_cfg_len;
+	u8 check_sum = 0;
+
+	if (fw->size > GOODIX_CONFIG_MAX_LENGTH) {
+		dev_err(&ts->client->dev,
+			"The length of the config buffer array is not correct");
+		return -EINVAL;
+	}
+
+	raw_cfg_len = fw->size - 2;
+	for (i = 0; i < raw_cfg_len; i++)
+		check_sum += fw->data[i];
+	check_sum = (~check_sum) + 1;
+	if (check_sum != fw->data[raw_cfg_len]) {
+		dev_err(&ts->client->dev,
+			"The checksum of the config buffer array is not correct");
+		return -EINVAL;
+	}
+
+	if (fw->data[raw_cfg_len + 1] != 1) {
+		dev_err(&ts->client->dev,
+			"The Config_Fresh register needs to be set");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_send_cfg - Write device config
+ *
+ * @ts: goodix_ts_data pointer
+ * @id: product id read from device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts, u16 id)
+{
+	const struct firmware *fw = NULL;
+	char *fw_name;
+	int ret;
+
+	fw_name = kasprintf(GFP_KERNEL, "goodix_%d_cfg.bin", id);
+	if (!fw_name)
+		return -ENOMEM;
+
+	ret = request_firmware(&fw, fw_name, &ts->client->dev);
+	if (ret) {
+		dev_err(&ts->client->dev, "Unable to open firmware %s\n",
+			fw_name);
+		goto err_free_fw_name;
+	}
+	kfree(fw_name);
+
+	ret = goodix_check_cfg(ts, fw);
+	if (ret)
+		goto err_release_fw;
+
+	ret = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, fw->data,
+			       fw->size);
+	if (ret) {
+		dev_err(&ts->client->dev, "Failed to write config data: %d",
+			ret);
+		goto err_release_fw;
+	}
+	dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+	/* Let the firmware reconfigure itself, so sleep for 10ms */
+	usleep_range(10000, 11000);
+
+	release_firmware(fw);
+
+	return 0;
+
+err_release_fw:
+	release_firmware(fw);
+	return ret;
+err_free_fw_name:
+	kfree(fw_name);
+	return ret;
+}
+
 static int goodix_int_sync(struct goodix_ts_data *ts)
 {
 	int ret;
@@ -465,6 +588,11 @@  static int goodix_ts_probe(struct i2c_client *client,
 		return error;
 	}
 
+	/* send device configuration to the firmware */
+	error = goodix_send_cfg(ts, id_info);
+	if (error)
+		return error;
+
 	goodix_read_config(ts);
 
 	error = goodix_request_input_dev(ts, version_info, id_info);