diff options
Diffstat (limited to 'drivers/input/touchscreen/ads7846.c')
| -rw-r--r-- | drivers/input/touchscreen/ads7846.c | 346 | 
1 files changed, 215 insertions, 131 deletions
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 14ea54b78e4..da201b8e37d 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -19,17 +19,21 @@   */  #include <linux/types.h>  #include <linux/hwmon.h> -#include <linux/init.h>  #include <linux/err.h>  #include <linux/sched.h>  #include <linux/delay.h>  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/slab.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h>  #include <linux/gpio.h>  #include <linux/spi/spi.h>  #include <linux/spi/ads7846.h>  #include <linux/regulator/consumer.h> +#include <linux/module.h>  #include <asm/irq.h>  /* @@ -96,8 +100,7 @@ struct ads7846 {  	struct spi_device	*spi;  	struct regulator	*reg; -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) -	struct attribute_group	*attr_group; +#if IS_ENABLED(CONFIG_HWMON)  	struct device		*hwmon;  #endif @@ -108,6 +111,7 @@ struct ads7846 {  	u16			pressure_max;  	bool			swap_xy; +	bool			use_internal;  	struct ads7846_packet	*packet; @@ -233,7 +237,12 @@ static void __ads7846_disable(struct ads7846 *ts)  /* Must be called with ts->lock held */  static void __ads7846_enable(struct ads7846 *ts)  { -	regulator_enable(ts->reg); +	int error; + +	error = regulator_enable(ts->reg); +	if (error != 0) +		dev_err(&ts->spi->dev, "Failed to enable supply: %d\n", error); +  	ads7846_restart(ts);  } @@ -280,17 +289,24 @@ struct ser_req {  	u8			command;  	u8			ref_off;  	u16			scratch; -	__be16			sample;  	struct spi_message	msg;  	struct spi_transfer	xfer[6]; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	__be16 sample ____cacheline_aligned;  };  struct ads7845_ser_req {  	u8			command[3]; -	u8			pwrdown[3]; -	u8			sample[3];  	struct spi_message	msg;  	struct spi_transfer	xfer[2]; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	u8 sample[3] ____cacheline_aligned;  };  static int ads7846_read12_ser(struct device *dev, unsigned command) @@ -299,7 +315,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  	struct ads7846 *ts = dev_get_drvdata(dev);  	struct ser_req *req;  	int status; -	int use_internal;  	req = kzalloc(sizeof *req, GFP_KERNEL);  	if (!req) @@ -307,11 +322,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  	spi_message_init(&req->msg); -	/* FIXME boards with ads7846 might use external vref instead ... */ -	use_internal = (ts->model == 7846); -  	/* maybe turn on internal vREF, and let it settle */ -	if (use_internal) { +	if (ts->use_internal) {  		req->ref_on = REF_ON;  		req->xfer[0].tx_buf = &req->ref_on;  		req->xfer[0].len = 1; @@ -323,8 +335,14 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)  		/* for 1uF, settle for 800 usec; no cap, 100 usec.  */  		req->xfer[1].delay_usecs = ts->vref_delay_usecs;  		spi_message_add_tail(&req->xfer[1], &req->msg); + +		/* Enable reference voltage */ +		command |= ADS_PD10_REF_ON;  	} +	/* Enable ADC in every case */ +	command |= ADS_PD10_ADC_ON; +  	/* take sample */  	req->command = (u8) command;  	req->xfer[2].tx_buf = &req->command; @@ -401,14 +419,14 @@ static int ads7845_read12_ser(struct device *dev, unsigned command)  	return status;  } -#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) +#if IS_ENABLED(CONFIG_HWMON)  #define SHOW(name, var, adjust) static ssize_t \  name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \  { \  	struct ads7846 *ts = dev_get_drvdata(dev); \ -	ssize_t v = ads7846_read12_ser(dev, \ -			READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ +	ssize_t v = ads7846_read12_ser(&ts->spi->dev, \ +			READ_12BIT_SER(var)); \  	if (v < 0) \  		return v; \  	return sprintf(buf, "%u\n", adjust(ts, v)); \ @@ -459,48 +477,43 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)  SHOW(in0_input, vaux, vaux_adjust)  SHOW(in1_input, vbatt, vbatt_adjust) -static struct attribute *ads7846_attributes[] = { -	&dev_attr_temp0.attr, -	&dev_attr_temp1.attr, -	&dev_attr_in0_input.attr, -	&dev_attr_in1_input.attr, -	NULL, -}; - -static struct attribute_group ads7846_attr_group = { -	.attrs = ads7846_attributes, -}; +static umode_t ads7846_is_visible(struct kobject *kobj, struct attribute *attr, +				  int index) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct ads7846 *ts = dev_get_drvdata(dev); -static struct attribute *ads7843_attributes[] = { -	&dev_attr_in0_input.attr, -	&dev_attr_in1_input.attr, -	NULL, -}; +	if (ts->model == 7843 && index < 2)	/* in0, in1 */ +		return 0; +	if (ts->model == 7845 && index != 2)	/* in0 */ +		return 0; -static struct attribute_group ads7843_attr_group = { -	.attrs = ads7843_attributes, -}; +	return attr->mode; +} -static struct attribute *ads7845_attributes[] = { -	&dev_attr_in0_input.attr, +static struct attribute *ads7846_attributes[] = { +	&dev_attr_temp0.attr,		/* 0 */ +	&dev_attr_temp1.attr,		/* 1 */ +	&dev_attr_in0_input.attr,	/* 2 */ +	&dev_attr_in1_input.attr,	/* 3 */  	NULL,  }; -static struct attribute_group ads7845_attr_group = { -	.attrs = ads7845_attributes, +static struct attribute_group ads7846_attr_group = { +	.attrs = ads7846_attributes, +	.is_visible = ads7846_is_visible,  }; +__ATTRIBUTE_GROUPS(ads7846_attr);  static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)  { -	struct device *hwmon; -	int err; -  	/* hwmon sensors need a reference voltage */  	switch (ts->model) {  	case 7846:  		if (!ts->vref_mv) {  			dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");  			ts->vref_mv = 2500; +			ts->use_internal = true;  		}  		break;  	case 7845: @@ -514,43 +527,19 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)  		break;  	} -	/* different chips have different sensor groups */ -	switch (ts->model) { -	case 7846: -		ts->attr_group = &ads7846_attr_group; -		break; -	case 7845: -		ts->attr_group = &ads7845_attr_group; -		break; -	case 7843: -		ts->attr_group = &ads7843_attr_group; -		break; -	default: -		dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); -		return 0; -	} - -	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); -	if (err) -		return err; +	ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias, +						      ts, ads7846_attr_groups); +	if (IS_ERR(ts->hwmon)) +		return PTR_ERR(ts->hwmon); -	hwmon = hwmon_device_register(&spi->dev); -	if (IS_ERR(hwmon)) { -		sysfs_remove_group(&spi->dev.kobj, ts->attr_group); -		return PTR_ERR(hwmon); -	} - -	ts->hwmon = hwmon;  	return 0;  }  static void ads784x_hwmon_unregister(struct spi_device *spi,  				     struct ads7846 *ts)  { -	if (ts->hwmon) { -		sysfs_remove_group(&spi->dev.kobj, ts->attr_group); +	if (ts->hwmon)  		hwmon_device_unregister(ts->hwmon); -	}  }  #else @@ -589,10 +578,12 @@ static ssize_t ads7846_disable_store(struct device *dev,  				     const char *buf, size_t count)  {  	struct ads7846 *ts = dev_get_drvdata(dev); -	unsigned long i; +	unsigned int i; +	int err; -	if (strict_strtoul(buf, 10, &i)) -		return -EINVAL; +	err = kstrtouint(buf, 10, &i); +	if (err) +		return err;  	if (i)  		ads7846_disable(ts); @@ -715,7 +706,7 @@ static void ads7846_read_state(struct ads7846 *ts)  		m = &ts->msg[msg_idx];  		error = spi_sync(ts->spi, m);  		if (error) { -			dev_err(&ts->spi->dev, "spi_async --> %d\n", error); +			dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);  			packet->tc.ignore = true;  			return;  		} @@ -892,9 +883,10 @@ static irqreturn_t ads7846_irq(int irq, void *handle)  	return IRQ_HANDLED;  } -static int ads7846_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ads7846_suspend(struct device *dev)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = dev_get_drvdata(dev);  	mutex_lock(&ts->lock); @@ -914,9 +906,9 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)  	return 0;  } -static int ads7846_resume(struct spi_device *spi) +static int ads7846_resume(struct device *dev)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = dev_get_drvdata(dev);  	mutex_lock(&ts->lock); @@ -935,34 +927,44 @@ static int ads7846_resume(struct spi_device *spi)  	return 0;  } +#endif -static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) +static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); + +static int ads7846_setup_pendown(struct spi_device *spi, +				 struct ads7846 *ts, +				 const struct ads7846_platform_data *pdata)  { -	struct ads7846_platform_data *pdata = spi->dev.platform_data;  	int err; -	/* REVISIT when the irq can be triggered active-low, or if for some +	/* +	 * REVISIT when the irq can be triggered active-low, or if for some  	 * reason the touchscreen isn't hooked up, we don't need to access  	 * the pendown state.  	 */ -	if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { -		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); -		return -EINVAL; -	}  	if (pdata->get_pendown_state) {  		ts->get_pendown_state = pdata->get_pendown_state; -		return 0; -	} +	} else if (gpio_is_valid(pdata->gpio_pendown)) { + +		err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN, +				       "ads7846_pendown"); +		if (err) { +			dev_err(&spi->dev, +				"failed to request/setup pendown GPIO%d: %d\n", +				pdata->gpio_pendown, err); +			return err; +		} -	err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); -	if (err) { -		dev_err(&spi->dev, "failed to request pendown GPIO%d\n", -			pdata->gpio_pendown); -		return err; -	} +		ts->gpio_pendown = pdata->gpio_pendown; -	ts->gpio_pendown = pdata->gpio_pendown; +		if (pdata->gpio_pendown_debounce) +			gpio_set_debounce(pdata->gpio_pendown, +					  pdata->gpio_pendown_debounce); +	} else { +		dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); +		return -EINVAL; +	}  	return 0;  } @@ -971,8 +973,8 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784   * Set up the transfers to read touchscreen state; this assumes we   * use formula #2 for pressure, not #3.   */ -static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, -				const struct ads7846_platform_data *pdata) +static void ads7846_setup_spi_msg(struct ads7846 *ts, +				  const struct ads7846_platform_data *pdata)  {  	struct spi_message *m = &ts->msg[0];  	struct spi_transfer *x = ts->xfer; @@ -1170,33 +1172,107 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts,  	spi_message_add_tail(x, m);  } -static int __devinit ads7846_probe(struct spi_device *spi) +#ifdef CONFIG_OF +static const struct of_device_id ads7846_dt_ids[] = { +	{ .compatible = "ti,tsc2046",	.data = (void *) 7846 }, +	{ .compatible = "ti,ads7843",	.data = (void *) 7843 }, +	{ .compatible = "ti,ads7845",	.data = (void *) 7845 }, +	{ .compatible = "ti,ads7846",	.data = (void *) 7846 }, +	{ .compatible = "ti,ads7873",	.data = (void *) 7873 }, +	{ } +}; +MODULE_DEVICE_TABLE(of, ads7846_dt_ids); + +static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) +{ +	struct ads7846_platform_data *pdata; +	struct device_node *node = dev->of_node; +	const struct of_device_id *match; + +	if (!node) { +		dev_err(dev, "Device does not have associated DT data\n"); +		return ERR_PTR(-EINVAL); +	} + +	match = of_match_device(ads7846_dt_ids, dev); +	if (!match) { +		dev_err(dev, "Unknown device model\n"); +		return ERR_PTR(-EINVAL); +	} + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); + +	pdata->model = (unsigned long)match->data; + +	of_property_read_u16(node, "ti,vref-delay-usecs", +			     &pdata->vref_delay_usecs); +	of_property_read_u16(node, "ti,vref-mv", &pdata->vref_mv); +	pdata->keep_vref_on = of_property_read_bool(node, "ti,keep-vref-on"); + +	pdata->swap_xy = of_property_read_bool(node, "ti,swap-xy"); + +	of_property_read_u16(node, "ti,settle-delay-usec", +			     &pdata->settle_delay_usecs); +	of_property_read_u16(node, "ti,penirq-recheck-delay-usecs", +			     &pdata->penirq_recheck_delay_usecs); + +	of_property_read_u16(node, "ti,x-plate-ohms", &pdata->x_plate_ohms); +	of_property_read_u16(node, "ti,y-plate-ohms", &pdata->y_plate_ohms); + +	of_property_read_u16(node, "ti,x-min", &pdata->x_min); +	of_property_read_u16(node, "ti,y-min", &pdata->y_min); +	of_property_read_u16(node, "ti,x-max", &pdata->x_max); +	of_property_read_u16(node, "ti,y-max", &pdata->y_max); + +	of_property_read_u16(node, "ti,pressure-min", &pdata->pressure_min); +	of_property_read_u16(node, "ti,pressure-max", &pdata->pressure_max); + +	of_property_read_u16(node, "ti,debounce-max", &pdata->debounce_max); +	of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol); +	of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep); + +	of_property_read_u32(node, "ti,pendown-gpio-debounce", +			     &pdata->gpio_pendown_debounce); + +	pdata->wakeup = of_property_read_bool(node, "linux,wakeup"); + +	pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0); + +	return pdata; +} +#else +static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev) +{ +	dev_err(dev, "no platform data defined\n"); +	return ERR_PTR(-EINVAL); +} +#endif + +static int ads7846_probe(struct spi_device *spi)  { +	const struct ads7846_platform_data *pdata;  	struct ads7846 *ts;  	struct ads7846_packet *packet;  	struct input_dev *input_dev; -	struct ads7846_platform_data *pdata = spi->dev.platform_data;  	unsigned long irq_flags;  	int err;  	if (!spi->irq) {  		dev_dbg(&spi->dev, "no IRQ?\n"); -		return -ENODEV; -	} - -	if (!pdata) { -		dev_dbg(&spi->dev, "no platform data?\n"); -		return -ENODEV; +		return -EINVAL;  	}  	/* don't exceed max specified sample rate */  	if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { -		dev_dbg(&spi->dev, "f(sample) %d KHz?\n", +		dev_err(&spi->dev, "f(sample) %d KHz?\n",  				(spi->max_speed_hz/SAMPLE_BITS)/1000);  		return -EINVAL;  	} -	/* We'd set TX word size 8 bits and RX word size to 13 bits ... except +	/* +	 * We'd set TX word size 8 bits and RX word size to 13 bits ... except  	 * that even if the hardware can do that, the SPI controller driver  	 * may not.  So we stick to very-portable 8 bit words, both RX and TX.  	 */ @@ -1214,22 +1290,30 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		goto err_free_mem;  	} -	dev_set_drvdata(&spi->dev, ts); +	spi_set_drvdata(spi, ts);  	ts->packet = packet;  	ts->spi = spi;  	ts->input = input_dev; -	ts->vref_mv = pdata->vref_mv; -	ts->swap_xy = pdata->swap_xy;  	mutex_init(&ts->lock);  	init_waitqueue_head(&ts->wait); +	pdata = dev_get_platdata(&spi->dev); +	if (!pdata) { +		pdata = ads7846_probe_dt(&spi->dev); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); +	} +  	ts->model = pdata->model ? : 7846;  	ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;  	ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;  	ts->pressure_max = pdata->pressure_max ? : ~0; +	ts->vref_mv = pdata->vref_mv; +	ts->swap_xy = pdata->swap_xy; +  	if (pdata->filter != NULL) {  		if (pdata->filter_init != NULL) {  			err = pdata->filter_init(pdata, &ts->filter_data); @@ -1250,7 +1334,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  		ts->filter = ads7846_no_filter;  	} -	err = ads7846_setup_pendown(spi, ts); +	err = ads7846_setup_pendown(spi, ts, pdata);  	if (err)  		goto err_cleanup_filter; @@ -1327,8 +1411,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	if (ts->model == 7845)  		ads7845_read12_ser(&spi->dev, PWRDOWN);  	else -		(void) ads7846_read12_ser(&spi->dev, -				READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); +		(void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));  	err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);  	if (err) @@ -1340,6 +1423,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	device_init_wakeup(&spi->dev, pdata->wakeup); +	/* +	 * If device does not carry platform data we must have allocated it +	 * when parsing DT data. +	 */ +	if (!dev_get_platdata(&spi->dev)) +		devm_kfree(&spi->dev, (void *)pdata); +  	return 0;   err_remove_attr_group: @@ -1353,7 +1443,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)   err_put_regulator:  	regulator_put(ts->reg);   err_free_gpio: -	if (ts->gpio_pendown != -1) +	if (!ts->get_pendown_state)  		gpio_free(ts->gpio_pendown);   err_cleanup_filter:  	if (ts->filter_cleanup) @@ -1365,9 +1455,9 @@ static int __devinit ads7846_probe(struct spi_device *spi)  	return err;  } -static int __devexit ads7846_remove(struct spi_device *spi) +static int ads7846_remove(struct spi_device *spi)  { -	struct ads7846 *ts = dev_get_drvdata(&spi->dev); +	struct ads7846 *ts = spi_get_drvdata(spi);  	device_init_wakeup(&spi->dev, false); @@ -1383,8 +1473,13 @@ static int __devexit ads7846_remove(struct spi_device *spi)  	regulator_disable(ts->reg);  	regulator_put(ts->reg); -	if (ts->gpio_pendown != -1) +	if (!ts->get_pendown_state) { +		/* +		 * If we are not using specialized pendown method we must +		 * have been relying on gpio we set up ourselves. +		 */  		gpio_free(ts->gpio_pendown); +	}  	if (ts->filter_cleanup)  		ts->filter_cleanup(ts->filter_data); @@ -1400,26 +1495,15 @@ static int __devexit ads7846_remove(struct spi_device *spi)  static struct spi_driver ads7846_driver = {  	.driver = {  		.name	= "ads7846", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE, +		.pm	= &ads7846_pm, +		.of_match_table = of_match_ptr(ads7846_dt_ids),  	},  	.probe		= ads7846_probe, -	.remove		= __devexit_p(ads7846_remove), -	.suspend	= ads7846_suspend, -	.resume		= ads7846_resume, +	.remove		= ads7846_remove,  }; -static int __init ads7846_init(void) -{ -	return spi_register_driver(&ads7846_driver); -} -module_init(ads7846_init); - -static void __exit ads7846_exit(void) -{ -	spi_unregister_driver(&ads7846_driver); -} -module_exit(ads7846_exit); +module_spi_driver(ads7846_driver);  MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");  MODULE_LICENSE("GPL");  | 
