18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Touchscreen driver for WM831x PMICs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Wolfson Microelectronics plc.
68c2ecf20Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/pm.h>
148c2ecf20Sopenharmony_ci#include <linux/input.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/core.h>
188c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/irq.h>
198c2ecf20Sopenharmony_ci#include <linux/mfd/wm831x/pdata.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/types.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * R16424 (0x4028) - Touch Control 1
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */
288c2ecf20Sopenharmony_ci#define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */
298c2ecf20Sopenharmony_ci#define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */
308c2ecf20Sopenharmony_ci#define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */
318c2ecf20Sopenharmony_ci#define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */
328c2ecf20Sopenharmony_ci#define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */
338c2ecf20Sopenharmony_ci#define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */
348c2ecf20Sopenharmony_ci#define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */
358c2ecf20Sopenharmony_ci#define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */
368c2ecf20Sopenharmony_ci#define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */
378c2ecf20Sopenharmony_ci#define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */
388c2ecf20Sopenharmony_ci#define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * R16425 (0x4029) - Touch Control 2
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci#define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */
448c2ecf20Sopenharmony_ci#define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */
458c2ecf20Sopenharmony_ci#define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */
468c2ecf20Sopenharmony_ci#define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */
478c2ecf20Sopenharmony_ci#define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */
488c2ecf20Sopenharmony_ci#define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */
498c2ecf20Sopenharmony_ci#define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * R16426-8 (0x402A-C) - Touch Data X/Y/X
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci#define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */
558c2ecf20Sopenharmony_ci#define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */
568c2ecf20Sopenharmony_ci#define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */
578c2ecf20Sopenharmony_ci#define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct wm831x_ts {
608c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
618c2ecf20Sopenharmony_ci	struct wm831x *wm831x;
628c2ecf20Sopenharmony_ci	unsigned int data_irq;
638c2ecf20Sopenharmony_ci	unsigned int pd_irq;
648c2ecf20Sopenharmony_ci	bool pressure;
658c2ecf20Sopenharmony_ci	bool pen_down;
668c2ecf20Sopenharmony_ci	struct work_struct pd_data_work;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void wm831x_pd_data_work(struct work_struct *work)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts =
728c2ecf20Sopenharmony_ci		container_of(work, struct wm831x_ts, pd_data_work);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (wm831x_ts->pen_down) {
758c2ecf20Sopenharmony_ci		enable_irq(wm831x_ts->data_irq);
768c2ecf20Sopenharmony_ci		dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
778c2ecf20Sopenharmony_ci	} else {
788c2ecf20Sopenharmony_ci		enable_irq(wm831x_ts->pd_irq);
798c2ecf20Sopenharmony_ci		dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts = irq_data;
868c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_ts->wm831x;
878c2ecf20Sopenharmony_ci	static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
888c2ecf20Sopenharmony_ci	u16 data[3];
898c2ecf20Sopenharmony_ci	int count;
908c2ecf20Sopenharmony_ci	int i, ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (wm831x_ts->pressure)
938c2ecf20Sopenharmony_ci		count = 3;
948c2ecf20Sopenharmony_ci	else
958c2ecf20Sopenharmony_ci		count = 2;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
988c2ecf20Sopenharmony_ci			WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
1018c2ecf20Sopenharmony_ci			       data);
1028c2ecf20Sopenharmony_ci	if (ret != 0) {
1038c2ecf20Sopenharmony_ci		dev_err(wm831x->dev, "Failed to read touch data: %d\n",
1048c2ecf20Sopenharmony_ci			ret);
1058c2ecf20Sopenharmony_ci		return IRQ_NONE;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * We get a pen down reading on every reading, report pen up if any
1108c2ecf20Sopenharmony_ci	 * individual reading does so.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	wm831x_ts->pen_down = true;
1138c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1148c2ecf20Sopenharmony_ci		if (!(data[i] & WM831X_TCH_PD)) {
1158c2ecf20Sopenharmony_ci			wm831x_ts->pen_down = false;
1168c2ecf20Sopenharmony_ci			continue;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci		input_report_abs(wm831x_ts->input_dev, data_types[i],
1198c2ecf20Sopenharmony_ci				 data[i] & WM831X_TCH_DATA_MASK);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!wm831x_ts->pen_down) {
1238c2ecf20Sopenharmony_ci		/* Switch from data to pen down */
1248c2ecf20Sopenharmony_ci		dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		disable_irq_nosync(wm831x_ts->data_irq);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		/* Don't need data any more */
1298c2ecf20Sopenharmony_ci		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
1308c2ecf20Sopenharmony_ci				WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
1318c2ecf20Sopenharmony_ci				WM831X_TCH_Z_ENA, 0);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		/* Flush any final samples that arrived while reading */
1348c2ecf20Sopenharmony_ci		wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
1358c2ecf20Sopenharmony_ci				WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		if (wm831x_ts->pressure)
1408c2ecf20Sopenharmony_ci			input_report_abs(wm831x_ts->input_dev,
1418c2ecf20Sopenharmony_ci					 ABS_PRESSURE, 0);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		schedule_work(&wm831x_ts->pd_data_work);
1468c2ecf20Sopenharmony_ci	} else {
1478c2ecf20Sopenharmony_ci		input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	input_sync(wm831x_ts->input_dev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts = irq_data;
1588c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_ts->wm831x;
1598c2ecf20Sopenharmony_ci	int ena = 0;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (wm831x_ts->pen_down)
1628c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	disable_irq_nosync(wm831x_ts->pd_irq);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Start collecting data */
1678c2ecf20Sopenharmony_ci	if (wm831x_ts->pressure)
1688c2ecf20Sopenharmony_ci		ena |= WM831X_TCH_Z_ENA;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
1718c2ecf20Sopenharmony_ci			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
1728c2ecf20Sopenharmony_ci			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
1758c2ecf20Sopenharmony_ci			WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	wm831x_ts->pen_down = true;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Switch from pen down to data */
1808c2ecf20Sopenharmony_ci	dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
1818c2ecf20Sopenharmony_ci	schedule_work(&wm831x_ts->pd_data_work);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int wm831x_ts_input_open(struct input_dev *idev)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
1898c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_ts->wm831x;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
1928c2ecf20Sopenharmony_ci			WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
1938c2ecf20Sopenharmony_ci			WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
1948c2ecf20Sopenharmony_ci			WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
1978c2ecf20Sopenharmony_ci			WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void wm831x_ts_input_close(struct input_dev *idev)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
2058c2ecf20Sopenharmony_ci	struct wm831x *wm831x = wm831x_ts->wm831x;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Shut the controller down, disabling all other functionality too */
2088c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
2098c2ecf20Sopenharmony_ci			WM831X_TCH_ENA | WM831X_TCH_X_ENA |
2108c2ecf20Sopenharmony_ci			WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Make sure any pending IRQs are done, the above will prevent
2138c2ecf20Sopenharmony_ci	 * new ones firing.
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	synchronize_irq(wm831x_ts->data_irq);
2168c2ecf20Sopenharmony_ci	synchronize_irq(wm831x_ts->pd_irq);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Make sure the IRQ completion work is quiesced */
2198c2ecf20Sopenharmony_ci	flush_work(&wm831x_ts->pd_data_work);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* If we ended up with the pen down then make sure we revert back
2228c2ecf20Sopenharmony_ci	 * to pen detection state for the next time we start up.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	if (wm831x_ts->pen_down) {
2258c2ecf20Sopenharmony_ci		disable_irq(wm831x_ts->data_irq);
2268c2ecf20Sopenharmony_ci		enable_irq(wm831x_ts->pd_irq);
2278c2ecf20Sopenharmony_ci		wm831x_ts->pen_down = false;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int wm831x_ts_probe(struct platform_device *pdev)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts;
2348c2ecf20Sopenharmony_ci	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
2358c2ecf20Sopenharmony_ci	struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
2368c2ecf20Sopenharmony_ci	struct wm831x_touch_pdata *pdata = NULL;
2378c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
2388c2ecf20Sopenharmony_ci	int error, irqf;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (core_pdata)
2418c2ecf20Sopenharmony_ci		pdata = core_pdata->touch;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
2448c2ecf20Sopenharmony_ci				 GFP_KERNEL);
2458c2ecf20Sopenharmony_ci	input_dev = devm_input_allocate_device(&pdev->dev);
2468c2ecf20Sopenharmony_ci	if (!wm831x_ts || !input_dev) {
2478c2ecf20Sopenharmony_ci		error = -ENOMEM;
2488c2ecf20Sopenharmony_ci		goto err_alloc;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	wm831x_ts->wm831x = wm831x;
2528c2ecf20Sopenharmony_ci	wm831x_ts->input_dev = input_dev;
2538c2ecf20Sopenharmony_ci	INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/*
2568c2ecf20Sopenharmony_ci	 * If we have a direct IRQ use it, otherwise use the interrupt
2578c2ecf20Sopenharmony_ci	 * from the WM831x IRQ controller.
2588c2ecf20Sopenharmony_ci	 */
2598c2ecf20Sopenharmony_ci	wm831x_ts->data_irq = wm831x_irq(wm831x,
2608c2ecf20Sopenharmony_ci					 platform_get_irq_byname(pdev,
2618c2ecf20Sopenharmony_ci								 "TCHDATA"));
2628c2ecf20Sopenharmony_ci	if (pdata && pdata->data_irq)
2638c2ecf20Sopenharmony_ci		wm831x_ts->data_irq = pdata->data_irq;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	wm831x_ts->pd_irq = wm831x_irq(wm831x,
2668c2ecf20Sopenharmony_ci				       platform_get_irq_byname(pdev, "TCHPD"));
2678c2ecf20Sopenharmony_ci	if (pdata && pdata->pd_irq)
2688c2ecf20Sopenharmony_ci		wm831x_ts->pd_irq = pdata->pd_irq;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (pdata)
2718c2ecf20Sopenharmony_ci		wm831x_ts->pressure = pdata->pressure;
2728c2ecf20Sopenharmony_ci	else
2738c2ecf20Sopenharmony_ci		wm831x_ts->pressure = true;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Five wire touchscreens can't report pressure */
2768c2ecf20Sopenharmony_ci	if (pdata && pdata->fivewire) {
2778c2ecf20Sopenharmony_ci		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
2788c2ecf20Sopenharmony_ci				WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		/* Pressure measurements are not possible for five wire mode */
2818c2ecf20Sopenharmony_ci		WARN_ON(pdata->pressure && pdata->fivewire);
2828c2ecf20Sopenharmony_ci		wm831x_ts->pressure = false;
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
2858c2ecf20Sopenharmony_ci				WM831X_TCH_5WIRE, 0);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (pdata) {
2898c2ecf20Sopenharmony_ci		switch (pdata->isel) {
2908c2ecf20Sopenharmony_ci		default:
2918c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
2928c2ecf20Sopenharmony_ci				pdata->isel);
2938c2ecf20Sopenharmony_ci			fallthrough;
2948c2ecf20Sopenharmony_ci		case 200:
2958c2ecf20Sopenharmony_ci		case 0:
2968c2ecf20Sopenharmony_ci			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
2978c2ecf20Sopenharmony_ci					WM831X_TCH_ISEL, 0);
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci		case 400:
3008c2ecf20Sopenharmony_ci			wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
3018c2ecf20Sopenharmony_ci					WM831X_TCH_ISEL, WM831X_TCH_ISEL);
3028c2ecf20Sopenharmony_ci			break;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
3078c2ecf20Sopenharmony_ci			WM831X_TCH_PDONLY, 0);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Default to 96 samples/sec */
3108c2ecf20Sopenharmony_ci	wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
3118c2ecf20Sopenharmony_ci			WM831X_TCH_RATE_MASK, 6);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (pdata && pdata->data_irqf)
3148c2ecf20Sopenharmony_ci		irqf = pdata->data_irqf;
3158c2ecf20Sopenharmony_ci	else
3168c2ecf20Sopenharmony_ci		irqf = IRQF_TRIGGER_HIGH;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	error = request_threaded_irq(wm831x_ts->data_irq,
3198c2ecf20Sopenharmony_ci				     NULL, wm831x_ts_data_irq,
3208c2ecf20Sopenharmony_ci				     irqf | IRQF_ONESHOT,
3218c2ecf20Sopenharmony_ci				     "Touchscreen data", wm831x_ts);
3228c2ecf20Sopenharmony_ci	if (error) {
3238c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
3248c2ecf20Sopenharmony_ci			wm831x_ts->data_irq, error);
3258c2ecf20Sopenharmony_ci		goto err_alloc;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	disable_irq(wm831x_ts->data_irq);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (pdata && pdata->pd_irqf)
3308c2ecf20Sopenharmony_ci		irqf = pdata->pd_irqf;
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		irqf = IRQF_TRIGGER_HIGH;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	error = request_threaded_irq(wm831x_ts->pd_irq,
3358c2ecf20Sopenharmony_ci				     NULL, wm831x_ts_pen_down_irq,
3368c2ecf20Sopenharmony_ci				     irqf | IRQF_ONESHOT,
3378c2ecf20Sopenharmony_ci				     "Touchscreen pen down", wm831x_ts);
3388c2ecf20Sopenharmony_ci	if (error) {
3398c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
3408c2ecf20Sopenharmony_ci			wm831x_ts->pd_irq, error);
3418c2ecf20Sopenharmony_ci		goto err_data_irq;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* set up touch configuration */
3458c2ecf20Sopenharmony_ci	input_dev->name = "WM831x touchscreen";
3468c2ecf20Sopenharmony_ci	input_dev->phys = "wm831x";
3478c2ecf20Sopenharmony_ci	input_dev->open = wm831x_ts_input_open;
3488c2ecf20Sopenharmony_ci	input_dev->close = wm831x_ts_input_close;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	__set_bit(EV_ABS, input_dev->evbit);
3518c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input_dev->evbit);
3528c2ecf20Sopenharmony_ci	__set_bit(BTN_TOUCH, input_dev->keybit);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
3558c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
3568c2ecf20Sopenharmony_ci	if (wm831x_ts->pressure)
3578c2ecf20Sopenharmony_ci		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, wm831x_ts);
3608c2ecf20Sopenharmony_ci	input_dev->dev.parent = &pdev->dev;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	error = input_register_device(input_dev);
3638c2ecf20Sopenharmony_ci	if (error)
3648c2ecf20Sopenharmony_ci		goto err_pd_irq;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, wm831x_ts);
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cierr_pd_irq:
3708c2ecf20Sopenharmony_ci	free_irq(wm831x_ts->pd_irq, wm831x_ts);
3718c2ecf20Sopenharmony_cierr_data_irq:
3728c2ecf20Sopenharmony_ci	free_irq(wm831x_ts->data_irq, wm831x_ts);
3738c2ecf20Sopenharmony_cierr_alloc:
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return error;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int wm831x_ts_remove(struct platform_device *pdev)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	free_irq(wm831x_ts->pd_irq, wm831x_ts);
3838c2ecf20Sopenharmony_ci	free_irq(wm831x_ts->data_irq, wm831x_ts);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic struct platform_driver wm831x_ts_driver = {
3898c2ecf20Sopenharmony_ci	.driver = {
3908c2ecf20Sopenharmony_ci		.name = "wm831x-touch",
3918c2ecf20Sopenharmony_ci	},
3928c2ecf20Sopenharmony_ci	.probe = wm831x_ts_probe,
3938c2ecf20Sopenharmony_ci	.remove = wm831x_ts_remove,
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_cimodule_platform_driver(wm831x_ts_driver);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci/* Module information */
3988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
3998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
4008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:wm831x-touch");
402