162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Atmel maXTouch Touchscreen driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics Co.Ltd 662306a36Sopenharmony_ci * Copyright (C) 2011-2014 Atmel Corporation 762306a36Sopenharmony_ci * Copyright (C) 2012 Google, Inc. 862306a36Sopenharmony_ci * Copyright (C) 2016 Zodiac Inflight Innovations 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/acpi.h> 1462306a36Sopenharmony_ci#include <linux/dmi.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/completion.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/firmware.h> 2062306a36Sopenharmony_ci#include <linux/i2c.h> 2162306a36Sopenharmony_ci#include <linux/input/mt.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/irq.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/property.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2862306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2962306a36Sopenharmony_ci#include <asm/unaligned.h> 3062306a36Sopenharmony_ci#include <media/v4l2-device.h> 3162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3262306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3362306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 3462306a36Sopenharmony_ci#include <dt-bindings/input/atmel-maxtouch.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Firmware files */ 3762306a36Sopenharmony_ci#define MXT_FW_NAME "maxtouch.fw" 3862306a36Sopenharmony_ci#define MXT_CFG_NAME "maxtouch.cfg" 3962306a36Sopenharmony_ci#define MXT_CFG_MAGIC "OBP_RAW V1" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Registers */ 4262306a36Sopenharmony_ci#define MXT_OBJECT_START 0x07 4362306a36Sopenharmony_ci#define MXT_OBJECT_SIZE 6 4462306a36Sopenharmony_ci#define MXT_INFO_CHECKSUM_SIZE 3 4562306a36Sopenharmony_ci#define MXT_MAX_BLOCK_WRITE 256 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Object types */ 4862306a36Sopenharmony_ci#define MXT_DEBUG_DIAGNOSTIC_T37 37 4962306a36Sopenharmony_ci#define MXT_GEN_MESSAGE_T5 5 5062306a36Sopenharmony_ci#define MXT_GEN_COMMAND_T6 6 5162306a36Sopenharmony_ci#define MXT_GEN_POWER_T7 7 5262306a36Sopenharmony_ci#define MXT_GEN_ACQUIRE_T8 8 5362306a36Sopenharmony_ci#define MXT_GEN_DATASOURCE_T53 53 5462306a36Sopenharmony_ci#define MXT_TOUCH_MULTI_T9 9 5562306a36Sopenharmony_ci#define MXT_TOUCH_KEYARRAY_T15 15 5662306a36Sopenharmony_ci#define MXT_TOUCH_PROXIMITY_T23 23 5762306a36Sopenharmony_ci#define MXT_TOUCH_PROXKEY_T52 52 5862306a36Sopenharmony_ci#define MXT_TOUCH_PTC_KEYS_T97 97 5962306a36Sopenharmony_ci#define MXT_PROCI_GRIPFACE_T20 20 6062306a36Sopenharmony_ci#define MXT_PROCG_NOISE_T22 22 6162306a36Sopenharmony_ci#define MXT_PROCI_ONETOUCH_T24 24 6262306a36Sopenharmony_ci#define MXT_PROCI_TWOTOUCH_T27 27 6362306a36Sopenharmony_ci#define MXT_PROCI_GRIP_T40 40 6462306a36Sopenharmony_ci#define MXT_PROCI_PALM_T41 41 6562306a36Sopenharmony_ci#define MXT_PROCI_TOUCHSUPPRESSION_T42 42 6662306a36Sopenharmony_ci#define MXT_PROCI_STYLUS_T47 47 6762306a36Sopenharmony_ci#define MXT_PROCG_NOISESUPPRESSION_T48 48 6862306a36Sopenharmony_ci#define MXT_SPT_COMMSCONFIG_T18 18 6962306a36Sopenharmony_ci#define MXT_SPT_GPIOPWM_T19 19 7062306a36Sopenharmony_ci#define MXT_SPT_SELFTEST_T25 25 7162306a36Sopenharmony_ci#define MXT_SPT_CTECONFIG_T28 28 7262306a36Sopenharmony_ci#define MXT_SPT_USERDATA_T38 38 7362306a36Sopenharmony_ci#define MXT_SPT_DIGITIZER_T43 43 7462306a36Sopenharmony_ci#define MXT_SPT_MESSAGECOUNT_T44 44 7562306a36Sopenharmony_ci#define MXT_SPT_CTECONFIG_T46 46 7662306a36Sopenharmony_ci#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 7762306a36Sopenharmony_ci#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* MXT_GEN_MESSAGE_T5 object */ 8062306a36Sopenharmony_ci#define MXT_RPTID_NOMSG 0xff 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* MXT_GEN_COMMAND_T6 field */ 8362306a36Sopenharmony_ci#define MXT_COMMAND_RESET 0 8462306a36Sopenharmony_ci#define MXT_COMMAND_BACKUPNV 1 8562306a36Sopenharmony_ci#define MXT_COMMAND_CALIBRATE 2 8662306a36Sopenharmony_ci#define MXT_COMMAND_REPORTALL 3 8762306a36Sopenharmony_ci#define MXT_COMMAND_DIAGNOSTIC 5 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* Define for T6 status byte */ 9062306a36Sopenharmony_ci#define MXT_T6_STATUS_RESET BIT(7) 9162306a36Sopenharmony_ci#define MXT_T6_STATUS_OFL BIT(6) 9262306a36Sopenharmony_ci#define MXT_T6_STATUS_SIGERR BIT(5) 9362306a36Sopenharmony_ci#define MXT_T6_STATUS_CAL BIT(4) 9462306a36Sopenharmony_ci#define MXT_T6_STATUS_CFGERR BIT(3) 9562306a36Sopenharmony_ci#define MXT_T6_STATUS_COMSERR BIT(2) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* MXT_GEN_POWER_T7 field */ 9862306a36Sopenharmony_cistruct t7_config { 9962306a36Sopenharmony_ci u8 idle; 10062306a36Sopenharmony_ci u8 active; 10162306a36Sopenharmony_ci} __packed; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define MXT_POWER_CFG_RUN 0 10462306a36Sopenharmony_ci#define MXT_POWER_CFG_DEEPSLEEP 1 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 field */ 10762306a36Sopenharmony_ci#define MXT_T9_CTRL 0 10862306a36Sopenharmony_ci#define MXT_T9_XSIZE 3 10962306a36Sopenharmony_ci#define MXT_T9_YSIZE 4 11062306a36Sopenharmony_ci#define MXT_T9_ORIENT 9 11162306a36Sopenharmony_ci#define MXT_T9_RANGE 18 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 status */ 11462306a36Sopenharmony_ci#define MXT_T9_UNGRIP BIT(0) 11562306a36Sopenharmony_ci#define MXT_T9_SUPPRESS BIT(1) 11662306a36Sopenharmony_ci#define MXT_T9_AMP BIT(2) 11762306a36Sopenharmony_ci#define MXT_T9_VECTOR BIT(3) 11862306a36Sopenharmony_ci#define MXT_T9_MOVE BIT(4) 11962306a36Sopenharmony_ci#define MXT_T9_RELEASE BIT(5) 12062306a36Sopenharmony_ci#define MXT_T9_PRESS BIT(6) 12162306a36Sopenharmony_ci#define MXT_T9_DETECT BIT(7) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct t9_range { 12462306a36Sopenharmony_ci __le16 x; 12562306a36Sopenharmony_ci __le16 y; 12662306a36Sopenharmony_ci} __packed; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* MXT_TOUCH_MULTI_T9 orient */ 12962306a36Sopenharmony_ci#define MXT_T9_ORIENT_SWITCH BIT(0) 13062306a36Sopenharmony_ci#define MXT_T9_ORIENT_INVERTX BIT(1) 13162306a36Sopenharmony_ci#define MXT_T9_ORIENT_INVERTY BIT(2) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* MXT_SPT_COMMSCONFIG_T18 */ 13462306a36Sopenharmony_ci#define MXT_COMMS_CTRL 0 13562306a36Sopenharmony_ci#define MXT_COMMS_CMD 1 13662306a36Sopenharmony_ci#define MXT_COMMS_RETRIGEN BIT(6) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* MXT_DEBUG_DIAGNOSTIC_T37 */ 13962306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_PAGEUP 0x01 14062306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_DELTAS 0x10 14162306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_REFS 0x11 14262306a36Sopenharmony_ci#define MXT_DIAGNOSTIC_SIZE 128 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define MXT_FAMILY_1386 160 14562306a36Sopenharmony_ci#define MXT1386_COLUMNS 3 14662306a36Sopenharmony_ci#define MXT1386_PAGES_PER_COLUMN 8 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct t37_debug { 14962306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 15062306a36Sopenharmony_ci u8 mode; 15162306a36Sopenharmony_ci u8 page; 15262306a36Sopenharmony_ci u8 data[MXT_DIAGNOSTIC_SIZE]; 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Define for MXT_GEN_COMMAND_T6 */ 15762306a36Sopenharmony_ci#define MXT_BOOT_VALUE 0xa5 15862306a36Sopenharmony_ci#define MXT_RESET_VALUE 0x01 15962306a36Sopenharmony_ci#define MXT_BACKUP_VALUE 0x55 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* T100 Multiple Touch Touchscreen */ 16262306a36Sopenharmony_ci#define MXT_T100_CTRL 0 16362306a36Sopenharmony_ci#define MXT_T100_CFG1 1 16462306a36Sopenharmony_ci#define MXT_T100_TCHAUX 3 16562306a36Sopenharmony_ci#define MXT_T100_XSIZE 9 16662306a36Sopenharmony_ci#define MXT_T100_XRANGE 13 16762306a36Sopenharmony_ci#define MXT_T100_YSIZE 20 16862306a36Sopenharmony_ci#define MXT_T100_YRANGE 24 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define MXT_T100_CFG_SWITCHXY BIT(5) 17162306a36Sopenharmony_ci#define MXT_T100_CFG_INVERTY BIT(6) 17262306a36Sopenharmony_ci#define MXT_T100_CFG_INVERTX BIT(7) 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define MXT_T100_TCHAUX_VECT BIT(0) 17562306a36Sopenharmony_ci#define MXT_T100_TCHAUX_AMPL BIT(1) 17662306a36Sopenharmony_ci#define MXT_T100_TCHAUX_AREA BIT(2) 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#define MXT_T100_DETECT BIT(7) 17962306a36Sopenharmony_ci#define MXT_T100_TYPE_MASK 0x70 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cienum t100_type { 18262306a36Sopenharmony_ci MXT_T100_TYPE_FINGER = 1, 18362306a36Sopenharmony_ci MXT_T100_TYPE_PASSIVE_STYLUS = 2, 18462306a36Sopenharmony_ci MXT_T100_TYPE_HOVERING_FINGER = 4, 18562306a36Sopenharmony_ci MXT_T100_TYPE_GLOVE = 5, 18662306a36Sopenharmony_ci MXT_T100_TYPE_LARGE_TOUCH = 6, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#define MXT_DISTANCE_ACTIVE_TOUCH 0 19062306a36Sopenharmony_ci#define MXT_DISTANCE_HOVERING 1 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci#define MXT_TOUCH_MAJOR_DEFAULT 1 19362306a36Sopenharmony_ci#define MXT_PRESSURE_DEFAULT 1 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* Delay times */ 19662306a36Sopenharmony_ci#define MXT_BACKUP_TIME 50 /* msec */ 19762306a36Sopenharmony_ci#define MXT_RESET_GPIO_TIME 20 /* msec */ 19862306a36Sopenharmony_ci#define MXT_RESET_INVALID_CHG 100 /* msec */ 19962306a36Sopenharmony_ci#define MXT_RESET_TIME 200 /* msec */ 20062306a36Sopenharmony_ci#define MXT_RESET_TIMEOUT 3000 /* msec */ 20162306a36Sopenharmony_ci#define MXT_CRC_TIMEOUT 1000 /* msec */ 20262306a36Sopenharmony_ci#define MXT_FW_RESET_TIME 3000 /* msec */ 20362306a36Sopenharmony_ci#define MXT_FW_CHG_TIMEOUT 300 /* msec */ 20462306a36Sopenharmony_ci#define MXT_WAKEUP_TIME 25 /* msec */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* Command to unlock bootloader */ 20762306a36Sopenharmony_ci#define MXT_UNLOCK_CMD_MSB 0xaa 20862306a36Sopenharmony_ci#define MXT_UNLOCK_CMD_LSB 0xdc 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* Bootloader mode status */ 21162306a36Sopenharmony_ci#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ 21262306a36Sopenharmony_ci#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ 21362306a36Sopenharmony_ci#define MXT_FRAME_CRC_CHECK 0x02 21462306a36Sopenharmony_ci#define MXT_FRAME_CRC_FAIL 0x03 21562306a36Sopenharmony_ci#define MXT_FRAME_CRC_PASS 0x04 21662306a36Sopenharmony_ci#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ 21762306a36Sopenharmony_ci#define MXT_BOOT_STATUS_MASK 0x3f 21862306a36Sopenharmony_ci#define MXT_BOOT_EXTENDED_ID BIT(5) 21962306a36Sopenharmony_ci#define MXT_BOOT_ID_MASK 0x1f 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* Touchscreen absolute values */ 22262306a36Sopenharmony_ci#define MXT_MAX_AREA 0xff 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#define MXT_PIXELS_PER_MM 20 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistruct mxt_info { 22762306a36Sopenharmony_ci u8 family_id; 22862306a36Sopenharmony_ci u8 variant_id; 22962306a36Sopenharmony_ci u8 version; 23062306a36Sopenharmony_ci u8 build; 23162306a36Sopenharmony_ci u8 matrix_xsize; 23262306a36Sopenharmony_ci u8 matrix_ysize; 23362306a36Sopenharmony_ci u8 object_num; 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistruct mxt_object { 23762306a36Sopenharmony_ci u8 type; 23862306a36Sopenharmony_ci u16 start_address; 23962306a36Sopenharmony_ci u8 size_minus_one; 24062306a36Sopenharmony_ci u8 instances_minus_one; 24162306a36Sopenharmony_ci u8 num_report_ids; 24262306a36Sopenharmony_ci} __packed; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistruct mxt_dbg { 24562306a36Sopenharmony_ci u16 t37_address; 24662306a36Sopenharmony_ci u16 diag_cmd_address; 24762306a36Sopenharmony_ci struct t37_debug *t37_buf; 24862306a36Sopenharmony_ci unsigned int t37_pages; 24962306a36Sopenharmony_ci unsigned int t37_nodes; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci struct v4l2_device v4l2; 25262306a36Sopenharmony_ci struct v4l2_pix_format format; 25362306a36Sopenharmony_ci struct video_device vdev; 25462306a36Sopenharmony_ci struct vb2_queue queue; 25562306a36Sopenharmony_ci struct mutex lock; 25662306a36Sopenharmony_ci int input; 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cienum v4l_dbg_inputs { 26062306a36Sopenharmony_ci MXT_V4L_INPUT_DELTAS, 26162306a36Sopenharmony_ci MXT_V4L_INPUT_REFS, 26262306a36Sopenharmony_ci MXT_V4L_INPUT_MAX, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cienum mxt_suspend_mode { 26662306a36Sopenharmony_ci MXT_SUSPEND_DEEP_SLEEP = 0, 26762306a36Sopenharmony_ci MXT_SUSPEND_T9_CTRL = 1, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Config update context */ 27162306a36Sopenharmony_cistruct mxt_cfg { 27262306a36Sopenharmony_ci u8 *raw; 27362306a36Sopenharmony_ci size_t raw_size; 27462306a36Sopenharmony_ci off_t raw_pos; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci u8 *mem; 27762306a36Sopenharmony_ci size_t mem_size; 27862306a36Sopenharmony_ci int start_ofs; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci struct mxt_info info; 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* Each client has this additional data */ 28462306a36Sopenharmony_cistruct mxt_data { 28562306a36Sopenharmony_ci struct i2c_client *client; 28662306a36Sopenharmony_ci struct input_dev *input_dev; 28762306a36Sopenharmony_ci char phys[64]; /* device physical location */ 28862306a36Sopenharmony_ci struct mxt_object *object_table; 28962306a36Sopenharmony_ci struct mxt_info *info; 29062306a36Sopenharmony_ci void *raw_info_block; 29162306a36Sopenharmony_ci unsigned int irq; 29262306a36Sopenharmony_ci unsigned int max_x; 29362306a36Sopenharmony_ci unsigned int max_y; 29462306a36Sopenharmony_ci bool invertx; 29562306a36Sopenharmony_ci bool inverty; 29662306a36Sopenharmony_ci bool xy_switch; 29762306a36Sopenharmony_ci u8 xsize; 29862306a36Sopenharmony_ci u8 ysize; 29962306a36Sopenharmony_ci bool in_bootloader; 30062306a36Sopenharmony_ci u16 mem_size; 30162306a36Sopenharmony_ci u8 t100_aux_ampl; 30262306a36Sopenharmony_ci u8 t100_aux_area; 30362306a36Sopenharmony_ci u8 t100_aux_vect; 30462306a36Sopenharmony_ci u8 max_reportid; 30562306a36Sopenharmony_ci u32 config_crc; 30662306a36Sopenharmony_ci u32 info_crc; 30762306a36Sopenharmony_ci u8 bootloader_addr; 30862306a36Sopenharmony_ci u8 *msg_buf; 30962306a36Sopenharmony_ci u8 t6_status; 31062306a36Sopenharmony_ci bool update_input; 31162306a36Sopenharmony_ci u8 last_message_count; 31262306a36Sopenharmony_ci u8 num_touchids; 31362306a36Sopenharmony_ci u8 multitouch; 31462306a36Sopenharmony_ci struct t7_config t7_cfg; 31562306a36Sopenharmony_ci struct mxt_dbg dbg; 31662306a36Sopenharmony_ci struct regulator_bulk_data regulators[2]; 31762306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 31862306a36Sopenharmony_ci struct gpio_desc *wake_gpio; 31962306a36Sopenharmony_ci bool use_retrigen_workaround; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Cached parameters from object table */ 32262306a36Sopenharmony_ci u16 T5_address; 32362306a36Sopenharmony_ci u8 T5_msg_size; 32462306a36Sopenharmony_ci u8 T6_reportid; 32562306a36Sopenharmony_ci u16 T6_address; 32662306a36Sopenharmony_ci u16 T7_address; 32762306a36Sopenharmony_ci u16 T71_address; 32862306a36Sopenharmony_ci u8 T9_reportid_min; 32962306a36Sopenharmony_ci u8 T9_reportid_max; 33062306a36Sopenharmony_ci u8 T15_reportid_min; 33162306a36Sopenharmony_ci u8 T15_reportid_max; 33262306a36Sopenharmony_ci u16 T18_address; 33362306a36Sopenharmony_ci u8 T19_reportid; 33462306a36Sopenharmony_ci u16 T44_address; 33562306a36Sopenharmony_ci u8 T97_reportid_min; 33662306a36Sopenharmony_ci u8 T97_reportid_max; 33762306a36Sopenharmony_ci u8 T100_reportid_min; 33862306a36Sopenharmony_ci u8 T100_reportid_max; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* for fw update in bootloader */ 34162306a36Sopenharmony_ci struct completion bl_completion; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* for reset handling */ 34462306a36Sopenharmony_ci struct completion reset_completion; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* for config update handling */ 34762306a36Sopenharmony_ci struct completion crc_completion; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci u32 *t19_keymap; 35062306a36Sopenharmony_ci unsigned int t19_num_keys; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci u32 *t15_keymap; 35362306a36Sopenharmony_ci unsigned int t15_num_keys; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci enum mxt_suspend_mode suspend_mode; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci u32 wakeup_method; 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistruct mxt_vb2_buffer { 36162306a36Sopenharmony_ci struct vb2_buffer vb; 36262306a36Sopenharmony_ci struct list_head list; 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic size_t mxt_obj_size(const struct mxt_object *obj) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci return obj->size_minus_one + 1; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic size_t mxt_obj_instances(const struct mxt_object *obj) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci return obj->instances_minus_one + 1; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic bool mxt_object_readable(unsigned int type) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci switch (type) { 37862306a36Sopenharmony_ci case MXT_GEN_COMMAND_T6: 37962306a36Sopenharmony_ci case MXT_GEN_POWER_T7: 38062306a36Sopenharmony_ci case MXT_GEN_ACQUIRE_T8: 38162306a36Sopenharmony_ci case MXT_GEN_DATASOURCE_T53: 38262306a36Sopenharmony_ci case MXT_TOUCH_MULTI_T9: 38362306a36Sopenharmony_ci case MXT_TOUCH_KEYARRAY_T15: 38462306a36Sopenharmony_ci case MXT_TOUCH_PROXIMITY_T23: 38562306a36Sopenharmony_ci case MXT_TOUCH_PROXKEY_T52: 38662306a36Sopenharmony_ci case MXT_TOUCH_PTC_KEYS_T97: 38762306a36Sopenharmony_ci case MXT_TOUCH_MULTITOUCHSCREEN_T100: 38862306a36Sopenharmony_ci case MXT_PROCI_GRIPFACE_T20: 38962306a36Sopenharmony_ci case MXT_PROCG_NOISE_T22: 39062306a36Sopenharmony_ci case MXT_PROCI_ONETOUCH_T24: 39162306a36Sopenharmony_ci case MXT_PROCI_TWOTOUCH_T27: 39262306a36Sopenharmony_ci case MXT_PROCI_GRIP_T40: 39362306a36Sopenharmony_ci case MXT_PROCI_PALM_T41: 39462306a36Sopenharmony_ci case MXT_PROCI_TOUCHSUPPRESSION_T42: 39562306a36Sopenharmony_ci case MXT_PROCI_STYLUS_T47: 39662306a36Sopenharmony_ci case MXT_PROCG_NOISESUPPRESSION_T48: 39762306a36Sopenharmony_ci case MXT_SPT_COMMSCONFIG_T18: 39862306a36Sopenharmony_ci case MXT_SPT_GPIOPWM_T19: 39962306a36Sopenharmony_ci case MXT_SPT_SELFTEST_T25: 40062306a36Sopenharmony_ci case MXT_SPT_CTECONFIG_T28: 40162306a36Sopenharmony_ci case MXT_SPT_USERDATA_T38: 40262306a36Sopenharmony_ci case MXT_SPT_DIGITIZER_T43: 40362306a36Sopenharmony_ci case MXT_SPT_CTECONFIG_T46: 40462306a36Sopenharmony_ci case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: 40562306a36Sopenharmony_ci return true; 40662306a36Sopenharmony_ci default: 40762306a36Sopenharmony_ci return false; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void mxt_dump_message(struct mxt_data *data, u8 *message) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci dev_dbg(&data->client->dev, "message: %*ph\n", 41462306a36Sopenharmony_ci data->T5_msg_size, message); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int mxt_wait_for_completion(struct mxt_data *data, 41862306a36Sopenharmony_ci struct completion *comp, 41962306a36Sopenharmony_ci unsigned int timeout_ms) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct device *dev = &data->client->dev; 42262306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(timeout_ms); 42362306a36Sopenharmony_ci long ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = wait_for_completion_interruptible_timeout(comp, timeout); 42662306a36Sopenharmony_ci if (ret < 0) { 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci } else if (ret == 0) { 42962306a36Sopenharmony_ci dev_err(dev, "Wait for completion timed out.\n"); 43062306a36Sopenharmony_ci return -ETIMEDOUT; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int mxt_bootloader_read(struct mxt_data *data, 43662306a36Sopenharmony_ci u8 *val, unsigned int count) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int ret; 43962306a36Sopenharmony_ci struct i2c_msg msg; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci msg.addr = data->bootloader_addr; 44262306a36Sopenharmony_ci msg.flags = data->client->flags & I2C_M_TEN; 44362306a36Sopenharmony_ci msg.flags |= I2C_M_RD; 44462306a36Sopenharmony_ci msg.len = count; 44562306a36Sopenharmony_ci msg.buf = val; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ret = i2c_transfer(data->client->adapter, &msg, 1); 44862306a36Sopenharmony_ci if (ret == 1) { 44962306a36Sopenharmony_ci ret = 0; 45062306a36Sopenharmony_ci } else { 45162306a36Sopenharmony_ci ret = ret < 0 ? ret : -EIO; 45262306a36Sopenharmony_ci dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", 45362306a36Sopenharmony_ci __func__, ret); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int mxt_bootloader_write(struct mxt_data *data, 46062306a36Sopenharmony_ci const u8 * const val, unsigned int count) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int ret; 46362306a36Sopenharmony_ci struct i2c_msg msg; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci msg.addr = data->bootloader_addr; 46662306a36Sopenharmony_ci msg.flags = data->client->flags & I2C_M_TEN; 46762306a36Sopenharmony_ci msg.len = count; 46862306a36Sopenharmony_ci msg.buf = (u8 *)val; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci ret = i2c_transfer(data->client->adapter, &msg, 1); 47162306a36Sopenharmony_ci if (ret == 1) { 47262306a36Sopenharmony_ci ret = 0; 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci ret = ret < 0 ? ret : -EIO; 47562306a36Sopenharmony_ci dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", 47662306a36Sopenharmony_ci __func__, ret); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci u8 appmode = data->client->addr; 48562306a36Sopenharmony_ci u8 bootloader; 48662306a36Sopenharmony_ci u8 family_id = data->info ? data->info->family_id : 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci switch (appmode) { 48962306a36Sopenharmony_ci case 0x4a: 49062306a36Sopenharmony_ci case 0x4b: 49162306a36Sopenharmony_ci /* Chips after 1664S use different scheme */ 49262306a36Sopenharmony_ci if (retry || family_id >= 0xa2) { 49362306a36Sopenharmony_ci bootloader = appmode - 0x24; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci fallthrough; /* for normal case */ 49762306a36Sopenharmony_ci case 0x4c: 49862306a36Sopenharmony_ci case 0x4d: 49962306a36Sopenharmony_ci case 0x5a: 50062306a36Sopenharmony_ci case 0x5b: 50162306a36Sopenharmony_ci bootloader = appmode - 0x26; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci default: 50562306a36Sopenharmony_ci dev_err(&data->client->dev, 50662306a36Sopenharmony_ci "Appmode i2c address 0x%02x not found\n", 50762306a36Sopenharmony_ci appmode); 50862306a36Sopenharmony_ci return -EINVAL; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci data->bootloader_addr = bootloader; 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct device *dev = &data->client->dev; 51862306a36Sopenharmony_ci int error; 51962306a36Sopenharmony_ci u8 val; 52062306a36Sopenharmony_ci bool crc_failure; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci error = mxt_lookup_bootloader_address(data, alt_address); 52362306a36Sopenharmony_ci if (error) 52462306a36Sopenharmony_ci return error; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci error = mxt_bootloader_read(data, &val, 1); 52762306a36Sopenharmony_ci if (error) 52862306a36Sopenharmony_ci return error; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Check app crc fail mode */ 53162306a36Sopenharmony_ci crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci dev_err(dev, "Detected bootloader, status:%02X%s\n", 53462306a36Sopenharmony_ci val, crc_failure ? ", APP_CRC_FAIL" : ""); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct device *dev = &data->client->dev; 54262306a36Sopenharmony_ci u8 buf[3]; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (val & MXT_BOOT_EXTENDED_ID) { 54562306a36Sopenharmony_ci if (mxt_bootloader_read(data, &buf[0], 3) != 0) { 54662306a36Sopenharmony_ci dev_err(dev, "%s: i2c failure\n", __func__); 54762306a36Sopenharmony_ci return val; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return buf[0]; 55362306a36Sopenharmony_ci } else { 55462306a36Sopenharmony_ci dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return val; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int mxt_check_bootloader(struct mxt_data *data, unsigned int state, 56162306a36Sopenharmony_ci bool wait) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct device *dev = &data->client->dev; 56462306a36Sopenharmony_ci u8 val; 56562306a36Sopenharmony_ci int ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cirecheck: 56862306a36Sopenharmony_ci if (wait) { 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * In application update mode, the interrupt 57162306a36Sopenharmony_ci * line signals state transitions. We must wait for the 57262306a36Sopenharmony_ci * CHG assertion before reading the status byte. 57362306a36Sopenharmony_ci * Once the status byte has been read, the line is deasserted. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci ret = mxt_wait_for_completion(data, &data->bl_completion, 57662306a36Sopenharmony_ci MXT_FW_CHG_TIMEOUT); 57762306a36Sopenharmony_ci if (ret) { 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * TODO: handle -ERESTARTSYS better by terminating 58062306a36Sopenharmony_ci * fw update process before returning to userspace 58162306a36Sopenharmony_ci * by writing length 0x000 to device (iff we are in 58262306a36Sopenharmony_ci * WAITING_FRAME_DATA state). 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci dev_err(dev, "Update wait error %d\n", ret); 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = mxt_bootloader_read(data, &val, 1); 59062306a36Sopenharmony_ci if (ret) 59162306a36Sopenharmony_ci return ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (state == MXT_WAITING_BOOTLOAD_CMD) 59462306a36Sopenharmony_ci val = mxt_get_bootloader_version(data, val); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (state) { 59762306a36Sopenharmony_ci case MXT_WAITING_BOOTLOAD_CMD: 59862306a36Sopenharmony_ci case MXT_WAITING_FRAME_DATA: 59962306a36Sopenharmony_ci case MXT_APP_CRC_FAIL: 60062306a36Sopenharmony_ci val &= ~MXT_BOOT_STATUS_MASK; 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case MXT_FRAME_CRC_PASS: 60362306a36Sopenharmony_ci if (val == MXT_FRAME_CRC_CHECK) { 60462306a36Sopenharmony_ci goto recheck; 60562306a36Sopenharmony_ci } else if (val == MXT_FRAME_CRC_FAIL) { 60662306a36Sopenharmony_ci dev_err(dev, "Bootloader CRC fail\n"); 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci default: 61162306a36Sopenharmony_ci return -EINVAL; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (val != state) { 61562306a36Sopenharmony_ci dev_err(dev, "Invalid bootloader state %02X != %02X\n", 61662306a36Sopenharmony_ci val, state); 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci u8 buf[2]; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (unlock) { 62862306a36Sopenharmony_ci buf[0] = MXT_UNLOCK_CMD_LSB; 62962306a36Sopenharmony_ci buf[1] = MXT_UNLOCK_CMD_MSB; 63062306a36Sopenharmony_ci } else { 63162306a36Sopenharmony_ci buf[0] = 0x01; 63262306a36Sopenharmony_ci buf[1] = 0x01; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return mxt_bootloader_write(data, buf, sizeof(buf)); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic bool mxt_wakeup_toggle(struct i2c_client *client, 63962306a36Sopenharmony_ci bool wake_up, bool in_i2c) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct mxt_data *data = i2c_get_clientdata(client); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci switch (data->wakeup_method) { 64462306a36Sopenharmony_ci case ATMEL_MXT_WAKEUP_I2C_SCL: 64562306a36Sopenharmony_ci if (!in_i2c) 64662306a36Sopenharmony_ci return false; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci case ATMEL_MXT_WAKEUP_GPIO: 65062306a36Sopenharmony_ci if (in_i2c) 65162306a36Sopenharmony_ci return false; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci gpiod_set_value(data->wake_gpio, wake_up); 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci return false; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (wake_up) { 66162306a36Sopenharmony_ci dev_dbg(&client->dev, "waking up controller\n"); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci msleep(MXT_WAKEUP_TIME); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return true; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int __mxt_read_reg(struct i2c_client *client, 67062306a36Sopenharmony_ci u16 reg, u16 len, void *val) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct i2c_msg xfer[2]; 67362306a36Sopenharmony_ci bool retried = false; 67462306a36Sopenharmony_ci u8 buf[2]; 67562306a36Sopenharmony_ci int ret; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci buf[0] = reg & 0xff; 67862306a36Sopenharmony_ci buf[1] = (reg >> 8) & 0xff; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Write register */ 68162306a36Sopenharmony_ci xfer[0].addr = client->addr; 68262306a36Sopenharmony_ci xfer[0].flags = 0; 68362306a36Sopenharmony_ci xfer[0].len = 2; 68462306a36Sopenharmony_ci xfer[0].buf = buf; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Read data */ 68762306a36Sopenharmony_ci xfer[1].addr = client->addr; 68862306a36Sopenharmony_ci xfer[1].flags = I2C_M_RD; 68962306a36Sopenharmony_ci xfer[1].len = len; 69062306a36Sopenharmony_ci xfer[1].buf = val; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciretry: 69362306a36Sopenharmony_ci ret = i2c_transfer(client->adapter, xfer, 2); 69462306a36Sopenharmony_ci if (ret == 2) { 69562306a36Sopenharmony_ci ret = 0; 69662306a36Sopenharmony_ci } else if (!retried && mxt_wakeup_toggle(client, true, true)) { 69762306a36Sopenharmony_ci retried = true; 69862306a36Sopenharmony_ci goto retry; 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci if (ret >= 0) 70162306a36Sopenharmony_ci ret = -EIO; 70262306a36Sopenharmony_ci dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", 70362306a36Sopenharmony_ci __func__, ret); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, 71062306a36Sopenharmony_ci const void *val) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci bool retried = false; 71362306a36Sopenharmony_ci u8 *buf; 71462306a36Sopenharmony_ci size_t count; 71562306a36Sopenharmony_ci int ret; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci count = len + 2; 71862306a36Sopenharmony_ci buf = kmalloc(count, GFP_KERNEL); 71962306a36Sopenharmony_ci if (!buf) 72062306a36Sopenharmony_ci return -ENOMEM; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci buf[0] = reg & 0xff; 72362306a36Sopenharmony_ci buf[1] = (reg >> 8) & 0xff; 72462306a36Sopenharmony_ci memcpy(&buf[2], val, len); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ciretry: 72762306a36Sopenharmony_ci ret = i2c_master_send(client, buf, count); 72862306a36Sopenharmony_ci if (ret == count) { 72962306a36Sopenharmony_ci ret = 0; 73062306a36Sopenharmony_ci } else if (!retried && mxt_wakeup_toggle(client, true, true)) { 73162306a36Sopenharmony_ci retried = true; 73262306a36Sopenharmony_ci goto retry; 73362306a36Sopenharmony_ci } else { 73462306a36Sopenharmony_ci if (ret >= 0) 73562306a36Sopenharmony_ci ret = -EIO; 73662306a36Sopenharmony_ci dev_err(&client->dev, "%s: i2c send failed (%d)\n", 73762306a36Sopenharmony_ci __func__, ret); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci kfree(buf); 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci return __mxt_write_reg(client, reg, 1, &val); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic struct mxt_object * 75062306a36Sopenharmony_cimxt_get_object(struct mxt_data *data, u8 type) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct mxt_object *object; 75362306a36Sopenharmony_ci int i; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci for (i = 0; i < data->info->object_num; i++) { 75662306a36Sopenharmony_ci object = data->object_table + i; 75762306a36Sopenharmony_ci if (object->type == type) 75862306a36Sopenharmony_ci return object; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci dev_warn(&data->client->dev, "Invalid object type T%u\n", type); 76262306a36Sopenharmony_ci return NULL; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct device *dev = &data->client->dev; 76862306a36Sopenharmony_ci u8 status = msg[1]; 76962306a36Sopenharmony_ci u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (crc != data->config_crc) { 77262306a36Sopenharmony_ci data->config_crc = crc; 77362306a36Sopenharmony_ci dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci complete(&data->crc_completion); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Detect reset */ 77962306a36Sopenharmony_ci if (status & MXT_T6_STATUS_RESET) 78062306a36Sopenharmony_ci complete(&data->reset_completion); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Output debug if status has changed */ 78362306a36Sopenharmony_ci if (status != data->t6_status) 78462306a36Sopenharmony_ci dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", 78562306a36Sopenharmony_ci status, 78662306a36Sopenharmony_ci status == 0 ? " OK" : "", 78762306a36Sopenharmony_ci status & MXT_T6_STATUS_RESET ? " RESET" : "", 78862306a36Sopenharmony_ci status & MXT_T6_STATUS_OFL ? " OFL" : "", 78962306a36Sopenharmony_ci status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", 79062306a36Sopenharmony_ci status & MXT_T6_STATUS_CAL ? " CAL" : "", 79162306a36Sopenharmony_ci status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", 79262306a36Sopenharmony_ci status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* Save current status */ 79562306a36Sopenharmony_ci data->t6_status = status; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic int mxt_write_object(struct mxt_data *data, 79962306a36Sopenharmony_ci u8 type, u8 offset, u8 val) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct mxt_object *object; 80262306a36Sopenharmony_ci u16 reg; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci object = mxt_get_object(data, type); 80562306a36Sopenharmony_ci if (!object || offset >= mxt_obj_size(object)) 80662306a36Sopenharmony_ci return -EINVAL; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci reg = object->start_address; 80962306a36Sopenharmony_ci return mxt_write_reg(data->client, reg + offset, val); 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void mxt_input_button(struct mxt_data *data, u8 *message) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct input_dev *input = data->input_dev; 81562306a36Sopenharmony_ci int i; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci for (i = 0; i < data->t19_num_keys; i++) { 81862306a36Sopenharmony_ci if (data->t19_keymap[i] == KEY_RESERVED) 81962306a36Sopenharmony_ci continue; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Active-low switch */ 82262306a36Sopenharmony_ci input_report_key(input, data->t19_keymap[i], 82362306a36Sopenharmony_ci !(message[1] & BIT(i))); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic void mxt_input_sync(struct mxt_data *data) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci input_mt_report_pointer_emulation(data->input_dev, 83062306a36Sopenharmony_ci data->t19_num_keys); 83162306a36Sopenharmony_ci input_sync(data->input_dev); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic void mxt_proc_t9_message(struct mxt_data *data, u8 *message) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct device *dev = &data->client->dev; 83762306a36Sopenharmony_ci struct input_dev *input_dev = data->input_dev; 83862306a36Sopenharmony_ci int id; 83962306a36Sopenharmony_ci u8 status; 84062306a36Sopenharmony_ci int x; 84162306a36Sopenharmony_ci int y; 84262306a36Sopenharmony_ci int area; 84362306a36Sopenharmony_ci int amplitude; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci id = message[0] - data->T9_reportid_min; 84662306a36Sopenharmony_ci status = message[1]; 84762306a36Sopenharmony_ci x = (message[2] << 4) | ((message[4] >> 4) & 0xf); 84862306a36Sopenharmony_ci y = (message[3] << 4) | ((message[4] & 0xf)); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Handle 10/12 bit switching */ 85162306a36Sopenharmony_ci if (data->max_x < 1024) 85262306a36Sopenharmony_ci x >>= 2; 85362306a36Sopenharmony_ci if (data->max_y < 1024) 85462306a36Sopenharmony_ci y >>= 2; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci area = message[5]; 85762306a36Sopenharmony_ci amplitude = message[6]; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci dev_dbg(dev, 86062306a36Sopenharmony_ci "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", 86162306a36Sopenharmony_ci id, 86262306a36Sopenharmony_ci (status & MXT_T9_DETECT) ? 'D' : '.', 86362306a36Sopenharmony_ci (status & MXT_T9_PRESS) ? 'P' : '.', 86462306a36Sopenharmony_ci (status & MXT_T9_RELEASE) ? 'R' : '.', 86562306a36Sopenharmony_ci (status & MXT_T9_MOVE) ? 'M' : '.', 86662306a36Sopenharmony_ci (status & MXT_T9_VECTOR) ? 'V' : '.', 86762306a36Sopenharmony_ci (status & MXT_T9_AMP) ? 'A' : '.', 86862306a36Sopenharmony_ci (status & MXT_T9_SUPPRESS) ? 'S' : '.', 86962306a36Sopenharmony_ci (status & MXT_T9_UNGRIP) ? 'U' : '.', 87062306a36Sopenharmony_ci x, y, area, amplitude); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci input_mt_slot(input_dev, id); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (status & MXT_T9_DETECT) { 87562306a36Sopenharmony_ci /* 87662306a36Sopenharmony_ci * Multiple bits may be set if the host is slow to read 87762306a36Sopenharmony_ci * the status messages, indicating all the events that 87862306a36Sopenharmony_ci * have happened. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci if (status & MXT_T9_RELEASE) { 88162306a36Sopenharmony_ci input_mt_report_slot_inactive(input_dev); 88262306a36Sopenharmony_ci mxt_input_sync(data); 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* if active, pressure must be non-zero */ 88662306a36Sopenharmony_ci if (!amplitude) 88762306a36Sopenharmony_ci amplitude = MXT_PRESSURE_DEFAULT; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* Touch active */ 89062306a36Sopenharmony_ci input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); 89162306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_POSITION_X, x); 89262306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_POSITION_Y, y); 89362306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); 89462306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); 89562306a36Sopenharmony_ci } else { 89662306a36Sopenharmony_ci /* Touch no longer active, close out slot */ 89762306a36Sopenharmony_ci input_mt_report_slot_inactive(input_dev); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci data->update_input = true; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic void mxt_proc_t15_messages(struct mxt_data *data, u8 *message) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct input_dev *input_dev = data->input_dev; 90662306a36Sopenharmony_ci unsigned long keystates = get_unaligned_le32(&message[2]); 90762306a36Sopenharmony_ci int key; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci for (key = 0; key < data->t15_num_keys; key++) 91062306a36Sopenharmony_ci input_report_key(input_dev, data->t15_keymap[key], 91162306a36Sopenharmony_ci keystates & BIT(key)); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci data->update_input = true; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic void mxt_proc_t97_messages(struct mxt_data *data, u8 *message) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci mxt_proc_t15_messages(data, message); 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic void mxt_proc_t100_message(struct mxt_data *data, u8 *message) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct device *dev = &data->client->dev; 92462306a36Sopenharmony_ci struct input_dev *input_dev = data->input_dev; 92562306a36Sopenharmony_ci int id; 92662306a36Sopenharmony_ci u8 status; 92762306a36Sopenharmony_ci u8 type = 0; 92862306a36Sopenharmony_ci u16 x; 92962306a36Sopenharmony_ci u16 y; 93062306a36Sopenharmony_ci int distance = 0; 93162306a36Sopenharmony_ci int tool = 0; 93262306a36Sopenharmony_ci u8 major = 0; 93362306a36Sopenharmony_ci u8 pressure = 0; 93462306a36Sopenharmony_ci u8 orientation = 0; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci id = message[0] - data->T100_reportid_min - 2; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* ignore SCRSTATUS events */ 93962306a36Sopenharmony_ci if (id < 0) 94062306a36Sopenharmony_ci return; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci status = message[1]; 94362306a36Sopenharmony_ci x = get_unaligned_le16(&message[2]); 94462306a36Sopenharmony_ci y = get_unaligned_le16(&message[4]); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (status & MXT_T100_DETECT) { 94762306a36Sopenharmony_ci type = (status & MXT_T100_TYPE_MASK) >> 4; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci switch (type) { 95062306a36Sopenharmony_ci case MXT_T100_TYPE_HOVERING_FINGER: 95162306a36Sopenharmony_ci tool = MT_TOOL_FINGER; 95262306a36Sopenharmony_ci distance = MXT_DISTANCE_HOVERING; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (data->t100_aux_vect) 95562306a36Sopenharmony_ci orientation = message[data->t100_aux_vect]; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci case MXT_T100_TYPE_FINGER: 96062306a36Sopenharmony_ci case MXT_T100_TYPE_GLOVE: 96162306a36Sopenharmony_ci tool = MT_TOOL_FINGER; 96262306a36Sopenharmony_ci distance = MXT_DISTANCE_ACTIVE_TOUCH; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (data->t100_aux_area) 96562306a36Sopenharmony_ci major = message[data->t100_aux_area]; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (data->t100_aux_ampl) 96862306a36Sopenharmony_ci pressure = message[data->t100_aux_ampl]; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (data->t100_aux_vect) 97162306a36Sopenharmony_ci orientation = message[data->t100_aux_vect]; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci case MXT_T100_TYPE_PASSIVE_STYLUS: 97662306a36Sopenharmony_ci tool = MT_TOOL_PEN; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* 97962306a36Sopenharmony_ci * Passive stylus is reported with size zero so 98062306a36Sopenharmony_ci * hardcode. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci major = MXT_TOUCH_MAJOR_DEFAULT; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (data->t100_aux_ampl) 98562306a36Sopenharmony_ci pressure = message[data->t100_aux_ampl]; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci case MXT_T100_TYPE_LARGE_TOUCH: 99062306a36Sopenharmony_ci /* Ignore suppressed touch */ 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci default: 99462306a36Sopenharmony_ci dev_dbg(dev, "Unexpected T100 type\n"); 99562306a36Sopenharmony_ci return; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* 100062306a36Sopenharmony_ci * Values reported should be non-zero if tool is touching the 100162306a36Sopenharmony_ci * device 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER) 100462306a36Sopenharmony_ci pressure = MXT_PRESSURE_DEFAULT; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci input_mt_slot(input_dev, id); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (status & MXT_T100_DETECT) { 100962306a36Sopenharmony_ci dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n", 101062306a36Sopenharmony_ci id, type, x, y, major, pressure, orientation); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci input_mt_report_slot_state(input_dev, tool, 1); 101362306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_POSITION_X, x); 101462306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_POSITION_Y, y); 101562306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); 101662306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); 101762306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_DISTANCE, distance); 101862306a36Sopenharmony_ci input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); 101962306a36Sopenharmony_ci } else { 102062306a36Sopenharmony_ci dev_dbg(dev, "[%u] release\n", id); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* close out slot */ 102362306a36Sopenharmony_ci input_mt_report_slot_inactive(input_dev); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci data->update_input = true; 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic int mxt_proc_message(struct mxt_data *data, u8 *message) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci u8 report_id = message[0]; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (report_id == MXT_RPTID_NOMSG) 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (report_id == data->T6_reportid) { 103762306a36Sopenharmony_ci mxt_proc_t6_messages(data, message); 103862306a36Sopenharmony_ci } else if (!data->input_dev) { 103962306a36Sopenharmony_ci /* 104062306a36Sopenharmony_ci * Do not report events if input device 104162306a36Sopenharmony_ci * is not yet registered. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci mxt_dump_message(data, message); 104462306a36Sopenharmony_ci } else if (report_id >= data->T9_reportid_min && 104562306a36Sopenharmony_ci report_id <= data->T9_reportid_max) { 104662306a36Sopenharmony_ci mxt_proc_t9_message(data, message); 104762306a36Sopenharmony_ci } else if (report_id >= data->T15_reportid_min && 104862306a36Sopenharmony_ci report_id <= data->T15_reportid_max) { 104962306a36Sopenharmony_ci mxt_proc_t15_messages(data, message); 105062306a36Sopenharmony_ci } else if (report_id >= data->T97_reportid_min && 105162306a36Sopenharmony_ci report_id <= data->T97_reportid_max) { 105262306a36Sopenharmony_ci mxt_proc_t97_messages(data, message); 105362306a36Sopenharmony_ci } else if (report_id >= data->T100_reportid_min && 105462306a36Sopenharmony_ci report_id <= data->T100_reportid_max) { 105562306a36Sopenharmony_ci mxt_proc_t100_message(data, message); 105662306a36Sopenharmony_ci } else if (report_id == data->T19_reportid) { 105762306a36Sopenharmony_ci mxt_input_button(data, message); 105862306a36Sopenharmony_ci data->update_input = true; 105962306a36Sopenharmony_ci } else { 106062306a36Sopenharmony_ci mxt_dump_message(data, message); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return 1; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic int mxt_read_and_process_messages(struct mxt_data *data, u8 count) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct device *dev = &data->client->dev; 106962306a36Sopenharmony_ci int ret; 107062306a36Sopenharmony_ci int i; 107162306a36Sopenharmony_ci u8 num_valid = 0; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Safety check for msg_buf */ 107462306a36Sopenharmony_ci if (count > data->max_reportid) 107562306a36Sopenharmony_ci return -EINVAL; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* Process remaining messages if necessary */ 107862306a36Sopenharmony_ci ret = __mxt_read_reg(data->client, data->T5_address, 107962306a36Sopenharmony_ci data->T5_msg_size * count, data->msg_buf); 108062306a36Sopenharmony_ci if (ret) { 108162306a36Sopenharmony_ci dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); 108262306a36Sopenharmony_ci return ret; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 108662306a36Sopenharmony_ci ret = mxt_proc_message(data, 108762306a36Sopenharmony_ci data->msg_buf + data->T5_msg_size * i); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (ret == 1) 109062306a36Sopenharmony_ci num_valid++; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* return number of messages read */ 109462306a36Sopenharmony_ci return num_valid; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic irqreturn_t mxt_process_messages_t44(struct mxt_data *data) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct device *dev = &data->client->dev; 110062306a36Sopenharmony_ci int ret; 110162306a36Sopenharmony_ci u8 count, num_left; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* Read T44 and T5 together */ 110462306a36Sopenharmony_ci ret = __mxt_read_reg(data->client, data->T44_address, 110562306a36Sopenharmony_ci data->T5_msg_size + 1, data->msg_buf); 110662306a36Sopenharmony_ci if (ret) { 110762306a36Sopenharmony_ci dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); 110862306a36Sopenharmony_ci return IRQ_NONE; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci count = data->msg_buf[0]; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* 111462306a36Sopenharmony_ci * This condition may be caused by the CHG line being configured in 111562306a36Sopenharmony_ci * Mode 0. It results in unnecessary I2C operations but it is benign. 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci if (count == 0) 111862306a36Sopenharmony_ci return IRQ_NONE; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (count > data->max_reportid) { 112162306a36Sopenharmony_ci dev_warn(dev, "T44 count %d exceeded max report id\n", count); 112262306a36Sopenharmony_ci count = data->max_reportid; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Process first message */ 112662306a36Sopenharmony_ci ret = mxt_proc_message(data, data->msg_buf + 1); 112762306a36Sopenharmony_ci if (ret < 0) { 112862306a36Sopenharmony_ci dev_warn(dev, "Unexpected invalid message\n"); 112962306a36Sopenharmony_ci return IRQ_NONE; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci num_left = count - 1; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Process remaining messages if necessary */ 113562306a36Sopenharmony_ci if (num_left) { 113662306a36Sopenharmony_ci ret = mxt_read_and_process_messages(data, num_left); 113762306a36Sopenharmony_ci if (ret < 0) 113862306a36Sopenharmony_ci goto end; 113962306a36Sopenharmony_ci else if (ret != num_left) 114062306a36Sopenharmony_ci dev_warn(dev, "Unexpected invalid message\n"); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ciend: 114462306a36Sopenharmony_ci if (data->update_input) { 114562306a36Sopenharmony_ci mxt_input_sync(data); 114662306a36Sopenharmony_ci data->update_input = false; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return IRQ_HANDLED; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic int mxt_process_messages_until_invalid(struct mxt_data *data) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct device *dev = &data->client->dev; 115562306a36Sopenharmony_ci int count, read; 115662306a36Sopenharmony_ci u8 tries = 2; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci count = data->max_reportid; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci /* Read messages until we force an invalid */ 116162306a36Sopenharmony_ci do { 116262306a36Sopenharmony_ci read = mxt_read_and_process_messages(data, count); 116362306a36Sopenharmony_ci if (read < count) 116462306a36Sopenharmony_ci return 0; 116562306a36Sopenharmony_ci } while (--tries); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (data->update_input) { 116862306a36Sopenharmony_ci mxt_input_sync(data); 116962306a36Sopenharmony_ci data->update_input = false; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci dev_err(dev, "CHG pin isn't cleared\n"); 117362306a36Sopenharmony_ci return -EBUSY; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic irqreturn_t mxt_process_messages(struct mxt_data *data) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci int total_handled, num_handled; 117962306a36Sopenharmony_ci u8 count = data->last_message_count; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (count < 1 || count > data->max_reportid) 118262306a36Sopenharmony_ci count = 1; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* include final invalid message */ 118562306a36Sopenharmony_ci total_handled = mxt_read_and_process_messages(data, count + 1); 118662306a36Sopenharmony_ci if (total_handled < 0) 118762306a36Sopenharmony_ci return IRQ_NONE; 118862306a36Sopenharmony_ci /* if there were invalid messages, then we are done */ 118962306a36Sopenharmony_ci else if (total_handled <= count) 119062306a36Sopenharmony_ci goto update_count; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* keep reading two msgs until one is invalid or reportid limit */ 119362306a36Sopenharmony_ci do { 119462306a36Sopenharmony_ci num_handled = mxt_read_and_process_messages(data, 2); 119562306a36Sopenharmony_ci if (num_handled < 0) 119662306a36Sopenharmony_ci return IRQ_NONE; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci total_handled += num_handled; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (num_handled < 2) 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci } while (total_handled < data->num_touchids); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciupdate_count: 120562306a36Sopenharmony_ci data->last_message_count = total_handled; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (data->update_input) { 120862306a36Sopenharmony_ci mxt_input_sync(data); 120962306a36Sopenharmony_ci data->update_input = false; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return IRQ_HANDLED; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic irqreturn_t mxt_interrupt(int irq, void *dev_id) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct mxt_data *data = dev_id; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (data->in_bootloader) { 122062306a36Sopenharmony_ci /* bootloader state transition completion */ 122162306a36Sopenharmony_ci complete(&data->bl_completion); 122262306a36Sopenharmony_ci return IRQ_HANDLED; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (!data->object_table) 122662306a36Sopenharmony_ci return IRQ_HANDLED; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (data->T44_address) { 122962306a36Sopenharmony_ci return mxt_process_messages_t44(data); 123062306a36Sopenharmony_ci } else { 123162306a36Sopenharmony_ci return mxt_process_messages(data); 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, 123662306a36Sopenharmony_ci u8 value, bool wait) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci u16 reg; 123962306a36Sopenharmony_ci u8 command_register; 124062306a36Sopenharmony_ci int timeout_counter = 0; 124162306a36Sopenharmony_ci int ret; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci reg = data->T6_address + cmd_offset; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci ret = mxt_write_reg(data->client, reg, value); 124662306a36Sopenharmony_ci if (ret) 124762306a36Sopenharmony_ci return ret; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (!wait) 125062306a36Sopenharmony_ci return 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci do { 125362306a36Sopenharmony_ci msleep(20); 125462306a36Sopenharmony_ci ret = __mxt_read_reg(data->client, reg, 1, &command_register); 125562306a36Sopenharmony_ci if (ret) 125662306a36Sopenharmony_ci return ret; 125762306a36Sopenharmony_ci } while (command_register != 0 && timeout_counter++ <= 100); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (timeout_counter > 100) { 126062306a36Sopenharmony_ci dev_err(&data->client->dev, "Command failed!\n"); 126162306a36Sopenharmony_ci return -EIO; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic int mxt_acquire_irq(struct mxt_data *data) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci int error; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci enable_irq(data->irq); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (data->use_retrigen_workaround) { 127462306a36Sopenharmony_ci error = mxt_process_messages_until_invalid(data); 127562306a36Sopenharmony_ci if (error) 127662306a36Sopenharmony_ci return error; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci return 0; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic int mxt_soft_reset(struct mxt_data *data) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct device *dev = &data->client->dev; 128562306a36Sopenharmony_ci int ret = 0; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci dev_info(dev, "Resetting device\n"); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci disable_irq(data->irq); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci reinit_completion(&data->reset_completion); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); 129462306a36Sopenharmony_ci if (ret) 129562306a36Sopenharmony_ci return ret; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* Ignore CHG line for 100ms after reset */ 129862306a36Sopenharmony_ci msleep(MXT_RESET_INVALID_CHG); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci mxt_acquire_irq(data); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = mxt_wait_for_completion(data, &data->reset_completion, 130362306a36Sopenharmony_ci MXT_RESET_TIMEOUT); 130462306a36Sopenharmony_ci if (ret) 130562306a36Sopenharmony_ci return ret; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return 0; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_cistatic void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci /* 131362306a36Sopenharmony_ci * On failure, CRC is set to 0 and config will always be 131462306a36Sopenharmony_ci * downloaded. 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_ci data->config_crc = 0; 131762306a36Sopenharmony_ci reinit_completion(&data->crc_completion); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci mxt_t6_command(data, cmd, value, true); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* 132262306a36Sopenharmony_ci * Wait for crc message. On failure, CRC is set to 0 and config will 132362306a36Sopenharmony_ci * always be downloaded. 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci static const unsigned int crcpoly = 0x80001B; 133162306a36Sopenharmony_ci u32 result; 133262306a36Sopenharmony_ci u32 data_word; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci data_word = (secondbyte << 8) | firstbyte; 133562306a36Sopenharmony_ci result = ((*crc << 1) ^ data_word); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (result & 0x1000000) 133862306a36Sopenharmony_ci result ^= crcpoly; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci *crc = result; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci u32 crc = 0; 134662306a36Sopenharmony_ci u8 *ptr = base + start_off; 134762306a36Sopenharmony_ci u8 *last_val = base + end_off - 1; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (end_off < start_off) 135062306a36Sopenharmony_ci return -EINVAL; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci while (ptr < last_val) { 135362306a36Sopenharmony_ci mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); 135462306a36Sopenharmony_ci ptr += 2; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* if len is odd, fill the last byte with 0 */ 135862306a36Sopenharmony_ci if (ptr == last_val) 135962306a36Sopenharmony_ci mxt_calc_crc24(&crc, *ptr, 0); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci /* Mask to 24-bit */ 136262306a36Sopenharmony_ci crc &= 0x00FFFFFF; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci return crc; 136562306a36Sopenharmony_ci} 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic int mxt_check_retrigen(struct mxt_data *data) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci struct i2c_client *client = data->client; 137062306a36Sopenharmony_ci int error; 137162306a36Sopenharmony_ci int val; 137262306a36Sopenharmony_ci struct irq_data *irqd; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci data->use_retrigen_workaround = false; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci irqd = irq_get_irq_data(data->irq); 137762306a36Sopenharmony_ci if (!irqd) 137862306a36Sopenharmony_ci return -EINVAL; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (irqd_is_level_type(irqd)) 138162306a36Sopenharmony_ci return 0; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (data->T18_address) { 138462306a36Sopenharmony_ci error = __mxt_read_reg(client, 138562306a36Sopenharmony_ci data->T18_address + MXT_COMMS_CTRL, 138662306a36Sopenharmony_ci 1, &val); 138762306a36Sopenharmony_ci if (error) 138862306a36Sopenharmony_ci return error; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (val & MXT_COMMS_RETRIGEN) 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); 139562306a36Sopenharmony_ci data->use_retrigen_workaround = true; 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci struct device *dev = &data->client->dev; 140262306a36Sopenharmony_ci struct mxt_object *object; 140362306a36Sopenharmony_ci unsigned int type, instance, size, byte_offset; 140462306a36Sopenharmony_ci int offset; 140562306a36Sopenharmony_ci int ret; 140662306a36Sopenharmony_ci int i; 140762306a36Sopenharmony_ci u16 reg; 140862306a36Sopenharmony_ci u8 val; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci while (cfg->raw_pos < cfg->raw_size) { 141162306a36Sopenharmony_ci /* Read type, instance, length */ 141262306a36Sopenharmony_ci ret = sscanf(cfg->raw + cfg->raw_pos, "%x %x %x%n", 141362306a36Sopenharmony_ci &type, &instance, &size, &offset); 141462306a36Sopenharmony_ci if (ret == 0) { 141562306a36Sopenharmony_ci /* EOF */ 141662306a36Sopenharmony_ci break; 141762306a36Sopenharmony_ci } else if (ret != 3) { 141862306a36Sopenharmony_ci dev_err(dev, "Bad format: failed to parse object\n"); 141962306a36Sopenharmony_ci return -EINVAL; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci cfg->raw_pos += offset; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci object = mxt_get_object(data, type); 142462306a36Sopenharmony_ci if (!object) { 142562306a36Sopenharmony_ci /* Skip object */ 142662306a36Sopenharmony_ci for (i = 0; i < size; i++) { 142762306a36Sopenharmony_ci ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", 142862306a36Sopenharmony_ci &val, &offset); 142962306a36Sopenharmony_ci if (ret != 1) { 143062306a36Sopenharmony_ci dev_err(dev, "Bad format in T%d at %d\n", 143162306a36Sopenharmony_ci type, i); 143262306a36Sopenharmony_ci return -EINVAL; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci cfg->raw_pos += offset; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci continue; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (size > mxt_obj_size(object)) { 144062306a36Sopenharmony_ci /* 144162306a36Sopenharmony_ci * Either we are in fallback mode due to wrong 144262306a36Sopenharmony_ci * config or config from a later fw version, 144362306a36Sopenharmony_ci * or the file is corrupt or hand-edited. 144462306a36Sopenharmony_ci */ 144562306a36Sopenharmony_ci dev_warn(dev, "Discarding %zu byte(s) in T%u\n", 144662306a36Sopenharmony_ci size - mxt_obj_size(object), type); 144762306a36Sopenharmony_ci } else if (mxt_obj_size(object) > size) { 144862306a36Sopenharmony_ci /* 144962306a36Sopenharmony_ci * If firmware is upgraded, new bytes may be added to 145062306a36Sopenharmony_ci * end of objects. It is generally forward compatible 145162306a36Sopenharmony_ci * to zero these bytes - previous behaviour will be 145262306a36Sopenharmony_ci * retained. However this does invalidate the CRC and 145362306a36Sopenharmony_ci * will force fallback mode until the configuration is 145462306a36Sopenharmony_ci * updated. We warn here but do nothing else - the 145562306a36Sopenharmony_ci * malloc has zeroed the entire configuration. 145662306a36Sopenharmony_ci */ 145762306a36Sopenharmony_ci dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", 145862306a36Sopenharmony_ci mxt_obj_size(object) - size, type); 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (instance >= mxt_obj_instances(object)) { 146262306a36Sopenharmony_ci dev_err(dev, "Object instances exceeded!\n"); 146362306a36Sopenharmony_ci return -EINVAL; 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci reg = object->start_address + mxt_obj_size(object) * instance; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci for (i = 0; i < size; i++) { 146962306a36Sopenharmony_ci ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", 147062306a36Sopenharmony_ci &val, 147162306a36Sopenharmony_ci &offset); 147262306a36Sopenharmony_ci if (ret != 1) { 147362306a36Sopenharmony_ci dev_err(dev, "Bad format in T%d at %d\n", 147462306a36Sopenharmony_ci type, i); 147562306a36Sopenharmony_ci return -EINVAL; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci cfg->raw_pos += offset; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (i > mxt_obj_size(object)) 148062306a36Sopenharmony_ci continue; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci byte_offset = reg + i - cfg->start_ofs; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (byte_offset >= 0 && byte_offset < cfg->mem_size) { 148562306a36Sopenharmony_ci *(cfg->mem + byte_offset) = val; 148662306a36Sopenharmony_ci } else { 148762306a36Sopenharmony_ci dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", 148862306a36Sopenharmony_ci reg, object->type, byte_offset); 148962306a36Sopenharmony_ci return -EINVAL; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic int mxt_upload_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci unsigned int byte_offset = 0; 150062306a36Sopenharmony_ci int error; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* Write configuration as blocks */ 150362306a36Sopenharmony_ci while (byte_offset < cfg->mem_size) { 150462306a36Sopenharmony_ci unsigned int size = cfg->mem_size - byte_offset; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (size > MXT_MAX_BLOCK_WRITE) 150762306a36Sopenharmony_ci size = MXT_MAX_BLOCK_WRITE; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci error = __mxt_write_reg(data->client, 151062306a36Sopenharmony_ci cfg->start_ofs + byte_offset, 151162306a36Sopenharmony_ci size, cfg->mem + byte_offset); 151262306a36Sopenharmony_ci if (error) { 151362306a36Sopenharmony_ci dev_err(&data->client->dev, 151462306a36Sopenharmony_ci "Config write error, ret=%d\n", error); 151562306a36Sopenharmony_ci return error; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci byte_offset += size; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci return 0; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic int mxt_init_t7_power_cfg(struct mxt_data *data); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci/* 152762306a36Sopenharmony_ci * mxt_update_cfg - download configuration to chip 152862306a36Sopenharmony_ci * 152962306a36Sopenharmony_ci * Atmel Raw Config File Format 153062306a36Sopenharmony_ci * 153162306a36Sopenharmony_ci * The first four lines of the raw config file contain: 153262306a36Sopenharmony_ci * 1) Version 153362306a36Sopenharmony_ci * 2) Chip ID Information (first 7 bytes of device memory) 153462306a36Sopenharmony_ci * 3) Chip Information Block 24-bit CRC Checksum 153562306a36Sopenharmony_ci * 4) Chip Configuration 24-bit CRC Checksum 153662306a36Sopenharmony_ci * 153762306a36Sopenharmony_ci * The rest of the file consists of one line per object instance: 153862306a36Sopenharmony_ci * <TYPE> <INSTANCE> <SIZE> <CONTENTS> 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * <TYPE> - 2-byte object type as hex 154162306a36Sopenharmony_ci * <INSTANCE> - 2-byte object instance number as hex 154262306a36Sopenharmony_ci * <SIZE> - 2-byte object size as hex 154362306a36Sopenharmony_ci * <CONTENTS> - array of <SIZE> 1-byte hex values 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_cistatic int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct device *dev = &data->client->dev; 154862306a36Sopenharmony_ci struct mxt_cfg cfg; 154962306a36Sopenharmony_ci int ret; 155062306a36Sopenharmony_ci int offset; 155162306a36Sopenharmony_ci int i; 155262306a36Sopenharmony_ci u32 info_crc, config_crc, calculated_crc; 155362306a36Sopenharmony_ci u16 crc_start = 0; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* Make zero terminated copy of the OBP_RAW file */ 155662306a36Sopenharmony_ci cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL); 155762306a36Sopenharmony_ci if (!cfg.raw) 155862306a36Sopenharmony_ci return -ENOMEM; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci cfg.raw_size = fw->size; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (strncmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { 156562306a36Sopenharmony_ci dev_err(dev, "Unrecognised config file\n"); 156662306a36Sopenharmony_ci ret = -EINVAL; 156762306a36Sopenharmony_ci goto release_raw; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci cfg.raw_pos = strlen(MXT_CFG_MAGIC); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* Load information block and check */ 157362306a36Sopenharmony_ci for (i = 0; i < sizeof(struct mxt_info); i++) { 157462306a36Sopenharmony_ci ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", 157562306a36Sopenharmony_ci (unsigned char *)&cfg.info + i, 157662306a36Sopenharmony_ci &offset); 157762306a36Sopenharmony_ci if (ret != 1) { 157862306a36Sopenharmony_ci dev_err(dev, "Bad format\n"); 157962306a36Sopenharmony_ci ret = -EINVAL; 158062306a36Sopenharmony_ci goto release_raw; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci cfg.raw_pos += offset; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if (cfg.info.family_id != data->info->family_id) { 158762306a36Sopenharmony_ci dev_err(dev, "Family ID mismatch!\n"); 158862306a36Sopenharmony_ci ret = -EINVAL; 158962306a36Sopenharmony_ci goto release_raw; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (cfg.info.variant_id != data->info->variant_id) { 159362306a36Sopenharmony_ci dev_err(dev, "Variant ID mismatch!\n"); 159462306a36Sopenharmony_ci ret = -EINVAL; 159562306a36Sopenharmony_ci goto release_raw; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* Read CRCs */ 159962306a36Sopenharmony_ci ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset); 160062306a36Sopenharmony_ci if (ret != 1) { 160162306a36Sopenharmony_ci dev_err(dev, "Bad format: failed to parse Info CRC\n"); 160262306a36Sopenharmony_ci ret = -EINVAL; 160362306a36Sopenharmony_ci goto release_raw; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci cfg.raw_pos += offset; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset); 160862306a36Sopenharmony_ci if (ret != 1) { 160962306a36Sopenharmony_ci dev_err(dev, "Bad format: failed to parse Config CRC\n"); 161062306a36Sopenharmony_ci ret = -EINVAL; 161162306a36Sopenharmony_ci goto release_raw; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci cfg.raw_pos += offset; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* 161662306a36Sopenharmony_ci * The Info Block CRC is calculated over mxt_info and the object 161762306a36Sopenharmony_ci * table. If it does not match then we are trying to load the 161862306a36Sopenharmony_ci * configuration from a different chip or firmware version, so 161962306a36Sopenharmony_ci * the configuration CRC is invalid anyway. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_ci if (info_crc == data->info_crc) { 162262306a36Sopenharmony_ci if (config_crc == 0 || data->config_crc == 0) { 162362306a36Sopenharmony_ci dev_info(dev, "CRC zero, attempting to apply config\n"); 162462306a36Sopenharmony_ci } else if (config_crc == data->config_crc) { 162562306a36Sopenharmony_ci dev_dbg(dev, "Config CRC 0x%06X: OK\n", 162662306a36Sopenharmony_ci data->config_crc); 162762306a36Sopenharmony_ci ret = 0; 162862306a36Sopenharmony_ci goto release_raw; 162962306a36Sopenharmony_ci } else { 163062306a36Sopenharmony_ci dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", 163162306a36Sopenharmony_ci data->config_crc, config_crc); 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci } else { 163462306a36Sopenharmony_ci dev_warn(dev, 163562306a36Sopenharmony_ci "Warning: Info CRC error - device=0x%06X file=0x%06X\n", 163662306a36Sopenharmony_ci data->info_crc, info_crc); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci /* Malloc memory to store configuration */ 164062306a36Sopenharmony_ci cfg.start_ofs = MXT_OBJECT_START + 164162306a36Sopenharmony_ci data->info->object_num * sizeof(struct mxt_object) + 164262306a36Sopenharmony_ci MXT_INFO_CHECKSUM_SIZE; 164362306a36Sopenharmony_ci cfg.mem_size = data->mem_size - cfg.start_ofs; 164462306a36Sopenharmony_ci cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); 164562306a36Sopenharmony_ci if (!cfg.mem) { 164662306a36Sopenharmony_ci ret = -ENOMEM; 164762306a36Sopenharmony_ci goto release_raw; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci ret = mxt_prepare_cfg_mem(data, &cfg); 165162306a36Sopenharmony_ci if (ret) 165262306a36Sopenharmony_ci goto release_mem; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci /* Calculate crc of the received configs (not the raw config file) */ 165562306a36Sopenharmony_ci if (data->T71_address) 165662306a36Sopenharmony_ci crc_start = data->T71_address; 165762306a36Sopenharmony_ci else if (data->T7_address) 165862306a36Sopenharmony_ci crc_start = data->T7_address; 165962306a36Sopenharmony_ci else 166062306a36Sopenharmony_ci dev_warn(dev, "Could not find CRC start\n"); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci if (crc_start > cfg.start_ofs) { 166362306a36Sopenharmony_ci calculated_crc = mxt_calculate_crc(cfg.mem, 166462306a36Sopenharmony_ci crc_start - cfg.start_ofs, 166562306a36Sopenharmony_ci cfg.mem_size); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (config_crc > 0 && config_crc != calculated_crc) 166862306a36Sopenharmony_ci dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n", 166962306a36Sopenharmony_ci calculated_crc, config_crc); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci ret = mxt_upload_cfg_mem(data, &cfg); 167362306a36Sopenharmony_ci if (ret) 167462306a36Sopenharmony_ci goto release_mem; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci ret = mxt_check_retrigen(data); 167962306a36Sopenharmony_ci if (ret) 168062306a36Sopenharmony_ci goto release_mem; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci ret = mxt_soft_reset(data); 168362306a36Sopenharmony_ci if (ret) 168462306a36Sopenharmony_ci goto release_mem; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci dev_info(dev, "Config successfully updated\n"); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci /* T7 config may have changed */ 168962306a36Sopenharmony_ci mxt_init_t7_power_cfg(data); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_cirelease_mem: 169262306a36Sopenharmony_ci kfree(cfg.mem); 169362306a36Sopenharmony_cirelease_raw: 169462306a36Sopenharmony_ci kfree(cfg.raw); 169562306a36Sopenharmony_ci return ret; 169662306a36Sopenharmony_ci} 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_cistatic void mxt_free_input_device(struct mxt_data *data) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci if (data->input_dev) { 170162306a36Sopenharmony_ci input_unregister_device(data->input_dev); 170262306a36Sopenharmony_ci data->input_dev = NULL; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci} 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_cistatic void mxt_free_object_table(struct mxt_data *data) 170762306a36Sopenharmony_ci{ 170862306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 170962306a36Sopenharmony_ci video_unregister_device(&data->dbg.vdev); 171062306a36Sopenharmony_ci v4l2_device_unregister(&data->dbg.v4l2); 171162306a36Sopenharmony_ci#endif 171262306a36Sopenharmony_ci data->object_table = NULL; 171362306a36Sopenharmony_ci data->info = NULL; 171462306a36Sopenharmony_ci kfree(data->raw_info_block); 171562306a36Sopenharmony_ci data->raw_info_block = NULL; 171662306a36Sopenharmony_ci kfree(data->msg_buf); 171762306a36Sopenharmony_ci data->msg_buf = NULL; 171862306a36Sopenharmony_ci data->T5_address = 0; 171962306a36Sopenharmony_ci data->T5_msg_size = 0; 172062306a36Sopenharmony_ci data->T6_reportid = 0; 172162306a36Sopenharmony_ci data->T7_address = 0; 172262306a36Sopenharmony_ci data->T71_address = 0; 172362306a36Sopenharmony_ci data->T9_reportid_min = 0; 172462306a36Sopenharmony_ci data->T9_reportid_max = 0; 172562306a36Sopenharmony_ci data->T15_reportid_min = 0; 172662306a36Sopenharmony_ci data->T15_reportid_max = 0; 172762306a36Sopenharmony_ci data->T18_address = 0; 172862306a36Sopenharmony_ci data->T19_reportid = 0; 172962306a36Sopenharmony_ci data->T44_address = 0; 173062306a36Sopenharmony_ci data->T97_reportid_min = 0; 173162306a36Sopenharmony_ci data->T97_reportid_max = 0; 173262306a36Sopenharmony_ci data->T100_reportid_min = 0; 173362306a36Sopenharmony_ci data->T100_reportid_max = 0; 173462306a36Sopenharmony_ci data->max_reportid = 0; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic int mxt_parse_object_table(struct mxt_data *data, 173862306a36Sopenharmony_ci struct mxt_object *object_table) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct i2c_client *client = data->client; 174162306a36Sopenharmony_ci int i; 174262306a36Sopenharmony_ci u8 reportid; 174362306a36Sopenharmony_ci u16 end_address; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci /* Valid Report IDs start counting from 1 */ 174662306a36Sopenharmony_ci reportid = 1; 174762306a36Sopenharmony_ci data->mem_size = 0; 174862306a36Sopenharmony_ci for (i = 0; i < data->info->object_num; i++) { 174962306a36Sopenharmony_ci struct mxt_object *object = object_table + i; 175062306a36Sopenharmony_ci u8 min_id, max_id; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci le16_to_cpus(&object->start_address); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (object->num_report_ids) { 175562306a36Sopenharmony_ci min_id = reportid; 175662306a36Sopenharmony_ci reportid += object->num_report_ids * 175762306a36Sopenharmony_ci mxt_obj_instances(object); 175862306a36Sopenharmony_ci max_id = reportid - 1; 175962306a36Sopenharmony_ci } else { 176062306a36Sopenharmony_ci min_id = 0; 176162306a36Sopenharmony_ci max_id = 0; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci dev_dbg(&data->client->dev, 176562306a36Sopenharmony_ci "T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n", 176662306a36Sopenharmony_ci object->type, object->start_address, 176762306a36Sopenharmony_ci mxt_obj_size(object), mxt_obj_instances(object), 176862306a36Sopenharmony_ci min_id, max_id); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci switch (object->type) { 177162306a36Sopenharmony_ci case MXT_GEN_MESSAGE_T5: 177262306a36Sopenharmony_ci if (data->info->family_id == 0x80 && 177362306a36Sopenharmony_ci data->info->version < 0x20) { 177462306a36Sopenharmony_ci /* 177562306a36Sopenharmony_ci * On mXT224 firmware versions prior to V2.0 177662306a36Sopenharmony_ci * read and discard unused CRC byte otherwise 177762306a36Sopenharmony_ci * DMA reads are misaligned. 177862306a36Sopenharmony_ci */ 177962306a36Sopenharmony_ci data->T5_msg_size = mxt_obj_size(object); 178062306a36Sopenharmony_ci } else { 178162306a36Sopenharmony_ci /* CRC not enabled, so skip last byte */ 178262306a36Sopenharmony_ci data->T5_msg_size = mxt_obj_size(object) - 1; 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci data->T5_address = object->start_address; 178562306a36Sopenharmony_ci break; 178662306a36Sopenharmony_ci case MXT_GEN_COMMAND_T6: 178762306a36Sopenharmony_ci data->T6_reportid = min_id; 178862306a36Sopenharmony_ci data->T6_address = object->start_address; 178962306a36Sopenharmony_ci break; 179062306a36Sopenharmony_ci case MXT_GEN_POWER_T7: 179162306a36Sopenharmony_ci data->T7_address = object->start_address; 179262306a36Sopenharmony_ci break; 179362306a36Sopenharmony_ci case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: 179462306a36Sopenharmony_ci data->T71_address = object->start_address; 179562306a36Sopenharmony_ci break; 179662306a36Sopenharmony_ci case MXT_TOUCH_MULTI_T9: 179762306a36Sopenharmony_ci data->multitouch = MXT_TOUCH_MULTI_T9; 179862306a36Sopenharmony_ci /* Only handle messages from first T9 instance */ 179962306a36Sopenharmony_ci data->T9_reportid_min = min_id; 180062306a36Sopenharmony_ci data->T9_reportid_max = min_id + 180162306a36Sopenharmony_ci object->num_report_ids - 1; 180262306a36Sopenharmony_ci data->num_touchids = object->num_report_ids; 180362306a36Sopenharmony_ci break; 180462306a36Sopenharmony_ci case MXT_TOUCH_KEYARRAY_T15: 180562306a36Sopenharmony_ci data->T15_reportid_min = min_id; 180662306a36Sopenharmony_ci data->T15_reportid_max = max_id; 180762306a36Sopenharmony_ci break; 180862306a36Sopenharmony_ci case MXT_SPT_COMMSCONFIG_T18: 180962306a36Sopenharmony_ci data->T18_address = object->start_address; 181062306a36Sopenharmony_ci break; 181162306a36Sopenharmony_ci case MXT_SPT_MESSAGECOUNT_T44: 181262306a36Sopenharmony_ci data->T44_address = object->start_address; 181362306a36Sopenharmony_ci break; 181462306a36Sopenharmony_ci case MXT_SPT_GPIOPWM_T19: 181562306a36Sopenharmony_ci data->T19_reportid = min_id; 181662306a36Sopenharmony_ci break; 181762306a36Sopenharmony_ci case MXT_TOUCH_PTC_KEYS_T97: 181862306a36Sopenharmony_ci data->T97_reportid_min = min_id; 181962306a36Sopenharmony_ci data->T97_reportid_max = max_id; 182062306a36Sopenharmony_ci break; 182162306a36Sopenharmony_ci case MXT_TOUCH_MULTITOUCHSCREEN_T100: 182262306a36Sopenharmony_ci data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; 182362306a36Sopenharmony_ci data->T100_reportid_min = min_id; 182462306a36Sopenharmony_ci data->T100_reportid_max = max_id; 182562306a36Sopenharmony_ci /* first two report IDs reserved */ 182662306a36Sopenharmony_ci data->num_touchids = object->num_report_ids - 2; 182762306a36Sopenharmony_ci break; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci end_address = object->start_address 183162306a36Sopenharmony_ci + mxt_obj_size(object) * mxt_obj_instances(object) - 1; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci if (end_address >= data->mem_size) 183462306a36Sopenharmony_ci data->mem_size = end_address + 1; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* Store maximum reportid */ 183862306a36Sopenharmony_ci data->max_reportid = reportid; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci /* If T44 exists, T5 position has to be directly after */ 184162306a36Sopenharmony_ci if (data->T44_address && (data->T5_address != data->T44_address + 1)) { 184262306a36Sopenharmony_ci dev_err(&client->dev, "Invalid T44 position\n"); 184362306a36Sopenharmony_ci return -EINVAL; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci data->msg_buf = kcalloc(data->max_reportid, 184762306a36Sopenharmony_ci data->T5_msg_size, GFP_KERNEL); 184862306a36Sopenharmony_ci if (!data->msg_buf) 184962306a36Sopenharmony_ci return -ENOMEM; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci return 0; 185262306a36Sopenharmony_ci} 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_cistatic int mxt_read_info_block(struct mxt_data *data) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci struct i2c_client *client = data->client; 185762306a36Sopenharmony_ci int error; 185862306a36Sopenharmony_ci size_t size; 185962306a36Sopenharmony_ci void *id_buf, *buf; 186062306a36Sopenharmony_ci uint8_t num_objects; 186162306a36Sopenharmony_ci u32 calculated_crc; 186262306a36Sopenharmony_ci u8 *crc_ptr; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci /* If info block already allocated, free it */ 186562306a36Sopenharmony_ci if (data->raw_info_block) 186662306a36Sopenharmony_ci mxt_free_object_table(data); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* Read 7-byte ID information block starting at address 0 */ 186962306a36Sopenharmony_ci size = sizeof(struct mxt_info); 187062306a36Sopenharmony_ci id_buf = kzalloc(size, GFP_KERNEL); 187162306a36Sopenharmony_ci if (!id_buf) 187262306a36Sopenharmony_ci return -ENOMEM; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci error = __mxt_read_reg(client, 0, size, id_buf); 187562306a36Sopenharmony_ci if (error) 187662306a36Sopenharmony_ci goto err_free_mem; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci /* Resize buffer to give space for rest of info block */ 187962306a36Sopenharmony_ci num_objects = ((struct mxt_info *)id_buf)->object_num; 188062306a36Sopenharmony_ci size += (num_objects * sizeof(struct mxt_object)) 188162306a36Sopenharmony_ci + MXT_INFO_CHECKSUM_SIZE; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci buf = krealloc(id_buf, size, GFP_KERNEL); 188462306a36Sopenharmony_ci if (!buf) { 188562306a36Sopenharmony_ci error = -ENOMEM; 188662306a36Sopenharmony_ci goto err_free_mem; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci id_buf = buf; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci /* Read rest of info block */ 189162306a36Sopenharmony_ci error = __mxt_read_reg(client, MXT_OBJECT_START, 189262306a36Sopenharmony_ci size - MXT_OBJECT_START, 189362306a36Sopenharmony_ci id_buf + MXT_OBJECT_START); 189462306a36Sopenharmony_ci if (error) 189562306a36Sopenharmony_ci goto err_free_mem; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* Extract & calculate checksum */ 189862306a36Sopenharmony_ci crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE; 189962306a36Sopenharmony_ci data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci calculated_crc = mxt_calculate_crc(id_buf, 0, 190262306a36Sopenharmony_ci size - MXT_INFO_CHECKSUM_SIZE); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci /* 190562306a36Sopenharmony_ci * CRC mismatch can be caused by data corruption due to I2C comms 190662306a36Sopenharmony_ci * issue or else device is not using Object Based Protocol (eg i2c-hid) 190762306a36Sopenharmony_ci */ 190862306a36Sopenharmony_ci if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { 190962306a36Sopenharmony_ci dev_err(&client->dev, 191062306a36Sopenharmony_ci "Info Block CRC error calculated=0x%06X read=0x%06X\n", 191162306a36Sopenharmony_ci calculated_crc, data->info_crc); 191262306a36Sopenharmony_ci error = -EIO; 191362306a36Sopenharmony_ci goto err_free_mem; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci data->raw_info_block = id_buf; 191762306a36Sopenharmony_ci data->info = (struct mxt_info *)id_buf; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci dev_info(&client->dev, 192062306a36Sopenharmony_ci "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", 192162306a36Sopenharmony_ci data->info->family_id, data->info->variant_id, 192262306a36Sopenharmony_ci data->info->version >> 4, data->info->version & 0xf, 192362306a36Sopenharmony_ci data->info->build, data->info->object_num); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci /* Parse object table information */ 192662306a36Sopenharmony_ci error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START); 192762306a36Sopenharmony_ci if (error) { 192862306a36Sopenharmony_ci dev_err(&client->dev, "Error %d parsing object table\n", error); 192962306a36Sopenharmony_ci mxt_free_object_table(data); 193062306a36Sopenharmony_ci return error; 193162306a36Sopenharmony_ci } 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci return 0; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cierr_free_mem: 193862306a36Sopenharmony_ci kfree(id_buf); 193962306a36Sopenharmony_ci return error; 194062306a36Sopenharmony_ci} 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_cistatic int mxt_read_t9_resolution(struct mxt_data *data) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci struct i2c_client *client = data->client; 194562306a36Sopenharmony_ci int error; 194662306a36Sopenharmony_ci struct t9_range range; 194762306a36Sopenharmony_ci unsigned char orient; 194862306a36Sopenharmony_ci struct mxt_object *object; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); 195162306a36Sopenharmony_ci if (!object) 195262306a36Sopenharmony_ci return -EINVAL; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci error = __mxt_read_reg(client, 195562306a36Sopenharmony_ci object->start_address + MXT_T9_XSIZE, 195662306a36Sopenharmony_ci sizeof(data->xsize), &data->xsize); 195762306a36Sopenharmony_ci if (error) 195862306a36Sopenharmony_ci return error; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci error = __mxt_read_reg(client, 196162306a36Sopenharmony_ci object->start_address + MXT_T9_YSIZE, 196262306a36Sopenharmony_ci sizeof(data->ysize), &data->ysize); 196362306a36Sopenharmony_ci if (error) 196462306a36Sopenharmony_ci return error; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci error = __mxt_read_reg(client, 196762306a36Sopenharmony_ci object->start_address + MXT_T9_RANGE, 196862306a36Sopenharmony_ci sizeof(range), &range); 196962306a36Sopenharmony_ci if (error) 197062306a36Sopenharmony_ci return error; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci data->max_x = get_unaligned_le16(&range.x); 197362306a36Sopenharmony_ci data->max_y = get_unaligned_le16(&range.y); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci error = __mxt_read_reg(client, 197662306a36Sopenharmony_ci object->start_address + MXT_T9_ORIENT, 197762306a36Sopenharmony_ci 1, &orient); 197862306a36Sopenharmony_ci if (error) 197962306a36Sopenharmony_ci return error; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci data->xy_switch = orient & MXT_T9_ORIENT_SWITCH; 198262306a36Sopenharmony_ci data->invertx = orient & MXT_T9_ORIENT_INVERTX; 198362306a36Sopenharmony_ci data->inverty = orient & MXT_T9_ORIENT_INVERTY; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci return 0; 198662306a36Sopenharmony_ci} 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistatic int mxt_read_t100_config(struct mxt_data *data) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci struct i2c_client *client = data->client; 199162306a36Sopenharmony_ci int error; 199262306a36Sopenharmony_ci struct mxt_object *object; 199362306a36Sopenharmony_ci u16 range_x, range_y; 199462306a36Sopenharmony_ci u8 cfg, tchaux; 199562306a36Sopenharmony_ci u8 aux; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); 199862306a36Sopenharmony_ci if (!object) 199962306a36Sopenharmony_ci return -EINVAL; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* read touchscreen dimensions */ 200262306a36Sopenharmony_ci error = __mxt_read_reg(client, 200362306a36Sopenharmony_ci object->start_address + MXT_T100_XRANGE, 200462306a36Sopenharmony_ci sizeof(range_x), &range_x); 200562306a36Sopenharmony_ci if (error) 200662306a36Sopenharmony_ci return error; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci data->max_x = get_unaligned_le16(&range_x); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci error = __mxt_read_reg(client, 201162306a36Sopenharmony_ci object->start_address + MXT_T100_YRANGE, 201262306a36Sopenharmony_ci sizeof(range_y), &range_y); 201362306a36Sopenharmony_ci if (error) 201462306a36Sopenharmony_ci return error; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci data->max_y = get_unaligned_le16(&range_y); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci error = __mxt_read_reg(client, 201962306a36Sopenharmony_ci object->start_address + MXT_T100_XSIZE, 202062306a36Sopenharmony_ci sizeof(data->xsize), &data->xsize); 202162306a36Sopenharmony_ci if (error) 202262306a36Sopenharmony_ci return error; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci error = __mxt_read_reg(client, 202562306a36Sopenharmony_ci object->start_address + MXT_T100_YSIZE, 202662306a36Sopenharmony_ci sizeof(data->ysize), &data->ysize); 202762306a36Sopenharmony_ci if (error) 202862306a36Sopenharmony_ci return error; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* read orientation config */ 203162306a36Sopenharmony_ci error = __mxt_read_reg(client, 203262306a36Sopenharmony_ci object->start_address + MXT_T100_CFG1, 203362306a36Sopenharmony_ci 1, &cfg); 203462306a36Sopenharmony_ci if (error) 203562306a36Sopenharmony_ci return error; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY; 203862306a36Sopenharmony_ci data->invertx = cfg & MXT_T100_CFG_INVERTX; 203962306a36Sopenharmony_ci data->inverty = cfg & MXT_T100_CFG_INVERTY; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci /* allocate aux bytes */ 204262306a36Sopenharmony_ci error = __mxt_read_reg(client, 204362306a36Sopenharmony_ci object->start_address + MXT_T100_TCHAUX, 204462306a36Sopenharmony_ci 1, &tchaux); 204562306a36Sopenharmony_ci if (error) 204662306a36Sopenharmony_ci return error; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci aux = 6; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci if (tchaux & MXT_T100_TCHAUX_VECT) 205162306a36Sopenharmony_ci data->t100_aux_vect = aux++; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci if (tchaux & MXT_T100_TCHAUX_AMPL) 205462306a36Sopenharmony_ci data->t100_aux_ampl = aux++; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci if (tchaux & MXT_T100_TCHAUX_AREA) 205762306a36Sopenharmony_ci data->t100_aux_area = aux++; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci dev_dbg(&client->dev, 206062306a36Sopenharmony_ci "T100 aux mappings vect:%u ampl:%u area:%u\n", 206162306a36Sopenharmony_ci data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci return 0; 206462306a36Sopenharmony_ci} 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_cistatic int mxt_input_open(struct input_dev *dev); 206762306a36Sopenharmony_cistatic void mxt_input_close(struct input_dev *dev); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic void mxt_set_up_as_touchpad(struct input_dev *input_dev, 207062306a36Sopenharmony_ci struct mxt_data *data) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci int i; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci input_dev->name = "Atmel maXTouch Touchpad"; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); 207962306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); 208062306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_MT_POSITION_X, 208162306a36Sopenharmony_ci MXT_PIXELS_PER_MM); 208262306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_MT_POSITION_Y, 208362306a36Sopenharmony_ci MXT_PIXELS_PER_MM); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci for (i = 0; i < data->t19_num_keys; i++) 208662306a36Sopenharmony_ci if (data->t19_keymap[i] != KEY_RESERVED) 208762306a36Sopenharmony_ci input_set_capability(input_dev, EV_KEY, 208862306a36Sopenharmony_ci data->t19_keymap[i]); 208962306a36Sopenharmony_ci} 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_cistatic int mxt_initialize_input_device(struct mxt_data *data) 209262306a36Sopenharmony_ci{ 209362306a36Sopenharmony_ci struct device *dev = &data->client->dev; 209462306a36Sopenharmony_ci struct input_dev *input_dev; 209562306a36Sopenharmony_ci int error; 209662306a36Sopenharmony_ci unsigned int num_mt_slots; 209762306a36Sopenharmony_ci unsigned int mt_flags = 0; 209862306a36Sopenharmony_ci int i; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci switch (data->multitouch) { 210162306a36Sopenharmony_ci case MXT_TOUCH_MULTI_T9: 210262306a36Sopenharmony_ci num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; 210362306a36Sopenharmony_ci error = mxt_read_t9_resolution(data); 210462306a36Sopenharmony_ci if (error) 210562306a36Sopenharmony_ci dev_warn(dev, "Failed to initialize T9 resolution\n"); 210662306a36Sopenharmony_ci break; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci case MXT_TOUCH_MULTITOUCHSCREEN_T100: 210962306a36Sopenharmony_ci num_mt_slots = data->num_touchids; 211062306a36Sopenharmony_ci error = mxt_read_t100_config(data); 211162306a36Sopenharmony_ci if (error) 211262306a36Sopenharmony_ci dev_warn(dev, "Failed to read T100 config\n"); 211362306a36Sopenharmony_ci break; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci default: 211662306a36Sopenharmony_ci dev_err(dev, "Invalid multitouch object\n"); 211762306a36Sopenharmony_ci return -EINVAL; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci /* Handle default values and orientation switch */ 212162306a36Sopenharmony_ci if (data->max_x == 0) 212262306a36Sopenharmony_ci data->max_x = 1023; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci if (data->max_y == 0) 212562306a36Sopenharmony_ci data->max_y = 1023; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (data->xy_switch) 212862306a36Sopenharmony_ci swap(data->max_x, data->max_y); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci /* Register input device */ 213362306a36Sopenharmony_ci input_dev = input_allocate_device(); 213462306a36Sopenharmony_ci if (!input_dev) 213562306a36Sopenharmony_ci return -ENOMEM; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci input_dev->name = "Atmel maXTouch Touchscreen"; 213862306a36Sopenharmony_ci input_dev->phys = data->phys; 213962306a36Sopenharmony_ci input_dev->id.bustype = BUS_I2C; 214062306a36Sopenharmony_ci input_dev->dev.parent = dev; 214162306a36Sopenharmony_ci input_dev->open = mxt_input_open; 214262306a36Sopenharmony_ci input_dev->close = mxt_input_close; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci input_dev->keycode = data->t15_keymap; 214562306a36Sopenharmony_ci input_dev->keycodemax = data->t15_num_keys; 214662306a36Sopenharmony_ci input_dev->keycodesize = sizeof(data->t15_keymap[0]); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* For single touch */ 215162306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); 215262306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTI_T9 || 215562306a36Sopenharmony_ci (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && 215662306a36Sopenharmony_ci data->t100_aux_ampl)) { 215762306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci /* If device has buttons we assume it is a touchpad */ 216162306a36Sopenharmony_ci if (data->t19_num_keys) { 216262306a36Sopenharmony_ci mxt_set_up_as_touchpad(input_dev, data); 216362306a36Sopenharmony_ci mt_flags |= INPUT_MT_POINTER; 216462306a36Sopenharmony_ci } else { 216562306a36Sopenharmony_ci mt_flags |= INPUT_MT_DIRECT; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci /* For multi touch */ 216962306a36Sopenharmony_ci error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); 217062306a36Sopenharmony_ci if (error) { 217162306a36Sopenharmony_ci dev_err(dev, "Error %d initialising slots\n", error); 217262306a36Sopenharmony_ci goto err_free_mem; 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) { 217662306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 217762306a36Sopenharmony_ci 0, MT_TOOL_MAX, 0, 0); 217862306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_DISTANCE, 217962306a36Sopenharmony_ci MXT_DISTANCE_ACTIVE_TOUCH, 218062306a36Sopenharmony_ci MXT_DISTANCE_HOVERING, 218162306a36Sopenharmony_ci 0, 0); 218262306a36Sopenharmony_ci } 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_POSITION_X, 218562306a36Sopenharmony_ci 0, data->max_x, 0, 0); 218662306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 218762306a36Sopenharmony_ci 0, data->max_y, 0, 0); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTI_T9 || 219062306a36Sopenharmony_ci (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && 219162306a36Sopenharmony_ci data->t100_aux_area)) { 219262306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 219362306a36Sopenharmony_ci 0, MXT_MAX_AREA, 0, 0); 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTI_T9 || 219762306a36Sopenharmony_ci (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && 219862306a36Sopenharmony_ci data->t100_aux_ampl)) { 219962306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_PRESSURE, 220062306a36Sopenharmony_ci 0, 255, 0, 0); 220162306a36Sopenharmony_ci } 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && 220462306a36Sopenharmony_ci data->t100_aux_vect) { 220562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 220662306a36Sopenharmony_ci 0, 255, 0, 0); 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && 221062306a36Sopenharmony_ci data->t100_aux_vect) { 221162306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 221262306a36Sopenharmony_ci 0, 255, 0, 0); 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci /* For T15 and T97 Key Array */ 221662306a36Sopenharmony_ci if (data->T15_reportid_min || data->T97_reportid_min) { 221762306a36Sopenharmony_ci for (i = 0; i < data->t15_num_keys; i++) 221862306a36Sopenharmony_ci input_set_capability(input_dev, 221962306a36Sopenharmony_ci EV_KEY, data->t15_keymap[i]); 222062306a36Sopenharmony_ci } 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci input_set_drvdata(input_dev, data); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci error = input_register_device(input_dev); 222562306a36Sopenharmony_ci if (error) { 222662306a36Sopenharmony_ci dev_err(dev, "Error %d registering input device\n", error); 222762306a36Sopenharmony_ci goto err_free_mem; 222862306a36Sopenharmony_ci } 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci data->input_dev = input_dev; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci return 0; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cierr_free_mem: 223562306a36Sopenharmony_ci input_free_device(input_dev); 223662306a36Sopenharmony_ci return error; 223762306a36Sopenharmony_ci} 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_cistatic int mxt_configure_objects(struct mxt_data *data, 224062306a36Sopenharmony_ci const struct firmware *cfg); 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_cistatic void mxt_config_cb(const struct firmware *cfg, void *ctx) 224362306a36Sopenharmony_ci{ 224462306a36Sopenharmony_ci mxt_configure_objects(ctx, cfg); 224562306a36Sopenharmony_ci release_firmware(cfg); 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_cistatic int mxt_initialize(struct mxt_data *data) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct i2c_client *client = data->client; 225162306a36Sopenharmony_ci int recovery_attempts = 0; 225262306a36Sopenharmony_ci int error; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci while (1) { 225562306a36Sopenharmony_ci error = mxt_read_info_block(data); 225662306a36Sopenharmony_ci if (!error) 225762306a36Sopenharmony_ci break; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci /* Check bootloader state */ 226062306a36Sopenharmony_ci error = mxt_probe_bootloader(data, false); 226162306a36Sopenharmony_ci if (error) { 226262306a36Sopenharmony_ci dev_info(&client->dev, "Trying alternate bootloader address\n"); 226362306a36Sopenharmony_ci error = mxt_probe_bootloader(data, true); 226462306a36Sopenharmony_ci if (error) { 226562306a36Sopenharmony_ci /* Chip is not in appmode or bootloader mode */ 226662306a36Sopenharmony_ci return error; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci /* OK, we are in bootloader, see if we can recover */ 227162306a36Sopenharmony_ci if (++recovery_attempts > 1) { 227262306a36Sopenharmony_ci dev_err(&client->dev, "Could not recover from bootloader mode\n"); 227362306a36Sopenharmony_ci /* 227462306a36Sopenharmony_ci * We can reflash from this state, so do not 227562306a36Sopenharmony_ci * abort initialization. 227662306a36Sopenharmony_ci */ 227762306a36Sopenharmony_ci data->in_bootloader = true; 227862306a36Sopenharmony_ci return 0; 227962306a36Sopenharmony_ci } 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci /* Attempt to exit bootloader into app mode */ 228262306a36Sopenharmony_ci mxt_send_bootloader_cmd(data, false); 228362306a36Sopenharmony_ci msleep(MXT_FW_RESET_TIME); 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci error = mxt_check_retrigen(data); 228762306a36Sopenharmony_ci if (error) 228862306a36Sopenharmony_ci return error; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci error = mxt_acquire_irq(data); 229162306a36Sopenharmony_ci if (error) 229262306a36Sopenharmony_ci return error; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, 229562306a36Sopenharmony_ci &client->dev, GFP_KERNEL, data, 229662306a36Sopenharmony_ci mxt_config_cb); 229762306a36Sopenharmony_ci if (error) { 229862306a36Sopenharmony_ci dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", 229962306a36Sopenharmony_ci error); 230062306a36Sopenharmony_ci return error; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci return 0; 230462306a36Sopenharmony_ci} 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_cistatic int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci struct device *dev = &data->client->dev; 230962306a36Sopenharmony_ci int error; 231062306a36Sopenharmony_ci struct t7_config *new_config; 231162306a36Sopenharmony_ci struct t7_config deepsleep = { .active = 0, .idle = 0 }; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci if (sleep == MXT_POWER_CFG_DEEPSLEEP) 231462306a36Sopenharmony_ci new_config = &deepsleep; 231562306a36Sopenharmony_ci else 231662306a36Sopenharmony_ci new_config = &data->t7_cfg; 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci error = __mxt_write_reg(data->client, data->T7_address, 231962306a36Sopenharmony_ci sizeof(data->t7_cfg), new_config); 232062306a36Sopenharmony_ci if (error) 232162306a36Sopenharmony_ci return error; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", 232462306a36Sopenharmony_ci new_config->active, new_config->idle); 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci return 0; 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic int mxt_init_t7_power_cfg(struct mxt_data *data) 233062306a36Sopenharmony_ci{ 233162306a36Sopenharmony_ci struct device *dev = &data->client->dev; 233262306a36Sopenharmony_ci int error; 233362306a36Sopenharmony_ci bool retry = false; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cirecheck: 233662306a36Sopenharmony_ci error = __mxt_read_reg(data->client, data->T7_address, 233762306a36Sopenharmony_ci sizeof(data->t7_cfg), &data->t7_cfg); 233862306a36Sopenharmony_ci if (error) 233962306a36Sopenharmony_ci return error; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { 234262306a36Sopenharmony_ci if (!retry) { 234362306a36Sopenharmony_ci dev_dbg(dev, "T7 cfg zero, resetting\n"); 234462306a36Sopenharmony_ci mxt_soft_reset(data); 234562306a36Sopenharmony_ci retry = true; 234662306a36Sopenharmony_ci goto recheck; 234762306a36Sopenharmony_ci } else { 234862306a36Sopenharmony_ci dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); 234962306a36Sopenharmony_ci data->t7_cfg.active = 20; 235062306a36Sopenharmony_ci data->t7_cfg.idle = 100; 235162306a36Sopenharmony_ci return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci } 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", 235662306a36Sopenharmony_ci data->t7_cfg.active, data->t7_cfg.idle); 235762306a36Sopenharmony_ci return 0; 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 236162306a36Sopenharmony_cistatic const struct v4l2_file_operations mxt_video_fops = { 236262306a36Sopenharmony_ci .owner = THIS_MODULE, 236362306a36Sopenharmony_ci .open = v4l2_fh_open, 236462306a36Sopenharmony_ci .release = vb2_fop_release, 236562306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 236662306a36Sopenharmony_ci .read = vb2_fop_read, 236762306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 236862306a36Sopenharmony_ci .poll = vb2_fop_poll, 236962306a36Sopenharmony_ci}; 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_cistatic u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, 237262306a36Sopenharmony_ci unsigned int y) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci struct mxt_info *info = data->info; 237562306a36Sopenharmony_ci struct mxt_dbg *dbg = &data->dbg; 237662306a36Sopenharmony_ci unsigned int ofs, page; 237762306a36Sopenharmony_ci unsigned int col = 0; 237862306a36Sopenharmony_ci unsigned int col_width; 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci if (info->family_id == MXT_FAMILY_1386) { 238162306a36Sopenharmony_ci col_width = info->matrix_ysize / MXT1386_COLUMNS; 238262306a36Sopenharmony_ci col = y / col_width; 238362306a36Sopenharmony_ci y = y % col_width; 238462306a36Sopenharmony_ci } else { 238562306a36Sopenharmony_ci col_width = info->matrix_ysize; 238662306a36Sopenharmony_ci } 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci ofs = (y + (x * col_width)) * sizeof(u16); 238962306a36Sopenharmony_ci page = ofs / MXT_DIAGNOSTIC_SIZE; 239062306a36Sopenharmony_ci ofs %= MXT_DIAGNOSTIC_SIZE; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci if (info->family_id == MXT_FAMILY_1386) 239362306a36Sopenharmony_ci page += col * MXT1386_PAGES_PER_COLUMN; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_cistatic int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci struct mxt_dbg *dbg = &data->dbg; 240162306a36Sopenharmony_ci unsigned int x = 0; 240262306a36Sopenharmony_ci unsigned int y = 0; 240362306a36Sopenharmony_ci unsigned int i, rx, ry; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci for (i = 0; i < dbg->t37_nodes; i++) { 240662306a36Sopenharmony_ci /* Handle orientation */ 240762306a36Sopenharmony_ci rx = data->xy_switch ? y : x; 240862306a36Sopenharmony_ci ry = data->xy_switch ? x : y; 240962306a36Sopenharmony_ci rx = data->invertx ? (data->xsize - 1 - rx) : rx; 241062306a36Sopenharmony_ci ry = data->inverty ? (data->ysize - 1 - ry) : ry; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci outbuf[i] = mxt_get_debug_value(data, rx, ry); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci /* Next value */ 241562306a36Sopenharmony_ci if (++x >= (data->xy_switch ? data->ysize : data->xsize)) { 241662306a36Sopenharmony_ci x = 0; 241762306a36Sopenharmony_ci y++; 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci return 0; 242262306a36Sopenharmony_ci} 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_cistatic int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode, 242562306a36Sopenharmony_ci u16 *outbuf) 242662306a36Sopenharmony_ci{ 242762306a36Sopenharmony_ci struct mxt_dbg *dbg = &data->dbg; 242862306a36Sopenharmony_ci int retries = 0; 242962306a36Sopenharmony_ci int page; 243062306a36Sopenharmony_ci int ret; 243162306a36Sopenharmony_ci u8 cmd = mode; 243262306a36Sopenharmony_ci struct t37_debug *p; 243362306a36Sopenharmony_ci u8 cmd_poll; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci for (page = 0; page < dbg->t37_pages; page++) { 243662306a36Sopenharmony_ci p = dbg->t37_buf + page; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci ret = mxt_write_reg(data->client, dbg->diag_cmd_address, 243962306a36Sopenharmony_ci cmd); 244062306a36Sopenharmony_ci if (ret) 244162306a36Sopenharmony_ci return ret; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci retries = 0; 244462306a36Sopenharmony_ci msleep(20); 244562306a36Sopenharmony_ciwait_cmd: 244662306a36Sopenharmony_ci /* Read back command byte */ 244762306a36Sopenharmony_ci ret = __mxt_read_reg(data->client, dbg->diag_cmd_address, 244862306a36Sopenharmony_ci sizeof(cmd_poll), &cmd_poll); 244962306a36Sopenharmony_ci if (ret) 245062306a36Sopenharmony_ci return ret; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci /* Field is cleared once the command has been processed */ 245362306a36Sopenharmony_ci if (cmd_poll) { 245462306a36Sopenharmony_ci if (retries++ > 100) 245562306a36Sopenharmony_ci return -EINVAL; 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci msleep(20); 245862306a36Sopenharmony_ci goto wait_cmd; 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci /* Read T37 page */ 246262306a36Sopenharmony_ci ret = __mxt_read_reg(data->client, dbg->t37_address, 246362306a36Sopenharmony_ci sizeof(struct t37_debug), p); 246462306a36Sopenharmony_ci if (ret) 246562306a36Sopenharmony_ci return ret; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci if (p->mode != mode || p->page != page) { 246862306a36Sopenharmony_ci dev_err(&data->client->dev, "T37 page mismatch\n"); 246962306a36Sopenharmony_ci return -EINVAL; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", 247362306a36Sopenharmony_ci __func__, page, retries); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci /* For remaining pages, write PAGEUP rather than mode */ 247662306a36Sopenharmony_ci cmd = MXT_DIAGNOSTIC_PAGEUP; 247762306a36Sopenharmony_ci } 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci return mxt_convert_debug_pages(data, outbuf); 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic int mxt_queue_setup(struct vb2_queue *q, 248362306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 248462306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 248562306a36Sopenharmony_ci{ 248662306a36Sopenharmony_ci struct mxt_data *data = q->drv_priv; 248762306a36Sopenharmony_ci size_t size = data->dbg.t37_nodes * sizeof(u16); 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci if (*nplanes) 249062306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci *nplanes = 1; 249362306a36Sopenharmony_ci sizes[0] = size; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci return 0; 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cistatic void mxt_buffer_queue(struct vb2_buffer *vb) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); 250162306a36Sopenharmony_ci u16 *ptr; 250262306a36Sopenharmony_ci int ret; 250362306a36Sopenharmony_ci u8 mode; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci ptr = vb2_plane_vaddr(vb, 0); 250662306a36Sopenharmony_ci if (!ptr) { 250762306a36Sopenharmony_ci dev_err(&data->client->dev, "Error acquiring frame ptr\n"); 250862306a36Sopenharmony_ci goto fault; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci switch (data->dbg.input) { 251262306a36Sopenharmony_ci case MXT_V4L_INPUT_DELTAS: 251362306a36Sopenharmony_ci default: 251462306a36Sopenharmony_ci mode = MXT_DIAGNOSTIC_DELTAS; 251562306a36Sopenharmony_ci break; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci case MXT_V4L_INPUT_REFS: 251862306a36Sopenharmony_ci mode = MXT_DIAGNOSTIC_REFS; 251962306a36Sopenharmony_ci break; 252062306a36Sopenharmony_ci } 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci ret = mxt_read_diagnostic_debug(data, mode, ptr); 252362306a36Sopenharmony_ci if (ret) 252462306a36Sopenharmony_ci goto fault; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16)); 252762306a36Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_DONE); 252862306a36Sopenharmony_ci return; 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_cifault: 253162306a36Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 253262306a36Sopenharmony_ci} 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci/* V4L2 structures */ 253562306a36Sopenharmony_cistatic const struct vb2_ops mxt_queue_ops = { 253662306a36Sopenharmony_ci .queue_setup = mxt_queue_setup, 253762306a36Sopenharmony_ci .buf_queue = mxt_buffer_queue, 253862306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 253962306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 254062306a36Sopenharmony_ci}; 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_cistatic const struct vb2_queue mxt_queue = { 254362306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 254462306a36Sopenharmony_ci .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, 254562306a36Sopenharmony_ci .buf_struct_size = sizeof(struct mxt_vb2_buffer), 254662306a36Sopenharmony_ci .ops = &mxt_queue_ops, 254762306a36Sopenharmony_ci .mem_ops = &vb2_vmalloc_memops, 254862306a36Sopenharmony_ci .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, 254962306a36Sopenharmony_ci .min_buffers_needed = 1, 255062306a36Sopenharmony_ci}; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_cistatic int mxt_vidioc_querycap(struct file *file, void *priv, 255362306a36Sopenharmony_ci struct v4l2_capability *cap) 255462306a36Sopenharmony_ci{ 255562306a36Sopenharmony_ci struct mxt_data *data = video_drvdata(file); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci strscpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver)); 255862306a36Sopenharmony_ci strscpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card)); 255962306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 256062306a36Sopenharmony_ci "I2C:%s", dev_name(&data->client->dev)); 256162306a36Sopenharmony_ci return 0; 256262306a36Sopenharmony_ci} 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_cistatic int mxt_vidioc_enum_input(struct file *file, void *priv, 256562306a36Sopenharmony_ci struct v4l2_input *i) 256662306a36Sopenharmony_ci{ 256762306a36Sopenharmony_ci if (i->index >= MXT_V4L_INPUT_MAX) 256862306a36Sopenharmony_ci return -EINVAL; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_TOUCH; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci switch (i->index) { 257362306a36Sopenharmony_ci case MXT_V4L_INPUT_REFS: 257462306a36Sopenharmony_ci strscpy(i->name, "Mutual Capacitance References", 257562306a36Sopenharmony_ci sizeof(i->name)); 257662306a36Sopenharmony_ci break; 257762306a36Sopenharmony_ci case MXT_V4L_INPUT_DELTAS: 257862306a36Sopenharmony_ci strscpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); 257962306a36Sopenharmony_ci break; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci return 0; 258362306a36Sopenharmony_ci} 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_cistatic int mxt_set_input(struct mxt_data *data, unsigned int i) 258662306a36Sopenharmony_ci{ 258762306a36Sopenharmony_ci struct v4l2_pix_format *f = &data->dbg.format; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci if (i >= MXT_V4L_INPUT_MAX) 259062306a36Sopenharmony_ci return -EINVAL; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci if (i == MXT_V4L_INPUT_DELTAS) 259362306a36Sopenharmony_ci f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; 259462306a36Sopenharmony_ci else 259562306a36Sopenharmony_ci f->pixelformat = V4L2_TCH_FMT_TU16; 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci f->width = data->xy_switch ? data->ysize : data->xsize; 259862306a36Sopenharmony_ci f->height = data->xy_switch ? data->xsize : data->ysize; 259962306a36Sopenharmony_ci f->field = V4L2_FIELD_NONE; 260062306a36Sopenharmony_ci f->colorspace = V4L2_COLORSPACE_RAW; 260162306a36Sopenharmony_ci f->bytesperline = f->width * sizeof(u16); 260262306a36Sopenharmony_ci f->sizeimage = f->width * f->height * sizeof(u16); 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci data->dbg.input = i; 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci return 0; 260762306a36Sopenharmony_ci} 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_cistatic int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i) 261062306a36Sopenharmony_ci{ 261162306a36Sopenharmony_ci return mxt_set_input(video_drvdata(file), i); 261262306a36Sopenharmony_ci} 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_cistatic int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i) 261562306a36Sopenharmony_ci{ 261662306a36Sopenharmony_ci struct mxt_data *data = video_drvdata(file); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci *i = data->dbg.input; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci return 0; 262162306a36Sopenharmony_ci} 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_cistatic int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f) 262462306a36Sopenharmony_ci{ 262562306a36Sopenharmony_ci struct mxt_data *data = video_drvdata(file); 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 262862306a36Sopenharmony_ci f->fmt.pix = data->dbg.format; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci return 0; 263162306a36Sopenharmony_ci} 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_cistatic int mxt_vidioc_enum_fmt(struct file *file, void *priv, 263462306a36Sopenharmony_ci struct v4l2_fmtdesc *fmt) 263562306a36Sopenharmony_ci{ 263662306a36Sopenharmony_ci if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 263762306a36Sopenharmony_ci return -EINVAL; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci switch (fmt->index) { 264062306a36Sopenharmony_ci case 0: 264162306a36Sopenharmony_ci fmt->pixelformat = V4L2_TCH_FMT_TU16; 264262306a36Sopenharmony_ci break; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci case 1: 264562306a36Sopenharmony_ci fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; 264662306a36Sopenharmony_ci break; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci default: 264962306a36Sopenharmony_ci return -EINVAL; 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci return 0; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_cistatic int mxt_vidioc_g_parm(struct file *file, void *fh, 265662306a36Sopenharmony_ci struct v4l2_streamparm *a) 265762306a36Sopenharmony_ci{ 265862306a36Sopenharmony_ci if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 265962306a36Sopenharmony_ci return -EINVAL; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci a->parm.capture.readbuffers = 1; 266262306a36Sopenharmony_ci a->parm.capture.timeperframe.numerator = 1; 266362306a36Sopenharmony_ci a->parm.capture.timeperframe.denominator = 10; 266462306a36Sopenharmony_ci return 0; 266562306a36Sopenharmony_ci} 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops mxt_video_ioctl_ops = { 266862306a36Sopenharmony_ci .vidioc_querycap = mxt_vidioc_querycap, 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt, 267162306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = mxt_vidioc_fmt, 267262306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = mxt_vidioc_fmt, 267362306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = mxt_vidioc_fmt, 267462306a36Sopenharmony_ci .vidioc_g_parm = mxt_vidioc_g_parm, 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci .vidioc_enum_input = mxt_vidioc_enum_input, 267762306a36Sopenharmony_ci .vidioc_g_input = mxt_vidioc_g_input, 267862306a36Sopenharmony_ci .vidioc_s_input = mxt_vidioc_s_input, 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 268162306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 268262306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 268362306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 268462306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 268562306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 268862306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 268962306a36Sopenharmony_ci}; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_cistatic const struct video_device mxt_video_device = { 269262306a36Sopenharmony_ci .name = "Atmel maxTouch", 269362306a36Sopenharmony_ci .fops = &mxt_video_fops, 269462306a36Sopenharmony_ci .ioctl_ops = &mxt_video_ioctl_ops, 269562306a36Sopenharmony_ci .release = video_device_release_empty, 269662306a36Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | 269762306a36Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, 269862306a36Sopenharmony_ci}; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_cistatic void mxt_debug_init(struct mxt_data *data) 270162306a36Sopenharmony_ci{ 270262306a36Sopenharmony_ci struct mxt_info *info = data->info; 270362306a36Sopenharmony_ci struct mxt_dbg *dbg = &data->dbg; 270462306a36Sopenharmony_ci struct mxt_object *object; 270562306a36Sopenharmony_ci int error; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci object = mxt_get_object(data, MXT_GEN_COMMAND_T6); 270862306a36Sopenharmony_ci if (!object) 270962306a36Sopenharmony_ci goto error; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); 271462306a36Sopenharmony_ci if (!object) 271562306a36Sopenharmony_ci goto error; 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci if (mxt_obj_size(object) != sizeof(struct t37_debug)) { 271862306a36Sopenharmony_ci dev_warn(&data->client->dev, "Bad T37 size"); 271962306a36Sopenharmony_ci goto error; 272062306a36Sopenharmony_ci } 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci dbg->t37_address = object->start_address; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci /* Calculate size of data and allocate buffer */ 272562306a36Sopenharmony_ci dbg->t37_nodes = data->xsize * data->ysize; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci if (info->family_id == MXT_FAMILY_1386) 272862306a36Sopenharmony_ci dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; 272962306a36Sopenharmony_ci else 273062306a36Sopenharmony_ci dbg->t37_pages = DIV_ROUND_UP(data->xsize * 273162306a36Sopenharmony_ci info->matrix_ysize * 273262306a36Sopenharmony_ci sizeof(u16), 273362306a36Sopenharmony_ci sizeof(dbg->t37_buf->data)); 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, 273662306a36Sopenharmony_ci sizeof(struct t37_debug), GFP_KERNEL); 273762306a36Sopenharmony_ci if (!dbg->t37_buf) 273862306a36Sopenharmony_ci goto error; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci /* init channel to zero */ 274162306a36Sopenharmony_ci mxt_set_input(data, 0); 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci /* register video device */ 274462306a36Sopenharmony_ci snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts"); 274562306a36Sopenharmony_ci error = v4l2_device_register(&data->client->dev, &dbg->v4l2); 274662306a36Sopenharmony_ci if (error) 274762306a36Sopenharmony_ci goto error; 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci /* initialize the queue */ 275062306a36Sopenharmony_ci mutex_init(&dbg->lock); 275162306a36Sopenharmony_ci dbg->queue = mxt_queue; 275262306a36Sopenharmony_ci dbg->queue.drv_priv = data; 275362306a36Sopenharmony_ci dbg->queue.lock = &dbg->lock; 275462306a36Sopenharmony_ci dbg->queue.dev = &data->client->dev; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci error = vb2_queue_init(&dbg->queue); 275762306a36Sopenharmony_ci if (error) 275862306a36Sopenharmony_ci goto error_unreg_v4l2; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci dbg->vdev = mxt_video_device; 276162306a36Sopenharmony_ci dbg->vdev.v4l2_dev = &dbg->v4l2; 276262306a36Sopenharmony_ci dbg->vdev.lock = &dbg->lock; 276362306a36Sopenharmony_ci dbg->vdev.vfl_dir = VFL_DIR_RX; 276462306a36Sopenharmony_ci dbg->vdev.queue = &dbg->queue; 276562306a36Sopenharmony_ci video_set_drvdata(&dbg->vdev, data); 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1); 276862306a36Sopenharmony_ci if (error) 276962306a36Sopenharmony_ci goto error_unreg_v4l2; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci return; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cierror_unreg_v4l2: 277462306a36Sopenharmony_ci v4l2_device_unregister(&dbg->v4l2); 277562306a36Sopenharmony_cierror: 277662306a36Sopenharmony_ci dev_warn(&data->client->dev, "Error initializing T37\n"); 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci#else 277962306a36Sopenharmony_cistatic void mxt_debug_init(struct mxt_data *data) 278062306a36Sopenharmony_ci{ 278162306a36Sopenharmony_ci} 278262306a36Sopenharmony_ci#endif 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_cistatic int mxt_configure_objects(struct mxt_data *data, 278562306a36Sopenharmony_ci const struct firmware *cfg) 278662306a36Sopenharmony_ci{ 278762306a36Sopenharmony_ci struct device *dev = &data->client->dev; 278862306a36Sopenharmony_ci int error; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci error = mxt_init_t7_power_cfg(data); 279162306a36Sopenharmony_ci if (error) { 279262306a36Sopenharmony_ci dev_err(dev, "Failed to initialize power cfg\n"); 279362306a36Sopenharmony_ci return error; 279462306a36Sopenharmony_ci } 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci if (cfg) { 279762306a36Sopenharmony_ci error = mxt_update_cfg(data, cfg); 279862306a36Sopenharmony_ci if (error) 279962306a36Sopenharmony_ci dev_warn(dev, "Error %d updating config\n", error); 280062306a36Sopenharmony_ci } 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci if (data->multitouch) { 280362306a36Sopenharmony_ci error = mxt_initialize_input_device(data); 280462306a36Sopenharmony_ci if (error) 280562306a36Sopenharmony_ci return error; 280662306a36Sopenharmony_ci } else { 280762306a36Sopenharmony_ci dev_warn(dev, "No touch object detected\n"); 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci mxt_debug_init(data); 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci return 0; 281362306a36Sopenharmony_ci} 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci/* Firmware Version is returned as Major.Minor.Build */ 281662306a36Sopenharmony_cistatic ssize_t mxt_fw_version_show(struct device *dev, 281762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 281862306a36Sopenharmony_ci{ 281962306a36Sopenharmony_ci struct mxt_data *data = dev_get_drvdata(dev); 282062306a36Sopenharmony_ci struct mxt_info *info = data->info; 282162306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", 282262306a36Sopenharmony_ci info->version >> 4, info->version & 0xf, info->build); 282362306a36Sopenharmony_ci} 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci/* Hardware Version is returned as FamilyID.VariantID */ 282662306a36Sopenharmony_cistatic ssize_t mxt_hw_version_show(struct device *dev, 282762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 282862306a36Sopenharmony_ci{ 282962306a36Sopenharmony_ci struct mxt_data *data = dev_get_drvdata(dev); 283062306a36Sopenharmony_ci struct mxt_info *info = data->info; 283162306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u.%u\n", 283262306a36Sopenharmony_ci info->family_id, info->variant_id); 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_cistatic ssize_t mxt_show_instance(char *buf, int count, 283662306a36Sopenharmony_ci struct mxt_object *object, int instance, 283762306a36Sopenharmony_ci const u8 *val) 283862306a36Sopenharmony_ci{ 283962306a36Sopenharmony_ci int i; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci if (mxt_obj_instances(object) > 1) 284262306a36Sopenharmony_ci count += scnprintf(buf + count, PAGE_SIZE - count, 284362306a36Sopenharmony_ci "Instance %u\n", instance); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci for (i = 0; i < mxt_obj_size(object); i++) 284662306a36Sopenharmony_ci count += scnprintf(buf + count, PAGE_SIZE - count, 284762306a36Sopenharmony_ci "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); 284862306a36Sopenharmony_ci count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci return count; 285162306a36Sopenharmony_ci} 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_cistatic ssize_t mxt_object_show(struct device *dev, 285462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 285562306a36Sopenharmony_ci{ 285662306a36Sopenharmony_ci struct mxt_data *data = dev_get_drvdata(dev); 285762306a36Sopenharmony_ci struct mxt_object *object; 285862306a36Sopenharmony_ci int count = 0; 285962306a36Sopenharmony_ci int i, j; 286062306a36Sopenharmony_ci int error; 286162306a36Sopenharmony_ci u8 *obuf; 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci /* Pre-allocate buffer large enough to hold max sized object. */ 286462306a36Sopenharmony_ci obuf = kmalloc(256, GFP_KERNEL); 286562306a36Sopenharmony_ci if (!obuf) 286662306a36Sopenharmony_ci return -ENOMEM; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci error = 0; 286962306a36Sopenharmony_ci for (i = 0; i < data->info->object_num; i++) { 287062306a36Sopenharmony_ci object = data->object_table + i; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci if (!mxt_object_readable(object->type)) 287362306a36Sopenharmony_ci continue; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci count += scnprintf(buf + count, PAGE_SIZE - count, 287662306a36Sopenharmony_ci "T%u:\n", object->type); 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci for (j = 0; j < mxt_obj_instances(object); j++) { 287962306a36Sopenharmony_ci u16 size = mxt_obj_size(object); 288062306a36Sopenharmony_ci u16 addr = object->start_address + j * size; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci error = __mxt_read_reg(data->client, addr, size, obuf); 288362306a36Sopenharmony_ci if (error) 288462306a36Sopenharmony_ci goto done; 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci count = mxt_show_instance(buf, count, object, j, obuf); 288762306a36Sopenharmony_ci } 288862306a36Sopenharmony_ci } 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_cidone: 289162306a36Sopenharmony_ci kfree(obuf); 289262306a36Sopenharmony_ci return error ?: count; 289362306a36Sopenharmony_ci} 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_cistatic int mxt_check_firmware_format(struct device *dev, 289662306a36Sopenharmony_ci const struct firmware *fw) 289762306a36Sopenharmony_ci{ 289862306a36Sopenharmony_ci unsigned int pos = 0; 289962306a36Sopenharmony_ci char c; 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci while (pos < fw->size) { 290262306a36Sopenharmony_ci c = *(fw->data + pos); 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci if (c < '0' || (c > '9' && c < 'A') || c > 'F') 290562306a36Sopenharmony_ci return 0; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci pos++; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci /* 291162306a36Sopenharmony_ci * To convert file try: 291262306a36Sopenharmony_ci * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw 291362306a36Sopenharmony_ci */ 291462306a36Sopenharmony_ci dev_err(dev, "Aborting: firmware file must be in binary format\n"); 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci return -EINVAL; 291762306a36Sopenharmony_ci} 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_cistatic int mxt_load_fw(struct device *dev, const char *fn) 292062306a36Sopenharmony_ci{ 292162306a36Sopenharmony_ci struct mxt_data *data = dev_get_drvdata(dev); 292262306a36Sopenharmony_ci const struct firmware *fw = NULL; 292362306a36Sopenharmony_ci unsigned int frame_size; 292462306a36Sopenharmony_ci unsigned int pos = 0; 292562306a36Sopenharmony_ci unsigned int retry = 0; 292662306a36Sopenharmony_ci unsigned int frame = 0; 292762306a36Sopenharmony_ci int ret; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci ret = request_firmware(&fw, fn, dev); 293062306a36Sopenharmony_ci if (ret) { 293162306a36Sopenharmony_ci dev_err(dev, "Unable to open firmware %s\n", fn); 293262306a36Sopenharmony_ci return ret; 293362306a36Sopenharmony_ci } 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci /* Check for incorrect enc file */ 293662306a36Sopenharmony_ci ret = mxt_check_firmware_format(dev, fw); 293762306a36Sopenharmony_ci if (ret) 293862306a36Sopenharmony_ci goto release_firmware; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (!data->in_bootloader) { 294162306a36Sopenharmony_ci /* Change to the bootloader mode */ 294262306a36Sopenharmony_ci data->in_bootloader = true; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci ret = mxt_t6_command(data, MXT_COMMAND_RESET, 294562306a36Sopenharmony_ci MXT_BOOT_VALUE, false); 294662306a36Sopenharmony_ci if (ret) 294762306a36Sopenharmony_ci goto release_firmware; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci msleep(MXT_RESET_TIME); 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci /* Do not need to scan since we know family ID */ 295262306a36Sopenharmony_ci ret = mxt_lookup_bootloader_address(data, 0); 295362306a36Sopenharmony_ci if (ret) 295462306a36Sopenharmony_ci goto release_firmware; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci mxt_free_input_device(data); 295762306a36Sopenharmony_ci mxt_free_object_table(data); 295862306a36Sopenharmony_ci } else { 295962306a36Sopenharmony_ci enable_irq(data->irq); 296062306a36Sopenharmony_ci } 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci reinit_completion(&data->bl_completion); 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); 296562306a36Sopenharmony_ci if (ret) { 296662306a36Sopenharmony_ci /* Bootloader may still be unlocked from previous attempt */ 296762306a36Sopenharmony_ci ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); 296862306a36Sopenharmony_ci if (ret) 296962306a36Sopenharmony_ci goto disable_irq; 297062306a36Sopenharmony_ci } else { 297162306a36Sopenharmony_ci dev_info(dev, "Unlocking bootloader\n"); 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci /* Unlock bootloader */ 297462306a36Sopenharmony_ci ret = mxt_send_bootloader_cmd(data, true); 297562306a36Sopenharmony_ci if (ret) 297662306a36Sopenharmony_ci goto disable_irq; 297762306a36Sopenharmony_ci } 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci while (pos < fw->size) { 298062306a36Sopenharmony_ci ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); 298162306a36Sopenharmony_ci if (ret) 298262306a36Sopenharmony_ci goto disable_irq; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci /* Take account of CRC bytes */ 298762306a36Sopenharmony_ci frame_size += 2; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci /* Write one frame to device */ 299062306a36Sopenharmony_ci ret = mxt_bootloader_write(data, fw->data + pos, frame_size); 299162306a36Sopenharmony_ci if (ret) 299262306a36Sopenharmony_ci goto disable_irq; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); 299562306a36Sopenharmony_ci if (ret) { 299662306a36Sopenharmony_ci retry++; 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci /* Back off by 20ms per retry */ 299962306a36Sopenharmony_ci msleep(retry * 20); 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci if (retry > 20) { 300262306a36Sopenharmony_ci dev_err(dev, "Retry count exceeded\n"); 300362306a36Sopenharmony_ci goto disable_irq; 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci } else { 300662306a36Sopenharmony_ci retry = 0; 300762306a36Sopenharmony_ci pos += frame_size; 300862306a36Sopenharmony_ci frame++; 300962306a36Sopenharmony_ci } 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci if (frame % 50 == 0) 301262306a36Sopenharmony_ci dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", 301362306a36Sopenharmony_ci frame, pos, fw->size); 301462306a36Sopenharmony_ci } 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci /* Wait for flash. */ 301762306a36Sopenharmony_ci ret = mxt_wait_for_completion(data, &data->bl_completion, 301862306a36Sopenharmony_ci MXT_FW_RESET_TIME); 301962306a36Sopenharmony_ci if (ret) 302062306a36Sopenharmony_ci goto disable_irq; 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci /* 302562306a36Sopenharmony_ci * Wait for device to reset. Some bootloader versions do not assert 302662306a36Sopenharmony_ci * the CHG line after bootloading has finished, so ignore potential 302762306a36Sopenharmony_ci * errors. 302862306a36Sopenharmony_ci */ 302962306a36Sopenharmony_ci mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci data->in_bootloader = false; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_cidisable_irq: 303462306a36Sopenharmony_ci disable_irq(data->irq); 303562306a36Sopenharmony_cirelease_firmware: 303662306a36Sopenharmony_ci release_firmware(fw); 303762306a36Sopenharmony_ci return ret; 303862306a36Sopenharmony_ci} 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_cistatic ssize_t mxt_update_fw_store(struct device *dev, 304162306a36Sopenharmony_ci struct device_attribute *attr, 304262306a36Sopenharmony_ci const char *buf, size_t count) 304362306a36Sopenharmony_ci{ 304462306a36Sopenharmony_ci struct mxt_data *data = dev_get_drvdata(dev); 304562306a36Sopenharmony_ci int error; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci error = mxt_load_fw(dev, MXT_FW_NAME); 304862306a36Sopenharmony_ci if (error) { 304962306a36Sopenharmony_ci dev_err(dev, "The firmware update failed(%d)\n", error); 305062306a36Sopenharmony_ci count = error; 305162306a36Sopenharmony_ci } else { 305262306a36Sopenharmony_ci dev_info(dev, "The firmware update succeeded\n"); 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci error = mxt_initialize(data); 305562306a36Sopenharmony_ci if (error) 305662306a36Sopenharmony_ci return error; 305762306a36Sopenharmony_ci } 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci return count; 306062306a36Sopenharmony_ci} 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_cistatic DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); 306362306a36Sopenharmony_cistatic DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); 306462306a36Sopenharmony_cistatic DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); 306562306a36Sopenharmony_cistatic DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_cistatic struct attribute *mxt_attrs[] = { 306862306a36Sopenharmony_ci &dev_attr_fw_version.attr, 306962306a36Sopenharmony_ci &dev_attr_hw_version.attr, 307062306a36Sopenharmony_ci &dev_attr_object.attr, 307162306a36Sopenharmony_ci &dev_attr_update_fw.attr, 307262306a36Sopenharmony_ci NULL 307362306a36Sopenharmony_ci}; 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_cistatic const struct attribute_group mxt_attr_group = { 307662306a36Sopenharmony_ci .attrs = mxt_attrs, 307762306a36Sopenharmony_ci}; 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_cistatic void mxt_start(struct mxt_data *data) 308062306a36Sopenharmony_ci{ 308162306a36Sopenharmony_ci mxt_wakeup_toggle(data->client, true, false); 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci switch (data->suspend_mode) { 308462306a36Sopenharmony_ci case MXT_SUSPEND_T9_CTRL: 308562306a36Sopenharmony_ci mxt_soft_reset(data); 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci /* Touch enable */ 308862306a36Sopenharmony_ci /* 0x83 = SCANEN | RPTEN | ENABLE */ 308962306a36Sopenharmony_ci mxt_write_object(data, 309062306a36Sopenharmony_ci MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83); 309162306a36Sopenharmony_ci break; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci case MXT_SUSPEND_DEEP_SLEEP: 309462306a36Sopenharmony_ci default: 309562306a36Sopenharmony_ci mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci /* Recalibrate since chip has been in deep sleep */ 309862306a36Sopenharmony_ci mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); 309962306a36Sopenharmony_ci break; 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci} 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_cistatic void mxt_stop(struct mxt_data *data) 310462306a36Sopenharmony_ci{ 310562306a36Sopenharmony_ci switch (data->suspend_mode) { 310662306a36Sopenharmony_ci case MXT_SUSPEND_T9_CTRL: 310762306a36Sopenharmony_ci /* Touch disable */ 310862306a36Sopenharmony_ci mxt_write_object(data, 310962306a36Sopenharmony_ci MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0); 311062306a36Sopenharmony_ci break; 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci case MXT_SUSPEND_DEEP_SLEEP: 311362306a36Sopenharmony_ci default: 311462306a36Sopenharmony_ci mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); 311562306a36Sopenharmony_ci break; 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci mxt_wakeup_toggle(data->client, false, false); 311962306a36Sopenharmony_ci} 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_cistatic int mxt_input_open(struct input_dev *dev) 312262306a36Sopenharmony_ci{ 312362306a36Sopenharmony_ci struct mxt_data *data = input_get_drvdata(dev); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci mxt_start(data); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci return 0; 312862306a36Sopenharmony_ci} 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_cistatic void mxt_input_close(struct input_dev *dev) 313162306a36Sopenharmony_ci{ 313262306a36Sopenharmony_ci struct mxt_data *data = input_get_drvdata(dev); 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci mxt_stop(data); 313562306a36Sopenharmony_ci} 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_cistatic int mxt_parse_device_properties(struct mxt_data *data) 313862306a36Sopenharmony_ci{ 313962306a36Sopenharmony_ci static const char keymap_property[] = "linux,gpio-keymap"; 314062306a36Sopenharmony_ci static const char buttons_property[] = "linux,keycodes"; 314162306a36Sopenharmony_ci struct device *dev = &data->client->dev; 314262306a36Sopenharmony_ci u32 *keymap; 314362306a36Sopenharmony_ci u32 *buttonmap; 314462306a36Sopenharmony_ci int n_keys; 314562306a36Sopenharmony_ci int error; 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci if (device_property_present(dev, keymap_property)) { 314862306a36Sopenharmony_ci n_keys = device_property_count_u32(dev, keymap_property); 314962306a36Sopenharmony_ci if (n_keys <= 0) { 315062306a36Sopenharmony_ci error = n_keys < 0 ? n_keys : -EINVAL; 315162306a36Sopenharmony_ci dev_err(dev, "invalid/malformed '%s' property: %d\n", 315262306a36Sopenharmony_ci keymap_property, error); 315362306a36Sopenharmony_ci return error; 315462306a36Sopenharmony_ci } 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap), 315762306a36Sopenharmony_ci GFP_KERNEL); 315862306a36Sopenharmony_ci if (!keymap) 315962306a36Sopenharmony_ci return -ENOMEM; 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci error = device_property_read_u32_array(dev, keymap_property, 316262306a36Sopenharmony_ci keymap, n_keys); 316362306a36Sopenharmony_ci if (error) { 316462306a36Sopenharmony_ci dev_err(dev, "failed to parse '%s' property: %d\n", 316562306a36Sopenharmony_ci keymap_property, error); 316662306a36Sopenharmony_ci return error; 316762306a36Sopenharmony_ci } 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci data->t19_keymap = keymap; 317062306a36Sopenharmony_ci data->t19_num_keys = n_keys; 317162306a36Sopenharmony_ci } 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ci if (device_property_present(dev, buttons_property)) { 317462306a36Sopenharmony_ci n_keys = device_property_count_u32(dev, buttons_property); 317562306a36Sopenharmony_ci if (n_keys <= 0) { 317662306a36Sopenharmony_ci error = n_keys < 0 ? n_keys : -EINVAL; 317762306a36Sopenharmony_ci dev_err(dev, "invalid/malformed '%s' property: %d\n", 317862306a36Sopenharmony_ci buttons_property, error); 317962306a36Sopenharmony_ci return error; 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci buttonmap = devm_kmalloc_array(dev, n_keys, sizeof(*buttonmap), 318362306a36Sopenharmony_ci GFP_KERNEL); 318462306a36Sopenharmony_ci if (!buttonmap) 318562306a36Sopenharmony_ci return -ENOMEM; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci error = device_property_read_u32_array(dev, buttons_property, 318862306a36Sopenharmony_ci buttonmap, n_keys); 318962306a36Sopenharmony_ci if (error) { 319062306a36Sopenharmony_ci dev_err(dev, "failed to parse '%s' property: %d\n", 319162306a36Sopenharmony_ci buttons_property, error); 319262306a36Sopenharmony_ci return error; 319362306a36Sopenharmony_ci } 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci data->t15_keymap = buttonmap; 319662306a36Sopenharmony_ci data->t15_num_keys = n_keys; 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci return 0; 320062306a36Sopenharmony_ci} 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_cistatic const struct dmi_system_id chromebook_T9_suspend_dmi[] = { 320362306a36Sopenharmony_ci { 320462306a36Sopenharmony_ci .matches = { 320562306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 320662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Link"), 320762306a36Sopenharmony_ci }, 320862306a36Sopenharmony_ci }, 320962306a36Sopenharmony_ci { 321062306a36Sopenharmony_ci .matches = { 321162306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), 321262306a36Sopenharmony_ci }, 321362306a36Sopenharmony_ci }, 321462306a36Sopenharmony_ci { } 321562306a36Sopenharmony_ci}; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_cistatic int mxt_probe(struct i2c_client *client) 321862306a36Sopenharmony_ci{ 321962306a36Sopenharmony_ci struct mxt_data *data; 322062306a36Sopenharmony_ci int error; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci /* 322362306a36Sopenharmony_ci * Ignore devices that do not have device properties attached to 322462306a36Sopenharmony_ci * them, as we need help determining whether we are dealing with 322562306a36Sopenharmony_ci * touch screen or touchpad. 322662306a36Sopenharmony_ci * 322762306a36Sopenharmony_ci * So far on x86 the only users of Atmel touch controllers are 322862306a36Sopenharmony_ci * Chromebooks, and chromeos_laptop driver will ensure that 322962306a36Sopenharmony_ci * necessary properties are provided (if firmware does not do that). 323062306a36Sopenharmony_ci */ 323162306a36Sopenharmony_ci if (!device_property_present(&client->dev, "compatible")) 323262306a36Sopenharmony_ci return -ENXIO; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci /* 323562306a36Sopenharmony_ci * Ignore ACPI devices representing bootloader mode. 323662306a36Sopenharmony_ci * 323762306a36Sopenharmony_ci * This is a bit of a hack: Google Chromebook BIOS creates ACPI 323862306a36Sopenharmony_ci * devices for both application and bootloader modes, but we are 323962306a36Sopenharmony_ci * interested in application mode only (if device is in bootloader 324062306a36Sopenharmony_ci * mode we'll end up switching into application anyway). So far 324162306a36Sopenharmony_ci * application mode addresses were all above 0x40, so we'll use it 324262306a36Sopenharmony_ci * as a threshold. 324362306a36Sopenharmony_ci */ 324462306a36Sopenharmony_ci if (ACPI_COMPANION(&client->dev) && client->addr < 0x40) 324562306a36Sopenharmony_ci return -ENXIO; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL); 324862306a36Sopenharmony_ci if (!data) 324962306a36Sopenharmony_ci return -ENOMEM; 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", 325262306a36Sopenharmony_ci client->adapter->nr, client->addr); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci data->client = client; 325562306a36Sopenharmony_ci data->irq = client->irq; 325662306a36Sopenharmony_ci i2c_set_clientdata(client, data); 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci init_completion(&data->bl_completion); 325962306a36Sopenharmony_ci init_completion(&data->reset_completion); 326062306a36Sopenharmony_ci init_completion(&data->crc_completion); 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ? 326362306a36Sopenharmony_ci MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci error = mxt_parse_device_properties(data); 326662306a36Sopenharmony_ci if (error) 326762306a36Sopenharmony_ci return error; 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci /* 327062306a36Sopenharmony_ci * VDDA is the analog voltage supply 2.57..3.47 V 327162306a36Sopenharmony_ci * VDD is the digital voltage supply 1.71..3.47 V 327262306a36Sopenharmony_ci */ 327362306a36Sopenharmony_ci data->regulators[0].supply = "vdda"; 327462306a36Sopenharmony_ci data->regulators[1].supply = "vdd"; 327562306a36Sopenharmony_ci error = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators), 327662306a36Sopenharmony_ci data->regulators); 327762306a36Sopenharmony_ci if (error) { 327862306a36Sopenharmony_ci if (error != -EPROBE_DEFER) 327962306a36Sopenharmony_ci dev_err(&client->dev, "Failed to get regulators %d\n", 328062306a36Sopenharmony_ci error); 328162306a36Sopenharmony_ci return error; 328262306a36Sopenharmony_ci } 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci /* Request the RESET line as asserted so we go into reset */ 328562306a36Sopenharmony_ci data->reset_gpio = devm_gpiod_get_optional(&client->dev, 328662306a36Sopenharmony_ci "reset", GPIOD_OUT_HIGH); 328762306a36Sopenharmony_ci if (IS_ERR(data->reset_gpio)) { 328862306a36Sopenharmony_ci error = PTR_ERR(data->reset_gpio); 328962306a36Sopenharmony_ci dev_err(&client->dev, "Failed to get reset gpio: %d\n", error); 329062306a36Sopenharmony_ci return error; 329162306a36Sopenharmony_ci } 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci /* Request the WAKE line as asserted so we go out of sleep */ 329462306a36Sopenharmony_ci data->wake_gpio = devm_gpiod_get_optional(&client->dev, 329562306a36Sopenharmony_ci "wake", GPIOD_OUT_HIGH); 329662306a36Sopenharmony_ci if (IS_ERR(data->wake_gpio)) { 329762306a36Sopenharmony_ci error = PTR_ERR(data->wake_gpio); 329862306a36Sopenharmony_ci dev_err(&client->dev, "Failed to get wake gpio: %d\n", error); 329962306a36Sopenharmony_ci return error; 330062306a36Sopenharmony_ci } 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 330362306a36Sopenharmony_ci NULL, mxt_interrupt, 330462306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_NO_AUTOEN, 330562306a36Sopenharmony_ci client->name, data); 330662306a36Sopenharmony_ci if (error) { 330762306a36Sopenharmony_ci dev_err(&client->dev, "Failed to register interrupt\n"); 330862306a36Sopenharmony_ci return error; 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci error = regulator_bulk_enable(ARRAY_SIZE(data->regulators), 331262306a36Sopenharmony_ci data->regulators); 331362306a36Sopenharmony_ci if (error) { 331462306a36Sopenharmony_ci dev_err(&client->dev, "failed to enable regulators: %d\n", 331562306a36Sopenharmony_ci error); 331662306a36Sopenharmony_ci return error; 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci /* 331962306a36Sopenharmony_ci * The device takes 40ms to come up after power-on according 332062306a36Sopenharmony_ci * to the mXT224 datasheet, page 13. 332162306a36Sopenharmony_ci */ 332262306a36Sopenharmony_ci msleep(MXT_BACKUP_TIME); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci if (data->reset_gpio) { 332562306a36Sopenharmony_ci /* Wait a while and then de-assert the RESET GPIO line */ 332662306a36Sopenharmony_ci msleep(MXT_RESET_GPIO_TIME); 332762306a36Sopenharmony_ci gpiod_set_value(data->reset_gpio, 0); 332862306a36Sopenharmony_ci msleep(MXT_RESET_INVALID_CHG); 332962306a36Sopenharmony_ci } 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_ci /* 333262306a36Sopenharmony_ci * Controllers like mXT1386 have a dedicated WAKE line that could be 333362306a36Sopenharmony_ci * connected to a GPIO or to I2C SCL pin, or permanently asserted low. 333462306a36Sopenharmony_ci * 333562306a36Sopenharmony_ci * This WAKE line is used for waking controller from a deep-sleep and 333662306a36Sopenharmony_ci * it needs to be asserted low for 25 milliseconds before I2C transfers 333762306a36Sopenharmony_ci * could be accepted by controller if it was in a deep-sleep mode. 333862306a36Sopenharmony_ci * Controller will go into sleep automatically after 2 seconds of 333962306a36Sopenharmony_ci * inactivity if WAKE line is deasserted and deep sleep is activated. 334062306a36Sopenharmony_ci * 334162306a36Sopenharmony_ci * If WAKE line is connected to I2C SCL pin, then the first I2C transfer 334262306a36Sopenharmony_ci * will get an instant NAK and transfer needs to be retried after 25ms. 334362306a36Sopenharmony_ci * 334462306a36Sopenharmony_ci * If WAKE line is connected to a GPIO line, the line must be asserted 334562306a36Sopenharmony_ci * 25ms before the host attempts to communicate with the controller. 334662306a36Sopenharmony_ci */ 334762306a36Sopenharmony_ci device_property_read_u32(&client->dev, "atmel,wakeup-method", 334862306a36Sopenharmony_ci &data->wakeup_method); 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci error = mxt_initialize(data); 335162306a36Sopenharmony_ci if (error) 335262306a36Sopenharmony_ci goto err_disable_regulators; 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); 335562306a36Sopenharmony_ci if (error) { 335662306a36Sopenharmony_ci dev_err(&client->dev, "Failure %d creating sysfs group\n", 335762306a36Sopenharmony_ci error); 335862306a36Sopenharmony_ci goto err_free_object; 335962306a36Sopenharmony_ci } 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci return 0; 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_cierr_free_object: 336462306a36Sopenharmony_ci mxt_free_input_device(data); 336562306a36Sopenharmony_ci mxt_free_object_table(data); 336662306a36Sopenharmony_cierr_disable_regulators: 336762306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(data->regulators), 336862306a36Sopenharmony_ci data->regulators); 336962306a36Sopenharmony_ci return error; 337062306a36Sopenharmony_ci} 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_cistatic void mxt_remove(struct i2c_client *client) 337362306a36Sopenharmony_ci{ 337462306a36Sopenharmony_ci struct mxt_data *data = i2c_get_clientdata(client); 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci disable_irq(data->irq); 337762306a36Sopenharmony_ci sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); 337862306a36Sopenharmony_ci mxt_free_input_device(data); 337962306a36Sopenharmony_ci mxt_free_object_table(data); 338062306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(data->regulators), 338162306a36Sopenharmony_ci data->regulators); 338262306a36Sopenharmony_ci} 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_cistatic int mxt_suspend(struct device *dev) 338562306a36Sopenharmony_ci{ 338662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 338762306a36Sopenharmony_ci struct mxt_data *data = i2c_get_clientdata(client); 338862306a36Sopenharmony_ci struct input_dev *input_dev = data->input_dev; 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci if (!input_dev) 339162306a36Sopenharmony_ci return 0; 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci mutex_lock(&input_dev->mutex); 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci if (input_device_enabled(input_dev)) 339662306a36Sopenharmony_ci mxt_stop(data); 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci mutex_unlock(&input_dev->mutex); 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci disable_irq(data->irq); 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_ci return 0; 340362306a36Sopenharmony_ci} 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_cistatic int mxt_resume(struct device *dev) 340662306a36Sopenharmony_ci{ 340762306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 340862306a36Sopenharmony_ci struct mxt_data *data = i2c_get_clientdata(client); 340962306a36Sopenharmony_ci struct input_dev *input_dev = data->input_dev; 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if (!input_dev) 341262306a36Sopenharmony_ci return 0; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci enable_irq(data->irq); 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci mutex_lock(&input_dev->mutex); 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci if (input_device_enabled(input_dev)) 341962306a36Sopenharmony_ci mxt_start(data); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci mutex_unlock(&input_dev->mutex); 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci return 0; 342462306a36Sopenharmony_ci} 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_cistatic const struct of_device_id mxt_of_match[] = { 342962306a36Sopenharmony_ci { .compatible = "atmel,maxtouch", }, 343062306a36Sopenharmony_ci /* Compatibles listed below are deprecated */ 343162306a36Sopenharmony_ci { .compatible = "atmel,qt602240_ts", }, 343262306a36Sopenharmony_ci { .compatible = "atmel,atmel_mxt_ts", }, 343362306a36Sopenharmony_ci { .compatible = "atmel,atmel_mxt_tp", }, 343462306a36Sopenharmony_ci { .compatible = "atmel,mXT224", }, 343562306a36Sopenharmony_ci {}, 343662306a36Sopenharmony_ci}; 343762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxt_of_match); 343862306a36Sopenharmony_ci 343962306a36Sopenharmony_ci#ifdef CONFIG_ACPI 344062306a36Sopenharmony_cistatic const struct acpi_device_id mxt_acpi_id[] = { 344162306a36Sopenharmony_ci { "ATML0000", 0 }, /* Touchpad */ 344262306a36Sopenharmony_ci { "ATML0001", 0 }, /* Touchscreen */ 344362306a36Sopenharmony_ci { } 344462306a36Sopenharmony_ci}; 344562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mxt_acpi_id); 344662306a36Sopenharmony_ci#endif 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_cistatic const struct i2c_device_id mxt_id[] = { 344962306a36Sopenharmony_ci { "qt602240_ts", 0 }, 345062306a36Sopenharmony_ci { "atmel_mxt_ts", 0 }, 345162306a36Sopenharmony_ci { "atmel_mxt_tp", 0 }, 345262306a36Sopenharmony_ci { "maxtouch", 0 }, 345362306a36Sopenharmony_ci { "mXT224", 0 }, 345462306a36Sopenharmony_ci { } 345562306a36Sopenharmony_ci}; 345662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mxt_id); 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_cistatic struct i2c_driver mxt_driver = { 345962306a36Sopenharmony_ci .driver = { 346062306a36Sopenharmony_ci .name = "atmel_mxt_ts", 346162306a36Sopenharmony_ci .of_match_table = mxt_of_match, 346262306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(mxt_acpi_id), 346362306a36Sopenharmony_ci .pm = pm_sleep_ptr(&mxt_pm_ops), 346462306a36Sopenharmony_ci }, 346562306a36Sopenharmony_ci .probe = mxt_probe, 346662306a36Sopenharmony_ci .remove = mxt_remove, 346762306a36Sopenharmony_ci .id_table = mxt_id, 346862306a36Sopenharmony_ci}; 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_cimodule_i2c_driver(mxt_driver); 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci/* Module information */ 347362306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 347462306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); 347562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3476