Hello all, This patch will need some more work. Currently, it is ignoring ti,settle-delay-usec property, which should be fixed in the next version. Regards, Oleksij On Wed, Nov 04, 2020 at 04:35:28PM +0100, Oleksij Rempel wrote: > Convert multiple full duplex transfers in to a single transfer. > > Current driver version support two modes: > - not filtered > - driver specific debounce filter > - platform specific debounce filter (do any platform provides such > filter?) > > Without filter this HW is not really usable, since the physic of > resistive touchscreen can provide some bounce effects. With filter, we > have constant amount of retries + debounce retries if some anomaly was > detected. > > This patch create one SPI transfer with all fields and not optional retires. If > bounce anomaly was detected, we will make more transfer if needed. > > Without this patch, we will get about 10% CPU load on iMX6S if some thing > is pressing on the screen (holding finger, etc.) > > With this patch, depending in the amount of retries, the CPU load will > be 1.5-2% with "ti,debounce-rep = <3>" and 1% or less with > "ti,debounce-rep = <10>". Depending on the buffer size, different SPI > controllers use different optimizations. On iMX, the buffer below > 64 Byte will be transfered in the PIO mode and beyond this threshold, > it will be transfered in the DMA mode. > > Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx> > --- > drivers/input/touchscreen/ads7846.c | 310 +++++++++++++--------------- > 1 file changed, 143 insertions(+), 167 deletions(-) > > diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c > index e9a520c9ad69..a68b9e5aa505 100644 > --- a/drivers/input/touchscreen/ads7846.c > +++ b/drivers/input/touchscreen/ads7846.c > @@ -64,37 +64,25 @@ > > struct ads7846_buf { > u8 cmd; > - /* This union is a temporary hack. The driver does an in-place > - * endianness conversion. This will be cleaned up in the next > - * patch. > - */ > - union { > - __be16 data_be16; > - u16 data; > - }; > + __be16 data; > } __attribute__((__packed__)); > > - > -struct ts_event { > - bool ignore; > - struct ads7846_buf x; > - struct ads7846_buf y; > - struct ads7846_buf z1; > - struct ads7846_buf z2; > -}; > - > /* > * We allocate this separately to avoid cache line sharing issues when > * driver is used with DMA-based SPI controllers (like atmel_spi) on > * systems where main memory is not DMA-coherent (most non-x86 boards). > */ > struct ads7846_packet { > - struct ts_event tc; > - struct ads7846_buf read_x_cmd; > - struct ads7846_buf read_y_cmd; > - struct ads7846_buf read_z1_cmd; > - struct ads7846_buf read_z2_cmd; > + unsigned int count; > + unsigned int fields; > + unsigned int last_field; > + struct ads7846_buf *rx; > + struct ads7846_buf *tx; > + > struct ads7846_buf pwrdown_cmd; > + > + bool ignore; > + u16 x, y, z1, z2; > }; > > struct ads7846 { > @@ -206,6 +194,13 @@ struct ads7846 { > #define REF_ON (READ_12BIT_DFR(x, 1, 1)) > #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) > > +enum ads7846_cmds { > + ADS7846_Y, > + ADS7846_X, > + ADS7846_Z1, > + ADS7846_Z2, > +}; > + > static int get_pendown_state(struct ads7846 *ts) > { > if (ts->get_pendown_state) > @@ -696,26 +691,68 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val) > return ADS7846_FILTER_OK; > } > > -static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) > +static int ads7846_get_value(struct ads7846_buf *buf) > { > int value; > - struct spi_transfer *t = > - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); > - struct ads7846_buf *buf = t->rx_buf; > > - value = be16_to_cpup(&buf->data_be16); > + value = be16_to_cpup(&buf->data); > > /* enforce ADC output is 12 bits width */ > return (value >> 3) & 0xfff; > } > > -static void ads7846_update_value(struct spi_message *m, int val) > +static void ads7846_set_field_val(struct ads7846 *ts, enum ads7846_cmds filed, > + u16 val) > { > - struct spi_transfer *t = > - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); > - struct ads7846_buf *buf = t->rx_buf; > + struct ads7846_packet *packet = ts->packet; > + > + switch (filed) { > + case ADS7846_Y: > + packet->y = val; > + break; > + case ADS7846_X: > + packet->x = val; > + break; > + case ADS7846_Z1: > + packet->z1 = val; > + break; > + case ADS7846_Z2: > + packet->z2 = val; > + break; > + default: > + pr_err("kappuuuut auch!!!\n"); > + } > +} > + > +static int ads7846_filter_state(struct ads7846 *ts) > +{ > + struct ads7846_packet *packet = ts->packet; > + int msg_idx = 0; > + int action; > + int val; > + unsigned int a, b; > > - buf->data = val; > + packet->ignore = false; > + for (a = packet->last_field; a < packet->fields; a++) { > + packet->last_field = a; > + for (b = 0; b < packet->count; b++) { > + val = ads7846_get_value(&packet->rx[b * packet->fields + a]); > + > + action = ts->filter(ts->filter_data, msg_idx, &val); > + if (action == ADS7846_FILTER_REPEAT) { > + if (b == packet->count - 1) > + return -EAGAIN; > + } else if (action == ADS7846_FILTER_OK) { > + ads7846_set_field_val(ts, a, val); > + break; > + } else { > + packet->ignore = true; > + return 0; > + } > + } > + } > + > + return 0; > } > > static void ads7846_read_state(struct ads7846 *ts) > @@ -723,52 +760,30 @@ static void ads7846_read_state(struct ads7846 *ts) > struct ads7846_packet *packet = ts->packet; > struct spi_message *m; > int msg_idx = 0; > - int val; > - int action; > int error; > > - while (msg_idx < ts->msg_count) { > + packet->last_field = 0; > > + while (msg_idx < ts->msg_count) { > ts->wait_for_sync(); > > m = &ts->msg[msg_idx]; > error = spi_sync(ts->spi, m); > if (error) { > dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); > - packet->tc.ignore = true; > + packet->ignore = true; > return; > } > > - /* > - * Last message is power down request, no need to convert > - * or filter the value. > - */ > - if (msg_idx < ts->msg_count - 1) { > + /* last message is power down request */ > + if (msg_idx == ts->msg_count - 1) > + break; > > - val = ads7846_get_value(ts, m); > + error = ads7846_filter_state(ts); > + if (error) > + continue; > > - action = ts->filter(ts->filter_data, msg_idx, &val); > - switch (action) { > - case ADS7846_FILTER_REPEAT: > - continue; > - > - case ADS7846_FILTER_IGNORE: > - packet->tc.ignore = true; > - msg_idx = ts->msg_count - 1; > - continue; > - > - case ADS7846_FILTER_OK: > - ads7846_update_value(m, val); > - packet->tc.ignore = false; > - msg_idx++; > - break; > - > - default: > - BUG(); > - } > - } else { > - msg_idx++; > - } > + msg_idx++; > } > } > > @@ -783,14 +798,14 @@ static void ads7846_report_state(struct ads7846 *ts) > * from on-the-wire format as part of debouncing to get stable > * readings. > */ > - x = packet->tc.x.data; > - y = packet->tc.y.data; > + x = packet->x; > + y = packet->y; > if (ts->model == 7845) { > z1 = 0; > z2 = 0; > } else { > - z1 = packet->tc.z1.data; > - z2 = packet->tc.z2.data; > + z1 = packet->z1; > + z2 = packet->z2; > } > > /* range filtering */ > @@ -822,9 +837,9 @@ static void ads7846_report_state(struct ads7846 *ts) > * the maximum. Don't report it to user space, repeat at least > * once more the measurement > */ > - if (packet->tc.ignore || Rt > ts->pressure_max) { > + if (packet->ignore || Rt > ts->pressure_max) { > dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", > - packet->tc.ignore, Rt); > + packet->ignore, Rt); > return; > } > > @@ -981,17 +996,62 @@ static int ads7846_setup_pendown(struct spi_device *spi, > return 0; > } > > + > +static u8 ads7846_get_cmd(enum ads7846_cmds filed, int vref) > +{ > + switch (filed) { > + case ADS7846_Y: > + return READ_Y(vref); > + case ADS7846_X: > + return READ_X(vref); > + > + /* 7846 specific commands */ > + case ADS7846_Z1: > + return READ_Z1(vref); > + case ADS7846_Z2: > + return READ_Z2(vref); > + default: > + pr_err("kappuuuut!!!\n"); > + } > + > + return 0; > +} > + > /* > * Set up the transfers to read touchscreen state; this assumes we > * use formula #2 for pressure, not #3. > */ > -static void ads7846_setup_spi_msg(struct ads7846 *ts, > +static int ads7846_setup_spi_msg(struct ads7846 *ts, > const struct ads7846_platform_data *pdata) > { > struct spi_message *m = &ts->msg[0]; > struct spi_transfer *x = ts->xfer; > struct ads7846_packet *packet = ts->packet; > int vref = pdata->keep_vref_on; > + unsigned int a, b; > + size_t size; > + > + if (ts->debounce_max && ts->debounce_rep) > + /* ads7846_debounce_filter() is making ts->debounce_rep + 2 > + * reads. So we need to get all samples for normal case. */ > + packet->count = ts->debounce_rep + 2; > + else > + packet->count = 1; > + > + if (ts->model == 7846) > + packet->fields = 4; /* x, y, z1, z2 */ > + else > + packet->fields = 2; /* x, y */ > + > + size = sizeof(*packet->tx) * packet->count * packet->fields; > + > + packet->tx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL); > + if (!packet->tx) > + return -ENOMEM; > + > + packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL); > + if (!packet->rx) > + return -ENOMEM; > > if (ts->model == 7873) { > /* > @@ -1007,110 +1067,24 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, > spi_message_init(m); > m->context = ts; > > - packet->read_y_cmd.cmd = READ_Y(vref); > - x->tx_buf = &packet->read_y_cmd; > - x->rx_buf = &packet->tc.y; > - x->len = 3; > - spi_message_add_tail(x, m); > - > - /* > - * The first sample after switching drivers can be low quality; > - * optionally discard it, using a second one after the signals > - * have had enough time to stabilize. > - */ > - if (pdata->settle_delay_usecs) { > - x->delay.value = pdata->settle_delay_usecs; > - x->delay.unit = SPI_DELAY_UNIT_USECS; > - x++; > - > - x->tx_buf = &packet->read_y_cmd; > - x->rx_buf = &packet->tc.y; > - x->len = 3; > - spi_message_add_tail(x, m); > + for (a = 0; a < packet->count; a++) { > + for (b = 0; b < packet->fields; b++) { > + u8 cmd = ads7846_get_cmd(b, vref); > + packet->tx[a * packet->fields + b].cmd = cmd; > + } > } > > - ts->msg_count++; > - m++; > - spi_message_init(m); > - m->context = ts; > - > - /* turn y- off, x+ on, then leave in lowpower */ > - x++; > - packet->read_x_cmd.cmd = READ_X(vref); > - x->tx_buf = &packet->read_x_cmd; > - x->rx_buf = &packet->tc.x; > - x->len = 3; > + x->tx_buf = packet->tx; > + x->rx_buf = packet->rx; > + x->len = size; > + x->cs_change = 1; /* do not set CS until we do the PWRDOWN */ > spi_message_add_tail(x, m); > > - /* ... maybe discard first sample ... */ > - if (pdata->settle_delay_usecs) { > - x->delay.value = pdata->settle_delay_usecs; > - x->delay.unit = SPI_DELAY_UNIT_USECS; > - > - x++; > - x->tx_buf = &packet->read_x_cmd; > - x->rx_buf = &packet->tc.x; > - x->len = 3; > - spi_message_add_tail(x, m); > - } > - > - /* turn y+ off, x- on; we'll use formula #2 */ > - if (ts->model == 7846) { > - ts->msg_count++; > - m++; > - spi_message_init(m); > - m->context = ts; > - > - x++; > - packet->read_z1_cmd.cmd = READ_Z1(vref); > - x->tx_buf = &packet->read_z1_cmd; > - x->rx_buf = &packet->tc.z1; > - x->len = 3; > - spi_message_add_tail(x, m); > - > - /* ... maybe discard first sample ... */ > - if (pdata->settle_delay_usecs) { > - x->delay.value = pdata->settle_delay_usecs; > - x->delay.unit = SPI_DELAY_UNIT_USECS; > - > - x++; > - x->tx_buf = &packet->read_z1_cmd; > - x->rx_buf = &packet->tc.z1; > - x->len = 3; > - spi_message_add_tail(x, m); > - } > - > - ts->msg_count++; > - m++; > - spi_message_init(m); > - m->context = ts; > - > - x++; > - packet->read_z2_cmd.cmd = READ_Z2(vref); > - x->tx_buf = &packet->read_z2_cmd; > - x->rx_buf = &packet->tc.z2; > - x->len = 3; > - spi_message_add_tail(x, m); > - > - /* ... maybe discard first sample ... */ > - if (pdata->settle_delay_usecs) { > - x->delay.value = pdata->settle_delay_usecs; > - x->delay.unit = SPI_DELAY_UNIT_USECS; > - > - x++; > - x->tx_buf = &packet->read_z2_cmd; > - x->rx_buf = &packet->tc.z2; > - x->len = 3; > - spi_message_add_tail(x, m); > - } > - } > - > - /* power down */ > ts->msg_count++; > m++; > spi_message_init(m); > m->context = ts; > - > + /* power down */ > x++; > packet->pwrdown_cmd.cmd = PWRDOWN; > x->tx_buf = &packet->pwrdown_cmd; > @@ -1118,6 +1092,8 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts, > > CS_CHANGE(*x); > spi_message_add_tail(x, m); > + > + return 0; > } > > #ifdef CONFIG_OF > -- > 2.28.0 > > -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |