| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Atmel maXTouch Touchscreen driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Copyright (C) 2016 Zodiac Inflight Innovations |
|---|
| 8 | 9 | * |
|---|
| 9 | 10 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 12 | | - * under the terms of the GNU General Public License as published by the |
|---|
| 13 | | - * Free Software Foundation; either version 2 of the License, or (at your |
|---|
| 14 | | - * option) any later version. |
|---|
| 15 | | - * |
|---|
| 16 | 11 | */ |
|---|
| 17 | 12 | |
|---|
| 18 | 13 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 25 | 20 | #include <linux/i2c.h> |
|---|
| 26 | 21 | #include <linux/input/mt.h> |
|---|
| 27 | 22 | #include <linux/interrupt.h> |
|---|
| 23 | +#include <linux/irq.h> |
|---|
| 28 | 24 | #include <linux/of.h> |
|---|
| 29 | 25 | #include <linux/property.h> |
|---|
| 30 | 26 | #include <linux/slab.h> |
|---|
| 31 | 27 | #include <linux/gpio/consumer.h> |
|---|
| 32 | | -#include <linux/property.h> |
|---|
| 33 | 28 | #include <asm/unaligned.h> |
|---|
| 34 | 29 | #include <media/v4l2-device.h> |
|---|
| 35 | 30 | #include <media/v4l2-ioctl.h> |
|---|
| .. | .. |
|---|
| 135 | 130 | /* MXT_SPT_COMMSCONFIG_T18 */ |
|---|
| 136 | 131 | #define MXT_COMMS_CTRL 0 |
|---|
| 137 | 132 | #define MXT_COMMS_CMD 1 |
|---|
| 133 | +#define MXT_COMMS_RETRIGEN BIT(6) |
|---|
| 138 | 134 | |
|---|
| 139 | 135 | /* MXT_DEBUG_DIAGNOSTIC_T37 */ |
|---|
| 140 | 136 | #define MXT_DIAGNOSTIC_PAGEUP 0x01 |
|---|
| .. | .. |
|---|
| 262 | 258 | MXT_V4L_INPUT_MAX, |
|---|
| 263 | 259 | }; |
|---|
| 264 | 260 | |
|---|
| 265 | | -static const struct v4l2_file_operations mxt_video_fops = { |
|---|
| 266 | | - .owner = THIS_MODULE, |
|---|
| 267 | | - .open = v4l2_fh_open, |
|---|
| 268 | | - .release = vb2_fop_release, |
|---|
| 269 | | - .unlocked_ioctl = video_ioctl2, |
|---|
| 270 | | - .read = vb2_fop_read, |
|---|
| 271 | | - .mmap = vb2_fop_mmap, |
|---|
| 272 | | - .poll = vb2_fop_poll, |
|---|
| 273 | | -}; |
|---|
| 274 | | - |
|---|
| 275 | 261 | enum mxt_suspend_mode { |
|---|
| 276 | 262 | MXT_SUSPEND_DEEP_SLEEP = 0, |
|---|
| 277 | 263 | MXT_SUSPEND_T9_CTRL = 1, |
|---|
| .. | .. |
|---|
| 324 | 310 | struct t7_config t7_cfg; |
|---|
| 325 | 311 | struct mxt_dbg dbg; |
|---|
| 326 | 312 | struct gpio_desc *reset_gpio; |
|---|
| 313 | + bool use_retrigen_workaround; |
|---|
| 327 | 314 | |
|---|
| 328 | 315 | /* Cached parameters from object table */ |
|---|
| 329 | 316 | u16 T5_address; |
|---|
| .. | .. |
|---|
| 334 | 321 | u16 T71_address; |
|---|
| 335 | 322 | u8 T9_reportid_min; |
|---|
| 336 | 323 | u8 T9_reportid_max; |
|---|
| 324 | + u16 T18_address; |
|---|
| 337 | 325 | u8 T19_reportid; |
|---|
| 338 | 326 | u16 T44_address; |
|---|
| 339 | 327 | u8 T100_reportid_min; |
|---|
| .. | .. |
|---|
| 489 | 477 | bootloader = appmode - 0x24; |
|---|
| 490 | 478 | break; |
|---|
| 491 | 479 | } |
|---|
| 492 | | - /* Fall through for normal case */ |
|---|
| 480 | + fallthrough; /* for normal case */ |
|---|
| 493 | 481 | case 0x4c: |
|---|
| 494 | 482 | case 0x4d: |
|---|
| 495 | 483 | case 0x5a: |
|---|
| .. | .. |
|---|
| 838 | 826 | * have happened. |
|---|
| 839 | 827 | */ |
|---|
| 840 | 828 | if (status & MXT_T9_RELEASE) { |
|---|
| 841 | | - input_mt_report_slot_state(input_dev, |
|---|
| 842 | | - MT_TOOL_FINGER, 0); |
|---|
| 829 | + input_mt_report_slot_inactive(input_dev); |
|---|
| 843 | 830 | mxt_input_sync(data); |
|---|
| 844 | 831 | } |
|---|
| 845 | 832 | |
|---|
| .. | .. |
|---|
| 855 | 842 | input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); |
|---|
| 856 | 843 | } else { |
|---|
| 857 | 844 | /* Touch no longer active, close out slot */ |
|---|
| 858 | | - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); |
|---|
| 845 | + input_mt_report_slot_inactive(input_dev); |
|---|
| 859 | 846 | } |
|---|
| 860 | 847 | |
|---|
| 861 | 848 | data->update_input = true; |
|---|
| .. | .. |
|---|
| 963 | 950 | dev_dbg(dev, "[%u] release\n", id); |
|---|
| 964 | 951 | |
|---|
| 965 | 952 | /* close out slot */ |
|---|
| 966 | | - input_mt_report_slot_state(input_dev, 0, 0); |
|---|
| 953 | + input_mt_report_slot_inactive(input_dev); |
|---|
| 967 | 954 | } |
|---|
| 968 | 955 | |
|---|
| 969 | 956 | data->update_input = true; |
|---|
| .. | .. |
|---|
| 1207 | 1194 | |
|---|
| 1208 | 1195 | enable_irq(data->irq); |
|---|
| 1209 | 1196 | |
|---|
| 1210 | | - error = mxt_process_messages_until_invalid(data); |
|---|
| 1211 | | - if (error) |
|---|
| 1212 | | - return error; |
|---|
| 1197 | + if (data->use_retrigen_workaround) { |
|---|
| 1198 | + error = mxt_process_messages_until_invalid(data); |
|---|
| 1199 | + if (error) |
|---|
| 1200 | + return error; |
|---|
| 1201 | + } |
|---|
| 1213 | 1202 | |
|---|
| 1214 | 1203 | return 0; |
|---|
| 1215 | 1204 | } |
|---|
| .. | .. |
|---|
| 1297 | 1286 | crc &= 0x00FFFFFF; |
|---|
| 1298 | 1287 | |
|---|
| 1299 | 1288 | return crc; |
|---|
| 1289 | +} |
|---|
| 1290 | + |
|---|
| 1291 | +static int mxt_check_retrigen(struct mxt_data *data) |
|---|
| 1292 | +{ |
|---|
| 1293 | + struct i2c_client *client = data->client; |
|---|
| 1294 | + int error; |
|---|
| 1295 | + int val; |
|---|
| 1296 | + struct irq_data *irqd; |
|---|
| 1297 | + |
|---|
| 1298 | + data->use_retrigen_workaround = false; |
|---|
| 1299 | + |
|---|
| 1300 | + irqd = irq_get_irq_data(data->irq); |
|---|
| 1301 | + if (!irqd) |
|---|
| 1302 | + return -EINVAL; |
|---|
| 1303 | + |
|---|
| 1304 | + if (irqd_is_level_type(irqd)) |
|---|
| 1305 | + return 0; |
|---|
| 1306 | + |
|---|
| 1307 | + if (data->T18_address) { |
|---|
| 1308 | + error = __mxt_read_reg(client, |
|---|
| 1309 | + data->T18_address + MXT_COMMS_CTRL, |
|---|
| 1310 | + 1, &val); |
|---|
| 1311 | + if (error) |
|---|
| 1312 | + return error; |
|---|
| 1313 | + |
|---|
| 1314 | + if (val & MXT_COMMS_RETRIGEN) |
|---|
| 1315 | + return 0; |
|---|
| 1316 | + } |
|---|
| 1317 | + |
|---|
| 1318 | + dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); |
|---|
| 1319 | + data->use_retrigen_workaround = true; |
|---|
| 1320 | + return 0; |
|---|
| 1300 | 1321 | } |
|---|
| 1301 | 1322 | |
|---|
| 1302 | 1323 | static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) |
|---|
| .. | .. |
|---|
| 1527 | 1548 | } else if (config_crc == data->config_crc) { |
|---|
| 1528 | 1549 | dev_dbg(dev, "Config CRC 0x%06X: OK\n", |
|---|
| 1529 | 1550 | data->config_crc); |
|---|
| 1530 | | - return 0; |
|---|
| 1551 | + ret = 0; |
|---|
| 1552 | + goto release_raw; |
|---|
| 1531 | 1553 | } else { |
|---|
| 1532 | 1554 | dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", |
|---|
| 1533 | 1555 | data->config_crc, config_crc); |
|---|
| .. | .. |
|---|
| 1577 | 1599 | |
|---|
| 1578 | 1600 | mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); |
|---|
| 1579 | 1601 | |
|---|
| 1602 | + ret = mxt_check_retrigen(data); |
|---|
| 1603 | + if (ret) |
|---|
| 1604 | + goto release_mem; |
|---|
| 1605 | + |
|---|
| 1580 | 1606 | ret = mxt_soft_reset(data); |
|---|
| 1581 | 1607 | if (ret) |
|---|
| 1582 | 1608 | goto release_mem; |
|---|
| .. | .. |
|---|
| 1620 | 1646 | data->T71_address = 0; |
|---|
| 1621 | 1647 | data->T9_reportid_min = 0; |
|---|
| 1622 | 1648 | data->T9_reportid_max = 0; |
|---|
| 1649 | + data->T18_address = 0; |
|---|
| 1623 | 1650 | data->T19_reportid = 0; |
|---|
| 1624 | 1651 | data->T44_address = 0; |
|---|
| 1625 | 1652 | data->T100_reportid_min = 0; |
|---|
| .. | .. |
|---|
| 1693 | 1720 | data->T9_reportid_max = min_id + |
|---|
| 1694 | 1721 | object->num_report_ids - 1; |
|---|
| 1695 | 1722 | data->num_touchids = object->num_report_ids; |
|---|
| 1723 | + break; |
|---|
| 1724 | + case MXT_SPT_COMMSCONFIG_T18: |
|---|
| 1725 | + data->T18_address = object->start_address; |
|---|
| 1696 | 1726 | break; |
|---|
| 1697 | 1727 | case MXT_SPT_MESSAGECOUNT_T44: |
|---|
| 1698 | 1728 | data->T44_address = object->start_address; |
|---|
| .. | .. |
|---|
| 2153 | 2183 | msleep(MXT_FW_RESET_TIME); |
|---|
| 2154 | 2184 | } |
|---|
| 2155 | 2185 | |
|---|
| 2186 | + error = mxt_check_retrigen(data); |
|---|
| 2187 | + if (error) |
|---|
| 2188 | + return error; |
|---|
| 2189 | + |
|---|
| 2156 | 2190 | error = mxt_acquire_irq(data); |
|---|
| 2157 | 2191 | if (error) |
|---|
| 2158 | 2192 | return error; |
|---|
| .. | .. |
|---|
| 2224 | 2258 | } |
|---|
| 2225 | 2259 | |
|---|
| 2226 | 2260 | #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 |
|---|
| 2261 | +static const struct v4l2_file_operations mxt_video_fops = { |
|---|
| 2262 | + .owner = THIS_MODULE, |
|---|
| 2263 | + .open = v4l2_fh_open, |
|---|
| 2264 | + .release = vb2_fop_release, |
|---|
| 2265 | + .unlocked_ioctl = video_ioctl2, |
|---|
| 2266 | + .read = vb2_fop_read, |
|---|
| 2267 | + .mmap = vb2_fop_mmap, |
|---|
| 2268 | + .poll = vb2_fop_poll, |
|---|
| 2269 | +}; |
|---|
| 2270 | + |
|---|
| 2227 | 2271 | static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, |
|---|
| 2228 | 2272 | unsigned int y) |
|---|
| 2229 | 2273 | { |
|---|
| .. | .. |
|---|
| 2995 | 3039 | int error; |
|---|
| 2996 | 3040 | |
|---|
| 2997 | 3041 | if (device_property_present(dev, keymap_property)) { |
|---|
| 2998 | | - n_keys = device_property_read_u32_array(dev, keymap_property, |
|---|
| 2999 | | - NULL, 0); |
|---|
| 3042 | + n_keys = device_property_count_u32(dev, keymap_property); |
|---|
| 3000 | 3043 | if (n_keys <= 0) { |
|---|
| 3001 | 3044 | error = n_keys < 0 ? n_keys : -EINVAL; |
|---|
| 3002 | 3045 | dev_err(dev, "invalid/malformed '%s' property: %d\n", |
|---|
| .. | .. |
|---|
| 3091 | 3134 | if (error) |
|---|
| 3092 | 3135 | return error; |
|---|
| 3093 | 3136 | |
|---|
| 3137 | + /* Request the RESET line as asserted so we go into reset */ |
|---|
| 3094 | 3138 | data->reset_gpio = devm_gpiod_get_optional(&client->dev, |
|---|
| 3095 | | - "reset", GPIOD_OUT_LOW); |
|---|
| 3139 | + "reset", GPIOD_OUT_HIGH); |
|---|
| 3096 | 3140 | if (IS_ERR(data->reset_gpio)) { |
|---|
| 3097 | 3141 | error = PTR_ERR(data->reset_gpio); |
|---|
| 3098 | 3142 | dev_err(&client->dev, "Failed to get reset gpio: %d\n", error); |
|---|
| .. | .. |
|---|
| 3110 | 3154 | disable_irq(client->irq); |
|---|
| 3111 | 3155 | |
|---|
| 3112 | 3156 | if (data->reset_gpio) { |
|---|
| 3157 | + /* Wait a while and then de-assert the RESET GPIO line */ |
|---|
| 3113 | 3158 | msleep(MXT_RESET_GPIO_TIME); |
|---|
| 3114 | | - gpiod_set_value(data->reset_gpio, 1); |
|---|
| 3159 | + gpiod_set_value(data->reset_gpio, 0); |
|---|
| 3115 | 3160 | msleep(MXT_RESET_INVALID_CHG); |
|---|
| 3116 | 3161 | } |
|---|
| 3117 | 3162 | |
|---|