From db88172eac3d336d043a113efab917720a62af55 Mon Sep 17 00:00:00 2001 From: Tobias Poganiuch <tobias.poganiuch@seco.com> Date: Mon, 21 Aug 2023 15:19:58 +0200 Subject: [PATCH] driver:ucb1400: Added reader/poll mode Added a new mode, that polls the touchscreen periodically instead of using the IRQ to trigger a read. BCS 746-001452 --- drivers/input/touchscreen/ucb1400_ts.c | 151 +++++++++++++++++-------- drivers/mfd/ucb1400_core.c | 4 +- include/linux/ucb1400.h | 5 + 3 files changed, 111 insertions(+), 49 deletions(-) diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index d0c3b11640626..32da555741439 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -202,27 +202,13 @@ static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb) "ucb1400: unexpected IE_STATUS = %#x\n", isr); } -/* - * A restriction with interrupts exists when using the ucb1400, as - * the codec read/write routines may sleep while waiting for codec - * access completion and uses semaphores for access control to the - * AC97 bus. Therefore the driver is forced to use threaded interrupt - * handler. - */ -static irqreturn_t ucb1400_irq(int irqnr, void *devid) +static void ucb1400_poll_touch(struct ucb1400_ts *ucb, struct touch_data *data) { - struct ucb1400_ts *ucb = devid; bool penup, discard; - struct touch_data data = {0}; int ret; int hys_down = SECO_INF_DOWN_HYSTERESIS; - if (unlikely(irqnr != ucb->irq)) - return IRQ_NONE; - - ucb1400_clear_pending_irq(ucb); - /* Start with a small delay before checking pendown state */ msleep(UCB1400_TS_POLL_PERIOD); @@ -235,14 +221,14 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) if (!penup) { ucb1400_adc_enable(ucb->ac97); - data.x = ucb1400_ts_read_xpos(ucb); - data.y = ucb1400_ts_read_ypos(ucb); - data.p = ucb1400_ts_read_pressure(ucb); + data->x = ucb1400_ts_read_xpos(ucb); + data->y = ucb1400_ts_read_ypos(ucb); + data->p = ucb1400_ts_read_pressure(ucb); ucb1400_adc_disable(ucb->ac97); - if ((SECO_INVALID_VALUE == data.x) || - (SECO_INVALID_VALUE == data.y) || - (SECO_INVALID_VALUE == data.p)) { + if ((SECO_INVALID_VALUE == data->x) || + (SECO_INVALID_VALUE == data->y) || + (SECO_INVALID_VALUE == data->p)) { discard = true; } @@ -254,7 +240,7 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) * needs major rework, but this work improves touch * performance already very much. */ - if (data.p < (UCB1400_FILTER_MAX_ADC_VALUE / 3)) + if (data->p < (UCB1400_FILTER_MAX_ADC_VALUE / 3)) discard = true; } @@ -277,10 +263,10 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) ucb->filter_data.pen_up_start = jiffies_to_msecs(jiffies); if (ucb->use_median_filter) - seco_median_filter(&ucb->ts_idev->dev, &ucb->filter_data, &data); + seco_median_filter(&ucb->ts_idev->dev, &ucb->filter_data, data); if (ucb->use_low_pass_filter) - seco_low_pass_filter(&ucb->ts_idev->dev, &ucb->filter_data, &data); + seco_low_pass_filter(&ucb->ts_idev->dev, &ucb->filter_data, data); if (hys_down) { hys_down--; @@ -289,7 +275,7 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) continue; } - ucb1400_ts_report_event(ucb->ts_idev, &data); + ucb1400_ts_report_event(ucb->ts_idev, data); wait_event_timeout(ucb->ts_wait, ucb->stopped, msecs_to_jiffies(UCB1400_TS_POLL_PERIOD)); @@ -304,6 +290,25 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) * later on. */ seco_init_filter(&ucb->ts_idev->dev, &ucb->filter_data); +} + +/* + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. Therefore the driver is forced to use threaded interrupt + * handler. + */ +static irqreturn_t ucb1400_irq(int irqnr, void *devid) +{ + struct ucb1400_ts *ucb = devid; + struct touch_data data = {0}; + + if (unlikely(irqnr != ucb->irq)) + return IRQ_NONE; + + ucb1400_clear_pending_irq(ucb); + ucb1400_poll_touch(ucb, &data); if (!ucb->stopped) { /* Switch back to interrupt mode. */ @@ -314,15 +319,38 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) return IRQ_HANDLED; } +static void ucb1400_ts_reader(struct work_struct *work) +{ + struct ucb1400_ts *ucb = container_of(work, struct ucb1400_ts, ts_reader.work); + struct touch_data data = {0}; + + ucb1400_poll_touch(ucb, &data); + + if (!ucb->stopped) { + /* Enable pen-down detection */ + ucb1400_ts_mode_int(ucb); + queue_delayed_work(ucb->ts_workq, &ucb->ts_reader, + UCB1400_READER_INTERVAL); + } +} + static void ucb1400_ts_stop(struct ucb1400_ts *ucb) { /* Signal IRQ thread to stop polling and disable the handler. */ ucb->stopped = true; mb(); - wake_up(&ucb->ts_wait); - disable_irq(ucb->irq); - ucb1400_ts_irq_disable(ucb); + if (ucb->irq) { + wake_up(&ucb->ts_wait); + disable_irq(ucb->irq); + ucb1400_ts_irq_disable(ucb); + } else { + cancel_delayed_work_sync(&ucb->ts_reader); + destroy_workqueue(ucb->ts_workq); + + dev_dbg(&ucb->ts_idev->dev, "Disabled ts_reader"); + } + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); } @@ -333,10 +361,30 @@ static void ucb1400_ts_start(struct ucb1400_ts *ucb) ucb->stopped = false; mb(); + /* + * The ucb1400_ts_mode_int function + * enables the pen-down detection, + * which is needed for both operation + * modes. + */ ucb1400_ts_mode_int(ucb); - ucb1400_ts_irq_enable(ucb); - enable_irq(ucb->irq); + if (ucb->irq) { + ucb1400_ts_irq_enable(ucb); + enable_irq(ucb->irq); + } else { + dev_dbg(&ucb->ts_idev->dev, "Init ts_reader"); + + ucb->ts_workq = alloc_ordered_workqueue("kucb1400", 0); + if (ucb->ts_workq) { + INIT_DELAYED_WORK(&ucb->ts_reader, ucb1400_ts_reader); + queue_delayed_work(ucb->ts_workq, &ucb->ts_reader, + UCB1400_READER_INTERVAL); + } else { + dev_err(&ucb->ts_idev->dev, + "Failed to create workqueue"); + } + } } static int ucb1400_ts_open(struct input_dev *idev) @@ -461,24 +509,28 @@ static int ucb1400_ts_probe(struct platform_device *pdev) input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, UCB1400_FILTER_MAX_ADC_VALUE, 0, 0); - ucb1400_ts_stop(ucb); + if (ucb->irq) { + ucb1400_ts_stop(ucb); - error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "UCB1400", ucb); - if (error) { - dev_err(&pdev->dev, - "unable to grab irq%d: %d\n", ucb->irq, error); - goto err_free_devs; - } + error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "UCB1400", ucb); + if (error) { + dev_err(&pdev->dev, + "unable to grab irq%d: %d\n", ucb->irq, error); + goto err_free_devs; + } - /* - * request_threaded_irq() also enables the IRQ. We disable it again - * immediately to balance enable/disable calls. - * Note: We already called ucb1400_ts_stop() just before, which has - * taken care of IRQ disabling in the UCB1400 itself. - */ - disable_irq(ucb->irq); + /* + * request_threaded_irq() also enables the IRQ. We disable it again + * immediately to balance enable/disable calls. + * Note: We already called ucb1400_ts_stop() just before, which has + * taken care of IRQ disabling in the UCB1400 itself. + */ + disable_irq(ucb->irq); + } else { + dev_info(&pdev->dev, "No IRQ configured"); + } error = input_register_device(ucb->ts_idev); if (error) @@ -487,7 +539,8 @@ static int ucb1400_ts_probe(struct platform_device *pdev) return 0; err_free_irq: - free_irq(ucb->irq, ucb); + if (ucb->irq) + free_irq(ucb->irq, ucb); err_free_devs: input_free_device(ucb->ts_idev); err: @@ -498,7 +551,9 @@ static int ucb1400_ts_remove(struct platform_device *pdev) { struct ucb1400_ts *ucb = dev_get_platdata(&pdev->dev); - free_irq(ucb->irq, ucb); + if (ucb->irq) + free_irq(ucb->irq, ucb); + input_unregister_device(ucb->ts_idev); return 0; diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index 0894831937fc1..a8ae575389010 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -135,8 +135,10 @@ static int ucb1400_core_probe(struct device *dev) if( ucb_ts.irq < 0 && pdata != NULL && pdata->irq >= 0) ucb_ts.irq = pdata->irq; - if( ucb_ts.irq < 0 ) + if( ucb_ts.irq < 0 ) { dev_warn(dev, "Failed to get irq: %d", ucb_ts.irq); + ucb_ts.irq = 0; + } if (of_find_property(dev->of_node, "seco,use-infinite-mode", NULL)) ucb_ts.use_infinite_mode = true; diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index 32ca9e935fb5d..71b3d60ed0f8a 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -86,6 +86,9 @@ #define UCB1400_FILTER_MAX_ADC_VALUE 0x03FF +/* Interval ts_reader in ms */ +#define UCB1400_READER_INTERVAL 10 + struct ucb1400_gpio { struct gpio_chip gc; struct snd_ac97 *ac97; @@ -106,6 +109,8 @@ struct ucb1400_ts { bool use_low_pass_filter; unsigned pen_up_debounce; struct seco_filter_data filter_data; + struct delayed_work ts_reader; + struct workqueue_struct *ts_workq; }; struct ucb1400 { -- GitLab