[RFC] misc: Add Allwinner Q8 tablet hardware manager

Pantelis Antoniou pantelis.antoniou at konsulko.com
Fri Sep 9 12:13:20 PDT 2016


Hi Hans,

> On Sep 1, 2016, at 22:08 , Hans de Goede <hdegoede at redhat.com> wrote:
> 
> Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets
> of which a new batch is produced every few weeks. Each batch uses a
> different mix of touchscreen, accelerometer and wifi peripherals.
> 
> Given that each batch is different creating a devicetree for each variant
> is not desirable. This commit adds a Q8 tablet hardware manager which
> auto-detects the touchscreen and accelerometer so that a single generic
> dts can be used for these tablets.
> 
> The wifi is connected to a discoverable bus (sdio or usb) and will be
> autodetected by the mmc resp. usb subsystems.
> 
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
> .../misc/allwinner,sunxi-q8-hardwaremgr.txt        |  52 +++
> drivers/misc/Kconfig                               |  12 +
> drivers/misc/Makefile                              |   1 +
> drivers/misc/q8-hardwaremgr.c                      | 512 +++++++++++++++++++++
> 4 files changed, 577 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
> create mode 100644 drivers/misc/q8-hardwaremgr.c
> 
> diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
> new file mode 100644
> index 0000000..f428bf5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
> @@ -0,0 +1,52 @@
> +Q8 tablet hardware manager
> +--------------------------
> +
> +Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets of
> +which a new batch is produced every few weeks. Each batch uses a different
> +mix of touchscreen, accelerometer and wifi peripherals.
> +
> +Given that each batch is different creating a devicetree for each variant is
> +not desirable. The Q8 tablet hardware manager bindings are bindings for an os
> +module which auto-detects the touchscreen so that a single
> +generic dts can be used for these tablets.
> +
> +The wifi is connected to a discoverable bus and will be autodetected by the os.
> +
> +Required properties:
> + - compatible         : "allwinner,sunxi-q8-hardwaremgr"
> + - touchscreen        : phandle of a template touchscreen node, this must be a
> +		        child node of the touchscreen i2c bus
> +
> +Optional properties:
> + - touchscreen-supply : regulator phandle for the touchscreen vdd supply
> +
> +touschreen node required properties:
> + - interrupt-parent   : phandle pointing to the interrupt controller
> +			serving the touchscreen interrupt
> + - interrupts         : interrupt specification for the touchscreen interrupt
> + - power-gpios        : Specification for the pin connected to the touchscreen's
> +			enable / wake pin. This needs to be driven high to
> +			enable the touchscreen controller
> +
> +Example:
> +
> +/ {
> +	hwmgr {
> +		compatible = "allwinner,sunxi-q8-hardwaremgr";
> +		touchscreen = <&touchscreen>;
> +		touchscreen-supply = <&reg_ldo_io1>;
> +	};
> +};
> +
> +&i2c0 {
> +	touchscreen: touchscreen at 0 {
> +		interrupt-parent = <&pio>;
> +		interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>; /* PB5 */
> +		power-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */
> +		/*
> +		 * Enabled by sunxi-q8-hardwaremgr if it detects a
> +		 * known model touchscreen.
> +		 */
> +		status = "disabled";
> +	};
> +};
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index a216b46..c3e7772 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -804,6 +804,18 @@ config PANEL_BOOT_MESSAGE
> 	  An empty message will only clear the display at driver init time. Any other
> 	  printf()-formatted message is valid with newline and escape codes.
> 
> +config Q8_HARDWAREMGR
> +	tristate "Allwinner Q8 tablet hardware manager"
> +	depends on GPIOLIB || COMPILE_TEST
> +	depends on I2C
> +	depends on OF
> +	default	n
> +	help
> +	  This option enables support for autodetecting the touchscreen
> +	  on Allwinner Q8 tablets.
> +
> +	  If unsure, say N.
> +
> source "drivers/misc/c2port/Kconfig"
> source "drivers/misc/eeprom/Kconfig"
> source "drivers/misc/cb710/Kconfig"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 7410c6d..cac76b7 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_ECHO)		+= echo/
> obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
> obj-$(CONFIG_CXL_BASE)		+= cxl/
> obj-$(CONFIG_PANEL)             += panel.o
> +obj-$(CONFIG_Q8_HARDWAREMGR)    += q8-hardwaremgr.o
> 
> lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
> lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
> diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
> new file mode 100644
> index 0000000..e75625e
> --- /dev/null
> +++ b/drivers/misc/q8-hardwaremgr.c
> @@ -0,0 +1,512 @@
> +/*
> + * Allwinner q8 formfactor tablet hardware manager
> + *
> + * Copyright (C) 2016 Hans de Goede <hdegoede at redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +/*
> + * We can detect which touchscreen controller is used automatically,
> + * but some controllers can be wired up differently depending on the
> + * q8 PCB variant used, so they need different firmware files / settings.
> + *
> + * We allow the user to specify a firmware_variant to select a config
> + * from a list of known configs. We also allow overriding each setting
> + * individually.
> + */
> +
> +static int touchscreen_variant = -1;
> +module_param(touchscreen_variant, int, 0444);
> +MODULE_PARM_DESC(touchscreen_variant, "Touchscreen variant 0-x, -1 for auto");
> +
> +static int touchscreen_width = -1;
> +module_param(touchscreen_width, int, 0444);
> +MODULE_PARM_DESC(touchscreen_width, "Touchscreen width, -1 for auto");
> +
> +static int touchscreen_height = -1;
> +module_param(touchscreen_height, int, 0444);
> +MODULE_PARM_DESC(touchscreen_height, "Touchscreen height, -1 for auto");
> +
> +static int touchscreen_invert_x = -1;
> +module_param(touchscreen_invert_x, int, 0444);
> +MODULE_PARM_DESC(touchscreen_invert_x, "Touchscreen invert x, -1 for auto");
> +
> +static int touchscreen_invert_y = -1;
> +module_param(touchscreen_invert_y, int, 0444);
> +MODULE_PARM_DESC(touchscreen_invert_y, "Touchscreen invert y, -1 for auto");
> +
> +static int touchscreen_swap_x_y = -1;
> +module_param(touchscreen_swap_x_y, int, 0444);
> +MODULE_PARM_DESC(touchscreen_swap_x_y, "Touchscreen swap x y, -1 for auto");
> +
> +static char *touchscreen_fw_name;
> +module_param(touchscreen_fw_name, charp, 0444);
> +MODULE_PARM_DESC(touchscreen_fw_name, "Touchscreen firmware filename");
> +
> +#define TOUCHSCREEN_POWER_ON_DELAY	20
> +#define SILEAD_REG_ID			0xFC
> +#define EKTF2127_RESPONSE		0x52
> +#define EKTF2127_REQUEST		0x53
> +#define EKTF2127_WIDTH			0x63
> +
> +enum touchscreen_model {
> +	touchscreen_unknown,
> +	gsl1680_a082,
> +	gsl1680_b482,
> +	ektf2127,
> +	zet6251,
> +};
> +
> +struct q8_hardwaremgr_data {
> +	struct device *dev;
> +	bool touchscreen_needs_regulator;
> +	enum touchscreen_model touchscreen_model;
> +	int touchscreen_addr;
> +	int touchscreen_variant;
> +	int touchscreen_width;
> +	int touchscreen_height;
> +	int touchscreen_invert_x;
> +	int touchscreen_invert_y;
> +	int touchscreen_swap_x_y;
> +	const char *touchscreen_compatible;
> +	const char *touchscreen_fw_name;
> +};
> +
> +typedef int (*probe_func)(struct q8_hardwaremgr_data *data,
> +			  struct i2c_adapter *adap);
> +
> +#if 0
> +	ret = i2c_smbus_xfer(adap, 0x40, 0, I2C_SMBUS_WRITE, 0,
> +			     I2C_SMBUS_QUICK, NULL);
> +	if (ret < 0)
> +		return -ENODEV;
> +
> +#endif
> +

^^^ crud?

> +static int q8_hardwaremgr_probe_touchscreen(struct q8_hardwaremgr_data *data,
> +					    struct i2c_adapter *adap)
> +{
> +	struct i2c_client *client;
> +	unsigned char buff[24];
> +	__le32 chip_id;
> +	int ret;
> +
> +	msleep(TOUCHSCREEN_POWER_ON_DELAY);
> +
> +	/* Check for silead touchsceen at addr 0x40 */
> +	client = i2c_new_dummy(adap, 0x40);
> +	if (!client)
> +		return -ENOMEM;
> +
> +	ret = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
> +					    sizeof(chip_id), (u8 *)&chip_id);
> +	if (ret == sizeof(chip_id)) {
> +		switch (le32_to_cpu(chip_id)) {
> +		case 0xa0820000:
> +			data->touchscreen_addr = 0x40;
> +			data->touchscreen_compatible = "silead,gsl1680";
> +			data->touchscreen_model = gsl1680_a082;
> +			dev_info(data->dev, "Found Silead touchscreen ID: 0xa0820000\n");
> +			break;
> +		case 0xb4820000:
> +			data->touchscreen_addr = 0x40;
> +			data->touchscreen_compatible = "silead,gsl1680";
> +			data->touchscreen_model = gsl1680_b482;
> +			dev_info(data->dev, "Found Silead touchscreen ID: 0xb4820000\n");
> +			break;

> +		default:
> +			dev_warn(data->dev, "Found Silead touchscreen with unknown ID: 0x%08x\n",
> +				 le32_to_cpu(chip_id));
> +		}
> +		ret = 0;
> +	}
> +	i2c_unregister_device(client);
> +	if (ret == 0 || ret == -ETIMEDOUT /* Bus stuck bail immediately */)
> +		return ret;
> +
> +	/* Check for Elan eKTF2127 touchsceen at addr 0x15 */
> +	client = i2c_new_dummy(adap, 0x15);
> +	if (!client)
> +		return -ENOMEM;
> +
> +	do {
> +		/* Read hello, ignore data, depends on initial power state */
> +		ret = i2c_master_recv(client, buff, 4);
> +		if (ret != 4)
> +			break;
> +
> +		/* Request width */
> +		buff[0] = EKTF2127_REQUEST;
> +		buff[1] = EKTF2127_WIDTH;
> +		buff[2] = 0x00;
> +		buff[3] = 0x00;
> +		ret = i2c_master_send(client, buff, 4);
> +		if (ret != 4)
> +			break;
> +
> +		msleep(20);
> +
> +		/* Read response */
> +		ret = i2c_master_recv(client, buff, 4);
> +		if (ret != 4)
> +			break;
> +			
> +		if (buff[0] == EKTF2127_RESPONSE && buff[1] == EKTF2127_WIDTH) {
> +			data->touchscreen_addr = 0x15;
> +			data->touchscreen_compatible = "elan,ektf2127";
> +			data->touchscreen_model = ektf2127;
> +			dev_info(data->dev, "Found Elan eKTF2127 touchscreen\n");
> +			ret = 0;
> +		}
> +	} while (0);
> +	i2c_unregister_device(client);
> +	if (ret == 0 || ret == -ETIMEDOUT /* Bus stuck bail immediately */)
> +		return ret;
> +
> +	/* Check for Zeitec zet6251 touchsceen at addr 0x76 */
> +	client = i2c_new_dummy(adap, 0x76);
> +	if (!client)
> +		return -ENOMEM;
> +
> +	/*
> +	 * We only do a simple read finger data packet test, because some
> +	 * versions require firmware to be loaded. If not firmware is loaded
> +	 * the buffer will be filed with 0xff, so we ignore the contents.
> +	 */
> +	ret = i2c_master_recv(client, buff, 24);
> +	if (ret == 24) {
> +		data->touchscreen_addr = 0x76;
> +		data->touchscreen_compatible = "zeitec,zet6251";
> +		data->touchscreen_model = zet6251;
> +		dev_info(data->dev, "Found Zeitec zet6251 touchscreen\n");
> +		ret = 0;
> +	}

I can understand having a switch here since it’s quite complicated but it would be better to
have a structure that defines them i.e.

struct touchscreen_detect_data {
	u32 chip_id;
	int addr;
	const char *compatible;
	enum touchscreen_model model;
	
};

static const struct touchscreen_detect_data ts_detect_data[] = {
	{
		.chip_id = 0xa0820000,
		.addr = 0x40,
		.compatible = “silead,gsl1680”,
		.model = gsl1680_a082,
	}, ...
};
 
And so on, and restructuring by having different paths by touchscreen model type.


> +	i2c_unregister_device(client);
> +	if (ret == 0 || ret == -ETIMEDOUT /* Bus stuck bail immediately */)
> +		return ret;
> +
> +	return -ENODEV;
> +}
> +
> +static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
> +				   const char *prefix, probe_func func)
> +{
> +	struct device *dev = data->dev;
> +	struct device_node *np;
> +	struct i2c_adapter *adap;
> +	struct regulator *reg;
> +	struct gpio_desc *gpio;
> +	int ret = 0;
> +
> +	np = of_parse_phandle(dev->of_node, prefix, 0);
> +	if (!np) {
> +		dev_err(dev, "Error %s not set\n", prefix);
> +		return -EINVAL;
> +	}
> +
> +	adap = of_get_i2c_adapter_by_node(np->parent);
> +	if (!adap) {
> +		ret = -EPROBE_DEFER;
> +		goto put_node;
> +	}
> +
> +	reg = regulator_get_optional(dev, prefix);
> +	if (IS_ERR(reg)) {
> +		ret = PTR_ERR(reg);
> +		if (ret == -EPROBE_DEFER)
> +			goto put_adapter;
> +		reg = NULL;
> +	}
> +
> +	gpio = fwnode_get_named_gpiod(&np->fwnode, "power-gpios");
> +	if (IS_ERR(gpio)) {
> +		ret = PTR_ERR(gpio);
> +		if (ret == -EPROBE_DEFER)
> +			goto put_reg;
> +		gpio = NULL;
> +	}
> +
> +	/* First try with only the power gpio driven high */
> +	if (gpio) {
> +		ret = gpiod_direction_output(gpio, 1);
> +		if (ret)
> +			goto put_gpio;
> +	}
> +
> +	dev_info(dev, "Looking for %s without a regulator\n", prefix);
> +	ret = func(data, adap);
> +	if (ret != 0 && reg) {
> +		/* Second try, also enable the regulator */
> +		ret = regulator_enable(reg);
> +		if (ret)
> +			goto restore_gpio;
> +
> +		dev_info(dev, "Looking for %s with a regulator\n", prefix);
> +		ret = func(data, adap);
> +		if (ret == 0)
> +			data->touchscreen_needs_regulator = true; 
> +
> +		regulator_disable(reg);
> +	}
> +	ret = 0; /* Not finding a device is not an error */
> +
> +restore_gpio:
> +	if (gpio)
> +		gpiod_direction_output(gpio, 0);
> +put_gpio:
> +	if (gpio)
> +		gpiod_put(gpio);
> +put_reg:
> +	if (reg)
> +		regulator_put(reg);
> +put_adapter:
> +	i2c_put_adapter(adap);
> +
> +put_node:
> +	of_node_put(np);
> +
> +	return ret;
> +}
> +
> +static void q8_hardwaremgr_apply_gsl1680_a082_variant(
> +	struct q8_hardwaremgr_data *data)
> +{
> +	if (touchscreen_variant != -1) {
> +		data->touchscreen_variant = touchscreen_variant;
> +	} else {
> +		if (of_machine_is_compatible("allwinner,sun8i-a33"))
> +			data->touchscreen_variant = 1;
> +		else
> +			data->touchscreen_variant = 0;
> +	}
> +
> +	switch (data->touchscreen_variant) {
> +	default:
> +		dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
> +			 touchscreen_variant);
> +		/* Fall through */
> +	case 0:
> +		data->touchscreen_width = 1024;
> +		data->touchscreen_height = 600;
> +		data->touchscreen_fw_name = "gsl1680-a082-q8-700.fw";
> +		break;
> +	case 1:
> +		data->touchscreen_width = 480;
> +		data->touchscreen_height = 800;
> +		data->touchscreen_swap_x_y = 1;
> +		data->touchscreen_fw_name = "gsl1680-a082-q8-a70.fw";
> +		break;
> +	}
> +}
> +
> +static void q8_hardwaremgr_apply_gsl1680_b482_variant(
> +	struct q8_hardwaremgr_data *data)
> +{
> +	if (touchscreen_variant != -1)
> +		data->touchscreen_variant = touchscreen_variant;
> +
> +	switch (data->touchscreen_variant) {
> +	default:
> +		dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
> +			 touchscreen_variant);
> +		/* Fall through */
> +	case 0:
> +		data->touchscreen_width = 960;
> +		data->touchscreen_height = 640;
> +		data->touchscreen_fw_name = "gsl1680-b482-q8-d702.fw";
> +		break;
> +	case 1:
> +		data->touchscreen_width = 960;
> +		data->touchscreen_height = 640;
> +		data->touchscreen_fw_name = "gsl1680-b482-q8-a70.fw";
> +		break;
> +	}
> +}
> +
> +static void q8_hardwaremgr_issue_gsl1680_warning(
> +	struct q8_hardwaremgr_data *data)
> +{
> +	dev_warn(data->dev, "gsl1680 touchscreen may require kernel cmdline parameters to function properly\n");
> +	dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_invert_x=1 if x coordinates are inverted\n");
> +	dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_variant=%d if coordinates are all over the place\n",
> +		 !data->touchscreen_variant);
> +
> +#define	show(x) \
> +	dev_info(data->dev, #x " %d (%s)\n", data->x, \
> +		 (x == -1) ? "auto" : "user supplied")
> +
> +	show(touchscreen_variant);
> +	show(touchscreen_width);
> +	show(touchscreen_height);
> +	show(touchscreen_invert_x);
> +	show(touchscreen_invert_y);
> +	show(touchscreen_swap_x_y);
> +	dev_info(data->dev, "touchscreen_fw_name %s (%s)\n",
> +		 data->touchscreen_fw_name,
> +		 (touchscreen_fw_name == NULL) ? "auto" : "user supplied");
> +#undef show
> +}
> +
> +static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
> +{
> +	struct device *dev = data->dev;
> +	struct of_changeset cset;
> +	struct device_node *np;
> +
> +	switch (data->touchscreen_model) {
> +	case touchscreen_unknown:
> +		return;
> +	case gsl1680_a082:
> +		q8_hardwaremgr_apply_gsl1680_a082_variant(data);
> +		break;
> +	case gsl1680_b482:
> +		q8_hardwaremgr_apply_gsl1680_b482_variant(data);
> +		break;
> +	case ektf2127:
> +	case zet6251:
> +		/* These have only 1 variant */
> +		break;
> +	}
> +
> +	if (touchscreen_width != -1)
> +		data->touchscreen_width = touchscreen_width;
> +
> +	if (touchscreen_height != -1)
> +		data->touchscreen_height = touchscreen_height;
> +
> +	if (touchscreen_invert_x != -1)
> +		data->touchscreen_invert_x = touchscreen_invert_x;
> +
> +	if (touchscreen_invert_y != -1)
> +		data->touchscreen_invert_y = touchscreen_invert_y;
> +
> +	if (touchscreen_swap_x_y != -1)
> +		data->touchscreen_swap_x_y = touchscreen_swap_x_y;
> +
> +	if (touchscreen_fw_name)
> +		data->touchscreen_fw_name = touchscreen_fw_name;
> +
> +	if (data->touchscreen_model == gsl1680_a082 ||
> +	    data->touchscreen_model == gsl1680_b482)
> +		q8_hardwaremgr_issue_gsl1680_warning(data);
> +
> +	np = of_parse_phandle(data->dev->of_node, "touchscreen", 0);
> +	/* Never happens already checked in q8_hardwaremgr_do_probe() */
> +	if (WARN_ON(!np))
> +		return;
> +
> +	of_changeset_init(&cset);
> +	of_changeset_add_property_u32(&cset, np, "reg", data->touchscreen_addr);
> +	of_changeset_add_property_string(&cset, np, "compatible",
> +					 data->touchscreen_compatible);
> +
> +	if (data->touchscreen_width)
> +		of_changeset_add_property_u32(&cset, np, "touchscreen-size-x",
> +					      data->touchscreen_width);
> +	if (data->touchscreen_height)
> +		of_changeset_add_property_u32(&cset, np, "touchscreen-size-y",
> +					      data->touchscreen_height);
> +	if (data->touchscreen_invert_x)
> +		of_changeset_add_property_bool(&cset, np,
> +					       "touchscreen-inverted-x");
> +	if (data->touchscreen_invert_y)
> +		of_changeset_add_property_bool(&cset, np,
> +					       "touchscreen-inverted-y");
> +	if (data->touchscreen_swap_x_y)
> +		of_changeset_add_property_bool(&cset, np,
> +					       "touchscreen-swapped-x-y");
> +	if (data->touchscreen_fw_name)
> +		of_changeset_add_property_string(&cset, np, "firmware-name",
> +						 data->touchscreen_fw_name);
> +	if (data->touchscreen_needs_regulator) {
> +		struct property *p;
> +
> +		p = of_find_property(dev->of_node, "touchscreen-supply", NULL);
> +		/* Never happens already checked in q8_hardwaremgr_do_probe() */
> +		if (WARN_ON(!p))
> +			return;
> +
> +		of_changeset_add_property_copy(&cset, np, "vddio-supply",
> +					       p->value, p->length);
> +	}
> +
> +	of_changeset_update_property_string(&cset, np, "status", "okay");
> +	of_changeset_apply(&cset);
> +
> +	of_node_put(np);
> +}
> +
> +static int q8_hardwaremgr_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct q8_hardwaremgr_data *data;
> +	int ret = 0;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->dev = &pdev->dev;
> +
> +	ret = q8_hardwaremgr_do_probe(data, "touchscreen",
> +				      q8_hardwaremgr_probe_touchscreen);
> +	if (ret)
> +		goto error;
> +
> +	/*
> +	 * Our pinctrl may conflict with the pinctrl of the detected devices
> +	 * we're adding, so remove it before adding detected devices.
> +	 */
> +	if (dev->pins) {
> +		devm_pinctrl_put(dev->pins->p);
> +		devm_kfree(dev, dev->pins);
> +		dev->pins = NULL;
> +	}
> +

Hmm, that’s weird for sure. How can it happen?

> +	q8_hardwaremgr_apply_touchscreen(data);
> +
> +error:
> +	kfree(data);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id q8_hardwaremgr_of_match[] = {
> +	{ .compatible = "allwinner,sunxi-q8-hardwaremgr", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, q8_hardwaremgr_of_match);
> +
> +static struct platform_driver q8_hardwaremgr_driver = {
> +	.driver = {
> +		.name	= "q8-hardwaremgr",
> +		.of_match_table = of_match_ptr(q8_hardwaremgr_of_match),
> +	},
> +	.probe	= q8_hardwaremgr_probe,
> +};
> +
> +module_platform_driver(q8_hardwaremgr_driver);
> +
> +MODULE_DESCRIPTION("Allwinner q8 formfactor tablet hardware manager");
> +MODULE_AUTHOR("Hans de Goede <hdegoede at redhat.com>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.9.3
> 

Regards

— Pantelis




More information about the linux-arm-kernel mailing list