From d9202af2ffa083f096684fd5f4b530aebbc07439 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Sat, 16 Jan 2016 20:22:07 -0800 Subject: Input: rotary_encoder - convert to devm-* api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use managed resource API for simplifying error paths. Signed-off-by: Timo Teräs Signed-off-by: Dmitry Torokhov --- drivers/input/misc/rotary_encoder.c | 86 +++++++++++-------------------------- 1 file changed, 25 insertions(+), 61 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 8aee71986430..ebbadf3f0579 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -211,8 +211,8 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic if (!of_id || !np) return NULL; - pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), - GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(struct rotary_encoder_platform_data), + GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); @@ -277,12 +277,13 @@ static int rotary_encoder_probe(struct platform_device *pdev) } } - encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); - input = input_allocate_device(); - if (!encoder || !input) { - err = -ENOMEM; - goto exit_free_mem; - } + encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); + if (!encoder) + return -ENOMEM; + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; encoder->input = input; encoder->pdata = pdata; @@ -301,16 +302,18 @@ static int rotary_encoder_probe(struct platform_device *pdev) } /* request the GPIOs */ - err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); + err = devm_gpio_request_one(dev, pdata->gpio_a, GPIOF_IN, + dev_name(dev)); if (err) { dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); - goto exit_free_mem; + return err; } - err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); + err = devm_gpio_request_one(dev, pdata->gpio_b, GPIOF_IN, + dev_name(dev)); if (err) { dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); - goto exit_free_gpio_a; + return err; } encoder->irq_a = gpio_to_irq(pdata->gpio_a); @@ -331,30 +334,29 @@ static int rotary_encoder_probe(struct platform_device *pdev) default: dev_err(dev, "'%d' is not a valid steps-per-period value\n", pdata->steps_per_period); - err = -EINVAL; - goto exit_free_gpio_b; + return -EINVAL; } - err = request_irq(encoder->irq_a, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); + err = devm_request_irq(dev, encoder->irq_a, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + DRV_NAME, encoder); if (err) { dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); - goto exit_free_gpio_b; + return err; } - err = request_irq(encoder->irq_b, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); + err = devm_request_irq(dev, encoder->irq_b, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + DRV_NAME, encoder); if (err) { dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); - goto exit_free_irq_a; + return err; } err = input_register_device(input); if (err) { dev_err(dev, "failed to register input device\n"); - goto exit_free_irq_b; + return err; } device_init_wakeup(&pdev->dev, pdata->wakeup_source); @@ -362,43 +364,6 @@ static int rotary_encoder_probe(struct platform_device *pdev) platform_set_drvdata(pdev, encoder); return 0; - -exit_free_irq_b: - free_irq(encoder->irq_b, encoder); -exit_free_irq_a: - free_irq(encoder->irq_a, encoder); -exit_free_gpio_b: - gpio_free(pdata->gpio_b); -exit_free_gpio_a: - gpio_free(pdata->gpio_a); -exit_free_mem: - input_free_device(input); - kfree(encoder); - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return err; -} - -static int rotary_encoder_remove(struct platform_device *pdev) -{ - struct rotary_encoder *encoder = platform_get_drvdata(pdev); - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - device_init_wakeup(&pdev->dev, false); - - free_irq(encoder->irq_a, encoder); - free_irq(encoder->irq_b, encoder); - gpio_free(pdata->gpio_a); - gpio_free(pdata->gpio_b); - - input_unregister_device(encoder->input); - kfree(encoder); - - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -432,7 +397,6 @@ static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, - .remove = rotary_encoder_remove, .driver = { .name = DRV_NAME, .pm = &rotary_encoder_pm_ops, -- cgit v1.2.3 From e4cf92ba44b86d287bdf1ef6a70417b98c441c7b Mon Sep 17 00:00:00 2001 From: Oreste Salerno Date: Wed, 27 Jan 2016 13:53:33 -0800 Subject: Input: cyttsp - use devres managed resource allocations Use devm_() functions for allocating memory, input device and IRQ. Signed-off-by: Oreste Salerno Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp_core.c | 68 +++++++++++++++------------------ drivers/input/touchscreen/cyttsp_core.h | 1 - drivers/input/touchscreen/cyttsp_i2c.c | 10 ----- drivers/input/touchscreen/cyttsp_spi.c | 10 ----- 4 files changed, 30 insertions(+), 59 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 5b74e8b84e79..2d699808b55a 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -528,6 +528,14 @@ static void cyttsp_close(struct input_dev *dev) cyttsp_disable(ts); } +static void cyttsp_platform_exit(void *data) +{ + struct cyttsp *ts = data; + + if (ts->pdata->exit) + ts->pdata->exit(); +} + struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size) { @@ -536,17 +544,16 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct input_dev *input_dev; int error; - if (!pdata || !pdata->name || irq <= 0) { - error = -EINVAL; - goto err_out; - } + if (!pdata || !pdata->name || irq <= 0) + return ERR_PTR(-EINVAL); - ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - error = -ENOMEM; - goto err_free_mem; - } + ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + if (!ts) + return ERR_PTR(-ENOMEM); + + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return ERR_PTR(-ENOMEM); ts->dev = dev; ts->input = input_dev; @@ -557,12 +564,18 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, init_completion(&ts->bl_ready); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + error = devm_add_action(dev, cyttsp_platform_exit, ts); + if (error) { + dev_err(dev, "failed to install exit action: %d\n", error); + return ERR_PTR(error); + } + if (pdata->init) { error = pdata->init(); if (error) { dev_err(ts->dev, "platform init failed, err: %d\n", error); - goto err_free_mem; + return ERR_PTR(error); } } @@ -586,53 +599,32 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_mt_init_slots(input_dev, CY_MAX_ID, 0); - error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - pdata->name, ts); + error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); if (error) { dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", ts->irq, error); - goto err_platform_exit; + return ERR_PTR(error); } disable_irq(ts->irq); error = cyttsp_power_on(ts); if (error) - goto err_free_irq; + return ERR_PTR(error); error = input_register_device(input_dev); if (error) { dev_err(ts->dev, "failed to register input device: %d\n", error); - goto err_free_irq; + return ERR_PTR(error); } return ts; - -err_free_irq: - free_irq(ts->irq, ts); -err_platform_exit: - if (pdata->exit) - pdata->exit(); -err_free_mem: - input_free_device(input_dev); - kfree(ts); -err_out: - return ERR_PTR(error); } EXPORT_SYMBOL_GPL(cyttsp_probe); -void cyttsp_remove(struct cyttsp *ts) -{ - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - if (ts->pdata->exit) - ts->pdata->exit(); - kfree(ts); -} -EXPORT_SYMBOL_GPL(cyttsp_remove); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index 07074110a902..c5e7afce6de8 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -143,7 +143,6 @@ struct cyttsp { struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size); -void cyttsp_remove(struct cyttsp *ts); int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, const void *values); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index eee51b3f2e3f..1edfdba96ede 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -56,15 +56,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client, return 0; } -static int cyttsp_i2c_remove(struct i2c_client *client) -{ - struct cyttsp *ts = i2c_get_clientdata(client); - - cyttsp_remove(ts); - - return 0; -} - static const struct i2c_device_id cyttsp_i2c_id[] = { { CY_I2C_NAME, 0 }, { } @@ -77,7 +68,6 @@ static struct i2c_driver cyttsp_i2c_driver = { .pm = &cyttsp_pm_ops, }, .probe = cyttsp_i2c_probe, - .remove = cyttsp_i2c_remove, .id_table = cyttsp_i2c_id, }; diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index bbeeb2488b57..3c9d18b1b6ef 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -170,22 +170,12 @@ static int cyttsp_spi_probe(struct spi_device *spi) return 0; } -static int cyttsp_spi_remove(struct spi_device *spi) -{ - struct cyttsp *ts = spi_get_drvdata(spi); - - cyttsp_remove(ts); - - return 0; -} - static struct spi_driver cyttsp_spi_driver = { .driver = { .name = CY_SPI_NAME, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_spi_probe, - .remove = cyttsp_spi_remove, }; module_spi_driver(cyttsp_spi_driver); -- cgit v1.2.3 From 69a124029b4bfcfc66b477ad2c3298f06c905ee6 Mon Sep 17 00:00:00 2001 From: Oreste Salerno Date: Wed, 27 Jan 2016 13:55:20 -0800 Subject: Input: cyttsp - check return value of input_mt_init_slots Signed-off-by: Oreste Salerno Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 2d699808b55a..b6653aa06108 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -597,7 +597,11 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, CY_MAXZ, 0, 0); - input_mt_init_slots(input_dev, CY_MAX_ID, 0); + error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); + if (error) { + dev_err(dev, "Unable to init MT slots.\n"); + return ERR_PTR(error); + } error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, -- cgit v1.2.3 From 707b61bba8b16adb6a2cbc72b71a75e09cb0f81a Mon Sep 17 00:00:00 2001 From: Oreste Salerno Date: Wed, 27 Jan 2016 13:55:43 -0800 Subject: Input: cyttsp - switch to using device properties Drop support for platform data passed via a C-structure and switch to device properties instead, which should make the driver compatible with all platforms: OF, ACPI and static boards. Static boards should use property sets to communicate device parameters to the driver. Signed-off-by: Oreste Salerno Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/cyttsp.txt | 95 ++++++++++++++ drivers/input/touchscreen/cyttsp_core.c | 136 ++++++++++++++------- drivers/input/touchscreen/cyttsp_core.h | 9 +- include/linux/input/cyttsp.h | 15 --- 4 files changed, 197 insertions(+), 58 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt b/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt new file mode 100644 index 000000000000..b75d4cfd2c36 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt @@ -0,0 +1,95 @@ +* Cypress cyttsp touchscreen controller + +Required properties: + - compatible : must be "cypress,cyttsp-i2c" or "cypress,cyttsp-spi" + - reg : Device I2C address or SPI chip select number + - spi-max-frequency : Maximum SPI clocking speed of the device (for cyttsp-spi) + - interrupt-parent : the phandle for the gpio controller + (see interrupt binding[0]). + - interrupts : (gpio) interrupt to which the chip is connected + (see interrupt binding[0]). + - bootloader-key : the 8-byte bootloader key that is required to switch + the chip from bootloader mode (default mode) to + application mode. + This property has to be specified as an array of 8 + '/bits/ 8' values. + +Optional properties: + - reset-gpios : the reset gpio the chip is connected to + (see GPIO binding[1] for more details). + - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) + - touchscreen-size-y : vertical resolution of touchscreen (in pixels) + - touchscreen-fuzz-x : horizontal noise value of the absolute input device + (in pixels) + - touchscreen-fuzz-y : vertical noise value of the absolute input device + (in pixels) + - active-distance : the distance in pixels beyond which a touch must move + before movement is detected and reported by the device. + Valid values: 0-15. + - active-interval-ms : the minimum period in ms between consecutive + scanning/processing cycles when the chip is in active mode. + Valid values: 0-255. + - lowpower-interval-ms : the minimum period in ms between consecutive + scanning/processing cycles when the chip is in low-power mode. + Valid values: 0-2550 + - touch-timeout-ms : minimum time in ms spent in the active power state while no + touches are detected before entering low-power mode. + Valid values: 0-2550 + - use-handshake : enable register-based handshake (boolean). This should + only be used if the chip is configured to use 'blocking + communication with timeout' (in this case the device + generates an interrupt at the end of every + scanning/processing cycle). + +[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +[1]: Documentation/devicetree/bindings/gpio/gpio.txt + +Example: + &i2c1 { + /* ... */ + cyttsp@a { + compatible = "cypress,cyttsp-i2c"; + reg = <0xa>; + interrupt-parent = <&gpio0>; + interrupts = <28 0>; + reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-fuzz-x = <4>; + touchscreen-fuzz-y = <7>; + + bootloader-key = /bits/ 8 <0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08>; + active-distance = <8>; + active-interval-ms = <0>; + lowpower-interval-ms = <200>; + touch-timeout-ms = <100>; + }; + + /* ... */ + }; + + &mcspi1 { + /* ... */ + cyttsp@0 { + compatible = "cypress,cyttsp-spi"; + spi-max-frequency = <6000000>; + reg = <0>; + interrupt-parent = <&gpio0>; + interrupts = <28 0>; + reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-fuzz-x = <4>; + touchscreen-fuzz-y = <7>; + + bootloader-key = /bits/ 8 <0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08>; + active-distance = <8>; + active-interval-ms = <0>; + lowpower-interval-ms = <200>; + touch-timeout-ms = <100>; + }; + + /* ... */ + }; diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index b6653aa06108..8814b6c87097 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -30,9 +30,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "cyttsp_core.h" @@ -57,6 +60,7 @@ #define CY_DELAY_DFLT 20 /* ms */ #define CY_DELAY_MAX 500 #define CY_ACT_DIST_DFLT 0xF8 +#define CY_ACT_DIST_MASK 0x0F #define CY_HNDSHK_BIT 0x80 /* device mode bits */ #define CY_OPERATE_MODE 0x00 @@ -120,7 +124,7 @@ static int ttsp_send_command(struct cyttsp *ts, u8 cmd) static int cyttsp_handshake(struct cyttsp *ts) { - if (ts->pdata->use_hndshk) + if (ts->use_hndshk) return ttsp_send_command(ts, ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); @@ -142,9 +146,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts) u8 bl_cmd[sizeof(bl_command)]; memcpy(bl_cmd, bl_command, sizeof(bl_command)); - if (ts->pdata->bl_keys) + if (ts->bl_keys) memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], - ts->pdata->bl_keys, CY_NUM_BL_KEYS); + ts->bl_keys, CY_NUM_BL_KEYS); error = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd), bl_cmd); @@ -217,14 +221,14 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) { int retval = 0; - if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || - ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || - ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + if (ts->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->lp_intrvl != CY_LP_INTRVL_DFLT) { u8 intrvl_ray[] = { - ts->pdata->act_intrvl, - ts->pdata->tch_tmout, - ts->pdata->lp_intrvl + ts->act_intrvl, + ts->tch_tmout, + ts->lp_intrvl }; /* set intrvl registers */ @@ -263,7 +267,7 @@ out: static int cyttsp_act_dist_setup(struct cyttsp *ts) { - u8 act_dist_setup = ts->pdata->act_dist; + u8 act_dist_setup = ts->act_dist; /* Init gesture; active distance setup */ return ttsp_write_block_data(ts, CY_REG_ACT_DIST, @@ -528,25 +532,82 @@ static void cyttsp_close(struct input_dev *dev) cyttsp_disable(ts); } -static void cyttsp_platform_exit(void *data) +static int cyttsp_parse_properties(struct cyttsp *ts) { - struct cyttsp *ts = data; + struct device *dev = ts->dev; + u32 dt_value; + int ret; + + ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL); + if (!ts->bl_keys) + return -ENOMEM; + + /* Set some default values */ + ts->use_hndshk = false; + ts->act_dist = CY_ACT_DIST_DFLT; + ts->act_intrvl = CY_ACT_INTRVL_DFLT; + ts->tch_tmout = CY_TCH_TMOUT_DFLT; + ts->lp_intrvl = CY_LP_INTRVL_DFLT; + + ret = device_property_read_u8_array(dev, "bootloader-key", + ts->bl_keys, CY_NUM_BL_KEYS); + if (ret) { + dev_err(dev, + "bootloader-key property could not be retrieved\n"); + return ret; + } + + ts->use_hndshk = device_property_present(dev, "use-handshake"); + + if (!device_property_read_u32(dev, "active-distance", &dt_value)) { + if (dt_value > 15) { + dev_err(dev, "active-distance (%u) must be [0-15]\n", + dt_value); + return -EINVAL; + } + ts->act_dist &= ~CY_ACT_DIST_MASK; + ts->act_dist |= dt_value; + } + + if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) { + if (dt_value > 255) { + dev_err(dev, "active-interval-ms (%u) must be [0-255]\n", + dt_value); + return -EINVAL; + } + ts->act_intrvl = dt_value; + } - if (ts->pdata->exit) - ts->pdata->exit(); + if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->lp_intrvl = dt_value / 10; + } + + if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->tch_tmout = dt_value / 10; + } + + return 0; } struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size) { - const struct cyttsp_platform_data *pdata = dev_get_platdata(dev); struct cyttsp *ts; struct input_dev *input_dev; int error; - if (!pdata || !pdata->name || irq <= 0) - return ERR_PTR(-EINVAL); - ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); if (!ts) return ERR_PTR(-ENOMEM); @@ -557,29 +618,24 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, ts->dev = dev; ts->input = input_dev; - ts->pdata = dev_get_platdata(dev); ts->bus_ops = bus_ops; ts->irq = irq; - init_completion(&ts->bl_ready); - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); - - error = devm_add_action(dev, cyttsp_platform_exit, ts); - if (error) { - dev_err(dev, "failed to install exit action: %d\n", error); + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(dev, "Failed to request reset gpio, error %d\n", error); return ERR_PTR(error); } - if (pdata->init) { - error = pdata->init(); - if (error) { - dev_err(ts->dev, "platform init failed, err: %d\n", - error); - return ERR_PTR(error); - } - } + error = cyttsp_parse_properties(ts); + if (error) + return ERR_PTR(error); + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); - input_dev->name = pdata->name; + input_dev->name = "Cypress TTSP TouchScreen"; input_dev->phys = ts->phys; input_dev->id.bustype = bus_ops->bustype; input_dev->dev.parent = ts->dev; @@ -589,13 +645,9 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_drvdata(input_dev, ts); - __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, pdata->maxx, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, pdata->maxy, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, CY_MAXZ, 0, 0); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); + touchscreen_parse_properties(input_dev, true); error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); if (error) { @@ -605,7 +657,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - pdata->name, ts); + "cyttsp", ts); if (error) { dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", ts->irq, error); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index c5e7afce6de8..7835e2bacf5a 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -129,7 +129,6 @@ struct cyttsp { int irq; struct input_dev *input; char phys[32]; - const struct cyttsp_platform_data *pdata; const struct cyttsp_bus_ops *bus_ops; struct cyttsp_bootloader_data bl_data; struct cyttsp_sysinfo_data sysinfo_data; @@ -138,6 +137,14 @@ struct cyttsp { enum cyttsp_state state; bool suspended; + struct gpio_desc *reset_gpio; + bool use_hndshk; + u8 act_dist; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; + u8 *bl_keys; + u8 xfer_buf[] ____cacheline_aligned; }; diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h index 5af7c66f1fca..586c8c95dcb0 100644 --- a/include/linux/input/cyttsp.h +++ b/include/linux/input/cyttsp.h @@ -40,19 +40,4 @@ /* Active distance in pixels for a gesture to be reported */ #define CY_ACT_DIST_DFLT 0xF8 /* pixels */ -struct cyttsp_platform_data { - u32 maxx; - u32 maxy; - bool use_hndshk; - u8 act_dist; /* Active distance */ - u8 act_intrvl; /* Active refresh interval; ms */ - u8 tch_tmout; /* Active touch timeout; ms */ - u8 lp_intrvl; /* Low power refresh interval; ms */ - int (*init)(void); - void (*exit)(void); - char *name; - s16 irq_gpio; - u8 *bl_keys; -}; - #endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From 8fb81d20019db3dea4126e3eac7ef8dc89b10b9e Mon Sep 17 00:00:00 2001 From: Oreste Salerno Date: Wed, 27 Jan 2016 13:58:38 -0800 Subject: Input: cyttsp - perform hard reset of the chip during probe Since we removed support for calling an init() callback from the platform data, introduce a function which initializes the chip by performing a hard reset, using the reset gpio defined in the device properties. Signed-off-by: Oreste Salerno Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 8814b6c87097..91cda8f8119d 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -240,6 +240,16 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) return retval; } +static void cyttsp_hard_reset(struct cyttsp *ts) +{ + if (ts->reset_gpio) { + gpiod_set_value_cansleep(ts->reset_gpio, 1); + msleep(CY_DELAY_DFLT); + gpiod_set_value_cansleep(ts->reset_gpio, 0); + msleep(CY_DELAY_DFLT); + } +} + static int cyttsp_soft_reset(struct cyttsp *ts) { unsigned long timeout; @@ -666,6 +676,8 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, disable_irq(ts->irq); + cyttsp_hard_reset(ts); + error = cyttsp_power_on(ts); if (error) return ERR_PTR(error); -- cgit v1.2.3 From 98ee377144935857d8ad5d7d70cdab1da4ede32e Mon Sep 17 00:00:00 2001 From: Chris Diamand Date: Wed, 27 Jan 2016 17:04:35 -0800 Subject: Input: byd - add BYD PS/2 touchpad driver Driver for the BYD BTP10463 touchpad, found in PC Specialist `Lafite' laptops. This patch sends the magic command sequence which causes the touchpad to stream intellimouse-style packets. Gestures are detected inside the touchpad, and exposed as special values in the Z component of each packet - absolute coordinates are not supported, even in the Windows driver. At present, this supports two-finger vertical and horizontal scrolling, and provides the framework to expose the other gestures it can recognize. Signed-off-by: Chris Diamand Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 10 ++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/byd.c | 337 +++++++++++++++++++++++++++++++++++++ drivers/input/mouse/byd.h | 18 ++ drivers/input/mouse/psmouse-base.c | 14 ++ drivers/input/mouse/psmouse.h | 1 + 6 files changed, 381 insertions(+) create mode 100644 drivers/input/mouse/byd.c create mode 100644 drivers/input/mouse/byd.h (limited to 'drivers/input') diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 17f97e5e11e7..096abb4ad5cd 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -48,6 +48,16 @@ config MOUSE_PS2_ALPS If unsure, say Y. +config MOUSE_PS2_BYD + bool "BYD PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a BYD PS/2 touchpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LOGIPS2PP bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ee6a6e9563d4..6168b134937b 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -28,6 +28,7 @@ cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c new file mode 100644 index 000000000000..9425e0f6c5ce --- /dev/null +++ b/drivers/input/mouse/byd.c @@ -0,0 +1,337 @@ +/* + * BYD TouchPad PS/2 mouse driver + * + * Copyright (C) 2015 Chris Diamand + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "psmouse.h" +#include "byd.h" + +#define PS2_Y_OVERFLOW BIT_MASK(7) +#define PS2_X_OVERFLOW BIT_MASK(6) +#define PS2_Y_SIGN BIT_MASK(5) +#define PS2_X_SIGN BIT_MASK(4) +#define PS2_ALWAYS_1 BIT_MASK(3) +#define PS2_MIDDLE BIT_MASK(2) +#define PS2_RIGHT BIT_MASK(1) +#define PS2_LEFT BIT_MASK(0) + +/* + * The touchpad reports gestures in the last byte of each packet. It can take + * any of the following values: + */ + +/* One-finger scrolling in one of the edge scroll zones. */ +#define BYD_SCROLLUP 0xCA +#define BYD_SCROLLDOWN 0x36 +#define BYD_SCROLLLEFT 0xCB +#define BYD_SCROLLRIGHT 0x35 +/* Two-finger scrolling. */ +#define BYD_2DOWN 0x2B +#define BYD_2UP 0xD5 +#define BYD_2LEFT 0xD6 +#define BYD_2RIGHT 0x2A +/* Pinching in or out. */ +#define BYD_ZOOMOUT 0xD8 +#define BYD_ZOOMIN 0x28 +/* Three-finger swipe. */ +#define BYD_3UP 0xD3 +#define BYD_3DOWN 0x2D +#define BYD_3LEFT 0xD4 +#define BYD_3RIGHT 0x2C +/* Four-finger swipe. */ +#define BYD_4UP 0xCD +#define BYD_4DOWN 0x33 + +int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 0x03; + param[1] = 0x00; + param[2] = 0x00; + param[3] = 0x00; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + if (param[1] != 0x03 || param[2] != 0x64) + return -ENODEV; + + psmouse_dbg(psmouse, "BYD touchpad detected\n"); + + if (set_properties) { + psmouse->vendor = "BYD"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + u8 *pkt = psmouse->packet; + + if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { + psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", + pkt[0]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + + /* Otherwise, a full packet has been received */ + switch (pkt[3]) { + case 0: { + /* Standard packet */ + /* Sign-extend if a sign bit is set. */ + unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; + unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; + int dx = signx | (int) pkt[1]; + int dy = signy | (int) pkt[2]; + + input_report_rel(psmouse->dev, REL_X, dx); + input_report_rel(psmouse->dev, REL_Y, -dy); + + input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT); + input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT); + input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE); + break; + } + + case BYD_SCROLLDOWN: + case BYD_2DOWN: + input_report_rel(dev, REL_WHEEL, -1); + break; + + case BYD_SCROLLUP: + case BYD_2UP: + input_report_rel(dev, REL_WHEEL, 1); + break; + + case BYD_SCROLLLEFT: + case BYD_2LEFT: + input_report_rel(dev, REL_HWHEEL, -1); + break; + + case BYD_SCROLLRIGHT: + case BYD_2RIGHT: + input_report_rel(dev, REL_HWHEEL, 1); + break; + + case BYD_ZOOMOUT: + case BYD_ZOOMIN: + case BYD_3UP: + case BYD_3DOWN: + case BYD_3LEFT: + case BYD_3RIGHT: + case BYD_4UP: + case BYD_4DOWN: + break; + + default: + psmouse_warn(psmouse, + "Unrecognized Z: pkt = %02x %02x %02x %02x\n", + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3]); + return PSMOUSE_BAD_DATA; + } + + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +/* Send a sequence of bytes, where each is ACKed before the next is sent. */ +static int byd_send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len) +{ + unsigned int i; + + for (i = 0; i < len; ++i) { + if (ps2_command(&psmouse->ps2dev, NULL, seq[i])) + return -1; + } + return 0; +} + +/* Keep scrolling after fingers are removed. */ +#define SCROLL_INERTIAL 0x01 +#define SCROLL_NO_INERTIAL 0x02 + +/* Clicking can be done by tapping or pressing. */ +#define CLICK_BOTH 0x01 +/* Clicking can only be done by pressing. */ +#define CLICK_PRESS_ONLY 0x02 + +static int byd_enable(struct psmouse *psmouse) +{ + const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 }; + const u8 seq2[] = { + 0xD3, 0x01, + 0xD0, 0x00, + 0xD0, 0x04, + /* Whether clicking is done by tapping or pressing. */ + 0xD4, CLICK_PRESS_ONLY, + 0xD5, 0x01, + 0xD7, 0x03, + /* Vertical and horizontal one-finger scroll zone inertia. */ + 0xD8, SCROLL_INERTIAL, + 0xDA, 0x05, + 0xDB, 0x02, + 0xE4, 0x05, + 0xD6, 0x01, + 0xDE, 0x04, + 0xE3, 0x01, + 0xCF, 0x00, + 0xD2, 0x03, + /* Vertical and horizontal two-finger scrolling inertia. */ + 0xE5, SCROLL_INERTIAL, + 0xD9, 0x02, + 0xD9, 0x07, + 0xDC, 0x03, + 0xDD, 0x03, + 0xDF, 0x03, + 0xE1, 0x03, + 0xD1, 0x00, + 0xCE, 0x00, + 0xCC, 0x00, + 0xE0, 0x00, + 0xE2, 0x01 + }; + u8 param[4]; + + if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1))) + return -1; + + /* Send a 0x01 command, which should return 4 bytes. */ + if (ps2_command(&psmouse->ps2dev, param, 0x0401)) + return -1; + + if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2))) + return -1; + + return 0; +} + +/* + * Send the set of PS/2 commands required to make it identify as an + * intellimouse with 4-byte instead of 3-byte packets. + */ +static int byd_send_intellimouse_sequence(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 param[4]; + int i; + const struct { + u16 command; + u8 arg; + } seq[] = { + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_RESET_BAT, 0 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_SETSCALE11, 0 }, + { PSMOUSE_CMD_GETINFO, 0 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRES, 0x03 }, + { PSMOUSE_CMD_ENABLE, 0 } + }; + + memset(param, 0, sizeof(param)); + for (i = 0; i < ARRAY_SIZE(seq); ++i) { + param[0] = seq[i].arg; + if (ps2_command(ps2dev, param, seq[i].command)) + return -1; + } + + return 0; +} + +static int byd_reset_touchpad(struct psmouse *psmouse) +{ + if (byd_send_intellimouse_sequence(psmouse)) + return -EIO; + + if (byd_enable(psmouse)) + return -EIO; + + return 0; +} + +static int byd_reconnect(struct psmouse *psmouse) +{ + int retry = 0, error = 0; + + psmouse_dbg(psmouse, "Reconnect\n"); + do { + psmouse_reset(psmouse); + if (retry) + ssleep(1); + error = byd_detect(psmouse, 0); + } while (error && ++retry < 3); + + if (error) + return error; + + psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); + + error = byd_reset_touchpad(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize device\n"); + return error; + } + + return 0; +} + +int byd_init(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + + if (psmouse_reset(psmouse)) + return -EIO; + + if (byd_reset_touchpad(psmouse)) + return -EIO; + + psmouse->reconnect = byd_reconnect; + psmouse->protocol_handler = byd_process_byte; + psmouse->pktsize = 4; + psmouse->resync_time = 0; + + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + + return 0; +} diff --git a/drivers/input/mouse/byd.h b/drivers/input/mouse/byd.h new file mode 100644 index 000000000000..d6c120cf36cd --- /dev/null +++ b/drivers/input/mouse/byd.h @@ -0,0 +1,18 @@ +#ifndef _BYD_H +#define _BYD_H + +#ifdef CONFIG_MOUSE_PS2_BYD +int byd_detect(struct psmouse *psmouse, bool set_properties); +int byd_init(struct psmouse *psmouse); +#else +static inline int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int byd_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_BYD */ + +#endif /* _BYD_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b9e4ee34c132..39d1becd35c9 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -37,6 +37,7 @@ #include "cypress_ps2.h" #include "focaltech.h" #include "vmmouse.h" +#include "byd.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -841,6 +842,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = vmmouse_detect, .init = vmmouse_init, }, +#endif +#ifdef CONFIG_MOUSE_PS2_BYD + { + .type = PSMOUSE_BYD, + .name = "BydPS/2", + .alias = "byd", + .detect = byd_detect, + .init = byd_init, + }, #endif { .type = PSMOUSE_AUTO, @@ -1105,6 +1115,10 @@ static int psmouse_extensions(struct psmouse *psmouse, if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, &max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2; + + if (psmouse_try_protocol(psmouse, PSMOUSE_BYD, + &max_proto, set_properties, true)) + return PSMOUSE_BYD; } /* diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index ad5a5a1ea872..e0ca6cda3d16 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -104,6 +104,7 @@ enum psmouse_type { PSMOUSE_CYPRESS, PSMOUSE_FOCALTECH, PSMOUSE_VMMOUSE, + PSMOUSE_BYD, PSMOUSE_AUTO /* This one should always be last */ }; -- cgit v1.2.3 From 1261d013daead425ee03c977a5b8dbe8016e25a6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 9 Feb 2016 10:33:40 -0800 Subject: Input: Allow compile test of Goodix and Colibri VF50 if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. If COMPILE_TEST is enabled, relax the dependency on GPIOLIB for the recently introduced or relaxed symbols TOUCHSCREEN_GOODIX and TOUCHSCREEN_COLIBRI_VF50. Signed-off-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 53a97b379c9f..f4e53b4a6062 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -334,7 +334,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your @@ -1112,7 +1112,8 @@ config TOUCHSCREEN_ZFORCE config TOUCHSCREEN_COLIBRI_VF50 tristate "Toradex Colibri on board touchscreen driver" - depends on GPIOLIB && IIO && VF610_ADC + depends on IIO && VF610_ADC + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a Colibri VF50 and plan to use the on-board provided 4-wire touchscreen driver. -- cgit v1.2.3 From 6ccb1d8f78bd349a8a5b2aa228d3c8b35ee704a8 Mon Sep 17 00:00:00 2001 From: Sangwon Jee Date: Thu, 14 Jan 2016 17:55:54 +0900 Subject: Input: add MELFAS MIP4 Touchscreen driver This is an input driver for MELFAS MIP4 Touchscreen devices, such as MMS400, MMS500, MCS8000, MIT200, MIT300, MIT400, MFS10. All devices implementing MIP4 protocol (MELFAS Interface Protocol Version 4) should be supported by this driver. Signed-off-by: Sangwon Jee Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/melfas_mip4.c | 1499 +++++++++++++++++++++++++++++++ 3 files changed, 1511 insertions(+) create mode 100644 drivers/input/touchscreen/melfas_mip4.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f4e53b4a6062..309951da0970 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -491,6 +491,17 @@ config TOUCHSCREEN_MMS114 To compile this driver as a module, choose M here: the module will be called mms114. +config TOUCHSCREEN_MELFAS_MIP4 + tristate "MELFAS MIP4 Touchscreen" + depends on I2C + help + Say Y here if you have a MELFAS MIP4 Touchscreen device. + + If unsure, say N. + + To compile this driver as a module, choose M here: + the module will be called melfas_mip4. + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 968ff12e3132..4b518c76e0d7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o +obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4) += melfas_mip4.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c new file mode 100644 index 000000000000..b8e9ead61ee2 --- /dev/null +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -0,0 +1,1499 @@ +/* + * MELFAS MIP4 Touchscreen + * + * Copyright (C) 2016 MELFAS Inc. + * + * Author : Sangwon Jee + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIP4_DEVICE_NAME "mip4_ts" + +/***************************************************************** + * Protocol + * Version : MIP 4.0 Rev 4.4 + *****************************************************************/ + +/* Address */ +#define MIP4_R0_BOOT 0x00 +#define MIP4_R1_BOOT_MODE 0x01 +#define MIP4_R1_BOOT_BUF_ADDR 0x10 +#define MIP4_R1_BOOT_STATUS 0x20 +#define MIP4_R1_BOOT_CMD 0x30 +#define MIP4_R1_BOOT_TARGET_ADDR 0x40 +#define MIP4_R1_BOOT_SIZE 0x44 + +#define MIP4_R0_INFO 0x01 +#define MIP4_R1_INFO_PRODUCT_NAME 0x00 +#define MIP4_R1_INFO_RESOLUTION_X 0x10 +#define MIP4_R1_INFO_RESOLUTION_Y 0x12 +#define MIP4_R1_INFO_NODE_NUM_X 0x14 +#define MIP4_R1_INFO_NODE_NUM_Y 0x15 +#define MIP4_R1_INFO_KEY_NUM 0x16 +#define MIP4_R1_INFO_VERSION_BOOT 0x20 +#define MIP4_R1_INFO_VERSION_CORE 0x22 +#define MIP4_R1_INFO_VERSION_APP 0x24 +#define MIP4_R1_INFO_VERSION_PARAM 0x26 +#define MIP4_R1_INFO_SECT_BOOT_START 0x30 +#define MIP4_R1_INFO_SECT_BOOT_END 0x31 +#define MIP4_R1_INFO_SECT_CORE_START 0x32 +#define MIP4_R1_INFO_SECT_CORE_END 0x33 +#define MIP4_R1_INFO_SECT_APP_START 0x34 +#define MIP4_R1_INFO_SECT_APP_END 0x35 +#define MIP4_R1_INFO_SECT_PARAM_START 0x36 +#define MIP4_R1_INFO_SECT_PARAM_END 0x37 +#define MIP4_R1_INFO_BUILD_DATE 0x40 +#define MIP4_R1_INFO_BUILD_TIME 0x44 +#define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48 +#define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A +#define MIP4_R1_INFO_PROTOCOL_NAME 0x50 +#define MIP4_R1_INFO_PROTOCOL_VERSION 0x58 +#define MIP4_R1_INFO_IC_ID 0x70 +#define MIP4_R1_INFO_IC_NAME 0x71 +#define MIP4_R1_INFO_IC_VENDOR_ID 0x75 +#define MIP4_R1_INFO_IC_HW_CATEGORY 0x77 +#define MIP4_R1_INFO_CONTACT_THD_SCR 0x78 +#define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A + +#define MIP4_R0_EVENT 0x02 +#define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00 +#define MIP4_R1_EVENT_FORMAT 0x04 +#define MIP4_R1_EVENT_SIZE 0x06 +#define MIP4_R1_EVENT_PACKET_INFO 0x10 +#define MIP4_R1_EVENT_PACKET_DATA 0x11 + +#define MIP4_R0_CTRL 0x06 +#define MIP4_R1_CTRL_READY_STATUS 0x00 +#define MIP4_R1_CTRL_EVENT_READY 0x01 +#define MIP4_R1_CTRL_MODE 0x10 +#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11 +#define MIP4_R1_CTRL_RECALIBRATE 0x12 +#define MIP4_R1_CTRL_POWER_STATE 0x13 +#define MIP4_R1_CTRL_GESTURE_TYPE 0x14 +#define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18 +#define MIP4_R1_CTRL_CHARGER_MODE 0x19 +#define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A +#define MIP4_R1_CTRL_WINDOW_MODE 0x1B +#define MIP4_R1_CTRL_PALM_REJECTION 0x1C +#define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D +#define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E +#define MIP4_R1_CTRL_I2C_ON_LPM 0x1F +#define MIP4_R1_CTRL_GESTURE_DEBUG 0x20 +#define MIP4_R1_CTRL_PALM_EVENT 0x22 +#define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23 + +/* Value */ +#define MIP4_BOOT_MODE_BOOT 0x01 +#define MIP4_BOOT_MODE_APP 0x02 + +#define MIP4_BOOT_STATUS_BUSY 0x05 +#define MIP4_BOOT_STATUS_ERROR 0x0E +#define MIP4_BOOT_STATUS_DONE 0xA0 + +#define MIP4_BOOT_CMD_MASS_ERASE 0x15 +#define MIP4_BOOT_CMD_PROGRAM 0x54 +#define MIP4_BOOT_CMD_ERASE 0x8F +#define MIP4_BOOT_CMD_WRITE 0xA5 +#define MIP4_BOOT_CMD_READ 0xC2 + +#define MIP4_EVENT_INPUT_TYPE_KEY 0 +#define MIP4_EVENT_INPUT_TYPE_SCREEN 1 +#define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2 + +#define I2C_RETRY_COUNT 3 /* 2~ */ + +#define MIP4_BUF_SIZE 128 +#define MIP4_MAX_FINGERS 10 +#define MIP4_MAX_KEYS 4 + +#define MIP4_TOUCH_MAJOR_MIN 0 +#define MIP4_TOUCH_MAJOR_MAX 255 +#define MIP4_TOUCH_MINOR_MIN 0 +#define MIP4_TOUCH_MINOR_MAX 255 +#define MIP4_PRESSURE_MIN 0 +#define MIP4_PRESSURE_MAX 255 + +#define MIP4_FW_NAME "melfas_mip4.fw" +#define MIP4_FW_UPDATE_DEBUG 0 /* 0 (default) or 1 */ + +struct mip4_fw_version { + u16 boot; + u16 core; + u16 app; + u16 param; +}; + +struct mip4_ts { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *gpio_ce; + + char phys[32]; + char product_name[16]; + + unsigned int max_x; + unsigned int max_y; + u8 node_x; + u8 node_y; + u8 node_key; + + struct mip4_fw_version fw_version; + + unsigned int event_size; + unsigned int event_format; + + unsigned int key_num; + unsigned short key_code[MIP4_MAX_KEYS]; + + bool wake_irq_enabled; + + u8 buf[MIP4_BUF_SIZE]; +}; + +static int mip4_i2c_xfer(struct mip4_ts *ts, + char *write_buf, unsigned int write_len, + char *read_buf, unsigned int read_len) +{ + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = write_buf, + .len = write_len, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = read_buf, + .len = read_len, + }, + }; + int retry = I2C_RETRY_COUNT; + int res; + int error; + + do { + res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (res == ARRAY_SIZE(msg)) + return 0; + + error = res < 0 ? res : -EIO; + dev_err(&ts->client->dev, + "%s - i2c_transfer failed: %d (%d)\n", + __func__, error, res); + } while (--retry); + + return error; +} + +static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v) +{ + v->boot = get_unaligned_le16(buf + 0); + v->core = get_unaligned_le16(buf + 2); + v->app = get_unaligned_le16(buf + 4); + v->param = get_unaligned_le16(buf + 6); +} + +/* + * Read chip firmware version + */ +static int mip4_get_fw_version(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT }; + u8 buf[sizeof(ts->fw_version)]; + int error; + + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf)); + if (error) { + memset(&ts->fw_version, 0xff, sizeof(ts->fw_version)); + return error; + } + + mip4_parse_fw_version(buf, &ts->fw_version); + + return 0; +} + +/* + * Fetch device characteristics + */ +static int mip4_query_device(struct mip4_ts *ts) +{ + int error; + u8 cmd[2]; + u8 buf[8]; + + /* Product name */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_PRODUCT_NAME; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), + ts->product_name, sizeof(ts->product_name)); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve product name: %d\n", error); + else + dev_dbg(&ts->client->dev, "product name: %.*s\n", + (int)sizeof(ts->product_name), ts->product_name); + + /* Firmware version */ + error = mip4_get_fw_version(ts); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve FW version: %d\n", error); + else + dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Resolution */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_RESOLUTION_X; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve touchscreen parameters: %d\n", + error); + } else { + ts->max_x = get_unaligned_le16(&buf[0]); + ts->max_y = get_unaligned_le16(&buf[2]); + dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n", + ts->max_x, ts->max_y); + + ts->node_x = buf[4]; + ts->node_y = buf[5]; + ts->node_key = buf[6]; + dev_dbg(&ts->client->dev, + "node_x: %d, node_y: %d, node_key: %d\n", + ts->node_x, ts->node_y, ts->node_key); + + /* Key ts */ + if (ts->node_key > 0) + ts->key_num = ts->node_key; + } + + /* Protocol */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve device type: %d\n", error); + ts->event_format = 0xff; + } else { + ts->event_format = get_unaligned_le16(&buf[4]); + ts->event_size = buf[6]; + dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n", + ts->event_format, ts->event_size); + + if (ts->event_format == 2 || ts->event_format > 3) + dev_warn(&ts->client->dev, + "Unknown event format %d\n", ts->event_format); + } + + return 0; +} + +static int mip4_power_on(struct mip4_ts *ts) +{ + if (ts->gpio_ce) { + gpiod_set_value_cansleep(ts->gpio_ce, 1); + + /* Booting delay : 200~300ms */ + usleep_range(200 * 1000, 300 * 1000); + } + + return 0; +} + +static void mip4_power_off(struct mip4_ts *ts) +{ + if (ts->gpio_ce) + gpiod_set_value_cansleep(ts->gpio_ce, 0); +} + +/* + * Clear touch input event status + */ +static void mip4_clear_input(struct mip4_ts *ts) +{ + int i; + + /* Screen */ + for (i = 0; i < MIP4_MAX_FINGERS; i++) { + input_mt_slot(ts->input, i); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + /* Keys */ + for (i = 0; i < ts->key_num; i++) + input_report_key(ts->input, ts->key_code[i], 0); + + input_sync(ts->input); +} + +static int mip4_enable(struct mip4_ts *ts) +{ + int error; + + error = mip4_power_on(ts); + if (error) + return error; + + enable_irq(ts->client->irq); + + return 0; +} + +static void mip4_disable(struct mip4_ts *ts) +{ + disable_irq(ts->client->irq); + + mip4_power_off(ts); + + mip4_clear_input(ts); +} + +/***************************************************************** + * Input handling + *****************************************************************/ + +static void mip4_report_keys(struct mip4_ts *ts, u8 *packet) +{ + u8 key; + bool down; + + switch (ts->event_format) { + case 0: + case 1: + key = packet[0] & 0x0F; + down = packet[0] & 0x80; + break; + + case 3: + default: + key = packet[0] & 0x0F; + down = packet[1] & 0x01; + break; + } + + /* Report key event */ + if (key >= 1 && key <= ts->key_num) { + unsigned short keycode = ts->key_code[key - 1]; + + dev_dbg(&ts->client->dev, + "Key - ID: %d, keycode: %d, state: %d\n", + key, keycode, down); + + input_event(ts->input, EV_MSC, MSC_SCAN, keycode); + input_report_key(ts->input, keycode, down); + + } else { + dev_err(&ts->client->dev, "Unknown key: %d\n", key); + } +} + +static void mip4_report_touch(struct mip4_ts *ts, u8 *packet) +{ + int id; + bool hover; + bool palm; + bool state; + u16 x, y; + u8 pressure_stage = 0; + u8 pressure; + u8 size; + u8 touch_major; + u8 touch_minor; + + switch (ts->event_format) { + case 0: + case 1: + /* Touch only */ + state = packet[0] & BIT(7); + hover = packet[0] & BIT(5); + palm = packet[0] & BIT(4); + id = (packet[0] & 0x0F) - 1; + x = ((packet[1] & 0x0F) << 8) | packet[2]; + y = (((packet[1] >> 4) & 0x0F) << 8) | + packet[3]; + pressure = packet[4]; + size = packet[5]; + if (ts->event_format == 0) { + touch_major = packet[5]; + touch_minor = packet[5]; + } else { + touch_major = packet[6]; + touch_minor = packet[7]; + } + break; + + case 3: + default: + /* Touch + Force(Pressure) */ + id = (packet[0] & 0x0F) - 1; + hover = packet[1] & BIT(2); + palm = packet[1] & BIT(1); + state = packet[1] & BIT(0); + x = ((packet[2] & 0x0F) << 8) | packet[3]; + y = (((packet[2] >> 4) & 0x0F) << 8) | + packet[4]; + size = packet[6]; + pressure_stage = (packet[7] & 0xF0) >> 4; + pressure = ((packet[7] & 0x0F) << 8) | + packet[8]; + touch_major = packet[9]; + touch_minor = packet[10]; + break; + } + + dev_dbg(&ts->client->dev, + "Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n", + id, state, x, y, pressure); + + if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) { + dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id); + } else if (state) { + /* Press or Move event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); + input_report_abs(ts->input, ABS_MT_POSITION_X, x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, y); + input_report_abs(ts->input, ABS_MT_PRESSURE, pressure); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor); + } else { + /* Release event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + input_mt_sync_frame(ts->input); +} + +static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet) +{ + u8 type; + + switch (ts->event_format) { + case 0: + case 1: + type = (packet[0] & 0x40) >> 6; + break; + + case 3: + type = (packet[0] & 0xF0) >> 4; + break; + + default: + /* Should not happen unless we have corrupted firmware */ + return -EINVAL; + } + + dev_dbg(&ts->client->dev, "Type: %d\n", type); + + /* Report input event */ + switch (type) { + case MIP4_EVENT_INPUT_TYPE_KEY: + mip4_report_keys(ts, packet); + break; + + case MIP4_EVENT_INPUT_TYPE_SCREEN: + mip4_report_touch(ts, packet); + break; + + default: + dev_err(&ts->client->dev, "Unknown event type: %d\n", type); + break; + } + + return 0; +} + +static irqreturn_t mip4_interrupt(int irq, void *dev_id) +{ + struct mip4_ts *ts = dev_id; + struct i2c_client *client = ts->client; + unsigned int i; + int error; + u8 cmd[2]; + u8 size; + bool alert; + + /* Read packet info */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_INFO; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1); + if (error) { + dev_err(&client->dev, + "Failed to read packet info: %d\n", error); + goto out; + } + + size = ts->buf[0] & 0x7F; + alert = ts->buf[0] & BIT(7); + dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert); + + /* Check size */ + if (!size) { + dev_err(&client->dev, "Empty packet\n"); + goto out; + } + + /* Read packet data */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_DATA; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size); + if (error) { + dev_err(&client->dev, + "Failed to read packet data: %d\n", error); + goto out; + } + + if (alert) { + dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]); + } else { + for (i = 0; i < size; i += ts->event_size) { + error = mip4_handle_packet(ts, &ts->buf[i]); + if (error) + break; + } + + input_sync(ts->input); + } + +out: + return IRQ_HANDLED; +} + +static int mip4_input_open(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + return mip4_enable(ts); +} + +static void mip4_input_close(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + mip4_disable(ts); +} + +/***************************************************************** + * Firmware update + *****************************************************************/ + +/* Firmware Info */ +#define MIP4_BL_PAGE_SIZE 512 /* 512 */ +#define MIP4_BL_PACKET_SIZE 512 /* 512, 256, 128, 64, ... */ + +/* + * Firmware binary tail info + */ + +struct mip4_bin_tail { + u8 tail_mark[4]; + u8 chip_name[4]; + + __le32 bin_start_addr; + __le32 bin_length; + + __le16 ver_boot; + __le16 ver_core; + __le16 ver_app; + __le16 ver_param; + + u8 boot_start; + u8 boot_end; + u8 core_start; + u8 core_end; + u8 app_start; + u8 app_end; + u8 param_start; + u8 param_end; + + u8 checksum_type; + u8 hw_category; + + __le16 param_id; + __le32 param_length; + __le32 build_date; + __le32 build_time; + + __le32 reserved1; + __le32 reserved2; + __le16 reserved3; + __le16 tail_size; + __le32 crc; +} __packed; + +#define MIP4_BIN_TAIL_MARK "MBT\001" +#define MIP4_BIN_TAIL_SIZE (sizeof(struct mip4_bin_tail)) + +/* +* Bootloader - Read status +*/ +static int mip4_bl_read_status(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_STATUS }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int ret; + int error; + int retry = 1000; + + do { + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read bootloader status: %d\n", + error); + return error; + } + + switch (result) { + case MIP4_BOOT_STATUS_DONE: + dev_dbg(&ts->client->dev, "%s - done\n", __func__); + return 0; + + case MIP4_BOOT_STATUS_ERROR: + dev_err(&ts->client->dev, "Bootloader failure\n"); + return -EIO; + + case MIP4_BOOT_STATUS_BUSY: + dev_dbg(&ts->client->dev, "%s - Busy\n", __func__); + error = -EBUSY; + break; + + default: + dev_err(&ts->client->dev, + "Unexpected bootloader status: %#02x\n", + result); + error = -EINVAL; + break; + } + + usleep_range(1000, 2000); + } while (--retry); + + return error; +} + +/* +* Bootloader - Change mode +*/ +static int mip4_bl_change_mode(struct mip4_ts *ts, u8 mode) +{ + u8 mode_chg_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE, mode }; + u8 mode_read_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = mode_read_cmd, + .len = sizeof(mode_read_cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int retry = 10; + int ret; + int error; + + do { + /* Send mode change command */ + ret = i2c_master_send(ts->client, + mode_chg_cmd, sizeof(mode_chg_cmd)); + if (ret != sizeof(mode_chg_cmd)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send %d mode change: %d (%d)\n", + mode, error, ret); + return error; + } + + dev_dbg(&ts->client->dev, + "Sent mode change request (mode: %d)\n", mode); + + /* Wait */ + msleep(1000); + + /* Verify target mode */ + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read device mode: %d\n", error); + return error; + } + + dev_dbg(&ts->client->dev, + "Current device mode: %d, want: %d\n", result, mode); + + if (result == mode) + return 0; + + } while (--retry); + + return -EIO; +} + +/* + * Bootloader - Start bootloader mode + */ +static int mip4_bl_enter(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_BOOT); +} + +/* + * Bootloader - Exit bootloader mode + */ +static int mip4_bl_exit(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_APP); +} + +static int mip4_bl_get_address(struct mip4_ts *ts, u16 *buf_addr) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_BUF_ADDR }; + u8 result[sizeof(u16)]; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = result, + .len = sizeof(result), + }, + }; + int ret; + int error; + + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to retrieve bootloader buffer address: %d\n", + error); + return error; + } + + *buf_addr = get_unaligned_le16(result); + dev_dbg(&ts->client->dev, + "Bootloader buffer address %#04x\n", *buf_addr); + + return 0; +} + +static int mip4_bl_program_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, u16 buf_addr) +{ + u8 cmd[6]; + u8 *data_buf; + u16 buf_offset; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Writing page @%#06x (%d)\n", + offset, length); + + if (length > MIP4_BL_PAGE_SIZE || length % MIP4_BL_PACKET_SIZE) { + dev_err(&ts->client->dev, + "Invalid page length: %d\n", length); + return -EINVAL; + } + + data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page address: %d\n", error); + goto out; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page size: %d\n", error); + goto out; + } + + /* Data */ + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "writing chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, data_buf); + memcpy(&data_buf[2], &data[buf_offset], MIP4_BL_PACKET_SIZE); + ret = i2c_master_send(ts->client, + data_buf, 2 + MIP4_BL_PACKET_SIZE); + if (ret != 2 + MIP4_BL_PACKET_SIZE) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + goto out; + } + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_PROGRAM; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'write' command: %d\n", error); + goto out; + } + + /* Status */ + error = mip4_bl_read_status(ts); + +out: + kfree(data_buf); + return error ? error : 0; +} + +static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, int buf_addr) +{ + u8 cmd[8]; + u8 *read_buf; + int buf_offset; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .len = MIP4_BL_PACKET_SIZE, + }, + }; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Validating page @%#06x (%d)\n", + offset, length); + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page address: %d\n", error); + return error; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page size: %d\n", error); + return error; + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_READ; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'read' command: %d\n", error); + return error; + } + + /* Status */ + error = mip4_bl_read_status(ts); + if (error) + return error; + + /* Read */ + msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!read_buf) + return -ENOMEM; + + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "reading chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, cmd); + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + break; + } + + if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) { + dev_err(&ts->client->dev, + "Failed to validate chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W File: ", + DUMP_PREFIX_OFFSET, 16, 1, + data + offset, MIP4_BL_PACKET_SIZE, + false); + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W Chip: ", + DUMP_PREFIX_OFFSET, 16, 1, + read_buf, MIP4_BL_PAGE_SIZE, false); +#endif + error = -EINVAL; + break; + } + } + + kfree(read_buf); + return error ? error : 0; +} + +/* + * Flash chip firmware + */ +static int mip4_flash_fw(struct mip4_ts *ts, + const u8 *fw_data, u32 fw_size, u32 fw_offset) +{ + struct i2c_client *client = ts->client; + int offset; + u16 buf_addr; + int error, error2; + + /* Enter bootloader mode */ + dev_dbg(&client->dev, "Entering bootloader mode\n"); + + error = mip4_bl_enter(ts); + if (error) { + dev_err(&client->dev, + "Failed to enter bootloader mode: %d\n", + error); + return error; + } + + /* Read info */ + error = mip4_bl_get_address(ts, &buf_addr); + if (error) + goto exit_bl; + + /* Program & Verify */ + dev_dbg(&client->dev, + "Program & Verify, page size: %d, packet size: %d\n", + MIP4_BL_PAGE_SIZE, MIP4_BL_PACKET_SIZE); + + for (offset = fw_offset; + offset < fw_offset + fw_size; + offset += MIP4_BL_PAGE_SIZE) { + /* Program */ + error = mip4_bl_program_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + + /* Verify */ + error = mip4_bl_verify_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + } + +exit_bl: + /* Exit bootloader mode */ + dev_dbg(&client->dev, "Exiting bootloader mode\n"); + + error2 = mip4_bl_exit(ts); + if (error2) { + dev_err(&client->dev, + "Failed to exit bootloader mode: %d\n", error2); + if (!error) + error = error2; + } + + /* Reset chip */ + mip4_power_off(ts); + mip4_power_on(ts); + + mip4_query_device(ts); + + /* Refresh device parameters */ + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0); + + return error ? error : 0; +} + +static int mip4_parse_firmware(struct mip4_ts *ts, const struct firmware *fw, + u32 *fw_offset_start, u32 *fw_size, + const struct mip4_bin_tail **pfw_info) +{ + const struct mip4_bin_tail *fw_info; + struct mip4_fw_version fw_version; + u16 tail_size; + + if (fw->size < MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "Invalid firmware, size mismatch (tail %zd vs %zd)\n", + MIP4_BIN_TAIL_SIZE, fw->size); + return -EINVAL; + } + + fw_info = (const void *)&fw->data[fw->size - MIP4_BIN_TAIL_SIZE]; + +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_ERR, MIP4_DEVICE_NAME " Bin Info: ", + DUMP_PREFIX_OFFSET, 16, 1, *fw_info, tail_size, false); +#endif + + tail_size = get_unaligned_le16(&fw_info->tail_size); + if (tail_size != MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "wrong tail size: %d (expected %zd)\n", + tail_size, MIP4_BIN_TAIL_SIZE); + return -EINVAL; + } + + /* Check bin format */ + if (memcmp(fw_info->tail_mark, MIP4_BIN_TAIL_MARK, + sizeof(fw_info->tail_mark))) { + dev_err(&ts->client->dev, + "unable to locate tail marker (%*ph vs %*ph)\n", + (int)sizeof(fw_info->tail_mark), fw_info->tail_mark, + (int)sizeof(fw_info->tail_mark), MIP4_BIN_TAIL_MARK); + return -EINVAL; + } + + *fw_offset_start = get_unaligned_le32(&fw_info->bin_start_addr); + *fw_size = get_unaligned_le32(&fw_info->bin_length); + + dev_dbg(&ts->client->dev, + "F/W Data offset: %#08x, size: %d\n", + *fw_offset_start, *fw_size); + + if (*fw_size % MIP4_BL_PAGE_SIZE) { + dev_err(&ts->client->dev, + "encoded fw length %d is not multiple of pages (%d)\n", + *fw_size, MIP4_BL_PAGE_SIZE); + return -EINVAL; + } + + if (fw->size != *fw_offset_start + *fw_size) { + dev_err(&ts->client->dev, + "Wrong firmware size, expected %d bytes, got %zd\n", + *fw_offset_start + *fw_size, fw->size); + return -EINVAL; + } + + mip4_parse_fw_version((const u8 *)&fw_info->ver_boot, &fw_version); + + dev_dbg(&ts->client->dev, + "F/W file version %04X %04X %04X %04X\n", + fw_version.boot, fw_version.core, + fw_version.app, fw_version.param); + + dev_dbg(&ts->client->dev, "F/W chip version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Check F/W type */ + if (fw_version.boot != 0xEEEE && fw_version.boot != 0xFFFF && + fw_version.core == 0xEEEE && + fw_version.app == 0xEEEE && + fw_version.param == 0xEEEE) { + dev_dbg(&ts->client->dev, "F/W type: Bootloader\n"); + } else if (fw_version.boot == 0xEEEE && + fw_version.core != 0xEEEE && fw_version.core != 0xFFFF && + fw_version.app != 0xEEEE && fw_version.app != 0xFFFF && + fw_version.param != 0xEEEE && fw_version.param != 0xFFFF) { + dev_dbg(&ts->client->dev, "F/W type: Main\n"); + } else { + dev_err(&ts->client->dev, "Wrong firmware type\n"); + return -EINVAL; + } + + return 0; +} + +static int mip4_execute_fw_update(struct mip4_ts *ts, const struct firmware *fw) +{ + const struct mip4_bin_tail *fw_info; + u32 fw_start_offset; + u32 fw_size; + int retires = 3; + int error; + + error = mip4_parse_firmware(ts, fw, + &fw_start_offset, &fw_size, &fw_info); + if (error) + return error; + + if (ts->input->users) { + disable_irq(ts->client->irq); + } else { + error = mip4_power_on(ts); + if (error) + return error; + } + + /* Update firmware */ + do { + error = mip4_flash_fw(ts, fw->data, fw_size, fw_start_offset); + if (!error) + break; + } while (--retires); + + if (error) + dev_err(&ts->client->dev, + "Failed to flash firmware: %d\n", error); + + /* Enable IRQ */ + if (ts->input->users) + enable_irq(ts->client->irq); + else + mip4_power_off(ts); + + return error ? error : 0; +} + +static ssize_t mip4_sysfs_fw_update(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + const struct firmware *fw; + int error; + + error = request_firmware(&fw, MIP4_FW_NAME, dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to retrieve firmware %s: %d\n", + MIP4_FW_NAME, error); + return error; + } + + /* + * Take input mutex to prevent racing with itself and also with + * userspace opening and closing the device and also suspend/resume + * transitions. + */ + mutex_lock(&ts->input->mutex); + + error = mip4_execute_fw_update(ts, fw); + + mutex_unlock(&ts->input->mutex); + + release_firmware(fw); + + if (error) { + dev_err(&ts->client->dev, + "Firmware update failed: %d\n", error); + return error; + } + + return count; +} + +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mip4_sysfs_fw_update); + +static ssize_t mip4_sysfs_read_fw_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + /* Take lock to prevent racing with firmware update */ + mutex_lock(&ts->input->mutex); + + count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL); + +static struct attribute *mip4_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_update_fw.attr, + NULL, +}; + +static const struct attribute_group mip4_attr_group = { + .attrs = mip4_attrs, +}; + +static void mip4_sysfs_remove(void *_data) +{ + struct mip4_ts *ts = _data; + + sysfs_remove_group(&ts->client->dev.kobj, &mip4_attr_group); +} + +static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mip4_ts *ts; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "Not supported I2C adapter\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; + + ts->client = client; + ts->input = input; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + ts->gpio_ce = devm_gpiod_get_optional(&client->dev, + "ce", GPIOD_OUT_LOW); + if (IS_ERR(ts->gpio_ce)) { + error = PTR_ERR(ts->gpio_ce); + if (error != EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get gpio: %d\n", error); + return error; + } + + error = mip4_power_on(ts); + if (error) + return error; + error = mip4_query_device(ts); + mip4_power_off(ts); + if (error) + return error; + + input->name = "MELFAS MIP4 Touchscreen"; + input->phys = ts->phys; + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x13c5; + + input->open = mip4_input_open; + input->close = mip4_input_close; + + input_set_drvdata(input, ts); + + input->keycode = ts->key_code; + input->keycodesize = sizeof(*ts->key_code); + input->keycodemax = ts->key_num; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, + MIP4_PRESSURE_MIN, MIP4_PRESSURE_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0); + + error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT); + if (error) + return error; + + i2c_set_clientdata(client, ts); + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mip4_interrupt, + IRQF_ONESHOT, MIP4_DEVICE_NAME, ts); + if (error) { + dev_err(&client->dev, + "Failed to request interrupt %d: %d\n", + client->irq, error); + return error; + } + + disable_irq(client->irq); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + error = sysfs_create_group(&client->dev.kobj, &mip4_attr_group); + if (error) { + dev_err(&client->dev, + "Failed to create sysfs attribute group: %d\n", error); + return error; + } + + error = devm_add_action(&client->dev, mip4_sysfs_remove, ts); + if (error) { + mip4_sysfs_remove(ts); + dev_err(&client->dev, + "Failed to install sysfs remoce action: %d\n", error); + return error; + } + + return 0; +} + +static int __maybe_unused mip4_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(dev)) + ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0; + else if (input->users) + mip4_disable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static int __maybe_unused mip4_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (ts->wake_irq_enabled) + disable_irq_wake(client->irq); + else if (input->users) + mip4_enable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mip4_pm_ops, mip4_suspend, mip4_resume); + +#ifdef CONFIG_OF +static const struct of_device_id mip4_of_match[] = { + { .compatible = "melfas,"MIP4_DEVICE_NAME, }, + { }, +}; +MODULE_DEVICE_TABLE(of, mip4_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id mip4_acpi_match[] = { + { "MLFS0000", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, mip4_acpi_match); +#endif + +static const struct i2c_device_id mip4_i2c_ids[] = { + { MIP4_DEVICE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids); + +static struct i2c_driver mip4_driver = { + .id_table = mip4_i2c_ids, + .probe = mip4_probe, + .driver = { + .name = MIP4_DEVICE_NAME, + .of_match_table = of_match_ptr(mip4_of_match), + .acpi_match_table = ACPI_PTR(mip4_acpi_match), + .pm = &mip4_pm_ops, + }, +}; +module_i2c_driver(mip4_driver); + +MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); +MODULE_VERSION("2016.01.13"); +MODULE_AUTHOR("Sangwon Jee "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From dee520e368f54e1bc0e387bfda7ef4e0676e248b Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Sat, 16 Jan 2016 20:46:24 -0800 Subject: Input: rotary_encoder - use threaded irqs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert to use threaded IRQs to support GPIOs that can sleep. Protect the irq handler with mutex as it can be triggered from two different irq lines accessing the same state. This allows using GPIO expanders behind I2C or SPI bus. Signed-off-by: Timo Teräs Signed-off-by: Dmitry Torokhov --- drivers/input/misc/rotary_encoder.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index ebbadf3f0579..aeeaaea69610 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -33,6 +33,7 @@ struct rotary_encoder { struct input_dev *input; const struct rotary_encoder_platform_data *pdata; + struct mutex access_mutex; unsigned int axis; unsigned int pos; @@ -48,8 +49,8 @@ struct rotary_encoder { static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) { - int a = !!gpio_get_value(pdata->gpio_a); - int b = !!gpio_get_value(pdata->gpio_b); + int a = !!gpio_get_value_cansleep(pdata->gpio_a); + int b = !!gpio_get_value_cansleep(pdata->gpio_b); a ^= pdata->inverted_a; b ^= pdata->inverted_b; @@ -94,6 +95,8 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) struct rotary_encoder *encoder = dev_id; int state; + mutex_lock(&encoder->access_mutex); + state = rotary_encoder_get_state(encoder->pdata); switch (state) { @@ -115,6 +118,8 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } @@ -123,6 +128,8 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) struct rotary_encoder *encoder = dev_id; int state; + mutex_lock(&encoder->access_mutex); + state = rotary_encoder_get_state(encoder->pdata); switch (state) { @@ -140,6 +147,8 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } @@ -149,6 +158,8 @@ static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) unsigned char sum; int state; + mutex_lock(&encoder->access_mutex); + state = rotary_encoder_get_state(encoder->pdata); /* @@ -189,6 +200,8 @@ static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) out: encoder->last_stable = state; + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } @@ -285,6 +298,8 @@ static int rotary_encoder_probe(struct platform_device *pdev) if (!input) return -ENOMEM; + mutex_init(&encoder->access_mutex); + encoder->input = input; encoder->pdata = pdata; @@ -337,17 +352,19 @@ static int rotary_encoder_probe(struct platform_device *pdev) return -EINVAL; } - err = devm_request_irq(dev, encoder->irq_a, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); + err = devm_request_threaded_irq(dev, encoder->irq_a, NULL, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + DRV_NAME, encoder); if (err) { dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); return err; } - err = devm_request_irq(dev, encoder->irq_b, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); + err = devm_request_threaded_irq(dev, encoder->irq_b, NULL, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + DRV_NAME, encoder); if (err) { dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); return err; -- cgit v1.2.3 From 6a6f70b3864a5f8f862b7153c1b4d1217f50e4d4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 16 Jan 2016 20:50:06 -0800 Subject: Input: rotary_encoder - mark PM methods as __maybe_unused Instead of guarding PM methods with #ifdef let's mark them as __maybe_unused as it allows for better compile coverage. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/rotary_encoder.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index aeeaaea69610..713247146167 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -383,8 +383,7 @@ static int rotary_encoder_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int rotary_encoder_suspend(struct device *dev) +static int __maybe_unused rotary_encoder_suspend(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); @@ -396,7 +395,7 @@ static int rotary_encoder_suspend(struct device *dev) return 0; } -static int rotary_encoder_resume(struct device *dev) +static int __maybe_unused rotary_encoder_resume(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); @@ -407,10 +406,9 @@ static int rotary_encoder_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, - rotary_encoder_suspend, rotary_encoder_resume); + rotary_encoder_suspend, rotary_encoder_resume); static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, -- cgit v1.2.3 From 77a8f0ad38844b5d4163424c4feb8abbbbb55af0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 17 Jan 2016 10:18:12 -0800 Subject: Input: rotary_encoder - convert to use gpiod API Instead of using old GPIO API, let's switch to GPIOD API, which automatically handles polarity. Signed-off-by: Dmitry Torokhov --- arch/arm/mach-pxa/raumfeld.c | 21 +++++++++-- drivers/input/misc/rotary_encoder.c | 74 +++++++++++++++++-------------------- include/linux/rotary_encoder.h | 4 -- 3 files changed, 50 insertions(+), 49 deletions(-) (limited to 'drivers/input') diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 36571a9a44fe..bac88e0be58a 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -366,14 +367,21 @@ static struct pxaohci_platform_data raumfeld_ohci_info = { * Rotary encoder input device */ +static struct gpiod_lookup_table raumfeld_rotary_gpios_table = { + .dev_id = "rotary-encoder.0", + .table = { + GPIO_LOOKUP_IDX("gpio-0", + GPIO_VOLENC_A, NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("gpio-0", + GPIO_VOLENC_B, NULL, 1, GPIO_ACTIVE_HIGH), + { }, + }, +}; + static struct rotary_encoder_platform_data raumfeld_rotary_encoder_info = { .steps = 24, .axis = REL_X, .relative_axis = 1, - .gpio_a = GPIO_VOLENC_A, - .gpio_b = GPIO_VOLENC_B, - .inverted_a = 1, - .inverted_b = 0, }; static struct platform_device rotary_encoder_device = { @@ -1051,7 +1059,10 @@ static void __init raumfeld_controller_init(void) int ret; pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_controller_pin_config)); + + gpiod_add_lookup_table(&raumfeld_rotary_gpios_table); platform_device_register(&rotary_encoder_device); + spi_register_board_info(ARRAY_AND_SIZE(controller_spi_devices)); i2c_register_board_info(0, &raumfeld_controller_i2c_board_info, 1); @@ -1086,6 +1097,8 @@ static void __init raumfeld_speaker_init(void) i2c_register_board_info(0, &raumfeld_connector_i2c_board_info, 1); platform_device_register(&smc91x_device); + + gpiod_add_lookup_table(&raumfeld_rotary_gpios_table); platform_device_register(&rotary_encoder_device); raumfeld_audio_init(); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 713247146167..471175e5306b 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -20,12 +20,11 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #define DRV_NAME "rotary-encoder" @@ -38,6 +37,9 @@ struct rotary_encoder { unsigned int axis; unsigned int pos; + struct gpio_desc *gpio_a; + struct gpio_desc *gpio_b; + unsigned int irq_a; unsigned int irq_b; @@ -47,13 +49,10 @@ struct rotary_encoder { char last_stable; }; -static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) +static int rotary_encoder_get_state(struct rotary_encoder *encoder) { - int a = !!gpio_get_value_cansleep(pdata->gpio_a); - int b = !!gpio_get_value_cansleep(pdata->gpio_b); - - a ^= pdata->inverted_a; - b ^= pdata->inverted_b; + int a = !!gpiod_get_value_cansleep(encoder->gpio_a); + int b = !!gpiod_get_value_cansleep(encoder->gpio_b); return ((a << 1) | b); } @@ -97,7 +96,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) mutex_lock(&encoder->access_mutex); - state = rotary_encoder_get_state(encoder->pdata); + state = rotary_encoder_get_state(encoder); switch (state) { case 0x0: @@ -130,7 +129,7 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) mutex_lock(&encoder->access_mutex); - state = rotary_encoder_get_state(encoder->pdata); + state = rotary_encoder_get_state(encoder); switch (state) { case 0x00: @@ -160,7 +159,7 @@ static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) mutex_lock(&encoder->access_mutex); - state = rotary_encoder_get_state(encoder->pdata); + state = rotary_encoder_get_state(encoder); /* * We encode the previous and the current state using a byte. @@ -218,7 +217,6 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic of_match_device(rotary_encoder_of_match, dev); struct device_node *np = dev->of_node; struct rotary_encoder_platform_data *pdata; - enum of_gpio_flags flags; int error; if (!of_id || !np) @@ -232,12 +230,6 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); of_property_read_u32(np, "linux,axis", &pdata->axis); - pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); - pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; - - pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); - pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; - pdata->relative_axis = of_property_read_bool(np, "rotary-encoder,relative-axis"); pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); @@ -294,14 +286,32 @@ static int rotary_encoder_probe(struct platform_device *pdev) if (!encoder) return -ENOMEM; + mutex_init(&encoder->access_mutex); + encoder->pdata = pdata; + + encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN); + if (IS_ERR(encoder->gpio_a)) { + err = PTR_ERR(encoder->gpio_a); + dev_err(dev, "unable to get GPIO at index 0: %d\n", err); + return err; + } + + encoder->irq_a = gpiod_to_irq(encoder->gpio_a); + + encoder->gpio_b = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN); + if (IS_ERR(encoder->gpio_b)) { + err = PTR_ERR(encoder->gpio_b); + dev_err(dev, "unable to get GPIO at index 1: %d\n", err); + return err; + } + + encoder->irq_b = gpiod_to_irq(encoder->gpio_b); + input = devm_input_allocate_device(dev); if (!input) return -ENOMEM; - mutex_init(&encoder->access_mutex); - encoder->input = input; - encoder->pdata = pdata; input->name = pdev->name; input->id.bustype = BUS_HOST; @@ -316,32 +326,14 @@ static int rotary_encoder_probe(struct platform_device *pdev) pdata->axis, 0, pdata->steps, 0, 1); } - /* request the GPIOs */ - err = devm_gpio_request_one(dev, pdata->gpio_a, GPIOF_IN, - dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); - return err; - } - - err = devm_gpio_request_one(dev, pdata->gpio_b, GPIOF_IN, - dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); - return err; - } - - encoder->irq_a = gpio_to_irq(pdata->gpio_a); - encoder->irq_b = gpio_to_irq(pdata->gpio_b); - switch (pdata->steps_per_period) { case 4: handler = &rotary_encoder_quarter_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 2: handler = &rotary_encoder_half_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 1: handler = &rotary_encoder_irq; diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h index fe3dc64e5aeb..4536c813a1e9 100644 --- a/include/linux/rotary_encoder.h +++ b/include/linux/rotary_encoder.h @@ -4,10 +4,6 @@ struct rotary_encoder_platform_data { unsigned int steps; unsigned int axis; - unsigned int gpio_a; - unsigned int gpio_b; - unsigned int inverted_a; - unsigned int inverted_b; unsigned int steps_per_period; bool relative_axis; bool rollover; -- cgit v1.2.3 From 8631580f4fde9584ef39480eb0f2219708e5aeba Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 17 Jan 2016 10:52:54 -0800 Subject: Input: rotary_encoder - use input_set_capability() Instead of manipulating capability bits directly let's use appropriate helpers. Also there is no need to explicitly set EV_ABS when calling input_set_abs_params(). Signed-off-by: Dmitry Torokhov --- drivers/input/misc/rotary_encoder.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 471175e5306b..70fdcce84866 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -317,14 +317,10 @@ static int rotary_encoder_probe(struct platform_device *pdev) input->id.bustype = BUS_HOST; input->dev.parent = dev; - if (pdata->relative_axis) { - input->evbit[0] = BIT_MASK(EV_REL); - input->relbit[0] = BIT_MASK(pdata->axis); - } else { - input->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(encoder->input, - pdata->axis, 0, pdata->steps, 0, 1); - } + if (pdata->relative_axis) + input_set_capability(input, EV_REL, pdata->axis); + else + input_set_abs_params(input, pdata->axis, 0, pdata->steps, 0, 1); switch (pdata->steps_per_period) { case 4: -- cgit v1.2.3 From 8c5dc5a1ada2b79259e55a4bd150135d23529c6a Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 26 Feb 2016 12:05:02 -0800 Subject: Input: goldfish_events - add devicetree bindings Add device tree bindings to the Goldfish virtual platform event driver. Signed-off-by: Greg Hackmann Signed-off-by: Jin Qian Signed-off-by: Alan Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/goldfish/events.txt | 17 +++++++++++++++++ drivers/input/keyboard/goldfish_events.c | 7 +++++++ 2 files changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/goldfish/events.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/goldfish/events.txt b/Documentation/devicetree/bindings/goldfish/events.txt new file mode 100644 index 000000000000..5babf46317a4 --- /dev/null +++ b/Documentation/devicetree/bindings/goldfish/events.txt @@ -0,0 +1,17 @@ +Android Goldfish Events Keypad + +Android goldfish events keypad device generated by android emulator. + +Required properties: + +- compatible : should contain "google,goldfish-events-keypad" to match emulator +- reg : +- interrupts : + +Example: + + goldfish-events@9040000 { + compatible = "google,goldfish-events-keypad"; + reg = <0x9040000 0x1000>; + interrupts = <0x5>; + }; diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index 907e4e278fce..b11d218604a7 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -178,10 +178,17 @@ static int events_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_events_of_match[] = { + { .compatible = "google,goldfish-events-keypad", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_events_of_match); + static struct platform_driver events_driver = { .probe = events_probe, .driver = { .name = "goldfish_events", + .of_match_table = goldfish_events_of_match, }, }; -- cgit v1.2.3 From 0581ce09fd2c976125a20791268d7206db156d2f Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Fri, 26 Feb 2016 12:06:47 -0800 Subject: Input: goldfish_events - enable ACPI-based enumeration for goldfish events Add ACPI binding to the goldfish events driver. Signed-off-by: Jason Hu Signed-off-by: Jin Qian Signed-off-by: Alan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/goldfish_events.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index b11d218604a7..f6e643b589b6 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -22,6 +22,7 @@ #include #include #include +#include enum { REG_READ = 0x00, @@ -184,11 +185,20 @@ static const struct of_device_id goldfish_events_of_match[] = { }; MODULE_DEVICE_TABLE(of, goldfish_events_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id goldfish_events_acpi_match[] = { + { "GFSH0002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match); +#endif + static struct platform_driver events_driver = { .probe = events_probe, .driver = { .name = "goldfish_events", .of_match_table = goldfish_events_of_match, + .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match), }, }; -- cgit v1.2.3 From 7ffae9909bddb0367052133fe74ec5c9898c27b4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2016 09:11:16 -0800 Subject: Input: spear-keyboard - use __maybe_unused to hide pm functions The spear keyboard driver uses #ifdef CONFIG_PM to hide its power management functions, but then uses references from SIMPLE_DEV_PM_OPS that are only present if both CONFIG_PM and CONFIG_PM_SLEEP are set, resulting in a warning about unused functions: drivers/input/keyboard/spear-keyboard.c:292:12: error: 'spear_kbd_suspend' defined but not used [-Werror=unused-function] drivers/input/keyboard/spear-keyboard.c:345:12: error: 'spear_kbd_resume' defined but not used [-Werror=unused-function] This removes the #ifdef and instead uses a __maybe_unused annotation. Signed-off-by: Arnd Bergmann Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/spear-keyboard.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 623d451767e3..8083eaa0524a 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -288,8 +288,7 @@ static int spear_kbd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int spear_kbd_suspend(struct device *dev) +static int __maybe_unused spear_kbd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -342,7 +341,7 @@ static int spear_kbd_suspend(struct device *dev) return 0; } -static int spear_kbd_resume(struct device *dev) +static int __maybe_unused spear_kbd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -368,7 +367,6 @@ static int spear_kbd_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); -- cgit v1.2.3 From 2d1a792357bf5ef7022f3ba82ab8976609425f4b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2016 09:11:46 -0800 Subject: Input: snvs_pwrkey - use __maybe_unused to hide pm functions The SNVS power key driver has suspend/resume functions that are accessed using SIMPLE_DEV_PM_OPS, which hide the reference when CONFIG_PM_SLEEP is not set, resulting in a warning about unused functions: drivers/input/keyboard/snvs_pwrkey.c:183:12: error: 'imx_snvs_pwrkey_suspend' defined but not used [-Werror=unused-function] drivers/input/keyboard/snvs_pwrkey.c:194:12: error: 'imx_snvs_pwrkey_resume' defined but not used [-Werror=unused-function] This adds __maybe_unused annotations to let the compiler know it can silently drop the function definition. Signed-off-by: Arnd Bergmann Acked-by: Frank Li Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/snvs_pwrkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 9adf13a5864a..b0ffadeb208c 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -180,7 +180,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return 0; } -static int imx_snvs_pwrkey_suspend(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); @@ -191,7 +191,7 @@ static int imx_snvs_pwrkey_suspend(struct device *dev) return 0; } -static int imx_snvs_pwrkey_resume(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); -- cgit v1.2.3 From 564a68de8e82b84697007e034a1b7d691effb60a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Feb 2016 11:37:49 -0800 Subject: Input: stmpe-ts - restore kerneldoc commit e4b88e19897f1039fd83f1630517becafc0dd163 "Input: stmpe-ts - enforce device tree only mode" removed platform data but accidentally stripped away useful kerneldoc, so reintroduce it. Signed-off-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index e414d43e5159..2a78e27b4495 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -63,6 +63,37 @@ #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff +/** + * struct stmpe_touch - stmpe811 touch screen controller state + * @stmpe: pointer back to STMPE MFD container + * @idev: registered input device + * @work: a work item used to scan the device + * @dev: a pointer back to the MFD cell struct device* + * @sample_time: ADC converstion time in number of clock. + * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, + * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks), + * recommended is 4. + * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) + * @ref_sel: ADC reference source + * (0 -> internal reference, 1 -> external reference) + * @adc_freq: ADC Clock speed + * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) + * @ave_ctrl: Sample average control + * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) + * @touch_det_delay: Touch detect interrupt delay + * (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us, + * 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) + * recommended is 3 + * @settling: Panel driver settling time + * (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms, + * 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) + * recommended is 2 + * @fraction_z: Length of the fractional part in z + * (fraction_z ([0..7]) = Count of the fractional part) + * recommended is 7 + * @i_drive: current limit value of the touchscreen drivers + * (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max) + */ struct stmpe_touch { struct stmpe *stmpe; struct input_dev *idev; -- cgit v1.2.3 From a9e340dce3c3bceeb42f6b6d33b7858822d76cb6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 17 Jan 2016 10:57:09 -0800 Subject: Input: rotary_encoder - move away from platform data structure Drop support for platform data passed via a C-structure and switch to device properties instead, which should make the driver compatible with all platforms: OF, ACPI and static boards. Static boards should use property sets to communicate device parameters to the driver. Signed-off-by: Dmitry Torokhov --- arch/arm/mach-pxa/raumfeld.c | 29 +++++-- drivers/input/misc/rotary_encoder.c | 146 ++++++++++++++---------------------- include/linux/rotary_encoder.h | 13 ---- 3 files changed, 76 insertions(+), 112 deletions(-) delete mode 100644 include/linux/rotary_encoder.h (limited to 'drivers/input') diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index bac88e0be58a..82689ac20ccf 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -18,13 +18,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -378,18 +378,27 @@ static struct gpiod_lookup_table raumfeld_rotary_gpios_table = { }, }; -static struct rotary_encoder_platform_data raumfeld_rotary_encoder_info = { - .steps = 24, - .axis = REL_X, - .relative_axis = 1, +static u32 raumfeld_rotary_encoder_steps = 24; +static u32 raumfeld_rotary_encoder_axis = REL_X; +static u32 raumfeld_rotary_encoder_relative_axis = 1; + +static struct property_entry raumfeld_rotary_properties[] = { + { "rotary-encoder,steps-per-period", + DEV_PROP_U32, 1, &raumfeld_rotary_encoder_steps, }, + { "linux,axis", + DEV_PROP_U32, 1, &raumfeld_rotary_encoder_axis, }, + { "rotary-encoder,relative_axis", + DEV_PROP_U32, 1, &raumfeld_rotary_encoder_relative_axis, }, + { NULL } +}; + +static struct property_set raumfeld_rotary_property_set = { + .properties = raumfeld_rotary_properties, }; static struct platform_device rotary_encoder_device = { .name = "rotary-encoder", .id = 0, - .dev = { - .platform_data = &raumfeld_rotary_encoder_info, - } }; /** @@ -1061,6 +1070,8 @@ static void __init raumfeld_controller_init(void) pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_controller_pin_config)); gpiod_add_lookup_table(&raumfeld_rotary_gpios_table); + device_add_property_set(&rotary_encoder_device.dev, + &raumfeld_rotary_property_set); platform_device_register(&rotary_encoder_device); spi_register_board_info(ARRAY_AND_SIZE(controller_spi_devices)); @@ -1099,6 +1110,8 @@ static void __init raumfeld_speaker_init(void) platform_device_register(&smc91x_device); gpiod_add_lookup_table(&raumfeld_rotary_gpios_table); + device_add_property_set(&rotary_encoder_device.dev, + &raumfeld_rotary_property_set); platform_device_register(&rotary_encoder_device); raumfeld_audio_init(); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 70fdcce84866..5a4e1d69c4af 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -21,20 +21,23 @@ #include #include #include -#include #include #include -#include #include +#include #define DRV_NAME "rotary-encoder" struct rotary_encoder { struct input_dev *input; - const struct rotary_encoder_platform_data *pdata; + struct mutex access_mutex; - unsigned int axis; + u32 steps; + u32 axis; + bool relative_axis; + bool rollover; + unsigned int pos; struct gpio_desc *gpio_a; @@ -59,31 +62,29 @@ static int rotary_encoder_get_state(struct rotary_encoder *encoder) static void rotary_encoder_report_event(struct rotary_encoder *encoder) { - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - if (pdata->relative_axis) { + if (encoder->relative_axis) { input_report_rel(encoder->input, - pdata->axis, encoder->dir ? -1 : 1); + encoder->axis, encoder->dir ? -1 : 1); } else { unsigned int pos = encoder->pos; if (encoder->dir) { /* turning counter-clockwise */ - if (pdata->rollover) - pos += pdata->steps; + if (encoder->rollover) + pos += encoder->steps; if (pos) pos--; } else { /* turning clockwise */ - if (pdata->rollover || pos < pdata->steps) + if (encoder->rollover || pos < encoder->steps) pos++; } - if (pdata->rollover) - pos %= pdata->steps; + if (encoder->rollover) + pos %= encoder->steps; encoder->pos = pos; - input_report_abs(encoder->input, pdata->axis, encoder->pos); + input_report_abs(encoder->input, encoder->axis, encoder->pos); } input_sync(encoder->input); @@ -204,90 +205,43 @@ out: return IRQ_HANDLED; } -#ifdef CONFIG_OF -static const struct of_device_id rotary_encoder_of_match[] = { - { .compatible = "rotary-encoder", }, - { }, -}; -MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); - -static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev) -{ - const struct of_device_id *of_id = - of_match_device(rotary_encoder_of_match, dev); - struct device_node *np = dev->of_node; - struct rotary_encoder_platform_data *pdata; - int error; - - if (!of_id || !np) - return NULL; - - pdata = devm_kzalloc(dev, sizeof(struct rotary_encoder_platform_data), - GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); - of_property_read_u32(np, "linux,axis", &pdata->axis); - - pdata->relative_axis = - of_property_read_bool(np, "rotary-encoder,relative-axis"); - pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); - - error = of_property_read_u32(np, "rotary-encoder,steps-per-period", - &pdata->steps_per_period); - if (error) { - /* - * The 'half-period' property has been deprecated, you must use - * 'steps-per-period' and set an appropriate value, but we still - * need to parse it to maintain compatibility. - */ - if (of_property_read_bool(np, "rotary-encoder,half-period")) { - pdata->steps_per_period = 2; - } else { - /* Fallback to one step per period behavior */ - pdata->steps_per_period = 1; - } - } - - pdata->wakeup_source = of_property_read_bool(np, "wakeup-source"); - - return pdata; -} -#else -static inline struct rotary_encoder_platform_data * -rotary_encoder_parse_dt(struct device *dev) -{ - return NULL; -} -#endif - static int rotary_encoder_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); struct rotary_encoder *encoder; struct input_dev *input; irq_handler_t handler; + u32 steps_per_period; int err; - if (!pdata) { - pdata = rotary_encoder_parse_dt(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - if (!pdata) { - dev_err(dev, "missing platform data\n"); - return -EINVAL; - } - } - encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); if (!encoder) return -ENOMEM; mutex_init(&encoder->access_mutex); - encoder->pdata = pdata; + + device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps); + + err = device_property_read_u32(dev, "rotary-encoder,steps-per-period", + &steps_per_period); + if (err) { + /* + * The 'half-period' property has been deprecated, you must + * use 'steps-per-period' and set an appropriate value, but + * we still need to parse it to maintain compatibility. If + * neither property is present we fall back to the one step + * per period behavior. + */ + steps_per_period = device_property_read_bool(dev, + "rotary-encoder,half-period") ? 2 : 1; + } + + encoder->rollover = + device_property_read_bool(dev, "rotary-encoder,rollover"); + + device_property_read_u32(dev, "linux,axis", &encoder->axis); + encoder->relative_axis = + device_property_read_bool(dev, "rotary-encoder,relative-axis"); encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN); if (IS_ERR(encoder->gpio_a)) { @@ -317,12 +271,13 @@ static int rotary_encoder_probe(struct platform_device *pdev) input->id.bustype = BUS_HOST; input->dev.parent = dev; - if (pdata->relative_axis) - input_set_capability(input, EV_REL, pdata->axis); + if (encoder->relative_axis) + input_set_capability(input, EV_REL, encoder->axis); else - input_set_abs_params(input, pdata->axis, 0, pdata->steps, 0, 1); + input_set_abs_params(input, + encoder->axis, 0, encoder->steps, 0, 1); - switch (pdata->steps_per_period) { + switch (steps_per_period) { case 4: handler = &rotary_encoder_quarter_period_irq; encoder->last_stable = rotary_encoder_get_state(encoder); @@ -336,7 +291,7 @@ static int rotary_encoder_probe(struct platform_device *pdev) break; default: dev_err(dev, "'%d' is not a valid steps-per-period value\n", - pdata->steps_per_period); + steps_per_period); return -EINVAL; } @@ -364,7 +319,8 @@ static int rotary_encoder_probe(struct platform_device *pdev) return err; } - device_init_wakeup(&pdev->dev, pdata->wakeup_source); + device_init_wakeup(dev, + device_property_read_bool(dev, "wakeup-source")); platform_set_drvdata(pdev, encoder); @@ -398,6 +354,14 @@ static int __maybe_unused rotary_encoder_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, rotary_encoder_suspend, rotary_encoder_resume); +#ifdef CONFIG_OF +static const struct of_device_id rotary_encoder_of_match[] = { + { .compatible = "rotary-encoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); +#endif + static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, .driver = { diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h deleted file mode 100644 index 4536c813a1e9..000000000000 --- a/include/linux/rotary_encoder.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __ROTARY_ENCODER_H__ -#define __ROTARY_ENCODER_H__ - -struct rotary_encoder_platform_data { - unsigned int steps; - unsigned int axis; - unsigned int steps_per_period; - bool relative_axis; - bool rollover; - bool wakeup_source; -}; - -#endif /* __ROTARY_ENCODER_H__ */ -- cgit v1.2.3 From 7dde4e74744772efdc85d7ed13495c7b6a0d881b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 1 Mar 2016 18:25:26 -0800 Subject: Input: rotary-encoder - support more than 2 gpios as input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes how the used gpios are stored (i.e. a struct gpio_descs instead of two struct gpio_desc) and as with >2 gpios the states are numbered differently the function rotary_encoder_get_state returns unencoded numbers instead of grey encoded numbers before. The latter has some implications on how the returned value is used and so the change is bigger than one might expect at first. Signed-off-by: Uwe Kleine-König Acked-by: Rob Herring Acked-by: Daniel Mack Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/rotary-encoder.txt | 2 +- drivers/input/misc/rotary_encoder.c | 156 +++++++++------------ 2 files changed, 65 insertions(+), 93 deletions(-) (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/rotary-encoder.txt b/Documentation/devicetree/bindings/input/rotary-encoder.txt index de99cbbbf6da..6c9f0c8a846c 100644 --- a/Documentation/devicetree/bindings/input/rotary-encoder.txt +++ b/Documentation/devicetree/bindings/input/rotary-encoder.txt @@ -1,7 +1,7 @@ Rotary encoder DT bindings Required properties: -- gpios: a spec for two GPIOs to be used +- gpios: a spec for at least two GPIOs to be used, most significant first Optional properties: - linux,axis: the input subsystem axis to map to this rotary encoder. diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 5a4e1d69c4af..96c486de49e0 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -40,35 +40,42 @@ struct rotary_encoder { unsigned int pos; - struct gpio_desc *gpio_a; - struct gpio_desc *gpio_b; + struct gpio_descs *gpios; - unsigned int irq_a; - unsigned int irq_b; + unsigned int *irq; bool armed; - unsigned char dir; /* 0 - clockwise, 1 - CCW */ + signed char dir; /* 1 - clockwise, -1 - CCW */ - char last_stable; + unsigned last_stable; }; -static int rotary_encoder_get_state(struct rotary_encoder *encoder) +static unsigned rotary_encoder_get_state(struct rotary_encoder *encoder) { - int a = !!gpiod_get_value_cansleep(encoder->gpio_a); - int b = !!gpiod_get_value_cansleep(encoder->gpio_b); + int i; + unsigned ret = 0; - return ((a << 1) | b); + for (i = 0; i < encoder->gpios->ndescs; ++i) { + int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]); + /* convert from gray encoding to normal */ + if (ret & 1) + val = !val; + + ret = ret << 1 | val; + } + + return ret & 3; } static void rotary_encoder_report_event(struct rotary_encoder *encoder) { if (encoder->relative_axis) { input_report_rel(encoder->input, - encoder->axis, encoder->dir ? -1 : 1); + encoder->axis, encoder->dir); } else { unsigned int pos = encoder->pos; - if (encoder->dir) { + if (encoder->dir < 0) { /* turning counter-clockwise */ if (encoder->rollover) pos += encoder->steps; @@ -93,7 +100,7 @@ static void rotary_encoder_report_event(struct rotary_encoder *encoder) static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned state; mutex_lock(&encoder->access_mutex); @@ -108,12 +115,12 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; case 0x1: - case 0x2: + case 0x3: if (encoder->armed) - encoder->dir = state - 1; + encoder->dir = 2 - state; break; - case 0x3: + case 0x2: encoder->armed = true; break; } @@ -126,25 +133,19 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned int state; mutex_lock(&encoder->access_mutex); state = rotary_encoder_get_state(encoder); - switch (state) { - case 0x00: - case 0x03: + if (state & 1) { + encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1; + } else { if (state != encoder->last_stable) { rotary_encoder_report_event(encoder); encoder->last_stable = state; } - break; - - case 0x01: - case 0x02: - encoder->dir = (encoder->last_stable + state) & 0x01; - break; } mutex_unlock(&encoder->access_mutex); @@ -155,46 +156,18 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - unsigned char sum; - int state; + unsigned int state; mutex_lock(&encoder->access_mutex); state = rotary_encoder_get_state(encoder); - /* - * We encode the previous and the current state using a byte. - * The previous state in the MSB nibble, the current state in the LSB - * nibble. Then use a table to decide the direction of the turn. - */ - sum = (encoder->last_stable << 4) + state; - switch (sum) { - case 0x31: - case 0x10: - case 0x02: - case 0x23: - encoder->dir = 0; /* clockwise */ - break; - - case 0x13: - case 0x01: - case 0x20: - case 0x32: - encoder->dir = 1; /* counter-clockwise */ - break; - - default: - /* - * Ignore all other values. This covers the case when the - * state didn't change (a spurious interrupt) and the - * cases where the state changed by two steps, making it - * impossible to tell the direction. - * - * In either case, don't report any event and save the - * state for later. - */ + if ((encoder->last_stable + 1) % 4 == state) + encoder->dir = 1; + else if (encoder->last_stable == (state + 1) % 4) + encoder->dir = -1; + else goto out; - } rotary_encoder_report_event(encoder); @@ -212,6 +185,7 @@ static int rotary_encoder_probe(struct platform_device *pdev) struct input_dev *input; irq_handler_t handler; u32 steps_per_period; + unsigned int i; int err; encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); @@ -243,24 +217,16 @@ static int rotary_encoder_probe(struct platform_device *pdev) encoder->relative_axis = device_property_read_bool(dev, "rotary-encoder,relative-axis"); - encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN); - if (IS_ERR(encoder->gpio_a)) { - err = PTR_ERR(encoder->gpio_a); - dev_err(dev, "unable to get GPIO at index 0: %d\n", err); - return err; + 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); } - - encoder->irq_a = gpiod_to_irq(encoder->gpio_a); - - encoder->gpio_b = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN); - if (IS_ERR(encoder->gpio_b)) { - err = PTR_ERR(encoder->gpio_b); - dev_err(dev, "unable to get GPIO at index 1: %d\n", err); - return err; + if (encoder->gpios->ndescs < 2) { + dev_err(dev, "not enough gpios found\n"); + return -EINVAL; } - encoder->irq_b = gpiod_to_irq(encoder->gpio_b); - input = devm_input_allocate_device(dev); if (!input) return -ENOMEM; @@ -277,7 +243,7 @@ static int rotary_encoder_probe(struct platform_device *pdev) input_set_abs_params(input, encoder->axis, 0, encoder->steps, 0, 1); - switch (steps_per_period) { + switch (steps_per_period >> (encoder->gpios->ndescs - 2)) { case 4: handler = &rotary_encoder_quarter_period_irq; encoder->last_stable = rotary_encoder_get_state(encoder); @@ -295,22 +261,26 @@ static int rotary_encoder_probe(struct platform_device *pdev) return -EINVAL; } - err = devm_request_threaded_irq(dev, encoder->irq_a, NULL, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); - return err; - } + encoder->irq = + devm_kzalloc(dev, + sizeof(*encoder->irq) * encoder->gpios->ndescs, + GFP_KERNEL); + if (!encoder->irq) + return -ENOMEM; - err = devm_request_threaded_irq(dev, encoder->irq_b, NULL, handler, + for (i = 0; i < encoder->gpios->ndescs; ++i) { + encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]); + + err = devm_request_threaded_irq(dev, encoder->irq[i], + NULL, handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); - return err; + if (err) { + dev_err(dev, "unable to request IRQ %d (gpio#%d)\n", + encoder->irq[i], i); + return err; + } } err = input_register_device(input); @@ -330,10 +300,11 @@ static int rotary_encoder_probe(struct platform_device *pdev) static int __maybe_unused rotary_encoder_suspend(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - enable_irq_wake(encoder->irq_a); - enable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + enable_irq_wake(encoder->irq[i]); } return 0; @@ -342,10 +313,11 @@ static int __maybe_unused rotary_encoder_suspend(struct device *dev) static int __maybe_unused rotary_encoder_resume(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - disable_irq_wake(encoder->irq_a); - disable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + disable_irq_wake(encoder->irq[i]); } return 0; -- cgit v1.2.3 From 7a3ed686f9f16d10202eefd00afbd607782da2e4 Mon Sep 17 00:00:00 2001 From: Sangwon Jee Date: Fri, 4 Mar 2016 11:13:37 -0800 Subject: Input: melfas_mip4 - add resolution reporting Add support for retrieving device resolution (pixels per mm) from firmware and using it when setting up input device. Signed-off-by: Sangwon Jee Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/melfas_mip4.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index b8e9ead61ee2..892729734c51 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -33,7 +33,7 @@ /***************************************************************** * Protocol - * Version : MIP 4.0 Rev 4.4 + * Version : MIP 4.0 Rev 4.6 *****************************************************************/ /* Address */ @@ -52,6 +52,11 @@ #define MIP4_R1_INFO_NODE_NUM_X 0x14 #define MIP4_R1_INFO_NODE_NUM_Y 0x15 #define MIP4_R1_INFO_KEY_NUM 0x16 +#define MIP4_R1_INFO_PRESSURE_NUM 0x17 +#define MIP4_R1_INFO_LENGTH_X 0x18 +#define MIP4_R1_INFO_LENGTH_Y 0x1A +#define MIP4_R1_INFO_PPM_X 0x1C +#define MIP4_R1_INFO_PPM_Y 0x1D #define MIP4_R1_INFO_VERSION_BOOT 0x20 #define MIP4_R1_INFO_VERSION_CORE 0x22 #define MIP4_R1_INFO_VERSION_APP 0x24 @@ -158,6 +163,8 @@ struct mip4_ts { u8 node_x; u8 node_y; u8 node_key; + unsigned int ppm_x; + unsigned int ppm_y; struct mip4_fw_version fw_version; @@ -242,7 +249,7 @@ static int mip4_query_device(struct mip4_ts *ts) { int error; u8 cmd[2]; - u8 buf[8]; + u8 buf[14]; /* Product name */ cmd[0] = MIP4_R0_INFO; @@ -269,7 +276,7 @@ static int mip4_query_device(struct mip4_ts *ts) /* Resolution */ cmd[0] = MIP4_R0_INFO; cmd[1] = MIP4_R1_INFO_RESOLUTION_X; - error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7); + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14); if (error) { dev_warn(&ts->client->dev, "Failed to retrieve touchscreen parameters: %d\n", @@ -287,6 +294,11 @@ static int mip4_query_device(struct mip4_ts *ts) "node_x: %d, node_y: %d, node_key: %d\n", ts->node_x, ts->node_y, ts->node_key); + ts->ppm_x = buf[12]; + ts->ppm_y = buf[13]; + dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n", + ts->ppm_x, ts->ppm_y); + /* Key ts */ if (ts->node_key > 0) ts->key_num = ts->node_key; @@ -1095,6 +1107,10 @@ exit_bl: input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0); input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); + input_abs_set_res(ts->input, ABS_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_Y, ts->ppm_y); return error ? error : 0; } @@ -1377,6 +1393,8 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id) MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT); if (error) @@ -1494,6 +1512,6 @@ static struct i2c_driver mip4_driver = { module_i2c_driver(mip4_driver); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); -MODULE_VERSION("2016.01.13"); +MODULE_VERSION("2016.03.03"); MODULE_AUTHOR("Sangwon Jee "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 5186b8c41a89fd071f8d036b41ec4b68fbcf2e4d Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 4 Mar 2016 11:21:18 -0800 Subject: Input: sh_keysc - remove dependency on SUPERH A dependency on ARCH_SHMOBILE seems to be the best option for sh_keysc: * For Super H based SoCs: sh_keysc is used on SH_MIGOR, SH_ECOVEC, SH_KFR2R09, SH_7722_SOLUTION_ENGINE, and SH_7724_SOLUTION_ENGINE, which depend on either CPU_SUBTYPE_SH7722 or CPU_SUBTYPE_SH7724, and both select ARCH_SHMOBILE. * For ARM Based SoCs: Since the removal of legacy (non-multiplatform) support this driver has not been used by any Renesas ARM based SoCs. The Renesas ARM based SoCs currently select ARCH_SHMOBILE, however, it is planned that this will no longer be the case. This is part of an ongoing process to migrate from ARCH_SHMOBILE to ARCH_RENESAS the motivation for which being that RENESAS seems to be a more appropriate name than SHMOBILE for the majority of Renesas ARM based SoCs. Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ddd8148d51d7..509608c95994 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -560,7 +560,7 @@ config KEYBOARD_SUNKBD config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" - depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_SHMOBILE || COMPILE_TEST help Say Y here if you want to use a keypad attached to the KEYSC block on SuperH processors such as sh7722 and sh7343. -- cgit v1.2.3 From 3cd47869431d7402d0613cf0f7fbb392f2b97565 Mon Sep 17 00:00:00 2001 From: Dudley Du Date: Fri, 4 Mar 2016 11:23:09 -0800 Subject: Input: cyapa - fix for losing events during device power transitions When changing the scan rate as part of runtime-resume process we may lose some of the events, because: 1) for gen3 trackpads, the driver must msleep() some time to ensure that the device is ready to accept next command; 2) for gen5 and later trackpads, the queue dumping function will simply ignore the events when waiting for the set power mode command response. The solution is to keep polling and report those valid events when the set power mode command is in progress. Signed-off-by: Dudley Du Tested-by: Jeremiah Mahler Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/cyapa.c | 22 ++++---- drivers/input/mouse/cyapa.h | 14 ++++- drivers/input/mouse/cyapa_gen3.c | 108 ++++++++++++++++++++++++++++----------- drivers/input/mouse/cyapa_gen5.c | 99 +++++++++++++++++++++++++++++------ drivers/input/mouse/cyapa_gen6.c | 4 +- 5 files changed, 188 insertions(+), 59 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index eb76b61418f3..dc2394292088 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -383,7 +383,7 @@ static int cyapa_open(struct input_dev *input) * when in operational mode. */ error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) { dev_warn(dev, "set active power failed: %d\n", error); goto out; @@ -424,7 +424,8 @@ static void cyapa_close(struct input_dev *input) pm_runtime_set_suspended(dev); if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); mutex_unlock(&cyapa->state_sync_lock); } @@ -534,7 +535,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) */ if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); /* Gen3 always using polling mode for command. */ if (cyapa->gen >= CYAPA_GEN5) enable_irq(cyapa->client->irq); @@ -550,7 +551,7 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) disable_irq(cyapa->client->irq); if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); } } @@ -617,7 +618,8 @@ static int cyapa_initialize(struct cyapa *cyapa) /* Power down the device until we need it. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); return 0; } @@ -634,7 +636,7 @@ static int cyapa_reinitialize(struct cyapa *cyapa) /* Avoid command failures when TP was in OFF state. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); error = cyapa_detect(cyapa); if (error) @@ -654,7 +656,7 @@ out: /* Reset to power OFF state to save power when no user open. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); } else if (!error && cyapa->operational) { /* * Make sure only enable runtime PM when device is @@ -1392,7 +1394,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev) power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode : PWR_MODE_OFF; error = cyapa->ops->set_power_mode(cyapa, power_mode, - cyapa->suspend_sleep_time, true); + cyapa->suspend_sleep_time, CYAPA_PM_SUSPEND); if (error) dev_err(dev, "suspend set power mode failed: %d\n", error); @@ -1447,7 +1449,7 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev) error = cyapa->ops->set_power_mode(cyapa, cyapa->runtime_suspend_power_mode, cyapa->runtime_suspend_sleep_time, - false); + CYAPA_PM_RUNTIME_SUSPEND); if (error) dev_warn(dev, "runtime suspend failed: %d\n", error); @@ -1460,7 +1462,7 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev) int error; error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_RUNTIME_RESUME); if (error) dev_warn(dev, "runtime resume failed: %d\n", error); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index b812bba8cdd7..ce951fe4516a 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -250,6 +250,15 @@ struct cyapa; typedef bool (*cb_sort)(struct cyapa *, u8 *, int); +enum cyapa_pm_stage { + CYAPA_PM_DEACTIVE, + CYAPA_PM_ACTIVE, + CYAPA_PM_SUSPEND, + CYAPA_PM_RESUME, + CYAPA_PM_RUNTIME_SUSPEND, + CYAPA_PM_RUNTIME_RESUME, +}; + struct cyapa_dev_ops { int (*check_fw)(struct cyapa *, const struct firmware *); int (*bl_enter)(struct cyapa *); @@ -273,7 +282,7 @@ struct cyapa_dev_ops { int (*sort_empty_output_data)(struct cyapa *, u8 *, int *, cb_sort); - int (*set_power_mode)(struct cyapa *, u8, u16, bool); + int (*set_power_mode)(struct cyapa *, u8, u16, enum cyapa_pm_stage); int (*set_proximity)(struct cyapa *, bool); }; @@ -289,6 +298,9 @@ struct cyapa_pip_cmd_states { u8 *resp_data; int *resp_len; + enum cyapa_pm_stage pm_stage; + struct mutex pm_stage_lock; + u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE]; u8 empty_buf[CYAPA_REG_MAP_SIZE]; }; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 1a9d12ae7538..f9600753eca5 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -269,6 +269,7 @@ static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { { CYAPA_SMBUS_MIN_BASELINE, 1 }, /* CYAPA_CMD_MIN_BASELINE */ }; +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa); /* * cyapa_smbus_read_block - perform smbus block read command @@ -950,12 +951,14 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused, bool is_suspend_unused) + u16 always_unused, enum cyapa_pm_stage pm_stage) { - int ret; + struct input_dev *input = cyapa->input; u8 power; int tries; - u16 sleep_time; + int sleep_time; + int interval; + int ret; if (cyapa->state != CYAPA_STATE_OP) return 0; @@ -977,7 +980,7 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, if ((ret & PWR_MODE_MASK) == power_mode) return 0; - sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); + sleep_time = (int)cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); power = ret; power &= ~PWR_MODE_MASK; power |= power_mode & PWR_MODE_MASK; @@ -995,7 +998,23 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, * doing so before issuing the next command may result in errors * depending on the command's content. */ - msleep(sleep_time); + if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_SUSPEND || + pm_stage == CYAPA_PM_RUNTIME_RESUME)) { + /* Try to polling in 120Hz, read may fail, just ignore it. */ + interval = 1000 / 120; + while (sleep_time > 0) { + if (sleep_time > interval) + msleep(interval); + else + msleep(sleep_time); + sleep_time -= interval; + cyapa_gen3_try_poll_handler(cyapa); + } + } else { + msleep(sleep_time); + } + return ret; } @@ -1112,7 +1131,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) * may cause problems, so we set the power mode first here. */ error = cyapa_gen3_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_err(dev, "%s: set full power mode failed: %d\n", __func__, error); @@ -1168,32 +1187,16 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa) return false; } -static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +static int cyapa_gen3_event_process(struct cyapa *cyapa, + struct cyapa_reg_data *data) { struct input_dev *input = cyapa->input; - struct device *dev = &cyapa->client->dev; - struct cyapa_reg_data data; int num_fingers; - int ret; int i; - ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); - if (ret != sizeof(data)) { - dev_err(dev, "failed to read report data, (%d)\n", ret); - return -EINVAL; - } - - if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || - (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || - (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { - dev_err(dev, "invalid device state bytes, %02x %02x\n", - data.device_status, data.finger_btn); - return -EINVAL; - } - - num_fingers = (data.finger_btn >> 4) & 0x0f; + num_fingers = (data->finger_btn >> 4) & 0x0f; for (i = 0; i < num_fingers; i++) { - const struct cyapa_touch *touch = &data.touches[i]; + const struct cyapa_touch *touch = &data->touches[i]; /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ int slot = touch->id - 1; @@ -1210,18 +1213,65 @@ static int cyapa_gen3_irq_handler(struct cyapa *cyapa) if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) input_report_key(input, BTN_LEFT, - !!(data.finger_btn & OP_DATA_LEFT_BTN)); + !!(data->finger_btn & OP_DATA_LEFT_BTN)); if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) input_report_key(input, BTN_MIDDLE, - !!(data.finger_btn & OP_DATA_MIDDLE_BTN)); + !!(data->finger_btn & OP_DATA_MIDDLE_BTN)); if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) input_report_key(input, BTN_RIGHT, - !!(data.finger_btn & OP_DATA_RIGHT_BTN)); + !!(data->finger_btn & OP_DATA_RIGHT_BTN)); input_sync(input); return 0; } +static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) { + dev_err(dev, "failed to read report data, (%d)\n", ret); + return -EINVAL; + } + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + dev_err(dev, "invalid device state bytes: %02x %02x\n", + data.device_status, data.finger_btn); + return -EINVAL; + } + + return cyapa_gen3_event_process(cyapa, &data); +} + +/* + * This function will be called in the cyapa_gen3_set_power_mode function, + * and it's known that it may failed in some situation after the set power + * mode command was sent. So this function is aimed to avoid the knwon + * and unwanted output I2C and data parse error messages. + */ +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa) +{ + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) + return -EINVAL; + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) + return -EINVAL; + + return cyapa_gen3_event_process(cyapa, &data); + +} + static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; } static int cyapa_gen3_bl_initiate(struct cyapa *cyapa, const struct firmware *fw) { return 0; } diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index 118ba977181e..5775d40b3d53 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -342,6 +342,9 @@ u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0x5a }; +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data); + int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) { struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; @@ -350,6 +353,9 @@ int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) atomic_set(&pip->cmd_issued, 0); mutex_init(&pip->cmd_lock); + mutex_init(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + pip->resp_sort_func = NULL; pip->in_progress_cmd = PIP_INVALID_CMD; pip->resp_data = NULL; @@ -397,6 +403,38 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) return 0; } +static void cyapa_set_pip_pm_state(struct cyapa *cyapa, + enum cyapa_pm_stage pm_stage) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = pm_stage; + mutex_unlock(&pip->pm_stage_lock); +} + +static void cyapa_reset_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + /* Indicates the pip->pm_stage is not valid. */ + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + mutex_unlock(&pip->pm_stage_lock); +} + +static enum cyapa_pm_stage cyapa_get_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage; + + mutex_lock(&pip->pm_stage_lock); + pm_stage = pip->pm_stage; + mutex_unlock(&pip->pm_stage_lock); + + return pm_stage; +} + /** * This function is aimed to dump all not read data in Gen5 trackpad * before send any command, otherwise, the interrupt line will be blocked. @@ -404,7 +442,9 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) int cyapa_empty_pip_output_data(struct cyapa *cyapa, u8 *buf, int *len, cb_sort func) { + struct input_dev *input = cyapa->input; struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage = cyapa_get_pip_pm_state(cyapa); int length; int report_count; int empty_count; @@ -478,6 +518,12 @@ int cyapa_empty_pip_output_data(struct cyapa *cyapa, *len = length; /* Response found, success. */ return 0; + } else if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_RESUME || + pm_stage == CYAPA_PM_RUNTIME_SUSPEND)) { + /* Parse the data and report it if it's valid. */ + cyapa_pip_event_process(cyapa, + (struct cyapa_pip_report_data *)pip->empty_buf); } error = -EINVAL; @@ -1566,15 +1612,17 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; u8 power_state; - int error; + int error = 0; if (cyapa->state != CYAPA_STATE_GEN5_APP) return 0; + cyapa_set_pip_pm_state(cyapa, pm_stage); + if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { /* * Assume TP in deep sleep mode when driver is loaded, @@ -1597,7 +1645,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, power_mode == PWR_MODE_BTN_ONLY || PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { /* Has in correct power mode state, early return. */ - return 0; + goto out; } } @@ -1605,11 +1653,11 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); if (error) { dev_err(dev, "enter deep sleep fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); - return 0; + goto out; } /* @@ -1621,7 +1669,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); if (error) { dev_err(dev, "deep sleep wake fail: %d\n", error); - return error; + goto out; } } @@ -1630,7 +1678,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_ACTIVE); if (error) { dev_err(dev, "change to active fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); @@ -1639,7 +1687,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_BTN_ONLY); if (error) { dev_err(dev, "fail to button only mode: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); @@ -1664,7 +1712,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, if (error) { dev_err(dev, "set power state to 0x%02x failed: %d\n", power_state, error); - return error; + goto out; } /* @@ -1677,14 +1725,16 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * is suspending which may cause interrupt line unable to be * asserted again. */ - if (is_suspend) + if (pm_stage == CYAPA_PM_SUSPEND) cyapa_gen5_disable_pip_report(cyapa); PIP_DEV_SET_PWR_STATE(cyapa, cyapa_sleep_time_to_pwr_cmd(sleep_time)); } - return 0; +out: + cyapa_reset_pip_pm_state(cyapa); + return error; } int cyapa_pip_resume_scanning(struct cyapa *cyapa) @@ -2513,7 +2563,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen5_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); @@ -2715,7 +2765,6 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) struct device *dev = &cyapa->client->dev; struct cyapa_pip_report_data report_data; unsigned int report_len; - u8 report_id; int ret; if (!cyapa_is_pip_app_mode(cyapa)) { @@ -2752,7 +2801,23 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) return -EINVAL; } - report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET]; + return cyapa_pip_event_process(cyapa, &report_data); +} + +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data) +{ + struct device *dev = &cyapa->client->dev; + unsigned int report_len; + u8 report_id; + + report_len = get_unaligned_le16( + &report_data->report_head[PIP_RESP_LENGTH_OFFSET]); + /* Idle, no data for report. */ + if (report_len == PIP_RESP_LENGTH_SIZE) + return 0; + + report_id = report_data->report_head[PIP_RESP_REPORT_ID_OFFSET]; if (report_id == PIP_WAKEUP_EVENT_REPORT_ID && report_len == PIP_WAKEUP_EVENT_SIZE) { /* @@ -2805,11 +2870,11 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) } if (report_id == PIP_TOUCH_REPORT_ID) - cyapa_pip_report_touches(cyapa, &report_data); + cyapa_pip_report_touches(cyapa, report_data); else if (report_id == PIP_PROXIMITY_REPORT_ID) - cyapa_pip_report_proximity(cyapa, &report_data); + cyapa_pip_report_proximity(cyapa, report_data); else - cyapa_pip_report_buttons(cyapa, &report_data); + cyapa_pip_report_buttons(cyapa, report_data); return 0; } diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index e4eb048d1bf6..016397850b1b 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -425,7 +425,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; struct gen6_interval_setting *interval_setting = @@ -689,7 +689,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen6_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); -- cgit v1.2.3 From cd1480ae4dd0bbfd3d7339d00c3573e4c64ebbd5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 4 Mar 2016 11:41:43 -0800 Subject: Input: ts4800 - add hardware dependency The Technologic Systems TS-4800 is an i.MX515 board, so its drivers are useless unless building a SOC_IMX51 kernel, except for build testing purposes. Signed-off-by: Jean Delvare Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 309951da0970..c7afa57f6287 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -952,6 +952,7 @@ config TOUCHSCREEN_TOUCHIT213 config TOUCHSCREEN_TS4800 tristate "TS-4800 touchscreen" depends on HAS_IOMEM && OF + depends on SOC_IMX51 || COMPILE_TEST select MFD_SYSCON select INPUT_POLLDEV help -- cgit v1.2.3 From 4774f400935fbb44a25f5e8fd5c5583431e28ac1 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 8 Mar 2016 10:34:04 -0800 Subject: Input: ad7879 - move header to platform_data directory The header file is used by the SPI and I2C variant of the driver. Therefore, move it to a more generic place under platform_data. Signed-off-by: Stefan Agner Signed-off-by: Dmitry Torokhov --- arch/blackfin/mach-bf527/boards/ezbrd.c | 2 +- arch/blackfin/mach-bf527/boards/ezkit.c | 2 +- arch/blackfin/mach-bf527/boards/tll6527m.c | 2 +- arch/blackfin/mach-bf537/boards/stamp.c | 2 +- arch/blackfin/mach-bf538/boards/ezkit.c | 2 +- drivers/input/touchscreen/ad7879.c | 10 ++++---- include/linux/platform_data/ad7879.h | 41 ++++++++++++++++++++++++++++++ include/linux/spi/ad7879.h | 41 ------------------------------ 8 files changed, 51 insertions(+), 51 deletions(-) create mode 100644 include/linux/platform_data/ad7879.h delete mode 100644 include/linux/spi/ad7879.h (limited to 'drivers/input') diff --git a/arch/blackfin/mach-bf527/boards/ezbrd.c b/arch/blackfin/mach-bf527/boards/ezbrd.c index a3a572352769..80bcfd1d023e 100644 --- a/arch/blackfin/mach-bf527/boards/ezbrd.c +++ b/arch/blackfin/mach-bf527/boards/ezbrd.c @@ -279,7 +279,7 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_AD7879) -#include +#include static const struct ad7879_platform_data bfin_ad7879_ts_info = { .model = 7879, /* Model = AD7879 */ .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */ diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index d4219e8e5ab8..571edfd2ecf3 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -477,7 +477,7 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_AD7879) -#include +#include static const struct ad7879_platform_data bfin_ad7879_ts_info = { .model = 7879, /* Model = AD7879 */ .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */ diff --git a/arch/blackfin/mach-bf527/boards/tll6527m.c b/arch/blackfin/mach-bf527/boards/tll6527m.c index a0f5856a5ff8..c1acce4c2e45 100644 --- a/arch/blackfin/mach-bf527/boards/tll6527m.c +++ b/arch/blackfin/mach-bf527/boards/tll6527m.c @@ -29,7 +29,7 @@ #include #if IS_ENABLED(CONFIG_TOUCHSCREEN_AD7879) -#include +#include #define LCD_BACKLIGHT_GPIO 0x40 /* TLL6527M uses TLL7UIQ35 / ADI LCD EZ Extender. AD7879 AUX GPIO is used for * LCD Backlight Enable diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index 88a19fc9844d..cb1b6a9ee4f2 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -776,7 +776,7 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_AD7879) -#include +#include static const struct ad7879_platform_data bfin_ad7879_ts_info = { .model = 7879, /* Model = AD7879 */ .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */ diff --git a/arch/blackfin/mach-bf538/boards/ezkit.c b/arch/blackfin/mach-bf538/boards/ezkit.c index ae2fcbb00119..4a03c4465dbe 100644 --- a/arch/blackfin/mach-bf538/boards/ezkit.c +++ b/arch/blackfin/mach-bf538/boards/ezkit.c @@ -521,7 +521,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = { #endif /* CONFIG_SPI_BFIN5XX */ #if IS_ENABLED(CONFIG_TOUCHSCREEN_AD7879) -#include +#include static const struct ad7879_platform_data bfin_ad7879_ts_info = { .model = 7879, /* Model = AD7879 */ .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */ diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index fec66ad80513..b32dbf389777 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include "ad7879.h" @@ -170,10 +170,10 @@ static int ad7879_report(struct ad7879 *ts) * filter. The combination of these two techniques provides a robust * solution, discarding the spurious noise in the signal and keeping * only the data of interest. The size of both filters is - * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other - * user-programmable conversion controls include variable acquisition - * time, and first conversion delay. Up to 16 averages can be taken - * per conversion. + * programmable. (dev.platform_data, see linux/platform_data/ad7879.h) + * Other user-programmable conversion controls include variable + * acquisition time, and first conversion delay. Up to 16 averages can + * be taken per conversion. */ if (likely(x && z1)) { diff --git a/include/linux/platform_data/ad7879.h b/include/linux/platform_data/ad7879.h new file mode 100644 index 000000000000..69e2e1fd2bc8 --- /dev/null +++ b/include/linux/platform_data/ad7879.h @@ -0,0 +1,41 @@ +/* linux/platform_data/ad7879.h */ + +/* Touchscreen characteristics vary between boards and models. The + * platform_data for the device's "struct device" holds this information. + * + * It's OK if the min/max values are zero. + */ +struct ad7879_platform_data { + u16 model; /* 7879 */ + u16 x_plate_ohms; + u16 x_min, x_max; + u16 y_min, y_max; + u16 pressure_min, pressure_max; + + bool swap_xy; /* swap x and y axes */ + + /* [0..255] 0=OFF Starts at 1=550us and goes + * all the way to 9.440ms in steps of 35us. + */ + u8 pen_down_acc_interval; + /* [0..15] Starts at 0=128us and goes all the + * way to 4.096ms in steps of 128us. + */ + u8 first_conversion_delay; + /* [0..3] 0 = 2us, 1 = 4us, 2 = 8us, 3 = 16us */ + u8 acquisition_time; + /* [0..3] Average X middle samples 0 = 2, 1 = 4, 2 = 8, 3 = 16 */ + u8 averaging; + /* [0..3] Perform X measurements 0 = OFF, + * 1 = 4, 2 = 8, 3 = 16 (median > averaging) + */ + u8 median; + /* 1 = AUX/VBAT/GPIO export GPIO to gpiolib + * requires CONFIG_GPIOLIB + */ + bool gpio_export; + /* identifies the first GPIO number handled by this chip; + * or, if negative, requests dynamic ID allocation. + */ + s32 gpio_base; +}; diff --git a/include/linux/spi/ad7879.h b/include/linux/spi/ad7879.h deleted file mode 100644 index 58368be0b4c0..000000000000 --- a/include/linux/spi/ad7879.h +++ /dev/null @@ -1,41 +0,0 @@ -/* linux/spi/ad7879.h */ - -/* Touchscreen characteristics vary between boards and models. The - * platform_data for the device's "struct device" holds this information. - * - * It's OK if the min/max values are zero. - */ -struct ad7879_platform_data { - u16 model; /* 7879 */ - u16 x_plate_ohms; - u16 x_min, x_max; - u16 y_min, y_max; - u16 pressure_min, pressure_max; - - bool swap_xy; /* swap x and y axes */ - - /* [0..255] 0=OFF Starts at 1=550us and goes - * all the way to 9.440ms in steps of 35us. - */ - u8 pen_down_acc_interval; - /* [0..15] Starts at 0=128us and goes all the - * way to 4.096ms in steps of 128us. - */ - u8 first_conversion_delay; - /* [0..3] 0 = 2us, 1 = 4us, 2 = 8us, 3 = 16us */ - u8 acquisition_time; - /* [0..3] Average X middle samples 0 = 2, 1 = 4, 2 = 8, 3 = 16 */ - u8 averaging; - /* [0..3] Perform X measurements 0 = OFF, - * 1 = 4, 2 = 8, 3 = 16 (median > averaging) - */ - u8 median; - /* 1 = AUX/VBAT/GPIO export GPIO to gpiolib - * requires CONFIG_GPIOLIB - */ - bool gpio_export; - /* identifies the first GPIO number handled by this chip; - * or, if negative, requests dynamic ID allocation. - */ - s32 gpio_base; -}; -- cgit v1.2.3 From 5f2940c4a544ae9040d0feff6da06a994cc201ff Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 8 Mar 2016 10:34:38 -0800 Subject: Input: ad7879 - fix default x/y axis assignment The X/Y position measurements read from the controller are interpreted wrong. The first measurement X+ contains the Y position, and the second measurement Y+ the X position (see also Table 11 Register Table in the data sheet). The problem is already known and a swap option has been introduced: commit 6680884a4420 ("Input: ad7879 - add option to correct xy axis") However, the meaning of the new boolean is inverted since the underlying values are already swapped. Let ts->swap_xy set to true actually be the swapped configuration of the two axis. Signed-off-by: Stefan Agner Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index b32dbf389777..a770b92a9aa1 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -94,8 +94,8 @@ #define AD7879_TEMP_BIT (1<<1) enum { - AD7879_SEQ_XPOS = 0, - AD7879_SEQ_YPOS = 1, + AD7879_SEQ_YPOS = 0, + AD7879_SEQ_XPOS = 1, AD7879_SEQ_Z1 = 2, AD7879_SEQ_Z2 = 3, AD7879_NR_SENSE = 4, @@ -517,7 +517,9 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->dev = dev; ts->input = input_dev; ts->irq = irq; - ts->swap_xy = pdata->swap_xy; + + /* Use swapped axis by default (backward compatibility) */ + ts->swap_xy = !pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); -- cgit v1.2.3 From fa6e3ca274429b66e12d06abc5a6c013cef66471 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 8 Mar 2016 10:35:24 -0800 Subject: Input: ad7879 - add device tree support Add device tree support for the I2C and SPI variant of AD7879(-1). This allows to specify the touchscreen controller as a I2C client node or SPI slave device. Most of the options available in platform data are also available as device tree properties, the only exception being GPIO capabilities, which can not be activated through device tree currently. Signed-off-by: Stefan Agner Acked-by: Rob Herring Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/ad7879.txt | 53 ++++++++ drivers/input/touchscreen/ad7879-i2c.c | 10 ++ drivers/input/touchscreen/ad7879-spi.c | 10 ++ drivers/input/touchscreen/ad7879.c | 148 +++++++++++++-------- 4 files changed, 163 insertions(+), 58 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/ad7879.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/touchscreen/ad7879.txt b/Documentation/devicetree/bindings/input/touchscreen/ad7879.txt new file mode 100644 index 000000000000..e3f22d23fc8f --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/ad7879.txt @@ -0,0 +1,53 @@ +* Analog Devices AD7879(-1)/AD7889(-1) touchscreen interface (SPI/I2C) + +Required properties: +- compatible : for SPI slave, use "adi,ad7879" + for I2C slave, use "adi,ad7879-1" +- reg : SPI chipselect/I2C slave address + See spi-bus.txt for more SPI slave properties +- interrupt-parent : the phandle for the interrupt controller +- interrupts : touch controller interrupt +- touchscreen-max-pressure : maximum reported pressure +- adi,resistance-plate-x : total resistance of X-plate (for pressure + calculation) +Optional properties: +- touchscreen-swapped-x-y : X and Y axis are swapped (boolean) +- adi,first-conversion-delay : 0-12: In 128us steps (starting with 128us) + 13 : 2.560ms + 14 : 3.584ms + 15 : 4.096ms + This property has to be a '/bits/ 8' value +- adi,acquisition-time : 0: 2us + 1: 4us + 2: 8us + 3: 16us + This property has to be a '/bits/ 8' value +- adi,median-filter-size : 0: disabled + 1: 4 measurements + 2: 8 measurements + 3: 16 measurements + This property has to be a '/bits/ 8' value +- adi,averaging : 0: 2 middle values (1 if median disabled) + 1: 4 middle values + 2: 8 middle values + 3: 16 values + This property has to be a '/bits/ 8' value +- adi,conversion-interval: : 0 : convert one time only + 1-255: 515us + val * 35us (up to 9.440ms) + This property has to be a '/bits/ 8' value + +Example: + + ad7879@2c { + compatible = "adi,ad7879-1"; + reg = <0x2c>; + interrupt-parent = <&gpio1>; + interrupts = <13 IRQ_TYPE_EDGE_FALLING>; + touchscreen-max-pressure = <4096>; + adi,resistance-plate-x = <120>; + adi,first-conversion-delay = /bits/ 8 <3>; + adi,acquisition-time = /bits/ 8 <1>; + adi,median-filter-size = /bits/ 8 <2>; + adi,averaging = /bits/ 8 <1>; + adi,conversion-interval = /bits/ 8 <255>; + }; diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index d66962c5b1c2..58f72e0246ab 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "ad7879.h" @@ -91,10 +92,19 @@ static const struct i2c_device_id ad7879_id[] = { }; MODULE_DEVICE_TABLE(i2c, ad7879_id); +#ifdef CONFIG_OF +static const struct of_device_id ad7879_i2c_dt_ids[] = { + { .compatible = "adi,ad7879-1", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_i2c_dt_ids); +#endif + static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_i2c_dt_ids), }, .probe = ad7879_i2c_probe, .remove = ad7879_i2c_remove, diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 48033c2689ab..d42b6b9af191 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "ad7879.h" @@ -146,10 +147,19 @@ static int ad7879_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ad7879_spi_dt_ids[] = { + { .compatible = "adi,ad7879", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_spi_dt_ids); +#endif + static struct spi_driver ad7879_spi_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_spi_dt_ids), }, .probe = ad7879_spi_probe, .remove = ad7879_spi_remove, diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index a770b92a9aa1..cd013434473d 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include "ad7879.h" @@ -126,7 +127,6 @@ struct ad7879 { u8 pen_down_acc_interval; u8 median; u16 x_plate_ohms; - u16 pressure_max; u16 cmd_crtl1; u16 cmd_crtl2; u16 cmd_crtl3; @@ -186,7 +186,7 @@ static int ad7879_report(struct ad7879 *ts) * Sample found inconsistent, pressure is beyond * the maximum. Don't report it to user space. */ - if (Rt > ts->pressure_max) + if (Rt > input_abs_get_max(input_dev, ABS_PRESSURE)) return -EINVAL; /* @@ -469,7 +469,7 @@ static void ad7879_gpio_remove(struct ad7879 *ts) { const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); - if (pdata->gpio_export) + if (pdata && pdata->gpio_export) gpiochip_remove(&ts->gc); } @@ -485,6 +485,32 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts) } #endif +static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) +{ + int err; + u32 tmp; + + err = device_property_read_u32(dev, "adi,resistance-plate-x", &tmp); + if (err) { + dev_err(dev, "failed to get resistance-plate-x property\n"); + return err; + } + ts->x_plate_ohms = (u16)tmp; + + device_property_read_u8(dev, "adi,first-conversion-delay", + &ts->first_conversion_delay); + device_property_read_u8(dev, "adi,acquisition-time", + &ts->acquisition_time); + device_property_read_u8(dev, "adi,median-filter-size", &ts->median); + device_property_read_u8(dev, "adi,averaging", &ts->averaging); + device_property_read_u8(dev, "adi,conversion-interval", + &ts->pen_down_acc_interval); + + ts->swap_xy = device_property_read_bool(dev, "touchscreen-swapped-x-y"); + + return 0; +} + struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, const struct ad7879_bus_ops *bops) { @@ -495,22 +521,36 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, u16 revid; if (!irq) { - dev_err(dev, "no IRQ?\n"); - err = -EINVAL; - goto err_out; + dev_err(dev, "No IRQ specified\n"); + return ERR_PTR(-EINVAL); } - if (!pdata) { - dev_err(dev, "no platform data?\n"); - err = -EINVAL; - goto err_out; + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return ERR_PTR(-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 if (dev->of_node) { + ad7879_parse_dt(dev, ts); + } else { + dev_err(dev, "No platform data\n"); + return ERR_PTR(-EINVAL); } - ts = kzalloc(sizeof(*ts), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - err = -ENOMEM; - goto err_free_mem; + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Failed to allocate input device\n"); + return ERR_PTR(-ENOMEM); } ts->bops = bops; @@ -518,20 +558,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->input = input_dev; ts->irq = irq; - /* Use swapped axis by default (backward compatibility) */ - ts->swap_xy = !pdata->swap_xy; - setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - 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; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; @@ -552,21 +579,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - 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); + 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); + touchscreen_parse_properties(input_dev, false); + if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { + dev_err(dev, "Touchscreen pressure is not specified\n"); + return ERR_PTR(-EINVAL); + } + } err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { dev_err(dev, "Failed to write %s\n", input_dev->name); - goto err_free_mem; + return ERR_PTR(err); } revid = ad7879_read(ts, AD7879_REG_REVID); @@ -575,8 +614,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, if (input_dev->id.product != devid) { dev_err(dev, "Failed to probe %s (%x vs %x)\n", input_dev->name, devid, revid); - err = -ENODEV; - goto err_free_mem; + return ERR_PTR(-ENODEV); } ts->cmd_crtl3 = AD7879_YPLUS_BIT | @@ -596,23 +634,25 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); - err = request_threaded_irq(ts->irq, NULL, ad7879_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), ts); + err = devm_request_threaded_irq(dev, ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), ts); if (err) { - dev_err(dev, "irq %d busy?\n", ts->irq); - goto err_free_mem; + dev_err(dev, "Failed to request IRQ: %d\n", err); + return ERR_PTR(err); } __ad7879_disable(ts); err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) - goto err_free_irq; + goto err_out; - err = ad7879_gpio_add(ts, pdata); - if (err) - goto err_remove_attr; + if (pdata) { + err = ad7879_gpio_add(ts, pdata); + if (err) + goto err_remove_attr; + } err = input_register_device(input_dev); if (err) @@ -624,11 +664,6 @@ err_remove_gpio: ad7879_gpio_remove(ts); err_remove_attr: sysfs_remove_group(&dev->kobj, &ad7879_attr_group); -err_free_irq: - free_irq(ts->irq, ts); -err_free_mem: - input_free_device(input_dev); - kfree(ts); err_out: return ERR_PTR(err); } @@ -638,9 +673,6 @@ void ad7879_remove(struct ad7879 *ts) { ad7879_gpio_remove(ts); sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - kfree(ts); } EXPORT_SYMBOL(ad7879_remove); -- cgit v1.2.3 From 2b6a321da9a2d8725a1d3dbb0b2e96a7618ebe56 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:35:49 -0800 Subject: Input: synaptics-rmi4 - add support for Synaptics RMI4 devices Synaptics uses the Register Mapped Interface (RMI) protocol as a communications interface for their devices. This driver adds the core functionality needed to interface with RMI4 devices. RMI devices can be connected to the host via several transport protocols and can supports a wide variety of functionality defined by RMI functions. Support for transport protocols and RMI functions are implemented in individual drivers. The RMI4 core driver uses a bus architecture to facilitate the various combinations of transport and function drivers needed by a particular device. Signed-off-by: Andrew Duggan Signed-off-by: Christopher Heiny Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/Kconfig | 2 + drivers/input/Makefile | 2 + drivers/input/rmi4/Kconfig | 10 + drivers/input/rmi4/Makefile | 2 + drivers/input/rmi4/rmi_bus.c | 375 ++++++++++++++ drivers/input/rmi4/rmi_bus.h | 186 +++++++ drivers/input/rmi4/rmi_driver.c | 1027 +++++++++++++++++++++++++++++++++++++++ drivers/input/rmi4/rmi_driver.h | 103 ++++ drivers/input/rmi4/rmi_f01.c | 574 ++++++++++++++++++++++ include/linux/rmi.h | 209 ++++++++ include/uapi/linux/input.h | 1 + 11 files changed, 2491 insertions(+) create mode 100644 drivers/input/rmi4/Kconfig create mode 100644 drivers/input/rmi4/Makefile create mode 100644 drivers/input/rmi4/rmi_bus.c create mode 100644 drivers/input/rmi4/rmi_bus.h create mode 100644 drivers/input/rmi4/rmi_driver.c create mode 100644 drivers/input/rmi4/rmi_driver.h create mode 100644 drivers/input/rmi4/rmi_f01.c create mode 100644 include/linux/rmi.h (limited to 'drivers/input') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a35532ec00e4..6261874c07c9 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -201,6 +201,8 @@ source "drivers/input/touchscreen/Kconfig" source "drivers/input/misc/Kconfig" +source "drivers/input/rmi4/Kconfig" + endif menu "Hardware I/O ports" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c9302ca9954..595820bbabe9 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,3 +26,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o + +obj-$(CONFIG_RMI4_CORE) += rmi4/ diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig new file mode 100644 index 000000000000..5ea60e338093 --- /dev/null +++ b/drivers/input/rmi4/Kconfig @@ -0,0 +1,10 @@ +# +# RMI4 configuration +# +config RMI4_CORE + tristate "Synaptics RMI4 bus support" + help + Say Y here if you want to support the Synaptics RMI4 bus. This is + required for all RMI4 device support. + + If unsure, say Y. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile new file mode 100644 index 000000000000..12f219779b74 --- /dev/null +++ b/drivers/input/rmi4/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_RMI4_CORE) += rmi_core.o +rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c new file mode 100644 index 000000000000..0a2bd5a0f2b7 --- /dev/null +++ b/drivers/input/rmi4/rmi_bus.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_bus.h" +#include "rmi_driver.h" + +static int debug_flags; +module_param(debug_flags, int, 0644); +MODULE_PARM_DESC(debug_flags, "control debugging information"); + +void rmi_dbg(int flags, struct device *dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (flags & debug_flags) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_printk(KERN_DEBUG, dev, "%pV", &vaf); + + va_end(args); + } +} +EXPORT_SYMBOL_GPL(rmi_dbg); + +/* + * RMI Physical devices + * + * Physical RMI device consists of several functions serving particular + * purpose. For example F11 is a 2D touch sensor while F01 is a generic + * function present in every RMI device. + */ + +static void rmi_release_device(struct device *dev) +{ + struct rmi_device *rmi_dev = to_rmi_device(dev); + + kfree(rmi_dev); +} + +static struct device_type rmi_device_type = { + .name = "rmi4_sensor", + .release = rmi_release_device, +}; + +bool rmi_is_physical_device(struct device *dev) +{ + return dev->type == &rmi_device_type; +} + +/** + * rmi_register_transport_device - register a transport device connection + * on the RMI bus. Transport drivers provide communication from the devices + * on a bus (such as SPI, I2C, and so on) to the RMI4 sensor. + * + * @xport: the transport device to register + */ +int rmi_register_transport_device(struct rmi_transport_dev *xport) +{ + static atomic_t transport_device_count = ATOMIC_INIT(0); + struct rmi_device *rmi_dev; + int error; + + rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL); + if (!rmi_dev) + return -ENOMEM; + + device_initialize(&rmi_dev->dev); + + rmi_dev->xport = xport; + rmi_dev->number = atomic_inc_return(&transport_device_count) - 1; + + dev_set_name(&rmi_dev->dev, "rmi4-%02d", rmi_dev->number); + + rmi_dev->dev.bus = &rmi_bus_type; + rmi_dev->dev.type = &rmi_device_type; + + xport->rmi_dev = rmi_dev; + + error = device_add(&rmi_dev->dev); + if (error) + goto err_put_device; + + rmi_dbg(RMI_DEBUG_CORE, xport->dev, + "%s: Registered %s as %s.\n", __func__, + dev_name(rmi_dev->xport->dev), dev_name(&rmi_dev->dev)); + + return 0; + +err_put_device: + put_device(&rmi_dev->dev); + return error; +} +EXPORT_SYMBOL_GPL(rmi_register_transport_device); + +/** + * rmi_unregister_transport_device - unregister a transport device connection + * @xport: the transport driver to unregister + * + */ +void rmi_unregister_transport_device(struct rmi_transport_dev *xport) +{ + struct rmi_device *rmi_dev = xport->rmi_dev; + + device_del(&rmi_dev->dev); + put_device(&rmi_dev->dev); +} +EXPORT_SYMBOL(rmi_unregister_transport_device); + + +/* Function specific stuff */ + +static void rmi_release_function(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + + kfree(fn); +} + +static struct device_type rmi_function_type = { + .name = "rmi4_function", + .release = rmi_release_function, +}; + +bool rmi_is_function_device(struct device *dev) +{ + return dev->type == &rmi_function_type; +} + +static int rmi_function_match(struct device *dev, struct device_driver *drv) +{ + struct rmi_function_handler *handler = to_rmi_function_handler(drv); + struct rmi_function *fn = to_rmi_function(dev); + + return fn->fd.function_number == handler->func; +} + +static int rmi_function_probe(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + struct rmi_function_handler *handler = + to_rmi_function_handler(dev->driver); + int error; + + if (handler->probe) { + error = handler->probe(fn); + return error; + } + + return 0; +} + +static int rmi_function_remove(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + struct rmi_function_handler *handler = + to_rmi_function_handler(dev->driver); + + if (handler->remove) + handler->remove(fn); + + return 0; +} + +int rmi_register_function(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + device_initialize(&fn->dev); + + dev_set_name(&fn->dev, "%s.fn%02x", + dev_name(&rmi_dev->dev), fn->fd.function_number); + + fn->dev.parent = &rmi_dev->dev; + fn->dev.type = &rmi_function_type; + fn->dev.bus = &rmi_bus_type; + + error = device_add(&fn->dev); + if (error) { + dev_err(&rmi_dev->dev, + "Failed device_register function device %s\n", + dev_name(&fn->dev)); + goto err_put_device; + } + + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Registered F%02X.\n", + fn->fd.function_number); + + return 0; + +err_put_device: + put_device(&fn->dev); + return error; +} + +void rmi_unregister_function(struct rmi_function *fn) +{ + device_del(&fn->dev); + + if (fn->dev.of_node) + of_node_put(fn->dev.of_node); + + put_device(&fn->dev); +} + +/** + * rmi_register_function_handler - register a handler for an RMI function + * @handler: RMI handler that should be registered. + * @module: pointer to module that implements the handler + * @mod_name: name of the module implementing the handler + * + * This function performs additional setup of RMI function handler and + * registers it with the RMI core so that it can be bound to + * RMI function devices. + */ +int __rmi_register_function_handler(struct rmi_function_handler *handler, + struct module *owner, + const char *mod_name) +{ + struct device_driver *driver = &handler->driver; + int error; + + driver->bus = &rmi_bus_type; + driver->owner = owner; + driver->mod_name = mod_name; + driver->probe = rmi_function_probe; + driver->remove = rmi_function_remove; + + error = driver_register(&handler->driver); + if (error) { + pr_err("driver_register() failed for %s, error: %d\n", + handler->driver.name, error); + return error; + } + + return 0; +} +EXPORT_SYMBOL_GPL(__rmi_register_function_handler); + +/** + * rmi_unregister_function_handler - unregister given RMI function handler + * @handler: RMI handler that should be unregistered. + * + * This function unregisters given function handler from RMI core which + * causes it to be unbound from the function devices. + */ +void rmi_unregister_function_handler(struct rmi_function_handler *handler) +{ + driver_unregister(&handler->driver); +} +EXPORT_SYMBOL_GPL(rmi_unregister_function_handler); + +/* Bus specific stuff */ + +static int rmi_bus_match(struct device *dev, struct device_driver *drv) +{ + bool physical = rmi_is_physical_device(dev); + + /* First see if types are not compatible */ + if (physical != rmi_is_physical_driver(drv)) + return 0; + + return physical || rmi_function_match(dev, drv); +} + +struct bus_type rmi_bus_type = { + .match = rmi_bus_match, + .name = "rmi4", +}; + +static struct rmi_function_handler *fn_handlers[] = { + &rmi_f01_handler, +}; + +static void __rmi_unregister_function_handlers(int start_idx) +{ + int i; + + for (i = start_idx; i >= 0; i--) + rmi_unregister_function_handler(fn_handlers[i]); +} + +static void rmi_unregister_function_handlers(void) +{ + __rmi_unregister_function_handlers(ARRAY_SIZE(fn_handlers) - 1); +} + +static int rmi_register_function_handlers(void) +{ + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(fn_handlers); i++) { + ret = rmi_register_function_handler(fn_handlers[i]); + if (ret) { + pr_err("%s: error registering the RMI F%02x handler: %d\n", + __func__, fn_handlers[i]->func, ret); + goto err_unregister_function_handlers; + } + } + + return 0; + +err_unregister_function_handlers: + __rmi_unregister_function_handlers(i - 1); + return ret; +} + +static int __init rmi_bus_init(void) +{ + int error; + + error = bus_register(&rmi_bus_type); + if (error) { + pr_err("%s: error registering the RMI bus: %d\n", + __func__, error); + return error; + } + + error = rmi_register_function_handlers(); + if (error) + goto err_unregister_bus; + + error = rmi_register_physical_driver(); + if (error) { + pr_err("%s: error registering the RMI physical driver: %d\n", + __func__, error); + goto err_unregister_bus; + } + + return 0; + +err_unregister_bus: + bus_unregister(&rmi_bus_type); + return error; +} +module_init(rmi_bus_init); + +static void __exit rmi_bus_exit(void) +{ + /* + * We should only ever get here if all drivers are unloaded, so + * all we have to do at this point is unregister ourselves. + */ + + rmi_unregister_physical_driver(); + rmi_unregister_function_handlers(); + bus_unregister(&rmi_bus_type); +} +module_exit(rmi_bus_exit); + +MODULE_AUTHOR("Christopher Heiny + +struct rmi_device; + +/** + * struct rmi_function - represents the implementation of an RMI4 + * function for a particular device (basically, a driver for that RMI4 function) + * + * @fd: The function descriptor of the RMI function + * @rmi_dev: Pointer to the RMI device associated with this function container + * @dev: The device associated with this particular function. + * + * @num_of_irqs: The number of irqs needed by this function + * @irq_pos: The position in the irq bitfield this function holds + * @irq_mask: For convenience, can be used to mask IRQ bits off during ATTN + * interrupt handling. + * + * @node: entry in device's list of functions + */ +struct rmi_function { + struct rmi_function_descriptor fd; + struct rmi_device *rmi_dev; + struct device dev; + struct list_head node; + + unsigned int num_of_irqs; + unsigned int irq_pos; + unsigned long irq_mask[]; +}; + +#define to_rmi_function(d) container_of(d, struct rmi_function, dev) + +bool rmi_is_function_device(struct device *dev); + +int __must_check rmi_register_function(struct rmi_function *); +void rmi_unregister_function(struct rmi_function *); + +/** + * struct rmi_function_handler - driver routines for a particular RMI function. + * + * @func: The RMI function number + * @reset: Called when a reset of the touch sensor is detected. The routine + * should perform any out-of-the-ordinary reset handling that might be + * necessary. Restoring of touch sensor configuration registers should be + * handled in the config() callback, below. + * @config: Called when the function container is first initialized, and + * after a reset is detected. This routine should write any necessary + * configuration settings to the device. + * @attention: Called when the IRQ(s) for the function are set by the touch + * sensor. + * @suspend: Should perform any required operations to suspend the particular + * function. + * @resume: Should perform any required operations to resume the particular + * function. + * + * All callbacks are expected to return 0 on success, error code on failure. + */ +struct rmi_function_handler { + struct device_driver driver; + + u8 func; + + int (*probe)(struct rmi_function *fn); + void (*remove)(struct rmi_function *fn); + int (*config)(struct rmi_function *fn); + int (*reset)(struct rmi_function *fn); + int (*attention)(struct rmi_function *fn, unsigned long *irq_bits); + int (*suspend)(struct rmi_function *fn); + int (*resume)(struct rmi_function *fn); +}; + +#define to_rmi_function_handler(d) \ + container_of(d, struct rmi_function_handler, driver) + +int __must_check __rmi_register_function_handler(struct rmi_function_handler *, + struct module *, const char *); +#define rmi_register_function_handler(handler) \ + __rmi_register_function_handler(handler, THIS_MODULE, KBUILD_MODNAME) + +void rmi_unregister_function_handler(struct rmi_function_handler *); + +#define to_rmi_driver(d) \ + container_of(d, struct rmi_driver, driver) + +#define to_rmi_device(d) container_of(d, struct rmi_device, dev) + +static inline struct rmi_device_platform_data * +rmi_get_platform_data(struct rmi_device *d) +{ + return &d->xport->pdata; +} + +bool rmi_is_physical_device(struct device *dev); + +/** + * rmi_read - read a single byte + * @d: Pointer to an RMI device + * @addr: The address to read from + * @buf: The read buffer + * + * Reads a single byte of data using the underlying transport protocol + * into memory pointed by @buf. It returns 0 on success or a negative + * error code. + */ +static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf) +{ + return d->xport->ops->read_block(d->xport, addr, buf, 1); +} + +/** + * rmi_read_block - read a block of bytes + * @d: Pointer to an RMI device + * @addr: The start address to read from + * @buf: The read buffer + * @len: Length of the read buffer + * + * Reads a block of byte data using the underlying transport protocol + * into memory pointed by @buf. It returns 0 on success or a negative + * error code. + */ +static inline int rmi_read_block(struct rmi_device *d, u16 addr, + void *buf, size_t len) +{ + return d->xport->ops->read_block(d->xport, addr, buf, len); +} + +/** + * rmi_write - write a single byte + * @d: Pointer to an RMI device + * @addr: The address to write to + * @data: The data to write + * + * Writes a single byte using the underlying transport protocol. It + * returns zero on success or a negative error code. + */ +static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data) +{ + return d->xport->ops->write_block(d->xport, addr, &data, 1); +} + +/** + * rmi_write_block - write a block of bytes + * @d: Pointer to an RMI device + * @addr: The start address to write to + * @buf: The write buffer + * @len: Length of the write buffer + * + * Writes a block of byte data from buf using the underlaying transport + * protocol. It returns the amount of bytes written or a negative error code. + */ +static inline int rmi_write_block(struct rmi_device *d, u16 addr, + const void *buf, size_t len) +{ + return d->xport->ops->write_block(d->xport, addr, buf, len); +} + +int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data)); + +extern struct bus_type rmi_bus_type; + +int rmi_of_property_read_u32(struct device *dev, u32 *result, + const char *prop, bool optional); +int rmi_of_property_read_u16(struct device *dev, u16 *result, + const char *prop, bool optional); +int rmi_of_property_read_u8(struct device *dev, u8 *result, + const char *prop, bool optional); + +#define RMI_DEBUG_CORE BIT(0) +#define RMI_DEBUG_XPORT BIT(1) +#define RMI_DEBUG_FN BIT(2) +#define RMI_DEBUG_2D_SENSOR BIT(3) + +void rmi_dbg(int flags, struct device *dev, const char *fmt, ...); +#endif diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c new file mode 100644 index 000000000000..b0f34b57a126 --- /dev/null +++ b/drivers/input/rmi4/rmi_driver.c @@ -0,0 +1,1027 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This driver provides the core support for a single RMI4-based device. + * + * The RMI4 specification can be found here (URL split for line length): + * + * http://www.synaptics.com/sites/default/files/ + * 511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_bus.h" +#include "rmi_driver.h" + +#define HAS_NONSTANDARD_PDT_MASK 0x40 +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x100 +#define RMI4_PAGE_MASK 0xFF00 + +#define RMI_DEVICE_RESET_CMD 0x01 +#define DEFAULT_RESET_DELAY_MS 100 + +static void rmi_free_function_list(struct rmi_device *rmi_dev) +{ + struct rmi_function *fn, *tmp; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + + data->f01_container = NULL; + + /* Doing it in the reverse order so F01 will be removed last */ + list_for_each_entry_safe_reverse(fn, tmp, + &data->function_list, node) { + list_del(&fn->node); + rmi_unregister_function(fn); + } +} + +static int reset_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->reset) { + retval = fh->reset(fn); + if (retval < 0) + dev_err(&fn->dev, "Reset failed with code %d.\n", + retval); + } + + return retval; +} + +static int configure_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->config) { + retval = fh->config(fn); + if (retval < 0) + dev_err(&fn->dev, "Config failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = reset_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = configure_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static void process_one_interrupt(struct rmi_driver_data *data, + struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + + if (!fn || !fn->dev.driver) + return; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fn->irq_mask && fh->attention) { + bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask, + data->irq_count); + if (!bitmap_empty(data->fn_irq_bits, data->irq_count)) + fh->attention(fn, data->fn_irq_bits); + } +} + +int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + struct rmi_function *entry; + int error; + + if (!data) + return 0; + + if (!rmi_dev->xport->attn_data) { + error = rmi_read_block(rmi_dev, + data->f01_container->fd.data_base_addr + 1, + data->irq_status, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "Failed to read irqs, code=%d\n", error); + return error; + } + } + + mutex_lock(&data->irq_mutex); + bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, + data->irq_count); + /* + * At this point, irq_status has all bits that are set in the + * interrupt status register and are enabled. + */ + mutex_unlock(&data->irq_mutex); + + /* + * It would be nice to be able to use irq_chip to handle these + * nested IRQs. Unfortunately, most of the current customers for + * this driver are using older kernels (3.0.x) that don't support + * the features required for that. Once they've shifted to more + * recent kernels (say, 3.3 and higher), this should be switched to + * use irq_chip. + */ + list_for_each_entry(entry, &data->function_list, node) + if (entry->irq_mask) + process_one_interrupt(data, entry); + + if (data->input) + input_sync(data->input); + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests); + +static int suspend_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->suspend) { + retval = fh->suspend(fn); + if (retval < 0) + dev_err(&fn->dev, "Suspend failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_suspend_functions(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = suspend_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int resume_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->resume) { + retval = fh->resume(fn); + if (retval < 0) + dev_err(&fn->dev, "Resume failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_resume_functions(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = resume_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int enable_sensor(struct rmi_device *rmi_dev) +{ + int retval = 0; + + retval = rmi_driver_process_config_requests(rmi_dev); + if (retval < 0) + return retval; + + return rmi_process_interrupt_requests(rmi_dev); +} + +/** + * rmi_driver_set_input_params - set input device id and other data. + * + * @rmi_dev: Pointer to an RMI device + * @input: Pointer to input device + * + */ +static int rmi_driver_set_input_params(struct rmi_device *rmi_dev, + struct input_dev *input) +{ + input->name = SYNAPTICS_INPUT_DEVICE_NAME; + input->id.vendor = SYNAPTICS_VENDOR_ID; + input->id.bustype = BUS_RMI; + return 0; +} + +static void rmi_driver_set_input_name(struct rmi_device *rmi_dev, + struct input_dev *input) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + char *device_name = rmi_f01_get_product_ID(data->f01_container); + char *name; + + name = devm_kasprintf(&rmi_dev->dev, GFP_KERNEL, + "Synaptics %s", device_name); + if (!name) + return; + + input->name = name; +} + +static int rmi_driver_set_irq_bits(struct rmi_device *rmi_dev, + unsigned long *mask) +{ + int error = 0; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + + mutex_lock(&data->irq_mutex); + bitmap_or(data->new_irq_mask, + data->current_irq_mask, mask, data->irq_count); + + error = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->new_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: Failed to change enabled interrupts!", + __func__); + goto error_unlock; + } + bitmap_copy(data->current_irq_mask, data->new_irq_mask, + data->num_of_irq_regs); + +error_unlock: + mutex_unlock(&data->irq_mutex); + return error; +} + +static int rmi_driver_clear_irq_bits(struct rmi_device *rmi_dev, + unsigned long *mask) +{ + int error = 0; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + + mutex_lock(&data->irq_mutex); + bitmap_andnot(data->new_irq_mask, + data->current_irq_mask, mask, data->irq_count); + + error = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->new_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: Failed to change enabled interrupts!", + __func__); + goto error_unlock; + } + bitmap_copy(data->current_irq_mask, data->new_irq_mask, + data->num_of_irq_regs); + +error_unlock: + mutex_unlock(&data->irq_mutex); + return error; +} + +static int rmi_driver_reset_handler(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int error; + + /* + * Can get called before the driver is fully ready to deal with + * this situation. + */ + if (!data || !data->f01_container) { + dev_warn(&rmi_dev->dev, + "Not ready to handle reset yet!\n"); + return 0; + } + + error = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->current_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n", + __func__); + return error; + } + + error = rmi_driver_process_reset_requests(rmi_dev); + if (error < 0) + return error; + + error = rmi_driver_process_config_requests(rmi_dev); + if (error < 0) + return error; + + return 0; +} + +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, + u16 pdt_address) +{ + u8 buf[RMI_PDT_ENTRY_SIZE]; + int error; + + error = rmi_read_block(rmi_dev, pdt_address, buf, RMI_PDT_ENTRY_SIZE); + if (error) { + dev_err(&rmi_dev->dev, "Read PDT entry at %#06x failed, code: %d.\n", + pdt_address, error); + return error; + } + + entry->page_start = pdt_address & RMI4_PAGE_MASK; + entry->query_base_addr = buf[0]; + entry->command_base_addr = buf[1]; + entry->control_base_addr = buf[2]; + entry->data_base_addr = buf[3]; + entry->interrupt_source_count = buf[4] & RMI_PDT_INT_SOURCE_COUNT_MASK; + entry->function_version = (buf[4] & RMI_PDT_FUNCTION_VERSION_MASK) >> 5; + entry->function_number = buf[5]; + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_read_pdt_entry); + +static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, + struct rmi_function_descriptor *fd) +{ + fd->query_base_addr = pdt->query_base_addr + pdt->page_start; + fd->command_base_addr = pdt->command_base_addr + pdt->page_start; + fd->control_base_addr = pdt->control_base_addr + pdt->page_start; + fd->data_base_addr = pdt->data_base_addr + pdt->page_start; + fd->function_number = pdt->function_number; + fd->interrupt_source_count = pdt->interrupt_source_count; + fd->function_version = pdt->function_version; +} + +#define RMI_SCAN_CONTINUE 0 +#define RMI_SCAN_DONE 1 + +static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, + int page, + void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, + const struct pdt_entry *entry)) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct pdt_entry pdt_entry; + u16 page_start = RMI4_PAGE_SIZE * page; + u16 pdt_start = page_start + PDT_START_SCAN_LOCATION; + u16 pdt_end = page_start + PDT_END_SCAN_LOCATION; + u16 addr; + int error; + int retval; + + for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) { + error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr); + if (error) + return error; + + if (RMI4_END_OF_PDT(pdt_entry.function_number)) + break; + + retval = callback(rmi_dev, ctx, &pdt_entry); + if (retval != RMI_SCAN_CONTINUE) + return retval; + } + + return (data->f01_bootloader_mode || addr == pdt_start) ? + RMI_SCAN_DONE : RMI_SCAN_CONTINUE; +} + +static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, + const struct pdt_entry *entry)) +{ + int page; + int retval = RMI_SCAN_DONE; + + for (page = 0; page <= RMI4_MAX_PAGE; page++) { + retval = rmi_scan_pdt_page(rmi_dev, page, ctx, callback); + if (retval != RMI_SCAN_CONTINUE) + break; + } + + return retval < 0 ? retval : 0; +} + +int rmi_read_register_desc(struct rmi_device *d, u16 addr, + struct rmi_register_descriptor *rdesc) +{ + int ret; + u8 size_presence_reg; + u8 buf[35]; + int presense_offset = 1; + u8 *struct_buf; + int reg; + int offset = 0; + int map_offset = 0; + int i; + int b; + + /* + * The first register of the register descriptor is the size of + * the register descriptor's presense register. + */ + ret = rmi_read(d, addr, &size_presence_reg); + if (ret) + return ret; + ++addr; + + if (size_presence_reg < 0 || size_presence_reg > 35) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + /* + * The presence register contains the size of the register structure + * and a bitmap which identified which packet registers are present + * for this particular register type (ie query, control, or data). + */ + ret = rmi_read_block(d, addr, buf, size_presence_reg); + if (ret) + return ret; + ++addr; + + if (buf[0] == 0) { + presense_offset = 3; + rdesc->struct_size = buf[1] | (buf[2] << 8); + } else { + rdesc->struct_size = buf[0]; + } + + for (i = presense_offset; i < size_presence_reg; i++) { + for (b = 0; b < 8; b++) { + if (buf[i] & (0x1 << b)) + bitmap_set(rdesc->presense_map, map_offset, 1); + ++map_offset; + } + } + + rdesc->num_registers = bitmap_weight(rdesc->presense_map, + RMI_REG_DESC_PRESENSE_BITS); + + rdesc->registers = devm_kzalloc(&d->dev, rdesc->num_registers * + sizeof(struct rmi_register_desc_item), + GFP_KERNEL); + if (!rdesc->registers) + return -ENOMEM; + + /* + * Allocate a temporary buffer to hold the register structure. + * I'm not using devm_kzalloc here since it will not be retained + * after exiting this function + */ + struct_buf = kzalloc(rdesc->struct_size, GFP_KERNEL); + if (!struct_buf) + return -ENOMEM; + + /* + * The register structure contains information about every packet + * register of this type. This includes the size of the packet + * register and a bitmap of all subpackets contained in the packet + * register. + */ + ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size); + if (ret) + goto free_struct_buff; + + reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS); + map_offset = 0; + for (i = 0; i < rdesc->num_registers; i++) { + struct rmi_register_desc_item *item = &rdesc->registers[i]; + int reg_size = struct_buf[offset]; + + ++offset; + if (reg_size == 0) { + reg_size = struct_buf[offset] | + (struct_buf[offset + 1] << 8); + offset += 2; + } + + if (reg_size == 0) { + reg_size = struct_buf[offset] | + (struct_buf[offset + 1] << 8) | + (struct_buf[offset + 2] << 16) | + (struct_buf[offset + 3] << 24); + offset += 4; + } + + item->reg = reg; + item->reg_size = reg_size; + + do { + for (b = 0; b < 7; b++) { + if (struct_buf[offset] & (0x1 << b)) + bitmap_set(item->subpacket_map, + map_offset, 1); + ++map_offset; + } + } while (struct_buf[offset++] & 0x80); + + item->num_subpackets = bitmap_weight(item->subpacket_map, + RMI_REG_DESC_SUBPACKET_BITS); + + rmi_dbg(RMI_DEBUG_CORE, &d->dev, + "%s: reg: %d reg size: %ld subpackets: %d\n", __func__, + item->reg, item->reg_size, item->num_subpackets); + + reg = find_next_bit(rdesc->presense_map, + RMI_REG_DESC_PRESENSE_BITS, reg + 1); + } + +free_struct_buff: + kfree(struct_buf); + return ret; +} +EXPORT_SYMBOL_GPL(rmi_read_register_desc); + +const struct rmi_register_desc_item *rmi_get_register_desc_item( + struct rmi_register_descriptor *rdesc, u16 reg) +{ + const struct rmi_register_desc_item *item; + int i; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + if (item->reg == reg) + return item; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(rmi_get_register_desc_item); + +size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) +{ + const struct rmi_register_desc_item *item; + int i; + size_t size = 0; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + size += item->reg_size; + } + return size; +} +EXPORT_SYMBOL_GPL(rmi_register_desc_calc_size); + +/* Compute the register offset relative to the base address */ +int rmi_register_desc_calc_reg_offset( + struct rmi_register_descriptor *rdesc, u16 reg) +{ + const struct rmi_register_desc_item *item; + int offset = 0; + int i; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + if (item->reg == reg) + return offset; + ++offset; + } + return -1; +} +EXPORT_SYMBOL_GPL(rmi_register_desc_calc_reg_offset); + +bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, + u8 subpacket) +{ + return find_next_bit(item->subpacket_map, RMI_REG_DESC_PRESENSE_BITS, + subpacket) == subpacket; +} + +/* Indicates that flash programming is enabled (bootloader mode). */ +#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40)) + +/* + * Given the PDT entry for F01, read the device status register to determine + * if we're stuck in bootloader mode or not. + * + */ +static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev, + const struct pdt_entry *pdt) +{ + int error; + u8 device_status; + + error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start, + &device_status); + if (error) { + dev_err(&rmi_dev->dev, + "Failed to read device status: %d.\n", error); + return error; + } + + return RMI_F01_STATUS_BOOTLOADER(device_status); +} + +static int rmi_count_irqs(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int *irq_count = ctx; + + *irq_count += pdt->interrupt_source_count; + if (pdt->function_number == 0x01) { + data->f01_bootloader_mode = + rmi_check_bootloader_mode(rmi_dev, pdt); + if (data->f01_bootloader_mode) + dev_warn(&rmi_dev->dev, + "WARNING: RMI4 device is in bootloader mode!\n"); + } + + return RMI_SCAN_CONTINUE; +} + +static int rmi_initial_reset(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + int error; + + if (pdt->function_number == 0x01) { + u16 cmd_addr = pdt->page_start + pdt->command_base_addr; + u8 cmd_buf = RMI_DEVICE_RESET_CMD; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(rmi_dev); + + if (rmi_dev->xport->ops->reset) { + error = rmi_dev->xport->ops->reset(rmi_dev->xport, + cmd_addr); + if (error) + return error; + + return RMI_SCAN_DONE; + } + + error = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1); + if (error) { + dev_err(&rmi_dev->dev, + "Initial reset failed. Code = %d.\n", error); + return error; + } + + mdelay(pdata->reset_delay_ms ?: DEFAULT_RESET_DELAY_MS); + + return RMI_SCAN_DONE; + } + + /* F01 should always be on page 0. If we don't find it there, fail. */ + return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; +} + +static int rmi_create_function(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + struct device *dev = &rmi_dev->dev; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int *current_irq_count = ctx; + struct rmi_function *fn; + int i; + int error; + + rmi_dbg(RMI_DEBUG_CORE, dev, "Initializing F%02X.\n", + pdt->function_number); + + fn = kzalloc(sizeof(struct rmi_function) + + BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long), + GFP_KERNEL); + if (!fn) { + dev_err(dev, "Failed to allocate memory for F%02X\n", + pdt->function_number); + return -ENOMEM; + } + + INIT_LIST_HEAD(&fn->node); + rmi_driver_copy_pdt_to_fd(pdt, &fn->fd); + + fn->rmi_dev = rmi_dev; + + fn->num_of_irqs = pdt->interrupt_source_count; + fn->irq_pos = *current_irq_count; + *current_irq_count += fn->num_of_irqs; + + for (i = 0; i < fn->num_of_irqs; i++) + set_bit(fn->irq_pos + i, fn->irq_mask); + + error = rmi_register_function(fn); + if (error) + goto err_put_fn; + + if (pdt->function_number == 0x01) + data->f01_container = fn; + + list_add_tail(&fn->node, &data->function_list); + + return RMI_SCAN_CONTINUE; + +err_put_fn: + put_device(&fn->dev); + return error; +} + +int rmi_driver_suspend(struct rmi_device *rmi_dev) +{ + int retval = 0; + + retval = rmi_suspend_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + return retval; +} +EXPORT_SYMBOL_GPL(rmi_driver_suspend); + +int rmi_driver_resume(struct rmi_device *rmi_dev) +{ + int retval; + + retval = rmi_resume_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + return retval; +} +EXPORT_SYMBOL_GPL(rmi_driver_resume); + +static int rmi_driver_remove(struct device *dev) +{ + struct rmi_device *rmi_dev = to_rmi_device(dev); + + rmi_free_function_list(rmi_dev); + + return 0; +} + +static int rmi_driver_probe(struct device *dev) +{ + struct rmi_driver *rmi_driver; + struct rmi_driver_data *data; + struct rmi_device_platform_data *pdata; + struct rmi_device *rmi_dev; + size_t size; + void *irq_memory; + int irq_count; + int retval; + + rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Starting probe.\n", + __func__); + + if (!rmi_is_physical_device(dev)) { + rmi_dbg(RMI_DEBUG_CORE, dev, "Not a physical device.\n"); + return -ENODEV; + } + + rmi_dev = to_rmi_device(dev); + rmi_driver = to_rmi_driver(dev->driver); + rmi_dev->driver = rmi_driver; + + pdata = rmi_get_platform_data(rmi_dev); + + data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + INIT_LIST_HEAD(&data->function_list); + data->rmi_dev = rmi_dev; + dev_set_drvdata(&rmi_dev->dev, data); + + /* + * Right before a warm boot, the sensor might be in some unusual state, + * such as F54 diagnostics, or F34 bootloader mode after a firmware + * or configuration update. In order to clear the sensor to a known + * state and/or apply any updates, we issue a initial reset to clear any + * previous settings and force it into normal operation. + * + * We have to do this before actually building the PDT because + * the reflash updates (if any) might cause various registers to move + * around. + * + * For a number of reasons, this initial reset may fail to return + * within the specified time, but we'll still be able to bring up the + * driver normally after that failure. This occurs most commonly in + * a cold boot situation (where then firmware takes longer to come up + * than from a warm boot) and the reset_delay_ms in the platform data + * has been set too short to accommodate that. Since the sensor will + * eventually come up and be usable, we don't want to just fail here + * and leave the customer's device unusable. So we warn them, and + * continue processing. + */ + retval = rmi_scan_pdt(rmi_dev, NULL, rmi_initial_reset); + if (retval < 0) + dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n"); + + retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props); + if (retval < 0) { + /* + * we'll print out a warning and continue since + * failure to get the PDT properties is not a cause to fail + */ + dev_warn(dev, "Could not read PDT properties from %#06x (code %d). Assuming 0x00.\n", + PDT_PROPERTIES_LOCATION, retval); + } + + /* + * We need to count the IRQs and allocate their storage before scanning + * the PDT and creating the function entries, because adding a new + * function can trigger events that result in the IRQ related storage + * being accessed. + */ + rmi_dbg(RMI_DEBUG_CORE, dev, "Counting IRQs.\n"); + irq_count = 0; + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); + if (retval < 0) { + dev_err(dev, "IRQ counting failed with code %d.\n", retval); + goto err; + } + data->irq_count = irq_count; + data->num_of_irq_regs = (data->irq_count + 7) / 8; + + mutex_init(&data->irq_mutex); + + size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); + irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!irq_memory) { + dev_err(dev, "Failed to allocate memory for irq masks.\n"); + goto err; + } + + data->irq_status = irq_memory + size * 0; + data->fn_irq_bits = irq_memory + size * 1; + data->current_irq_mask = irq_memory + size * 2; + data->new_irq_mask = irq_memory + size * 3; + + if (rmi_dev->xport->input) { + /* + * The transport driver already has an input device. + * In some cases it is preferable to reuse the transport + * devices input device instead of creating a new one here. + * One example is some HID touchpads report "pass-through" + * button events are not reported by rmi registers. + */ + data->input = rmi_dev->xport->input; + } else { + data->input = devm_input_allocate_device(dev); + if (!data->input) { + dev_err(dev, "%s: Failed to allocate input device.\n", + __func__); + retval = -ENOMEM; + goto err_destroy_functions; + } + rmi_driver_set_input_params(rmi_dev, data->input); + data->input->phys = devm_kasprintf(dev, GFP_KERNEL, + "%s/input0", dev_name(dev)); + } + + irq_count = 0; + rmi_dbg(RMI_DEBUG_CORE, dev, "Creating functions."); + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); + if (retval < 0) { + dev_err(dev, "Function creation failed with code %d.\n", + retval); + goto err_destroy_functions; + } + + if (!data->f01_container) { + dev_err(dev, "Missing F01 container!\n"); + retval = -EINVAL; + goto err_destroy_functions; + } + + retval = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->current_irq_mask, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to read current IRQ mask.\n", + __func__); + goto err_destroy_functions; + } + + if (data->input) { + rmi_driver_set_input_name(rmi_dev, data->input); + if (!rmi_dev->xport->input) { + if (input_register_device(data->input)) { + dev_err(dev, "%s: Failed to register input device.\n", + __func__); + goto err_destroy_functions; + } + } + } + + if (data->f01_container->dev.driver) + /* Driver already bound, so enable ATTN now. */ + return enable_sensor(rmi_dev); + + return 0; + +err_destroy_functions: + rmi_free_function_list(rmi_dev); +err: + return retval < 0 ? retval : 0; +} + +static struct rmi_driver rmi_physical_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rmi4_physical", + .bus = &rmi_bus_type, + .probe = rmi_driver_probe, + .remove = rmi_driver_remove, + }, + .reset_handler = rmi_driver_reset_handler, + .clear_irq_bits = rmi_driver_clear_irq_bits, + .set_irq_bits = rmi_driver_set_irq_bits, + .set_input_params = rmi_driver_set_input_params, +}; + +bool rmi_is_physical_driver(struct device_driver *drv) +{ + return drv == &rmi_physical_driver.driver; +} + +int __init rmi_register_physical_driver(void) +{ + int error; + + error = driver_register(&rmi_physical_driver.driver); + if (error) { + pr_err("%s: driver register failed, code=%d.\n", __func__, + error); + return error; + } + + return 0; +} + +void __exit rmi_unregister_physical_driver(void) +{ + driver_unregister(&rmi_physical_driver.driver); +} diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h new file mode 100644 index 000000000000..3ab3f1a8078f --- /dev/null +++ b/drivers/input/rmi4/rmi_driver.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _RMI_DRIVER_H +#define _RMI_DRIVER_H + +#include +#include +#include +#include +#include "rmi_bus.h" + +#define RMI_DRIVER_VERSION "2.0" + +#define SYNAPTICS_INPUT_DEVICE_NAME "Synaptics RMI4 Touch Sensor" +#define SYNAPTICS_VENDOR_ID 0x06cb + +#define GROUP(_attrs) { \ + .attrs = _attrs, \ +} + +#define PDT_PROPERTIES_LOCATION 0x00EF +#define BSR_LOCATION 0x00FE + +#define RMI_PDT_PROPS_HAS_BSR 0x02 + +#define NAME_BUFFER_SIZE 256 + +#define RMI_PDT_ENTRY_SIZE 6 +#define RMI_PDT_FUNCTION_VERSION_MASK 0x60 +#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION 0x0005 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) + +struct pdt_entry { + u16 page_start; + u8 query_base_addr; + u8 command_base_addr; + u8 control_base_addr; + u8 data_base_addr; + u8 interrupt_source_count; + u8 function_version; + u8 function_number; +}; + +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, + u16 pdt_address); + +#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE) +#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE) + +/* describes a single packet register */ +struct rmi_register_desc_item { + u16 reg; + unsigned long reg_size; + u8 num_subpackets; + unsigned long subpacket_map[BITS_TO_LONGS( + RMI_REG_DESC_SUBPACKET_BITS)]; +}; + +/* + * describes the packet registers for a particular type + * (ie query, control, data) + */ +struct rmi_register_descriptor { + unsigned long struct_size; + unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)]; + u8 num_registers; + struct rmi_register_desc_item *registers; +}; + +int rmi_read_register_desc(struct rmi_device *d, u16 addr, + struct rmi_register_descriptor *rdesc); +const struct rmi_register_desc_item *rmi_get_register_desc_item( + struct rmi_register_descriptor *rdesc, u16 reg); + +/* + * Calculate the total size of all of the registers described in the + * descriptor. + */ +size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc); +int rmi_register_desc_calc_reg_offset( + struct rmi_register_descriptor *rdesc, u16 reg); +bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, + u8 subpacket); + +bool rmi_is_physical_driver(struct device_driver *); +int rmi_register_physical_driver(void); +void rmi_unregister_physical_driver(void); + +char *rmi_f01_get_product_ID(struct rmi_function *fn); + +extern struct rmi_function_handler rmi_f01_handler; + +#endif diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c new file mode 100644 index 000000000000..e50ecc6699e7 --- /dev/null +++ b/drivers/input/rmi4/rmi_f01.c @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_PRODUCT_ID_LENGTH 10 +#define RMI_PRODUCT_INFO_LENGTH 2 + +#define RMI_DATE_CODE_LENGTH 3 + +#define PRODUCT_ID_OFFSET 0x10 +#define PRODUCT_INFO_OFFSET 0x1E + + +/* Force a firmware reset of the sensor */ +#define RMI_F01_CMD_DEVICE_RESET 1 + +/* Various F01_RMI_QueryX bits */ + +#define RMI_F01_QRY1_CUSTOM_MAP BIT(0) +#define RMI_F01_QRY1_NON_COMPLIANT BIT(1) +#define RMI_F01_QRY1_HAS_LTS BIT(2) +#define RMI_F01_QRY1_HAS_SENSOR_ID BIT(3) +#define RMI_F01_QRY1_HAS_CHARGER_INP BIT(4) +#define RMI_F01_QRY1_HAS_ADJ_DOZE BIT(5) +#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF BIT(6) +#define RMI_F01_QRY1_HAS_QUERY42 BIT(7) + +#define RMI_F01_QRY5_YEAR_MASK 0x1f +#define RMI_F01_QRY6_MONTH_MASK 0x0f +#define RMI_F01_QRY7_DAY_MASK 0x1f + +#define RMI_F01_QRY2_PRODINFO_MASK 0x7f + +#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */ + +struct f01_basic_properties { + u8 manufacturer_id; + bool has_lts; + bool has_adjustable_doze; + bool has_adjustable_doze_holdoff; + char dom[11]; /* YYYY/MM/DD + '\0' */ + u8 product_id[RMI_PRODUCT_ID_LENGTH + 1]; + u16 productinfo; + u32 firmware_id; +}; + +/* F01 device status bits */ + +/* Most recent device status event */ +#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) +/* The device has lost its configuration for some reason. */ +#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) + +/* Control register bits */ + +/* + * Sleep mode controls power management on the device and affects all + * functions of the device. + */ +#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03 + +#define RMI_SLEEP_MODE_NORMAL 0x00 +#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01 +#define RMI_SLEEP_MODE_RESERVED0 0x02 +#define RMI_SLEEP_MODE_RESERVED1 0x03 + +/* + * This bit disables whatever sleep mode may be selected by the sleep_mode + * field and forces the device to run at full power without sleeping. + */ +#define RMI_F01_CRTL0_NOSLEEP_BIT BIT(2) + +/* + * When this bit is set, the touch controller employs a noise-filtering + * algorithm designed for use with a connected battery charger. + */ +#define RMI_F01_CRTL0_CHARGER_BIT BIT(5) + +/* + * Sets the report rate for the device. The effect of this setting is + * highly product dependent. Check the spec sheet for your particular + * touch sensor. + */ +#define RMI_F01_CRTL0_REPORTRATE_BIT BIT(6) + +/* + * Written by the host as an indicator that the device has been + * successfully configured. + */ +#define RMI_F01_CRTL0_CONFIGURED_BIT BIT(7) + +/** + * @ctrl0 - see the bit definitions above. + * @doze_interval - controls the interval between checks for finger presence + * when the touch sensor is in doze mode, in units of 10ms. + * @wakeup_threshold - controls the capacitance threshold at which the touch + * sensor will decide to wake up from that low power state. + * @doze_holdoff - controls how long the touch sensor waits after the last + * finger lifts before entering the doze state, in units of 100ms. + */ +struct f01_device_control { + u8 ctrl0; + u8 doze_interval; + u8 wakeup_threshold; + u8 doze_holdoff; +}; + +struct f01_data { + struct f01_basic_properties properties; + struct f01_device_control device_control; + + u16 doze_interval_addr; + u16 wakeup_threshold_addr; + u16 doze_holdoff_addr; + + bool suspended; + bool old_nosleep; + + unsigned int num_of_irq_regs; +}; + +static int rmi_f01_read_properties(struct rmi_device *rmi_dev, + u16 query_base_addr, + struct f01_basic_properties *props) +{ + u8 queries[RMI_F01_BASIC_QUERY_LEN]; + int ret; + int query_offset = query_base_addr; + bool has_ds4_queries = false; + bool has_query42 = false; + bool has_sensor_id = false; + bool has_package_id_query = false; + bool has_build_id_query = false; + u16 prod_info_addr; + u8 ds4_query_len; + + ret = rmi_read_block(rmi_dev, query_offset, + queries, RMI_F01_BASIC_QUERY_LEN); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read device query registers: %d\n", ret); + return ret; + } + + prod_info_addr = query_offset + 17; + query_offset += RMI_F01_BASIC_QUERY_LEN; + + /* Now parse what we got */ + props->manufacturer_id = queries[0]; + + props->has_lts = queries[1] & RMI_F01_QRY1_HAS_LTS; + props->has_adjustable_doze = + queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE; + props->has_adjustable_doze_holdoff = + queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF; + has_query42 = queries[1] & RMI_F01_QRY1_HAS_QUERY42; + has_sensor_id = queries[1] & RMI_F01_QRY1_HAS_SENSOR_ID; + + snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d", + queries[5] & RMI_F01_QRY5_YEAR_MASK, + queries[6] & RMI_F01_QRY6_MONTH_MASK, + queries[7] & RMI_F01_QRY7_DAY_MASK); + + memcpy(props->product_id, &queries[11], + RMI_PRODUCT_ID_LENGTH); + props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0'; + + props->productinfo = + ((queries[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) | + (queries[3] & RMI_F01_QRY2_PRODINFO_MASK); + + if (has_sensor_id) + query_offset++; + + if (has_query42) { + ret = rmi_read(rmi_dev, query_offset, queries); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read query 42 register: %d\n", ret); + return ret; + } + + has_ds4_queries = !!(queries[0] & BIT(0)); + query_offset++; + } + + if (has_ds4_queries) { + ret = rmi_read(rmi_dev, query_offset, &ds4_query_len); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read DS4 queries length: %d\n", ret); + return ret; + } + query_offset++; + + if (ds4_query_len > 0) { + ret = rmi_read(rmi_dev, query_offset, queries); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read DS4 queries: %d\n", + ret); + return ret; + } + + has_package_id_query = !!(queries[0] & BIT(0)); + has_build_id_query = !!(queries[0] & BIT(1)); + } + + if (has_package_id_query) + prod_info_addr++; + + if (has_build_id_query) { + ret = rmi_read_block(rmi_dev, prod_info_addr, queries, + 3); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read product info: %d\n", + ret); + return ret; + } + + props->firmware_id = queries[1] << 8 | queries[0]; + props->firmware_id += queries[2] * 65536; + } + } + + return 0; +} + +char *rmi_f01_get_product_ID(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + + return f01->properties.product_id; +} + +static int rmi_f01_probe(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct f01_data *f01; + int error; + u16 ctrl_base_addr = fn->fd.control_base_addr; + u8 device_status; + u8 temp; + + f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL); + if (!f01) + return -ENOMEM; + + f01->num_of_irq_regs = driver_data->num_of_irq_regs; + + /* + * Set the configured bit and (optionally) other important stuff + * in the device control register. + */ + + error = rmi_read(rmi_dev, fn->fd.control_base_addr, + &f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to read F01 control: %d\n", error); + return error; + } + + switch (pdata->power_management.nosleep) { + case RMI_F01_NOSLEEP_DEFAULT: + break; + case RMI_F01_NOSLEEP_OFF: + f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT; + break; + case RMI_F01_NOSLEEP_ON: + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + break; + } + + /* + * Sleep mode might be set as a hangover from a system crash or + * reboot without power cycle. If so, clear it so the sensor + * is certain to function. + */ + if ((f01->device_control.ctrl0 & RMI_F01_CTRL0_SLEEP_MODE_MASK) != + RMI_SLEEP_MODE_NORMAL) { + dev_warn(&fn->dev, + "WARNING: Non-zero sleep mode found. Clearing...\n"); + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + } + + f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT; + + error = rmi_write(rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to write F01 control: %d\n", error); + return error; + } + + /* Dummy read in order to clear irqs */ + error = rmi_read(rmi_dev, fn->fd.data_base_addr + 1, &temp); + if (error < 0) { + dev_err(&fn->dev, "Failed to read Interrupt Status.\n"); + return error; + } + + error = rmi_f01_read_properties(rmi_dev, fn->fd.query_base_addr, + &f01->properties); + if (error < 0) { + dev_err(&fn->dev, "Failed to read F01 properties.\n"); + return error; + } + + dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s, fw id: %d\n", + f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown", + f01->properties.product_id, f01->properties.firmware_id); + + /* Advance to interrupt control registers, then skip over them. */ + ctrl_base_addr++; + ctrl_base_addr += f01->num_of_irq_regs; + + /* read control register */ + if (f01->properties.has_adjustable_doze) { + f01->doze_interval_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.doze_interval) { + f01->device_control.doze_interval = + pdata->power_management.doze_interval; + error = rmi_write(rmi_dev, f01->doze_interval_addr, + f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 doze interval register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->doze_interval_addr, + &f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to read F01 doze interval register: %d\n", + error); + return error; + } + } + + f01->wakeup_threshold_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.wakeup_threshold) { + f01->device_control.wakeup_threshold = + pdata->power_management.wakeup_threshold; + error = rmi_write(rmi_dev, f01->wakeup_threshold_addr, + f01->device_control.wakeup_threshold); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 wakeup threshold register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->wakeup_threshold_addr, + &f01->device_control.wakeup_threshold); + if (error < 0) { + dev_err(&fn->dev, + "Failed to read F01 wakeup threshold register: %d\n", + error); + return error; + } + } + } + + if (f01->properties.has_lts) + ctrl_base_addr++; + + if (f01->properties.has_adjustable_doze_holdoff) { + f01->doze_holdoff_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.doze_holdoff) { + f01->device_control.doze_holdoff = + pdata->power_management.doze_holdoff; + error = rmi_write(rmi_dev, f01->doze_holdoff_addr, + f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 doze holdoff register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->doze_holdoff_addr, + &f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to read F01 doze holdoff register: %d\n", + error); + return error; + } + } + } + + error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status); + if (error < 0) { + dev_err(&fn->dev, + "Failed to read device status: %d\n", error); + return error; + } + + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { + dev_err(&fn->dev, + "Device was reset during configuration process, status: %#02x!\n", + RMI_F01_STATUS_CODE(device_status)); + return -EINVAL; + } + + dev_set_drvdata(&fn->dev, f01); + + return 0; +} + +static int rmi_f01_config(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, + "Failed to write device_control register: %d\n", error); + return error; + } + + if (f01->properties.has_adjustable_doze) { + error = rmi_write(fn->rmi_dev, f01->doze_interval_addr, + f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to write doze interval: %d\n", error); + return error; + } + + error = rmi_write_block(fn->rmi_dev, + f01->wakeup_threshold_addr, + &f01->device_control.wakeup_threshold, + sizeof(u8)); + if (error) { + dev_err(&fn->dev, + "Failed to write wakeup threshold: %d\n", + error); + return error; + } + } + + if (f01->properties.has_adjustable_doze_holdoff) { + error = rmi_write(fn->rmi_dev, f01->doze_holdoff_addr, + f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to write doze holdoff: %d\n", error); + return error; + } + } + + return 0; +} + +static int rmi_f01_suspend(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + f01->old_nosleep = + f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT; + f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT; + + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + if (device_may_wakeup(fn->rmi_dev->xport->dev)) + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_RESERVED1; + else + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_SENSOR_SLEEP; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error); + if (f01->old_nosleep) + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL; + return error; + } + + return 0; +} + +static int rmi_f01_resume(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + if (f01->old_nosleep) + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, + "Failed to restore normal operation: %d.\n", error); + return error; + } + + return 0; +} + +static int rmi_f01_attention(struct rmi_function *fn, + unsigned long *irq_bits) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + u8 device_status; + + error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status); + if (error) { + dev_err(&fn->dev, + "Failed to read device status: %d.\n", error); + return error; + } + + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { + dev_warn(&fn->dev, "Device reset detected.\n"); + error = rmi_dev->driver->reset_handler(rmi_dev); + if (error) { + dev_err(&fn->dev, "Device reset failed: %d\n", error); + return error; + } + } + + return 0; +} + +struct rmi_function_handler rmi_f01_handler = { + .driver = { + .name = "rmi4_f01", + /* + * Do not allow user unbinding F01 as it is critical + * function. + */ + .suppress_bind_attrs = true, + }, + .func = 0x01, + .probe = rmi_f01_probe, + .config = rmi_f01_config, + .attention = rmi_f01_attention, + .suspend = rmi_f01_suspend, + .resume = rmi_f01_resume, +}; diff --git a/include/linux/rmi.h b/include/linux/rmi.h new file mode 100644 index 000000000000..c748fa302067 --- /dev/null +++ b/include/linux/rmi.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _RMI_H +#define _RMI_H +#include +#include +#include +#include +#include +#include +#include + +#define NAME_BUFFER_SIZE 256 + +/** + * struct rmi_f01_power - override default power management settings. + * + */ +enum rmi_f01_nosleep { + RMI_F01_NOSLEEP_DEFAULT = 0, + RMI_F01_NOSLEEP_OFF = 1, + RMI_F01_NOSLEEP_ON = 2 +}; + +/** + * struct rmi_f01_power_management -When non-zero, these values will be written + * to the touch sensor to override the default firmware settigns. For a + * detailed explanation of what each field does, see the corresponding + * documention in the RMI4 specification. + * + * @nosleep - specifies whether the device is permitted to sleep or doze (that + * is, enter a temporary low power state) when no fingers are touching the + * sensor. + * @wakeup_threshold - controls the capacitance threshold at which the touch + * sensor will decide to wake up from that low power state. + * @doze_holdoff - controls how long the touch sensor waits after the last + * finger lifts before entering the doze state, in units of 100ms. + * @doze_interval - controls the interval between checks for finger presence + * when the touch sensor is in doze mode, in units of 10ms. + */ +struct rmi_f01_power_management { + enum rmi_f01_nosleep nosleep; + u8 wakeup_threshold; + u8 doze_holdoff; + u8 doze_interval; +}; + +/** + * struct rmi_device_platform_data - system specific configuration info. + * + * @reset_delay_ms - after issuing a reset command to the touch sensor, the + * driver waits a few milliseconds to give the firmware a chance to + * to re-initialize. You can override the default wait period here. + */ +struct rmi_device_platform_data { + int reset_delay_ms; + + /* function handler pdata */ + struct rmi_f01_power_management power_management; +}; + +/** + * struct rmi_function_descriptor - RMI function base addresses + * + * @query_base_addr: The RMI Query base address + * @command_base_addr: The RMI Command base address + * @control_base_addr: The RMI Control base address + * @data_base_addr: The RMI Data base address + * @interrupt_source_count: The number of irqs this RMI function needs + * @function_number: The RMI function number + * + * This struct is used when iterating the Page Description Table. The addresses + * are 16-bit values to include the current page address. + * + */ +struct rmi_function_descriptor { + u16 query_base_addr; + u16 command_base_addr; + u16 control_base_addr; + u16 data_base_addr; + u8 interrupt_source_count; + u8 function_number; + u8 function_version; +}; + +struct rmi_device; + +/** + * struct rmi_transport_dev - represent an RMI transport device + * + * @dev: Pointer to the communication device, e.g. i2c or spi + * @rmi_dev: Pointer to the RMI device + * @proto_name: name of the transport protocol (SPI, i2c, etc) + * @ops: pointer to transport operations implementation + * + * The RMI transport device implements the glue between different communication + * buses such as I2C and SPI. + * + */ +struct rmi_transport_dev { + struct device *dev; + struct rmi_device *rmi_dev; + + const char *proto_name; + const struct rmi_transport_ops *ops; + + struct rmi_device_platform_data pdata; + + struct input_dev *input; + + void *attn_data; + int attn_size; +}; + +/** + * struct rmi_transport_ops - defines transport protocol operations. + * + * @write_block: Writing a block of data to the specified address + * @read_block: Read a block of data from the specified address. + */ +struct rmi_transport_ops { + int (*write_block)(struct rmi_transport_dev *xport, u16 addr, + const void *buf, size_t len); + int (*read_block)(struct rmi_transport_dev *xport, u16 addr, + void *buf, size_t len); + int (*reset)(struct rmi_transport_dev *xport, u16 reset_addr); +}; + +/** + * struct rmi_driver - driver for an RMI4 sensor on the RMI bus. + * + * @driver: Device driver model driver + * @reset_handler: Called when a reset is detected. + * @clear_irq_bits: Clear the specified bits in the current interrupt mask. + * @set_irq_bist: Set the specified bits in the current interrupt mask. + * @store_productid: Callback for cache product id from function 01 + * @data: Private data pointer + * + */ +struct rmi_driver { + struct device_driver driver; + + int (*reset_handler)(struct rmi_device *rmi_dev); + int (*clear_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask); + int (*set_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask); + int (*store_productid)(struct rmi_device *rmi_dev); + int (*set_input_params)(struct rmi_device *rmi_dev, + struct input_dev *input); + void *data; +}; + +/** + * struct rmi_device - represents an RMI4 sensor device on the RMI bus. + * + * @dev: The device created for the RMI bus + * @number: Unique number for the device on the bus. + * @driver: Pointer to associated driver + * @xport: Pointer to the transport interface + * + */ +struct rmi_device { + struct device dev; + int number; + + struct rmi_driver *driver; + struct rmi_transport_dev *xport; + +}; + +struct rmi_driver_data { + struct list_head function_list; + + struct rmi_device *rmi_dev; + + struct rmi_function *f01_container; + bool f01_bootloader_mode; + + u32 attn_count; + int num_of_irq_regs; + int irq_count; + unsigned long *irq_status; + unsigned long *fn_irq_bits; + unsigned long *current_irq_mask; + unsigned long *new_irq_mask; + struct mutex irq_mutex; + struct input_dev *input; + + u8 pdt_props; + u8 bsr; + + bool enabled; + + void *data; +}; + +int rmi_register_transport_device(struct rmi_transport_dev *xport); +void rmi_unregister_transport_device(struct rmi_transport_dev *xport); +int rmi_process_interrupt_requests(struct rmi_device *rmi_dev); + +int rmi_driver_suspend(struct rmi_device *rmi_dev); +int rmi_driver_resume(struct rmi_device *rmi_dev); +#endif diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 2758687300b4..01113841190d 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -246,6 +246,7 @@ struct input_mask { #define BUS_GSC 0x1A #define BUS_ATARI 0x1B #define BUS_SPI 0x1C +#define BUS_RMI 0x1D /* * MT_TOOL types -- cgit v1.2.3 From fdf51604f104f95bbb828019e6b8f17e7a0f834a Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:44:27 -0800 Subject: Input: synaptics-rmi4 - add I2C transport driver Add the transport driver for devices using RMI4 over I2C. Signed-off-by: Andrew Duggan Signed-off-by: Christopher Heiny Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 9 + drivers/input/rmi4/Makefile | 3 + drivers/input/rmi4/rmi_i2c.c | 387 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) create mode 100644 drivers/input/rmi4/rmi_i2c.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 5ea60e338093..cc3f7c508c19 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -8,3 +8,12 @@ config RMI4_CORE required for all RMI4 device support. If unsure, say Y. + +config RMI4_I2C + tristate "RMI4 I2C Support" + depends on RMI4_CORE && I2C + help + Say Y here if you want to support RMI4 devices connected to an I2C + bus. + + If unsure, say Y. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 12f219779b74..5539494aecd1 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -1,2 +1,5 @@ obj-$(CONFIG_RMI4_CORE) += rmi_core.o rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o + +# Transports +obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c new file mode 100644 index 000000000000..f93742b6537e --- /dev/null +++ b/drivers/input/rmi4/rmi_i2c.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include "rmi_driver.h" + +#define BUFFER_SIZE_INCREMENT 32 + +/** + * struct rmi_i2c_xport - stores information for i2c communication + * + * @xport: The transport interface structure + * + * @page_mutex: Locks current page to avoid changing pages in unexpected ways. + * @page: Keeps track of the current virtual page + * + * @tx_buf: Buffer used for transmitting data to the sensor over i2c. + * @tx_buf_size: Size of the buffer + */ +struct rmi_i2c_xport { + struct rmi_transport_dev xport; + struct i2c_client *client; + + struct mutex page_mutex; + int page; + + int irq; + + u8 *tx_buf; + size_t tx_buf_size; +}; + +#define RMI_PAGE_SELECT_REGISTER 0xff +#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff) + +/* + * rmi_set_page - Set RMI page + * @xport: The pointer to the rmi_transport_dev struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the transport + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct rmi_i2c_xport *rmi_i2c, u8 page) +{ + struct i2c_client *client = rmi_i2c->client; + u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page}; + int retval; + + retval = i2c_master_send(client, txbuf, sizeof(txbuf)); + if (retval != sizeof(txbuf)) { + dev_err(&client->dev, + "%s: set page failed: %d.", __func__, retval); + return (retval < 0) ? retval : -EIO; + } + + rmi_i2c->page = page; + return 0; +} + +static int rmi_i2c_write_block(struct rmi_transport_dev *xport, u16 addr, + const void *buf, size_t len) +{ + struct rmi_i2c_xport *rmi_i2c = + container_of(xport, struct rmi_i2c_xport, xport); + struct i2c_client *client = rmi_i2c->client; + size_t tx_size = len + 1; + int retval; + + mutex_lock(&rmi_i2c->page_mutex); + + if (!rmi_i2c->tx_buf || rmi_i2c->tx_buf_size < tx_size) { + if (rmi_i2c->tx_buf) + devm_kfree(&client->dev, rmi_i2c->tx_buf); + rmi_i2c->tx_buf_size = tx_size + BUFFER_SIZE_INCREMENT; + rmi_i2c->tx_buf = devm_kzalloc(&client->dev, + rmi_i2c->tx_buf_size, + GFP_KERNEL); + if (!rmi_i2c->tx_buf) { + rmi_i2c->tx_buf_size = 0; + retval = -ENOMEM; + goto exit; + } + } + + rmi_i2c->tx_buf[0] = addr & 0xff; + memcpy(rmi_i2c->tx_buf + 1, buf, len); + + if (RMI_I2C_PAGE(addr) != rmi_i2c->page) { + retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr)); + if (retval) + goto exit; + } + + retval = i2c_master_send(client, rmi_i2c->tx_buf, tx_size); + if (retval == tx_size) + retval = 0; + else if (retval >= 0) + retval = -EIO; + +exit: + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "write %zd bytes at %#06x: %d (%*ph)\n", + len, addr, retval, (int)len, buf); + + mutex_unlock(&rmi_i2c->page_mutex); + return retval; +} + +static int rmi_i2c_read_block(struct rmi_transport_dev *xport, u16 addr, + void *buf, size_t len) +{ + struct rmi_i2c_xport *rmi_i2c = + container_of(xport, struct rmi_i2c_xport, xport); + struct i2c_client *client = rmi_i2c->client; + u8 addr_offset = addr & 0xff; + int retval; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .len = sizeof(addr_offset), + .buf = &addr_offset, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + mutex_lock(&rmi_i2c->page_mutex); + + if (RMI_I2C_PAGE(addr) != rmi_i2c->page) { + retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr)); + if (retval) + goto exit; + } + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval == ARRAY_SIZE(msgs)) + retval = 0; /* success */ + else if (retval >= 0) + retval = -EIO; + +exit: + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "read %zd bytes at %#06x: %d (%*ph)\n", + len, addr, retval, (int)len, buf); + + mutex_unlock(&rmi_i2c->page_mutex); + return retval; +} + +static const struct rmi_transport_ops rmi_i2c_ops = { + .write_block = rmi_i2c_write_block, + .read_block = rmi_i2c_read_block, +}; + +static irqreturn_t rmi_i2c_irq(int irq, void *dev_id) +{ + struct rmi_i2c_xport *rmi_i2c = dev_id; + struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_i2c_init_irq(struct i2c_client *client) +{ + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq)); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL, + rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name, + rmi_i2c); + if (ret < 0) { + dev_warn(&client->dev, "Failed to register interrupt %d\n", + rmi_i2c->irq); + + return ret; + } + + return 0; +} + +static int rmi_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rmi_device_platform_data *pdata; + struct rmi_device_platform_data *client_pdata = + dev_get_platdata(&client->dev); + struct rmi_i2c_xport *rmi_i2c; + int retval; + + rmi_i2c = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_xport), + GFP_KERNEL); + if (!rmi_i2c) + return -ENOMEM; + + pdata = &rmi_i2c->xport.pdata; + + if (client_pdata) + *pdata = *client_pdata; + + if (client->irq > 0) + rmi_i2c->irq = client->irq; + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", + dev_name(&client->dev)); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "adapter does not support required functionality.\n"); + return -ENODEV; + } + + rmi_i2c->client = client; + mutex_init(&rmi_i2c->page_mutex); + + rmi_i2c->xport.dev = &client->dev; + rmi_i2c->xport.proto_name = "i2c"; + rmi_i2c->xport.ops = &rmi_i2c_ops; + + i2c_set_clientdata(client, rmi_i2c); + + /* + * Setting the page to zero will (a) make sure the PSR is in a + * known state, and (b) make sure we can talk to the device. + */ + retval = rmi_set_page(rmi_i2c, 0); + if (retval) { + dev_err(&client->dev, "Failed to set page select to 0.\n"); + return retval; + } + + retval = rmi_register_transport_device(&rmi_i2c->xport); + if (retval) { + dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n", + client->addr); + return retval; + } + + retval = rmi_i2c_init_irq(client); + if (retval < 0) + return retval; + + dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n", + client->addr); + return 0; +} + +static int rmi_i2c_remove(struct i2c_client *client) +{ + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + + rmi_unregister_transport_device(&rmi_i2c->xport); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rmi_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_i2c->irq); + if (device_may_wakeup(&client->dev)) { + ret = enable_irq_wake(rmi_i2c->irq); + if (!ret) + dev_warn(dev, "Failed to enable irq for wake: %d\n", + ret); + } + return ret; +} + +static int rmi_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + enable_irq(rmi_i2c->irq); + if (device_may_wakeup(&client->dev)) { + ret = disable_irq_wake(rmi_i2c->irq); + if (!ret) + dev_warn(dev, "Failed to disable irq for wake: %d\n", + ret); + } + + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return ret; +} +#endif + +#ifdef CONFIG_PM +static int rmi_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_i2c->irq); + + return 0; +} + +static int rmi_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + enable_irq(rmi_i2c->irq); + + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} +#endif + +static const struct dev_pm_ops rmi_i2c_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_i2c_suspend, rmi_i2c_resume) + SET_RUNTIME_PM_OPS(rmi_i2c_runtime_suspend, rmi_i2c_runtime_resume, + NULL) +}; + +static const struct i2c_device_id rmi_id[] = { + { "rmi4_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rmi_id); + +static struct i2c_driver rmi_i2c_driver = { + .driver = { + .name = "rmi4_i2c", + .pm = &rmi_i2c_pm, + }, + .id_table = rmi_id, + .probe = rmi_i2c_probe, + .remove = rmi_i2c_remove, +}; + +module_i2c_driver(rmi_i2c_driver); + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_AUTHOR("Andrew Duggan "); +MODULE_DESCRIPTION("RMI I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION); -- cgit v1.2.3 From d8a8b3edfd922e3886684a3434bd2b752167ff29 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:46:32 -0800 Subject: Input: synaptics-rmi4 - add device tree support for RMI4 I2C devices Add devicetree binding for I2C devices and add bindings for optional parameters in the function drivers. Parameters for function drivers are defined in child nodes for each of the functions. Signed-off-by: Andrew Duggan Acked-by: Rob Herring Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/rmi4/rmi_f01.txt | 39 ++++++++++++++++ .../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/input/rmi4/rmi_bus.c | 35 ++++++++++++++ drivers/input/rmi4/rmi_bus.h | 4 -- drivers/input/rmi4/rmi_driver.c | 28 ++++++++++++ drivers/input/rmi4/rmi_f01.c | 50 ++++++++++++++++++++ drivers/input/rmi4/rmi_i2c.c | 12 ++++- 8 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt new file mode 100644 index 000000000000..079cad2b6843 --- /dev/null +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt @@ -0,0 +1,39 @@ +Synaptics RMI4 F01 Device Binding + +The Synaptics RMI4 core is able to support RMI4 devices using different +transports and different functions. This file describes the device tree +bindings for devices which contain Function 1. Complete documentation +for transports and other functions can be found in: +Documentation/devicetree/bindings/input/rmi4. + +Additional documentation for F01 can be found at: +http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf + +Optional Properties: +- syna,nosleep-mode: If set the device will run at full power without sleeping. + nosleep has 3 modes, 0 will not change the default + setting, 1 will disable nosleep (allow sleeping), + and 2 will enable nosleep (disabling sleep). +- syna,wakeup-threshold: Defines the amplitude of the disturbance to the + background capacitance that will cause the + device to wake from dozing. +- syna,doze-holdoff-ms: The delay to wait after the last finger lift and the + first doze cycle. +- syna,doze-interval-ms: The time period that the device sleeps between finger + activity. + + +Example of a RMI4 I2C device with F01: + Example: + &i2c1 { + rmi4-i2c-dev@2c { + compatible = "syna,rmi4-i2c"; + + ... + + rmi4-f01@1 { + reg = <0x1>; + syna,nosleep-mode = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt new file mode 100644 index 000000000000..95fa715c6046 --- /dev/null +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt @@ -0,0 +1,53 @@ +Synaptics RMI4 I2C Device Binding + +The Synaptics RMI4 core is able to support RMI4 devices using different +transports and different functions. This file describes the device tree +bindings for devices using the I2C transport driver. Complete documentation +for other transports and functions can be found in +Documentation/devicetree/bindings/input/rmi4. + +Required Properties: +- compatible: syna,rmi4-i2c +- reg: I2C address +- #address-cells: Set to 1 to indicate that the function child nodes + consist of only on uint32 value. +- #size-cells: Set to 0 to indicate that the function child nodes do not + have a size property. + +Optional Properties: +- interrupts: interrupt which the rmi device is connected to. +- interrupt-parent: The interrupt controller. +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +- syna,reset-delay-ms: The number of milliseconds to wait after resetting the + device. + +Function Parameters: +Parameters specific to RMI functions are contained in child nodes of the rmi device + node. Documentation for the parameters of each function can be found in: +Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt. + + + +Example: + &i2c1 { + rmi4-i2c-dev@2c { + compatible = "syna,rmi4-i2c"; + reg = <0x2c>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&gpio>; + interrupts = <4 2>; + + rmi4-f01@1 { + reg = <0x1>; + syna,nosleep-mode = <1>; + }; + + rmi4-f11@11 { + reg = <0x11>; + touchscreen-inverted-y; + syna,sensor-type = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 55df1d444e9f..7a1bf68a70fa 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -220,6 +220,7 @@ sprd Spreadtrum Communications Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson +syna Synaptics Inc. synology Synology, Inc. tbs TBS Technologies tcl Toby Churchill Ltd. diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 0a2bd5a0f2b7..434477ea0c4e 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -153,6 +153,21 @@ static int rmi_function_match(struct device *dev, struct device_driver *drv) return fn->fd.function_number == handler->func; } +#ifdef CONFIG_OF +static void rmi_function_of_probe(struct rmi_function *fn) +{ + char of_name[9]; + + snprintf(of_name, sizeof(of_name), "rmi4-f%02x", + fn->fd.function_number); + fn->dev.of_node = of_find_node_by_name( + fn->rmi_dev->xport->dev->of_node, of_name); +} +#else +static inline void rmi_function_of_probe(struct rmi_function *fn) +{} +#endif + static int rmi_function_probe(struct device *dev) { struct rmi_function *fn = to_rmi_function(dev); @@ -160,6 +175,8 @@ static int rmi_function_probe(struct device *dev) to_rmi_function_handler(dev->driver); int error; + rmi_function_of_probe(fn); + if (handler->probe) { error = handler->probe(fn); return error; @@ -325,6 +342,24 @@ err_unregister_function_handlers: return ret; } +int rmi_of_property_read_u32(struct device *dev, u32 *result, + const char *prop, bool optional) +{ + int retval; + u32 val = 0; + + retval = of_property_read_u32(dev->of_node, prop, &val); + if (retval && (!optional && retval == -EINVAL)) { + dev_err(dev, "Failed to get %s value: %d\n", + prop, retval); + return retval; + } + *result = val; + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_of_property_read_u32); + static int __init rmi_bus_init(void) { int error; diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index 13b148d44b37..899579830536 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -172,10 +172,6 @@ extern struct bus_type rmi_bus_type; int rmi_of_property_read_u32(struct device *dev, u32 *result, const char *prop, bool optional); -int rmi_of_property_read_u16(struct device *dev, u16 *result, - const char *prop, bool optional); -int rmi_of_property_read_u8(struct device *dev, u8 *result, - const char *prop, bool optional); #define RMI_DEBUG_CORE BIT(0) #define RMI_DEBUG_XPORT BIT(1) diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index b0f34b57a126..da38f0ad80ed 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include "rmi_bus.h" @@ -821,6 +822,27 @@ static int rmi_driver_remove(struct device *dev) return 0; } +#ifdef CONFIG_OF +static int rmi_driver_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + int retval; + + retval = rmi_of_property_read_u32(dev, &pdata->reset_delay_ms, + "syna,reset-delay-ms", 1); + if (retval) + return retval; + + return 0; +} +#else +static inline int rmi_driver_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int rmi_driver_probe(struct device *dev) { struct rmi_driver *rmi_driver; @@ -846,6 +868,12 @@ static int rmi_driver_probe(struct device *dev) pdata = rmi_get_platform_data(rmi_dev); + if (rmi_dev->xport->dev->of_node) { + retval = rmi_driver_of_probe(rmi_dev->xport->dev, pdata); + if (retval) + return retval; + } + data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index e50ecc6699e7..eb362bc71a4c 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -247,6 +247,50 @@ char *rmi_f01_get_product_ID(struct rmi_function *fn) return f01->properties.product_id; } +#ifdef CONFIG_OF +static int rmi_f01_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + int retval; + u32 val; + + retval = rmi_of_property_read_u32(dev, + (u32 *)&pdata->power_management.nosleep, + "syna,nosleep-mode", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,wakeup-threshold", 1); + if (retval) + return retval; + + pdata->power_management.wakeup_threshold = val; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,doze-holdoff-ms", 1); + if (retval) + return retval; + + pdata->power_management.doze_holdoff = val * 100; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,doze-interval-ms", 1); + if (retval) + return retval; + + pdata->power_management.doze_interval = val / 10; + + return 0; +} +#else +static inline int rmi_f01_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int rmi_f01_probe(struct rmi_function *fn) { struct rmi_device *rmi_dev = fn->rmi_dev; @@ -258,6 +302,12 @@ static int rmi_f01_probe(struct rmi_function *fn) u8 device_status; u8 temp; + if (fn->dev.of_node) { + error = rmi_f01_of_probe(&fn->dev, pdata); + if (error) + return error; + } + f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL); if (!f01) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index f93742b6537e..a96a326b53bd 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "rmi_driver.h" #define BUFFER_SIZE_INCREMENT 32 @@ -207,6 +208,14 @@ static int rmi_i2c_init_irq(struct i2c_client *client) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id rmi_i2c_of_match[] = { + { .compatible = "syna,rmi4-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rmi_i2c_of_match); +#endif + static int rmi_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -223,7 +232,7 @@ static int rmi_i2c_probe(struct i2c_client *client, pdata = &rmi_i2c->xport.pdata; - if (client_pdata) + if (!client->dev.of_node && client_pdata) *pdata = *client_pdata; if (client->irq > 0) @@ -372,6 +381,7 @@ static struct i2c_driver rmi_i2c_driver = { .driver = { .name = "rmi4_i2c", .pm = &rmi_i2c_pm, + .of_match_table = of_match_ptr(rmi_i2c_of_match), }, .id_table = rmi_id, .probe = rmi_i2c_probe, -- cgit v1.2.3 From ff8f83708b3e36c050dc3fd7e2f04ea7f1752599 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:47:28 -0800 Subject: Input: synaptics-rmi4 - add support for 2D sensors and F11 RMI4 currently defines two functions for reporting data for 2D sensors (F11 and F12). This patch adds the common functionality which is shared by devices with 2D reporting along with implementing functionality for F11. Signed-off-by: Andrew Duggan Signed-off-by: Christopher Heiny Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 15 + drivers/input/rmi4/Makefile | 5 + drivers/input/rmi4/rmi_2d_sensor.c | 221 ++++++ drivers/input/rmi4/rmi_2d_sensor.h | 84 +++ drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 2 +- drivers/input/rmi4/rmi_f11.c | 1312 ++++++++++++++++++++++++++++++++++++ include/linux/rmi.h | 83 +++ 8 files changed, 1724 insertions(+), 1 deletion(-) create mode 100644 drivers/input/rmi4/rmi_2d_sensor.c create mode 100644 drivers/input/rmi4/rmi_2d_sensor.h create mode 100644 drivers/input/rmi4/rmi_f11.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index cc3f7c508c19..dc7de6bbb8e6 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -17,3 +17,18 @@ config RMI4_I2C bus. If unsure, say Y. + +config RMI4_2D_SENSOR + bool + depends on RMI4_CORE + +config RMI4_F11 + bool "RMI4 Function 11 (2D pointing)" + select RMI4_2D_SENSOR + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 11. + + Function 11 provides 2D multifinger pointing for touchscreens and + touchpads. For sensors that support relative pointing, F11 also + provides mouse input. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 5539494aecd1..9e7424ae1c72 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -1,5 +1,10 @@ obj-$(CONFIG_RMI4_CORE) += rmi_core.o rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o +rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o + +# Function drivers +rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o + # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c new file mode 100644 index 000000000000..8f48647ca5b0 --- /dev/null +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +#define RMI_2D_REL_POS_MIN -128 +#define RMI_2D_REL_POS_MAX 127 + +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + +void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + + /* we keep the previous values if the finger is released */ + if (obj->type == RMI_2D_OBJECT_NONE) + return; + + if (axis_align->swap_axes) + swap(obj->x, obj->y); + + if (axis_align->flip_x) + obj->x = sensor->max_x - obj->x; + + if (axis_align->flip_y) + obj->y = sensor->max_y - obj->y; + + /* + * Here checking if X offset or y offset are specified is + * redundant. We just add the offsets or clip the values. + * + * Note: offsets need to be applied before clipping occurs, + * or we could get funny values that are outside of + * clipping boundaries. + */ + obj->x += axis_align->offset_x; + obj->y += axis_align->offset_y; + + obj->x = max(axis_align->clip_x_low, obj->x); + obj->y = max(axis_align->clip_y_low, obj->y); + + if (axis_align->clip_x_high) + obj->x = min(sensor->max_x, obj->x); + + if (axis_align->clip_y_high) + obj->y = min(sensor->max_y, obj->y); + + sensor->tracking_pos[slot].x = obj->x; + sensor->tracking_pos[slot].y = obj->y; +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_process); + +void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + struct input_dev *input = sensor->input; + int wide, major, minor; + + if (sensor->kernel_tracking) + input_mt_slot(input, sensor->tracking_slots[slot]); + else + input_mt_slot(input, slot); + + input_mt_report_slot_state(input, obj->mt_tool, + obj->type != RMI_2D_OBJECT_NONE); + + if (obj->type != RMI_2D_OBJECT_NONE) { + obj->x = sensor->tracking_pos[slot].x; + obj->y = sensor->tracking_pos[slot].y; + + if (axis_align->swap_axes) + swap(obj->wx, obj->wy); + + wide = (obj->wx > obj->wy); + major = max(obj->wx, obj->wy); + minor = min(obj->wx, obj->wy); + + if (obj->type == RMI_2D_OBJECT_STYLUS) { + major = max(1, major); + minor = max(1, minor); + } + + input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x); + input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y); + input_event(sensor->input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(sensor->input, EV_ABS, ABS_MT_PRESSURE, obj->z); + input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + + rmi_dbg(RMI_DEBUG_2D_SENSOR, &sensor->input->dev, + "%s: obj[%d]: type: 0x%02x X: %d Y: %d Z: %d WX: %d WY: %d\n", + __func__, slot, obj->type, obj->x, obj->y, obj->z, + obj->wx, obj->wy); + } +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_report); + +void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + + x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x)); + y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y)); + + if (axis_align->swap_axes) + swap(x, y); + + if (axis_align->flip_x) + x = min(RMI_2D_REL_POS_MAX, -x); + + if (axis_align->flip_y) + y = min(RMI_2D_REL_POS_MAX, -y); + + if (x || y) { + input_report_rel(sensor->input, REL_X, x); + input_report_rel(sensor->input, REL_Y, y); + } +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_rel_report); + +static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) +{ + struct input_dev *input = sensor->input; + int res_x; + int res_y; + int input_flags = 0; + + if (sensor->report_abs) { + if (sensor->axis_align.swap_axes) + swap(sensor->max_x, sensor->max_y); + + sensor->min_x = sensor->axis_align.clip_x_low; + if (sensor->axis_align.clip_x_high) + sensor->max_x = min(sensor->max_x, + sensor->axis_align.clip_x_high); + + sensor->min_y = sensor->axis_align.clip_y_low; + if (sensor->axis_align.clip_y_high) + sensor->max_y = min(sensor->max_y, + sensor->axis_align.clip_y_high); + + set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x, + 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y, + 0, 0); + + if (sensor->x_mm && sensor->y_mm) { + res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm; + res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm; + + input_abs_set_res(input, ABS_X, res_x); + input_abs_set_res(input, ABS_Y, res_y); + + input_abs_set_res(input, ABS_MT_POSITION_X, res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); + + if (!sensor->dmax) + sensor->dmax = DMAX * res_x; + } + + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + + if (sensor->sensor_type == rmi_sensor_touchpad) + input_flags = INPUT_MT_POINTER; + else + input_flags = INPUT_MT_DIRECT; + + if (sensor->kernel_tracking) + input_flags |= INPUT_MT_TRACK; + + input_mt_init_slots(input, sensor->nbr_fingers, input_flags); + } + + if (sensor->report_rel) { + set_bit(EV_REL, input->evbit); + set_bit(REL_X, input->relbit); + set_bit(REL_Y, input->relbit); + } + + if (sensor->topbuttonpad) + set_bit(INPUT_PROP_TOPBUTTONPAD, input->propbit); +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_set_input_params); + +int rmi_2d_sensor_configure_input(struct rmi_function *fn, + struct rmi_2d_sensor *sensor) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + + if (!drv_data->input) + return -ENODEV; + + sensor->input = drv_data->input; + rmi_2d_sensor_set_input_params(sensor); + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_configure_input); diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h new file mode 100644 index 000000000000..6a715d392ec2 --- /dev/null +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _RMI_2D_SENSOR_H +#define _RMI_2D_SENSOR_H + +enum rmi_2d_sensor_object_type { + RMI_2D_OBJECT_NONE, + RMI_2D_OBJECT_FINGER, + RMI_2D_OBJECT_STYLUS, + RMI_2D_OBJECT_PALM, + RMI_2D_OBJECT_UNCLASSIFIED, +}; + +struct rmi_2d_sensor_abs_object { + enum rmi_2d_sensor_object_type type; + int mt_tool; + u16 x; + u16 y; + u8 z; + u8 wx; + u8 wy; +}; + +/** + * @axis_align - controls parameters that are useful in system prototyping + * and bring up. + * @max_x - The maximum X coordinate that will be reported by this sensor. + * @max_y - The maximum Y coordinate that will be reported by this sensor. + * @nbr_fingers - How many fingers can this sensor report? + * @data_pkt - buffer for data reported by this sensor. + * @pkt_size - number of bytes in that buffer. + * @attn_size - Size of the HID attention report (only contains abs data). + * position when two fingers are on the device. When this is true, we + * assume we have one of those sensors and report events appropriately. + * @sensor_type - indicates whether we're touchscreen or touchpad. + * @input - input device for absolute pointing stream + * @input_phys - buffer for the absolute phys name for this sensor. + */ +struct rmi_2d_sensor { + struct rmi_2d_axis_alignment axis_align; + struct input_mt_pos *tracking_pos; + int *tracking_slots; + bool kernel_tracking; + struct rmi_2d_sensor_abs_object *objs; + int dmax; + u16 min_x; + u16 max_x; + u16 min_y; + u16 max_y; + u8 nbr_fingers; + u8 *data_pkt; + int pkt_size; + int attn_size; + bool topbuttonpad; + enum rmi_sensor_type sensor_type; + struct input_dev *input; + struct rmi_function *fn; + char input_phys[32]; + u8 report_abs; + u8 report_rel; + u8 x_mm; + u8 y_mm; +}; + +void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot); + +void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot); + +void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y); + +int rmi_2d_sensor_configure_input(struct rmi_function *fn, + struct rmi_2d_sensor *sensor); +#endif /* _RMI_2D_SENSOR_H */ diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 434477ea0c4e..2e3878343961 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -306,6 +306,9 @@ struct bus_type rmi_bus_type = { static struct rmi_function_handler *fn_handlers[] = { &rmi_f01_handler, +#ifdef CONFIG_RMI4_F11 + &rmi_f11_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 3ab3f1a8078f..09d2aa7f11d4 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -99,5 +99,5 @@ void rmi_unregister_physical_driver(void); char *rmi_f01_get_product_ID(struct rmi_function *fn); extern struct rmi_function_handler rmi_f01_handler; - +extern struct rmi_function_handler rmi_f11_handler; #endif diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c new file mode 100644 index 000000000000..948a360b6261 --- /dev/null +++ b/drivers/input/rmi4/rmi_f11.c @@ -0,0 +1,1312 @@ +/* + * Copyright (c) 2011-2015 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +#define F11_MAX_NUM_OF_FINGERS 10 +#define F11_MAX_NUM_OF_TOUCH_SHAPES 16 + +#define FINGER_STATE_MASK 0x03 + +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6 +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8 + +#define DEFAULT_XY_MAX 9999 +#define DEFAULT_MAX_ABS_MT_PRESSURE 255 +#define DEFAULT_MAX_ABS_MT_TOUCH 15 +#define DEFAULT_MAX_ABS_MT_ORIENTATION 1 +#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1 +#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10 + +/** A note about RMI4 F11 register structure. + * + * The properties for + * a given sensor are described by its query registers. The number of query + * registers and the layout of their contents are described by the F11 device + * queries as well as the sensor query information. + * + * Similarly, each sensor has control registers that govern its behavior. The + * size and layout of the control registers for a given sensor can be determined + * by parsing that sensors query registers. + * + * And in a likewise fashion, each sensor has data registers where it reports + * its touch data and other interesting stuff. The size and layout of a + * sensors data registers must be determined by parsing its query registers. + * + * The short story is that we need to read and parse a lot of query + * registers in order to determine the attributes of a sensor. Then + * we need to use that data to compute the size of the control and data + * registers for sensor. + * + * The end result is that we have a number of structs that aren't used to + * directly generate the input events, but their size, location and contents + * are critical to determining where the data we are interested in lives. + * + * At this time, the driver does not yet comprehend all possible F11 + * configuration options, but it should be sufficient to cover 99% of RMI4 F11 + * devices currently in the field. + */ + +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + +/** + * @rezero - writing this to the F11 command register will cause the sensor to + * calibrate to the current capacitive state. + */ +#define RMI_F11_REZERO 0x01 + +#define RMI_F11_HAS_QUERY9 (1 << 3) +#define RMI_F11_HAS_QUERY11 (1 << 4) +#define RMI_F11_HAS_QUERY12 (1 << 5) +#define RMI_F11_HAS_QUERY27 (1 << 6) +#define RMI_F11_HAS_QUERY28 (1 << 7) + +/** Defs for Query 1 */ + +#define RMI_F11_NR_FINGERS_MASK 0x07 +#define RMI_F11_HAS_REL (1 << 3) +#define RMI_F11_HAS_ABS (1 << 4) +#define RMI_F11_HAS_GESTURES (1 << 5) +#define RMI_F11_HAS_SENSITIVITY_ADJ (1 << 6) +#define RMI_F11_CONFIGURABLE (1 << 7) + +/** Defs for Query 2, 3, and 4. */ +#define RMI_F11_NR_ELECTRODES_MASK 0x7F + +/** Defs for Query 5 */ + +#define RMI_F11_ABS_DATA_SIZE_MASK 0x03 +#define RMI_F11_HAS_ANCHORED_FINGER (1 << 2) +#define RMI_F11_HAS_ADJ_HYST (1 << 3) +#define RMI_F11_HAS_DRIBBLE (1 << 4) +#define RMI_F11_HAS_BENDING_CORRECTION (1 << 5) +#define RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION (1 << 6) +#define RMI_F11_HAS_JITTER_FILTER (1 << 7) + +/** Defs for Query 7 */ +#define RMI_F11_HAS_SINGLE_TAP (1 << 0) +#define RMI_F11_HAS_TAP_AND_HOLD (1 << 1) +#define RMI_F11_HAS_DOUBLE_TAP (1 << 2) +#define RMI_F11_HAS_EARLY_TAP (1 << 3) +#define RMI_F11_HAS_FLICK (1 << 4) +#define RMI_F11_HAS_PRESS (1 << 5) +#define RMI_F11_HAS_PINCH (1 << 6) +#define RMI_F11_HAS_CHIRAL (1 << 7) + +/** Defs for Query 8 */ +#define RMI_F11_HAS_PALM_DET (1 << 0) +#define RMI_F11_HAS_ROTATE (1 << 1) +#define RMI_F11_HAS_TOUCH_SHAPES (1 << 2) +#define RMI_F11_HAS_SCROLL_ZONES (1 << 3) +#define RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES (1 << 4) +#define RMI_F11_HAS_MF_SCROLL (1 << 5) +#define RMI_F11_HAS_MF_EDGE_MOTION (1 << 6) +#define RMI_F11_HAS_MF_SCROLL_INERTIA (1 << 7) + +/** Defs for Query 9. */ +#define RMI_F11_HAS_PEN (1 << 0) +#define RMI_F11_HAS_PROXIMITY (1 << 1) +#define RMI_F11_HAS_PALM_DET_SENSITIVITY (1 << 2) +#define RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT (1 << 3) +#define RMI_F11_HAS_TWO_PEN_THRESHOLDS (1 << 4) +#define RMI_F11_HAS_CONTACT_GEOMETRY (1 << 5) +#define RMI_F11_HAS_PEN_HOVER_DISCRIMINATION (1 << 6) +#define RMI_F11_HAS_PEN_FILTERS (1 << 7) + +/** Defs for Query 10. */ +#define RMI_F11_NR_TOUCH_SHAPES_MASK 0x1F + +/** Defs for Query 11 */ + +#define RMI_F11_HAS_Z_TUNING (1 << 0) +#define RMI_F11_HAS_ALGORITHM_SELECTION (1 << 1) +#define RMI_F11_HAS_W_TUNING (1 << 2) +#define RMI_F11_HAS_PITCH_INFO (1 << 3) +#define RMI_F11_HAS_FINGER_SIZE (1 << 4) +#define RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS (1 << 5) +#define RMI_F11_HAS_XY_CLIP (1 << 6) +#define RMI_F11_HAS_DRUMMING_FILTER (1 << 7) + +/** Defs for Query 12. */ + +#define RMI_F11_HAS_GAPLESS_FINGER (1 << 0) +#define RMI_F11_HAS_GAPLESS_FINGER_TUNING (1 << 1) +#define RMI_F11_HAS_8BIT_W (1 << 2) +#define RMI_F11_HAS_ADJUSTABLE_MAPPING (1 << 3) +#define RMI_F11_HAS_INFO2 (1 << 4) +#define RMI_F11_HAS_PHYSICAL_PROPS (1 << 5) +#define RMI_F11_HAS_FINGER_LIMIT (1 << 6) +#define RMI_F11_HAS_LINEAR_COEFF (1 << 7) + +/** Defs for Query 13. */ + +#define RMI_F11_JITTER_WINDOW_MASK 0x1F +#define RMI_F11_JITTER_FILTER_MASK 0x60 +#define RMI_F11_JITTER_FILTER_SHIFT 5 + +/** Defs for Query 14. */ +#define RMI_F11_LIGHT_CONTROL_MASK 0x03 +#define RMI_F11_IS_CLEAR (1 << 2) +#define RMI_F11_CLICKPAD_PROPS_MASK 0x18 +#define RMI_F11_CLICKPAD_PROPS_SHIFT 3 +#define RMI_F11_MOUSE_BUTTONS_MASK 0x60 +#define RMI_F11_MOUSE_BUTTONS_SHIFT 5 +#define RMI_F11_HAS_ADVANCED_GESTURES (1 << 7) + +#define RMI_F11_QUERY_SIZE 4 +#define RMI_F11_QUERY_GESTURE_SIZE 2 + +#define F11_LIGHT_CTL_NONE 0x00 +#define F11_LUXPAD 0x01 +#define F11_DUAL_MODE 0x02 + +#define F11_NOT_CLICKPAD 0x00 +#define F11_HINGED_CLICKPAD 0x01 +#define F11_UNIFORM_CLICKPAD 0x02 + +/** + * Query registers 1 through 4 are always present. + * + * @nr_fingers - describes the maximum number of fingers the 2-D sensor + * supports. + * @has_rel - the sensor supports relative motion reporting. + * @has_abs - the sensor supports absolute poition reporting. + * @has_gestures - the sensor supports gesture reporting. + * @has_sensitivity_adjust - the sensor supports a global sensitivity + * adjustment. + * @configurable - the sensor supports various configuration options. + * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor + * supports on the X axis. + * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor + * supports on the Y axis. + * @max_electrodes - the total number of X and Y electrodes that may be + * configured. + * + * Query 5 is present if the has_abs bit is set. + * + * @abs_data_size - describes the format of data reported by the absolute + * data source. Only one format (the kind used here) is supported at this + * time. + * @has_anchored_finger - then the sensor supports the high-precision second + * finger tracking provided by the manual tracking and motion sensitivity + * options. + * @has_adjust_hyst - the difference between the finger release threshold and + * the touch threshold. + * @has_dribble - the sensor supports the generation of dribble interrupts, + * which may be enabled or disabled with the dribble control bit. + * @has_bending_correction - Bending related data registers 28 and 36, and + * control register 52..57 are present. + * @has_large_object_suppression - control register 58 and data register 28 + * exist. + * @has_jitter_filter - query 13 and control 73..76 exist. + * + * Gesture information queries 7 and 8 are present if has_gestures bit is set. + * + * @has_single_tap - a basic single-tap gesture is supported. + * @has_tap_n_hold - tap-and-hold gesture is supported. + * @has_double_tap - double-tap gesture is supported. + * @has_early_tap - early tap is supported and reported as soon as the finger + * lifts for any tap event that could be interpreted as either a single tap + * or as the first tap of a double-tap or tap-and-hold gesture. + * @has_flick - flick detection is supported. + * @has_press - press gesture reporting is supported. + * @has_pinch - pinch gesture detection is supported. + * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive + * object such as a palm or a cheek touches the 2-D sensor. + * @has_rotate - rotation gesture detection is supported. + * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed + * rectangular area on the sensor that behaves like a capacitive button. + * @has_scroll_zones - scrolling areas near the sensor edges are supported. + * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported; + * if 0, then only two are supported. + * @has_mf_scroll - the multifinger_scrolling bit will be set when + * more than one finger is involved in a scrolling action. + * + * Convenience for checking bytes in the gesture info registers. This is done + * often enough that we put it here to declutter the conditionals + * + * @query7_nonzero - true if none of the query 7 bits are set + * @query8_nonzero - true if none of the query 8 bits are set + * + * Query 9 is present if the has_query9 is set. + * + * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20 + * and F11_2D_Ctrl21 exist. + * @has_proximity - detection of fingers near the sensor is supported and + * registers F11_2D_Ctrl22 through F11_2D_Ctrl26 exist. + * @has_palm_det_sensitivity - the sensor supports the palm detect sensitivity + * feature and register F11_2D_Ctrl27 exists. + * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists. + * @has_contact_geometry - the sensor supports the use of contact geometry to + * map absolute X and Y target positions and registers F11_2D_Data18 + * through F11_2D_Data27 exist. + * + * Touch shape info (query 10) is present if has_touch_shapes is set. + * + * @nr_touch_shapes - the total number of touch shapes supported. + * + * Query 11 is present if the has_query11 bit is set in query 0. + * + * @has_z_tuning - if set, the sensor supports Z tuning and registers + * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist. + * @has_algorithm_selection - controls choice of noise suppression algorithm + * @has_w_tuning - the sensor supports Wx and Wy scaling and registers + * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist. + * @has_pitch_info - the X and Y pitches of the sensor electrodes can be + * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist. + * @has_finger_size - the default finger width settings for the + * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44 + * exist. + * @has_segmentation_aggressiveness - the sensor’s ability to distinguish + * multiple objects close together can be configured and register F11_2D_Ctrl45 + * exists. + * @has_XY_clip - the inactive outside borders of the sensor can be + * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist. + * @has_drumming_filter - the sensor can be configured to distinguish + * between a fast flick and a quick drumming movement and registers + * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist. + * + * Query 12 is present if hasQuery12 bit is set. + * + * @has_gapless_finger - control registers relating to gapless finger are + * present. + * @has_gapless_finger_tuning - additional control and data registers relating + * to gapless finger are present. + * @has_8bit_w - larger W value reporting is supported. + * @has_adjustable_mapping - TBD + * @has_info2 - the general info query14 is present + * @has_physical_props - additional queries describing the physical properties + * of the sensor are present. + * @has_finger_limit - indicates that F11 Ctrl 80 exists. + * @has_linear_coeff - indicates that F11 Ctrl 81 exists. + * + * Query 13 is present if Query 5's has_jitter_filter bit is set. + * @jitter_window_size - used by Design Studio 4. + * @jitter_filter_type - used by Design Studio 4. + * + * Query 14 is present if query 12's has_general_info2 flag is set. + * + * @light_control - Indicates what light/led control features are present, if + * any. + * @is_clear - if set, this is a clear sensor (indicating direct pointing + * application), otherwise it's opaque (indicating indirect pointing). + * @clickpad_props - specifies if this is a clickpad, and if so what sort of + * mechanism it uses + * @mouse_buttons - specifies the number of mouse buttons present (if any). + * @has_advanced_gestures - advanced driver gestures are supported. + */ +struct f11_2d_sensor_queries { + /* query1 */ + u8 nr_fingers; + bool has_rel; + bool has_abs; + bool has_gestures; + bool has_sensitivity_adjust; + bool configurable; + + /* query2 */ + u8 nr_x_electrodes; + + /* query3 */ + u8 nr_y_electrodes; + + /* query4 */ + u8 max_electrodes; + + /* query5 */ + u8 abs_data_size; + bool has_anchored_finger; + bool has_adj_hyst; + bool has_dribble; + bool has_bending_correction; + bool has_large_object_suppression; + bool has_jitter_filter; + + u8 f11_2d_query6; + + /* query 7 */ + bool has_single_tap; + bool has_tap_n_hold; + bool has_double_tap; + bool has_early_tap; + bool has_flick; + bool has_press; + bool has_pinch; + bool has_chiral; + + bool query7_nonzero; + + /* query 8 */ + bool has_palm_det; + bool has_rotate; + bool has_touch_shapes; + bool has_scroll_zones; + bool has_individual_scroll_zones; + bool has_mf_scroll; + bool has_mf_edge_motion; + bool has_mf_scroll_inertia; + + bool query8_nonzero; + + /* Query 9 */ + bool has_pen; + bool has_proximity; + bool has_palm_det_sensitivity; + bool has_suppress_on_palm_detect; + bool has_two_pen_thresholds; + bool has_contact_geometry; + bool has_pen_hover_discrimination; + bool has_pen_filters; + + /* Query 10 */ + u8 nr_touch_shapes; + + /* Query 11. */ + bool has_z_tuning; + bool has_algorithm_selection; + bool has_w_tuning; + bool has_pitch_info; + bool has_finger_size; + bool has_segmentation_aggressiveness; + bool has_XY_clip; + bool has_drumming_filter; + + /* Query 12 */ + bool has_gapless_finger; + bool has_gapless_finger_tuning; + bool has_8bit_w; + bool has_adjustable_mapping; + bool has_info2; + bool has_physical_props; + bool has_finger_limit; + bool has_linear_coeff_2; + + /* Query 13 */ + u8 jitter_window_size; + u8 jitter_filter_type; + + /* Query 14 */ + u8 light_control; + bool is_clear; + u8 clickpad_props; + u8 mouse_buttons; + bool has_advanced_gestures; + + /* Query 15 - 18 */ + u16 x_sensor_size_mm; + u16 y_sensor_size_mm; +}; + +/* Defs for Ctrl0. */ +#define RMI_F11_REPORT_MODE_MASK 0x07 +#define RMI_F11_ABS_POS_FILT (1 << 3) +#define RMI_F11_REL_POS_FILT (1 << 4) +#define RMI_F11_REL_BALLISTICS (1 << 5) +#define RMI_F11_DRIBBLE (1 << 6) +#define RMI_F11_REPORT_BEYOND_CLIP (1 << 7) + +/* Defs for Ctrl1. */ +#define RMI_F11_PALM_DETECT_THRESH_MASK 0x0F +#define RMI_F11_MOTION_SENSITIVITY_MASK 0x30 +#define RMI_F11_MANUAL_TRACKING (1 << 6) +#define RMI_F11_MANUAL_TRACKED_FINGER (1 << 7) + +#define RMI_F11_DELTA_X_THRESHOLD 2 +#define RMI_F11_DELTA_Y_THRESHOLD 3 + +#define RMI_F11_CTRL_REG_COUNT 12 + +struct f11_2d_ctrl { + u8 ctrl0_11[RMI_F11_CTRL_REG_COUNT]; + u16 ctrl0_11_address; +}; + +#define RMI_F11_ABS_BYTES 5 +#define RMI_F11_REL_BYTES 2 + +/* Defs for Data 8 */ + +#define RMI_F11_SINGLE_TAP (1 << 0) +#define RMI_F11_TAP_AND_HOLD (1 << 1) +#define RMI_F11_DOUBLE_TAP (1 << 2) +#define RMI_F11_EARLY_TAP (1 << 3) +#define RMI_F11_FLICK (1 << 4) +#define RMI_F11_PRESS (1 << 5) +#define RMI_F11_PINCH (1 << 6) + +/* Defs for Data 9 */ + +#define RMI_F11_PALM_DETECT (1 << 0) +#define RMI_F11_ROTATE (1 << 1) +#define RMI_F11_SHAPE (1 << 2) +#define RMI_F11_SCROLLZONE (1 << 3) +#define RMI_F11_GESTURE_FINGER_COUNT_MASK 0x70 + +/** Handy pointers into our data buffer. + * + * @f_state - start of finger state registers. + * @abs_pos - start of absolute position registers (if present). + * @rel_pos - start of relative data registers (if present). + * @gest_1 - gesture flags (if present). + * @gest_2 - gesture flags & finger count (if present). + * @pinch - pinch motion register (if present). + * @flick - flick distance X & Y, flick time (if present). + * @rotate - rotate motion and finger separation. + * @multi_scroll - chiral deltas for X and Y (if present). + * @scroll_zones - scroll deltas for 4 regions (if present). + */ +struct f11_2d_data { + u8 *f_state; + u8 *abs_pos; + s8 *rel_pos; + u8 *gest_1; + u8 *gest_2; + s8 *pinch; + u8 *flick; + u8 *rotate; + u8 *shapes; + s8 *multi_scroll; + s8 *scroll_zones; +}; + +/** Data pertaining to F11 in general. For per-sensor data, see struct + * f11_2d_sensor. + * + * @dev_query - F11 device specific query registers. + * @dev_controls - F11 device specific control registers. + * @dev_controls_mutex - lock for the control registers. + * @rezero_wait_ms - if nonzero, upon resume we will wait this many + * milliseconds before rezeroing the sensor(s). This is useful in systems with + * poor electrical behavior on resume, where the initial calibration of the + * sensor(s) coming out of sleep state may be bogus. + * @sensors - per sensor data structures. + */ +struct f11_data { + bool has_query9; + bool has_query11; + bool has_query12; + bool has_query27; + bool has_query28; + bool has_acm; + struct f11_2d_ctrl dev_controls; + struct mutex dev_controls_mutex; + u16 rezero_wait_ms; + struct rmi_2d_sensor sensor; + struct f11_2d_sensor_queries sens_query; + struct f11_2d_data data; + struct rmi_2d_sensor_platform_data sensor_pdata; + unsigned long *abs_mask; + unsigned long *rel_mask; + unsigned long *result_bits; +}; + +enum f11_finger_state { + F11_NO_FINGER = 0x00, + F11_PRESENT = 0x01, + F11_INACCURATE = 0x02, + F11_RESERVED = 0x03 +}; + +static void rmi_f11_rel_pos_report(struct f11_data *f11, u8 n_finger) +{ + struct rmi_2d_sensor *sensor = &f11->sensor; + struct f11_2d_data *data = &f11->data; + s8 x, y; + + x = data->rel_pos[n_finger * 2]; + y = data->rel_pos[n_finger * 2 + 1]; + + rmi_2d_sensor_rel_report(sensor, x, y); +} + +static void rmi_f11_abs_pos_process(struct f11_data *f11, + struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + enum f11_finger_state finger_state, + u8 n_finger) +{ + struct f11_2d_data *data = &f11->data; + u8 *pos_data = &data->abs_pos[n_finger * RMI_F11_ABS_BYTES]; + int tool_type = MT_TOOL_FINGER; + + switch (finger_state) { + case F11_PRESENT: + obj->type = RMI_2D_OBJECT_FINGER; + break; + default: + obj->type = RMI_2D_OBJECT_NONE; + } + + obj->mt_tool = tool_type; + obj->x = (pos_data[0] << 4) | (pos_data[2] & 0x0F); + obj->y = (pos_data[1] << 4) | (pos_data[2] >> 4); + obj->z = pos_data[4]; + obj->wx = pos_data[3] & 0x0f; + obj->wy = pos_data[3] >> 4; + + rmi_2d_sensor_abs_process(sensor, obj, n_finger); +} + +static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) +{ + return (f_state[n_finger / 4] >> (2 * (n_finger % 4))) & + FINGER_STATE_MASK; +} + +static void rmi_f11_finger_handler(struct f11_data *f11, + struct rmi_2d_sensor *sensor, + unsigned long *irq_bits, int num_irq_regs) +{ + const u8 *f_state = f11->data.f_state; + u8 finger_state; + u8 i; + + int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, + num_irq_regs * 8); + int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, + num_irq_regs * 8); + + for (i = 0; i < sensor->nbr_fingers; i++) { + /* Possible of having 4 fingers per f_statet register */ + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) { + pr_err("Invalid finger state[%d]: 0x%02x", i, + finger_state); + continue; + } + + if (abs_bits) + rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], + finger_state, i); + + if (rel_bits) + rmi_f11_rel_pos_report(f11, i); + } + + if (abs_bits) { + /* + * the absolute part is made in 2 parts to allow the kernel + * tracking to take place. + */ + if (sensor->kernel_tracking) + input_mt_assign_slots(sensor->input, + sensor->tracking_slots, + sensor->tracking_pos, + sensor->nbr_fingers, + sensor->dmax); + + for (i = 0; i < sensor->nbr_fingers; i++) { + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) + /* no need to send twice the error */ + continue; + + rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); + } + + input_mt_sync_frame(sensor->input); + } +} + +static int f11_2d_construct_data(struct f11_data *f11) +{ + struct rmi_2d_sensor *sensor = &f11->sensor; + struct f11_2d_sensor_queries *query = &f11->sens_query; + struct f11_2d_data *data = &f11->data; + int i; + + sensor->nbr_fingers = (query->nr_fingers == 5 ? 10 : + query->nr_fingers + 1); + + sensor->pkt_size = DIV_ROUND_UP(sensor->nbr_fingers, 4); + + if (query->has_abs) { + sensor->pkt_size += (sensor->nbr_fingers * 5); + sensor->attn_size = sensor->pkt_size; + } + + if (query->has_rel) + sensor->pkt_size += (sensor->nbr_fingers * 2); + + /* Check if F11_2D_Query7 is non-zero */ + if (query->query7_nonzero) + sensor->pkt_size += sizeof(u8); + + /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */ + if (query->query7_nonzero || query->query8_nonzero) + sensor->pkt_size += sizeof(u8); + + if (query->has_pinch || query->has_flick || query->has_rotate) { + sensor->pkt_size += 3; + if (!query->has_flick) + sensor->pkt_size--; + if (!query->has_rotate) + sensor->pkt_size--; + } + + if (query->has_touch_shapes) + sensor->pkt_size += + DIV_ROUND_UP(query->nr_touch_shapes + 1, 8); + + sensor->data_pkt = devm_kzalloc(&sensor->fn->dev, sensor->pkt_size, + GFP_KERNEL); + if (!sensor->data_pkt) + return -ENOMEM; + + data->f_state = sensor->data_pkt; + i = DIV_ROUND_UP(sensor->nbr_fingers, 4); + + if (query->has_abs) { + data->abs_pos = &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * RMI_F11_ABS_BYTES); + } + + if (query->has_rel) { + data->rel_pos = &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * RMI_F11_REL_BYTES); + } + + if (query->query7_nonzero) { + data->gest_1 = &sensor->data_pkt[i]; + i++; + } + + if (query->query7_nonzero || query->query8_nonzero) { + data->gest_2 = &sensor->data_pkt[i]; + i++; + } + + if (query->has_pinch) { + data->pinch = &sensor->data_pkt[i]; + i++; + } + + if (query->has_flick) { + if (query->has_pinch) { + data->flick = data->pinch; + i += 2; + } else { + data->flick = &sensor->data_pkt[i]; + i += 3; + } + } + + if (query->has_rotate) { + if (query->has_flick) { + data->rotate = data->flick + 1; + } else { + data->rotate = &sensor->data_pkt[i]; + i += 2; + } + } + + if (query->has_touch_shapes) + data->shapes = &sensor->data_pkt[i]; + + return 0; +} + +static int f11_read_control_regs(struct rmi_function *fn, + struct f11_2d_ctrl *ctrl, u16 ctrl_base_addr) { + struct rmi_device *rmi_dev = fn->rmi_dev; + int error = 0; + + ctrl->ctrl0_11_address = ctrl_base_addr; + error = rmi_read_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11, + RMI_F11_CTRL_REG_COUNT); + if (error < 0) { + dev_err(&fn->dev, "Failed to read ctrl0, code: %d.\n", error); + return error; + } + + return 0; +} + +static int f11_write_control_regs(struct rmi_function *fn, + struct f11_2d_sensor_queries *query, + struct f11_2d_ctrl *ctrl, + u16 ctrl_base_addr) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + error = rmi_write_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11, + RMI_F11_CTRL_REG_COUNT); + if (error < 0) + return error; + + return 0; +} + +static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev, + struct f11_data *f11, + struct f11_2d_sensor_queries *sensor_query, + u16 query_base_addr) +{ + int query_size; + int rc; + u8 query_buf[RMI_F11_QUERY_SIZE]; + bool has_query36 = false; + + rc = rmi_read_block(rmi_dev, query_base_addr, query_buf, + RMI_F11_QUERY_SIZE); + if (rc < 0) + return rc; + + sensor_query->nr_fingers = query_buf[0] & RMI_F11_NR_FINGERS_MASK; + sensor_query->has_rel = !!(query_buf[0] & RMI_F11_HAS_REL); + sensor_query->has_abs = !!(query_buf[0] & RMI_F11_HAS_ABS); + sensor_query->has_gestures = !!(query_buf[0] & RMI_F11_HAS_GESTURES); + sensor_query->has_sensitivity_adjust = + !!(query_buf[0] && RMI_F11_HAS_SENSITIVITY_ADJ); + sensor_query->configurable = !!(query_buf[0] & RMI_F11_CONFIGURABLE); + + sensor_query->nr_x_electrodes = + query_buf[1] & RMI_F11_NR_ELECTRODES_MASK; + sensor_query->nr_y_electrodes = + query_buf[2] & RMI_F11_NR_ELECTRODES_MASK; + sensor_query->max_electrodes = + query_buf[3] & RMI_F11_NR_ELECTRODES_MASK; + + query_size = RMI_F11_QUERY_SIZE; + + if (sensor_query->has_abs) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->abs_data_size = + query_buf[0] & RMI_F11_ABS_DATA_SIZE_MASK; + sensor_query->has_anchored_finger = + !!(query_buf[0] & RMI_F11_HAS_ANCHORED_FINGER); + sensor_query->has_adj_hyst = + !!(query_buf[0] & RMI_F11_HAS_ADJ_HYST); + sensor_query->has_dribble = + !!(query_buf[0] & RMI_F11_HAS_DRIBBLE); + sensor_query->has_bending_correction = + !!(query_buf[0] & RMI_F11_HAS_BENDING_CORRECTION); + sensor_query->has_large_object_suppression = + !!(query_buf[0] && RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION); + sensor_query->has_jitter_filter = + !!(query_buf[0] & RMI_F11_HAS_JITTER_FILTER); + query_size++; + } + + if (sensor_query->has_rel) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + &sensor_query->f11_2d_query6); + if (rc < 0) + return rc; + query_size++; + } + + if (sensor_query->has_gestures) { + rc = rmi_read_block(rmi_dev, query_base_addr + query_size, + query_buf, RMI_F11_QUERY_GESTURE_SIZE); + if (rc < 0) + return rc; + + sensor_query->has_single_tap = + !!(query_buf[0] & RMI_F11_HAS_SINGLE_TAP); + sensor_query->has_tap_n_hold = + !!(query_buf[0] & RMI_F11_HAS_TAP_AND_HOLD); + sensor_query->has_double_tap = + !!(query_buf[0] & RMI_F11_HAS_DOUBLE_TAP); + sensor_query->has_early_tap = + !!(query_buf[0] & RMI_F11_HAS_EARLY_TAP); + sensor_query->has_flick = + !!(query_buf[0] & RMI_F11_HAS_FLICK); + sensor_query->has_press = + !!(query_buf[0] & RMI_F11_HAS_PRESS); + sensor_query->has_pinch = + !!(query_buf[0] & RMI_F11_HAS_PINCH); + sensor_query->has_chiral = + !!(query_buf[0] & RMI_F11_HAS_CHIRAL); + + /* query 8 */ + sensor_query->has_palm_det = + !!(query_buf[1] & RMI_F11_HAS_PALM_DET); + sensor_query->has_rotate = + !!(query_buf[1] & RMI_F11_HAS_ROTATE); + sensor_query->has_touch_shapes = + !!(query_buf[1] & RMI_F11_HAS_TOUCH_SHAPES); + sensor_query->has_scroll_zones = + !!(query_buf[1] & RMI_F11_HAS_SCROLL_ZONES); + sensor_query->has_individual_scroll_zones = + !!(query_buf[1] & RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES); + sensor_query->has_mf_scroll = + !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL); + sensor_query->has_mf_edge_motion = + !!(query_buf[1] & RMI_F11_HAS_MF_EDGE_MOTION); + sensor_query->has_mf_scroll_inertia = + !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL_INERTIA); + + sensor_query->query7_nonzero = !!(query_buf[0]); + sensor_query->query8_nonzero = !!(query_buf[1]); + + query_size += 2; + } + + if (f11->has_query9) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_pen = + !!(query_buf[0] & RMI_F11_HAS_PEN); + sensor_query->has_proximity = + !!(query_buf[0] & RMI_F11_HAS_PROXIMITY); + sensor_query->has_palm_det_sensitivity = + !!(query_buf[0] & RMI_F11_HAS_PALM_DET_SENSITIVITY); + sensor_query->has_suppress_on_palm_detect = + !!(query_buf[0] & RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT); + sensor_query->has_two_pen_thresholds = + !!(query_buf[0] & RMI_F11_HAS_TWO_PEN_THRESHOLDS); + sensor_query->has_contact_geometry = + !!(query_buf[0] & RMI_F11_HAS_CONTACT_GEOMETRY); + sensor_query->has_pen_hover_discrimination = + !!(query_buf[0] & RMI_F11_HAS_PEN_HOVER_DISCRIMINATION); + sensor_query->has_pen_filters = + !!(query_buf[0] & RMI_F11_HAS_PEN_FILTERS); + + query_size++; + } + + if (sensor_query->has_touch_shapes) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->nr_touch_shapes = query_buf[0] & + RMI_F11_NR_TOUCH_SHAPES_MASK; + + query_size++; + } + + if (f11->has_query11) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_z_tuning = + !!(query_buf[0] & RMI_F11_HAS_Z_TUNING); + sensor_query->has_algorithm_selection = + !!(query_buf[0] & RMI_F11_HAS_ALGORITHM_SELECTION); + sensor_query->has_w_tuning = + !!(query_buf[0] & RMI_F11_HAS_W_TUNING); + sensor_query->has_pitch_info = + !!(query_buf[0] & RMI_F11_HAS_PITCH_INFO); + sensor_query->has_finger_size = + !!(query_buf[0] & RMI_F11_HAS_FINGER_SIZE); + sensor_query->has_segmentation_aggressiveness = + !!(query_buf[0] & + RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS); + sensor_query->has_XY_clip = + !!(query_buf[0] & RMI_F11_HAS_XY_CLIP); + sensor_query->has_drumming_filter = + !!(query_buf[0] & RMI_F11_HAS_DRUMMING_FILTER); + + query_size++; + } + + if (f11->has_query12) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_gapless_finger = + !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER); + sensor_query->has_gapless_finger_tuning = + !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER_TUNING); + sensor_query->has_8bit_w = + !!(query_buf[0] & RMI_F11_HAS_8BIT_W); + sensor_query->has_adjustable_mapping = + !!(query_buf[0] & RMI_F11_HAS_ADJUSTABLE_MAPPING); + sensor_query->has_info2 = + !!(query_buf[0] & RMI_F11_HAS_INFO2); + sensor_query->has_physical_props = + !!(query_buf[0] & RMI_F11_HAS_PHYSICAL_PROPS); + sensor_query->has_finger_limit = + !!(query_buf[0] & RMI_F11_HAS_FINGER_LIMIT); + sensor_query->has_linear_coeff_2 = + !!(query_buf[0] & RMI_F11_HAS_LINEAR_COEFF); + + query_size++; + } + + if (sensor_query->has_jitter_filter) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->jitter_window_size = query_buf[0] & + RMI_F11_JITTER_WINDOW_MASK; + sensor_query->jitter_filter_type = (query_buf[0] & + RMI_F11_JITTER_FILTER_MASK) >> + RMI_F11_JITTER_FILTER_SHIFT; + + query_size++; + } + + if (sensor_query->has_info2) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->light_control = + query_buf[0] & RMI_F11_LIGHT_CONTROL_MASK; + sensor_query->is_clear = + !!(query_buf[0] & RMI_F11_IS_CLEAR); + sensor_query->clickpad_props = + (query_buf[0] & RMI_F11_CLICKPAD_PROPS_MASK) >> + RMI_F11_CLICKPAD_PROPS_SHIFT; + sensor_query->mouse_buttons = + (query_buf[0] & RMI_F11_MOUSE_BUTTONS_MASK) >> + RMI_F11_MOUSE_BUTTONS_SHIFT; + sensor_query->has_advanced_gestures = + !!(query_buf[0] & RMI_F11_HAS_ADVANCED_GESTURES); + + query_size++; + } + + if (sensor_query->has_physical_props) { + rc = rmi_read_block(rmi_dev, query_base_addr + + query_size, query_buf, 4); + if (rc < 0) + return rc; + + sensor_query->x_sensor_size_mm = + (query_buf[0] | (query_buf[1] << 8)) / 10; + sensor_query->y_sensor_size_mm = + (query_buf[2] | (query_buf[3] << 8)) / 10; + + /* + * query 15 - 18 contain the size of the sensor + * and query 19 - 26 contain bezel dimensions + */ + query_size += 12; + } + + if (f11->has_query27) + ++query_size; + + if (f11->has_query28) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + query_buf); + if (rc < 0) + return rc; + + has_query36 = !!(query_buf[0] & BIT(6)); + } + + if (has_query36) { + query_size += 2; + rc = rmi_read(rmi_dev, query_base_addr + query_size, + query_buf); + if (rc < 0) + return rc; + + if (!!(query_buf[0] & BIT(5))) + f11->has_acm = true; + } + + return query_size; +} + +static int rmi_f11_initialize(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f11_data *f11; + struct f11_2d_ctrl *ctrl; + u8 query_offset; + u16 query_base_addr; + u16 control_base_addr; + u16 max_x_pos, max_y_pos; + int rc; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi_2d_sensor *sensor; + u8 buf; + int mask_size; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Initializing F11 values.\n"); + + mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long); + + /* + ** init instance data, fill in values and create any sysfs files + */ + f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data) + mask_size * 3, + GFP_KERNEL); + if (!f11) + return -ENOMEM; + + if (pdata->sensor_pdata) + f11->sensor_pdata = *pdata->sensor_pdata; + + f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; + + f11->abs_mask = (unsigned long *)((char *)f11 + + sizeof(struct f11_data)); + f11->rel_mask = (unsigned long *)((char *)f11 + + sizeof(struct f11_data) + mask_size); + f11->result_bits = (unsigned long *)((char *)f11 + + sizeof(struct f11_data) + mask_size * 2); + + set_bit(fn->irq_pos, f11->abs_mask); + set_bit(fn->irq_pos + 1, f11->rel_mask); + + query_base_addr = fn->fd.query_base_addr; + control_base_addr = fn->fd.control_base_addr; + + rc = rmi_read(rmi_dev, query_base_addr, &buf); + if (rc < 0) + return rc; + + f11->has_query9 = !!(buf & RMI_F11_HAS_QUERY9); + f11->has_query11 = !!(buf & RMI_F11_HAS_QUERY11); + f11->has_query12 = !!(buf & RMI_F11_HAS_QUERY12); + f11->has_query27 = !!(buf & RMI_F11_HAS_QUERY27); + f11->has_query28 = !!(buf & RMI_F11_HAS_QUERY28); + + query_offset = (query_base_addr + 1); + sensor = &f11->sensor; + sensor->fn = fn; + + rc = rmi_f11_get_query_parameters(rmi_dev, f11, + &f11->sens_query, query_offset); + if (rc < 0) + return rc; + query_offset += rc; + + rc = f11_read_control_regs(fn, &f11->dev_controls, + control_base_addr); + if (rc < 0) { + dev_err(&fn->dev, + "Failed to read F11 control params.\n"); + return rc; + } + + if (f11->sens_query.has_info2) { + if (f11->sens_query.is_clear) + f11->sensor.sensor_type = rmi_sensor_touchscreen; + else + f11->sensor.sensor_type = rmi_sensor_touchpad; + } + + sensor->report_abs = f11->sens_query.has_abs; + + sensor->axis_align = + f11->sensor_pdata.axis_align; + + sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad; + sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking; + sensor->dmax = f11->sensor_pdata.dmax; + + if (f11->sens_query.has_physical_props) { + sensor->x_mm = f11->sens_query.x_sensor_size_mm; + sensor->y_mm = f11->sens_query.y_sensor_size_mm; + } else { + sensor->x_mm = f11->sensor_pdata.x_mm; + sensor->y_mm = f11->sensor_pdata.y_mm; + } + + if (sensor->sensor_type == rmi_sensor_default) + sensor->sensor_type = + f11->sensor_pdata.sensor_type; + + sensor->report_abs = sensor->report_abs + && !(f11->sensor_pdata.disable_report_mask + & RMI_F11_DISABLE_ABS_REPORT); + + if (!sensor->report_abs) + /* + * If device doesn't have abs or if it has been disables + * fallback to reporting rel data. + */ + sensor->report_rel = f11->sens_query.has_rel; + + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET, + (u8 *)&max_x_pos, sizeof(max_x_pos)); + if (rc < 0) + return rc; + + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET, + (u8 *)&max_y_pos, sizeof(max_y_pos)); + if (rc < 0) + return rc; + + sensor->max_x = max_x_pos; + sensor->max_y = max_y_pos; + + rc = f11_2d_construct_data(f11); + if (rc < 0) + return rc; + + if (f11->has_acm) + f11->sensor.attn_size += f11->sensor.nbr_fingers * 2; + + /* allocate the in-kernel tracking buffers */ + sensor->tracking_pos = devm_kzalloc(&fn->dev, + sizeof(struct input_mt_pos) * sensor->nbr_fingers, + GFP_KERNEL); + sensor->tracking_slots = devm_kzalloc(&fn->dev, + sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); + sensor->objs = devm_kzalloc(&fn->dev, + sizeof(struct rmi_2d_sensor_abs_object) + * sensor->nbr_fingers, GFP_KERNEL); + if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) + return -ENOMEM; + + ctrl = &f11->dev_controls; + if (sensor->axis_align.delta_x_threshold) + ctrl->ctrl0_11[RMI_F11_DELTA_X_THRESHOLD] = + sensor->axis_align.delta_x_threshold; + + if (sensor->axis_align.delta_y_threshold) + ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = + sensor->axis_align.delta_y_threshold; + + if (f11->sens_query.has_dribble) + ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6); + + if (f11->sens_query.has_palm_det) + ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0); + + rc = f11_write_control_regs(fn, &f11->sens_query, + &f11->dev_controls, fn->fd.query_base_addr); + if (rc) + dev_warn(&fn->dev, "Failed to write control registers\n"); + + mutex_init(&f11->dev_controls_mutex); + + dev_set_drvdata(&fn->dev, f11); + + return 0; +} + +static int rmi_f11_config(struct rmi_function *fn) +{ + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + struct rmi_driver *drv = fn->rmi_dev->driver; + struct rmi_2d_sensor *sensor = &f11->sensor; + int rc; + + if (!sensor->report_abs) + drv->clear_irq_bits(fn->rmi_dev, f11->abs_mask); + else + drv->set_irq_bits(fn->rmi_dev, f11->abs_mask); + + if (!sensor->report_rel) + drv->clear_irq_bits(fn->rmi_dev, f11->rel_mask); + else + drv->set_irq_bits(fn->rmi_dev, f11->rel_mask); + + rc = f11_write_control_regs(fn, &f11->sens_query, + &f11->dev_controls, fn->fd.query_base_addr); + if (rc < 0) + return rc; + + return 0; +} + +static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + u16 data_base_addr = fn->fd.data_base_addr; + u16 data_base_addr_offset = 0; + int error; + + if (rmi_dev->xport->attn_data) { + memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, + f11->sensor.attn_size); + rmi_dev->xport->attn_data += f11->sensor.attn_size; + rmi_dev->xport->attn_size -= f11->sensor.attn_size; + } else { + error = rmi_read_block(rmi_dev, + data_base_addr + data_base_addr_offset, + f11->sensor.data_pkt, + f11->sensor.pkt_size); + if (error < 0) + return error; + } + + rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, + drvdata->num_of_irq_regs); + data_base_addr_offset += f11->sensor.pkt_size; + + return 0; +} + +static int rmi_f11_resume(struct rmi_function *fn) +{ + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + int error; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Resuming...\n"); + if (!f11->rezero_wait_ms) + return 0; + + mdelay(f11->rezero_wait_ms); + + error = rmi_write(fn->rmi_dev, fn->fd.command_base_addr, + RMI_F11_REZERO); + if (error) { + dev_err(&fn->dev, + "%s: failed to issue rezero command, error = %d.", + __func__, error); + return error; + } + + return 0; +} + +static int rmi_f11_probe(struct rmi_function *fn) +{ + int error; + struct f11_data *f11; + + error = rmi_f11_initialize(fn); + if (error) + return error; + + f11 = dev_get_drvdata(&fn->dev); + error = rmi_2d_sensor_configure_input(fn, &f11->sensor); + if (error) + return error; + + return 0; +} + +struct rmi_function_handler rmi_f11_handler = { + .driver = { + .name = "rmi4_f11", + }, + .func = 0x11, + .probe = rmi_f11_probe, + .config = rmi_f11_config, + .attention = rmi_f11_attention, + .resume = rmi_f11_resume, +}; diff --git a/include/linux/rmi.h b/include/linux/rmi.h index c748fa302067..b527064bac47 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -19,6 +19,88 @@ #define NAME_BUFFER_SIZE 256 +/** + * struct rmi_2d_axis_alignment - target axis alignment + * @swap_axes: set to TRUE if desired to swap x- and y-axis + * @flip_x: set to TRUE if desired to flip direction on x-axis + * @flip_y: set to TRUE if desired to flip direction on y-axis + * @clip_x_low - reported X coordinates below this setting will be clipped to + * the specified value + * @clip_x_high - reported X coordinates above this setting will be clipped to + * the specified value + * @clip_y_low - reported Y coordinates below this setting will be clipped to + * the specified value + * @clip_y_high - reported Y coordinates above this setting will be clipped to + * the specified value + * @offset_x - this value will be added to all reported X coordinates + * @offset_y - this value will be added to all reported Y coordinates + * @rel_report_enabled - if set to true, the relative reporting will be + * automatically enabled for this sensor. + */ +struct rmi_2d_axis_alignment { + bool swap_axes; + bool flip_x; + bool flip_y; + u16 clip_x_low; + u16 clip_y_low; + u16 clip_x_high; + u16 clip_y_high; + u16 offset_x; + u16 offset_y; + u8 delta_x_threshold; + u8 delta_y_threshold; +}; + +/** This is used to override any hints an F11 2D sensor might have provided + * as to what type of sensor it is. + * + * @rmi_f11_sensor_default - do not override, determine from F11_2D_QUERY14 if + * available. + * @rmi_f11_sensor_touchscreen - treat the sensor as a touchscreen (direct + * pointing). + * @rmi_f11_sensor_touchpad - thread the sensor as a touchpad (indirect + * pointing). + */ +enum rmi_sensor_type { + rmi_sensor_default = 0, + rmi_sensor_touchscreen, + rmi_sensor_touchpad +}; + +#define RMI_F11_DISABLE_ABS_REPORT BIT(0) + +/** + * struct rmi_2d_sensor_data - overrides defaults for a 2D sensor. + * @axis_align - provides axis alignment overrides (see above). + * @sensor_type - Forces the driver to treat the sensor as an indirect + * pointing device (touchpad) rather than a direct pointing device + * (touchscreen). This is useful when F11_2D_QUERY14 register is not + * available. + * @disable_report_mask - Force data to not be reported even if it is supported + * by the firware. + * @topbuttonpad - Used with the "5 buttons touchpads" found on the Lenovo 40 + * series + * @kernel_tracking - most moderns RMI f11 firmwares implement Multifinger + * Type B protocol. However, there are some corner cases where the user + * triggers some jumps by tapping with two fingers on the touchpad. + * Use this setting and dmax to filter out these jumps. + * Also, when using an old sensor using MF Type A behavior, set to true to + * report an actual MT protocol B. + * @dmax - the maximum distance (in sensor units) the kernel tracking allows two + * distincts fingers to be considered the same. + */ +struct rmi_2d_sensor_platform_data { + struct rmi_2d_axis_alignment axis_align; + enum rmi_sensor_type sensor_type; + int x_mm; + int y_mm; + int disable_report_mask; + u16 rezero_wait; + bool topbuttonpad; + bool kernel_tracking; + int dmax; +}; + /** * struct rmi_f01_power - override default power management settings. * @@ -63,6 +145,7 @@ struct rmi_device_platform_data { int reset_delay_ms; /* function handler pdata */ + struct rmi_2d_sensor_platform_data *sensor_pdata; struct rmi_f01_power_management power_management; }; -- cgit v1.2.3 From aaa27982b3bcc3cce6a1d835673adccd75903a2e Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:48:32 -0800 Subject: Input: synaptics-rmi4 - add device tree support for 2d sensors and F11 2D sensors have several parameter which can be set in the platform data. This patch adds support for getting those values from devicetree. Signed-off-by: Andrew Duggan Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- .../bindings/input/rmi4/rmi_2d_sensor.txt | 56 +++++++++++ .../bindings/input/touchscreen/touchscreen.txt | 2 + drivers/input/rmi4/rmi_2d_sensor.c | 108 +++++++++++++++++++++ drivers/input/rmi4/rmi_2d_sensor.h | 3 + drivers/input/rmi4/rmi_f11.c | 7 +- 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt new file mode 100644 index 000000000000..f2c30c8b725d --- /dev/null +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt @@ -0,0 +1,56 @@ +Synaptics RMI4 2D Sensor Device Binding + +The Synaptics RMI4 core is able to support RMI4 devices using different +transports and different functions. This file describes the device tree +bindings for devices which contain 2D sensors using Function 11 or +Function 12. Complete documentation for transports and other functions +can be found in: +Documentation/devicetree/bindings/input/rmi4. + +RMI4 Function 11 and Function 12 are for 2D touch position sensing. +Additional documentation for F11 can be found at: +http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf + +Optional Touch Properties: +Description in Documentation/devicetree/bindings/input/touch +- touchscreen-inverted-x +- touchscreen-inverted-y +- touchscreen-swapped-x-y +- touchscreen-x-mm +- touchscreen-y-mm + +Optional Properties: +- syna,clip-x-low: Sets a minimum value for X. +- syna,clip-y-low: Sets a minimum value for Y. +- syna,clip-x-high: Sets a maximum value for X. +- syna,clip-y-high: Sets a maximum value for Y. +- syna,offset-x: Add an offset to X. +- syna,offset-y: Add an offset to Y. +- syna,delta-x-threshold: Set the minimum distance on the X axis required + to generate an interrupt in reduced reporting + mode. +- syna,delta-y-threshold: Set the minimum distance on the Y axis required + to generate an interrupt in reduced reporting + mode. +- syna,sensor-type: Set the sensor type. 1 for touchscreen 2 for touchpad. +- syna,disable-report-mask: Mask for disabling posiiton reporting. Used to + disable reporing absolute position data. +- syna,rezero-wait-ms: Time in miliseconds to wait after issuing a rezero + command. + + +Example of a RMI4 I2C device with F11: +Example: + &i2c1 { + rmi4-i2c-dev@2c { + compatible = "syna,rmi4-i2c"; + + ... + + rmi4-f11@11 { + reg = <0x11>; + touchscreen-inverted-y; + syna,sensor-type = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index ac23caf518ad..bccaa4e73045 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -18,6 +18,8 @@ Optional properties for Touchscreens: - touchscreen-inverted-y : Y axis is inverted (boolean) - touchscreen-swapped-x-y : X and Y axis are swapped (boolean) Swapping is done after inverting the axis + - touchscreen-x-mm : horizontal length in mm of the touchscreen + - touchscreen-y-mm : vertical length in mm of the touchscreen Deprecated properties for Touchscreens: - x-size : deprecated name for touchscreen-size-x diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index 8f48647ca5b0..e97bd7fabccc 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -219,3 +219,111 @@ int rmi_2d_sensor_configure_input(struct rmi_function *fn, return 0; } EXPORT_SYMBOL_GPL(rmi_2d_sensor_configure_input); + +#ifdef CONFIG_OF +int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata) +{ + int retval; + u32 val; + + pdata->axis_align.swap_axes = of_property_read_bool(dev->of_node, + "touchscreen-swapped-x-y"); + + pdata->axis_align.flip_x = of_property_read_bool(dev->of_node, + "touchscreen-inverted-x"); + + pdata->axis_align.flip_y = of_property_read_bool(dev->of_node, + "touchscreen-inverted-y"); + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-low", 1); + if (retval) + return retval; + + pdata->axis_align.clip_x_low = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-low", 1); + if (retval) + return retval; + + pdata->axis_align.clip_y_low = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-high", 1); + if (retval) + return retval; + + pdata->axis_align.clip_x_high = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-high", 1); + if (retval) + return retval; + + pdata->axis_align.clip_y_high = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,offset-x", 1); + if (retval) + return retval; + + pdata->axis_align.offset_x = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,offset-y", 1); + if (retval) + return retval; + + pdata->axis_align.offset_y = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,delta-x-threshold", + 1); + if (retval) + return retval; + + pdata->axis_align.delta_x_threshold = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,delta-y-threshold", + 1); + if (retval) + return retval; + + pdata->axis_align.delta_y_threshold = val; + + retval = rmi_of_property_read_u32(dev, (u32 *)&pdata->sensor_type, + "syna,sensor-type", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, &val, "touchscreen-x-mm", 1); + if (retval) + return retval; + + pdata->x_mm = val; + + retval = rmi_of_property_read_u32(dev, &val, "touchscreen-y-mm", 1); + if (retval) + return retval; + + pdata->y_mm = val; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,disable-report-mask", 1); + if (retval) + return retval; + + pdata->disable_report_mask = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,rezero-wait-ms", + 1); + if (retval) + return retval; + + pdata->rezero_wait = val; + + return 0; +} +#else +inline int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata) +{ + return -ENODEV; +} +#endif +EXPORT_SYMBOL_GPL(rmi_2d_sensor_of_probe); diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 6a715d392ec2..77fcdfef003c 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -69,6 +69,9 @@ struct rmi_2d_sensor { u8 y_mm; }; +int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata); + void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, struct rmi_2d_sensor_abs_object *obj, int slot); diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 948a360b6261..77a5eb84aed9 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1059,8 +1059,13 @@ static int rmi_f11_initialize(struct rmi_function *fn) if (!f11) return -ENOMEM; - if (pdata->sensor_pdata) + if (fn->dev.of_node) { + rc = rmi_2d_sensor_of_probe(&fn->dev, &f11->sensor_pdata); + if (rc) + return rc; + } else if (pdata->sensor_pdata) { f11->sensor_pdata = *pdata->sensor_pdata; + } f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; -- cgit v1.2.3 From b43d2c1e9353814ce1716fff03d9223f40fa73b5 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:55:29 -0800 Subject: Input: synaptics-rmi4 - add support for F12 Function 12 implements 2D touch position sensor for newer Synaptics touch devices. It replaces F11 and no device will contain both functions. Signed-off-by: Andrew Duggan Signed-off-by: Christopher Heiny Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 11 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f12.c | 457 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 473 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f12.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index dc7de6bbb8e6..aabb8542a55d 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -32,3 +32,14 @@ config RMI4_F11 Function 11 provides 2D multifinger pointing for touchscreens and touchpads. For sensors that support relative pointing, F11 also provides mouse input. + +config RMI4_F12 + bool "RMI4 Function 12 (2D pointing)" + select RMI4_2D_SENSOR + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 12. + + Function 12 provides 2D multifinger pointing for touchscreens and + touchpads. For sensors that support relative pointing, F12 also + provides mouse input. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 9e7424ae1c72..d01e703304c3 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -5,6 +5,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o # Function drivers rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o +rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 2e3878343961..a3d766118b61 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -309,6 +309,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F11 &rmi_f11_handler, #endif +#ifdef CONFIG_RMI4_F12 + &rmi_f12_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 09d2aa7f11d4..ed9127daf5a2 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -100,4 +100,5 @@ char *rmi_f01_get_product_ID(struct rmi_function *fn); extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; +extern struct rmi_function_handler rmi_f12_handler; #endif diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c new file mode 100644 index 000000000000..8dd3fb5e1f94 --- /dev/null +++ b/drivers/input/rmi4/rmi_f12.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2012-2016 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#include +#include +#include +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +enum rmi_f12_object_type { + RMI_F12_OBJECT_NONE = 0x00, + RMI_F12_OBJECT_FINGER = 0x01, + RMI_F12_OBJECT_STYLUS = 0x02, + RMI_F12_OBJECT_PALM = 0x03, + RMI_F12_OBJECT_UNCLASSIFIED = 0x04, + RMI_F12_OBJECT_GLOVED_FINGER = 0x06, + RMI_F12_OBJECT_NARROW_OBJECT = 0x07, + RMI_F12_OBJECT_HAND_EDGE = 0x08, + RMI_F12_OBJECT_COVER = 0x0A, + RMI_F12_OBJECT_STYLUS_2 = 0x0B, + RMI_F12_OBJECT_ERASER = 0x0C, + RMI_F12_OBJECT_SMALL_OBJECT = 0x0D, +}; + +struct f12_data { + struct rmi_function *fn; + struct rmi_2d_sensor sensor; + struct rmi_2d_sensor_platform_data sensor_pdata; + + u16 data_addr; + + struct rmi_register_descriptor query_reg_desc; + struct rmi_register_descriptor control_reg_desc; + struct rmi_register_descriptor data_reg_desc; + + /* F12 Data1 describes sensed objects */ + const struct rmi_register_desc_item *data1; + u16 data1_offset; + + /* F12 Data5 describes finger ACM */ + const struct rmi_register_desc_item *data5; + u16 data5_offset; + + /* F12 Data5 describes Pen */ + const struct rmi_register_desc_item *data6; + u16 data6_offset; + + + /* F12 Data9 reports relative data */ + const struct rmi_register_desc_item *data9; + u16 data9_offset; + + const struct rmi_register_desc_item *data15; + u16 data15_offset; +}; + +static int rmi_f12_read_sensor_tuning(struct f12_data *f12) +{ + const struct rmi_register_desc_item *item; + struct rmi_2d_sensor *sensor = &f12->sensor; + struct rmi_function *fn = sensor->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + int ret; + int offset; + u8 buf[14]; + int pitch_x = 0; + int pitch_y = 0; + int clip_x_low = 0; + int clip_x_high = 0; + int clip_y_low = 0; + int clip_y_high = 0; + int rx_receivers = 0; + int tx_receivers = 0; + int sensor_flags = 0; + + item = rmi_get_register_desc_item(&f12->control_reg_desc, 8); + if (!item) { + dev_err(&fn->dev, + "F12 does not have the sensor tuning control register\n"); + return -ENODEV; + } + + offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 8); + + if (item->reg_size > 14) { + dev_err(&fn->dev, "F12 control8 should be 14 bytes, not: %ld\n", + item->reg_size); + return -ENODEV; + } + + ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, buf, + item->reg_size); + if (ret) + return ret; + + offset = 0; + if (rmi_register_desc_has_subpacket(item, 0)) { + sensor->max_x = (buf[offset + 1] << 8) | buf[offset]; + sensor->max_y = (buf[offset + 3] << 8) | buf[offset + 2]; + offset += 4; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: max_x: %d max_y: %d\n", __func__, + sensor->max_x, sensor->max_y); + + if (rmi_register_desc_has_subpacket(item, 1)) { + pitch_x = (buf[offset + 1] << 8) | buf[offset]; + pitch_y = (buf[offset + 3] << 8) | buf[offset + 2]; + offset += 4; + } + + if (rmi_register_desc_has_subpacket(item, 2)) { + sensor->axis_align.clip_x_low = buf[offset]; + sensor->axis_align.clip_x_high = sensor->max_x + - buf[offset + 1]; + sensor->axis_align.clip_y_low = buf[offset + 2]; + sensor->axis_align.clip_y_high = sensor->max_y + - buf[offset + 3]; + offset += 4; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n", + __func__, clip_x_low, clip_x_high, clip_y_low, clip_y_high); + + if (rmi_register_desc_has_subpacket(item, 3)) { + rx_receivers = buf[offset]; + tx_receivers = buf[offset + 1]; + offset += 2; + } + + if (rmi_register_desc_has_subpacket(item, 4)) { + sensor_flags = buf[offset]; + offset += 1; + } + + sensor->x_mm = (pitch_x * rx_receivers) >> 12; + sensor->y_mm = (pitch_y * tx_receivers) >> 12; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__, + sensor->x_mm, sensor->y_mm); + + return 0; +} + +static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) +{ + int i; + struct rmi_2d_sensor *sensor = &f12->sensor; + + for (i = 0; i < f12->data1->num_subpackets; i++) { + struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i]; + + obj->type = RMI_2D_OBJECT_NONE; + obj->mt_tool = MT_TOOL_FINGER; + + switch (data1[0]) { + case RMI_F12_OBJECT_FINGER: + obj->type = RMI_2D_OBJECT_FINGER; + break; + case RMI_F12_OBJECT_STYLUS: + obj->type = RMI_2D_OBJECT_STYLUS; + obj->mt_tool = MT_TOOL_PEN; + break; + case RMI_F12_OBJECT_PALM: + obj->type = RMI_2D_OBJECT_PALM; + obj->mt_tool = MT_TOOL_PALM; + break; + case RMI_F12_OBJECT_UNCLASSIFIED: + obj->type = RMI_2D_OBJECT_UNCLASSIFIED; + break; + } + + obj->x = (data1[2] << 8) | data1[1]; + obj->y = (data1[4] << 8) | data1[3]; + obj->z = data1[5]; + obj->wx = data1[6]; + obj->wy = data1[7]; + + rmi_2d_sensor_abs_process(sensor, obj, i); + + data1 += 8; + } + + if (sensor->kernel_tracking) + input_mt_assign_slots(sensor->input, + sensor->tracking_slots, + sensor->tracking_pos, + sensor->nbr_fingers, + sensor->dmax); + + for (i = 0; i < sensor->nbr_fingers; i++) + rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); +} + +static int rmi_f12_attention(struct rmi_function *fn, + unsigned long *irq_nr_regs) +{ + int retval; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f12_data *f12 = dev_get_drvdata(&fn->dev); + struct rmi_2d_sensor *sensor = &f12->sensor; + + if (rmi_dev->xport->attn_data) { + memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, + sensor->attn_size); + rmi_dev->xport->attn_data += sensor->attn_size; + rmi_dev->xport->attn_size -= sensor->attn_size; + } else { + retval = rmi_read_block(rmi_dev, f12->data_addr, + sensor->data_pkt, sensor->pkt_size); + if (retval < 0) { + dev_err(&fn->dev, "Failed to read object data. Code: %d.\n", + retval); + return retval; + } + } + + if (f12->data1) + rmi_f12_process_objects(f12, + &sensor->data_pkt[f12->data1_offset]); + + input_mt_sync_frame(sensor->input); + + return 0; +} + +static int rmi_f12_config(struct rmi_function *fn) +{ + struct rmi_driver *drv = fn->rmi_dev->driver; + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f12_probe(struct rmi_function *fn) +{ + struct f12_data *f12; + int ret; + struct rmi_device *rmi_dev = fn->rmi_dev; + char buf; + u16 query_addr = fn->fd.query_base_addr; + const struct rmi_register_desc_item *item; + struct rmi_2d_sensor *sensor; + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_transport_dev *xport = rmi_dev->xport; + u16 data_offset = 0; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); + + ret = rmi_read(fn->rmi_dev, query_addr, &buf); + if (ret < 0) { + dev_err(&fn->dev, "Failed to read general info register: %d\n", + ret); + return -ENODEV; + } + ++query_addr; + + if (!(buf & 0x1)) { + dev_err(&fn->dev, + "Behavior of F12 without register descriptors is undefined.\n"); + return -ENODEV; + } + + f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data), GFP_KERNEL); + if (!f12) + return -ENOMEM; + + if (fn->dev.of_node) { + ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); + if (ret) + return ret; + } else if (pdata->sensor_pdata) { + f12->sensor_pdata = *pdata->sensor_pdata; + } + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->query_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Query Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->control_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Control Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->data_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Data Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + sensor = &f12->sensor; + sensor->fn = fn; + f12->data_addr = fn->fd.data_base_addr; + sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc); + + sensor->axis_align = + f12->sensor_pdata.axis_align; + + sensor->x_mm = f12->sensor_pdata.x_mm; + sensor->y_mm = f12->sensor_pdata.y_mm; + + if (sensor->sensor_type == rmi_sensor_default) + sensor->sensor_type = + f12->sensor_pdata.sensor_type; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__, + sensor->pkt_size); + sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL); + if (!sensor->data_pkt) + return -ENOMEM; + + dev_set_drvdata(&fn->dev, f12); + + ret = rmi_f12_read_sensor_tuning(f12); + if (ret) + return ret; + + /* + * Figure out what data is contained in the data registers. HID devices + * may have registers defined, but their data is not reported in the + * HID attention report. Registers which are not reported in the HID + * attention report check to see if the device is receiving data from + * HID attention reports. + */ + item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); + if (item) { + f12->data1 = item; + f12->data1_offset = data_offset; + data_offset += item->reg_size; + sensor->nbr_fingers = item->num_subpackets; + sensor->report_abs = 1; + sensor->attn_size += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); + if (item) { + f12->data5 = item; + f12->data5_offset = data_offset; + data_offset += item->reg_size; + sensor->attn_size += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); + if (item && !xport->attn_data) { + f12->data6 = item; + f12->data6_offset = data_offset; + data_offset += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); + if (item && !xport->attn_data) { + f12->data9 = item; + f12->data9_offset = data_offset; + data_offset += item->reg_size; + if (!sensor->report_abs) + sensor->report_rel = 1; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); + if (item && !xport->attn_data) { + f12->data15 = item; + f12->data15_offset = data_offset; + data_offset += item->reg_size; + } + + /* allocate the in-kernel tracking buffers */ + sensor->tracking_pos = devm_kzalloc(&fn->dev, + sizeof(struct input_mt_pos) * sensor->nbr_fingers, + GFP_KERNEL); + sensor->tracking_slots = devm_kzalloc(&fn->dev, + sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); + sensor->objs = devm_kzalloc(&fn->dev, + sizeof(struct rmi_2d_sensor_abs_object) + * sensor->nbr_fingers, GFP_KERNEL); + if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) + return -ENOMEM; + + ret = rmi_2d_sensor_configure_input(fn, sensor); + if (ret) + return ret; + + return 0; +} + +struct rmi_function_handler rmi_f12_handler = { + .driver = { + .name = "rmi4_f12", + }, + .func = 0x12, + .probe = rmi_f12_probe, + .config = rmi_f12_config, + .attention = rmi_f12_attention, +}; -- cgit v1.2.3 From 562b42d3ee305472e1b2ea31574c59925e95fd7e Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:56:58 -0800 Subject: Input: synaptics-rmi4 - add support for F30 RMI4 F30 supports input from clickpad buttons and controls LEDs located on the touchpad PCB. This patch adds support of the clickpad buttons and defers supporting LEDs for the future. Signed-off-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 9 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f30.c | 407 ++++++++++++++++++++++++++++++++++++++++ include/linux/rmi.h | 16 ++ 6 files changed, 437 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f30.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index aabb8542a55d..284faec30a7a 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -43,3 +43,12 @@ config RMI4_F12 Function 12 provides 2D multifinger pointing for touchscreens and touchpads. For sensors that support relative pointing, F12 also provides mouse input. + +config RMI4_F30 + bool "RMI4 Function 30 (GPIO LED)" + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 30. + + Function 30 provides GPIO and LED support for RMI4 devices. This + includes support for buttons on TouchPads and ClickPads. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index d01e703304c3..ad7156d8252c 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -6,6 +6,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o # Function drivers rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o +rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index a3d766118b61..b368b0515c5a 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -312,6 +312,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F12 &rmi_f12_handler, #endif +#ifdef CONFIG_RMI4_F30 + &rmi_f30_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index ed9127daf5a2..6e140fa3cce1 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -101,4 +101,5 @@ char *rmi_f01_get_product_ID(struct rmi_function *fn); extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; +extern struct rmi_function_handler rmi_f30_handler; #endif diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c new file mode 100644 index 000000000000..760aff1bc420 --- /dev/null +++ b/drivers/input/rmi4/rmi_f30.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012-2016 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_F30_QUERY_SIZE 2 + +/* Defs for Query 0 */ +#define RMI_F30_EXTENDED_PATTERNS 0x01 +#define RMI_F30_HAS_MAPPABLE_BUTTONS (1 << 1) +#define RMI_F30_HAS_LED (1 << 2) +#define RMI_F30_HAS_GPIO (1 << 3) +#define RMI_F30_HAS_HAPTIC (1 << 4) +#define RMI_F30_HAS_GPIO_DRV_CTL (1 << 5) +#define RMI_F30_HAS_MECH_MOUSE_BTNS (1 << 6) + +/* Defs for Query 1 */ +#define RMI_F30_GPIO_LED_COUNT 0x1F + +/* Defs for Control Registers */ +#define RMI_F30_CTRL_1_GPIO_DEBOUNCE 0x01 +#define RMI_F30_CTRL_1_HALT (1 << 4) +#define RMI_F30_CTRL_1_HALTED (1 << 5) +#define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS 0x03 + +struct rmi_f30_ctrl_data { + int address; + int length; + u8 *regs; +}; + +#define RMI_F30_CTRL_MAX_REGS 32 +#define RMI_F30_CTRL_MAX_BYTES ((RMI_F30_CTRL_MAX_REGS + 7) >> 3) +#define RMI_F30_CTRL_MAX_REG_BLOCKS 11 + +#define RMI_F30_CTRL_REGS_MAX_SIZE (RMI_F30_CTRL_MAX_BYTES \ + + 1 \ + + RMI_F30_CTRL_MAX_BYTES \ + + RMI_F30_CTRL_MAX_BYTES \ + + RMI_F30_CTRL_MAX_BYTES \ + + 6 \ + + RMI_F30_CTRL_MAX_REGS \ + + RMI_F30_CTRL_MAX_REGS \ + + RMI_F30_CTRL_MAX_BYTES \ + + 1 \ + + 1) + +struct f30_data { + /* Query Data */ + bool has_extended_pattern; + bool has_mappable_buttons; + bool has_led; + bool has_gpio; + bool has_haptic; + bool has_gpio_driver_control; + bool has_mech_mouse_btns; + u8 gpioled_count; + + u8 register_count; + + /* Control Register Data */ + struct rmi_f30_ctrl_data ctrl[RMI_F30_CTRL_MAX_REG_BLOCKS]; + u8 ctrl_regs[RMI_F30_CTRL_REGS_MAX_SIZE]; + u32 ctrl_regs_size; + + u8 data_regs[RMI_F30_CTRL_MAX_BYTES]; + u16 *gpioled_key_map; + + struct input_dev *input; +}; + +static int rmi_f30_read_control_parameters(struct rmi_function *fn, + struct f30_data *f30) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error = 0; + + error = rmi_read_block(rmi_dev, fn->fd.control_base_addr, + f30->ctrl_regs, f30->ctrl_regs_size); + if (error) { + dev_err(&rmi_dev->dev, "%s : Could not read control registers at 0x%x error (%d)\n", + __func__, fn->fd.control_base_addr, error); + return error; + } + + return 0; +} + +static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct rmi_device *rmi_dev = fn->rmi_dev; + int retval; + int gpiled = 0; + int value = 0; + int i; + int reg_num; + + if (!f30->input) + return 0; + + /* Read the gpi led data. */ + if (rmi_dev->xport->attn_data) { + memcpy(f30->data_regs, rmi_dev->xport->attn_data, + f30->register_count); + rmi_dev->xport->attn_data += f30->register_count; + rmi_dev->xport->attn_size -= f30->register_count; + } else { + retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, + f30->data_regs, f30->register_count); + + if (retval) { + dev_err(&fn->dev, "%s: Failed to read F30 data registers.\n", + __func__); + return retval; + } + } + + for (reg_num = 0; reg_num < f30->register_count; ++reg_num) { + for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i, + ++gpiled) { + if (f30->gpioled_key_map[gpiled] != 0) { + /* buttons have pull up resistors */ + value = (((f30->data_regs[reg_num] >> i) & 0x01) + == 0); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: call input report key (0x%04x) value (0x%02x)", + __func__, + f30->gpioled_key_map[gpiled], value); + input_report_key(f30->input, + f30->gpioled_key_map[gpiled], + value); + } + + } + } + + return 0; +} + +static int rmi_f30_register_device(struct rmi_function *fn) +{ + int i; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct input_dev *input_dev; + int button_count = 0; + + input_dev = drv_data->input; + if (!input_dev) { + dev_info(&fn->dev, "F30: no input device found, ignoring.\n"); + return -EINVAL; + } + + f30->input = input_dev; + + set_bit(EV_KEY, input_dev->evbit); + + input_dev->keycode = f30->gpioled_key_map; + input_dev->keycodesize = sizeof(u16); + input_dev->keycodemax = f30->gpioled_count; + + for (i = 0; i < f30->gpioled_count; i++) { + if (f30->gpioled_key_map[i] != 0) { + input_set_capability(input_dev, EV_KEY, + f30->gpioled_key_map[i]); + button_count++; + } + } + + if (button_count == 1) + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + return 0; +} + +static int rmi_f30_config(struct rmi_function *fn) +{ + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct rmi_driver *drv = fn->rmi_dev->driver; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(fn->rmi_dev); + int error; + + if (pdata->f30_data && pdata->f30_data->disable) { + drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); + } else { + /* Write Control Register values back to device */ + error = rmi_write_block(fn->rmi_dev, fn->fd.control_base_addr, + f30->ctrl_regs, f30->ctrl_regs_size); + if (error) { + dev_err(&fn->rmi_dev->dev, + "%s : Could not write control registers at 0x%x error (%d)\n", + __func__, fn->fd.control_base_addr, error); + return error; + } + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + } + return 0; +} + +static inline void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl, + int *ctrl_addr, int len, u8 **reg) +{ + ctrl->address = *ctrl_addr; + ctrl->length = len; + ctrl->regs = *reg; + *ctrl_addr += len; + *reg += len; +} + +static inline bool rmi_f30_is_valid_button(int button, + struct rmi_f30_ctrl_data *ctrl) +{ + int byte_position = button >> 3; + int bit_position = button & 0x07; + + /* + * ctrl2 -> dir == 0 -> input mode + * ctrl3 -> data == 1 -> actual button + */ + return !(ctrl[2].regs[byte_position] & BIT(bit_position)) && + (ctrl[3].regs[byte_position] & BIT(bit_position)); +} + +static inline int rmi_f30_initialize(struct rmi_function *fn) +{ + struct f30_data *f30; + struct rmi_device *rmi_dev = fn->rmi_dev; + const struct rmi_device_platform_data *pdata; + int retval = 0; + int control_address; + int i; + int button; + u8 buf[RMI_F30_QUERY_SIZE]; + u8 *ctrl_reg; + u8 *map_memory; + + f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data), + GFP_KERNEL); + if (!f30) + return -ENOMEM; + + dev_set_drvdata(&fn->dev, f30); + + retval = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, buf, + RMI_F30_QUERY_SIZE); + + if (retval) { + dev_err(&fn->dev, "Failed to read query register.\n"); + return retval; + } + + f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS; + f30->has_mappable_buttons = buf[0] & RMI_F30_HAS_MAPPABLE_BUTTONS; + f30->has_led = buf[0] & RMI_F30_HAS_LED; + f30->has_gpio = buf[0] & RMI_F30_HAS_GPIO; + f30->has_haptic = buf[0] & RMI_F30_HAS_HAPTIC; + f30->has_gpio_driver_control = buf[0] & RMI_F30_HAS_GPIO_DRV_CTL; + f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS; + f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT; + + f30->register_count = (f30->gpioled_count + 7) >> 3; + + control_address = fn->fd.control_base_addr; + ctrl_reg = f30->ctrl_regs; + + if (f30->has_gpio && f30->has_led) + rmi_f30_set_ctrl_data(&f30->ctrl[0], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address, sizeof(u8), + &ctrl_reg); + + if (f30->has_gpio) { + rmi_f30_set_ctrl_data(&f30->ctrl[2], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[3], &control_address, + f30->register_count, &ctrl_reg); + } + + if (f30->has_led) { + int ctrl5_len; + + rmi_f30_set_ctrl_data(&f30->ctrl[4], &control_address, + f30->register_count, &ctrl_reg); + + if (f30->has_extended_pattern) + ctrl5_len = 6; + else + ctrl5_len = 2; + + rmi_f30_set_ctrl_data(&f30->ctrl[5], &control_address, + ctrl5_len, &ctrl_reg); + } + + if (f30->has_led || f30->has_gpio_driver_control) { + /* control 6 uses a byte per gpio/led */ + rmi_f30_set_ctrl_data(&f30->ctrl[6], &control_address, + f30->gpioled_count, &ctrl_reg); + } + + if (f30->has_mappable_buttons) { + /* control 7 uses a byte per gpio/led */ + rmi_f30_set_ctrl_data(&f30->ctrl[7], &control_address, + f30->gpioled_count, &ctrl_reg); + } + + if (f30->has_haptic) { + rmi_f30_set_ctrl_data(&f30->ctrl[8], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[9], &control_address, + sizeof(u8), &ctrl_reg); + } + + if (f30->has_mech_mouse_btns) + rmi_f30_set_ctrl_data(&f30->ctrl[10], &control_address, + sizeof(u8), &ctrl_reg); + + f30->ctrl_regs_size = ctrl_reg - f30->ctrl_regs + ?: RMI_F30_CTRL_REGS_MAX_SIZE; + + retval = rmi_f30_read_control_parameters(fn, f30); + if (retval < 0) { + dev_err(&fn->dev, + "Failed to initialize F19 control params.\n"); + return retval; + } + + map_memory = devm_kzalloc(&fn->dev, + (f30->gpioled_count * (sizeof(u16))), + GFP_KERNEL); + if (!map_memory) { + dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n"); + return -ENOMEM; + } + + f30->gpioled_key_map = (u16 *)map_memory; + + pdata = rmi_get_platform_data(rmi_dev); + if (pdata && f30->has_gpio) { + button = BTN_LEFT; + for (i = 0; i < f30->gpioled_count; i++) { + if (rmi_f30_is_valid_button(i, f30->ctrl)) { + f30->gpioled_key_map[i] = button++; + + /* + * buttonpad might be given by + * f30->has_mech_mouse_btns, but I am + * not sure, so use only the pdata info + */ + if (pdata->f30_data && + pdata->f30_data->buttonpad) + break; + } + } + } + + return 0; +} + +static int rmi_f30_probe(struct rmi_function *fn) +{ + int rc; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(fn->rmi_dev); + + if (pdata->f30_data && pdata->f30_data->disable) + return 0; + + rc = rmi_f30_initialize(fn); + if (rc < 0) + goto error_exit; + + rc = rmi_f30_register_device(fn); + if (rc < 0) + goto error_exit; + + return 0; + +error_exit: + return rc; + +} + +struct rmi_function_handler rmi_f30_handler = { + .driver = { + .name = "rmi4_f30", + }, + .func = 0x30, + .probe = rmi_f30_probe, + .config = rmi_f30_config, + .attention = rmi_f30_attention, +}; diff --git a/include/linux/rmi.h b/include/linux/rmi.h index b527064bac47..ac89d1e731dc 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -101,6 +101,21 @@ struct rmi_2d_sensor_platform_data { int dmax; }; +/** + * struct rmi_f30_data - overrides defaults for a single F30 GPIOs/LED chip. + * @buttonpad - the touchpad is a buttonpad, so enable only the first actual + * button that is found. + * @trackstick_buttons - Set when the function 30 is handling the physical + * buttons of the trackstick (as a PD/2 passthrough device. + * @disable - the touchpad incorrectly reports F30 and it should be ignored. + * This is a special case which is due to misconfigured firmware. + */ +struct rmi_f30_data { + bool buttonpad; + bool trackstick_buttons; + bool disable; +}; + /** * struct rmi_f01_power - override default power management settings. * @@ -147,6 +162,7 @@ struct rmi_device_platform_data { /* function handler pdata */ struct rmi_2d_sensor_platform_data *sensor_pdata; struct rmi_f01_power_management power_management; + struct rmi_f30_data *f30_data; }; /** -- cgit v1.2.3 From 8d99758dee31ff4a72bfc35d3a7a51fe66b7bb91 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:58:12 -0800 Subject: Input: synaptics-rmi4 - add SPI transport driver Add the transport driver for devices using RMI4 over SPI. Signed-off-by: Andrew Duggan Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 9 + drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_spi.c | 547 +++++++++++++++++++++++++++++++++++++++++++ include/linux/rmi.h | 51 ++++ 4 files changed, 608 insertions(+) create mode 100644 drivers/input/rmi4/rmi_spi.c (limited to 'drivers/input') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 284faec30a7a..f73df2495fed 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -18,6 +18,15 @@ config RMI4_I2C If unsure, say Y. +config RMI4_SPI + tristate "RMI4 SPI Support" + depends on RMI4_CORE && SPI + help + Say Y here if you want to support RMI4 devices connected to a SPI + bus. + + If unsure, say N. + config RMI4_2D_SENSOR bool depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index ad7156d8252c..95c00a783992 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -10,3 +10,4 @@ rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o +obj-$(CONFIG_RMI4_SPI) += rmi_spi.o diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c new file mode 100644 index 000000000000..4319c634553f --- /dev/null +++ b/drivers/input/rmi4/rmi_spi.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_SPI_DEFAULT_XFER_BUF_SIZE 64 + +#define RMI_PAGE_SELECT_REGISTER 0x00FF +#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80) +#define RMI_SPI_XFER_SIZE_LIMIT 255 + +#define BUFFER_SIZE_INCREMENT 32 + +enum rmi_spi_op { + RMI_SPI_WRITE = 0, + RMI_SPI_READ, + RMI_SPI_V2_READ_UNIFIED, + RMI_SPI_V2_READ_SPLIT, + RMI_SPI_V2_WRITE, +}; + +struct rmi_spi_cmd { + enum rmi_spi_op op; + u16 addr; +}; + +struct rmi_spi_xport { + struct rmi_transport_dev xport; + struct spi_device *spi; + + struct mutex page_mutex; + int page; + + int irq; + + u8 *rx_buf; + u8 *tx_buf; + int xfer_buf_size; + + struct spi_transfer *rx_xfers; + struct spi_transfer *tx_xfers; + int rx_xfer_count; + int tx_xfer_count; +}; + +static int rmi_spi_manage_pools(struct rmi_spi_xport *rmi_spi, int len) +{ + struct spi_device *spi = rmi_spi->spi; + int buf_size = rmi_spi->xfer_buf_size + ? rmi_spi->xfer_buf_size : RMI_SPI_DEFAULT_XFER_BUF_SIZE; + struct spi_transfer *xfer_buf; + void *buf; + void *tmp; + + while (buf_size < len) + buf_size *= 2; + + if (buf_size > RMI_SPI_XFER_SIZE_LIMIT) + buf_size = RMI_SPI_XFER_SIZE_LIMIT; + + tmp = rmi_spi->rx_buf; + buf = devm_kzalloc(&spi->dev, buf_size * 2, + GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + rmi_spi->rx_buf = buf; + rmi_spi->tx_buf = &rmi_spi->rx_buf[buf_size]; + rmi_spi->xfer_buf_size = buf_size; + + if (tmp) + devm_kfree(&spi->dev, tmp); + + if (rmi_spi->xport.pdata.spi_data.read_delay_us) + rmi_spi->rx_xfer_count = buf_size; + else + rmi_spi->rx_xfer_count = 1; + + if (rmi_spi->xport.pdata.spi_data.write_delay_us) + rmi_spi->tx_xfer_count = buf_size; + else + rmi_spi->tx_xfer_count = 1; + + /* + * Allocate a pool of spi_transfer buffers for devices which need + * per byte delays. + */ + tmp = rmi_spi->rx_xfers; + xfer_buf = devm_kzalloc(&spi->dev, + (rmi_spi->rx_xfer_count + rmi_spi->tx_xfer_count) + * sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfer_buf) + return -ENOMEM; + + rmi_spi->rx_xfers = xfer_buf; + rmi_spi->tx_xfers = &xfer_buf[rmi_spi->rx_xfer_count]; + + if (tmp) + devm_kfree(&spi->dev, tmp); + + return 0; +} + +static int rmi_spi_xfer(struct rmi_spi_xport *rmi_spi, + const struct rmi_spi_cmd *cmd, const u8 *tx_buf, + int tx_len, u8 *rx_buf, int rx_len) +{ + struct spi_device *spi = rmi_spi->spi; + struct rmi_device_platform_data_spi *spi_data = + &rmi_spi->xport.pdata.spi_data; + struct spi_message msg; + struct spi_transfer *xfer; + int ret = 0; + int len; + int cmd_len = 0; + int total_tx_len; + int i; + u16 addr = cmd->addr; + + spi_message_init(&msg); + + switch (cmd->op) { + case RMI_SPI_WRITE: + case RMI_SPI_READ: + cmd_len += 2; + break; + case RMI_SPI_V2_READ_UNIFIED: + case RMI_SPI_V2_READ_SPLIT: + case RMI_SPI_V2_WRITE: + cmd_len += 4; + break; + } + + total_tx_len = cmd_len + tx_len; + len = max(total_tx_len, rx_len); + + if (len > RMI_SPI_XFER_SIZE_LIMIT) + return -EINVAL; + + if (rmi_spi->xfer_buf_size < len) + rmi_spi_manage_pools(rmi_spi, len); + + if (addr == 0) + /* + * SPI needs an address. Use 0x7FF if we want to keep + * reading from the last position of the register pointer. + */ + addr = 0x7FF; + + switch (cmd->op) { + case RMI_SPI_WRITE: + rmi_spi->tx_buf[0] = (addr >> 8); + rmi_spi->tx_buf[1] = addr & 0xFF; + break; + case RMI_SPI_READ: + rmi_spi->tx_buf[0] = (addr >> 8) | 0x80; + rmi_spi->tx_buf[1] = addr & 0xFF; + break; + case RMI_SPI_V2_READ_UNIFIED: + break; + case RMI_SPI_V2_READ_SPLIT: + break; + case RMI_SPI_V2_WRITE: + rmi_spi->tx_buf[0] = 0x40; + rmi_spi->tx_buf[1] = (addr >> 8) & 0xFF; + rmi_spi->tx_buf[2] = addr & 0xFF; + rmi_spi->tx_buf[3] = tx_len; + break; + } + + if (tx_buf) + memcpy(&rmi_spi->tx_buf[cmd_len], tx_buf, tx_len); + + if (rmi_spi->tx_xfer_count > 1) { + for (i = 0; i < total_tx_len; i++) { + xfer = &rmi_spi->tx_xfers[i]; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->tx_buf = &rmi_spi->tx_buf[i]; + xfer->len = 1; + xfer->delay_usecs = spi_data->write_delay_us; + spi_message_add_tail(xfer, &msg); + } + } else { + xfer = rmi_spi->tx_xfers; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->tx_buf = rmi_spi->tx_buf; + xfer->len = total_tx_len; + spi_message_add_tail(xfer, &msg); + } + + rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: cmd: %s tx_buf len: %d tx_buf: %*ph\n", + __func__, cmd->op == RMI_SPI_WRITE ? "WRITE" : "READ", + total_tx_len, total_tx_len, rmi_spi->tx_buf); + + if (rx_buf) { + if (rmi_spi->rx_xfer_count > 1) { + for (i = 0; i < rx_len; i++) { + xfer = &rmi_spi->rx_xfers[i]; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->rx_buf = &rmi_spi->rx_buf[i]; + xfer->len = 1; + xfer->delay_usecs = spi_data->read_delay_us; + spi_message_add_tail(xfer, &msg); + } + } else { + xfer = rmi_spi->rx_xfers; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->rx_buf = rmi_spi->rx_buf; + xfer->len = rx_len; + spi_message_add_tail(xfer, &msg); + } + } + + ret = spi_sync(spi, &msg); + if (ret < 0) { + dev_err(&spi->dev, "spi xfer failed: %d\n", ret); + return ret; + } + + if (rx_buf) { + memcpy(rx_buf, rmi_spi->rx_buf, rx_len); + rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: (%d) %*ph\n", + __func__, rx_len, rx_len, rx_buf); + } + + return 0; +} + +/* + * rmi_set_page - Set RMI page + * @xport: The pointer to the rmi_transport_dev struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the transport + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct rmi_spi_xport *rmi_spi, u8 page) +{ + struct rmi_spi_cmd cmd; + int ret; + + cmd.op = RMI_SPI_WRITE; + cmd.addr = RMI_PAGE_SELECT_REGISTER; + + ret = rmi_spi_xfer(rmi_spi, &cmd, &page, 1, NULL, 0); + + if (ret) + rmi_spi->page = page; + + return ret; +} + +static int rmi_spi_write_block(struct rmi_transport_dev *xport, u16 addr, + const void *buf, size_t len) +{ + struct rmi_spi_xport *rmi_spi = + container_of(xport, struct rmi_spi_xport, xport); + struct rmi_spi_cmd cmd; + int ret; + + mutex_lock(&rmi_spi->page_mutex); + + if (RMI_SPI_PAGE(addr) != rmi_spi->page) { + ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr)); + if (ret) + goto exit; + } + + cmd.op = RMI_SPI_WRITE; + cmd.addr = addr; + + ret = rmi_spi_xfer(rmi_spi, &cmd, buf, len, NULL, 0); + +exit: + mutex_unlock(&rmi_spi->page_mutex); + return ret; +} + +static int rmi_spi_read_block(struct rmi_transport_dev *xport, u16 addr, + void *buf, size_t len) +{ + struct rmi_spi_xport *rmi_spi = + container_of(xport, struct rmi_spi_xport, xport); + struct rmi_spi_cmd cmd; + int ret; + + mutex_lock(&rmi_spi->page_mutex); + + if (RMI_SPI_PAGE(addr) != rmi_spi->page) { + ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr)); + if (ret) + goto exit; + } + + cmd.op = RMI_SPI_READ; + cmd.addr = addr; + + ret = rmi_spi_xfer(rmi_spi, &cmd, NULL, 0, buf, len); + +exit: + mutex_unlock(&rmi_spi->page_mutex); + return ret; +} + +static const struct rmi_transport_ops rmi_spi_ops = { + .write_block = rmi_spi_write_block, + .read_block = rmi_spi_read_block, +}; + +static irqreturn_t rmi_spi_irq(int irq, void *dev_id) +{ + struct rmi_spi_xport *rmi_spi = dev_id; + struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_spi_init_irq(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq)); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL, + rmi_spi_irq, irq_flags | IRQF_ONESHOT, + dev_name(&spi->dev), rmi_spi); + if (ret < 0) { + dev_warn(&spi->dev, "Failed to register interrupt %d\n", + rmi_spi->irq); + return ret; + } + + return 0; +} + +static int rmi_spi_probe(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi; + struct rmi_device_platform_data *pdata; + struct rmi_device_platform_data *spi_pdata = spi->dev.platform_data; + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) + return -EINVAL; + + rmi_spi = devm_kzalloc(&spi->dev, sizeof(struct rmi_spi_xport), + GFP_KERNEL); + if (!rmi_spi) + return -ENOMEM; + + pdata = &rmi_spi->xport.pdata; + + if (spi_pdata) + *pdata = *spi_pdata; + + if (pdata->spi_data.bits_per_word) + spi->bits_per_word = pdata->spi_data.bits_per_word; + + if (pdata->spi_data.mode) + spi->mode = pdata->spi_data.mode; + + retval = spi_setup(spi); + if (retval < 0) { + dev_err(&spi->dev, "spi_setup failed!\n"); + return retval; + } + + if (spi->irq > 0) + rmi_spi->irq = spi->irq; + + rmi_spi->spi = spi; + mutex_init(&rmi_spi->page_mutex); + + rmi_spi->xport.dev = &spi->dev; + rmi_spi->xport.proto_name = "spi"; + rmi_spi->xport.ops = &rmi_spi_ops; + + spi_set_drvdata(spi, rmi_spi); + + retval = rmi_spi_manage_pools(rmi_spi, RMI_SPI_DEFAULT_XFER_BUF_SIZE); + if (retval) + return retval; + + /* + * Setting the page to zero will (a) make sure the PSR is in a + * known state, and (b) make sure we can talk to the device. + */ + retval = rmi_set_page(rmi_spi, 0); + if (retval) { + dev_err(&spi->dev, "Failed to set page select to 0.\n"); + return retval; + } + + retval = rmi_register_transport_device(&rmi_spi->xport); + if (retval) { + dev_err(&spi->dev, "failed to register transport.\n"); + return retval; + } + + retval = rmi_spi_init_irq(spi); + if (retval < 0) + return retval; + + dev_info(&spi->dev, "registered RMI SPI driver\n"); + return 0; +} + +static int rmi_spi_remove(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + + rmi_unregister_transport_device(&rmi_spi->xport); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rmi_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_spi->irq); + if (device_may_wakeup(&spi->dev)) { + ret = enable_irq_wake(rmi_spi->irq); + if (!ret) + dev_warn(dev, "Failed to enable irq for wake: %d\n", + ret); + } + return ret; +} + +static int rmi_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + enable_irq(rmi_spi->irq); + if (device_may_wakeup(&spi->dev)) { + ret = disable_irq_wake(rmi_spi->irq); + if (!ret) + dev_warn(dev, "Failed to disable irq for wake: %d\n", + ret); + } + + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return ret; +} +#endif + +#ifdef CONFIG_PM +static int rmi_spi_runtime_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_spi->irq); + + return 0; +} + +static int rmi_spi_runtime_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + enable_irq(rmi_spi->irq); + + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} +#endif + +static const struct dev_pm_ops rmi_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_spi_suspend, rmi_spi_resume) + SET_RUNTIME_PM_OPS(rmi_spi_runtime_suspend, rmi_spi_runtime_resume, + NULL) +}; + +static const struct spi_device_id rmi_id[] = { + { "rmi4_spi", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rmi_id); + +static struct spi_driver rmi_spi_driver = { + .driver = { + .name = "rmi4_spi", + .pm = &rmi_spi_pm, + }, + .id_table = rmi_id, + .probe = rmi_spi_probe, + .remove = rmi_spi_remove, +}; + +module_spi_driver(rmi_spi_driver); + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_AUTHOR("Andrew Duggan "); +MODULE_DESCRIPTION("RMI SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION); diff --git a/include/linux/rmi.h b/include/linux/rmi.h index ac89d1e731dc..e0aca1476001 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -149,6 +149,55 @@ struct rmi_f01_power_management { u8 doze_interval; }; +/** + * struct rmi_device_platform_data_spi - provides parameters used in SPI + * communications. All Synaptics SPI products support a standard SPI + * interface; some also support what is called SPI V2 mode, depending on + * firmware and/or ASIC limitations. In V2 mode, the touch sensor can + * support shorter delays during certain operations, and these are specified + * separately from the standard mode delays. + * + * @block_delay - for standard SPI transactions consisting of both a read and + * write operation, the delay (in microseconds) between the read and write + * operations. + * @split_read_block_delay_us - for V2 SPI transactions consisting of both a + * read and write operation, the delay (in microseconds) between the read and + * write operations. + * @read_delay_us - the delay between each byte of a read operation in normal + * SPI mode. + * @write_delay_us - the delay between each byte of a write operation in normal + * SPI mode. + * @split_read_byte_delay_us - the delay between each byte of a read operation + * in V2 mode. + * @pre_delay_us - the delay before the start of a SPI transaction. This is + * typically useful in conjunction with custom chip select assertions (see + * below). + * @post_delay_us - the delay after the completion of an SPI transaction. This + * is typically useful in conjunction with custom chip select assertions (see + * below). + * @cs_assert - For systems where the SPI subsystem does not control the CS/SSB + * line, or where such control is broken, you can provide a custom routine to + * handle a GPIO as CS/SSB. This routine will be called at the beginning and + * end of each SPI transaction. The RMI SPI implementation will wait + * pre_delay_us after this routine returns before starting the SPI transfer; + * and post_delay_us after completion of the SPI transfer(s) before calling it + * with assert==FALSE. + */ +struct rmi_device_platform_data_spi { + u32 block_delay_us; + u32 split_read_block_delay_us; + u32 read_delay_us; + u32 write_delay_us; + u32 split_read_byte_delay_us; + u32 pre_delay_us; + u32 post_delay_us; + u8 bits_per_word; + u16 mode; + + void *cs_assert_data; + int (*cs_assert)(const void *cs_assert_data, const bool assert); +}; + /** * struct rmi_device_platform_data - system specific configuration info. * @@ -159,6 +208,8 @@ struct rmi_f01_power_management { struct rmi_device_platform_data { int reset_delay_ms; + struct rmi_device_platform_data_spi spi_data; + /* function handler pdata */ struct rmi_2d_sensor_platform_data *sensor_pdata; struct rmi_f01_power_management power_management; -- cgit v1.2.3 From 48147b9768b83265bf2e1211bcadeca035011380 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Thu, 10 Mar 2016 15:59:52 -0800 Subject: Input: synaptics-rmi4 - add device tree support to the SPI transport driver Add devicetree binding for SPI devices. Signed-off-by: Andrew Duggan Acked-by: Rob Herring Tested-by: Benjamin Tissoires Tested-by: Linus Walleij Tested-by: Bjorn Andersson Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/rmi4/rmi_spi.txt | 57 ++++++++++++++++++++++ Documentation/devicetree/bindings/spi/spi-bus.txt | 2 + drivers/input/rmi4/rmi_spi.c | 44 ++++++++++++++++- 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt (limited to 'drivers/input') diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt new file mode 100644 index 000000000000..a4ca7828f21d --- /dev/null +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt @@ -0,0 +1,57 @@ +Synaptics RMI4 SPI Device Binding + +The Synaptics RMI4 core is able to support RMI4 devices using different +transports and different functions. This file describes the device tree +bindings for devices using the SPI transport driver. Complete documentation +for other transports and functions can be found in +Documentation/devicetree/bindings/input/rmi4. + +Required Properties: +- compatible: syna,rmi4-spi +- reg: Chip select address for the device +- #address-cells: Set to 1 to indicate that the function child nodes + consist of only on uint32 value. +- #size-cells: Set to 0 to indicate that the function child nodes do not + have a size property. + +Optional Properties: +- interrupts: interrupt which the rmi device is connected to. +- interrupt-parent: The interrupt controller. +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +- spi-rx-delay-us: microsecond delay after a read transfer. +- spi-tx-delay-us: microsecond delay after a write transfer. + +Function Parameters: +Parameters specific to RMI functions are contained in child nodes of the rmi device + node. Documentation for the parameters of each function can be found in: +Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt. + + + +Example: + spi@7000d800 { + rmi4-spi-dev@0 { + compatible = "syna,rmi4-spi"; + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <4000000>; + spi-cpha; + spi-cpol; + interrupt-parent = <&gpio>; + interrupts = ; + spi-rx-delay-us = <30>; + + rmi4-f01@1 { + reg = <0x1>; + syna,nosleep-mode = <1>; + }; + + rmi4-f11@11 { + reg = <0x11>; + touchscreen-inverted-y; + syna,sensor-type = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index bbaa857dd68f..42d595425dfb 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -61,6 +61,8 @@ contain the following properties. used for MOSI. Defaults to 1 if not present. - spi-rx-bus-width - (optional) The bus width(number of data wires) that used for MISO. Defaults to 1 if not present. +- spi-rx-delay-us - (optional) Microsecond delay after a read transfer. +- spi-tx-delay-us - (optional) Microsecond delay after a write transfer. Some SPI controllers and devices support Dual and Quad SPI transfer mode. It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD). diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 4319c634553f..55bd1b34970c 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "rmi_driver.h" #define RMI_SPI_DEFAULT_XFER_BUF_SIZE 64 @@ -360,6 +361,41 @@ static int rmi_spi_init_irq(struct spi_device *spi) return 0; } +#ifdef CONFIG_OF +static int rmi_spi_of_probe(struct spi_device *spi, + struct rmi_device_platform_data *pdata) +{ + struct device *dev = &spi->dev; + int retval; + + retval = rmi_of_property_read_u32(dev, + &pdata->spi_data.read_delay_us, + "spi-rx-delay-us", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, + &pdata->spi_data.write_delay_us, + "spi-tx-delay-us", 1); + if (retval) + return retval; + + return 0; +} + +static const struct of_device_id rmi_spi_of_match[] = { + { .compatible = "syna,rmi4-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rmi_spi_of_match); +#else +static inline int rmi_spi_of_probe(struct spi_device *spi, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int rmi_spi_probe(struct spi_device *spi) { struct rmi_spi_xport *rmi_spi; @@ -377,8 +413,13 @@ static int rmi_spi_probe(struct spi_device *spi) pdata = &rmi_spi->xport.pdata; - if (spi_pdata) + if (spi->dev.of_node) { + retval = rmi_spi_of_probe(spi, pdata); + if (retval) + return retval; + } else if (spi_pdata) { *pdata = *spi_pdata; + } if (pdata->spi_data.bits_per_word) spi->bits_per_word = pdata->spi_data.bits_per_word; @@ -532,6 +573,7 @@ static struct spi_driver rmi_spi_driver = { .driver = { .name = "rmi4_spi", .pm = &rmi_spi_pm, + .of_match_table = of_match_ptr(rmi_spi_of_match), }, .id_table = rmi_id, .probe = rmi_spi_probe, -- cgit v1.2.3 From 9979c1c70724ccca8fec10012e320b8145ad5368 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 8 Mar 2016 11:06:39 -0800 Subject: Input: snvs_pwrkey - fix returned value check of syscon_regmap_lookup_by_phandle() On error syscon_regmap_lookup_by_phandle() returns ERR_PTR() value, which makes a check for NULL invalid and may lead to oops on error path. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/snvs_pwrkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index b0ffadeb208c..24a9f599082f 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -111,9 +111,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return -ENOMEM; pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap"); - if (!pdata->snvs) { + if (IS_ERR(pdata->snvs)) { dev_err(&pdev->dev, "Can't get snvs syscon\n"); - return -ENODEV; + return PTR_ERR(pdata->snvs); } if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) { -- cgit v1.2.3 From 9c6ba456711687b794dcf285856fc14e2c76074f Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 14 Mar 2016 09:33:40 -0700 Subject: Input: powermate - fix oops with malicious USB descriptors The powermate driver expects at least one valid USB endpoint in its probe function. If given malicious descriptors that specify 0 for the number of endpoints, it will crash. Validate the number of endpoints on the interface before using them. The full report for this issue can be found here: http://seclists.org/bugtraq/2016/Mar/85 Reported-by: Ralf Spenneberg Cc: stable Signed-off-by: Josh Boyer Signed-off-by: Dmitry Torokhov --- drivers/input/misc/powermate.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index 63b539d3daba..84909a12ff36 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -307,6 +307,9 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i int error = -ENOMEM; interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 1) + return -EINVAL; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) return -EIO; -- cgit v1.2.3 From 24672bdfb357b91bed0ea8d432241bf7bdefc8a8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2016 16:45:07 -0700 Subject: Input: synaptics-rmi4 - using logical instead of bitwise AND There is a typo so we have && instead of &. Fixes: ff8f83708b3e ('Input: synaptics-rmi4 - add support for 2D sensors and F11') Signed-off-by: Dan Carpenter Acked-by: Andrew Duggan Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f11.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index 77a5eb84aed9..ec8a10d53288 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -775,7 +775,7 @@ static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev, sensor_query->has_abs = !!(query_buf[0] & RMI_F11_HAS_ABS); sensor_query->has_gestures = !!(query_buf[0] & RMI_F11_HAS_GESTURES); sensor_query->has_sensitivity_adjust = - !!(query_buf[0] && RMI_F11_HAS_SENSITIVITY_ADJ); + !!(query_buf[0] & RMI_F11_HAS_SENSITIVITY_ADJ); sensor_query->configurable = !!(query_buf[0] & RMI_F11_CONFIGURABLE); sensor_query->nr_x_electrodes = @@ -803,7 +803,7 @@ static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev, sensor_query->has_bending_correction = !!(query_buf[0] & RMI_F11_HAS_BENDING_CORRECTION); sensor_query->has_large_object_suppression = - !!(query_buf[0] && RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION); + !!(query_buf[0] & RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION); sensor_query->has_jitter_filter = !!(query_buf[0] & RMI_F11_HAS_JITTER_FILTER); query_size++; -- cgit v1.2.3