diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-03 05:56:59 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-03 05:56:59 +0300 |
commit | 645ff1e8e704c4f33ab1fcd3c87f95cb9b6d7144 (patch) | |
tree | 4e4e8940747da96d56ac1e56db1c55fd22a29677 /drivers | |
parent | d548e65904ae43b0637d200a2441fc94e0589c30 (diff) | |
parent | 592b15ba7260f8dec8bfc71ef0ea1934801a35aa (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
"A tiny pull request this merge window unfortunately, should get more
material in for the next release:
- new driver for Raspberry Pi's touchscreen (firmware interface)
- miscellaneous input driver fixes"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
Input: elan_i2c - add ACPI ID for touchpad in ASUS Aspire F5-573G
Input: atmel_mxt_ts - don't try to free unallocated kernel memory
Input: drv2667 - fix indentation issues
Input: touchscreen - fix coding style issue
Input: add official Raspberry Pi's touchscreen driver
Input: nomadik-ske-keypad - fix a loop timeout test
Input: rotary-encoder - don't log EPROBE_DEFER to kernel log
Input: olpc_apsp - remove set but not used variable 'np'
Input: olpc_apsp - enable the SP clock
Input: olpc_apsp - check FIFO status on open(), not probe()
Input: olpc_apsp - drop CONFIG_OLPC dependency
clk: mmp2: add SP clock
dt-bindings: marvell,mmp2: Add clock id for the SP clock
Input: ad7879 - drop platform data support
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/mmp/clk-of-mmp2.c | 4 | ||||
-rw-r--r-- | drivers/input/keyboard/nomadik-ske-keypad.c | 2 | ||||
-rw-r--r-- | drivers/input/misc/drv2667.c | 6 | ||||
-rw-r--r-- | drivers/input/misc/rotary_encoder.c | 6 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_core.c | 1 | ||||
-rw-r--r-- | drivers/input/serio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/serio/olpc_apsp.c | 28 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/ad7879.c | 109 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/ektf2127.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/gunze.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/inexio.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/mtouch.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/raspberrypi-ts.c | 227 |
16 files changed, 316 insertions, 99 deletions
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index d083b860f083..61fefc046ec5 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -53,6 +53,7 @@ #define APMU_DISP1 0x110 #define APMU_CCIC0 0x50 #define APMU_CCIC1 0xf4 +#define APMU_SP 0x68 #define MPMU_UART_PLL 0x14 struct mmp2_clk_unit { @@ -209,6 +210,8 @@ static struct mmp_clk_mix_config ccic1_mix_config = { .reg_info = DEFINE_MIX_REG_INFO(4, 16, 2, 6, 32), }; +static DEFINE_SPINLOCK(sp_lock); + static struct mmp_param_mux_clk apmu_mux_clks[] = { {MMP2_CLK_DISP0_MUX, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 2, 0, &disp0_lock}, {MMP2_CLK_DISP1_MUX, "disp1_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP1, 6, 2, 0, &disp1_lock}, @@ -239,6 +242,7 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = { {MMP2_CLK_CCIC1, "ccic1_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x1b, 0x1b, 0x0, 0, &ccic1_lock}, {MMP2_CLK_CCIC1_PHY, "ccic1_phy_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x24, 0x24, 0x0, 0, &ccic1_lock}, {MMP2_CLK_CCIC1_SPHY, "ccic1_sphy_clk", "ccic1_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x300, 0x300, 0x0, 0, &ccic1_lock}, + {MMP2_CLK_SP, "sp_clk", NULL, CLK_SET_RATE_PARENT, APMU_SP, 0x1b, 0x1b, 0x0, 0, &sp_lock}, }; static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 8567ee47761e..ae3b04557074 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -100,7 +100,7 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad) while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) cpu_relax(); - if (!timeout) + if (timeout == -1) return -EINVAL; /* diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index 2849bb6906a8..6091f0490e8f 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -177,9 +177,9 @@ static int drv2667_set_waveform_freq(struct drv2667_data *haptics) error = regmap_write(haptics->regmap, DRV2667_PAGE, read_buf); if (error) { dev_err(&haptics->client->dev, - "Failed to set the page: %d\n", error); - return -EIO; - } + "Failed to set the page: %d\n", error); + return -EIO; + } } return error; diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 30ec77ad32c6..d748897bf5e9 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -240,8 +240,10 @@ static int rotary_encoder_probe(struct platform_device *pdev) encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); if (IS_ERR(encoder->gpios)) { - dev_err(dev, "unable to get gpios\n"); - return PTR_ERR(encoder->gpios); + err = PTR_ERR(encoder->gpios); + if (err != -EPROBE_DEFER) + dev_err(dev, "unable to get gpios: %d\n", err); + return err; } if (encoder->gpios->ndescs < 2) { dev_err(dev, "not enough gpios found\n"); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index a94b6494e71a..f322a1768fbb 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1336,6 +1336,7 @@ MODULE_DEVICE_TABLE(i2c, elan_id); static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, + { "ELAN0501", 0 }, { "ELAN0600", 0 }, { "ELAN0602", 0 }, { "ELAN0605", 0 }, diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index d90d9f1098ff..c9c7224d5ae0 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -254,7 +254,6 @@ config SERIO_APBPS2 config SERIO_OLPC_APSP tristate "OLPC AP-SP input support" - depends on OLPC || COMPILE_TEST help Say Y here if you want support for the keyboard and touchpad included in the OLPC XO-1.75 and XO-4 laptops. diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c index 8e9a4209fcad..b36084710f69 100644 --- a/drivers/input/serio/olpc_apsp.c +++ b/drivers/input/serio/olpc_apsp.c @@ -23,6 +23,7 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/clk.h> /* * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller. @@ -74,6 +75,7 @@ struct olpc_apsp { struct serio *kbio; struct serio *padio; void __iomem *base; + struct clk *clk; int open_count; int irq; }; @@ -145,8 +147,21 @@ static int olpc_apsp_open(struct serio *port) { struct olpc_apsp *priv = port->port_data; unsigned int tmp; + unsigned long l; + int error; if (priv->open_count++ == 0) { + error = clk_prepare_enable(priv->clk); + if (error) + return error; + + l = readl(priv->base + COMMAND_FIFO_STATUS); + if (!(l & CMD_STS_MASK)) { + dev_err(priv->dev, "SP cannot accept commands.\n"); + clk_disable_unprepare(priv->clk); + return -EIO; + } + /* Enable interrupt 0 by clearing its bit */ tmp = readl(priv->base + PJ_INTERRUPT_MASK); writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK); @@ -164,6 +179,8 @@ static void olpc_apsp_close(struct serio *port) /* Disable interrupt 0 */ tmp = readl(priv->base + PJ_INTERRUPT_MASK); writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK); + + clk_disable_unprepare(priv->clk); } } @@ -172,15 +189,12 @@ static int olpc_apsp_probe(struct platform_device *pdev) struct serio *kb_serio, *pad_serio; struct olpc_apsp *priv; struct resource *res; - struct device_node *np; - unsigned long l; int error; priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL); if (!priv) return -ENOMEM; - np = pdev->dev.of_node; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) { @@ -192,11 +206,9 @@ static int olpc_apsp_probe(struct platform_device *pdev) if (priv->irq < 0) return priv->irq; - l = readl(priv->base + COMMAND_FIFO_STATUS); - if (!(l & CMD_STS_MASK)) { - dev_err(&pdev->dev, "SP cannot accept commands.\n"); - return -EIO; - } + priv->clk = devm_clk_get(&pdev->dev, "sp"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); /* KEYBOARD */ kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2a80675cfd94..af6027cc7bbf 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -696,6 +696,18 @@ config TOUCHSCREEN_EDT_FT5X06 To compile this driver as a module, choose M here: the module will be called edt-ft5x06. +config TOUCHSCREEN_RASPBERRYPI_FW + tristate "Raspberry Pi's firmware base touch screen support" + depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST + help + Say Y here if you have the official Raspberry Pi 7 inch screen on + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called raspberrypi-ts. + config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on (SH_MIGOR || COMPILE_TEST) && I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5911a4190cd2..fcc7605fba8d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -109,3 +109,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223) += zet6223.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o +obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 6bad23ee47a1..6fa714c587b4 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -29,10 +29,9 @@ #include <linux/property.h> #include <linux/regmap.h> #include <linux/slab.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/input/touchscreen.h> -#include <linux/platform_data/ad7879.h> #include <linux/module.h> #include "ad7879.h" @@ -290,7 +289,7 @@ static int ad7879_open(struct input_dev *input) return 0; } -static void ad7879_close(struct input_dev* input) +static void ad7879_close(struct input_dev *input) { struct ad7879 *ts = input_get_drvdata(input); @@ -452,47 +451,36 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip, mutex_unlock(&ts->mutex); } -static int ad7879_gpio_add(struct ad7879 *ts, - const struct ad7879_platform_data *pdata) +static int ad7879_gpio_add(struct ad7879 *ts) { - bool gpio_export; - int gpio_base; int ret = 0; - if (pdata) { - gpio_export = pdata->gpio_export; - gpio_base = pdata->gpio_base; - } else { - gpio_export = device_property_read_bool(ts->dev, - "gpio-controller"); - gpio_base = -1; - } - mutex_init(&ts->mutex); - if (gpio_export) { - ts->gc.direction_input = ad7879_gpio_direction_input; - ts->gc.direction_output = ad7879_gpio_direction_output; - ts->gc.get = ad7879_gpio_get_value; - ts->gc.set = ad7879_gpio_set_value; - ts->gc.can_sleep = 1; - ts->gc.base = gpio_base; - ts->gc.ngpio = 1; - ts->gc.label = "AD7879-GPIO"; - ts->gc.owner = THIS_MODULE; - ts->gc.parent = ts->dev; - - ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts); - if (ret) - dev_err(ts->dev, "failed to register gpio %d\n", - ts->gc.base); - } + /* Do not create a chip unless flagged for it */ + if (!device_property_read_bool(ts->dev, "gpio-controller")) + return 0; + + ts->gc.direction_input = ad7879_gpio_direction_input; + ts->gc.direction_output = ad7879_gpio_direction_output; + ts->gc.get = ad7879_gpio_get_value; + ts->gc.set = ad7879_gpio_set_value; + ts->gc.can_sleep = 1; + ts->gc.base = -1; + ts->gc.ngpio = 1; + ts->gc.label = "AD7879-GPIO"; + ts->gc.owner = THIS_MODULE; + ts->gc.parent = ts->dev; + + ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts); + if (ret) + dev_err(ts->dev, "failed to register gpio %d\n", + ts->gc.base); return ret; } #else -static int ad7879_gpio_add(struct ad7879 *ts, - const struct ad7879_platform_data *pdata) +static int ad7879_gpio_add(struct ad7879 *ts) { return 0; } @@ -527,7 +515,6 @@ static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) int ad7879_probe(struct device *dev, struct regmap *regmap, int irq, u16 bustype, u8 devid) { - struct ad7879_platform_data *pdata = dev_get_platdata(dev); struct ad7879 *ts; struct input_dev *input_dev; int err; @@ -542,22 +529,9 @@ int ad7879_probe(struct device *dev, struct regmap *regmap, if (!ts) return -ENOMEM; - if (pdata) { - /* Platform data use swapped axis (backward compatibility) */ - ts->swap_xy = !pdata->swap_xy; - - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - - ts->first_conversion_delay = pdata->first_conversion_delay; - ts->acquisition_time = pdata->acquisition_time; - ts->averaging = pdata->averaging; - ts->pen_down_acc_interval = pdata->pen_down_acc_interval; - ts->median = pdata->median; - } else { - err = ad7879_parse_dt(dev, ts); - if (err) - return err; - } + err = ad7879_parse_dt(dev, ts); + if (err) + return err; input_dev = devm_input_allocate_device(dev); if (!input_dev) { @@ -585,28 +559,13 @@ int ad7879_probe(struct device *dev, struct regmap *regmap, input_set_capability(input_dev, EV_KEY, BTN_TOUCH); - if (pdata) { - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, - pdata->pressure_max ? : ~0, - 0, 0); - } else { - input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - input_set_capability(input_dev, EV_ABS, ABS_PRESSURE); - touchscreen_parse_properties(input_dev, false, NULL); - if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { - dev_err(dev, "Touchscreen pressure is not specified\n"); - return -EINVAL; - } + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_capability(input_dev, EV_ABS, ABS_PRESSURE); + touchscreen_parse_properties(input_dev, false, NULL); + if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { + dev_err(dev, "Touchscreen pressure is not specified\n"); + return -EINVAL; } err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); @@ -655,7 +614,7 @@ int ad7879_probe(struct device *dev, struct regmap *regmap, if (err) return err; - err = ad7879_gpio_add(ts, pdata); + err = ad7879_gpio_add(ts); if (err) return err; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d3aacd534e9c..5c63d25ce84e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1585,10 +1585,10 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) /* T7 config may have changed */ mxt_init_t7_power_cfg(data); -release_raw: - kfree(cfg.raw); release_mem: kfree(cfg.mem); +release_raw: + kfree(cfg.raw); return ret; } diff --git a/drivers/input/touchscreen/ektf2127.c b/drivers/input/touchscreen/ektf2127.c index 0ed34ff787ce..28fa1b36f7a5 100644 --- a/drivers/input/touchscreen/ektf2127.c +++ b/drivers/input/touchscreen/ektf2127.c @@ -51,7 +51,7 @@ struct ektf2127_ts { struct touchscreen_properties prop; }; -static void ektf2127_parse_coordinates(const u8* buf, unsigned int touch_count, +static void ektf2127_parse_coordinates(const u8 *buf, unsigned int touch_count, struct input_mt_pos *touches) { int index = 0; diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index 054c2537b392..56cf1a1ea3ea 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -53,7 +53,7 @@ struct gunze { char phys[32]; }; -static void gunze_process_packet(struct gunze* gunze) +static void gunze_process_packet(struct gunze *gunze) { struct input_dev *dev = gunze->dev; @@ -72,7 +72,7 @@ static void gunze_process_packet(struct gunze* gunze) static irqreturn_t gunze_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { - struct gunze* gunze = serio_get_drvdata(serio); + struct gunze *gunze = serio_get_drvdata(serio); if (data == '\r') { gunze_process_packet(gunze); diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c index b9bc56233ccc..13bd0bf580a7 100644 --- a/drivers/input/touchscreen/inexio.c +++ b/drivers/input/touchscreen/inexio.c @@ -79,7 +79,7 @@ static void inexio_process_data(struct inexio *pinexio) static irqreturn_t inexio_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { - struct inexio* pinexio = serio_get_drvdata(serio); + struct inexio *pinexio = serio_get_drvdata(serio); pinexio->data[pinexio->idx] = data; @@ -97,7 +97,7 @@ static irqreturn_t inexio_interrupt(struct serio *serio, static void inexio_disconnect(struct serio *serio) { - struct inexio* pinexio = serio_get_drvdata(serio); + struct inexio *pinexio = serio_get_drvdata(serio); input_get_device(pinexio->dev); input_unregister_device(pinexio->dev); diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c index a3707fad4d1c..8278a9058640 100644 --- a/drivers/input/touchscreen/mtouch.c +++ b/drivers/input/touchscreen/mtouch.c @@ -90,7 +90,7 @@ static void mtouch_process_response(struct mtouch *mtouch) static irqreturn_t mtouch_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { - struct mtouch* mtouch = serio_get_drvdata(serio); + struct mtouch *mtouch = serio_get_drvdata(serio); mtouch->data[mtouch->idx] = data; @@ -110,7 +110,7 @@ static irqreturn_t mtouch_interrupt(struct serio *serio, static void mtouch_disconnect(struct serio *serio) { - struct mtouch* mtouch = serio_get_drvdata(serio); + struct mtouch *mtouch = serio_get_drvdata(serio); input_get_device(mtouch->dev); input_unregister_device(mtouch->dev); diff --git a/drivers/input/touchscreen/raspberrypi-ts.c b/drivers/input/touchscreen/raspberrypi-ts.c new file mode 100644 index 000000000000..f456c1125bd6 --- /dev/null +++ b/drivers/input/touchscreen/raspberrypi-ts.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Raspberry Pi firmware based touchscreen driver + * + * Copyright (C) 2015, 2017 Raspberry Pi + * Copyright (C) 2018 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input-polldev.h> +#include <linux/input/touchscreen.h> +#include <soc/bcm2835/raspberrypi-firmware.h> + +#define RPI_TS_DEFAULT_WIDTH 800 +#define RPI_TS_DEFAULT_HEIGHT 480 + +#define RPI_TS_MAX_SUPPORTED_POINTS 10 + +#define RPI_TS_FTS_TOUCH_DOWN 0 +#define RPI_TS_FTS_TOUCH_CONTACT 2 + +#define RPI_TS_POLL_INTERVAL 17 /* 60fps */ + +#define RPI_TS_NPOINTS_REG_INVALIDATE 99 + +struct rpi_ts { + struct platform_device *pdev; + struct input_polled_dev *poll_dev; + struct touchscreen_properties prop; + + void __iomem *fw_regs_va; + dma_addr_t fw_regs_phys; + + int known_ids; +}; + +struct rpi_ts_regs { + u8 device_mode; + u8 gesture_id; + u8 num_points; + struct rpi_ts_touch { + u8 xh; + u8 xl; + u8 yh; + u8 yl; + u8 pressure; /* Not supported */ + u8 area; /* Not supported */ + } point[RPI_TS_MAX_SUPPORTED_POINTS]; +}; + +static void rpi_ts_poll(struct input_polled_dev *dev) +{ + struct input_dev *input = dev->input; + struct rpi_ts *ts = dev->private; + struct rpi_ts_regs regs; + int modified_ids = 0; + long released_ids; + int event_type; + int touchid; + int x, y; + int i; + + memcpy_fromio(®s, ts->fw_regs_va, sizeof(regs)); + /* + * We poll the memory based register copy of the touchscreen chip using + * the number of points register to know whether the copy has been + * updated (we write 99 to the memory copy, the GPU will write between + * 0 - 10 points) + */ + iowrite8(RPI_TS_NPOINTS_REG_INVALIDATE, + ts->fw_regs_va + offsetof(struct rpi_ts_regs, num_points)); + + if (regs.num_points == RPI_TS_NPOINTS_REG_INVALIDATE || + (regs.num_points == 0 && ts->known_ids == 0)) + return; + + for (i = 0; i < regs.num_points; i++) { + x = (((int)regs.point[i].xh & 0xf) << 8) + regs.point[i].xl; + y = (((int)regs.point[i].yh & 0xf) << 8) + regs.point[i].yl; + touchid = (regs.point[i].yh >> 4) & 0xf; + event_type = (regs.point[i].xh >> 6) & 0x03; + + modified_ids |= BIT(touchid); + + if (event_type == RPI_TS_FTS_TOUCH_DOWN || + event_type == RPI_TS_FTS_TOUCH_CONTACT) { + input_mt_slot(input, touchid); + input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); + touchscreen_report_pos(input, &ts->prop, x, y, true); + } + } + + released_ids = ts->known_ids & ~modified_ids; + for_each_set_bit(i, &released_ids, RPI_TS_MAX_SUPPORTED_POINTS) { + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, 0); + modified_ids &= ~(BIT(i)); + } + ts->known_ids = modified_ids; + + input_mt_sync_frame(input); + input_sync(input); +} + +static void rpi_ts_dma_cleanup(void *data) +{ + struct rpi_ts *ts = data; + struct device *dev = &ts->pdev->dev; + + dma_free_coherent(dev, PAGE_SIZE, ts->fw_regs_va, ts->fw_regs_phys); +} + +static int rpi_ts_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct input_polled_dev *poll_dev; + struct device_node *fw_node; + struct rpi_firmware *fw; + struct input_dev *input; + struct rpi_ts *ts; + u32 touchbuf; + int error; + + fw_node = of_get_parent(np); + if (!fw_node) { + dev_err(dev, "Missing firmware node\n"); + return -ENOENT; + } + + fw = rpi_firmware_get(fw_node); + of_node_put(fw_node); + if (!fw) + return -EPROBE_DEFER; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + ts->pdev = pdev; + + ts->fw_regs_va = dma_zalloc_coherent(dev, PAGE_SIZE, &ts->fw_regs_phys, + GFP_KERNEL); + if (!ts->fw_regs_va) { + dev_err(dev, "failed to dma_alloc_coherent\n"); + return -ENOMEM; + } + + error = devm_add_action_or_reset(dev, rpi_ts_dma_cleanup, ts); + if (error) { + dev_err(dev, "failed to devm_add_action_or_reset, %d\n", error); + return error; + } + + + touchbuf = (u32)ts->fw_regs_phys; + error = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF, + &touchbuf, sizeof(touchbuf)); + + if (error || touchbuf != 0) { + dev_warn(dev, "Failed to set touchbuf, %d\n", error); + return error; + } + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) { + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + ts->poll_dev = poll_dev; + input = poll_dev->input; + + input->name = "raspberrypi-ts"; + input->id.bustype = BUS_HOST; + poll_dev->poll_interval = RPI_TS_POLL_INTERVAL; + poll_dev->poll = rpi_ts_poll; + poll_dev->private = ts; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + RPI_TS_DEFAULT_WIDTH, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + RPI_TS_DEFAULT_HEIGHT, 0, 0); + touchscreen_parse_properties(input, true, &ts->prop); + + error = input_mt_init_slots(input, RPI_TS_MAX_SUPPORTED_POINTS, + INPUT_MT_DIRECT); + if (error) { + dev_err(dev, "could not init mt slots, %d\n", error); + return error; + } + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "could not register input device, %d\n", error); + return error; + } + + return 0; +} + +static const struct of_device_id rpi_ts_match[] = { + { .compatible = "raspberrypi,firmware-ts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rpi_ts_match); + +static struct platform_driver rpi_ts_driver = { + .driver = { + .name = "raspberrypi-ts", + .of_match_table = rpi_ts_match, + }, + .probe = rpi_ts_probe, +}; +module_platform_driver(rpi_ts_driver); + +MODULE_AUTHOR("Gordon Hollingworth"); +MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); +MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver"); +MODULE_LICENSE("GPL v2"); |