* Re: [PATCH] mc13xxx-ts: use zero as default value if no pdata was defined
From: Michael Grzeschik @ 2013-08-14 3:36 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Michael Grzeschik, linux-input, linux-kernel, kernel,
Samuel Ortiz
In-Reply-To: <20130813164609.GA21548@core.coreip.homeip.net>
Hi Dimitry,
On Tue, Aug 13, 2013 at 09:46:09AM -0700, Dmitry Torokhov wrote:
> Hi Michael,
>
> On Tue, Aug 13, 2013 at 02:14:30PM +0200, Michael Grzeschik wrote:
> > In case of devicetree, we currently don't have a way to append pdata for
> > the touchscreen. The current approach is to bail out in that case.
> > This patch makes it possible to probe the touchscreen without pdata
> > and use zero as default values for the atox and ato adc conversion.
>
> I'd rather you added the devicetree support to the driver.
I know that we will need real devictree glue that generates pdata in the
long run. I am working on that. Beside that, for now this patch makes
sense anyway.
Regards,
Michael
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH 1/4] input: ti_am335x_tsc: correct step mask update after IRQ
From: Zubair Lutfullah @ 2013-08-13 20:05 UTC (permalink / raw)
To: jic23, dmitry.torokhov
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376424303-22740-1-git-send-email-zubair.lutfullah@gmail.com>
TSC steps should be enabled again after IRQ routine.
This fix ensures they are updated correctly every time.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index e1c5300..e165fcb 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -52,6 +52,7 @@ struct titsc {
u32 config_inp[4];
u32 bit_xp, bit_xn, bit_yp, bit_yn;
u32 inp_xp, inp_xn, inp_yp, inp_yn;
+ u32 step_mask;
};
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
@@ -196,7 +197,8 @@ static void titsc_step_config(struct titsc *ts_dev)
/* The steps1 … end and bit 0 for TS_Charge */
stepenable = (1 << (end_step + 2)) - 1;
- am335x_tsc_se_set(ts_dev->mfd_tscadc, stepenable);
+ ts_dev->step_mask = stepenable;
+ am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
}
static void titsc_read_coordinates(struct titsc *ts_dev,
@@ -316,7 +318,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
if (irqclr) {
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
- am335x_tsc_se_update(ts_dev->mfd_tscadc);
+ am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
return IRQ_HANDLED;
}
return IRQ_NONE;
--
1.7.9.5
--
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
^ permalink raw reply related
* [PATCH 0/4] iio: input: ti_am335x_adc: Add continuous sampling support round 5
From: Zubair Lutfullah @ 2013-08-13 20:04 UTC (permalink / raw)
To: jic23, dmitry.torokhov
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
Round 5 updates. Fixed the define order in the header as guided by Lee.
Round 4 updates below.
Note: These apply to the fixes-togreg branch in IIO because of
a fix on the adc side in there.
The first few are for input which tweak the TSC driver to
allow ADC in continuous mode with irqs to work nicely together.
The last one is for iio which adds continuous mode to the adc side.
Received feedback on previous series.
1. What if IRQs occur together?
This is handled now. Even if both assert.
They both work.
2. IIO error handling wrong.
Fixed now.
3. Headers wierd in IIO.
Fixed.
Apart from that, found a few bugs in continuous mode
here and there. And squashed them into the same patch.
Thanks
Zubair Lutfullah
Zubair Lutfullah (4):
input: ti_am335x_tsc: correct step mask update after IRQ
input: ti_am335x_tsc: Increase sequencer delay time
input: ti_am335x_tsc: Enable shared IRQ for TSC, add overrun and
underflow checks
iio: ti_am335x_adc: Add continuous sampling and trigger support
drivers/iio/adc/ti_am335x_adc.c | 353 ++++++++++++++++++++++++-----
drivers/input/touchscreen/ti_am335x_tsc.c | 45 +++-
include/linux/mfd/ti_am335x_tscadc.h | 14 ++
3 files changed, 342 insertions(+), 70 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH 4/4] iio: ti_am335x_adc: Add continuous sampling and trigger support
From: Zubair Lutfullah @ 2013-08-13 20:05 UTC (permalink / raw)
To: jic23, dmitry.torokhov
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376424303-22740-1-git-send-email-zubair.lutfullah@gmail.com>
Previously the driver had only one-shot reading functionality.
This patch adds triggered buffer support to the driver.
A buffer of samples can now be read via /dev/iio.
Any IIO trigger can be used to start acquisition.
Patil Rachna (TI) laid the ground work for ADC HW register access.
Russ Dill (TI) fixed bugs in the driver relevant to FIFOs and IRQs.
I fixed channel scanning so multiple ADC channels can be read
simultaneously and pushed to userspace. Restructured the driver
to fit IIO ABI. And added trigger support.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
---
drivers/iio/adc/ti_am335x_adc.c | 353 ++++++++++++++++++++++++++++------
include/linux/mfd/ti_am335x_tscadc.h | 12 ++
2 files changed, 303 insertions(+), 62 deletions(-)
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 3ceac3e..0d7e313 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -24,16 +24,28 @@
#include <linux/iio/iio.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
int channels;
u8 channel_line[8];
u8 channel_step[8];
+ struct work_struct poll_work;
+ wait_queue_head_t wq_data_avail;
+ bool data_avail;
+ u32 *inputbuffer;
+ int sample_count;
+ int irq;
+ int buffer_en_ch_steps;
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@@ -56,27 +68,28 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
return step_en;
}
-static void tiadc_step_config(struct tiadc_device *adc_dev)
+static void tiadc_step_config(struct iio_dev *indio_dev)
{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
unsigned int stepconfig;
- int i, steps;
+ int i, steps, chan;
/*
* There are 16 configurable steps and 8 analog input
* lines available which are shared between Touchscreen and ADC.
- *
* Steps backwards i.e. from 16 towards 0 are used by ADC
* depending on number of input lines needed.
* Channel would represent which analog input
* needs to be given to ADC to digitalize data.
*/
-
steps = TOTAL_STEPS - adc_dev->channels;
- stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
+ if (iio_buffer_enabled(indio_dev))
+ stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
+ | STEPCONFIG_MODE_SWCNT;
+ else
+ stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
for (i = 0; i < adc_dev->channels; i++) {
- int chan;
-
chan = adc_dev->channel_line[i];
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
stepconfig | STEPCONFIG_INP(chan));
@@ -85,7 +98,203 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
adc_dev->channel_step[i] = steps;
steps++;
}
+}
+
+static irqreturn_t tiadc_irq(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct tiadc_device *adc_dev = iio_priv(idev);
+ unsigned int status, config;
+ status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+
+ /* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
+ if (status & IRQENB_FIFO1OVRRUN) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config);
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
+ tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+ } else if (status & IRQENB_FIFO1THRES) {
+ /* Wake adc_work that pushes FIFO data to iio buffer */
+ tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
+ adc_dev->data_avail = 1;
+ wake_up_interruptible(&adc_dev->wq_data_avail);
+ } else
+ return IRQ_NONE;
+
+ status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+ if (status == false)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static irqreturn_t tiadc_trigger_h(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int config;
+
+ schedule_work(&adc_dev->poll_work);
+ am335x_tsc_se_set(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, config & ~CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config | CNTRLREG_TSCSSENB);
+ }
+
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
+ tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
+ | IRQENB_FIFO1OVRRUN);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
+{
+ return iio_sw_buffer_preenable(indio_dev);
+}
+
+static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ unsigned int enb = 0, stepnum;
+ u8 bit;
+
+ tiadc_step_config(indio_dev);
+ for_each_set_bit(bit, buffer->scan_mask,
+ adc_dev->channels) {
+ struct iio_chan_spec const *chan = indio_dev->channels + bit;
+ /*
+ * There are a total of 16 steps available
+ * that are shared between ADC and touchscreen.
+ * We start configuring from step 16 to 0 incase of
+ * ADC. Hence the relation between input channel
+ * and step for ADC would be as below.
+ */
+ stepnum = chan->channel + 9;
+ enb |= (1 << stepnum);
+ }
+ adc_dev->buffer_en_ch_steps = enb;
+
+ return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int fifo1count, i, read, config;
+
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config);
+ } else
+ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB_TC);
+
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+
+ /* Flush FIFO of any leftover data */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int config;
+
+ tiadc_step_config(indio_dev);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
+ .preenable = &tiadc_buffer_preenable,
+ .postenable = &tiadc_buffer_postenable,
+ .predisable = &tiadc_buffer_predisable,
+ .postdisable = &tiadc_buffer_postdisable,
+};
+
+static void tiadc_adc_work(struct work_struct *work_s)
+{
+ struct tiadc_device *adc_dev =
+ container_of(work_s, struct tiadc_device, poll_work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc_dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int i, j, k, fifo1count, read;
+ unsigned int config;
+ int size_to_acquire = buffer->access->get_length(buffer);
+ int sample_count = 0;
+ u32 *data;
+
+ adc_dev->data_avail = 0;
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data == NULL)
+ goto out;
+
+ while (sample_count < size_to_acquire) {
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
+ tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+
+ wait_event_interruptible(adc_dev->wq_data_avail,
+ (adc_dev->data_avail == 1));
+ adc_dev->data_avail = 0;
+
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ if (fifo1count * sizeof(u32) <
+ buffer->access->get_bytes_per_datum(buffer))
+ continue;
+
+ sample_count = sample_count + fifo1count;
+ for (k = 0; k < fifo1count; k = k + i) {
+ for (i = 0, j = 0; i < (indio_dev->scan_bytes)/4; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ data[i] = read & FIFOREAD_DATA_MASK;
+ }
+ iio_push_to_buffers(indio_dev, (u8 *) data);
+ }
+ }
+out:
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+ am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, config & ~CNTRLREG_TSCSSENB);
+ }
+}
+
+irqreturn_t tiadc_iio_pollfunc(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i, fifo1count, read;
+
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW));
+
+ /* Flush FIFO before trigger */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ return IRQ_WAKE_THREAD;
}
static const char * const chan_name_ain[] = {
@@ -120,13 +329,13 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
chan->channel = adc_dev->channel_line[i];
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
chan->datasheet_name = chan_name_ain[chan->channel];
+ chan->scan_index = i;
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 12;
chan->scan_type.storagebits = 32;
}
indio_dev->channels = chan_array;
-
return 0;
}
@@ -141,58 +350,51 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
int i, map_val;
- unsigned int fifo1count, read, stepid;
- u32 step = UINT_MAX;
- bool found = false;
- u32 step_en;
- unsigned long timeout = jiffies + usecs_to_jiffies
- (IDLE_TIMEOUT * adc_dev->channels);
- step_en = get_adc_step_mask(adc_dev);
- am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
-
- /* Wait for ADC sequencer to complete sampling */
- while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
- if (time_after(jiffies, timeout))
- return -EAGAIN;
- }
- map_val = chan->channel + TOTAL_CHANNELS;
-
- /*
- * When the sub-system is first enabled,
- * the sequencer will always start with the
- * lowest step (1) and continue until step (16).
- * For ex: If we have enabled 4 ADC channels and
- * currently use only 1 out of them, the
- * sequencer still configures all the 4 steps,
- * leading to 3 unwanted data.
- * Hence we need to flush out this data.
- */
-
- for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
- if (chan->channel == adc_dev->channel_line[i]) {
- step = adc_dev->channel_step[i];
- break;
- }
- }
- if (WARN_ON_ONCE(step == UINT_MAX))
- return -EINVAL;
-
- fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
- for (i = 0; i < fifo1count; i++) {
- read = tiadc_readl(adc_dev, REG_FIFO1);
- stepid = read & FIFOREAD_CHNLID_MASK;
- stepid = stepid >> 0x10;
+ unsigned int fifo1count, read, stepid, step_en;
- if (stepid == map_val) {
- read = read & FIFOREAD_DATA_MASK;
- found = true;
- *val = read;
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ else {
+ unsigned long timeout = jiffies + usecs_to_jiffies
+ (IDLE_TIMEOUT * adc_dev->channels);
+ step_en = get_adc_step_mask(adc_dev);
+ am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
+
+ /* Wait for ADC sequencer to complete sampling */
+ while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
+ if (time_after(jiffies, timeout))
+ return -EAGAIN;
+ }
+ map_val = chan->channel + TOTAL_CHANNELS;
+
+ /*
+ * When the sub-system is first enabled,
+ * the sequencer will always start with the
+ * lowest step (1) and continue until step (16).
+ * For ex: If we have enabled 4 ADC channels and
+ * currently use only 1 out of them, the
+ * sequencer still configures all the 4 steps,
+ * leading to 3 unwanted data.
+ * Hence we need to flush out this data.
+ */
+
+ *val = -1;
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ stepid = read & FIFOREAD_CHNLID_MASK;
+ stepid = stepid >> 0x10;
+
+ if (stepid == map_val) {
+ read = read & FIFOREAD_DATA_MASK;
+ *val = read;
+ }
}
+ if (*val != -1)
+ return IIO_VAL_INT;
+ else
+ return -EAGAIN;
}
-
- if (found == false)
- return -EBUSY;
- return IIO_VAL_INT;
}
static const struct iio_info tiadc_info = {
@@ -231,26 +433,45 @@ static int tiadc_probe(struct platform_device *pdev)
channels++;
}
adc_dev->channels = channels;
+ adc_dev->irq = adc_dev->mfd_tscadc->irq;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &tiadc_info;
- tiadc_step_config(adc_dev);
+ tiadc_step_config(indio_dev);
+ tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
err = tiadc_channel_init(indio_dev, adc_dev->channels);
if (err < 0)
goto err_free_device;
- err = iio_device_register(indio_dev);
+ INIT_WORK(&adc_dev->poll_work, &tiadc_adc_work);
+ init_waitqueue_head(&adc_dev->wq_data_avail);
+
+ err = request_irq(adc_dev->irq, tiadc_irq, IRQF_SHARED,
+ indio_dev->name, indio_dev);
if (err)
goto err_free_channels;
+ err = iio_triggered_buffer_setup(indio_dev, &tiadc_iio_pollfunc,
+ &tiadc_trigger_h, &tiadc_buffer_setup_ops);
+ if (err)
+ goto err_free_irq;
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto err_buffer_unregister;
+
platform_set_drvdata(pdev, indio_dev);
return 0;
+err_buffer_unregister:
+ iio_buffer_unregister(indio_dev);
+err_free_irq:
+ free_irq(adc_dev->irq, indio_dev);
err_free_channels:
tiadc_channels_remove(indio_dev);
err_free_device:
@@ -265,7 +486,9 @@ static int tiadc_remove(struct platform_device *pdev)
struct tiadc_device *adc_dev = iio_priv(indio_dev);
u32 step_en;
+ free_irq(adc_dev->irq, indio_dev);
iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
tiadc_channels_remove(indio_dev);
step_en = get_adc_step_mask(adc_dev);
@@ -303,10 +526,16 @@ static int tiadc_resume(struct device *dev)
/* Make sure ADC is powered up */
restore = tiadc_readl(adc_dev, REG_CTRL);
- restore &= ~(CNTRLREG_POWERDOWN);
+ restore &= ~(CNTRLREG_TSCSSENB);
tiadc_writel(adc_dev, REG_CTRL, restore);
- tiadc_step_config(adc_dev);
+ tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+ tiadc_step_config(indio_dev);
+
+ /* Make sure ADC is powered up */
+ restore &= ~(CNTRLREG_POWERDOWN);
+ restore |= CNTRLREG_TSCSSENB;
+ tiadc_writel(adc_dev, REG_CTRL, restore);
return 0;
}
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e2db978..14f75a4 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -46,6 +46,9 @@
/* Step Enable */
#define STEPENB_MASK (0x1FFFF << 0)
#define STEPENB(val) ((val) << 0)
+#define ENB(val) (1 << (val))
+#define STPENB_STEPENB STEPENB(0x1FFFF)
+#define STPENB_STEPENB_TC STEPENB(0x1FFF)
/* IRQ enable */
#define IRQENB_HW_PEN BIT(0)
@@ -53,12 +56,15 @@
#define IRQENB_FIFO0OVRRUN BIT(3)
#define IRQENB_FIFO0UNDRFLW BIT(4)
#define IRQENB_FIFO1THRES BIT(5)
+#define IRQENB_FIFO1OVRRUN BIT(6)
+#define IRQENB_FIFO1UNDRFLW BIT(7)
#define IRQENB_PENUP BIT(9)
/* Step Configuration */
#define STEPCONFIG_MODE_MASK (3 << 0)
#define STEPCONFIG_MODE(val) ((val) << 0)
#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2)
+#define STEPCONFIG_MODE_SWCNT STEPCONFIG_MODE(1)
#define STEPCONFIG_AVG_MASK (7 << 2)
#define STEPCONFIG_AVG(val) ((val) << 2)
#define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4)
@@ -126,6 +132,8 @@
#define MAX_CLK_DIV 7
#define TOTAL_STEPS 16
#define TOTAL_CHANNELS 8
+#define FIFO1_THRESHOLD 19
+#define FIFO_SIZE 64
/*
* ADC runs at 3MHz, and it takes
@@ -155,6 +163,10 @@ struct ti_tscadc_dev {
/* adc device */
struct adc_device *adc;
+
+ /* Context save */
+ unsigned int irqstat;
+ unsigned int ctrl;
};
static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/4] input: ti_am335x_tsc: Enable shared IRQ for TSC, add overrun and underflow checks
From: Zubair Lutfullah @ 2013-08-13 20:05 UTC (permalink / raw)
To: jic23-KWPb1pKIrIJaa/9Udqfwiw,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bigeasy-hfZtesqFncYOwBW4kG4KsQ,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, Russ.Dill-l0cyMroinI0
In-Reply-To: <1376424303-22740-1-git-send-email-zubair.lutfullah-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Enable shared IRQ to allow ADC to share IRQ line from
parent MFD core. Only FIFO0 IRQs are for TSC and handled
on the TSC side.
Patch also adds overrun and underflow irq handlers.
Russ Dill (TI) worked on overrun and underflow handlers.
Rachna Patil (TI) laid ground work for shared IRQ.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 37 +++++++++++++++++++++++++----
include/linux/mfd/ti_am335x_tscadc.h | 2 ++
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 766bc7e..9c114b2 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -256,14 +256,39 @@ static irqreturn_t titsc_irq(int irq, void *dev)
{
struct titsc *ts_dev = dev;
struct input_dev *input_dev = ts_dev->input;
- unsigned int status, irqclr = 0;
+ unsigned int status, irqclr = 0, config = 0;
unsigned int x = 0, y = 0;
unsigned int z1, z2, z;
unsigned int fsm;
status = titsc_readl(ts_dev, REG_IRQSTATUS);
- if (status & IRQENB_FIFO0THRES) {
+ /*
+ * ADC and touchscreen share the IRQ line.
+ * FIFO1 threshold, FIFO1 Overrun and FIFO1 underflow
+ * interrupts are used by ADC. Handle FIFO0 IRQs here only
+ * and check if any IRQs left in case both fifos interrupt.
+ * If any irq left, return none, else return handled.
+ */
+ if ((status & IRQENB_FIFO0OVRRUN) ||
+ (status & IRQENB_FIFO0UNDRFLW)) {
+
+ config = titsc_readl(ts_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ titsc_writel(ts_dev, REG_CTRL, config);
+
+ if (status & IRQENB_FIFO0UNDRFLW) {
+ titsc_writel(ts_dev, REG_IRQSTATUS,
+ (status | IRQENB_FIFO0UNDRFLW));
+ irqclr |= IRQENB_FIFO0UNDRFLW;
+ } else {
+ titsc_writel(ts_dev, REG_IRQSTATUS,
+ (status | IRQENB_FIFO0OVRRUN));
+ irqclr |= IRQENB_FIFO0OVRRUN;
+ }
+ titsc_writel(ts_dev, REG_CTRL,
+ (config | CNTRLREG_TSCSSENB));
+ } else if (status & IRQENB_FIFO0THRES) {
titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
@@ -317,9 +342,11 @@ static irqreturn_t titsc_irq(int irq, void *dev)
}
if (irqclr) {
- titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+ titsc_writel(ts_dev, REG_IRQSTATUS, (status | irqclr));
am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
- return IRQ_HANDLED;
+ status = titsc_readl(ts_dev, REG_IRQSTATUS);
+ if (status == false)
+ return IRQ_HANDLED;
}
return IRQ_NONE;
}
@@ -391,7 +418,7 @@ static int titsc_probe(struct platform_device *pdev)
}
err = request_irq(ts_dev->irq, titsc_irq,
- 0, pdev->dev.driver->name, ts_dev);
+ IRQF_SHARED, pdev->dev.driver->name, ts_dev);
if (err) {
dev_err(&pdev->dev, "failed to allocate irq.\n");
goto err_free_mem;
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index db1791b..e2db978 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -50,6 +50,8 @@
/* IRQ enable */
#define IRQENB_HW_PEN BIT(0)
#define IRQENB_FIFO0THRES BIT(2)
+#define IRQENB_FIFO0OVRRUN BIT(3)
+#define IRQENB_FIFO0UNDRFLW BIT(4)
#define IRQENB_FIFO1THRES BIT(5)
#define IRQENB_PENUP BIT(9)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/4] input: ti_am335x_tsc: Increase sequencer delay time
From: Zubair Lutfullah @ 2013-08-13 20:05 UTC (permalink / raw)
To: jic23, dmitry.torokhov
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376424303-22740-1-git-send-email-zubair.lutfullah@gmail.com>
Before checking PEN UP event, IRQ delays for this amount
of time to let FSM stabilize. Previously, with only the
TSC driver in IRQ mode, this delay was sufficient.
The delay is increased slightly to accomodate if the ADC
is also being used simultaneously in continuous mode.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index e165fcb..766bc7e 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -31,7 +31,7 @@
#include <linux/mfd/ti_am335x_tscadc.h>
#define ADCFSM_STEPID 0x10
-#define SEQ_SETTLE 275
+#define SEQ_SETTLE 350
#define MAX_12BIT ((1 << 12) - 1)
static const int config_pins[] = {
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 1/3] HID: Use existing parser for pre-scanning the report descriptors
From: Benjamin Tissoires @ 2013-08-13 19:15 UTC (permalink / raw)
To: Alexander Holler
Cc: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, linux-input, linux-kernel@vger.kernel.org,
Srinivas Pandruvada
In-Reply-To: <520A7CDF.3070808@ahsoftware.de>
On Tue, Aug 13, 2013 at 8:37 PM, Alexander Holler <holler@ahsoftware.de> wrote:
> Am 13.08.2013 16:58, schrieb Benjamin Tissoires:
>
>> hid_scan_report() implements its own HID report descriptor parsing. It was
>> fine until we added the SENSOR_HUB detection. It is going to be even worse
>> with the detection of Win 8 certified touchscreen, as this detection
>> relies on a special feature and on the report_size and report_count
>> fields.
>
>
> Sorry, but I can't agree with your wording here.
>
> If you look at the if expression I've added to support HID sensor hubs, you
> will notice that the first expression was a test for the usage page of
> sensor hubs. That wasn't the first test by accident, instead I've choosen
> the order of those 4 tests very carefully to make the impact on the existing
> parsing of other HID devices very small. So it might not have be pleasing to
> your eyes, but it was for sure an appropriate solution.
I'm sorry if you feel like it was a personal attack. I'm sure the
upstream code is working good and that there is no extra tests and
that you implemented it that way without proper testing and
validating. What I meant was that the initial code was not exactly
meant to address the particular problem of sensor hub (at that time,
we only needed to check for one input report). However, I need now to
add a test against a feature report, which would need a new custom
detection, and a heavy modification of the existing code.
>
> Anyway, I've tested your patch 1/3 on top of 3.11-rc5 here and the detection
> of sensor hubs doesn't work anymore with your patch applied. Up to now I
> only had a quick look at it, but it looks like the test in
> hid_scan_open_collection() isn't hit for my device.
oops, this is not intentional. I'll try to fix this in v2.
>
> Another problem is that I don't have any commercial sensor hub and I'm
> therefor not a very relvant as tester (I've implemented the firmware for my
> HID (sensor hub) device too). Therefor I've added Srinivas Pandruvada to cc,
> because he's the only one I know who has HID sensor hubs.
It is not a problem if you don't have a commercial sensor hub :)
Thanks for adding Srinivas in CC.
>
> And, as said, I've implemented the other side here too, therefor I've added
> the descriptor I'm using below.
>
Thanks, this will help me to test against your report descriptors.
Can I also ask you to send me some hid-recorder[1] traces of your
sensor? With hid-replay, I can then re-inject them in the hid
subsystem, and then I include the results in a regression test suite.
Cheers,
Benjamin
[1] http://bentiss.github.io/hid-replay-docs/
>
> --my-descriptor--
> 0x05, 0x20, // HID_USAGE_PAGE_SENSOR
> 0xa1, 0x01, // COLLECTION (Application)
> 0x09, 0xa0, // HID_USAGE_SENSOR_CATEGORY_TIME
> 0xa1, 0x00, // COLLECTION (Physical)
> #ifndef USE_FULL_YEAR
> 0x75, 0x08, // REPORT_SIZE (8 bits)
> #endif
> 0x95, 0x01, // REPORT_COUNT (1)
> 0x85, HID_REPORT_ID_TIME, // REPORT_ID
>
> 0x0a, 0x21, 0x05, // USAGE (Year)
> #ifdef USE_FULL_YEAR
> 0x75, 0x10, // REPORT_SIZE (16 bits)
> #else
> 0x15, 0x00, // LOGICAL_MINIMUM (0) (Range is
> currently not specified in HUTRR39)
> 0x25, 0x63, // LOGICAL_MAXIMUM (99)
> #endif
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x22, 0x05, // USAGE (Month)
> #ifdef USE_FULL_YEAR
> 0x75, 0x08, // REPORT_SIZE (8 bits)
> #endif
> 0x15, 0x01, // LOGICAL_MINIMUM (1)
> 0x25, 0x0c, // LOGICAL_MAXIMUM (12)
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x23, 0x05, // USAGE (Day)
> 0x15, 0x01, // LOGICAL_MINIMUM (1)
> 0x25, 0x1f, // LOGICAL_MAXIMUM (31)
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x24, 0x05, // USAGE (Day of Week)
> 0x15, 0x00, // LOGICAL_MINIMUM (0)
> 0x25, 0x06, // LOGICAL_MAXIMUM (6)
> 0xa1, 0x02, // COLLECTION (Logical)
> 0x0a, 0xc0, 0x08, // Day of Week: Sunday
> 0x0a, 0xc1, 0x08, // Day of Week: Monday
> 0x0a, 0xc2, 0x08, // Day of Week: Tuesday
> 0x0a, 0xc3, 0x08, // Day of Week: Wednesday
> 0x0a, 0xc4, 0x08, // Day of Week: Thursday
> 0x0a, 0xc5, 0x08, // Day of Week: Friday
> 0x0a, 0xc6, 0x08, // Day of Week: Saturday
> 0x81, 0x02, // INPUT (Const,Arr,Abs)
> 0xc0, // END_COLLECTION
>
> 0x0a, 0x25, 0x05, // USAGE (Hour)
> 0x15, 0x00, // LOGICAL_MINIMUM (0)
> 0x25, 0x17, // LOGICAL_MAXIMUM (23)
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x26, 0x05, // USAGE (Minute)
> 0x15, 0x00, // LOGICAL_MINIMUM (0)
> 0x25, 0x3b, // LOGICAL_MAXIMUM (59)
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x27, 0x05, // USAGE (Second)
> 0x15, 0x00, // LOGICAL_MINIMUM (0)
> 0x25, 0x3b, // LOGICAL_MAXIMUM (59)
> 0x65, 0x00, // UNIT (None)
> //0x66, 0x10, 0x01, // UNIT (second)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0x0a, 0x28, 0x05, // USAGE (Millisecond)
> 0x75, 0x10, // REPORT_SIZE (16 bits)
> 0x65, 0x00, // UNIT (None)
> 0x81, 0x02, // INPUT (Data,Var,Abs)
>
> 0xc0, // END_COLLECTION
> 0xc0, // END_COLLECTION
> --my-descriptor--
>
>
^ permalink raw reply
* Re: [PATCH 1/3] HID: Use existing parser for pre-scanning the report descriptors
From: rydberg @ 2013-08-13 19:17 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, Jiri Kosina, Stephane Chatty, Mika Westerberg,
Alexander Holler, linux-input, linux-kernel
In-Reply-To: <1376405889-12378-2-git-send-email-benjamin.tissoires@redhat.com>
Hi Benjamin,
thanks for the patches, things are looking a lot better this way.
> hid_scan_report() implements its own HID report descriptor parsing. It was
> fine until we added the SENSOR_HUB detection. It is going to be even worse
> with the detection of Win 8 certified touchscreen, as this detection
> relies on a special feature and on the report_size and report_count fields.
It was fine with sensors added as well. You seem to have found a
reasonable way to add support for all the tags, but there is a
rationale for the current scanner that may not have been addressed in
this patch: it is robust against parse errors. This is particularly
important for devices which later tweak the report, often in order to
parse properly.
Please find some further comments inline.
> We can use the existing HID parser in hid-core for hid_scan_report()
> by re-using the code from hid_open_report(). hid_parser_global,
> hid_parser_local and hid_parser_reserved does not have any side effects.
> We just need to reimplement the MAIN_ITEM callback to have a proper
> parsing without side effects.
>
> Instead of directly overwriting the ->group field, this patch introduce
> a ->flags field and then decide which group the device belongs to,
> depending on the whole parsing (not just the local item). This will be
> useful for Win 8 multitouch devices, which are multitouch devices and
> Win 8 certified (so 2 flags to check).
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> drivers/hid/hid-core.c | 131 +++++++++++++++++++++++++++++++++++--------------
> include/linux/hid.h | 4 ++
> 2 files changed, 97 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 3efe19f..d8cdb0a 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -677,10 +677,49 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
> return NULL;
> }
>
> -static void hid_scan_usage(struct hid_device *hid, u32 usage)
> +static void hid_scan_input_usage(struct hid_parser *parser, u32 usage)
> {
> if (usage == HID_DG_CONTACTID)
> - hid->group = HID_GROUP_MULTITOUCH;
> + parser->flags |= HID_FLAG_MULTITOUCH;
Did you consider reusing the group flags, e.g., parser->groups |= (1
<< HID_GROUP_MULTITOUCH)? This change could be made regardless of the
parser logic.
> +}
> +
> +static void hid_scan_open_collection(struct hid_parser *parser, unsigned type)
We are not really opening anything here, so perhaps
hid_scan_collection would suffice.
> +{
> + if (parser->global.usage_page == HID_UP_SENSOR &&
> + type == HID_COLLECTION_PHYSICAL)
> + parser->flags |= HID_FLAG_SENSOR_HUB;
> +}
> +
> +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
> +{
> + __u32 data;
> + int i;
> +
> + data = item_udata(item);
> +
> + switch (item->tag) {
> + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
> + hid_scan_open_collection(parser, data & 0xff);
> + break;
> + case HID_MAIN_ITEM_TAG_END_COLLECTION:
> + break;
> + case HID_MAIN_ITEM_TAG_INPUT:
> + for (i = 0; i < parser->local.usage_index; i++)
> + hid_scan_input_usage(parser, parser->local.usage[i]);
> + break;
> + case HID_MAIN_ITEM_TAG_OUTPUT:
> + break;
> + case HID_MAIN_ITEM_TAG_FEATURE:
> + break;
> + default:
> + hid_err(parser->device, "unknown main item tag 0x%x\n",
> + item->tag);
> + }
> +
> + /* Reset the local parser environment */
> + memset(&parser->local, 0, sizeof(parser->local));
> +
> + return 0;
> }
>
> /*
> @@ -690,49 +729,65 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage)
> */
> static int hid_scan_report(struct hid_device *hid)
> {
> - unsigned int page = 0, delim = 0;
> + struct hid_parser *parser;
> + struct hid_item item;
> __u8 *start = hid->dev_rdesc;
> __u8 *end = start + hid->dev_rsize;
> - unsigned int u, u_min = 0, u_max = 0;
> - struct hid_item item;
> + int ret;
> + static int (*dispatch_type[])(struct hid_parser *parser,
> + struct hid_item *item) = {
> + hid_scan_main,
> + hid_parser_global,
> + hid_parser_local,
> + hid_parser_reserved
> + };
>
> - hid->group = HID_GROUP_GENERIC;
> + parser = vzalloc(sizeof(struct hid_parser));
Argh, I realize it is inevitable for this patch, but it still makes my
eyes bleed. The parser takes quite a bit of memory...
> + if (!parser)
> + return -ENOMEM;
> +
> + parser->device = hid;
> +
> + ret = -EINVAL;
> while ((start = fetch_item(start, end, &item)) != NULL) {
> - if (item.format != HID_ITEM_FORMAT_SHORT)
> - return -EINVAL;
> - if (item.type == HID_ITEM_TYPE_GLOBAL) {
> - if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE)
> - page = item_udata(&item) << 16;
> - } else if (item.type == HID_ITEM_TYPE_LOCAL) {
> - if (delim > 1)
> - break;
> - u = item_udata(&item);
> - if (item.size <= 2)
> - u += page;
> - switch (item.tag) {
> - case HID_LOCAL_ITEM_TAG_DELIMITER:
> - delim += !!u;
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE:
> - hid_scan_usage(hid, u);
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
> - u_min = u;
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
> - u_max = u;
> - for (u = u_min; u <= u_max; u++)
> - hid_scan_usage(hid, u);
> - break;
> +
> + if (item.format != HID_ITEM_FORMAT_SHORT) {
> + hid_err(hid, "unexpected long global item\n");
I do not think we should be verbose on errors during scan, for the
reason stated at the top. Since this goes also for the global parser
functions, we might have a problem.
> + goto out;
> + }
> +
> + if (dispatch_type[item.type](parser, &item)) {
> + hid_err(hid, "item %u %u %u %u parsing failed\n",
> + item.format, (unsigned)item.size,
> + (unsigned)item.type, (unsigned)item.tag);
Ditto.
> + goto out;
> + }
> +
> + if (start == end) {
> + if (parser->local.delimiter_depth) {
> + hid_err(hid, "unbalanced delimiter at end of report description\n");
Robustness, see top.
> + goto out;
> }
> - } else if (page == HID_UP_SENSOR &&
> - item.type == HID_ITEM_TYPE_MAIN &&
> - item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION &&
> - (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL)
> - hid->group = HID_GROUP_SENSOR_HUB;
At the end of the day, It may be best to simply extend this branch as
the main item type and add whatever you need to detect win8 from
there.
> + ret = 0;
> + goto out;
> + }
> }
>
> - return 0;
> + hid_err(hid, "item fetching failed at offset %d\n", (int)(end - start));
> +out:
> + switch (parser->flags) {
> + case HID_FLAG_MULTITOUCH:
> + hid->group = HID_GROUP_MULTITOUCH;
> + break;
> + case HID_FLAG_SENSOR_HUB:
> + hid->group = HID_GROUP_SENSOR_HUB;
> + break;
> + default:
> + hid->group = HID_GROUP_GENERIC;
> + }
Looks odd to switch on flags, but it works pretty well with the rest
of the patches in the series.
> +
> + vfree(parser);
> + return ret;
> }
>
> /**
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 5a4e789..7d823db 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -533,6 +533,9 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
> #define HID_GLOBAL_STACK_SIZE 4
> #define HID_COLLECTION_STACK_SIZE 4
>
> +#define HID_FLAG_MULTITOUCH 0x0001
> +#define HID_FLAG_SENSOR_HUB 0x0002
> +
> struct hid_parser {
> struct hid_global global;
> struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
> @@ -541,6 +544,7 @@ struct hid_parser {
> unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
> unsigned collection_stack_ptr;
> struct hid_device *device;
> + unsigned flags;
> };
>
> struct hid_class_descriptor {
> --
> 1.8.3.1
>
Thanks,
Henrik
^ permalink raw reply
* Re: kernel Oops: 0003 on usbhid_submit_report
From: Greg KH @ 2013-08-13 18:40 UTC (permalink / raw)
To: Andreas Lillebø Holm; +Cc: linux-usb, linux-input
In-Reply-To: <9921350AD19C46EFA70EA181A0C1F7E9@sqhead.com>
<adding linux-input@ where the hid developers live...>
On Mon, Aug 12, 2013 at 01:56:40PM +0200, Andreas Lillebø Holm wrote:
> When communicating with AT90USB1287, at random intervals (1/25 boots)
> the linux hid_output_field Oopses and kills the communicating thread.
> The AT90USB1287 microcontroller uses LUFA library for usb/hid
> communication. It is trigged by a ioctl call from userspace and fails
> in a kernel paging request. The system is after the oops in a state
> where no hid commands is sent anymore and only a boot can fix the
> system.
>
> Keywords: usbhid hid
>
> Kernel version: Linux version 3.8.13-03081301-generic (apw@gomeisa) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #201305311535 SMP Fri May 31 19:44:30 UTC 2013
>
> Oopses:
> http://paste.debian.net/24305
> http://paste.debian.net/24306
> http://paste.debian.net/24307
>
> Code:
> The error is triggered by:
> ioctl(fd,HIDIOCSUSAGES, &ref_multi_u);
> ioctl(fd,HIDIOCSREPORT, &rep_info_u);
>
> Notes:
> It is very hard to reproduce so seems like race condition…
>
> Any tips to resolve/workaround this issue is appreciated and please
> let me know if my information is incomplete (This is my first kernel
> bug report)
Any chance you can try a supported kernel, like 3.10.6 or 3.11-rc5 to
see if that also causes problems? We can't do anything with
distro-specific kernel releases like your 3.8.13 release from Ubuntu,
sorry.
thanks,
greg k-h
--
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
^ permalink raw reply
* Re: [PATCH 1/3] HID: Use existing parser for pre-scanning the report descriptors
From: Alexander Holler @ 2013-08-13 18:37 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, linux-input, linux-kernel, Srinivas Pandruvada
In-Reply-To: <1376405889-12378-2-git-send-email-benjamin.tissoires@redhat.com>
Am 13.08.2013 16:58, schrieb Benjamin Tissoires:
> hid_scan_report() implements its own HID report descriptor parsing. It was
> fine until we added the SENSOR_HUB detection. It is going to be even worse
> with the detection of Win 8 certified touchscreen, as this detection
> relies on a special feature and on the report_size and report_count fields.
Sorry, but I can't agree with your wording here.
If you look at the if expression I've added to support HID sensor hubs,
you will notice that the first expression was a test for the usage page
of sensor hubs. That wasn't the first test by accident, instead I've
choosen the order of those 4 tests very carefully to make the impact on
the existing parsing of other HID devices very small. So it might not
have be pleasing to your eyes, but it was for sure an appropriate solution.
Anyway, I've tested your patch 1/3 on top of 3.11-rc5 here and the
detection of sensor hubs doesn't work anymore with your patch applied.
Up to now I only had a quick look at it, but it looks like the test in
hid_scan_open_collection() isn't hit for my device.
Another problem is that I don't have any commercial sensor hub and I'm
therefor not a very relvant as tester (I've implemented the firmware for
my HID (sensor hub) device too). Therefor I've added Srinivas Pandruvada
to cc, because he's the only one I know who has HID sensor hubs.
And, as said, I've implemented the other side here too, therefor I've
added the descriptor I'm using below.
Regards,
Alexander Holler
--my-descriptor--
0x05, 0x20, // HID_USAGE_PAGE_SENSOR
0xa1, 0x01, // COLLECTION (Application)
0x09, 0xa0, // HID_USAGE_SENSOR_CATEGORY_TIME
0xa1, 0x00, // COLLECTION (Physical)
#ifndef USE_FULL_YEAR
0x75, 0x08, // REPORT_SIZE (8 bits)
#endif
0x95, 0x01, // REPORT_COUNT (1)
0x85, HID_REPORT_ID_TIME, // REPORT_ID
0x0a, 0x21, 0x05, // USAGE (Year)
#ifdef USE_FULL_YEAR
0x75, 0x10, // REPORT_SIZE (16 bits)
#else
0x15, 0x00, // LOGICAL_MINIMUM (0) (Range is currently not specified
in HUTRR39)
0x25, 0x63, // LOGICAL_MAXIMUM (99)
#endif
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x22, 0x05, // USAGE (Month)
#ifdef USE_FULL_YEAR
0x75, 0x08, // REPORT_SIZE (8 bits)
#endif
0x15, 0x01, // LOGICAL_MINIMUM (1)
0x25, 0x0c, // LOGICAL_MAXIMUM (12)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x23, 0x05, // USAGE (Day)
0x15, 0x01, // LOGICAL_MINIMUM (1)
0x25, 0x1f, // LOGICAL_MAXIMUM (31)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x24, 0x05, // USAGE (Day of Week)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x06, // LOGICAL_MAXIMUM (6)
0xa1, 0x02, // COLLECTION (Logical)
0x0a, 0xc0, 0x08, // Day of Week: Sunday
0x0a, 0xc1, 0x08, // Day of Week: Monday
0x0a, 0xc2, 0x08, // Day of Week: Tuesday
0x0a, 0xc3, 0x08, // Day of Week: Wednesday
0x0a, 0xc4, 0x08, // Day of Week: Thursday
0x0a, 0xc5, 0x08, // Day of Week: Friday
0x0a, 0xc6, 0x08, // Day of Week: Saturday
0x81, 0x02, // INPUT (Const,Arr,Abs)
0xc0, // END_COLLECTION
0x0a, 0x25, 0x05, // USAGE (Hour)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x17, // LOGICAL_MAXIMUM (23)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x26, 0x05, // USAGE (Minute)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x3b, // LOGICAL_MAXIMUM (59)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x27, 0x05, // USAGE (Second)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x3b, // LOGICAL_MAXIMUM (59)
0x65, 0x00, // UNIT (None)
//0x66, 0x10, 0x01, // UNIT (second)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x0a, 0x28, 0x05, // USAGE (Millisecond)
0x75, 0x10, // REPORT_SIZE (16 bits)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
--my-descriptor--
>
> We can use the existing HID parser in hid-core for hid_scan_report()
> by re-using the code from hid_open_report(). hid_parser_global,
> hid_parser_local and hid_parser_reserved does not have any side effects.
> We just need to reimplement the MAIN_ITEM callback to have a proper
> parsing without side effects.
>
> Instead of directly overwriting the ->group field, this patch introduce
> a ->flags field and then decide which group the device belongs to,
> depending on the whole parsing (not just the local item). This will be
> useful for Win 8 multitouch devices, which are multitouch devices and
> Win 8 certified (so 2 flags to check).
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> drivers/hid/hid-core.c | 131 +++++++++++++++++++++++++++++++++++--------------
> include/linux/hid.h | 4 ++
> 2 files changed, 97 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 3efe19f..d8cdb0a 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -677,10 +677,49 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
> return NULL;
> }
>
> -static void hid_scan_usage(struct hid_device *hid, u32 usage)
> +static void hid_scan_input_usage(struct hid_parser *parser, u32 usage)
> {
> if (usage == HID_DG_CONTACTID)
> - hid->group = HID_GROUP_MULTITOUCH;
> + parser->flags |= HID_FLAG_MULTITOUCH;
> +}
> +
> +static void hid_scan_open_collection(struct hid_parser *parser, unsigned type)
> +{
> + if (parser->global.usage_page == HID_UP_SENSOR &&
> + type == HID_COLLECTION_PHYSICAL)
> + parser->flags |= HID_FLAG_SENSOR_HUB;
> +}
> +
> +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
> +{
> + __u32 data;
> + int i;
> +
> + data = item_udata(item);
> +
> + switch (item->tag) {
> + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
> + hid_scan_open_collection(parser, data & 0xff);
> + break;
> + case HID_MAIN_ITEM_TAG_END_COLLECTION:
> + break;
> + case HID_MAIN_ITEM_TAG_INPUT:
> + for (i = 0; i < parser->local.usage_index; i++)
> + hid_scan_input_usage(parser, parser->local.usage[i]);
> + break;
> + case HID_MAIN_ITEM_TAG_OUTPUT:
> + break;
> + case HID_MAIN_ITEM_TAG_FEATURE:
> + break;
> + default:
> + hid_err(parser->device, "unknown main item tag 0x%x\n",
> + item->tag);
> + }
> +
> + /* Reset the local parser environment */
> + memset(&parser->local, 0, sizeof(parser->local));
> +
> + return 0;
> }
>
> /*
> @@ -690,49 +729,65 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage)
> */
> static int hid_scan_report(struct hid_device *hid)
> {
> - unsigned int page = 0, delim = 0;
> + struct hid_parser *parser;
> + struct hid_item item;
> __u8 *start = hid->dev_rdesc;
> __u8 *end = start + hid->dev_rsize;
> - unsigned int u, u_min = 0, u_max = 0;
> - struct hid_item item;
> + int ret;
> + static int (*dispatch_type[])(struct hid_parser *parser,
> + struct hid_item *item) = {
> + hid_scan_main,
> + hid_parser_global,
> + hid_parser_local,
> + hid_parser_reserved
> + };
>
> - hid->group = HID_GROUP_GENERIC;
> + parser = vzalloc(sizeof(struct hid_parser));
> + if (!parser)
> + return -ENOMEM;
> +
> + parser->device = hid;
> +
> + ret = -EINVAL;
> while ((start = fetch_item(start, end, &item)) != NULL) {
> - if (item.format != HID_ITEM_FORMAT_SHORT)
> - return -EINVAL;
> - if (item.type == HID_ITEM_TYPE_GLOBAL) {
> - if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE)
> - page = item_udata(&item) << 16;
> - } else if (item.type == HID_ITEM_TYPE_LOCAL) {
> - if (delim > 1)
> - break;
> - u = item_udata(&item);
> - if (item.size <= 2)
> - u += page;
> - switch (item.tag) {
> - case HID_LOCAL_ITEM_TAG_DELIMITER:
> - delim += !!u;
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE:
> - hid_scan_usage(hid, u);
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
> - u_min = u;
> - break;
> - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
> - u_max = u;
> - for (u = u_min; u <= u_max; u++)
> - hid_scan_usage(hid, u);
> - break;
> +
> + if (item.format != HID_ITEM_FORMAT_SHORT) {
> + hid_err(hid, "unexpected long global item\n");
> + goto out;
> + }
> +
> + if (dispatch_type[item.type](parser, &item)) {
> + hid_err(hid, "item %u %u %u %u parsing failed\n",
> + item.format, (unsigned)item.size,
> + (unsigned)item.type, (unsigned)item.tag);
> + goto out;
> + }
> +
> + if (start == end) {
> + if (parser->local.delimiter_depth) {
> + hid_err(hid, "unbalanced delimiter at end of report description\n");
> + goto out;
> }
> - } else if (page == HID_UP_SENSOR &&
> - item.type == HID_ITEM_TYPE_MAIN &&
> - item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION &&
> - (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL)
> - hid->group = HID_GROUP_SENSOR_HUB;
> + ret = 0;
> + goto out;
> + }
> }
>
> - return 0;
> + hid_err(hid, "item fetching failed at offset %d\n", (int)(end - start));
> +out:
> + switch (parser->flags) {
> + case HID_FLAG_MULTITOUCH:
> + hid->group = HID_GROUP_MULTITOUCH;
> + break;
> + case HID_FLAG_SENSOR_HUB:
> + hid->group = HID_GROUP_SENSOR_HUB;
> + break;
> + default:
> + hid->group = HID_GROUP_GENERIC;
> + }
> +
> + vfree(parser);
> + return ret;
> }
>
> /**
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 5a4e789..7d823db 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -533,6 +533,9 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
> #define HID_GLOBAL_STACK_SIZE 4
> #define HID_COLLECTION_STACK_SIZE 4
>
> +#define HID_FLAG_MULTITOUCH 0x0001
> +#define HID_FLAG_SENSOR_HUB 0x0002
> +
> struct hid_parser {
> struct hid_global global;
> struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
> @@ -541,6 +544,7 @@ struct hid_parser {
> unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
> unsigned collection_stack_ptr;
> struct hid_device *device;
> + unsigned flags;
> };
>
> struct hid_class_descriptor {
>
^ permalink raw reply
* Re: [PATCH 3/4] input: ti_tsc: Enable shared IRQ for TSC and add overrun, underflow checks
From: Lee Jones @ 2013-08-13 17:07 UTC (permalink / raw)
To: Zubair Lutfullah
Cc: jic23, dmitry.torokhov, sameo, linux-iio, linux-input,
linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-4-git-send-email-zubair.lutfullah@gmail.com>
On Tue, 13 Aug 2013, Zubair Lutfullah wrote:
> Enable shared IRQ to allow ADC to share IRQ line from
> parent MFD core. Only FIFO0 IRQs are for TSC and handled
> on the TSC side.
>
> Patch also adds overrun and underflow irq handlers.
>
> Russ Dill (TI) worked on overrun and underflow handlers.
> Rachna Patil (TI) laid ground work for shared IRQ.
>
> Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
> ---
> drivers/input/touchscreen/ti_am335x_tsc.c | 37 +++++++++++++++++++++++++----
> include/linux/mfd/ti_am335x_tscadc.h | 2 ++
> 2 files changed, 34 insertions(+), 5 deletions(-)
For the MFD part when the remainder of the entries have been placed in
order:
Acked-by: Lee Jones <lee.jones@linaro.org>
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH 4/4] iio: ti_am335x_adc: Add continuous sampling and trigger support
From: Lee Jones @ 2013-08-13 17:05 UTC (permalink / raw)
To: Zubair Lutfullah
Cc: jic23, dmitry.torokhov, sameo, linux-iio, linux-input,
linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-5-git-send-email-zubair.lutfullah@gmail.com>
On Tue, 13 Aug 2013, Zubair Lutfullah wrote:
> Previously the driver had only one-shot reading functionality.
> This patch adds triggered buffer support to the driver.
> A buffer of samples can now be read via /dev/iio.
> Any IIO trigger can be used to start acquisition.
>
> Patil Rachna (TI) laid the ground work for ADC HW register access.
> Russ Dill (TI) fixed bugs in the driver relevant to FIFOs and IRQs.
>
> I fixed channel scanning so multiple ADC channels can be read
> simultaneously and pushed to userspace. Restructured the driver
> to fit IIO ABI. And added trigger support.
>
> Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Russ Dill <Russ.Dill@ti.com>
> ---
> drivers/iio/adc/ti_am335x_adc.c | 353 ++++++++++++++++++++++++++++------
> include/linux/mfd/ti_am335x_tscadc.h | 13 +-
> 2 files changed, 303 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
<snip>
> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
> index e2db978..a1d22de 100644
> --- a/include/linux/mfd/ti_am335x_tscadc.h
> +++ b/include/linux/mfd/ti_am335x_tscadc.h
> @@ -46,6 +46,9 @@
> /* Step Enable */
> #define STEPENB_MASK (0x1FFFF << 0)
> #define STEPENB(val) ((val) << 0)
> +#define ENB(val) (1 << (val))
> +#define STPENB_STEPENB STEPENB(0x1FFFF)
> +#define STPENB_STEPENB_TC STEPENB(0x1FFF)
>
> /* IRQ enable */
> #define IRQENB_HW_PEN BIT(0)
> @@ -54,11 +57,14 @@
> #define IRQENB_FIFO0UNDRFLW BIT(4)
> #define IRQENB_FIFO1THRES BIT(5)
> #define IRQENB_PENUP BIT(9)
> +#define IRQENB_FIFO1OVRRUN BIT(6)
> +#define IRQENB_FIFO1UNDRFLW BIT(7)
Nit: Can we keep these in numerical order?
> /* Step Configuration */
> #define STEPCONFIG_MODE_MASK (3 << 0)
> #define STEPCONFIG_MODE(val) ((val) << 0)
> #define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2)
> +#define STEPCONFIG_MODE_SWCNT STEPCONFIG_MODE(1)
> #define STEPCONFIG_AVG_MASK (7 << 2)
> #define STEPCONFIG_AVG(val) ((val) << 2)
> #define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4)
> @@ -126,7 +132,8 @@
> #define MAX_CLK_DIV 7
> #define TOTAL_STEPS 16
> #define TOTAL_CHANNELS 8
> -
> +#define FIFO1_THRESHOLD 19
> +#define FIFO_SIZE 64
Nit: Keep the line space between the defines and the multi-line comment.
> /*
> * ADC runs at 3MHz, and it takes
> * 15 cycles to latch one data output.
> @@ -155,6 +162,10 @@ struct ti_tscadc_dev {
>
> /* adc device */
> struct adc_device *adc;
> +
> + /* Context save */
> + unsigned int irqstat;
> + unsigned int ctrl;
> };
>
> static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
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
^ permalink raw reply
* [PATCH 4/4] iio: ti_am335x_adc: Add continuous sampling and trigger support
From: Zubair Lutfullah @ 2013-08-13 16:48 UTC (permalink / raw)
To: jic23, dmitry.torokhov, sameo, lee.jones
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-1-git-send-email-zubair.lutfullah@gmail.com>
Previously the driver had only one-shot reading functionality.
This patch adds triggered buffer support to the driver.
A buffer of samples can now be read via /dev/iio.
Any IIO trigger can be used to start acquisition.
Patil Rachna (TI) laid the ground work for ADC HW register access.
Russ Dill (TI) fixed bugs in the driver relevant to FIFOs and IRQs.
I fixed channel scanning so multiple ADC channels can be read
simultaneously and pushed to userspace. Restructured the driver
to fit IIO ABI. And added trigger support.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
---
drivers/iio/adc/ti_am335x_adc.c | 353 ++++++++++++++++++++++++++++------
include/linux/mfd/ti_am335x_tscadc.h | 13 +-
2 files changed, 303 insertions(+), 63 deletions(-)
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 3ceac3e..0d7e313 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -24,16 +24,28 @@
#include <linux/iio/iio.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
int channels;
u8 channel_line[8];
u8 channel_step[8];
+ struct work_struct poll_work;
+ wait_queue_head_t wq_data_avail;
+ bool data_avail;
+ u32 *inputbuffer;
+ int sample_count;
+ int irq;
+ int buffer_en_ch_steps;
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@@ -56,27 +68,28 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
return step_en;
}
-static void tiadc_step_config(struct tiadc_device *adc_dev)
+static void tiadc_step_config(struct iio_dev *indio_dev)
{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
unsigned int stepconfig;
- int i, steps;
+ int i, steps, chan;
/*
* There are 16 configurable steps and 8 analog input
* lines available which are shared between Touchscreen and ADC.
- *
* Steps backwards i.e. from 16 towards 0 are used by ADC
* depending on number of input lines needed.
* Channel would represent which analog input
* needs to be given to ADC to digitalize data.
*/
-
steps = TOTAL_STEPS - adc_dev->channels;
- stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
+ if (iio_buffer_enabled(indio_dev))
+ stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
+ | STEPCONFIG_MODE_SWCNT;
+ else
+ stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
for (i = 0; i < adc_dev->channels; i++) {
- int chan;
-
chan = adc_dev->channel_line[i];
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
stepconfig | STEPCONFIG_INP(chan));
@@ -85,7 +98,203 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
adc_dev->channel_step[i] = steps;
steps++;
}
+}
+
+static irqreturn_t tiadc_irq(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct tiadc_device *adc_dev = iio_priv(idev);
+ unsigned int status, config;
+ status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+
+ /* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
+ if (status & IRQENB_FIFO1OVRRUN) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config);
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
+ tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+ } else if (status & IRQENB_FIFO1THRES) {
+ /* Wake adc_work that pushes FIFO data to iio buffer */
+ tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
+ adc_dev->data_avail = 1;
+ wake_up_interruptible(&adc_dev->wq_data_avail);
+ } else
+ return IRQ_NONE;
+
+ status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+ if (status == false)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static irqreturn_t tiadc_trigger_h(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int config;
+
+ schedule_work(&adc_dev->poll_work);
+ am335x_tsc_se_set(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, config & ~CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config | CNTRLREG_TSCSSENB);
+ }
+
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
+ tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
+ | IRQENB_FIFO1OVRRUN);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
+{
+ return iio_sw_buffer_preenable(indio_dev);
+}
+
+static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ unsigned int enb = 0, stepnum;
+ u8 bit;
+
+ tiadc_step_config(indio_dev);
+ for_each_set_bit(bit, buffer->scan_mask,
+ adc_dev->channels) {
+ struct iio_chan_spec const *chan = indio_dev->channels + bit;
+ /*
+ * There are a total of 16 steps available
+ * that are shared between ADC and touchscreen.
+ * We start configuring from step 16 to 0 incase of
+ * ADC. Hence the relation between input channel
+ * and step for ADC would be as below.
+ */
+ stepnum = chan->channel + 9;
+ enb |= (1 << stepnum);
+ }
+ adc_dev->buffer_en_ch_steps = enb;
+
+ return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int fifo1count, i, read, config;
+
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config);
+ } else
+ tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB_TC);
+
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+
+ /* Flush FIFO of any leftover data */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int config;
+
+ tiadc_step_config(indio_dev);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
+ .preenable = &tiadc_buffer_preenable,
+ .postenable = &tiadc_buffer_postenable,
+ .predisable = &tiadc_buffer_predisable,
+ .postdisable = &tiadc_buffer_postdisable,
+};
+
+static void tiadc_adc_work(struct work_struct *work_s)
+{
+ struct tiadc_device *adc_dev =
+ container_of(work_s, struct tiadc_device, poll_work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc_dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int i, j, k, fifo1count, read;
+ unsigned int config;
+ int size_to_acquire = buffer->access->get_length(buffer);
+ int sample_count = 0;
+ u32 *data;
+
+ adc_dev->data_avail = 0;
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data == NULL)
+ goto out;
+
+ while (sample_count < size_to_acquire) {
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
+ tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+
+ wait_event_interruptible(adc_dev->wq_data_avail,
+ (adc_dev->data_avail == 1));
+ adc_dev->data_avail = 0;
+
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ if (fifo1count * sizeof(u32) <
+ buffer->access->get_bytes_per_datum(buffer))
+ continue;
+
+ sample_count = sample_count + fifo1count;
+ for (k = 0; k < fifo1count; k = k + i) {
+ for (i = 0, j = 0; i < (indio_dev->scan_bytes)/4; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ data[i] = read & FIFOREAD_DATA_MASK;
+ }
+ iio_push_to_buffers(indio_dev, (u8 *) data);
+ }
+ }
+out:
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
+ am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
+ if (adc_dev->mfd_tscadc->tsc_cell == -1) {
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ tiadc_writel(adc_dev, REG_CTRL, config & ~CNTRLREG_TSCSSENB);
+ }
+}
+
+irqreturn_t tiadc_iio_pollfunc(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i, fifo1count, read;
+
+ tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
+ IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW));
+
+ /* Flush FIFO before trigger */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ return IRQ_WAKE_THREAD;
}
static const char * const chan_name_ain[] = {
@@ -120,13 +329,13 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
chan->channel = adc_dev->channel_line[i];
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
chan->datasheet_name = chan_name_ain[chan->channel];
+ chan->scan_index = i;
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 12;
chan->scan_type.storagebits = 32;
}
indio_dev->channels = chan_array;
-
return 0;
}
@@ -141,58 +350,51 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
int i, map_val;
- unsigned int fifo1count, read, stepid;
- u32 step = UINT_MAX;
- bool found = false;
- u32 step_en;
- unsigned long timeout = jiffies + usecs_to_jiffies
- (IDLE_TIMEOUT * adc_dev->channels);
- step_en = get_adc_step_mask(adc_dev);
- am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
-
- /* Wait for ADC sequencer to complete sampling */
- while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
- if (time_after(jiffies, timeout))
- return -EAGAIN;
- }
- map_val = chan->channel + TOTAL_CHANNELS;
-
- /*
- * When the sub-system is first enabled,
- * the sequencer will always start with the
- * lowest step (1) and continue until step (16).
- * For ex: If we have enabled 4 ADC channels and
- * currently use only 1 out of them, the
- * sequencer still configures all the 4 steps,
- * leading to 3 unwanted data.
- * Hence we need to flush out this data.
- */
-
- for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
- if (chan->channel == adc_dev->channel_line[i]) {
- step = adc_dev->channel_step[i];
- break;
- }
- }
- if (WARN_ON_ONCE(step == UINT_MAX))
- return -EINVAL;
-
- fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
- for (i = 0; i < fifo1count; i++) {
- read = tiadc_readl(adc_dev, REG_FIFO1);
- stepid = read & FIFOREAD_CHNLID_MASK;
- stepid = stepid >> 0x10;
+ unsigned int fifo1count, read, stepid, step_en;
- if (stepid == map_val) {
- read = read & FIFOREAD_DATA_MASK;
- found = true;
- *val = read;
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ else {
+ unsigned long timeout = jiffies + usecs_to_jiffies
+ (IDLE_TIMEOUT * adc_dev->channels);
+ step_en = get_adc_step_mask(adc_dev);
+ am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
+
+ /* Wait for ADC sequencer to complete sampling */
+ while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) {
+ if (time_after(jiffies, timeout))
+ return -EAGAIN;
+ }
+ map_val = chan->channel + TOTAL_CHANNELS;
+
+ /*
+ * When the sub-system is first enabled,
+ * the sequencer will always start with the
+ * lowest step (1) and continue until step (16).
+ * For ex: If we have enabled 4 ADC channels and
+ * currently use only 1 out of them, the
+ * sequencer still configures all the 4 steps,
+ * leading to 3 unwanted data.
+ * Hence we need to flush out this data.
+ */
+
+ *val = -1;
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ stepid = read & FIFOREAD_CHNLID_MASK;
+ stepid = stepid >> 0x10;
+
+ if (stepid == map_val) {
+ read = read & FIFOREAD_DATA_MASK;
+ *val = read;
+ }
}
+ if (*val != -1)
+ return IIO_VAL_INT;
+ else
+ return -EAGAIN;
}
-
- if (found == false)
- return -EBUSY;
- return IIO_VAL_INT;
}
static const struct iio_info tiadc_info = {
@@ -231,26 +433,45 @@ static int tiadc_probe(struct platform_device *pdev)
channels++;
}
adc_dev->channels = channels;
+ adc_dev->irq = adc_dev->mfd_tscadc->irq;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &tiadc_info;
- tiadc_step_config(adc_dev);
+ tiadc_step_config(indio_dev);
+ tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
err = tiadc_channel_init(indio_dev, adc_dev->channels);
if (err < 0)
goto err_free_device;
- err = iio_device_register(indio_dev);
+ INIT_WORK(&adc_dev->poll_work, &tiadc_adc_work);
+ init_waitqueue_head(&adc_dev->wq_data_avail);
+
+ err = request_irq(adc_dev->irq, tiadc_irq, IRQF_SHARED,
+ indio_dev->name, indio_dev);
if (err)
goto err_free_channels;
+ err = iio_triggered_buffer_setup(indio_dev, &tiadc_iio_pollfunc,
+ &tiadc_trigger_h, &tiadc_buffer_setup_ops);
+ if (err)
+ goto err_free_irq;
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto err_buffer_unregister;
+
platform_set_drvdata(pdev, indio_dev);
return 0;
+err_buffer_unregister:
+ iio_buffer_unregister(indio_dev);
+err_free_irq:
+ free_irq(adc_dev->irq, indio_dev);
err_free_channels:
tiadc_channels_remove(indio_dev);
err_free_device:
@@ -265,7 +486,9 @@ static int tiadc_remove(struct platform_device *pdev)
struct tiadc_device *adc_dev = iio_priv(indio_dev);
u32 step_en;
+ free_irq(adc_dev->irq, indio_dev);
iio_device_unregister(indio_dev);
+ iio_buffer_unregister(indio_dev);
tiadc_channels_remove(indio_dev);
step_en = get_adc_step_mask(adc_dev);
@@ -303,10 +526,16 @@ static int tiadc_resume(struct device *dev)
/* Make sure ADC is powered up */
restore = tiadc_readl(adc_dev, REG_CTRL);
- restore &= ~(CNTRLREG_POWERDOWN);
+ restore &= ~(CNTRLREG_TSCSSENB);
tiadc_writel(adc_dev, REG_CTRL, restore);
- tiadc_step_config(adc_dev);
+ tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+ tiadc_step_config(indio_dev);
+
+ /* Make sure ADC is powered up */
+ restore &= ~(CNTRLREG_POWERDOWN);
+ restore |= CNTRLREG_TSCSSENB;
+ tiadc_writel(adc_dev, REG_CTRL, restore);
return 0;
}
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e2db978..a1d22de 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -46,6 +46,9 @@
/* Step Enable */
#define STEPENB_MASK (0x1FFFF << 0)
#define STEPENB(val) ((val) << 0)
+#define ENB(val) (1 << (val))
+#define STPENB_STEPENB STEPENB(0x1FFFF)
+#define STPENB_STEPENB_TC STEPENB(0x1FFF)
/* IRQ enable */
#define IRQENB_HW_PEN BIT(0)
@@ -54,11 +57,14 @@
#define IRQENB_FIFO0UNDRFLW BIT(4)
#define IRQENB_FIFO1THRES BIT(5)
#define IRQENB_PENUP BIT(9)
+#define IRQENB_FIFO1OVRRUN BIT(6)
+#define IRQENB_FIFO1UNDRFLW BIT(7)
/* Step Configuration */
#define STEPCONFIG_MODE_MASK (3 << 0)
#define STEPCONFIG_MODE(val) ((val) << 0)
#define STEPCONFIG_MODE_HWSYNC STEPCONFIG_MODE(2)
+#define STEPCONFIG_MODE_SWCNT STEPCONFIG_MODE(1)
#define STEPCONFIG_AVG_MASK (7 << 2)
#define STEPCONFIG_AVG(val) ((val) << 2)
#define STEPCONFIG_AVG_16 STEPCONFIG_AVG(4)
@@ -126,7 +132,8 @@
#define MAX_CLK_DIV 7
#define TOTAL_STEPS 16
#define TOTAL_CHANNELS 8
-
+#define FIFO1_THRESHOLD 19
+#define FIFO_SIZE 64
/*
* ADC runs at 3MHz, and it takes
* 15 cycles to latch one data output.
@@ -155,6 +162,10 @@ struct ti_tscadc_dev {
/* adc device */
struct adc_device *adc;
+
+ /* Context save */
+ unsigned int irqstat;
+ unsigned int ctrl;
};
static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 0/4] iio: input: ti_am335x_adc: Add continuous sampling support round 4
From: Zubair Lutfullah @ 2013-08-13 16:48 UTC (permalink / raw)
To: jic23, dmitry.torokhov, sameo, lee.jones
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
Note: These apply to the fixes-togreg branch in IIO because of
a fix on the adc side in there.
The first few are for input which tweak the TSC driver to
allow ADC in continuous mode with irqs to work nicely together.
The last one is for iio which adds continuous mode to the adc side.
Received feedback on previous series.
1. What if IRQs occur together?
This is handled now. Even if both assert.
They both work.
2. IIO error handling wrong.
Fixed now.
3. Headers wierd in IIO.
Fixed.
Apart from that, found a few bugs in continuous mode
here and there. And squashed them into the same patch.
Thanks
Zubair Lutfullah
Zubair Lutfullah (4):
input: ti_am335x_tsc: correct step mask update after IRQ
input: ti_am335x_tsc: Increase sequencer delay time
input: ti_tsc: Enable shared IRQ for TSC and add overrun, underflow
checks
iio: ti_am335x_adc: Add continuous sampling and trigger support
drivers/iio/adc/ti_am335x_adc.c | 353 ++++++++++++++++++++++++-----
drivers/input/touchscreen/ti_am335x_tsc.c | 45 +++-
include/linux/mfd/ti_am335x_tscadc.h | 15 +-
3 files changed, 342 insertions(+), 71 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH 3/4] input: ti_tsc: Enable shared IRQ for TSC and add overrun, underflow checks
From: Zubair Lutfullah @ 2013-08-13 16:48 UTC (permalink / raw)
To: jic23, dmitry.torokhov, sameo, lee.jones
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-1-git-send-email-zubair.lutfullah@gmail.com>
Enable shared IRQ to allow ADC to share IRQ line from
parent MFD core. Only FIFO0 IRQs are for TSC and handled
on the TSC side.
Patch also adds overrun and underflow irq handlers.
Russ Dill (TI) worked on overrun and underflow handlers.
Rachna Patil (TI) laid ground work for shared IRQ.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 37 +++++++++++++++++++++++++----
include/linux/mfd/ti_am335x_tscadc.h | 2 ++
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 766bc7e..9c114b2 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -256,14 +256,39 @@ static irqreturn_t titsc_irq(int irq, void *dev)
{
struct titsc *ts_dev = dev;
struct input_dev *input_dev = ts_dev->input;
- unsigned int status, irqclr = 0;
+ unsigned int status, irqclr = 0, config = 0;
unsigned int x = 0, y = 0;
unsigned int z1, z2, z;
unsigned int fsm;
status = titsc_readl(ts_dev, REG_IRQSTATUS);
- if (status & IRQENB_FIFO0THRES) {
+ /*
+ * ADC and touchscreen share the IRQ line.
+ * FIFO1 threshold, FIFO1 Overrun and FIFO1 underflow
+ * interrupts are used by ADC. Handle FIFO0 IRQs here only
+ * and check if any IRQs left in case both fifos interrupt.
+ * If any irq left, return none, else return handled.
+ */
+ if ((status & IRQENB_FIFO0OVRRUN) ||
+ (status & IRQENB_FIFO0UNDRFLW)) {
+
+ config = titsc_readl(ts_dev, REG_CTRL);
+ config &= ~(CNTRLREG_TSCSSENB);
+ titsc_writel(ts_dev, REG_CTRL, config);
+
+ if (status & IRQENB_FIFO0UNDRFLW) {
+ titsc_writel(ts_dev, REG_IRQSTATUS,
+ (status | IRQENB_FIFO0UNDRFLW));
+ irqclr |= IRQENB_FIFO0UNDRFLW;
+ } else {
+ titsc_writel(ts_dev, REG_IRQSTATUS,
+ (status | IRQENB_FIFO0OVRRUN));
+ irqclr |= IRQENB_FIFO0OVRRUN;
+ }
+ titsc_writel(ts_dev, REG_CTRL,
+ (config | CNTRLREG_TSCSSENB));
+ } else if (status & IRQENB_FIFO0THRES) {
titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
@@ -317,9 +342,11 @@ static irqreturn_t titsc_irq(int irq, void *dev)
}
if (irqclr) {
- titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+ titsc_writel(ts_dev, REG_IRQSTATUS, (status | irqclr));
am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
- return IRQ_HANDLED;
+ status = titsc_readl(ts_dev, REG_IRQSTATUS);
+ if (status == false)
+ return IRQ_HANDLED;
}
return IRQ_NONE;
}
@@ -391,7 +418,7 @@ static int titsc_probe(struct platform_device *pdev)
}
err = request_irq(ts_dev->irq, titsc_irq,
- 0, pdev->dev.driver->name, ts_dev);
+ IRQF_SHARED, pdev->dev.driver->name, ts_dev);
if (err) {
dev_err(&pdev->dev, "failed to allocate irq.\n");
goto err_free_mem;
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index db1791b..e2db978 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -50,6 +50,8 @@
/* IRQ enable */
#define IRQENB_HW_PEN BIT(0)
#define IRQENB_FIFO0THRES BIT(2)
+#define IRQENB_FIFO0OVRRUN BIT(3)
+#define IRQENB_FIFO0UNDRFLW BIT(4)
#define IRQENB_FIFO1THRES BIT(5)
#define IRQENB_PENUP BIT(9)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/4] input: ti_am335x_tsc: Increase sequencer delay time
From: Zubair Lutfullah @ 2013-08-13 16:48 UTC (permalink / raw)
To: jic23, dmitry.torokhov, sameo, lee.jones
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-1-git-send-email-zubair.lutfullah@gmail.com>
Before checking PEN UP event, IRQ delays for this amount
of time to let FSM stabilize. Previously, with only the
TSC driver in IRQ mode, this delay was sufficient.
The delay is increased slightly to accomodate if the ADC
is also being used simultaneously in continuous mode.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index e165fcb..766bc7e 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -31,7 +31,7 @@
#include <linux/mfd/ti_am335x_tscadc.h>
#define ADCFSM_STEPID 0x10
-#define SEQ_SETTLE 275
+#define SEQ_SETTLE 350
#define MAX_12BIT ((1 << 12) - 1)
static const int config_pins[] = {
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/4] input: ti_am335x_tsc: correct step mask update after IRQ
From: Zubair Lutfullah @ 2013-08-13 16:48 UTC (permalink / raw)
To: jic23, dmitry.torokhov, sameo, lee.jones
Cc: linux-iio, linux-input, linux-kernel, bigeasy, gregkh, Russ.Dill
In-Reply-To: <1376412499-21007-1-git-send-email-zubair.lutfullah@gmail.com>
TSC steps should be enabled again after IRQ routine.
This fix ensures they are updated correctly every time.
Signed-off-by: Zubair Lutfullah <zubair.lutfullah@gmail.com>
---
drivers/input/touchscreen/ti_am335x_tsc.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index e1c5300..e165fcb 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -52,6 +52,7 @@ struct titsc {
u32 config_inp[4];
u32 bit_xp, bit_xn, bit_yp, bit_yn;
u32 inp_xp, inp_xn, inp_yp, inp_yn;
+ u32 step_mask;
};
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
@@ -196,7 +197,8 @@ static void titsc_step_config(struct titsc *ts_dev)
/* The steps1 … end and bit 0 for TS_Charge */
stepenable = (1 << (end_step + 2)) - 1;
- am335x_tsc_se_set(ts_dev->mfd_tscadc, stepenable);
+ ts_dev->step_mask = stepenable;
+ am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
}
static void titsc_read_coordinates(struct titsc *ts_dev,
@@ -316,7 +318,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
if (irqclr) {
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
- am335x_tsc_se_update(ts_dev->mfd_tscadc);
+ am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
return IRQ_HANDLED;
}
return IRQ_NONE;
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH] mc13xxx-ts: use zero as default value if no pdata was defined
From: Dmitry Torokhov @ 2013-08-13 16:46 UTC (permalink / raw)
To: Michael Grzeschik; +Cc: linux-input, linux-kernel, kernel, Samuel Ortiz
In-Reply-To: <1376396070-24507-1-git-send-email-m.grzeschik@pengutronix.de>
Hi Michael,
On Tue, Aug 13, 2013 at 02:14:30PM +0200, Michael Grzeschik wrote:
> In case of devicetree, we currently don't have a way to append pdata for
> the touchscreen. The current approach is to bail out in that case.
> This patch makes it possible to probe the touchscreen without pdata
> and use zero as default values for the atox and ato adc conversion.
I'd rather you added the devicetree support to the driver.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 1/2] Revert "Revert "HID: Fix logitech-dj: missing Unifying device issue""
From: Peter Hurley @ 2013-08-13 16:34 UTC (permalink / raw)
To: Peter Wu
Cc: Nestor Lopez Casado, jkosina, benjamin.tissoires, adlr,
joseph.salisbury, linux-input, linux-kernel, Sarah Sharp,
linux-usb
In-Reply-To: <1614065.3ZkY1dAtg4@al>
On 08/13/2013 11:42 AM, Peter Wu wrote:
> On Tuesday 13 August 2013 08:13:17 Peter Hurley wrote:
>> On 08/12/2013 05:54 PM, Peter Wu wrote:
>>> On Thursday 18 July 2013 16:28:01 Peter Hurley wrote:
>>>> Before we revert to using the workaround, I'd like to suggest that
>>>> this new "hidden" problem may be an interaction with the xhci_hcd host
>>>> controller driver only.
>>>>
>>>> Looking at the related bug, the OP indicates the machine only has
>>>> USB3 ports. Additionally, comments #7, #100, and #104 of the original
>>>> bug report add additional information that would seem to confirm
>>>> this suspicion.
>>>>
>>>> Let me add I have this USB device running on the uhci_hcd driver
>>>> with or without this workaround on v3.10.
>>>
>>> This problem does not seem specific to xhci, uhci seems also effected.
>>
>> If true, it would certainly help to have a bug report confirming uhci
>> failure from a bare-metal system which contained:
>> 1) kernel version
>> 2) complete dmesg output
>> 3) lsusb -v output
>> 4) lsmod output
>> 5) usbmon capture from a plug attempt
>
> I was too fast in drawing a conclusion, besides the kernel I also upgraded
> some other packages. Today the issue also showed up in 3.9.9 + updated
> packages.
>
> When checking the dmesg, the issue solved by this patch did not occur (the
> enumeration was successful).
Thanks for double-checking.
>>> Today I
>>> upgraded a system (running Arch Linux) from kernel 3.9.9 to 3.10.5. After
>>> a
>>> reboot to 3.10.5, things broke. The setup:
>>>
>>> - There are two USB receivers plugged into USB 1.1 ports (different buses
>>> according to lsusb, uhci), each receiver is paired to a K360 keyboard.
>>> - One of the receivers are passed to a QEMU guest with -usbdevice
>>> host:$busid. $devid. This keyboard is working (probably because QEMU
>>> performed a reset). - Since 3.10.5, the keyboard that is *not* passed to
>>> the QEMU guest is not functioning on reboot.
>>>
>>> After closing the QEMU guest, the USB bus gets reset(?) after which the
>>> other keyboard suddenly gets detected. I had only booted 3.10.5 twice
>>> before rolling back to 3.9.9, both boots triggered the issue. Do I need
>>> to provide a usbmon, lsusb, dmesg and/ or other details from 3.10.5?
>>
>> Do both keyboards work on bare metal? Seems like this problem might be
>> specific to qemu (or kvm) and you may get more insight on those lists.
>
> I haven't tested that, the system automatically boots into openbox + QEMU.
> Previously, both keyboards worked on bare metal, so I think it still works.
>
>>> Note that there are other Arch Linux users who have reported issues[1][2]
>>
>> Unfortunately, not even one user in the referenced reports identified
>> the usb hub the receiver was plugged into.
>
> I've asked it now.
Thanks. And if someone has a uhci failure, filing a new bug on
bugzilla.kernel.org _and_ posting to linux-usb@ and
linux-input@vger.kernel.org improves the chances of the right
people seeing the problem. [xhci already has several reports plus I
subscribed to the kernel bug linked from the ArchLinux bug report.]
>>> since upgrading to 3.10.z. Triggering a re-enumeration by writing the
>>> magic
>>> HID++ message[3] makes the paired devices appear again (as reported in
>>> forums[1], I haven't tried this on the affected UHCI machine).
>>>
>>> While the underlying bug is fixed, can this patch be forwarded to stable?
>>> I see that 3.10.6 has been released, but still without this patch.
>>
>> This is still a workaround and not really a fix for the underlying bug.
>
> I meant to say, "while the underlying bug is *being* fixed". Anyway, can this
> patch be applied to 3.10?
That's really Jiri's call. TBH, I can't blame him for wanting to shake this
out in 3.11 before pushing to stable.
> Sorry for the confusion with uhci, looking further it seems that the wrong USB
> receiver is being passed to QEMU. It's not a kernel issue, perhaps I can blame
> libusbx.
No apologies necessary.
Regards,
Peter Hurley
^ permalink raw reply
* Re: [PATCH 1/2] Revert "Revert "HID: Fix logitech-dj: missing Unifying device issue""
From: Peter Wu @ 2013-08-13 15:42 UTC (permalink / raw)
To: Peter Hurley
Cc: Nestor Lopez Casado, jkosina-AlSwsSmVLrQ,
benjamin.tissoires-Re5JQEeQqe8AvxtiuMwx3w,
adlr-F7+t8E8rja9g9hUCZPvPmw,
joseph.salisbury-Z7WLFzj8eWMS+FvcfC7Uqw,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sarah Sharp,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <520A22DD.3010308-WaGBZJeGNqdsbIuE7sb01tBPR1lH4CV8@public.gmane.org>
On Tuesday 13 August 2013 08:13:17 Peter Hurley wrote:
> On 08/12/2013 05:54 PM, Peter Wu wrote:
> > On Thursday 18 July 2013 16:28:01 Peter Hurley wrote:
> >> Before we revert to using the workaround, I'd like to suggest that
> >> this new "hidden" problem may be an interaction with the xhci_hcd host
> >> controller driver only.
> >>
> >> Looking at the related bug, the OP indicates the machine only has
> >> USB3 ports. Additionally, comments #7, #100, and #104 of the original
> >> bug report add additional information that would seem to confirm
> >> this suspicion.
> >>
> >> Let me add I have this USB device running on the uhci_hcd driver
> >> with or without this workaround on v3.10.
> >
> > This problem does not seem specific to xhci, uhci seems also effected.
>
> If true, it would certainly help to have a bug report confirming uhci
> failure from a bare-metal system which contained:
> 1) kernel version
> 2) complete dmesg output
> 3) lsusb -v output
> 4) lsmod output
> 5) usbmon capture from a plug attempt
I was too fast in drawing a conclusion, besides the kernel I also upgraded
some other packages. Today the issue also showed up in 3.9.9 + updated
packages.
When checking the dmesg, the issue solved by this patch did not occur (the
enumeration was successful).
> > Today I
> > upgraded a system (running Arch Linux) from kernel 3.9.9 to 3.10.5. After
> > a
> > reboot to 3.10.5, things broke. The setup:
> >
> > - There are two USB receivers plugged into USB 1.1 ports (different buses
> > according to lsusb, uhci), each receiver is paired to a K360 keyboard.
> > - One of the receivers are passed to a QEMU guest with -usbdevice
> > host:$busid. $devid. This keyboard is working (probably because QEMU
> > performed a reset). - Since 3.10.5, the keyboard that is *not* passed to
> > the QEMU guest is not functioning on reboot.
> >
> > After closing the QEMU guest, the USB bus gets reset(?) after which the
> > other keyboard suddenly gets detected. I had only booted 3.10.5 twice
> > before rolling back to 3.9.9, both boots triggered the issue. Do I need
> > to provide a usbmon, lsusb, dmesg and/ or other details from 3.10.5?
>
> Do both keyboards work on bare metal? Seems like this problem might be
> specific to qemu (or kvm) and you may get more insight on those lists.
I haven't tested that, the system automatically boots into openbox + QEMU.
Previously, both keyboards worked on bare metal, so I think it still works.
> > Note that there are other Arch Linux users who have reported issues[1][2]
>
> Unfortunately, not even one user in the referenced reports identified
> the usb hub the receiver was plugged into.
I've asked it now.
> > since upgrading to 3.10.z. Triggering a re-enumeration by writing the
> > magic
> > HID++ message[3] makes the paired devices appear again (as reported in
> > forums[1], I haven't tried this on the affected UHCI machine).
> >
> > While the underlying bug is fixed, can this patch be forwarded to stable?
> > I see that 3.10.6 has been released, but still without this patch.
>
> This is still a workaround and not really a fix for the underlying bug.
I meant to say, "while the underlying bug is *being* fixed". Anyway, can this
patch be applied to 3.10?
Sorry for the confusion with uhci, looking further it seems that the wrong USB
receiver is being passed to QEMU. It's not a kernel issue, perhaps I can blame
libusbx.
Regards,
Peter
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 0/3] HID: Win 8 multitouch panels detection in core
From: Benjamin Tissoires @ 2013-08-13 14:58 UTC (permalink / raw)
To: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, Alexander Holler, linux-input, linux-kernel
Hi guys,
This series is the following of the patch "HID: multitouch: do not init
reports for multitouch devices" which I sent on the 12th of July.
The goal is just to not call a GET_REPORT on input reports to imitate
what Win 8 is doing with the touch panels. However, to have an accurate
detection of Win 8 panels and to not introduce regressions, we need
to adapt the pre-scanning of hid devices.
The first patch drops the custom parsing and relies on the well tested
parser that we have in hid-core. This cleans up the pre-scanning and
allows us to scan the features in addition to the input reports and the
collections.
The second patch uses the pre-scanning method to detect Win 8 multitouch
panels. This allows a simplification in hid-multitouch because those
panels will now use the general path instead of having special quirks
attached during the parsing.
The third patch introduce the actual quirk which would allow us not to
have to maintain a growing list of quirks.
Cheers,
Benjamin
Benjamin Tissoires (3):
HID: Use existing parser for pre-scanning the report descriptors
HID: detect Win 8 multitouch devices in core
HID: do not init input reports for Win 8 multitouch devices
drivers/hid/hid-core.c | 143 +++++++++++++++++++++++++++++++-----------
drivers/hid/hid-multitouch.c | 36 +++++++----
drivers/hid/usbhid/hid-core.c | 11 +++-
include/linux/hid.h | 7 +++
4 files changed, 143 insertions(+), 54 deletions(-)
--
1.8.3.1
^ permalink raw reply
* [PATCH 3/3] HID: do not init input reports for Win 8 multitouch devices
From: Benjamin Tissoires @ 2013-08-13 14:58 UTC (permalink / raw)
To: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, Alexander Holler, linux-input, linux-kernel
In-Reply-To: <1376405889-12378-1-git-send-email-benjamin.tissoires@redhat.com>
Some multitouch screens do not like to be polled for input reports.
However, the Win8 spec says that all touches should be sent during
each report, making the initialization of reports unnecessary.
The Win7 spec is less precise, so do not use this for those devices.
Add the quirk HID_QUIRK_NO_INIT_INPUT_REPORTS so that we do not have to
introduce a quirk for each problematic device. This quirk makes the driver
behave the same way the Win 8 does. It actually retrieves the features,
but not the inputs.
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/hid/hid-multitouch.c | 12 ++++++++++++
drivers/hid/usbhid/hid-core.c | 11 ++++++++---
include/linux/hid.h | 1 +
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index c28ef86..ac28f08 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -951,6 +951,18 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
+ /*
+ * Handle special quirks for Windows 8 certified devices.
+ */
+ if (id->group == HID_GROUP_MULTITOUCH_WIN_8)
+ /*
+ * Some multitouch screens do not like to be polled for input
+ * reports. Fortunately, the Win8 spec says that all touches
+ * should be sent during each report, making the initialization
+ * of input reports unnecessary.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS;
+
td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
dev_err(&hdev->dev, "cannot allocate multitouch data\n");
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index bd38cdf..44df131 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -750,12 +750,17 @@ void usbhid_init_reports(struct hid_device *hid)
{
struct hid_report *report;
struct usbhid_device *usbhid = hid->driver_data;
+ struct hid_report_enum *report_enum;
int err, ret;
- list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
- usbhid_submit_report(hid, report, USB_DIR_IN);
+ if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) {
+ report_enum = &hid->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(report, &report_enum->report_list, list)
+ usbhid_submit_report(hid, report, USB_DIR_IN);
+ }
- list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+ report_enum = &hid->report_enum[HID_FEATURE_REPORT];
+ list_for_each_entry(report, &report_enum->report_list, list)
usbhid_submit_report(hid, report, USB_DIR_IN);
err = 0;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 28ee7dd..d55f244 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -283,6 +283,7 @@ struct hid_item {
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
+#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
--
1.8.3.1
^ permalink raw reply related
* [PATCH 2/3] HID: detect Win 8 multitouch devices in core
From: Benjamin Tissoires @ 2013-08-13 14:58 UTC (permalink / raw)
To: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, Alexander Holler, linux-input, linux-kernel
In-Reply-To: <1376405889-12378-1-git-send-email-benjamin.tissoires@redhat.com>
Detecting Win 8 multitouch devices in core allows us to set quirks
before the device is parsed through hid_hw_start().
It also simplifies the detection of those devices in hid-multitouch and
makes the handling of those devices cleaner.
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/hid/hid-core.c | 12 ++++++++++++
drivers/hid/hid-multitouch.c | 24 +++++++++++-------------
include/linux/hid.h | 2 ++
3 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d8cdb0a..11c6f2b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -683,6 +683,13 @@ static void hid_scan_input_usage(struct hid_parser *parser, u32 usage)
parser->flags |= HID_FLAG_MULTITOUCH;
}
+static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage)
+{
+ if (usage == 0xff0000c5 && parser->global.report_count == 256 &&
+ parser->global.report_size == 8)
+ parser->flags |= HID_FLAG_WIN_8_CERTIFIED;
+}
+
static void hid_scan_open_collection(struct hid_parser *parser, unsigned type)
{
if (parser->global.usage_page == HID_UP_SENSOR &&
@@ -710,6 +717,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
case HID_MAIN_ITEM_TAG_OUTPUT:
break;
case HID_MAIN_ITEM_TAG_FEATURE:
+ for (i = 0; i < parser->local.usage_index; i++)
+ hid_scan_feature_usage(parser, parser->local.usage[i]);
break;
default:
hid_err(parser->device, "unknown main item tag 0x%x\n",
@@ -779,6 +788,9 @@ out:
case HID_FLAG_MULTITOUCH:
hid->group = HID_GROUP_MULTITOUCH;
break;
+ case HID_FLAG_MULTITOUCH | HID_FLAG_WIN_8_CERTIFIED:
+ hid->group = HID_GROUP_MULTITOUCH_WIN_8;
+ break;
case HID_FLAG_SENSOR_HUB:
hid->group = HID_GROUP_SENSOR_HUB;
break;
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 0fe00e2..c28ef86 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -133,6 +133,7 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_NSMU 0x000a
#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010
#define MT_CLS_DUAL_CONTACT_ID 0x0011
+#define MT_CLS_WIN_8 0x0012
/* vendor specific classes */
#define MT_CLS_3M 0x0101
@@ -205,6 +206,11 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_SLOT_IS_CONTACTID,
.maxcontacts = 2 },
+ { .name = MT_CLS_WIN_8,
+ .quirks = MT_QUIRK_ALWAYS_VALID |
+ MT_QUIRK_IGNORE_DUPLICATES |
+ MT_QUIRK_HOVERING |
+ MT_QUIRK_CONTACT_CNT_ACCURATE },
/*
* vendor specific classes
@@ -332,19 +338,6 @@ static void mt_feature_mapping(struct hid_device *hdev,
td->maxcontacts = td->mtclass.maxcontacts;
break;
- case 0xff0000c5:
- if (field->report_count == 256 && field->report_size == 8) {
- /* Win 8 devices need special quirks */
- __s32 *quirks = &td->mtclass.quirks;
- *quirks |= MT_QUIRK_ALWAYS_VALID;
- *quirks |= MT_QUIRK_IGNORE_DUPLICATES;
- *quirks |= MT_QUIRK_HOVERING;
- *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE;
- *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP;
- *quirks &= ~MT_QUIRK_VALID_IS_INRANGE;
- *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE;
- }
- break;
}
}
@@ -1346,6 +1339,11 @@ static const struct hid_device_id mt_devices[] = {
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
+
+ /* Generic Win 8 certified MT device */
+ { .driver_data = MT_CLS_WIN_8,
+ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8,
+ HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, mt_devices);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7d823db..28ee7dd 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -295,6 +295,7 @@ struct hid_item {
#define HID_GROUP_GENERIC 0x0001
#define HID_GROUP_MULTITOUCH 0x0002
#define HID_GROUP_SENSOR_HUB 0x0003
+#define HID_GROUP_MULTITOUCH_WIN_8 0x0004
/*
* This is the global environment of the parser. This information is
@@ -535,6 +536,7 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
#define HID_FLAG_MULTITOUCH 0x0001
#define HID_FLAG_SENSOR_HUB 0x0002
+#define HID_FLAG_WIN_8_CERTIFIED 0x0004
struct hid_parser {
struct hid_global global;
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/3] HID: Use existing parser for pre-scanning the report descriptors
From: Benjamin Tissoires @ 2013-08-13 14:58 UTC (permalink / raw)
To: Benjamin Tissoires, Henrik Rydberg, Jiri Kosina, Stephane Chatty,
Mika Westerberg, Alexander Holler, linux-input, linux-kernel
In-Reply-To: <1376405889-12378-1-git-send-email-benjamin.tissoires@redhat.com>
hid_scan_report() implements its own HID report descriptor parsing. It was
fine until we added the SENSOR_HUB detection. It is going to be even worse
with the detection of Win 8 certified touchscreen, as this detection
relies on a special feature and on the report_size and report_count fields.
We can use the existing HID parser in hid-core for hid_scan_report()
by re-using the code from hid_open_report(). hid_parser_global,
hid_parser_local and hid_parser_reserved does not have any side effects.
We just need to reimplement the MAIN_ITEM callback to have a proper
parsing without side effects.
Instead of directly overwriting the ->group field, this patch introduce
a ->flags field and then decide which group the device belongs to,
depending on the whole parsing (not just the local item). This will be
useful for Win 8 multitouch devices, which are multitouch devices and
Win 8 certified (so 2 flags to check).
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/hid/hid-core.c | 131 +++++++++++++++++++++++++++++++++++--------------
include/linux/hid.h | 4 ++
2 files changed, 97 insertions(+), 38 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 3efe19f..d8cdb0a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -677,10 +677,49 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
return NULL;
}
-static void hid_scan_usage(struct hid_device *hid, u32 usage)
+static void hid_scan_input_usage(struct hid_parser *parser, u32 usage)
{
if (usage == HID_DG_CONTACTID)
- hid->group = HID_GROUP_MULTITOUCH;
+ parser->flags |= HID_FLAG_MULTITOUCH;
+}
+
+static void hid_scan_open_collection(struct hid_parser *parser, unsigned type)
+{
+ if (parser->global.usage_page == HID_UP_SENSOR &&
+ type == HID_COLLECTION_PHYSICAL)
+ parser->flags |= HID_FLAG_SENSOR_HUB;
+}
+
+static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
+{
+ __u32 data;
+ int i;
+
+ data = item_udata(item);
+
+ switch (item->tag) {
+ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+ hid_scan_open_collection(parser, data & 0xff);
+ break;
+ case HID_MAIN_ITEM_TAG_END_COLLECTION:
+ break;
+ case HID_MAIN_ITEM_TAG_INPUT:
+ for (i = 0; i < parser->local.usage_index; i++)
+ hid_scan_input_usage(parser, parser->local.usage[i]);
+ break;
+ case HID_MAIN_ITEM_TAG_OUTPUT:
+ break;
+ case HID_MAIN_ITEM_TAG_FEATURE:
+ break;
+ default:
+ hid_err(parser->device, "unknown main item tag 0x%x\n",
+ item->tag);
+ }
+
+ /* Reset the local parser environment */
+ memset(&parser->local, 0, sizeof(parser->local));
+
+ return 0;
}
/*
@@ -690,49 +729,65 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage)
*/
static int hid_scan_report(struct hid_device *hid)
{
- unsigned int page = 0, delim = 0;
+ struct hid_parser *parser;
+ struct hid_item item;
__u8 *start = hid->dev_rdesc;
__u8 *end = start + hid->dev_rsize;
- unsigned int u, u_min = 0, u_max = 0;
- struct hid_item item;
+ int ret;
+ static int (*dispatch_type[])(struct hid_parser *parser,
+ struct hid_item *item) = {
+ hid_scan_main,
+ hid_parser_global,
+ hid_parser_local,
+ hid_parser_reserved
+ };
- hid->group = HID_GROUP_GENERIC;
+ parser = vzalloc(sizeof(struct hid_parser));
+ if (!parser)
+ return -ENOMEM;
+
+ parser->device = hid;
+
+ ret = -EINVAL;
while ((start = fetch_item(start, end, &item)) != NULL) {
- if (item.format != HID_ITEM_FORMAT_SHORT)
- return -EINVAL;
- if (item.type == HID_ITEM_TYPE_GLOBAL) {
- if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE)
- page = item_udata(&item) << 16;
- } else if (item.type == HID_ITEM_TYPE_LOCAL) {
- if (delim > 1)
- break;
- u = item_udata(&item);
- if (item.size <= 2)
- u += page;
- switch (item.tag) {
- case HID_LOCAL_ITEM_TAG_DELIMITER:
- delim += !!u;
- break;
- case HID_LOCAL_ITEM_TAG_USAGE:
- hid_scan_usage(hid, u);
- break;
- case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
- u_min = u;
- break;
- case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
- u_max = u;
- for (u = u_min; u <= u_max; u++)
- hid_scan_usage(hid, u);
- break;
+
+ if (item.format != HID_ITEM_FORMAT_SHORT) {
+ hid_err(hid, "unexpected long global item\n");
+ goto out;
+ }
+
+ if (dispatch_type[item.type](parser, &item)) {
+ hid_err(hid, "item %u %u %u %u parsing failed\n",
+ item.format, (unsigned)item.size,
+ (unsigned)item.type, (unsigned)item.tag);
+ goto out;
+ }
+
+ if (start == end) {
+ if (parser->local.delimiter_depth) {
+ hid_err(hid, "unbalanced delimiter at end of report description\n");
+ goto out;
}
- } else if (page == HID_UP_SENSOR &&
- item.type == HID_ITEM_TYPE_MAIN &&
- item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION &&
- (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL)
- hid->group = HID_GROUP_SENSOR_HUB;
+ ret = 0;
+ goto out;
+ }
}
- return 0;
+ hid_err(hid, "item fetching failed at offset %d\n", (int)(end - start));
+out:
+ switch (parser->flags) {
+ case HID_FLAG_MULTITOUCH:
+ hid->group = HID_GROUP_MULTITOUCH;
+ break;
+ case HID_FLAG_SENSOR_HUB:
+ hid->group = HID_GROUP_SENSOR_HUB;
+ break;
+ default:
+ hid->group = HID_GROUP_GENERIC;
+ }
+
+ vfree(parser);
+ return ret;
}
/**
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 5a4e789..7d823db 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -533,6 +533,9 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
#define HID_GLOBAL_STACK_SIZE 4
#define HID_COLLECTION_STACK_SIZE 4
+#define HID_FLAG_MULTITOUCH 0x0001
+#define HID_FLAG_SENSOR_HUB 0x0002
+
struct hid_parser {
struct hid_global global;
struct hid_global global_stack[HID_GLOBAL_STACK_SIZE];
@@ -541,6 +544,7 @@ struct hid_parser {
unsigned collection_stack[HID_COLLECTION_STACK_SIZE];
unsigned collection_stack_ptr;
struct hid_device *device;
+ unsigned flags;
};
struct hid_class_descriptor {
--
1.8.3.1
^ permalink raw reply related
* [PATCH] mc13xxx-ts: use zero as default value if no pdata was defined
From: Michael Grzeschik @ 2013-08-13 12:14 UTC (permalink / raw)
To: linux-input; +Cc: linux-kernel, kernel, Samuel Ortiz, Dmitry Torokhov
In case of devicetree, we currently don't have a way to append pdata for
the touchscreen. The current approach is to bail out in that case.
This patch makes it possible to probe the touchscreen without pdata
and use zero as default values for the atox and ato adc conversion.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
drivers/input/touchscreen/mc13783_ts.c | 14 +++++++++-----
drivers/mfd/mc13xxx-core.c | 11 +++++++----
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index d6f099c..0e24304 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -124,10 +124,17 @@ static void mc13783_ts_work(struct work_struct *work)
container_of(work, struct mc13783_ts_priv, work.work);
unsigned int mode = MC13XXX_ADC_MODE_TS;
unsigned int channel = 12;
+ u8 ato = 0;
+ bool atox = 0;
+
+ if (priv->touch) {
+ ato = priv->touch->ato;
+ atox = priv->touch->atox;
+ }
if (mc13xxx_adc_do_conversion(priv->mc13xxx,
mode, channel,
- priv->touch->ato, priv->touch->atox,
+ ato, atox,
priv->sample) == 0)
mc13783_ts_report_sample(priv);
}
@@ -183,11 +190,8 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
priv->idev = idev;
priv->touch = dev_get_platdata(&pdev->dev);
- if (!priv->touch) {
+ if (!priv->touch)
dev_err(&pdev->dev, "missing platform data\n");
- ret = -ENODEV;
- goto err_free_mem;
- }
/*
* We need separate workqueue because mc13783_adc_do_conversion
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 2a9b100..278601f 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -694,10 +694,6 @@ err_revision:
if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
- if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
- &pdata->touch, sizeof(pdata->touch));
-
if (pdata) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
&pdata->regulators, sizeof(pdata->regulators));
@@ -705,10 +701,17 @@ err_revision:
pdata->leds, sizeof(*pdata->leds));
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
pdata->buttons, sizeof(*pdata->buttons));
+
+ if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
+ &pdata->touch, sizeof(pdata->touch));
} else {
mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
mc13xxx_add_subdevice(mc13xxx, "%s-led");
mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
+
+ if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
+ mc13xxx_add_subdevice(mc13xxx, "%s-ts");
}
return 0;
--
1.8.4.rc2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox