diff options
Diffstat (limited to 'drivers/input')
24 files changed, 1364 insertions, 539 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 34f416a3ebcb..cfcc81c47b50 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -38,7 +38,7 @@ static DEFINE_MUTEX(gameport_mutex); static LIST_HEAD(gameport_list); -static struct bus_type gameport_bus; +static const struct bus_type gameport_bus; static void gameport_add_port(struct gameport *gameport); static void gameport_attach_driver(struct gameport_driver *drv); @@ -813,7 +813,7 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv) return !gameport_drv->ignore; } -static struct bus_type gameport_bus = { +static const struct bus_type gameport_bus = { .name = "gameport", .dev_groups = gameport_device_groups, .drv_groups = gameport_driver_groups, diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c index 0e935914bc3a..6bbf3806ea37 100644 --- a/drivers/input/input-leds.c +++ b/drivers/input/input-leds.c @@ -18,6 +18,12 @@ #define VT_TRIGGER(_name) .trigger = NULL #endif +#if IS_ENABLED(CONFIG_SND_CTL_LED) +#define AUDIO_TRIGGER(_name) .trigger = _name +#else +#define AUDIO_TRIGGER(_name) .trigger = NULL +#endif + static const struct { const char *name; const char *trigger; @@ -29,7 +35,7 @@ static const struct { [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, [LED_SLEEP] = { "sleep" } , [LED_SUSPEND] = { "suspend" }, - [LED_MUTE] = { "mute" }, + [LED_MUTE] = { "mute", AUDIO_TRIGGER("audio-mute") }, [LED_MISC] = { "misc" }, [LED_MAIL] = { "mail" }, [LED_CHARGING] = { "charging" }, diff --git a/drivers/input/input.c b/drivers/input/input.c index f71ea4fb173f..711485437567 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1918,7 +1918,7 @@ static char *input_devnode(const struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); } -struct class input_class = { +const struct class input_class = { .name = "input", .devnode = input_devnode, }; @@ -2629,17 +2629,15 @@ int input_get_new_minor(int legacy_base, unsigned int legacy_num, * locking is needed here. */ if (legacy_base >= 0) { - int minor = ida_simple_get(&input_ida, - legacy_base, - legacy_base + legacy_num, - GFP_KERNEL); + int minor = ida_alloc_range(&input_ida, legacy_base, + legacy_base + legacy_num - 1, + GFP_KERNEL); if (minor >= 0 || !allow_dynamic) return minor; } - return ida_simple_get(&input_ida, - INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES, - GFP_KERNEL); + return ida_alloc_range(&input_ida, INPUT_FIRST_DYNAMIC_DEV, + INPUT_MAX_CHAR_DEVICES - 1, GFP_KERNEL); } EXPORT_SYMBOL(input_get_new_minor); @@ -2652,7 +2650,7 @@ EXPORT_SYMBOL(input_get_new_minor); */ void input_free_minor(unsigned int minor) { - ida_simple_remove(&input_ida, minor); + ida_free(&input_ida, minor); } EXPORT_SYMBOL(input_free_minor); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 14c828adebf7..f50848ed5575 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -127,6 +127,7 @@ static const struct xpad_device { u8 mapping; u8 xtype; } xpad_device[] = { + /* Please keep this list sorted by vendor and product ID. */ { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 }, { 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 }, { 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 }, @@ -152,9 +153,9 @@ static const struct xpad_device { { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE }, - { 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE }, { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, + { 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE }, { 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE }, { 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE }, { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, @@ -340,7 +341,6 @@ static const struct xpad_device { { 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE }, { 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 }, - { 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE }, { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, @@ -355,9 +355,9 @@ static const struct xpad_device { { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, - { 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, @@ -366,8 +366,11 @@ static const struct xpad_device { { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 }, + { 0x294b, 0x3303, "Snakebyte GAMEPAD BASE X", 0, XTYPE_XBOXONE }, + { 0x294b, 0x3404, "Snakebyte GAMEPAD RGB X", 0, XTYPE_XBOXONE }, { 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE }, { 0x2dc8, 0x3106, "8BitDo Pro 2 Wired Controller", 0, XTYPE_XBOX360 }, + { 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE }, { 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 }, @@ -465,6 +468,10 @@ static const signed short xpad_btn_paddles[] = { { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) } static const struct usb_device_id xpad_table[] = { + /* + * Please keep this list sorted by vendor ID. Note that there are 2 + * macros - XPAD_XBOX360_VENDOR and XPAD_XBOXONE_VENDOR. + */ { USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not-approved class */ XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */ XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */ @@ -507,6 +514,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA controllers */ XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */ XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */ + XPAD_XBOXONE_VENDOR(0x294b), /* Snakebyte */ XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */ XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */ XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */ diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c index f3c3746acd4c..6b46f83a9edb 100644 --- a/drivers/input/keyboard/bcm-keypad.c +++ b/drivers/input/keyboard/bcm-keypad.c @@ -418,7 +418,7 @@ static struct platform_driver bcm_kp_device_driver = { .probe = bcm_kp_probe, .driver = { .name = "bcm-keypad", - .of_match_table = of_match_ptr(bcm_kp_of_match), + .of_match_table = bcm_kp_of_match, } }; diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 50fa764c82d2..695c03e075b5 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -28,7 +28,9 @@ struct matrix_keypad { struct input_dev *input_dev; unsigned int row_shift; - DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); + unsigned int row_irqs[MATRIX_MAX_ROWS]; + unsigned int num_row_irqs; + DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS); uint32_t last_key_state[MATRIX_MAX_COLS]; struct delayed_work work; @@ -85,28 +87,18 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata, static void enable_row_irqs(struct matrix_keypad *keypad) { - const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - if (pdata->clustered_irq > 0) - enable_irq(pdata->clustered_irq); - else { - for (i = 0; i < pdata->num_row_gpios; i++) - enable_irq(gpio_to_irq(pdata->row_gpios[i])); - } + for (i = 0; i < keypad->num_row_irqs; i++) + enable_irq(keypad->row_irqs[i]); } static void disable_row_irqs(struct matrix_keypad *keypad) { - const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - if (pdata->clustered_irq > 0) - disable_irq_nosync(pdata->clustered_irq); - else { - for (i = 0; i < pdata->num_row_gpios; i++) - disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); - } + for (i = 0; i < keypad->num_row_irqs; i++) + disable_irq_nosync(keypad->row_irqs[i]); } /* @@ -232,44 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev) static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) { - const struct matrix_keypad_platform_data *pdata = keypad->pdata; - unsigned int gpio; int i; - if (pdata->clustered_irq > 0) { - if (enable_irq_wake(pdata->clustered_irq) == 0) - keypad->gpio_all_disabled = true; - } else { - - for (i = 0; i < pdata->num_row_gpios; i++) { - if (!test_bit(i, keypad->disabled_gpios)) { - gpio = pdata->row_gpios[i]; - - if (enable_irq_wake(gpio_to_irq(gpio)) == 0) - __set_bit(i, keypad->disabled_gpios); - } - } - } + for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) + if (enable_irq_wake(keypad->row_irqs[i]) == 0) + __set_bit(i, keypad->wakeup_enabled_irqs); } static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) { - const struct matrix_keypad_platform_data *pdata = keypad->pdata; - unsigned int gpio; int i; - if (pdata->clustered_irq > 0) { - if (keypad->gpio_all_disabled) { - disable_irq_wake(pdata->clustered_irq); - keypad->gpio_all_disabled = false; - } - } else { - for (i = 0; i < pdata->num_row_gpios; i++) { - if (test_and_clear_bit(i, keypad->disabled_gpios)) { - gpio = pdata->row_gpios[i]; - disable_irq_wake(gpio_to_irq(gpio)); - } - } + for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) { + disable_irq_wake(keypad->row_irqs[i]); + __clear_bit(i, keypad->wakeup_enabled_irqs); } } @@ -306,96 +274,83 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev, struct matrix_keypad *keypad) { const struct matrix_keypad_platform_data *pdata = keypad->pdata; - int i, err; + int i, irq, err; /* initialized strobe lines as outputs, activated */ for (i = 0; i < pdata->num_col_gpios; i++) { - err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); + err = devm_gpio_request(&pdev->dev, + pdata->col_gpios[i], "matrix_kbd_col"); if (err) { dev_err(&pdev->dev, "failed to request GPIO%d for COL%d\n", pdata->col_gpios[i], i); - goto err_free_cols; + return err; } gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); } for (i = 0; i < pdata->num_row_gpios; i++) { - err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); + err = devm_gpio_request(&pdev->dev, + pdata->row_gpios[i], "matrix_kbd_row"); if (err) { dev_err(&pdev->dev, "failed to request GPIO%d for ROW%d\n", pdata->row_gpios[i], i); - goto err_free_rows; + return err; } gpio_direction_input(pdata->row_gpios[i]); } if (pdata->clustered_irq > 0) { - err = request_any_context_irq(pdata->clustered_irq, + err = devm_request_any_context_irq(&pdev->dev, + pdata->clustered_irq, matrix_keypad_interrupt, pdata->clustered_irq_flags, "matrix-keypad", keypad); if (err < 0) { dev_err(&pdev->dev, "Unable to acquire clustered interrupt\n"); - goto err_free_rows; + return err; } + + keypad->row_irqs[0] = pdata->clustered_irq; + keypad->num_row_irqs = 1; } else { for (i = 0; i < pdata->num_row_gpios; i++) { - err = request_any_context_irq( - gpio_to_irq(pdata->row_gpios[i]), + irq = gpio_to_irq(pdata->row_gpios[i]); + if (irq < 0) { + err = irq; + dev_err(&pdev->dev, + "Unable to convert GPIO line %i to irq: %d\n", + pdata->row_gpios[i], err); + return err; + } + + err = devm_request_any_context_irq(&pdev->dev, + irq, matrix_keypad_interrupt, IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING, "matrix-keypad", keypad); if (err < 0) { dev_err(&pdev->dev, "Unable to acquire interrupt for GPIO line %i\n", pdata->row_gpios[i]); - goto err_free_irqs; + return err; } + + keypad->row_irqs[i] = irq; } + + keypad->num_row_irqs = pdata->num_row_gpios; } /* initialized as disabled - enabled by input->open */ disable_row_irqs(keypad); - return 0; - -err_free_irqs: - while (--i >= 0) - free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); - i = pdata->num_row_gpios; -err_free_rows: - while (--i >= 0) - gpio_free(pdata->row_gpios[i]); - i = pdata->num_col_gpios; -err_free_cols: - while (--i >= 0) - gpio_free(pdata->col_gpios[i]); - - return err; -} - -static void matrix_keypad_free_gpio(struct matrix_keypad *keypad) -{ - const struct matrix_keypad_platform_data *pdata = keypad->pdata; - int i; - if (pdata->clustered_irq > 0) { - free_irq(pdata->clustered_irq, keypad); - } else { - for (i = 0; i < pdata->num_row_gpios; i++) - free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); - } - - for (i = 0; i < pdata->num_row_gpios; i++) - gpio_free(pdata->row_gpios[i]); - - for (i = 0; i < pdata->num_col_gpios; i++) - gpio_free(pdata->col_gpios[i]); + return 0; } #ifdef CONFIG_OF @@ -494,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev) return -EINVAL; } - keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { - err = -ENOMEM; - goto err_free_mem; - } + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; keypad->input_dev = input_dev; keypad->pdata = pdata; @@ -510,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; - input_dev->dev.parent = &pdev->dev; input_dev->open = matrix_keypad_start; input_dev->close = matrix_keypad_stop; @@ -520,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev) NULL, input_dev); if (err) { dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_free_mem; + return -ENOMEM; } if (!pdata->no_autorepeat) @@ -530,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev) err = matrix_keypad_init_gpio(pdev, keypad); if (err) - goto err_free_mem; + return err; err = input_register_device(keypad->input_dev); if (err) - goto err_free_gpio; + return err; device_init_wakeup(&pdev->dev, pdata->wakeup); platform_set_drvdata(pdev, keypad); return 0; - -err_free_gpio: - matrix_keypad_free_gpio(keypad); -err_free_mem: - input_free_device(input_dev); - kfree(keypad); - return err; -} - -static void matrix_keypad_remove(struct platform_device *pdev) -{ - struct matrix_keypad *keypad = platform_get_drvdata(pdev); - - matrix_keypad_free_gpio(keypad); - input_unregister_device(keypad->input_dev); - kfree(keypad); } #ifdef CONFIG_OF @@ -568,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); static struct platform_driver matrix_keypad_driver = { .probe = matrix_keypad_probe, - .remove_new = matrix_keypad_remove, .driver = { .name = "matrix-keypad", .pm = pm_sleep_ptr(&matrix_keypad_pm_ops), diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c index 31f0702c3d01..4b0685f96113 100644 --- a/drivers/input/misc/88pm80x_onkey.c +++ b/drivers/input/misc/88pm80x_onkey.c @@ -1,22 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Marvell 88PM80x ONKEY driver * * Copyright (C) 2012 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> * Qiao Zhou <zhouqiao@marvell.com> - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index 36aeeae77611..9ca5a743f19f 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -622,6 +622,118 @@ static const struct iqs7222_dev_desc iqs7222_devs[] = { }, { .prod_num = IQS7222_PROD_NUM_D, + .fw_major = 1, + .fw_minor = 2, + .touch_link = 1770, + .allow_offset = 9, + .event_offset = 10, + .comms_offset = 11, + .reg_grps = { + [IQS7222_REG_GRP_STAT] = { + .base = IQS7222_SYS_STATUS, + .num_row = 1, + .num_col = 7, + }, + [IQS7222_REG_GRP_CYCLE] = { + .base = 0x8000, + .num_row = 7, + .num_col = 2, + }, + [IQS7222_REG_GRP_GLBL] = { + .base = 0x8700, + .num_row = 1, + .num_col = 3, + }, + [IQS7222_REG_GRP_BTN] = { + .base = 0x9000, + .num_row = 14, + .num_col = 3, + }, + [IQS7222_REG_GRP_CHAN] = { + .base = 0xA000, + .num_row = 14, + .num_col = 4, + }, + [IQS7222_REG_GRP_FILT] = { + .base = 0xAE00, + .num_row = 1, + .num_col = 2, + }, + [IQS7222_REG_GRP_TPAD] = { + .base = 0xB000, + .num_row = 1, + .num_col = 24, + }, + [IQS7222_REG_GRP_GPIO] = { + .base = 0xC000, + .num_row = 3, + .num_col = 3, + }, + [IQS7222_REG_GRP_SYS] = { + .base = IQS7222_SYS_SETUP, + .num_row = 1, + .num_col = 12, + }, + }, + }, + { + .prod_num = IQS7222_PROD_NUM_D, + .fw_major = 1, + .fw_minor = 1, + .touch_link = 1774, + .allow_offset = 9, + .event_offset = 10, + .comms_offset = 11, + .reg_grps = { + [IQS7222_REG_GRP_STAT] = { + .base = IQS7222_SYS_STATUS, + .num_row = 1, + .num_col = 7, + }, + [IQS7222_REG_GRP_CYCLE] = { + .base = 0x8000, + .num_row = 7, + .num_col = 2, + }, + [IQS7222_REG_GRP_GLBL] = { + .base = 0x8700, + .num_row = 1, + .num_col = 3, + }, + [IQS7222_REG_GRP_BTN] = { + .base = 0x9000, + .num_row = 14, + .num_col = 3, + }, + [IQS7222_REG_GRP_CHAN] = { + .base = 0xA000, + .num_row = 14, + .num_col = 4, + }, + [IQS7222_REG_GRP_FILT] = { + .base = 0xAE00, + .num_row = 1, + .num_col = 2, + }, + [IQS7222_REG_GRP_TPAD] = { + .base = 0xB000, + .num_row = 1, + .num_col = 24, + }, + [IQS7222_REG_GRP_GPIO] = { + .base = 0xC000, + .num_row = 3, + .num_col = 3, + }, + [IQS7222_REG_GRP_SYS] = { + .base = IQS7222_SYS_SETUP, + .num_row = 1, + .num_col = 12, + }, + }, + }, + { + .prod_num = IQS7222_PROD_NUM_D, .fw_major = 0, .fw_minor = 37, .touch_link = 1770, diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 32cc4c62a716..833b643f0616 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -439,16 +439,4 @@ config MOUSE_SYNAPTICS_USB To compile this driver as a module, choose M here: the module will be called synaptics_usb. -config MOUSE_NAVPOINT_PXA27x - tristate "Synaptics NavPoint (PXA27x SSP/SPI)" - depends on PXA27x && PXA_SSP - help - This driver adds support for the Synaptics NavPoint touchpad connected - to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; - a tap or tap-and-a-half drag gesture emulates the left mouse button. - For example, use the xf86-input-evdev driver for an X pointing device. - - To compile this driver as a module, choose M here: the - module will be called navpoint. - endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 92b3204ce84e..a1336d5bee6f 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o -obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c deleted file mode 100644 index ba757783c258..000000000000 --- a/drivers/input/mouse/navpoint.c +++ /dev/null @@ -1,350 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Synaptics NavPoint (PXA27x SSP/SPI) driver. - * - * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/input.h> -#include <linux/input/navpoint.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/pxa2xx_ssp.h> -#include <linux/slab.h> - -/* - * Synaptics Modular Embedded Protocol: Module Packet Format. - * Module header byte 2:0 = Length (# bytes that follow) - * Module header byte 4:3 = Control - * Module header byte 7:5 = Module Address - */ -#define HEADER_LENGTH(byte) ((byte) & 0x07) -#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03) -#define HEADER_ADDRESS(byte) ((byte) >> 5) - -struct navpoint { - struct ssp_device *ssp; - struct input_dev *input; - struct device *dev; - struct gpio_desc *gpiod; - int index; - u8 data[1 + HEADER_LENGTH(0xff)]; -}; - -/* - * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. - */ -static const u32 sscr0 = 0 - | SSCR0_TUM /* TIM = 1; No TUR interrupts */ - | SSCR0_RIM /* RIM = 1; No ROR interrupts */ - | SSCR0_SSE /* SSE = 1; SSP enabled */ - | SSCR0_Motorola /* FRF = 0; Motorola SPI */ - | SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */ - ; -static const u32 sscr1 = 0 - | SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */ - | SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */ - | SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */ - | SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */ - | SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */ - | SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ - | SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */ - ; -static const u32 sssr = 0 - | SSSR_BCE /* BCE = 1; Clear BCE */ - | SSSR_TUR /* TUR = 1; Clear TUR */ - | SSSR_EOC /* EOC = 1; Clear EOC */ - | SSSR_TINT /* TINT = 1; Clear TINT */ - | SSSR_PINT /* PINT = 1; Clear PINT */ - | SSSR_ROR /* ROR = 1; Clear ROR */ - ; - -/* - * MEP Query $22: Touchpad Coordinate Range Query is not supported by - * the NavPoint module, so sampled values provide the default limits. - */ -#define NAVPOINT_X_MIN 1278 -#define NAVPOINT_X_MAX 5340 -#define NAVPOINT_Y_MIN 1572 -#define NAVPOINT_Y_MAX 4396 -#define NAVPOINT_PRESSURE_MIN 0 -#define NAVPOINT_PRESSURE_MAX 255 - -static void navpoint_packet(struct navpoint *navpoint) -{ - int finger; - int gesture; - int x, y, z; - - switch (navpoint->data[0]) { - case 0xff: /* Garbage (packet?) between reset and Hello packet */ - case 0x00: /* Module 0, NULL packet */ - break; - - case 0x0e: /* Module 0, Absolute packet */ - finger = (navpoint->data[1] & 0x01); - gesture = (navpoint->data[1] & 0x02); - x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; - y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; - z = navpoint->data[6]; - input_report_key(navpoint->input, BTN_TOUCH, finger); - input_report_abs(navpoint->input, ABS_X, x); - input_report_abs(navpoint->input, ABS_Y, y); - input_report_abs(navpoint->input, ABS_PRESSURE, z); - input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); - input_report_key(navpoint->input, BTN_LEFT, gesture); - input_sync(navpoint->input); - break; - - case 0x19: /* Module 0, Hello packet */ - if ((navpoint->data[1] & 0xf0) == 0x10) - break; - fallthrough; - default: - dev_warn(navpoint->dev, - "spurious packet: data=0x%02x,0x%02x,...\n", - navpoint->data[0], navpoint->data[1]); - break; - } -} - -static irqreturn_t navpoint_irq(int irq, void *dev_id) -{ - struct navpoint *navpoint = dev_id; - struct ssp_device *ssp = navpoint->ssp; - irqreturn_t ret = IRQ_NONE; - u32 status; - - status = pxa_ssp_read_reg(ssp, SSSR); - if (status & sssr) { - dev_warn(navpoint->dev, - "unexpected interrupt: status=0x%08x\n", status); - pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); - ret = IRQ_HANDLED; - } - - while (status & SSSR_RNE) { - u32 data; - - data = pxa_ssp_read_reg(ssp, SSDR); - navpoint->data[navpoint->index + 0] = (data >> 8); - navpoint->data[navpoint->index + 1] = data; - navpoint->index += 2; - if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { - navpoint_packet(navpoint); - navpoint->index = 0; - } - status = pxa_ssp_read_reg(ssp, SSSR); - ret = IRQ_HANDLED; - } - - return ret; -} - -static void navpoint_up(struct navpoint *navpoint) -{ - struct ssp_device *ssp = navpoint->ssp; - int timeout; - - clk_prepare_enable(ssp->clk); - - pxa_ssp_write_reg(ssp, SSCR1, sscr1); - pxa_ssp_write_reg(ssp, SSSR, sssr); - pxa_ssp_write_reg(ssp, SSTO, 0); - pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */ - - /* Wait until SSP port is ready for slave clock operations */ - for (timeout = 100; timeout != 0; --timeout) { - if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) - break; - msleep(1); - } - - if (timeout == 0) - dev_err(navpoint->dev, - "timeout waiting for SSSR[CSS] to clear\n"); - - gpiod_set_value(navpoint->gpiod, 1); -} - -static void navpoint_down(struct navpoint *navpoint) -{ - struct ssp_device *ssp = navpoint->ssp; - - gpiod_set_value(navpoint->gpiod, 0); - - pxa_ssp_write_reg(ssp, SSCR0, 0); - - clk_disable_unprepare(ssp->clk); -} - -static int navpoint_open(struct input_dev *input) -{ - struct navpoint *navpoint = input_get_drvdata(input); - - navpoint_up(navpoint); - - return 0; -} - -static void navpoint_close(struct input_dev *input) -{ - struct navpoint *navpoint = input_get_drvdata(input); - - navpoint_down(navpoint); -} - -static int navpoint_probe(struct platform_device *pdev) -{ - const struct navpoint_platform_data *pdata = - dev_get_platdata(&pdev->dev); - struct ssp_device *ssp; - struct input_dev *input; - struct navpoint *navpoint; - int error; - - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - - ssp = pxa_ssp_request(pdata->port, pdev->name); - if (!ssp) - return -ENODEV; - - /* HaRET does not disable devices before jumping into Linux */ - if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { - pxa_ssp_write_reg(ssp, SSCR0, 0); - dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); - } - - navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); - input = input_allocate_device(); - if (!navpoint || !input) { - error = -ENOMEM; - goto err_free_mem; - } - - navpoint->gpiod = gpiod_get_optional(&pdev->dev, - NULL, GPIOD_OUT_LOW); - if (IS_ERR(navpoint->gpiod)) { - error = PTR_ERR(navpoint->gpiod); - dev_err(&pdev->dev, "error getting GPIO\n"); - goto err_free_mem; - } - gpiod_set_consumer_name(navpoint->gpiod, "SYNAPTICS_ON"); - - navpoint->ssp = ssp; - navpoint->input = input; - navpoint->dev = &pdev->dev; - - input->name = pdev->name; - input->dev.parent = &pdev->dev; - - __set_bit(EV_KEY, input->evbit); - __set_bit(EV_ABS, input->evbit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - input_set_abs_params(input, ABS_X, - NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); - input_set_abs_params(input, ABS_Y, - NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); - input_set_abs_params(input, ABS_PRESSURE, - NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, - 0, 0); - - input->open = navpoint_open; - input->close = navpoint_close; - - input_set_drvdata(input, navpoint); - - error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); - if (error) - goto err_free_mem; - - error = input_register_device(input); - if (error) - goto err_free_irq; - - platform_set_drvdata(pdev, navpoint); - dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); - - return 0; - -err_free_irq: - free_irq(ssp->irq, navpoint); -err_free_mem: - input_free_device(input); - kfree(navpoint); - pxa_ssp_free(ssp); - - return error; -} - -static void navpoint_remove(struct platform_device *pdev) -{ - struct navpoint *navpoint = platform_get_drvdata(pdev); - struct ssp_device *ssp = navpoint->ssp; - - free_irq(ssp->irq, navpoint); - - input_unregister_device(navpoint->input); - kfree(navpoint); - - pxa_ssp_free(ssp); -} - -static int navpoint_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct navpoint *navpoint = platform_get_drvdata(pdev); - struct input_dev *input = navpoint->input; - - mutex_lock(&input->mutex); - if (input_device_enabled(input)) - navpoint_down(navpoint); - mutex_unlock(&input->mutex); - - return 0; -} - -static int navpoint_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct navpoint *navpoint = platform_get_drvdata(pdev); - struct input_dev *input = navpoint->input; - - mutex_lock(&input->mutex); - if (input_device_enabled(input)) - navpoint_up(navpoint); - mutex_unlock(&input->mutex); - - return 0; -} - -static DEFINE_SIMPLE_DEV_PM_OPS(navpoint_pm_ops, - navpoint_suspend, navpoint_resume); - -static struct platform_driver navpoint_driver = { - .probe = navpoint_probe, - .remove_new = navpoint_remove, - .driver = { - .name = "navpoint", - .pm = pm_sleep_ptr(&navpoint_pm_ops), - }, -}; - -module_platform_driver(navpoint_driver); - -MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); -MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:navpoint"); diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 1b45b1d3077d..343030290d78 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -344,7 +344,7 @@ static int rmi_bus_match(struct device *dev, struct device_driver *drv) return physical || rmi_function_match(dev, drv); } -struct bus_type rmi_bus_type = { +const struct bus_type rmi_bus_type = { .match = rmi_bus_match, .name = "rmi4", }; diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index 25df6320f9f1..ea46ad9447ec 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -185,7 +185,7 @@ static inline int rmi_write_block(struct rmi_device *d, u16 addr, int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data)); -extern struct bus_type rmi_bus_type; +extern const struct bus_type rmi_bus_type; int rmi_of_property_read_u32(struct device *dev, u32 *result, const char *prop, bool optional); diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 42eaebb3bf5c..ef9ea295f9e0 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -1196,7 +1196,11 @@ static int rmi_driver_probe(struct device *dev) } rmi_driver_set_input_params(rmi_dev, data->input); data->input->phys = devm_kasprintf(dev, GFP_KERNEL, - "%s/input0", dev_name(dev)); + "%s/input0", dev_name(dev)); + if (!data->input->phys) { + retval = -ENOMEM; + goto err; + } } retval = rmi_init_functions(data); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 767fc9efb4a8..a8838b522627 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -1007,7 +1007,7 @@ irqreturn_t serio_interrupt(struct serio *serio, } EXPORT_SYMBOL(serio_interrupt); -struct bus_type serio_bus = { +const struct bus_type serio_bus = { .name = "serio", .drv_groups = serio_driver_groups, .match = serio_bus_match, diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index d8f9faf2b529..bb758346a33d 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -219,8 +219,7 @@ static void sxps2_close(struct serio *pserio) /** * xps2_of_probe - probe method for the PS/2 device. - * @of_dev: pointer to OF device structure - * @match: pointer to the structure used for matching a device + * @ofdev: pointer to OF device structure * * This function probes the PS/2 device in the device tree. * It initializes the driver data structure and the hardware. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e3e2324547b9..c821fe3ee794 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -416,6 +416,37 @@ config TOUCHSCREEN_GOODIX To compile this driver as a module, choose M here: the module will be called goodix. +config TOUCHSCREEN_GOODIX_BERLIN_CORE + tristate + +config TOUCHSCREEN_GOODIX_BERLIN_I2C + tristate "Goodix Berlin I2C touchscreen" + depends on I2C + select REGMAP_I2C + select TOUCHSCREEN_GOODIX_BERLIN_CORE + help + Say Y here if you have a Goodix Berlin IC connected to + your system via I2C. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix_berlin_i2c. + +config TOUCHSCREEN_GOODIX_BERLIN_SPI + tristate "Goodix Berlin SPI touchscreen" + depends on SPI_MASTER + select REGMAP + select TOUCHSCREEN_GOODIX_BERLIN_CORE + help + Say Y here if you have a Goodix Berlin IC connected to + your system via SPI. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix_berlin_spi. + config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 62bd24f3ac8e..a81cb5aa21a5 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -47,6 +47,9 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/goodix_berlin.h b/drivers/input/touchscreen/goodix_berlin.h new file mode 100644 index 000000000000..1fd77eb69c9a --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_berlin_berlin driver. + */ + +#ifndef __GOODIX_BERLIN_H_ +#define __GOODIX_BERLIN_H_ + +#include <linux/pm.h> + +struct device; +struct input_id; +struct regmap; + +int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id, + struct regmap *regmap); + +extern const struct dev_pm_ops goodix_berlin_pm_ops; + +#endif diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c new file mode 100644 index 000000000000..e7b41a926ef8 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -0,0 +1,755 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix "Berlin" Touchscreen IC driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + * + * This driver is distinct from goodix.c since hardware interface + * is different enough to require a new driver. + * None of the register address or data structure are close enough + * to the previous generations. + * + * Currently the driver only handles Multitouch events with already + * programmed firmware and "config" for "Revision D" Berlin IC. + * + * Support is missing for: + * - ESD Management + * - Firmware update/flashing + * - "Config" update/flashing + * - Stylus Events + * - Gesture Events + * - Support for older revisions (A & B) + */ + +#include <linux/bitfield.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/sizes.h> +#include <asm/unaligned.h> + +#include "goodix_berlin.h" + +#define GOODIX_BERLIN_MAX_TOUCH 10 + +#define GOODIX_BERLIN_NORMAL_RESET_DELAY_MS 100 + +#define GOODIX_BERLIN_TOUCH_EVENT BIT(7) +#define GOODIX_BERLIN_REQUEST_EVENT BIT(6) +#define GOODIX_BERLIN_TOUCH_COUNT_MASK GENMASK(3, 0) + +#define GOODIX_BERLIN_REQUEST_CODE_RESET 3 + +#define GOODIX_BERLIN_POINT_TYPE_MASK GENMASK(3, 0) +#define GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER 1 +#define GOODIX_BERLIN_POINT_TYPE_STYLUS 3 + +#define GOODIX_BERLIN_TOUCH_ID_MASK GENMASK(7, 4) + +#define GOODIX_BERLIN_DEV_CONFIRM_VAL 0xAA +#define GOODIX_BERLIN_BOOTOPTION_ADDR 0x10000 +#define GOODIX_BERLIN_FW_VERSION_INFO_ADDR 0x10014 + +#define GOODIX_BERLIN_IC_INFO_MAX_LEN SZ_1K +#define GOODIX_BERLIN_IC_INFO_ADDR 0x10070 + +#define GOODIX_BERLIN_CHECKSUM_SIZE sizeof(u16) + +struct goodix_berlin_fw_version { + u8 rom_pid[6]; + u8 rom_vid[3]; + u8 rom_vid_reserved; + u8 patch_pid[8]; + u8 patch_vid[4]; + u8 patch_vid_reserved; + u8 sensor_id; + u8 reserved[2]; + __le16 checksum; +}; + +struct goodix_berlin_ic_info_version { + u8 info_customer_id; + u8 info_version_id; + u8 ic_die_id; + u8 ic_version_id; + __le32 config_id; + u8 config_version; + u8 frame_data_customer_id; + u8 frame_data_version_id; + u8 touch_data_customer_id; + u8 touch_data_version_id; + u8 reserved[3]; +} __packed; + +struct goodix_berlin_ic_info_feature { + __le16 freqhop_feature; + __le16 calibration_feature; + __le16 gesture_feature; + __le16 side_touch_feature; + __le16 stylus_feature; +} __packed; + +struct goodix_berlin_ic_info_misc { + __le32 cmd_addr; + __le16 cmd_max_len; + __le32 cmd_reply_addr; + __le16 cmd_reply_len; + __le32 fw_state_addr; + __le16 fw_state_len; + __le32 fw_buffer_addr; + __le16 fw_buffer_max_len; + __le32 frame_data_addr; + __le16 frame_data_head_len; + __le16 fw_attr_len; + __le16 fw_log_len; + u8 pack_max_num; + u8 pack_compress_version; + __le16 stylus_struct_len; + __le16 mutual_struct_len; + __le16 self_struct_len; + __le16 noise_struct_len; + __le32 touch_data_addr; + __le16 touch_data_head_len; + __le16 point_struct_len; + __le16 reserved1; + __le16 reserved2; + __le32 mutual_rawdata_addr; + __le32 mutual_diffdata_addr; + __le32 mutual_refdata_addr; + __le32 self_rawdata_addr; + __le32 self_diffdata_addr; + __le32 self_refdata_addr; + __le32 iq_rawdata_addr; + __le32 iq_refdata_addr; + __le32 im_rawdata_addr; + __le16 im_readata_len; + __le32 noise_rawdata_addr; + __le16 noise_rawdata_len; + __le32 stylus_rawdata_addr; + __le16 stylus_rawdata_len; + __le32 noise_data_addr; + __le32 esd_addr; +} __packed; + +struct goodix_berlin_touch { + u8 status; + u8 reserved; + __le16 x; + __le16 y; + __le16 w; +}; +#define GOODIX_BERLIN_TOUCH_SIZE sizeof(struct goodix_berlin_touch) + +struct goodix_berlin_header { + u8 status; + u8 reserved1; + u8 request_type; + u8 reserved2[3]; + __le16 checksum; +}; +#define GOODIX_BERLIN_HEADER_SIZE sizeof(struct goodix_berlin_header) + +struct goodix_berlin_event { + struct goodix_berlin_header hdr; + /* The data below is u16/__le16 aligned */ + u8 data[GOODIX_BERLIN_TOUCH_SIZE * GOODIX_BERLIN_MAX_TOUCH + + GOODIX_BERLIN_CHECKSUM_SIZE]; +}; + +struct goodix_berlin_core { + struct device *dev; + struct regmap *regmap; + struct regulator *avdd; + struct regulator *iovdd; + struct gpio_desc *reset_gpio; + struct touchscreen_properties props; + struct goodix_berlin_fw_version fw_version; + struct input_dev *input_dev; + int irq; + + /* Runtime parameters extracted from IC_INFO buffer */ + u32 touch_data_addr; + + struct goodix_berlin_event event; +}; + +static bool goodix_berlin_checksum_valid(const u8 *data, int size) +{ + u32 cal_checksum = 0; + u16 r_checksum; + int i; + + if (size < GOODIX_BERLIN_CHECKSUM_SIZE) + return false; + + for (i = 0; i < size - GOODIX_BERLIN_CHECKSUM_SIZE; i++) + cal_checksum += data[i]; + + r_checksum = get_unaligned_le16(&data[i]); + + return (u16)cal_checksum == r_checksum; +} + +static bool goodix_berlin_is_dummy_data(struct goodix_berlin_core *cd, + const u8 *data, int size) +{ + int i; + + /* + * If the device is missing or doesn't respond the buffer + * could be filled with bus default line state, 0x00 or 0xff, + * so declare success the first time we encounter neither. + */ + for (i = 0; i < size; i++) + if (data[i] > 0 && data[i] < 0xff) + return false; + + return true; +} + +static int goodix_berlin_dev_confirm(struct goodix_berlin_core *cd) +{ + u8 tx_buf[8], rx_buf[8]; + int retry = 3; + int error; + + memset(tx_buf, GOODIX_BERLIN_DEV_CONFIRM_VAL, sizeof(tx_buf)); + while (retry--) { + error = regmap_raw_write(cd->regmap, + GOODIX_BERLIN_BOOTOPTION_ADDR, + tx_buf, sizeof(tx_buf)); + if (error) + return error; + + error = regmap_raw_read(cd->regmap, + GOODIX_BERLIN_BOOTOPTION_ADDR, + rx_buf, sizeof(rx_buf)); + if (error) + return error; + + if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) + return 0; + + usleep_range(5000, 5100); + } + + dev_err(cd->dev, "device confirm failed, rx_buf: %*ph\n", + (int)sizeof(rx_buf), rx_buf); + + return -EINVAL; +} + +static int goodix_berlin_power_on(struct goodix_berlin_core *cd) +{ + int error; + + error = regulator_enable(cd->iovdd); + if (error) { + dev_err(cd->dev, "Failed to enable iovdd: %d\n", error); + return error; + } + + /* Vendor waits 3ms for IOVDD to settle */ + usleep_range(3000, 3100); + + error = regulator_enable(cd->avdd); + if (error) { + dev_err(cd->dev, "Failed to enable avdd: %d\n", error); + goto err_iovdd_disable; + } + + /* Vendor waits 15ms for IOVDD to settle */ + usleep_range(15000, 15100); + + gpiod_set_value_cansleep(cd->reset_gpio, 0); + + /* Vendor waits 4ms for Firmware to initialize */ + usleep_range(4000, 4100); + + error = goodix_berlin_dev_confirm(cd); + if (error) + goto err_dev_reset; + + /* Vendor waits 100ms for Firmware to fully boot */ + msleep(GOODIX_BERLIN_NORMAL_RESET_DELAY_MS); + + return 0; + +err_dev_reset: + gpiod_set_value_cansleep(cd->reset_gpio, 1); + regulator_disable(cd->avdd); +err_iovdd_disable: + regulator_disable(cd->iovdd); + return error; +} + +static void goodix_berlin_power_off(struct goodix_berlin_core *cd) +{ + gpiod_set_value_cansleep(cd->reset_gpio, 1); + regulator_disable(cd->avdd); + regulator_disable(cd->iovdd); +} + +static int goodix_berlin_read_version(struct goodix_berlin_core *cd) +{ + int error; + + error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_FW_VERSION_INFO_ADDR, + &cd->fw_version, sizeof(cd->fw_version)); + if (error) { + dev_err(cd->dev, "error reading fw version, %d\n", error); + return error; + } + + if (!goodix_berlin_checksum_valid((u8 *)&cd->fw_version, + sizeof(cd->fw_version))) { + dev_err(cd->dev, "invalid fw version: checksum error\n"); + return -EINVAL; + } + + return 0; +} + +/* Only extract necessary data for runtime */ +static int goodix_berlin_parse_ic_info(struct goodix_berlin_core *cd, + const u8 *data, u16 length) +{ + struct goodix_berlin_ic_info_misc *misc; + unsigned int offset = 0; + + offset += sizeof(__le16); /* length */ + offset += sizeof(struct goodix_berlin_ic_info_version); + offset += sizeof(struct goodix_berlin_ic_info_feature); + + /* IC_INFO Parameters, variable width structure */ + offset += 4 * sizeof(u8); /* drv_num, sen_num, button_num, force_num */ + if (offset >= length) + goto invalid_offset; + +#define ADVANCE_LE16_PARAMS() \ + do { \ + u8 param_num = data[offset++]; \ + offset += param_num * sizeof(__le16); \ + if (offset >= length) \ + goto invalid_offset; \ + } while (0) + ADVANCE_LE16_PARAMS(); /* active_scan_rate_num */ + ADVANCE_LE16_PARAMS(); /* mutual_freq_num*/ + ADVANCE_LE16_PARAMS(); /* self_tx_freq_num */ + ADVANCE_LE16_PARAMS(); /* self_rx_freq_num */ + ADVANCE_LE16_PARAMS(); /* stylus_freq_num */ +#undef ADVANCE_LE16_PARAMS + + misc = (struct goodix_berlin_ic_info_misc *)&data[offset]; + cd->touch_data_addr = le32_to_cpu(misc->touch_data_addr); + + return 0; + +invalid_offset: + dev_err(cd->dev, "ic_info length is invalid (offset %d length %d)\n", + offset, length); + return -EINVAL; +} + +static int goodix_berlin_get_ic_info(struct goodix_berlin_core *cd) +{ + u8 *afe_data __free(kfree) = NULL; + __le16 length_raw; + u16 length; + int error; + + afe_data = kzalloc(GOODIX_BERLIN_IC_INFO_MAX_LEN, GFP_KERNEL); + if (!afe_data) + return -ENOMEM; + + error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR, + &length_raw, sizeof(length_raw)); + if (error) { + dev_err(cd->dev, "failed get ic info length, %d\n", error); + return error; + } + + length = le16_to_cpu(length_raw); + if (length >= GOODIX_BERLIN_IC_INFO_MAX_LEN) { + dev_err(cd->dev, "invalid ic info length %d\n", length); + return -EINVAL; + } + + error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR, + afe_data, length); + if (error) { + dev_err(cd->dev, "failed get ic info data, %d\n", error); + return error; + } + + /* check whether the data is valid (ex. bus default values) */ + if (goodix_berlin_is_dummy_data(cd, afe_data, length)) { + dev_err(cd->dev, "fw info data invalid\n"); + return -EINVAL; + } + + if (!goodix_berlin_checksum_valid(afe_data, length)) { + dev_err(cd->dev, "fw info checksum error\n"); + return -EINVAL; + } + + error = goodix_berlin_parse_ic_info(cd, afe_data, length); + if (error) + return error; + + /* check some key info */ + if (!cd->touch_data_addr) { + dev_err(cd->dev, "touch_data_addr is null\n"); + return -EINVAL; + } + + return 0; +} + +static int goodix_berlin_get_remaining_contacts(struct goodix_berlin_core *cd, + int n) +{ + size_t offset = 2 * GOODIX_BERLIN_TOUCH_SIZE + + GOODIX_BERLIN_CHECKSUM_SIZE; + u32 addr = cd->touch_data_addr + GOODIX_BERLIN_HEADER_SIZE + offset; + int error; + + error = regmap_raw_read(cd->regmap, addr, + &cd->event.data[offset], + (n - 2) * GOODIX_BERLIN_TOUCH_SIZE); + if (error) { + dev_err_ratelimited(cd->dev, "failed to get touch data, %d\n", + error); + return error; + } + + return 0; +} + +static void goodix_berlin_report_state(struct goodix_berlin_core *cd, int n) +{ + struct goodix_berlin_touch *touch_data = + (struct goodix_berlin_touch *)cd->event.data; + struct goodix_berlin_touch *t; + int i; + u8 type, id; + + for (i = 0; i < n; i++) { + t = &touch_data[i]; + + type = FIELD_GET(GOODIX_BERLIN_POINT_TYPE_MASK, t->status); + if (type == GOODIX_BERLIN_POINT_TYPE_STYLUS || + type == GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER) { + dev_warn_once(cd->dev, "Stylus event type not handled\n"); + continue; + } + + id = FIELD_GET(GOODIX_BERLIN_TOUCH_ID_MASK, t->status); + if (id >= GOODIX_BERLIN_MAX_TOUCH) { + dev_warn_ratelimited(cd->dev, "invalid finger id %d\n", id); + continue; + } + + input_mt_slot(cd->input_dev, id); + input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, true); + + touchscreen_report_pos(cd->input_dev, &cd->props, + __le16_to_cpu(t->x), __le16_to_cpu(t->y), + true); + input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR, + __le16_to_cpu(t->w)); + } + + input_mt_sync_frame(cd->input_dev); + input_sync(cd->input_dev); +} + +static void goodix_berlin_touch_handler(struct goodix_berlin_core *cd) +{ + u8 touch_num; + int error; + + touch_num = FIELD_GET(GOODIX_BERLIN_TOUCH_COUNT_MASK, + cd->event.hdr.request_type); + if (touch_num > GOODIX_BERLIN_MAX_TOUCH) { + dev_warn(cd->dev, "invalid touch num %d\n", touch_num); + return; + } + + if (touch_num > 2) { + /* read additional contact data if more than 2 touch events */ + error = goodix_berlin_get_remaining_contacts(cd, touch_num); + if (error) + return; + } + + if (touch_num) { + int len = touch_num * GOODIX_BERLIN_TOUCH_SIZE + + GOODIX_BERLIN_CHECKSUM_SIZE; + if (!goodix_berlin_checksum_valid(cd->event.data, len)) { + dev_err(cd->dev, "touch data checksum error: %*ph\n", + len, cd->event.data); + return; + } + } + + goodix_berlin_report_state(cd, touch_num); +} + +static int goodix_berlin_request_handle_reset(struct goodix_berlin_core *cd) +{ + gpiod_set_value_cansleep(cd->reset_gpio, 1); + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cd->reset_gpio, 0); + + msleep(GOODIX_BERLIN_NORMAL_RESET_DELAY_MS); + + return 0; +} + +static irqreturn_t goodix_berlin_irq(int irq, void *data) +{ + struct goodix_berlin_core *cd = data; + int error; + + /* + * First, read buffer with space for 2 touch events: + * - GOODIX_BERLIN_HEADER_SIZE = 8 bytes + * - GOODIX_BERLIN_TOUCH_SIZE * 2 = 16 bytes + * - GOODIX_BERLIN_CHECKLSUM_SIZE = 2 bytes + * For a total of 26 bytes. + * + * If only a single finger is reported, we will read 8 bytes more than + * needed: + * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) + * - bytes 8-15: Finger 0 Data + * - bytes 24-25: Checksum + * - bytes 18-25: Unused 8 bytes + * + * If 2 fingers are reported, we would have read the exact needed + * amount of data and checksum would be at the end of the buffer: + * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) + * - bytes 8-15: Finger 0 Bytes 0-7 + * - bytes 16-23: Finger 1 Bytes 0-7 + * - bytes 24-25: Checksum + * + * If more than 2 fingers were reported, the "Checksum" bytes would + * in fact contain part of the next finger data, and then + * goodix_berlin_get_remaining_contacts() would complete the buffer + * with the missing bytes, including the trailing checksum. + * For example, if 3 fingers are reported, then we would do: + * Read 1: + * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) + * - bytes 8-15: Finger 0 Bytes 0-7 + * - bytes 16-23: Finger 1 Bytes 0-7 + * - bytes 24-25: Finger 2 Bytes 0-1 + * Read 2 (with length of (3 - 2) * 8 = 8 bytes): + * - bytes 26-31: Finger 2 Bytes 2-7 + * - bytes 32-33: Checksum + */ + error = regmap_raw_read(cd->regmap, cd->touch_data_addr, + &cd->event, + GOODIX_BERLIN_HEADER_SIZE + + 2 * GOODIX_BERLIN_TOUCH_SIZE + + GOODIX_BERLIN_CHECKSUM_SIZE); + if (error) { + dev_warn_ratelimited(cd->dev, + "failed get event head data: %d\n", error); + goto out; + } + + if (cd->event.hdr.status == 0) + goto out; + + if (!goodix_berlin_checksum_valid((u8 *)&cd->event.hdr, + GOODIX_BERLIN_HEADER_SIZE)) { + dev_warn_ratelimited(cd->dev, + "touch head checksum error: %*ph\n", + (int)GOODIX_BERLIN_HEADER_SIZE, + &cd->event.hdr); + goto out_clear; + } + + if (cd->event.hdr.status & GOODIX_BERLIN_TOUCH_EVENT) + goodix_berlin_touch_handler(cd); + + if (cd->event.hdr.status & GOODIX_BERLIN_REQUEST_EVENT) { + switch (cd->event.hdr.request_type) { + case GOODIX_BERLIN_REQUEST_CODE_RESET: + if (cd->reset_gpio) + goodix_berlin_request_handle_reset(cd); + break; + + default: + dev_warn(cd->dev, "unsupported request code 0x%x\n", + cd->event.hdr.request_type); + } + } + + +out_clear: + /* Clear up status field */ + regmap_write(cd->regmap, cd->touch_data_addr, 0); + +out: + return IRQ_HANDLED; +} + +static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd, + const struct input_id *id) +{ + struct input_dev *input_dev; + int error; + + input_dev = devm_input_allocate_device(cd->dev); + if (!input_dev) + return -ENOMEM; + + cd->input_dev = input_dev; + input_set_drvdata(input_dev, cd); + + input_dev->name = "Goodix Berlin Capacitive TouchScreen"; + input_dev->phys = "input/ts"; + + input_dev->id = *id; + + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_X, + 0, SZ_64K - 1, 0, 0); + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_Y, + 0, SZ_64K - 1, 0, 0); + input_set_abs_params(cd->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + touchscreen_parse_properties(cd->input_dev, true, &cd->props); + + error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = input_register_device(cd->input_dev); + if (error) + return error; + + return 0; +} + +static int goodix_berlin_suspend(struct device *dev) +{ + struct goodix_berlin_core *cd = dev_get_drvdata(dev); + + disable_irq(cd->irq); + goodix_berlin_power_off(cd); + + return 0; +} + +static int goodix_berlin_resume(struct device *dev) +{ + struct goodix_berlin_core *cd = dev_get_drvdata(dev); + int error; + + error = goodix_berlin_power_on(cd); + if (error) + return error; + + enable_irq(cd->irq); + + return 0; +} + +EXPORT_GPL_SIMPLE_DEV_PM_OPS(goodix_berlin_pm_ops, + goodix_berlin_suspend, goodix_berlin_resume); + +static void goodix_berlin_power_off_act(void *data) +{ + struct goodix_berlin_core *cd = data; + + goodix_berlin_power_off(cd); +} + +int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id, + struct regmap *regmap) +{ + struct goodix_berlin_core *cd; + int error; + + if (irq <= 0) { + dev_err(dev, "Missing interrupt number\n"); + return -EINVAL; + } + + cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL); + if (!cd) + return -ENOMEM; + + cd->dev = dev; + cd->regmap = regmap; + cd->irq = irq; + + cd->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(cd->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(cd->reset_gpio), + "Failed to request reset gpio\n"); + + cd->avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(cd->avdd)) + return dev_err_probe(dev, PTR_ERR(cd->avdd), + "Failed to request avdd regulator\n"); + + cd->iovdd = devm_regulator_get(dev, "iovdd"); + if (IS_ERR(cd->iovdd)) + return dev_err_probe(dev, PTR_ERR(cd->iovdd), + "Failed to request iovdd regulator\n"); + + error = goodix_berlin_power_on(cd); + if (error) { + dev_err(dev, "failed power on"); + return error; + } + + error = devm_add_action_or_reset(dev, goodix_berlin_power_off_act, cd); + if (error) + return error; + + error = goodix_berlin_read_version(cd); + if (error) { + dev_err(dev, "failed to get version info"); + return error; + } + + error = goodix_berlin_get_ic_info(cd); + if (error) { + dev_err(dev, "invalid ic info, abort"); + return error; + } + + error = goodix_berlin_input_dev_config(cd, id); + if (error) { + dev_err(dev, "failed set input device"); + return error; + } + + error = devm_request_threaded_irq(dev, cd->irq, NULL, goodix_berlin_irq, + IRQF_ONESHOT, "goodix-berlin", cd); + if (error) { + dev_err(dev, "request threaded irq failed: %d\n", error); + return error; + } + + dev_set_drvdata(dev, cd); + + dev_dbg(dev, "Goodix Berlin %s Touchscreen Controller", + cd->fw_version.patch_pid); + + return 0; +} +EXPORT_SYMBOL_GPL(goodix_berlin_probe); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin Core Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); diff --git a/drivers/input/touchscreen/goodix_berlin_i2c.c b/drivers/input/touchscreen/goodix_berlin_i2c.c new file mode 100644 index 000000000000..6ed9aa8088cb --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_i2c.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix Berlin Touchscreen Driver + * + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + */ +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/input.h> + +#include "goodix_berlin.h" + +#define I2C_MAX_TRANSFER_SIZE 256 + +static const struct regmap_config goodix_berlin_i2c_regmap_conf = { + .reg_bits = 32, + .val_bits = 8, + .max_raw_read = I2C_MAX_TRANSFER_SIZE, + .max_raw_write = I2C_MAX_TRANSFER_SIZE, +}; + +/* vendor & product left unassigned here, should probably be updated from fw info */ +static const struct input_id goodix_berlin_i2c_input_id = { + .bustype = BUS_I2C, +}; + +static int goodix_berlin_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int error; + + regmap = devm_regmap_init_i2c(client, &goodix_berlin_i2c_regmap_conf); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + error = goodix_berlin_probe(&client->dev, client->irq, + &goodix_berlin_i2c_input_id, regmap); + if (error) + return error; + + return 0; +} + +static const struct i2c_device_id goodix_berlin_i2c_id[] = { + { "gt9916", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, goodix_berlin_i2c_id); + +static const struct of_device_id goodix_berlin_i2c_of_match[] = { + { .compatible = "goodix,gt9916", }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_berlin_i2c_of_match); + +static struct i2c_driver goodix_berlin_i2c_driver = { + .driver = { + .name = "goodix-berlin-i2c", + .of_match_table = goodix_berlin_i2c_of_match, + .pm = pm_sleep_ptr(&goodix_berlin_pm_ops), + }, + .probe = goodix_berlin_i2c_probe, + .id_table = goodix_berlin_i2c_id, +}; +module_i2c_driver(goodix_berlin_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin I2C Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); diff --git a/drivers/input/touchscreen/goodix_berlin_spi.c b/drivers/input/touchscreen/goodix_berlin_spi.c new file mode 100644 index 000000000000..4cc557da048a --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_spi.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix Berlin Touchscreen Driver + * + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + */ +#include <asm/unaligned.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/input.h> + +#include "goodix_berlin.h" + +#define GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN 1 +#define GOODIX_BERLIN_REGISTER_WIDTH 4 +#define GOODIX_BERLIN_SPI_READ_DUMMY_LEN 3 +#define GOODIX_BERLIN_SPI_READ_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \ + GOODIX_BERLIN_REGISTER_WIDTH + \ + GOODIX_BERLIN_SPI_READ_DUMMY_LEN) +#define GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \ + GOODIX_BERLIN_REGISTER_WIDTH) + +#define GOODIX_BERLIN_SPI_WRITE_FLAG 0xF0 +#define GOODIX_BERLIN_SPI_READ_FLAG 0xF1 + +static int goodix_berlin_spi_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct spi_device *spi = context; + struct spi_transfer xfers; + struct spi_message spi_msg; + const u32 *reg = reg_buf; /* reg is stored as native u32 at start of buffer */ + u8 *buf; + int error; + + if (reg_size != GOODIX_BERLIN_REGISTER_WIDTH) + return -EINVAL; + + buf = kzalloc(GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ + buf[0] = GOODIX_BERLIN_SPI_READ_FLAG; + put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN); + memset(buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + GOODIX_BERLIN_REGISTER_WIDTH, + 0xff, GOODIX_BERLIN_SPI_READ_DUMMY_LEN); + + xfers.tx_buf = buf; + xfers.rx_buf = buf; + xfers.len = GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + + error = spi_sync(spi, &spi_msg); + if (error < 0) + dev_err(&spi->dev, "spi transfer error, %d", error); + else + memcpy(val_buf, buf + GOODIX_BERLIN_SPI_READ_PREFIX_LEN, val_size); + + kfree(buf); + return error; +} + +static int goodix_berlin_spi_write(void *context, const void *data, + size_t count) +{ + unsigned int len = count - GOODIX_BERLIN_REGISTER_WIDTH; + struct spi_device *spi = context; + struct spi_transfer xfers; + struct spi_message spi_msg; + const u32 *reg = data; /* reg is stored as native u32 at start of buffer */ + u8 *buf; + int error; + + buf = kzalloc(GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + buf[0] = GOODIX_BERLIN_SPI_WRITE_FLAG; + put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN); + memcpy(buf + GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN, + data + GOODIX_BERLIN_REGISTER_WIDTH, len); + + xfers.tx_buf = buf; + xfers.len = GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + + error = spi_sync(spi, &spi_msg); + if (error < 0) + dev_err(&spi->dev, "spi transfer error, %d", error); + + kfree(buf); + return error; +} + +static const struct regmap_config goodix_berlin_spi_regmap_conf = { + .reg_bits = 32, + .val_bits = 8, + .read = goodix_berlin_spi_read, + .write = goodix_berlin_spi_write, +}; + +/* vendor & product left unassigned here, should probably be updated from fw info */ +static const struct input_id goodix_berlin_spi_input_id = { + .bustype = BUS_SPI, +}; + +static int goodix_berlin_spi_probe(struct spi_device *spi) +{ + struct regmap_config regmap_config; + struct regmap *regmap; + size_t max_size; + int error = 0; + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + error = spi_setup(spi); + if (error) + return error; + + max_size = spi_max_transfer_size(spi); + + regmap_config = goodix_berlin_spi_regmap_conf; + regmap_config.max_raw_read = max_size - GOODIX_BERLIN_SPI_READ_PREFIX_LEN; + regmap_config.max_raw_write = max_size - GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN; + + regmap = devm_regmap_init(&spi->dev, NULL, spi, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + error = goodix_berlin_probe(&spi->dev, spi->irq, + &goodix_berlin_spi_input_id, regmap); + if (error) + return error; + + return 0; +} + +static const struct spi_device_id goodix_berlin_spi_ids[] = { + { "gt9916" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, goodix_berlin_spi_ids); + +static const struct of_device_id goodix_berlin_spi_of_match[] = { + { .compatible = "goodix,gt9916", }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_berlin_spi_of_match); + +static struct spi_driver goodix_berlin_spi_driver = { + .driver = { + .name = "goodix-berlin-spi", + .of_match_table = goodix_berlin_spi_of_match, + .pm = pm_sleep_ptr(&goodix_berlin_pm_ops), + }, + .probe = goodix_berlin_spi_probe, + .id_table = goodix_berlin_spi_ids, +}; +module_spi_driver(goodix_berlin_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin SPI Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); diff --git a/drivers/input/touchscreen/imagis.c b/drivers/input/touchscreen/imagis.c index 07111ca24455..074dd6c342ec 100644 --- a/drivers/input/touchscreen/imagis.c +++ b/drivers/input/touchscreen/imagis.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include <linux/bitfield.h> #include <linux/bits.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -11,9 +12,15 @@ #include <linux/property.h> #include <linux/regulator/consumer.h> +#define IST3032C_WHOAMI 0x32c + +#define IST3038B_REG_STATUS 0x20 +#define IST3038B_REG_CHIPID 0x30 +#define IST3038B_WHOAMI 0x30380b + #define IST3038C_HIB_ACCESS (0x800B << 16) #define IST3038C_DIRECT_ACCESS BIT(31) -#define IST3038C_REG_CHIPID 0x40001000 +#define IST3038C_REG_CHIPID (0x40001000 | IST3038C_DIRECT_ACCESS) #define IST3038C_REG_HIB_BASE 0x30000100 #define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS) #define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8) @@ -23,19 +30,29 @@ #define IST3038C_I2C_RETRY_COUNT 3 #define IST3038C_MAX_FINGER_NUM 10 #define IST3038C_X_MASK GENMASK(23, 12) -#define IST3038C_X_SHIFT 12 #define IST3038C_Y_MASK GENMASK(11, 0) #define IST3038C_AREA_MASK GENMASK(27, 24) -#define IST3038C_AREA_SHIFT 24 #define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12) -#define IST3038C_FINGER_COUNT_SHIFT 12 #define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0) +#define IST3032C_KEY_STATUS_MASK GENMASK(20, 16) + +struct imagis_properties { + unsigned int interrupt_msg_cmd; + unsigned int touch_coord_cmd; + unsigned int whoami_cmd; + unsigned int whoami_val; + bool protocol_b; + bool touch_keys_supported; +}; struct imagis_ts { struct i2c_client *client; + const struct imagis_properties *tdata; struct input_dev *input_dev; struct touchscreen_properties prop; struct regulator_bulk_data supplies[2]; + u32 keycodes[5]; + int num_keycodes; }; static int imagis_i2c_read_reg(struct imagis_ts *ts, @@ -80,20 +97,18 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) { struct imagis_ts *ts = dev_id; u32 intr_message, finger_status; - unsigned int finger_count, finger_pressed; + unsigned int finger_count, finger_pressed, key_pressed; int i; int error; - error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE, - &intr_message); + error = imagis_i2c_read_reg(ts, ts->tdata->interrupt_msg_cmd, &intr_message); if (error) { dev_err(&ts->client->dev, "failed to read the interrupt message: %d\n", error); goto out; } - finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >> - IST3038C_FINGER_COUNT_SHIFT; + finger_count = FIELD_GET(IST3038C_FINGER_COUNT_MASK, intr_message); if (finger_count > IST3038C_MAX_FINGER_NUM) { dev_err(&ts->client->dev, "finger count %d is more than maximum supported\n", @@ -101,12 +116,16 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) goto out; } - finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK; + finger_pressed = FIELD_GET(IST3038C_FINGER_STATUS_MASK, intr_message); for (i = 0; i < finger_count; i++) { - error = imagis_i2c_read_reg(ts, - IST3038C_REG_TOUCH_COORD + (i * 4), - &finger_status); + if (ts->tdata->protocol_b) + error = imagis_i2c_read_reg(ts, + ts->tdata->touch_coord_cmd, &finger_status); + else + error = imagis_i2c_read_reg(ts, + ts->tdata->touch_coord_cmd + (i * 4), + &finger_status); if (error) { dev_err(&ts->client->dev, "failed to read coordinates for finger %d: %d\n", @@ -118,14 +137,19 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, finger_pressed & BIT(i)); touchscreen_report_pos(ts->input_dev, &ts->prop, - (finger_status & IST3038C_X_MASK) >> - IST3038C_X_SHIFT, - finger_status & IST3038C_Y_MASK, 1); + FIELD_GET(IST3038C_X_MASK, finger_status), + FIELD_GET(IST3038C_Y_MASK, finger_status), + true); input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, - (finger_status & IST3038C_AREA_MASK) >> - IST3038C_AREA_SHIFT); + FIELD_GET(IST3038C_AREA_MASK, finger_status)); } + key_pressed = FIELD_GET(IST3032C_KEY_STATUS_MASK, intr_message); + + for (int i = 0; i < ts->num_keycodes; i++) + input_report_key(ts->input_dev, ts->keycodes[i], + key_pressed & BIT(i)); + input_mt_sync_frame(ts->input_dev); input_sync(ts->input_dev); @@ -210,7 +234,24 @@ static int imagis_init_input_dev(struct imagis_ts *ts) input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 16, 0, 0); + if (ts->tdata->touch_keys_supported) { + ts->num_keycodes = of_property_read_variable_u32_array( + ts->client->dev.of_node, "linux,keycodes", + ts->keycodes, 0, ARRAY_SIZE(ts->keycodes)); + if (ts->num_keycodes <= 0) { + ts->keycodes[0] = KEY_APPSELECT; + ts->keycodes[1] = KEY_BACK; + ts->num_keycodes = 2; + } + + input_dev->keycodemax = ts->num_keycodes; + input_dev->keycodesize = sizeof(ts->keycodes[0]); + input_dev->keycode = ts->keycodes; + } + + for (int i = 0; i < ts->num_keycodes; i++) + input_set_capability(input_dev, EV_KEY, ts->keycodes[i]); touchscreen_parse_properties(input_dev, true, &ts->prop); if (!ts->prop.max_x || !ts->prop.max_y) { @@ -261,6 +302,12 @@ static int imagis_probe(struct i2c_client *i2c) ts->client = i2c; + ts->tdata = device_get_match_data(dev); + if (!ts->tdata) { + dev_err(dev, "missing chip data\n"); + return -EINVAL; + } + error = imagis_init_regulators(ts); if (error) { dev_err(dev, "regulator init error: %d\n", error); @@ -279,15 +326,13 @@ static int imagis_probe(struct i2c_client *i2c) return error; } - error = imagis_i2c_read_reg(ts, - IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS, - &chip_id); + error = imagis_i2c_read_reg(ts, ts->tdata->whoami_cmd, &chip_id); if (error) { dev_err(dev, "chip ID read failure: %d\n", error); return error; } - if (chip_id != IST3038C_WHOAMI) { + if (chip_id != ts->tdata->whoami_val) { dev_err(dev, "unknown chip ID: 0x%x\n", chip_id); return -EINVAL; } @@ -343,9 +388,34 @@ static int imagis_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); +static const struct imagis_properties imagis_3032c_data = { + .interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE, + .touch_coord_cmd = IST3038C_REG_TOUCH_COORD, + .whoami_cmd = IST3038C_REG_CHIPID, + .whoami_val = IST3032C_WHOAMI, + .touch_keys_supported = true, +}; + +static const struct imagis_properties imagis_3038b_data = { + .interrupt_msg_cmd = IST3038B_REG_STATUS, + .touch_coord_cmd = IST3038B_REG_STATUS, + .whoami_cmd = IST3038B_REG_CHIPID, + .whoami_val = IST3038B_WHOAMI, + .protocol_b = true, +}; + +static const struct imagis_properties imagis_3038c_data = { + .interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE, + .touch_coord_cmd = IST3038C_REG_TOUCH_COORD, + .whoami_cmd = IST3038C_REG_CHIPID, + .whoami_val = IST3038C_WHOAMI, +}; + #ifdef CONFIG_OF static const struct of_device_id imagis_of_match[] = { - { .compatible = "imagis,ist3038c", }, + { .compatible = "imagis,ist3032c", .data = &imagis_3032c_data }, + { .compatible = "imagis,ist3038b", .data = &imagis_3038b_data }, + { .compatible = "imagis,ist3038c", .data = &imagis_3038c_data }, { }, }; MODULE_DEVICE_TABLE(of, imagis_of_match); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 34324f8512ac..294b7ceded27 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -157,7 +157,6 @@ static void titsc_step_config(struct titsc *ts_dev) n++ == 0 ? STEPCONFIG_OPENDLY : 0); } - config = 0; config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_yn | STEPCONFIG_INM_ADCREFM; |