123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- /*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (C) 2012 Alexandra Chin <[email protected]>
- * Copyright (C) 2012 Scott Lin <[email protected]>
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/input.h>
- #include <linux/platform_device.h>
- #include <linux/input/synaptics_dsx.h>
- #include "synaptics_dsx_core.h"
- #define APEN_PHYS_NAME "synaptics_dsx/active_pen"
- #define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535
- #define ACTIVE_PEN_MAX_PRESSURE_8BIT 255
- struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- };
- unsigned char data[2];
- };
- };
- struct apen_data_8b_pressure {
- union {
- struct {
- unsigned char status_pen:1;
- unsigned char status_invert:1;
- unsigned char status_barrel:1;
- unsigned char status_reserved:5;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char pressure_msb;
- unsigned char battery_state;
- unsigned char pen_id_0_7;
- unsigned char pen_id_8_15;
- unsigned char pen_id_16_23;
- unsigned char pen_id_24_31;
- } __packed;
- unsigned char data[11];
- };
- };
- struct apen_data {
- union {
- struct {
- unsigned char status_pen:1;
- unsigned char status_invert:1;
- unsigned char status_barrel:1;
- unsigned char status_reserved:5;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char pressure_lsb;
- unsigned char pressure_msb;
- unsigned char battery_state;
- unsigned char pen_id_0_7;
- unsigned char pen_id_8_15;
- unsigned char pen_id_16_23;
- unsigned char pen_id_24_31;
- } __packed;
- unsigned char data[12];
- };
- };
- struct synaptics_rmi4_apen_handle {
- bool apen_present;
- unsigned char intr_mask;
- unsigned char battery_state;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short apen_data_addr;
- unsigned short max_pressure;
- unsigned int pen_id;
- struct input_dev *apen_dev;
- struct apen_data *apen_data;
- struct synaptics_rmi4_data *rmi4_data;
- };
- static struct synaptics_rmi4_apen_handle *apen;
- DECLARE_COMPLETION(apen_remove_complete);
- static void apen_lift(void)
- {
- input_report_key(apen->apen_dev, BTN_TOUCH, 0);
- input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0);
- input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0);
- input_sync(apen->apen_dev);
- apen->apen_present = false;
- }
- static void apen_report(void)
- {
- int retval;
- int x;
- int y;
- int pressure;
- static int invert = -1;
- struct apen_data_8b_pressure *apen_data_8b;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->apen_data_addr,
- apen->apen_data->data,
- sizeof(apen->apen_data->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read active pen data\n",
- __func__);
- return;
- }
- if (apen->apen_data->status_pen == 0) {
- if (apen->apen_present)
- apen_lift();
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: No active pen data\n",
- __func__);
- return;
- }
- x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb);
- y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb);
- if ((x == -1) && (y == -1)) {
- if (apen->apen_present)
- apen_lift();
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Active pen in range but no valid x & y\n",
- __func__);
- return;
- }
- if (!apen->apen_present)
- invert = -1;
- if (invert != -1 && invert != apen->apen_data->status_invert)
- apen_lift();
- invert = apen->apen_data->status_invert;
- if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) {
- pressure = (apen->apen_data->pressure_msb << 8) |
- apen->apen_data->pressure_lsb;
- apen->battery_state = apen->apen_data->battery_state;
- apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) |
- (apen->apen_data->pen_id_16_23 << 16) |
- (apen->apen_data->pen_id_8_15 << 8) |
- apen->apen_data->pen_id_0_7;
- } else {
- apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data;
- pressure = apen_data_8b->pressure_msb;
- apen->battery_state = apen_data_8b->battery_state;
- apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) |
- (apen_data_8b->pen_id_16_23 << 16) |
- (apen_data_8b->pen_id_8_15 << 8) |
- apen_data_8b->pen_id_0_7;
- }
- input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0);
- input_report_key(apen->apen_dev,
- apen->apen_data->status_invert > 0 ?
- BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1);
- input_report_key(apen->apen_dev,
- BTN_STYLUS, apen->apen_data->status_barrel > 0 ?
- 1 : 0);
- input_report_abs(apen->apen_dev, ABS_X, x);
- input_report_abs(apen->apen_dev, ABS_Y, y);
- input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure);
- input_sync(apen->apen_dev);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n",
- __func__,
- apen->apen_data->status_pen,
- apen->apen_data->status_invert,
- apen->apen_data->status_barrel,
- x, y, pressure);
- apen->apen_present = true;
- }
- static void apen_set_params(void)
- {
- input_set_abs_params(apen->apen_dev, ABS_X, 0,
- apen->rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(apen->apen_dev, ABS_Y, 0,
- apen->rmi4_data->sensor_max_y, 0, 0);
- input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0,
- apen->max_pressure, 0, 0);
- }
- static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8)
- {
- int retval;
- unsigned char ii;
- unsigned char data_reg_presence;
- unsigned char size_of_query_9;
- unsigned char *query_9;
- unsigned char *data_desc;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
- data_reg_presence = query_8->data[1];
- size_of_query_9 = query_8->size_of_query9;
- query_9 = kmalloc(size_of_query_9, GFP_KERNEL);
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 9,
- query_9,
- size_of_query_9);
- if (retval < 0)
- goto exit;
- data_desc = query_9;
- for (ii = 0; ii < 6; ii++) {
- if (!(data_reg_presence & (1 << ii)))
- continue; /* The data register is not present */
- data_desc++; /* Jump over the size entry */
- while (*data_desc & (1 << 7))
- data_desc++;
- data_desc++; /* Go to the next descriptor */
- }
- data_desc++; /* Jump over the size entry */
- /* Check for the presence of subpackets 1 and 2 */
- if ((*data_desc & (3 << 1)) == (3 << 1))
- apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT;
- else
- apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT;
- exit:
- kfree(query_9);
- return retval;
- }
- static int apen_reg_init(void)
- {
- int retval;
- unsigned char data_offset;
- unsigned char size_of_query8;
- struct synaptics_rmi4_f12_query_8 query_8;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 7,
- &size_of_query8,
- sizeof(size_of_query8));
- if (retval < 0)
- return retval;
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 8,
- query_8.data,
- sizeof(query_8.data));
- if (retval < 0)
- return retval;
- if ((size_of_query8 >= 2) && (query_8.data6_is_present)) {
- data_offset = query_8.data0_is_present +
- query_8.data1_is_present +
- query_8.data2_is_present +
- query_8.data3_is_present +
- query_8.data4_is_present +
- query_8.data5_is_present;
- apen->apen_data_addr = apen->data_base_addr + data_offset;
- retval = apen_pressure(&query_8);
- if (retval < 0)
- return retval;
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Active pen support unavailable\n",
- __func__);
- retval = -ENODEV;
- }
- return retval;
- }
- static int apen_scan_pdt(void)
- {
- int retval;
- unsigned char ii;
- unsigned char page;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- struct synaptics_rmi4_fn_desc fd;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&fd,
- sizeof(fd));
- if (retval < 0)
- return retval;
- addr &= ~(MASK_8BIT << 8);
- if (fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, fd.fn_number);
- switch (fd.fn_number) {
- case SYNAPTICS_RMI4_F12:
- goto f12_found;
- }
- } else {
- break;
- }
- intr_count += fd.intr_src_count;
- }
- }
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F12\n",
- __func__);
- return -EINVAL;
- f12_found:
- apen->query_base_addr = fd.query_base_addr | (page << 8);
- apen->control_base_addr = fd.ctrl_base_addr | (page << 8);
- apen->data_base_addr = fd.data_base_addr | (page << 8);
- apen->command_base_addr = fd.cmd_base_addr | (page << 8);
- retval = apen_reg_init();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize active pen registers\n",
- __func__);
- return retval;
- }
- apen->intr_mask = 0;
- intr_src = fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < (intr_src + intr_off);
- ii++) {
- apen->intr_mask |= 1 << ii;
- }
- rmi4_data->intr_mask[0] |= apen->intr_mask;
- addr = rmi4_data->f01_ctrl_base_addr + 1;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &(rmi4_data->intr_mask[0]),
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
- return 0;
- }
- static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
- {
- if (!apen)
- return;
- if (apen->intr_mask & intr_mask)
- apen_report();
- return;
- }
- static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data)
- {
- int retval;
- if (apen) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
- apen = kzalloc(sizeof(*apen), GFP_KERNEL);
- if (!apen) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for apen\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
- apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL);
- if (!apen->apen_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for apen_data\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_apen;
- }
- apen->rmi4_data = rmi4_data;
- retval = apen_scan_pdt();
- if (retval < 0)
- goto exit_free_apen_data;
- apen->apen_dev = input_allocate_device();
- if (apen->apen_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate active pen device\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_apen_data;
- }
- apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME;
- apen->apen_dev->phys = APEN_PHYS_NAME;
- apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(apen->apen_dev, rmi4_data);
- set_bit(EV_KEY, apen->apen_dev->evbit);
- set_bit(EV_ABS, apen->apen_dev->evbit);
- set_bit(BTN_TOUCH, apen->apen_dev->keybit);
- set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit);
- set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit);
- set_bit(BTN_STYLUS, apen->apen_dev->keybit);
- #ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit);
- #endif
- apen_set_params();
- retval = input_register_device(apen->apen_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register active pen device\n",
- __func__);
- goto exit_free_input_device;
- }
- return 0;
- exit_free_input_device:
- input_free_device(apen->apen_dev);
- exit_free_apen_data:
- kfree(apen->apen_data);
- exit_free_apen:
- kfree(apen);
- apen = NULL;
- exit:
- return retval;
- }
- static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data)
- {
- if (!apen)
- goto exit;
- input_unregister_device(apen->apen_dev);
- kfree(apen->apen_data);
- kfree(apen);
- apen = NULL;
- exit:
- complete(&apen_remove_complete);
- }
- static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data)
- {
- if (!apen) {
- synaptics_rmi4_apen_init(rmi4_data);
- return;
- }
- apen_lift();
- apen_scan_pdt();
- }
- static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data)
- {
- if (!apen)
- return;
- apen_lift();
- }
- static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data)
- {
- if (!apen)
- return;
- apen_lift();
- }
- static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data)
- {
- if (!apen)
- return;
- apen_lift();
- }
- static struct synaptics_rmi4_exp_fn active_pen_module = {
- .fn_type = RMI_ACTIVE_PEN,
- .init = synaptics_rmi4_apen_init,
- .remove = synaptics_rmi4_apen_remove,
- .reset = synaptics_rmi4_apen_reset,
- .reinit = synaptics_rmi4_apen_reinit,
- .early_suspend = synaptics_rmi4_apen_e_suspend,
- .suspend = synaptics_rmi4_apen_suspend,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_apen_attn,
- };
- static int __init rmi4_active_pen_module_init(void)
- {
- synaptics_rmi4_new_function(&active_pen_module, true);
- return 0;
- }
- static void __exit rmi4_active_pen_module_exit(void)
- {
- synaptics_rmi4_new_function(&active_pen_module, false);
- wait_for_completion(&apen_remove_complete);
- }
- module_init(rmi4_active_pen_module_init);
- module_exit(rmi4_active_pen_module_exit);
- MODULE_AUTHOR("Synaptics, Inc.");
- MODULE_DESCRIPTION("Synaptics DSX Active Pen Module");
- MODULE_LICENSE("GPL v2");
|